0174b93d0f
BCP 47 language tags are case-insensitive (RFC 5646 §2.1.1) but the
locale files mix conventions (pt-br.json vs zh-CN.json). On
case-sensitive filesystems, '--lang PT-BR' or '--lang zh-cn' silently
missed the file, _load_entity_section returned {}, and entity
detection ran in English with no warning.
The cache key in get_entity_patterns was built from raw input, so
('PT-BR',) and ('pt-br',) produced two distinct entries, both wrong.
Add _canonical_lang(lang) that resolves any casing to the on-disk
filename stem via lowercase comparison, and route load_lang,
_load_entity_section, and the cache key through it.
Closes #927
87 lines
2.9 KiB
Python
87 lines
2.9 KiB
Python
"""Regression tests for issue #927 — language code lookup must be case-insensitive.
|
|
|
|
The locale files use mixed case for the region subtag (``pt-br.json`` vs
|
|
``zh-CN.json``). BCP 47 tags are case-insensitive (RFC 5646 §2.1.1), so
|
|
``--lang PT-BR``, ``--lang zh-cn``, and ``--lang ZH-TW`` must all resolve
|
|
to the canonical file rather than silently falling back to English.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from mempalace import i18n
|
|
from mempalace.i18n import (
|
|
_canonical_lang,
|
|
_load_entity_section,
|
|
available_languages,
|
|
get_entity_patterns,
|
|
load_lang,
|
|
)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _reset_state():
|
|
"""Reset the module-level entity cache between tests."""
|
|
i18n._entity_cache.clear()
|
|
yield
|
|
i18n._entity_cache.clear()
|
|
|
|
|
|
def test_canonical_lang_lowercase_passthrough():
|
|
assert _canonical_lang("en") == "en"
|
|
assert _canonical_lang("pt-br") == "pt-br"
|
|
|
|
|
|
def test_canonical_lang_uppercase_resolves():
|
|
assert _canonical_lang("PT-BR") == "pt-br"
|
|
assert _canonical_lang("ZH-CN") == "zh-CN"
|
|
assert _canonical_lang("zh-cn") == "zh-CN"
|
|
assert _canonical_lang("Pt-Br") == "pt-br"
|
|
|
|
|
|
def test_canonical_lang_unknown_returns_none():
|
|
assert _canonical_lang("xx") is None
|
|
assert _canonical_lang("") is None
|
|
|
|
|
|
def test_load_lang_case_insensitive():
|
|
"""`load_lang('PT-BR')` must load the pt-br dictionary, not English."""
|
|
en = load_lang("en")
|
|
pt_lower = load_lang("pt-br")
|
|
pt_upper = load_lang("PT-BR")
|
|
assert pt_lower == pt_upper, "case should not change the loaded dict"
|
|
# If load_lang silently fell back to English, both would equal `en`.
|
|
if "pt-br" in available_languages() and pt_lower != en:
|
|
assert i18n.current_lang() == "pt-br"
|
|
|
|
|
|
def test_entity_section_loads_for_uppercase_input():
|
|
"""`_load_entity_section('PT-BR')` must read pt-br.json, not return {}."""
|
|
pt_lower = _load_entity_section("pt-br")
|
|
pt_upper = _load_entity_section("PT-BR")
|
|
assert pt_lower == pt_upper
|
|
|
|
|
|
def test_get_entity_patterns_case_insensitive():
|
|
"""Entity patterns must be identical regardless of input case."""
|
|
lower = get_entity_patterns(("pt-br",))
|
|
upper = get_entity_patterns(("PT-BR",))
|
|
assert lower == upper
|
|
|
|
|
|
def test_get_entity_patterns_shares_cache_across_cases():
|
|
"""Different casing must hit the same cache entry — not duplicate work."""
|
|
get_entity_patterns(("zh-CN",))
|
|
cache_keys = list(i18n._entity_cache.keys())
|
|
get_entity_patterns(("ZH-CN",))
|
|
get_entity_patterns(("zh-cn",))
|
|
assert len(i18n._entity_cache) == len(
|
|
cache_keys
|
|
), "different casings of the same language must not create new cache entries"
|
|
|
|
|
|
def test_unknown_language_still_falls_back_to_english():
|
|
"""A code with no matching file must fall through to English (existing contract)."""
|
|
patterns = get_entity_patterns(("xx-yy",))
|
|
en = get_entity_patterns(("en",))
|
|
assert patterns["candidate_patterns"] == en["candidate_patterns"]
|