Web internationalization
The web console has a single localization boundary under web/src/i18n.
- Runtime shell, primary navigation, and command-palette copy must use typed
message keys from
messages.tsthroughuseTranslation()orformatMessage(). - Production runtime catalogs ship for
en-USandes-ES.en-XAandar-XBare pseudo-locales for expansion and RTL tests only; they do not count as production localization coverage. - New user-facing source literals must be captured by
web/src/i18n/extractedMessages.gen.ts, and the count must stay at or belowweb/src/i18n/extractedMessages.budget.json. Runnpm --prefix web run i18n:extractintentionally when migrating copy, andnpm --prefix web run i18n:checkin CI; the check fails if the generated catalog is stale or if the unmanaged-literal budget grows. - Pseudo-locale tests use
en-XA; RTL document-direction tests usear-XB. - Locale and timezone state is in-memory or server-provided through
/auth/me. Operators can switch locale from the shell header for the current browser session. Do not store locale, timezone, auth state, or tenant choice inlocalStorageorsessionStorage. - Date, number, and plural display code should use
web/src/i18n/format.tsso later page migrations inherit the same explicit locale/timezone policy.
Routes and navigation
The console's task-based navigation and route table live in web/src/lib/navigation.ts,
and every nav group label and item label is a typed message key resolved through the
catalog — there is one label per route, and route parity is test-enforced
(route_parity.test.ts). When a new surface is added it must register its route, a typed
nav.item.* message, and a feature mapping together; the privacy (/privacy) and
integrate (/integrate) surfaces follow exactly that pattern. See
The web console for the full map of routes to screens.