Finish translation system. #191

This commit is contained in:
Michael Teeuw 2016-05-11 12:38:41 +02:00
parent 32c9f197b1
commit 7a067a0f6e
20 changed files with 393 additions and 100 deletions

View File

@ -33,6 +33,7 @@
<script type="text/javascript" src="vendor/vendor.js"></script> <script type="text/javascript" src="vendor/vendor.js"></script>
<script type="text/javascript" src="modules/default/defaultmodules.js"></script> <script type="text/javascript" src="modules/default/defaultmodules.js"></script>
<script type="text/javascript" src="js/logger.js"></script> <script type="text/javascript" src="js/logger.js"></script>
<script type="text/javascript" src="translations/translations.js"></script>
<script type="text/javascript" src="js/translator.js"></script> <script type="text/javascript" src="js/translator.js"></script>
<script type="text/javascript" src="js/class.js"></script> <script type="text/javascript" src="js/class.js"></script>
<script type="text/javascript" src="js/module.js"></script> <script type="text/javascript" src="js/module.js"></script>

View File

@ -34,8 +34,8 @@ var Loader = (function() {
// All modules loaded. Load custom.css // All modules loaded. Load custom.css
// This is done after all the moduels so we can // This is done after all the moduels so we can
// overwrite all the defined styls. // overwrite all the defined styls.
loadFile('css/custom.css', function() { loadFile("css/custom.css", function() {
// custom.css loaded. Start all modules. // custom.css loaded. Start all modules.
startModules(); startModules();
}); });

View File

@ -331,6 +331,7 @@ var MM = (function() {
init: function() { init: function() {
Log.info("Initializing MagicMirror."); Log.info("Initializing MagicMirror.");
loadConfig(); loadConfig();
Translator.loadCoreTranslations(config.language);
Loader.loadModules(); Loader.loadModules();
}, },

View File

@ -58,7 +58,7 @@ var Module = Class.extend({
* return Map<String, String> - A map with langKeys and filenames. * return Map<String, String> - A map with langKeys and filenames.
*/ */
getTranslations: function() { getTranslations: function() {
return {}; return false;
}, },
/* getDom() /* getDom()
@ -221,9 +221,25 @@ var Module = Class.extend({
loadTranslations: function(callback) { loadTranslations: function(callback) {
var self = this; var self = this;
var translations = this.getTranslations(); var translations = this.getTranslations();
var translationFile = translations && (translations[config.language.toLowerCase()] || translations.en) || undefined; var lang = config.language.toLowerCase();
if(translationFile) {
Translator.load(this, translationFile, callback); // The variable `first` will contain the first
// defined translation after the following line.
for (var first in translations) {break;}
if (translations) {
var translationFile = translations[lang] || undefined;
var translationsFallbackFile = translations[first];
// If a translation file is set, load it and then also load the fallback translation file.
// Otherwise only load the fallback translation file.
if (translationFile !== undefined) {
Translator.load(self, translationFile, false, function() {
Translator.load(self, translationsFallbackFile, true, callback);
});
} else {
Translator.load(self, translationsFallbackFile, true, callback);
}
} else { } else {
callback(); callback();
} }

View File

@ -21,6 +21,7 @@ var Server = function(config, callback) {
app.use("/fonts", express.static(path.resolve(__dirname + "/../fonts"))); app.use("/fonts", express.static(path.resolve(__dirname + "/../fonts")));
app.use("/modules", express.static(path.resolve(__dirname + "/../modules"))); app.use("/modules", express.static(path.resolve(__dirname + "/../modules")));
app.use("/vendor", express.static(path.resolve(__dirname + "/../vendor"))); app.use("/vendor", express.static(path.resolve(__dirname + "/../vendor")));
app.use("/translations", express.static(path.resolve(__dirname + "/../translations")));
app.get("/", function(req, res) { app.get("/", function(req, res) {
res.sendFile(path.resolve(__dirname + "/../index.html")); res.sendFile(path.resolve(__dirname + "/../index.html"));

View File

@ -6,8 +6,111 @@
* MIT Licensed. * MIT Licensed.
*/ */
var Translator = (function() { var Translator = (function() {
/* 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 () {
if (xhr.readyState == 4 && xhr.status == "200") {
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));
}
return { return {
coreTranslations: {},
coreTranslationsFallback: {},
translations: {}, translations: {},
translationsFallback: {},
/* translate(module, key) /* translate(module, key)
* Load a translation for a given key for a given module. * Load a translation for a given key for a given module.
* *
@ -15,10 +118,28 @@ var Translator = (function() {
* argument key string - The key of the text to translate. * argument key string - The key of the text to translate.
*/ */
translate: function(module, key) { translate: function(module, key) {
if(this.translations[module.name]) {
if(this.translations[module.name] && key in this.translations[module.name]) {
// Log.log("Got translation for " + key + " from module translation: ");
return this.translations[module.name][key]; return this.translations[module.name][key];
} }
return undefined;
if (key in this.coreTranslations) {
// Log.log("Got translation for " + key + " from core translation.");
return this.coreTranslations[key];
}
if (this.translationsFallback[module.name] && key in this.translationsFallback[module.name]) {
// Log.log("Got translation for " + key + " from module translation fallback.");
return this.translationsFallback[module.name][key];
}
if (key in this.coreTranslationsFallback) {
// Log.log("Got translation for " + key + " from core translation fallback.");
return this.coreTranslationsFallback[key];
}
return key;
}, },
/* load(module, file, callback) /* load(module, file, callback)
* Load a translation file (json) and remember the data. * Load a translation file (json) and remember the data.
@ -27,33 +148,63 @@ var Translator = (function() {
* argument file string - Path of the file we want to load. * argument file string - Path of the file we want to load.
* argument callback function - Function called when done. * argument callback function - Function called when done.
*/ */
load: function(module, file, callback) { load: function(module, file, isFallback, callback) {
if (!isFallback) {
Log.log(module.name + " - Load translation: " + file);
} else {
Log.log(module.name + " - Load translation fallback: " + file);
}
var self = this; var self = this;
if(!this.translations[module.name]) { if(!this.translationsFallback[module.name]) {
this._loadJSON(module.file(file), function(json) { loadJSON(module.file(file), function(json) {
self.translations[module.name] = json; if (!isFallback) {
self.translations[module.name] = json;
} else {
self.translationsFallback[module.name] = json;
}
callback(); callback();
}); });
} else { } else {
callback(); callback();
} }
}, },
/* _loadJSON(file, callback)
* Load a JSON file via XHR. /* loadCoreTranslations(lang)
* Load the core translations.
* *
* argument file string - Path of the file we want to load. * argument lang String - The language identifier of the core language.
* argument callback function - Function called when done.
*/ */
_loadJSON: function(file, callback) { loadCoreTranslations: function(lang) {
var xhr = new XMLHttpRequest(); var self = this;
xhr.overrideMimeType("application/json");
xhr.open("GET", file, true); if (lang in translations) {
xhr.onreadystatechange = function () { Log.log("Loading core translation file: " + translations[lang]);
if (xhr.readyState == 4 && xhr.status == "200") { loadJSON(translations[lang], function(translations) {
callback(JSON.parse(xhr.responseText)); self.coreTranslations = translations;
} });
}; } else {
xhr.send(null); 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;}
Log.log("Loading core translation fallback file: " + translations[first]);
loadJSON(translations[first], function(translations) {
self.coreTranslationsFallback = translations;
});
},
}; };
})(); })();

View File

@ -4,9 +4,9 @@ This document describes the way to develop your own MagicMirror² modules.
## Module structure ## Module structure
All modules are loaded in de `modules` folder. The default modules are grouped together in the `modules/default` folder. Your module should be placed in a subfolder of `modules`. Note that any file or folder your create in the `modules` folder will be ignored by git, allowing you to upgrade the MagicMirror² without the loss of your files. All modules are loaded in de `modules` folder. The default modules are grouped together in the `modules/default` folder. Your module should be placed in a subfolder of `modules`. Note that any file or folder your create in the `modules` folder will be ignored by git, allowing you to upgrade the MagicMirror² without the loss of your files.
A module can be placed in one single folder. Or multiple modules can be grouped in a subfoler. Note that name of the module must be unique. Even when a module with a similar name is placed in a different folder, they can't be loaded at the same time. A module can be placed in one single folder. Or multiple modules can be grouped in a subfoler. Note that name of the module must be unique. Even when a module with a similar name is placed in a different folder, they can't be loaded at the same time.
### Files ### Files
- **modulename/modulename.js** - This is your core module script. - **modulename/modulename.js** - This is your core module script.
@ -47,12 +47,12 @@ After the module is initialized, the module instance has a few available module
####`this.name` ####`this.name`
**String** **String**
The name of the module. The name of the module.
####`this.identifier` ####`this.identifier`
**String** **String**
This is a unique identifier for the module instance. This is a unique identifier for the module instance.
####`this.hidden` ####`this.hidden`
**Boolean** **Boolean**
@ -97,7 +97,7 @@ start: function() {
####`getScripts()` ####`getScripts()`
**Should return: Array** **Should return: Array**
The getScripts method is called to request any additional scripts that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.js')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder. The getScripts method is called to request any additional scripts that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.js')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
**Example:** **Example:**
````javascript ````javascript
@ -114,10 +114,10 @@ getScripts: function() {
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore it's advised not to use any external urls. **Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore it's advised not to use any external urls.
####`getStyles()` ####`getStyles()`
**Should return: Array** **Should return: Array**
The getStyles method is called to request any additional stylesheets that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.css')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder. The getStyles method is called to request any additional stylesheets that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.css')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
**Example:** **Example:**
````javascript ````javascript
@ -133,11 +133,13 @@ getStyles: function() {
```` ````
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore it's advised not to use any external urls. **Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore it's advised not to use any external urls.
####`getTranslations()` ####`getTranslations()`
**Should return: Dictionary** **Should return: Dictionary**
The getTranslations method is called to request translation files that need to be loaded. This method should therefore return a dictionary with the files to load, identified by the country's short name. The getTranslations method is called to request translation files that need to be loaded. This method should therefore return a dictionary with the files to load, identified by the country's short name.
If the module does not have any module specific translations, the function can just be omitted or return `false`.
**Example:** **Example:**
````javascript ````javascript
getTranslations: function() { getTranslations: function() {
@ -149,7 +151,7 @@ getTranslations: function() {
```` ````
####`getDom()` ####`getDom()`
**Should return:** Dom Object **Should return:** Dom Object
Whenever the MagicMirror needs to update the information on screen (because it starts, or because your module asked a refresh using `this.updateDom()`), the system calls the getDom method. This method should therefor return a dom object. Whenever the MagicMirror needs to update the information on screen (because it starts, or because your module asked a refresh using `this.updateDom()`), the system calls the getDom method. This method should therefor return a dom object.
@ -203,7 +205,7 @@ When using a node_helper, the node helper can send your module notifications. Wh
````javascript ````javascript
socketNotificationReceived: function(notification, payload) { socketNotificationReceived: function(notification, payload) {
Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload); Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
}, },
```` ````
### Module instance methods ### Module instance methods
@ -233,7 +235,7 @@ start: function() {
}, 1000); //perform every 1000 milliseconds. }, 1000); //perform every 1000 milliseconds.
}, },
... ...
```` ````
####`this.sendNotification(notification, payload)` ####`this.sendNotification(notification, payload)`
***notification* String** - The notification identifier.<br> ***notification* String** - The notification identifier.<br>
@ -282,6 +284,13 @@ To show a module, you can call the `show(speed, callback)` method. You can call
The Magic Mirror contains a convenience wrapper for `l18n`. You can use this to automatically serve different translations for your modules based on the user's `language` configuration. The Magic Mirror contains a convenience wrapper for `l18n`. You can use this to automatically serve different translations for your modules based on the user's `language` configuration.
If no translation is found, a fallback will be used. The fallback sequence is as follows:
1. Translation as defined in module translation file of the user's preferred language.
2. Translation as defined in core translation file of the user's preferred language.
3. Translation as defined in module translation file of the fallback language (the first defined module translation file).
4. Translation as defined in core translation file of the fallback language (the first defined core translation file).
5. The key (identifier) of the translation.
**Example:** **Example:**
````javascript ````javascript
this.translate("INFO") //Will return a translated string for the identifier INFO this.translate("INFO") //Will return a translated string for the identifier INFO
@ -294,12 +303,12 @@ this.translate("INFO") //Will return a translated string for the identifier INFO
} }
```` ````
**Note:** Currently there is no fallback if a translation identifier does not exist in one language. Right now you always have to add all identifier to all your translations even if they are not translated yet (see [#191](https://github.com/MichMich/MagicMirror/issues/191)). **Note:** although comments are officially not supported in JSON files, MagicMirror allows it by stripping the comments before parsing the JSON file. Comments in translation files could help other translators.
## The Node Helper: node_helper.js ## The Node Helper: node_helper.js
The node helper is a Node.js script that is able to do some backend task to support your module. For every module type, only one node helper instance will be created. For example: if your MagicMirror uses two calendar modules, there will be only one calendar node helper instantiated. The node helper is a Node.js script that is able to do some backend task to support your module. For every module type, only one node helper instance will be created. For example: if your MagicMirror uses two calendar modules, there will be only one calendar node helper instantiated.
**Note:** Because there is only one node helper per module type, there is no default config available within your module. It's your task to send the desired config from your module to your node helper. **Note:** Because there is only one node helper per module type, there is no default config available within your module. It's your task to send the desired config from your module to your node helper.
@ -376,7 +385,7 @@ With this method, your node helper can receive notifications form your modules.
````javascript ````javascript
socketNotificationReceived: function(notification, payload) { socketNotificationReceived: function(notification, payload) {
Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload); Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
}, },
```` ````
### Module instance methods ### Module instance methods
@ -389,7 +398,7 @@ Each node helper has some handy methods which can be helpfull building your modu
If you want to send a notification to all your modules, use the `sendSocketNotification(notification, payload)`. Only the module of your module type will recieve the socket notification. If you want to send a notification to all your modules, use the `sendSocketNotification(notification, payload)`. Only the module of your module type will recieve the socket notification.
**Note:** Since all instances of you module will receive the notifications, it's your task to make sure the right module responds to your messages. **Note:** Since all instances of you module will receive the notifications, it's your task to make sure the right module responds to your messages.
**Example:** **Example:**
````javascript ````javascript
@ -398,7 +407,7 @@ this.sendSocketNotification('SET_CONFIG', this.config);
## MagicMirror Helper Methods ## MagicMirror Helper Methods
The core Magic Mirror object: `MM` has some handy method that will help you in controlling your and other modules. Most of the `MM` methods are available via convenience methods on the Module instance. The core Magic Mirror object: `MM` has some handy method that will help you in controlling your and other modules. Most of the `MM` methods are available via convenience methods on the Module instance.
### Module selection ### Module selection
The only additional method available for your module, is the feature to retrieve references to other modules. This can be used to hide and show other modules. The only additional method available for your module, is the feature to retrieve references to other modules. This can be used to hide and show other modules.
@ -479,10 +488,10 @@ Module.register("modulename",{
}); });
}); });
} }
}, },
//... //...
}); });
```` ````
## MagicMirror Logger ## MagicMirror Logger

