diff --git a/static/resources_list.txt b/static/resources_list.txt index 1ad8a412e..cf11aa3d8 100644 --- a/static/resources_list.txt +++ b/static/resources_list.txt @@ -15,6 +15,7 @@ skin/fonts/Roboto.ttf skin/search_results.css skin/blank.html skin/viewer.js +skin/i18n.js skin/languages.js skin/mustache.min.js viewer.html diff --git a/static/skin/i18n.js b/static/skin/i18n.js new file mode 100644 index 000000000..a5ce72d2b --- /dev/null +++ b/static/skin/i18n.js @@ -0,0 +1,89 @@ +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 DEFAULT_UI_LANGUAGE = 'en'; + +Translations.load(DEFAULT_UI_LANGUAGE, /*asDefault=*/true); + +function getUserLanguage() { + return new URLSearchParams(window.location.search).get('userlang') + || DEFAULT_UI_LANGUAGE; +} + +function setUserLanguage(lang, callback) { + Translations.load(lang); + Translations.whenReady(callback); +} + +window.$t = $t; +window.getUserLanguage = getUserLanguage; +window.setUserLanguage = setUserLanguage; diff --git a/test/server.cpp b/test/server.cpp index ce7819dd9..f8c2ba35a 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -58,6 +58,8 @@ const ResourceCollection resources200Compressible{ { STATIC_CONTENT, "/ROOT/skin/css/autoComplete.css?cacheid=08951e06" }, { DYNAMIC_CONTENT, "/ROOT/skin/favicon/favicon.ico" }, { STATIC_CONTENT, "/ROOT/skin/favicon/favicon.ico?cacheid=fba03a27" }, + { DYNAMIC_CONTENT, "/ROOT/skin/i18n.js" }, + { STATIC_CONTENT, "/ROOT/skin/i18n.js?cacheid=eb41f5ce" }, { DYNAMIC_CONTENT, "/ROOT/skin/index.css" }, { STATIC_CONTENT, "/ROOT/skin/index.css?cacheid=0f9ba34e" }, { DYNAMIC_CONTENT, "/ROOT/skin/index.js" },