diff --git a/index.ts b/index.ts index e04f2183..fb1c117e 100644 --- a/index.ts +++ b/index.ts @@ -1,13 +1,25 @@ -import {NgModule, ModuleWithProviders} from "@angular/core"; -import {TranslatePipe} from "./src/translate.pipe"; -import {TranslateParser, DefaultTranslateParser} from "./src/translate.parser"; -import {TranslateService, TranslateLoader} from "./src/translate.service"; +import {NgModule, ModuleWithProviders, Provider} from "@angular/core"; +import {TranslateStore} from "./src/translate.store"; +import {TranslateLoader, TranslateFakeLoader} from "./src/translate.loader"; +import {TranslateService} from "./src/translate.service"; +import {MissingTranslationHandler, FakeMissingTranslationHandler} from "./src/missing-translation-handler"; +import {TranslateParser, TranslateDefaultParser} from "./src/translate.parser"; import {TranslateDirective} from "./src/translate.directive"; +import {TranslatePipe} from "./src/translate.pipe"; -export * from "./src/translate.pipe"; +export * from "./src/translate.store"; +export * from "./src/translate.loader"; export * from "./src/translate.service"; +export * from "./src/missing-translation-handler"; export * from "./src/translate.parser"; export * from "./src/translate.directive"; +export * from "./src/translate.pipe"; + +export interface TranslateModuleConfig { + loader?: Provider; + parser?: Provider; + missingTranslationHandler?: Provider; +} @NgModule({ declarations: [ @@ -20,16 +32,36 @@ export * from "./src/translate.directive"; ] }) export class TranslateModule { - static forRoot(providedLoader: any = { - provide: TranslateLoader, - useFactory: () => {} - }): ModuleWithProviders { + /** + * Use this method in your root module to provide the TranslateService + * @param {TranslateModuleConfig} config + * @returns {ModuleWithProviders} + */ + static forRoot(config: TranslateModuleConfig = {}): ModuleWithProviders { + let baseConfig = this.forChild(config); + Array.prototype.push.call(baseConfig.providers, TranslateStore); + return baseConfig; + } + + /** + * Use this method in your other (non root) modules to import the directive/pipe + * @param {TranslateModuleConfig} config + * @returns {ModuleWithProviders} + */ + static forChild(config: TranslateModuleConfig = {}): ModuleWithProviders { + let optionalProviders: Provider[] = []; + if(config.missingTranslationHandler) { + Array.prototype.push.call(optionalProviders, config.missingTranslationHandler); + } + return { ngModule: TranslateModule, providers: [ - providedLoader, - TranslateService, - { provide: TranslateParser, useClass: DefaultTranslateParser } + config.loader || {provide: TranslateLoader, useClass: TranslateFakeLoader}, + config.parser || {provide: TranslateParser, useClass: TranslateDefaultParser}, + config.missingTranslationHandler || {provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler}, + ...optionalProviders, + TranslateService ] }; } diff --git a/src/missing-translation-handler.ts b/src/missing-translation-handler.ts new file mode 100644 index 00000000..81b6b92e --- /dev/null +++ b/src/missing-translation-handler.ts @@ -0,0 +1,49 @@ +import {TranslateService} from "./translate.service"; +import {Injectable} from "@angular/core"; + +export interface MissingTranslationHandlerParams { + /** + * the key that's missing in translation files + * + * @type {string} + */ + key: string; + + /** + * an instance of the service that was unable to translate the key. + * + * @type {TranslateService} + */ + translateService: TranslateService; + + /** + * interpolation params that were passed along for translating the given key. + * + * @type {Object} + */ + interpolateParams?: Object; +} + +export abstract class MissingTranslationHandler { + /** + * A function that handles missing translations. + * + * @abstract + * @param {MissingTranslationHandlerParams} params context for resolving a missing translation + * @returns {any} a value or an observable + * If it returns a value, then this value is used. + * If it return an observable, the value returned by this observable will be used (except if the method was "instant"). + * If it doesn't return then the key will be used as a value + */ + abstract handle(params: MissingTranslationHandlerParams): any; +} + +/** + * This handler is just a placeholder that does nothing, in case you don't need a missing translation handler at all + */ +@Injectable() +export class FakeMissingTranslationHandler implements MissingTranslationHandler { + handle(params: MissingTranslationHandlerParams): string { + return params.key; + } +} diff --git a/src/translate.loader.ts b/src/translate.loader.ts new file mode 100644 index 00000000..89c70fde --- /dev/null +++ b/src/translate.loader.ts @@ -0,0 +1,16 @@ +import {Observable} from "rxjs/Observable"; +import {Injectable} from "@angular/core"; + +export abstract class TranslateLoader { + abstract getTranslation(lang: string): Observable; +} + +/** + * This loader is just a placeholder that does nothing, in case you don't need a loader at all + */ +@Injectable() +export class TranslateFakeLoader extends TranslateLoader { + getTranslation(lang: string): Observable { + return Observable.of({}); + } +} diff --git a/src/translate.parser.ts b/src/translate.parser.ts index 99e7e7fd..373da2a2 100644 --- a/src/translate.parser.ts +++ b/src/translate.parser.ts @@ -22,7 +22,7 @@ export abstract class TranslateParser { } @Injectable() -export class DefaultTranslateParser extends TranslateParser { +export class TranslateDefaultParser extends TranslateParser { templateMatcher: RegExp = /{{\s?([^{}\s]*)\s?}}/g; public interpolate(expr: string, params?: any): string { diff --git a/src/translate.service.ts b/src/translate.service.ts index c5767bfe..336da32b 100644 --- a/src/translate.service.ts +++ b/src/translate.service.ts @@ -1,4 +1,4 @@ -import {Injectable, EventEmitter, Optional} from "@angular/core"; +import {Injectable, EventEmitter} from "@angular/core"; import {Observable} from "rxjs/Observable"; import {Observer} from "rxjs/Observer"; import "rxjs/add/observable/of"; @@ -8,6 +8,8 @@ import "rxjs/add/operator/merge"; import "rxjs/add/operator/toArray"; import "rxjs/add/operator/take"; +import {TranslateLoader} from "./translate.loader"; +import {MissingTranslationHandler, MissingTranslationHandlerParams} from "./missing-translation-handler"; import {TranslateParser} from "./translate.parser"; import {isDefined} from "./util"; @@ -26,52 +28,11 @@ export interface DefaultLangChangeEvent { translations: any; } -export interface MissingTranslationHandlerParams { - /** - * the key that's missing in translation files - * - * @type {string} - */ - key: string; - - /** - * an instance of the service that was unable to translate the key. - * - * @type {TranslateService} - */ - translateService: TranslateService; - - /** - * interpolation params that were passed along for translating the given key. - * - * @type {Object} - */ - interpolateParams?: Object; -} - declare interface Window { navigator: any; } declare const window: Window; -export abstract class MissingTranslationHandler { - /** - * A function that handles missing translations. - * - * @abstract - * @param {MissingTranslationHandlerParams} params context for resolving a missing translation - * @returns {any} a value or an observable - * If it returns a value, then this value is used. - * If it return an observable, the value returned by this observable will be used (except if the method was "instant"). - * If it doesn't return then the key will be used as a value - */ - abstract handle(params: MissingTranslationHandlerParams): any; -} - -export abstract class TranslateLoader { - abstract getTranslation(lang: string): Observable; -} - @Injectable() export class TranslateService { /** @@ -120,7 +81,7 @@ export class TranslateService { */ constructor(public currentLoader: TranslateLoader, public parser: TranslateParser, - @Optional() private missingTranslationHandler: MissingTranslationHandler) { + public missingTranslationHandler: MissingTranslationHandler) { } /** @@ -312,7 +273,7 @@ export class TranslateService { res = this.parser.interpolate(this.parser.getValue(this.translations[this.defaultLang], key), interpolateParams); } - if(!res && this.missingTranslationHandler) { + if(typeof res === "undefined") { let params: MissingTranslationHandlerParams = {key, translateService: this}; if(typeof interpolateParams !== 'undefined') { params.interpolateParams = interpolateParams; diff --git a/src/translate.store.ts b/src/translate.store.ts new file mode 100644 index 00000000..69b8fe7f --- /dev/null +++ b/src/translate.store.ts @@ -0,0 +1,3 @@ +export class TranslateStore { + +} diff --git a/tests/missing-translation-handler.spec.ts b/tests/missing-translation-handler.spec.ts new file mode 100644 index 00000000..5e50ab29 --- /dev/null +++ b/tests/missing-translation-handler.spec.ts @@ -0,0 +1,185 @@ +import {Injector} from "@angular/core"; +import {TestBed, getTestBed} from "@angular/core/testing"; +import {Observable} from "rxjs/Observable"; +import { + TranslateService, + MissingTranslationHandler, + MissingTranslationHandlerParams, + TranslateModule, + TranslateLoader +} from "../index"; + +let translations: any = {"TEST": "This is a test"}; +class FakeLoader implements TranslateLoader { + getTranslation(lang: string): Observable { + return Observable.of(translations); + } +} + +describe('MissingTranslationHandler', () => { + let injector: Injector; + let translate: TranslateService; + let missingTranslationHandler: MissingTranslationHandler; + + class Missing implements MissingTranslationHandler { + handle(params: MissingTranslationHandlerParams) { + return "handled"; + } + } + + class MissingObs implements MissingTranslationHandler { + handle(params: MissingTranslationHandlerParams): Observable { + return Observable.of(`handled: ${params.key}`); + } + } + + let prepare = ((handlerClass: Function) => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: FakeLoader} + }) + ], + providers: [ + {provide: MissingTranslationHandler, useClass: handlerClass} + ] + }); + injector = getTestBed(); + translate = injector.get(TranslateService); + missingTranslationHandler = injector.get(MissingTranslationHandler); + }); + + afterEach(() => { + injector = undefined; + translate = undefined; + translations = {"TEST": "This is a test"}; + missingTranslationHandler = undefined; + }); + + it('should use the MissingTranslationHandler when the key does not exist', () => { + prepare(Missing); + translate.use('en'); + spyOn(missingTranslationHandler, 'handle').and.callThrough(); + + translate.get('nonExistingKey').subscribe((res: string) => { + expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({key: 'nonExistingKey'})); + //test that the instance of the last called argument is string + expect(res).toEqual('handled'); + }); + }); + + it('should propagate interpolation params when the key does not exist', () => { + prepare(Missing); + translate.use('en'); + spyOn(missingTranslationHandler, 'handle').and.callThrough(); + let interpolateParams = {some: 'params'}; + + translate.get('nonExistingKey', interpolateParams).subscribe((res: string) => { + expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({interpolateParams: interpolateParams})); + //test that the instance of the last called argument is string + expect(res).toEqual('handled'); + }); + }); + + it('should propagate TranslationService params when the key does not exist', () => { + prepare(Missing); + translate.use('en'); + spyOn(missingTranslationHandler, 'handle').and.callThrough(); + let interpolateParams = {some: 'params'}; + + translate.get('nonExistingKey', interpolateParams).subscribe((res: string) => { + expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({translateService: translate})); + //test that the instance of the last called argument is string + expect(res).toEqual('handled'); + }); + }); + + it('should return the key when using MissingTranslationHandler & the handler returns nothing', () => { + class MissingUndef implements MissingTranslationHandler { + handle(params: MissingTranslationHandlerParams) { + } + } + + prepare(MissingUndef); + translate.use('en'); + spyOn(missingTranslationHandler, 'handle').and.callThrough(); + + translate.get('nonExistingKey').subscribe((res: string) => { + expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({key: 'nonExistingKey'})); + expect(res).toEqual('nonExistingKey'); + }); + }); + + it('should not call the MissingTranslationHandler when the key exists', () => { + prepare(Missing); + translate.use('en'); + spyOn(missingTranslationHandler, 'handle').and.callThrough(); + + translate.get('TEST').subscribe(() => { + expect(missingTranslationHandler.handle).not.toHaveBeenCalled(); + }); + }); + + it('should use the MissingTranslationHandler when the key does not exist & we use instant translation', () => { + prepare(Missing); + translate.use('en'); + spyOn(missingTranslationHandler, 'handle').and.callThrough(); + + expect(translate.instant('nonExistingKey')).toEqual('handled'); + expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({key: 'nonExistingKey'})); + }); + + it('should wait for the MissingTranslationHandler when it returns an observable & we use get', () => { + prepare(MissingObs); + translate.use('en'); + spyOn(missingTranslationHandler, 'handle').and.callThrough(); + + translate.get('nonExistingKey').subscribe((res: string) => { + expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({key: 'nonExistingKey'})); + expect(res).toEqual('handled: nonExistingKey'); + }); + }); + + it('should wait for the MissingTranslationHandler when it returns an observable & we use get with an array', () => { + let tr = { + nonExistingKey1: 'handled: nonExistingKey1', + nonExistingKey2: 'handled: nonExistingKey2', + nonExistingKey3: 'handled: nonExistingKey3' + }; + + prepare(MissingObs); + translate.use('en'); + spyOn(missingTranslationHandler, 'handle').and.callThrough(); + + translate.get(Object.keys(tr)).subscribe((res: string) => { + expect(missingTranslationHandler.handle).toHaveBeenCalledTimes(3); + expect(res).toEqual(tr); + }); + }); + + it('should not wait for the MissingTranslationHandler when it returns an observable & we use instant', () => { + prepare(MissingObs); + translate.use('en'); + spyOn(missingTranslationHandler, 'handle').and.callThrough(); + + expect(translate.instant('nonExistingKey')).toEqual('nonExistingKey'); + }); + + it('should not wait for the MissingTranslationHandler when it returns an observable & we use instant with an array', () => { + let tr = { + nonExistingKey1: 'handled: nonExistingKey1', + nonExistingKey2: 'handled: nonExistingKey2', + nonExistingKey3: 'handled: nonExistingKey3' + }; + + prepare(MissingObs); + translate.use('en'); + spyOn(missingTranslationHandler, 'handle').and.callThrough(); + + expect(translate.instant(Object.keys(tr))).toEqual({ + nonExistingKey1: 'nonExistingKey1', + nonExistingKey2: 'nonExistingKey2', + nonExistingKey3: 'nonExistingKey3' + }); + }); +}); diff --git a/tests/translate.directive.spec.ts b/tests/translate.directive.spec.ts index 54c91b3d..87b10f0e 100644 --- a/tests/translate.directive.spec.ts +++ b/tests/translate.directive.spec.ts @@ -1,8 +1,7 @@ import {Component, ViewChild, ElementRef} from '@angular/core'; import {TestBed, ComponentFixture, getTestBed} from '@angular/core/testing'; import {Injector, Injectable, ChangeDetectionStrategy, ViewContainerRef} from '@angular/core'; -import {TranslateService} from '../src/translate.service'; -import {TranslateModule} from '../index'; +import {TranslateService, TranslateModule} from '../index'; @Injectable() @Component({ @@ -36,7 +35,9 @@ describe('TranslateDirective', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot()], + imports: [ + TranslateModule.forRoot() + ], declarations: [App] }); injector = getTestBed(); diff --git a/tests/translate.loader.spec.ts b/tests/translate.loader.spec.ts new file mode 100644 index 00000000..3c21797a --- /dev/null +++ b/tests/translate.loader.spec.ts @@ -0,0 +1,74 @@ +import {Injector} from "@angular/core"; +import {TestBed, getTestBed} from "@angular/core/testing"; +import {TranslateService, TranslateModule, TranslateLoader} from "../index"; +import {Observable} from "rxjs/Observable"; + +let translations: any = {"TEST": "This is a test"}; +class FakeLoader implements TranslateLoader { + getTranslation(lang: string): Observable { + return Observable.of(translations); + } +} + +describe('TranslateLoader', () => { + let injector: Injector; + let translate: TranslateService; + + let prepare = (_injector: Injector) => { + translate = _injector.get(TranslateService); + }; + + it('should be able to provide TranslateStaticLoader', () => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: FakeLoader} + }) + ], + }); + injector = getTestBed(); + prepare(injector); + + expect(translate).toBeDefined(); + expect(translate.currentLoader).toBeDefined(); + expect(translate.currentLoader instanceof FakeLoader).toBeTruthy(); + + // the lang to use, if the lang isn't available, it will use the current loader to get them + translate.use('en'); + + // this will request the translation from the backend because we use a static files loader for TranslateService + translate.get('TEST').subscribe((res: string) => { + expect(res).toEqual('This is a test'); + }); + }); + + it('should be able to provide any TranslateLoader', () => { + class CustomLoader implements TranslateLoader { + getTranslation(lang: string): Observable { + return Observable.of({"TEST": "This is also a test"}); + } + } + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: CustomLoader} + }) + ] + }); + injector = getTestBed(); + prepare(injector); + + expect(translate).toBeDefined(); + expect(translate.currentLoader).toBeDefined(); + expect(translate.currentLoader instanceof CustomLoader).toBeTruthy(); + + // the lang to use, if the lang isn't available, it will use the current loader to get them + translate.use('en'); + + // this will request the translation from the CustomLoader + translate.get('TEST').subscribe((res: string) => { + expect(res).toEqual('This is also a test'); + }); + }); + +}); diff --git a/tests/translate.parser.spec.ts b/tests/translate.parser.spec.ts index 23f239a2..1f98f233 100644 --- a/tests/translate.parser.spec.ts +++ b/tests/translate.parser.spec.ts @@ -1,10 +1,10 @@ -import {TranslateParser, DefaultTranslateParser} from '../src/translate.parser'; +import {TranslateParser, TranslateDefaultParser} from '../index'; describe('Parser', () => { let parser: TranslateParser; beforeEach(() => { - parser = new DefaultTranslateParser(); + parser = new TranslateDefaultParser(); }); it('is defined', () => { diff --git a/tests/translate.pipe.spec.ts b/tests/translate.pipe.spec.ts index bfd11617..2c09e2b0 100644 --- a/tests/translate.pipe.spec.ts +++ b/tests/translate.pipe.spec.ts @@ -1,10 +1,8 @@ import {TranslatePipe} from '../src/translate.pipe'; -import {TranslateService, TranslateModule} from "../index"; +import {TranslateService, TranslateModule, TranslateLoader, LangChangeEvent, DefaultLangChangeEvent} from "../index"; import {Component, Injector, ChangeDetectorRef, ChangeDetectionStrategy, Injectable, ViewContainerRef} from "@angular/core"; -import {LangChangeEvent, DefaultLangChangeEvent} from "../src/translate.service"; import {getTestBed, TestBed} from "@angular/core/testing"; -import {Observable} from "rxjs"; -import {TranslateLoader} from "../src/translate.service"; +import {Observable} from "rxjs/Observable"; class FakeChangeDetectorRef extends ChangeDetectorRef { markForCheck(): void {} @@ -47,7 +45,11 @@ describe('TranslatePipe', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({provide: TranslateLoader, useClass: FakeLoader})], + imports: [ + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: FakeLoader} + }) + ], declarations: [App] }); injector = getTestBed(); diff --git a/tests/translate.service.spec.ts b/tests/translate.service.spec.ts index e1836eff..885cde21 100644 --- a/tests/translate.service.spec.ts +++ b/tests/translate.service.spec.ts @@ -1,12 +1,5 @@ import {Injector} from "@angular/core"; -import { - TranslateService, - MissingTranslationHandler, - MissingTranslationHandlerParams, - TranslateLoader, - LangChangeEvent, - TranslationChangeEvent, TranslateModule -} from '../index'; +import {TranslateService, TranslateLoader, LangChangeEvent, TranslationChangeEvent, TranslateModule} from '../index'; import {Observable} from "rxjs/Observable"; import {getTestBed, TestBed} from "@angular/core/testing"; @@ -23,7 +16,11 @@ describe('TranslateService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({provide: TranslateLoader, useClass: FakeLoader})] + imports: [ + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: FakeLoader} + }) + ] }); injector = getTestBed(); translate = injector.get(TranslateService); @@ -264,7 +261,7 @@ describe('TranslateService', () => { translate.get('TEST').subscribe((res: string) => { expect(res).toEqual('TEST'); - translations = {"TEST": "This is a test 2"}; + translations = {"TEST": "This is a test 2"}; // reset the lang as if it was never initiated translate.reloadLang('en').subscribe((res2: string) => { @@ -300,222 +297,3 @@ describe('TranslateService', () => { expect(typeof browserCultureLand === 'string').toBeTruthy(); }); }); - -describe('MissingTranslationHandler', () => { - let injector: Injector; - let translate: TranslateService; - let missingTranslationHandler: MissingTranslationHandler; - - class Missing implements MissingTranslationHandler { - handle(params: MissingTranslationHandlerParams) { - return "handled"; - } - } - - class MissingObs implements MissingTranslationHandler { - handle(params: MissingTranslationHandlerParams): Observable { - return Observable.of(`handled: ${params.key}`); - } - } - - let prepare = ((handlerClass: Function) => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({provide: TranslateLoader, useClass: FakeLoader})], - providers: [ - { provide: MissingTranslationHandler, useClass: handlerClass } - ] - }); - injector = getTestBed(); - translate = injector.get(TranslateService); - missingTranslationHandler = injector.get(MissingTranslationHandler); - }); - - afterEach(() => { - injector = undefined; - translate = undefined; - translations = {"TEST": "This is a test"}; - missingTranslationHandler = undefined; - }); - - it('should use the MissingTranslationHandler when the key does not exist', () => { - prepare(Missing); - translate.use('en'); - spyOn(missingTranslationHandler, 'handle').and.callThrough(); - - translate.get('nonExistingKey').subscribe((res: string) => { - expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({ key: 'nonExistingKey' })); - //test that the instance of the last called argument is string - expect(res).toEqual('handled'); - }); - }); - - it('should propagate interpolation params when the key does not exist', () => { - prepare(Missing); - translate.use('en'); - spyOn(missingTranslationHandler, 'handle').and.callThrough(); - let interpolateParams = { some: 'params' }; - - translate.get('nonExistingKey', interpolateParams).subscribe((res: string) => { - expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({ interpolateParams: interpolateParams })); - //test that the instance of the last called argument is string - expect(res).toEqual('handled'); - }); - }); - - it('should propagate TranslationService params when the key does not exist', () => { - prepare(Missing); - translate.use('en'); - spyOn(missingTranslationHandler, 'handle').and.callThrough(); - let interpolateParams = { some: 'params' }; - - translate.get('nonExistingKey', interpolateParams).subscribe((res: string) => { - expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({ translateService: translate })); - //test that the instance of the last called argument is string - expect(res).toEqual('handled'); - }); - }); - - it('should return the key when using MissingTranslationHandler & the handler returns nothing', () => { - class MissingUndef implements MissingTranslationHandler { - handle(params: MissingTranslationHandlerParams) { - } - } - - prepare(MissingUndef); - translate.use('en'); - spyOn(missingTranslationHandler, 'handle').and.callThrough(); - - translate.get('nonExistingKey').subscribe((res: string) => { - expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({ key: 'nonExistingKey' })); - expect(res).toEqual('nonExistingKey'); - }); - }); - - it('should not call the MissingTranslationHandler when the key exists', () => { - prepare(Missing); - translate.use('en'); - spyOn(missingTranslationHandler, 'handle').and.callThrough(); - - translate.get('TEST').subscribe(() => { - expect(missingTranslationHandler.handle).not.toHaveBeenCalled(); - }); - }); - - it('should use the MissingTranslationHandler when the key does not exist & we use instant translation', () => { - prepare(Missing); - translate.use('en'); - spyOn(missingTranslationHandler, 'handle').and.callThrough(); - - expect(translate.instant('nonExistingKey')).toEqual('handled'); - expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({ key: 'nonExistingKey' })); - }); - - it('should wait for the MissingTranslationHandler when it returns an observable & we use get', () => { - prepare(MissingObs); - translate.use('en'); - spyOn(missingTranslationHandler, 'handle').and.callThrough(); - - translate.get('nonExistingKey').subscribe((res: string) => { - expect(missingTranslationHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining({ key: 'nonExistingKey' })); - expect(res).toEqual('handled: nonExistingKey'); - }); - }); - - it('should wait for the MissingTranslationHandler when it returns an observable & we use get with an array', () => { - let tr = { - nonExistingKey1: 'handled: nonExistingKey1', - nonExistingKey2: 'handled: nonExistingKey2', - nonExistingKey3: 'handled: nonExistingKey3' - }; - - prepare(MissingObs); - translate.use('en'); - spyOn(missingTranslationHandler, 'handle').and.callThrough(); - - translate.get(Object.keys(tr)).subscribe((res: string) => { - expect(missingTranslationHandler.handle).toHaveBeenCalledTimes(3); - expect(res).toEqual(tr); - }); - }); - - it('should not wait for the MissingTranslationHandler when it returns an observable & we use instant', () => { - prepare(MissingObs); - translate.use('en'); - spyOn(missingTranslationHandler, 'handle').and.callThrough(); - - expect(translate.instant('nonExistingKey')).toEqual('nonExistingKey'); - }); - - it('should not wait for the MissingTranslationHandler when it returns an observable & we use instant with an array', () => { - let tr = { - nonExistingKey1: 'handled: nonExistingKey1', - nonExistingKey2: 'handled: nonExistingKey2', - nonExistingKey3: 'handled: nonExistingKey3' - }; - - prepare(MissingObs); - translate.use('en'); - spyOn(missingTranslationHandler, 'handle').and.callThrough(); - - expect(translate.instant(Object.keys(tr))).toEqual({ - nonExistingKey1: 'nonExistingKey1', - nonExistingKey2: 'nonExistingKey2', - nonExistingKey3: 'nonExistingKey3' - }); - }); -}); - -describe('TranslateLoader', () => { - let injector: Injector; - let translate: TranslateService; - - let prepare = (_injector: Injector) => { - translate = _injector.get(TranslateService); - }; - - it('should be able to provide TranslateStaticLoader', () => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({provide: TranslateLoader, useClass: FakeLoader})], - }); - injector = getTestBed(); - prepare(injector); - - expect(translate).toBeDefined(); - expect(translate.currentLoader).toBeDefined(); - expect(translate.currentLoader instanceof FakeLoader).toBeTruthy(); - - // the lang to use, if the lang isn't available, it will use the current loader to get them - translate.use('en'); - - // this will request the translation from the backend because we use a static files loader for TranslateService - translate.get('TEST').subscribe((res: string) => { - expect(res).toEqual('This is a test'); - }); - }); - - it('should be able to provide any TranslateLoader', () => { - class CustomLoader implements TranslateLoader { - getTranslation(lang: string): Observable { - return Observable.of({"TEST": "This is also a test"}); - } - } - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({provide: TranslateLoader, useClass: CustomLoader})] - }); - injector = getTestBed(); - prepare(injector); - - expect(translate).toBeDefined(); - expect(translate.currentLoader).toBeDefined(); - expect(translate.currentLoader instanceof CustomLoader).toBeTruthy(); - - // the lang to use, if the lang isn't available, it will use the current loader to get them - translate.use('en'); - - // this will request the translation from the CustomLoader - translate.get('TEST').subscribe((res: string) => { - expect(res).toEqual('This is also a test'); - }); - }); - -}); diff --git a/tests/translate.store.spec.ts b/tests/translate.store.spec.ts new file mode 100644 index 00000000..e69de29b