Back to Blog
JavaScriptIntli18nLocalization

JavaScript Intl API Guide

Master internationalization with the Intl API. From number formatting to dates to relative time.

B
Bootspring Team
Engineering
February 25, 2021
6 min read

The Intl API provides language-sensitive formatting and comparison. Here's how to use it.

Number Formatting#

1// Basic number formatting 2const formatter = new Intl.NumberFormat('en-US'); 3console.log(formatter.format(1234567.89)); // '1,234,567.89' 4 5// Different locales 6console.log(new Intl.NumberFormat('de-DE').format(1234567.89)); // '1.234.567,89' 7console.log(new Intl.NumberFormat('fr-FR').format(1234567.89)); // '1 234 567,89' 8console.log(new Intl.NumberFormat('ja-JP').format(1234567.89)); // '1,234,567.89' 9 10// Currency formatting 11const currency = new Intl.NumberFormat('en-US', { 12 style: 'currency', 13 currency: 'USD', 14}); 15console.log(currency.format(1234.56)); // '$1,234.56' 16 17const euro = new Intl.NumberFormat('de-DE', { 18 style: 'currency', 19 currency: 'EUR', 20}); 21console.log(euro.format(1234.56)); // '1.234,56 €' 22 23// Compact notation 24const compact = new Intl.NumberFormat('en-US', { 25 notation: 'compact', 26 compactDisplay: 'short', 27}); 28console.log(compact.format(1234567)); // '1.2M' 29 30const compactLong = new Intl.NumberFormat('en-US', { 31 notation: 'compact', 32 compactDisplay: 'long', 33}); 34console.log(compactLong.format(1234567)); // '1.2 million'

Percentage and Units#

1// Percentage 2const percent = new Intl.NumberFormat('en-US', { 3 style: 'percent', 4 minimumFractionDigits: 1, 5}); 6console.log(percent.format(0.1234)); // '12.3%' 7 8// Units 9const speed = new Intl.NumberFormat('en-US', { 10 style: 'unit', 11 unit: 'kilometer-per-hour', 12}); 13console.log(speed.format(100)); // '100 km/h' 14 15const temp = new Intl.NumberFormat('en-US', { 16 style: 'unit', 17 unit: 'celsius', 18}); 19console.log(temp.format(25)); // '25°C' 20 21const bytes = new Intl.NumberFormat('en-US', { 22 style: 'unit', 23 unit: 'megabyte', 24}); 25console.log(bytes.format(512)); // '512 MB' 26 27// Available units: acre, bit, byte, celsius, centimeter, day, degree, 28// fahrenheit, fluid-ounce, foot, gallon, gigabit, gigabyte, gram, hectare, 29// hour, inch, kilobit, kilobyte, kilogram, kilometer, liter, megabit, 30// megabyte, meter, mile, mile-scandinavian, milliliter, millimeter, 31// millisecond, minute, month, ounce, percent, petabyte, pound, second, 32// stone, terabit, terabyte, week, yard, year

Date and Time Formatting#

1const date = new Date('2024-03-15T14:30:00'); 2 3// Basic formatting 4const dateFormatter = new Intl.DateTimeFormat('en-US'); 5console.log(dateFormatter.format(date)); // '3/15/2024' 6 7// Different locales 8console.log(new Intl.DateTimeFormat('de-DE').format(date)); // '15.3.2024' 9console.log(new Intl.DateTimeFormat('ja-JP').format(date)); // '2024/3/15' 10 11// Full date and time 12const full = new Intl.DateTimeFormat('en-US', { 13 dateStyle: 'full', 14 timeStyle: 'long', 15}); 16console.log(full.format(date)); 17// 'Friday, March 15, 2024 at 2:30:00 PM GMT-4' 18 19// Custom options 20const custom = new Intl.DateTimeFormat('en-US', { 21 weekday: 'long', 22 year: 'numeric', 23 month: 'long', 24 day: 'numeric', 25 hour: '2-digit', 26 minute: '2-digit', 27}); 28console.log(custom.format(date)); 29// 'Friday, March 15, 2024, 02:30 PM' 30 31// Time only 32const time = new Intl.DateTimeFormat('en-US', { 33 hour: 'numeric', 34 minute: '2-digit', 35 hour12: true, 36}); 37console.log(time.format(date)); // '2:30 PM' 38 39// Range formatting 40const start = new Date('2024-03-15'); 41const end = new Date('2024-03-20'); 42const range = new Intl.DateTimeFormat('en-US', { 43 month: 'short', 44 day: 'numeric', 45}); 46console.log(range.formatRange(start, end)); // 'Mar 15 – 20'

