ng-dynamic-mf
Version:
[](https://www.npmjs.com/package/ng-dynamic-mf) [ • 11 kB
JavaScript
import * as i0 from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { resourceMapper } from 'ng-dynamic-mf';
import { BehaviorSubject, filter, combineLatest, startWith, map, distinctUntilChanged, firstValueFrom } from 'rxjs';
function isAssetResolver(resolver) {
return resolver.moduleName !== undefined;
}
/**
* This service is used to dynamically load translations from different modules.
* It uses @ngx-translate/core internally, so make sure you have it installed and
* the TranslateModule is imported correctly.
*/
class DynamicTranslationService {
constructor() {
this._translateService = inject(TranslateService);
this._translationsInvalidated = new BehaviorSubject(false);
this._translationsUpdated = new BehaviorSubject(undefined);
this._translationStore = {};
this._locale = this._translateService.currentLang || this._translateService.defaultLang || 'en';
this._debugMode = false;
this._translateService.onLangChange.subscribe(e => {
this._locale = e.lang;
this.invalidateTranslations();
});
this._translationsInvalidated.pipe(filter(x => x)).subscribe(() => {
this.loadTranslations();
});
}
/**
* Sets the debug mode. In debug mode, the translation values are replaced with their keys.
* @param debugMode Whether to enable debug mode
*/
setDebugMode(debugMode) {
this._debugMode = debugMode;
this.invalidateTranslations();
}
/**
* Registers a resolver for translations for a specific locale.
* @param translationSetIdentifier The identifier of the translations (e.g. the name of the module)
* @param locale The locale of the translations
* @param translationResolver The resolver for the translations
*/
registerTranslationSetForLocale(translationSetIdentifier, locale, translationResolver) {
this.registerSingleTranslation(translationSetIdentifier, locale, translationResolver);
this.invalidateTranslations();
}
/**
* Registers resolvers for translations for multiple locales.
* @param translationSetIdentifier The identifier of the translations (e.g. the name of the module)
* @param translationResolvers The resolvers for the translations
*/
registerTranslationSet(translationSetIdentifier, translationResolvers) {
this.doRegisterTranslationSet(translationSetIdentifier, translationResolvers);
this.invalidateTranslations();
}
/**
* Registers multiple translation resolvers for multiple locales.
* @param set The set of resolvers for the translations
*/
registerMultipleTranslationSets(set) {
Object.keys(set).forEach(translationSetKey => {
this.doRegisterTranslationSet(translationSetKey, set[translationSetKey]);
});
this.invalidateTranslations();
}
doRegisterTranslationSet(translationSetIdentifier, translationResolvers) {
Object.keys(translationResolvers).forEach(locale => {
this.registerSingleTranslation(translationSetIdentifier, locale, translationResolvers[locale]);
});
}
registerSingleTranslation(translationSetIdentifier, locale, translationResolver) {
const translationResolvers = this._translationStore[locale] || [];
const existing = translationResolvers.find(r => r.key === translationSetIdentifier);
if (existing) {
existing.resolver = translationResolver;
existing.loadedTranslations = null;
}
else {
translationResolvers.push({
resolver: translationResolver,
key: translationSetIdentifier,
loadedTranslations: null,
});
this._translationStore[locale] ??= translationResolvers;
}
}
/**
* Removes a translation set for a specific locale.
* @param translationSetIdentifier The identifier of the translations (e.g. the name of the module)
* @param locale The locale of the translations. If not specified, the translations will be removed for all locales.
*/
removeTranslation(translationSetIdentifier, locale) {
if (!locale) {
Object.keys(this._translationStore).forEach(l => {
this.doRemoveTranslation(translationSetIdentifier, l);
});
}
else {
this.doRemoveTranslation(translationSetIdentifier, locale);
}
this.invalidateTranslations();
}
/**
* Asynchronously checks if a translation set is loaded for the current locale.
* @param translationSetIdentifier The identifier of the translations (e.g. the name of the module)
* @returns Observable that emits true if the translation set is loaded, false otherwise
*/
isTranslationSetLoaded(translationSetIdentifier) {
return combineLatest([
this._translateService.onLangChange.pipe(startWith(undefined)),
this._translationsUpdated,
]).pipe(map(() => this.isTranslationSetLoadedSync(translationSetIdentifier)), distinctUntilChanged());
}
/**
* Resolves if a translation set is loaded for the current locale.
* @param translationSetIdentifier The identifier of the translations (e.g. the name of the module)
* @returns Promise that resolves if the translation set is loaded, never rejects (use isTranslationSetLoaded() to check if the translation set is loaded)
*/
async ensureTranslationSetIsLoaded(translationSetIdentifier) {
await firstValueFrom(this.isTranslationSetLoaded(translationSetIdentifier).pipe(filter(loaded => loaded)));
}
/**
* Checks if a translation set is loaded for the current locale.
* @param translationSetIdentifier The identifier of the translations (e.g. the name of the module)
* @returns true if the translation set is loaded, false otherwise
*/
isTranslationSetLoadedSync(translationSetIdentifier) {
if (!this._locale) {
return false;
}
const set = this._translationStore[this._locale];
if (!set) {
return false;
}
return set.some(r => r.key === translationSetIdentifier && !!r.loadedTranslations);
}
doRemoveTranslation(translationSetIdentifier, locale) {
const translationResolvers = this._translationStore[locale] ?? [];
const index = translationResolvers.findIndex(r => r.key === translationSetIdentifier);
if (index !== -1) {
translationResolvers.splice(index, 1);
}
}
invalidateTranslations() {
this._translationsInvalidated.next(true);
}
async loadTranslations() {
const locale = this._locale;
if (!locale) {
return;
}
const translationResolvers = this._translationStore[locale] ?? [];
await this.resolveTranslations(translationResolvers);
const loadedTranslations = this.mergeLoadedTranslations(translationResolvers);
this._translateService.setTranslation(locale, loadedTranslations, true);
if (this._locale) {
this._translateService.use(this._locale);
}
this._translationsUpdated.next();
}
async resolveTranslations(translationResolvers) {
const promises = translationResolvers.map(async (translationResolver) => {
if (translationResolver.loadedTranslations) {
return;
}
const loadedTranslations = await this.resolveTranslation(translationResolver.resolver);
translationResolver.loadedTranslations = loadedTranslations;
});
await Promise.all(promises);
}
mergeLoadedTranslations(translationResolvers) {
const translations = {};
translationResolvers.forEach(translationResolver => {
if (!translationResolver.loadedTranslations) {
return;
}
if (this._debugMode) {
const debugTranslations = this.putTranslationsIntoDebugMode(translationResolver.loadedTranslations);
Object.assign(translations, debugTranslations);
}
else {
Object.assign(translations, translationResolver.loadedTranslations);
}
});
return translations;
}
async resolveTranslation(translationResolver) {
let promiseResolver;
// check if translationResolver is a promise
if (translationResolver instanceof Promise) {
promiseResolver = translationResolver;
}
else if (typeof translationResolver === 'function') {
const fnResult = translationResolver();
if (fnResult instanceof Promise) {
promiseResolver = fnResult;
}
else {
promiseResolver = Promise.resolve(fnResult);
}
}
else if (isAssetResolver(translationResolver)) {
promiseResolver = this.loadLocaleFromAssetPath(translationResolver.moduleName, translationResolver.path);
}
else {
promiseResolver = Promise.resolve(translationResolver);
}
const loadedTranslations = await promiseResolver;
return loadedTranslations;
}
async loadLocaleFromAssetPath(moduleName, path) {
const resourceUrl = resourceMapper(moduleName, path);
const response = await fetch(resourceUrl);
const content = (await response.json());
return content;
}
putTranslationsIntoDebugMode(translations, prefix = '') {
const debugTranslations = {};
Object.keys(translations).forEach(key => {
const value = translations[key];
if (typeof value === 'string') {
debugTranslations[key] = `${prefix}${key}`;
}
else {
debugTranslations[key] = this.putTranslationsIntoDebugMode(value, `${prefix}${key}.`);
}
});
return debugTranslations;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicTranslationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicTranslationService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: DynamicTranslationService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [] });
/**
* Generated bundle index. Do not edit.
*/
export { DynamicTranslationService, isAssetResolver };
//# sourceMappingURL=ng-dynamic-mf-translate.mjs.map