What is the JavaScript Internationalization API (I18n)?

Share this article

An introduction to the JavaScript Internationalization API (i18n)

English is the world’s most widely used language, yet only one in seven people speak it. It’s the first (native) language of 379 million people, but 917 million speak Mandarin Chinese, 460 million speak Spanish, and 341 million speak Hindi.

Many non-English speakers reside in emerging markets with exponential internet growth. If your web app can be globally translated, your potential target market could increase by 700%!

The JavaScript Internationalization API (also known as i18n) allows you to design web pages and applications in such a way that they can be easily adapted to support the needs of users that speak different languages.

In this article, we’ll look at the various methods the API offers and how you can implement them in your code to reach a wider, more international audience.

Internationalization (I18n) Can Be Tricky

Internationalization looks easy … until you try to do it.

Latin-based languages can be superficially similar. For example, a form requesting a name, email, and date translates like this:

  • Spanish: nombre, email, fecha
  • French: nom, e-mail, date
  • German: name, email, datum

The Gettext internationalization and localization system has been around for several decades, and libraries are available for most programming languages.

In simpler cases, you could use some form of tokenization. For example, take an HTML template containing the following:

<label for="name">{{ NAME }}</label>

This is dynamically replaced by ‘name’ when a user has English set as their primary language. Unfortunately, that’s where the problems start for your user interface:

  1. There can be different variations of the same language. The Spanish spoken in Spain is not identical to that spoken in South America.
  2. Words in one language can be considerably longer in others. For example, “email” translates to “электронное письмо” in Russian.
  3. Text isn’t always oriented from left to right. Some is written from right to left — such as Arabic, Hebrew, Kurdish, and Yiddish. Others can be written from top to bottom, such as Chinese, Korean, Japanese, and Taiwanese.

Many issues can be addressed by keeping text to a minimum and adopting CSS properties such as direction, writing-mode, and logical dimensions for layout.

Terminology Turmoil

Further confusion will arise when your application needs to display dates, times, numbers, currencies, or units.

Consider a date shown as “12/03/24”. It will be read as:

  • “3 December 2024” by US residents who use the MDY format
  • “12 March 2024” by European, South American, and Asian residents who use the DMY format, and
  • “24 March 2012” by Canadian, Chinese, Japanese, and Hungarian residents who opt for the considerably more practical YMD format.

(Be aware that date delimiter slashes are not common in all languages!)

The number “1,000” will be read as:

  • “one thousand” by those in the US, UK, Canada, China, and Japan, and
  • “one (point zero)” by those in Spain, France, Germany, and Russia where a number’s decimal fraction is separated by a comma.

The situation can even be complex in English alone. The term “1,000 meters” means:

  • 1 kilometer (or 0.62 of a mile) to US residents
  • a collection of one thousand measuring instruments to those in the UK, Canada, and Australia!

The JavaScript Intl API

The little-known JavaScript Intl object implements the ECMAScript Internationalization API in most modern browsers and runtimes. Support is generally good, and even IE11 has many of the more useful methods. For older browsers, there’s a polyfill, and the API can be detected like so:

if (window.Intl) {
  // Intl supported
}

The API is slightly unusual. It provides several object constructors for dates, times, numbers, and lists, which are passed a locale and an optional object containing configuration parameters. For example, here’s a DateTime object specifying US English:

const dateFormatter = new Intl.DateTimeFormat('en-US');

This object can be used any number of times to call various methods which are passed a Date() value (or an ES6 Temporal when available). The format method is usually the most practical option. For example:

const valentinesDay = dateFormatter.format( new Date('2022-02-14') );
// returns US format "2/14/2022"

const starwarsDay = dateFormatter.format( new Date('2022-05-04') );
// returns US format "5/4/2022"

Alternatively, you can create the Intl object and run a method in one line of code:

const starwarsDay = new Intl.DateTimeFormat('en-US').format( new Date('2022-05-04') );

As well as the format() method, some objects support these:

  • formatToParts(): returns an array of objects containing formatted strings, such as { type: 'weekday', value: 'Monday' }
  • resolvedOptions(): returns a new object with properties reflecting the locale and formatting options used, such as dateFormatter.resolvedOptions().locale.

Defining Locales

All Intl objects require a locale argument. This is a string which identifies:

  • a language subtag
  • a script subtag (optional)
  • a region (or country) subtag (optional)
  • one or more variant subtags (optional)
  • one or more BCP 47 extension sequences (optional)
  • a private-use extension sequence (optional)

The language and region is often enough. For example, "en-US", "fr-FR", and so on.

