Skip to content

Commit

Permalink
Allow editor language to be chosen in editor settings
Browse files Browse the repository at this point in the history
This gets stored in localStorage of the browser which is not
ideal. This is because we load language catalogs before we
load user preferences - so if this was stored in the runtime,
the editor wouldn't know the user's preference until it was
too late to apply it.

This is likely good enough for now - may need to do something
more convoluted later on.
  • Loading branch information
knolleary committed Apr 25, 2019
1 parent c2aa8a2 commit 493687b
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 22 deletions.
Expand Up @@ -25,8 +25,8 @@ var auth = require("../auth");
var nodes = require("../admin/nodes"); // TODO: move /icons into here
var needsPermission;
var runtimeAPI;
var log = require("@node-red/util").log; // TODO: separate module
var i18n = require("@node-red/util").i18n; // TODO: separate module
var log = require("@node-red/util").log;
var i18n = require("@node-red/util").i18n;

var apiUtil = require("../util");

Expand Down
Expand Up @@ -19,6 +19,8 @@ var sshkeys = require("./sshkeys");
var theme = require("./theme");
var clone = require("clone");

var i18n = require("@node-red/util").i18n

function extend(target, source) {
var keys = Object.keys(source);
var i = keys.length;
Expand Down Expand Up @@ -53,12 +55,14 @@ module.exports = {
user: req.user
}
runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) {
result.editorTheme = result.editorTheme||{};
var themeSettings = theme.settings();
if (themeSettings) {
// result.editorTheme may already exist with the palette
// disabled. Need to merge that into the receive settings
result.editorTheme = extend(clone(themeSettings),result.editorTheme||{});
result.editorTheme = extend(clone(themeSettings),result.editorTheme);
}
result.editorTheme.languages = i18n.availableLanguages("editor");
res.json(result);
});
},
Expand Down
Expand Up @@ -42,7 +42,9 @@
"defaultDir": "Default",
"ltr": "Left-to-right",
"rtl": "Right-to-left",
"auto": "Contextual"
"auto": "Contextual",
"language": "Language",
"browserDefault": "Browser default"
},
"sidebar": {
"show": "Show sidebar"
Expand Down
9 changes: 7 additions & 2 deletions packages/node_modules/@node-red/editor-client/src/js/i18n.js
Expand Up @@ -21,7 +21,8 @@ RED.i18n = (function() {
return {
init: function(options, done) {
apiRootUrl = options.apiRootUrl||"";
i18n.init({
var preferredLanguage = localStorage.getItem("editor-language");
var opts = {
resGetPath: apiRootUrl+'locales/__ns__?lng=__lng__',
dynamicLoad: false,
load:'current',
Expand All @@ -32,7 +33,11 @@ RED.i18n = (function() {
fallbackLng: ['en-US'],
useCookie: false,
returnObjectTrees: true
},function() {
};
if (preferredLanguage) {
opts.lng = preferredLanguage;
}
i18n.init(opts,function() {
done();
});
RED["_"] = function() {
Expand Down
Expand Up @@ -104,6 +104,10 @@ RED.userSettings = (function() {

var viewSettings = [
{
options: [
{setting:"editor-language",local: true, label:"menu.label.view.language",options:function(done){ done([{val:'',text:RED._('menu.label.view.browserDefault')}].concat(RED.settings.theme("languages"))) }},
]
},{
title: "menu.label.view.grid",
options: [
{setting:"view-show-grid",oldSetting:"menu-menu-item-view-show-grid",label:"menu.label.view.showGrid",toggle:true,onchange:"core:toggle-show-grid"},
Expand Down Expand Up @@ -136,14 +140,40 @@ RED.userSettings = (function() {
currentEditorSettings.view = currentEditorSettings.view || {};

viewSettings.forEach(function(section) {
$('<h3></h3>').text(RED._(section.title)).appendTo(pane);
if (section.title) {
$('<h3></h3>').text(RED._(section.title)).appendTo(pane);
}
section.options.forEach(function(opt) {
var initialState = currentEditorSettings.view[opt.setting];
var initialState;
if (opt.local) {
initialState = localStorage.getItem(opt.setting);
} else {
initialState = currentEditorSettings.view[opt.setting];
}
var row = $('<div class="user-settings-row"></div>').appendTo(pane);
var input;
if (opt.toggle) {
input = $('<label for="user-settings-'+opt.setting+'"><input id="user-settings-'+opt.setting+'" type="checkbox"> '+RED._(opt.label)+'</label>').appendTo(row).find("input");
input.prop('checked',initialState);
} else if (opt.options) {
$('<label for="user-settings-'+opt.setting+'">'+RED._(opt.label)+'</label>').appendTo(row);
var select = $('<select id="user-settings-'+opt.setting+'"></select>').appendTo(row);
if (typeof opt.options === 'function') {
opt.options(function(options) {
options.forEach(function(opt) {
var val = opt;
var text = opt;
if (typeof opt !== 'string') {
val = opt.val;
text = opt.text;
}
$('<option>').val(val).text(text).appendTo(select);
})
})
select.val(initialState)
} else {
// TODO: support other option types
}
} else {
$('<label for="user-settings-'+opt.setting+'">'+RED._(opt.label)+'</label>').appendTo(row);
$('<input id="user-settings-'+opt.setting+'" type="'+(opt.type||"text")+'">').appendTo(row).val(initialState);
Expand All @@ -155,16 +185,20 @@ RED.userSettings = (function() {

function setSelected(id, value) {
var opt = allSettings[id];
var currentEditorSettings = RED.settings.get('editor') || {};
currentEditorSettings.view = currentEditorSettings.view || {};
currentEditorSettings.view[opt.setting] = value;
RED.settings.set('editor', currentEditorSettings);
var callback = opt.onchange;
if (typeof callback === 'string') {
callback = RED.actions.get(callback);
}
if (callback) {
callback.call(opt,value);
if (opt.local) {
localStorage.setItem(opt.setting,value);
} else {
var currentEditorSettings = RED.settings.get('editor') || {};
currentEditorSettings.view = currentEditorSettings.view || {};
currentEditorSettings.view[opt.setting] = value;
RED.settings.set('editor', currentEditorSettings);
var callback = opt.onchange;
if (typeof callback === 'string') {
callback = RED.actions.get(callback);
}
if (callback) {
callback.call(opt,value);
}
}
}
function toggle(id) {
Expand Down Expand Up @@ -202,6 +236,10 @@ RED.userSettings = (function() {
var editorSettingsChanged = false;
viewSettings.forEach(function(section) {
section.options.forEach(function(opt) {
if (opt.local) {
allSettings[opt.setting] = opt;
return;
}
if (opt.oldSetting) {
var oldValue = RED.settings.get(opt.oldSetting);
if (oldValue !== undefined && oldValue !== null) {
Expand Down
32 changes: 28 additions & 4 deletions packages/node_modules/@node-red/util/lib/i18n.js
Expand Up @@ -50,10 +50,21 @@ function registerMessageCatalogs(catalogs) {
function registerMessageCatalog(namespace,dir,file) {
return initPromise.then(function() {
return new Promise((resolve,reject) => {
resourceMap[namespace] = { basedir:dir, file:file};
i18n.loadNamespaces(namespace,function() {
resolve();
});
resourceMap[namespace] = { basedir:dir, file:file, lngs: []};
fs.readdir(dir,function(err, files) {
if (err) {
resolve();
} else {
files.forEach(function(f) {
if (fs.existsSync(path.join(dir,f,file))) {
resourceMap[namespace].lngs.push(f);
}
});
i18n.loadNamespaces(namespace,function() {
resolve();
});
}
})
});
});
}
Expand Down Expand Up @@ -163,11 +174,24 @@ function getCatalog(namespace,lang) {
return result;
}

/**
* Gets a list of languages a given catalog is available in.
* @name availableLanguages
* @function
* @memberof @node-red/util_i18n
*/
function availableLanguages(namespace) {
if (resourceMap.hasOwnProperty(namespace)) {
return resourceMap[namespace].lngs
}
}

var obj = module.exports = {
init: init,
registerMessageCatalog: registerMessageCatalog,
registerMessageCatalogs: registerMessageCatalogs,
catalog: getCatalog,
availableLanguages: availableLanguages,
/**
* The underlying i18n library for when direct access is really needed
*/
Expand Down

0 comments on commit 493687b

Please sign in to comment.