diff --git a/ROADMAP.md b/ROADMAP.md index 1db8ea9..9561dbe 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -170,6 +170,7 @@ Ranked; estimates in Opus 5.5 agent-hours. Detail and the full beta-gate list in | 2026-10-11 | minor | Progress log: XLSX threaded comments round trip (#300) | | 2026-10-11 | minor | Catalog 258 → 259 / 290: Allow Edit Ranges (#307) | | 2026-10-11 | minor | Worksheet functions 505 / 523 (FORECAST.ETS family); progress log row | +| 2026-10-11 | minor | Czech (cs) listed under other shipped languages: ribbon only, ~12% | | 2026-10-10 | minor | Readiness-by-audience table with hours to ~95% per audience; full number unchanged (additive weighted sum) | | 2026-10-10 | minor | Added Mainstream practitioner (~50%) and Essentials user (~63%); languages updated for the 10-10 merges (ribbon in ja, zh, ko, pt-BR, ru); localization 5%→10%; full number re-checked (51.6, unchanged) | | 2026-10-10 | minor | Checked against the alpha gate (six core workflows pass): stays alpha | diff --git a/contributors/people.toml b/contributors/people.toml index 2e3ad53..734bf81 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -42,3 +42,6 @@ real_name = "Alexander Alber" [people.webdevtodayjason] display_name = "Jason Brashear" sync_github_name = false + +[people.Losminek] +sync_github_name = true diff --git a/crates/ui-egui/examples/snapshot.rs b/crates/ui-egui/examples/snapshot.rs index 93359f0..0a481b2 100644 --- a/crates/ui-egui/examples/snapshot.rs +++ b/crates/ui-egui/examples/snapshot.rs @@ -1,7 +1,7 @@ //! Renders the whole GridCraft window offscreen (wgpu, no window) to a PNG. //! //! `cargo run --release -p gridcraft-ui-egui --example snapshot -- [--sample sales] [--in file.xlsx] -//! [--size 1440x900] [--scale 2] [--cmd 'id={json}']... [--tab Insert] [--dark] [--language en|zh|ja|ko|ru|pt] +//! [--size 1440x900] [--scale 2] [--cmd 'id={json}']... [--tab Insert] [--dark] [--language en|zh|ja|ko|ru|pt|cs] //! [--dialog 'name={json}'] out.png` use gridcraft_engine::Session; diff --git a/crates/ui-egui/src/control.rs b/crates/ui-egui/src/control.rs index ade7cd2..7c9a39e 100644 --- a/crates/ui-egui/src/control.rs +++ b/crates/ui-egui/src/control.rs @@ -175,7 +175,7 @@ pub fn handle(app: &mut SheetApp, ctx: &egui::Context, method: &str, p: &Json) - ("view.interfaceScale", "Interface Size", "{percent: 75–200 | \"auto\"} (100 = auto; clamped)"), ("ui.dialog", "Open Dialog", ""), ("ui.backstage", "File (Backstage view)", "{page?: home|new|open|info|saveAs|print|export|options|close}"), - ("app.language.set", "Interface Language", "{language: \"en\"|\"zh\"|\"ja\"|\"ko\"|\"ru\"|\"pt\"|\"de\"} (alias: code)"), + ("app.language.set", "Interface Language", "{language: \"en\"|\"zh\"|\"ja\"|\"ko\"|\"ru\"|\"pt\"|\"de\"|\"cs\"} (alias: code)"), ("app.language.english", "Interface Language: English", ""), ("app.language.japanese", "Interface Language: Japanese", ""), ] { diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index 79ab82c..bbaf71e 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize}; mod de; mod es; +mod cs; mod ko; mod pt; mod ru; @@ -29,10 +30,11 @@ pub enum Language { #[serde(rename = "pt")] PtBr, De, + Cs, } impl Language { - pub const ALL: [Self; 8] = [Self::En, Self::Es, Self::Zh, Self::Ja, Self::Ko, Self::Ru, Self::PtBr, Self::De]; + pub const ALL: [Self; 9] = [Self::En, Self::Es, Self::Zh, Self::Ja, Self::Ko, Self::Ru, Self::PtBr, Self::De, Self::Cs]; /// The language's own name, shown in the switcher. pub fn name(self) -> &'static str { @@ -45,6 +47,7 @@ impl Language { Self::Ru => "Русский", Self::PtBr => "Português (Brasil)", Self::De => "Deutsch", + Self::Cs => "Čeština", } } @@ -59,6 +62,7 @@ impl Language { Self::Ru => "ru", Self::PtBr => "pt", Self::De => "de", + Self::Cs => "cs", } } @@ -76,6 +80,7 @@ impl Language { "ru" => Some(Self::Ru), "pt" => Some(Self::PtBr), "de" => Some(Self::De), + "cs" => Some(Self::Cs), _ => None, } } @@ -111,6 +116,7 @@ impl Language { Self::Ru => ru::translate(text), Self::PtBr => pt::translate(text), Self::De => de::translate(text), + Self::Cs => cs::translate(text), }; translated.unwrap_or_else(|| text.rsplit_once('|').map_or(text, |(_, label)| label)) } @@ -519,6 +525,7 @@ mod tests { assert!(pt::translate(english).is_some_and(|text| !text.is_empty()), "missing Portuguese: {english}"); assert!(es::translate(english).is_some_and(|text| !text.is_empty()), "missing Spanish: {english}"); assert!(de::translate(english).is_some_and(|text| !text.is_empty()), "missing German: {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"), "数据"); @@ -536,10 +543,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); } @@ -572,6 +585,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/i18n/cs.rs b/crates/ui-egui/src/i18n/cs.rs new file mode 100644 index 0000000..5b56b20 --- /dev/null +++ b/crates/ui-egui/src/i18n/cs.rs @@ -0,0 +1,273 @@ +//! Czech translations for the interface strings in i18n.rs. + +pub(super) fn translate(text: &str) -> Option<&'static str> { + Some(match text { + "Paste" => "Vložit", + "Paste Values" => "Vložit hodnoty", + "Paste Formulas" => "Vložit vzorce", + "Paste Formatting" => "Vložit formátování", + "Transpose" => "Transponovat", + "Paste Link" => "Vložit propojení", + "Paste Special…" => "Vložit jinak…", + "Cut (⌘X)" => "Vyjmout (⌘X)", + "Copy (⌘C)" => "Kopírovat (⌘C)", + "Format Painter (double-click to keep it on)" => "Štětec formátu (dvojklikem zůstane zapnutý)", + "Bold (⌘B)" => "Tučné (⌘B)", + "Italic (⌘I)" => "Kurzíva (⌘I)", + "Underline (⌘U)" => "Podtržení (⌘U)", + "Double Underline" => "Dvojité podtržení", + "Strikethrough" => "Přeškrtnutí", + "Underline" => "Podtržení", + "Increase Font Size" => "Zvětšit písmo", + "Decrease Font Size" => "Zmenšit písmo", + "Wrap Text" => "Zalamovat text", + "Merge & Center" => "Sloučit a vycentrovat", + "Merge Cells" => "Sloučit buňky", + "Merge Across" => "Sloučit po řádcích", + "Unmerge Cells" => "Zrušit sloučení buněk", + "Fill Color" => "Barva výplně", + "Font Color" => "Barva písma", + "Borders" => "Ohraničení", + "Top\nAlign" => "Zarovnat\nnahoru", + "Middle\nAlign" => "Zarovnat\ndoprostřed", + "Bottom\nAlign" => "Zarovnat\ndolů", + "Left" => "Doleva", + "Right" => "Doprava", + "Center" => "Zarovnat na střed", + "Horizontal" => "Vodorovně", + "Vertical Text" => "Svislý text", + "Decrease Indent" => "Zmenšit odsazení", + "Increase Indent" => "Zvětšit odsazení", + "Orientation" => "Orientace textu", + "Page Layout|Orientation" => "Orientace", + "Angle Counterclockwise" => "Natočit proti směru hodin", + "Angle Clockwise" => "Natočit po směru hodin", + "Rotate Text Up" => "Otočit text nahoru", + "Rotate Text Down" => "Otočit text dolů", + "Accounting Number Format" => "Účetnický formát čísla", + "Percent Style" => "Styl procent", + "Comma Style" => "Styl s oddělovačem tisíců", + "Increase Decimal" => "Přidat desetinné místo", + "Decrease Decimal" => "Odebrat desetinné místo", + "More Accounting Formats…" => "Další účetnické formáty…", + "Conditional\nFormatting" => "Podmíněné\nformátování", + "Conditional Formatting" => "Podmíněné formátování", + "Format\nas Table" => "Formátovat\njako tabulku", + "Cell\nStyles" => "Styly\nbuněk", + "Format Cells…" => "Formát buněk…", + "Format Cell Alignment…" => "Zarovnání buněk…", + "Format\nPane" => "Podokno\nformátu", + "Format" => "Formát", + "Insert" => "Vložit", + "Insert Cells…" => "Vložit buňky…", + "Delete" => "Odstranit", + "Delete Cells…" => "Odstranit buňky…", + "Insert Sheet Rows" => "Vložit řádky listu", + "Insert Sheet Columns" => "Vložit sloupce listu", + "Delete Sheet Rows" => "Odstranit řádky listu", + "Delete Sheet Columns" => "Odstranit sloupce listu", + "Row Height…" => "Výška řádku…", + "AutoFit Row Height" => "Přizpůsobit výšku řádku", + "Column Width…" => "Šířka sloupce…", + "AutoFit Column Width" => "Přizpůsobit šířku sloupce", + "Default Width…" => "Výchozí šířka…", + "Hide Rows" => "Skrýt řádky", + "Unhide Rows" => "Zobrazit řádky", + "Hide Columns" => "Skrýt sloupce", + "Unhide Columns" => "Zobrazit sloupce", + "Lock Cell" => "Zamknout buňku", + "Clear" => "Vymazat", + "Clear All" => "Vymazat vše", + "Clear Formats" => "Vymazat formáty", + "Clear Contents" => "Vymazat obsah", + "Clear Comments and Notes" => "Vymazat komentáře a poznámky", + "Clear Hyperlinks" => "Vymazat hypertextové odkazy", + "AutoSum" => "Automatický\nsoučet", + "AutoSum (⌘⇧T)" => "Automatický součet (⌘⇧T)", + "Flash\nFill" => "Dynamické\ndoplňování", + "Flash Fill" => "Dynamické doplňování", + "Fill" => "Vyplnit", + "Clear Print Area" => "Vymazat oblast tisku", + "Find &\nSelect" => "Najít a\nvybrat", + "Find…" => "Najít…", + "Replace…" => "Nahradit…", + "Go To…" => "Přejít na…", + "Go To Special…" => "Přejít na jinak…", + "PivotTable" => "Kontingenční\ntabulka", + "Recommended\nCharts" => "Doporučené\ngrafy", + "Table" => "Tabulka", + "Pictures" => "Obrázky", + "Shapes" => "Obrazce", + "Icons" => "Ikony", + "Text\nBox" => "Textové\npole", + "Comment" => "Komentář", + "New\nComment" => "Nový\nkomentář", + "New Note" => "Nová poznámka", + "Show/Hide Note" => "Zobrazit/skrýt poznámku", + "Header &\nFooter" => "Záhlaví a\nzápatí", + "Text to\nColumns" => "Text do\nsloupců", + "Link" => "Odkaz", + "Symbol" => "Symbol", + "Sparklines" => "Minigrafy", + "Header & Footer" => "Záhlaví a zápatí", + "Draw with ink" => "Kreslit od ruky", + "Eraser" => "Guma", + "Ink to\nShape" => "Rukopis\nna obrazec", + "Select\nObjects" => "Vybrat\nobjekty", + "Themes" => "Motivy", + "Margins" => "Okraje", + "Size" => "Velikost", + "Print\nArea" => "Oblast\ntisku", + "Set Print Area" => "Nastavit oblast tisku", + "Breaks" => "Konce", + "Insert Page Break" => "Vložit konec stránky", + "Remove Page Break" => "Odebrat konec stránky", + "Reset All Page Breaks" => "Obnovit všechny konce stránek", + "Print\nTitles" => "Tisk\nnázvů", + "Narrow" => "Úzké", + "Wide" => "Široké", + "Normal" => "Normální", + "Portrait" => "Na výšku", + "Landscape" => "Na šířku", + "Custom Margins…" => "Vlastní okraje…", + "Insert\nFunction" => "Vložit\nfunkci", + "Name\nManager" => "Správce\nnázvů", + "Define Name" => "Definovat název", + "Use in Formula" => "Použít ve vzorci", + "Create from Selection" => "Vytvořit z výběru", + "Trace Precedents" => "Sledovat předchůdce", + "Trace Dependents" => "Sledovat následníky", + "Remove Arrows" => "Odebrat šipky", + "Show Formulas" => "Zobrazit vzorce", + "Error Checking" => "Kontrola chyb", + "Evaluate Formula" => "Vyhodnotit vzorec", + "Watch Window" => "Okno sledování", + "Calculation\nOptions" => "Možnosti\nvýpočtu", + "Calculate Now" => "Přepočítat", + "Calculate Sheet" => "Přepočítat list", + "Automatic" => "Automaticky", + "Automatic Except for Data Tables" => "Automaticky kromě tabulek dat", + "Manual" => "Ručně", + "Sort &\nFilter" => "Seřadit a\nfiltrovat", + "Sort" => "Seřadit", + "Sort A to Z" => "Seřadit od A do Z", + "Sort Z to A" => "Seřadit od Z do A", + "Custom Sort…" => "Vlastní řazení…", + "Filter" => "Filtr", + "Reapply" => "Použít znovu", + "Get Data\n(Text/CSV)" => "Načíst data\n(text/CSV)", + "Remove\nDuplicates" => "Odebrat\nduplicity", + "Data\nValidation" => "Ověření\ndat", + "Data Validation" => "Ověření dat", + "Data Validation…" => "Ověření dat…", + "Circle Invalid Data" => "Zakroužkovat neplatná data", + "Clear Validation" => "Vymazat ověření", + "Group" => "Seskupit", + "Ungroup" => "Zrušit seskupení", + "Subtotal" => "Mezisoučet", + "What-If\nAnalysis" => "Citlivostní\nanalýza", + "Data Table…" => "Tabulka dat…", + "Scenario Manager…" => "Správce scénářů…", + "Goal Seek…" => "Hledat řešení…", + "Spelling" => "Pravopis", + "Check\nAccessibility" => "Zkontrolovat\npřístupnost", + "Threaded Comments" => "Komentáře ve vláknech", + "Show\nComments" => "Zobrazit\nkomentáře", + "Protect\nWorkbook" => "Zamknout\nsešit", + "Unprotect\nSheet" => "Odemknout\nlist", + "Protect Sheet…" => "Zamknout list…", + "Comments" => "Komentáře", + "Notes" => "Poznámky", + "Page Break\nPreview" => "Náhled\nkonců stránek", + "Page\nLayout" => "Rozložení\nstránky", + "Formula Bar" => "Řádek vzorců", + "Gridlines" => "Mřížka", + "Headings" => "Záhlaví", + "Zoom" => "Lupa", + "100%" => "100 %", + "Zoom to\nSelection" => "Přiblížit\nna výběr", + "Freeze\nPanes" => "Ukotvit\npříčky", + "Freeze Panes" => "Ukotvit příčky", + "Freeze Top Row" => "Ukotvit horní řádek", + "Freeze First Column" => "Ukotvit první sloupec", + "Unfreeze Panes" => "Uvolnit příčky", + "Dark Mode" => "Tmavý režim", + "Display theme" => "Vzhled", + "System" => "Podle systému", + "Light" => "Světlý", + "Dark" => "Tmavý", + "Interface language" => "Jazyk rozhraní", + "Command\nPalette" => "Paleta\npříkazů", + "Agent\nControl" => "Řízení\nagenty", + "Action\nJournal" => "Deník\nakcí", + "About\nGridCraft" => "O aplikaci\nGridCraft", + "Share" => "Sdílet", + "Add Chart\nElement" => "Přidat prvek\ngrafu", + "Change\nChart Type" => "Změnit\ntyp grafu", + "Switch\nRow/Column" => "Prohodit\nřádky/sloupce", + "Data Labels: None" => "Popisky dat: žádné", + "Data Labels: Show" => "Popisky dat: zobrazit", + "Legend: None" => "Legenda: žádná", + "Legend: Bottom" => "Legenda: dole", + "Legend: Top" => "Legenda: nahoře", + "Legend: Right" => "Legenda: vpravo", + "Gridlines: None" => "Mřížka: žádná", + "Gridlines: Show" => "Mřížka: zobrazit", + "Chart\nTitle" => "Název\ngrafu", + "Delete\nChart" => "Odstranit\ngraf", + "Convert\nto Range" => "Převést\nna oblast", + "Table\nStyles" => "Styly\ntabulky", + "Top 10 Items" => "Prvních 10 položek", + "Top 10%" => "Prvních 10 %", + "Bottom 10 Items" => "Posledních 10 položek", + "Bottom 10%" => "Posledních 10 %", + "Above Average" => "Nad průměrem", + "Below Average" => "Pod průměrem", + "Clear Rules from Entire Sheet" => "Vymazat pravidla z celého listu", + "Clear Rules from Selected Cells" => "Vymazat pravidla z vybraných buněk", + "Constants" => "Konstanty", + "Data Bars" => "Datové pruhy", + "Color Scales" => "Barevné škály", + "Icon Sets" => "Sady ikon", + "Home" => "Domů", + "Draw" => "Kreslení", + "Page Layout" => "Rozložení stránky", + "Formulas" => "Vzorce", + "Data" => "Data", + "Review" => "Revize", + "View" => "Zobrazení", + "Automate" => "Automatizace", + "Table Design" => "Návrh tabulky", + "Chart Design" => "Návrh grafu", + "Ready" => "Připraveno", + "Enter" => "Zadat", + "Edit" => "Úpravy", + "Point" => "Výběr odkazu", + "Calculate" => "Přepočítat", + "Select destination and press Enter or choose Paste" => "Vyberte cíl a stiskněte Enter nebo zvolte Vložit", + "General" => "Obecný", + "Number" => "Číslo", + "Currency" => "Měna", + "Accounting" => "Účetnický", + "Date" => "Datum", + "Time" => "Čas", + "Percentage" => "Procenta", + "Fraction" => "Zlomek", + "Scientific" => "Vědecký", + "Text" => "Text", + "Special" => "Speciální", + "Custom" => "Vlastní", + "Short Date" => "Krátké datum", + "Long Date" => "Dlouhé datum", + "More Number Formats…" => "Další formáty čísel…", + "Financial" => "Finanční", + "Logical" => "Logické", + "Date &\nTime" => "Datum a\nčas", + "Lookup &\nReference" => "Vyhledávání\na odkazy", + "Math &\nTrig" => "Matematika a\ntrigonometrie", + "More\nFunctions" => "Další\nfunkce", + "Sheet Options|View" => "Zobrazit", + "Print" => "Tisknout", + _ => return None, + }) +} diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 4221923..d174397 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -389,7 +389,7 @@ impl SheetApp { self.ui.language = l; Ok(json!({"language": l})) } - None => Err(format!("unknown language {code:?} (use \"en\", \"es\", \"zh\", \"ja\", \"ko\", \"ru\" or \"pt\")")), + None => Err(format!("unknown language {code:?} (use \"en\", \"es\", \"zh\", \"ja\", \"ko\", \"ru\", \"pt\" or \"cs\")")), } } "app.language.english" => { diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs index ef596b1..6812c51 100644 --- a/crates/ui-egui/src/theme.rs +++ b/crates/ui-egui/src/theme.rs @@ -621,7 +621,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, Language::De] { + for l in [Language::En, Language::Zh, Language::Ko, Language::Ru, Language::PtBr, Language::De, Language::Cs] { assert_eq!(HanOrder::of(l), HanOrder::Chinese, "{l:?} shares the default order (no font rebuild)"); } } @@ -647,13 +647,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 9dcb65d..fe21218 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"), "Orientación"); } + +#[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 bffd328..260b80a 100644 --- a/crates/ui-egui/tests/interaction.rs +++ b/crates/ui-egui/tests/interaction.rs @@ -268,6 +268,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); } diff --git a/docs/localization-parity.md b/docs/localization-parity.md index 133930b..d8738c8 100644 --- a/docs/localization-parity.md +++ b/docs/localization-parity.md @@ -79,5 +79,6 @@ language (none so far) and of localized function names. |---|---|---| | 2026-10-11 | minor | German ribbon catalog (`i18n/de.rs`, 266 labels, ~12%); `de` locale tags (de-DE, de-AT, de-CH…) resolve to German. PR #163 (localized German formulas/numbers) still open | | 2026-10-11 | minor | Sheet Right-to-Left mirrors the grid (#146) | +| 2026-10-11 | minor | Czech (cs) ribbon table: 266 labels (~12%), menus only | | 2026-10-10 | minor | Re-measured after #31, #104, #82, #38 merged: ribbon translated into ja, zh, ko, pt-BR and ru (266–268 labels, ~12%), system-language default, CJK/Thai/Hebrew font fallback; ~10% ready, 100–165 h | | 2026-10-10 | major | Created: English only, no catalog, ~2,200 strings, script-support gaps, open language PRs |