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 Intl — Intl.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();
Each constructor follows a consistent API pattern: instantiate with a locale string and an optional options object, then call the formatting method. The locale determines the default behavior for separators, grouping, and presentation.
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"
The locale drives the separator character and digit grouping. en-US uses commas for thousands and a period for decimals, while de-DE inverts this convention. NumberFormat handles these differences automatically, so you never need to write manual formatting logic or maintain locale-specific string manipulation code.
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, so always specify the currency code explicitly. Japanese Yen (JPY) has no decimal subdivision, and NumberFormat respects that convention automatically by rounding to whole yen and using the correct currency symbol for the locale.
Compact Notation
const compact = new Intl.NumberFormat("en-US", {
notation: "compact",
compactDisplay: "short"
});
compact.format(12345678)
// "12M"
Compact notation is useful for dashboards and summary views where space is limited. The compactDisplay option controls whether you see "12M" (short) or "12 million" (long), giving you control over the level of abbreviation. This works well for display contexts like social media counts and analytics summaries.
Significant Digits
const sig = new Intl.NumberFormat("en-US", {
maximumSignificantDigits: 3
});
sig.format(12345.67)
// "12,300"
Significant digit control is more precise than fixed decimal places when you need a consistent number of meaningful digits regardless of magnitude. This is common in scientific and financial displays.
DateTimeFormat
Intl.DateTimeFormat formats dates and times with locale-aware patterns. The constructor accepts the same locale and options pattern as NumberFormat, and the .format() method takes a Date object or timestamp and returns a formatted string for display.
Date and time styles
const fullDate = new Intl.DateTimeFormat("en-US", {
dateStyle: "long"
});
fullDate.format(new Date())
// "May 14, 2026"
Date and time styles accept "full", "long", "medium", and "short". Using dateStyle and timeStyle together is simpler than specifying individual components when you want the locale’s conventional date-time format. The two styles combine naturally into a single localized string.
const shortTime = new Intl.DateTimeFormat("en-US", {
timeStyle: "short"
});
shortTime.format(new Date())
// "5:30 PM"
The dateStyle and timeStyle options are the simplest way to format dates and times for display. When you need only specific date parts rather than the full localized format, switch to listing individual components instead of using the convenience style options.
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"
When you need only specific date parts, list them individually rather than using dateStyle. This gives you control over which components appear and in what detail. The weekday option supports "narrow", "short", and "long" forms, and each component accepts its own formatting options.
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. The timeZoneName option can be set to "short" or "long" to display the time zone alongside the formatted time.
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
The compare method returns -1, 0, or 1 following the same contract as Array.prototype.sort comparators. Pass the .compare method directly to .sort() for locale-aware ordering. This is the standard way to sort user-facing string lists in any internationalized application.
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
German collation treats umlauts according to dictionary rules, so "ä" sorts near "a" rather than after "z". The locale determines these rules, and Collator applies them without any additional code on your part. Swedish and Finnish sort "ä" near the end of the alphabet instead.
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. When you enable numeric sorting, Collator detects numeric substrings and compares them as numbers rather than character-by-character. This means "file2" correctly comes before "file10" regardless of the string length of the numeric portion.
Sensitivity
const caseInsensitive = new Intl.Collator("en-US", {
sensitivity: "base"
});
caseInsensitive.compare("A", "a") // 0 — treated as equal
Sensitivity levels are "base" (ignores case and accents), "accent" (ignores case only), "case" (ignores accents only), and "variant" (considers everything). Choose the level that matches your use case — search boxes typically want "base", while sorted lists may need "variant".
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"
English has only two plural categories: "one" and "other". Other languages like Arabic and Polish have up to six categories. PluralRules abstracts these differences so you can write a single code path for all locales. The returned category can drive which translation string you select from a message bundle.
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"
Ordinal rules differ from cardinal rules in most languages. For example, English uses "one" for 1st but "two" for 2nd, while French uses "one" only for 1er and "other" for everything else. Arabic has even more granular categories than English:
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"
Arabic distinguishes six plural forms: "zero", "one", "two", "few", "many", and "other". PluralRules handles these language-specific rules so your UI can select the correct translation string for any number in any locale.
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"
Negative values produce past-tense strings, positive values produce future-tense. The unit argument accepts "second", "minute", "hour", "day", "week", "month", "quarter", and "year".
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"
When numeric is "auto", the formatter uses natural language like "tomorrow" instead of "in 1 day". This only applies to the values 0 and ±1. Larger numbers always use the numeric form regardless of this setting. The auto mode is particularly useful for notification timestamps and chat applications.
Narrow Style
const narrow = new Intl.RelativeTimeFormat("en-US", { style: "narrow" });
narrow.format(3, "minute")
// "3 min. ago"
The style option accepts "long" (default), "short", and "narrow". Narrow style abbreviates unit names where possible, which is ideal for compact UI spaces like notification badges or timestamp displays. Each style follows locale-specific abbreviation conventions for consistency.
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"
The separator and conjunction adapt to the locale. English uses ", " and "and", while Spanish uses ", " and "y". ListFormat handles the Oxford comma rules per locale automatically without any configuration on your part. The default type is "conjunction" which joins items with the word ‘and’ in the target language.
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"
Three list types are available: "conjunction" (and), "disjunction" (or), and "unit" (no separator, just spaces). The unit type is designed for measurement combinations where a comma or conjunction would be unnatural in any language.
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"
DisplayNames is useful for language selectors, region pickers, and any UI that needs to present locale codes as human-readable labels. The type can be "language", "region", "script", or "currency". Each type maps ISO codes to their localized display form for the requested locale.
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"
Intl.Locale parses BCP 47 language tags into their components. Properties like .language, .region, and .script are available directly, and the .baseName property returns the minimal canonical form. You can also inspect .calendar, .numberingSystem, and .hourCycle on the parsed locale object.
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"
Passing options to the Locale constructor creates a new locale with Unicode extension subtags. The resulting tag can be passed to any Intl constructor to get formatting that matches the modified locale settings without overriding options at every call site.
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
| Constructor | Purpose | Key Method |
|---|---|---|
Intl.NumberFormat | Number and currency formatting | .format(n) |
Intl.DateTimeFormat | Date and time formatting | .format(date) |
Intl.Collator | String comparison and sorting | .compare(a, b) |
Intl.PluralRules | Plural category selection | .select(n) |
Intl.RelativeTimeFormat | Relative time strings | .format(v, unit) |
Intl.ListFormat | Localized list formatting | .format(array) |
Intl.DisplayNames | Language/region/script names | .of(code) |
Intl.Locale | Locale identifier object | .toString() |
Intl.DurationFormat | Duration formatting | .format(duration) |
Common Mistakes
new Intl()throws. Access constructors as properties:Intl.NumberFormat, notnew Intl().- Missing
currencywithstyle: "currency"silently falls back to decimal — always pass the ISO code. - Wrong time zone format — use IANA names like
"Asia/Tokyo", not"JST". numericmeans different things in different constructors. InCollator,numeric: trueenables natural number sorting. InRelativeTimeFormat,numeric: "auto"omits “in” for positive future values.
See Also
- /reference/built-in-objects/date/ — Date object for creating and manipulating dates
- /reference/string-methods/locale-compare/ — String locale-aware comparison
- /guides/javascript-intl-api/ — Deep dive into the Intl API ecosystem