View File

@ -47,12 +47,10 @@ Module.register("calendar",{
// Define required translations. // Define required translations.
getTranslations: function() { getTranslations: function() {
return { // The translations for the defaut modules are defined in the core translation files.
en: "translations/en.json", // Therefor we can just return false. Otherwise we should have returned a dictionairy.
de: "translations/de.json", // If you're trying to build yiur own module including translations, check out the documentation.
nl: "translations/nl.json", return false;
fr: "translations/fr.json"
};
}, },
// Override start method. // Override start method.
@ -120,20 +118,20 @@ Module.register("calendar",{
var titleWrapper = document.createElement("td"), var titleWrapper = document.createElement("td"),
repeatingCountTitle = ''; repeatingCountTitle = '';
if (this.config.displayRepeatingCountTitle) { if (this.config.displayRepeatingCountTitle) {
repeatingCountTitle = this.countTitleForUrl(event.url); repeatingCountTitle = this.countTitleForUrl(event.url);
if(repeatingCountTitle !== '') { if(repeatingCountTitle !== '') {
var thisYear = new Date().getFullYear(), var thisYear = new Date().getFullYear(),
yearDiff = thisYear - event.firstYear; yearDiff = thisYear - event.firstYear;
repeatingCountTitle = ', '+ yearDiff + '. ' + repeatingCountTitle; repeatingCountTitle = ', '+ yearDiff + '. ' + repeatingCountTitle;
} }
} }
titleWrapper.innerHTML = this.titleTransform(event.title) + repeatingCountTitle; titleWrapper.innerHTML = this.titleTransform(event.title) + repeatingCountTitle;
titleWrapper.className = "title bright"; titleWrapper.className = "title bright";
eventWrapper.appendChild(titleWrapper); eventWrapper.appendChild(titleWrapper);
@ -356,4 +354,3 @@ Module.register("calendar",{
return title; return title;
} }
}); });

