Files
orisys-app/apps/studio/tests/test_locales.py
T
ykcheungab 9354d7be1a cd D:\orisys_ws\orisys-app
git add apps/studio/config/locales/zh_CN.json `
        apps/studio/config/locales/en_US.json `
        apps/studio/tests/test_locales.py `
        apps/studio/tools/ui_check.py `
        apps/studio/tests/test_ui_smoke.py `
        apps/studio/src/orisys_studio/app_window.py `
        apps/studio/src/orisys_studio/ui/cal_panel.py `
        apps/studio/src/orisys_studio/ui/playback_dialog.py `
        apps/studio/src/orisys_studio/ui/force_plots.py `
        apps/studio/docs/record-channel.md `
        shared/src/orisys_app_shared/force/calibration_recorder.py `
        shared/src/orisys_app_shared/record/__init__.py `
        shared/src/orisys_app_shared/record/playback.py `
        shared/src/orisys_app_shared/record/depth.py `
        shared/tests/test_record_channel.py `
        shared/tests/test_playback.py
2026-09-13 14:01:24 +08:00

120 lines
4.1 KiB
Python

"""Static checks: translation keys and preference defaults stay in sync."""
from __future__ import annotations
import json
import re
from pathlib import Path
APP_ROOT = Path(__file__).resolve().parents[1]
SRC_DIR = APP_ROOT / "src"
LOCALES_DIR = APP_ROOT / "config" / "locales"
DEFAULTS_JSON = APP_ROOT / "config" / "preferences.default.json"
REPO_ROOT = APP_ROOT.parents[1]
STORE_PY = REPO_ROOT / "shared" / "src" / "orisys_app_shared" / "prefs" / "store.py"
_T_CALL = re.compile(r"""\bt\(\s*["']([A-Za-z0-9_.]+)["']""")
_DEFAULT_BLOCK = re.compile(r"_DEFAULTS[^=]*=\s*\{(?P<body>.*?)\n\}", re.DOTALL)
_KEY = re.compile(r"""["']([A-Za-z0-9_]+)["']\s*:""")
def _load_locale(name: str) -> dict:
return json.loads((LOCALES_DIR / f"{name}.json").read_text(encoding="utf-8"))
def _flatten(data: dict, prefix: str = "") -> set[str]:
keys: set[str] = set()
for key, value in data.items():
dotted = f"{prefix}{key}"
if isinstance(value, dict):
keys |= _flatten(value, f"{dotted}.")
else:
keys.add(dotted)
return keys
def _referenced_keys() -> set[str]:
found: set[str] = set()
for path in sorted(SRC_DIR.rglob("*.py")):
found |= set(_T_CALL.findall(path.read_text(encoding="utf-8")))
return found
def test_locales_parity_and_used_keys():
zh = _flatten(_load_locale("zh_CN"))
en = _flatten(_load_locale("en_US"))
assert zh == en, {
"zh_only": sorted(zh - en),
"en_only": sorted(en - zh),
}
referenced = _referenced_keys()
assert referenced, "no t() calls found — the source scan is broken"
missing = sorted(key for key in referenced if key not in zh)
assert not missing, f"t() keys missing from locale files: {missing}"
def test_preference_defaults_match_store_defaults():
json_keys = set(json.loads(DEFAULTS_JSON.read_text(encoding="utf-8")))
source = STORE_PY.read_text(encoding="utf-8")
match = _DEFAULT_BLOCK.search(source)
assert match, "could not locate _DEFAULTS in prefs/store.py"
store_keys = set(_KEY.findall(match.group("body")))
assert json_keys == store_keys, {
"json_only": sorted(json_keys - store_keys),
"store_only": sorted(store_keys - json_keys),
}
def test_locale_files_are_valid_utf8_json():
for name in ("zh_CN", "en_US"):
data = _load_locale(name)
assert isinstance(data, dict)
assert data.get("app", {}).get("title")
#: Sequences that only ever appear when UTF-8 text was decoded as a single-byte
#: codepage and then re-encoded. A ``Get-Content -Raw | Set-Content`` round trip
#: in Windows PowerShell does exactly that and silently mangles every non-ASCII
#: string, which is how the Chinese UI once ended up unreadable.
#:
#: Written as escapes, with no literal examples in the comments, so that this
#: guard cannot itself be corrupted by the same mistake.
_MOJIBAKE = (
"\u00c3",
"\u00c2",
"\u00e2\u20ac",
"\u00e3\u20ac",
"\u00ef\u00bf\u00bd",
)
def test_locale_files_are_utf8_without_bom_and_not_mojibake():
for name in ("zh_CN", "en_US"):
path = LOCALES_DIR / f"{name}.json"
raw = path.read_bytes()
assert not raw.startswith(b"\xef\xbb\xbf"), f"{name}.json starts with a BOM"
text = raw.decode("utf-8") # raises if it is not valid UTF-8
for sequence in _MOJIBAKE:
assert sequence not in text, (
f"{name}.json contains {sequence!r}: the file was decoded with the "
"wrong encoding. Rewrite it with an explicit UTF-8 writer instead "
"of a PowerShell Get-Content/Set-Content round trip."
)
def test_chinese_locale_actually_contains_chinese():
"""A corrupted zh_CN.json still parses as JSON, so check the content."""
data = _load_locale("zh_CN")
for key in ("menu/theme_dark", "app/about_title", "chart/mode_follow"):
node = data
for part in key.split("/"):
node = node[part]
assert any("\u4e00" <= char <= "\u9fff" for char in node), (
f"zh_CN.{key} has no Chinese characters: {node!r}"
)