/* js intl, inspired by danabr/jsI18n */ class JsI18n { constructor() { this.locale = ""; //Current locale this.locales = new Array(); //Available locales this.defaultLocale = "fr"; } /* Method for automatically detecting the language, does not work in every browser. */ detectLanguage() { const customLangs = document.querySelectorAll('hubl-lang'); if(customLangs) { for(let lang of customLangs) { let name = lang.getAttribute('lang'), file = lang.getAttribute('file'); if(window.hubl.intl.locales[name.toString()] == undefined) { return fetch(file).then((result) => { if (result.ok) { result.json().then(e => { window.hubl.intl.addLocale(name, e); }); } window.hubl.intl.resumeDetection(); }); } } } this.resumeDetection(); } resumeDetection() { const langComponent = document.querySelector('hubl-fallback-lang'); if(langComponent) { if(langComponent.hasAttribute('lang')) { this.defaultLocale = langComponent.getAttribute('lang'); if(langComponent.hasAttribute('force')) { localStorage.setItem('language', this.defaultLocale); } } } if (localStorage.getItem('language') || (window.navigator.language !== null && window.navigator.language !== undefined)) { this.fetchLocale(this.defaultLocale); this.setLocale(localStorage.getItem('language') || window.navigator.language.slice(0, 2)); } else { console.error('Language not found'); this.setLocale(this.defaultLocale); } }; /* Translates tag contents and attributes depending on the value of key. */ translateTag(node, key) { if (key.indexOf("=") == -1) { //Simple key this.translateNodeContent(node, key); } else { //Attribute/key pairs var parts = key.split(";"); for (var i = 0; i < parts.length; i++) { var pair = parts[i].split("="); var attr = pair[0].toLowerCase().trim(); var k = pair[1].trim(); if (attr == "html") { this.translateNodeContent(node, k); } else { // https://git.startinblox.com/framework/sib-core/issues/733 if (attr.startsWith('label-')) { let label = node.querySelector('[name="' + attr.replace("label-", "") + '"] > label'); if (label != null) { this.translateNodeContent(label, k); } } this.translateNodeContent(node.attributes[attr], k); } } } } /** Replace the content of the given node if there is a translation for the given key. **/ translateNodeContent(node, key) { var translation = this.t(key); if (node != null && translation != undefined) { if (node.nodeType == 1) { //Element try { if (node.innerHTML != translation) node.innerHTML = translation; } catch (e) { if (node.text != translation) node.text = translation; } } else if (node.nodeType == 2) { //Attribute if (node.value != translation) node.value = translation; } } } /* Helper for translating a node and all its child nodes. */ processNode(node) { if (node != undefined) { if (node.nodeType == 1) { //Element node var key = node.attributes["data-trans"]; if (key != null) { this.translateTag(node, key.nodeValue); } } //Process child nodes var children = node.childNodes; for (var i = 0; i < children.length; i++) { this.processNode(children[i]); } } } /* Adds a locale to the list, replacing the translations if the locale is already defined. */ addLocale(locale, translations) { this.locales[locale.toString()] = translations; } fetchLocale(locale) { if(this.locales[locale.toString()] == undefined) { return fetch(`/locales/${locale}.json`).then((result) => { if (result.ok) { result.json().then(e => { this.addLocale(locale, e); }); } }); } else { return (new Promise()).resolve(); } } /* Sets the locale to use when translating. */ setLocale(locale) { try { this.fetchLocale(locale).then(() => { if(this.locale) { localStorage.setItem('language', this.locale); } this.processPage(); this.locale = locale; }); } catch { if (locale != this.defaultLocale) { console.warn(`Locale not found: ${locale}, fallback to ${this.defaultLocale}`); this.setLocale(this.defaultLocale); } else { console.error("Language not found"); } } } /* Fetches the translation associated with the given key. */ t(key) { var translations = this.locales[this.locale]; if (translations == undefined) { translations = this.locales[this.defaultLocale]; } if(translations != undefined) { let translation = key.toString().split('.').reduce((o, i) => (o ? o[i] : undefined), translations); if (typeof translation == "string") { return translation; } else { try { let keySplitted = key.toString().split('.'); let first = keySplitted.shift(); translation = translations[first][keySplitted.join('.')]; return translation; } catch { return translations[key.toString()]; } } } return undefined; } /* Alias for JsI18n.t */ translate(key) { this.t(key); } /** Replaces the contents of all tags that have the data-trans attribute set. **/ processPage() { this.processNode(document.getElementsByTagName("html")[0]); } } //Global window.hubl.intl = new JsI18n; document.addEventListener("DOMContentLoaded", () => { // Detect the lang & initialize, based on the browser or "language" item from localstorage window.hubl.intl.detectLanguage(); let timer; (new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.target.attributes["data-trans"] != null) { // Render the target of the mutation instantly window.hubl.intl.processNode(mutation.target); // Then wait one arbitrary second to re-render the whole document in case a widget re-rendered clearTimeout(timer); timer = setTimeout(() => window.hubl.intl.processNode(document.querySelector('body')), 500); } }); }).observe(document.body, { subtree: true, childList: true })); document.addEventListener('widgetRendered', event => { window.hubl.intl.processNode(event.target); // Then wait one arbitrary second to re-render the whole document in case a widget re-rendered clearTimeout(timer); timer = setTimeout(() => window.hubl.intl.processNode(document.querySelector('body')), 500); }); });