diff --git a/locales/en/messages.json b/locales/en/messages.json
index d661f280..05dfe774 100644
--- a/locales/en/messages.json
+++ b/locales/en/messages.json
@@ -622,25 +622,32 @@
},
"statusbar_port_utilization": {
- "message": "Port utilization:"
+ "message": "Port utilization:",
+ "description": "Port utilization text shown in the status bar"
},
"statusbar_usage_download": {
- "message": "D: $1%"
+ "message": "D:",
+ "description": "References 'Download' in the status bar, port utilization. Keep one character long if possible"
},
"statusbar_usage_upload": {
- "message": "U: $1%"
+ "message": "U:",
+ "description": "References 'Upload' in the status bar, port utilization. Keep one character long if possible"
},
"statusbar_packet_error": {
- "message": "Packet error:"
+ "message": "Packet error:",
+ "description": "Packet error text shown in the status bar"
},
"statusbar_i2c_error": {
- "message": "I2C error:"
+ "message": "I2C error:",
+ "description": "CPU load text shown in the status bar"
},
"statusbar_cycle_time": {
- "message": "Cycle Time:"
+ "message": "Cycle Time:",
+ "description": "Cycle time text shown in the status bar"
},
"statusbar_cpu_load": {
- "message": "CPU Load: $1%"
+ "message": "CPU Load:",
+ "description": "CPU load text shown in the status bar"
},
"dfu_connect_message": {
diff --git a/package.json b/package.json
index 58e6eb3a..4eae71b4 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.13.0",
+ "@panter/vue-i18next": "^0.15.2",
"bluebird": "^3.7.2",
"djv": "^2.1.3-alpha.0",
"i18next": "^19.0.0",
@@ -67,8 +68,7 @@
"short-unique-id": "^1.1.1",
"three": "~0.97.0",
"universal-ga": "^1.2.0",
- "vue": "2.6.12",
- "vue-i18n": "8.21.1"
+ "vue": "2.6.12"
},
"devDependencies": {
"@quanle94/innosetup": "^6.0.2",
diff --git a/src/components/betaflight-logo/BetaflightLogo.vue b/src/components/betaflight-logo/BetaflightLogo.vue
index 51461b64..6c8bcb8e 100644
--- a/src/components/betaflight-logo/BetaflightLogo.vue
+++ b/src/components/betaflight-logo/BetaflightLogo.vue
@@ -72,15 +72,15 @@
- {{ $t("versionLabelConfigurator.message") }}: {{ configuratorVersion }}
+ {{ $t("versionLabelConfigurator") }}: {{ configuratorVersion }}
- {{ $t("versionLabelFirmware.message") }}: {{ firmwareVersion }}
+ {{ $t("versionLabelFirmware") }}: {{ firmwareVersion }}
{{ firmwareId }}
- {{ $t("versionLabelTarget.message") }}: {{ hardwareId }}
+ {{ $t("versionLabelTarget") }}: {{ hardwareId }}
diff --git a/src/components/init.js b/src/components/init.js
index 776e4dfd..eb1c3aa9 100644
--- a/src/components/init.js
+++ b/src/components/init.js
@@ -4,14 +4,6 @@ import BatteryLegend from "./quad-status/BatteryLegend.vue";
import BetaflightLogo from "./betaflight-logo/BetaflightLogo.vue";
import StatusBar from "./status-bar/StatusBar.vue";
-// a bit of a hack here to get around the current translations.
-// vue i18n provides slightly different api for this. But
-// it's also possible to provide custom formatter
-Vue.filter(
- "stripEnd",
- (value) => value.replace(/\$1%/, "")
-);
-
// Most of the global objects can go here at first.
// It's a bit of overkill for simple components,
// but these instance would eventually have more children
diff --git a/src/components/status-bar/PortUtilization.vue b/src/components/status-bar/PortUtilization.vue
index 863a7112..ccb5d782 100644
--- a/src/components/status-bar/PortUtilization.vue
+++ b/src/components/status-bar/PortUtilization.vue
@@ -1,6 +1,6 @@
-
{{ $t("statusbar_port_utilization.message") }}
+
{{ $t("statusbar_port_utilization") }}
- {{ $t(message + ".message") | stripEnd }}
+ {{ $t(message) }}
{{ value }}
{{ unit }}
diff --git a/src/components/status-bar/StatusBarVersion.vue b/src/components/status-bar/StatusBarVersion.vue
index 5dc23671..521a4305 100644
--- a/src/components/status-bar/StatusBarVersion.vue
+++ b/src/components/status-bar/StatusBarVersion.vue
@@ -1,12 +1,12 @@
- {{ $t("versionLabelConfigurator.message") }}: {{ configuratorVersion }}
+ {{ $t("versionLabelConfigurator") }}: {{ configuratorVersion }}
- , {{ $t("versionLabelFirmware.message") }}: {{ firmwareVersion }}
+ , {{ $t("versionLabelFirmware") }}: {{ firmwareVersion }}
{{ firmwareId }}
- , {{ $t("versionLabelTarget.message") }}: {{ hardwareId }}
+ , {{ $t("versionLabelTarget") }}: {{ hardwareId }}
({{ gitChangesetId }})
diff --git a/src/components/vueI18n.js b/src/components/vueI18n.js
index 0f07aba7..436553f7 100644
--- a/src/components/vueI18n.js
+++ b/src/components/vueI18n.js
@@ -1,18 +1,8 @@
import Vue from "vue";
-import VueI18n from "vue-i18n";
+import VueI18Next from "@panter/vue-i18next";
-Vue.use(VueI18n);
+Vue.use(VueI18Next);
-const vueI18n = new VueI18n(i18next);
-
-i18next.on("initialized", () => {
- vueI18n.setLocaleMessage("en", i18next.getDataByLanguage("en").messages);
-});
-
-i18next.on("languageChanged", (lang) => {
- vueI18n.setLocaleMessage(lang, i18next.getDataByLanguage(lang).messages);
- vueI18n.locale = lang;
- document.querySelector("html").setAttribute("lang", lang);
-});
+const vueI18n = new VueI18Next(i18next);
export default vueI18n;
diff --git a/src/js/localization.js b/src/js/localization.js
index 541487bb..e15ce290 100644
--- a/src/js/localization.js
+++ b/src/js/localization.js
@@ -29,26 +29,53 @@ i18n.init = function(cb) {
ns: ['messages'],
defaultNS:['messages'],
fallbackLng: languageFallback,
- backend: { loadPath: '/_locales/{{lng}}/{{ns}}.json' },
- }, function(err) {
- if (err !== undefined) {
- console.error(`Error loading i18n: ${err}`);
- } else {
- console.log('i18n system loaded');
- const detectedLanguage = i18n.getMessage(`language_${getValidLocale("DEFAULT")}`);
- i18n.addResources({"detectedLanguage": detectedLanguage });
- i18next.on('languageChanged', function () {
- i18n.localizePage(true);
- });
- }
- if (cb !== undefined) {
- cb();
- }
+ backend: {
+ loadPath: '/_locales/{{lng}}/{{ns}}.json',
+ parse: i18n.parseInputFile,
+ },
+ },
+ function(err) {
+ if (err !== undefined) {
+ console.error(`Error loading i18n: ${err}`);
+ } else {
+ console.log('i18n system loaded');
+ const detectedLanguage = i18n.getMessage(`language_${getValidLocale("DEFAULT")}`);
+ i18n.addResources({"detectedLanguage": detectedLanguage });
+ i18next.on('languageChanged', function () {
+ i18n.localizePage(true);
+ });
+ }
+ if (cb !== undefined) {
+ cb();
+ }
});
});
};
+/**
+ * We have different interpolate methods in the input messages file,
+ * we unify all of them here to the i18next style and simplify it
+ */
+i18n.parseInputFile = function(data) {
+
+ // Remove the $n interpolate of Chrome $1, $2, ... -> {{1}}, {{2}}, ...
+ const REGEXP_CHROME = /\$([1-9])/g;
+ const dataChrome = data.replace(REGEXP_CHROME, '{{$1}}');
+
+ // Remove the .message of the nesting $t(xxxxx.message) -> $t(xxxxx)
+ const REGEXP_NESTING = /\$t\(([^\)]*).message\)/g;
+ const dataNesting = dataChrome.replace(REGEXP_NESTING, '$t($1)');
+
+ // Move the .message of the json object to root xxxxx.message -> xxxxx
+ const jsonData = JSON.parse(dataNesting);
+ Object.entries(jsonData).forEach(([key, value]) => {
+ jsonData[key] = value.message;
+ });
+
+ return jsonData;
+};
+
i18n.changeLanguage = function(languageSelected) {
if (typeof ConfigStorage !== 'undefined') {
ConfigStorage.set({'userLanguageSelect': languageSelected});
@@ -60,28 +87,29 @@ i18n.changeLanguage = function(languageSelected) {
i18n.getMessage = function(messageID, parameters) {
- let translatedString;
+ let parametersObject;
// Option 1, no parameters or Object as parameters (i18Next type parameters)
if ((parameters === undefined) || ((parameters.constructor !== Array) && (parameters instanceof Object))) {
- translatedString = i18next.t(`${messageID}.message`, parameters);
+ parametersObject = parameters;
// Option 2: parameters as $1, $2, etc.
// (deprecated, from the old Chrome i18n
} else {
- translatedString = i18next.t(`${messageID}.message`);
-
+ // Convert the input to an array
let parametersArray = parameters;
if (parametersArray.constructor !== Array) {
parametersArray = [parameters];
}
- parametersArray.forEach(function(element, index) {
- translatedString = translatedString.replace(`$${(index + 1)}`, element);
+
+ parametersObject = {};
+ parametersArray.forEach(function(parameter, index) {
+ parametersObject[index + 1] = parameter;
});
}
- return translatedString;
+ return i18next.t(messageID, parametersObject);
};
i18n.getLanguagesAvailables = function() {
diff --git a/yarn.lock b/yarn.lock
index b377b042..a69a35dc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -53,6 +53,13 @@
"@nodelib/fs.scandir" "2.1.3"
fastq "^1.6.0"
+"@panter/vue-i18next@^0.15.2":
+ version "0.15.2"
+ resolved "https://registry.yarnpkg.com/@panter/vue-i18next/-/vue-i18next-0.15.2.tgz#814f6774237e444eb9b69156e9c507d41b7fbd32"
+ integrity sha512-7VX9GyxHJNEJKa2CRzC294Oz5EEbzVDZ1o3O/P8gL/PWBmcFOFsuivRbP/1a9ga2ihv/NBzoCWMCNIEEeCcONQ==
+ dependencies:
+ deepmerge "^2.0.0"
+
"@quanle94/innosetup@^6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@quanle94/innosetup/-/innosetup-6.0.2.tgz#b678b67240486302a08e3469151faca2c29f80ab"
@@ -1787,6 +1794,11 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+deepmerge@^2.0.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
+ integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
+
deepmerge@^4.2.1, deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
@@ -7470,11 +7482,6 @@ void-elements@^2.0.0, void-elements@^2.0.1:
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
-vue-i18n@8.21.1:
- version "8.21.1"
- resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.21.1.tgz#afa8e6390a5de3b65bd17533c4269181f86f1d61"
- integrity sha512-KEakJLI7R6+UCmhJOMZ0K7C+Zf5FcMh7QDkBRaEq39V7d9JgSrTDBf/9HuHU3TaxQYXx4fUi5PTIPdwLXq+iow==
-
vue-runtime-helpers@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vue-runtime-helpers/-/vue-runtime-helpers-1.1.2.tgz#446b7b820888ab0c5264d2c3a32468e72e4100f3"