PureDevTools

JavaScript Intl API Reference

All Intl classes — NumberFormat, DateTimeFormat, Collator, PluralRules, RelativeTimeFormat, ListFormat, Segmenter — with options, methods, edge cases, and a live code runner

All processing happens in your browser. No data is sent to any server.
ClassESPurpose
Intl.NumberFormatES2015Currency, percent, compact, unit formatting
Intl.DateTimeFormatES2015Date/time formatting with timezone & range
Intl.CollatorES2015Locale-aware string sorting & search
Intl.PluralRulesES2016Plural categories: one / few / many / other
Intl.RelativeTimeFormatES2020"3 days ago", "in 1 hour"
Intl.ListFormatES2021"A, B, and C" or "A, B, or C"
Intl.SegmenterES2022Grapheme clusters, word & sentence boundaries
Intl.NumberFormatFormattingES2015
new 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.DateTimeFormatFormattingES2015
new 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.RelativeTimeFormatFormattingES2020
new 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.ListFormatFormattingES2021
new 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.CollatorComparisonES2015
new 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.PluralRulesComparisonES2016
new 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.SegmenterSegmentationES2022
new 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 APINumberFormat, 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:


Overview of Intl Classes

ClassES VersionPurpose
Intl.NumberFormatES2015Format numbers: currency, percent, compact, units
Intl.DateTimeFormatES2015Format dates and times with timezone support
Intl.CollatorES2015Locale-aware string comparison and sorting
Intl.PluralRulesES2016Determine plural categories (one/other/few/many)
Intl.RelativeTimeFormatES2020Format relative times: “3 days ago”, “in 1 hour”
Intl.ListFormatES2021Format lists: “A, B, and C” or “A, B, or C”
Intl.SegmenterES2022Split 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.

Related Tools

More JavaScript Tools