# Internationalization (i18n) > Ship UI that works correctly in any locale without code changes. Frontend architecture guidance from fearchitect, written by Abas Turabli and last reviewed 2026-06-21. Source: https://fearchitect.com/topics/internationalization Use it as reference for the task at hand. Before changing code, check this guidance against the codebase: where the code already makes a different, deliberate choice, flag the conflict instead of rewriting it. Library APIs move faster than this guide, so confirm exact signatures in the official docs linked at the end. ## Summary Internationalization separates locale-dependent content — strings, numbers, dates, plurals — from application logic so the same code path serves every market. The `Intl` API handles formatting natively; ICU MessageFormat handles plurals and gender; CSS logical properties handle RTL. Getting this wrong at the data layer causes string-concatenation bugs that are expensive to fix late. ## Internationalization (i18n) Internationalization means engineering an application so it can be adapted to any locale without source changes. Localization (l10n) is the act of adding a specific language. The most common mistake is string concatenation: `"You have " + count + " items"` breaks the moment word order, plural rules, or grammatical gender differs — which is most languages. The fix is a message format that expresses the full sentence as a single translatable unit with embedded slots. The browser's built-in `Intl` API handles numbers, dates, relative times, and plural categories. ICU MessageFormat (used by react-intl, @formatjs/intl, and Lit's `msg`) handles sentence-level messages with placeholders, select, and plural built in. ## Rules that prevent i18n defects - Never concatenate translated strings — one message, one translatable unit. - Use ICU MessageFormat `{count, plural, one {# item} other {# items}}` for any quantity. - Format numbers and dates with `Intl.NumberFormat` and `Intl.DateTimeFormat` — never `toLocaleString()` without a locale argument. - Locale routing: serve `en-US` vs `fr-FR` via URL prefix (`/en/`) or `Accept-Language` negotiation at the edge. - RTL support requires `dir="rtl"` on the root element and CSS logical properties (`margin-inline-start`) instead of physical ones (`margin-left`). - Run pseudo-localization in CI — expands strings ~40% and adds RTL markers — to catch layout breaks before translation exists. ## Intl API and ICU MessageFormat Three `Intl` constructors cover the most common formatting needs. ICU MessageFormat (via `@formatjs/intl`) handles plurals and select — both compile to locale-aware output without any string concatenation. **Intl formatters and ICU plural message** ```ts // --- Intl: number, date, relative time, plural category --- const locale = "de-DE"; const price = new Intl.NumberFormat(locale, { style: "currency", currency: "EUR", }).format(1234.5); // "1.234,50 €" const date = new Intl.DateTimeFormat(locale, { dateStyle: "long", }).format(new Date("2026-06-21")); // "21. Juni 2026" const rel = new Intl.RelativeTimeFormat(locale, { numeric: "auto" }).format( -1, "day", ); // "gestern" // Which plural category does a number fall into for this locale? const rules = new Intl.PluralRules("ar"); // Arabic has 6 plural forms console.log(rules.select(2)); // "two" console.log(rules.select(11)); // "many" // --- ICU MessageFormat via @formatjs/intl --- // Message defined in en.json: // "cart": "{count, plural, =0 {No items} one {# item} other {# items}}" // // At runtime (react-intl / @formatjs/intl): // intl.formatMessage({ id: "cart" }, { count: 3 }) // → "3 items" // // German translator writes: // "cart": "{count, plural, one {# Artikel} other {# Artikel}}" // Same call, correct output in every locale. ``` Pass the locale explicitly to every `Intl` constructor — never rely on `toLocaleString()` without one. ICU `plural` selects the right form per locale automatically; the translator owns the whole sentence. ## Watch out: RTL and CSS logical properties Setting `dir="rtl"` on `` flips text direction but physical CSS properties (`margin-left`, `padding-right`, `border-left`) do not flip. Replace every physical side property with its logical equivalent: `margin-inline-start`, `padding-inline-end`, `border-inline-start`. Logical properties are supported in all modern browsers and cost nothing at runtime. ## Key terms - **ICU MessageFormat**: A message syntax with `{var}`, `{count, plural, …}`, and `{gender, select, …}` — one translatable unit per sentence. - **Intl API**: Browser-native namespace: `NumberFormat`, `DateTimeFormat`, `RelativeTimeFormat`, `PluralRules`, `Collator`, and more. - **locale routing**: Serving locale-specific content via URL prefix (`/fr/`) or `Accept-Language` header negotiation at the edge. - **CSS logical properties**: Flow-relative CSS like `margin-inline-start` that automatically mirrors for RTL without extra overrides. - **pseudo-localization**: Replacing strings with expanded, accented variants in CI to surface layout bugs before real translations exist. ## Related topics - [Accessibility (a11y)](https://fearchitect.com/topics/accessibility.md): WCAG 2.2 AA: semantic HTML, keyboard nav, and ARIA done right. - [SEO for Frontend](https://fearchitect.com/topics/seo.md): Rendering choices, metadata, structured data, and CWV for search ranking. - [Frontend Testing Strategy](https://fearchitect.com/topics/frontend-testing.md): Test what the user sees, not how the code is wired. - [Frontend Observability](https://fearchitect.com/topics/frontend-observability.md): Capture errors, measure real-user performance, and trace what breaks in production. - [A/B Testing & Experimentation](https://fearchitect.com/topics/ab-testing-experimentation.md): Ship variants without flicker using server-side bucketing and guardrail metrics. - [Error Boundaries & Resilience](https://fearchitect.com/topics/error-boundaries-resilience.md): Isolate render failures so one widget can't crash the page. ## Further reading - [MDN — Intl](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) - [MDN — Intl.PluralRules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules) - [MDN — CSS logical properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values) - [Unicode ICU MessageFormat 2.0 specification](https://unicode-org.github.io/icu/userguide/format_parse/messages/) - [FormatJS — @formatjs/intl](https://formatjs.github.io/docs/intl/)