/ember-intl

🌐 Translate simple to complex messages. Localized formatting for date/time, number, and relative time.

Primary LanguageJavaScriptOtherNOASSERTION

ember-intl

npm Version Build Status npm Ember Observer Score

Notable Features

  • 💵 Locale-aware numbers. Formatting of currencies, decimals, and percentages.
  • 📅 Locale-aware dates and times formatting
  • 🕑 Locale-aware display of relative time. I.e, "now", "yesterday", "2 mo. ago"
  • 💬 ICU Message Syntax. Pluralization and formatted segments (numbers, datetime, etc.).
  • 🌐 Support for 150+ languages.
  • 📜 Built largely on standards. ICU message syntax & Native Intl API.
  • ⚡ Extensive Ember Service API and template helpers for formatting and translating.
  • 🎉 Advanced addon support to provide translations to the host app

Installation

ember i ember-intl

Documentation

3.0 (current)

2.0 docs

Migrating from 2.0?

Here is a light touch migration guide to help you get started. If you uncover any gaps, submit a PR to update the migration doc or open an issue.

Documentation

Documentation is hosted in the repository within the /docs folder.

Translations

Translations are defined in ICU message syntax and store in <project_root>/translations in either JSON and/or YAML format. Nested directories are supported along with nested objects within your translation files.

Example basic translation file /translations/homepage/en-us.yaml:

homepage:
  banner: '<strong>{product}</strong> will cost <em>{price, number, USD}</em> if ordered by {deadline, date, time}'

Setting Locale

This is can be done at any point within your app boots. This is typically done within your Application route's beforeModel hook by calling intl.setLocale('en-us') Read more about the Service API.

  // app/routes/application.js
  export default Route.extend({
    intl: service(),
    beforeModel() {
      /* NOTE: if you lazily load translations, here is also where you would load them via `intl.addTranslations` */
      return this.get('intl').setLocale(['fr-fr', 'en-us']); /* array optional */
    }
  });

Helper Examples

Format Message

Compiles a ICU message syntax strings with its hash values passed.

# en-us.yml
photos:
  banner: "You have {numPhotos, plural, =0 {no photos.} =1 {one photo.} other {# photos.}}"

Template Helper

{{t 'photos.banner' numPhotos=model.photos.length}}

Service API

export default Component.extend({
  intl: service(),

  banner: computed('intl.locale', 'model.photos.length', function() {
    return this.get('intl').t('photos.banner', {
      photos: this.get('model.photos.length')
    });
  })
});

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 Component.extend({
  intl: service(),
  computedNumber: computed('intl.locale', 'cost', function() {
    return this.get('intl').formatNumber(this.get('cost')/*, optional options hash */);
  })
});

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 Component.extend({
  intl: service(),
  computedNow: computed('intl.locale', function() {
    return this.get('intl').formatDate(new Date()/*, optional options hash */);
  })
});

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.

// example
export default Component.extend({
  intl: service(),
  computedNow: computed('intl.locale', function() {
    return this.get('intl').formatTime(new Date()/*, optional options hash */);
  })
});

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 Component.extend({
  timestamp: computed(function() {
    let 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 Component.extend({
  intl: service(),
  yesterday: computed('intl.locale', function() {
    let date = new Date();
    return this.get('intl').formatRelative(date.setDate(date.getDate() - 1)/*, optional options hash */);
  })
});

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

Formatting a string literal ICU messages

Template Helper

{{format-message "{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}} on {timestamp, date, long}"
  name=user.username
  numPhotos=num
  timestamp=yesterday
}}

Service API

export default Component.extend({
  intl: service(),
  count: 0,
  label: computed('intl.locale', 'model.photos.length', function() {
    return this.get('intl').formatMessage(`
      You took {numPhotos, plural,
        =0 {no photos}
        =1 {one photo}
        other {# photos}
      }
    `,
    {
      numPhotos: this.get('model.photos.length')
    });
  }).readOnly()
});

Format HTML Message

Escapes all hash arguments and returns as an htmlSafe String which renders an ElementNode. To enable rendering HTML within translations, pass an htmlSafe attribute to the t helper.

{{t 'a.translation' htmlSafe=true}}
{{format-message '<em>{photos, number}</em>' photos=models.photos.length htmlSafe=true}}

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:

// app/formats.js
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

Helper Options

  • All helpers accept optional arguments:
    • locale argument to explicitly pass/override the application locale
    • format 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 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

Asynchronously loading translations instead of bundling translations within app.js are fully-supported as of 2.x. https://github.com/ember-intl/ember-intl/blob/2.x/docs/asynchronously-loading-translations.md

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.

Migrating from ember-i18n

  • Simple migration tool to convert your translations files and application code to this addon. Feel free to report any issues with the migration tool here.

Running

Running Tests

  • ember test
  • ember test --server