Frontend5 min read

Internationalization (i18n)

Internationalize web apps with react-i18next - ICU message format, pluralization, date/number formatting, RTL layouts, and locale detection.

i18nLocalizationreact-i18nextICUPluralization

skill: Internationalization (i18n) category: Frontend tags: [i18n, Localization, react-i18next, ICU, Pluralization] readingTime: 5

Internationalization is a requirement, not a feature. Design for it from day one - retrofitting i18n onto an existing app is 5-10x more work than building it in. The hardest parts are pluralization rules (they differ by language), date/time formatting (calendar systems, timezone), and RTL layouts (Arabic, Hebrew).

Workflow

  1. Set up react-i18next with ICU formatreact-i18next is the standard React i18n library. ICU message format handles plurals, genders, and select across 200+ language rules.
  2. Write ICU message strings for plurals and gendersPluralization rules differ by language - English has 2 forms, Russian has 4, Arabic has 6. ICU format handles all cases without code changes.
  3. Format dates, numbers, and currencies with IntlDate formats vary by locale (MM/DD/YYYY vs. DD.MM.YYYY vs. YYYY年MM月DD日), and currency formatting is complex. The built-in Intl API handles all cases.
  4. Support RTL layouts with logical propertiesArabic, Hebrew, and Farsi are right-to-left. CSS logical properties (margin-inline-start vs. margin-left) flip correctly for both LTR and RTL without duplicating styles.
  5. Load translations lazily and manage keysLoading all translation files on startup bloats the initial bundle. Namespace splitting loads only what the current route needs.

Output Format

i18n: react-i18next with ICU format, plural/gender messages, Intl API for dates/currencies, CSS logical properties for RTL, and namespace-split lazy loading.

i18n.init({ use: [ICU, LanguageDetector], resources, fallbackLng })
ICU: {count, plural, one {# item} other {# items}}
Intl.DateTimeFormat(locale).format(date)
document.documentElement.dir = i18n.dir()
namespace split: common.json + route-specific.json

Examples

Input

Output

Edge Cases & Gotchas

  • Missing translation keys - react-i18next returns the key string when a translation is missing. In development, enable missingKeyHandler to log missing keys. In CI, fail on missing keys with a custom detector.
  • Pseudo-localization testing - Before professional translation, use pseudo-localization (replace characters with accented versions, add ~30% length) to catch layout breaks from longer translations (German is 30% longer than English).
  • Number input from RTL users - Arabic users enter numbers left-to-right (1234) even in RTL layouts. Don't mirror input direction for numeric fields.
  • Currency without symbol - Intl.NumberFormat with currency shows the currency symbol. For prices where currency is shown elsewhere, use style: "decimal" and format separately.

When to Break These Rules

  • Use simple JSON + t() function without ICU format for small apps with limited locales - the ICU parser adds ~10KB and is overkill for apps that only need simple string substitution.
  • Use a localization platform (Phrase, Lokalise, Crowdin) for professional translation workflows - managing JSON files in git is only sustainable for small teams with in-house translators.

Reference Files

  • https://react.i18next.com/
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.