From afcede00872b4f856e8e615ac1578e0ec4d947c1 Mon Sep 17 00:00:00 2001 From: Fahad <42780409+F2had@users.noreply.github.com> Date: Wed, 13 May 2026 20:02:42 +0300 Subject: [PATCH 1/5] lang: add language picker strings (en, ar) --- locales/ar.toml | 2 ++ locales/en.toml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/locales/ar.toml b/locales/ar.toml index 3f809c50..0ff424a6 100644 --- a/locales/ar.toml +++ b/locales/ar.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "التشغيل عند بدء النظام" settings_export_p12 = "تصدير P12" settings_select_teams = "اختر فريقًا..." settings_loading_teams = "جارٍ تحميل الفِرَق..." +settings_language = "اللغة:" +settings_system_language = "النظام" utilities_loading = "جارٍ التحميل..." utilities_refresh_installed_apps = "تحديث التطبيقات المثبتة" diff --git a/locales/en.toml b/locales/en.toml index 1324c608..cbda1593 100644 --- a/locales/en.toml +++ b/locales/en.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "Launch on Startup" settings_export_p12 = "Export P12" settings_select_teams = "Select team..." settings_loading_teams = "Loading teams..." +settings_language = "Language:" +settings_system_language = "System" utilities_loading = "Loading..." utilities_refresh_installed_apps = "Refresh Installed Apps" From 8d3c29ad00af05dd0b15702cbd311fe31742b218 Mon Sep 17 00:00:00 2001 From: Fahad <42780409+F2had@users.noreply.github.com> Date: Wed, 13 May 2026 20:04:25 +0300 Subject: [PATCH 2/5] feat(settings): add language picker UI --- apps/plumeimpactor/src/screen/settings.rs | 69 ++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/apps/plumeimpactor/src/screen/settings.rs b/apps/plumeimpactor/src/screen/settings.rs index d9777acb..cbf70c66 100644 --- a/apps/plumeimpactor/src/screen/settings.rs +++ b/apps/plumeimpactor/src/screen/settings.rs @@ -29,6 +29,38 @@ pub enum Message { FetchTeams(String), TeamsLoaded(String, Vec), ToggleAutoStart(bool), + SelectLocale(Option), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LocaleChoice { + code: Option, +} + +impl LocaleChoice { + fn system() -> Self { + Self { code: None } + } + + fn explicit(code: String) -> Self { + Self { code: Some(code) } + } + + fn code(&self) -> Option<&str> { + self.code.as_deref() + } +} + +impl std::fmt::Display for LocaleChoice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.code { + None => write!(f, "{}", rust_i18n::t!("settings_system_language")), + Some(code) => { + let name = rust_i18n::t!("_language_name", locale = code); + write!(f, "{}", name) + } + } + } } #[derive(Debug)] @@ -58,11 +90,16 @@ impl SettingsScreen { } Message::ToggleAutoStart(_) => Task::none(), Message::SelectTeam(_, _) => Task::none(), + Message::SelectLocale(_) => Task::none(), _ => Task::none(), } } - pub fn view<'a>(&'a self, account_store: &'a Option) -> Element<'a, Message> { + pub fn view<'a>( + &'a self, + account_store: &'a Option, + selected_locale: &'a Option, + ) -> Element<'a, Message> { let Some(store) = account_store else { return column![text(t!("settings_loading_accounts"))] .spacing(appearance::THEME_PADDING) @@ -156,6 +193,7 @@ impl SettingsScreen { let auto_start_enabled = crate::startup::auto_start_enabled(); content = content.push(self.view_auto_start_toggle(auto_start_enabled)); + content = content.push(self.view_language_picker(selected_locale)); content = content.push(self.view_account_buttons(selected_index)); content.into() @@ -168,6 +206,35 @@ impl SettingsScreen { .into() } + fn view_language_picker<'a>( + &'a self, + selected_locale: &'a Option, + ) -> Element<'a, Message> { + let mut codes: Vec = rust_i18n::available_locales!() + .into_iter() + .map(|s| s.to_string()) + .collect(); + codes.sort(); + + let mut choices: Vec = Vec::with_capacity(codes.len() + 1); + choices.push(LocaleChoice::system()); + choices.extend(codes.into_iter().map(LocaleChoice::explicit)); + + let current = match selected_locale { + None => LocaleChoice::system(), + Some(code) => LocaleChoice::explicit(code.clone()), + }; + + let picker = pick_list(choices, Some(current), |choice: LocaleChoice| { + Message::SelectLocale(choice.code().map(|s| s.to_string())) + }) + .style(appearance::s_pick_list); + + column![text(t!("settings_language")), picker] + .spacing(appearance::THEME_PADDING) + .into() + } + fn view_account_buttons(&self, selected_index: Option) -> Element<'_, Message> { let mut buttons = row![ button(appearance::icon_text( From 0f48db5aee1ba2a78ae24ed62bd1e7018f60ea57 Mon Sep 17 00:00:00 2001 From: Fahad <42780409+F2had@users.noreply.github.com> Date: Wed, 13 May 2026 20:05:59 +0300 Subject: [PATCH 3/5] feat(settings): wire language picker into Impactor --- apps/plumeimpactor/src/screen/mod.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/plumeimpactor/src/screen/mod.rs b/apps/plumeimpactor/src/screen/mod.rs index ba45f804..0b5cfd27 100644 --- a/apps/plumeimpactor/src/screen/mod.rs +++ b/apps/plumeimpactor/src/screen/mod.rs @@ -93,6 +93,7 @@ pub struct Impactor { login_windows: std::collections::HashMap, pending_installation: bool, certificate_reset_queue: VecDeque, + selected_locale: Option, } #[derive(Debug, Clone, PartialEq)] @@ -139,6 +140,7 @@ impl Impactor { login_windows: std::collections::HashMap::new(), pending_installation: false, certificate_reset_queue: VecDeque::new(), + selected_locale: None, }, open_task, ) @@ -572,6 +574,14 @@ impl Impactor { } screen.update(msg).map(Message::SettingsScreen) } + settings::Message::SelectLocale(choice) => { + self.selected_locale = choice.clone(); + let effective = choice + .or_else(|| current_locale::current_locale().ok()) + .unwrap_or_else(|| "en".to_string()); + rust_i18n::set_locale(&effective); + Task::none() + } _ => screen.update(msg).map(Message::SettingsScreen), } } else { @@ -835,7 +845,7 @@ impl Impactor { ImpactorScreen::Main(screen) => screen.view().map(Message::MainScreen), ImpactorScreen::Utilities(screen) => screen.view().map(Message::UtilitiesScreen), ImpactorScreen::Settings(screen) => screen - .view(&self.account_store) + .view(&self.account_store, &self.selected_locale) .map(Message::SettingsScreen), ImpactorScreen::Installer(screen) => { screen.view(has_device).map(Message::InstallerScreen) From 41349ead9cce0b562c1b4d4021bfd9cc513a0949 Mon Sep 17 00:00:00 2001 From: Fahad <42780409+F2had@users.noreply.github.com> Date: Mon, 18 May 2026 09:09:52 +0300 Subject: [PATCH 4/5] feat(settings): persist selected locale across launches Store the chosen locale in AccountStore so it survives a relaunch; None keeps following the system locale. --- apps/plumeimpactor/src/screen/mod.rs | 12 +++++++++++- crates/plume_store/src/store.rs | 11 +++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/plumeimpactor/src/screen/mod.rs b/apps/plumeimpactor/src/screen/mod.rs index 0b5cfd27..b9819ced 100644 --- a/apps/plumeimpactor/src/screen/mod.rs +++ b/apps/plumeimpactor/src/screen/mod.rs @@ -118,6 +118,10 @@ impl Impactor { let mut tray = ImpactorTray::new(); let store = Self::init_account_store_sync(); tray.update_refresh_apps(&store); + let selected_locale = store.locale().map(str::to_string); + if let Some(code) = &selected_locale { + rust_i18n::set_locale(code); + } let start_in_tray = crate::startup::start_in_tray_from_args(); let (main_window, open_task) = if start_in_tray { (None, Task::none()) @@ -140,7 +144,7 @@ impl Impactor { login_windows: std::collections::HashMap::new(), pending_installation: false, certificate_reset_queue: VecDeque::new(), - selected_locale: None, + selected_locale, }, open_task, ) @@ -577,9 +581,15 @@ impl Impactor { settings::Message::SelectLocale(choice) => { self.selected_locale = choice.clone(); let effective = choice + .clone() .or_else(|| current_locale::current_locale().ok()) .unwrap_or_else(|| "en".to_string()); rust_i18n::set_locale(&effective); + if let Some(store) = &mut self.account_store { + if let Err(err) = store.set_locale_sync(choice) { + log::error!("Failed to persist locale: {err}"); + } + } Task::none() } _ => screen.update(msg).map(Message::SettingsScreen), diff --git a/crates/plume_store/src/store.rs b/crates/plume_store/src/store.rs index 1ff28b54..f8b8a051 100644 --- a/crates/plume_store/src/store.rs +++ b/crates/plume_store/src/store.rs @@ -13,6 +13,8 @@ pub struct AccountStore { accounts: HashMap, // Email -> GsaAccount #[serde(default)] refreshes: HashMap, // UDID -> RefreshDevice (apps?) + #[serde(default)] + locale: Option, // None = system locale #[serde(skip)] path: Option, } @@ -138,6 +140,15 @@ impl AccountStore { } } + pub fn locale(&self) -> Option<&str> { + self.locale.as_deref() + } + + pub fn set_locale_sync(&mut self, locale: Option) -> Result<(), Error> { + self.locale = locale; + self.save_sync() + } + pub async fn accounts_add_from_session( &mut self, email: String, From 121cb884440ecacfdf5f9719473b3a7a49e1dc49 Mon Sep 17 00:00:00 2001 From: CLARATION Date: Wed, 20 May 2026 02:14:44 -0700 Subject: [PATCH 5/5] chore: add missing locales --- locales/de.toml | 2 ++ locales/es.toml | 2 ++ locales/fi.toml | 2 ++ locales/fr.toml | 2 ++ locales/it.toml | 2 ++ locales/pl.toml | 2 ++ locales/ru.toml | 4 +++- locales/ua.toml | 4 +++- locales/vi.toml | 2 ++ locales/zh_cn.toml | 2 ++ 10 files changed, 22 insertions(+), 2 deletions(-) diff --git a/locales/de.toml b/locales/de.toml index 336f6891..8e3fb8ff 100644 --- a/locales/de.toml +++ b/locales/de.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "Beim Systemstart öffnen" settings_export_p12 = "P12 Exportieren" settings_select_teams = "Team auswählen..." settings_loading_teams = "Lade Teams..." +settings_language = "Language:" +settings_system_language = "System" utilities_loading = "Laden..." utilities_refresh_installed_apps = "Installierte Apps reinstallieren" diff --git a/locales/es.toml b/locales/es.toml index 29b5af66..3d503e22 100644 --- a/locales/es.toml +++ b/locales/es.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "Abrir al iniciar el sistema" settings_export_p12 = "Exportar P12" settings_select_teams = "Seleccionar equipo..." settings_loading_teams = "Cargando equipos..." +settings_language = "Language:" +settings_system_language = "System" utilities_loading = "Cargando..." utilities_refresh_installed_apps = "Actualizar aplicaciones instaladas" diff --git a/locales/fi.toml b/locales/fi.toml index dd556e6f..d20b81fe 100644 --- a/locales/fi.toml +++ b/locales/fi.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "Avaa käynnistyksen yhteydessä" settings_export_p12 = "Ulosvie P12" settings_select_teams = "Valitse tiimi..." settings_loading_teams = "Ladataan tiimejä..." +settings_language = "Language:" +settings_system_language = "System" utilities_loading = "Ladataan..." utilities_refresh_installed_apps = "Päivitä Asennetut Sovellukset" diff --git a/locales/fr.toml b/locales/fr.toml index cb7f5345..695b9e52 100644 --- a/locales/fr.toml +++ b/locales/fr.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "Lancer au démarrage" settings_export_p12 = "Exporter P12" settings_select_teams = "Choisir une équipe..." settings_loading_teams = "Chargement des équipes..." +settings_language = "Language:" +settings_system_language = "System" utilities_loading = "Chargement..." utilities_refresh_installed_apps = "Actualiser les apps installées" diff --git a/locales/it.toml b/locales/it.toml index fc0d234f..ae03e347 100644 --- a/locales/it.toml +++ b/locales/it.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "Launcia all'avvio" settings_export_p12 = "Esporta P12" settings_select_teams = "Scelgo il team..." settings_loading_teams = "Carico team..." +settings_language = "Language:" +settings_system_language = "System" utilities_loading = "Carico..." utilities_refresh_installed_apps = "Refresh App Installate" diff --git a/locales/pl.toml b/locales/pl.toml index 8a2ff3e7..ec2c54eb 100644 --- a/locales/pl.toml +++ b/locales/pl.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "Uruchamiaj przy starcie systemu" settings_export_p12 = "Eksportuj P12" settings_select_teams = "Wybierz zespół..." settings_loading_teams = "Ładowanie zespołów..." +settings_language = "Language:" +settings_system_language = "System" utilities_loading = "Ładowanie..." utilities_refresh_installed_apps = "Odśwież zainstalowane aplikacje" diff --git a/locales/ru.toml b/locales/ru.toml index ae931185..80fc034e 100644 --- a/locales/ru.toml +++ b/locales/ru.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "Запускать при старте систе settings_export_p12 = "Экспортировать P12" settings_select_teams = "Выбрать команду..." settings_loading_teams = "Загрузка команд..." +settings_language = "Language:" +settings_system_language = "System" utilities_loading = "Загрузка..." utilities_refresh_installed_apps = "Обновить список установленных приложений" @@ -70,4 +72,4 @@ login_loading = "Вход в систему..." login_two_fa = "Двухфакторная аутентификация" login_two_fa_desc = "Введите проверочный код, отправленный на ваше устройство:" login_verify = "Подтвердить" -login_verifying = "Проверка..." \ No newline at end of file +login_verifying = "Проверка..." diff --git a/locales/ua.toml b/locales/ua.toml index 6ee368d9..24d75a27 100644 --- a/locales/ua.toml +++ b/locales/ua.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "Запускати під час старту си settings_export_p12 = "Експортувати P12" settings_select_teams = "Обрати команду..." settings_loading_teams = "Завантаження команд..." +settings_language = "Language:" +settings_system_language = "System" utilities_loading = "Завантаження..." utilities_refresh_installed_apps = "Оновити список встановлених програм" @@ -70,4 +72,4 @@ login_loading = "Вхід до системи..." login_two_fa = "Двофакторна автентифікація" login_two_fa_desc = "Введіть перевірочний код, надісланий на ваш пристрій:" login_verify = "Підтвердити" -login_verifying = "Перевірка..." \ No newline at end of file +login_verifying = "Перевірка..." diff --git a/locales/vi.toml b/locales/vi.toml index f28a53d5..e668ddad 100644 --- a/locales/vi.toml +++ b/locales/vi.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "Khởi động cùng hệ điều hành" settings_export_p12 = "Xuất chứng chỉ P12" settings_select_teams = "Chọn nhóm..." settings_loading_teams = "Đang tải danh sách nhóm..." +settings_language = "Language:" +settings_system_language = "System" utilities_loading = "Đang tải công cụ..." utilities_refresh_installed_apps = "Làm mới các ứng dụng đã cài đặt" diff --git a/locales/zh_cn.toml b/locales/zh_cn.toml index d4c3657e..10f1fedc 100644 --- a/locales/zh_cn.toml +++ b/locales/zh_cn.toml @@ -47,6 +47,8 @@ settings_launch_on_startup = "开机自启动" settings_export_p12 = "导出 P12 文件" settings_select_teams = "选择团队..." settings_loading_teams = "正在加载团队..." +settings_language = "Language:" +settings_system_language = "System" utilities_loading = "加载中..." utilities_refresh_installed_apps = "刷新已安装应用"