As well as using a string, an Intl.locale object can be used to construct locales, such as English US with 12-hour time format:

const us = new Intl.Locale('en', {
  region: 'US', hourCycle: 'h12', calendar: 'gregory'
});

This can be used in another Intl constructor. For example:

new Intl.DateTimeFormat(us, { timeStyle: 'medium' })
  .format( new Date('2022-05-04T13:00:00') );

// "1:00:00 PM"

If no locale is defined, the device’s current language and region settings are used. For example:

new Intl.DateTimeFormat().format( new Date('2022-05-04') );

This returns "5/4/2022" on a device with US settings and "04/05/2022" on a device with UK settings.

Dates and Times

The following tool shows examples of dates and times formatted using Intl.DateTimeFormat() (apologies if your language or region isn’t listed!):

See the Pen i18n date and time formatting tool by SitePoint (@SitePoint) on CodePen.

The constructor is passed the locale and an options object. This has many possible properties, although you rarely require more than dateStyle and/or timeStyle:

property description
dateStyle the date style: "full" "long" "medium" "short"
timeStyle the time style: "full" "long" "medium" "short"
calendar options include: "chinese" "gregory" "hebrew" "indian" "islamic" etc.
dayPeriod period expressions: "narrow" "short" "long"
numberingSystem numbering system: "arab" "beng" "fullwide" "latn" etc.
localeMatcher locale matching algorithm: "lookup" "best fit"
timeZone time zone: "America/New_York" "Europe/Paris" etc.
hour12 set true to use 12-hour time notation
hourCycle hour cycle: "h11" "h12" "h23" "h24"
formatMatcher format matching algorithm: "basic" "best fit"
weekday weekday format: "long" "short" "narrow"
era era format: "long" "short" "narrow"
year year format: "numeric" "2-digit"
month month format: "numeric" "2-digit" "long" "short" "narrow"
day day format: "numeric" "2-digit"
hour hour format: "numeric" "2-digit"
minute minute format: "numeric" "2-digit"
second second format: "numeric" "2-digit"
timeZoneName either: "long" "short"

Examples:

// Japanese short date, no time: "2022/05/04"
new Intl.DateTimeFormat("ja-JP", { dateStyle: "short" })
  .format( new Date("2022-05-04T13:00") );

// US short date and time: "5/4/22, 1:00 PM"
new Intl.DateTimeFormat("en-US", { dateStyle: "short", timeStyle: "short" })
  .format( new Date("2022-05-04T13:00") );

// UK long date, short time: "4 May 2022 at 13:00"
new Intl.DateTimeFormat("en-GB", { dateStyle: "long", timeStyle: "short" })
  .format( new Date("2022-05-04T13:00") );

// Spanish full date and time (dependent on your local time zone)
// "miércoles, 4 de mayo de 2022, 13:00:00 (hora de verano británica)"
new Intl.DateTimeFormat("es-ES", { dateStyle: "full", timeStyle: "full" })
  .format( new Date("2022-05-04T13:00") );

Date Ranges

A formatRange() method takes two dates and formats the period in the most concise way depending on the locale and options. For example:

// result: "4 May 2022, 13:00–14:00"
new Intl.DateTimeFormat("en-US", { dateStyle: "long", timeStyle: "short" })
  .formatRange(new Date("2022-05-04T13:00"), new Date("2022-05-04T14:00"))

This method has more limited browser support but was implemented in Chrome 76.

Relative Periods

The Intl.RelativeTimeFormat() object can display periods relative to this moment in time. The options object has fewer options:

property description
localeMatcher locale matching algorithm: "lookup" "best fit"
numeric either "always", e.g. "1 day ago" or "auto", e.g. "yesterday"
style format: "long" "short" "narrow"

The format() method is passed a numeric value and a unit: "year", "quarter", "month", "week", "day", "hour", "minute", or "second". Examples:

// US 1 day ago, numeric: "1 day ago"
new Intl.RelativeTimeFormat("en-US")
  .format( -1, "day" );

// US in one 1 day, auto: "tomorrow"
new Intl.RelativeTimeFormat("en-US", { numeric: "auto" })
  .format( -1, "day" );

// German, next month auto: "nächsten Monat"
new Intl.RelativeTimeFormat("de-DE", { numeric: "auto" })
  .format( 1, "month" );

Numbers, Currencies, Percentages, and Units

The following tool shows examples using Intl.NumberFormat() to format numbers, currencies, percentages, and measurement units:

See the Pen i18n number and currency formatting tool by SitePoint (@SitePoint) on CodePen.

The constructor is passed the locale and an options object:

