Skip to content

Commit 4c314b5

Browse files
committed
Refactor to Octane
* Use Glimmer component for <GetText> * Avoid using observers for helpers * Refactor to ES6 classes * Refactor to use config from config/environment.js instead of extending service
1 parent f393d0f commit 4c314b5

File tree

26 files changed

+507
-462
lines changed

26 files changed

+507
-462
lines changed

README.md

Lines changed: 76 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,28 @@ Compatibility
2424

2525
## Configuration
2626

27-
You should activate fingerprinting for your translation files, to ensure they can be properly cached.
27+
You configure ember-l10n in your `config/environment.js`:
28+
29+
```js
30+
ENV['ember-l10n'] = {
31+
// This is required to be set
32+
locales: ['en', 'de'],
33+
34+
// These are optional (defaults listed)
35+
defaultLocale: 'en',
36+
autoInitialize: false,
37+
defaultPluralForm: 'nplurals=2; plural=(n != 1);',
38+
jsonPath: '/assets/locales'
39+
}
40+
```
41+
42+
* `locales`: The available locales for your application.
43+
* `defaultLocale`: The default locale to use, if no other matching locale is found.
44+
* `autoInitialize`: If you set this to true, the browser locale will be detected on initialization and automatically be set.
45+
* `defaultPluralForm`: Overwrite this if your default locale has a different plural form.
46+
* `jsonPath`: The folder where the JSON files containing the translations can be found.
47+
48+
You should also activate fingerprinting for your translation files, to ensure they can be properly cached.
2849
To do so, you need to add this to your `ember-cli-build.js`:
2950

