Guardrails
i18n failures are often related to string-identity problems rather than TypeScript failures. A code value, database seed row, translation key, namespace filename, or generated asset path can drift while the compiler still stays green.
Wavemap’s current posture is to make those identities explicit and continuously checked.
Primary Commands
Section titled “Primary Commands”pnpm format:i18npnpm verify:i18nformat:i18n rewrites source translation sheets into canonical order.
verify:i18n checks the source translation surface and fails CI when a contract drifts.
What verify:i18n Checks
Section titled “What verify:i18n Checks”The source verifier currently checks:
- Translation JSON parses correctly.
- Registered namespace constants match locale filenames.
- Every locale has the expected namespace files.
- Non-English locales mirror English (
en) key shape. - Ordinary translation key segments use the expected lower-kebab style.
_metashape, interpolation hints, and last-modified conventions are valid.- Translation sheets are canonically ordered.
- ICU syntax parses through
intl-messageformat. - Non-English locales preserve English interpolation, plural, and select contracts.
- Domain-code translation coverage matches source contract manifests.
- Backend base-seed files import and map over expected canonical arrays.
This is intentionally source-level. It catches most drift before tests need a running app or database.
server--errors and email--* namespaces are checked through the same source-level rules as the rest of the translation
surface: locale parity, key shape, ICU validity, interpolation metadata, and canonical ordering.
Namespace Registry And Sheet Filename Parity
Section titled “Namespace Registry And Sheet Filename Parity”verify:i18n enforces a two-way match between registered namespace names and source translation sheet filenames.
The verifier builds the registered namespace set from AVAILABLE_NAMESPACES, following the layered namespace arrays in
common, wavemap, email, and server. It builds the source namespace set from
packages/i18n/locales/<locale>/*.json, using each filename without .json as the namespace name.
This catches three drift cases:
- A namespace constant is registered but one or more locale sheets are missing.
- A translation sheet exists on disk but is not included in the runtime namespace registry.
- One locale has a namespace sheet that another locale does not.
The filename stem is therefore part of the runtime contract. For example, a namespace value of common--pagination must
be backed by common--pagination.json in every supported locale.
CI Lane
Section titled “CI Lane”.github/workflows/ci.yml has a dedicated i18n_verification job after verify_tooling_and_code.
That job runs:
pnpm verify:i18nKeeping it separate makes i18n failures visible as i18n failures instead of discovering them indirectly through Playwright smoke tests or missing labels in the browser.
The front-end E2E smoke lane also runs:
pnpm -C packages/i18n build-and-validate:i18n-assetsThat proves the generated static asset snapshot before the frontend is built and started.
Domain-To-i18n Contracts
Section titled “Domain-To-i18n Contracts”Some display values are code-driven. Examples include event statuses, event types, venue statuses, artist link destinations, user-facing roles, display modes, themes, and external institutions.
The manifest at packages/i18n/scripts/i18n/helpers/domainContracts.js ties an exported source array to:
- A target translation namespace.
- A key prefix.
- The set of required localized keys.
At a high level, the verifier:
- Reads each manifest entry.
- Parses the TypeScript source file with the TypeScript compiler API.
- Resolves exported string arrays such as
AVAILABLE_EVENT_STATUS_CODES. - Builds required keys such as
statuses.upcoming. - Verifies every supported locale has those keys.
- Fails if a checked translation sheet contains stale keys for removed or unknown codes.
- Requires checked code values to be lower-kebab so they can safely become key segments.
Translation files should render localized labels. They should not define the allowed domain states.
Seed-Source Contracts
Section titled “Seed-Source Contracts”The same verifier checks backend base-seed source wiring through DOMAIN_SEED_SOURCE_CONTRACTS.
Each seed contract ties a backend base seed file to the canonical exported array it should use for lookup-table rows. The check verifies that the seed imports the expected array and maps over it when building seed rows.
Current coverage includes roles, permissions, artist/event/event-series/venue lookup seeds, and media types.
Database-backed row parity is still deferred until the CI database path is stable enough.
Server Error And Email Boundaries
Section titled “Server Error And Email Boundaries”The verifier checks the source translation sheets, but it does not yet scan backend source for every messageKey usage.
New user-facing backend error migrations should therefore add targeted route, middleware, or resolver tests when a missing
or wrong key would be user-visible.
Email copy adapters are the testable boundary for automated email localization. Tests should prove the adapter resolves supported locales, falls back to the default locale, and formats ICU values before the React Email template receives copy.
Reusable component label maps follow a similar split: namespace files are verified by verify:i18n, while component
tests prove the label map shape, default labels, legacy scalar props, and formatter behavior.
Deferred Surfaces
Section titled “Deferred Surfaces”Known deferred i18n guardrails:
- Auth permission display coverage until a user-facing admin/permissions UI exists.
- Database-backed checks proving seeded lookup-table rows match canonical source arrays exactly.
- Static analysis that matches backend
messageKeyusage toserver--errorsentries. - A manifest or structural verifier for large reusable component label-map contracts.
- Targeted tests for CDN publish/prune behavior before enabling AWS i18n CDN publishing.
- A decision on whether generated i18n asset validation should become its own named CI job.
The internal dev-debug role is intentionally excluded from checked role.* translation coverage.