property description
numberingSystem options include "arab" "beng" "deva" "fullwide" "latn" etc.
notation type: "standard" "scientific" "engineering" "compact"
style formatting: "decimal" "currency" "percent" "unit" — this determines which other options can be set
currency currency code: "USD" "EUR" "GBP" etc.
currencyDisplay currency formatting: "symbol" "narrowSymbol" "code" "name"
currencySign for negative currency values, "standard" a minus sign or "accounting" for parenthesis
unit a unit type: "centimeter" "inch" "hour" etc.
unitDisplay unit format: "long" "short" "narrow"
useGrouping set false to disable thousands separators
minimumIntegerDigits minimum number of integer digits
minimumFractionDigits minimum number of fraction digits
maximumFractionDigits maximum number of fraction digits
minimumSignificantDigits minimum number of significant digits
maximumSignificantDigits maximum number of significant digits

Examples:

// US number rounded to 2 decimal places: "12,345.68"
new Intl.NumberFormat("en-US", { maximumSignificantDigits: 2 })
  .format( 12345.6789 );

// French number rounded to 3 decimal places: "12 345,689"
new Intl.NumberFormat("fr-FR", { maximumSignificantDigits: 3 })
  .format( 12345.6789 );

// US compact number, 0 decimal places: "12K"
new Intl.NumberFormat("en-US", { notation: "compact", maximumSignificantDigits: 0 })
  .format( 12345.6789 );

// Spanish US dollar value: "12.345,68 US$"
new Intl.NumberFormat("es-ES", {
  style: "currency",
  currency: "USD",
  currencyDisplay: "symbol"
})
  .format( 12345.6789 );

// UK meters in long format, 0 decimal places: "12,346 metres"
new Intl.NumberFormat("en-GB", {
  maximumSignificantDigits: 0,
  style: "unit",
  unit: "meter",
  unitDisplay: "long"
})
  .format( 12345.6789 );

Lists

A Intl.ListFormat() object can format an array of items into a language-sensitive list. In English, that typically requires an “and” or an “or” before the last item.

The options object can set the following properties:

property description
type output format: "conjunction" for and-based lists, "disjunction" for or-based lists
style formatting: "long" "short" "narrow"

Examples:

const browsers = ['Chrome', 'Firefox', 'Edge', 'Safari'];

// US English: "Chrome, Firefox, Edge, and Safari"
new Intl.ListFormat("en-US", { type: "conjunction" }).format(browsers);

// US English: "Chrome, Firefox, Edge, or Safari"
new Intl.ListFormat("en-US", { type: "disjunction" }).format(browsers);

// French: "Chrome, Firefox, Edge, et Safari"
new Intl.ListFormat("fr-FR", { type: "conjunction" }).format(browsers);

// French: "Chrome, Firefox, Edge, ou Safari"
new Intl.ListFormat("fr-FR", { type: "disjunction" }).format(browsers);

Plurals

The slightly bizarre Intl.PluralRules() object enables plural-sensitive language rules where you have a number of items. The options object can set a type property to either:

  • cardinal: the quantity of things (the default), or
  • ordinal: the ranking of things, such as 1st, 2nd, or 3rd in English

The select() method returns an English string representing the pluralization category of the number: either zero, one, two, few, many, or other.

Examples:

// US English zero cardinal: "other"
new Intl.PluralRules("en-US", { type: "cardinal" }).select(0);

// US English zero ordinal: "other"
new Intl.PluralRules("en-US", { type: "ordinal" }).select(0);

// US English 1 cardinal: "one"
new Intl.PluralRules("en-US", { type: "cardinal" }).select(1);

// US English 1 ordinal: "one"
new Intl.PluralRules("en-US", { type: "ordinal" }).select(1);

// US English 2 cardinal: "other"
new Intl.PluralRules("en-US", { type: "cardinal" }).select(2);

// US English 2 ordinal: "two"
new Intl.PluralRules("en-US", { type: "ordinal" }).select(2);

// US English 3 cardinal: "other"
new Intl.PluralRules("en-US", { type: "cardinal" }).select(3);

// US English 3 ordinal: "few"
new Intl.PluralRules("en-US", { type: "ordinal" }).select(3);

String Comparison

Finally, the Intl.Collator() object enables language-sensitive string comparison. Its options object can set the following properties:

property description
collation variant collation for certain locales
numeric set true for numeric collation where “1” < “2” < “10”
caseFirst either "upper" or "lower" case first
usage either string "sort" (default) or "search"
sensitivity "base" "accent" "case" "variant" comparisons
ignorePunctuation set true to ignore punctuation

The compare() method compares two strings. For example:

