How to fallback on a global json if the module specific JSON hasn't the key
Hello,
I don't know if it's a feature request or a support request. In doubt I'm doing a support request.
Context
In each of my feature modules, I define a new provider to have a specific JSON translation file.
In these same modules, we're calling "common components", for example our implementation of dynamic form, which contains common form validation message translations.
These common components have their own JSON translation file, because we want to use the same translations across the app for these messages.
Issue
This doesn't work because since we define new JSON provider in our feature modules, it seems that the "common JSON" provider is overridden in the feature module scope, and it fails to translate i18n keys defined in the common module.
Seek for solution / support
Is it possible to make this work ? To coexist different JSON ? Maybe some kind of fallback or "JSON inheritance" ?
Our am I just doing it bad ?
Thank you
In my project to achieve this kind of functionality we had to create custom solution:
export const TRANSLATION_CONFIG: InjectionToken<TranslationConfiguration> =
new InjectionToken<TranslationConfiguration>('Translation Config Token');
@Injectable()
export class TranslationService {
private registeredModules: Set<string>;
constructor(private translate: TranslateService,
private http: HttpClient,
private cache: CacheService,
@Inject(TRANSLATION_CONFIG) private config: TranslationConfiguration) {
this.translate.addLangs(this.config.langs);
this.translate.setDefaultLang(this.config.defaultLang);
this.translate.use(this.config.defaultLang);
this.registeredModules = new Set<string>();
}
registerFor(modulePath: string): void {
this.getTranslations(this.translate.currentLang, modulePath)
.subscribe(translation => {
this.translate.setTranslation(this.translate.currentLang, translation, true);
this.registeredModules.add(modulePath);
});
}
changeLanguage(lang: string): Observable<string> {
return from(Array.from(this.registeredModules))
.pipe(
mergeMap(modulePath => this.getTranslations(lang, modulePath)),
tap(translation => {
this.translate.setTranslation(lang, translation, true);
this.translate.use(lang);
}),
mapTo(lang)
);
}
instant(key: string | Array<string>, interpolateParams?: object): string | any {
return this.translate.instant(key, interpolateParams);
}
private getTranslations(lang: string, modulePath: string): Observable<{ [key: string]: string }> {
const prefix: string = modulePath ? `${modulePath.split('/').pop()}-${lang}` : lang;
const {basePath = '', suffix} = this.config;
const url: string = `${basePath}${modulePath}/i18n/${prefix}${suffix}`;
return race(
this.cache.get<{ [key: string]: string }>(url),
this.http.get<{ [key: string]: string }>(url)
.pipe(
tap(translations => this.cache.set<{ [key: string]: string }>(url, translations))
)
);
}
}
Caching is optional and you can remove that part easily. It boils down to have custom loader and to register only one global module and respectively add more translations coming from your submodules. To make application aware of all of the respective modules we need to register them and keep a set (unique array) in memory, that will come in handy when switching the language. Can't promise that it's the best solution but it's working :)