Relative Time#

1const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }); 2 3console.log(rtf.format(-1, 'day')); // 'yesterday' 4console.log(rtf.format(1, 'day')); // 'tomorrow' 5console.log(rtf.format(-3, 'day')); // '3 days ago' 6console.log(rtf.format(2, 'week')); // 'in 2 weeks' 7console.log(rtf.format(-1, 'month')); // 'last month' 8console.log(rtf.format(1, 'year')); // 'next year' 9 10// Always show numbers 11const rtfNumeric = new Intl.RelativeTimeFormat('en', { numeric: 'always' }); 12console.log(rtfNumeric.format(-1, 'day')); // '1 day ago' 13console.log(rtfNumeric.format(1, 'day')); // 'in 1 day' 14 15// Different styles 16const rtfShort = new Intl.RelativeTimeFormat('en', { style: 'short' }); 17console.log(rtfShort.format(-3, 'month')); // '3 mo. ago' 18 19const rtfNarrow = new Intl.RelativeTimeFormat('en', { style: 'narrow' }); 20console.log(rtfNarrow.format(-3, 'month')); // '3mo ago' 21 22// Helper function 23function getRelativeTime(date) { 24 const now = new Date(); 25 const diff = date - now; 26 const seconds = Math.round(diff / 1000); 27 const minutes = Math.round(seconds / 60); 28 const hours = Math.round(minutes / 60); 29 const days = Math.round(hours / 24); 30 const weeks = Math.round(days / 7); 31 const months = Math.round(days / 30); 32 const years = Math.round(days / 365); 33 34 const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }); 35 36 if (Math.abs(seconds) < 60) return rtf.format(seconds, 'second'); 37 if (Math.abs(minutes) < 60) return rtf.format(minutes, 'minute'); 38 if (Math.abs(hours) < 24) return rtf.format(hours, 'hour'); 39 if (Math.abs(days) < 7) return rtf.format(days, 'day'); 40 if (Math.abs(weeks) < 4) return rtf.format(weeks, 'week'); 41 if (Math.abs(months) < 12) return rtf.format(months, 'month'); 42 return rtf.format(years, 'year'); 43}

List Formatting#

1const list = new Intl.ListFormat('en', { 2 style: 'long', 3 type: 'conjunction', 4}); 5console.log(list.format(['Apple', 'Banana', 'Orange'])); 6// 'Apple, Banana, and Orange' 7 8const disjunction = new Intl.ListFormat('en', { 9 style: 'long', 10 type: 'disjunction', 11}); 12console.log(disjunction.format(['Red', 'Green', 'Blue'])); 13// 'Red, Green, or Blue' 14 15const unit = new Intl.ListFormat('en', { 16 style: 'narrow', 17 type: 'unit', 18}); 19console.log(unit.format(['5 feet', '10 inches'])); 20// '5 feet 10 inches' 21 22// Different locales 23const germanList = new Intl.ListFormat('de', { 24 style: 'long', 25 type: 'conjunction', 26}); 27console.log(germanList.format(['Äpfel', 'Bananen', 'Orangen'])); 28// 'Äpfel, Bananen und Orangen'

Plural Rules#

1const pr = new Intl.PluralRules('en'); 2 3console.log(pr.select(0)); // 'other' 4console.log(pr.select(1)); // 'one' 5console.log(pr.select(2)); // 'other' 6 7// Use for pluralization 8function pluralize(count, singular, plural) { 9 const pr = new Intl.PluralRules('en'); 10 return pr.select(count) === 'one' ? singular : plural; 11} 12 13console.log(`3 ${pluralize(3, 'item', 'items')}`); // '3 items' 14console.log(`1 ${pluralize(1, 'item', 'items')}`); // '1 item' 15 16// Ordinal numbers 17const ordinal = new Intl.PluralRules('en', { type: 'ordinal' }); 18 19function getOrdinalSuffix(n) { 20 const suffixes = { 21 one: 'st', 22 two: 'nd', 23 few: 'rd', 24 other: 'th', 25 }; 26 return n + suffixes[ordinal.select(n)]; 27} 28 29console.log(getOrdinalSuffix(1)); // '1st' 30console.log(getOrdinalSuffix(2)); // '2nd' 31console.log(getOrdinalSuffix(3)); // '3rd' 32console.log(getOrdinalSuffix(4)); // '4th' 33console.log(getOrdinalSuffix(21)); // '21st'

