From cbd91c42aa6df3c6bed5391b9466e24398ff5bd7 Mon Sep 17 00:00:00 2001 From: Observer KRypt0n_ Date: Sat, 25 Feb 2023 14:24:04 +0200 Subject: [PATCH] feat(i18n): added automatic default launcher language selection Improved `i18n` mod to list supported languages and some other stuff As well was added `get_default_lang` function which will return language key based on current system language. This function is called at the first launcher start to update default `launcher.language` config property, so user will immediately have proper language in initial setup window --- src/background.rs | 13 +------- src/i18n.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++-- src/main.rs | 21 ++++++++----- 3 files changed, 88 insertions(+), 23 deletions(-) diff --git a/src/background.rs b/src/background.rs index c58a9d3..4a03c5e 100644 --- a/src/background.rs +++ b/src/background.rs @@ -8,20 +8,9 @@ pub struct Background { } pub fn get_uri() -> String { - let mut lang; - - unsafe { - lang = crate::i18n::LANG.language.as_str().to_string(); - - if let Some(region) = crate::i18n::LANG.region { - lang += "-"; - lang += ®ion.as_str().to_ascii_lowercase(); - } - } - let uri = concat!("https://sdk-os-static.", "ho", "yo", "verse", ".com/hk4e_global/mdk/launcher/api/content?filter_adv=true&key=gcStgarh&launcher_id=10&language="); - uri.to_owned() + &lang + uri.to_owned() + crate::i18n::get_lang() } #[cached::proc_macro::cached(result)] diff --git a/src/i18n.rs b/src/i18n.rs index a245132..bc89bd4 100644 --- a/src/i18n.rs +++ b/src/i18n.rs @@ -8,17 +8,88 @@ fluent_templates::static_loader! { }; } -pub static mut LANG: LanguageIdentifier = langid!("en"); +/// Map of supported languages +pub const SUPPORTED_LANGUAGES: &[(&str, LanguageIdentifier)] = &[ + ("en-us", langid!("en")), + ("ru-ru", langid!("ru")) +]; +static mut LANG: LanguageIdentifier = langid!("en"); + +/// Set launcher language +pub fn set_lang>(lang: T) -> anyhow::Result<()> { + for (key, id) in SUPPORTED_LANGUAGES { + if key == &lang.as_ref() { + unsafe { + LANG = id.to_owned() + } + + return Ok(()); + } + } + + anyhow::bail!("Language {} is not supported", lang.as_ref()) +} + +/// Get launcher language +pub fn get_lang() -> &'static str { + for (key, lang) in SUPPORTED_LANGUAGES { + if lang == unsafe { &LANG } { + return key; + } + } + + unreachable!() +} + +/// Get system language or default language if system one is not supported +/// +/// Checks env variables in following order: +/// - `LC_ALL` +/// - `LC_MESSAGES` +/// - `LANG` +pub fn get_default_lang() -> String { + let lang = std::env::var("LC_ALL") + .unwrap_or_else(|_| std::env::var("LC_MESSAGES") + .unwrap_or_else(|_| std::env::var("LANG") + .unwrap_or_else(|_| String::from("en_US.UTF-8")))); + + let lang = lang.split('.') + .next() + .unwrap_or("en_US") + .replace('_', "-") + .to_ascii_lowercase(); + + for (key, _) in SUPPORTED_LANGUAGES { + if key == &lang { + return lang; + } + } + + String::from("en-us") +} + +/// Get translated message by key +/// +/// ```no_run +/// println!("Translated message: {}", tr("launch")); +/// ``` #[allow(clippy::expect_fun_call)] pub fn tr(id: &str) -> String { unsafe { LOCALES .lookup(&LANG, id) - .expect(&format!("Failed to find message with a given id: {id}")) + .expect(&format!("Failed to find message with given id: {id}")) } } +/// Get translated message by key with filled arguments +/// +/// ```no_run +/// println!("Translated message: {}", tr_args("game-outdated", [ +/// ("latest", "3.3.0".into()) +/// ])); +/// ``` #[allow(clippy::expect_fun_call)] pub fn tr_args(id: &str, args: I) -> String where @@ -28,6 +99,6 @@ where unsafe { LOCALES .lookup_with_args(&LANG, id, &std::collections::HashMap::from_iter(args.into_iter())) - .expect(&format!("Failed to find message with a given id: {id}")) + .expect(&format!("Failed to find message with given id: {id}")) } } diff --git a/src/main.rs b/src/main.rs index 337a513..25f689c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,6 +65,13 @@ fn main() { // This one is kinda critical buy well, I can't do something with it std::fs::write(FIRST_RUN_FILE.as_path(), "").expect("Failed to create .first-run file"); + + // Set initial launcher language based on system language + let mut config = config::get().expect("Failed to get config"); + + config.launcher.language = i18n::get_default_lang(); + + config::update_raw(config).expect("Failed to update config"); } // Force debug output @@ -130,7 +137,12 @@ fn main() { background-size: cover; }} ", BACKGROUND_FILE.to_string_lossy())); - + + // Set UI language + i18n::set_lang(config::get().unwrap().launcher.language).expect("Failed to set launcher language"); + + tracing::info!("Set UI language to {}", i18n::get_lang()); + // Run FirstRun window if .first-run file persist if FIRST_RUN_FILE.exists() { app.run::(()); @@ -138,13 +150,6 @@ fn main() { // Run the app if everything's ready else { - // Set UI language - unsafe { - i18n::LANG = config::get().unwrap().launcher.language.parse().unwrap(); - - tracing::info!("Set UI language to {}", i18n::LANG); - } - app.run::(()); } }