v1.60.0
[1.60.0] — 2026-05-22
refactor(i18n): split the 8-column megafile into per-locale files (I18N-SPLIT). The translation dictionary lived in one 849-line public/js/lib/i18n-dict.js (key → { en, es, … } across 8 locales). It is now one file per locale — public/js/lib/locales/i18n-dict.{en,es,pt-BR,ko,ja,ru,zh-CN,zh-TW}.js (each assigns window.__I18N_DICT_<LANG> = { key: string }) plus a shared i18n-dict.aliases.js — so a translator edits a single language in isolation (the i18next / OpenWA layout). i18n-dict.js is now a small assembler that merges the per-locale tables back into the exact same window.__I18N_DICT shape, so t(), every view, and every call-site are byte-for-byte unchanged. Everything loads synchronously via <script src> — no build step, no runtime fetch (both hard rules preserved). A captured snapshot proves the migration is lossless (assembled dict ≡ pre-split dict, 678 keys). Tooling + tests made split-aware via a shared tests/helpers/i18n-vm.mjs: tools/i18n-audit.mjs, the CI inline coverage check (which had silently become a no-op against the post-v1.23 split — it now actually loads the dictionary), and ~25 dictionary tests. New tests/i18n-locale-files.test.mjs (snapshot equality · per-locale key parity · alias integrity · index.html load order) and tests/playwright-locale-sweep.mjs (every page × 8 locales renders + localizes in real Chromium). 994 → 1000 unit · 62 → 70 Playwright. No user-facing behaviour change. (I18N-SPLIT)