Skip to content

Doc: Translating Text

Keith Silgard edited this page Apr 19, 2017 · 16 revisions

{{t}} Helper

A simple translation:

<h2>{{t "user.edit.title"}}</h2>


<h2>Edit User</h2>

Translating an attribute:

<input name="email" placeholder={{t ""}}>

or an attribute inside an handlebars input:

{{input name="email" placeholder=(t "")}}


<input name="email" placeholder="e.g.">

A translation based on a bound key:

<h2>{{t title_i18n_key}}</h2>


<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>


<h2>All 2 Followers</h2>

Interpolated values can be bound:

<h2>{{t "user.followers.title" count=user.followers.count}}</h2>


<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({

  i18n: Ember.inject.service(),

  // A simple translation.
  title: t("user.edit.title"),

  followersCount: 1,

  // A translation with interpolations. This computed property
  // depends on `count` and will send `{ count: this.get('followersCount') }`
  // in to the translation.
  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.get('i18n') being the service:i18n. See Doc: i18n Service for more information on where it is available.


If neither the helper nor the macro works for your use-case, you can use the i18n service directly:

export default Ember.Component.extend({

  // The dependency on i18n.locale is important if you want the
  // translated value to be recomputed when the user changes their locale.
  title: Ember.computed('i18n.locale', 'user.isAdmin', function() {
    if (this.get('user.isAdmin')) {
      return this.get('i18n').t('admin.edit.title');
    } else {
      return this.get('i18n').t('user.edit.title');


Note that i18n.t returns an instance of Ember.SafeString, which is perfect for emitting to HTMLBars templates, but may not be what you want in other situations. In that case, you can convert it to a plain string via '' + i18n.t('...') or i18n.t('...').toString():

const i18n = this.get('i18n');
const tooltipText = i18n.t('my.tooltip');
this.set('tooltipText', '' + tooltipText);

Fallback Keys

You can pass additional translation keys in an Array under the default key. They will be used, in order, if the primary translation is not found.

For example, given the translations

  "": "kitty",
  "": "cub"

i18n.t will behave as follows:

i18n.t('puppies.tiger', { default: '' }) // => "kitty"
i18n.t('puppies.wolf', { default: ['', ''] }) // => "cub"
i18n.t('puppies.ferret', { default: ['puppies.polecat'] }) // => "Translation Missing: puppet.ferret"

See also the similar feature in Rails

Nesting Translations

You want to use a translated value in other translations. You may try to nest like this:

"appname": "Application Name",
"welcome": "Welcome to {{t 'appname'}}",
"copyright": "Copyright 2015. {{t 'appname'}}"

This is possible, but not like that. The translation files shouldn't have handlebars logic (other than variable lookups), so you need to do it in two steps:

{{t "welcome" appname=(t "appname")}}
{{t "copyright" appname=(t "appname")}}
"appname": "Application Name",
"welcome": "Welcome to {{appname}}",
"copyright": "Copyright 2015. {{appname}}"
You can’t perform that action at this time.