Ember-I18n
Internationalization for Ember
NOTE
The following documentation is for v4.0, which has not yet been released.
For documentation on the most recent release, see
the v3.1.1 README.
This version does not yet work with Glimmer or Ember v1.13. See
the stateful helpers RFC and
the helper registration RFC
for more information.
Requirements
Ember-I18n v4 requires
- Ember v1.10 - v1.12
- Ember-CLI
- jQuery v1.7 - v2.x
Installation
For ember-cli projects, run
$ ember install ember-i18n
For non-ember-cli projects, use
v3.1.1 or earlier.
Defining Translations
Put translation files in app/locales/[locale]/translations.js
. Each file
should export an object. If the values are Function
s, they will be
used as-is. If they are String
s, they will first be compiled using
util:i18n/compile-translation
. A default Handlebars-like implementation is
provided. See below for more information on overriding the compiler.
For example,
export default {
'user.edit.title': 'Edit User',
'user.followers.title.one': 'One Follower',
'user.followers.title.other': 'All {{count}} Followers',
'button': {
'add_user': {
'title': 'Add a user',
'text': 'Add',
'disabled': 'Saving...'
}
}
};
The translations
generator will generate a new translations file for you:
$ ember generate translations es
i18n
Service
Many pieces of ember-i18n rely on the service:i18n
. This service is automatically
injected into every Route, Controller and Component in your app. If you need it
elsewhere, you can register your own injection:
export default {
name: 'i18n',
initialize: function(app) {
app.inject('model', 'i18n', 'service:i18n')
}
};
or you can ask for the service on a case-by-case basis:
export default Ember.Object.extend({
i18n: Ember.inject.service()
});
Adding Translations at Runtime
If you have a translations API (so you can manage them across apps centrally
or so you can deliver only the translations you need), you can add new
translations at runtime via the service:i18n
:
this.i18n.addTranslations('en', {
'user.profile.gravatar.help': 'Manage your avatar at gravatar.com.'
});
Setting the Locale
Set the default locale in config/environment.js
:
ENV.i18n = {
defaultLocale: 'zh'
};
Override the default by setting locale
on the i18n
service:
export default Ember.Route.extend({
afterModel: function(user) {
this.set('i18n.locale', user.get('locale'));
}
});
Note: if you do this in an initializer and use FastBoot, you probably want to
use an instance-initializer
.
Translating Text
{{t}}
Helper
A simple translation:
<h2>{{t "user.edit.title"}}</h2>
yields
<h2>Edit User</h2>
A translation based on a bound key:
<h2>{{t title_i18n_key}}</h2>
yields
<h2>Add a user</h2>
if component.title_i18n_key
is 'button.add_user.title'
. If
it subsequently changes to 'user.edit.title'
, the HTML will
become
<h2>Edit User</h2>
A translation with interpolated values:
<h2>{{t "user.followers.title" count="2"}}</h2>
yields
<h2>All 2 Followers</h2>
Interpolated values can be bound:
<h2>{{t "user.followers.title" count=user.followers.count}}</h2>
yields
<h2>All 2 Followers</h2>
if user.get('followers.count')
returns 2
.
Translation Computed Property Macro
translationMacro
defines a computed property macro that
makes it easy to define translated computed properties. For example,
import { translationMacro as t } from "ember-i18n";
export default Ember.Component.extend({
title: t("user.edit.title"),
followersCount: 1,
followersTitle: t("user.followers.title", { count: "followersCount" })
});
The first argument is the translation key. The second is a hash where the keys
are interpolations in the translation and the values are paths to the values
relative to this
.
The macro relies on this.i18n
being the service:i18n
. See "i18n Service"
docs for more information on where it is available.
i18n.t
If the neither the helper nor the macro works for your use-case, you can use
the i18n service directly:
export default Ember.Component.extend({
title: Ember.computed('i18n.locale', 'user.isAdmin', function() {
if (this.get('user.isAdmin')) {
return this.i18n.t('admin.edit.title');
} else {
return this.i18n.t('user.edit.title');
}
})
});
Pluralization
Ember-i18n includes support for inflection based on a count
interpolation.
Pluralization rules are based on the
Unicode Common Locale Data Repository.
Whenever you pass the count
option to the t
function, template will be pluralized:
export default {
'dog': {
'one': 'a dog',
'other': '{{count}} dogs'
}
};
i18n.t('dog', { count: 1 });
i18n.t('dog', { count: 2 });
ember-i18n converts 1
to .one
and 2
to .other
. Depending on the
locale, there could be up to 6 plural forms used: 'zero', 'one', 'two',
'few', 'many', 'other'.
If you want to override the inflection rules for a locale, you can define
your own in app/locales/[locale]/config.js
. For example, to add support
for zero
to English:
export default {
pluralForm: function englishWithZero(n) {
if (n === 0) { return 'zero'; }
if (n === 1) { return 'one'; }
return 'other';
}
}
Translation Compiler
Default Translation Compiler
ember-i18n includes a default compiler that acts mostly like (unbound) Handlebars.
It supports interpolations with dots in them. It emits strings that are marked as
HTML-safe.
The compiler treats interpolated values as HTML-unsafe by default. You can get
HTML-safe interpolations in two ways:
(1) Mark the interpolated value as HTML-safe:
seeUserMessage: Ember.computed('i18n.locale', 'user.id', function() {
var userLink = '<a href="/users/' + user.get('id') + '">' + user.get('name') + '</a>';
return this.i18n.t('info.see-other-user', {
userLink: Ember.String.htmlSafe(userLink)
});
})
(2) Use triple-stache notation in the translation:
export default {
'info.see-other-user': "Please see {{{userLink}}}"
};
In general, the first method is preferred because it makes it more difficult
to accidentally introduce an XSS vulnerability.
The compiler also adds Unicode RTL markers around the output if the locale specifies it. You can
override this behavior by setting rtl
in the locale's configuration file:
export default {
rtl: true
}
Overriding the Compiler
You can override the compiler by defining util:i18n/compile-translation
.
For example, if your translation templates use %{}
to indicate an
interpolation, you might do
const interp = /%\{([^\}]+)\}/g;
const escape = Ember.Handlebars.Utils.escapeExpression;
const get = Ember.get;
export default function compile(template) {
return function(context) {
return template
.replace(interp, function(i, match) {
return escape(get(context, match));
});
};
}
Missing translations
When t
is called with a nonexistent key, it returns the result of calling
util:i18n/missing-translation
with the key and the context as arguments. The
default behavior is to return "Missing translation: [key]", but you can
customize this by overriding the function. The below example spits out the
key along with the values of any arguments that were passed:
export default function(locale, key, context) {
var values = Object.keys(context).map(function(key) { return context[key]; });
return key + ': ' + (values.join(', '));
}
t('nothing.here', { arg1: 'foo', arg2: 'bar' });
When a missing translation is encountered, a missing
event is also triggered
on the i18n
service, with the key and the context as arguments. You can use this
to execute other missing-translation behavior such as logging the key somewhere.
i18n.on('missing', function(locale, key, context) {
Ember.Logger.warn("Missing translation: " + key);
};