// German: returns 1
new Intl.Collator('de').compare('z', 'ä');

Profit!

It should be straightforward to show information using the user’s local format if you’re using JavaScript to display data. For example, the following code defines a dateFormat() function which uses the Intl short date format or falls back to YYYY-MM-DD when that’s not supported:

// date formatting function
const dateFormat = (Intl && Intl.DateTimeFormat ?
  date => new Intl.DateTimeFormat({ dateStyle: 'short' }).format(date) :
  date => date.toISOString().slice(0, 10)
);

// insert today's date into DOM #today element
document.getElementById('today').textContent = dateFormat( new Date() );

This alone won’t make your app easy for an international audience, but it’s one step closer to global distribution.

Frequently Asked Questions (FAQs) about JavaScript Internationalization API (i18n)

What is the purpose of the JavaScript Internationalization API (i18n)?

The JavaScript Internationalization API, also known as i18n, is a built-in JavaScript API that provides language-sensitive string comparison, number formatting, and date and time formatting. It allows developers to internationalize their applications by providing support for different languages and cultural conventions. This is particularly useful for applications that are used globally, as it allows them to adapt to the language and formatting conventions of different regions.

How does JavaScript i18n handle date and time formatting?

JavaScript i18n provides a DateTimeFormat object that can be used to format dates and times according to different cultural conventions. This object takes a locale and an options object as parameters, which define the formatting conventions to be used. The options object can specify the format of the date, the time, the time zone, and other aspects of the date and time format.

How does JavaScript i18n handle number formatting?

JavaScript i18n provides a NumberFormat object that can be used to format numbers according to different cultural conventions. This object takes a locale and an options object as parameters, which define the formatting conventions to be used. The options object can specify the style of the number (decimal, percent, or currency), the use of grouping separators, the minimum and maximum number of decimal places, and other aspects of the number format.

How does JavaScript i18n handle string comparison?

JavaScript i18n provides a Collator object that can be used to compare strings according to different cultural conventions. This object takes a locale and an options object as parameters, which define the comparison conventions to be used. The options object can specify the sensitivity of the comparison (base, accent, case, or variant), the use of numeric collation, and other aspects of the string comparison.

How can I specify the locale for JavaScript i18n?

The locale for JavaScript i18n can be specified as a parameter when creating a DateTimeFormat, NumberFormat, or Collator object. The locale is a string that represents a language and a region, such as “en-US” for American English or “fr-FR” for French as used in France. If no locale is specified, the default locale of the JavaScript environment is used.

Can I use multiple locales with JavaScript i18n?

Yes, you can specify multiple locales as an array when creating a DateTimeFormat, NumberFormat, or Collator object. JavaScript i18n will use the first locale in the array that it supports. This is useful for applications that are used in multiple regions, as it allows them to adapt to the language and formatting conventions of different regions.

How can I customize the formatting options for JavaScript i18n?

The formatting options for JavaScript i18n can be customized by providing an options object when creating a DateTimeFormat, NumberFormat, or Collator object. The options object can specify various aspects of the formatting or comparison, such as the format of the date or number, the sensitivity of the string comparison, and so on.

Can I use JavaScript i18n with other JavaScript APIs?

Yes, JavaScript i18n can be used in conjunction with other JavaScript APIs. For example, you can use the Date object with the DateTimeFormat object to format dates, or you can use the Number object with the NumberFormat object to format numbers. This allows you to leverage the full power of JavaScript to internationalize your applications.

Is JavaScript i18n supported in all browsers?

JavaScript i18n is supported in most modern browsers, including Chrome, Firefox, Safari, and Edge. However, it may not be supported in older browsers or in certain mobile browsers. You can check the compatibility table on the Mozilla Developer Network (MDN) for the most up-to-date information on browser support.

Where can I learn more about JavaScript i18n?

You can learn more about JavaScript i18n from the official ECMAScript Internationalization API specification, the Mozilla Developer Network (MDN), and various online tutorials and articles. These resources provide detailed information on the API and its usage, as well as examples and best practices for internationalizing JavaScript applications.

Craig BucklerCraig Buckler
View Author

Craig is a freelance UK web consultant who built his first page for IE2.0 in 1995. Since that time he's been advocating standards, accessibility, and best-practice HTML5 techniques. He's created enterprise specifications, websites and online applications for companies and organisations including the UK Parliament, the European Parliament, the Department of Energy & Climate Change, Microsoft, and more. He's written more than 1,000 articles for SitePoint and you can find him @craigbuckler.

i18nInternationalization APIJavaScript Internationalization APItranslation
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week