From f44b31e15443507e2a03d54cf741428d5f0d900c Mon Sep 17 00:00:00 2001 From: Losminek <301412321+Losminek@users.noreply.github.com> Date: Sun, 11 Oct 2026 13:36:33 +0200 Subject: [PATCH] i18n: cover Czech in the interface-language tests - The catalog-completeness test requires a non-empty Czech label for every key of the reference (Japanese) table. - Czech context keys: `Page Layout|Orientation` and `Orientation` get different labels; unknown text such as `Sheet1!A1` passes through. - `cs-CZ` / `cs_CZ.UTF-8` system tags and `parse("cs")` resolve to Czech. - The ribbon renders after switching to Czech, and the choice persists in UiState. - Czech keeps canonical formula entry (`=SUM(1.5,2.25)`), and `=SUMA(1;2)` is not accepted. - The UI font draws every Czech accented letter, upper and lower case, without tofu; Czech shares the default Han font order. Czech spells "Data" as English does, so the Czech checks probe "Formulas" -> "Vzorce", where an English fallback would be caught. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/i18n.rs | 10 ++++++++++ crates/ui-egui/src/theme.rs | 6 ++++-- crates/ui-egui/tests/formula_locale.rs | 16 ++++++++++++++++ crates/ui-egui/tests/interaction.rs | 7 +++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index 2ff83990e..c55d9c889 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -440,6 +440,7 @@ mod tests { assert!(ko::translate(english).is_some_and(|text| !text.is_empty()), "missing Korean: {english}"); assert!(ru::translate(english).is_some_and(|text| !text.is_empty()), "missing Russian: {english}"); assert!(pt::translate(english).is_some_and(|text| !text.is_empty()), "missing Portuguese: {english}"); + assert!(cs::translate(english).is_some_and(|text| !text.is_empty()), "missing Czech: {english}"); } assert_eq!(Language::Ja.tr("Data"), "データ"); assert_eq!(Language::Zh.tr("Data"), "数据"); @@ -452,10 +453,16 @@ mod tests { assert_eq!(Language::Zh.tr("Sheet1!A1"), "Sheet1!A1"); assert_eq!(Language::Ko.tr("Sheet1!A1"), "Sheet1!A1"); assert_eq!(Language::Ru.tr("Sheet1!A1"), "Sheet1!A1"); + // Czech "Data" is spelled as in English, so probe it with a label that differs. + assert_eq!(Language::Cs.tr("Data"), "Data"); + assert_eq!(Language::Cs.tr("Formulas"), "Vzorce"); + assert_eq!(Language::Cs.tr("Sheet1!A1"), "Sheet1!A1"); // A `context|label` key shows only its label in English and splits the translation. assert_eq!(Language::En.tr("Page Layout|Orientation"), "Orientation"); assert_eq!(Language::Ja.tr("Page Layout|Orientation"), "印刷の向き"); assert_eq!(Language::Ja.tr("Orientation"), "方向"); + assert_eq!(Language::Cs.tr("Page Layout|Orientation"), "Orientace"); + assert_eq!(Language::Cs.tr("Orientation"), "Orientace textu"); assert_eq!(Language::parse("xx"), None); } @@ -486,6 +493,9 @@ mod tests { assert_eq!(Language::from_tag("pt-BR"), Some(Language::PtBr)); assert_eq!(Language::parse("pt"), Some(Language::PtBr)); assert_eq!(Language::parse("pt-br"), Some(Language::PtBr)); + assert_eq!(Language::from_tag("cs-CZ"), Some(Language::Cs)); + assert_eq!(Language::from_tag("cs_CZ.UTF-8"), Some(Language::Cs)); + assert_eq!(Language::parse("cs"), Some(Language::Cs)); } #[test] diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs index 219e2d289..25b9ca878 100644 --- a/crates/ui-egui/src/theme.rs +++ b/crates/ui-egui/src/theme.rs @@ -587,7 +587,7 @@ mod tests { assert_eq!(keys(HanOrder::Japanese), ["sys-cjk-han-ja", "sys-cjk-han-zh", "sys-cjk-hangul", "sys-thai", "sys-hebrew"]); assert_eq!(keys(HanOrder::Chinese), ["sys-cjk-han-zh", "sys-cjk-han-ja", "sys-cjk-hangul", "sys-thai", "sys-hebrew"]); assert_eq!(HanOrder::of(Language::Ja), HanOrder::Japanese); - for l in [Language::En, Language::Zh, Language::Ko, Language::Ru, Language::PtBr] { + for l in [Language::En, Language::Zh, Language::Ko, Language::Ru, Language::PtBr, Language::Cs] { assert_eq!(HanOrder::of(l), HanOrder::Chinese, "{l:?} shares the default order (no font rebuild)"); } } @@ -613,13 +613,15 @@ mod tests { // Real render check, per script: the proportional family (what cells and the UI use) must // resolve a sample string to a real glyph, not the tofu replacement char. Skips a script // whose face is absent (a minimal machine) and fails if a face is present but not wired in. - let cases: [(&str, Vec<(String, u32)>, &str); 6] = [ + let cases: [(&str, Vec<(String, u32)>, &str); 7] = [ ("sys-cjk-han-ja", cjk_han_japanese(), "カテゴリ"), ("sys-cjk-han-zh", cjk_han_simplified(), "页面布局 客户端配置"), ("sys-cjk-hangul", cjk_hangul(), "한글"), ("sys-thai", thai(), "ทดสอบ"), ("sys-hebrew", hebrew(), "עברית"), ("ui", candidates(UI), "Русский"), + // Czech interface labels: every Czech accented letter, in both cases. + ("ui", candidates(UI), "Příliš žluťoučký kůň úpěl ďábelské ódy ŘŠČŽÝÁÍÉÚŮĚŤĎŇÓ"), ]; for language in [Language::En, Language::Ja] { let ctx = egui::Context::default(); diff --git a/crates/ui-egui/tests/formula_locale.rs b/crates/ui-egui/tests/formula_locale.rs index a92ab1370..6ce22082d 100644 --- a/crates/ui-egui/tests/formula_locale.rs +++ b/crates/ui-egui/tests/formula_locale.rs @@ -185,3 +185,19 @@ fn portuguese_interface_retains_its_canonical_formula_fallback() { assert_eq!(restored.language, Language::PtBr); assert_eq!(Language::Es.tr("Page Layout|Orientation"), "Orientation"); } + +#[test] +fn czech_interface_retains_its_canonical_formula_fallback() { + // The Czech table translates interface labels only: formulas keep canonical English names, + // `,` separators and `.` decimals until a Czech formula locale exists. + let mut app = app(); + app.run("app.language.set", json!({"code":"cs-CZ"})).unwrap(); + assert_eq!(app.ui.language, Language::Cs); + assert_eq!(app.ui.language.tr("Formulas"), "Vzorce"); + assert!(enter(&mut app, "=SUM(1.5,2.25)")); + app.begin_edit(None, true); + assert_eq!(app.editor.as_ref().unwrap().text, "=SUM(1.5,2.25)"); + assert!(!enter(&mut app, "=SUMA(1;2)")); + let restored: UiState = serde_json::from_str(&serde_json::to_string(&app.ui).unwrap()).unwrap(); + assert_eq!(restored.language, Language::Cs); +} diff --git a/crates/ui-egui/tests/interaction.rs b/crates/ui-egui/tests/interaction.rs index 84f20a675..1b71c82f1 100644 --- a/crates/ui-egui/tests/interaction.rs +++ b/crates/ui-egui/tests/interaction.rs @@ -209,6 +209,13 @@ fn language_switch_translates_the_ribbon_and_persists() { let restored: gridcraft_ui_egui::UiState = serde_json::from_str(&serde_json::to_string(&h.state().ui).unwrap()).unwrap(); assert_eq!(restored.language, language); } + // Czech spells "Data" as English does, so the Czech table is checked on a label that differs. + h.state_mut().run("app.language.set", json!({"language": "cs"})).unwrap(); + h.run_steps(2); + assert_eq!(h.state().ui.language, Language::Cs); + assert_eq!(Language::Cs.tr("Formulas"), "Vzorce"); + let restored: gridcraft_ui_egui::UiState = serde_json::from_str(&serde_json::to_string(&h.state().ui).unwrap()).unwrap(); + assert_eq!(restored.language, Language::Cs); h.state_mut().run("app.language.set", json!({"language": "en"})).unwrap(); assert_eq!(h.state().ui.language, Language::En); }