import mustache from '../skin/mustache.min.js?KIWIXCACHEID' const Translations = { defaultLanguage: null, currentLanguage: null, promises: {}, data: {}, load: function(lang, asDefault=false) { if ( asDefault ) { this.defaultLanguage = lang; this.loadTranslationsJSON(lang); } else { this.currentLanguage = lang; if ( lang != this.defaultLanguage ) { this.loadTranslationsJSON(lang); } } }, loadTranslationsJSON: function(lang) { if ( this.promises[lang] ) return; const errorMsg = `Error loading translations for language '${lang}': `; this.promises[lang] = fetch(`./skin/i18n/${lang}.json`).then(async (resp) => { if ( resp.ok ) { this.data[lang] = JSON.parse(await resp.text()); } else { console.log(errorMsg + resp.statusText); } }).catch((err) => { console.log(errorMsg + err); }); }, whenReady: function(callback) { const defaultLangPromise = this.promises[this.defaultLanguage]; const currentLangPromise = this.promises[this.currentLanguage]; Promise.all([defaultLangPromise, currentLangPromise]).then(callback); }, get: function(msgId) { const activeTranslation = this.data[this.currentLanguage]; const r = activeTranslation && activeTranslation[msgId]; if ( r ) return r; const defaultMsgs = this.data[this.defaultLanguage]; if ( defaultMsgs ) return defaultMsgs[msgId]; throw "Translations are not loaded"; } } function $t(msgId, params={}) { try { const msgTemplate = Translations.get(msgId); if ( ! msgTemplate ) { return "Invalid message id: " + msgId; } return mustache.render(msgTemplate, params); } catch (err) { return "ERROR: " + err; } } const I18n = { instantiateParameterizedMessages: function(data) { if ( data.__proto__ == Array.prototype ) { const result = []; for ( const x of data ) { result.push(this.instantiateParameterizedMessages(x)); } return result; } else if ( data.__proto__ == Object.prototype ) { const msgId = data.msgid; const msgParams = data.params; if ( msgId && msgId.__proto__ == String.prototype && msgParams && msgParams.__proto__ == Object.prototype ) { return $t(msgId, msgParams); } else { const result = {}; for ( const p in data ) { result[p] = this.instantiateParameterizedMessages(data[p]); } return result; } } else { return data; } }, render: function (template, params) { params = this.instantiateParameterizedMessages(params); return mustache.render(template, params); } } const DEFAULT_UI_LANGUAGE = 'en'; Translations.load(DEFAULT_UI_LANGUAGE, /*asDefault=*/true); // Below function selects the most suitable UI language from the list // of preferred languages in browser preferences and available translations. // Since, unlike Accept-Language header, navigator.languages doesn't contain // qvalues, they are computed using the same algorithm as in Firefox 121 function getDefaultUserLanguage() { const mostSuitableLang = { code: DEFAULT_UI_LANGUAGE, score: 0 } const n = navigator.languages.length; for (const lang of uiLanguages ) { const rank = navigator.languages.indexOf(lang.iso_code); if ( rank >= 0 ) { const qvalue = Math.round(10*(1 - rank/n))/10; const score = qvalue * lang.translation_count; if ( score > mostSuitableLang.score ) { mostSuitableLang.code = lang.iso_code; mostSuitableLang.score = score; } } } return mostSuitableLang.code; } function getUserLanguage() { return new URLSearchParams(window.location.search).get('userlang') || window.localStorage.getItem('userlang') || getDefaultUserLanguage(); } function setUserLanguage(lang, callback) { window.localStorage.setItem('userlang', lang); Translations.load(lang); Translations.whenReady(callback); } function createModalUILanguageSelector() { document.body.insertAdjacentHTML('beforeend', ``); window.modalUILanguageSelector = { show: () => { document.getElementById('uiLanguageSelector').style.display = 'flex'; }, close: () => { document.getElementById('uiLanguageSelector').style.display = 'none'; } }; } function initUILanguageSelector(activeLanguage, languageChangeCallback) { if ( document.getElementById("ui_language") == null ) { createModalUILanguageSelector(); } const languageSelector = document.getElementById("ui_language"); for (const lang of uiLanguages ) { const is_selected = lang.iso_code == activeLanguage; languageSelector.appendChild(new Option(lang.self_name, lang.iso_code, is_selected, is_selected)); } languageSelector.onchange = languageChangeCallback; } window.$t = $t; window.getUserLanguage = getUserLanguage; window.setUserLanguage = setUserLanguage; window.initUILanguageSelector = initUILanguageSelector; window.I18n = I18n;