"""UX-13 P5: the page's words, app/ui_text/.json, in English or Dutch. One file per language with the same keys. A key missing from a language falls back to English, key by key, the way the Supervisor's translations do. test_ui_text.py holds both files to STYLE.md and to each other. """ import copy import io import json import os DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ui_text") LANGS = ("en", "nl") _CACHE: dict = {} def lang_of(value) -> str: """"nl", "nl-BE" or "nl_NL" -> "nl"; anything else -> "en".""" head = str(value or "").strip().lower().replace("_", "-").split("-")[0] return head if head in LANGS else "en" def raw(lang: str) -> dict: """One language file as written, without the English fallback.""" if lang not in _CACHE: with io.open(os.path.join(DIR, f"{lang}.json"), encoding="utf-8") as f: _CACHE[lang] = json.load(f) return _CACHE[lang] def _merge(base, over): if not (isinstance(base, dict) and isinstance(over, dict)): return copy.deepcopy(over) out = copy.deepcopy(base) for k, v in over.items(): out[k] = _merge(base.get(k), v) if k in base else copy.deepcopy(v) return out def words(lang="en") -> dict: """Every word in `lang`, English where a key is missing.""" lang = lang_of(lang) return raw("en") if lang == "en" else _merge(raw("en"), raw(lang)) def say(template: str, **values) -> str: """Fill `{name}` placeholders, as settings.js `say()` does.""" for k, v in values.items(): template = template.replace("{" + k + "}", str(v)) return template