ESNext: Get localized language, currency, and region names with Intl.DisplayNames

An ECMAScript Internationalization API Feature that currently is in Stage-3 and that has already landed in V8 version 8.1 is Intl.DisplayNames. It’s a way to get localized display names for languages, scripts, regions and others.

🤔 Note that this proposal is part of ECMA-402 (the ECMAScript Internationalization API Specification) and not ECMA-262 (The ECMAScript Language Specification). Therefore this proposal will not be part of the yearly "ES20XX”, as that term only applies to ECMA-262.

To advance proposals in ECMA-402, TC39 uses the same 5-stage-process as they use in ECMA-262.

💁‍♂️ Stage-3?

The Technical Committee which is concerned with the standardization of ECMAScript (e.g. TC39) has a 5 stage process in place, ranging from stage-0 to stage-4, by which it develops a new language feature.

Stage-3 is the Candidate Stage where the feature is considered complete and only critical changes will happen based on implementation experience. If all goes well the proposal will advance to Stage 4 without any changes, after which it will to become part of the ECMAScript Specification.

The idea is that you as a developer should not build your own list of localized strings for languages, regions, etc. but that the JavaScript runtime should provide if for you:

Translation of languages, regions or script display names requires large amount of data to transmit on the network, which is already available in most browsers. The Intl.DisplayNames API will allow web developers to shrink the size of their HTML and/or ECMA script code without the need to include the human readable form of display names and therefore reduce the download size to decrease latency.

💁‍♂️ Another nice API exposed on Intl is Intl.RelativeTimeFormat which allows you to relatively format time in JavaScript.

~

Examples

// Get display names of region in English
const regionNames = new Intl.DisplayNames(['en'], {type: 'region'});
console.log(regionNames.of('419')); // "Latin America"
console.log(regionNames.of('BZ')); // "Belize"
console.log(regionNames.of('US')); // "United States"
console.log(regionNames.of('BA')); // "Bosnia & Herzegovina"
console.log(regionNames.of('MM')); // "Myanmar (Burma)"

// Get display names of language in Traditional Chinese
const languageNames = new Intl.DisplayNames(['zh-Hant'], {type: 'language'});
console.log(languageNames.of('fr')); // "法文"
console.log(languageNames.of('zh')); // "中文"
console.log(languageNames.of('de')); // "德文"

// Get display names of script in Traditional Chinese
const scriptNames = new Intl.DisplayNames(['zh-Hant'], {type: 'script'});
console.log(scriptNames.of('Latn')); // "拉丁文"
console.log(scriptNames.of('Arab')); // "阿拉伯文"
console.log(scriptNames.of('Kana')); // "片假名"

// Get display names of currency code in English
const currencyNames = new Intl.DisplayNames(['en'], {type: 'currency'});
console.log(currencyNames.of('USD')); // "US Dollar"
console.log(currencyNames.of('EUR')); // "Euro"
console.log(currencyNames.of('TWD')); // "New Taiwan Dollar"
console.log(currencyNames.of('CNY')); // "Chinese Yuan"

~

Syntax

The constructor takes two arguments: locales and options. Both are optional:

new Intl.DisplayNames([locales[, options]]);

Once you have a Intl.DisplayNames instance, you use its .of method to request a localised string

Intl.DisplayNames.prototype.of(code);

~

Constructor Arguments

locales

The locales argument is a BCP 47 language tag which identifies the locale to use. The main parts, amongst others, of a BCP 47 language tag are language, script, and region

  • The language part is a 2/3 character long ISO 639-1 code
  • The (optional) script part is a 4 character long ISO 15924 code
  • The (optional) region part is a 2 alpha or 3 digit long ISO 3166-1 code

When more than one part is present, they are separated by a dash (-)

Examples:

  • "nl": Dutch (language).
  • "nl-BE": Dutch as used in Belgium (language + region).
  • "zh-Hans-CN": Chinese written in simplified characters as used in China (language + script + region).

When locales is omitted, it will default to the language-setting of the browser/system.

options

The options object allows you to tweak how Intl.DisplayNames behaves. It has the following properties:

  • localeMatcher

    The locale matching algorithm to use. Possible values are "lookup" and "best fit" (default)

  • style

    The length of the output message, e.g. "English (United States)" vs. "English (US)". Possible values are "long" (default) and "short", or “narrow” (which mostly yields the same result as "short", depending on the language)

  • type

    What you want to display. Possible values are "region", "script", "language", or "currency".

~

Intl.DisplayNames.prototype.of Arguments

code

Depending on the type you passed into the constructor, you pass in a different value for code:

~

JS/Browser Support

Intl.DisplayNames already shipped in V8 v8.1 (included in the upcoming Chrome 81) and will be available in Node 14. Other JS runtimes / browsers don’t support it at the time of writing.

~

The future: More types to come!

When looking at the open issues listed on the proposal’s repository you can see that there’s more to come in a future version of Intl.DisplayNames: support for units, timezones, emoji names, date parts (such as months, weekdays, etc).

For this first release the scope was limited to supporting the types "region", "script", "language", and "currency".

~

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

RTL Styling 101

Ahmad Shadeed created this extensive RTL reference.

Over 292 million people around the world speak Arabic as their first language. Arabic is my native language, and I sometimes build websites that need to support both left-to-right (LTR) and right-to-left (RTL) styles.

