chore: Add a locale completeness check

An incomplete locale falls back to English at runtime, so a gap breaks nothing
and nobody notices it on their own. 2.2.1 shipped fifty-six user-facing strings
that existed only in English and German, and it took someone looking for
something else to find them. `npm run client:locales:check` compares every
locale against the reference and exits non-zero when one is behind.

It compares `login.js` and `core.js` separately and never merges them.
`login.js` is the embedded bundle and is all that exists until a session is
established, so a key present only in `core.js` is still missing from every
screen shown before login — the two-factor challenge among them. Merging the
two hides exactly that, which is how the six strings of the challenge stayed
English in thirty-three languages.

Keys are compared on their base name, with the CLDR plural category stripped,
because how many categories a language needs is a property of that language:
English has two, Japanese one, Arabic six. Comparing the suffixed names would
have demanded an English `_one` from Japanese, where i18next never reads it.
Each plural key is then checked against the categories the language itself
declares, through the same Intl.PluralRules that i18next resolves with — a
missing category falls back to `_other` silently, and Russian `few` and `many`
are reachable with counts a user really sees.

It is deliberately not part of `lint`: a pull request that adds a string is
expected to leave the other locales behind for a while, and a check that fails
every such request would only be switched off. This one belongs before a
release.
This commit is contained in:
Daniel Hiller
2026-09-08 19:54:27 +02:00
parent 76d3911d03
commit 9069b24cfb
3 changed files with 182 additions and 0 deletions
+1
View File
@@ -6,6 +6,7 @@
"build": "vite build",
"postinstall": "patch-package",
"lint": "eslint --ext js,jsx src --report-unused-disable-directives",
"locales:check": "node scripts/check-locales.js",
"start": "vite",
"test": "jest",
"test:acceptance": "cucumber-js --import tests/acceptance/cucumber.conf.js --import tests/acceptance/steps/**/*.js --format @cucumber/pretty-formatter tests"