diff --git a/locales/en/messages.json b/locales/en/messages.json
index a0bf2d16..732acc8e 100644
--- a/locales/en/messages.json
+++ b/locales/en/messages.json
@@ -69,75 +69,81 @@
"analyticsOptOut": {
"message": "Opt out of the anonymised collection of statistics data"
},
- "userLanguageSelect": {
- "message": "Language (need to restart the application for the changes to take effect)"
+ "language_changed": {
+ "message": "Language change saved"
+ },
+ "language_choice_message": {
+ "message": "Change language:",
+ "description": "Try and be brief"
},
"language_default": {
- "message": "Default"
+ "message": "System Default"
+ },
+ "language_default_pretty": {
+ "message": "System Default ($t(detectedLanguage))"
},
"language_ca": {
- "message": "Catal\u00e0 (ca)",
+ "message": "Catal\u00e0",
"description": "Don't translate!!!"
},
"language_de": {
- "message": "Deutsch (de)",
+ "message": "Deutsch",
"description": "Don't translate!!!"
},
"language_en": {
- "message": "English (en)",
+ "message": "English",
"description": "Don't translate!!!"
},
"language_es": {
- "message": "Espa\u00f1ol (es)",
+ "message": "Espa\u00f1ol",
"description": "Don't translate!!!"
},
"language_fr": {
- "message": "Fran\u00e7ais (fr)",
+ "message": "Fran\u00e7ais",
"description": "Don't translate!!!"
},
"language_gl": {
- "message": "Galego (gl)",
+ "message": "Galego",
"description": "Don't translate!!!"
},
"language_hr": {
- "message": "Hrvatski (hr)",
+ "message": "Hrvatski",
"description": "Don't translate!!!"
},
-
"language_id": {
- "message": "Bahasa Indonesia (id)",
+ "message": "Bahasa Indonesia",
"description": "Don't translate!!!"
},
"language_it": {
- "message": "Italiano (it)",
+ "message": "Italiano",
"description": "Don't translate!!!"
},
"language_ja": {
- "message": "\u65E5\u672C\u8A9E (ja)",
+ "message": "\u65E5\u672C\u8A9E",
"description": "Don't translate!!!"
},
"language_ko": {
- "message": "\ud55c\uad6d\uc5b4 (ko)",
+ "message": "\ud55c\uad6d\uc5b4",
"description": "Don't translate!!!"
},
"language_lv": {
- "message": "Latvie\u0161u (lv)",
+ "message": "Latvie\u0161u",
"description": "Don't translate!!!"
},
"language_pt": {
- "message": "Portugu\u00EAs (pt)",
+ "message": "Portugu\u00EAs",
"description": "Don't translate!!!"
},
"language_ru": {
- "message": "\u0420\u0443\u0441\u0441\u043A\u0438\u0439 \u044F\u0437\u044B\u043A (ru)",
+ "message": "\u0420\u0443\u0441\u0441\u043A\u0438\u0439 \u044F\u0437\u044B\u043A",
"description": "Don't translate!!!"
},
"language_sv": {
- "message": "Svenska (sv)",
+ "message": "Svenska",
"description": "Don't translate!!!"
},
"language_zh_CN": {
- "message": "\u7b80\u4f53\u4e2d\u6587 (zh_CN)",
+ "message": "\u7b80\u4f53\u4e2d\u6587",
"description": "Don't translate!!!"
},
@@ -231,6 +237,12 @@
"tabLanding": {
"message": "Welcome"
},
+ "tabChangelog": {
+ "message": "Changelog"
+ },
+ "tabPrivacyPolicy": {
+ "message": "Privacy Policy"
+ },
"tabHelp": {
"message": "Documentation & Support"
},
@@ -573,15 +585,9 @@
"defaultFacebookText": {
"message": "We also have a Facebook Group.
Join us to get a place to talk about Betaflight, ask configuration questions, or just hang out with fellow pilots."
},
- "defaultChangelogAction": {
- "message": "Changelog"
- },
"defaultChangelogHead": {
"message": "Configurator - Changelog"
},
- "defaultPrivacyPolicyAction": {
- "message": "Privacy Policy"
- },
"defaultButtonFirmwareFlasher": {
"message": "Firmware Flasher"
},
diff --git a/src/css/main.css b/src/css/main.css
index cc0dd048..139faad4 100644
--- a/src/css/main.css
+++ b/src/css/main.css
@@ -40,7 +40,7 @@ body {
a {
text-decoration: none;
color: var(--defaultText);
- font-family: 'open_sanssemibold', Arial;
+ font-weight: bold;
}
a:hover {
@@ -513,7 +513,7 @@ input[type="number"]::-webkit-inner-spin-button {
}
#log a {
- font-weight: regular;
+ font-weight: normal;
color: #ffbb00;
}
@@ -590,6 +590,7 @@ input[type="number"]::-webkit-inner-spin-button {
#tabs li a {
font-family: 'open_sansregular', Arial;
+ font-weight: normal;
padding-left: 33px;
padding-top: 5px;
padding-bottom: 3px;
@@ -1654,6 +1655,7 @@ dialog {
color: #fff;
font-size: 12px;
font-family: 'open_sansregular', Arial;
+ font-weight: normal;
text-shadow: 0px 1px rgba(0, 0, 0, 0.25);
margin-top: -1px;
}
@@ -1666,6 +1668,7 @@ dialog {
color: #fff;
font-size: 12px;
font-family: 'open_sansregular', Arial;
+ font-weight: normal;
text-shadow: 0px 1px rgba(0, 0, 0, 0.25);
margin-top: -1px;
}
diff --git a/src/css/tabs/landing.css b/src/css/tabs/landing.css
index 2dccedfb..6692cbc3 100644
--- a/src/css/tabs/landing.css
+++ b/src/css/tabs/landing.css
@@ -180,3 +180,22 @@
display: block;
float: left;
}
+
+.tab-landing .languageSwitcher .selected_language {
+ font-weight: bold;
+}
+.tab-landing .languageSwitcher {
+ margin-left: auto;
+ margin-right: auto;
+ text-align: center;
+ color: silver;
+}
+.tab-landing .languageSwitcher a {
+ color: silver;
+ font-weight: normal;
+ white-space: nowrap;
+}
+.tab-landing .languageSwitcher a:not(:last-child):after {
+ content: ", ";
+ font-weight: normal;
+}
diff --git a/src/js/localization.js b/src/js/localization.js
index 0c50c7e3..f2e74d17 100644
--- a/src/js/localization.js
+++ b/src/js/localization.js
@@ -30,14 +30,30 @@ i18n.init = function(cb) {
console.error('Error loading i18n ' + err);
} else {
console.log('i18n system loaded');
+ var detectedLanguage = i18n.getMessage('language_' + getValidLocale("DEFAULT"));
+ i18n.addResources({"detectedLanguage": detectedLanguage });
}
if (cb !== undefined) {
cb();
}
});
});
+ // This function should do the same things that the i18n.localizePage function below does.
+ i18next.on('languageChanged', function (newLang) {
+ var translate = function(messageID) {
+ return i18n.getMessage(messageID);
+ };
+ i18n.localizePage(true);
+ updateStatusBarVersion();
+ });
}
+i18n.changeLanguage = function(languageSelected) {
+ ConfigStorage.set({'userLanguageSelect': languageSelected});
+ i18next.changeLanguage(getValidLocale(languageSelected));
+ i18n.selectedLanguage = languageSelected;
+ GUI.log(i18n.getMessage('language_changed'));
+}
i18n.getMessage = function(messageID, parameters) {
var translatedString;
@@ -79,44 +95,58 @@ i18n.existsMessage = function(key) {
* Helper functions, don't depend of the i18n framework
*/
-i18n.localizePage = function() {
+i18n.localizePage = function(forceReTranslate) {
var localized = 0;
var translate = function(messageID) {
localized++;
-
return i18n.getMessage(messageID);
};
- $('[i18n]:not(.i18n-replaced)').each(function() {
- var element = $(this);
+ if (forceReTranslate) {
+ $('[i18n]').each(function() {
+ var element = $(this);
+ element.html(translate(element.attr('i18n')));
+ });
+ $('[i18n_title]').each(function() {
+ var element = $(this);
+ element.attr('title', translate(element.attr('i18n_title')));
+ });
+ $('[i18n_value]').each(function() {
+ var element = $(this);
+ element.val(translate(element.attr('i18n_value')));
+ });
+ $('[i18n_placeholder]').each(function() {
+ var element = $(this);
+ element.attr('placeholder', translate(element.attr('i18n_placeholder')));
+ });
+ } else {
- element.html(translate(element.attr('i18n')));
- element.addClass('i18n-replaced');
- });
+ $('[i18n]:not(.i18n-replaced)').each(function() {
+ var element = $(this);
+ element.html(translate(element.attr('i18n')));
+ element.addClass('i18n-replaced');
+ });
- $('[i18n_title]:not(.i18n_title-replaced)').each(function() {
- var element = $(this);
+ $('[i18n_title]:not(.i18n_title-replaced)').each(function() {
+ var element = $(this);
+ element.attr('title', translate(element.attr('i18n_title')));
+ element.addClass('i18n_title-replaced');
+ });
- element.attr('title', translate(element.attr('i18n_title')));
- element.addClass('i18n_title-replaced');
- });
-
- $('[i18n_value]:not(.i18n_value-replaced)').each(function() {
- var element = $(this);
-
- element.val(translate(element.attr('i18n_value')));
- element.addClass('i18n_value-replaced');
- });
-
- $('[i18n_placeholder]:not(.i18n_placeholder-replaced)').each(function() {
- var element = $(this);
-
- element.attr('placeholder', translate(element.attr('i18n_placeholder')));
- element.addClass('i18n_placeholder-replaced');
- });
+ $('[i18n_value]:not(.i18n_value-replaced)').each(function() {
+ var element = $(this);
+ element.val(translate(element.attr('i18n_value')));
+ element.addClass('i18n_value-replaced');
+ });
+ $('[i18n_placeholder]:not(.i18n_placeholder-replaced)').each(function() {
+ var element = $(this);
+ element.attr('placeholder', translate(element.attr('i18n_placeholder')));
+ element.addClass('i18n_placeholder-replaced');
+ });
+ }
return localized;
}
@@ -129,7 +159,8 @@ function getStoredUserLocale(cb) {
var userLanguage = 'DEFAULT';
if (result.userLanguageSelect) {
userLanguage = result.userLanguageSelect
- }
+ }
+ i18n.selectedLanguage = userLanguage;
userLanguage = getValidLocale(userLanguage);
diff --git a/src/js/main.js b/src/js/main.js
index 579f72ba..9050d6f5 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -134,12 +134,6 @@ function startProcess() {
checkForConfiguratorUpdates();
}
- ConfigStorage.get('logopen', function (result) {
- if (result.logopen) {
- $("#showlog").trigger('click');
- }
- });
-
// log webgl capability
// it would seem the webgl "enabling" through advanced settings will be ignored in the future
// and webgl will be supported if gpu supports it by default (canary 40.0.2175.0), keep an eye on this one
@@ -398,29 +392,6 @@ function startProcess() {
DarkTheme.setConfig(checked);
}).change();
- ConfigStorage.get('userLanguageSelect', function (result) {
-
- var userLanguage_e = $('div.userLanguage select');
- var languagesAvailables = i18n.getLanguagesAvailables();
- userLanguage_e.append('');
- userLanguage_e.append('');
- languagesAvailables.forEach(function(element) {
- var languageName = i18n.getMessage('language_' + element);
- userLanguage_e.append('');
- });
-
- if (result.userLanguageSelect) {
- userLanguage_e.val(result.userLanguageSelect);
- }
-
- userLanguage_e.change(function () {
- var languageSelected = $(this).val();
-
- // Select the new language, a restart is required
- ConfigStorage.set({'userLanguageSelect': languageSelected});
- });
- });
-
function close_and_cleanup(e) {
if (e.type == 'click' && !$.contains($('div#options-window')[0], e.target) || e.type == 'keyup' && e.keyCode == 27) {
$(document).unbind('click keyup', close_and_cleanup);
@@ -542,6 +513,12 @@ function startProcess() {
$(this).data('state', state);
});
+ ConfigStorage.get('logopen', function (result) {
+ if (result.logopen) {
+ $("#showlog").trigger('click');
+ }
+ });
+
ConfigStorage.get('permanentExpertMode', function (result) {
if (result.permanentExpertMode) {
$('input[name="expertModeCheckbox"]').prop('checked', true);
diff --git a/src/js/port_handler.js b/src/js/port_handler.js
index ea7181d6..91997761 100644
--- a/src/js/port_handler.js
+++ b/src/js/port_handler.js
@@ -174,7 +174,9 @@ PortHandler.update_port_select = function (ports) {
$('div#port-picker #port').append($("", {value: ports[i], text: ports[i], data: {isManual: false}}));
}
- $('div#port-picker #port').append($("", {value: 'manual', text: i18n.getMessage('portsSelectManual'), data: {isManual: true}}));
+ $('div#port-picker #port').append($("", {value: 'manual', i18n: 'portsSelectManual', data: {isManual: true}}));
+ i18n.localizePage();
+
};
PortHandler.port_detected = function(name, code, timeout, ignore_timeout) {
diff --git a/src/js/tabs/firmware_flasher.js b/src/js/tabs/firmware_flasher.js
index cc5b3b0b..cbb4c218 100644
--- a/src/js/tabs/firmware_flasher.js
+++ b/src/js/tabs/firmware_flasher.js
@@ -84,7 +84,7 @@ TABS.firmware_flasher.initialize = function (callback) {
$('div.release_info').slideDown();
} else {
- self.flashingMessage(i18n.getMessage('firmwareFlasherHexCorrupted'), self.FLASH_MESSAGE_TYPES.INVALID);
+ self.flashingMessage('firmwareFlasherHexCorrupted', self.FLASH_MESSAGE_TYPES.INVALID);
}
});
}
@@ -252,7 +252,7 @@ TABS.firmware_flasher.initialize = function (callback) {
function buildBuildTypeOptionsList() {
buildType_e.empty();
buildTypesToShow.forEach((build, index) => {
- buildType_e.append($("".format(index, build.tag ? i18n.getMessage(build.tag) : build.title)))
+ buildType_e.append($("".format(index,build.tag ? 'i18n="' + build.tag + '" ' : '', build.tag ? i18n.getMessage(build.tag) : build.title)))
});
$('select[name="build_type"]').val($('select[name="build_type"] option:first').val());
}
@@ -300,16 +300,17 @@ TABS.firmware_flasher.initialize = function (callback) {
var build_type = $(this).val();
$('select[name="board"]').empty()
- .append($("".format(i18n.getMessage('firmwareFlasherOptionLabelSelectBoard'))));
+ .append($(""));
$('select[name="firmware_version"]').empty()
- .append($("".format(i18n.getMessage('firmwareFlasherOptionLabelSelectFirmwareVersion'))));
+ .append($(""));
if (!GUI.connect_lock) {
buildTypesToShow[build_type].loader();
}
chrome.storage.local.set({'selected_build_type': build_type});
+ i18n.localizePage();
});
$('select[name="board"]').change(function() {
@@ -317,7 +318,7 @@ TABS.firmware_flasher.initialize = function (callback) {
var target = $(this).val();
if (!GUI.connect_lock) {
- self.flashingMessage(i18n.getMessage('firmwareFlasherLoadFirmwareFile'), self.FLASH_MESSAGE_TYPES.NEUTRAL)
+ self.flashingMessage('firmwareFlasherLoadFirmwareFile', self.FLASH_MESSAGE_TYPES.NEUTRAL)
.flashProgress(0);
$('div.git_info').slideUp();
@@ -401,7 +402,7 @@ TABS.firmware_flasher.initialize = function (callback) {
self.flashingMessage(i18n.getMessage('firmwareFlasherFirmwareLocalLoaded', parsed_hex.bytes_total), self.FLASH_MESSAGE_TYPES.NEUTRAL);
} else {
- self.flashingMessage(i18n.getMessage('firmwareFlasherHexCorrupted'), self.FLASH_MESSAGE_TYPES.INVALID);
+ self.flashingMessage('firmwareFlasherHexCorrupted', self.FLASH_MESSAGE_TYPES.INVALID);
}
});
}
@@ -454,9 +455,10 @@ TABS.firmware_flasher.initialize = function (callback) {
}
function failed_to_load() {
- $('span.progressLabel').text(i18n.getMessage('firmwareFlasherFailedToLoadOnlineFirmware'));
+ $('span.progressLabel').attr('i18n','firmwareFlasherFailedToLoadOnlineFirmware').removeClass('i18n-replaced');
$("a.load_remote_file").removeClass('disabled');
$("a.load_remote_file").text(i18n.getMessage('firmwareFlasherButtonLoadOnline'));
+ i18n.localizePage();
}
var summary = $('select[name="firmware_version"] option:selected').data('summary');
@@ -466,7 +468,8 @@ TABS.firmware_flasher.initialize = function (callback) {
$("a.load_remote_file").addClass('disabled');
$.get(summary.url, onLoadSuccess).fail(failed_to_load);
} else {
- $('span.progressLabel').text(i18n.getMessage('firmwareFlasherFailedToLoadOnlineFirmware'));
+ $('span.progressLabel').attr('i18n','firmwareFlasherFailedToLoadOnlineFirmware').removeClass('i18n-replaced');
+ i18n.localizePage();
}
});
@@ -512,7 +515,8 @@ TABS.firmware_flasher.initialize = function (callback) {
STM32DFU.connect(usbDevices, parsed_hex, options);
}
} else {
- $('span.progressLabel').text(i18n.getMessage('firmwareFlasherFirmwareNotLoaded'));
+ $('span.progressLabel').attr('i18n','firmwareFlasherFirmwareNotLoaded').removeClass('i18n-replaced');
+ i18n.localizePage();
}
}
}
@@ -740,7 +744,13 @@ TABS.firmware_flasher.flashingMessage = function(message, type) {
break;
}
if (message != null) {
- progressLabel_e.html(message);
+ if (i18next.exists(message)) {
+ progressLabel_e.attr('i18n',message).removeClass('i18n-replaced');
+ i18n.localizePage();
+ } else {
+ progressLabel_e.removeAttr('i18n');
+ progressLabel_e.html(message);
+ }
}
return self;
diff --git a/src/js/tabs/landing.js b/src/js/tabs/landing.js
index dd6928ee..9034af6b 100644
--- a/src/js/tabs/landing.js
+++ b/src/js/tabs/landing.js
@@ -2,18 +2,51 @@
TABS.landing = {};
TABS.landing.initialize = function (callback) {
- var self = this;
+ var self = this;
- if (GUI.active_tab != 'landing') {
- GUI.active_tab = 'landing';
+ if (GUI.active_tab != 'landing') {
+ GUI.active_tab = 'landing';
+ }
+
+ $('#content').load("./tabs/landing.html", function () {
+ function showLang(newLang) {
+ var bottomSection = $('.languageSwitcher');
+ bottomSection.find('a').each(function(index) {
+ var element = $(this);
+ var languageSelected = element.attr('lang');
+ if (newLang == languageSelected) {
+ element.removeClass('selected_language');
+ element.addClass('selected_language');
+ } else {
+ element.removeClass('selected_language');
+ }
+ });
}
-
- $('#content').load("./tabs/landing.html", function () {
- // translate to user-selected language
- i18n.localizePage();
-
- GUI.content_ready(callback);
+ var bottomSection = $('.languageSwitcher');
+ bottomSection.html(' ');
+ bottomSection.append(' ');
+ var languagesAvailables = i18n.getLanguagesAvailables();
+ languagesAvailables.forEach(function(element) {
+ bottomSection.append(' ');
});
+ bottomSection.find('a').each(function(index) {
+ var element = $(this);
+ element.click(function(){
+ var element = $(this);
+ var languageSelected = element.attr('lang');
+ if (!languageSelected) { return; }
+ if (i18n.selectedLanguage != languageSelected) {
+ i18n.changeLanguage(languageSelected);
+ showLang(languageSelected);
+ }
+ });
+ });
+ showLang(i18n.selectedLanguage);
+ // translate to user-selected language
+ i18n.localizePage();
+
+ GUI.content_ready(callback);
+ });
};
diff --git a/src/main.html b/src/main.html
index 4e27c536..24420676 100644
--- a/src/main.html
+++ b/src/main.html
@@ -292,8 +292,8 @@