1 | # ember-intl
|
2 |
|
3 | [![npm Version][npm-badge]][npm]
|
4 | [![Build Status][travis-badge]][travis]
|
5 | [![npm](https://img.shields.io/npm/dm/ember-intl.svg)](https://www.npmjs.com/package/ember-intl)
|
6 | [![Ember Observer Score](http://emberobserver.com/badges/ember-intl.svg)](http://emberobserver.com/addons/ember-intl)
|
7 |
|
8 | ## Notable Features
|
9 |
|
10 | * 💵 Locale-aware numbers. Formatting of currencies, decimals, and percentages.
|
11 | * 📅 Locale-aware dates and times formatting
|
12 | * 🕑 Locale-aware display of relative time. I.e, `"now"`, `"yesterday"`, `"2 mo. ago"`
|
13 | * 💬 ICU Message Syntax. Pluralization and formatted segments (numbers, datetime, etc.).
|
14 | * 🌐 Support for 150+ languages.
|
15 | * 📜 Built largely on standards. [ICU message syntax][ICU] & [Native Intl API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl).
|
16 | * ⚡ Extensive Ember Service API and template helpers for formatting and translating.
|
17 | * 🎉 [Advanced addon support](https://ember-intl.github.io/ember-intl/docs/advanced/addon-support) to provide translations to the host app
|
18 |
|
19 | ## Installation
|
20 |
|
21 | `ember i ember-intl`
|
22 |
|
23 | ## Documentation
|
24 |
|
25 | [4.x (current stable)](https://ember-intl.github.io/ember-intl/versions/v4.0.0/docs)
|
26 | [3.x](https://github.com/ember-intl/ember-intl/tree/3.x/docs)
|
27 | [2.x](https://github.com/ember-intl/ember-intl/tree/2.x/docs)
|
28 |
|
29 | ## Migrating from 2.0?
|
30 |
|
31 | [Here is a light touch migration guide to help you get started](https://ember-intl.github.io/ember-intl/docs/legacy/migration-2-0-to-3-0). If you uncover any gaps, submit a PR to update the migration doc or open an issue.
|
32 |
|
33 | ## Translations
|
34 | Translations are defined in [ICU message syntax][ICU] 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.
|
35 |
|
36 | Example basic translation file `/translations/homepage/en-us.yaml`:
|
37 |
|
38 | ```yaml
|
39 | homepage:
|
40 | banner: '<strong>{product}</strong> will cost <em>{price, number, USD}</em> if ordered by {deadline, date, time}'
|
41 | ```
|
42 |
|
43 | ## Setting Locale
|
44 |
|
45 | This can be done at any point after 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](https://ember-intl.github.io/ember-intl/versions/v4.0.0/docs/guide/ember-service-api).
|
46 |
|
47 | ```js
|
48 | // app/routes/application.js
|
49 | export default Route.extend({
|
50 | intl: service(),
|
51 | beforeModel() {
|
52 | this._super(...arguments)
|
53 | /* NOTE: if you lazily load translations, here is also where you would load them via `intl.addTranslations` */
|
54 | return this.intl.setLocale(['fr-fr', 'en-us']); /* array optional */
|
55 | }
|
56 | });
|
57 | ```
|
58 |
|
59 | ## Helper Examples
|
60 |
|
61 | ### Format Message
|
62 |
|
63 | Compiles a [ICU message syntax][ICU] strings with its hash values passed.
|
64 |
|
65 | ```
|
66 | # en-us.yml
|
67 | photos:
|
68 | banner: "You have {numPhotos, plural, =0 {no photos.} =1 {one photo.} other {# photos.}}"
|
69 | ```
|
70 |
|
71 | **Template Helper**
|
72 |
|
73 | ```hbs
|
74 | {{t 'photos.banner' numPhotos=model.photos.length}}
|
75 | ```
|
76 |
|
77 | **Service API**
|
78 |
|
79 | ```js
|
80 | export default Component.extend({
|
81 | intl: service(),
|
82 |
|
83 | banner: computed('intl.locale', 'model.photos.length', function() {
|
84 | return this.intl.t('photos.banner', {
|
85 | photos: this.get('model.photos.length')
|
86 | });
|
87 | })
|
88 | });
|
89 | ```
|
90 |
|
91 | ### Format Number
|
92 | Formats numbers using [`Intl.NumberFormat`][Intl-NF], and returns the formatted string value.
|
93 |
|
94 | ```hbs
|
95 | {{format-number num}}
|
96 | {{format-number num format='EUR'}}
|
97 | {{format-number num style='currency' currency='USD'}}
|
98 | ```
|
99 |
|
100 | Or programmatically convert a number within any Ember Object.
|
101 |
|
102 | ```js
|
103 | export default Component.extend({
|
104 | intl: service(),
|
105 | computedNumber: computed('intl.locale', 'cost', function() {
|
106 | return this.intl.formatNumber(this.cost/*, optional options hash */);
|
107 | })
|
108 | });
|
109 | ```
|
110 |
|
111 | #### Format Number Options
|
112 |
|
113 | [List of supported format number options](https://ember-intl.github.io/ember-intl/versions/v4.0.0/docs/helpers/format-number#format-number-options)
|
114 |
|
115 | ### Format Date
|
116 |
|
117 | Formats dates using [`Intl.DateTimeFormat`][Intl-DTF], and returns the formatted string value.
|
118 |
|
119 | ```hbs
|
120 | {{format-date now weekday='long' timeZone='UTC'}}
|
121 | {{format-date now hour='numeric' minute='numeric' hour12=false}}
|
122 | ```
|
123 |
|
124 | Or programmatically convert a date within any Ember Object.
|
125 |
|
126 | ```js
|
127 | export default Component.extend({
|
128 | intl: service(),
|
129 | computedNow: computed('intl.locale', function() {
|
130 | return this.intl.formatDate(new Date()/*, optional options hash */);
|
131 | })
|
132 | });
|
133 | ```
|
134 |
|
135 | #### Format Date Options
|
136 |
|
137 | [List of supported format date options](https://ember-intl.github.io/ember-intl/versions/v4.0.0/docs/helpers/format-date#format-date-time-options)
|
138 |
|
139 | ### Format Time
|
140 |
|
141 | This is just like the `{{format-date}}` helper, except it will reference any string-named `format` from [`formats.time`](#dataintlformats).
|
142 |
|
143 | ```hbs
|
144 | {{format-time now format='hhmmss'}}
|
145 | {{format-time now hour='numeric' minute='numeric' hour12=false}}
|
146 | ```
|
147 |
|
148 | Or programmatically convert a time within any Ember Object.
|
149 |
|
150 | ```js
|
151 | // example
|
152 | export default Component.extend({
|
153 | intl: service(),
|
154 | computedNow: computed('intl.locale', function() {
|
155 | return this.intl.formatTime(new Date()/*, optional options hash */);
|
156 | })
|
157 | });
|
158 | ```
|
159 |
|
160 | #### Format Time Options
|
161 | [List of supported format date options](https://ember-intl.github.io/ember-intl/versions/v4.0.0/docs/helpers/format-date#format-date-time-options)
|
162 |
|
163 | ### Format Relative
|
164 |
|
165 | Formats dates relative to "now" using [`IntlRelativeFormat`][Intl-RF], and returns the formatted string value.
|
166 |
|
167 | ```js
|
168 | export default Component.extend({
|
169 | timestamp: computed(function() {
|
170 | let date = new Date();
|
171 | date.setDate(date.getDate() - 3);
|
172 | return date;
|
173 | })
|
174 | });
|
175 | ```
|
176 |
|
177 | ```hbs
|
178 | {{format-relative timestamp}} -> 3 days ago
|
179 | ```
|
180 |
|
181 | ```hbs
|
182 | {{format-relative timestamp units='day'}} -> 3 days ago
|
183 | ```
|
184 |
|
185 | `units` is optional, by default will default to `best-fit`. [A full list of supported unit options](https://ember-intl.github.io/ember-intl/versions/v4.0.0/docs/helpers/format-relative#format-relative-options)
|
186 |
|
187 | Or programmatically convert a relative time within any Ember Object.
|
188 |
|
189 | ```js
|
190 | export default Component.extend({
|
191 | intl: service(),
|
192 | yesterday: computed('intl.locale', function() {
|
193 | let date = new Date();
|
194 | return this.intl.formatRelative(date.setDate(date.getDate() - 1)/*, optional options hash */);
|
195 | })
|
196 | });
|
197 | ```
|
198 |
|
199 | #### Live Relative Timestamp
|
200 |
|
201 | Recompute the relative timestamp on an interval by passing an `interval` argument (in milliseconds).
|
202 |
|
203 | ```hbs
|
204 | {{format-relative now interval=1000}} -> now, 1 second ago, 2 seconds ago, etc. (will recompute every 1s)
|
205 | ```
|
206 |
|
207 | #### Format Relative Options
|
208 |
|
209 | [List of supported format relative options](https://ember-intl.github.io/ember-intl/versions/v4.0.0/docs/helpers/format-relative#format-relative-options)
|
210 |
|
211 | #### Formatting a string literal ICU messages
|
212 |
|
213 | **Template Helper**
|
214 |
|
215 | ```hbs
|
216 | {{format-message "{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}} on {timestamp, date, long}"
|
217 | name=user.username
|
218 | numPhotos=num
|
219 | timestamp=yesterday
|
220 | }}
|
221 | ```
|
222 |
|
223 | **Service API**
|
224 |
|
225 | ```js
|
226 | export default Component.extend({
|
227 | intl: service(),
|
228 | count: 0,
|
229 | label: computed('intl.locale', 'model.photos.length', function() {
|
230 | return this.intl.formatMessage(`
|
231 | You took {numPhotos, plural,
|
232 | =0 {no photos}
|
233 | =1 {one photo}
|
234 | other {# photos}
|
235 | }
|
236 | `,
|
237 | {
|
238 | numPhotos: this.get('model.photos.length')
|
239 | });
|
240 | }).readOnly()
|
241 | });
|
242 | ```
|
243 |
|
244 | ### Format HTML Message
|
245 |
|
246 | 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.
|
247 |
|
248 | ```hbs
|
249 | {{t 'a.translation' htmlSafe=true}}
|
250 | {{format-message '<em>{photos, number}</em>' photos=models.photos.length htmlSafe=true}}
|
251 | ```
|
252 |
|
253 | ## Named Formats
|
254 |
|
255 | 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).
|
256 |
|
257 | For example:
|
258 |
|
259 |
|
260 | ```js
|
261 | // app/formats.js
|
262 | export default {
|
263 | date: {
|
264 | hhmmss: {
|
265 | hour: 'numeric',
|
266 | minute: 'numeric',
|
267 | second: 'numeric'
|
268 | }
|
269 | }
|
270 | };
|
271 | ```
|
272 |
|
273 | ```hbs
|
274 | {{format-date 'Thu Jan 23 2014 13:00:44' format='hhmmss'}}
|
275 | ```
|
276 |
|
277 | ```js
|
278 | this.intl.formatDate('Thu Jan 23 2014 13:00:44', {
|
279 | format: 'hhmmss'
|
280 | })
|
281 | ```
|
282 |
|
283 | Output of both the helper and the programmatic example:
|
284 |
|
285 | > 1:00:44 PM
|
286 |
|
287 | ## Helper Options
|
288 |
|
289 | * All helpers accept optional arguments:
|
290 | * `locale` argument to explicitly pass/override the application locale
|
291 | * `format` argument which you pass in a key corresponding to a format configuration in `app/formats.js`
|
292 |
|
293 | ## Asynchronously loading translations
|
294 |
|
295 | Asynchronously loading translations instead of bundling translations within `app.js` are fully-supported as of 2.x.
|
296 | https://ember-intl.github.io/ember-intl/docs/guide/asynchronously-loading-translations
|
297 |
|
298 | ## Testing with ember-intl
|
299 |
|
300 | ember-intl ships with a number of helpers for assist with writing tests. [Documentation](https://ember-intl.github.io/ember-intl/versions/v4.0.0/docs/guide/testing)
|
301 |
|
302 |
|
303 | ## Common Errors
|
304 |
|
305 | > `date value is not finite in DateTimeFormat.format()`
|
306 |
|
307 | 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');`
|
308 |
|
309 | 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.
|
310 |
|
311 | ## Migrating from ember-i18n
|
312 |
|
313 | * 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](https://github.com/DockYard/ember-i18n-to-intl-migrator/issues).
|
314 | - https://github.com/DockYard/ember-i18n-to-intl-migrator
|
315 |
|
316 | [npm]: https://www.npmjs.org/package/ember-intl
|
317 | [npm-badge]: https://img.shields.io/npm/v/ember-intl.svg?style=flat-square
|
318 | [travis]: https://travis-ci.org/ember-intl/ember-intl
|
319 | [travis-badge]: https://travis-ci.org/ember-intl/ember-intl.svg?branch=master
|
320 | [Intl-RF]: https://github.com/yahoo/intl-relativeformat
|
321 | [Intl-MF]: https://github.com/yahoo/intl-messageformat
|
322 | [Intl]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl
|
323 | [Intl-NF]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
|
324 | [Intl-DTF]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
|
325 | [ICU]: https://formatjs.io/guides/message-syntax/
|
326 | [CLDR]: http://cldr.unicode.org/
|
327 | [Intl.js]: https://github.com/andyearnshaw/Intl.js
|
328 | [LICENSE]: https://github.com/yahoo/yahoo-intl/blob/master/LICENSE
|
329 | [FormatJS]: http://formatjs.io/
|