From 9435f1e192e61cba5588587392d45588b70cec1b Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 15 Oct 2026 21:05:52 -0700 Subject: [PATCH] Fall back to English for untranslated strings; fix Windows console encoding in doit localize Untranslated keys currently show as the raw .properties key until the next bulk .mo regeneration touches that locale. Fall back to the English catalog instead so users see real text in the interim. Also fixes a Windows-only cosmetic issue: doit localize's check_mo task prints ja/ko/zh sample text that cp1252 consoles can't encode, raising charmap errors during validation. Force UTF-8 on the console streams doit actually writes to. Co-Authored-By: Claude Code --- dodo.py | 7 +++++++ tabcmd/execution/localize.py | 6 ++++++ tests/test_localize.py | 13 +++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 tests/test_localize.py diff --git a/dodo.py b/dodo.py index b4aa7357..70e8015b 100644 --- a/dodo.py +++ b/dodo.py @@ -226,6 +226,13 @@ def generate_mo(): """ def check_mo(): + # The sample translations below include ja/ko/zh text, which a Windows console + # (cp1252) cannot print. Force UTF-8 so validation output is readable everywhere. + # doit replaces sys.stdout with its own writer while a task runs, so reconfigure + # the underlying console stream as well. + for stream in (sys.stdout, sys.__stdout__): + if hasattr(stream, "reconfigure"): + stream.reconfigure(encoding="utf-8", errors="replace") print("\n****** Validate all generated .mo files") for current_locale in LOCALES: LANG_DIR = os.path.join("tabcmd", "locales") diff --git a/tabcmd/execution/localize.py b/tabcmd/execution/localize.py index 07e0c4e8..14221a39 100644 --- a/tabcmd/execution/localize.py +++ b/tabcmd/execution/localize.py @@ -79,6 +79,12 @@ def define_locale_dir(logger): def _load_language(current_locale, domain, logger): locale_dir = define_locale_dir(logger) language: gettext.NullTranslations = gettext.translation(domain, locale_dir, languages=[current_locale]) + if current_locale != "en": + # Messages not yet translated fall back to English instead of showing the raw key. + try: + language.add_fallback(gettext.translation(domain, locale_dir, languages=["en"])) + except Exception as e: + logger.debug("Could not load English fallback catalog: {}".format(e)) language.install() # I believe this is the expensive call _ = language.gettext return _ diff --git a/tests/test_localize.py b/tests/test_localize.py new file mode 100644 index 00000000..16c8347c --- /dev/null +++ b/tests/test_localize.py @@ -0,0 +1,13 @@ +import logging +import unittest + +from tabcmd.execution import localize + + +class LocalizeFallbackTests(unittest.TestCase): + def test_untranslated_key_falls_back_to_english(self): + key = "session.errors.password_file_not_found" + translate = localize._load_language("de", "tabcmd", logging.getLogger("test")) + text = translate(key) + assert text != key, text + assert "{0}" in text, text