// Fenrir console screen reader. // Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // // You should have received a copy of the GNU General Public License along with // this program. If not, see . package i18n import ( "os" "path/filepath" "strings" "testing" ) func TestCatalogReusePluralRulesAndPythonFields(t *testing.T) { root := t.TempDir() dir := filepath.Join(root, "xx", "LC_MESSAGES") if err := os.MkdirAll(dir, 0700); err != nil { t.Fatal(err) } data := `msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Start Fenrir" msgstr "translated-start" msgid "screen {0}" msgstr "translated-screen {0}" msgid "line {0}, column {1}, Terminal {2}" msgstr "{2}: {1}, {0}" msgid "one item" msgid_plural "many items" msgstr[0] "translated-one" msgstr[1] "translated-many" ` if err := os.WriteFile(filepath.Join(dir, "fenrir.po"), []byte(data), 0600); err != nil { t.Fatal(err) } previous := Use(New(root, "missing:xx_YY.UTF-8")) t.Cleanup(func() { Use(previous) }) if got := T("Start Fenrir"); got != "translated-start" { t.Fatal("catalog not reused", got) } if got := Format("screen {0}", "3"); got != "translated-screen 3" { t.Fatal(got) } if got := Format("line {0}, column {1}, Terminal {2}", "7", "8", "9"); got != "9: 8, 7" { t.Fatal("translated field order lost", got) } if got := Format("screen {0}", "{0}"); got != "translated-screen {0}" { t.Fatal("replacement data was formatted again", got) } if got := N("one item", "many items", 1); got != "translated-one" { t.Fatal(got) } if got := N("one item", "many items", 3); got != "translated-many" { t.Fatal(got) } if got := T("start of screen"); got != "start of screen" { t.Fatal("missing translation lost English msgid", got) } } func TestLanguagePrecedenceAndCStopsFallback(t *testing.T) { for _, name := range []string{"LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG"} { t.Setenv(name, "") } if Language() != "C" { t.Fatal(Language()) } for _, pair := range [][2]string{{"LANG", "en"}, {"LC_MESSAGES", "es"}, {"LC_ALL", "de"}, {"LANGUAGE", "pl:ru"}} { t.Setenv(pair[0], pair[1]) if Language() != pair[1] { t.Fatal(Language()) } } catalog := New(t.TempDir(), "C.UTF-8:de") if len(catalog.locales) != 0 { t.Fatal("C locale continued translation fallback") } } func TestInstalledFenrirCatalogWhenRequested(t *testing.T) { root := os.Getenv("FENRIR_TEST_LOCALE_DIR") if root == "" { t.Skip("set FENRIR_TEST_LOCALE_DIR to check an installed Fenrir catalog") } previous := Use(New(root, "de_DE.UTF-8")) t.Cleanup(func() { Use(previous) }) if got := T("Start Fenrir"); got != "Fenrir starten" { t.Fatal("installed MO not reused", got) } if got := Format("screen {0}", "2"); got != "Bildschirm 2" { t.Fatal("Python fields not reused", got) } } func TestBundledCatalogsWithoutInstalledTranslations(t *testing.T) { root := t.TempDir() for _, trial := range []struct{ language, menu, tab string }{ {"de_DE.UTF-8", "Fenrir-Menü", "Lesen Registerkarte"}, {"es_ES.UTF-8", "Menú de Fenrir", "Lectura pestaña"}, {"pl_PL.UTF-8", "Menu Fenrira", "Czytanie zakładka"}, {"pt_BR.UTF-8", "Menu do Fenrir", "Leitura aba"}, {"ru_RU.UTF-8", "Меню Fenrir", "Чтение, вкладка"}, } { t.Run(trial.language, func(t *testing.T) { previous := Use(New(root, trial.language)) t.Cleanup(func() { Use(previous) }) if got := T("Fenrir Menu"); got != trial.menu { t.Fatalf("bundled menu: %q", got) } if got := Format("{0} tab", T("Reading")); got != trial.tab { t.Fatalf("nested translation: %q", got) } if got := Format("Added {0} to clipboard", "{0}: application text"); !strings.Contains(got, "{0}: application text") { t.Fatalf("application value reformatted: %q", got) } for _, message := range []string{"Start Fenrir", "character {0}", "number", "tab", "Could not check spelling: {0}"} { if T(message) == message { t.Errorf("untranslated %q", message) } } }) } } func TestInstalledPriorityAndBundledLanguageFallback(t *testing.T) { root := t.TempDir() dir := filepath.Join(root, "de", "LC_MESSAGES") if err := os.MkdirAll(dir, 0700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "fenrir.po"), []byte("msgid \"Start Fenrir\"\nmsgstr \"custom-start\"\n"), 0600); err != nil { t.Fatal(err) } previous := Use(New(root, "missing:de:es")) t.Cleanup(func() { Use(previous) }) if got := T("Start Fenrir"); got != "custom-start" { t.Fatal("installed translation lost priority", got) } if got := T("Fenrir Menu"); got != "Fenrir-Menü" { t.Fatal("later language replaced bundled German", got) } for _, language := range []string{"C.UTF-8:de", "POSIX:es", "en_US.UTF-8", "missing"} { Use(New(root, language)) if got := T("Fenrir Menu"); got != "Fenrir Menu" { t.Fatalf("%s did not retain English: %q", language, got) } } }