3051
```js
@@ -71,7 +92,7 @@ be used for translations message ids from JS source:
7192

7293
`tVar()` works exactly the same as `t()`, but it will be ignored by the gettext parser. This is useful if your message ids are variables, for example: `l10n.t(myProperty)` would create a `myProperty` entry in your po-file when gettext is run. So in this case, `l10n.tVar(myProperty)` should be used instead.
7394

74-
Furthermore, there's an auto initialization feature (default: true), which detects user's locale according to system preferences. If the user's locale is supported in `availableLocales`, the corresponding translations are loaded. If the user's locale is not supported, the default locale will be used instead (default: "en"). Please use the following method to change locales:
95+
Furthermore, there's an auto initialization feature (default: false), which detects user's locale according to system preferences. If the user's locale is supported , the corresponding translations are loaded. If the user's locale is not supported, the default locale will be used instead (default: "en"). Please use the following method to change locales:
7596

7697
* `setLocale(locale)`
7798

@@ -80,71 +101,11 @@ The following utility methods are also available:
80101
* `getLocale()`
81102
* `hasLocale(locale)`
82103
* `detectLocale()`
83-
84-
To configure the path of the JSON files (depending on the path configured via convertor's `-o` option) use the `jsonPath` property (default: "/assets/locales").
85-
86-
When installing via `ember install ember-l10n`, an `l10n` service will be created for you under `app/services/l10n.js`.
87-
There, you can configure (and overwrite) all service properties/methods:
88-
89-
```js
90-
import { computed } from '@ember/object';
91-
import L10n from 'ember-l10n/services/l10n';
92-
93-
export default L10n.extend({
94-
/**
95-
* Defines available locales as hash map, where key corresponds
96-
* to ISO_639-1 country codes and value can be any truthy value.
97-
* By default, it's used to translate the language codes, which
98-
* could be used for a language drop down. Adjust the hash map
99-
* for each new language being added your translatable project.
100-
*
101-
* @property availableLocales
102-
* @type {object}
103-
* @public
104-
*/
105-
availableLocales: computed('locale', function() {
106-
return {
107-
'en': 'English'
108-
};
109-
}),
110-
111-
/**
112-
* Flag indicating if service should try to detect user langugage
113-
* from browser settings and load/set the corresponding JSON file.
114-
*
115-
* @property autoInitialize
116-
* @type {boolean}
117-
* @public
118-
*/
119-
autoInitialize: true,
120-
});
121-
```
122-
123-
You can create an initializer to inject the l10n-service everywhere with the following blueprint:
124-
125-
```bash
126-
ember g ember-l10n-initializer my-l10n-initializer
127-
```
128-
129-
This will produce an initializer such as:
130-
131-
```js
132-
export function initialize(application) {
133-
application.inject('model', 'l10n', 'service:l10n');
134-
application.inject('route', 'l10n', 'service:l10n');
135-
application.inject('controller', 'l10n', 'service:l10n');
136-
application.inject('component', 'l10n', 'service:l10n');
137-
}
138-
139-
export default {
140-
initialize: initialize,
141-
name: 'my-l10n-initializer'
142-
};
143-
```
104+
* `setDetectedLocale()`
144105

145106
### Helpers
146107

147-
For handling your translations within your handlebar templates you can use `t` and `n` helper:
108+
For handling your translations within your templates you can use `t` and `n` helper:
148109

149110
###### Singular translations:
150111

@@ -190,8 +151,8 @@ Please note: Both contextual helpers also accept placeholders just as their non-
190151
If you have complex message ids, which should contain "dynamic" placeholders, which can also be replaced with components (such as a `link-to`), you can use the `get-text` component.
191152

192153
```hbs
193-
{{#get-text
194-
message=(t "My translation with {{dynamicLink 'optional link text'}} and {{staticLink}}.") as |text placeholder|}}
154+
<GetText
155+
@message={{t "My translation with {{dynamicLink 'optional link text'}} and {{staticLink}}."}} as |text placeholder|>
195156
{{!-- You can omit the if helper if you have only one placeholder --}}
196157
{{~#if (eq placeholder 'dynamicLink')}}
197158
{{~#link-to 'my-route'}}
@@ -201,29 +162,14 @@ If you have complex message ids, which should contain "dynamic" placeholders, wh
201162
{{~#if (eq placeholder 'staticLink')}}
202163
<a href="http://www.google.com">Google</a>
203164
{{~/if~}}
204-
{{/get-text}}
165+
</GetText>
205166
```
206167

207-
Please note: If your message id contains HTML, you have to set `unescapeText=true` on the component. Be sure to use this option only in combination with safe strings and don't make use of it when dealing with user generated inputs (XSS)!
168+
Please note: If your message id contains HTML, you have to set `@unescapeText={{true}}` on the component. Be sure to use this option only in combination with safe strings and don't make use of it when dealing with user generated inputs (XSS)!
208169

209170
### Testing
210171

211-
In acceptance tests, ember-l10n should work without any further work. In integration tests, you can use the provided test helpers to provide easy to use `{{t}}`,`{{tVar}}` and `{{n}}` helpers:
212-
213-
```js
214-
// tests/integration/components/my-component-test.js
215-
import l10nTestHelper from 'ember-l10n/test-helpers';
216-
217-
moduleForComponent('my-component', 'Integration | Component | my component', {
218-
integration: true,
219-
220-
beforeEach() {
221-
l10nTestHelper(this);
222-
}
223-
});
224-
```
225-
226-
These helpers will basically just pass the string through replacing only placeholders.
172+
In tests, ember-l10n should work without any further work.
227173

228174
## 2. CLI
229175

@@ -306,6 +252,8 @@ ember l10n:convert <options...>
306252
Convert PO files to JSON
307253
--convert-from (String) (Default: ./translations) Directory of PO file to convert
308254
aliases: -i <value>
255+
--convert-from-file (String) Optional full path to file to convert. Takes precedence over convert-from & language.
256+
aliases: -f <value>
309257
--convert-to (String) (Default: ./public/assets/locales) Directory to write JSON files to
310258
aliases: -o <value>
311259
--language (String) (Default: en) Target language for PO to JSON conversion
@@ -367,6 +315,49 @@ module.exports = {
367315

368316
## Note for Upgrading
369317

318+
### Upgrading from 4.x to 5.x
319+
320+
In 5.0.0, the configuration format was changed, in order to align better with the Octane world.
321+
Instead of overwriting the l10n service in your app, you now pass the configuration in `config/environment.js`:
322+
323+
```js
324+
ENV['ember-l10n'] = {
325+
// This is required to be set
326+
locales: ['en', 'de'],
327+
328+
// These are optional (defaults listed)
329+
defaultLocale: 'en',
330+
autoInitialize: false,
331+
defaultPluralForm: 'nplurals=2; plural=(n != 1);',
332+
jsonPath: '/assets/locales'
333+
}
334+
```
335+
336+
Other notable changes:
337+
338+
* `autoInitialize` defaults to false now, as you usually want to set the locale manually (e.g. in the application route's `beforeModel` hook).
339+
* If you have been overwriting some fields for tests, the recommended way to do so now is different:
340+
341+
```js
342+
class ExtendedL10nService extends L10nService {
343+
_loadConfig() {
344+
let config = {
345+
locales: ['de', 'en', 'ko'],
346+
autoInitialize: false,
347+
defaultLocale: 'de',
348+
};
349+
350+
return super._loadConfig(config);
351+
}
352+
353+
_getWindow() {
354+
return {};
355+
}
356+
}
357+
```
358+
359+
Unless you are doing something very special, you shouldn't need to extend `ember-l10n/services/l10n` anymore.
360+
370361
### Upgrading from 3.x to 4.x
371362

372363
In 4.0.0, the dependency on `ember-cli-ifa` was dropped.
@@ -392,7 +383,7 @@ This library follows [Semantic Versioning](http://semver.org)
392383

393384
## Legal
394385

395-
[Cropster](https://cropster.com), GmbH &copy; 2019
386+
[Cropster](https://cropster.com), GmbH &copy; 2020
396387

397388
[@cropster](http://twitter.com/cropster)
398389

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{{! template-lint-disable no-triple-curlies }}
2-
{{#each messageParts as |part|~}}
2+
{{#each this.messageParts as |part|~}}
33
{{~#if part.name}}
44
{{~yield part.text part.name}}
55
{{~else~}}
6-
{{~#if unescapeText~}}
6+
{{~#if @unescapeText~}}
77
{{{part.text}}}
88
{{~else~}}
99
{{~part.text~}}

addon/components/get-text.js

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import { typeOf, isEmpty } from '@ember/utils';
2-
import { get, computed } from '@ember/object';
3-
import Component from '@ember/component';
4-
import layout from '../templates/get-text';
1+
import { isEmpty } from '@ember/utils';
2+
import Component from '@glimmer/component';
53

64
/**
75
* A simple helper component to include dynamic parts - mostly link-to helper - within gettext message ids.
86
*
97
* ```html
10-
* {{#get-text message=(t "My translation with {{dynamicLink 'optional link text'}} and {{staticLink}}") as |text placeholder|}}
8+
* <GetText @message={{t "My translation with {{dynamicLink 'optional link text'}} and {{staticLink}}"}} as |text placeholder|>
119
* {{!-- You can omit the if helper if you have only one dynamic part --}}
1210
* {{~#if (eq placeholder 'myLink')}}
1311
* {{~#link-to 'my-route'}}
@@ -17,18 +15,15 @@ import layout from '../templates/get-text';
1715
* {{~#if (eq placeholder 'staticLink')}}
1816
* <a href="http://www.google.com">Google</a>
1917
* {{~/if~}}
20-
* {{/get-text}}
18+
* </GetText>
2119
* ```
2220
*
2321
* @namespace Component
2422
* @class GetText
2523
* @extends Ember.Component
2624
* @public
2725
*/
28-
export default Component.extend({
29-
tagName: '',
30-
layout,
31-
26+
export default class GetTextComponent extends Component {
3227
// -------------------------------------------------------------------------
3328
// Attributes
3429

@@ -40,7 +35,7 @@ export default Component.extend({
4035
* @type {String}
4136
* @public
4237
*/
43-
message: '',
38+
message;
4439

4540
/**
4641
* Whether parsed strings should be unescaped with three curly brackets
@@ -50,9 +45,10 @@ export default Component.extend({
5045
*
5146
* @attribute unescapeText
5247
* @type {String}
48+
* @default: false
5349
* @public
5450
*/
55-
unescapeText: false,
51+
unescapeText;
5652

5753
// -------------------------------------------------------------------------
5854
// Properties
@@ -66,26 +62,26 @@ export default Component.extend({
6662
* @type {Array}
6763
* @public
6864
*/
69-
messageParts: computed('message', function () {
70-
let message = get(this, 'message');
65+
get messageParts() {
66+
let { message } = this.args;
7167

7268
if (!message) {
7369
// eslint-disable-next-line no-console
7470
console.error(
75-
'get-text.js: You need to provide a "message" attribute containing a gettext message!'
71+
'<GetText>: You have to provide a @message containing a gettext message!'
7672
);
77-
return;
73+
return [];
7874
}
7975

80-
if (typeOf(message) !== 'string') {
76+
if (typeof message !== 'string') {
8177
try {
8278
message = message.toString();
8379
} catch (e) {
8480
// eslint-disable-next-line no-console
8581
console.error(
86-
'get-text.js: "message" must be either a string or an object implementing toString() method!'
82+
'<GetText>: @message must be either a string or an object implementing toString() method!'
8783
);
88-
return;
84+
return [];
8985
}
9086
}
9187

@@ -123,5 +119,5 @@ export default Component.extend({
123119
}
124120

125121
return parts;
126-
}),
127-
});
122+
}
123+
}

addon/helpers/-base.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Helper from '@ember/component/helper';
2+
import { inject as service } from '@ember/service';
3+
4+
export default class BaseHelper extends Helper {
5+
@service l10n;
6+
7+
constructor() {
8+
super(...arguments);
9+
10+
this._callback = () => this.recompute();
11+
this.l10n.registerLocaleChangeCallback(this._callback);
12+
}
13+
14+
willDestroy() {
15+
this.l10n.unregisterLocaleChangeCallback(this._callback);
16+
}
17+
}

addon/helpers/n.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { get } from '@ember/object';
2-
import THelper from './t';
1+
import BaseHelper from './-base';
32

43
/**
54
* This helper provides gettext pluralization for message ids.
@@ -16,14 +15,14 @@ import THelper from './t';
1615
* @extends Ember.Helper
1716
* @public
1817
*/
19-
export default THelper.extend({
18+
export default class NHelper extends BaseHelper {
2019
compute([msgid, msgidPlural, count], hash) {
21-
let l10n = get(this, 'l10n');
20+
let { l10n } = this;
2221

2322
if (!msgid) {
2423
return msgid;
2524
}
2625

2726
return l10n.n(msgid, msgidPlural, count, hash);
28-
},
29-
});
27+
}
28+
}

0 commit comments

Comments
 (0)