Repository navigation
feat(control-plane): add i18n support with 8 locales - #1775
Merged
Merged
Conversation
Internationalize the control plane UI using next-intl. Pages move under [locale] segment with locale-prefixed routing (default English has no prefix). Adds en/es/fr/de/pt/ja/ko/zh catalogs, a Globe language switcher, and combines i18n routing with the existing auth middleware. The matcher uses an explicit file-extension allowlist so bank IDs with dots (e.g. SX.Products.GovComply.Build) still get the locale rewrite. Adds a locale parity test (vitest) and a static finder (scripts/find-untranslated.ts, exposed as npm run i18n:check) that walks the TSX AST to flag hardcoded user-facing strings — both wired into CI via the build-control-plane job so future drift fails the build.
Run scripts/hooks/lint.sh to normalize formatting on the i18n changes so verify-generated-files passes.
This was referenced May 27, 2026
4 tasks done
nicoloboschi
pushed a commit
that referenced
this pull request
May 27, 2026
Polish 18 Chinese (zh) translation strings introduced in #1775 to improve fluency and reduce translation artifacts (passive voice, literal renderings, redundant connectives), while preserving the upstream policy of keeping product operation names (Retain / Recall / Reflect / Webhooks) untranslated across all locales. No structural / framework changes. Locale parity tests pass.
This was referenced Oct 5, 2026
This was referenced Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Internationalizes the control plane UI across 8 locales (en, es, fr, de, pt, ja, ko, zh) using next-intl. English keeps no prefix; other locales live under
/{locale}/…. Adds a Globe language switcher in the navbar and login page.Highlights
[locale]segment. The existing auth middleware composes withcreateIntlMiddlewareand strips the locale prefix before applying public-route rules. The matcher uses an explicit file-extension allowlist so bank IDs with dots (e.g.SX.Products.GovComply.Build) still hit the locale rewrite.src/messages/, ~1100 keys each, covering bank navigation, all view panels (data, documents, entities, mental-models, think, recall debug, webhooks, audit logs), bank-config + profile + stats + operations, all detail modals, the graph/constellation, and the Add Document dialog. ICU plurals used for counts ({count, plural, one {…} other {…}}).[locale]/layout.tsxwraps children withNextIntlClientProvideralongside the existingThemeProvider/FeaturesProvider/BankProvider. Rootlayout.tsxbecomes a minimal shell.src/messages/messages.test.ts, runs undernpm test). Asserts (1) every declared locale has a JSON on disk and vice versa, (2) every locale has the exact same key set asen, (3) no leaf is empty.scripts/find-untranslated.ts, exposed asnpm run i18n:check). Walks the TSX AST undersrc/componentsandsrc/app, flags JSX text + human-facing JSX attributes (placeholder,title,aria-label,alt,label,description,tooltip), skipping anything already inside at(...)/t.rich(...)/useTranslations(...)call expression. Skips identifiers, URLs, HTML entities, env vars, JSON sample placeholders, formulas, and pure comma-separated token lists.build-control-planeand the equivalent step in thebuild-hindsight-alljob now runnpm run i18n:checkafternpm test. Future drift in translations or untranslated strings fails the build.Test plan
npm run --workspace=hindsight-control-plane test— 49/49 (33 base + 16 locale-parity)npm run --workspace=hindsight-control-plane i18n:check— exits 0npx tsc --noEmit— clean/,/dashboard,/es/login,/fr/banks/<id>,/ja/banks/<id>all render correctly; language switcher persists viaNEXT_LOCALEcookie;/api/*skips i18n rewrites.Notes
next buildfails on_global-errorprerender (useContextnull) — this reproduces onmainand is unrelated to the port.