@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
134 lines (133 loc) • 5.16 kB
JavaScript
import ConfigManager from '../configuration/configmanager';
import StateManager from '../state/statemanager';
import GirafeSingleton from '../../base/GirafeSingleton';
class I18nManager extends GirafeSingleton {
constructor(type) {
super(type);
Object.defineProperty(this, "translations", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
Object.defineProperty(this, "loadingLanguagePromise", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "configManager", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "stateManager", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.configManager = ConfigManager.getInstance();
this.stateManager = StateManager.getInstance();
this.stateManager.subscribe('language', () => this.handleLanguageChange());
this.handleLanguageChange();
}
formatNumber(number) {
return parseFloat(`${number}`).toLocaleString(this.configManager.Config.general.locale);
}
async loadTranslations(language) {
if (this.loadingLanguagePromise) {
// There's already a promise for loading translations
// => return it instead of starting another request
return this.loadingLanguagePromise;
}
if (language in this.translations) {
// Translation were already loaded.
// => stop here
return Promise.resolve(this.translations[language]);
}
// Load translations
this.loadingLanguagePromise = this.configManager.loadConfig().then(async () => {
if (this.configManager.Config?.languages.translations &&
language in this.configManager.Config.languages.translations) {
let mergedTranslations = {};
// Translations are loaded in the order defined in the list of files
// If an element is present in both results, the last value overwrite all the others
for (const url of this.configManager.Config.languages.translations[language]) {
const response = await fetch(url);
const content = await response.json();
mergedTranslations = { ...mergedTranslations, ...content[language] };
}
this.translations[language] = mergedTranslations;
this.loadingLanguagePromise = null;
return this.translations[language];
}
else {
throw new Error('No languages found in config.json');
}
});
return this.loadingLanguagePromise;
}
getTranslation(key) {
const currentLanguage = this.stateManager?.state?.language ?? 'en';
const translationDict = this.translations[currentLanguage];
const translation = translationDict ? translationDict[key] : null;
if (translation !== undefined && translation !== null) {
return translation;
}
return key;
}
async translate(dom) {
if (!this.stateManager.state?.language) {
return;
}
try {
await this.loadTranslations(this.stateManager.state.language);
}
catch (err) {
console.warn('Skipping translation due to config error:', err);
return;
}
const toTranslate = dom.querySelectorAll('[i18n]');
toTranslate.forEach((item) => {
const key = item.getAttribute('i18n');
let translation;
if (item.hasAttribute('i18nFn')) {
translation = this.getFnTranslated(item, key);
}
else {
translation = this.getTranslation(key);
}
if (item.hasAttribute('placeholder')) {
item.setAttribute('placeholder', translation);
}
else {
// Default : simply set innerHTML.
item.innerHTML = translation;
}
});
const tooltips = dom.querySelectorAll('[tip]');
tooltips.forEach((item) => {
const key = item.getAttribute('tip');
const translation = this.getTranslation(key);
item.setAttribute('title', translation);
});
}
handleLanguageChange() {
const newLanguage = this.stateManager.state.language;
if (!newLanguage) {
return;
}
const htmlLangElement = document.querySelector('html[lang]');
htmlLangElement?.setAttribute('lang', newLanguage);
}
getFnTranslated(item, key) {
const fnName = item.getAttribute('i18nFn');
if (fnName === 'formatNumber') {
return this.formatNumber(key);
}
return key;
}
}
export default I18nManager;