jsguides

Intl

The Intl object is the namespace for the ECMAScript Internationalization API. It provides language-sensitive formatting and comparison across numbers, dates, strings, and lists. You access each capability through constructors attached to IntlIntl.NumberFormat, Intl.DateTimeFormat, Intl.Collator, and so on.

A critical point: Intl is not a constructor. Calling new Intl() throws a TypeError. Each capability lives as a property of the Intl object.

Accessing Intl Constructors

All Intl APIs follow the same pattern — you call the constructor as a property of Intl, optionally passing a locale string and configuration options.

// These work
const nf = new Intl.NumberFormat("en-US");
const dtf = new Intl.DateTimeFormat("en-GB");

// This throws: TypeError: Intl is not a constructor
// const bad = new Intl();

NumberFormat

Intl.NumberFormat formats numbers according to locale conventions — decimal separators, grouping, currency symbols, and more.

Basic Usage

const nf = new Intl.NumberFormat("en-US");
nf.format(1234567.89)
// "1,234,567.89"

const nfDe = new Intl.NumberFormat("de-DE");
nfDe.format(1234567.89)
// "1.234.567,89"

Currency Formatting

Pass style: "currency" and a valid ISO 4217 currency code:

const cf = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
});
cf.format(1234.56)
// "$1,234.56"

// Japanese Yen — no decimals by default
const jpy = new Intl.NumberFormat("ja-JP", {
  style: "currency",
  currency: "JPY"
});
jpy.format(1234.56)
// "¥1,235"

Omitting the currency option when using style: "currency" silently falls back to decimal formatting — no error is thrown. Always specify the currency code explicitly.

Compact Notation

const compact = new Intl.NumberFormat("en-US", {
  notation: "compact",
  compactDisplay: "short"
});
compact.format(12345678)
// "12M"

Significant Digits

const sig = new Intl.NumberFormat("en-US", {
  maximumSignificantDigits: 3
});
sig.format(12345.67)
// "12,300"

DateTimeFormat

Intl.DateTimeFormat formats dates and times with locale-aware patterns.

Date and Time Styles

const fullDate = new Intl.DateTimeFormat("en-US", {
  dateStyle: "long"
});
fullDate.format(new Date())
// "May 14, 2026"

const shortTime = new Intl.DateTimeFormat("en-US", {
  timeStyle: "short"
});
shortTime.format(new Date())
// "5:30 PM"

Individual Components

const parts = new Intl.DateTimeFormat("en-US", {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric"
});
parts.format(new Date())
// "Thursday, May 14, 2026"

Time Zones

const nyTime = new Intl.DateTimeFormat("en-US", {
  timeStyle: "short",
  timeZone: "America/New_York"
});
nyTime.format(new Date())
// "5:30 PM EDT"

Use IANA time zone names like "America/New_York", not abbreviations like "EST". Invalid time zone strings may be silently ignored.

Collator

Intl.Collator provides locale-aware string comparison. The .compare(a, b) method returns -1, 0, or 1.

const collator = new Intl.Collator("en-US");
collator.compare("a", "b")  // -1
collator.compare("b", "a")  // 1
collator.compare("a", "a")  // 0

Sorting with Collator

const items = ["banana", "cherry", "apple"];

// Locale-aware sort
items.sort(new Intl.Collator("de-DE").compare)
// ["apple", "banana", "cherry"] — de-DE handles umlauts

Numeric Sorting

const files = ["file1", "file10", "file2"];
files.sort(new Intl.Collator("en-US", { numeric: true }).compare)
// ["file1", "file2", "file10"]

Without numeric: true, "file10" comes before "file2" because comparison stops at the first differing character.

Sensitivity

const caseInsensitive = new Intl.Collator("en-US", {
  sensitivity: "base"
});
caseInsensitive.compare("A", "a")  // 0 — treated as equal

PluralRules

Intl.PluralRules maps numbers to plural categories for localized grammar.

const pr = new Intl.PluralRules("en-US");
pr.select(0)   // "other"
pr.select(1)   // "one"
pr.select(2)   // "other"

Ordinal Suffixes

const ordinal = new Intl.PluralRules("en-US", { type: "ordinal" });
ordinal.select(1)  // "one"   → "1st"
ordinal.select(2)  // "two"   → "2nd"
ordinal.select(3)  // "few"   → "3rd"
ordinal.select(4)  // "other" → "4th"
ordinal.select(21) // "one"   → "21st"

