Skip to content

Commit 5da101d

Browse files
committed
feat(Translate): adding TRANSLATE_PROVIDERS for DI
Fixes #48
1 parent e0ac54e commit 5da101d

6 files changed

+128
-27
lines changed

README.md

+28-14
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,21 @@ System.config({
2121
```
2222

2323
Finally, you can use ng2-translate in your Angular 2 project (make sure that you've loaded the angular2/http bundle as well).
24-
It is recommended to use `NG_TRANSLATE_PROVIDERS` in the bootstrap of your application and to never add `TranslateService` to the "providers" property of your components, this way you will keep it as a singleton.
25-
`NG_TRANSLATE_PROVIDERS` provides a default configuration for the static translation file loader.
24+
It is recommended to use `TRANSLATE_PROVIDERS` in the bootstrap of your application and to never add `TranslateService` to the "providers" property of your components, this way you will keep it as a singleton.
25+
`TRANSLATE_PROVIDERS` provides a default configuration for the static translation file loader.
2626
If you add `TranslateService` to the "providers" property of a component it will instantiate a new instance of the service that won't be initialized with the language to use or the default language.
2727

2828
```js
2929
import {HTTP_PROVIDERS} from 'angular2/http';
3030
import {Component, Injectable, provide} from 'angular2/core';
31-
import {NG_TRANSLATE_PROVIDERS, TranslateService, TranslatePipe,
31+
import {TRANSLATE_PROVIDERS, TranslateService, TranslatePipe,
3232
TranslateLoader, TranslateStaticLoader} from 'ng2-translate/ng2-translate';
3333
import {bootstrap} from 'angular2/platform/browser';
3434

3535
bootstrap(AppComponent, [
3636
HTTP_PROVIDERS,
37-
// not required if you use TranslateStaticLoader (default)
38-
// use this if you want to use another loader
39-
// or to configure TranslateStaticLoader
40-
provide(TranslateLoader, {
41-
useFactory: (http: Http) => new TranslateStaticLoader(http),
42-
deps: [Http]
43-
}),
44-
// recommended to have 1 unique instance of your service
45-
NG_TRANSLATE_PROVIDERS
37+
// not required, but recommended to have 1 unique instance of your service
38+
TRANSLATE_PROVIDERS
4639
]);
4740

4841
@Component({
@@ -110,10 +103,13 @@ translate.setTranslation('en', {
110103
```
111104

112105
#### Methods:
106+
- `useLoader(loader: TranslateLoader)`: Use a different loader
113107
- `setDefaultLang(lang: string)`: Sets the default language to use as a fallback
114108
- `use(lang: string): Observable<any>`: Changes the lang currently used
115109
- `getTranslation(lang: string): Observable<any>`: Gets an object of translations for a given language with the current loader
116110
- `setTranslation(lang: string, translations: Object)`: Manually sets an object of translations for a given language
111+
- `setMissingTranslationHandler(handler: MissingTranslationHandler): void`: sets the Missing Translation Handler which will be
112+
used when the requested translation is not available
117113
- `getLangs()`: Returns an array of currently available langs
118114
- `get(key: string|Array<string>, interpolateParams?: Object): Observable<string|Object>`: Gets the translated value of a key (or an array of keys)
119115
- `instant(key: string|Array<string>, interpolateParams?: Object): string|Object`: Gets the instant translated value of a key (or an array of keys)
@@ -141,8 +137,17 @@ bootstrap(AppComponent, [
141137
]);
142138
```
143139

140+
Or you can just use the `useLoader` method:
141+
```js
142+
export class AppComponent {
143+
constructor(translate: TranslateService, myLoader: CustomLoader) {
144+
translate.useLoader(myLoader);
145+
}
146+
}
147+
```
148+
144149
#### How to handle missing translations
145-
You can setup a provider for `MissingTranslationHandler` to define a handler that will be called when the requested translation is not available.
150+
You can setup a provider for `MissingTranslationHandler` in the bootstrap of your application (recommended), or you can use the method `setMissingTranslationHandler` later to define a handler that will be called when the requested translation is not available.
146151
The only required method is `handle` where you can do whatever you want. Just don't forget that it will be called synchronously from the `get` & `instant` methods.
147152

148153
##### Example:
@@ -157,11 +162,20 @@ export class MyMissingTranslationHandler implements MissingTranslationHandler {
157162
}
158163
```
159164

160-
Setup the Missing Translation Handler in bootstrap
165+
Setup the Missing Translation Handler in bootstrap (recommended)
161166
```js
162167
provide(MissingTranslationHandler, { useClass: MyMissingTranslationHandler })
163168
```
164169

170+
Set the Missing Translation Handler later
171+
```js
172+
constructor(translate: TranslateService) {
173+
...
174+
translate.setMissingTranslationHandler(new MyMissingTranslationHandler());
175+
...
176+
}
177+
```
178+
165179
### TranslatePipe
166180
You can call the TranslatePipe with some optional parameters that will be transpolated into the translation for the given key.
167181

ng2-translate.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@ export * from './src/translate.pipe';
77
export * from './src/translate.service';
88
export * from './src/translate.parser';
99

10-
export const NG_TRANSLATE_PROVIDERS: any = [
10+
export const TRANSLATE_PROVIDERS: any = [
1111
provide(TranslateLoader, {
1212
useFactory: (http: Http) => new TranslateStaticLoader(http),
1313
deps: [Http]
1414
}),
1515
TranslateService
16-
];
16+
];
17+
18+
// for angular-cli
19+
export default {
20+
pipes: [TranslatePipe],
21+
providers: [TranslateService]
22+
}

src/translate.service.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export abstract class TranslateLoader {
1616
}
1717

1818
export class TranslateStaticLoader implements TranslateLoader {
19-
2019
constructor(private http: Http, private prefix: string = 'i18n', private suffix: string = '.json') {
2120
}
2221

@@ -53,9 +52,21 @@ export class TranslateService {
5352
private langs: Array<string>;
5453
private parser: Parser = new Parser();
5554

56-
constructor(private http: Http,
57-
public currentLoader: TranslateLoader,
58-
@Optional() private missingTranslationHandler: MissingTranslationHandler) {}
55+
/**
56+
*
57+
* @param http The Angular 2 http provider
58+
* @param currentLoader An instance of the loader currently used
59+
* @param missingTranslationHandler A handler for missing translations
60+
*/
61+
constructor(private http: Http, public currentLoader: TranslateLoader, @Optional() private missingTranslationHandler: MissingTranslationHandler) {}
62+
63+
/**
64+
* Use a translations loader
65+
* @param loader
66+
*/
67+
public useLoader(loader: TranslateLoader) {
68+
this.currentLoader = loader;
69+
}
5970

6071
/**
6172
* Sets the default language to use as a fallback
@@ -220,4 +231,8 @@ export class TranslateService {
220231
this.onLangChange.emit({lang: lang, translations: this.translations[lang]});
221232
}
222233

234+
public setMissingTranslationHandler(handler: MissingTranslationHandler) {
235+
this.missingTranslationHandler = handler;
236+
}
237+
223238
}

tests/translate.pipe.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {TranslatePipe} from '../src/translate.pipe';
22
import {MockConnection, MockBackend} from "angular2/src/http/backends/mock_backend";
3-
import {NG_TRANSLATE_PROVIDERS, TranslateService} from "./../ng2-translate";
3+
import {TRANSLATE_PROVIDERS, TranslateService} from "./../ng2-translate";
44
import {XHRBackend, HTTP_PROVIDERS} from "angular2/http";
55
import {provide, Injector} from "angular2/core";
66

@@ -17,7 +17,7 @@ export function main() {
1717
HTTP_PROVIDERS,
1818
// Provide a mocked (fake) backend for Http
1919
provide(XHRBackend, {useClass: MockBackend}),
20-
NG_TRANSLATE_PROVIDERS,
20+
TRANSLATE_PROVIDERS,
2121
TranslatePipe
2222
]);
2323
backend = injector.get(XHRBackend);

tests/translate.service.spec.ts

+68-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
} from "angular2/http";
77
import {MockBackend, MockConnection} from "angular2/http/testing";
88
import {
9-
NG_TRANSLATE_PROVIDERS,
9+
TRANSLATE_PROVIDERS,
1010
TranslateService, MissingTranslationHandler, TranslateLoader,
1111
TranslateStaticLoader
1212
} from './../ng2-translate';
@@ -30,7 +30,7 @@ export function main() {
3030
HTTP_PROVIDERS,
3131
// Provide a mocked (fake) backend for Http
3232
provide(XHRBackend, {useClass: MockBackend}),
33-
NG_TRANSLATE_PROVIDERS
33+
TRANSLATE_PROVIDERS
3434
]);
3535
backend = injector.get(XHRBackend);
3636
translate = injector.get(TranslateService);
@@ -184,6 +184,69 @@ export function main() {
184184

185185
expect(translate.instant('TEST2')).toEqual('TEST2');
186186
});
187+
188+
function prepareMissingTranslationHandler() {
189+
class Missing implements MissingTranslationHandler {
190+
handle(key: string) {}
191+
}
192+
let handler = new Missing();
193+
spyOn(handler, 'handle');
194+
195+
translate.setMissingTranslationHandler(handler);
196+
197+
return handler;
198+
}
199+
200+
it('should use the MissingTranslationHandler when the key does not exist', () => {
201+
translate.use('en');
202+
let handler = prepareMissingTranslationHandler();
203+
204+
translate.get('nonExistingKey').subscribe(() => {
205+
expect(handler.handle).toHaveBeenCalledWith('nonExistingKey');
206+
});
207+
});
208+
209+
it('should not call the MissingTranslationHandler when the key exists', () => {
210+
translate.use('en');
211+
let handler = prepareMissingTranslationHandler();
212+
213+
translate.get('TEST').subscribe(() => {
214+
expect(handler.handle).not.toHaveBeenCalled();
215+
});
216+
});
217+
218+
it('should use the MissingTranslationHandler when the key does not exist & we use instant translation', () => {
219+
translate.use('en');
220+
let handler = prepareMissingTranslationHandler();
221+
222+
translate.instant('nonExistingKey');
223+
expect(handler.handle).toHaveBeenCalledWith('nonExistingKey');
224+
});
225+
226+
it('should be able to change the loader', () => {
227+
class CustomLoader implements TranslateLoader {
228+
getTranslation(lang: string): Observable<any> {
229+
return Observable.of({"TEST": "This is a test"});
230+
}
231+
}
232+
translate.use('en');
233+
234+
expect(translate).toBeDefined();
235+
expect(translate.currentLoader).toBeDefined();
236+
expect(translate.currentLoader instanceof TranslateStaticLoader).toBeTruthy();
237+
238+
translate.useLoader(new CustomLoader());
239+
expect(translate.currentLoader).toBeDefined();
240+
expect(translate.currentLoader instanceof CustomLoader).toBeTruthy();
241+
242+
// the lang to use, if the lang isn't available, it will use the current loader to get them
243+
translate.use('en');
244+
245+
// this will request the translation from the backend because we use a static files loader for TranslateService
246+
translate.get('TEST').subscribe((res: string) => {
247+
expect(res).toEqual('This is a test');
248+
});
249+
});
187250
});
188251

189252
describe('MissingTranslationHandler', () => {
@@ -202,7 +265,7 @@ export function main() {
202265
HTTP_PROVIDERS,
203266
// Provide a mocked (fake) backend for Http
204267
provide(XHRBackend, {useClass: MockBackend}),
205-
NG_TRANSLATE_PROVIDERS,
268+
TRANSLATE_PROVIDERS,
206269
provide(MissingTranslationHandler, { useClass: Missing })
207270
]);
208271
backend = injector.get(XHRBackend);
@@ -271,7 +334,7 @@ export function main() {
271334
HTTP_PROVIDERS,
272335
// Provide a mocked (fake) backend for Http
273336
provide(XHRBackend, {useClass: MockBackend}),
274-
NG_TRANSLATE_PROVIDERS
337+
TRANSLATE_PROVIDERS
275338
]);
276339
prepare(injector);
277340

@@ -301,7 +364,7 @@ export function main() {
301364
HTTP_PROVIDERS,
302365
// Provide a mocked (fake) backend for Http
303366
provide(XHRBackend, {useClass: MockBackend}),
304-
NG_TRANSLATE_PROVIDERS,
367+
TRANSLATE_PROVIDERS,
305368
provide(TranslateLoader, { useClass: CustomLoader })
306369
]);
307370
prepare(injector);

tsconfig.json

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
"files": [
1414
"typings/main.d.ts",
1515
"ng2-translate.ts",
16+
"./src/translate.pipe.ts",
17+
"./src/translate.service.ts",
18+
"./src/translate.parser.ts",
1619
"tests/translate.parser.spec.ts",
1720
"tests/translate.service.spec.ts",
1821
"tests/translate.pipe.spec.ts"

0 commit comments

Comments
 (0)