Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

Typed Translation Catalogues

A t(key) function that falls back to returning the key when no translation exists is a function that ships strings like error.payment.declined to end users. @frenchexdev/ddd-i18n reifies translation as a port where keys are closed unions — every consumer's t('error.payment.declined') is type-checked against the catalogue, every missing translation is a compile error before it reaches production.


What i18n Reifies

The port has two interesting design choices. Typed keys: the translation catalogue is the source of truth, the codegen emits a closed union of key literals, and consumers receive a TranslationKey type that refuses unknown keys. A rename in the catalogue propagates as a compile error at every consumer; an addition appears in the union the next time the codegen runs.

Typed params: ICU message format supports placeholders ({count, plural, one {# item} other {# items}}), each requiring a typed value. The codegen reads each catalogue entry, derives the params shape, and types t('cart.itemCount', { count: 3 }) so passing a string where a number is expected fails at compile time. The catalogue stops being a YAML blob that breaks at runtime when the params drift; it becomes a typed contract.


The Runtime: ddd-i18n and adapter

Two packages — ddd-i18n, ddd-i18n-icu-adapter — both M4/M5 stubs pinned to I18nTranslationRequirement. The ICU adapter is the standard substrate; alternative adapters (gettext, custom) satisfy the same port.


The Analyzer: ddd-i18n-analyzer

Spec-first (spec.ts). Priority Medium. Two info-severity naming rules — DDD-I18N-001 for the Translator suffix on the port class, DDD-I18N-002 for the I18n suffix on the key namespace.

The deeper invariants (typed keys, typed params) live in the codegen-emitted catalogue types — TypeScript itself enforces the contract once the codegen has run. The analyzer's job is the surface-level naming discipline; the codegen's job is the typing.


  • Consumed by @Notification for email bodies, push payloads, WebSocket frames.
  • Consumed by application-service boundary code that shapes HTTP responses.
  • May be invoked from a @DomainEvent when an event needs a human-readable summary — though the typical pattern keeps the translation at the boundary, not in the event itself.
  • Lives behind @Port/@Adapter; the ICU adapter satisfies the conformance suite; the typed catalogue types are the codegen's contribution to the application surface.

Back to the series index.

⬇ Download