Skip to content

Internationalization & Localization Testing

Internationalization (i18n) testing proves the app can be localised: strings are externalised, encodings survive the round trip, and layout accommodates translated text. Localization (l10n) testing proves a specific translation is correct: locale formats, pluralisation, gender and region conventions.

source string ──▶ message catalog ──▶ rendered locale
(en) (vi, ar, ja) + formats
date · number · currency · plural

The high-risk areas are rarely the words themselves. They are the edges: right-to-left mirroring, text expansion (German and Finnish run long), Unicode normalisation, time zones, and languages with complex plural rules such as Arabic, Russian and Polish.

Use it for

  • Every release that ships more than one locale
  • New UI strings, especially those with placeholders, plurals or gender
  • Layouts that must mirror for right-to-left languages (Arabic, Hebrew, Persian)
  • Any flow involving dates, times, numbers, currency or sorting/collation

Avoid it when

  • The product is genuinely single-locale and will stay that way
  • You are duplicating a functional test only to change the language — check the format layer instead
  • The concern is pure encoding detection with no user-visible output
Tool Ecosystem Notes
ICU MessageFormat Multi Plural, select and gender in one message syntax
CLDR / Intl Multi Platform-standard locale data for formatting
pseudo-localization Multi Fake locale to reveal unexternalised and hard-coded strings
i18next / i18n ally JS/TS Runtime i18n and editor-time missing-key linting
Babel / gettext Python Message extraction and catalog tooling
go-i18n / golang.org/x/text Go Locale-aware formatting and messages
Fluent Multi (Rust, JS, Python) Asymmetric localisation syntax for rich grammar
RTL screenshot diffing Multi Visual regression for mirrored layouts
Terminal window
npm install -D vitest
npm install intl-messageformat
import { IntlMessageFormat } from 'intl-messageformat';
new Intl.NumberFormat('de-DE').format(1234.5); // "1.234,5"
new Intl.DateTimeFormat('ja-JP').format(new Date()); // locale order
const pr = new Intl.PluralRules('ar');
// Arabic has six plural categories
pr.select(0); // "zero"
pr.select(1); // "one"
pr.select(2); // "two"
pr.select(11); // "many"
  • Never concatenate sentences from fragments. Word order differs per language; use full ICU messages with placeholders.
  • Test with pseudo-localization. A fake locale exposes hard-coded strings and text expansion before translators do.
  • Assert encodings explicitly. UTF-8 end to end; store and serve with the declared charset.
  • Test RTL as a first-class layout. Mirror the whole UI, not just flip the text direction.
  • Cover plural categories, not just one and many. Arabic, Russian and Polish have several forms.
  • Pin the time zone and locale in tests. Ambiguous environments produce false failures around dates.

Self-assessment quiz

Pick one answer per question, then press Check answers.

  1. What is the difference between i18n and l10n testing?
  2. Why is pseudo-localization useful?
  3. How many plural categories does Arabic have?
  4. What is the risk of building sentences from string concatenation?
  5. Why pin time zone and locale in i18n tests?