UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

134 lines (133 loc) 5.16 kB
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;