Formatting & Plurals
Pluralization and number/date formatting go through the platform Intl APIs — correct for any BCP-47 locale, with no bundled CLDR data.
Pluralization
plural selects the CLDR category via Intl.PluralRules — correct for
any BCP-47 locale. Provide a <key>_plural entry as a JSON object of categories:
en/de collapse to one/other; Slavic
locales add few/many; Arabic uses the full set. Without a _plural object the base string is returned as-is — fail-honest, no anglocentric +'s' guessing.
Formatters
formatNumber, formatDate, and formatRelativeTime wrap
the matching Intl API for the active locale and take the same options it does —
so anything Intl.NumberFormat / Intl.DateTimeFormat / Intl.RelativeTimeFormat accepts works here.
formatTimeAgo(date) is the exception: it composes a relative string from
registered time.* translation keys (and falls back to formatDate past ~30
days), so it takes no options and needs those keys registered. The other three are pure Intl wrappers.
Live demo
These values are formatted by the real useI18n() hook against the active site locale (en). Switch
the language with the locale switcher in the sidebar and watch them re-render.
| Call | Output |
|---|---|
| formatNumber(1234567.89) | 1,234,567.89 |
| formatNumber(0.4267, { style: 'percent' }) | 43% |
| formatNumber(42, { style: 'currency', currency: 'EUR' }) | €42.00 |
| formatDate(date, { dateStyle: 'long' }) | March 14, 2026 |
| formatRelativeTime(-3, 'day') | 3 days ago |