Be sure to read the section on CSS Logical Properties too, as that will simplify things in modern browsers.

RTL Styling 101 →

ESNext: Relatively format time with Intl.RelativeTimeFormat

A proposal to the ECMAScript Internationalization API Specification (ECMA-402) that just moved to Stage-4, is Intl.RelativeTimeFormat. It’s a way to format time in a relative manner – e.g. “10 minutes ago” or “in 3 quarters” – while also taking a language into account.

🤔 Note that this proposal is part of ECMA-402 (the ECMAScript Internationalization API Specification) and not ECMA-262 (The ECMAScript Language Specification). Therefore this proposal will not be part of the yearly "ES20XX”, as that term only applies to ECMA-262.

To advance proposals in ECMA-402, TC39 uses the same 5-stage-process as they use in ECMA-262.

💁‍♂️ Stage-4?

The Technical Committee which is concerned with the standardization of ECMAScript (e.g. TC39) has a 5 stage process in place, ranging from stage-0 to stage-4, by which it develops a new language feature.

Stage-4 is the Finished Stage and indicates that the proposal is ready to become part of the ECMAScript Specification.

const rtf_en = new Intl.RelativeTimeFormat('en');

console.log(rtf_en.format(-1, 'day')); // ~> 1 day ago
console.log(rtf_en.format(30, 'seconds')); // ~> in 30 seconds
const rtf_nl = new Intl.RelativeTimeFormat('nl-BE', {
    localeMatcher: 'best fit', // other values: 'lookup'
    numeric: 'auto', // other values: 'always'
    style: 'short', // other values: 'long' or 'narrow'
});

console.log(rtf_nl.format(-1, 'day')); // ~> gisteren
console.log(rtf_nl.format(30, 'seconds')); // ~> over 30 sec.

~

Syntax

The constructor takes two arguments: locales and options. Both are optional:

new Intl.RelativeTimeFormat([locales[, options]]);

Possible units to use while formatting are "year", "quarter", "month", "week", "day", "hour", "minute", and "second".

~

Constructor Arguments

locales

The locales argument is a BCP 47 language tag which identifies the locale to use. The main parts, amongst others, of a BCP 47 language tag are language, script, and region

  • The language part is a 2/3 character long ISO 639-1 code
  • The (optional) script part is a 4 character long ISO 15924 code
  • The (optional) region part is a 2 alpha or 3 digit long ISO 3166-1 code

When more than one part is present, they are separated by a dash (-)

Examples:

  • "nl": Dutch (language).
  • "nl-BE": Dutch as used in Belgium (language + region).
  • "zh-Hans-CN": Chinese written in simplified characters as used in China (language + script + region).

When locale is omitted, it will default to the language-setting of the browser/system.

options

The options object allows you to tweak how Intl.RelativeTimeFormat behaves. It has the following properties:

  • localeMatcher

    The locale matching algorithm to use. Possible values are "lookup" and "best fit" (default)

  • numeric

    The format of output message. Possible values are "always" (always include a number in the output, default) or "auto" (which allows output such as “in 1 day” to be rewritten in the more natural "tomorrow")

  • style

    The length of the output message, e.g. "in 1 month" vs. "in 1 mo.". Possible values are "long" (default), "short", or “narrow” (which mostly yields the same result as "short", depending on the language)

~

Extracting all parts with formatToParts

If you don’t want a string to be returned, you can use formatToParts() to get an object describing all parts:

const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });

// Format relative time using the day unit.
rtf.formatToParts(-1, "day");
// > [{ type: "literal", value: "yesterday"}]

rtf.formatToParts(100, "day");
// > [{ type: "literal", value: "in " }, { type: "integer", value: "100", unit: "day" }, { type: "literal", value: " days" }]

~

JS/Browser Support

Intl.RelativeTimeFormat shipped in V8 v7.1.179 (included Chrome 71) and Firefox 65.

Polyfills are available:

~

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Multilingual Sites: Styling translated text

Ire Aderinokun on why you should use the :lang pseudo-class over the lang attribute selector (e.g. [lang]) for language-specific styles:

The problem with having language-specific styles based on the [lang] attribute selector is that the selector isn’t actually aware of the language of the element. It’s just like any other “dumb” attribute selector.

This can become a problem if we have a document with multiple, nested elements of different languages. Let’s take this section below. The section has a French lang attribute but, within the section, we have a quote in Spanish.

<section lang="fr">
    <p>Comment dites-vous "Bonjour" en Espanol?</p>
    <blockquote lang="es">
        <p>Hola!</p>
    </blockquote>
</section>

If one were to be styling based on the lang attribute, the styling would only work nice if the order of the CSS declarations was the same as the order in which the different languages appear in the markup. Flip your CSS around, and the styling will be wrong.

The fix is to use the :lang pseudo-class, a selector whose use wasn’t very clear to me … up until now 😉

Use the :lang pseudo-class over the lang attribute selector for language-specific styles →

Want to know how to link to a translation? Check Multilingual Sites: Linking to Translations.

Multilingual Sites: Linking to Translations

Good piece by Hidde on the markup one needs to use when linking to a translation of a page.

The other day I worked on some front-end code that takes users to a version of the website in a different language. Two attributes can make such links more accessible: lang and hreflang. What’s the difference?

Linking to Translations →