From a2842333c01b290500ee80230e54ec9911f36c78 Mon Sep 17 00:00:00 2001 From: Tait Hoyem Date: Mon, 17 Apr 2023 13:30:29 -0600 Subject: [PATCH] Add language switching links to bottom of page --- Cargo.lock | 7 ++++ Cargo.toml | 1 + src/languages.rs | 68 ++++++++++++++++++++++++++++++++++++- src/main.rs | 49 ++++++++++++++++++++++---- templates/master.html | 6 ++-- translations/en-CA/urls.ftl | 4 +++ 6 files changed, 125 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1fdefd2..118f464 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -832,6 +832,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "hashmap_macro" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a181d5ff5b742b5a830beb8c172562e6f22dccba6e1308bbacaba9fda84c8d" + [[package]] name = "heck" version = "0.4.1" @@ -984,6 +990,7 @@ dependencies = [ "chrono", "const_format", "derive_more", + "hashmap_macro", "once_cell", "ormx", "rust-i18n", diff --git a/Cargo.toml b/Cargo.toml index 94afcf7..a636f4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ once_cell = "1.17.1" derive_more = "0.99.17" const_format = "0.2.30" strum_macros = "0.24.3" +hashmap_macro = "0.1.0" [dev-dependencies] tokio-test = "0.4.2" diff --git a/src/languages.rs b/src/languages.rs index c5c97a4..7d92972 100644 --- a/src/languages.rs +++ b/src/languages.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use crate::LOCALES; use askama::i18n::{langid, LanguageIdentifier, Locale}; use askama::i18n::fluent_templates::Loader; @@ -5,6 +6,9 @@ use serde::{ Serialize, Deserialize, }; +use strum::{ + IntoEnumIterator, +}; use strum_macros::{ EnumIter, AsRefStr, @@ -14,7 +18,7 @@ use strum_macros::{ use derive_more::Display; -#[derive(Serialize, Deserialize, Clone, Copy, Debug, Display, EnumIter, EnumCount, EnumVariantNames, AsRefStr)] +#[derive(Serialize, Deserialize, Clone, Copy, Debug, Display, EnumIter, EnumCount, EnumVariantNames, AsRefStr, PartialEq, Eq)] pub enum SupportedLanguage { #[serde(rename="en-ca")] #[display(fmt="en-ca")] @@ -40,4 +44,66 @@ impl SupportedLanguage { pub fn lookup(&self, key: &str) -> String { LOCALES.lookup(&(*self).into(), key).expect("Unable to find key {key} in locale {self}.") } + pub fn other_langs(&self) -> impl Iterator + '_ { + Self::iter() + .filter(move |lang| lang != self) + } + pub fn native_name(&self) -> String { + match self { + Self::English => "English", + Self::French => "Français" + }.to_string() + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct LangLink { + pub href: String, + pub name: String, +} + +macro_rules! lang_url_match { + ($lang:expr, $link_name:expr) => { + Into::::into($lang) + .translate( + $link_name, + hashmap_macro::hashmap! { + "lang" => $lang.to_string().into() + }, + ) + .expect("Unable to find key {key} in locale {self}.") + }; + ($lang:expr, $link_name:expr, $id:expr) => { + Into::::into($lang) + .translate( + $link_name, + hashmap_macro::hashmap! { + "lang" => $lang.to_string().into(), + "id" => $id.into() + }, + ) + .expect("Unable to find key {key} in locale {self}.") + }; +} + +// TODO: Genericize this so it can accept any arugments through a impl Iterator<(K, V>) or something similar. +impl LangLink { + pub fn from_lang(lang: SupportedLanguage, link_name: &str) -> Self { + Self { + name: lang.native_name(), + href: lang_url_match!(lang, link_name), + } + } + pub fn from_lang_and_id(lang: SupportedLanguage, id: i32, link_name: &str) -> Self { + Self { + name: lang.native_name(), + href: lang_url_match!(lang, link_name, id), + } + } + pub fn from_lang_and_name(lang: SupportedLanguage, name: &str, link_name: &str) -> Self { + Self { + name: lang.native_name(), + href: lang_url_match!(lang, link_name, name), + } + } } diff --git a/src/main.rs b/src/main.rs index c6eb4a2..ad686e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,10 @@ askama::i18n::load!(LOCALES); use crate::model::{Division, Game, GamePlayer, League, Player, Shot, Team, Language}; use views::{GoalDetails, PlayerStats, ShotDetails, TeamStats, IihfStatsI64}; -use languages::SupportedLanguage; +use languages::{ + SupportedLanguage, + LangLink, +}; use askama::Template; use axum::{ @@ -39,6 +42,9 @@ struct HelloTemplate<'a> { struct LanguageListTemplate<'a> { #[locale] pub loc: Locale<'a>, + pub url_name: &'a str, + pub lang_links: Vec, + pub lang: SupportedLanguage, pub languages: Vec, } @@ -47,8 +53,8 @@ struct LanguageListTemplate<'a> { struct BoxScoreTemplate<'a> { #[locale] locale: Locale<'a>, - goals: Vec, lang: SupportedLanguage, + goals: Vec, } #[derive(Template)] @@ -56,8 +62,8 @@ struct BoxScoreTemplate<'a> { struct IndividualGamePointsTableTemplate<'a> { #[locale] locale: Locale<'a>, - players: Vec, lang: SupportedLanguage, + players: Vec, } #[derive(Template)] @@ -71,7 +77,11 @@ struct TeamGameStatsTemplate<'a> { #[derive(Template)] #[template(path = "division_list.html")] -struct DivisionListTemplate { +struct DivisionListTemplate<'a> { + #[locale] + locale: Locale<'a>, + url_name: &'a str, + lang_links: Vec, league: League, divisions: Vec, lang: SupportedLanguage, @@ -82,9 +92,11 @@ struct DivisionListTemplate { struct LeagueListTemplate<'a> { #[locale] locale: Locale<'a>, + url_name: &'a str, + lang_links: Vec, + lang: SupportedLanguage, leagues: Vec, heading: String, - lang: SupportedLanguage, } #[derive(Template)] @@ -101,6 +113,8 @@ struct IihfTeamStatsTableTemplate<'a> { struct GameListTemplate<'a> { #[locale] locale: Locale<'a>, + url_name: &'a str, + lang_links: Vec, division: Division, iihf_team_stats_table: IihfTeamStatsTableTemplate<'a>, games: Vec, @@ -121,6 +135,8 @@ struct ShotsTableTemplate<'a> { struct GameScorePageTemplate<'a> { #[locale] locale: Locale<'a>, + url_name: &'a str, + lang_links: Vec, game: Game, division: Division, box_score: BoxScoreTemplate<'a>, @@ -132,7 +148,11 @@ struct GameScorePageTemplate<'a> { #[derive(Template)] #[template(path = "player_page.html")] -pub struct PlayerPageTemplate { +pub struct PlayerPageTemplate<'a> { + #[locale] + locale: Locale<'a>, + lang_links: Vec, + url_name: &'a str, player: Player, league: League, league_stats: PlayerStats, @@ -178,6 +198,9 @@ async fn language_list( .unwrap(); let lang_list_tmpl = LanguageListTemplate { loc: Locale::new(langid!("en-ca"), &LOCALES), + url_name: "root_url_tmpl", + lang_links: SupportedLanguage::English.other_langs().map(move |olang| LangLink::from_lang(olang, "root_url_tmpl")).collect(), + lang: SupportedLanguage::English, languages }; (StatusCode::OK, lang_list_tmpl) @@ -187,7 +210,7 @@ async fn player_from_name( State(server_config): State, Path((lang, name)): Path<(SupportedLanguage, String)>, ) -> impl IntoResponse { - let player = Player::from_name_case_insensitive(&server_config.db_pool, name) + let player = Player::from_name_case_insensitive(&server_config.db_pool, name.clone()) .await .unwrap(); let latest_league = Player::latest_league(&server_config.db_pool, player.id) @@ -203,7 +226,10 @@ async fn player_from_name( .unwrap(); let html = PlayerPageTemplate { player, + lang_links: lang.other_langs().map(move |olang| LangLink::from_lang_and_name(olang, &name, "player_url_tmpl")).collect(), lang, + locale: lang.into(), + url_name: "player_url_tmpl", league: latest_league, league_stats: latest_league_stats, lifetime_stats, @@ -253,6 +279,8 @@ async fn league_html( } .to_string(); let leagues_template = LeagueListTemplate { + lang_links: lang.other_langs().map(move |olang| LangLink::from_lang(olang, "root_url_tmpl")).collect(), + url_name: "root_url_tmpl", locale: lang.into(), leagues, heading, @@ -272,6 +300,9 @@ async fn divisions_for_league_html( .await .unwrap(); let html = DivisionListTemplate { + locale: lang.into(), + lang_links: lang.other_langs().map(move |olang| LangLink::from_lang_and_id(olang, league_id, "league_url_tmpl")).collect(), + url_name: "league_url_tmpl", league, divisions, lang, @@ -294,6 +325,8 @@ async fn games_for_division_html( .unwrap(); let games_template = GameListTemplate { locale: lang.into(), + lang_links: lang.other_langs().map(move |olang| LangLink::from_lang_and_id(olang, division_id, "division_url_tmpl")).collect(), + url_name: "division_url_tmpl", division, iihf_team_stats_table: IihfTeamStatsTableTemplate { locale: lang.into(), @@ -339,6 +372,8 @@ async fn score_for_game_html( let pbp_html = ShotsTableTemplate { locale: lang.into(), shots: pbp, lang }; let game_template = GameScorePageTemplate { locale: lang.into(), + lang_links: lang.other_langs().map(move |olang| LangLink::from_lang_and_id(olang, game_id, "game_url_tmpl")).collect(), + url_name: "game_url_tmpl", division, game, lang, diff --git a/templates/master.html b/templates/master.html index 3b6e244..83f1341 100644 --- a/templates/master.html +++ b/templates/master.html @@ -1,6 +1,5 @@ - - + {% block title %}{% endblock %} | IBIHF Stats @@ -15,6 +14,9 @@ View Code
Version: {{ VERSION }} + {% for ll in lang_links %} + {{ ll.name }} + {% endfor %} diff --git a/translations/en-CA/urls.ftl b/translations/en-CA/urls.ftl index 85cfc43..bfcfa51 100644 --- a/translations/en-CA/urls.ftl +++ b/translations/en-CA/urls.ftl @@ -1,3 +1,7 @@ +root_url = /:lang/ +root_url_tmpl = /{ $lang }/ +player_url = /:lang/player/:name/ +player_url_tmpl = /{ $lang }/player/{ $name }/ game_url = /:lang/game/:id/ game_url_tmpl = /{ $lang }/game/{ $id }/ league_url = /:lang/league/:id/