diff --git a/Cargo.lock b/Cargo.lock index dd0815b..a605e29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,6 +31,15 @@ dependencies = [ "libc", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anyhow" version = "1.0.70" @@ -125,6 +134,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -300,6 +320,21 @@ dependencies = [ "phf_codegen", ] +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -310,6 +345,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -413,6 +454,19 @@ dependencies = [ "syn 2.0.11", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + [[package]] name = "deunicode" version = "0.4.3" @@ -604,6 +658,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "globset" version = "0.4.10" @@ -661,6 +721,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.2.6" @@ -793,7 +862,10 @@ dependencies = [ "axum", "axum-macros", "chrono", + "derive_more", + "once_cell", "ormx", + "rust-i18n", "serde", "serde-xml-rs", "serde_plain", @@ -904,6 +976,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "lock_api" version = "0.4.9" @@ -1013,7 +1091,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -1339,6 +1417,84 @@ dependencies = [ "winapi", ] +[[package]] +name = "rust-i18n" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3dcb86b090a450cb642b6a465f0e9a3d4be26bdb9d8795a2a49fb02601b370f" +dependencies = [ + "anyhow", + "clap", + "glob", + "itertools", + "once_cell", + "quote", + "regex", + "rust-i18n-extract", + "rust-i18n-macro", + "serde", + "serde_derive", + "toml", +] + +[[package]] +name = "rust-i18n-extract" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec44568e2cdf4bfb7a62381bbc6fcdf0a27c60cd503dfa12c59e6c17cf3177fa" +dependencies = [ + "anyhow", + "ignore", + "proc-macro2", + "quote", + "regex", + "rust-i18n-support", + "serde", + "serde_json", + "serde_yaml", + "syn 1.0.109", +] + +[[package]] +name = "rust-i18n-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e7e3e8f27d472822c5cf092a22631ebc667d9f8dc89dfc50ef4e87f4ebdf92f" +dependencies = [ + "glob", + "once_cell", + "proc-macro2", + "quote", + "rust-i18n-support", + "serde", + "serde_json", + "serde_yaml", + "syn 1.0.109", +] + +[[package]] +name = "rust-i18n-support" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e6bbf2d058c3558bef952564ceb9afcb19631cde22b47dc44f436e62ecfb916" +dependencies = [ + "glob", + "once_cell", + "proc-macro2", + "serde", + "serde_json", + "serde_yaml", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustls" version = "0.19.1" @@ -1395,6 +1551,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + [[package]] name = "serde" version = "1.0.159" @@ -1489,6 +1651,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + [[package]] name = "sha-1" version = "0.10.1" @@ -1687,6 +1861,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "strum" version = "0.24.1" @@ -1775,6 +1955,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.40" @@ -1893,6 +2082,15 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "tower" version = "0.4.13" @@ -2084,6 +2282,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" @@ -2383,3 +2587,12 @@ name = "xml-rs" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/Cargo.toml b/Cargo.toml index 75fc933..fb69ab9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,21 @@ sqlx = { version = "0.5", features = ["macros", "postgres", "runtime-tokio-rustl ormx = { version = "0.10.0", features = ["postgres"] } serde-xml-rs = "0.6.0" strum = { version = "0.24.1", features = ["derive"] } +rust-i18n = "1.1.4" +once_cell = "1.17.1" +derive_more = "0.99.17" [dev-dependencies] tokio-test = "0.4.2" + +[package.metadata.i18n] +# The available locales for your application, default: ["en"]. +available-loCAles = ["en-CA", "fr-CA"] + +# The default locale, default: "en". +default-locale = "en-CA" + +# Path for your translations YAML file, default: "locales". +# This config for let `cargo i18n` command line tool know where to find your translations. +# You must keep this path is same as the path you pass to `rust_i18n::i18n!` method. +load-path = "translations" diff --git a/src/main.rs b/src/main.rs index 5f01d54..571c6b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,38 @@ mod db; mod filters; mod model; -mod translations; mod views; -use crate::model::{Division, Game, GamePlayer, League, Player, Shot, Team}; -use translations::SupportedLanguage; +use crate::model::{Division, Game, GamePlayer, League, Player, Shot, Team, Language}; + +use serde::{Serialize, Deserialize}; +use derive_more::Display; + +#[derive(Serialize, Deserialize, Clone, Copy, Debug, Display)] +pub enum SupportedLanguage { + #[serde(rename="en-ca")] + #[display(fmt="en-ca")] + English, + #[serde(rename="fr-ca")] + #[display(fmt="fr-ca")] + French, +} +/* +impl std::fmt::Display for SupportedLanguage { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::English => write!(fmt, "en-ca"), + Self::French => write!(fmt, "fr-ca"), + } + } +} +*/ + use views::{GoalDetails, PlayerStats, ShotDetails, TeamStats, IihfStatsI64}; +use rust_i18n::t; +rust_i18n::i18n!("translations"); + use askama::Template; use axum::{ extract::{Path, State}, @@ -29,6 +54,12 @@ struct HelloTemplate<'a> { years: i32, } +#[derive(Template)] +#[template(path = "language_list.html")] +struct LanguageListTemplate { + pub languages: Vec, +} + #[derive(Template)] #[template(path = "partials/box_score_table.html")] struct BoxScoreTemplate { @@ -117,13 +148,14 @@ pub struct ServerState { #[tokio::main] async fn main() { + println!("{}", t!("hello", locale="fr")); + println!("{:?}", available_locales()); let pool = db::connect().await; - let xml_en = translations::en_lang(); - let xml_fr = translations::fr_lang(); let state = ServerState { db_pool: Arc::new(pool), }; let router = Router::new() + .route("/", get(language_list)) .route("/:lang/", get(league_html)) .route("/:lang/shots/", get(shots_all)) .route("/:lang/test/", get(test_template)) @@ -140,6 +172,18 @@ async fn main() { .unwrap(); } +async fn language_list( + State(server_config): State, +) -> impl IntoResponse { + let languages = Language::all(&*server_config.db_pool) + .await + .unwrap(); + let lang_list_tmpl = LanguageListTemplate { + languages + }; + (StatusCode::OK, lang_list_tmpl) +} + async fn player_from_name( State(server_config): State, Path((lang, name)): Path<(SupportedLanguage, String)>, diff --git a/src/model.rs b/src/model.rs index 40444e1..a3c7dba 100644 --- a/src/model.rs +++ b/src/model.rs @@ -14,6 +14,14 @@ macro_rules! impl_table_name { } } +#[derive(FromRow, Serialize, Deserialize, Debug, ormx::Table)] +#[ormx(table = "supported_languages", id = id, insertable, deletable)] +pub struct Language { + pub id: i32, + pub native_name: String, + pub short_name: String, +} + #[derive(FromRow, Serialize, Deserialize, Debug, ormx::Table)] #[ormx(table = "leagues", id = id, insertable, deletable)] pub struct League { diff --git a/src/translations.rs b/src/translations.rs deleted file mode 100644 index ae65c79..0000000 --- a/src/translations.rs +++ /dev/null @@ -1,79 +0,0 @@ -use serde::{Serialize, Deserialize}; -use strum::{ - EnumIter, - IntoEnumIterator, -}; - -#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)] -pub enum SupportedLanguage { - #[serde(rename = "en")] - English, - #[serde(rename = "fr")] - French, -} -impl std::fmt::Display for SupportedLanguage { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let output = match self { - Self::English => "en", - Self::French => "fr", - }; - write!(f, "{}", output) - } -} - -#[derive(Serialize, Deserialize, Debug, PartialEq, EnumIter)] -#[serde(rename_all = "camelCase")] -pub enum TranslatedKey { - UrlGame, - UrlDivision, - UrlLeague, - IbihfLeagues, - Goals, - Assists, - Period, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct TranslatedString { - #[serde(rename = "name")] - pub key: TranslatedKey, - #[serde(rename = "$value")] - pub value: String, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct LanguageStrings { - #[serde(rename = "$value")] - pub kvs: Vec, -} - -/// Verify that all keys are present for translations. -pub fn verify_resources(ls: &LanguageStrings) -> bool { - for key in TranslatedKey::iter() { - let mut is_available = false; - for strs in &ls.kvs { - if strs.key == key { - is_available = true; - } - } - if !is_available { - return false; - } - } - true -} - -macro_rules! add_language { - ($func_name:ident, $file_name:expr) => { - pub fn $func_name() -> LanguageStrings { - let strings = serde_xml_rs::from_str(include_str!($file_name)).unwrap(); - if !verify_resources(&strings) { - panic!("The language XML for {} is not correct.", $file_name); - } - strings - } - } -} - -add_language!(en_lang, "../translations/en.xml"); -add_language!(fr_lang, "../translations/fr.xml");