Intl API
debt(d5/e3/b3/t5)
Closest to 'specialist tool catches it' (d5). ESLint and TypeScript can detect hardcoded date format strings and missing locale parameters via linting rules, but detection is not automatic—requires configured rules and code review to catch patterns like `new Date().toLocaleDateString()` without locale or manual currency formatting with `.toFixed(2)`. Most misuses (caching formatters, choosing wrong unit for RelativeTimeFormat) require runtime testing or careful inspection.
Closest to 'simple parameterised fix' (e3). The quick_fix is to replace hardcoded formatting with `Intl.NumberFormat`, `Intl.DateTimeFormat`, or `Intl.RelativeTimeFormat` calls—typically a single-line or few-line substitution per occurrence. However, if the codebase has many hardcoded formats scattered across files, or if formatters are being reconstructed in loops (requiring caching refactors), effort scales up slightly. Most common cases stay within e3 range.
Closest to 'localised tax' (b3). The Intl API choice primarily affects formatting logic in UI components and output routines; it does not impose system-wide architectural constraints. Teams using it consistently pay a small productivity cost (learning the API surface, caching patterns, locale handling), but this burden is localized to i18n-aware modules. The rest of the codebase is unaffected by this choice.
Closest to 'notable trap' (t5). The misconception—that you need an external library like moment.js or date-fns for proper localization—is documented and most developers eventually learn the Intl API is built-in. However, the API has several gotchas: forgetting to cache formatters (performance trap), not using `navigator.language` (silently defaults to en-US), omitting the `unit` parameter in RelativeTimeFormat (breaks), and overlooking `Intl.Collator` for string sorting (silent wrong order with accented characters). These are documented but require learning; they don't contradict similar concepts, making this a tier-5 rather than tier-7 trap.
Also Known As
TL;DR
Explanation
The Intl API provides: Intl.NumberFormat (numbers, currencies, percentages), Intl.DateTimeFormat (dates, times with locale-aware formatting), Intl.RelativeTimeFormat ('2 days ago', 'in 3 hours'), Intl.ListFormat ('A, B, and C'), Intl.Collator (locale-aware string sorting), Intl.PluralRules (correct pluralisation per locale), and Intl.Segmenter (word/sentence boundaries). All major browsers support it. It handles locale-specific rules like Arabic RTL, German ordinal numbers, Japanese date formats, and Indian number grouping.
Common Misconception
Why It Matters
Common Mistakes
- Creating a new Intl.NumberFormat on every call in a loop — formatters are expensive to construct; cache them.
- Not using navigator.language for the user's locale — hardcoding 'en-US' breaks for international users.
- Using Intl.RelativeTimeFormat without specifying the unit — 'days', 'hours' must be explicit.
- Forgetting Intl.Collator for sorting strings — standard < operator gives wrong order for accented characters.
Code Examples
// Manual formatting — locale-unaware:
const price = '$' + amount.toFixed(2); // Wrong for non-US users
const date = month + '/' + day + '/' + year; // Ambiguous internationally
// Wrong sort order for accented chars:
words.sort((a, b) => a < b ? -1 : 1); // Wrong for 'café' vs 'can'
// Intl API — automatic locale handling:
const price = new Intl.NumberFormat(navigator.language, {
style: 'currency', currency: 'USD'
}).format(amount); // '1,234.56' (US) or '1.234,56 $' (DE)
const date = new Intl.DateTimeFormat(navigator.language, {
year: 'numeric', month: 'long', day: 'numeric'
}).format(new Date()); // 'March 16, 2026' or '16. März 2026'
const relative = new Intl.RelativeTimeFormat(navigator.language)
.format(-2, 'day'); // '2 days ago' or 'il y a 2 jours'
words.sort(new Intl.Collator(navigator.language).compare); // Correct sort