JavaScript Intl API Reference
All Intl classes — NumberFormat, DateTimeFormat, Collator, PluralRules, RelativeTimeFormat, ListFormat, Segmenter — with options, methods, edge cases, and a live code runner
| Class | ES | Purpose |
|---|---|---|
| Intl.NumberFormat | ES2015 | Currency, percent, compact, unit formatting |
| Intl.DateTimeFormat | ES2015 | Date/time formatting with timezone & range |
| Intl.Collator | ES2015 | Locale-aware string sorting & search |
| Intl.PluralRules | ES2016 | Plural categories: one / few / many / other |
| Intl.RelativeTimeFormat | ES2020 | "3 days ago", "in 1 hour" |
| Intl.ListFormat | ES2021 | "A, B, and C" or "A, B, or C" |
| Intl.Segmenter | ES2022 | Grapheme clusters, word & sentence boundaries |
Intl.NumberFormatFormattingES2015new Intl.NumberFormat(locales?, options?)Formats numbers according to locale conventions — decimal separators, currency symbols, percent signs, compact notation (1.5M), and unit formatting (5 km/h). Create once and reuse for best performance.
Intl.DateTimeFormatFormattingES2015new Intl.DateTimeFormat(locales?, options?)Formats Date objects to locale-aware strings. Supports preset styles (dateStyle/timeStyle), individual field configuration, timezone handling, hour12 toggle, and date range formatting.
Intl.RelativeTimeFormatFormattingES2020new Intl.RelativeTimeFormat(locales?, options?)Formats a time value relative to now — '3 days ago', 'in 1 hour', 'yesterday'. Produces natural language output for any locale without hardcoded strings. You must compute the delta yourself.
Intl.ListFormatFormattingES2021new Intl.ListFormat(locales?, options?)Formats an array of strings as a locale-aware list: 'A, B, and C' (conjunction), 'A, B, or C' (disjunction), or 'A, B, C' (unit). Handles locale-specific separators and conjunctions automatically.
Intl.CollatorComparisonES2015new Intl.Collator(locales?, options?)Compares strings in a locale-sensitive manner for sorting and searching. Handles accents, case, and locale-specific ordering rules (e.g. ß in German, ø in Norwegian) correctly — unlike String.prototype.localeCompare used directly.
Intl.PluralRulesComparisonES2016new Intl.PluralRules(locales?, options?)Determines the CLDR plural category ('one', 'two', 'few', 'many', 'other') for a number in a given locale. Essential for correct pluralization — English uses 'one'/'other', but Arabic uses all six categories and Russian changes forms at 1, 2-4, and 5+.
Intl.SegmenterSegmentationES2022new Intl.Segmenter(locales?, options?)Splits text into locale-sensitive segments: grapheme clusters (user-perceived characters), word boundaries, or sentence boundaries. Solves the fundamental problem that String.length and string[i] count UTF-16 code units, not characters.
You need to display 1234567.89 as “$1,234,567.89” in the US, “1.234.567,89 €” in Germany, and “¥1,234,568” in Japan. You also need “3 days ago” in the user’s language, a sorted list that handles accented characters correctly (is “ö” after “o” or after “z”?), and a pluralization rule that knows English has “1 item / 2 items” but Arabic has six plural forms. All of this is built into Intl — zero dependencies.
Why This Reference (Not the Date Formatter)
PureDevTools has a JavaScript Date Formatter focused specifically on date formatting with Intl.DateTimeFormat. This reference covers the entire Intl API — NumberFormat, DateTimeFormat, Collator, PluralRules, RelativeTimeFormat, ListFormat, and Segmenter — with interactive examples for each. Use it when you need any locale-sensitive formatting beyond dates.
What Is the JavaScript Intl API?
The JavaScript Intl namespace provides the ECMAScript Internationalization API — a built-in suite of locale-sensitive formatting, comparison, and text processing utilities. Instead of writing fragile date strings, currency formatters, or sort comparators by hand, Intl gives you locale-correct output for any language with a single constructor call.
Key advantages of the Intl API:
- Zero dependencies — built into every modern JavaScript runtime (browser, Node.js, Deno, Bun)
- Locale-aware — handles language-specific rules for thousands of locales automatically
- Performance — constructors are expensive; instances are reusable and cache the locale data internally
- CLDR-backed — all formatting data comes from the Unicode CLDR (Common Locale Data Repository)
Overview of Intl Classes
| Class | ES Version | Purpose |
|---|---|---|
Intl.NumberFormat | ES2015 | Format numbers: currency, percent, compact, units |
Intl.DateTimeFormat | ES2015 | Format dates and times with timezone support |
Intl.Collator | ES2015 | Locale-aware string comparison and sorting |
Intl.PluralRules | ES2016 | Determine plural categories (one/other/few/many) |
Intl.RelativeTimeFormat | ES2020 | Format relative times: “3 days ago”, “in 1 hour” |
Intl.ListFormat | ES2021 | Format lists: “A, B, and C” or “A, B, or C” |
Intl.Segmenter | ES2022 | Split text into grapheme clusters, words, or sentences |
Intl.NumberFormat
Intl.NumberFormat formats numbers for display in user interfaces. It handles locale-specific grouping separators, decimal separators, currency symbols, percent signs, and more.
Common use cases
// Currency (locale-aware separators and symbol placement)
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(1234.5);
// "$1,234.50"
// German uses . for thousands, , for decimal, and puts € after
new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(1234.5);
// "1.234,50 €"
// Compact notation for large numbers
new Intl.NumberFormat('en-US', { notation: 'compact' }).format(2500000);
// "2.5M"
// Percent
new Intl.NumberFormat('en-US', { style: 'percent' }).format(0.75);
// "75%"
Performance best practice
NumberFormat instances cache locale data internally. Create them once and call .format() many times:
// ✓ Good — create once
const fmt = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
prices.map(p => fmt.format(p));
// ✗ Bad — recreates the formatter for every item
prices.map(p => new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(p));
Intl.DateTimeFormat
Intl.DateTimeFormat formats Date objects with full control over which components to show and how. Use preset dateStyle/timeStyle for common cases, or configure individual fields for precise control.
Mixing presets and fields
A common mistake is combining dateStyle/timeStyle with individual fields — this throws TypeError:
// ✗ TypeError: cannot mix dateStyle with year/month/day
new Intl.DateTimeFormat('en-US', {
dateStyle: 'long',
year: 'numeric'
});
// ✓ Use either presets or individual fields
new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
Date range formatting
formatRange() smartly collapses shared date components:
const fmt = new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
const start = new Date('2024-03-10');
const end = new Date('2024-03-15');
fmt.formatRange(start, end); // "Mar 10 – 15, 2024" (year shared, collapsed)
Intl.Collator
Intl.Collator provides locale-sensitive string comparison for sorting and searching. Standard Array.prototype.sort() uses Unicode code point order, which breaks for accented characters, case-insensitive sorting, and languages with special characters.
Why localeCompare alone is not enough
// ✗ Wrong: sorts by code point — uppercase before lowercase
['banana', 'Apple', 'cherry'].sort();
// ['Apple', 'banana', 'cherry']
// ✓ Correct: locale-aware, case-insensitive
const col = new Intl.Collator('en', { sensitivity: 'base' });
['banana', 'Apple', 'cherry'].sort(col.compare);
// ['Apple', 'banana', 'cherry']
Numeric file sorting
// ✗ Wrong: lexicographic — 'file10' before 'file2'
['file10', 'file2', 'file1'].sort();
// ['file1', 'file10', 'file2']
// ✓ Correct: numeric
const num = new Intl.Collator('en', { numeric: true });
['file10', 'file2', 'file1'].sort(num.compare);
// ['file1', 'file2', 'file10']
Intl.PluralRules
Intl.PluralRules.select() returns one of six CLDR categories: 'zero', 'one', 'two', 'few', 'many', 'other'. The exact categories available depend on the locale and type.
Building a pluralization helper
const rules = new Intl.PluralRules('en');
const messages = {
one: '{n} item in cart',
other: '{n} items in cart',
};
function cartLabel(n) {
return messages[rules.select(n)].replace('{n}', n);
}
cartLabel(1); // "1 item in cart"
cartLabel(5); // "5 items in cart"
Ordinal numbers (1st, 2nd, 3rd)
const ordinal = new Intl.PluralRules('en', { type: 'ordinal' });
const suffixMap = { one: 'st', two: 'nd', few: 'rd', other: 'th' };
function toOrdinal(n) {
return n + (suffixMap[ordinal.select(n)] ?? 'th');
}
[1, 2, 3, 4, 11, 12, 21, 22].map(toOrdinal);
// ['1st', '2nd', '3rd', '4th', '11th', '12th', '21st', '22nd']
Intl.RelativeTimeFormat
Intl.RelativeTimeFormat produces human-readable relative time strings. You must calculate the numeric delta — the API formats it.
Computing and formatting the delta
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
function getRelativeTime(date) {
const seconds = Math.round((date - Date.now()) / 1000);
const minutes = Math.round(seconds / 60);
const hours = Math.round(minutes / 60);
const days = Math.round(hours / 24);
if (Math.abs(days) >= 1) return rtf.format(days, 'day');
if (Math.abs(hours) >= 1) return rtf.format(hours, 'hour');
if (Math.abs(minutes) >= 1) return rtf.format(minutes, 'minute');
return rtf.format(seconds, 'second');
}
getRelativeTime(new Date(Date.now() - 86400000)); // "yesterday"
getRelativeTime(new Date(Date.now() + 3600000)); // "in 1 hour"
Intl.ListFormat
Intl.ListFormat handles the connector words (“and”, “or”) and separators (commas, Arabic connectors) that vary between locales.
const and = new Intl.ListFormat('en', { type: 'conjunction' });
// Edge cases
and.format([]); // ""
and.format(['one']); // "one"
and.format(['a', 'b']); // "a and b"
and.format(['a', 'b', 'c']); // "a, b, and c"
Intl.Segmenter
Intl.Segmenter solves the fundamental problem that string.length counts UTF-16 code units, not user-perceived characters. An emoji with a skin tone modifier (👋🏽) is one grapheme but 4 code units.
Grapheme cluster counting
const text = '👋🏽 Hi!';
console.log(text.length); // 7 (code units — wrong!)
const seg = new Intl.Segmenter('en', { granularity: 'grapheme' });
const chars = [...seg.segment(text)];
console.log(chars.length); // 5 (graphemes — correct!)
console.log(chars.map(s => s.segment)); // ['👋🏽', ' ', 'H', 'i', '!']
Browser support note
Intl.Segmenter is not supported in Firefox (all versions as of early 2024). For broad compatibility, check:
if (typeof Intl.Segmenter !== 'undefined') {
// use Segmenter
} else {
// fallback to grapheme-splitter library or spread [...str]
}
FAQ
How do I get the user’s locale in the browser?
const locale = navigator.language; // e.g. 'en-US'
const locales = navigator.languages; // e.g. ['en-US', 'en', 'fr']
new Intl.NumberFormat(locale).format(1234567); // uses user's locale
Can I use Intl APIs in Node.js?
Yes — all modern versions of Node.js include the full ICU dataset. Older builds (pre-13) may only have a partial ICU dataset; check with process.versions.icu. In these cases use the full-icu npm package or build Node with --with-intl=full-icu.
Are Intl APIs fast enough for production?
Yes — but avoid creating new instances inside tight loops. Construction is expensive (it parses CLDR locale data); .format() calls on an existing instance are fast. Cache formatter instances at module level or in a Map keyed by locale + options.
What locales are supported?
All modern runtimes support the BCP 47 language tag format. Common examples: 'en-US', 'de-DE', 'zh-CN', 'ja-JP', 'ar-SA', 'ru-RU'. You can check support with Intl.supportedValuesOf('calendar') and similar static methods.