Background
PR #4806 added Czech (LanguageCode.cs) translations for configurable operations. To do so it had to edit ~20 core source files (ShippingCalculator, ShippingEligibilityChecker, all promotion actions/conditions, entity duplicators, payment/fulfillment handlers, collection filters) and append a { languageCode, value } entry to every inline LocalizedString[] array.
// packages/core/src/config/shipping-method/default-shipping-calculator.ts
description: [
{ languageCode: LanguageCode.en, value: 'Default Flat-Rate Shipping Calculator' },
{ languageCode: LanguageCode.cs, value: 'Výchozí kalkulátor dopravy s pevnou sazbou' }, // ← added
],
Problem
Inline LocalizedString[] arrays mean every new locale linearly bloats every config file. The translation content (a translator's concern) is interleaved with operation logic (a developer's concern), in the wrong file type, with no tooling. This pattern doesn't scale past a couple of languages and diverges from how the rest of the packages handle i18n (lingui .po catalogs — e.g. packages/dashboard/src/i18n/locales/cs.po).
Proposed approach
Extract configurable-operation labels/descriptions out of the .ts source into dedicated locale catalogs, keep LanguageCode.en inline as the source/default, and use lingui (consistent with the dashboard & other packages). At build time, copy/merge the compiled catalogs into the dashboard.
Sketch:
- Source
.ts keeps only the English default (or a stable message key).
- Translations live in per-locale catalog files owned by translators, not developers.
- A build step compiles + copies catalogs into the dashboard bundle.
- Server resolves
LocalizedString for a given languageCode from the catalog rather than from the inline array.
Open questions / decisions
- Key strategy — derive translation keys from operation
code + arg name (e.g. default-shipping-calculator.args.rate.label), or use the English string as the msgid (lingui default)?
- Backwards compatibility — third-party plugins still ship inline
LocalizedString[]. The resolver must fall back to inline arrays so existing plugins keep working.
- Server vs dashboard — these labels are consumed both server-side (Admin API responses) and in the dashboard UI. Where does resolution happen, and how do catalogs reach both?
- Build wiring — exact mechanism for the "copy to dashboard at build" step.
Scope
Related to #4806.
Background
PR #4806 added Czech (
LanguageCode.cs) translations for configurable operations. To do so it had to edit ~20 core source files (ShippingCalculator,ShippingEligibilityChecker, all promotion actions/conditions, entity duplicators, payment/fulfillment handlers, collection filters) and append a{ languageCode, value }entry to every inlineLocalizedString[]array.Problem
Inline
LocalizedString[]arrays mean every new locale linearly bloats every config file. The translation content (a translator's concern) is interleaved with operation logic (a developer's concern), in the wrong file type, with no tooling. This pattern doesn't scale past a couple of languages and diverges from how the rest of the packages handle i18n (lingui.pocatalogs — e.g.packages/dashboard/src/i18n/locales/cs.po).Proposed approach
Extract configurable-operation labels/descriptions out of the
.tssource into dedicated locale catalogs, keepLanguageCode.eninline as the source/default, and use lingui (consistent with the dashboard & other packages). At build time, copy/merge the compiled catalogs into the dashboard.Sketch:
.tskeeps only the English default (or a stable message key).LocalizedStringfor a givenlanguageCodefrom the catalog rather than from the inline array.Open questions / decisions
code+ arg name (e.g.default-shipping-calculator.args.rate.label), or use the English string as the msgid (lingui default)?LocalizedString[]. The resolver must fall back to inline arrays so existing plugins keep working.Scope
LocalizedString[](no breaking change for plugins)Related to #4806.