Skip to content

feat(control-plane): add i18n support with 8 locales - #1775

Merged
nicoloboschi merged 2 commits into
mainfrom
feat/i18n-control-plane
May 27, 2026
Merged

nicoloboschi merged 2 commits into
mainfrom
feat/i18n-control-plane

Conversation

@nicoloboschi

Copy link
Copy Markdown
Collaborator

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

  • Routing. Pages move under the [locale] segment. The existing auth middleware composes with createIntlMiddleware and 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.
  • Catalogs. Eight JSON catalogs in 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 {…}}).
  • Provider plumbing. [locale]/layout.tsx wraps children with NextIntlClientProvider alongside the existing ThemeProvider / FeaturesProvider / BankProvider. Root layout.tsx becomes a minimal shell.
  • Locale parity test (src/messages/messages.test.ts, runs under npm test). Asserts (1) every declared locale has a JSON on disk and vice versa, (2) every locale has the exact same key set as en, (3) no leaf is empty.
  • Static finder for hardcoded strings (scripts/find-untranslated.ts, exposed as npm run i18n:check). Walks the TSX AST under src/components and src/app, flags JSX text + human-facing JSX attributes (placeholder, title, aria-label, alt, label, description, tooltip), skipping anything already inside a t(...) / t.rich(...) / useTranslations(...) call expression. Skips identifiers, URLs, HTML entities, env vars, JSON sample placeholders, formulas, and pure comma-separated token lists.
  • CI wiring. Both build-control-plane and the equivalent step in the build-hindsight-all job now run npm run i18n:check after npm 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 0
  • npx tsc --noEmit — clean
  • Dev server smoke test: /, /dashboard, /es/login, /fr/banks/<id>, /ja/banks/<id> all render correctly; language switcher persists via NEXT_LOCALE cookie; /api/* skips i18n rewrites.

Notes

  • next build fails on _global-error prerender (useContext null) — this reproduces on main and is unrelated to the port.

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.
@nicoloboschi
nicoloboschi merged commit 486c3a8 into main May 27, 2026
69 of 72 checks passed
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.
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