"""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 = {} # SMA-10. The sentences that move with the roster (handback.overlay): empty for # every GoodWe-only roster, and then `raw()` is the file as it always was. _IDLE: dict = {} _GEN = [0] _MERGED: 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 _file(lang: str) -> dict: 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 set_idle(overlay: dict) -> None: """SMA-10: the roster-built sentences, `{lang: nested dict}` (or {}).""" _IDLE.clear() _IDLE.update(overlay or {}) _MERGED.clear() _GEN[0] += 1 def generation() -> int: """Changes whenever `set_idle()` does, so a cache of words can tell.""" return _GEN[0] def raw(lang: str) -> dict: """One language file as written, without the English fallback (and with the roster's own sentences laid over it when a unit is handed back).""" if lang not in _IDLE: return _file(lang) if lang not in _MERGED: _MERGED[lang] = _merge(_file(lang), _IDLE[lang]) return _MERGED[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