i18n enable runtime node files

This commit is contained in:
Nick O'Leary 2015-04-25 23:29:53 +01:00
parent 7d41781fb4
commit 6d4c64fcd5
9 changed files with 196 additions and 32 deletions

View File

@ -1,5 +1,5 @@
/**
* Copyright 2013, 2014 IBM Corp.
* Copyright 2013, 2015 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,12 +32,12 @@ module.exports = function(RED) {
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
this.repeat = this.repeat * 1000;
if (RED.settings.verbose) { this.log("repeat = "+this.repeat); }
if (RED.settings.verbose) { this.log(RED._("inject.repeat",this)); }
this.interval_id = setInterval( function() {
node.emit("input",{});
}, this.repeat );
} else if (this.crontab) {
if (RED.settings.verbose) { this.log("crontab = "+this.crontab); }
if (RED.settings.verbose) { this.log(RED._("inject.crontab",this)); }
this.cronjob = new cron.CronJob(this.crontab,
function() {
node.emit("input",{});
@ -68,10 +68,10 @@ module.exports = function(RED) {
InjectNode.prototype.close = function() {
if (this.interval_id != null) {
clearInterval(this.interval_id);
if (RED.settings.verbose) { this.log("inject: repeat stopped"); }
if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
} else if (this.cronjob != null) {
this.cronjob.stop();
if (RED.settings.verbose) { this.log("inject: cronjob stopped"); }
if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
delete this.cronjob;
}
}
@ -84,7 +84,7 @@ module.exports = function(RED) {
res.send(200);
} catch(err) {
res.send(500);
node.error("Inject failed:"+err);
node.error(RED._("inject.failed",{error:err}));
}
} else {
res.send(404);

View File

@ -0,0 +1,8 @@
{
"inject": {
"repeat": "repeat = __repeat__",
"crontab": "crontab = __crontab__",
"stopped": "stopped",
"failed": "Inject failed: __error__"
}
}

View File

@ -19,15 +19,21 @@ var when = require("when");
var path = require("path");
var fs = require("fs");
var defaultLang = "en-US";
var resourceMap = {
"messages": path.resolve(__dirname+"/../locales")
"messages": {
basedir: path.resolve(__dirname+"/../locales"),
file:"messages.json"
}
}
var resourceCache = {}
function registerMessageCatalog(namespace,dir) {
function registerMessageCatalog(namespace,dir,file) {
return when.promise(function(resolve,reject) {
resourceMap[namespace] = dir;
resourceMap[namespace] = { basedir:dir, file:file};
i18n.loadNamespace(namespace,function() {
//console.log(namespace,dir);
resolve();
});
});
@ -36,13 +42,16 @@ function registerMessageCatalog(namespace,dir) {
var MessageFileLoader = {
fetchOne: function(lng, ns, callback) {
if (resourceMap[ns]) {
var file = path.join(resourceMap[ns],lng,"messages.json");
var file = path.join(resourceMap[ns].basedir,lng,resourceMap[ns].file);
fs.readFile(file,"utf8",function(err,content) {
if (err) {
callback(err);
} else {
try {
callback(null, JSON.parse(content.replace(/^\uFEFF/, '')));
//console.log(">>",ns,file);
resourceCache[ns] = resourceCache[ns]||{};
resourceCache[ns][lng] = JSON.parse(content.replace(/^\uFEFF/, ''));
callback(null, resourceCache[ns][lng]);
} catch(e) {
callback(e);
}
@ -70,10 +79,26 @@ function init() {
});
}
function getCatalog(namespace,lang) {
var result = null;
if (resourceCache.hasOwnProperty(namespace)) {
result = resourceCache[namespace][lang];
if (!result) {
var langParts = lang.split("-");
if (langParts.length == 2) {
result = getCatalog(namespace,langParts[0]);
}
}
}
return result;
}
var obj = module.exports = {
init: init,
registerMessageCatalog: registerMessageCatalog
registerMessageCatalog: registerMessageCatalog,
catalog: getCatalog,
i: i18n
}
obj['_'] = function() {
@ -81,5 +106,6 @@ obj['_'] = function() {
//if (def) {
// opts.defaultValue = def;
//}
//console.log(arguments);
return i18n.t.apply(null,arguments);
}

View File

@ -26,8 +26,8 @@ var settings;
function init(_settings) {
settings = _settings;
registry.init(settings);
loader.init(settings);
registry.init(settings,loader);
}
//TODO: defaultNodesDir/disableNodePathScan are to make testing easier.
// When the tests are componentized to match the new registry structure,

View File

@ -18,16 +18,26 @@ var when = require("when");
var fs = require("fs");
var path = require("path");
var events = require("../../events");
var localfilesystem = require("./localfilesystem");
var registry = require("./registry");
var RED;
var settings;
var i18n = require("../../i18n");
events.on("node-locales-dir", function(info) {
i18n.registerMessageCatalog(info.namespace,info.dir,info.file);
});
function init(_settings) {
settings = _settings;
localfilesystem.init(settings);
RED = require('../../red');
}
function load(defaultNodesDir,disableNodePathScan) {
@ -133,6 +143,7 @@ function loadNodeConfig(fileInfo) {
node.types = [];
node.err = err.toString();
}
resolve(node);
} else {
var types = [];
@ -143,8 +154,32 @@ function loadNodeConfig(fileInfo) {
types.push(match[2]);
}
node.types = types;
node.config = content;
var langRegExp = /^<script[^>]* data-lang=['"](.+?)['"]/i;
regExp = /(<script[^>]* data-help-name=[\s\S]*?<\/script>)/gi;
match = null;
var mainContent = "";
var helpContent = {};
var index = 0;
while((match = regExp.exec(content)) !== null) {
mainContent += content.substring(index,regExp.lastIndex-match[1].length);
index = regExp.lastIndex;
var help = content.substring(regExp.lastIndex-match[1].length,regExp.lastIndex);
var lang = "en-US";
if ((match = langRegExp.exec(help)) !== null) {
lang = match[1];
}
if (!helpContent.hasOwnProperty(lang)) {
helpContent[lang] = "";
}
helpContent[lang] += help;
}
mainContent += content.substring(index);
node.config = mainContent;
node.help = helpContent;
// TODO: parse out the javascript portion of the template
//node.script = "";
for (var i=0;i<node.types.length;i++) {
@ -153,13 +188,38 @@ function loadNodeConfig(fileInfo) {
break;
}
}
fs.stat(path.join(path.dirname(file),"locales"),function(err,stat) {
if (!err) {
node.namespace = node.id;
i18n.registerMessageCatalog(node.id,
path.join(path.dirname(file),"locales"),
path.basename(file,".js")+".json")
.then(function() {
resolve(node);
});
} else {
node.namespace = node.module;
resolve(node);
}
});
}
resolve(node);
});
});
}
//function getAPIForNode(node) {
// var red = {
// nodes: RED.nodes,
// library: RED.library,
// credentials: RED.credentials,
// events: RED.events,
// log: RED.log,
//
// }
//
//}
/**
* Loads the specified node into the runtime
@ -180,7 +240,20 @@ function loadNodeSet(node) {
var loadPromise = null;
var r = require(node.file);
if (typeof r === "function") {
var promise = r(require('../../red'));
var red = {};
for (var i in RED) {
if (RED.hasOwnProperty(i) && !/^(init|start|stop)$/.test(i)) {
var propDescriptor = Object.getOwnPropertyDescriptor(RED,i);
Object.defineProperty(red,i,propDescriptor);
}
}
red["_"] = function() {
var args = Array.prototype.slice.call(arguments, 0);
args[0] = node.namespace+":"+args[0];
return red.i18n._.apply(null,args);
}
var promise = r(red);
if (promise != null && typeof promise.then === "function") {
loadPromise = promise.then(function() {
node.enabled = true;
@ -269,10 +342,43 @@ function addFile(file) {
}
}
function loadNodeHelp(node,lang) {
var dir = path.dirname(node.template);
var base = path.basename(node.template);
var localePath = path.join(dir,"locales",lang,base);
try {
// TODO: make this async
var content = fs.readFileSync(localePath, "utf8")
return content;
} catch(err) {
return null;
}
}
function getNodeHelp(node,lang) {
if (!node.help[lang]) {
var help = loadNodeHelp(node,lang);
if (help == null) {
var langParts = lang.split("-");
if (langParts.length == 2) {
help = loadNodeHelp(node,langParts[0]);
}
}
if (help) {
node.help[lang] = help;
} else {
node.help[lang] = node.help["en-US"];
}
}
return node.help[lang];
}
module.exports = {
init: init,
load: load,
addModule: addModule,
addFile: addFile,
loadNodeSet: loadNodeSet
loadNodeSet: loadNodeSet,
getNodeHelp: getNodeHelp
}

View File

@ -87,7 +87,7 @@ function getLocalNodeFiles(dir) {
}
} else if (stats.isDirectory()) {
// Ignore /.dirs/, /lib/ /node_modules/
if (!/^(\..*|lib|icons|node_modules|test)$/.test(fn)) {
if (!/^(\..*|lib|icons|node_modules|test|locales)$/.test(fn)) {
result = result.concat(getLocalNodeFiles(path.join(dir,fn)));
} else if (fn === "icons") {
events.emit("node-icon-dir",path.join(dir,fn));
@ -196,6 +196,13 @@ function getNodeFiles(_defaultNodesDir,disableNodePathScan) {
var nodeFiles = getLocalNodeFiles(path.resolve(defaultNodesDir));
//console.log(nodeFiles);
var defaultLocalesPath = path.resolve(path.join(defaultNodesDir,"core","locales"));
events.emit("node-locales-dir", {
namespace:"node-red",
dir: defaultLocalesPath,
file: "messages.json"
});
if (settings.userDir) {
dir = path.join(settings.userDir,"nodes");
nodeFiles = nodeFiles.concat(getLocalNodeFiles(dir));

View File

@ -23,6 +23,8 @@ var settings;
var Node;
var loader;
var nodeConfigCache = null;
var moduleConfigs = {};
var nodeList = [];
@ -30,8 +32,9 @@ var nodeConstructors = {};
var nodeTypeToId = {};
var moduleNodes = {};
function init(_settings) {
function init(_settings,_loader) {
settings = _settings;
loader = _loader;
if (settings.available()) {
moduleConfigs = loadNodeConfigs();
} else {
@ -323,7 +326,7 @@ function registerNodeConstructor(type,constructor) {
events.emit("type-registered",type);
}
function getAllNodeConfigs() {
function getAllNodeConfigs(lang) {
if (!nodeConfigCache) {
var result = "";
var script = "";
@ -332,6 +335,7 @@ function getAllNodeConfigs() {
var config = moduleConfigs[getModule(id)].nodes[getNode(id)];
if (config.enabled && !config.err) {
result += config.config;
result += loader.getNodeHelp(config,lang||"en-US")
//script += config.script;
}
}
@ -345,7 +349,7 @@ function getAllNodeConfigs() {
return nodeConfigCache;
}
function getNodeConfig(id) {
function getNodeConfig(id,lang) {
var config = moduleConfigs[getModule(id)];
if (!config) {
return null;
@ -353,6 +357,8 @@ function getNodeConfig(id) {
config = config.nodes[getNode(id)];
if (config) {
var result = config.config;
result += loader.getNodeHelp(config,lang||"en-US")
//if (config.script) {
// result += '<script type="text/javascript">'+config.script+'</script>';
//}

View File

@ -20,6 +20,7 @@ var library = require("./api/library");
var comms = require("./comms");
var log = require("./log");
var util = require("./util");
var i18n = require("./i18n");
var fs = require("fs");
var settings = require("./settings");
var credentials = require("./nodes/credentials");
@ -56,6 +57,7 @@ var RED = {
credentials: credentials,
events: events,
log: log,
i18n: i18n,
comms: comms,
settings:settings,
util: util,
@ -75,4 +77,7 @@ var RED = {
get httpNode() { return server.nodeApp },
get server() { return server.server }
};
//RED["_"] = i18n._;
module.exports = RED;

View File

@ -160,14 +160,16 @@ describe('red/nodes/registry/index', function() {
list[0].should.have.property("enabled",true);
list[0].should.not.have.property("err");
eventEmitSpy.callCount.should.equal(2);
eventEmitSpy.callCount.should.equal(3);
eventEmitSpy.firstCall.args[0].should.be.equal("node-icon-dir");
eventEmitSpy.firstCall.args[1].should.be.equal(
resourcesDir + "NestedDirectoryNode" + path.sep + "NestedNode" + path.sep + "icons");
eventEmitSpy.secondCall.args[0].should.be.equal("type-registered");
eventEmitSpy.secondCall.args[1].should.be.equal("nested-node-1");
eventEmitSpy.secondCall.args[0].should.be.equal("node-locales-dir");
eventEmitSpy.thirdCall.args[0].should.be.equal("type-registered");
eventEmitSpy.thirdCall.args[1].should.be.equal("nested-node-1");
done();
}).catch(function(e) {
@ -284,11 +286,11 @@ describe('red/nodes/registry/index', function() {
var nodeConfigs = typeRegistry.getNodeConfigs();
// TODO: this is brittle...
nodeConfigs.should.equal("<script type=\"text/x-red\" data-template-name=\"test-node-1\"></script>\n<script type=\"text/x-red\" data-help-name=\"test-node-1\"></script>\n<script type=\"text/javascript\">RED.nodes.registerType('test-node-1',{});</script>\n<style></style>\n<p>this should be filtered out</p>\n<script type=\"text/x-red\" data-template-name=\"test-node-2\"></script>\n<script type=\"text/x-red\" data-help-name=\"test-node-2\"></script>\n<script type=\"text/javascript\">RED.nodes.registerType('test-node-2',{});</script>\n<style></style>\n");
nodeConfigs.should.equal("<script type=\"text/x-red\" data-template-name=\"test-node-1\"></script>\n\n<script type=\"text/javascript\">RED.nodes.registerType('test-node-1',{});</script>\n<style></style>\n<p>this should be filtered out</p>\n<script type=\"text/x-red\" data-help-name=\"test-node-1\"></script><script type=\"text/x-red\" data-template-name=\"test-node-2\"></script>\n\n<script type=\"text/javascript\">RED.nodes.registerType('test-node-2',{});</script>\n<style></style>\n<script type=\"text/x-red\" data-help-name=\"test-node-2\"></script>");
var nodeId = list[0].id;
var nodeConfig = typeRegistry.getNodeConfig(nodeId);
nodeConfig.should.equal("<script type=\"text/x-red\" data-template-name=\"test-node-1\"></script>\n<script type=\"text/x-red\" data-help-name=\"test-node-1\"></script>\n<script type=\"text/javascript\">RED.nodes.registerType('test-node-1',{});</script>\n<style></style>\n<p>this should be filtered out</p>\n");
nodeConfig.should.equal("<script type=\"text/x-red\" data-template-name=\"test-node-1\"></script>\n\n<script type=\"text/javascript\">RED.nodes.registerType('test-node-1',{});</script>\n<style></style>\n<p>this should be filtered out</p>\n<script type=\"text/x-red\" data-help-name=\"test-node-1\"></script>");
done();
}).catch(function(e) {
done(e);
@ -548,14 +550,18 @@ describe('red/nodes/registry/index', function() {
list[1].should.have.property("err");
eventEmitSpy.callCount.should.equal(2);
eventEmitSpy.callCount.should.equal(3);
eventEmitSpy.firstCall.args[0].should.be.equal("node-locales-dir");
eventEmitSpy.firstCall.args[0].should.be.equal("node-icon-dir");
eventEmitSpy.firstCall.args[1].should.be.equal(
eventEmitSpy.secondCall.args[0].should.be.equal("node-icon-dir");
eventEmitSpy.secondCall.args[1].should.be.equal(
resourcesDir + "TestNodeModule" + path.sep+ "node_modules" + path.sep + "TestNodeModule" + path.sep + "icons");
eventEmitSpy.secondCall.args[0].should.be.equal("type-registered");
eventEmitSpy.secondCall.args[1].should.be.equal("test-node-mod-1");
eventEmitSpy.thirdCall.args[0].should.be.equal("type-registered");
eventEmitSpy.thirdCall.args[1].should.be.equal("test-node-mod-1");
done();
}).catch(function(e) {