-
-
Save tarlepp/9b6b645de74c3645f5e7640c687b77d5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from '@angular/core'; | |
import { Http, Response } from '@angular/http'; | |
import { Event, NavigationEnd, Router } from '@angular/router'; | |
import { TranslateService, LangChangeEvent } from '@ngx-translate/core'; | |
import { LocalStorageService } from 'ng2-webstorage'; | |
import { Observable } from 'rxjs/Observable'; | |
import { ConfigService } from '../../services/config.service'; | |
import { TranslationCacheService } from './translation-cache.service'; | |
import { LocaleInterface } from '../interfaces/'; | |
import { DomainCacheModel } from '../models/domain-cache.model'; | |
@Injectable() | |
export class TranslationService { | |
private translationUrl: string; | |
private language: string; | |
private domain: string; | |
private url: string; | |
private loadedDomains: Array<string> = []; | |
private loadedDomainsCommon: Array<string> = []; | |
private loadedDomainsCache: Object = {}; | |
private loadedDomainsCacheCommons: Object = {}; | |
/** | |
* Constructor of the class. | |
* | |
* @param {Http} http | |
* @param {Router} router | |
* @param {LocalStorageService} localStorage | |
* @param {TranslateService} translateService | |
* @param {TranslationCacheService} translationCacheService | |
* @param {ConfigService} configService | |
*/ | |
public constructor( | |
private http: Http, | |
private router: Router, | |
private localStorage: LocalStorageService, | |
private translateService: TranslateService, | |
private translationCacheService: TranslationCacheService, | |
private configService: ConfigService | |
) { | |
// Store current language | |
this.language = this.translateService.currentLang; | |
// Determine what url to use to fetch these translations; 1) local 2) remote | |
this.translationUrl = this.configService.get('USE_LOCAL_TRANSLATIONS') | |
? `./assets/i18n/` : `${this.configService.getApiUrl()}translation/`; | |
// On language changes we need to make sure that domain related texts are loaded | |
this.translateService | |
.onLangChange | |
.subscribe((event: LangChangeEvent) => { | |
if (event.lang !== this.language) { | |
const domains: Array<DomainCacheModel> = []; | |
this.language = event.lang; | |
this.localStorage.store('language', this.language); | |
// Domain in the common cache so load it | |
if (this.loadedDomainsCacheCommons.hasOwnProperty(this.url)) { | |
this.loadedDomainsCacheCommons[this.url] | |
.map(domain => { | |
domains.push({domain: domain, common: true}); | |
}); | |
} | |
// Domain in the cache so load it | |
if (this.loadedDomainsCache.hasOwnProperty(this.url)) { | |
this.loadedDomainsCache[this.url] | |
.map(domain => { | |
domains.push({domain: domain, common: false}); | |
}); | |
} | |
const observables = domains.map(data => this.fetchTranslations(data.domain, data.common)); | |
Observable | |
.forkJoin(observables) | |
.subscribe((results) => { | |
results | |
.filter(domainPart => domainPart) | |
.map((domainPart: string) => { | |
this.loadTranslations(this.language, domainPart); | |
}); | |
}); | |
} | |
}); | |
// Subscribe to router events, so we can store/reset some needed data | |
this.router | |
.events | |
.subscribe((event: Event) => { | |
if (event instanceof NavigationEnd) { | |
// Store loaded domains to cache | |
this.loadedDomainsCache[event.url] = this.loadedDomains; | |
this.loadedDomainsCacheCommons[event.url] = this.loadedDomainsCommon; | |
// Reset cache | |
this.loadedDomains = []; | |
this.loadedDomainsCommon = []; | |
// Store current url | |
this.url = event.url; | |
} | |
}); | |
} | |
/** | |
* Method to fetch supported locales. | |
* | |
* @returns {Observable<Array<LocaleInterface>>} | |
*/ | |
public getLocales(): Observable<Array<LocaleInterface>> { | |
return this.http | |
.get(`${this.translationUrl}locales.json`) | |
.map((res: Response) => res.json()); | |
} | |
/** | |
* Method to load translations for given domain. Note that this will split given domain to parts and try to fetch | |
* translations to each of them. Eg. if domain is /Foo/Bar/FooBar then this will try to load following translations: | |
* - /Foo/en.json | |
* - /Foo/Bar/en.json | |
* - /Foo/Bar/FooBar/en.json | |
* | |
* And those texts are merged to final result in that order - So that you can easily override some texts within your | |
* domain and still have some "default" translation for that same. | |
* | |
* @param {string} domain | |
* @param {boolean} common | |
* @returns {Observable<Array<string>>} | |
*/ | |
public loadTranslationsForDomain(domain: string, common: boolean): Observable<Array<string>> { | |
const parts: Array<string> = []; | |
const observables = domain.split('/').map((part: string) => { | |
parts.push(part); | |
return this.fetchTranslations([...parts].join('/'), common); | |
}); | |
return new Observable<Array<string>>(observer => { | |
Observable | |
.forkJoin(observables) | |
.subscribe((results) => { | |
observer.next(results | |
.filter(domainPart => domainPart) | |
.map((domainPart: string) => { | |
this.loadTranslations(this.language, domainPart); | |
return domainPart; | |
}) | |
); | |
observer.complete(); | |
}); | |
}); | |
} | |
/** | |
* Method to fetch domain translations from cache or specified translation url. | |
* | |
* @param {string} domain | |
* @param {boolean} common | |
* @returns {Observable<string|boolean>} | |
*/ | |
private fetchTranslations(domain: string, common: boolean): Observable<string|boolean> { | |
// Store current domain to cache | |
common ? this.loadedDomainsCommon.push(domain) : this.loadedDomains.push(domain); | |
return this.translationCacheService.cached(this.language, domain, common) | |
? Observable.of(domain) | |
: this.fetchTranslationsForDomain(domain, this.language, common); | |
} | |
/** | |
* Method to fetch domain + language specified translations from specified location: | |
* 1) Local | |
* 2) Remote | |
* | |
* And if/when an error happens when fetching those translations, just silently ignore those - there aren't errors | |
* in all of the cases - and really these don't prevent to use application. | |
* | |
* Note that in each case we store language + domain data to cache - so that we won't trigger fetching multiple times | |
* when user navigates in application. | |
* | |
* @param {string} language | |
* @param {string} domain | |
* @param {boolean} common | |
* @returns {Observable<string|boolean>} | |
*/ | |
private fetchTranslationsForDomain(domain: string, language: string, common: boolean): Observable<string|boolean> { | |
return this.http | |
.get(`${this.translationUrl}${domain}/${language}.json`) | |
.map((res: Response) => { // Aah, happy path - so happy now | |
const translations = res.json(); | |
// Store translations to cache | |
this.translationCacheService.store(language, domain, translations, common); | |
return domain; | |
}) | |
.catch((error: any) => { // And in any error we just want to resolve true and log possible errors... | |
console.warn(`Translation not found for domain '${domain}'...`, error); | |
// Store translations to cache | |
this.translationCacheService.store(language, domain, {}, common); | |
return Observable.of(false); | |
}); | |
} | |
/** | |
* Method to load translations from cache and set those to translate service. | |
* | |
* @param {string} language | |
* @param {string} domain | |
*/ | |
private loadTranslations(language: string, domain: string) { | |
this.translateService.setTranslation(language, this.translationCacheService.get(language, domain), true); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment