refactor: make UI localization extensible and integrate upstream
This commit is contained in:
commit
6d4c031090
199 files changed
+31595
-2151
No files matched your search
+33
-30
@@ -46,7 +46,7 @@ pub struct Prefs {
|
||||
pub angle_decimals: u8,
|
||||
/// `dark`, `light` or `system` (follow the operating system).
|
||||
pub theme: String,
|
||||
/// Interface language (`en`, `ja`).
|
||||
/// Interface language (a registered locale ID).
|
||||
pub language: String,
|
||||
/// Interface scale (1 = 100 %).
|
||||
pub ui_scale: f32,
|
||||
@@ -380,7 +380,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
let mut open = true;
|
||||
let mut button: Option<&str> = None;
|
||||
let before = app.preferences.clone();
|
||||
crate::frame::window(ctx, app.tr("Preferences"), crate::frame::Width::Wide)
|
||||
crate::frame::window(ctx, app.tr("ui.preferences"), crate::frame::Width::Wide)
|
||||
.id(egui::Id::new("sc_prefs"))
|
||||
.open(&mut open)
|
||||
.collapsible(false)
|
||||
@@ -393,7 +393,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
ui.vertical(|ui| {
|
||||
ui.set_width(140.0);
|
||||
for (i, s) in PAGES.iter().enumerate() {
|
||||
if ui.selectable_label(app.prefs_window.section == i, app.tr(s)).clicked() {
|
||||
if ui.selectable_label(app.prefs_window.section == i, app.tr_label(s)).clicked() {
|
||||
app.prefs_window.section = i;
|
||||
app.keymap.read_only = false;
|
||||
}
|
||||
@@ -403,24 +403,24 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
ui.vertical(|ui| {
|
||||
// A fixed width, so the window keeps its size from page to page.
|
||||
ui.set_width(540.0);
|
||||
ui.label(RichText::new(app.tr(PAGES.get(app.prefs_window.section).copied().unwrap_or_default())).strong().size(14.0));
|
||||
ui.label(RichText::new(app.tr_label(PAGES.get(app.prefs_window.section).copied().unwrap_or_default())).strong().size(14.0));
|
||||
ui.add_space(6.0);
|
||||
page(app, ui, &t);
|
||||
});
|
||||
});
|
||||
ui.separator();
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button(app.tr("Restore Defaults")).on_hover_text(app.tr("Reset the settings on this page")).clicked() {
|
||||
if ui.button(app.tr("ui.restore_defaults")).on_hover_text(app.tr("ui.reset_the_settings_on_this_page")).clicked() {
|
||||
button = Some("defaults");
|
||||
}
|
||||
crate::frame::right_aligned(ui, 700.0, 170.0, |ui| {
|
||||
if ui.button(app.tr("OK")).clicked() {
|
||||
if ui.button(app.tr("ui.heading_ok")).clicked() {
|
||||
button = Some("ok");
|
||||
}
|
||||
if ui.button(app.tr("Cancel")).clicked() {
|
||||
if ui.button(app.tr("ui.cancel")).clicked() {
|
||||
button = Some("cancel");
|
||||
}
|
||||
if ui.button(app.tr("Apply")).clicked() {
|
||||
if ui.button(app.tr("ui.apply")).clicked() {
|
||||
button = Some("apply");
|
||||
}
|
||||
});
|
||||
@@ -471,6 +471,7 @@ fn up_axis_changed(app: &mut SolveApp) {
|
||||
|
||||
/// One page of the dialog.
|
||||
fn page(app: &mut SolveApp, ui: &mut egui::Ui, t: &Tokens) {
|
||||
let _language = crate::i18n::scope(&app.preferences.language);
|
||||
let note = |ui: &mut egui::Ui, s: &str| {
|
||||
ui.label("");
|
||||
ui.label(RichText::new(s).size(11.0).color(t.text_dim));
|
||||
@@ -482,19 +483,21 @@ fn page(app: &mut SolveApp, ui: &mut egui::Ui, t: &Tokens) {
|
||||
}
|
||||
egui::Grid::new(("sc_prefs_grid", app.prefs_window.section)).num_columns(2).spacing([16.0, 8.0]).show(ui, |ui| match app.prefs_window.section {
|
||||
0 => {
|
||||
ui.label(app.tr("Language"));
|
||||
egui::ComboBox::from_id_salt("pref_language")
|
||||
.selected_text(crate::i18n::LANGUAGES.iter().find(|(id, _)| *id == app.preferences.language).map_or("English", |x| x.1))
|
||||
.show_ui(ui, |ui| {
|
||||
for (id, label) in crate::i18n::LANGUAGES {
|
||||
ui.label(app.tr("ui.language"));
|
||||
egui::ComboBox::from_id_salt("pref_language").selected_text(crate::i18n::Locale::for_language(&app.preferences.language).name()).show_ui(
|
||||
ui,
|
||||
|ui| {
|
||||
for locale in crate::i18n::LOCALES {
|
||||
let (id, label) = (locale.id, locale.name);
|
||||
ui.selectable_value(&mut app.preferences.language, id.to_string(), label);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
ui.end_row();
|
||||
ui.label(app.tr("Theme"));
|
||||
ui.label(app.tr("ui.theme"));
|
||||
ui.horizontal(|ui| {
|
||||
for (id, label) in THEMES {
|
||||
if ui.radio(app.preferences.theme == id, app.tr(label)).clicked() {
|
||||
if ui.radio(app.preferences.theme == id, app.tr_label(label)).clicked() {
|
||||
app.preferences.theme = id.to_string();
|
||||
if id != "system" {
|
||||
app.ui.dark = id == "dark";
|
||||
@@ -503,30 +506,30 @@ fn page(app: &mut SolveApp, ui: &mut egui::Ui, t: &Tokens) {
|
||||
}
|
||||
});
|
||||
ui.end_row();
|
||||
ui.label(app.tr("UI scale"));
|
||||
ui.label(app.tr("ui.ui_scale"));
|
||||
ui.add(egui::Slider::new(&mut app.preferences.ui_scale, 0.75..=2.0).step_by(0.05).custom_formatter(|v, _| format!("{:.0} %", v * 100.0)));
|
||||
ui.end_row();
|
||||
ui.label(app.tr("Units of new designs"));
|
||||
ui.label(app.tr("ui.units_of_new_designs"));
|
||||
egui::ComboBox::from_id_salt("pref_units").selected_text(app.preferences.default_units.clone()).show_ui(ui, |ui| {
|
||||
for u in UNITS {
|
||||
ui.selectable_value(&mut app.preferences.default_units, u.to_string(), u);
|
||||
}
|
||||
});
|
||||
ui.end_row();
|
||||
ui.label(app.tr("Autosave every"));
|
||||
ui.label(app.tr("ui.autosave_every"));
|
||||
if ui.add(egui::DragValue::new(&mut app.autosave_minutes).range(0.0..=120.0).speed(0.5).suffix(" min")).changed() {
|
||||
set_autosave(app);
|
||||
}
|
||||
ui.end_row();
|
||||
note(ui, "0 turns it off");
|
||||
ui.label(app.tr("Length decimals"));
|
||||
ui.label(app.tr("ui.length_decimals"));
|
||||
ui.add(egui::DragValue::new(&mut app.preferences.length_decimals).range(0..=6));
|
||||
ui.end_row();
|
||||
ui.label(app.tr("Angle decimals"));
|
||||
ui.label(app.tr("ui.angle_decimals"));
|
||||
ui.add(egui::DragValue::new(&mut app.preferences.angle_decimals).range(0..=6));
|
||||
ui.end_row();
|
||||
ui.label(app.tr("Agent cursor"));
|
||||
let speed_label = app.tr("Agent cursor speed");
|
||||
ui.label(app.tr("ui.agent_cursor"));
|
||||
let speed_label = app.tr("ui.agent_cursor_speed");
|
||||
let s = &mut app.ui.agent_cursor;
|
||||
ui.checkbox(&mut s.show, "Show the pointer an AI agent moves (#35)");
|
||||
ui.end_row();
|
||||
@@ -707,8 +710,8 @@ pub fn set(app: &mut SolveApp, p: &serde_json::Value) -> Result<serde_json::Valu
|
||||
if !THEMES.iter().any(|(id, _)| *id == new.theme) || !["z", "y"].contains(&new.up_axis.as_str()) {
|
||||
return Err("`theme` must be dark, light or system and `up_axis` z or y".into());
|
||||
}
|
||||
if !crate::i18n::LANGUAGES.iter().any(|(id, _)| *id == new.language) {
|
||||
return Err("`language` must be en or ja".into());
|
||||
if !crate::i18n::is_supported(&new.language) {
|
||||
return Err(format!("`language` must be one of: {}", crate::i18n::LOCALES.iter().map(|locale| locale.id).collect::<Vec<_>>().join(", ")));
|
||||
}
|
||||
if !(0.5..=2.5).contains(&new.ui_scale) || !(0.25..=4.0).contains(&new.look_at_speed) {
|
||||
return Err("`ui_scale` must be 0.5…2.5 and `look_at_speed` 0.25…4".into());
|
||||
@@ -808,20 +811,20 @@ mod tests {
|
||||
fn language_preference_switches_and_persists() {
|
||||
let mut app = SolveApp::new(solvecraft_engine::Session::default(), Default::default());
|
||||
assert_eq!(app.preferences.language, "en");
|
||||
assert_eq!(app.tr("Preferences"), "Preferences");
|
||||
assert_eq!(app.tr("Extrude"), "Extrude");
|
||||
assert_eq!(app.tr("ui.preferences"), "Preferences");
|
||||
assert_eq!(app.tr("ui.extrude"), "Extrude");
|
||||
|
||||
// Switch language to Japanese
|
||||
set(&mut app, &json!({"language": "ja"})).unwrap();
|
||||
assert_eq!(app.preferences.language, "ja");
|
||||
assert_eq!(app.tr("Preferences"), "環境設定");
|
||||
assert_eq!(app.tr("Extrude"), "押し出し");
|
||||
assert_eq!(app.tr("ui.preferences"), "環境設定");
|
||||
assert_eq!(app.tr("ui.extrude"), "押し出し");
|
||||
|
||||
// Preferences round-trip
|
||||
let mut other = SolveApp::new(solvecraft_engine::Session::default(), Default::default());
|
||||
other.load_prefs(&app.prefs());
|
||||
assert_eq!(other.preferences.language, "ja");
|
||||
assert_eq!(other.tr("Preferences"), "環境設定");
|
||||
assert_eq!(other.tr("ui.preferences"), "環境設定");
|
||||
}
|
||||
|
||||
/// A STEP file opened Z-up is offered to be stood up: a quarter turn about X, undoable.
|
||||
|
||||
Reference in new issue
Block a user