feat(dashboard): localize the tenant app with react-i18next (4/4) - #1363
Open
marcelo-maciel wants to merge 1 commit into
Open
feat(dashboard): localize the tenant app with react-i18next (4/4)#1363marcelo-maciel wants to merge 1 commit into
marcelo-maciel wants to merge 1 commit into
Conversation
Dashboard slice of the i18n work (split of fullstackhero#1344 as requested in review). Self-contained: it needs nothing from the backend slices, and the backend needs nothing from it. - `react-i18next` wiring in `src/i18n.ts`, language detected from the stored preference and negotiated with the API through `Accept-Language`. - English and Brazilian Portuguese catalogs, split per feature namespace, covering catalog, chat, tickets, files, billing, identity and settings. - Language switcher in the topbar; the chosen language is persisted to the user profile, and `html[lang]` follows it through a `languageChanged` listener. - The topbar stops hydrating the language from the profile once the user has chosen one in the session, so a concurrent profile save cannot silently switch the app back. The underlying lost update on `PUT /identity/profile` is tracked in fullstackhero#1359 and fixed separately. - Impersonation handoff adopts `locale` from the URL before `createRoot`, so an operator arriving from the admin app keeps their language. Harmless on its own: on `main` nothing sends the parameter yet. - Playwright specs pin catalog parity (keys and placeholders, both directions), the switcher, the shell, the hydration guard and the handoff parameter. The `SSH.NET` pin (`2026.0.0`) rides along because `template-smoke.yml` runs on `clients/**` and builds the scaffolded solution, which fails `restore` with `NU1903` until fullstackhero#1333 merges. It is byte-identical to that PR.
marcelo-maciel
marked this pull request as ready for review
August 17, 2026 04:02
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This was referenced Aug 17, 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.
clients/dashboardslice of the i18n work, split out of #1344. 134 files, of which 32 are JSON catalogs. Same shape as the admin slice.Independent of the backend slices. Nothing here needs the API change, and the API change needs nothing here.
clients/adminclients/dashboardWhat is in here
react-i18nextwiring insrc/i18n.ts. The chosen language is persisted to the user profile and sent to the API asAccept-LanguagethroughapiFetch; variants are canonicalised onto a supported tag before the call, so the API never sees a barept.en-USandpt-BRcatalogs per feature namespace — catalog, chat, tickets, files, billing, identity, settings — held at strict key and placeholder parity in both directions bytests/i18n/parity.spec.ts.html[lang]follows the active language through alanguageChangedlistener, replacing the staticlang="en"that nothing updated.localefrom the URL beforecreateRoot, which fixes the shell language andAccept-Languagein the same step. Harmless on its own: onmainnothing sends the parameter until the admin slice lands, and this PR'shandoff-locale.spec.tspasses with only this half present.updateMyProfileis a read-modify-write with no concurrency token, and Settings › Profile invalidates the same["identity","me"]key the topbar reads, so a save whose read preceded the language PUT could echo the old locale back and win — and the hydration effect would then switch the UI to it. A locale set on another device still carries over on a fresh mount.Testing
Everything below is this slice on its own, at
mainplus these 134 files — not a share of the unsplit branch's totals.npm ci,npm run build(tsc -b+vite build),npx tsc -b tsconfig.tests.jsonandnpm run lint: all exit 0.tests/impersonation/handoff-locale.spec.ts, which passes with only this half of the handoff present.src/Directory.Packages.propsOne backend file in a React PR, which needs explaining.
template-smoke.ymltriggers onpaths: clients/**and runsdotnet buildon the scaffolded solution, and.template.config/template.jsondoes not excludesrc/Tests/**— so a front-end-only PR still restores the full package graph and hitsNU1903/GHSA-q939-rpr3-3284onSSH.NET2025.1.0, pulled transitively by Testcontainers. That advisory failsrestoreonmaintoo, re-verified today at3f2959e6.The pin is byte-identical to #1333, which owns the fix, so both stay mergeable in either order and this copy can simply be dropped once #1333 lands. All four slices carry it for the same reason.
Notes
element detached from the DOM). Three distinct approaches, then stopped rather than paper over it. The underlying lost update onPUT /identity/profileis a contract change and is tracked in #1359, where theponytail:comment in the topbar points.apiFetch, soAccept-Languageon the negotiate is the browser's. Named explicitly inhandoff-locale.spec.tsso any other channel that stops carrying the locale fails the test.Docs (Golden Rule #10)
fullstackhero/docs#238, kept as a single PR covering all four slices —
internationalization.mdxis one page whose sections map across the split. The Frontend (admin and dashboard) section is this slice and the admin one: catalogs, language detection and normalization,Accept-Language, the switcher and locale-aware formatting. That PR should merge after the last of the four, so the page never describes code that is not onmainyet.