2020-10-20 14:31:30 +00:00
|
|
|
/*
|
|
|
|
js intl, inspired by danabr/jsI18n
|
|
|
|
*/
|
|
|
|
class JsI18n {
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
this.locale = ""; //Current locale
|
|
|
|
this.locales = new Array(); //Available locales
|
2021-03-16 14:27:30 +00:00
|
|
|
this.defaultLocale = "fr";
|
2020-10-20 14:31:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Method for automatically detecting the language, does not work in every browser.
|
|
|
|
*/
|
|
|
|
detectLanguage() {
|
2021-03-16 14:27:30 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-20 14:31:30 +00:00
|
|
|
if (localStorage.getItem('language') || (window.navigator.language !== null && window.navigator.language !== undefined)) {
|
2021-03-16 14:27:30 +00:00
|
|
|
this.fetchLocale(this.defaultLocale);
|
2020-10-20 14:31:30 +00:00
|
|
|
this.setLocale(localStorage.getItem('language') || window.navigator.language.slice(0, 2));
|
|
|
|
} else {
|
|
|
|
console.error('Language not found');
|
2021-03-16 14:27:30 +00:00
|
|
|
this.setLocale(this.defaultLocale);
|
2020-10-20 14:31:30 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
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 {
|
2021-02-08 09:51:08 +00:00
|
|
|
// https://git.startinblox.com/framework/sib-core/issues/733
|
2021-01-26 16:48:05 +00:00
|
|
|
if (attr.startsWith('label-')) {
|
2021-02-22 20:27:58 +00:00
|
|
|
let label = node.querySelector('[name="' + attr.replace("label-", "") + '"] > label');
|
|
|
|
if (label != null) {
|
2021-01-26 16:48:05 +00:00
|
|
|
this.translateNodeContent(label, k);
|
|
|
|
}
|
|
|
|
}
|
2020-10-20 14:31:30 +00:00
|
|
|
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 {
|
2021-02-22 20:27:58 +00:00
|
|
|
if (node.innerHTML != translation)
|
2021-01-26 16:48:05 +00:00
|
|
|
node.innerHTML = translation;
|
2020-10-20 14:31:30 +00:00
|
|
|
} catch (e) {
|
2021-02-22 20:27:58 +00:00
|
|
|
if (node.text != translation)
|
2021-01-26 16:48:05 +00:00
|
|
|
node.text = translation;
|
2020-10-20 14:31:30 +00:00
|
|
|
}
|
|
|
|
} else if (node.nodeType == 2) { //Attribute
|
2021-02-22 20:27:58 +00:00
|
|
|
if (node.value != translation)
|
2021-01-26 16:48:05 +00:00
|
|
|
node.value = translation;
|
2020-10-20 14:31:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-03-16 14:27:30 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-20 14:31:30 +00:00
|
|
|
/*
|
|
|
|
Sets the locale to use when translating.
|
|
|
|
*/
|
|
|
|
setLocale(locale) {
|
|
|
|
try {
|
2021-03-16 14:27:30 +00:00
|
|
|
this.fetchLocale(locale).then(() => {
|
|
|
|
if(this.locale) {
|
|
|
|
localStorage.setItem('language', this.locale);
|
2020-10-20 14:31:30 +00:00
|
|
|
}
|
2021-03-16 14:27:30 +00:00
|
|
|
this.processPage();
|
|
|
|
this.locale = locale;
|
2020-10-20 14:31:30 +00:00
|
|
|
});
|
|
|
|
} catch {
|
2021-03-16 14:27:30 +00:00
|
|
|
if (locale != this.defaultLocale) {
|
|
|
|
console.warn(`Locale not found: ${locale}, fallback to ${this.defaultLocale}`);
|
|
|
|
this.setLocale(this.defaultLocale);
|
2020-10-20 14:31:30 +00:00
|
|
|
} else {
|
|
|
|
console.error("Language not found");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Fetches the translation associated with the given key.
|
|
|
|
*/
|
|
|
|
t(key) {
|
|
|
|
var translations = this.locales[this.locale];
|
2021-03-16 14:27:30 +00:00
|
|
|
if (translations == undefined) {
|
2021-03-17 10:19:32 +00:00
|
|
|
if(this.locales.length > 1) {
|
|
|
|
translations = this.locales[this.defaultLocale];
|
|
|
|
}
|
2021-03-16 14:27:30 +00:00
|
|
|
}
|
|
|
|
if(translations != undefined) {
|
2020-10-20 14:31:30 +00:00
|
|
|
let translation = key.toString().split('.').reduce((o, i) => (o ? o[i] : undefined), translations);
|
2021-01-26 16:48:05 +00:00
|
|
|
if (typeof translation == "string") {
|
2020-10-20 14:31:30 +00:00
|
|
|
return translation;
|
|
|
|
} else {
|
2021-03-16 14:27:30 +00:00
|
|
|
try {
|
|
|
|
let keySplitted = key.toString().split('.');
|
|
|
|
let first = keySplitted.shift();
|
|
|
|
translation = translations[first][keySplitted.join('.')];
|
|
|
|
return translation;
|
|
|
|
} catch {
|
|
|
|
return translations[key.toString()];
|
|
|
|
}
|
2020-10-20 14:31:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
2021-03-16 14:27:30 +00:00
|
|
|
window.hubl.intl = new JsI18n;
|
2020-10-20 14:31:30 +00:00
|
|
|
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
|
|
// Detect the lang & initialize, based on the browser or "language" item from localstorage
|
2021-03-16 14:27:30 +00:00
|
|
|
window.hubl.intl.detectLanguage();
|
2021-01-26 17:19:42 +00:00
|
|
|
let timer;
|
2021-01-26 16:48:05 +00:00
|
|
|
(new MutationObserver((mutations) => {
|
|
|
|
mutations.forEach(mutation => {
|
2021-02-22 20:27:58 +00:00
|
|
|
if (mutation.target.attributes["data-trans"] != null) {
|
2021-01-26 16:48:05 +00:00
|
|
|
// Render the target of the mutation instantly
|
2021-03-16 14:27:30 +00:00
|
|
|
window.hubl.intl.processNode(mutation.target);
|
2021-01-26 16:48:05 +00:00
|
|
|
// Then wait one arbitrary second to re-render the whole document in case a widget re-rendered
|
2021-01-26 17:19:42 +00:00
|
|
|
clearTimeout(timer);
|
2021-03-16 14:27:30 +00:00
|
|
|
timer = setTimeout(() => window.hubl.intl.processNode(document.querySelector('body')), 500);
|
2020-12-09 11:20:35 +00:00
|
|
|
}
|
2020-10-20 14:31:30 +00:00
|
|
|
});
|
2021-01-26 16:48:05 +00:00
|
|
|
}).observe(document.body, {
|
|
|
|
subtree: true,
|
|
|
|
childList: true
|
|
|
|
}));
|
2021-02-08 13:01:36 +00:00
|
|
|
document.addEventListener('widgetRendered', event => {
|
2021-03-16 14:27:30 +00:00
|
|
|
window.hubl.intl.processNode(event.target);
|
2021-02-08 13:01:36 +00:00
|
|
|
// Then wait one arbitrary second to re-render the whole document in case a widget re-rendered
|
|
|
|
clearTimeout(timer);
|
2021-03-16 14:27:30 +00:00
|
|
|
timer = setTimeout(() => window.hubl.intl.processNode(document.querySelector('body')), 500);
|
2021-02-08 13:01:36 +00:00
|
|
|
});
|
2020-10-20 14:31:30 +00:00
|
|
|
});
|