From ecc61d994fa61d9136b8ce278afee22013425b33 Mon Sep 17 00:00:00 2001 From: Muneeb Sheikh Date: Sun, 11 Oct 2026 11:20:45 -0400 Subject: [PATCH] Honor custom catalog paths when activating translations --- src/humanize/i18n.py | 15 +++++++++++---- tests/test_i18n.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/humanize/i18n.py b/src/humanize/i18n.py index 0bf73de5..c8b6a964 100644 --- a/src/humanize/i18n.py +++ b/src/humanize/i18n.py @@ -51,7 +51,7 @@ def _get_default_locale_path() -> pathlib.Path | None: def get_translation() -> gettext_module.NullTranslations: - return _TRANSLATIONS.get(getattr(_CURRENT, "locale", None), _TRANSLATIONS[None]) + return getattr(_CURRENT, "translation", _TRANSLATIONS[None]) def activate( @@ -74,8 +74,10 @@ def activate( """ if locale is None or locale.startswith("en"): _CURRENT.locale = None + _CURRENT.translation = _TRANSLATIONS[None] return _TRANSLATIONS[None] + default_path = path is None if path is None: path = _get_default_locale_path() @@ -85,16 +87,21 @@ def activate( "You need to pass the path explicitly." ) raise FileNotFoundError(msg) - if locale not in _TRANSLATIONS: + if default_path and locale in _TRANSLATIONS: + translation = _TRANSLATIONS[locale] + else: translation = gettext_module.translation("humanize", path, [locale]) - _TRANSLATIONS[locale] = translation + if default_path: + _TRANSLATIONS[locale] = translation _CURRENT.locale = locale - return _TRANSLATIONS[locale] + _CURRENT.translation = translation + return translation def deactivate() -> None: """Deactivate internationalisation.""" _CURRENT.locale = None + _CURRENT.translation = _TRANSLATIONS[None] def _gettext(message: str) -> str: diff --git a/tests/test_i18n.py b/tests/test_i18n.py index 683fe3ce..5dd54bd6 100644 --- a/tests/test_i18n.py +++ b/tests/test_i18n.py @@ -6,6 +6,7 @@ import gettext import importlib import shutil +import struct import subprocess from pathlib import Path @@ -20,6 +21,34 @@ NOW = dt.datetime.now(tz=dt.UTC) +def test_activate_uses_catalog_from_requested_path(tmp_path: Path) -> None: + def write_catalog(root: Path, translated: str) -> None: + directory = root / "zz" / "LC_MESSAGES" + directory.mkdir(parents=True) + original = b"one" + translation = translated.encode("utf-8") + data = struct.pack("<7I", 0x950412DE, 0, 1, 28, 36, 0, 0) + data += struct.pack("<2I", len(original), 44) + data += struct.pack("<2I", len(translation), 48) + data += original + b"\0" + translation + b"\0" + (directory / "humanize.mo").write_bytes(data) + + first = tmp_path / "first" + second = tmp_path / "second" + write_catalog(first, "first") + write_catalog(second, "second") + try: + first_translation = humanize.activate("zz", first) + assert humanize.apnumber(1) == "first" + second_translation = humanize.activate("zz", second) + assert humanize.apnumber(1) == "second" + assert first_translation is not second_translation + humanize.activate("zz", first) + assert humanize.apnumber(1) == "first" + finally: + humanize.deactivate() + + @pytest.mark.parametrize("locale, one", [("de_DE", "eins"), ("fr_FR", "un")]) def test_update_translations( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, locale: str, one: str