Collation (Sorting)#

1// Locale-aware sorting 2const names = ['Ängström', 'Zulu', 'Apple', 'Öresund']; 3 4// Default sort (may not be locale-aware) 5console.log([...names].sort()); 6// ['Apple', 'Zulu', 'Ängström', 'Öresund'] 7 8// Swedish sorting 9const collator = new Intl.Collator('sv'); 10console.log([...names].sort(collator.compare)); 11// ['Apple', 'Zulu', 'Ängström', 'Öresund'] 12// (In Swedish, Ä and Ö come after Z) 13 14// Case-insensitive 15const insensitive = new Intl.Collator('en', { sensitivity: 'base' }); 16console.log(insensitive.compare('a', 'A')); // 0 (equal) 17console.log(insensitive.compare('a', 'b')); // -1 (a < b) 18 19// Natural sorting (numbers) 20const files = ['file1', 'file10', 'file2', 'file21']; 21const natural = new Intl.Collator('en', { numeric: true }); 22console.log([...files].sort(natural.compare)); 23// ['file1', 'file2', 'file10', 'file21']

Display Names#

1// Language names 2const langNames = new Intl.DisplayNames(['en'], { type: 'language' }); 3console.log(langNames.of('en')); // 'English' 4console.log(langNames.of('de')); // 'German' 5console.log(langNames.of('ja')); // 'Japanese' 6 7// Region names 8const regionNames = new Intl.DisplayNames(['en'], { type: 'region' }); 9console.log(regionNames.of('US')); // 'United States' 10console.log(regionNames.of('DE')); // 'Germany' 11console.log(regionNames.of('JP')); // 'Japan' 12 13// Currency names 14const currencyNames = new Intl.DisplayNames(['en'], { type: 'currency' }); 15console.log(currencyNames.of('USD')); // 'US Dollar' 16console.log(currencyNames.of('EUR')); // 'Euro' 17console.log(currencyNames.of('JPY')); // 'Japanese Yen' 18 19// Script names 20const scriptNames = new Intl.DisplayNames(['en'], { type: 'script' }); 21console.log(scriptNames.of('Latn')); // 'Latin' 22console.log(scriptNames.of('Cyrl')); // 'Cyrillic' 23 24// In different languages 25const germanDisplay = new Intl.DisplayNames(['de'], { type: 'language' }); 26console.log(germanDisplay.of('en')); // 'Englisch' 27console.log(germanDisplay.of('de')); // 'Deutsch'

Segmenter#

1// Word segmentation 2const wordSegmenter = new Intl.Segmenter('en', { granularity: 'word' }); 3const words = [...wordSegmenter.segment('Hello, world!')]; 4console.log(words.map(s => s.segment)); 5// ['Hello', ',', ' ', 'world', '!'] 6 7// Sentence segmentation 8const sentenceSegmenter = new Intl.Segmenter('en', { granularity: 'sentence' }); 9const text = 'Hello! How are you? I am fine.'; 10const sentences = [...sentenceSegmenter.segment(text)]; 11console.log(sentences.map(s => s.segment)); 12// ['Hello! ', 'How are you? ', 'I am fine.'] 13 14// Grapheme segmentation (useful for emojis) 15const graphemeSegmenter = new Intl.Segmenter('en', { granularity: 'grapheme' }); 16const emoji = '👨‍👩‍👧‍👦'; 17const graphemes = [...graphemeSegmenter.segment(emoji)]; 18console.log(graphemes.length); // 1 (single family emoji) 19console.log(emoji.length); // 11 (code units)

Best Practices#

Usage: ✓ Cache formatters for reuse ✓ Handle missing locale gracefully ✓ Use formatToParts for custom rendering ✓ Test with multiple locales Performance: ✓ Create formatters once, reuse ✓ Use appropriate precision ✓ Avoid creating in loops ✓ Consider memoization Fallbacks: ✓ Provide default locale ✓ Handle unsupported features ✓ Test browser compatibility ✓ Use polyfills if needed

Conclusion#

The Intl API provides comprehensive internationalization support. Use it for numbers, dates, lists, and sorting. Cache formatter instances for performance and test with various locales to ensure proper display across regions.

Share this article

Help spread the word about Bootspring