JavaScript Internationalization with Intl
Learn the ECMAScript Internationalization API: locale negotiation, number and date formatting, comparison, lists, plural rules, relative time, locale identifiers, display names, segmentation, formatter parts, and capability detection.
Advanced
Output
Ready to run.
Quick check
What is Intl.NumberFormat responsible for?
const value=1234567.89; const formatted=new Intl.NumberFormat("en-US").format(value); console.log(formatted); document.querySelector("#output").textContent=formatted;
Output
Ready to run.
Quick check
Which option selects currency formatting?
const formatted=new Intl.NumberFormat("en-US",{style:"currency",currency:"USD"}).format(1234.5); console.log(formatted); document.querySelector("#output").textContent=formatted;
Output
Ready to run.
Quick check
What does percent style do?
const formatted=new Intl.NumberFormat("en-US",{style:"percent"}).format(0.256); console.log(formatted); document.querySelector("#output").textContent=formatted;
Output
Ready to run.
Quick check
Why is timeZone specified here?
const date=new Date("2026-09-27T12:30:00Z"); const formatted=new Intl.DateTimeFormat("en-US",{dateStyle:"medium",timeZone:"UTC"}).format(date); console.log(formatted); document.querySelector("#output").textContent=formatted;
Output
Ready to run.
Quick check
What does Collator provide?
const collator=new Intl.Collator("en",{sensitivity:"base"}); const result=collator.compare("resume","résumé"); console.log(result); document.querySelector("#output").textContent=String(result);
Output
Ready to run.
Quick check
What does ListFormat solve?
const formatted=new Intl.ListFormat("en",{style:"long",type:"conjunction"}).format(["HTML","CSS","JavaScript"]); console.log(formatted); document.querySelector("#output").textContent=formatted;
Output
Ready to run.
Quick check
What does PluralRules.select return?
const rules=new Intl.PluralRules("en"); const categories=[1,2,5].map(n=>rules.select(n)); console.log(categories); document.querySelector("#output").textContent=categories.join(", ");
Output
Ready to run.
Quick check
What is RelativeTimeFormat for?
const formatted=new Intl.RelativeTimeFormat("en",{numeric:"auto"}).format(-1,"day"); console.log(formatted); document.querySelector("#output").textContent=formatted;
Output
Ready to run.
Quick check
What does Intl.Locale represent?
const locale=new Intl.Locale("ar-EG"); const result={baseName:locale.baseName,language:locale.language,region:locale.region}; console.log(result); document.querySelector("#output").textContent=JSON.stringify(result);
Output
Ready to run.
Quick check
What does supportedLocalesOf report?
const requested=["fr-FR","xx-XX"]; const supported=Intl.NumberFormat.supportedLocalesOf(requested); console.log(supported); document.querySelector("#output").textContent=JSON.stringify(supported);
Output
Ready to run.
Quick check
Why use formatToParts?
const parts=new Intl.NumberFormat("en-US",{style:"currency",currency:"USD"}).formatToParts(1234.5); console.log(parts); document.querySelector("#output").textContent=parts.map(p=>p.type+":"+p.value).join(" | ");
Output
Ready to run.
Quick check
What does resolvedOptions provide?
const formatter=new Intl.NumberFormat("de-DE",{style:"currency",currency:"EUR"}); const options=formatter.resolvedOptions(); console.log(options); document.querySelector("#output").textContent=JSON.stringify({locale:options.locale,numberingSystem:options.numberingSystem,currency:options.currency});
Output
Ready to run.
Quick check
What does DisplayNames do here?
const formatter=new Intl.DisplayNames("en",{type:"language"}); const result=formatter.of("fr"); console.log(result); document.querySelector("#output").textContent=result;
Output
Ready to run.
Quick check
What does Segmenter provide?
const segmenter=new Intl.Segmenter("en",{granularity:"word"}); const segments=[...segmenter.segment("Foxipa teaches JavaScript")].map(x=>x.segment); console.log(segments); document.querySelector("#output").textContent=segments.join(" | ");
Output
Ready to run.
Quick check
What should code do when a newer Intl service is unavailable?
const output=document.querySelector("#output"); if(typeof Intl.DurationFormat!=="function"){output.textContent="Intl.DurationFormat is not available in this runtime.";}else{const f=new Intl.DurationFormat("en",{style:"long"}); const result=f.format({hours:2,minutes:30}); console.log(result); output.textContent=result;}
Output
Ready to run.
Quick check
Why reuse a formatter instance?
const formatter=new Intl.NumberFormat("en-US",{maximumFractionDigits:2}); const values=[12.3,4567.89,0.125]; const results=values.map(formatter.format,formatter); console.log(results); document.querySelector("#output").textContent=results.join(" | ");
The Intl model
ECMA-402 defines the ECMAScript Internationalization API as a family of locale-sensitive services. Intl services do not translate arbitrary application prose for you; they format, compare, segment, or expose localized representations according to standardized locale identifiers and service-specific options. The actual locale data is implementation-provided within the specification constraints, so exact punctuation, symbols, names, and wording can vary by locale and runtime.
Locale negotiation is part of the API
Most Intl constructors accept a locale or locale list plus options. The implementation resolves the requested locales against supported locale data. Use supportedLocalesOf when you need to test support explicitly, and resolvedOptions when you need to inspect the effective configuration.
Formatting is presentation, not data mutation
Intl.NumberFormat and Intl.DateTimeFormat return strings for presentation. They do not change the underlying Number or Date value. Prefer keeping application data in its semantic form and formatting it at the presentation boundary.
Use parts when markup or semantics matter
formatToParts lets applications inspect typed pieces of a localized result. This is useful when a UI needs to style, annotate, or otherwise structure portions of a formatted value without hard-coding locale-specific punctuation or ordering.
Authoring rules
Prefer Intl services over hand-written locale rules. Do not assume comma/period conventions, month names, plural categories, list punctuation, or word boundaries are universal. Keep locale-sensitive presentation at the boundary, preserve the original data, reuse formatter instances when appropriate, and feature-detect services whose availability may vary across runtimes.
Complete the ECMA-402 service surface
The core Intl constructors are only part of the API. ECMA-402 also provides locale canonicalization and capability discovery, range formatting, richer formatter options, plural ranges, locale metadata, and structured formatter output. These belong to the Intl lesson because they are part of the same internationalization service model. This section focuses on observable API behavior rather than reproducing the specification algorithms.
Output
Ready to run.
Quick check
What does Intl.getCanonicalLocales return?
const result=Intl.getCanonicalLocales(["EN-us","fr-fr"]); console.log(result); document.querySelector("#output").textContent=result.join(" | ");
Output
Ready to run.
Quick check
What is Intl.supportedValuesOf for?
const keys=["calendar","currency","timeZone","numberingSystem","language"]; const result=Object.fromEntries(keys.map(k=>[k,Intl.supportedValuesOf(k).slice(0,4)])); console.log(result); document.querySelector("#output").textContent=JSON.stringify(result);
Output
Ready to run.
Quick check
What does DateTimeFormat.formatRange do?
const f=new Intl.DateTimeFormat("en-US",{dateStyle:"medium",timeZone:"UTC"}); const result=f.formatRange(new Date("2026-09-27T00:00:00Z"),new Date("2026-09-30T00:00:00Z")); console.log(result); document.querySelector("#output").textContent=result;
Output
Ready to run.
Quick check
Why use formatRangeToParts?
const f=new Intl.DateTimeFormat("en-US",{dateStyle:"medium",timeZone:"UTC"}); const parts=f.formatRangeToParts(new Date("2026-09-27T00:00:00Z"),new Date("2026-09-30T00:00:00Z")); console.log(parts); document.querySelector("#output").textContent=parts.map(p=>`${p.type}:${p.value}`).join(" | ");
Output
Ready to run.
Quick check
What does notation:"compact" control?
const f=new Intl.NumberFormat("en-US",{notation:"compact",maximumFractionDigits:1}); const result=f.format(1234567); console.log(result); document.querySelector("#output").textContent=result;
Output
Ready to run.
Quick check
Which NumberFormat options select unit formatting?
const f=new Intl.NumberFormat("en-US",{style:"unit",unit:"kilometer-per-hour",unitDisplay:"long"}); const result=f.format(88); console.log(result); document.querySelector("#output").textContent=result;
Output
Ready to run.
Quick check
What is roundingMode responsible for?
const f=new Intl.NumberFormat("en-US",{maximumFractionDigits:0,roundingMode:"halfExpand"}); const result=f.format(2.5); console.log(result); document.querySelector("#output").textContent=result;
Output
Ready to run.
Quick check
What do NumberFormat range methods provide?
const f=new Intl.NumberFormat("en-US"); const result=f.formatRange(1200,1500); const parts=f.formatRangeToParts(1200,1500); console.log(result,parts); document.querySelector("#output").textContent=result+"\n"+parts.map(p=>p.type+":"+p.value).join(" | ");
Output
Ready to run.
Quick check
How does PluralRules.selectRange differ from select?
const rules=new Intl.PluralRules("en"); const result=rules.selectRange(1,2); console.log(result); document.querySelector("#output").textContent=result;
Output
Ready to run.
Quick check
Why use RelativeTimeFormat.formatToParts?
const f=new Intl.RelativeTimeFormat("en",{numeric:"always"}); const parts=f.formatToParts(-3,"day"); console.log(parts); document.querySelector("#output").textContent=parts.map(p=>p.type+":"+p.value).join(" | ");
Output
Ready to run.
Quick check
What does DurationFormat.formatToParts provide?
const f=new Intl.DurationFormat("en",{style:"long"}); if(typeof f.formatToParts!=="function"){document.querySelector("#output").textContent="formatToParts unavailable";}else{const parts=f.formatToParts({hours:2,minutes:30}); console.log(parts); document.querySelector("#output").textContent=parts.map(p=>p.type+":"+p.value).join(" | ");}
Output
Ready to run.
Quick check
What does Intl.Locale provide?
const locale=new Intl.Locale("en-US"); const result={base:locale.baseName,language:locale.language,region:locale.region,calendar:locale.calendar,numberingSystem:locale.numberingSystem}; console.log(result); document.querySelector("#output").textContent=JSON.stringify(result);
Output
Ready to run.
Quick check
What do maximize() and minimize() do?
const locale=new Intl.Locale("en-Latn"); const result={max:locale.maximize().toString(),min:locale.minimize().toString()}; console.log(result); document.querySelector("#output").textContent=JSON.stringify(result);
Output
Ready to run.
Quick check
Why use Locale getter methods such as getCalendars()?
const locale=new Intl.Locale("en-US"); const result={calendars:locale.getCalendars(),collations:locale.getCollations(),hourCycles:locale.getHourCycles(),numberingSystems:locale.getNumberingSystems()}; console.log(result); document.querySelector("#output").textContent=JSON.stringify(result);
Output
Ready to run.
Quick check
What does Segmenter.resolvedOptions() tell you?
const segmenter=new Intl.Segmenter("en",{granularity:"word"}); const options=segmenter.resolvedOptions(); console.log(options); document.querySelector("#output").textContent=JSON.stringify(options);
What remains implementation-dependent
ECMA-402 specifies API behavior but intentionally permits variation in supported locales, localized data, and some locale-dependent presentation details. Code should therefore depend on the API contract and feature detection, not on one machine’s exact punctuation, month names, numbering data, or locale inventory.
Thanks for your feedback.
Mark this learning resource complete to track your learning progress.