Arabic has more granular categories:

const ar = new Intl.PluralRules("ar");
ar.select(0)   // "zero"
ar.select(1)   // "one"
ar.select(2)   // "two"
ar.select(6)   // "few"
ar.select(100) // "other"

RelativeTimeFormat

Intl.RelativeTimeFormat formats relative time spans like “5 seconds ago” or “in 2 weeks”.

const rtf = new Intl.RelativeTimeFormat("en-US");

rtf.format(5, "second")   // "in 5 seconds"
rtf.format(-5, "second")  // "5 seconds ago"
rtf.format(1, "day")     // "in 1 day"
rtf.format(-1, "day")     // "1 day ago"

The numeric Option

By default (numeric: "always"), both past and future times use “in X” / “X ago”. Switch to "auto" to use natural phrases for positive future values:

const autoRtf = new Intl.RelativeTimeFormat("en-US", { numeric: "auto" });
autoRtf.format(1, "day")  // "tomorrow" — not "in 1 day"
autoRtf.format(-1, "day") // "yesterday"

Narrow Style

const narrow = new Intl.RelativeTimeFormat("en-US", { style: "narrow" });
narrow.format(3, "minute")
// "3 min. ago"

ListFormat

Intl.ListFormat formats arrays into localized, grammatically correct lists.

const lf = new Intl.ListFormat("en-US");

lf.format(["A", "B", "C"])
// "A, B, and C"

// Spanish uses different conjunction
const esLf = new Intl.ListFormat("es");
esLf.format(["A", "B", "C"])
// "A, B y C"

List Types

// Disjunction (or)
lf.format(["A", "B"], { type: "disjunction" })
// "A or B"

// Unit (measurements)
const unitLf = new Intl.ListFormat("en-US", { type: "unit" });
unitLf.format(["5 lbs", "3 oz"])
// "5 lbs and 3 oz"

DisplayNames

Intl.DisplayNames provides consistent translations of language, region, and script codes.

const dn = new Intl.DisplayNames("en-US", { type: "language" });

dn.of("en")      // "English"
dn.of("ja")      // "Japanese"
dn.of("zh-Hant") // "Chinese (Traditional)"

// Region names
const regionDn = new Intl.DisplayNames("en-US", { type: "region" });
regionDn.of("US") // "United States"
regionDn.of("GB") // "United Kingdom"

Locale

Intl.Locale represents a Unicode locale identifier. It lets you inspect and manipulate locale tags programmatically.

const loc = new Intl.Locale("en-US");
loc.language  // "en"
loc.region   // "US"
loc.baseName  // "en-US"
loc.toString() // "en-US"

Modifying a Locale

Pass options as a second argument to derive a new locale:

const loc2 = new Intl.Locale("de-DE", {
  calendar: "islamic",
  hourCycle: "h24"
});
loc2.calendar   // "islamic"
loc2.hourCycle  // "h24"
loc2.toString() // "de-DE-u-ca-islamic-hc-h24"

DurationFormat

Intl.DurationFormat formats duration objects into human-readable strings. Browser support is limited — check compatibility tables before using in production.

const df = new Intl.DurationFormat("en-US", { style: "long" });
df.format({
  years: 1,
  months: 2,
  days: 3,
  hours: 4,
  minutes: 5,
  seconds: 6
})
// "1 year, 2 months, 3 days, 4 hours, 5 minutes, and 6 seconds"

Quick Reference

ConstructorPurposeKey Method
Intl.NumberFormatNumber and currency formatting.format(n)
Intl.DateTimeFormatDate and time formatting.format(date)
Intl.CollatorString comparison and sorting.compare(a, b)
Intl.PluralRulesPlural category selection.select(n)
Intl.RelativeTimeFormatRelative time strings.format(v, unit)
Intl.ListFormatLocalized list formatting.format(array)
Intl.DisplayNamesLanguage/region/script names.of(code)
Intl.LocaleLocale identifier object.toString()
Intl.DurationFormatDuration formatting.format(duration)

Common Mistakes

  • new Intl() throws. Access constructors as properties: Intl.NumberFormat, not new Intl().
  • Missing currency with style: "currency" silently falls back to decimal — always pass the ISO code.
  • Wrong time zone format — use IANA names like "Asia/Tokyo", not "JST".
  • numeric means different things in different constructors. In Collator, numeric: true enables natural number sorting. In RelativeTimeFormat, numeric: "auto" omits “in” for positive future values.

See Also