Intl API
Learn how to use JavaScript's built-in Intl object to format numbers, currencies, dates, and relative times correctly for different locales, instead of hand-building formatting logic.
Different regions write numbers, dates, and currency differently — 1,234.5 in the US is 1.234,5 in Germany, and "03/04/2026" means a different date depending on where you are. The Intl API is JavaScript's built-in toolkit for formatting values the way a specific locale expects, without pulling in a third-party library.
What a locale is
A locale is an identifier that describes a language and region, following the BCP 47 format, such as "en-US" (English, United States), "de-DE" (German, Germany), "ja-JP" (Japanese, Japan), or "fr-CA" (French, Canada). Every Intl constructor takes a locale (or array of locales) as its first argument, and an options object as its second.
You can also pass undefined as the locale to use the environment's default locale (whatever the browser or runtime is configured for), which is convenient for building an app that automatically adapts to each visitor without you hardcoding a locale.
Intl.NumberFormat
Intl.NumberFormat formats plain numbers, percentages, and currency amounts according to locale conventions — the right thousands separator, decimal separator, digit grouping, and currency symbol placement are all handled for you.
For currency, you must specify both style: "currency" and a currency code (like "USD", "EUR", or "JPY") — the locale controls the formatting conventions, but the currency code controls which currency is actually shown, and the two are independent (you could format a USD amount using German number conventions, for example).
• new Intl.NumberFormat("en-US").format(1234567.891) → "1,234,567.891" • new Intl.NumberFormat("de-DE").format(1234567.891) → "1.234.567,891" • new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(49.9) → "$49.90" • new Intl.NumberFormat("en-US", { style: "percent" }).format(0.256) → "26%"
Intl.DateTimeFormat
Intl.DateTimeFormat formats a JavaScript Date object into a locale-appropriate string. The options object lets you choose exactly which pieces to show (year, month, day, hour, minute, weekday, timeZone, and so on) rather than getting an all-or-nothing format.
This is generally preferred over manually building date strings with getMonth()/getDate()/etc., because it automatically handles locale-specific ordering (month-day-year vs day-month-year), month names in the right language, and 12-hour vs 24-hour time conventions.
• new Intl.DateTimeFormat("en-US").format(new Date(2026, 8, 15)) → "9/15/2026" • new Intl.DateTimeFormat("en-GB").format(new Date(2026, 8, 15)) → "15/09/2026" • new Intl.DateTimeFormat("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" }).format(new Date(2026, 8, 15)) → "Tuesday, September 15, 2026"
Intl.RelativeTimeFormat
Intl.RelativeTimeFormat produces human-friendly relative phrases like "3 days ago" or "in 2 hours", automatically translated and grammatically correct for the given locale. You provide a numeric value and a unit, and it handles pluralization and wording.
This is the API behind the relative timestamps you see on social media posts and comment threads ("5 minutes ago", "yesterday"), and it saves you from writing brittle, locale-specific string logic by hand.
• new Intl.RelativeTimeFormat("en").format(-3, "day") → "3 days ago" • new Intl.RelativeTimeFormat("en").format(2, "hour") → "in 2 hours" • new Intl.RelativeTimeFormat("es").format(-1, "day") → "ayer" (or an equivalent Spanish phrase, style-dependent)
General Intl constructor pattern
Create the formatter object once and reuse it with .format() for every value — constructing a new Intl.NumberFormat repeatedly in a loop is wasteful. The locale argument can be a single string, an array of preferred locales (the first supported one wins), or omitted/undefined to use the runtime's default.
const formatter = new Intl.SomeFormat(locale, options);
formatter.format(value);
// Example:
const money = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
console.log(money.format(1500)); // "$1,500.00"Code Examples
This turns a raw number into a properly formatted price string with a dollar sign, thousands separator, and exactly two decimal places — all handled automatically. Expected result: $2,599.50 style: "currency" combined with currency: "USD" produces locale-correct currency formatting.
The same Date object is formatted three different ways just by changing the locale and options — no manual string concatenation of month/day/year is needed. Expected result: 3/1/2026 01/03/2026 Sunday, March 1, 2026
Locale changes the day/month ORDER; the options object changes which PIECES are shown and how.
This builds a small 'time ago' utility on top of Intl.RelativeTimeFormat: it picks the most readable unit (minutes, hours, or days) and lets the formatter produce correctly worded, correctly pluralized output. Expected result: 10 minutes ago numeric: "auto" lets the formatter use natural words like "now" instead of always saying "0 minutes ago".
const price = 2599.5;
const formatted = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
}).format(price);
console.log(formatted);const launchDate = new Date(2026, 2, 1); // March 1, 2026
const usFormat = new Intl.DateTimeFormat("en-US").format(launchDate);
const gbFormat = new Intl.DateTimeFormat("en-GB").format(launchDate);
const longFormat = new Intl.DateTimeFormat("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
}).format(launchDate);
console.log(usFormat);
console.log(gbFormat);
console.log(longFormat);function timeAgo(pastDate) {
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
const diffMs = pastDate.getTime() - Date.now();
const diffMinutes = Math.round(diffMs / 60000);
if (Math.abs(diffMinutes) < 60) return rtf.format(diffMinutes, "minute");
const diffHours = Math.round(diffMinutes / 60);
if (Math.abs(diffHours) < 24) return rtf.format(diffHours, "hour");
const diffDays = Math.round(diffHours / 24);
return rtf.format(diffDays, "day");
}
const tenMinutesAgo = new Date(Date.now() - 10 * 60000);
console.log(timeAgo(tenMinutesAgo));Important Rules
Always pass a currency code alongside style: "currency" — Intl.NumberFormat cannot guess which currency you mean from the locale alone.
Reuse a single Intl formatter instance for repeated formatting instead of constructing a new one on every call — construction has real overhead.
Intl.DateTimeFormat's options object controls WHICH date/time parts appear; the locale controls their order, language, and separators.
Prefer Intl over manual string-building for anything locale-sensitive — hand-rolled formatting almost always breaks for at least one real-world locale.
Where this shows up
E-commerce sites use Intl.NumberFormat to show prices in each visitor's currency and number style; dashboards and admin panels use Intl.DateTimeFormat so dates read naturally regardless of which country a user is in; and social feeds, chat apps, and notification systems use Intl.RelativeTimeFormat for those familiar "2 hours ago" style timestamps.
const formatted = amount.toFixed(2); // "1234.50", ignores locale entirely const formatted = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(amount);
toFixed() only controls decimal places — it never adds thousands separators, currency symbols, or adapts to a user's locale, so it is not a substitute for real currency formatting.
new Intl.NumberFormat("en-US", { style: "currency" }).format(10); // throws: currency code required new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(10);
style: "currency" without a currency code is invalid and throws a TypeError at runtime — the two options must be supplied together.
[Multiple Choice] Which Intl constructor would you use to produce output like "3 days ago" or "in 5 minutes"? A. Intl.NumberFormat B. Intl.DateTimeFormat C. Intl.RelativeTimeFormat D. Intl.Collator
C. Intl.RelativeTimeFormat — Intl.RelativeTimeFormat is specifically designed to produce relative time phrases like "3 days ago", handling both the wording and locale-correct pluralization.
[True / False] new Intl.NumberFormat("de-DE", { style: "currency", currency: "USD" }) will format a US dollar amount using German number conventions (like using a comma as the decimal separator). A. True B. False
A. True — True. The locale controls formatting conventions (separators, symbol placement) while the currency option controls which currency is displayed — they are independent, so you can mix a German locale with a US dollar amount.
Write a function formatPercent(value, locale) that takes a decimal fraction (like 0.437) and returns it formatted as a percentage string (like "43.7%") using Intl.NumberFormat for the given locale, rounded to one decimal place.
function formatPercent(value, locale) { return new Intl.NumberFormat(locale, { style: "percent", minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(value); } console.log(formatPercent(0.437, "en-US")); // "43.7%"
Use style: "percent" along with maximumFractionDigits and minimumFractionDigits both set to 1.