Engineering guide

Internationalization Architecture and Workflow

Structure internationalized software with externalized messages, Unicode, RTL, locale negotiation, formatting services, explicit fallbacks, and staged delivery.

Reviewed 19 July 2026 · Editorial owner: localization.guide

Internationalization and localization are separate responsibilities

Internationalization (i18n)

Engineering work that makes adaptation possible without rewriting core features: message extraction, Unicode-safe storage, flexible layouts, locale negotiation, formatting services, bidi support, and testable fallbacks.

Localization (l10n)

Adaptation for a specific audience: translation, terminology, imagery, units, legal content, market conventions, locale data, linguistic review, and regional release decisions.

Formatting is not translation, and validation is not formatting. Keep those concerns independently testable so a copy update cannot alter price logic and a locale change cannot mutate stored business data.

Recommended application boundaries

LayerOwnsMust not own
DomainCanonical amounts, dates, identifiers, rulesTranslated labels or display strings
MessagesKeys, plurals, selects, context, translator notesNumber/date calculations
Locale servicesNumbers, dates, lists, display names, collationBusiness validation decisions
Regional validationAddress/phone metadata and user input rulesUI translation
PresentationLayout, direction, accessible labels, componentsLocale inference from hidden globals

Externalize complete messages

A translator needs the complete thought and its grammatical context. Avoid concatenating fragments such as "You have " + count + " items". Use message syntax that preserves variables, plural branches, and select branches, and attach notes explaining UI context and variable meaning.

// messages/en.json
{
  "cart.summary": "{count, plural, =0 {Your cart is empty} one {# item} other {# items}}",
  "invite.sent": "Invitation sent to {recipientName}"
}

// Keep stable semantic keys. Do not use the English sentence as the key.
// Translator note: recipientName is a person or team display name.
  • Validate that every locale has the same variables and required plural/select branches.
  • Escape or sanitize variables according to their output context.
  • Do not split links, emphasis, or placeholders in ways that force English word order.

Use Unicode throughout—and normalize intentionally

Store, transport, search, render, and log Unicode without lossy conversions. Visually identical text can have different code-point sequences, so choose a documented normalization policy for comparisons and identifiers. Normalization is not transliteration, case folding, or accent removal; apply those separately and only when the product contract requires them.

const displayName = input.normalize("NFC");

// Search normalization is a product decision, not a display mutation.
const searchKey = displayName.normalize("NFKC").toLocaleLowerCase(locale);

// Preserve the original user-visible value when normalization would surprise them.

Font coverage is part of the architecture. Define fallback stacks and test shaped scripts, combining marks, variation selectors, emoji sequences, and missing-glyph behavior with production fonts.

Design for RTL and mixed-direction content

  • Set the document or component base direction with the HTML `dir` attribute.
  • Use CSS logical properties such as `margin-inline-start`, `padding-inline`, and `border-inline-end`.
  • Use dir="auto" or bidi isolation for user-generated names, phone numbers, codes, and other mixed-direction strings.
  • Mirror arrows that express direction; do not mirror universal media controls, numbers, or brand marks without a design reason.
  • Test focus order and reading order independently from visual mirroring.
export function UserLabel({ value }: { value: string }) {
  return <bdi dir="auto">{value}</bdi>;
}

// Prefer logical layout utilities/properties.
// padding-inline-start: 1rem;
// inset-inline-end: 0;

A small Next.js reference structure

app/
  [lang]/
    layout.tsx          # validates locale; sets lang and dir
    page.tsx
i18n/
  config.ts             # supported locales and default fallback
  negotiate.ts          # URL/cookie/header negotiation policy
  messages.ts           # allowlisted dynamic imports
  formatters.ts         # Intl number/date/list helpers
messages/
  en.json
  de.json
  ar.json
lib/
  address/              # versioned regional metadata
  phone/                # parsing/validation boundary
tests/
  messages.test.ts
  locale-fixtures.test.ts
  visual/
export const supportedLocales = ["en", "de", "ar"] as const;
export type AppLocale = (typeof supportedLocales)[number];

export async function loadMessages(locale: AppLocale) {
  switch (locale) {
    case "de": return (await import("../messages/de.json")).default;
    case "ar": return (await import("../messages/ar.json")).default;
    default: return (await import("../messages/en.json")).default;
  }
}

Make fallback behavior explicit

Unsupported locale

Redirect or negotiate to a documented default, preserve the requested route where possible, and never load arbitrary import paths from an unchecked URL segment.

Missing message

Log a structured error with locale and key, fall back to the source locale in non-regulated UI, and fail the build for critical or newly introduced gaps.

Incomplete regional data

Use flexible input and generic display rather than applying a neighboring country’s validation rules.

Formatter/runtime failure

Use a safe, labeled fallback while preserving canonical data; do not invent a localized value by string replacement.

Localization delivery as a staged system

  1. Source design: write complete messages, define variables, allow expansion, and design bidi-safe components.
  2. Extraction: generate catalogs and reject duplicate, obsolete, or structurally invalid messages.
  3. Translation: provide screenshots, descriptions, terminology, constraints, and plural/select context.
  4. Review: perform linguistic and subject-matter review on complete workflows, not isolated strings.
  5. Integration: validate placeholders, compile messages, and package only allowlisted locale resources.
  6. Locale QA: run pseudo, automated locale fixtures, visual tests, accessibility checks, and regional devices.
  7. Release and monitoring: gate by locale, track fallback/missing-key errors, collect local feedback, and schedule data updates.

Primary and official sources