Runtime Environments

Here is information on how to setup FormatJS in different environments, including how to polyfill the runtimes that don't have built-in internationalization support.

Intl Built-in APIs

The FormatJS libraries rely on the following ECMA 402 Internationalization APIs introduced in ECMAScript 5.1:

If these APIs aren't present in a runtime your app is targeting, then you'll need to polyfill the runtime to include them.

Runtimes with Intl APIs

The Intl APIs are currently available on Node.js 0.12 and all modern browsers, with the exception of Firefox on Android. See the Mozilla Developer Network documentation for an up-to-date list of capable browsers.

Intl.js Polyfill

The Intl.js polyfill, developed by Andy Earnshaw, provides a polyfill for the Intl.NumberFormat and Intl.DateTimeFormat APIs — both of which are needed by the FormatJS libraries. The polyfill also includes separate locale data files for each of the 700+ locales it supports.

Note that the Intl.js polyfill does not have the Intl.Collator API. The size of the locale data required to support the API is not practical to load in a client.

Server (Node.js)

Node.js 0.12 has the Intl APIs built-in, but only includes the English locale data by default. If your app needs to support more locales than English, you'll need to build or configure Node with the extra locale data, or polyfill the runtime.

Node.js versions prior to 0.12 don't provide the Intl APIs, so they require that the runtime is polyfilled.

How to Polyfill Node.js

Node.js <= 0.10 doesn't have the built-in Intl APIs (ECMA-402). Node.js 0.12 does, but the default build/distribution only supports English.

The following code snippet uses the intl and intl-locales-supported npm packages which will help you polyfill the Node.js runtime when the Intl APIs are missing, or if the built-in Intl is missing locale data that's needed for your app:

var areIntlLocalesSupported = require('intl-locales-supported');

var localesMyAppSupports = [
    /* list locales here */
];

if (global.Intl) {
    // Determine if the built-in `Intl` has the locale data we need.
    if (!areIntlLocalesSupported(localesMyAppSupports)) {
        // `Intl` exists, but it doesn't have the data we need, so load the
        // polyfill and replace the constructors with need with the polyfill's.
        var IntlPolyfill = require('intl');
        Intl.NumberFormat   = IntlPolyfill.NumberFormat;
        Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;
    }
} else {
    // No `Intl`, so use and load the polyfill.
    global.Intl = require('intl');
}

Determining the User's Locale

When a request comes in you'll need to determine the locale for the response. Best practice is to provide the user an explicit way of choosing one of the locales your app supports (and persisting that choice in a user profile database).

If you wish to programmatically infer the user's locale you can inspect the Accept-Language HTTP request header. (This is part of HTTP content negotiation.) There are npm modules that can help with this, accepts and negotiator being two good choices. As well, express has some content negotiation built in.

var app        = require('express')();
var appLocales = ['de', 'en', 'fr'];

app.get('/', function (req, res) {
    var locale = req.acceptsLanguages(appLocales) || 'en';
    // ...customize the response to locale
});

Client (Browsers)

The Intl APIs are currently available on all modern browsers. See the Mozilla Developer Network documentation for an up-to-date list of capable browsers.

It is recommended that you separate the Intl polyfill from your app's JavaScript code bundle so the polyfill is only loaded in the browsers which need it. Also, if you're using the Intl.js polyfill, you will need to load a separate file for each locale. In this case you can dynamically load a single locale file for the user's locale.

How to Polyfill Browsers

The Intl.js polyfill, developed by Andy Earnshaw, provides a polyfill for the Intl API. It needs a locale data file for the current page.

<script src="path/to/intl/Intl.js"></script>
<script src="path/to/intl/locale-data/jsonp/en.js"></script>
...use the locale for the current user, instead of hard-coding to "en"

Since some runtimes already have the Intl APIs, ideally you should only load the polyfill when needed. You can do this using a conditional loader.

One conditional loader is yepnopejs. Here's an overview of how to use it and it provides the general idea for other loading mechanisms:

yepnope({
    test    : window.Intl,
    nope    : ['path/to/intl/polyfill'],
    complete: function () {
    // ...the Intl API can be used here...
    }
});

Note that our integrations require that window.Intl exists before they are loaded.

Determining the User's Locale

When running internationalization code in the browser it is best if the locale used is the same as was used when the page was generated on the server. You can do this by having the server embed the chosen locale into the generate page. This ensures that the user gets a consistent experience and that the UI doesn't suddenly "switch languages" on them.

If this isn't possible or if you have an application which is served statically, the best practice is to provide the user an explicit way to choose one of the locales your app supports. If you wish to programmatically infer the user's locale you can match the following against the locales your app supports.

navigator.language || navigator.browserLanguage

FormatJS Guides