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.
Installation
ember install ember-intl
- Polyfill the Intl.js API (required for Safari/PhantomJS, and legacy browser)
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}'
If you wish, you can organize your translations into subdirectories such as /translations/login-page/en-us.yaml
and translations/purchase-page/en-us.yaml
.
Translation keys containing periods, i.e., foo.bar
, conflicts with internal accessors -- which assumes nested objects and therefor triggers errors like: Error: Property set failed: object in path "foo" could not be found or was destroyed
. However, this is supported as of 2.5.x and to enable run ember g ember-intl-dot-notation
.
Translation Compilation
At build time, ember-intl walks all of the translations within the project and attempts to locate missing translations keys. This is done through the baseLocale
config property. If translations keys are found on the base but not on other locales, a warning is written to the console and ember-intl will automatically use the value from the base locale as a filler.
return {
intl: {
baseLocale: 'en-us'
}
};
Setting runtime locale
Open, or create, app/routes/application.js
and within beforeModel
invoke intl.setLocale
. Example:
export default Ember.Route.extend({
intl: Ember.inject.service(),
beforeModel() {
return this.get('intl').setLocale('en-us');
}
});
Helper 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
List of supported format number options
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
List of supported format date options
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 Time Options
List of supported format date options
Format Relative
Formats dates relative to "now" using IntlRelativeFormat
, and returns the formatted string value.
export default Ember.Component.extend({
timestamp: Ember.computed(function() {
var date = new Date();
date.setDate(date.getDate() - 3);
return date;
})
});
{{format-relative timestamp}} -> 3 days ago
Or programmatically convert a relative time within any Ember Object.
export default Ember.Component.extend({
intl: Ember.inject.service(),
yesterday: Ember.computed(function() {
var date = new Date();
return this.get('intl').formatRelative(date.setDate(date.getDate() - 1));
})
});
Live Relative Timestamp
Recompute the relative timestamp on an interval by passing an interval
argument (in milliseconds).
{{format-relative now interval=1000}} -> now, 1 second ago, 2 seconds ago, etc. (will recompute every 1s)
Format Relative Options
List of supported format date options
Format Message
Formats ICU Message strings with the given values supplied as the hash arguments. A short-hand form of the {{format-message}}
is {{t}}
.
You have {numPhotos, plural,
=0 {no photos.}
=1 {one photo.}
other {# photos.}}
{{t 'product.info'
product='Apple watch'
price=200
deadline=yesterday}}
{{t 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() {
return this.get('intl').formatMessage('Hello {name}', { name: 'Jason' });
})
});
Passing a string literal to Format Message
This is done by using the {{l}}
(lowercase L) helper as a subexpression. This is useful for computed properties where you are programmatically constructing a translation string.
{{t (l '{name} took {numPhotos, plural,\n =0 {no photos}\n =1 {one photo}\n other {# photos}\n} on {takenDate, date, long}')
name='Jason'
numPhotos=num
takenDate=yesterday}}
Format HTML Message
This delegates to the {{t}}
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 'product.html.info'
product='Apple watch'
price=200
deadline=yesterday}}
{{format-html-message (l '<strong>{numPhotos}</strong>')
numPhotos=(format-number num)}}
Named Formats
Specifying format options (e.g.: style="currency" currency="USD") in every use of format helper can become a problem in large code bases, and isn't DRY. Instead, you can provide named formats through the use of exporting a POJO from app/formats
. All helpers accept a format
property which accepts a key that maps to the format option under its respected type (time, date, number, relative).
For example:
export default {
date: {
hhmmss: {
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
}
}
};
{{format-date 'Thu Jan 23 2014 13:00:44' format='hhmmss'}}
this.get('intl').formatDate('Thu Jan 23 2014 13:00:44', {
format: 'hhmmss'
})
Output of both the helper and the programmatic example:
1:00:44 PM
Polyfill
There are two options on how to load the Intl.js polyfill, either through the polyfill which ships with ember-intl or through polyfill.io. Both of which are documented: https://github.com/yahoo/ember-intl/wiki/Intl.js-Polyfill
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
Phantom does support the Intl API, so in order for for you ember-intl to work in a browser which does not support the Intl API, it needs to be polyfilled.
To resolve this, add the following above all script tags in tests/index.html
:
<script src="assets/intl/intl.complete.js"></script>
Asynchronously loading translations
Asynchronously loading translations instead of bundling translations within app.js
are fully-supported as of 2.x.
https://github.com/yahoo/ember-intl/wiki/Asynchronously-loading-translations
Testing with ember-intl
Common Errors
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