From 64eae845f7bbaaeed0a53f80ff33c798f574883a Mon Sep 17 00:00:00 2001 From: Matthew Edell Date: Sat, 2 Nov 2024 04:53:58 +0000 Subject: [PATCH 1/7] Basic translation test, options & intial render needs fix --- Cargo.toml | 6 ++ locales/en_us.json | 106 +++++++++++++++++++++++++++++ locales/fr.json | 99 +++++++++++++++++++++++++++ src/common/localization.rs | 66 ++++++++++++++++++ src/common/mod.rs | 1 + src/lib.rs | 20 +++++- src/training/ui/menu.rs | 10 +-- training_mod_consts/src/lib.rs | 2 + training_mod_consts/src/options.rs | 23 +++++++ 9 files changed, 326 insertions(+), 7 deletions(-) create mode 100644 locales/en_us.json create mode 100644 locales/fr.json create mode 100644 src/common/localization.rs diff --git a/Cargo.toml b/Cargo.toml index 2a7ec478d..6ea0bad0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ log = "0.4.17" byte-unit = "4.0.18" zip = { version = "0.6", default-features = false, features = ["deflate"] } anyhow = "1.0.72" +rust-i18n = "3.1.2" [patch.crates-io] native-tls = { git = "https://github.com/skyline-rs/rust-native-tls", branch = "switch-timeout-panic" } @@ -64,6 +65,11 @@ plugin-dependencies = [ { name = "libparam_hook.nro", url = "https://github.com/ultimate-research/params-hook-plugin/releases/download/v0.1.1/libparam_hook.nro" }, ] +[package.metadata.i18n] +load-path = "locales" +default-locale = "en_us" +available-locales = ["en_us", "fr"] + [features] outside_training_mode = [] layout_arc_from_file = [] diff --git a/locales/en_us.json b/locales/en_us.json new file mode 100644 index 000000000..b168cf52a --- /dev/null +++ b/locales/en_us.json @@ -0,0 +1,106 @@ +{ + "common": { + "Welcome!": "welcome!", + "plugin_title": "Training Modpack", + "hold_button": "Hold %{button}", + "open_menu": "Open Menu", + "language": "Language", + "modpack_menu": "Modpack Menu", + "yes": "yes", + "no": "no", + "on": "on", + "off": "off" + }, + "buttons": { + "start": "Start", + "a": "a", + "b": "b", + "x": "x", + "y": "y", + "l": "l", + "r": "r", + "z": "z", + "dpad_Down": "dpad down", + "dpad_Left": "dpad left", + "dpad_Right": "dpad right", + "dpad_Up": "dpad up", + "pro": { + "l": "pro l", + "r": "pro r; gcc z", + "zl_gcc_l": "pro zl; gcc l", + "zr_gcc_r": "pro zr; gcc r" + }, + "analog_stick": { + "up": "up", + "down": "down", + "left": "left", + "right": "right" + } + }, + "menus": { + "mash_settings": { + "mash_toggles": { + "title": "Mash Toggles", + "description": "Actions to be performed as soon as possible" + }, + "followup_toggles": { + "title": "Followup Toggles", + "description": "Actions to be performed after a mash option" + }, + "mash_triggers": { + "title": "Mash Triggers", + "description": "Configure what causes the cpu to perform a mash option" + }, + "attack_angle": { + "title": "Attack Angle", + "description": "For attacks that can be angled, such as some forward tilts" + }, + "throw_options": { + "title": "Throw Options", + "description": "Throw to be performed when a grab is landed" + }, + "throw_delay": { + "title": "Throw Delay", + "description": "How many frames to delay the throw option" + }, + "pummel_delay": { + "title": "Pummel Delay", + "description": "How many frames after a grab to wait before starting to pummel" + }, + "falling_aerials": { + "title": "Falling Aerials", + "description": "Should aerials be performed when rising or when falling" + }, + "full_hop": { + "title": "Full Hop", + "description": "Should the cpu perform a full hop or a short hop" + }, + "aerial_delay": { + "title": "Aerial Delay", + "description": "How long to delay a mash aerial attack" + }, + "fast_fall": { + "title": "Fast Fall", + "description": "Should the cpu fastfall during a jump" + }, + "fast_fall_delay": { + "title": "Fast Fall Delay", + "description": "How many frames the cpu should delay their fastfall" + }, + "oos_offset": { + "title": "OoS Offset", + "description": "How many times the cpu shield can be hit before performing a mash option" + }, + "reaction_time": { + "title": "Reaction Time", + "description": "How many frames to delay before performing a mash option" + } + }, + "misc_settings": { + "language": { + "title": "Language", + "description": "How many frames to delay before performing a mash option" + } + } + } +} \ No newline at end of file diff --git a/locales/fr.json b/locales/fr.json new file mode 100644 index 000000000..de5f94c1d --- /dev/null +++ b/locales/fr.json @@ -0,0 +1,99 @@ +{ + "common": { + "plugin_title": "Modpack de Formation", + "hold_button": "Maintenir %{button}", + "open_menu": "Ouvrir le Menu", + "language": "Langue", + "modpack_menu": "asdfsdfasfdad", + "yes": "oui", + "no": "non", + "on": "activé", + "off": "désactivé" + }, + "buttons": { + "start": "commencer", + "a": "a", + "b": "b", + "x": "x", + "y": "y", + "l": "l", + "r": "r", + "z": "z", + "dpad_Up": "dpad haut", + "dpad_Down": "dpad descendante", + "dpad_Left": "dpad gauche", + "dpad_Right": "dpad droit", + "pro": { + "l": "pro l", + "r": "pro r; gcc z", + "zl_gcc_l": "pro zl; gcc l", + "zr_gcc_r": "pro zr; gcc r" + }, + "analog_stick": { + "up": "haut", + "down": "descendante", + "left": "gauche", + "right": "droit" + } + }, + "menus": { + "mash_settings": { + "mash_toggles": { + "title": "Ecraser Toggles", + "description": "Actions à mener dans les plus brefs délais" + }, + "followup_toggles": { + "title": "Déclencheurs de Suivi", + "description": "Actions à effectuer après une option d'empâtage" + }, + "mash_triggers": { + "title": "Mash Triggers", + "description": "Configure what causes the cpu to perform a mash option" + }, + "attack_angle": { + "title": "Attack Angle", + "description": "For attacks that can be angled, such as some forward tilts" + }, + "throw_options": { + "title": "Throw Options", + "description": "Throw to be performed when a grab is landed" + }, + "throw_delay": { + "title": "Throw Delay", + "description": "How many frames to delay the throw option" + }, + "pummel_delay": { + "title": "Pummel Delay", + "description": "How many frames after a grab to wait before starting to pummel" + }, + "falling_aerials": { + "title": "Falling Aerials", + "description": "Should aerials be performed when rising or when falling" + }, + "full_hop": { + "title": "Full Hop", + "description": "Should the cpu perform a full hop or a short hop" + }, + "aerial_delay": { + "title": "Aerial Delay", + "description": "How long to delay a mash aerial attack" + }, + "fast_fall": { + "title": "Fast Fall", + "description": "Should the cpu fastfall during a jump" + }, + "fast_fall_delay": { + "title": "Fast Fall Delay", + "description": "How many frames the cpu should delay their fastfall" + }, + "oos_offset": { + "title": "OoS Offset", + "description": "How many times the cpu shield can be hit before performing a mash option" + }, + "reaction_time": { + "title": "Reaction Time", + "description": "How many frames to delay before performing a mash option" + } + } + } +} \ No newline at end of file diff --git a/src/common/localization.rs b/src/common/localization.rs new file mode 100644 index 000000000..65b3ca180 --- /dev/null +++ b/src/common/localization.rs @@ -0,0 +1,66 @@ +use crate::common::MENU; +use crate::logging::*; +use crate::training::ui::notifications::notification; +use training_mod_consts::Locale; + +#[repr(u8)] +#[derive(Debug)] +pub enum ModLanguageId { + English, + French, +} + +impl From for ModLanguageId { + fn from(byte: u8) -> Self { + match byte { + 0 => Self::English, + 1 => Self::French, + _ => Self::English, + } + } +} + +impl From<&str> for ModLanguageId { + fn from(locale_code: &str) -> Self { + match locale_code { + "en_us" => Self::English, + "fr" => Self::French, + _ => Self::English, + } + } +} + +impl From for ModLanguageId { + fn from(locale_code: Locale) -> Self { + match locale_code { + Locale::ENGLISH_US => Self::English, + Locale::FRENCH => Self::French, + _ => Self::English, + } + } +} + +impl ModLanguageId { + pub fn get_locale_code(&self) -> &str { + match self { + ModLanguageId::English => "en_us", + ModLanguageId::French => "fr", + } + } +} + +pub unsafe fn set_language_from_menu() { + let locale = ModLanguageId::from(MENU.selected_locale); + info!("Setting language to {:?}", locale); + + let locale_code = locale.get_locale_code(); + if rust_i18n::available_locales!().contains(&locale_code) { + info!("Setting language to {:?}", locale_code); + rust_i18n::set_locale(locale_code); + + notification("Language".to_string(), locale_code.to_string(), 8); + } else { + info!("{} not found using en_us instead.", locale_code); + rust_i18n::set_locale("en_us"); + } +} diff --git a/src/common/mod.rs b/src/common/mod.rs index cfbb5320c..baef6bd6a 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -15,6 +15,7 @@ pub mod dev_config; pub mod dialog; pub mod events; pub mod input; +pub mod localization; pub mod menu; pub mod offsets; pub mod raygun_printer; diff --git a/src/lib.rs b/src/lib.rs index 0c1728c58..35848bada 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,10 @@ clippy::missing_transmute_annotations )] +#[macro_use] +extern crate rust_i18n; +i18n!(fallback = "fr"); + use std::fs; use std::path::PathBuf; @@ -25,6 +29,7 @@ use training_mod_sync::*; use crate::common::button_config::DEFAULT_OPEN_MENU_CONFIG; use crate::common::events::events_loop; +use crate::common::localization::set_language_from_menu; use crate::common::*; use crate::consts::TRAINING_MODPACK_ROOT; use crate::events::{Event, EVENT_QUEUE}; @@ -126,16 +131,25 @@ pub fn main() { info!("Skipping version check because we are using an emulator"); } - notification("Training Modpack".to_string(), "Welcome!".to_string(), 60); + info!("Setting mod language"); + set_language_from_menu(); + notification( - "Open Menu".to_string(), + t!("common.plugin_title").to_string(), + "Welcome!".to_string(), + 60, + ); + + notification( + t!("common.open_menu").to_string(), if read(&MENU).menu_open_start_press == OnOff::ON { - "Hold Start".to_string() + t!("common.hold_button", button = t!("buttons.start")).to_string() } else { DEFAULT_OPEN_MENU_CONFIG.to_string() }, 120, ); + notification( "Save State".to_string(), read(&MENU).save_state_save.to_string(), diff --git a/src/training/ui/menu.rs b/src/training/ui/menu.rs index d8ce8f0cb..431787d2c 100644 --- a/src/training/ui/menu.rs +++ b/src/training/ui/menu.rs @@ -109,7 +109,7 @@ unsafe fn render_submenu_page(app: &mut App, root_pane: &Pane) { && col == tab.submenus.state.selected_col().unwrap(); // Set Pane Visibility - title_text.set_text_string(submenu.title); + title_text.set_text_string(&t!(submenu.title)); // In the actual 'layout.arc' file, every icon image is stacked // into a single container pane, with each image directly on top of another. @@ -146,7 +146,7 @@ unsafe fn render_submenu_page(app: &mut App, root_pane: &Pane) { .find_pane_by_name_recursive("FooterTxt") .unwrap() .as_textbox() - .set_text_string(submenu.help_text); + .set_text_string(&t!(submenu.help_text)); title_bg_material.set_white_res_color(BG_LEFT_ON_WHITE_COLOR); title_bg_material.set_black_res_color(BG_LEFT_ON_BLACK_COLOR); @@ -223,7 +223,7 @@ unsafe fn render_toggle_page(app: &mut App, root_pane: &Pane) { } } - title_text.set_text_string(toggle.title); + title_text.set_text_string(&t!(toggle.title)); if use_check_icon { menu_button @@ -491,7 +491,9 @@ pub unsafe fn draw(root_pane: &Pane) { if let Some(quit_button) = root_pane.find_pane_by_name_recursive("TrModTitle") { for quit_txt_s in &["set_txt_00", "set_txt_01"] { if let Some(quit_txt) = quit_button.find_pane_by_name_recursive(quit_txt_s) { - quit_txt.as_textbox().set_text_string("Modpack Menu"); + quit_txt + .as_textbox() + .set_text_string(&t!("common.modpack_menu")); } } } diff --git a/training_mod_consts/src/lib.rs b/training_mod_consts/src/lib.rs index 8242abed5..d468b9a74 100644 --- a/training_mod_consts/src/lib.rs +++ b/training_mod_consts/src/lib.rs @@ -95,6 +95,7 @@ pub struct TrainingModpackMenu { pub tech_hide: OnOff, pub update_policy: UpdatePolicy, pub lra_reset: OnOff, + pub selected_locale: Locale, } #[repr(C)] @@ -203,6 +204,7 @@ pub static BASE_MENU: TrainingModpackMenu = TrainingModpackMenu { tech_hide: OnOff::OFF, update_policy: UpdatePolicy::default(), lra_reset: OnOff::ON, + selected_locale: Locale::default(), }; pub static DEFAULTS_MENU: RwLock = RwLock::new(BASE_MENU); diff --git a/training_mod_consts/src/options.rs b/training_mod_consts/src/options.rs index 0df898efb..2c21491d7 100644 --- a/training_mod_consts/src/options.rs +++ b/training_mod_consts/src/options.rs @@ -1141,3 +1141,26 @@ byteflags! { } impl_submenutrait!(InputDisplay); + +byteflags! { + pub struct Locale { + pub ENGLISH_US = "en_us", + pub FRENCH = "fr", + } +} + +impl Locale { + pub const fn default() -> Locale { + Locale::ENGLISH_US + } +} + +impl From for Locale { + fn from(id: u8) -> Locale { + match id { + 0 => Locale::ENGLISH_US, + 1 => Locale::FRENCH, + _ => Locale::ENGLISH_US, + } + } +} From e8d2de7a26e1ef70ef8c1fc73e4eabe6928f353b Mon Sep 17 00:00:00 2001 From: Muhammad Kassar <36678834+mk360@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:02:04 +0100 Subject: [PATCH 2/7] initial french translation --- locales/fr.json | 88 ++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index de5f94c1d..6a2428217 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1,28 +1,28 @@ { "common": { - "plugin_title": "Modpack de Formation", + "plugin_title": "Modpack d'Entraînement'", "hold_button": "Maintenir %{button}", "open_menu": "Ouvrir le Menu", "language": "Langue", - "modpack_menu": "asdfsdfasfdad", - "yes": "oui", - "no": "non", - "on": "activé", - "off": "désactivé" + "modpack_menu": "Menu du Modpack", + "yes": "Oui", + "no": "Non", + "on": "Activé", + "off": "Désactivé" }, "buttons": { - "start": "commencer", - "a": "a", - "b": "b", - "x": "x", - "y": "y", - "l": "l", - "r": "r", + "start": "Commencer", + "a": "A", + "b": "B", + "x": "X", + "y": "Y", + "l": "L", + "r": "R", "z": "z", "dpad_Up": "dpad haut", - "dpad_Down": "dpad descendante", + "dpad_Down": "dpad bas", "dpad_Left": "dpad gauche", - "dpad_Right": "dpad droit", + "dpad_Right": "dpad droite", "pro": { "l": "pro l", "r": "pro r; gcc z", @@ -31,68 +31,68 @@ }, "analog_stick": { "up": "haut", - "down": "descendante", + "down": "bas", "left": "gauche", - "right": "droit" + "right": "droite" } }, "menus": { "mash_settings": { "mash_toggles": { - "title": "Ecraser Toggles", - "description": "Actions à mener dans les plus brefs délais" + "title": "Options de Mashing", + "description": "Mashing à effectuer dès que possible" }, "followup_toggles": { - "title": "Déclencheurs de Suivi", - "description": "Actions à effectuer après une option d'empâtage" + "title": "Déclencheurs de Followups", + "description": "Followups à effectuer dès que possible" }, "mash_triggers": { - "title": "Mash Triggers", - "description": "Configure what causes the cpu to perform a mash option" + "title": "Déclencheurs de Mashing", + "description": "Configurer les actions qui feront que le CPU mashera." }, "attack_angle": { - "title": "Attack Angle", - "description": "For attacks that can be angled, such as some forward tilts" + "title": "Angle d'Attaque", + "description": "Modifie l'angle des attaques qui peuvent être orientées, par ex. le F-Tilt de Falco." }, "throw_options": { - "title": "Throw Options", - "description": "Throw to be performed when a grab is landed" + "title": "Options de Grabs", + "description": "Définit dans quelle direction le CPU doit faire un grab." }, "throw_delay": { - "title": "Throw Delay", - "description": "How many frames to delay the throw option" + "title": "Délai de Grab", + "description": "De combien de frames retarder le grab." }, "pummel_delay": { - "title": "Pummel Delay", - "description": "How many frames after a grab to wait before starting to pummel" + "title": "Délai de Pummel", + "description": "Combien de frames attendre avant de pummel pendant un grab." }, "falling_aerials": { - "title": "Falling Aerials", - "description": "Should aerials be performed when rising or when falling" + "title": "Aerials descendants", + "description": "Définit si les Aerials doivent se faire pendant la montée ou la descente d'un saut." }, "full_hop": { - "title": "Full Hop", - "description": "Should the cpu perform a full hop or a short hop" + "title": "Saut", + "description": "Définit si le CPU doit faire un Saut ou un Demi-Saut" }, "aerial_delay": { - "title": "Aerial Delay", - "description": "How long to delay a mash aerial attack" + "title": "Délai d'Aerial", + "description": "De combien de frames retarder l'Aerial." }, "fast_fall": { "title": "Fast Fall", - "description": "Should the cpu fastfall during a jump" + "description": "Définit si le CPU doit Fast Fall pendant son saut." }, "fast_fall_delay": { - "title": "Fast Fall Delay", - "description": "How many frames the cpu should delay their fastfall" + "title": "Délai de Fast Fall", + "description": "De combien de frames retarder le Fast Fall." }, "oos_offset": { - "title": "OoS Offset", - "description": "How many times the cpu shield can be hit before performing a mash option" + "title": "Limite d'Out of Shield", + "description": "Combien de fois le shield doit être touché avant que l'option Out of Shield se lance." }, "reaction_time": { - "title": "Reaction Time", - "description": "How many frames to delay before performing a mash option" + "title": "Temps de réaction", + "description": "De combien de frames retarder l'option de mashing." } } } From 3506038f904bc01829d0cd0b98f1a51e0bd2f35b Mon Sep 17 00:00:00 2001 From: Matthew Edell Date: Tue, 26 Nov 2024 01:09:26 +0000 Subject: [PATCH 3/7] Added i18n-ally config, updated some text to use keys, fixed fallback language, updated language display names --- .devcontainer/devcontainer.json | 4 ++-- .vscode/i18n-ally-custom-framework.yml | 7 +++++++ .vscode/settings.json | 3 +++ locales/en_us.json | 9 ++++++++- src/lib.rs | 2 +- training_mod_consts/src/lib.rs | 2 +- training_mod_consts/src/options.rs | 4 ++-- 7 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 .vscode/i18n-ally-custom-framework.yml diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 65f5d85d1..f4b9ef4a1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -12,8 +12,8 @@ "vscode": { "extensions": [ "rust-lang.rust-analyzer", - "bungcip.better-toml", - "serayuzgur.crates", + "tamasfe.even-better-toml", + "fill-labs.dependi", "statiolake.vscode-rustfmt" ] } diff --git a/.vscode/i18n-ally-custom-framework.yml b/.vscode/i18n-ally-custom-framework.yml new file mode 100644 index 000000000..7cca0a3b7 --- /dev/null +++ b/.vscode/i18n-ally-custom-framework.yml @@ -0,0 +1,7 @@ +languageIds: + - rust + +usageMatchRegex: + - "[^\\w\\d]t!\\([\\s\\n\\r]*['\"]({key})['\"]" + +monopoly: true \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6a9e639cc..b1150f58a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,4 +7,7 @@ "--message-format=json", "--all-features", ], + "i18n-ally.localesPaths": [ + "locales" + ], } \ No newline at end of file diff --git a/locales/en_us.json b/locales/en_us.json index b168cf52a..45d6bc4f1 100644 --- a/locales/en_us.json +++ b/locales/en_us.json @@ -6,6 +6,7 @@ "open_menu": "Open Menu", "language": "Language", "modpack_menu": "Modpack Menu", + "welcome": "Welcome!", "yes": "yes", "no": "no", "on": "on", @@ -39,6 +40,7 @@ }, "menus": { "mash_settings": { + "tab_name": "Mash Settings", "mash_toggles": { "title": "Mash Toggles", "description": "Actions to be performed as soon as possible" @@ -97,9 +99,14 @@ } }, "misc_settings": { + "tab_name": "Misc Settings", "language": { "title": "Language", - "description": "How many frames to delay before performing a mash option" + "help_text": "Language: Select your preferred language", + "locales": { + "en_us": "English (US)", + "fr": "French" + } } } } diff --git a/src/lib.rs b/src/lib.rs index 35848bada..dacce47de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ #[macro_use] extern crate rust_i18n; -i18n!(fallback = "fr"); +i18n!(fallback = "en_us"); use std::fs; use std::path::PathBuf; diff --git a/training_mod_consts/src/lib.rs b/training_mod_consts/src/lib.rs index d468b9a74..1bc545f8d 100644 --- a/training_mod_consts/src/lib.rs +++ b/training_mod_consts/src/lib.rs @@ -301,7 +301,7 @@ pub unsafe fn create_app<'a>() -> App<'a> { )); let mash_tab = Tab { id: "mash", - title: "Mash Settings", + title: "menus.mash_settings.title", submenus: StatefulTable::with_items(NX_SUBMENU_ROWS, NX_SUBMENU_COLUMNS, mash_tab_submenus), }; overall_menu.tabs.push(mash_tab); diff --git a/training_mod_consts/src/options.rs b/training_mod_consts/src/options.rs index 2c21491d7..f5ce98895 100644 --- a/training_mod_consts/src/options.rs +++ b/training_mod_consts/src/options.rs @@ -1144,8 +1144,8 @@ impl_submenutrait!(InputDisplay); byteflags! { pub struct Locale { - pub ENGLISH_US = "en_us", - pub FRENCH = "fr", + pub ENGLISH_US = "English (US)", + pub FRENCH = "French", } } From c7f8d7e429dc5dc9f326c290d3cb8fde36183320 Mon Sep 17 00:00:00 2001 From: Matthew Edell Date: Wed, 27 Nov 2024 04:49:46 +0000 Subject: [PATCH 4/7] Use translations for welcome & button notifications, refactored localization module, add localization to per frame loop, use translations for tab titles, updated mash tab options to use translations, added submenutrait impl for Locale --- src/common/localization.rs | 27 +++++++++++-- src/lib.rs | 16 +++----- src/training/mod.rs | 5 ++- src/training/ui/menu.rs | 2 +- training_mod_consts/src/lib.rs | 64 ++++++++++++++++-------------- training_mod_consts/src/options.rs | 2 + 6 files changed, 70 insertions(+), 46 deletions(-) diff --git a/src/common/localization.rs b/src/common/localization.rs index 65b3ca180..118d773aa 100644 --- a/src/common/localization.rs +++ b/src/common/localization.rs @@ -2,6 +2,7 @@ use crate::common::MENU; use crate::logging::*; use crate::training::ui::notifications::notification; use training_mod_consts::Locale; +use training_mod_sync::*; #[repr(u8)] #[derive(Debug)] @@ -49,18 +50,36 @@ impl ModLanguageId { } } -pub unsafe fn set_language_from_menu() { - let locale = ModLanguageId::from(MENU.selected_locale); +pub fn init() { + info!("Initializing localization"); + handle_language_change(); + info!( + "Initialized localization with {:#?}", + ModLanguageId::from(read(&MENU).selected_locale) + ); +} + +pub fn set_language() { + let locale: ModLanguageId = ModLanguageId::from(read(&MENU).selected_locale); info!("Setting language to {:?}", locale); let locale_code = locale.get_locale_code(); + if rust_i18n::available_locales!().contains(&locale_code) { - info!("Setting language to {:?}", locale_code); rust_i18n::set_locale(locale_code); - notification("Language".to_string(), locale_code.to_string(), 8); + notification("Language".to_string(), locale_code.to_string(), 360); } else { info!("{} not found using en_us instead.", locale_code); rust_i18n::set_locale("en_us"); } } + +pub fn handle_language_change() { + let has_locale_changed = + *ModLanguageId::from(read(&MENU).selected_locale).get_locale_code() != *rust_i18n::locale(); + + if has_locale_changed { + set_language(); + } +} diff --git a/src/lib.rs b/src/lib.rs index dacce47de..5ddb55813 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,6 @@ use training_mod_sync::*; use crate::common::button_config::DEFAULT_OPEN_MENU_CONFIG; use crate::common::events::events_loop; -use crate::common::localization::set_language_from_menu; use crate::common::*; use crate::consts::TRAINING_MODPACK_ROOT; use crate::events::{Event, EVENT_QUEUE}; @@ -84,7 +83,11 @@ pub fn main() { let mut event_queue = lock_write(&EVENT_QUEUE); (*event_queue).push(Event::smash_open()); drop(event_queue); - notification("Training Modpack".to_string(), "Welcome!".to_string(), 60); + notification( + t!("common.plugin_title").to_string(), + "Welcome!".to_string(), + 60, + ); hitbox_visualizer::hitbox_visualization(); hazard_manager::hazard_manager(); @@ -131,14 +134,7 @@ pub fn main() { info!("Skipping version check because we are using an emulator"); } - info!("Setting mod language"); - set_language_from_menu(); - - notification( - t!("common.plugin_title").to_string(), - "Welcome!".to_string(), - 60, - ); + localization::init(); notification( t!("common.open_menu").to_string(), diff --git a/src/training/mod.rs b/src/training/mod.rs index e63e8d325..b29d5dcfe 100644 --- a/src/training/mod.rs +++ b/src/training/mod.rs @@ -4,8 +4,8 @@ use crate::common::button_config; use crate::common::consts::{BuffOption, FighterId, MENU}; use crate::common::offsets::*; use crate::common::{ - dev_config, get_module_accessor, is_operation_cpu, is_training_mode, menu, PauseMenu, - FIGHTER_MANAGER_ADDR, ITEM_MANAGER_ADDR, STAGE_MANAGER_ADDR, TRAINING_MENU_ADDR, + dev_config, get_module_accessor, is_operation_cpu, is_training_mode, localization, menu, + PauseMenu, FIGHTER_MANAGER_ADDR, ITEM_MANAGER_ADDR, STAGE_MANAGER_ADDR, TRAINING_MENU_ADDR, }; use crate::hitbox_visualizer; use crate::input::*; @@ -154,6 +154,7 @@ fn once_per_frame_per_fighter(module_accessor: &mut BattleObjectModuleAccessor, shield::get_command_flag_cat(module_accessor); directional_influence::get_command_flag_cat(module_accessor); reset::check_reset(module_accessor); + localization::handle_language_change(); } /** diff --git a/src/training/ui/menu.rs b/src/training/ui/menu.rs index 431787d2c..0a32fb49e 100644 --- a/src/training/ui/menu.rs +++ b/src/training/ui/menu.rs @@ -602,7 +602,7 @@ pub unsafe fn draw(root_pane: &Pane) { help_pane.set_default_material_colors(); help_pane.set_color(255, 255, 0, 255); } - help_pane.set_text_string(tab_titles[idx]); + help_pane.set_text_string(&t!(tab_titles[idx])); }); // Save Defaults Keyhelp diff --git a/training_mod_consts/src/lib.rs b/training_mod_consts/src/lib.rs index 1bc545f8d..94d0276fc 100644 --- a/training_mod_consts/src/lib.rs +++ b/training_mod_consts/src/lib.rs @@ -216,87 +216,87 @@ pub unsafe fn create_app<'a>() -> App<'a> { // Mash Tab let mut mash_tab_submenus: Vec = Vec::new(); mash_tab_submenus.push(Action::to_submenu( - "Mash Toggles", + "menus.mash_settings.mash_toggles.title", "mash_state", - "Action to be performed as soon as possible", + "menus.mash_settings.mash_toggles.description", ToggleMultiple, )); mash_tab_submenus.push(Action::to_submenu( - "Followup Toggles", + "menus.mash_settings.follow_up.title", "follow_up", - "Actions to be performed after a Mash Option", + "menus.mash_settings.follow_up.description", ToggleMultiple, )); mash_tab_submenus.push(MashTrigger::to_submenu( - "Mash Triggers", + "menus.mash_settings.mash_triggers.title", "mash_triggers", - "Configure what causes the CPU to perform a Mash Option", + "menus.mash_settings.mash_triggers.description", ToggleSingle, )); mash_tab_submenus.push(AttackAngle::to_submenu( - "Attack Angle", + "menus.mash_settings.attack_angle.title", "attack_angle", - "For attacks that can be angled, such as some forward tilts", + "menus.mash_settings.attack_angle.description", ToggleMultiple, )); mash_tab_submenus.push(ThrowOption::to_submenu( - "Throw Options", + "menus.mash_settings.throw_options.title", "throw_state", - "Throw to be performed when a grab is landed", + "menus.mash_settings.throw_options.description", ToggleMultiple, )); mash_tab_submenus.push(MedDelay::to_submenu( - "Throw Delay", + "menus.mash_settings.throw_delay.title", "throw_delay", - "How many frames to delay the throw option", + "menus.mash_settings.throw_delay.description", ToggleMultiple, )); mash_tab_submenus.push(MedDelay::to_submenu( - "Pummel Delay", + "menus.mash_settings.pummel_delay.title", "pummel_delay", - "How many frames after a grab to wait before starting to pummel", + "menus.mash_settings.pummel_delay.description", ToggleMultiple, )); mash_tab_submenus.push(BoolFlag::to_submenu( - "Falling Aerials", + "menus.mash_settings.falling_aerials.title", "falling_aerials", - "Should aerials be performed when rising or when falling", + "menus.mash_settings.falling_aerials.description", ToggleMultiple, )); mash_tab_submenus.push(BoolFlag::to_submenu( - "Full Hop", + "menus.mash_settings.full_hop.title", "full_hop", - "Should the CPU perform a ful hop or a short hop when jumping", + "menus.mash_settings.full_hop.description", ToggleMultiple, )); mash_tab_submenus.push(Delay::to_submenu( - "Aerial Delay", + "menus.mash_settings.aerial_delay.title", "aerial_delay", - "How long to delay an aerial attack", + "menus.mash_settings.aerial_delay.description", ToggleMultiple, )); mash_tab_submenus.push(BoolFlag::to_submenu( - "Fast Fall", + "menus.mash_settings.fast_fall.title", "fast_fall", - "Should the CPU fastfall during a jump", + "menus.mash_settings.fast_fall.description", ToggleMultiple, )); mash_tab_submenus.push(Delay::to_submenu( - "Fast Fall Delay", + "menus.mash_settings.fast_fall_delay.title", "fast_fall_delay", - "How many frames the CPU should delay their fastfall", + "menus.mash_settings.fast_fall_delay.description", ToggleMultiple, )); mash_tab_submenus.push(Delay::to_submenu( - "OoS Offset", + "menus.mash_settings.oos_offset.title", "oos_offset", - "How many times the CPU shield can be hit before performing a Mash option", + "menus.mash_settings.oos_offset.description", ToggleMultiple, )); mash_tab_submenus.push(Delay::to_submenu( - "Reaction Time", + "menus.mash_settings.reaction_time.title", "reaction_time", - "How many frames to delay before performing a Mash option", + "menus.mash_settings.reaction_time.description", ToggleMultiple, )); let mash_tab = Tab { @@ -719,9 +719,15 @@ pub unsafe fn create_app<'a>() -> App<'a> { "Reset Training Room when pressing L+R+A", ToggleSingle, )); + misc_tab_submenus.push(Locale::to_submenu( + "menus.misc_settings.language.title", + "selected_locale", + "menus.misc_settings.language.help_text", + ToggleSingle, + )); let misc_tab = Tab { id: "misc", - title: "Misc Settings", + title: "menus.misc_settings.tab_name", submenus: StatefulTable::with_items(NX_SUBMENU_ROWS, NX_SUBMENU_COLUMNS, misc_tab_submenus), }; overall_menu.tabs.push(misc_tab); diff --git a/training_mod_consts/src/options.rs b/training_mod_consts/src/options.rs index f5ce98895..e77f509f1 100644 --- a/training_mod_consts/src/options.rs +++ b/training_mod_consts/src/options.rs @@ -1149,6 +1149,8 @@ byteflags! { } } +impl_submenutrait!(Locale); + impl Locale { pub const fn default() -> Locale { Locale::ENGLISH_US From b8ca149966dc03fb1ee605f4501f522246b33b74 Mon Sep 17 00:00:00 2001 From: Matthew Edell Date: Wed, 27 Nov 2024 05:08:28 +0000 Subject: [PATCH 5/7] Re-added translation update --- locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index 6a2428217..87ee8541e 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -87,7 +87,7 @@ "description": "De combien de frames retarder le Fast Fall." }, "oos_offset": { - "title": "Limite d'Out of Shield", + "title": "Coups avant Out of Shield", "description": "Combien de fois le shield doit être touché avant que l'option Out of Shield se lance." }, "reaction_time": { From 007edde93cba45f240091eb6adba8ae63f6c3396 Mon Sep 17 00:00:00 2001 From: Matthew Edell Date: Wed, 27 Nov 2024 05:11:51 +0000 Subject: [PATCH 6/7] added missing newlines --- locales/en_us.json | 2 +- locales/fr.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en_us.json b/locales/en_us.json index 45d6bc4f1..7c6465f62 100644 --- a/locales/en_us.json +++ b/locales/en_us.json @@ -110,4 +110,4 @@ } } } -} \ No newline at end of file +} diff --git a/locales/fr.json b/locales/fr.json index 87ee8541e..84ef5d456 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -96,4 +96,4 @@ } } } -} \ No newline at end of file +} From be854cfd8e07a97f2dcd0018182bee8226d558ac Mon Sep 17 00:00:00 2001 From: Muhammad Kassar <36678834+mk360@users.noreply.github.com> Date: Sat, 30 Nov 2024 14:10:59 +0100 Subject: [PATCH 7/7] fix start button in fr --- locales/fr.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 84ef5d456..efa48cd0d 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -11,7 +11,7 @@ "off": "Désactivé" }, "buttons": { - "start": "Commencer", + "start": "Start", "a": "A", "b": "B", "x": "X", @@ -43,12 +43,12 @@ "description": "Mashing à effectuer dès que possible" }, "followup_toggles": { - "title": "Déclencheurs de Followups", + "title": "Options de Followups", "description": "Followups à effectuer dès que possible" }, "mash_triggers": { "title": "Déclencheurs de Mashing", - "description": "Configurer les actions qui feront que le CPU mashera." + "description": "Configurer les actions qui feront réagir le CPU." }, "attack_angle": { "title": "Angle d'Attaque",