From b993940bfcea1cd97d9eb0eb9ced188142912d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Marcin=20Brzuchalski?= Date: Mon, 12 Oct 2026 08:50:30 +0200 Subject: [PATCH 1/6] i18n: add Polish (pl) interface A 381-entry table covering every key of the reference (Japanese) table, registered as a Pl variant in the Language enum (name "Polski", code pl, system tags pl-* / pl_*). Written from the meaning of the English labels; no other product's localisation was consulted. Formulas and number formats keep the canonical en-US locale; a test pins it. Co-Authored-By: Claude Opus 5.5 --- ROADMAP.md | 2 +- crates/ui-egui/examples/snapshot.rs | 2 +- crates/ui-egui/src/control.rs | 6 +- crates/ui-egui/src/i18n.rs | 16 +- crates/ui-egui/src/i18n/pl.rs | 397 +++++++++++++++++++++++++ crates/ui-egui/src/lib.rs | 2 +- crates/ui-egui/src/theme.rs | 2 +- crates/ui-egui/tests/formula_locale.rs | 16 + crates/ui-egui/tests/interaction.rs | 1 + docs/localization-parity.md | 6 +- 10 files changed, 442 insertions(+), 8 deletions(-) create mode 100644 crates/ui-egui/src/i18n/pl.rs diff --git a/ROADMAP.md b/ROADMAP.md index fbe48d35d1..baf5d80297 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -111,7 +111,7 @@ Localized German formulas and numbers are in PR #163. Detail: [localization-pari | Korean | ko | menus only | ~12% | | Vietnamese | vi | none | 0% | -Other shipped languages: 2 (Russian and German, menus only, ~12%). +Other shipped languages: 3 (Russian, German and Polish, menus only, ~12%). ## Upcoming diff --git a/crates/ui-egui/examples/snapshot.rs b/crates/ui-egui/examples/snapshot.rs index a171199a1c..2d8b4b7ceb 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|cs] +//! [--size 1440x900] [--scale 2] [--cmd 'id={json}']... [--tab Insert] [--dark] [--language en|zh|ja|ko|ru|pt|cs|pl] //! [--dialog 'name={json}'] [--hf-edit header|footer] out.png` use gridcraft_engine::Session; diff --git a/crates/ui-egui/src/control.rs b/crates/ui-egui/src/control.rs index 6f6280ef73..c7d974e6b6 100644 --- a/crates/ui-egui/src/control.rs +++ b/crates/ui-egui/src/control.rs @@ -184,7 +184,11 @@ 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\"|\"cs\"} (alias: code)"), + ( + "app.language.set", + "Interface Language", + "{language: \"en\"|\"zh\"|\"ja\"|\"ko\"|\"ru\"|\"pt\"|\"de\"|\"cs\"|\"pl\"} (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 406cc3dd51..9a52b21475 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -14,6 +14,7 @@ mod de; mod es; mod fr; mod ko; +mod pl; mod pt; mod ru; mod zh; @@ -33,10 +34,11 @@ pub enum Language { De, Cs, Fr, + Pl, } impl Language { - pub const ALL: [Self; 10] = [Self::En, Self::Es, Self::Zh, Self::Ja, Self::Ko, Self::Ru, Self::PtBr, Self::De, Self::Cs, Self::Fr]; + pub const ALL: [Self; 11] = [Self::En, Self::Es, Self::Zh, Self::Ja, Self::Ko, Self::Ru, Self::PtBr, Self::De, Self::Cs, Self::Fr, Self::Pl]; /// The language's own name, shown in the switcher. pub fn name(self) -> &'static str { @@ -51,6 +53,7 @@ impl Language { Self::De => "Deutsch", Self::Cs => "Čeština", Self::Fr => "Français", + Self::Pl => "Polski", } } @@ -67,6 +70,7 @@ impl Language { Self::De => "de", Self::Cs => "cs", Self::Fr => "fr", + Self::Pl => "pl", } } @@ -86,6 +90,7 @@ impl Language { "de" => Some(Self::De), "cs" => Some(Self::Cs), "fr" => Some(Self::Fr), + "pl" => Some(Self::Pl), _ => None, } } @@ -123,6 +128,7 @@ impl Language { Self::De => de::translate(text), Self::Cs => cs::translate(text), Self::Fr => fr::translate(text), + Self::Pl => pl::translate(text), }; translated.unwrap_or_else(|| text.rsplit_once('|').map_or(text, |(_, label)| label)) } @@ -587,6 +593,7 @@ mod tests { 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!(fr::translate(english).is_some_and(|text| !text.is_empty()), "missing French: {english}"); + assert!(pl::translate(english).is_some_and(|text| !text.is_empty()), "missing Polish: {english}"); } assert_eq!(Language::Ja.tr("Data"), "データ"); assert_eq!(Language::Zh.tr("Data"), "数据"); @@ -600,6 +607,8 @@ mod tests { assert_eq!(Language::De.tr("Sheet1!A1"), "Sheet1!A1"); assert_eq!(Language::Fr.tr("Data"), "Données"); assert_eq!(Language::Fr.tr("Sheet1!A1"), "Sheet1!A1"); + assert_eq!(Language::Pl.tr("Data"), "Dane"); + assert_eq!(Language::Pl.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"); @@ -616,6 +625,8 @@ mod tests { 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::Pl.tr("Page Layout|Orientation"), "Orientacja"); + assert_eq!(Language::Pl.tr("Orientation"), "Kierunek tekstu"); assert_eq!(Language::parse("xx"), None); } @@ -654,6 +665,9 @@ mod tests { assert_eq!(Language::from_tag("fr-FR"), Some(Language::Fr)); assert_eq!(Language::from_tag("fr_CA"), Some(Language::Fr)); assert_eq!(Language::parse("fr"), Some(Language::Fr)); + assert_eq!(Language::from_tag("pl-PL"), Some(Language::Pl)); + assert_eq!(Language::from_tag("pl_PL.UTF-8"), Some(Language::Pl)); + assert_eq!(Language::parse("pl"), Some(Language::Pl)); } #[test] diff --git a/crates/ui-egui/src/i18n/pl.rs b/crates/ui-egui/src/i18n/pl.rs new file mode 100644 index 0000000000..332f4d700c --- /dev/null +++ b/crates/ui-egui/src/i18n/pl.rs @@ -0,0 +1,397 @@ +//! Polish translations for the interface strings in i18n.rs. Written from the meaning of the +//! English labels in ordinary Polish computing vocabulary; anything absent falls back to English. + +pub(super) fn translate(text: &str) -> Option<&'static str> { + Some(match text { + "Paste" => "Wklej", + "Paste Values" => "Wklej wartości", + "Paste Formulas" => "Wklej formuły", + "Paste Formatting" => "Wklej formatowanie", + "Transpose" => "Transponuj", + "Paste Link" => "Wklej łącze", + "Paste Special…" => "Wklej specjalnie…", + "Insert Copied Cells" => "Wstaw skopiowane komórki", + "Insert Copied Cells…" => "Wstaw skopiowane komórki…", + "Insert Cut Cells" => "Wstaw wycięte komórki", + "Insert Paste" => "Wstaw i wklej", + "Cut (⌘X)" => "Wytnij (⌘X)", + "Copy (⌘C)" => "Kopiuj (⌘C)", + "Format Painter (double-click to keep it on)" => "Malarz formatów (kliknij dwukrotnie, aby pozostał włączony)", + "Bold (⌘B)" => "Pogrubienie (⌘B)", + "Italic (⌘I)" => "Kursywa (⌘I)", + "Underline (⌘U)" => "Podkreślenie (⌘U)", + "Double Underline" => "Podwójne podkreślenie", + "Strikethrough" => "Przekreślenie", + "Underline" => "Podkreślenie", + "Increase Font Size" => "Zwiększ rozmiar czcionki", + "Decrease Font Size" => "Zmniejsz rozmiar czcionki", + "Wrap Text" => "Zawijaj tekst", + "Merge & Center" => "Scal i wyśrodkuj", + "Merge Cells" => "Scal komórki", + "Merge Across" => "Scal wierszami", + "Unmerge Cells" => "Rozdziel komórki", + "Fill Color" => "Kolor wypełnienia", + "Font Color" => "Kolor czcionki", + "Borders" => "Obramowania", + "Top\nAlign" => "Do\ngóry", + "Middle\nAlign" => "Do\nśrodka", + "Bottom\nAlign" => "Do\ndołu", + "Left" => "Do lewej", + "Right" => "Do prawej", + "Center" => "Wyśrodkuj", + "Horizontal" => "Poziomo", + "Vertical Text" => "Tekst pionowy", + "Decrease Indent" => "Zmniejsz wcięcie", + "Increase Indent" => "Zwiększ wcięcie", + "Orientation" => "Kierunek tekstu", + "Page Layout|Orientation" => "Orientacja", + "Angle Counterclockwise" => "Obróć w lewo pod kątem", + "Angle Clockwise" => "Obróć w prawo pod kątem", + "Rotate Text Up" => "Obróć tekst w górę", + "Rotate Text Down" => "Obróć tekst w dół", + "Accounting Number Format" => "Format księgowy", + "Percent Style" => "Styl procentowy", + "Comma Style" => "Styl z separatorem tysięcy", + "Increase Decimal" => "Zwiększ liczbę miejsc dziesiętnych", + "Decrease Decimal" => "Zmniejsz liczbę miejsc dziesiętnych", + "More Accounting Formats…" => "Więcej formatów księgowych…", + "Conditional\nFormatting" => "Formatowanie\nwarunkowe", + "Conditional Formatting" => "Formatowanie warunkowe", + "Format\nas Table" => "Formatuj\njako tabelę", + "Cell\nStyles" => "Style\nkomórek", + "Format Cells…" => "Formatuj komórki…", + "Format Cell Alignment…" => "Wyrównanie komórek…", + "Format\nPane" => "Panel\nformatowania", + "Format" => "Format", + "Insert" => "Wstaw", + "Insert Cells…" => "Wstaw komórki…", + "Delete" => "Usuń", + "Delete Cells…" => "Usuń komórki…", + "Insert Sheet Rows" => "Wstaw wiersze arkusza", + "Insert Sheet Columns" => "Wstaw kolumny arkusza", + "Delete Sheet Rows" => "Usuń wiersze arkusza", + "Delete Sheet Columns" => "Usuń kolumny arkusza", + "Row Height…" => "Wysokość wiersza…", + "AutoFit Row Height" => "Dopasuj wysokość wiersza", + "Column Width…" => "Szerokość kolumny…", + "AutoFit Column Width" => "Dopasuj szerokość kolumny", + "Default Width…" => "Szerokość domyślna…", + "Hide Rows" => "Ukryj wiersze", + "Unhide Rows" => "Odkryj wiersze", + "Hide Columns" => "Ukryj kolumny", + "Unhide Columns" => "Odkryj kolumny", + "Lock Cell" => "Zablokuj komórkę", + "Clear" => "Wyczyść", + "Clear All" => "Wyczyść wszystko", + "Clear Formats" => "Wyczyść formaty", + "Clear Contents" => "Wyczyść zawartość", + "Clear Comments and Notes" => "Wyczyść komentarze i notatki", + "Clear Hyperlinks" => "Wyczyść hiperłącza", + "AutoSum" => "Autosumowanie", + "AutoSum (⌘⇧T)" => "Autosumowanie (⌘⇧T)", + "Flash\nFill" => "Wypełnianie\nbłyskawiczne", + "Flash Fill" => "Wypełnianie błyskawiczne", + "Fill" => "Wypełnij", + "Clear Print Area" => "Wyczyść obszar wydruku", + "Find &\nSelect" => "Znajdź\ni zaznacz", + "Find…" => "Znajdź…", + "Replace…" => "Zamień…", + "Go To…" => "Przejdź do…", + "Go To Special…" => "Przejdź do — specjalnie…", + "PivotTable" => "Tabela przestawna", + "Recommended\nCharts" => "Polecane\nwykresy", + "Table" => "Tabela", + "Pictures" => "Obrazy", + "Shapes" => "Kształty", + "Icons" => "Ikony", + "Text\nBox" => "Pole\ntekstowe", + "Comment" => "Komentarz", + "New\nComment" => "Nowy\nkomentarz", + "New Note" => "Nowa notatka", + "Show/Hide Note" => "Pokaż/ukryj notatkę", + "Header &\nFooter" => "Nagłówek\ni stopka", + "Text to\nColumns" => "Tekst jako\nkolumny", + "Link" => "Łącze", + "Symbol" => "Symbol", + "Sparklines" => "Wykresy przebiegu w czasie", + "Checkbox" => "Pole wyboru", + "Insert checkboxes in the selected cells" => "Wstaw pola wyboru w zaznaczonych komórkach", + "Header & Footer" => "Nagłówek i stopka", + "Draw with ink" => "Rysuj odręcznie", + "Eraser" => "Gumka", + "Ink to\nShape" => "Odręczne\nna kształt", + "Select\nObjects" => "Zaznacz\nobiekty", + "Themes" => "Motywy", + "Margins" => "Marginesy", + "Size" => "Rozmiar", + "Print\nArea" => "Obszar\nwydruku", + "Set Print Area" => "Ustaw obszar wydruku", + "Breaks" => "Podziały", + "Insert Page Break" => "Wstaw podział strony", + "Remove Page Break" => "Usuń podział strony", + "Reset All Page Breaks" => "Resetuj wszystkie podziały stron", + "Print\nTitles" => "Tytuły\nwydruku", + "Sheet\nRight-to-Left" => "Arkusz od prawej\ndo lewej", + "Sheet Right-to-Left" => "Arkusz od prawej do lewej", + "Narrow" => "Wąskie", + "Wide" => "Szerokie", + "Normal" => "Normalne", + "Portrait" => "Pionowa", + "Landscape" => "Pozioma", + "Custom Margins…" => "Marginesy niestandardowe…", + "Insert\nFunction" => "Wstaw\nfunkcję", + "Name\nManager" => "Menedżer\nnazw", + "Define Name" => "Definiuj nazwę", + "Use in Formula" => "Użyj w formule", + "Create from Selection" => "Utwórz z zaznaczenia", + "Trace Precedents" => "Śledź poprzedniki", + "Trace Dependents" => "Śledź zależności", + "Remove Arrows" => "Usuń strzałki", + "Show Formulas" => "Pokaż formuły", + "Error Checking" => "Sprawdzanie błędów", + "Evaluate Formula" => "Szacuj formułę", + "Watch Window" => "Okno czujki", + "Calculation\nOptions" => "Opcje\nobliczania", + "Calculate Now" => "Oblicz teraz", + "Calculate Sheet" => "Oblicz arkusz", + "Automatic" => "Automatycznie", + "Automatic Except for Data Tables" => "Automatycznie z wyjątkiem tabel danych", + "Manual" => "Ręcznie", + "Sort &\nFilter" => "Sortuj\ni filtruj", + "Sort" => "Sortuj", + "Sort A to Z" => "Sortuj od A do Z", + "Sort Z to A" => "Sortuj od Z do A", + "Custom Sort…" => "Sortowanie niestandardowe…", + "Filter" => "Filtruj", + "Reapply" => "Zastosuj ponownie", + "Get Data\n(Text/CSV)" => "Pobierz dane\n(tekst/CSV)", + "Remove\nDuplicates" => "Usuń\nduplikaty", + "Data\nValidation" => "Poprawność\ndanych", + "Data Validation" => "Poprawność danych", + "Data Validation…" => "Poprawność danych…", + "Circle Invalid Data" => "Zakreśl nieprawidłowe dane", + "Clear Validation" => "Wyczyść reguły poprawności", + "Group" => "Grupuj", + "Ungroup" => "Rozgrupuj", + "Show Detail" => "Pokaż szczegóły", + "Hide Detail" => "Ukryj szczegóły", + "Subtotal" => "Suma częściowa", + "Data\nAnalysis" => "Analiza\ndanych", + "Data Analysis" => "Analiza danych", + "Solver" => "Solver", + "What-If\nAnalysis" => "Analiza\nwarunkowa", + "Data Table…" => "Tabela danych…", + "Scenario Manager…" => "Menedżer scenariuszy…", + "Goal Seek…" => "Szukaj wyniku…", + "Forecast\nSheet" => "Arkusz\nprognozy", + "Spelling" => "Pisownia", + "Check\nAccessibility" => "Sprawdź\ndostępność", + "Threaded Comments" => "Komentarze w wątkach", + "Show\nComments" => "Pokaż\nkomentarze", + "Previous" => "Poprzedni", + "Next" => "Następny", + "Previous Comment" => "Poprzedni komentarz", + "Next Comment" => "Następny komentarz", + "Show All Notes" => "Pokaż wszystkie notatki", + "Convert to Comments" => "Konwertuj na komentarze", + "Protect\nWorkbook" => "Chroń\nskoroszyt", + "Allow Edit\nRanges" => "Zezwól na edycję\nzakresów", + "Allow Edit Ranges" => "Zezwól na edycję zakresów", + "Unprotect\nSheet" => "Nie chroń\narkusza", + "Protect Sheet…" => "Chroń arkusz…", + "Comments" => "Komentarze", + "Notes" => "Notatki", + "Page Break\nPreview" => "Podgląd\npodziału stron", + "Page\nLayout" => "Układ\nstrony", + "Formula Bar" => "Pasek formuły", + "Gridlines" => "Linie siatki", + "Headings" => "Nagłówki", + "Zoom" => "Powiększenie", + "100%" => "100%", + "Zoom to\nSelection" => "Powiększ do\nzaznaczenia", + "Freeze\nPanes" => "Zablokuj\nokienka", + "Freeze Panes" => "Zablokuj okienka", + "Freeze Top Row" => "Zablokuj górny wiersz", + "Freeze First Column" => "Zablokuj pierwszą kolumnę", + "Unfreeze Panes" => "Odblokuj okienka", + "Dark Mode" => "Tryb ciemny", + "Display theme" => "Motyw wyświetlania", + "Animations" => "Animacje", + "Glide the selection outline to the cell you move to instead of jumping there" => { + "Płynnie przesuwaj obramowanie zaznaczenia do komórki docelowej, zamiast przeskakiwać" + } + "System" => "Systemowy", + "Light" => "Jasny", + "Dark" => "Ciemny", + "Interface language" => "Język interfejsu", + "Interface size" => "Rozmiar interfejsu", + "Auto" => "Automatycznie", + "Drag to freeze columns" => "Przeciągnij, aby zablokować kolumny", + "Drag to freeze rows" => "Przeciągnij, aby zablokować wiersze", + "Command\nPalette" => "Paleta\npoleceń", + "Agent\nControl" => "Sterowanie\nprzez agenta", + "Action\nJournal" => "Dziennik\nakcji", + "About\nGridCraft" => "GridCraft —\ninformacje", + "Share" => "Udostępnij", + "Add Chart\nElement" => "Dodaj element\nwykresu", + "Change\nChart Type" => "Zmień typ\nwykresu", + "Switch\nRow/Column" => "Przełącz\nwiersz/kolumnę", + "Select\nData" => "Zaznacz\ndane", + "Data Labels: None" => "Etykiety danych: brak", + "Data Labels: Show" => "Etykiety danych: pokaż", + "Legend: None" => "Legenda: brak", + "Legend: Bottom" => "Legenda: na dole", + "Legend: Top" => "Legenda: u góry", + "Legend: Right" => "Legenda: z prawej", + "Gridlines: None" => "Linie siatki: brak", + "Gridlines: Show" => "Linie siatki: pokaż", + "Chart\nTitle" => "Tytuł\nwykresu", + "Delete\nChart" => "Usuń\nwykres", + "Convert\nto Range" => "Konwertuj\nna zakres", + "Table\nStyles" => "Style\ntabeli", + "Top 10 Items" => "10 pierwszych elementów", + "Top 10%" => "Pierwsze 10%", + "Bottom 10 Items" => "10 ostatnich elementów", + "Bottom 10%" => "Ostatnie 10%", + "Above Average" => "Powyżej średniej", + "Below Average" => "Poniżej średniej", + "Clear Rules from Entire Sheet" => "Wyczyść reguły z całego arkusza", + "Clear Rules from Selected Cells" => "Wyczyść reguły z zaznaczonych komórek", + "Constants" => "Stałe", + "Data Bars" => "Paski danych", + "Color Scales" => "Skale kolorów", + "Icon Sets" => "Zestawy ikon", + "Home" => "Narzędzia główne", + "Draw" => "Rysowanie", + "Page Layout" => "Układ strony", + "Formulas" => "Formuły", + "Data" => "Dane", + "Review" => "Recenzja", + "View" => "Widok", + "Automate" => "Automatyzacja", + "Table Design" => "Projekt tabeli", + "Chart Design" => "Projekt wykresu", + "Ready" => "Gotowy", + "Enter" => "Wprowadzanie", + "Edit" => "Edycja", + "Point" => "Wskazywanie", + "Calculate" => "Oblicz", + "Select destination and press Enter or choose Paste" => "Zaznacz miejsce docelowe i naciśnij Enter albo wybierz Wklej", + "General" => "Ogólne", + "Number" => "Liczbowe", + "Currency" => "Walutowe", + "Accounting" => "Księgowe", + "Date" => "Data", + "Time" => "Czas", + "Percentage" => "Procentowe", + "Fraction" => "Ułamkowe", + "Scientific" => "Naukowe", + "Text" => "Tekstowe", + "Special" => "Specjalne", + "Custom" => "Niestandardowe", + "Short Date" => "Data krótka", + "Long Date" => "Data długa", + "More Number Formats…" => "Więcej formatów liczb…", + "Financial" => "Finansowe", + "Logical" => "Logiczne", + "Date &\nTime" => "Data\ni czas", + "Lookup &\nReference" => "Wyszukiwanie\ni odwołania", + "Math &\nTrig" => "Matematyczne\ni trygonometryczne", + "More\nFunctions" => "Więcej\nfunkcji", + "Sheet Options|View" => "Wyświetl", + "Print" => "Drukuj", + "Click to add data" => "Kliknij, aby dodać dane", + "Add header" => "Dodaj nagłówek", + "Add footer" => "Dodaj stopkę", + "(none)" => "(brak)", + "Header" => "Nagłówek", + "Footer" => "Stopka", + "Page\nNumber" => "Numer\nstrony", + "Number\nof Pages" => "Liczba\nstron", + "Current\nDate" => "Bieżąca\ndata", + "Current\nTime" => "Bieżąca\ngodzina", + "File\nPath" => "Ścieżka\npliku", + "File\nName" => "Nazwa\npliku", + "Sheet\nName" => "Nazwa\narkusza", + "Go to\nHeader" => "Przejdź do\nnagłówka", + "Go to\nFooter" => "Przejdź do\nstopki", + "Page Number" => "Numer strony", + "Number of Pages" => "Liczba stron", + "Current Date" => "Bieżąca data", + "Current Time" => "Bieżąca godzina", + "File Path" => "Ścieżka pliku", + "File Name" => "Nazwa pliku", + "Sheet Name" => "Nazwa arkusza", + "Go to Header" => "Przejdź do nagłówka", + "Different First Page" => "Inna pierwsza strona", + "Different Odd & Even Pages" => "Inne strony parzyste i nieparzyste", + "Go to Footer" => "Przejdź do stopki", + "Ruler" => "Linijka", + "File" => "Plik", + "Back" => "Wstecz", + "New" => "Nowy", + "Open" => "Otwórz", + "Info" => "Informacje", + "Save" => "Zapisz", + "Save As" => "Zapisz jako", + "Export" => "Eksportuj", + "Close" => "Zamknij", + "Options" => "Opcje", + "Blank workbook" => "Pusty skoroszyt", + "Recent" => "Ostatnie", + "No recent workbooks yet." => "Nie ma jeszcze ostatnio używanych skoroszytów.", + "More workbooks" => "Więcej skoroszytów", + "Browse…" => "Przeglądaj…", + "Not saved yet" => "Jeszcze nie zapisano", + "Location" => "Lokalizacja", + "Sheets" => "Arkusze", + "Protect Workbook" => "Chroń skoroszyt", + "Sheet structure is locked: sheets can't be added, moved, renamed or deleted." => { + "Struktura arkuszy jest zablokowana: arkuszy nie można dodawać, przenosić ani usuwać, ani zmieniać ich nazw." + } + "Lock the sheet structure so sheets can't be added, moved, renamed or deleted." => { + "Zablokuj strukturę arkuszy, aby nie można było ich dodawać, przenosić ani usuwać, ani zmieniać ich nazw." + } + "Properties" => "Właściwości", + "Title" => "Tytuł", + "Subject" => "Temat", + "Author" => "Autor", + "Company" => "Firma", + "Keywords" => "Słowa kluczowe", + "Description" => "Opis", + "No workbook is open." => "Żaden skoroszyt nie jest otwarty.", + "Save as type" => "Zapisz jako typ", + "Excel Workbook (*.xlsx)" => "Skoroszyt programu Excel (*.xlsx)", + "CSV UTF-8 (Comma delimited) (*.csv)" => "CSV UTF-8 (rozdzielany przecinkami) (*.csv)", + "Web Page (*.html)" => "Strona internetowa (*.html)", + "PDF (*.pdf)" => "PDF (*.pdf)", + "Download" => "Pobierz", + "A CSV file, web page or PDF is a copy: the workbook keeps its own file." => { + "Plik CSV, strona internetowa i PDF to kopie: skoroszyt zachowuje własny plik." + } + "Save the workbook under another name or in another folder." => "Zapisz skoroszyt pod inną nazwą lub w innym folderze.", + "Print Active Sheets" => "Drukuj aktywne arkusze", + "Print Entire Workbook" => "Drukuj cały skoroszyt", + "Fit Sheet on One Page" => "Dopasuj arkusz do jednej strony", + "Page Setup…" => "Ustawienia strony…", + "Page {page} of {pages}" => "Strona {page} z {pages}", + "Nothing to print." => "Nie ma nic do wydrukowania.", + "Previous page" => "Poprzednia strona", + "Next page" => "Następna strona", + "Create PDF" => "Utwórz plik PDF", + "Save the sheet as a PDF document that keeps its page layout." => "Zapisz arkusz jako dokument PDF z zachowaniem układu strony.", + "Change File Type" => "Zmień typ pliku", + "CSV (Comma delimited)" => "CSV (rozdzielany przecinkami)", + "Save the active sheet as comma-separated text." => "Zapisz aktywny arkusz jako tekst rozdzielany przecinkami.", + "Web Page (.html)" => "Strona internetowa (.html)", + "Save the workbook as a web page." => "Zapisz skoroszyt jako stronę internetową.", + "Options|General" => "Ogólne", + "Options|Save" => "Zapisywanie", + "Show formula bar" => "Pokaż pasek formuły", + "Save .xlsx workbooks automatically after every change" => "Automatycznie zapisuj skoroszyty .xlsx po każdej zmianie", + "Workbook calculation" => "Obliczanie skoroszytu", + "Enable iterative calculation" => "Włącz obliczanie iteracyjne", + "Calculation settings are saved with the workbook." => "Ustawienia obliczania są zapisywane razem ze skoroszytem.", + _ => return None, + }) +} diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index ec29139d38..c14f997ea2 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -497,7 +497,7 @@ impl SheetApp { self.ui.language = l; Ok(json!({"language": l})) } - None => Err(format!("unknown language {code:?} (use \"en\", \"es\", \"zh\", \"ja\", \"ko\", \"ru\", \"pt\" or \"cs\")")), + None => Err(format!("unknown language {code:?} (use \"en\", \"es\", \"zh\", \"ja\", \"ko\", \"ru\", \"pt\", \"cs\" or \"pl\")")), } } "app.language.english" => { diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs index 485b9a2f37..ec117ca04c 100644 --- a/crates/ui-egui/src/theme.rs +++ b/crates/ui-egui/src/theme.rs @@ -822,7 +822,7 @@ mod tests { ["sys-cjk-han-zh", "sys-cjk-han-ja", "sys-cjk-hangul", "sys-thai", "sys-hebrew", "sys-symbols", "sys-symbols-2"] ); assert_eq!(HanOrder::of(Language::Ja), HanOrder::Japanese); - for l in [Language::En, Language::Zh, Language::Ko, Language::Ru, Language::PtBr, Language::De, Language::Cs] { + for l in [Language::En, Language::Zh, Language::Ko, Language::Ru, Language::PtBr, Language::De, Language::Cs, Language::Pl] { assert_eq!(HanOrder::of(l), HanOrder::Chinese, "{l:?} shares the default order (no font rebuild)"); } } diff --git a/crates/ui-egui/tests/formula_locale.rs b/crates/ui-egui/tests/formula_locale.rs index fe21218b32..b6ee498562 100644 --- a/crates/ui-egui/tests/formula_locale.rs +++ b/crates/ui-egui/tests/formula_locale.rs @@ -201,3 +201,19 @@ fn czech_interface_retains_its_canonical_formula_fallback() { let restored: UiState = serde_json::from_str(&serde_json::to_string(&app.ui).unwrap()).unwrap(); assert_eq!(restored.language, Language::Cs); } + +#[test] +fn polish_interface_retains_its_canonical_formula_fallback() { + // The Polish table translates interface labels only: formulas keep canonical English names, + // `,` separators and `.` decimals until a Polish formula locale exists. + let mut app = app(); + app.run("app.language.set", json!({"code":"pl-PL"})).unwrap(); + assert_eq!(app.ui.language, Language::Pl); + assert_eq!(app.ui.language.tr("Formulas"), "Formuły"); + 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::Pl); +} diff --git a/crates/ui-egui/tests/interaction.rs b/crates/ui-egui/tests/interaction.rs index 7093933895..e9621fe42b 100644 --- a/crates/ui-egui/tests/interaction.rs +++ b/crates/ui-egui/tests/interaction.rs @@ -260,6 +260,7 @@ fn language_switch_translates_the_ribbon_and_persists() { (Language::Ru, "Данные"), (Language::PtBr, "Dados"), (Language::De, "Daten"), + (Language::Pl, "Dane"), ] { 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 e7d1de9f70..e373f3675e 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-11 · **Change:** minor (German ribbon catalog added) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4 +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Polish 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, @@ -55,7 +55,8 @@ separators, number/date formats, machine translation with agent review, and scri | 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) and German (de), 266 labels each (~12%), menus only. +Other shipped languages: Russian (ru) and German (de), 266 labels each (~12%), menus only; Polish +(pl), 381 labels (the full reference table), menus only, not yet reviewed by a second native speaker. **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 @@ -77,6 +78,7 @@ language (none so far) and of localized function names. | Date | Change | Summary | |---|---|---| +| 2026-10-12 | minor | Polish (pl) ribbon table (`i18n/pl.rs`, 381 labels: every key of the reference table); `pl` locale tags (pl-PL, pl_PL.UTF-8) resolve to Polish; formulas and number formats stay en-US | | 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 | From 7aad1744d747b11ce117361d76c349f778c23165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Marcin=20Brzuchalski?= Date: Mon, 12 Oct 2026 20:30:17 +0200 Subject: [PATCH 2/6] i18n(pl): translate the Format Cells dialog labels Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/i18n/pl.rs | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/crates/ui-egui/src/i18n/pl.rs b/crates/ui-egui/src/i18n/pl.rs index 332f4d700c..6bff8f797b 100644 --- a/crates/ui-egui/src/i18n/pl.rs +++ b/crates/ui-egui/src/i18n/pl.rs @@ -392,6 +392,44 @@ pub(super) fn translate(text: &str) -> Option<&'static str> { "Workbook calculation" => "Obliczanie skoroszytu", "Enable iterative calculation" => "Włącz obliczanie iteracyjne", "Calculation settings are saved with the workbook." => "Ustawienia obliczania są zapisywane razem ze skoroszytem.", + // Format Cells dialog. Its labels are still English literals in the code; these entries + // take effect once the dialog looks them up in the catalog. + "Format Cells" => "Formatuj komórki", + "Cancel" => "Anuluj", + "Alignment" => "Wyrównanie", + "Font" => "Czcionka", + "Border" => "Obramowanie", + "Protection" => "Ochrona", + "Category:" => "Kategoria:", + "Sample" => "Przykład", + "Type (custom format code):" => "Typ (własny kod formatu):", + "Vertical" => "Pionowo", + "Top" => "Góra", + "Bottom" => "Dół", + "Justify" => "Wyjustuj", + "Distributed" => "Rozłożone", + "Center Across Selection" => "Wyśrodkuj w zaznaczeniu", + "Indent" => "Wcięcie", + "Orientation (degrees)" => "Orientacja (stopnie)", + "Wrap text" => "Zawijaj tekst", + "Shrink to fit" => "Zmniejsz, aby dopasować", + "Bold" => "Pogrubienie", + "Italic" => "Kursywa", + "Subscript" => "Indeks dolny", + "Superscript" => "Indeks górny", + "None" => "Brak", + "Single" => "Pojedyncze", + "Double" => "Podwójne", + "Single Accounting" => "Pojedyncze księgowe", + "Double Accounting" => "Podwójne księgowe", + "Color:" => "Kolor:", + "Background Color:" => "Kolor tła:", + "No Color" => "Brak koloru", + "Outline" => "Kontur", + "Diagonal ↗" => "Ukośna ↗", + "Diagonal ↘" => "Ukośna ↘", + "Locked" => "Zablokowane", + "Hidden" => "Ukryte", _ => return None, }) } From 9b63bbd390656e0f1e08dafdc5305052dd2513ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Marcin=20Brzuchalski?= Date: Mon, 12 Oct 2026 20:35:48 +0200 Subject: [PATCH 3/6] i18n(pl): Format Cells Fill tab, border styles and protection note Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/i18n/pl.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/i18n/pl.rs b/crates/ui-egui/src/i18n/pl.rs index 6bff8f797b..8812e46517 100644 --- a/crates/ui-egui/src/i18n/pl.rs +++ b/crates/ui-egui/src/i18n/pl.rs @@ -392,8 +392,7 @@ pub(super) fn translate(text: &str) -> Option<&'static str> { "Workbook calculation" => "Obliczanie skoroszytu", "Enable iterative calculation" => "Włącz obliczanie iteracyjne", "Calculation settings are saved with the workbook." => "Ustawienia obliczania są zapisywane razem ze skoroszytem.", - // Format Cells dialog. Its labels are still English literals in the code; these entries - // take effect once the dialog looks them up in the catalog. + // Format Cells dialog (looked up through `Language::tr` once storytold/gridcraft#788 lands). "Format Cells" => "Formatuj komórki", "Cancel" => "Anuluj", "Alignment" => "Wyrównanie", @@ -430,6 +429,17 @@ pub(super) fn translate(text: &str) -> Option<&'static str> { "Diagonal ↘" => "Ukośna ↘", "Locked" => "Zablokowane", "Hidden" => "Ukryte", + "Format Cells|Fill" => "Wypełnienie", + "none" => "brak", + "thin" => "cienka", + "medium" => "średnia", + "thick" => "gruba", + "dashed" => "kreskowana", + "dotted" => "kropkowana", + "double" => "podwójna", + "Locking cells or hiding formulas has no effect until you protect the worksheet (Review › Protect Sheet)." => { + "Blokowanie komórek i ukrywanie formuł działa dopiero po włączeniu ochrony arkusza (Recenzja › Chroń arkusz)." + } _ => return None, }) } From bc3e46462b9d448f3a8323aabf7cc8969bd1e49f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Marcin=20Brzuchalski?= Date: Tue, 13 Oct 2026 08:08:16 +0200 Subject: [PATCH 4/6] i18n(pl): Recent list ages and Modified column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main's Backstage Recent list added ten reference keys (Modified, Not available, Just now, Yesterday and the {n} … ago forms). Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/i18n/pl.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/ui-egui/src/i18n/pl.rs b/crates/ui-egui/src/i18n/pl.rs index fe9daed4fa..376b64cfb5 100644 --- a/crates/ui-egui/src/i18n/pl.rs +++ b/crates/ui-egui/src/i18n/pl.rs @@ -398,6 +398,18 @@ pub(super) fn translate(text: &str) -> Option<&'static str> { "Bottom Margin" => "Margines dolny", "Header Margin" => "Margines nagłówka", "Footer Margin" => "Margines stopki", + // Recent list ages. Counts are never one; weeks, months and years are abbreviated because + // their Polish plural changes with the number (2 tygodnie, 5 tygodni). + "Modified" => "Zmodyfikowano", + "Not available" => "Niedostępne", + "Just now" => "Przed chwilą", + "Yesterday" => "Wczoraj", + "{n} min ago" => "{n} min temu", + "{n} h ago" => "{n} godz. temu", + "{n} days ago" => "{n} dni temu", + "{n} weeks ago" => "{n} tyg. temu", + "{n} months ago" => "{n} mies. temu", + "{n} years ago" => "{n} l. temu", // Format Cells dialog (looked up through `Language::tr` since storytold/gridcraft#788). "Format Cells" => "Formatuj komórki", "Cancel" => "Anuluj", From b33fa55e8b61a25f3b5c089d26bfb7fc6c5bd336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Marcin=20Brzuchalski?= Date: Tue, 13 Oct 2026 09:39:10 +0200 Subject: [PATCH 5/6] i18n(pl): Enable AutoComplete for cell values A reference key main added; the catalog test requires it in Polish. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/i18n/pl.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ui-egui/src/i18n/pl.rs b/crates/ui-egui/src/i18n/pl.rs index 376b64cfb5..8c5b208707 100644 --- a/crates/ui-egui/src/i18n/pl.rs +++ b/crates/ui-egui/src/i18n/pl.rs @@ -398,6 +398,7 @@ pub(super) fn translate(text: &str) -> Option<&'static str> { "Bottom Margin" => "Margines dolny", "Header Margin" => "Margines nagłówka", "Footer Margin" => "Margines stopki", + "Enable AutoComplete for cell values" => "Włącz autouzupełnianie wartości komórek", // Recent list ages. Counts are never one; weeks, months and years are abbreviated because // their Polish plural changes with the number (2 tygodnie, 5 tygodni). "Modified" => "Zmodyfikowano", From 6ab7cb777b6e5ffc3780330fcf57aa61aedfc681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Marcin=20Brzuchalski?= Date: Mon, 19 Oct 2026 19:16:51 +0200 Subject: [PATCH 6/6] i18n(pl): Top/Bottom 10 rules name values, not positions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conditional-formatting rules pick the highest or lowest values, so "10 pierwszych elementów" and "Pierwsze 10%" (first items) read wrong. They are now "10 największych wartości", "10% największych wartości", "10 najmniejszych wartości" and "10% najmniejszych wartości". Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/i18n/pl.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/src/i18n/pl.rs b/crates/ui-egui/src/i18n/pl.rs index 8c5b208707..31bd3ea241 100644 --- a/crates/ui-egui/src/i18n/pl.rs +++ b/crates/ui-egui/src/i18n/pl.rs @@ -249,10 +249,10 @@ pub(super) fn translate(text: &str) -> Option<&'static str> { "Delete\nChart" => "Usuń\nwykres", "Convert\nto Range" => "Konwertuj\nna zakres", "Table\nStyles" => "Style\ntabeli", - "Top 10 Items" => "10 pierwszych elementów", - "Top 10%" => "Pierwsze 10%", - "Bottom 10 Items" => "10 ostatnich elementów", - "Bottom 10%" => "Ostatnie 10%", + "Top 10 Items" => "10 największych wartości", + "Top 10%" => "10% największych wartości", + "Bottom 10 Items" => "10 najmniejszych wartości", + "Bottom 10%" => "10% najmniejszych wartości", "Above Average" => "Powyżej średniej", "Below Average" => "Poniżej średniej", "Clear Rules from Entire Sheet" => "Wyczyść reguły z całego arkusza",