test(i18n): enforce catalogue key placeholder and nonblank parity

This commit is contained in:
cesnimda
2026-11-02 23:20:25 +01:00
parent 557efde073
commit 1756273bc1
4 changed files with 32 additions and 0 deletions
@@ -1,5 +1,7 @@
# Direct roadmap integration — 2 November 2026 # Direct roadmap integration — 2 November 2026
Published implementation checkpoint `557efde073a380030a39066fab2e6d961ce717f4` was independently read back on origin/codex/direct-roadmap-integration. Follow-up test-only catalogue QA adds three invariants and runs with existing provider/persistence stability tests: 10 passed in three additional suites; see ../i18n/EN_NB_CATALOGUE_QA.md. No runtime application change after the passing build.
Validated isolated review branch `codex/direct-roadmap-integration`. Base Phase 10 `db818e5ebd19eeedc05ae58dc3f7a908ce0e713d`; selected application paths and docs/i18n from published `4172c23d7ce13e8327a5e51bbb09f296c24fb9b8`; backup parser/deploy function and release-readiness reports from `f19e7ebf4aeae350b43fa84cf115c393030280f0`. Content was integrated by explicit owned paths, preserving the Phase 10 accepted handoff records and approved App.css deletion. No full-branch history overwrite, orchestrator access, main merge, final UI design or deployment. Validated isolated review branch `codex/direct-roadmap-integration`. Base Phase 10 `db818e5ebd19eeedc05ae58dc3f7a908ce0e713d`; selected application paths and docs/i18n from published `4172c23d7ce13e8327a5e51bbb09f296c24fb9b8`; backup parser/deploy function and release-readiness reports from `f19e7ebf4aeae350b43fa84cf115c393030280f0`. Content was integrated by explicit owned paths, preserving the Phase 10 accepted handoff records and approved App.css deletion. No full-branch history overwrite, orchestrator access, main merge, final UI design or deployment.
## Fresh qualification ## Fresh qualification
+9
View File
@@ -0,0 +1,9 @@
# Automated EN/Bokmål catalogue QA
2 November 2026. Added translation-catalogue.test.ts to the existing Jest suite. Reuses the current interpolation contract and the checks demonstrated by the historical Phase 9 localisation helper; that helper/report was not modified or overwritten.
Three automated invariants: equal English/Bokmål key sets; equal sets of current `{word}` substitution variables per message; no blank message values. Current resources contain 3,119 English and 3,119 Bokmål keys. These are resource counts, not thousands of test cases. Identical proper names/technical labels are allowed; machine equality does not determine whether natural-language copy is translated well.
PASSED: 10 tests across translation-catalogue, i18n-provider and locale-fetch-stability, zero failures/pending, native exit 0. This includes the three new global invariants and existing persistence/legacy alias/fetch-stability coverage. Scoped lint passed and full typechecking completed without errors. [Native report](../direct-roadmap/integration-evidence/locale-catalogue-native.json.gz). These three suites are separate from the 16-suite/195-test integration execution; do not represent them as a single fresh full-suite run. Runtime application source is unchanged from the passing integration production build; no build was repeated for a test-only addition.
Limits: current simple interpolation only, not a future ICU/plural engine; no every-string/frontend/backend completeness, native translation quality, human/device or European locale release acceptance. The 60 research locale candidates remain unshipped. Native terminology, market priorities, Montenegrin identity/canonicalisation and registry/migration decisions retain their documented gates.
@@ -0,0 +1,21 @@
import { translations } from './i18n/translations';
const placeholders = (text: string) => [...new Set(text.match(/\{\w+\}/g) ?? [])].sort();
test('English and Bokmål expose the same UI message keys', () => {
expect(Object.keys(translations.nb).sort()).toEqual(Object.keys(translations.en).sort());
});
test('Bokmål preserves every English substitution variable', () => {
const mismatches = Object.entries(translations.en).filter(([key, text]) =>
JSON.stringify(placeholders(text)) !== JSON.stringify(placeholders(translations.nb[key as keyof typeof translations.en]))
).map(([key]) => key);
expect(mismatches).toEqual([]);
});
test('supported UI catalogues contain no blank messages', () => {
const blanks = Object.entries(translations).flatMap(([language, messages]) =>
Object.entries(messages).filter(([, text]) => !text.trim()).map(([key]) => `${language}.${key}`)
);
expect(blanks).toEqual([]);
});