Standard Library: Internationalization
|
This section documents modern ECMAScript and core browser JavaScript APIs — it is not tied to any specific framework or library (React, Vue, Angular, etc.). This content was generated with the assistance of AI and should be verified against the current ECMAScript specification and MDN documentation before relying on it in production, since JavaScript language features and browser API support continue to evolve. This section’s bibliography lists the reference material consulted while preparing these pages. |
The Intl namespace groups together a family of locale-aware formatting and comparison classes — Intl.NumberFormat, Intl.DateTimeFormat, Intl.Collator, and Intl.PluralRules. These are not part of the core
ECMAScript language (they are defined by the separate ECMA-402 standard) but are well supported across modern
browsers and Node.js, and are the right tool any time text needs to look natural to users in different locales
rather than hardcoded to one language or region.
Intl.NumberFormat
Intl.NumberFormat formats numbers — including currency amounts and percentages — the way a given locale
expects: decimal separators, thousands separators, digit scripts, and currency symbols all vary by locale.
The constructor takes an optional locale (a BCP 47 language tag such as "en-US", "fr", or an array of locales
to pick the best-supported one from) and an options object:
let euros = new Intl.NumberFormat("es", { style: "currency", currency: "EUR" });
euros.format(10); // "10,00 €"
let pounds = new Intl.NumberFormat("en", { style: "currency", currency: "GBP" });
pounds.format(1000); // "£1,000.00"
let percent = new Intl.NumberFormat(undefined, {
style: "percent",
minimumFractionDigits: 1,
maximumFractionDigits: 1,
});
[0.05, 0.75, 1].map(percent.format); // ["5.0%", "75.0%", "100.0%"] in en-US
Key options: style ("decimal" (default), "percent", or "currency", with currency required when style
is "currency"), useGrouping (set false to disable thousands separators), and the digit-count pair
minimumFractionDigits/maximumFractionDigits. format() is bound to its Intl.NumberFormat instance, so it can
be extracted as a standalone function (as percent.format is above) and passed directly to Array.prototype.map.
Intl.DateTimeFormat
Intl.DateTimeFormat formats a Date with fine-grained control over which fields appear and how: numeric or
spelled-out months, 12- vs. 24-hour clocks, time zones, and even non-Gregorian calendars.
let d = new Date("2020-01-02T13:14:15Z");
new Intl.DateTimeFormat("en-US").format(d); // "1/2/2020"
new Intl.DateTimeFormat("fr-FR").format(d); // "02/01/2020"
let opts = { weekday: "long", month: "long", year: "numeric", day: "numeric" };
new Intl.DateTimeFormat("en-US", opts).format(d); // "Thursday, January 2, 2020"
// The time in New York, formatted for a French-speaking Canadian
new Intl.DateTimeFormat("fr-CA", {
hour: "numeric",
minute: "2-digit",
timeZone: "America/New_York",
}).format(d); // "8 h 14"
Only specify the fields you want to appear (year, month, day, weekday, era, hour, minute, second,
timeZone, timeZoneName, hour12); the formatter picks a locale-appropriate layout matching the requested
fields as closely as possible. Cross-reference
Standard Library: Dates, Errors & JSON for Date construction
and the simpler toLocaleDateString()/toLocaleTimeString() methods this class supersedes when finer control is
needed.
Intl.Collator
Sorting strings "naturally" for a user is more than an ASCII comparison: Spanish sorts ñ between n and o,
Lithuanian sorts Y before J, and case/accent sensitivity varies by locale. Intl.Collator produces a
locale-aware compare() function suitable for Array.prototype.sort():
// Never sort human-readable strings without something like this:
const collator = new Intl.Collator().compare;
["a", "z", "A", "Z"].sort(collator); // ["a", "A", "z", "Z"]
// Filenames often embed numbers -- sort those numerically, not lexicographically
const filenameOrder = new Intl.Collator(undefined, { numeric: true }).compare;
["page10", "page9"].sort(filenameOrder); // ["page9", "page10"]
// Loose, accent-/case-insensitive matching
const fuzzy = new Intl.Collator(undefined, { sensitivity: "base", ignorePunctuation: true }).compare;
["food", "fool", "Føø Bar"].findIndex((s) => fuzzy(s, "foobar") === 0); // 2
Useful options: sensitivity ("base" ignores case and accents, "accent" considers accents only, "case"
considers case only, "variant" — the default — considers both), numeric (sort embedded digit runs
numerically), ignorePunctuation, and caseFirst ("upper"/"lower"). compare() is bound to its collator
instance, so it can be passed straight to sort() without a wrapper.
Intl.PluralRules
Intl.PluralRules answers the "how many plural forms does this locale need, and which one applies to this
number" question — essential for building correctly pluralized UI strings, since English’s simple
one-vs-other split doesn’t hold across languages (e.g. Polish has distinct forms for numbers ending in 2-4 vs.
5-21). This class is defined alongside the others by ECMA-402 but is not covered by the reference book above — see MDN’s
Intl.PluralRules reference for the full details.
const rules = new Intl.PluralRules("en-US");
rules.select(0); // "other"
rules.select(1); // "one"
rules.select(2); // "other"
const messages = { one: "# item", other: "# items" };
function pluralize(n) {
return messages[rules.select(n)].replace("#", n);
}
pluralize(1); // "1 item"
pluralize(5); // "5 items"
// Polish distinguishes far more plural categories than English
new Intl.PluralRules("pl").select(2); // "few"
new Intl.PluralRules("pl").select(5); // "many"
select() returns one of the CLDR plural category keywords ("zero", "one", "two", "few", "many",
"other") — which categories actually occur depends on the locale, so always look them up via select() rather
than assuming English’s two-category system generalizes.