Modern React 39: Internationalization and formatting
This is part 39 of the Modern React development series.
Internationalization starts when UI stops assuming one language, date shape, number format, currency, plural rule, and text direction. React renders whatever strings and formatted values you give it, so formatting needs a clear home.
Concept
Internationalization, often shortened to i18n, is the work of making software adaptable to different languages and regions. Localization, often shortened to l10n, is the specific translation and regional formatting for one locale.
Terms
- i18n: Internationalization, a numeronym where 18 stands for the letters between i and n.
- l10n: Localization, adapting text and formats for one locale.
- Locale: A language and regional preference such as
en-USorfr-CA. - Formatter: A function or object that turns values into locale-aware display text.
Mental model
Think of locale as a lens over display values. Store facts as data, then format them through the user’s lens at the edge where text appears.
How it is used
Use internationalization for dates, times, currencies, numbers, names, addresses, plural text, translated labels, route metadata, validation messages, and content that will be read by users in different locales.
How to use it
- Keep raw facts in stable machine formats, such as ISO date strings, numbers, and currency codes.
- Choose a locale source from route, user preference, browser preference, or account settings.
- Format values close to display with
Intlor the project’s i18n library. - Keep translated message keys stable and meaningful.
- Test long text, missing translations, right-to-left layouts, and plural cases.
Example: Currency formatter
import type { ReactElement } from "react";
export function Price({ cents, currency, locale,}: { cents: number; currency: string; locale: string;}): ReactElement { const amount = cents / 100; const formatted = new Intl.NumberFormat(locale, { style: "currency", currency, }).format(amount);
return <span>{formatted}</span>;}The component receives facts and locale context, then formats at the display boundary.
Example: Date formatter
import type { ReactElement } from "react";
export function AppointmentTime({ startsAt, locale,}: { startsAt: string; locale: string;}): ReactElement { const date = new Date(startsAt); const formatted = new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short", }).format(date);
return <time dateTime={startsAt}>{formatted}</time>;}dateTime preserves the machine-readable value while the visible text follows the locale.
Details to watch
- Raw values: Do not store already formatted currency or dates as the source of truth.
- Text length: Translated strings can be much longer than English.
- Plural rules: Pluralization is locale-specific. Avoid hand-built English-only suffix logic.
- Server and client: Keep locale selection consistent across server rendering and hydration.
Series navigation
- Previous: Part 38: Auth, roles, and protected UI
- Next: Part 40: Deployment, observability, and feature flags
- Series index: Modern React development