feat(gui): let the UI language be chosen manually (#179)

The interface followed the system locale with no way to override it. Settings now
carry a language picker, stored as forced_lang in gui.json, with "System" listed
first so the choice stays undoable.

rust-i18n keeps the current locale in a process-wide atomic and t! reads it on
every lookup, so switching takes effect on the next frame without a restart.

Languages are listed by their own name rather than a translated one, since that
is what a reader looking for their language recognises. A test asserts every
shipped locale has such a name, so adding a translation without naming it fails
rather than silently showing a bare language code.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Tarasov Aleksandr
2026-07-26 21:24:54 +03:00
committed by GitHub
co-authored by Claude Opus 5
parent 7b87dc02dd
commit 389640ee0a
5 changed files with 116 additions and 4 deletions
+30 -1
View File
@@ -1,4 +1,4 @@
use crate::gui::SoundpadGui;
use crate::{gui::SoundpadGui, locale};
use egui::{Align, Button, Color32, ComboBox, Layout, RichText, Slider, Ui};
use egui_material_icons::icons::ICON_ARROW_BACK;
use pwsp_lib::types::config::PreferredTheme;
@@ -64,6 +64,7 @@ impl SoundpadGui {
// ---------- Selectors -----------
self.draw_mic_selection(ui);
self.draw_output_selection(ui);
self.draw_language_selection(ui);
let mut selected_theme = self.config.preferred_theme.clone();
ComboBox::from_label(t!("gui.settings.theme.label"))
@@ -184,4 +185,32 @@ impl SoundpadGui {
self.set_output(selected_output);
}
}
fn draw_language_selection(&mut self, ui: &mut Ui) {
let mut selected = self.config.forced_lang.clone();
let previous = selected.clone();
let selected_text = match &selected {
Some(code) => locale::display_name(code).to_string(),
None => t!("gui.settings.language.system").to_string(),
};
ComboBox::from_label(t!("gui.settings.language.label"))
.height(30.0)
.selected_text(selected_text)
.show_ui(ui, |ui| {
// Listed first so picking a language stays undoable.
ui.selectable_value(&mut selected, None, t!("gui.settings.language.system"));
for code in locale::available() {
let name = locale::display_name(&code);
ui.selectable_value(&mut selected, Some(code.clone()), name);
}
});
if selected != previous {
self.config.forced_lang = selected;
self.config.save_to_file().ok();
locale::apply(&self.config);
}
}
}