diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ee2ca4e..239eb13d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@ _This release is scheduled to be released on 2021-04-01._
- Converted newsfeed module to use templates.
- Update documentation and help screen about invalid config files.
- Moving weather provider specific code and configuration into each provider and making hourly part of the interface.
+- Bump electron to v11
### Removed
@@ -41,6 +42,7 @@ _This release is scheduled to be released on 2021-04-01._
- Fix Issue with weather forecast icons unit tests with different timezones (#2221)
- Fix issue with unencoded characters in translated strings when using nunjuck template (`Loading …` as an example)
- Fix socket.io backward compatibility with socket v2 clients
+- 3rd party module language loading if language is English
## [2.14.0] - 2021-01-01
diff --git a/js/module.js b/js/module.js
index 14458f8a..60c7200d 100644
--- a/js/module.js
+++ b/js/module.js
@@ -311,33 +311,31 @@ var Module = Class.extend({
*
* @param {Function} callback Function called when done.
*/
- loadTranslations: function (callback) {
- var self = this;
- var translations = this.getTranslations();
- var lang = config.language.toLowerCase();
+ loadTranslations(callback) {
+ const translations = this.getTranslations() || {};
+ const language = config.language.toLowerCase();
- // The variable `first` will contain the first
- // defined translation after the following line.
- for (var first in translations) {
- break;
+ const languages = Object.keys(translations);
+ const fallbackLanguage = languages[0];
+
+ if (languages.length === 0) {
+ return callback();
}
- if (translations) {
- var translationFile = translations[lang] || undefined;
- var translationsFallbackFile = translations[first];
+ const translationFile = translations[language];
+ const translationsFallbackFile = translations[fallbackLanguage];
- // 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 && translationFile !== translationsFallbackFile) {
- Translator.load(self, translationFile, false, function () {
- Translator.load(self, translationsFallbackFile, true, callback);
- });
+ if (!translationFile) {
+ return Translator.load(this, translationsFallbackFile, true, callback);
+ }
+
+ Translator.load(this, translationFile, false, () => {
+ if (translationFile !== translationsFallbackFile) {
+ Translator.load(this, translationsFallbackFile, true, callback);
} else {
- Translator.load(self, translationsFallbackFile, true, callback);
+ callback();
}
- } else {
- callback();
- }
+ });
},
/**
diff --git a/js/translator.js b/js/translator.js
index 82eec53a..6cddfae1 100644
--- a/js/translator.js
+++ b/js/translator.js
@@ -103,26 +103,18 @@ var Translator = (function () {
* @param {boolean} isFallback Flag to indicate fallback translations.
* @param {Function} callback Function called when done.
*/
- load: function (module, file, isFallback, callback) {
- if (!isFallback) {
- Log.log(module.name + " - Load translation: " + file);
- } else {
- Log.log(module.name + " - Load translation fallback: " + file);
+ load(module, file, isFallback, callback) {
+ Log.log(`${module.name} - Load translation${isFallback && " fallback"}: ${file}`);
+
+ if (this.translationsFallback[module.name]) {
+ return callback();
}
- var self = this;
- if (!this.translationsFallback[module.name]) {
- loadJSON(module.file(file), function (json) {
- if (!isFallback) {
- self.translations[module.name] = json;
- } else {
- self.translationsFallback[module.name] = json;
- }
- callback();
- });
- } else {
+ loadJSON(module.file(file), (json) => {
+ const property = isFallback ? "translationsFallback" : "translations";
+ this[property][module.name] = json;
callback();
- }
+ });
},
/**
diff --git a/package-lock.json b/package-lock.json
index 1cded957..43ca50d5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -382,6 +382,41 @@
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ=="
},
+ "@sinonjs/commons": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz",
+ "integrity": "sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==",
+ "dev": true,
+ "requires": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "@sinonjs/fake-timers": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz",
+ "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1.7.0"
+ }
+ },
+ "@sinonjs/samsam": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz",
+ "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1.6.0",
+ "lodash.get": "^4.4.2",
+ "type-detect": "^4.0.8"
+ }
+ },
+ "@sinonjs/text-encoding": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
+ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
+ "dev": true
+ },
"@stylelint/postcss-css-in-js": {
"version": "0.37.2",
"resolved": "https://registry.npmjs.org/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz",
@@ -3898,6 +3933,12 @@
"verror": "1.10.0"
}
},
+ "just-extend": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz",
+ "integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==",
+ "dev": true
+ },
"keyv": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
@@ -4013,6 +4054,12 @@
"integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=",
"dev": true
},
+ "lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
+ "dev": true
+ },
"lodash.isobject": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz",
@@ -4537,6 +4584,36 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
+ "nise": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.4.tgz",
+ "integrity": "sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1.7.0",
+ "@sinonjs/fake-timers": "^6.0.0",
+ "@sinonjs/text-encoding": "^0.7.1",
+ "just-extend": "^4.0.2",
+ "path-to-regexp": "^1.7.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
+ "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+ "dev": true,
+ "requires": {
+ "isarray": "0.0.1"
+ }
+ }
+ }
+ },
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
@@ -5926,6 +6003,37 @@
}
}
},
+ "sinon": {
+ "version": "9.2.4",
+ "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz",
+ "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1.8.1",
+ "@sinonjs/fake-timers": "^6.0.1",
+ "@sinonjs/samsam": "^5.3.1",
+ "diff": "^4.0.2",
+ "nise": "^4.0.4",
+ "supports-color": "^7.1.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
"slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
diff --git a/package.json b/package.json
index 6818977e..bbd9cc2e 100644
--- a/package.json
+++ b/package.json
@@ -58,6 +58,7 @@
"nyc": "^15.1.0",
"prettier": "^2.2.1",
"pretty-quick": "^3.1.0",
+ "sinon": "^9.2.4",
"spectron": "^13.0.0",
"stylelint": "^13.8.0",
"stylelint-config-prettier": "^8.0.2",
diff --git a/tests/e2e/translations_spec.js b/tests/e2e/translations_spec.js
index a66d0ea4..5f0d6053 100644
--- a/tests/e2e/translations_spec.js
+++ b/tests/e2e/translations_spec.js
@@ -7,6 +7,7 @@ const translations = require("../../translations/translations.js");
const helmet = require("helmet");
const { JSDOM } = require("jsdom");
const express = require("express");
+const sinon = require("sinon");
describe("Translations", function () {
let server;
@@ -34,6 +35,97 @@ describe("Translations", function () {
}
});
+ describe("loadTranslations", () => {
+ let dom;
+
+ beforeEach(() => {
+ dom = new JSDOM(
+ `\
+ \
+ `,
+ { runScripts: "dangerously", resources: "usable" }
+ );
+ });
+
+ it("should load translation file", (done) => {
+ dom.window.onload = async function () {
+ const { Translator, Module, config } = dom.window;
+ config.language = "en";
+ Translator.load = sinon.stub().callsFake((_m, _f, _fb, callback) => callback());
+
+ Module.register("name", { getTranslations: () => translations });
+ const MMM = Module.create("name");
+
+ const loaded = sinon.stub();
+ MMM.loadTranslations(loaded);
+
+ expect(loaded.callCount).to.equal(1);
+ expect(Translator.load.args.length).to.equal(1);
+ expect(Translator.load.calledWith(MMM, "translations/en.json", false, sinon.match.func)).to.be.true;
+
+ done();
+ };
+ });
+
+ it("should load translation + fallback file", (done) => {
+ dom.window.onload = async function () {
+ const { Translator, Module } = dom.window;
+ Translator.load = sinon.stub().callsFake((_m, _f, _fb, callback) => callback());
+
+ Module.register("name", { getTranslations: () => translations });
+ const MMM = Module.create("name");
+
+ const loaded = sinon.stub();
+ MMM.loadTranslations(loaded);
+
+ expect(loaded.callCount).to.equal(1);
+ expect(Translator.load.args.length).to.equal(2);
+ expect(Translator.load.calledWith(MMM, "translations/de.json", false, sinon.match.func)).to.be.true;
+ expect(Translator.load.calledWith(MMM, "translations/en.json", true, sinon.match.func)).to.be.true;
+
+ done();
+ };
+ });
+
+ it("should load translation fallback file", (done) => {
+ dom.window.onload = async function () {
+ const { Translator, Module, config } = dom.window;
+ config.language = "--";
+ Translator.load = sinon.stub().callsFake((_m, _f, _fb, callback) => callback());
+
+ Module.register("name", { getTranslations: () => translations });
+ const MMM = Module.create("name");
+
+ const loaded = sinon.stub();
+ MMM.loadTranslations(loaded);
+
+ expect(loaded.callCount).to.equal(1);
+ expect(Translator.load.args.length).to.equal(1);
+ expect(Translator.load.calledWith(MMM, "translations/en.json", true, sinon.match.func)).to.be.true;
+
+ done();
+ };
+ });
+
+ it("should load no file", (done) => {
+ dom.window.onload = async function () {
+ const { Translator, Module } = dom.window;
+ Translator.load = sinon.stub();
+
+ Module.register("name", {});
+ const MMM = Module.create("name");
+
+ const loaded = sinon.stub();
+ MMM.loadTranslations(loaded);
+
+ expect(loaded.callCount).to.equal(1);
+ expect(Translator.load.callCount).to.equal(0);
+
+ done();
+ };
+ });
+ });
+
const mmm = {
name: "TranslationTest",
file(file) {