""" Rekey frontend t('dot.notation') -> t('Deutscher Klartext') using locales/de.ts mapping. Usage (from repo root): python gateway/scripts/i18n_rekey_plaintext_keys.py Excludes: src/locales/, this script's output is in-place file edits. """ from __future__ import annotations import re import sys from pathlib import Path _REPO = Path(__file__).resolve().parents[2] _SRC = _REPO / "frontend_nyla" / "src" _DE_FILE = _SRC / "locales" / "de.ts" def _unescape_ts_single_quoted(raw: str) -> str: out: list[str] = [] i = 0 while i < len(raw): c = raw[i] if c == "\\" and i + 1 < len(raw): n = raw[i + 1] if n == "n": out.append("\n") i += 2 continue if n == "r": out.append("\r") i += 2 continue if n == "t": out.append("\t") i += 2 continue out.append(n) i += 2 continue out.append(c) i += 1 return "".join(out) def _escape_for_ts_single_quoted(s: str) -> str: return ( s.replace("\\", "\\\\") .replace("'", "\\'") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t") ) def _parse_de_ts(path: Path) -> dict[str, str]: text = path.read_text(encoding="utf-8") mapping: dict[str, str] = {} line_re = re.compile( r"^\s*'((?:\\.|[^'])*)':\s*'((?:\\.|[^'])*)'\s*,?\s*(//.*)?$" ) for line in text.splitlines(): m = line_re.match(line.strip()) if not m: continue key = _unescape_ts_single_quoted(m.group(1)) val = _unescape_ts_single_quoted(m.group(2)) mapping[key] = val return mapping def _iter_source_files(): for ext in ("*.tsx", "*.ts"): for p in _SRC.rglob(ext): rel = p.relative_to(_SRC).as_posix() if rel.startswith("locales/"): continue yield p def _rekey_content(content: str, mapping: dict[str, str]) -> tuple[str, int]: changes = 0 keys = sorted(mapping.keys(), key=len, reverse=True) for key in keys: if f"'{key}'" not in content: continue german = mapping[key] escaped = _escape_for_ts_single_quoted(german) repl_single = f"t('{escaped}')" key_re = re.escape(key) # t('key', "..." ) content, c = re.subn( rf"t\(\s*'{key_re}'\s*,\s*\"(?:\\.|[^\"])*\"\s*\)", repl_single, content, ) changes += c # t('key', '...' ) content, c = re.subn( rf"t\(\s*'{key_re}'\s*,\s*'(?:\\.|[^'])*'\s*\)", repl_single, content, ) changes += c # t('key') content, c = re.subn(rf"t\(\s*'{key_re}'\s*\)", repl_single, content) changes += c return content, changes def main() -> int: if not _DE_FILE.is_file(): print("Missing", _DE_FILE, file=sys.stderr) return 1 mapping = _parse_de_ts(_DE_FILE) print("Loaded", len(mapping), "entries from de.ts") total = 0 for path in _iter_source_files(): raw = path.read_text(encoding="utf-8") new_raw, n = _rekey_content(raw, mapping) if n and new_raw != raw: path.write_text(new_raw, encoding="utf-8", newline="\n") print(path.relative_to(_REPO), n, "replacements") total += n print("Done. Total replacements:", total) return 0 if __name__ == "__main__": raise SystemExit(main())