What is the JavaScript Internationalization (I18n) API?

What is the JavaScript Internationalization (I18n) API? – SitePoint

Skip to main content

Free JavaScript Book!

Write powerful, clean and maintainable JavaScript.

RRP $11.95

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) { }

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') ); const starwarsDay = dateFormatter.format( new Date('2022-05-04') ); 

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') ); 

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:


new Intl.DateTimeFormat("ja-JP", { dateStyle: "short" }) .format( new Date("2022-05-04T13:00") ); new Intl.DateTimeFormat("en-US", { dateStyle: "short", timeStyle: "short" }) .format( new Date("2022-05-04T13:00") ); new Intl.DateTimeFormat("en-GB", { dateStyle: "long", timeStyle: "short" }) .format( new Date("2022-05-04T13:00") ); 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:


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:


new Intl.RelativeTimeFormat("en-US") .format( -1, "day" ); new Intl.RelativeTimeFormat("en-US", { numeric: "auto" }) .format( -1, "day" ); 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:


new Intl.NumberFormat("en-US", { maximumSignificantDigits: 2 }) .format( 12345.6789 ); new Intl.NumberFormat("fr-FR", { maximumSignificantDigits: 3 }) .format( 12345.6789 ); new Intl.NumberFormat("en-US", { notation: "compact", maximumSignificantDigits: 0 }) .format( 12345.6789 ); new Intl.NumberFormat("es-ES", { style: "currency", currency: "USD", currencyDisplay: "symbol"
}) .format( 12345.6789 ); 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']; new Intl.ListFormat("en-US", { type: "conjunction" }).format(browsers); new Intl.ListFormat("en-US", { type: "disjunction" }).format(browsers); new Intl.ListFormat("fr-FR", { type: "conjunction" }).format(browsers); 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:


new Intl.PluralRules("en-US", { type: "cardinal" }).select(0); new Intl.PluralRules("en-US", { type: "ordinal" }).select(0); new Intl.PluralRules("en-US", { type: "cardinal" }).select(1); new Intl.PluralRules("en-US", { type: "ordinal" }).select(1); new Intl.PluralRules("en-US", { type: "cardinal" }).select(2); new Intl.PluralRules("en-US", { type: "ordinal" }).select(2); new Intl.PluralRules("en-US", { type: "cardinal" }).select(3); 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:


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:


const dateFormat = (Intl && Intl.DateTimeFormat ? date => new Intl.DateTimeFormat({ dateStyle: 'short' }).format(date) : date => date.toISOString().slice(0, 10)
); 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.

Leave a Reply

Your email address will not be published. Required fields are marked *