From 6d593700249d932c05f2b734d2f6533a4b563117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Lo=C5=A1=C3=A1k?= Date: Mon, 12 Oct 2026 08:45:12 +0200 Subject: [PATCH] i18n: add Czech (cs) interface language (#167) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * i18n: add Czech (cs) interface language Czech catalog with every key of the most complete catalog (287 rows), the cs language entry with locale detection (cs-CZ, cs_CZ.UTF-8, ?lang=cs), a Czech plural rule (1 / 2-4 / other) with a unit test, and the README language list. Co-Authored-By: Claude Opus 5.5 * i18n(cs): translate "Installed · latest {latest}" (added by #195) Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Losminek <301412321+Losminek@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 Co-authored-by: Brandon Thomas --- README.md | 20 +-- crates/ui-egui/src/i18n/cs.tsv | 295 +++++++++++++++++++++++++++++++ crates/ui-egui/src/i18n/lang.rs | 30 +++- crates/ui-egui/src/i18n/mod.rs | 3 +- crates/ui-egui/src/i18n/tests.rs | 20 ++- 5 files changed, 352 insertions(+), 16 deletions(-) create mode 100644 crates/ui-egui/src/i18n/cs.tsv diff --git a/README.md b/README.md index 76f055e..b9967fa 100644 --- a/README.md +++ b/README.md @@ -84,16 +84,16 @@ The web version shows the same catalog in the browser, with the latest download ## Interface languages -The interface is in English, Brazilian Portuguese (Português (Brasil)), French (Français), German -(Deutsch), Russian (Русский), Simplified Chinese (简体中文) or Spanish (Español). **Auto**, the -default, follows the system or browser language; **Settings → Language** picks one and switches at -once, and the choice is kept between runs. German locales (`de-DE`, `de-AT`, `de-CH`, -`de_DE.UTF-8`…) select German, French ones (`fr-FR`, `fr-CA`…) French, Spanish ones (`es-ES`, -`es-MX`, `es-419`…) Spanish, and Portuguese ones (`pt-BR`, and `pt-PT` too, for now) Brazilian -Portuguese. Simplified Chinese locales (`zh-CN`, `zh-SG`, `zh-Hans`, and POSIX forms such as -`zh_CN.UTF-8`) select Chinese; Traditional Chinese ones don't. To try a language without changing -the setting, start the desktop app with `ARTCRAFT_LAUNCHER_LOCALE=pt-BR`, or add `?lang=ru` to the -web address. +The interface is in English, Brazilian Portuguese (Português (Brasil)), Czech (Čeština), French +(Français), German (Deutsch), Russian (Русский), Simplified Chinese (简体中文) or Spanish +(Español). **Auto**, the default, follows the system or browser language; **Settings → Language** +picks one and switches at once, and the choice is kept between runs. Czech locales (`cs-CZ`, +`cs_CZ.UTF-8`…) select Czech, German ones (`de-DE`, `de-AT`, `de-CH`, `de_DE.UTF-8`…) German, +French ones (`fr-FR`, `fr-CA`…) French, Spanish ones (`es-ES`, `es-MX`, `es-419`…) Spanish, and +Portuguese ones (`pt-BR`, and `pt-PT` too, for now) Brazilian Portuguese. Simplified Chinese locales +(`zh-CN`, `zh-SG`, `zh-Hans`, and POSIX forms such as `zh_CN.UTF-8`) select Chinese; Traditional +Chinese ones don't. To try a language without changing the setting, start the desktop app with +`ARTCRAFT_LAUNCHER_LOCALE=pt-BR`, or add `?lang=ru` to the web address. Chinese is drawn with the system's own Chinese font (PingFang on macOS, Microsoft YaHei on Windows, Noto Sans CJK or WenQuanYi on Linux and FreeBSD). Where there is none, and in the web diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv new file mode 100644 index 0000000..f08f86c --- /dev/null +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -0,0 +1,295 @@ +# Czech (cs) catalog for ArtCraft Launcher. +# Format: contextsourcetranslation +# Empty context = plain UI string (tl!, t). @id = command-id keyed. @plural = one|few|other. +# @msg = status/error message template. Other context = disambiguation for tr_ctx. +# AI-assisted Czech translation, translated from the meaning of the English source strings. +# Product and app names, shortcuts, units, file formats, placeholders and trailing ellipses are kept. + + Launcher Launcher + Craft App Suite Craft App Suite + Open Source Tools for Creatives, Makers, and Builders Open-source nástroje pro kreativce, tvůrce a vývojáře + Settings Nastavení + Check for updates Zkontrolovat aktualizace + Grid Mřížka + Table Tabulka + Show apps as cards Zobrazit aplikace jako karty + Show apps as a table Zobrazit aplikace v tabulce + All Vše + Updates Aktualizace + Not installed Nenainstalované + Search apps Hledat aplikace + Sort Seřadit + Suite order Pořadí v sadě + Name Název + Inspired by Inspirace + Status Stav + Installed version Nainstalovaná verze + Latest version Nejnovější verze + Newest release Nejnovější vydání + Discord Discord + GitHub GitHub + Website Web + Classic apps Klasické aplikace + Classic Klasické + New apps Nové aplikace + NEW NOVÉ + Install Nainstalovat + Update to {v} Aktualizovat na {v} + Open Otevřít + Download for {os} Stáhnout pro {os} + Download Stáhnout + Coming soon Již brzy + Checking… Kontrola… + Not available Není k dispozici + Offline Bez připojení + Downloading {done} of {total} Stahování {done} z {total} + Downloading… Stahování… + Reconnecting… Obnovování připojení… + Verifying… Ověřování… + Installing, then restarting… Instalace a restart… + Installing… Instalace… + Removing… Odebírání… + Pause download Pozastavit stahování + Cancel download Zrušit stahování + Resume download Pokračovat ve stahování + Discard download Zahodit stahování + Release notes Poznámky k vydání + Show in Finder Zobrazit ve Finderu + Show in Explorer Zobrazit v Průzkumníku + Show in Files Zobrazit v Souborech + Reinstall Znovu nainstalovat + Uninstall… Odinstalovat… + All releases Všechna vydání + ArtCraft Launcher {version} is available. Je k dispozici ArtCraft Launcher {version}. + Dismiss Skrýt + Update and restart Aktualizovat a restartovat + A new version is available. Would you like to relaunch with the updated version? Je k dispozici nová verze. Chcete aplikaci znovu spustit s aktualizovanou verzí? + Later Později + What's new Co je nového + No notes were published with this release. K tomuto vydání nebyly zveřejněny žádné poznámky. + Released {date} Vydáno {date} + Open on GitHub Otevřít na GitHubu + Close Zavřít + ArtCraft Launcher {version} ArtCraft Launcher {version} + Only when I ask Pouze na vyžádání + Automatically download and install updates Automaticky stahovat a instalovat aktualizace + Notify me when an installed app has an update Upozornit, když má nainstalovaná aplikace aktualizaci + Include pre-releases (betas and release candidates) Zahrnout předběžná vydání (beta verze a kandidáty na vydání) + Installing on Linux Instalace v Linuxu + Install new apps as Instalovat nové aplikace jako + System package (recommended) Systémový balíček (doporučeno) + Updates always use the format an app was installed with. Aktualizace vždy použijí formát, ve kterém byla aplikace nainstalována. + Running in the background Běh na pozadí + Keep running in the tray when the window is closed Po zavření okna ponechat spuštěné v oznamovací oblasti + Show the window on the current desktop Zobrazit okno na aktuální ploše + Start ArtCraft Launcher when I log in Spouštět ArtCraft Launcher po přihlášení + Community Komunita + Questions, ideas and news from the ArtCraft team Dotazy, nápady a novinky od týmu ArtCraft + Appearance Vzhled + Quit ArtCraft Launcher Ukončit ArtCraft Launcher + Done Hotovo + Uninstall {name}? Odinstalovat {name}? + The app will be moved to the Trash. Your documents and settings stay. Aplikace bude přesunuta do Koše. Vaše dokumenty a nastavení zůstanou zachovány. + Windows will remove it like any installed program. Your documents and settings stay. Windows ji odebere jako každý nainstalovaný program. Vaše dokumenty a nastavení zůstanou zachovány. + Your system's package manager will remove it, and may ask for your password. Your documents and settings stay. Odebere ji správce balíčků systému a může požádat o heslo. Vaše dokumenty a nastavení zůstanou zachovány. + The app will be removed. Your documents and settings stay. Aplikace bude odebrána. Vaše dokumenty a nastavení zůstanou zachovány. + Uninstall Odinstalovat + Cancel Zrušit + Checked {ago} Zkontrolováno {ago} + just now právě teď + yesterday včera + {month} {day}, {year} {day}. {month} {year} + Automatically Automaticky + Every Craft app Všechny aplikace Craft + Apps on this computer Aplikace v tomto počítači + Installed apps with a newer version Nainstalované aplikace s novější verzí + Apps you can install Aplikace, které lze nainstalovat + Checked {ago} · using {sources} Zkontrolováno {ago} · zdroje: {sources} + {a} and {b} {a} a {b} + Join the Discord Připojit se na Discord + Learn more Další informace + Minimize Minimalizovat + Maximize Maximalizovat + Restore Obnovit + New Nové + OK OK + Open link Otevřít odkaz + Update Aktualizovat + {name} icon Ikona aplikace {name} + Loading ArtCraft Launcher… Načítání aplikace ArtCraft Launcher… + Cancel update Zrušit aktualizaci + Stop waiting; the update can be started again later Přestat čekat; aktualizaci lze spustit znovu později + Download again Stáhnout znovu + Install cancelled Instalace zrušena + Install cancelled. The download is kept for the next try. Instalace zrušena. Stažený soubor zůstane připravený pro další pokus. + Update cancelled Aktualizace zrušena + Uninstall cancelled Odinstalace zrušena + Retry install Zkusit instalaci znovu + Retry update to {version} Zkusit aktualizaci na {version} znovu + The download is still here. It's checked again, then installed. Stažený soubor je stále k dispozici. Znovu se ověří a poté nainstaluje. + Waiting for another app… Čekání na jinou aplikaci… + Waiting for other installs… Čekání na dokončení ostatních instalací… + Installer (.msi) Instalační program (.msi) + Portable (.zip) Přenosná verze (.zip) + Disk image (.dmg) Obraz disku (.dmg) + Debian package (.deb) Balíček Debian (.deb) + RPM package (.rpm) Balíček RPM (.rpm) + Flatpak bundle Balíček Flatpak + Tarball (.tar.gz) Archiv tar (.tar.gz) +photocraft Image editing Úpravy obrázků +vectorcraft Vector illustration Vektorová ilustrace +filmcraft Video editing, color and sound Střih videa, barvy a zvuk +lightcraft Photo library and raw development Katalog fotografií a vyvolávání RAW +pdfcraft Reading, organizing and protecting PDFs Čtení, organizace a ochrana souborů PDF +effectcraft Motion graphics and visual effects Pohyblivá grafika a vizuální efekty +designcraft Page layout and publishing Sazba stránek a publikování +wordcraft Writing and document design Psaní a návrh dokumentů +deckcraft Presentations and slide shows Prezentace a promítání snímků +gridcraft Spreadsheets and data Tabulky a data +cadcraft Computer-aided design and drafting Počítačem podporované navrhování a kreslení +soundcraft Recording, editing and mixing audio Nahrávání, úpravy a mixování zvuku +month Jan 1. +month Feb 2. +month Mar 3. +month Apr 4. +month May 5. +month Jun 6. +month Jul 7. +month Aug 8. +month Sep 9. +month Oct 10. +month Nov 11. +month Dec 12. + Not checked yet Zatím nezkontrolováno + GitHub's hourly limit was reached; full details return in {min} min Byl dosažen hodinový limit GitHubu; úplné informace budou znovu dostupné za {min} min + GitHub limit hit; back in {min} min Dosažen limit GitHubu; obnovení za {min} min + Installing for {platform} Instalace pro {platform} + Downloads for {platform} Stažení pro {platform} + No downloads for this system Pro tento systém nejsou k dispozici žádné soubory ke stažení + Join the ArtCraft community on Discord Připojit se ke komunitě ArtCraft na Discordu + ArtCraft on {name} ArtCraft na {name} + Couldn't open {name}: {e} {name} nelze otevřít: {e} + Download cancelled Stahování zrušeno + Available to install K dispozici k instalaci + Available K dispozici + Installed · up to date Nainstalováno · aktuální + Update available Je k dispozici aktualizace + Installed Nainstalováno + installed nainstalováno + Not available here Zde není k dispozici + Updating… Aktualizuje se… + Update paused Aktualizace pozastavena + Download paused Stahování pozastaveno + {version} installed → {latest} released Nainstalována verze {version} → vydána {latest} + Version {version} Verze {version} + {installed} installed · latest {latest} Nainstalována verze {installed} · nejnovější {latest} + Installed · latest {latest} Nainstalováno · nejnovější {latest} + Version {version} installed Nainstalována verze {version} + Version {latest} Verze {latest} + No release published yet Zatím nebylo zveřejněno žádné vydání + ArtCraft Launcher ArtCraft Launcher + App Aplikace + Latest Nejnovější + Released Vydáno + Auto Automaticky + Language Jazyk + Ascending Vzestupně + Descending Sestupně + Click to reverse the order Kliknutím obrátíte pořadí + Click to sort by this column Kliknutím seřadíte podle tohoto sloupce + Like {name} Obdoba {name} + {done} of {total} {done} z {total} + Downloading Stahuje se + Verifying checksum… Ověřování kontrolního součtu… + Resume Pokračovat + {done} of {total} downloaded Staženo {done} z {total} + {done} downloaded Staženo {done} + Checking for updates… Kontrola aktualizací… + Downloads the new version, replaces this one and opens it again Stáhne novou verzi, nahradí tuto verzi a znovu ji otevře + Couldn't check for updates: {f} Aktualizace nelze zkontrolovat: {f} + Website (coming soon) Web (již brzy) + GitHub repository Repozitář na GitHubu + ArtCraft ArtCraft + Update 1 app Aktualizovat 1 aplikaci + Update all ({count}) Aktualizovat vše ({count}) + ⊞ Grid ⊞ Mřížka + ☰ Table ☰ Tabulka + 🔍 Search apps 🔍 Hledat aplikace + {name} {version} {name} {version} + Match system Podle systému + Dark Tmavý + Light Světlý + Full changelog Úplný přehled změn + link odkaz + No apps match Žádné aplikace neodpovídají hledání + Open ArtCraft Launcher to update. Aktualizujte otevřením aplikace ArtCraft Launcher. + {name} {v} is available Je k dispozici {name} {v} + {count} updates are available Počet dostupných aktualizací: {count} + These notes weren't fetched (GitHub's request limit was reached). They're on the release page. Tyto poznámky se nepodařilo načíst (byl dosažen limit požadavků GitHubu). Najdete je na stránce vydání. + When on, opening the launcher from the menu bar brings its window to the desktop you're on. When off, macOS switches you to the desktop the window was on. Je-li tato volba zapnutá, otevření launcheru z řádku nabídek zobrazí jeho okno na aktuální ploše. Je-li vypnutá, macOS přepne na plochu, na které se okno nacházelo. + Couldn't change the login item: {e} Položku po přihlášení nelze změnit: {e} +column App Aplikace +column Installed Nainstalováno +column Latest Nejnovější +column Released Vydáno +filter Installed Nainstalováno +@plural {n} app|{n} apps {n} aplikace|{n} aplikace|{n} aplikací +@plural {n} min ago|{n} min ago před {n} min|před {n} min|před {n} min +@plural {n} h ago|{n} h ago před {n} h|před {n} h|před {n} h +@plural {n} day ago|{n} days ago před {n} dnem|před {n} dny|před {n} dny +@msg Couldn't open {name}: {e} {name} nelze otevřít: {e} +@msg Download cancelled Stahování zrušeno +@msg {name} updated to {version} Aplikace {name} byla aktualizována na verzi {version} +@msg {name} installed: {version} Aplikace {name} byla nainstalována ve verzi {version} +@msg {name} updated to {version} (this release publishes no checksum, so only its size was checked) Aplikace {name} byla aktualizována na verzi {version} (toto vydání neobsahuje kontrolní součet, ověřena byla pouze velikost) +@msg {name} installed: {version} (this release publishes no checksum, so only its size was checked) Aplikace {name} byla nainstalována ve verzi {version} (toto vydání neobsahuje kontrolní součet, ověřena byla pouze velikost) +@msg {name} was removed Aplikace {name} byla odebrána +@msg {name} is running. Quit it, then try again. Aplikace {name} je spuštěná. Ukončete ji a zkuste to znovu. +@id app.install Nainstalovat +@id app.update Aktualizovat na {v} +@id app.open Otevřít +@id app.download Stáhnout pro {os} +@id app.reveal Zobrazit v umístění +@id app.reinstall Znovu nainstalovat +@id app.uninstall Odinstalovat… +@id app.notes Poznámky k vydání +@id app.website Web +@id app.github Repozitář na GitHubu +@id app.releases Všechna vydání +@id launcher.update Aktualizovat a restartovat +@id launcher.download Stáhnout +@id launcher.notes Co je nového +@id launcher.dismiss Zavřít +@id settings.open Nastavení +@id settings.quit Ukončit ArtCraft Launcher +@id view.grid Mřížka +@id view.table Tabulka +@id filter.all Vše +@id filter.installed Nainstalované +@id filter.updates Aktualizace +@id filter.notinstalled Nenainstalované +@id sort.suite Pořadí v sadě +@id sort.name Název +@id sort.inspired Inspirace +@id sort.status Stav +@id sort.installed Nainstalovaná verze +@id sort.latest Nejnovější verze +@id sort.released Nejnovější vydání +@id help.discord Discord +@id help.github GitHub +@id help.website Web +@id update.check Zkontrolovat aktualizace +@id update.check_auto Automaticky kontrolovat aktualizace +@id update.notify Upozornit, když má nainstalovaná aplikace aktualizaci +@id update.prereleases Zahrnout předběžná vydání (beta verze a kandidáty na vydání) +@id linux.preferred_kind Instalovat nové aplikace jako +@id tray.close_to_tray Po zavření okna ponechat spuštěné v oznamovací oblasti +@id tray.show_on_current_desktop Zobrazit okno na aktuální ploše +@id tray.login_item Spouštět ArtCraft Launcher po přihlášení +@id appearance.theme Vzhled + Show ArtCraft Launcher Zobrazit ArtCraft Launcher + Check for Updates Zkontrolovat aktualizace + Visit getartcraft.com Navštívit getartcraft.com + ArtCraft on GitHub ArtCraft na GitHubu + Join the ArtCraft Discord Připojit se na ArtCraft Discord +@plural ArtCraft Launcher · {n} update available|ArtCraft Launcher · {n} updates available ArtCraft Launcher · {n} dostupná aktualizace|ArtCraft Launcher · {n} dostupné aktualizace|ArtCraft Launcher · {n} dostupných aktualizací diff --git a/crates/ui-egui/src/i18n/lang.rs b/crates/ui-egui/src/i18n/lang.rs index 0ebea27..db2360b 100644 --- a/crates/ui-egui/src/i18n/lang.rs +++ b/crates/ui-egui/src/i18n/lang.rs @@ -7,7 +7,7 @@ use super::detect::system_lang; /// One supported UI language. pub struct LangInfo { - /// Canonical BCP 47 code (`en`, `ru`, `zh-CN`, `de`, `fr`). Also the `interfaceLanguage` value. + /// Canonical BCP 47 code (`en`, `ru`, `zh-CN`, `de`, `fr`, `cs`). Also the `interfaceLanguage` value. pub code: &'static str, /// The language's name in itself, shown in the Preferences dropdown. pub name: &'static str, @@ -18,8 +18,9 @@ pub struct LangInfo { pub cjk: bool, /// Catalog file contents (empty for the built-in English). pub source: &'static str, - /// Plural form index for a count (English, German and Spanish: 0 = one, 1 = other; Russian: 0 = one, - /// 1 = few, 2 = many; French: 0 = one for 0 and 1; Chinese: always 0). + /// Plural form index for a count (English, German and Spanish: 0 = one, 1 = other; Russian: + /// 0 = one, 1 = few, 2 = many; Czech: 0 = one, 1 = few, 2 = other; French: 0 = one for 0 and 1; + /// Chinese: always 0). pub plural: fn(u64) -> usize, /// Number of plural forms the catalog's `@plural` rows carry (checked by tests). pub plural_forms: usize, @@ -55,8 +56,17 @@ pub(super) fn plural_east_slavic(n: u64) -> usize { } } +/// Czech integer counts: 1 is `one`, 2–4 are `few`, everything else is `other`. +pub(super) fn plural_czech(n: u64) -> usize { + match n { + 1 => 0, + 2..=4 => 1, + _ => 2, + } +} + /// The registry. English first: it is the fallback and the source language. -pub static LANGUAGES: [LangInfo; 7] = [ +pub static LANGUAGES: [LangInfo; 8] = [ LangInfo { code: "en", name: "English", @@ -134,6 +144,17 @@ pub static LANGUAGES: [LangInfo; 7] = [ complete_menus: true, catalog: OnceLock::new(), }, + LangInfo { + code: "cs", + name: "Čeština", + english_name: "Czech", + cjk: false, + source: include_str!("cs.tsv"), + plural: plural_czech, + plural_forms: 3, + complete_menus: true, + catalog: OnceLock::new(), + }, ]; impl LangInfo { @@ -168,6 +189,7 @@ impl Lang { pub const FR: Lang = Lang(&LANGUAGES[4]); pub const PT_BR: Lang = Lang(&LANGUAGES[5]); pub const ES: Lang = Lang(&LANGUAGES[6]); + pub const CS: Lang = Lang(&LANGUAGES[7]); pub fn code(self) -> &'static str { self.0.code diff --git a/crates/ui-egui/src/i18n/mod.rs b/crates/ui-egui/src/i18n/mod.rs index 58a8de4..ca7e84d 100644 --- a/crates/ui-egui/src/i18n/mod.rs +++ b/crates/ui-egui/src/i18n/mod.rs @@ -4,7 +4,8 @@ //! # Adding a language //! 1. Add `xx.tsv` next to `ru.tsv` (copy its header; translate from the *meaning* of the //! English text, clean-room). -//! 2. Add one row to [`LANGUAGES`] (code, native name, catalog, plural rule). +//! 2. Add one row to [`LANGUAGES`] (code, native name, catalog, plural rule). For a complete +//! catalog, cover every UI literal and plural key; the tests enforce that claim. //! //! That is all: the Preferences dropdown, the system-locale match and the catalog tests //! (parse, placeholders, plural forms, coverage of every `tl!` literal) pick it up from the diff --git a/crates/ui-egui/src/i18n/tests.rs b/crates/ui-egui/src/i18n/tests.rs index e47b0d5..85bd2ab 100644 --- a/crates/ui-egui/src/i18n/tests.rs +++ b/crates/ui-egui/src/i18n/tests.rs @@ -4,7 +4,7 @@ use std::collections::{BTreeSet, HashSet}; use super::catalog::{Catalog, unescape}; -use super::lang::plural_east_slavic; +use super::lang::{plural_czech, plural_east_slavic}; use super::{LANGUAGES, Lang, LangInfo, lang_from_tag, message, tr, trn}; /// Each catalog's rows as (line number, context, source, translation), split exactly as the @@ -208,6 +208,20 @@ fn russian_plural_rule_works() { assert_eq!(trn(Lang::RU, 3, "{n} day ago", "{n} days ago"), "3 дня назад"); } +#[test] +fn czech_plural_rule_works() { + for n in [0, 5, 11, 21, 22, 25] { + assert_eq!(plural_czech(n), 2, "{n}"); + } + assert_eq!(plural_czech(1), 0); + for n in [2, 4] { + assert_eq!(plural_czech(n), 1, "{n}"); + } + assert_eq!(trn(Lang::CS, 1, "{n} app", "{n} apps"), "1 aplikace"); + assert_eq!(trn(Lang::CS, 2, "{n} app", "{n} apps"), "2 aplikace"); + assert_eq!(trn(Lang::CS, 5, "{n} app", "{n} apps"), "5 aplikací"); +} + #[test] fn untranslated_plurals_fall_back_to_english_by_count() { // Russian's rule says "one" for 21, but English says "other". @@ -218,6 +232,10 @@ fn untranslated_plurals_fall_back_to_english_by_count() { #[test] fn locale_tags_resolve() { + for tag in ["cs", "cs-CZ", "cs_CZ.UTF-8", "CS_cz"] { + assert_eq!(lang_from_tag(tag), Some(Lang::CS), "{tag}"); + } + assert_eq!(super::detect::first_supported(["xx-XX", "cs-CZ", "en-US"]), Some(Lang::CS)); assert_eq!(lang_from_tag("pt_BR.UTF-8"), Some(Lang::PT_BR)); assert_eq!(lang_from_tag("pt-BR"), Some(Lang::PT_BR)); assert_eq!(lang_from_tag("pt"), Some(Lang::PT_BR));