View File

@ -1,7 +0,0 @@
{
"TODAY": "Heute"
, "TOMORROW": "Morgen"
, "RUNNING": "noch"
, "LOADING": "Lade Termine &hellip;"
, "EMPTY": "Keine Termine."
}

View File

@ -1,7 +0,0 @@
{
"TODAY": "Today"
, "TOMORROW": "Tomorrow"
, "RUNNING": "Ends in"
, "LOADING": "Loading events &hellip;"
, "EMPTY": "No upcoming events."
}

View File

@ -1,7 +0,0 @@
{
"TODAY": "Aujourd'hui"
, "TOMORROW": "Demain"
, "RUNNING": "Se termine dans"
, "LOADING": "Chargement des RDV &hellip;"
, "EMPTY": "Aucun RDV."
}

View File

@ -1,7 +0,0 @@
{
"TODAY": "Vandaag"
, "TOMORROW": "Morgen"
, "RUNNING": "Eindigd over"
, "LOADING": "Bezig met laden &hellip;"
, "EMPTY": "Geen geplande afspraken."
}

View File

@ -62,6 +62,14 @@ Module.register("currentweather",{
return ["weather-icons.css", "currentweather.css"]; return ["weather-icons.css", "currentweather.css"];
}, },
// Define required translations.
getTranslations: function() {
// The translations for the defaut modules are defined in the core translation files.
// Therefor we can just return false. Otherwise we should have returned a dictionairy.
// If you're trying to build yiur own module including translations, check out the documentation.
return false;
},
// Define start sequence. // Define start sequence.
start: function() { start: function() {
Log.info("Starting module: " + this.name); Log.info("Starting module: " + this.name);
@ -100,7 +108,7 @@ Module.register("currentweather",{
} }
if (!this.loaded) { if (!this.loaded) {
wrapper.innerHTML = "Loading weather ..."; wrapper.innerHTML = this.translate('LOADING');
wrapper.className = "dimmed light small"; wrapper.className = "dimmed light small";
return wrapper; return wrapper;
} }
@ -111,14 +119,14 @@ Module.register("currentweather",{
var windIcon = document.createElement("span"); var windIcon = document.createElement("span");
windIcon.className = "wi wi-strong-wind dimmed"; windIcon.className = "wi wi-strong-wind dimmed";
small.appendChild(windIcon); small.appendChild(windIcon);
var windSpeed = document.createElement("span"); var windSpeed = document.createElement("span");
windSpeed.innerHTML = " " + this.windSpeed; windSpeed.innerHTML = " " + this.windSpeed;
small.appendChild(windSpeed); small.appendChild(windSpeed);
if (this.config.showWindDirection) { if (this.config.showWindDirection) {
var windDirection = document.createElement("span"); var windDirection = document.createElement("span");
windDirection.innerHTML = " " + this.windDirection; windDirection.innerHTML = " " + this.translate(this.windDirection);
small.appendChild(windDirection); small.appendChild(windDirection);
} }
var spacer = document.createElement("span"); var spacer = document.createElement("span");
@ -205,13 +213,13 @@ Module.register("currentweather",{
*/ */
processWeather: function(data) { processWeather: function(data) {
this.temperature = this.roundValue(data.main.temp); this.temperature = this.roundValue(data.main.temp);
if (this.config.useBeaufort){ if (this.config.useBeaufort){
this.windSpeed = this.ms2Beaufort(this.roundValue(data.wind.speed)); this.windSpeed = this.ms2Beaufort(this.roundValue(data.wind.speed));
}else { }else {
this.windSpeed = parseFloat(data.wind.speed).toFixed(0); this.windSpeed = parseFloat(data.wind.speed).toFixed(0);
} }
this.windDirection = this.deg2Cardinal(data.wind.deg); this.windDirection = this.deg2Cardinal(data.wind.deg);
this.weatherType = this.config.iconTable[data.weather[0].icon]; this.weatherType = this.config.iconTable[data.weather[0].icon];
@ -220,7 +228,7 @@ Module.register("currentweather",{
var sunrise = new Date(data.sys.sunrise * 1000); var sunrise = new Date(data.sys.sunrise * 1000);
var sunset = new Date(data.sys.sunset * 1000); var sunset = new Date(data.sys.sunset * 1000);
// The moment().format('h') method has a bug on the Raspberry Pi. // The moment().format('h') method has a bug on the Raspberry Pi.
// So we need to generate the timestring manually. // So we need to generate the timestring manually.
// See issue: https://github.com/MichMich/MagicMirror/issues/181 // See issue: https://github.com/MichMich/MagicMirror/issues/181
var sunriseSunsetDateObject = (sunrise < now && sunset > now) ? sunset : sunrise; var sunriseSunsetDateObject = (sunrise < now && sunset > now) ? sunset : sunrise;
@ -293,7 +301,7 @@ Module.register("currentweather",{
* *
* return number - Rounded Temperature. * return number - Rounded Temperature.
*/ */
deg2Cardinal: function(deg) { deg2Cardinal: function(deg) {
if (deg>11.25 && deg<=33.75){ if (deg>11.25 && deg<=33.75){
return "NNE"; return "NNE";
@ -330,7 +338,7 @@ Module.register("currentweather",{
} }
}, },
roundValue: function(temperature) { roundValue: function(temperature) {
return parseFloat(temperature).toFixed(1); return parseFloat(temperature).toFixed(1);
} }

View File

@ -31,6 +31,14 @@ Module.register("newsfeed",{
return ["moment.js"]; return ["moment.js"];
}, },
// Define required translations.
getTranslations: function() {
// The translations for the defaut modules are defined in the core translation files.
// Therefor we can just return false. Otherwise we should have returned a dictionairy.
// If you're trying to build yiur own module including translations, check out the documentation.
return false;
},
// Define start sequence. // Define start sequence.
start: function() { start: function() {
Log.info("Starting module: " + this.name); Log.info("Starting module: " + this.name);
@ -55,7 +63,7 @@ Module.register("newsfeed",{
this.scheduleUpdateInterval(); this.scheduleUpdateInterval();
} }
this.loaded = true; this.loaded = true;
} }
}, },
@ -100,7 +108,7 @@ Module.register("newsfeed",{
} }
} else { } else {
wrapper.innerHTML = "Loading news ..."; wrapper.innerHTML = this.translate("LOADING");
wrapper.className = "small dimmed"; wrapper.className = "small dimmed";
} }

View File

@ -61,6 +61,14 @@ Module.register("weatherforecast",{
return ["weather-icons.css", "weatherforecast.css"]; return ["weather-icons.css", "weatherforecast.css"];
}, },
// Define required translations.
getTranslations: function() {
// The translations for the defaut modules are defined in the core translation files.
// Therefor we can just return false. Otherwise we should have returned a dictionairy.
// If you're trying to build yiur own module including translations, check out the documentation.
return false;
},
// Define start sequence. // Define start sequence.
start: function() { start: function() {
Log.info("Starting module: " + this.name); Log.info("Starting module: " + this.name);
@ -93,7 +101,7 @@ Module.register("weatherforecast",{
} }
if (!this.loaded) { if (!this.loaded) {
wrapper.innerHTML = "Loading weather ..."; wrapper.innerHTML = this.translate('LOADING');
wrapper.className = "dimmed light small"; wrapper.className = "dimmed light small";
return wrapper; return wrapper;
} }

27
translations/de.json Normal file
View File

@ -0,0 +1,27 @@
{
/* GENERAL */
"LOADING": "Lade &hellip;",
/* CALENDAR */
"TODAY": "Heute",
"TOMORROW": "Morgen",
"RUNNING": "noch",
"EMPTY": "Keine Termine.",
/* WEATHER */
"N": "N",
"NNE": "NNO",
"ENE": "ONO",
"E": "O",
"ESE": "OSO",
"SE": "SO",
"SSE": "SSO",
"S": "S",
"SSW": "SSW",
"SW": "SW",
"WSW": "WSW",
"W": "W",
"WNW": "WNW",
"NW": "NW",
"NNW": "NNW"
}

27
translations/en.json Normal file
View File

@ -0,0 +1,27 @@
{
/* GENERAL */
"LOADING": "Loading &hellip;",
/* CALENDAR */
"TODAY": "Today",
"TOMORROW": "Tomorrow",
"RUNNING": "Ends in",
"EMPTY": "No upcoming events.",
/* WEATHER */
"N": "N",
"NNE": "NNE",
"ENE": "ENE",
"E": "E",
"ESE": "ESE",
"SE": "SE",
"SSE": "SSE",
"S": "S",
"SSW": "SSW",
"SW": "SW",
"WSW": "WSW",
"W": "W",
"WNW": "WNW",
"NW": "NW",
"NNW": "NNW"
}

27
translations/fr.json Normal file
View File

@ -0,0 +1,27 @@
{
/* GENERAL */
"LOADING": "Chargement &hellip;",
/* CALENDAR */
"TODAY": "Aujourd'hui",
"TOMORROW": "Demain",
"RUNNING": "Se termine dans",
"EMPTY": "Aucun RDV.",
/* WEATHER */
"N": "N",
"NNE": "NNE",
"ENE": "ENE",
"E": "E",
"ESE": "ESE",
"SE": "SE",
"SSE": "SSE",
"S": "S",
"SSW": "SSO",
"SW": "SO",
"WSW": "OSO",
"W": "O",
"WNW": "ONO",
"NW": "NO",
"NNW": "NNO"
}

27
translations/nl.json Normal file
View File

@ -0,0 +1,27 @@
{
/* GENERAL */
"LOADING": "Bezig met laden &hellip;",
/* CALENDAR */
"TODAY": "Vandaag",
"TOMORROW": "Morgen",
"RUNNING": "Eindigd over",
"EMPTY": "Geen geplande afspraken.",
/* WEATHER */
"N": "N",
"NNE": "NNO",
"ENE": "ONO",
"E": "O",
"ESE": "OZO",
"SE": "ZO",
"SSE": "ZZO",
"S": "Z",
"SSW": "ZZW",
"SW": "ZW",
"WSW": "WZW",
"W": "W",
"WNW": "WNW",
"NW": "NW",
"NNW": "NNW"
}

View File

@ -0,0 +1,13 @@
/* Magic Mirror
* Translation Definition
*
* By Michael Teeuw http://michaelteeuw.nl
* MIT Licensed.
*/
var translations = {
"en" : "translations/en.json",
"nl" : "translations/nl.json",
"de" : "translations/de.json",
"fr" : "translations/fr.json",
};