ember-intl
This library provides Ember Handlebar helpers and a localization service. The service, and helpers, provide a way to format dates, numbers, strings messages, including pluralization.
Ember-Intl 2.0
This README is targeting and explaining the 2.0 API which differs from previous versions and the latest stable release. If you are looking for the 1.3.x documentation, please check here.
Overview
Ember Intl is part of FormatJS, the docs can be found on the website:
http://formatjs.io/ember/
Requirements
- Ember-cli >= 0.2.0
- Ember >= 1.10.x <= 1.13.x
- HTMLBars (Glimmer supported)
Installation
ember install ember-intl@2.0.0-beta.7
(or ember install:addon ember-intl@2.0.0-beta.7
for ember-cli < v0.2.3)- If you are targeting a browser that doesn't support the native Intl API (such as Safari or PhantomJS), you need to load the shim. The Intl.JS polyfill is automatically added into the asset distribution folder at build time.
<script src="assets/intl/intl.min.js"></script>
<script src="assets/intl/locales/en-us.js"></script>
<script src="assets/intl/locales/fr-fr.js"></script>
<script src="assets/intl/locales/es-es.js"></script>
Translations
Translations are defined in /translations
, outside of app
in either JSON or YAML format. Example of /translations/en-us.yaml
:
product:
info: '{product} will cost {price, number, USD} if ordered by {deadline, date, time}'
title: 'Hello world!'
html:
info: '<strong>{product}</strong> will cost <em>{price, number, USD}</em> if ordered by {deadline, date, time}'
Configure application-wide locale
Open, or create, app/routes/application.js
and in the beforeModel
hook set intl.locale
. Example:
export default Ember.Route.extend({
intl: Ember.inject.service(),
beforeModel() {
this.set('intl.locale', 'en-us');
}
});
- A default locale is required. This is used as the "source of truth" to determine if any translations are missing a translation at build time. It will offer warnings displaying with locale's are missing translations for a particular key. The default locale is configurable within
config/environment.js
.
ENV: {
...
intl: {
defaultLocale: 'en-us'
}
}
Examples
Format Number
Formats numbers using Intl.NumberFormat
, and returns the formatted string value.
{{format-number num}}
{{format-number num format='EUR'}}
{{format-number num style='currency' currency='USD'}}
Or programmatically convert a number within any Ember Object.
export default Ember.Component.extend({
intl: Ember.inject.service(),
computedNumber: Ember.computed('cost', function() {
return this.get('intl').formatNumber(this.get('cost'));
})
});
Format Number Options
localeMatcher
The locale matching algorithm to use. Possible values are "lookup" and "best fit"; the default is "best fit". For information about this option, see the Intl page.
style
The formatting style to use. Possible values are "decimal" for plain number formatting, "currency" for currency formatting, and "percent" for percent formatting; the default is "decimal".
currency
The currency to use in currency formatting. Possible values are the ISO 4217 currency codes, such as "USD" for the US dollar, "EUR" for the euro, or "CNY" for the Chinese RMB — see the Current currency & funds code list. There is no default value; if the style is "currency", the currency property must be provided.
currencyDisplay
How to display the currency in currency formatting. Possible values are "symbol" to use a localized currency symbol such as €, "code" to use the ISO currency code, "name" to use a localized currency name such as "dollar"; the default is "symbol".
useGrouping
Whether to use grouping separators, such as thousands separators or thousand/lakh/crore separators. Possible values are true and false; the default is true.
The following properties fall into two groups: minimumIntegerDigits, minimumFractionDigits, and maximumFractionDigits in one group, minimumSignificantDigits and maximumSignificantDigits in the other. If at least one property from the second group is defined, then the first group is ignored.
minimumIntegerDigits
The minimum number of integer digits to use. Possible values are from 1 to 21; the default is 1.
minimumFractionDigits
The minimum number of fraction digits to use. Possible values are from 0 to 20; the default for plain number and percent formatting is 0; the default for currency formatting is the number of minor unit digits provided by the ISO 4217 currency code list (2 if the list doesn't provide that information).
maximumFractionDigits
The maximum number of fraction digits to use. Possible values are from 0 to 20; the default for plain number formatting is the larger of minimumFractionDigits and 3; the default for currency formatting is the larger of minimumFractionDigits and the number of minor unit digits provided by the ISO 4217 currency code list (2 if the list doesn't provide that information); the default for percent formatting is the larger of minimumFractionDigits and 0.
minimumSignificantDigits
The minimum number of significant digits to use. Possible values are from 1 to 21; the default is 1.
maximumSignificantDigits
The maximum number of significant digits to use. Possible values are from 1 to 21; the default is minimumSignificantDigits.
Format Date
Formats dates using Intl.DateTimeFormat
, and returns the formatted string value.
{{format-date now weekday='long' timeZone='UTC'}}
{{format-date now hour='numeric' minute='numeric' hour12=false}}
Or programmatically convert a date within any Ember Object.
export default Ember.Component.extend({
intl: Ember.inject.service(),
computedNow: Ember.computed(function() {
return this.get('intl').formatDate(new Date());
})
});
Format Date Options
localeMatcher
The locale matching algorithm to use. Possible values are "lookup" and "best fit"; the default is "best fit". For information about this option, see the Intl page.
timeZone
The time zone to use. The only value implementations must recognize is "UTC"; the default is the runtime's default time zone. Implementations may also recognize the time zone names of the IANA time zone database, such as "Asia/Shanghai", "Asia/Kolkata", "America/New_York".
hour12
Whether to use 12-hour time (as opposed to 24-hour time). Possible values are true and false; the default is locale dependent.
formatMatcher
The format matching algorithm to use. Possible values are "basic" and "best fit"; the default is "best fit". See the following paragraphs for information about the use of this property.
weekday
The representation of the weekday. Possible values are "narrow", "short", "long".
era
The representation of the era. Possible values are "narrow", "short", "long".
year
The representation of the year. Possible values are "numeric", "2-digit".
month
The representation of the month. Possible values are "numeric", "2-digit", "narrow", "short", "long".
day
The representation of the day. Possible values are "numeric", "2-digit".
hour
The representation of the hour. Possible values are "numeric", "2-digit".
minute
The representation of the minute. Possible values are "numeric", "2-digit".
second
The representation of the second. Possible values are "numeric", "2-digit".
timeZoneName
The representation of the time zone name. Possible values are "short", "long".
Format Time
This is just like the {{format-date}}
helper, except it will reference any string-named format
from formats.time
.
{{format-time now format='hhmmss'}}
{{format-time now hour='numeric' minute='numeric' hour12=false}}
Or programmatically convert a time within any Ember Object.
export default Ember.Component.extend({
intl: Ember.inject.service(),
computedNow: Ember.computed(function() {
return this.get('intl').formatTime(new Date());
})
});
Format Relative
Formats dates relative to "now" using IntlRelativeFormat
, and returns the formatted string value.
{{format-relative yesterday}}
Or programmatically convert a relative time within any Ember Object.
export default Ember.Component.extend({
intl: Ember.inject.service(),
yesterday: Ember.computed(function() {
var now = new Date();
return this.get('intl').formatRelative(now.setDate(now.getDate() - 1));
})
});
Format Relative Options
style
options for "best fit" ("yesterday") and "numeric" ("1 day ago") output.
units
options for always rendering in a particular unit; e.g. "30 days ago", instead of "1 month ago".
Format Message
Formats ICU Message strings with the given values supplied as the hash arguments.
You have {numPhotos, plural,
=0 {no photos.}
=1 {one photo.}
other {# photos.}}
{{format-message (intl-get 'product.info')
product='Apple watch'
price=200
deadline=yesterday}}
{{format-message boundProperty
name='Jason'
numPhotos=num
takenDate=yesterday}}
Or programmatically convert a message within any Ember Object.
export default Ember.Component.extend({
intl: Ember.inject.service(),
yesterday: Ember.computed(function() {
var now = new Date();
return this.get('intl').formatMessage('Hello {name}', { name: 'Jason' });
})
});
Format HTML Message
This delegates to the {{format-message}}
helper, but will first HTML-escape all of the hash argument values. This allows the message
string to contain HTML and it will be considered safe since it's part of the template and not user-supplied data.
{{format-html-message (intl-get 'product.html.info')
product='Apple watch'
price=200
deadline=yesterday}}
{{format-html-message '<strong>{numPhotos}</strong>'
numPhotos=(format-number num)}}
Intl-Get
Utility helper for returning the value, or eventual value, based on a translation key. Should only ever be used as a subexpression, never as a standalone helper.
{{format-message (intl-get 'product.info')
product='Apple watch'
price=200
deadline=yesterday}}
Will return the message from the current locale, or locale explicitly passed as an argument, message object.
product:
info: '{product} will cost {price, number, EUR} if ordered by {deadline, date, time}'
Helper Options
- All helpers accept optional arguments:
locale
argument to explicitly pass/override the application localeformat
argument which you pass in a key corresponding to a format configuration in app/formats.js
Writing Unit Tests
If using the intl helpers within a components or views that is unit tested, needs
the service, helper, and formatter into the unit test.
In the setup hook of moduleFor
/moduleForComponent
you'll want to also invoke registerIntl(container);
-- which is a utility function to setup the injection logic on the unit test container.
NOTE: Add the following above all script tags in tests/index.html
<script src="assets/intl/intl.complete.js"></script>
This is to shim your test runner if running within phantomjs, or any browser which does not natively support the Intl API.
=======
Example unit test
import Ember from 'ember';
import { registerIntl } from '../../../initializers/ember-intl';
import {
moduleFor,
test
} from 'ember-qunit';
moduleFor('view:index', 'IndexView', {
needs: [
'template:index',
'ember-intl@adapter:-intl-adapter',
'service:intl',
'helper:intl-get',
'ember-intl@formatter:format-message',
'translation:en-us',
'translation:es-es'
],
setup: function () {
var container = this.container || arguments[0];
registerIntl(container);
var intl = container.lookup('service:intl');
intl.set('locale', 'en-us');
}
});
test('index renders', function () {
expect(2);
var view = this.subject({
context: Ember.Object.create({
firstName: 'Tom'
})
});
var intl = view.get('intl');
Ember.run(view, 'appendTo', '#qunit-fixture');
equal(view.$().text().trim(), "hello Tom");
Ember.run(function () { intl.set('locale', 'es-es'); });
equal(view.$().text().trim(), "hola Tom");
Ember.run(view, 'destroy');
});
Known Gotchas
date value is not finite in DateTimeFormat.format()
Browser vendors implement date/time parsing differently. For example, the following will parse correctly in Chrome but fail in Firefox: new Intl.DateTimeFormat().format('2015-04-21 20:47:31 GMT');
The solution is the ensure that the value you are passing in is in a format which is valid for the Date
constructor. This library currently does not try and normalize date strings outside of what the browser already implements.
Running
Running Tests
ember test
ember test --server