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.
- Addon support (addons can provide translations up to the project application)
- Built on top of native standards (Intl API and ICU message syntax)
- Translations are able to be lazily loaded (not bundled with the app code)
- Formatting for relative time, datetime, and numbers
- Handlebar helpers and service API
ember install ember-intl
- Polyfill the Intl.js API (required for Safari/PhantomJS, and legacy browser)
- Instructions on loading from assets directory
- Instructions on conditionally loading the polyfill based on User-Agent
- Instructions on disabling polyfill
Translations are defined in /translations
, outside of app
in either JSON or YAML format. Example of /translations/en-us.yaml
:
# en-us
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
.
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.
// config/ember-intl.js
module.exports = function(environment) {
return {
baseLocale: 'en-us' // default build-time locale
};
};
Open, or create, app/routes/application.js
and within beforeModel
invoke intl.setLocale
. Example:
// app/routes/application.js
export default Ember.Route.extend({
intl: Ember.inject.service(),
beforeModel() {
// define the app's runtime locale
// For example, here you would maybe do an API lookup to resolver
// which locale the user should be targeted and perhaps lazily
// load translations using XHR and calling intl's `addTranslation`/`addTranslations`
// method with the results of the XHR request
return this.get('intl').setLocale('en-us');
// OR for those that sideload, an array is accepted to handle fallback lookups
// en-ca is the primary locale, en-us is the fallback.
// this is optional, and likely unnecessary if you define baseLocale (see below)
// The primary usecase is if you side load all translations
//
// return this.get('intl').setLocale(['en-ca', 'en-us']);
}
});
Formats numbers using Intl.NumberFormat
, and returns the formatted string value.
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')/*, optional options hash */);
})
});
List of supported format number options
Formats dates using Intl.DateTimeFormat
, and returns the formatted string value.
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()/*, optional options hash */);
})
});
List of supported format date options
This is just like the {{format-date}}
helper, except it will reference any string-named format
from formats.time
.
Or programmatically convert a time within any Ember Object.
// example
export default Ember.Component.extend({
intl: Ember.inject.service(),
computedNow: Ember.computed(function() {
return this.get('intl').formatTime(new Date()/*, optional options hash */);
})
});
List of supported format date options
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;
})
});
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)/*, optional options hash */);
})
});
Recompute the relative timestamp on an interval by passing an interval
argument (in milliseconds).
List of supported format date options
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.}}
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' });
})
});
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.
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.
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:
// app/formats.js
export default {
date: {
hhmmss: {
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
}
}
};
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
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/jasonmit/ember-intl/wiki/Intl.js-Polyfill
- 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 inapp/formats.js
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="{{rootURL}}assets/intl/intl.complete.js"></script>
Asynchronously loading translations instead of bundling translations within app.js
are fully-supported as of 2.x.
https://github.com/jasonmit/ember-intl/wiki/Asynchronously-loading-translations
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.
ember server
- Visit your app at http://localhost:4200.
ember test
ember test --server