From 7d41781fb4a0fd707b7035f56b36ffb6e04a4c4e Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 15 Apr 2015 20:59:39 +0100 Subject: [PATCH] Add initial red/i18n implementation --- Gruntfile.js | 3 +- editor/vendor/i18next/i18next.min.js | 5 ++ locales/en-US/messages.json | 37 ++++++++++++ package.json | 1 + red/i18n.js | 85 ++++++++++++++++++++++++++++ red/server.js | 52 +++++++++-------- test/red/i18n_spec.js | 0 7 files changed, 157 insertions(+), 26 deletions(-) create mode 100644 editor/vendor/i18next/i18next.min.js create mode 100644 locales/en-US/messages.json create mode 100644 red/i18n.js create mode 100644 test/red/i18n_spec.js diff --git a/Gruntfile.js b/Gruntfile.js index 522056a4b..a732a18b4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -128,7 +128,8 @@ module.exports = function(grunt) { "editor/vendor/jquery/js/jquery.ui.touch-punch.min.js", "editor/vendor/marked/marked.min.js", "editor/vendor/orion/built-editor.min.js", - "editor/vendor/d3/d3.v3.min.js" + "editor/vendor/d3/d3.v3.min.js", + "editor/vendor/i18next/i18next.min.js" ], "public/vendor/vendor.css": [ "editor/vendor/orion/built-editor.css" diff --git a/editor/vendor/i18next/i18next.min.js b/editor/vendor/i18next/i18next.min.js new file mode 100644 index 000000000..3ba215ddb --- /dev/null +++ b/editor/vendor/i18next/i18next.min.js @@ -0,0 +1,5 @@ +// i18next, v1.8.1 +// Copyright (c)2015 Jan Mühlemann (jamuhl). +// Distributed under MIT license +// http://i18next.com +!function(a){function b(a,b){if(!b||"function"==typeof b)return a;for(var c in b)a[c]=b[c];return a}function c(a,b){for(var d in b)d in a?c(a[d],b[d]):a[d]=b[d];return a}function d(a,b,c){var d,e=0,f=a.length,g=void 0===f||"[object Array]"!==Object.prototype.toString.apply(a)||"function"==typeof a;if(c)if(g){for(d in a)if(b.apply(a[d],c)===!1)break}else for(;f>e&&b.apply(a[e++],c)!==!1;);else if(g){for(d in a)if(b.call(a[d],d,a[d])===!1)break}else for(;f>e&&b.call(a[e],e,a[e++])!==!1;);return a}function e(a){return"string"==typeof a?a.replace(/[&<>"'\/]/g,function(a){return Q[a]}):a}function f(a){var b=function(a){if(window.XMLHttpRequest)return a(null,new XMLHttpRequest);if(window.ActiveXObject)try{return a(null,new ActiveXObject("Msxml2.XMLHTTP"))}catch(b){return a(null,new ActiveXObject("Microsoft.XMLHTTP"))}return a(new Error)},c=function(a){if("string"==typeof a)return a;var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(encodeURIComponent(c)+"="+encodeURIComponent(a[c]));return b.join("&")},d=function(a){a=a.replace(/\r\n/g,"\n");for(var b="",c=0;cd?b+=String.fromCharCode(d):d>127&&2048>d?(b+=String.fromCharCode(d>>6|192),b+=String.fromCharCode(63&d|128)):(b+=String.fromCharCode(d>>12|224),b+=String.fromCharCode(d>>6&63|128),b+=String.fromCharCode(63&d|128))}return b},e=function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";a=d(a);var c,e,f,g,h,i,j,k="",l=0;do c=a.charCodeAt(l++),e=a.charCodeAt(l++),f=a.charCodeAt(l++),g=c>>2,h=(3&c)<<4|e>>4,i=(15&e)<<2|f>>6,j=63&f,isNaN(e)?i=j=64:isNaN(f)&&(j=64),k+=b.charAt(g)+b.charAt(h)+b.charAt(i)+b.charAt(j),c=e=f="",g=h=i=j="";while(l1&&(d+=d.indexOf("?")>-1?"&"+k:"?"+k),e.jsonp){var l=document.getElementsByTagName("head")[0],m=document.createElement("script");return m.type="text/javascript",m.src=d,void l.appendChild(m)}}b(function(b,c){if(b)return h(b);c.open(a,d,e.async);for(var f in j)j.hasOwnProperty(f)&&c.setRequestHeader(f,j[f]);c.onreadystatechange=function(){if(4===c.readyState){var a=c.responseText||"";if(!h)return;h(c.status,{text:function(){return a},json:function(){try{return JSON.parse(a)}catch(b){return T.error("Can not parse JSON. URL: "+d),{}}}})}},c.send(i)})},h={authBasic:function(a,b){g.headers.Authorization="Basic "+e(a+":"+b)},connect:function(a,b,c){return g("CONNECT",a,b,c)},del:function(a,b,c){return g("DELETE",a,b,c)},get:function(a,b,c){return g("GET",a,b,c)},head:function(a,b,c){return g("HEAD",a,b,c)},headers:function(a){g.headers=a||{}},isAllowed:function(a,b,c){this.options(a,function(a,d){c(-1!==d.text().indexOf(b))})},options:function(a,b,c){return g("OPTIONS",a,b,c)},patch:function(a,b,c){return g("PATCH",a,b,c)},post:function(a,b,c){return g("POST",a,b,c)},put:function(a,b,c){return g("PUT",a,b,c)},trace:function(a,b,c){return g("TRACE",a,b,c)}},i=a.type?a.type.toLowerCase():"get";h[i](a.url,a,function(b,c){200===b||0===b&&c.text()?a.success(c.json(),b,null):a.error(c.text(),b,null)})}function g(a,b){"function"==typeof a&&(b=a,a={}),a=a||{},T.extend(P,a),delete P.fixLng,P.functions&&(delete P.functions,T.extend(T,a.functions)),"string"==typeof P.ns&&(P.ns={namespaces:[P.ns],defaultNs:P.ns}),"string"==typeof P.fallbackNS&&(P.fallbackNS=[P.fallbackNS]),("string"==typeof P.fallbackLng||"boolean"==typeof P.fallbackLng)&&(P.fallbackLng=[P.fallbackLng]),P.interpolationPrefixEscaped=T.regexEscape(P.interpolationPrefix),P.interpolationSuffixEscaped=T.regexEscape(P.interpolationSuffix),P.lng||(P.lng=T.detectLanguage()),M=T.toLanguages(P.lng),H=M[0],T.log("currentLng set to: "+H),P.useCookie&&T.cookie.read(P.cookieName)!==H&&T.cookie.create(P.cookieName,H,P.cookieExpirationTime,P.cookieDomain),P.detectLngFromLocalStorage&&"undefined"!=typeof document&&window.localStorage&&T.localStorage.setItem("i18next_lng",H);var c=B;a.fixLng&&(c=function(a,b){return b=b||{},b.lng=b.lng||c.lng,B(a,b)},c.lng=H),W.setCurrentLng(H),I&&P.setJqueryExt&&u();var d;if(I&&I.Deferred&&(d=I.Deferred()),!P.resStore){var e=T.toLanguages(P.lng);"string"==typeof P.preload&&(P.preload=[P.preload]);for(var f=0,g=P.preload.length;g>f;f++)for(var h=T.toLanguages(P.preload[f]),i=0,j=h.length;j>i;i++)e.indexOf(h[i])<0&&e.push(h[i]);return J.sync.load(e,P,function(a,e){K=e,N=!0,b&&b(c),d&&d.resolve(c)}),d?d.promise():void 0}return K=P.resStore,N=!0,b&&b(c),d&&d.resolve(c),d?d.promise():void 0}function h(a,b){"string"==typeof a&&(a=[a]);for(var c=0,d=a.length;d>c;c++)P.preload.indexOf(a[c])<0&&P.preload.push(a[c]);return g(b)}function i(a,b,c,d){"string"!=typeof b?(c=b,b=P.ns.defaultNs):P.ns.namespaces.indexOf(b)<0&&P.ns.namespaces.push(b),K[a]=K[a]||{},K[a][b]=K[a][b]||{},d?T.deepExtend(K[a][b],c):T.extend(K[a][b],c),P.useLocalStorage&&O._storeLocal(K)}function j(a,b){"string"!=typeof b&&(b=P.ns.defaultNs),K[a]=K[a]||{};var c=K[a][b]||{},d=!1;for(var e in c)c.hasOwnProperty(e)&&(d=!0);return d}function k(a,b){return"string"!=typeof b&&(b=P.ns.defaultNs),K[a]=K[a]||{},T.extend({},K[a][b])}function l(a,b){"string"!=typeof b&&(b=P.ns.defaultNs),K[a]=K[a]||{},K[a][b]={},P.useLocalStorage&&O._storeLocal(K)}function m(a,b,c,d){"string"!=typeof b?(resource=b,b=P.ns.defaultNs):P.ns.namespaces.indexOf(b)<0&&P.ns.namespaces.push(b),K[a]=K[a]||{},K[a][b]=K[a][b]||{};for(var e=c.split(P.keyseparator),f=0,g=K[a][b];e[f];)f==e.length-1?g[e[f]]=d:(null==g[e[f]]&&(g[e[f]]={}),g=g[e[f]]),f++;P.useLocalStorage&&O._storeLocal(K)}function n(a,b,c){"string"!=typeof b?(resource=b,b=P.ns.defaultNs):P.ns.namespaces.indexOf(b)<0&&P.ns.namespaces.push(b);for(var d in c)"string"==typeof c[d]&&m(a,b,d,c[d])}function o(a){P.ns.defaultNs=a}function p(a,b){q([a],b)}function q(a,b){var c={dynamicLoad:P.dynamicLoad,resGetPath:P.resGetPath,getAsync:P.getAsync,customLoad:P.customLoad,ns:{namespaces:a,defaultNs:""}},d=T.toLanguages(P.lng);"string"==typeof P.preload&&(P.preload=[P.preload]);for(var e=0,f=P.preload.length;f>e;e++)for(var g=T.toLanguages(P.preload[e]),h=0,i=g.length;i>h;h++)d.indexOf(g[h])<0&&d.push(g[h]);for(var j=[],k=0,l=d.length;l>k;k++){var m=!1,n=K[d[k]];if(n)for(var o=0,p=a.length;p>o;o++)n[a[o]]||(m=!0);else m=!0;m&&j.push(d[k])}j.length?J.sync._fetch(j,c,function(c,d){var e=a.length*j.length;T.each(a,function(a,c){P.ns.namespaces.indexOf(c)<0&&P.ns.namespaces.push(c),T.each(j,function(a,f){K[f]=K[f]||{},K[f][c]=d[f][c],e--,0===e&&b&&(P.useLocalStorage&&J.sync._storeLocal(K),b())})})}):b&&b()}function r(a,b,c){return"function"==typeof b?(c=b,b={}):b||(b={}),b.lng=a,g(b,c)}function s(){return H}function t(a){K={},r(H,a)}function u(){function a(a,b,c){if(0!==b.length){var d="text";if(0===b.indexOf("[")){var e=b.split("]");b=e[1],d=e[0].substr(1,e[0].length-1)}b.indexOf(";")===b.length-1&&(b=b.substr(0,b.length-2));var f;if("html"===d)f=P.defaultValueFromContent?I.extend({defaultValue:a.html()},c):c,a.html(I.t(b,f));else if("text"===d)f=P.defaultValueFromContent?I.extend({defaultValue:a.text()},c):c,a.text(I.t(b,f));else if("prepend"===d)f=P.defaultValueFromContent?I.extend({defaultValue:a.html()},c):c,a.prepend(I.t(b,f));else if("append"===d)f=P.defaultValueFromContent?I.extend({defaultValue:a.html()},c):c,a.append(I.t(b,f));else if(0===d.indexOf("data-")){var g=d.substr("data-".length);f=P.defaultValueFromContent?I.extend({defaultValue:a.data(g)},c):c;var h=I.t(b,f);a.data(g,h),a.attr(d,h)}else f=P.defaultValueFromContent?I.extend({defaultValue:a.attr(d)},c):c,a.attr(d,I.t(b,f))}}function b(b,c){var d=b.attr(P.selectorAttr);if(d||"undefined"==typeof d||d===!1||(d=b.text()||b.val()),d){var e=b,f=b.data("i18n-target");if(f&&(e=b.find(f)||b),c||P.useDataAttrOptions!==!0||(c=b.data("i18n-options")),c=c||{},d.indexOf(";")>=0){var g=d.split(";");I.each(g,function(b,d){""!==d&&a(e,d,c)})}else a(e,d,c);P.useDataAttrOptions===!0&&b.data("i18n-options",c)}}I.t=I.t||B,I.fn.i18n=function(a){return this.each(function(){b(I(this),a);var c=I(this).find("["+P.selectorAttr+"]");c.each(function(){b(I(this),a)})})}}function v(a,b,c,d){if(!a)return a;if(d=d||b,a.indexOf(d.interpolationPrefix||P.interpolationPrefix)<0)return a;var e=d.interpolationPrefix?T.regexEscape(d.interpolationPrefix):P.interpolationPrefixEscaped,f=d.interpolationSuffix?T.regexEscape(d.interpolationSuffix):P.interpolationSuffixEscaped,g="HTML"+f,h=b.replace&&"object"==typeof b.replace?b.replace:b;return T.each(h,function(b,h){var i=c?c+P.keyseparator+b:b;"object"==typeof h&&null!==h?a=v(a,h,i,d):d.escapeInterpolation||P.escapeInterpolation?(a=a.replace(new RegExp([e,i,g].join(""),"g"),T.regexReplacementEscape(h)),a=a.replace(new RegExp([e,i,f].join(""),"g"),T.regexReplacementEscape(T.escape(h)))):a=a.replace(new RegExp([e,i,f].join(""),"g"),T.regexReplacementEscape(h))}),a}function w(a,b){var c=",",d="{",e="}",f=T.extend({},b);for(delete f.postProcess;-1!=a.indexOf(P.reusePrefix)&&(L++,!(L>P.maxRecursion));){var g=a.lastIndexOf(P.reusePrefix),h=a.indexOf(P.reuseSuffix,g)+P.reuseSuffix.length,i=a.substring(g,h),j=i.replace(P.reusePrefix,"").replace(P.reuseSuffix,"");if(g>=h)return T.error("there is an missing closing in following translation value",a),"";if(-1!=j.indexOf(c)){var k=j.indexOf(c);if(-1!=j.indexOf(d,k)&&-1!=j.indexOf(e,k)){var l=j.indexOf(d,k),m=j.indexOf(e,l)+e.length;try{f=T.extend(f,JSON.parse(j.substring(l,m))),j=j.substring(0,k)}catch(n){}}}var o=E(j,f);a=a.replace(i,T.regexReplacementEscape(o))}return a}function x(a){return a.context&&("string"==typeof a.context||"number"==typeof a.context)}function y(a){return void 0!==a.count&&"string"!=typeof a.count}function z(a){return void 0!==a.indefinite_article&&"string"!=typeof a.indefinite_article&&a.indefinite_article}function A(a,b){b=b||{};var c=C(a,b),d=F(a,b);return void 0!==d||d===c}function B(a,b){return b=b||{},N?(L=0,E.apply(null,arguments)):(T.log("i18next not finished initialization. you might have called t function before loading resources finished."),b.defaultValue||"")}function C(a,b){return void 0!==b.defaultValue?b.defaultValue:a}function D(){for(var a=[],b=1;b1)for(var d=0;d-1&&(e=c.split(P.nsseparator),i=e[0],c=e[1]),void 0===g&&P.sendMissing&&"function"==typeof P.missingKeyHandler&&(b.lng?P.missingKeyHandler(h[0],i,c,f,h):P.missingKeyHandler(P.lng,i,c,f,h));var j;j="string"==typeof P.postProcess&&""!==P.postProcess?[P.postProcess]:"array"==typeof P.postProcess||"object"==typeof P.postProcess?P.postProcess:[],"string"==typeof b.postProcess&&""!==b.postProcess?j=j.concat([b.postProcess]):("array"==typeof b.postProcess||"object"==typeof b.postProcess)&&(j=j.concat(b.postProcess)),void 0!==g&&j.length&&j.forEach(function(a){X[a]&&(g=X[a](g,c,b))});var k=f;if(f.indexOf(P.nsseparator)>-1&&(e=f.split(P.nsseparator),k=e[1]),k===c&&P.parseMissingKey&&(f=P.parseMissingKey(f)),void 0===g&&(f=v(f,b),f=w(f,b),j.length)){var l=C(c,b);j.forEach(function(a){X[a]&&(g=X[a](l,c,b))})}return void 0!==g?g:f}function F(a,b){b=b||{};var c,d,e=C(a,b),f=M;if(!K)return e;if("cimode"===f[0].toLowerCase())return e;if(b.lngs&&(f=b.lngs),b.lng&&(f=T.toLanguages(b.lng,b.fallbackLng),!K[f[0]])){var g=P.getAsync;P.getAsync=!1,J.sync.load(f,P,function(a,b){T.extend(K,b),P.getAsync=g})}var h=b.ns||P.ns.defaultNs;if(a.indexOf(P.nsseparator)>-1){var i=a.split(P.nsseparator);h=i[0],a=i[1]}if(x(b)){c=T.extend({},b),delete c.context,c.defaultValue=P.contextNotFound;var j=h+P.nsseparator+a+"_"+b.context;if(d=B(j,c),d!=P.contextNotFound)return v(d,{context:b.context})}if(y(b,f[0])){c=T.extend({lngs:[f[0]]},b),delete c.count,c._origLng=c._origLng||c.lng||f[0],delete c.lng,c.defaultValue=P.pluralNotFound;var k;if(W.needsPlural(f[0],b.count)){k=h+P.nsseparator+a+P.pluralSuffix;var l=W.get(f[0],b.count);l>=0?k=k+"_"+l:1===l&&(k=h+P.nsseparator+a)}else k=h+P.nsseparator+a;if(d=B(k,c),d!=P.pluralNotFound)return v(d,{count:b.count,interpolationPrefix:b.interpolationPrefix,interpolationSuffix:b.interpolationSuffix});if(!(f.length>1))return c.lng=c._origLng,delete c._origLng,d=B(h+P.nsseparator+a,c),v(d,{count:b.count,interpolationPrefix:b.interpolationPrefix,interpolationSuffix:b.interpolationSuffix});var m=f.slice();if(m.shift(),b=T.extend(b,{lngs:m}),b._origLng=c._origLng,delete b.lng,d=B(h+P.nsseparator+a,b),d!=P.pluralNotFound)return d}if(z(b)){var n=T.extend({},b);delete n.indefinite_article,n.defaultValue=P.indefiniteNotFound;var o=h+P.nsseparator+a+(b.count&&!y(b,f[0])||!b.count?P.indefiniteSuffix:"");if(d=B(o,n),d!=P.indefiniteNotFound)return d}for(var p,q=a.split(P.keyseparator),r=0,s=f.length;s>r&&void 0===p;r++){for(var t=f[r],u=0,A=K[t]&&K[t][h];q[u];)A=A&&A[q[u]],u++;if(void 0!==A){var D=Object.prototype.toString.apply(A);if("string"==typeof A)A=v(A,b),A=w(A,b);else if("[object Array]"!==D||P.returnObjectTrees||b.returnObjectTrees){if(null===A&&P.fallbackOnNull===!0)A=void 0;else if(null!==A)if(P.returnObjectTrees||b.returnObjectTrees){if("[object Number]"!==D&&"[object Function]"!==D&&"[object RegExp]"!==D){var G="[object Array]"===D?[]:{};T.each(A,function(c){G[c]=E(h+P.nsseparator+a+P.keyseparator+c,b)}),A=G}}else P.objectTreeKeyHandler&&"function"==typeof P.objectTreeKeyHandler?A=P.objectTreeKeyHandler(a,A,t,h,b):(A="key '"+h+":"+a+" ("+t+")' returned an object instead of string.",T.log(A))}else A=A.join("\n"),A=v(A,b),A=w(A,b);"string"==typeof A&&""===A.trim()&&P.fallbackOnEmpty===!0&&(A=void 0),p=A}}if(void 0===p&&!b.isFallbackLookup&&(P.fallbackToDefaultNS===!0||P.fallbackNS&&P.fallbackNS.length>0)){if(b.isFallbackLookup=!0,P.fallbackNS.length){for(var H=0,I=P.fallbackNS.length;I>H;H++)if(p=F(P.fallbackNS[H]+P.nsseparator+a,b),p||""===p&&P.fallbackOnEmpty===!1){var L=p.indexOf(P.nsseparator)>-1?p.split(P.nsseparator)[1]:p,N=e.indexOf(P.nsseparator)>-1?e.split(P.nsseparator)[1]:e;if(L!==N)break}}else p=F(a,b);b.isFallbackLookup=!1}return p}function G(){var a,b=P.lngWhitelist||[],c=[];if("undefined"!=typeof window&&!function(){for(var a=window.location.search.substring(1),b=a.split("&"),d=0;d0){var f=b[d].substring(0,e);f==P.detectLngQS&&c.push(b[d].substring(e+1))}}}(),P.useCookie&&"undefined"!=typeof document){var d=T.cookie.read(P.cookieName);d&&c.push(d)}if(P.detectLngFromLocalStorage&&"undefined"!=typeof window&&window.localStorage&&c.push(T.localStorage.getItem("i18next_lng")),"undefined"!=typeof navigator){if(navigator.languages)for(var e=0;e-1){var f=e.split("-");e=P.lowerCaseLng?f[0].toLowerCase()+"-"+f[1].toLowerCase():f[0].toLowerCase()+"-"+f[1].toUpperCase()}if(0===b.length||b.indexOf(e)>-1){a=e;break}}}(),a||(a=P.fallbackLng[0]),a}Array.prototype.indexOf||(Array.prototype.indexOf=function(a){"use strict";if(null==this)throw new TypeError;var b=Object(this),c=b.length>>>0;if(0===c)return-1;var d=0;if(arguments.length>0&&(d=Number(arguments[1]),d!=d?d=0:0!=d&&d!=1/0&&d!=-(1/0)&&(d=(d>0||-1)*Math.floor(Math.abs(d)))),d>=c)return-1;for(var e=d>=0?d:Math.max(c-Math.abs(d),0);c>e;e++)if(e in b&&b[e]===a)return e;return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(a){"use strict";if(null==this)throw new TypeError;var b=Object(this),c=b.length>>>0;if(0===c)return-1;var d=c;arguments.length>1&&(d=Number(arguments[1]),d!=d?d=0:0!=d&&d!=1/0&&d!=-(1/0)&&(d=(d>0||-1)*Math.floor(Math.abs(d))));for(var e=d>=0?Math.min(d,c-1):c-Math.abs(d);e>=0;e--)if(e in b&&b[e]===a)return e;return-1}),"function"!=typeof String.prototype.trim&&(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")});var H,I=a.jQuery||a.Zepto,J={},K={},L=0,M=[],N=!1,O={};"undefined"!=typeof module&&module.exports?module.exports=J:(I&&(I.i18n=I.i18n||J),a.i18n=a.i18n||J),O={load:function(a,b,c){b.useLocalStorage?O._loadLocal(a,b,function(d,e){for(var f=[],g=0,h=a.length;h>g;g++)e[a[g]]||f.push(a[g]);f.length>0?O._fetch(f,b,function(a,b){T.extend(e,b),O._storeLocal(b),c(null,e)}):c(null,e)}):O._fetch(a,b,function(a,b){c(null,b)})},_loadLocal:function(a,b,c){var d={},e=(new Date).getTime();if(window.localStorage){var f=a.length;T.each(a,function(a,g){var h=T.localStorage.getItem("res_"+g);h&&(h=JSON.parse(h),h.i18nStamp&&h.i18nStamp+b.localStorageExpirationTime>e&&(d[g]=h)),f--,0===f&&c(null,d)})}},_storeLocal:function(a){if(window.localStorage)for(var b in a)a[b].i18nStamp=(new Date).getTime(),T.localStorage.setItem("res_"+b,JSON.stringify(a[b]))},_fetch:function(a,b,c){var d=b.ns,e={};if(b.dynamicLoad){var f=function(a,b){c(null,b)};if("function"==typeof b.customLoad)b.customLoad(a,d.namespaces,b,f);else{var g=v(b.resGetPath,{lng:a.join("+"),ns:d.namespaces.join("+")});T.ajax({url:g,success:function(a){T.log("loaded: "+g),f(null,a)},error:function(a,b,c){T.log("failed loading: "+g),f("failed loading resource.json error: "+c)},dataType:"json",async:b.getAsync})}}else{var h,i=d.namespaces.length*a.length;T.each(d.namespaces,function(d,f){T.each(a,function(a,d){var g=function(a,b){a&&(h=h||[],h.push(a)),e[d]=e[d]||{},e[d][f]=b,i--,0===i&&c(h,e)};"function"==typeof b.customLoad?b.customLoad(d,f,b,g):O._fetchOne(d,f,b,g)})})}},_fetchOne:function(a,b,c,d){var e=v(c.resGetPath,{lng:a,ns:b});T.ajax({url:e,success:function(a){T.log("loaded: "+e),d(null,a)},error:function(a,b,c){if(b&&200==b||a&&a.status&&200==a.status)T.error("There is a typo in: "+e);else if(b&&404==b||a&&a.status&&404==a.status)T.log("Does not exist: "+e);else{var f=b?b:a&&a.status?a.status:null;T.log(f+" when loading "+e)}d(c,{})},dataType:"json",async:c.getAsync})},postMissing:function(a,b,c,d,e){var f={};f[c]=d;var g=[];if("fallback"===P.sendMissingTo&&P.fallbackLng[0]!==!1)for(var h=0;hh;h++)g.push({lng:e[h],url:v(P.resPostPath,{lng:e[h],ns:b})});for(var j=0,k=g.length;k>j;j++){var l=g[j];T.ajax({url:l.url,type:P.sendType,data:f,success:function(){T.log("posted missing key '"+c+"' to: "+l.url);for(var a=c.split("."),e=0,f=K[l.lng][b];a[e];)f=f[a[e]]=e===a.length-1?d:f[a[e]]||{},e++},error:function(){T.log("failed posting missing key '"+c+"' to: "+l.url)},dataType:"json",async:P.postAsync})}},reload:t};var P={lng:void 0,load:"all",preload:[],lowerCaseLng:!1,returnObjectTrees:!1,fallbackLng:["dev"],fallbackNS:[],detectLngQS:"setLng",detectLngFromLocalStorage:!1,ns:"translation",fallbackOnNull:!0,fallbackOnEmpty:!1,fallbackToDefaultNS:!1,nsseparator:":",keyseparator:".",selectorAttr:"data-i18n",debug:!1,resGetPath:"locales/__lng__/__ns__.json",resPostPath:"locales/add/__lng__/__ns__",getAsync:!0,postAsync:!0,resStore:void 0,useLocalStorage:!1,localStorageExpirationTime:6048e5,dynamicLoad:!1,sendMissing:!1,sendMissingTo:"fallback",sendType:"POST",interpolationPrefix:"__",interpolationSuffix:"__",defaultVariables:!1,reusePrefix:"$t(",reuseSuffix:")",pluralSuffix:"_plural",pluralNotFound:["plural_not_found",Math.random()].join(""),contextNotFound:["context_not_found",Math.random()].join(""),escapeInterpolation:!1,indefiniteSuffix:"_indefinite",indefiniteNotFound:["indefinite_not_found",Math.random()].join(""),setJqueryExt:!0,defaultValueFromContent:!0,useDataAttrOptions:!1,cookieExpirationTime:void 0,useCookie:!0,cookieName:"i18next",cookieDomain:void 0,objectTreeKeyHandler:void 0,postProcess:void 0,parseMissingKey:void 0,missingKeyHandler:O.postMissing,shortcutFunction:"sprintf"},Q={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},R={create:function(a,b,c,d){var e;if(c){var f=new Date;f.setTime(f.getTime()+60*c*1e3),e="; expires="+f.toGMTString()}else e="";d=d?"domain="+d+";":"",document.cookie=a+"="+b+e+";"+d+"path=/"},read:function(a){for(var b=a+"=",c=document.cookie.split(";"),d=0;d-1){var c=a.split("-");b=P.lowerCaseLng?c[0].toLowerCase()+"-"+c[1].toLowerCase():c[0].toLowerCase()+"-"+c[1].toUpperCase()}else b=P.lowerCaseLng?a.toLowerCase():a;return b}var c=this.log,d=[],e=P.lngWhitelist||!1,f=function(a){!e||e.indexOf(a)>-1?d.push(a):c("rejecting non-whitelisted language: "+a)};if("string"==typeof a&&a.indexOf("-")>-1){var g=a.split("-");"unspecific"!==P.load&&f(b(a)),"current"!==P.load&&f(b(g[this.getCountyIndexOfLng(a)]))}else f(b(a));for(var h=0;h1)},2:function(a){return Number(1!=a)},3:function(){return 0},4:function(a){return Number(a%10==1&&a%100!=11?0:a%10>=2&&4>=a%10&&(10>a%100||a%100>=20)?1:2)},5:function(a){return Number(0===a?0:1==a?1:2==a?2:a%100>=3&&10>=a%100?3:a%100>=11?4:5)},6:function(a){return Number(1==a?0:a>=2&&4>=a?1:2)},7:function(a){return Number(1==a?0:a%10>=2&&4>=a%10&&(10>a%100||a%100>=20)?1:2)},8:function(a){return Number(1==a?0:2==a?1:8!=a&&11!=a?2:3)},9:function(a){return Number(a>=2)},10:function(a){return Number(1==a?0:2==a?1:7>a?2:11>a?3:4)},11:function(a){return Number(1==a||11==a?0:2==a||12==a?1:a>2&&20>a?2:3)},12:function(a){return Number(a%10!=1||a%100==11)},13:function(a){return Number(0!==a)},14:function(a){return Number(1==a?0:2==a?1:3==a?2:3)},15:function(a){return Number(a%10==1&&a%100!=11?0:a%10>=2&&(10>a%100||a%100>=20)?1:2)},16:function(a){return Number(a%10==1&&a%100!=11?0:0!==a?1:2)},17:function(a){return Number(1==a||a%10==1?0:1)},18:function(a){return Number(1==a?1:2)},19:function(a){return Number(1==a?0:0===a||a%100>1&&11>a%100?1:a%100>10&&20>a%100?2:3)},20:function(a){return Number(1==a?0:0===a||a%100>0&&20>a%100?1:2)},21:function(a){return Number(a%100==1?1:a%100==2?2:a%100==3||a%100==4?3:0)}},W={rules:function(){var a,b={};for(a=U.length;a--;)b[U[a][0]]={name:U[a][1],numbers:U[a][2],plurals:V[U[a][3]]};return b}(),addRule:function(a,b){W.rules[a]=b},setCurrentLng:function(a){if(!W.currentRule||W.currentRule.lng!==a){var b=a.split("-");W.currentRule={lng:a,rule:W.rules[b[0]]}}},needsPlural:function(a,b){var c,d=a.split("-");return c=W.currentRule&&W.currentRule.lng===a?W.currentRule.rule:W.rules[d[T.getCountyIndexOfLng(a)]],c&&c.numbers.length<=1?!1:1!==this.get(a,b)},get:function(a,b){function c(b,c){var d;if(d=W.currentRule&&W.currentRule.lng===a?W.currentRule.rule:W.rules[b]){var e;e=d.plurals(d.noAbs?c:Math.abs(c));var f=d.numbers[e];return 2===d.numbers.length&&1===d.numbers[0]&&(2===f?f=-1:1===f&&(f=1)),f}return 1===c?"1":"-1"}var d=a.split("-");return c(d[T.getCountyIndexOfLng(a)],b)}},X={},Y=function(a,b){X[a]=b},Z=function(){function a(a){return Object.prototype.toString.call(a).slice(8,-1).toLowerCase()}function b(a,b){for(var c=[];b>0;c[--b]=a);return c.join("")}var c=function(){return c.cache.hasOwnProperty(arguments[0])||(c.cache[arguments[0]]=c.parse(arguments[0])),c.format.call(null,c.cache[arguments[0]],arguments)};return c.format=function(c,d){var e,f,g,h,i,j,k,l=1,m=c.length,n="",o=[];for(f=0;m>f;f++)if(n=a(c[f]),"string"===n)o.push(c[f]);else if("array"===n){if(h=c[f],h[2])for(e=d[l],g=0;g=0?"+"+e:e,j=h[4]?"0"==h[4]?"0":h[4].charAt(1):" ",k=h[6]-String(e).length,i=h[6]?b(j,k):"",o.push(h[5]?e+i:i+e)}return o.join("")},c.cache={},c.parse=function(a){for(var b=a,c=[],d=[],e=0;b;){if(null!==(c=/^[^\x25]+/.exec(b)))d.push(c[0]);else if(null!==(c=/^\x25{2}/.exec(b)))d.push("%");else{if(null===(c=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(b)))throw"[sprintf] huh?";if(c[2]){e|=1;var f=[],g=c[2],h=[];if(null===(h=/^([a-z_][a-z_\d]*)/i.exec(g)))throw"[sprintf] huh?";for(f.push(h[1]);""!==(g=g.substring(h[0].length));)if(null!==(h=/^\.([a-z_][a-z_\d]*)/i.exec(g)))f.push(h[1]);else{if(null===(h=/^\[(\d+)\]/.exec(g)))throw"[sprintf] huh?";f.push(h[1])}c[2]=f}else e|=2;if(3===e)throw"[sprintf] mixing positional and named placeholders is not (yet) supported";d.push(c)}b=b.substring(c[0].length)}return d},c}(),$=function(a,b){return b.unshift(a),Z.apply(null,b)};Y("sprintf",function(a,b,c){return c.sprintf?"[object Array]"===Object.prototype.toString.apply(c.sprintf)?$(a,c.sprintf):"object"==typeof c.sprintf?Z(a,c.sprintf):a:a}),J.init=g,J.setLng=r,J.preload=h,J.addResourceBundle=i,J.hasResourceBundle=j,J.getResourceBundle=k,J.addResource=m,J.addResources=n,J.removeResourceBundle=l,J.loadNamespace=p,J.loadNamespaces=q,J.setDefaultNamespace=o,J.t=B,J.translate=B,J.exists=A,J.detectLanguage=T.detectLanguage,J.pluralExtensions=W,J.sync=O,J.functions=T,J.lng=s,J.addPostProcessor=Y,J.applyReplacement=T.applyReplacement,J.options=P}("undefined"==typeof exports?window:exports); \ No newline at end of file diff --git a/locales/en-US/messages.json b/locales/en-US/messages.json new file mode 100644 index 000000000..5164883aa --- /dev/null +++ b/locales/en-US/messages.json @@ -0,0 +1,37 @@ +{ + "runtime": { + "welcome": "Welcome to Node-RED", + "version": "__component__ version: __version__", + "paths": { + "settings": "Settings file : __path__" + } + }, + + "nodes": { + "loading": "Loading palette nodes", + "errors": "Failed to register __count__ node type", + "errors_plural": "Failed to register __count__ node types", + "errors-help": "Run with -v for details", + "missing-modules": "Missing node modules:", + "removing-modules": "Removing modules from config", + "added-types": "Added node types:", + "removed-types": "Removed node types:", + "install": { + "invalid": "Invalid module name", + "installing": "Installing module: __name__", + "installed": "Installed module: __name__", + "install-failed": "Install failed", + "install-failed-long": "Installation of module __name__ failed:", + "install-failed-not-found": "$t(install-failed-long) module not found", + + "uninstalling": "Uninstalling module: __name__", + "uninstall-failed": "Uninstall failed", + "uninstall-failed-long": "Uninstall of module __name__ failed:", + "uninstalled": "Uninstalled module: __name__" + + + + } + } + +} diff --git a/package.json b/package.json index 9b1fa3eaf..e4c0a998b 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "passport-http-bearer":"1.0.1", "passport-oauth2-client-password":"0.1.2", "oauth2orize":"1.0.1", + "i18next":"1.7.10", "node-red-node-feedparser":"0.0.*", "node-red-node-email":"0.0.*", "node-red-node-twitter":"0.0.*" diff --git a/red/i18n.js b/red/i18n.js new file mode 100644 index 000000000..9bc5bfa53 --- /dev/null +++ b/red/i18n.js @@ -0,0 +1,85 @@ +/** + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var i18n = require("i18next"); +var when = require("when"); +var path = require("path"); +var fs = require("fs"); + +var resourceMap = { + "messages": path.resolve(__dirname+"/../locales") +} + + +function registerMessageCatalog(namespace,dir) { + return when.promise(function(resolve,reject) { + resourceMap[namespace] = dir; + i18n.loadNamespace(namespace,function() { + resolve(); + }); + }); +} + +var MessageFileLoader = { + fetchOne: function(lng, ns, callback) { + if (resourceMap[ns]) { + var file = path.join(resourceMap[ns],lng,"messages.json"); + fs.readFile(file,"utf8",function(err,content) { + if (err) { + callback(err); + } else { + try { + callback(null, JSON.parse(content.replace(/^\uFEFF/, ''))); + } catch(e) { + callback(e); + } + } + }); + } else { + callback(new Error("Unrecognised namespace")); + } + } + +} + +function init() { + return when.promise(function(resolve,reject) { + i18n.backend(MessageFileLoader); + i18n.init({ + ns: { + namespaces: ["messages"], + defaultNs: "messages" + }, + fallbackLng: ['en-US'] + },function() { + resolve(); + }); + }); +} + + +var obj = module.exports = { + init: init, + registerMessageCatalog: registerMessageCatalog +} + +obj['_'] = function() { + //var opts = {}; + //if (def) { + // opts.defaultValue = def; + //} + return i18n.t.apply(null,arguments); +} diff --git a/red/server.js b/red/server.js index 57a450ace..6fdb76524 100644 --- a/red/server.js +++ b/red/server.js @@ -24,6 +24,7 @@ var redNodes = require("./nodes"); var comms = require("./comms"); var storage = require("./storage"); var log = require("./log"); +var i18n = require("./i18n"); var app = null; var nodeApp = null; @@ -44,7 +45,8 @@ function init(_server,_settings) { } function start() { - return storage.init(settings) + return i18n.init() + .then(function() { return storage.init(settings)}) .then(function() { return settings.load(storage)}) .then(function() { if (settings.httpAdminRoot !== false) { @@ -56,12 +58,12 @@ function start() { reportMetrics(); }, settings.runtimeMetricInterval||15000); } - console.log("\n\nWelcome to Node-RED\n===================\n"); + console.log("\n\n"+i18n._("runtime.welcome")+"\n===================\n"); if (settings.version) { - log.info("Node-RED version: v"+settings.version); + log.info(i18n._("runtime.version",{component:"Node-RED",version:"v"+settings.version})); } - log.info("Node.js version: "+process.version); - log.info("Loading palette nodes"); + log.info(i18n._("runtime.version",{component:"Node.js ",version:process.version})); + log.info(i18n._("nodes.loading")); redNodes.init(settings,storage,app); return redNodes.load().then(function() { @@ -75,13 +77,13 @@ function start() { log.warn("["+nodeErrors[i].name+"] "+nodeErrors[i].err); } } else { - log.warn("Failed to register "+nodeErrors.length+" node type"+(nodeErrors.length==1?"":"s")); - log.warn("Run with -v for details"); + log.warn(i18n._("nodes.errors",{count:nodeErrors.length})); + log.warn(i18n._("nodes.errors-help")); } log.warn("------------------------------------------"); } if (nodeMissing.length > 0) { - log.warn("Missing node modules:"); + log.warn(i18n._("nodes.missing-modules")); var missingModules = {}; for (i=0;i 0) { - log.info("Added node types:"); + log.info(i18n._("nodes.added-types")); for (var i=0;i