You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
i18n: catalogParity compares key SETS, so a translation that drifts in MEANING passes every gate #688
Found during #678 (the #666/#667 date-filter slice), where it nearly shipped.
The hole
web/src/i18n/catalogParity.test.ts enforces that en, es and tl carry the same key set for every namespace in TRANSLATED_NAMESPACES. That is a real guard and it works — a missing or extra key fails.
It says nothing about what the strings mean. So a translated string can:
call a UI element a name that element never uses in that locale, or
contradict another string in the same locale about the same thing,
and pass catalogParity, typecheck, the full Vitest suite, CodeQL, and review.
What actually happened
A help-text rewrite in #678 changed the word for the expiry field. Nothing caught it:
locale
the UI label (inventory.expiryLabel)
what the help text said
en
Expiry
"expiry" ✓
es
Vencimiento
"caducidad" in one string, "vencimiento" in another — it disagreed with itself
tl
Expiry
"pagkaluma" — becoming old / obsolete, which is not an expiry date
A Tagalog user would have read help text naming a field something the field never calls itself. Caught only because the driver happened to grep how the catalog says "expiry" elsewhere — not by any gate.
This is also the class the repo already worries about: help text exists to point at controls, and it silently stopped pointing.
Why the existing guards can't see it
catalogParity — key sets only, by construction.
i18n.test.ts / enums.test.ts — structure and enum coverage, not prose.
Review — three locales rewritten in one pass; a reviewer who does not read Tagalog cannot catch it, and the driver's own summary described the change correctly while the text said something else.
Proposal
A small guard pairing help/glossary strings that name a UI element against that element's own label key, per locale. Concretely: a table of { helpKey, labelKey } pairs, asserting the help string contains the label's term in each locale.
Deliberately narrow to start — the handful of help strings that name a specific labelled control. A broad "every noun must match" check would be unmaintainable and would fight legitimate grammar (Spanish inflects, Tagalog uses different particles).
Open question for whoever picks this up, and it is the crux: the assertion has to survive inflection. "vencimiento" appears inside "fecha de vencimiento", which is fine, but a stemming-free includes() will eventually fight a locale that declines the word. If that turns out to be unworkable, the honest fallback is to document the pairs and check them in review — say so rather than shipping a guard that has to be skipped later.
Found during #678 (the #666/#667 date-filter slice), where it nearly shipped.
The hole
web/src/i18n/catalogParity.test.tsenforces thaten,esandtlcarry the same key set for every namespace inTRANSLATED_NAMESPACES. That is a real guard and it works — a missing or extra key fails.It says nothing about what the strings mean. So a translated string can:
and pass
catalogParity,typecheck, the full Vitest suite, CodeQL, and review.What actually happened
A help-text rewrite in #678 changed the word for the expiry field. Nothing caught it:
inventory.expiryLabel)enExpiryesVencimientotlExpiryA Tagalog user would have read help text naming a field something the field never calls itself. Caught only because the driver happened to grep how the catalog says "expiry" elsewhere — not by any gate.
This is also the class the repo already worries about: help text exists to point at controls, and it silently stopped pointing.
Why the existing guards can't see it
catalogParity— key sets only, by construction.i18n.test.ts/enums.test.ts— structure and enum coverage, not prose.badgeCase.test.ts(AGENTS.md: three conventions earned while shipping #651 + #652 #662) — precedent that this repo does pin string content when it matters, but only for casing.Proposal
A small guard pairing help/glossary strings that name a UI element against that element's own label key, per locale. Concretely: a table of
{ helpKey, labelKey }pairs, asserting the help string contains the label's term in each locale.Deliberately narrow to start — the handful of help strings that name a specific labelled control. A broad "every noun must match" check would be unmaintainable and would fight legitimate grammar (Spanish inflects, Tagalog uses different particles).
Open question for whoever picks this up, and it is the crux: the assertion has to survive inflection.
"vencimiento"appears inside"fecha de vencimiento", which is fine, but a stemming-freeincludes()will eventually fight a locale that declines the word. If that turns out to be unworkable, the honest fallback is to document the pairs and check them in review — say so rather than shipping a guard that has to beskipped later.Not in scope
es/tlgenerally. Those ship machine-drafted pending review by policy (i18n infrastructure (SPA foundation): react-i18next + resolution + string sweep (tracker) #182); this is about a string contradicting the same catalog, which is checkable mechanically.2fd1f3c4).Related
#182 (translate-now policy), #662 (
badgeCase.test.ts— precedent for pinning string content), #678 (where this surfaced).