diff --git a/ROADMAP.md b/ROADMAP.md index 1189fd7..d8ef747 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -62,7 +62,7 @@ collaboration, mobile, AI). | File formats | ~50% | 80–120 | [file-format-parity.md](docs/file-format-parity.md) | | Performance | ~35% | 35–55 | [target-app-parity.md](docs/target-app-parity.md#performance) | | Stability | ~60% | 15–25 | [gaps.md](docs/gaps.md) | -| Localization | ~10% (ribbon in 5 languages) | 100–165 | [localization-parity.md](docs/localization-parity.md) | +| Localization | ~10% (ribbon in 6 languages) | 100–165 | [localization-parity.md](docs/localization-parity.md) | | Hardware (multi-core calc, GPU, pen, multi-window) | ~40% | 25–40 | [hardware-parity.md](docs/hardware-parity.md) | | Platforms | ~80% (no iOS/Android; ahead on Linux, BSD, RISC-V) | 30–60 | [target-app-parity.md](docs/target-app-parity.md#platforms) | | Ecosystem / add-ins | ~10% (VBA out of scope; own scripts, MCP, CLI) | 25–40 | [target-app-parity.md](docs/target-app-parity.md#ecosystem-and-ai) | @@ -89,11 +89,12 @@ collaboration, mobile, AI). ## Languages -The ribbon is translated into Japanese, Simplified Chinese, Korean, Brazilian Portuguese and -Russian (266–268 labels each, ~12% of ~2,200 strings; merged 10-10 in #31, #104, #82), the +The ribbon is translated into Japanese, Simplified Chinese, Korean, Brazilian Portuguese, Russian +and German (266–268 labels each, ~12% of ~2,200 strings; merged 10-10 in #31, #104, #82, German +added 10-11), the interface follows the system language, and CJK, Thai and Hebrew text falls back to system fonts -(#38). Dialogs, menus and messages are English; formulas and number formats are en-US only. German -is in PR #163. Detail: [localization-parity.md](docs/localization-parity.md). +(#38). Dialogs, menus and messages are English; formulas and number formats are en-US only. +Localized German formulas and numbers are in PR #163. Detail: [localization-parity.md](docs/localization-parity.md). | Language | Code | Status | UI strings | |---|---|---|---| @@ -106,11 +107,11 @@ is in PR #163. Detail: [localization-parity.md](docs/localization-parity.md). | Portuguese | pt-BR | menus only | ~12% | | Indonesian | id | none | 0% | | Japanese | ja | menus only | ~12% | -| German | de | none (PR #163 open) | 0% | +| German | de | menus only | ~12% | | Korean | ko | menus only | ~12% | | Vietnamese | vi | none | 0% | -Other shipped languages: 1 (Russian, menus only, ~12%). +Other shipped languages: 2 (Russian and German, menus only, ~12%). ## Upcoming @@ -131,6 +132,7 @@ Ranked; estimates in Opus 5.5 agent-hours. Detail and the full beta-gate list in | Date | What landed | |---|---| +| 2026-10-11 | Ribbon translated into German (266 labels; `de`, `de-DE`, `de-AT`, `de-CH` locale tags resolve to German) | | 2026-10-10 | Ribbon translated into Japanese, Chinese, Korean, Brazilian Portuguese and Russian with system-language default (#31, #104, #82); CJK/Thai/Hebrew font fallback (#38); clear messages for encrypted and `.xls` files (#17); smooth line charts (#62). Progress docs restructured to the craftrules standard; full re-measure (this page). Initial ribbon key tips (#43), in-place text box editing (#84), copy as HTML table (#36). ODS and XLSB data import (#100, #101); recalc linear in the number of range formulas (#66); RISC-V Linux build; Flatpak repository on Pages; web cell-commit trap fixed | | 2026-10-09 | Community fixes: spill range operator `A1#`, sheet-scoped names, protection refuses locked-cell edits, Insert/Delete Cells adjust references, `DATE(1900,2,29)`, SUMIF sizing, `ROUND` at 15 digits, `_xlpm.` LAMBDA parameters in XLSX, vertical scrollbar, system appearance, border previews; v0.4.0 | | 2026-10-08 | v0.2.0 and v0.3.0 releases; Flatpak, AppImage zsync, branded DMG; desktop log file; DirectX 12 default on Windows | diff --git a/crates/ui-egui/src/control.rs b/crates/ui-egui/src/control.rs index e6da0f0..fa46f2f 100644 --- a/crates/ui-egui/src/control.rs +++ b/crates/ui-egui/src/control.rs @@ -174,7 +174,7 @@ pub fn handle(app: &mut SheetApp, ctx: &egui::Context, method: &str, p: &Json) - ("view.theme", "Display Theme", ""), ("view.zoom100", "100%", ""), ("ui.dialog", "Open Dialog", ""), - ("app.language.set", "Interface Language", "{language: \"en\"|\"zh\"|\"ja\"|\"ko\"|\"ru\"|\"pt\"} (alias: code)"), + ("app.language.set", "Interface Language", "{language: \"en\"|\"zh\"|\"ja\"|\"ko\"|\"ru\"|\"pt\"|\"de\"} (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 1e7b212..a52801c 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; +mod de; mod ko; mod pt; mod ru; @@ -26,10 +27,11 @@ pub enum Language { Ru, #[serde(rename = "pt")] PtBr, + De, } impl Language { - pub const ALL: [Self; 7] = [Self::En, Self::Es, Self::Zh, Self::Ja, Self::Ko, Self::Ru, Self::PtBr]; + pub const ALL: [Self; 8] = [Self::En, Self::Es, Self::Zh, Self::Ja, Self::Ko, Self::Ru, Self::PtBr, Self::De]; /// The language's own name, shown in the switcher. pub fn name(self) -> &'static str { @@ -41,6 +43,7 @@ impl Language { Self::Ko => "한국어", Self::Ru => "Русский", Self::PtBr => "Português (Brasil)", + Self::De => "Deutsch", } } @@ -54,6 +57,7 @@ impl Language { Self::Ko => "ko", Self::Ru => "ru", Self::PtBr => "pt", + Self::De => "de", } } @@ -70,6 +74,7 @@ impl Language { "ko" => Some(Self::Ko), "ru" => Some(Self::Ru), "pt" => Some(Self::PtBr), + "de" => Some(Self::De), _ => None, } } @@ -103,6 +108,7 @@ impl Language { Self::Ko => ko::translate(text), Self::Ru => ru::translate(text), Self::PtBr => pt::translate(text), + Self::De => de::translate(text), }; translated.unwrap_or_else(|| text.rsplit_once('|').map_or(text, |(_, label)| label)) } @@ -434,12 +440,15 @@ 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!(de::translate(english).is_some_and(|text| !text.is_empty()), "missing German: {english}"); } assert_eq!(Language::Ja.tr("Data"), "データ"); assert_eq!(Language::Zh.tr("Data"), "数据"); assert_eq!(Language::Ko.tr("Data"), "데이터"); assert_eq!(Language::Ru.tr("Data"), "Данные"); assert_eq!(Language::PtBr.tr("Data"), "Dados"); + assert_eq!(Language::De.tr("Data"), "Daten"); + assert_eq!(Language::De.tr("Sheet1!A1"), "Sheet1!A1"); assert_eq!(Language::PtBr.tr("Date"), "Data"); assert_eq!(Language::PtBr.tr("Sheet1!A1"), "Sheet1!A1"); assert_eq!(Language::Ja.tr("Sheet1!A1"), "Sheet1!A1"); @@ -476,7 +485,9 @@ mod tests { assert_eq!(Language::from_tag("zh-Hans-CN"), Some(Language::Zh)); assert_eq!(Language::from_tag("ko-KR"), Some(Language::Ko)); assert_eq!(Language::from_tag("RU_ru"), Some(Language::Ru)); - assert_eq!(Language::from_tag("de-DE"), None); + assert_eq!(Language::from_tag("de-DE"), Some(Language::De)); + assert_eq!(Language::from_tag("de_AT"), Some(Language::De)); + assert_eq!(Language::from_tag("de-CH"), Some(Language::De)); 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)); diff --git a/crates/ui-egui/src/i18n/de.rs b/crates/ui-egui/src/i18n/de.rs new file mode 100644 index 0000000..d797baa --- /dev/null +++ b/crates/ui-egui/src/i18n/de.rs @@ -0,0 +1,274 @@ +//! German translations for the interface strings in i18n.rs. Labels follow the wording German +//! spreadsheet users know; anything absent falls back to English. + +pub(super) fn translate(text: &str) -> Option<&'static str> { + Some(match text { + "Paste" => "Einfügen", + "Paste Values" => "Werte einfügen", + "Paste Formulas" => "Formeln einfügen", + "Paste Formatting" => "Formatierung einfügen", + "Transpose" => "Transponieren", + "Paste Link" => "Verknüpfung einfügen", + "Paste Special…" => "Inhalte einfügen…", + "Cut (⌘X)" => "Ausschneiden (⌘X)", + "Copy (⌘C)" => "Kopieren (⌘C)", + "Format Painter (double-click to keep it on)" => "Format übertragen (Doppelklick zum Beibehalten)", + "Bold (⌘B)" => "Fett (⌘B)", + "Italic (⌘I)" => "Kursiv (⌘I)", + "Underline (⌘U)" => "Unterstrichen (⌘U)", + "Double Underline" => "Doppelt unterstrichen", + "Strikethrough" => "Durchgestrichen", + "Underline" => "Unterstrichen", + "Increase Font Size" => "Schriftgrad vergrößern", + "Decrease Font Size" => "Schriftgrad verkleinern", + "Wrap Text" => "Zeilenumbruch", + "Merge & Center" => "Verbinden und zentrieren", + "Merge Cells" => "Zellen verbinden", + "Merge Across" => "Zeilenweise verbinden", + "Unmerge Cells" => "Zellverbindung aufheben", + "Fill Color" => "Füllfarbe", + "Font Color" => "Schriftfarbe", + "Borders" => "Rahmen", + "Top\nAlign" => "Oben\nausrichten", + "Middle\nAlign" => "Vertikal\nzentrieren", + "Bottom\nAlign" => "Unten\nausrichten", + "Left" => "Links", + "Right" => "Rechts", + "Center" => "Zentriert", + "Horizontal" => "Horizontal", + "Vertical Text" => "Vertikaler Text", + "Decrease Indent" => "Einzug verkleinern", + "Increase Indent" => "Einzug vergrößern", + "Orientation" => "Ausrichtung", + "Angle Counterclockwise" => "Gegen den Uhrzeigersinn neigen", + "Angle Clockwise" => "Im Uhrzeigersinn neigen", + "Rotate Text Up" => "Text nach oben drehen", + "Rotate Text Down" => "Text nach unten drehen", + "Accounting Number Format" => "Buchhaltungszahlenformat", + "Percent Style" => "Prozentformat", + "Comma Style" => "Kommaformat", + "Increase Decimal" => "Dezimalstelle hinzufügen", + "Decrease Decimal" => "Dezimalstelle entfernen", + "More Accounting Formats…" => "Weitere Buchhaltungsformate…", + "Conditional\nFormatting" => "Bedingte\nFormatierung", + "Conditional Formatting" => "Bedingte Formatierung", + "Format\nas Table" => "Als Tabelle\nformatieren", + "Cell\nStyles" => "Zell-\nformate", + "Format Cells…" => "Zellen formatieren…", + "Format Cell Alignment…" => "Zellausrichtung…", + "Format\nPane" => "Format-\nbereich", + "Format" => "Format", + "Insert" => "Einfügen", + "Insert Cells…" => "Zellen einfügen…", + "Delete" => "Löschen", + "Delete Cells…" => "Zellen löschen…", + "Insert Sheet Rows" => "Blattzeilen einfügen", + "Insert Sheet Columns" => "Blattspalten einfügen", + "Delete Sheet Rows" => "Blattzeilen löschen", + "Delete Sheet Columns" => "Blattspalten löschen", + "Row Height…" => "Zeilenhöhe…", + "AutoFit Row Height" => "Zeilenhöhe automatisch anpassen", + "Column Width…" => "Spaltenbreite…", + "AutoFit Column Width" => "Spaltenbreite automatisch anpassen", + "Default Width…" => "Standardbreite…", + "Hide Rows" => "Zeilen ausblenden", + "Unhide Rows" => "Zeilen einblenden", + "Hide Columns" => "Spalten ausblenden", + "Unhide Columns" => "Spalten einblenden", + "Lock Cell" => "Zelle sperren", + "Clear" => "Löschen", + "Clear All" => "Alles löschen", + "Clear Formats" => "Formate löschen", + "Clear Contents" => "Inhalte löschen", + "Clear Comments and Notes" => "Kommentare und Notizen löschen", + "Clear Hyperlinks" => "Hyperlinks löschen", + "AutoSum" => "AutoSumme", + "AutoSum (⌘⇧T)" => "AutoSumme (⌘⇧T)", + "Flash\nFill" => "Blitz-\nvorschau", + "Flash Fill" => "Blitzvorschau", + "Fill" => "Ausfüllen", + "Clear Print Area" => "Druckbereich aufheben", + "Find &\nSelect" => "Suchen und\nAuswählen", + "Find…" => "Suchen…", + "Replace…" => "Ersetzen…", + "Go To…" => "Gehe zu…", + "Go To Special…" => "Inhalte auswählen…", + "PivotTable" => "PivotTable", + "Recommended\nCharts" => "Empfohlene\nDiagramme", + "Table" => "Tabelle", + "Pictures" => "Bilder", + "Shapes" => "Formen", + "Icons" => "Symbole", + "Text\nBox" => "Text-\nfeld", + "Comment" => "Kommentar", + "New\nComment" => "Neuer\nKommentar", + "New Note" => "Neue Notiz", + "Show/Hide Note" => "Notiz anzeigen/ausblenden", + "Header &\nFooter" => "Kopf- und\nFußzeile", + "Text to\nColumns" => "Text in\nSpalten", + "Link" => "Link", + "Symbol" => "Symbol", + "Sparklines" => "Sparklines", + "Header & Footer" => "Kopf- und Fußzeile", + "Draw with ink" => "Mit Freihand zeichnen", + "Eraser" => "Radierer", + "Ink to\nShape" => "Freihand in\nForm umwandeln", + "Select\nObjects" => "Objekte\nauswählen", + "Themes" => "Designs", + "Margins" => "Seitenränder", + "Size" => "Größe", + "Print\nArea" => "Druck-\nbereich", + "Set Print Area" => "Druckbereich festlegen", + "Breaks" => "Umbrüche", + "Insert Page Break" => "Seitenumbruch einfügen", + "Remove Page Break" => "Seitenumbruch entfernen", + "Reset All Page Breaks" => "Alle Seitenumbrüche zurücksetzen", + "Print\nTitles" => "Druck-\ntitel", + "Narrow" => "Schmal", + "Wide" => "Breit", + "Normal" => "Normal", + "Portrait" => "Hochformat", + "Landscape" => "Querformat", + "Custom Margins…" => "Benutzerdefinierte Seitenränder…", + "Insert\nFunction" => "Funktion\neinfügen", + "Name\nManager" => "Namens-\nmanager", + "Define Name" => "Namen definieren", + "Use in Formula" => "In Formel verwenden", + "Create from Selection" => "Aus Auswahl erstellen", + "Trace Precedents" => "Spur zum Vorgänger", + "Trace Dependents" => "Spur zum Nachfolger", + "Remove Arrows" => "Pfeile entfernen", + "Show Formulas" => "Formeln anzeigen", + "Error Checking" => "Fehlerüberprüfung", + "Evaluate Formula" => "Formel auswerten", + "Watch Window" => "Überwachungsfenster", + "Calculation\nOptions" => "Berechnungs-\noptionen", + "Calculate Now" => "Jetzt berechnen", + "Calculate Sheet" => "Blatt berechnen", + "Automatic" => "Automatisch", + "Automatic Except for Data Tables" => "Automatisch, außer für Datentabellen", + "Manual" => "Manuell", + "Sort &\nFilter" => "Sortieren und\nFiltern", + "Sort" => "Sortieren", + "Sort A to Z" => "Von A bis Z sortieren", + "Sort Z to A" => "Von Z bis A sortieren", + "Custom Sort…" => "Benutzerdefiniertes Sortieren…", + "Filter" => "Filtern", + "Reapply" => "Erneut anwenden", + "Get Data\n(Text/CSV)" => "Daten abrufen\n(Text/CSV)", + "Remove\nDuplicates" => "Duplikate\nentfernen", + "Data\nValidation" => "Daten-\nüberprüfung", + "Data Validation" => "Datenüberprüfung", + "Data Validation…" => "Datenüberprüfung…", + "Circle Invalid Data" => "Ungültige Daten einkreisen", + "Clear Validation" => "Datenüberprüfung löschen", + "Group" => "Gruppieren", + "Ungroup" => "Gruppierung aufheben", + "Subtotal" => "Teilergebnis", + "What-If\nAnalysis" => "Was-wäre-wenn-\nAnalyse", + "Data Table…" => "Datentabelle…", + "Scenario Manager…" => "Szenario-Manager…", + "Goal Seek…" => "Zielwertsuche…", + "Spelling" => "Rechtschreibung", + "Check\nAccessibility" => "Barrierefreiheit\nprüfen", + "Threaded Comments" => "Threadkommentare", + "Show\nComments" => "Kommentare\nanzeigen", + "Protect\nWorkbook" => "Arbeitsmappe\nschützen", + "Unprotect\nSheet" => "Blattschutz\naufheben", + "Protect Sheet…" => "Blatt schützen…", + "Comments" => "Kommentare", + "Notes" => "Notizen", + "Page Break\nPreview" => "Seitenumbruch-\nvorschau", + "Page\nLayout" => "Seiten-\nlayout", + "Formula Bar" => "Formelleiste", + "Gridlines" => "Gitternetzlinien", + "Headings" => "Überschriften", + "Zoom" => "Zoom", + "100%" => "100%", + "Zoom to\nSelection" => "Zoom auf die\nAuswahl", + "Freeze\nPanes" => "Fenster\nfixieren", + "Freeze Panes" => "Fenster fixieren", + "Freeze Top Row" => "Oberste Zeile fixieren", + "Freeze First Column" => "Erste Spalte fixieren", + "Unfreeze Panes" => "Fixierung aufheben", + "Dark Mode" => "Dunkler Modus", + "Interface language" => "Anzeigesprache", + "Command\nPalette" => "Befehls-\npalette", + "Agent\nControl" => "Agenten-\nsteuerung", + "Action\nJournal" => "Aktions-\nprotokoll", + "About\nGridCraft" => "Über\nGridCraft", + "Share" => "Teilen", + "Add Chart\nElement" => "Diagrammelement\nhinzufügen", + "Change\nChart Type" => "Diagrammtyp\nändern", + "Switch\nRow/Column" => "Zeile/Spalte\nwechseln", + "Data Labels: None" => "Datenbeschriftungen: Keine", + "Data Labels: Show" => "Datenbeschriftungen: Anzeigen", + "Legend: None" => "Legende: Keine", + "Legend: Bottom" => "Legende: Unten", + "Legend: Top" => "Legende: Oben", + "Legend: Right" => "Legende: Rechts", + "Gridlines: None" => "Gitternetzlinien: Keine", + "Gridlines: Show" => "Gitternetzlinien: Anzeigen", + "Chart\nTitle" => "Diagramm-\ntitel", + "Delete\nChart" => "Diagramm\nlöschen", + "Convert\nto Range" => "In Bereich\nkonvertieren", + "Table\nStyles" => "Tabellen-\nformate", + "Top 10 Items" => "Obere 10 Elemente", + "Top 10%" => "Obere 10 %", + "Bottom 10 Items" => "Untere 10 Elemente", + "Bottom 10%" => "Untere 10 %", + "Above Average" => "Überdurchschnittlich", + "Below Average" => "Unterdurchschnittlich", + "Clear Rules from Entire Sheet" => "Regeln im gesamten Blatt löschen", + "Clear Rules from Selected Cells" => "Regeln in den ausgewählten Zellen löschen", + "Constants" => "Konstanten", + "Data Bars" => "Datenbalken", + "Color Scales" => "Farbskalen", + "Icon Sets" => "Symbolsätze", + "Home" => "Start", + "Draw" => "Zeichnen", + "Page Layout" => "Seitenlayout", + "Formulas" => "Formeln", + "Data" => "Daten", + "Review" => "Überprüfen", + "View" => "Ansicht", + "Automate" => "Automatisieren", + "Table Design" => "Tabellenentwurf", + "Chart Design" => "Diagrammentwurf", + "Ready" => "Bereit", + "Enter" => "Eingeben", + "Edit" => "Bearbeiten", + "Point" => "Zeigen", + "Calculate" => "Berechnen", + "Select destination and press Enter or choose Paste" => "Ziel auswählen und die Eingabetaste drücken oder „Einfügen“ wählen", + "General" => "Standard", + "Number" => "Zahl", + "Currency" => "Währung", + "Accounting" => "Buchhaltung", + "Date" => "Datum", + "Time" => "Uhrzeit", + "Percentage" => "Prozentsatz", + "Fraction" => "Bruch", + "Scientific" => "Wissenschaftlich", + "Text" => "Text", + "Special" => "Sonderformat", + "Custom" => "Benutzerdefiniert", + "Short Date" => "Kurzes Datum", + "Long Date" => "Langes Datum", + "More Number Formats…" => "Weitere Zahlenformate…", + "Financial" => "Finanzmathematik", + "Logical" => "Logisch", + "Date &\nTime" => "Datum &\nUhrzeit", + "Lookup &\nReference" => "Nachschlagen &\nVerweisen", + "Math &\nTrig" => "Mathematik &\nTrigonometrie", + "More\nFunctions" => "Weitere\nFunktionen", + "Page Layout|Orientation" => "Seitenausrichtung", + "Display theme" => "Anzeigedesign", + "System" => "System", + "Light" => "Hell", + "Dark" => "Dunkel", + "Sheet Options|View" => "Anzeige", + "Print" => "Drucken", + _ => return None, + }) +} diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs index 219e2d2..d3d9a15 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::De] { assert_eq!(HanOrder::of(l), HanOrder::Chinese, "{l:?} shares the default order (no font rebuild)"); } } diff --git a/crates/ui-egui/tests/interaction.rs b/crates/ui-egui/tests/interaction.rs index 84f20a6..d166f4a 100644 --- a/crates/ui-egui/tests/interaction.rs +++ b/crates/ui-egui/tests/interaction.rs @@ -200,7 +200,7 @@ fn language_switch_translates_the_ribbon_and_persists() { assert!(ids.contains(&id), "engine.commands lists {id}"); } for (language, expected) in - [(Language::Zh, "数据"), (Language::Ja, "データ"), (Language::Ko, "데이터"), (Language::Ru, "Данные"), (Language::PtBr, "Dados")] + [(Language::Zh, "数据"), (Language::Ja, "データ"), (Language::Ko, "데이터"), (Language::Ru, "Данные"), (Language::PtBr, "Dados"), (Language::De, "Daten")] { h.state_mut().run("app.language.set", json!({"language": language.code()})).unwrap(); h.run_steps(2); // every locale renders the ribbon without panicking diff --git a/docs/localization-parity.md b/docs/localization-parity.md index 3a8a991..3b14bf5 100644 --- a/docs/localization-parity.md +++ b/docs/localization-parity.md @@ -1,6 +1,6 @@ # Localization parity -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-10 · **Change:** minor (re-measured after the 10-10 language and font-fallback merges) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4 +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** minor (German ribbon catalog added) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4 Per-language status against Excel, which ships its UI in dozens of languages (Windows language packs cover 100+), with localized function names, argument separators, number and date formats, @@ -10,9 +10,10 @@ right-to-left sheets and full complex-script support. Part of ## Where we are (measured from source on `main`, after the 2026-10-10 merges) - **A first catalog exists, for the ribbon only.** `crates/ui-egui/src/i18n.rs` (merged 10-10 in - #31, #104, #82) adds an interface-language preference that defaults to the system language - (closes #20) and a View-tab switcher, with English-keyed lookup tables: Japanese 268 labels, - Simplified Chinese, Korean, Russian and Brazilian Portuguese 266 each. The tables are used by + #31, #104, #82; German added 10-11) adds an interface-language preference that defaults to the + system language (closes #20) and a View-tab switcher, with English-keyed lookup tables: + Japanese 268 labels, Simplified Chinese, Korean, Russian, Brazilian Portuguese and German 266 + each. The tables are used by the ribbon, sheet tabs and widgets (`ribbon.rs`, `tabs.rs`, `widgets.rs`); dialogs, menus, context menus, task panes, status messages, command labels and function descriptions are still English literals. Untranslated labels fall back to English. @@ -47,11 +48,11 @@ separators, number/date formats, machine translation with agent review, and scri | Portuguese | pt-BR | 266 (~12%) | no | Latin: yes | no | menus only | 4–8 h | | Indonesian | id | 0 (0%) | no | Latin: yes | no | none | 5–8 h | | Japanese | ja | 268 (~12%) | no | CJK system-font fallback; IME unverified; no furigana display | no | menus only | 8–12 h | -| German | de | 0 (0%; PR #163 open) | no | Latin: yes | no | none | 6–10 h | +| German | de | 266 (~12%) | no | Latin: yes | no | menus only | 4–8 h | | Korean | ko | 266 (~12%) | no | Hangul via the CJK fallback; IME unverified | no | menus only | 6–10 h | | Vietnamese | vi | 0 (0%) | no | Latin with stacked diacritics: unverified | no | none | 6–10 h | -Other shipped languages: Russian (ru), 266 labels (~12%), menus only. +Other shipped languages: Russian (ru) and German (de), 266 labels each (~12%), menus only. **Total:** catalog work 10–20 h + languages 92–144 h ≈ **100–165 h**. Parallelizes almost fully once the catalog covers dialogs and commands. **Needs a human:** native-speaker review of every @@ -73,5 +74,6 @@ language (none so far) and of localized function names. | Date | Change | Summary | |---|---|---| +| 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-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 |