internationalization
Advanced tools
Comparing version 0.0.7 to 0.1.0
234
index.js
// Generated by CoffeeScript 1.8.0 | ||
(function() { | ||
var Template, bundleAsJavascript, debug, findBestMatch, fs, getAcceptLanguage, javascript, langs, load, loadSingle, middleware, parse, path, setLangs, templateCache, translate, translatePlural, | ||
__slice = [].slice; | ||
var angular, client, server; | ||
fs = require('fs'); | ||
server = require("./server"); | ||
path = require('path'); | ||
client = require("./client"); | ||
Template = require('./template'); | ||
angular = require("./angular"); | ||
parse = require('miff').parse; | ||
debug = require('debug')('i18n'); | ||
langs = {}; | ||
templateCache = {}; | ||
loadSingle = function(directory) { | ||
var content, e, file, pth, ret, _i, _len, _ref; | ||
ret = {}; | ||
_ref = fs.readdirSync(directory); | ||
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||
file = _ref[_i]; | ||
pth = path.join(directory, file); | ||
content = fs.readFileSync(pth, { | ||
encoding: 'utf8' | ||
}); | ||
try { | ||
parse(content, { | ||
equal: true, | ||
section: true, | ||
quote: true | ||
}, ret); | ||
} catch (_error) { | ||
e = _error; | ||
throw new Error("" + e.message + " in " + pth); | ||
} | ||
} | ||
return ret; | ||
}; | ||
load = function(directory) { | ||
var subdir, _i, _len, _ref; | ||
_ref = fs.readdirSync(directory); | ||
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||
subdir = _ref[_i]; | ||
langs[subdir] = loadSingle(path.join(directory, subdir)); | ||
} | ||
return langs; | ||
}; | ||
setLangs = function(dict) { | ||
return langs = dict; | ||
}; | ||
getAcceptLanguage = function(header) { | ||
return header.split(',').map(function(item) { | ||
var lang, q, _ref; | ||
_ref = item.split(';'), lang = _ref[0], q = _ref[1]; | ||
return lang.trim(); | ||
}); | ||
}; | ||
findBestMatch = function(accepts, available) { | ||
var lang, _i, _j, _len, _len1; | ||
for (_i = 0, _len = accepts.length; _i < _len; _i++) { | ||
lang = accepts[_i]; | ||
if (lang in available) { | ||
return lang; | ||
} | ||
} | ||
accepts = accepts.map(function(x) { | ||
return x.split('-'); | ||
}).filter(function(x) { | ||
return x.length > 1; | ||
}).map(function(_arg) { | ||
var x; | ||
x = _arg[0]; | ||
return x; | ||
}); | ||
for (_j = 0, _len1 = accepts.length; _j < _len1; _j++) { | ||
lang = accepts[_j]; | ||
if (lang in available) { | ||
return lang; | ||
} | ||
} | ||
}; | ||
translate = function() { | ||
var idx, key, lang, template, vars, _ref, _ref1, _ref2, _ref3; | ||
lang = arguments[0], key = arguments[1], vars = 3 <= arguments.length ? __slice.call(arguments, 2) : []; | ||
idx = key.indexOf(":"); | ||
if (idx !== -1) { | ||
template = ((_ref = langs[lang]) != null ? (_ref1 = _ref[key.substr(0, idx)]) != null ? _ref1[key.substr(idx + 1)] : void 0 : void 0) || key.substr(idx + 1); | ||
} else { | ||
template = ((_ref2 = langs[lang]) != null ? _ref2[key] : void 0) || key; | ||
} | ||
if (!vars.length) { | ||
return template; | ||
} | ||
if (!(template in templateCache)) { | ||
templateCache[template] = new Template(template); | ||
} | ||
if ('object' === typeof vars[0]) { | ||
return templateCache[template].render(vars[0], vars.slice(1)); | ||
} else { | ||
return (_ref3 = templateCache[template]).render.apply(_ref3, [null].concat(__slice.call(vars))); | ||
} | ||
}; | ||
translatePlural = function(lang, key, keyPlural, count, vars) { | ||
if (count === 1) { | ||
return translate(lang, key, vars, count); | ||
} else { | ||
return translate(lang, keyPlural, vars, count); | ||
} | ||
}; | ||
middleware = function(_arg) { | ||
var cookie, directory, env, fallback, _langs; | ||
cookie = _arg.cookie, directory = _arg.directory, fallback = _arg.fallback; | ||
if (!directory) { | ||
throw new Error("you hava to specify directory for locales"); | ||
} | ||
env = process.env.NODE_ENV || "development"; | ||
if (cookie == null) { | ||
cookie = 'lang'; | ||
} | ||
load(directory); | ||
_langs = Object.keys(langs); | ||
if (_langs.length) { | ||
debug("languages loaded: " + _langs); | ||
} else { | ||
debug("no languages loaded"); | ||
} | ||
_langs = void 0; | ||
return function(req, res, next) { | ||
var lang; | ||
if (env === "development") { | ||
load(directory); | ||
} | ||
if (req.query['set-lang']) { | ||
lang = req.query['set-lang']; | ||
debug("language preference from query " + lang); | ||
} else { | ||
lang = req.cookies[cookie]; | ||
if (lang) { | ||
debug("language preference from cookie " + lang); | ||
} | ||
} | ||
if (!(lang in langs)) { | ||
debug("accept language " + req.headers['accept-language']); | ||
lang = req.headers['accept-language'] ? findBestMatch(getAcceptLanguage(req.headers['accept-language']), langs) : void 0; | ||
if (lang) { | ||
debug("accept language intepreted as " + lang); | ||
} else { | ||
lang = fallback; | ||
debug("fallback to " + lang); | ||
} | ||
} | ||
res.locals._ = function() { | ||
var args; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
return translate.apply(null, [lang].concat(__slice.call(args))); | ||
}; | ||
res.locals.__ = function() { | ||
var args; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
return translatePlural.apply(null, [lang].concat(__slice.call(args))); | ||
}; | ||
res.locals.lang = lang; | ||
req.lang = lang; | ||
if (lang && req.cookies[cookie] !== lang) { | ||
res.cookie(cookie, lang, { | ||
maxAge: 31536000000 | ||
}); | ||
} | ||
debug("lang " + lang); | ||
return next(); | ||
}; | ||
}; | ||
bundleAsJavascript = function(resource, lang, exportAs) { | ||
return ";var " + exportAs + " = {};\n(function() {\n var dict = " + (JSON.stringify(resource)) + ";\n " + exportAs + ".translate = function(key, ns) {\n if (ns) {\n return dict[ns][key];\n } else {\n return dict[key];\n }\n };\n " + exportAs + ".resource = dict;\n " + exportAs + ".lang = '" + lang + "';\n})();"; | ||
}; | ||
javascript = function(_arg) { | ||
var cookie, directory, exportAs, path, pth, url; | ||
directory = _arg.directory, cookie = _arg.cookie, path = _arg.path, exportAs = _arg.exportAs; | ||
url = require('url'); | ||
pth = require('path'); | ||
if (path == null) { | ||
path = '/i18n.js'; | ||
} | ||
if (cookie == null) { | ||
cookie = 'lang'; | ||
} | ||
if (exportAs == null) { | ||
exportAs = 'i18n'; | ||
} | ||
return function(req, res, next) { | ||
var e, pathname, realpath; | ||
pathname = url.parse(req.url).pathname; | ||
if (req.method === 'GET' && pathname === path) { | ||
realpath = pth.join(directory, pathname.substr(1)); | ||
try { | ||
return res.end(bundleAsJavascript(langs[req.cookies[cookie]], req.cookies[cookie], exportAs)); | ||
} catch (_error) { | ||
e = _error; | ||
debug(e); | ||
res.write(';console.error(' + JSON.stringify(e.toString()) + ');'); | ||
return res.end(); | ||
} | ||
} else { | ||
return next(); | ||
} | ||
}; | ||
}; | ||
module.exports = { | ||
load: load, | ||
middleware: middleware, | ||
javascript: javascript, | ||
translate: translate, | ||
translatePlural: translatePlural, | ||
getAcceptLanguage: getAcceptLanguage, | ||
findBestMatch: findBestMatch, | ||
bundleAsJavascript: bundleAsJavascript, | ||
setLangs: setLangs | ||
Translator: require("./translator").Translator, | ||
middleware: server.middleware, | ||
javascript: client.middleware, | ||
bundleAsJavascript: client.bundle, | ||
angular: angular.middleware, | ||
bundleAsAngularModule: angular.bundle | ||
}; | ||
}).call(this); |
{ | ||
"name": "internationalization", | ||
"version": "0.0.7", | ||
"version": "0.1.0", | ||
"description": "i18n", | ||
"main": "index.js", | ||
"devDependencies": { | ||
"chai": "^1.9.2" | ||
"chai": "^1.9.2", | ||
"coffee-script": "^1.8.0", | ||
"mocha": "^2.0.1" | ||
}, | ||
"scripts": { | ||
"test": "mocha --compilers coffee:coffee-script/register test.coffee" | ||
"test": "./node_modules/mocha/bin/mocha --compilers coffee:coffee-script/register test.coffee" | ||
}, | ||
@@ -16,3 +18,3 @@ "author": "zf", | ||
"debug": "^2.1.0", | ||
"miff": "0.0.3" | ||
"miff": "0.1.0" | ||
}, | ||
@@ -19,0 +21,0 @@ "repository": { |
# internationalization | ||
[![NPM Version][npm-image]][npm-url] | ||
[![Build Status][travis-image]][travis-url] | ||
## setup | ||
@@ -9,3 +12,5 @@ | ||
app.use(i18n.middleware({ | ||
directory: path.join(__dirname, 'locales') | ||
directory: path.join(__dirname, 'locales'), | ||
fallback: "en", | ||
cookie: "lang" // default is "lang" | ||
})); | ||
@@ -52,2 +57,62 @@ ``` | ||
## client side | ||
```javascript | ||
app.use(i18n.javascript({ | ||
directory: path.join(__dirname, 'locales'), | ||
})); | ||
``` | ||
include in html | ||
```html | ||
<script src="/i18n.js"></script> | ||
<script> | ||
console.log(i18n.resource); | ||
console.log(i18n.lang); | ||
</script> | ||
``` | ||
more options: | ||
* `cookie` default `lang` | ||
* `path` default `/i18n.js` | ||
* `exportAs` default `i18n` | ||
### angularjs intergartion | ||
```javascript | ||
app.use(i18n.angular({ | ||
directory: path.join(__dirname, 'locales'), | ||
module: 'i18n', | ||
service: 'i18n', | ||
filter: 'translate' | ||
})); | ||
``` | ||
include in html | ||
```html | ||
<script src="/path/to/angularjs.js"></script> | ||
<script src="/i18n.js"></script> | ||
``` | ||
using filter | ||
```html | ||
<p>{{'delete warning'| translate:resource.name}}</p> | ||
<button>{{'action:delete' | translate}}</button> | ||
``` | ||
using service | ||
```javascript | ||
app.directive("", function(i18n)-> | ||
return { | ||
link: function(){ | ||
console.log(i18n.translate("resource key", {name: "foo"})); | ||
} | ||
}; | ||
``` | ||
## generate/update language files(TBD) | ||
@@ -65,1 +130,6 @@ | ||
``` | ||
[npm-image]: https://img.shields.io/npm/v/internationalization.svg?style=flat | ||
[npm-url]: https://npmjs.org/package/internationalization | ||
[travis-image]: https://img.shields.io/travis/zweifisch/internationalization.svg?style=flat | ||
[travis-url]: https://travis-ci.org/zweifisch/internationalization |
@@ -83,4 +83,8 @@ // Generated by CoffeeScript 1.8.0 | ||
module.exports = Template; | ||
if (typeof module !== "undefined" && module !== null) { | ||
module.exports = Template; | ||
} else { | ||
return Template; | ||
} | ||
}).call(this); |
119
test.js
// Generated by CoffeeScript 1.8.0 | ||
(function() { | ||
var Template, chai, expect, findBestMatch, getAcceptLanguage, parse, _ref; | ||
var chai, expect; | ||
@@ -11,44 +11,5 @@ chai = require('chai'); | ||
parse = require('./parser').parse; | ||
Template = require('./template'); | ||
_ref = require('./index'), getAcceptLanguage = _ref.getAcceptLanguage, findBestMatch = _ref.findBestMatch; | ||
describe('parse', function() { | ||
it('should turn lines into a dict', function() { | ||
var source; | ||
source = "key=value"; | ||
return parse(source).should.deep.equal({ | ||
key: 'value' | ||
}); | ||
}); | ||
it('should allow white space in key and value', function() { | ||
var source; | ||
source = "key=value\nk e y= v a l"; | ||
return parse(source).should.deep.equal({ | ||
key: 'value', | ||
'k e y': 'v a l' | ||
}); | ||
}); | ||
it('should escape', function() { | ||
var source; | ||
source = "key\\==value="; | ||
return parse(source).should.deep.equal({ | ||
'key=': 'value=' | ||
}); | ||
}); | ||
it('should parse chinese', function() { | ||
var source; | ||
source = "key = 钥匙"; | ||
return parse(source).should.deep.equal({ | ||
'key': '钥匙' | ||
}); | ||
}); | ||
return it('should skip empty line', function() { | ||
return parse('').should.deep.equal({}); | ||
}); | ||
}); | ||
describe('template', function() { | ||
var Template; | ||
Template = require('./template'); | ||
it('should support named vars', function() { | ||
@@ -107,2 +68,4 @@ var t; | ||
describe('header parsing', function() { | ||
var getAcceptLanguage; | ||
getAcceptLanguage = require('./utils').getAcceptLanguage; | ||
return it('should parse header', function() { | ||
@@ -113,17 +76,65 @@ return getAcceptLanguage('da, en-gb;q=0.8, en;q=0.7').should.deep.equal(['da', 'en-gb', 'en']); | ||
describe('misc', function() { | ||
return it('should find best match', function() { | ||
findBestMatch(['da', 'en-gb', 'en'], { | ||
describe("translate", function() { | ||
var Translator; | ||
Translator = require("./translator").Translator; | ||
it("should find key under namespace", function() { | ||
var translator; | ||
translator = new Translator(); | ||
translator.langs = { | ||
en: { | ||
ns: { | ||
key: "value" | ||
} | ||
} | ||
}; | ||
translator.translate("en", "ns:key").should.equal("value"); | ||
translator = new Translator("."); | ||
translator.langs = { | ||
en: { | ||
ns: { | ||
key: "value" | ||
} | ||
} | ||
}; | ||
return translator.translate("en", "ns.key").should.equal("value"); | ||
}); | ||
it("should render template", function() { | ||
var translator; | ||
translator = new Translator(); | ||
translator.langs = { | ||
en: { | ||
positional: "second is {2}, first is {1}", | ||
named: "key is {key}, value is {value}", | ||
mixed: "value is {value}, first is {1}" | ||
} | ||
}; | ||
translator.translate("en", "positional", "first", "second").should.equal("second is second, first is first"); | ||
translator.translate("en", "named", { | ||
key: "key", | ||
value: "value" | ||
}).should.equal("key is key, value is value"); | ||
return translator.translate("en", "mixed", { | ||
value: "value" | ||
}, "first").should.equal("value is value, first is first"); | ||
}); | ||
return it("should find closest", function() { | ||
var translator; | ||
translator = new Translator(); | ||
translator.langs = { | ||
'da': true, | ||
'en-gb': true | ||
}).should.equal('da'); | ||
findBestMatch(['da', 'en-gb', 'en'], { | ||
}; | ||
translator["try"](['da', 'en-gb', 'en']).should.equal('da'); | ||
translator.langs = { | ||
'en-gb': true | ||
}).should.equal('en-gb'); | ||
findBestMatch(['da', 'en-gb', 'en'], { | ||
'en': true | ||
}).should.equal('en'); | ||
return findBestMatch(['da', 'en-gb'], { | ||
'en': true | ||
}).should.equal('en'); | ||
}; | ||
translator["try"](['da', 'en-gb', 'en']).should.equal('en-gb'); | ||
translator.langs = { | ||
en: true | ||
}; | ||
translator["try"](['da', 'en-gb', 'en']).should.equal('en'); | ||
translator.langs = { | ||
en: true | ||
}; | ||
return translator["try"](['da', 'en-gb']).should.equal('en'); | ||
}); | ||
@@ -130,0 +141,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
34529
20
574
133
3
4
1
+ Addedmiff@0.1.0(transitive)
- Removedmiff@0.0.3(transitive)
Updatedmiff@0.1.0