Skip to content

test(control-plane): validate t() keys resolve against the i18n catalog - #2232

Merged
nicoloboschi merged 1 commit into
mainfrom
fix/i18n-used-key-validation
Jun 16, 2026
Merged

test(control-plane): validate t() keys resolve against the i18n catalog#2232
nicoloboschi merged 1 commit into
mainfrom
fix/i18n-used-key-validation

Conversation

@nicoloboschi

Copy link
Copy Markdown
Collaborator

Summary

Adds a CI guard that validates translation keys referenced in code resolve against the message catalog — the gap that let dataView.filterActive (#2226) ship missing.

The control plane already had two i18n checks, but a missing key fell straight between them:

  • messages.test.ts only compares locale catalogs against each other (en is the baseline). A key missing from every catalog — en included — passes parity unnoticed.
  • find-untranslated.ts (i18n:check) does the inverse: it flags hardcoded strings not wrapped in t(...), and explicitly skips anything inside t().

Neither one ever walked from a t("key") call site back to the catalog, so a t() reference with no entry anywhere only surfaced as a runtime next-intl error in the browser — invisible to CI.

What this adds

tests/messages/used-keys.test.ts — a vitest guard that:

  1. Walks every .ts/.tsx under src, parses with the TypeScript AST.
  2. Resolves each useTranslations("ns") binding to its namespace.
  3. Asserts every statically-resolvable t("key") / t.rich("key") reference maps to a leaf key in en.json, reporting file:line for any miss.

Dynamic keys (t(a_${x})), t.has() (the sanctioned presence-probe), and unknown bindings are skipped to avoid false positives. It runs under the existing npm test step in the build-control-plane CI job — no workflow change needed.

Fixes it surfaced

The guard immediately caught 14 keys referenced by the curation feature (#1976) but missing from all 10 catalogs — #2226 patched only one of them:

  • dataView.filterActive, dataView.filterInvalidated, dataView.invalidatedHint
  • documentsView.invalidatedFactsTitle
  • memoryDetailPanel.curationActions / curationEdit / curationReasonLabel / curationRevert / curationSave / curationStateInvalidated
  • memoryDetailPanel.editFieldContext / editFieldOccurredEnd / editFieldOccurredStart / editFieldType

Translations added for all 10 locales so the suite is green.

Supersedes #2226 (which added only filterActive) — that PR can be closed.

Tests

  • npx vitest run (67 passed, incl. the new guard + existing parity)
  • npm run i18n:check
  • ./scripts/hooks/lint.sh

Add a vitest guard that walks every src .ts/.tsx file, resolves each
useTranslations("ns") binding, and asserts every static t("key") /
t.rich("key") reference maps to a leaf key in en.json. This closes the
gap between the two existing i18n checks: messages.test.ts only compares
locale catalogs against each other (a key missing from *every* catalog,
en included, passes parity), and find-untranslated.ts does the inverse
(flags strings *not* wrapped in t()). Neither walked from a t() call
site back to the catalog, so a missing key only surfaced as a runtime
next-intl error in the browser.

Runs under the existing `npm test` step in the build-control-plane CI
job, so no workflow change is needed.

The guard immediately surfaced 14 keys referenced by the curation
feature (#1976) but missing from all 10 catalogs (filterActive,
filterInvalidated, invalidatedHint, invalidatedFactsTitle, and the
memoryDetailPanel curation*/editField* set). Add translations for all
locales so the suite is green. Supersedes #2226, which patched only
filterActive.
@nicoloboschi
nicoloboschi merged commit 08cfa5d into main Jun 16, 2026
81 checks passed
@nicoloboschi
nicoloboschi deleted the fix/i18n-used-key-validation branch June 16, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant