2020-05-03 18:59:26 +02:00
|
|
|
/* global translations */
|
2020-04-21 07:36:18 +02:00
|
|
|
|
2016-04-21 01:03:26 +02:00
|
|
|
/* Magic Mirror
|
|
|
|
* Translator (l10n)
|
|
|
|
*
|
2020-04-28 23:05:28 +02:00
|
|
|
* By Christopher Fenner https://github.com/CFenner
|
2016-04-21 01:03:26 +02:00
|
|
|
* MIT Licensed.
|
|
|
|
*/
|
|
|
|
var Translator = (function() {
|
2016-05-11 12:38:41 +02:00
|
|
|
|
|
|
|
/* loadJSON(file, callback)
|
|
|
|
* Load a JSON file via XHR.
|
|
|
|
*
|
|
|
|
* argument file string - Path of the file we want to load.
|
|
|
|
* argument callback function - Function called when done.
|
|
|
|
*/
|
|
|
|
function loadJSON(file, callback) {
|
|
|
|
var xhr = new XMLHttpRequest();
|
|
|
|
xhr.overrideMimeType("application/json");
|
|
|
|
xhr.open("GET", file, true);
|
|
|
|
xhr.onreadystatechange = function () {
|
2019-06-05 08:55:17 +02:00
|
|
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
2016-05-11 12:38:41 +02:00
|
|
|
callback(JSON.parse(stripComments(xhr.responseText)));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
xhr.send(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* loadJSON(str, options)
|
|
|
|
* Remove any commenting from a json file so it can be parsed.
|
|
|
|
*
|
|
|
|
* argument str string - The string that contains json with comments.
|
|
|
|
* argument opts function - Strip options.
|
|
|
|
*
|
|
|
|
* return the stripped string.
|
|
|
|
*/
|
|
|
|
function stripComments(str, opts) {
|
|
|
|
// strip comments copied from: https://github.com/sindresorhus/strip-json-comments
|
|
|
|
|
|
|
|
var singleComment = 1;
|
|
|
|
var multiComment = 2;
|
|
|
|
|
|
|
|
function stripWithoutWhitespace() {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
function stripWithWhitespace(str, start, end) {
|
|
|
|
return str.slice(start, end).replace(/\S/g, " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
opts = opts || {};
|
|
|
|
|
|
|
|
var currentChar;
|
|
|
|
var nextChar;
|
|
|
|
var insideString = false;
|
|
|
|
var insideComment = false;
|
|
|
|
var offset = 0;
|
|
|
|
var ret = "";
|
|
|
|
var strip = opts.whitespace === false ? stripWithoutWhitespace : stripWithWhitespace;
|
|
|
|
|
|
|
|
for (var i = 0; i < str.length; i++) {
|
|
|
|
currentChar = str[i];
|
|
|
|
nextChar = str[i + 1];
|
|
|
|
|
|
|
|
if (!insideComment && currentChar === "\"") {
|
|
|
|
var escaped = str[i - 1] === "\\" && str[i - 2] !== "\\";
|
|
|
|
if (!escaped) {
|
|
|
|
insideString = !insideString;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (insideString) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!insideComment && currentChar + nextChar === "//") {
|
|
|
|
ret += str.slice(offset, i);
|
|
|
|
offset = i;
|
|
|
|
insideComment = singleComment;
|
|
|
|
i++;
|
|
|
|
} else if (insideComment === singleComment && currentChar + nextChar === "\r\n") {
|
|
|
|
i++;
|
|
|
|
insideComment = false;
|
|
|
|
ret += strip(str, offset, i);
|
|
|
|
offset = i;
|
|
|
|
continue;
|
|
|
|
} else if (insideComment === singleComment && currentChar === "\n") {
|
|
|
|
insideComment = false;
|
|
|
|
ret += strip(str, offset, i);
|
|
|
|
offset = i;
|
|
|
|
} else if (!insideComment && currentChar + nextChar === "/*") {
|
|
|
|
ret += str.slice(offset, i);
|
|
|
|
offset = i;
|
|
|
|
insideComment = multiComment;
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
} else if (insideComment === multiComment && currentChar + nextChar === "*/") {
|
|
|
|
i++;
|
|
|
|
insideComment = false;
|
|
|
|
ret += strip(str, offset, i + 1);
|
|
|
|
offset = i + 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret + (insideComment ? strip(str.substr(offset)) : str.substr(offset));
|
|
|
|
}
|
|
|
|
|
2016-04-21 01:03:26 +02:00
|
|
|
return {
|
2016-05-11 12:38:41 +02:00
|
|
|
coreTranslations: {},
|
|
|
|
coreTranslationsFallback: {},
|
2016-04-21 01:03:26 +02:00
|
|
|
translations: {},
|
2016-05-11 12:38:41 +02:00
|
|
|
translationsFallback: {},
|
|
|
|
|
2017-04-25 23:15:34 +03:00
|
|
|
/* translate(module, key, variables)
|
2016-04-21 01:03:26 +02:00
|
|
|
* Load a translation for a given key for a given module.
|
|
|
|
*
|
|
|
|
* argument module Module - The module to load the translation for.
|
|
|
|
* argument key string - The key of the text to translate.
|
2017-04-25 23:15:34 +03:00
|
|
|
* argument variables - The variables to use within the translation template (optional)
|
2016-04-21 01:03:26 +02:00
|
|
|
*/
|
2017-04-25 23:15:34 +03:00
|
|
|
translate: function(module, key, variables) {
|
|
|
|
variables = variables || {}; //Empty object by default
|
|
|
|
|
|
|
|
// Combines template and variables like:
|
|
|
|
// template: "Please wait for {timeToWait} before continuing with {work}."
|
|
|
|
// variables: {timeToWait: "2 hours", work: "painting"}
|
|
|
|
// to: "Please wait for 2 hours before continuing with painting."
|
|
|
|
function createStringFromTemplate(template, variables) {
|
2018-03-25 14:55:42 +02:00
|
|
|
if(Object.prototype.toString.call(template) !== "[object String]") {
|
2018-02-23 11:51:59 +01:00
|
|
|
return template;
|
|
|
|
}
|
2020-04-21 07:36:18 +02:00
|
|
|
if(variables.fallback && !template.match(new RegExp("{.+}"))) {
|
2017-04-25 23:15:34 +03:00
|
|
|
template = variables.fallback;
|
|
|
|
}
|
2020-04-21 07:36:18 +02:00
|
|
|
return template.replace(new RegExp("{([^}]+)}", "g"), function(_unused, varName){
|
2017-04-25 23:15:34 +03:00
|
|
|
return variables[varName] || "{"+varName+"}";
|
|
|
|
});
|
|
|
|
}
|
2016-05-11 12:38:41 +02:00
|
|
|
|
|
|
|
if(this.translations[module.name] && key in this.translations[module.name]) {
|
|
|
|
// Log.log("Got translation for " + key + " from module translation: ");
|
2017-04-25 23:15:34 +03:00
|
|
|
return createStringFromTemplate(this.translations[module.name][key], variables);
|
2016-04-21 01:03:26 +02:00
|
|
|
}
|
2016-05-11 12:38:41 +02:00
|
|
|
|
|
|
|
if (key in this.coreTranslations) {
|
|
|
|
// Log.log("Got translation for " + key + " from core translation.");
|
2017-04-25 23:15:34 +03:00
|
|
|
return createStringFromTemplate(this.coreTranslations[key], variables);
|
2016-05-11 12:38:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this.translationsFallback[module.name] && key in this.translationsFallback[module.name]) {
|
|
|
|
// Log.log("Got translation for " + key + " from module translation fallback.");
|
2017-04-25 23:15:34 +03:00
|
|
|
return createStringFromTemplate(this.translationsFallback[module.name][key], variables);
|
2016-05-11 12:38:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (key in this.coreTranslationsFallback) {
|
|
|
|
// Log.log("Got translation for " + key + " from core translation fallback.");
|
2017-04-25 23:15:34 +03:00
|
|
|
return createStringFromTemplate(this.coreTranslationsFallback[key], variables);
|
2016-05-11 12:38:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return key;
|
2016-04-21 01:03:26 +02:00
|
|
|
},
|
2019-06-04 09:33:53 +02:00
|
|
|
|
2018-02-10 12:38:55 +01:00
|
|
|
/* load(module, file, isFallback, callback)
|
2016-04-21 01:03:26 +02:00
|
|
|
* Load a translation file (json) and remember the data.
|
|
|
|
*
|
|
|
|
* argument module Module - The module to load the translation file for.
|
|
|
|
* argument file string - Path of the file we want to load.
|
2018-02-10 12:38:55 +01:00
|
|
|
* argument isFallback boolean - Flag to indicate fallback translations.
|
2016-04-21 01:03:26 +02:00
|
|
|
* argument callback function - Function called when done.
|
|
|
|
*/
|
2016-05-11 12:38:41 +02:00
|
|
|
load: function(module, file, isFallback, callback) {
|
|
|
|
if (!isFallback) {
|
|
|
|
Log.log(module.name + " - Load translation: " + file);
|
|
|
|
} else {
|
|
|
|
Log.log(module.name + " - Load translation fallback: " + file);
|
|
|
|
}
|
|
|
|
|
2016-04-21 01:03:26 +02:00
|
|
|
var self = this;
|
2016-05-11 12:38:41 +02:00
|
|
|
if(!this.translationsFallback[module.name]) {
|
|
|
|
loadJSON(module.file(file), function(json) {
|
|
|
|
if (!isFallback) {
|
|
|
|
self.translations[module.name] = json;
|
|
|
|
} else {
|
|
|
|
self.translationsFallback[module.name] = json;
|
|
|
|
}
|
2016-04-21 01:03:26 +02:00
|
|
|
callback();
|
|
|
|
});
|
2016-04-23 16:57:02 +02:00
|
|
|
} else {
|
|
|
|
callback();
|
2016-04-21 01:03:26 +02:00
|
|
|
}
|
|
|
|
},
|
2016-05-11 12:38:41 +02:00
|
|
|
|
|
|
|
/* loadCoreTranslations(lang)
|
|
|
|
* Load the core translations.
|
2016-04-21 01:03:26 +02:00
|
|
|
*
|
2016-05-11 12:38:41 +02:00
|
|
|
* argument lang String - The language identifier of the core language.
|
2016-04-21 01:03:26 +02:00
|
|
|
*/
|
2016-05-11 12:38:41 +02:00
|
|
|
loadCoreTranslations: function(lang) {
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
if (lang in translations) {
|
|
|
|
Log.log("Loading core translation file: " + translations[lang]);
|
|
|
|
loadJSON(translations[lang], function(translations) {
|
|
|
|
self.coreTranslations = translations;
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
Log.log("Configured language not found in core translations.");
|
|
|
|
}
|
|
|
|
|
|
|
|
self.loadCoreTranslationsFallback();
|
|
|
|
},
|
|
|
|
|
|
|
|
/* loadCoreTranslationsFallback()
|
|
|
|
* Load the core translations fallback.
|
|
|
|
* The first language defined in translations.js will be used.
|
|
|
|
*/
|
|
|
|
loadCoreTranslationsFallback: function() {
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
// The variable `first` will contain the first
|
|
|
|
// defined translation after the following line.
|
|
|
|
for (var first in translations) {break;}
|
|
|
|
|
2018-02-11 09:07:41 +01:00
|
|
|
if (first) {
|
2018-02-13 07:17:46 +01:00
|
|
|
Log.log("Loading core translation fallback file: " + translations[first]);
|
|
|
|
loadJSON(translations[first], function(translations) {
|
|
|
|
self.coreTranslationsFallback = translations;
|
|
|
|
});
|
2018-02-11 09:07:41 +01:00
|
|
|
}
|
2016-05-11 12:38:41 +02:00
|
|
|
},
|
2016-04-21 01:03:26 +02:00
|
|
|
};
|
|
|
|
})();
|