Documentation
adrs/047-translation-message-id-strategy.md
ADR-047: Static Message Id Strategy for UI Translations
Status
Accepted
Context
The assettrak UI uses react-intl with FormatJS extraction. The app was logging "missing translation" warnings for nearly every label, even in English. Investigation showed that many components constructed message ids dynamically (for example, formatMessage({ id: someVariable }) or interpolated ids), which prevents FormatJS from extracting those messages into translations/en.json. As a result, the runtime could not find translations even though default English strings were displayed.
We also identified dynamic translation attempts for values that are not a finite, known set at build time (for example, process names, printer names, and unit names).
Decision
- All message ids must be static. Use literal string ids in
FormattedMessageandintl.formatMessage. No dynamically constructed ids. - Finite reference data uses static mapping helpers. For known enumerations (shipment status/type, process category/occurrence, attribute types), define a mapping using
defineMessagesand a normalizer. Use helper functions to translate these values (for example,shipmentI18n.ts,processI18n.ts,attributeI18n.ts). - Truly dynamic data stays raw. Names coming from user data or infinite sets (process names, unit names, printer names, template names) are rendered directly without translation attempts.
- English default strings remain in
defaultMessage. This is the fallback for untranslated locales and is the source for extraction. - Extraction is the source of truth. Use
npm run intl-buildto generatetranslations/en.jsonand compile thelang/output. Missing id warnings should indicate real gaps, not dynamic ids.
Consequences
- Pros: FormatJS extraction now captures all UI strings; missing translation warnings are reduced to real gaps. Enumerated values display consistently in English and are ready for localization.
- Cons: Any new status/type/category requires updating the corresponding mapping helper. Dynamic labels (for example, process names) are not translated until a dedicated localization mechanism exists.
- Follow-up: The tenant terminology table is not integrated yet; when it is, a new ADR should document how runtime terminology overrides are layered onto the static id system.