Intl MessageFormat
Formats ICU Message strings with number, date, plural, and select placeholders to create localized messages.
Overview
Goals
This package aims to provide a way for you to manage and format your JavaScript app's string messages into localized strings for people using your app. You can use this package in the browser and on the server via Node.js.
This implementation is based on the Strawman proposal, but there are a few places this implementation diverges.
Note: This IntlMessageFormat
API may change to stay in sync with ECMA-402, but this package will follow semver.
How It Works
Messages are provided into the constructor as a String
message, or a pre-parsed AST object.
var msg = new IntlMessageFormat(message, locales, [formats]);
The string message
is parsed, then stored internally in a compiled form that is optimized for the format()
method to produce the formatted string for displaying to the user.
var output = msg.format(values);
Common Usage Example
A very common example is formatting messages that have numbers with plural labels. With this package you can make sure that the string is properly formatted for a person's locale, e.g.:
var MESSAGES = {
'en-US': {
NUM_PHOTOS: 'You have {numPhotos, plural, ' +
'=0 {no photos.}' +
'=1 {one photo.}' +
'other {# photos.}}'
},
'es-MX': {
NUM_PHOTOS: 'Usted {numPhotos, plural, ' +
'=0 {no tiene fotos.}' +
'=1 {tiene una foto.}' +
'other {tiene # fotos.}}'
}
};
var output;
var enNumPhotos = new IntlMessageFormat(MESSAGES['en-US'].NUM_PHOTOS, 'en-US');
output = enNumPhotos.format({numPhotos: 1000});
console.log(output);
var esNumPhotos = new IntlMessageFormat(MESSAGES['es-MX'].NUM_PHOTOS, 'es-MX');
output = esNumPhotos.format({numPhotos: 1000});
console.log(output);
Message Syntax
The message syntax that this package uses is not proprietary, in fact it's a common standard message syntax that works across programming languages and one that professional translators are familiar with. This package uses the ICU Message syntax and works for all CLDR languages which have pluralization rules defined.
Features
-
Uses industry standards: ICU Message syntax and CLDR locale data.
-
Supports plural, select, and selectordinal message arguments.
-
Formats numbers and dates/times in messages using Intl.NumberFormat
and Intl.DateTimeFormat
, respectively.
-
Optimized for repeated calls to an IntlMessageFormat
instance's format()
method.
-
Supports defining custom format styles/options.
-
Supports escape sequences for message syntax chars, e.g.: "\\{foo\\}"
will output: "{foo}"
in the formatted output instead of interpreting it as a foo
argument.
Usage
Intl
Dependency
This package assumes that the Intl
global object exists in the runtime. Intl
is present in all modern browsers and there's work happening to integrate Intl
into Node.js.
Luckly, there's the Intl.js polyfill! You will need to conditionally load the polyfill if you want to support runtimes which Intl
is not already built-in.
Loading Intl.js Polyfill in a browser
If the browser does not already have the Intl
APIs built-in, the Intl.js Polyfill will need to be loaded on the page along with the locale data for any locales that need to be supported:
<script src="intl/Intl.min.js"></script>
<script src="intl/locale-data/jsonp/en-US.js"></script>
Note: Modern browsers already have the Intl
APIs built-in, so you can load the Intl.js Polyfill conditionally, by for checking for window.Intl
.
Loading Intl.js Polyfill in Node.js
Conditionally require the Intl.js Polyfill if it doesn't already exist in the runtime. As of Node <= 0.10, this polyfill will be required.
if (!global.Intl) {
require('intl');
}
Note: When using the Intl.js Polyfill in Node.js, it will automatically load the locale data for all supported locales.
Loading Intl MessageFormat in a browser
<script src="intl-messageformat/intl-messageformat.min.js"></script>
By default, Intl MessageFormat ships with the locale data for English (en
) built-in to the library's runtime. When you need to format data in another locale, include its data; e.g., for French:
<script src="intl-messageformat/locale-data/fr.js"></script>
Note: All 200+ languages supported by this package use their root BCP 47 language tag; i.e., the part before the first hyphen (if any).
Loading Intl MessageFormat in Node.js
Simply require()
this package:
var IntlMessageFormat = require('intl-messageformat');
Note: in Node.js, the data for all 200+ languages is loaded along with the library.
Public API
IntlMessageFormat
Constructor
To create a message to format, use the IntlMessageFormat
constructor. The constructor takes three parameters:
-
message - {String | AST} - String message (or pre-parsed AST) that serves as formatting pattern.
-
locales - {String | String[]} - A string with a BCP 47 language tag, or an array of such strings. If you do not provide a locale, the default locale will be used. When an array of locales is provided, each item and its ancestor locales are checked and the first one with registered locale data is returned. See: Locale Resolution for more details.
-
[formats] - {Object} - Optional object with user defined options for format styles.
var msg = new IntlMessageFormat('My name is {name}.', 'en-US');
Locale Resolution
IntlMessageFormat
uses a locale resolution process similar to that of the built-in Intl
APIs to determine which locale data to use based on the locales
value passed to the constructor. The result of this resolution process can be determined by call the resolvedOptions()
prototype method.
The following are the abstract steps IntlMessageFormat
goes through to resolve the locale value:
-
If no extra locale data is loaded, the locale will always resolved to "en"
.
-
If locale data is missing for a leaf locale like "fr-FR"
, but there is data for one of its ancestors, "fr"
in this case, then its ancestor will be used.
-
If there's data for the specified locale, then that locale will be resolved; i.e.,
var mf = new IntlMessageFormat('', 'en-US');
assert(mf.resolvedOptions().locale === 'en-US');
-
The resolved locales are now normalized; e.g., "en-us"
will resolve to: "en-US"
.
Note: When an array is provided for locales
, the above steps happen for each item in that array until a match is found.
resolvedOptions()
Method
This method returns an object with the options values that were resolved during instance creation. It currently only contains a locale
property; here's an example:
var msg = new IntlMessageFormat('', 'en-us');
console.log(msg.resolvedOptions().locale);
Notice how the specified locale was the all lower-case value: "en-us"
, but it was resolved and normalized to: "en-US"
.
format(values)
Method
Once the message is created, formatting the message is done by calling the format()
method on the instance and passing a collection of values
:
var output = msg.format({name: "Eric"});
console.log(output);
Note: A value must be supplied for every argument in the message pattern the instance was constructed with.
User Defined Formats
Define custom format styles is useful you need supply a set of options to the underlying formatter; e.g., outputting a number in USD:
var msg = new IntlMessageFormat('The price is: {price, number, USD}', 'en-US', {
number: {
USD: {
style : 'currency',
currency: 'USD'
}
}
});
var output = msg.format({price: 100});
console.log(output);
In this example, we're defining a USD
number format style which is passed to the underlying Intl.NumberFormat
instance as its options.
Examples
Plural Label
This example shows how to use the ICU Message syntax to define a message that has a plural label; e.g., "You have 10 photos"
:
You have {numPhotos, plural,
=0 {no photos.}
=1 {one photo.}
other {# photos.}
}
var MESSAGES = {
photos: '...',
...
};
var msg = new IntlMessageFormat(MESSAGES.photos, 'en-US');
console.log(msg.format({numPhotos: 0}));
console.log(msg.format({numPhotos: 1}));
console.log(msg.format({numPhotos: 1000}));
Note: how when numPhotos
was 1000
, the number is formatted with the correct thousands separator.
License
This software is free to use under the Yahoo! Inc. BSD license.
See the LICENSE file for license text and copyright information.