From 36bd1d5202481fbe57d63b1de01b09f1bb22cbde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=97=B2=E8=80=98=E2=84=A2?= Date: Sun, 20 Sep 2015 14:38:07 +0800 Subject: [PATCH] refact(es6): use es6 syntax. * fixed #10, But iojs-2 not support Object.assign, so use object-assign shim instead for now. * use es6 syntax. * use eslint to instead jshint. --- .jshintignore => .eslintignore | 0 .eslintrc | 190 ++++++++++++++++++++++++++++++ .jshintrc | 95 --------------- README.md | 6 +- benchmark/apply.js | 16 +-- benchmark/arguments-to-args.js | 16 +-- benchmark/ends-with.js | 8 +- benchmark/flattening.js | 203 +++++++++++++++++++++++++++++++++ benchmark/nested-value.js | 98 ++++++++-------- index.js | 147 +++++++++++++----------- package.json | 8 +- test/index.test.js | 78 ++++++------- test/locales/zh-CN.js | 10 +- test/other-locales/zh-CN.js | 6 +- 14 files changed, 602 insertions(+), 279 deletions(-) rename .jshintignore => .eslintignore (100%) create mode 100644 .eslintrc delete mode 100644 .jshintrc create mode 100644 benchmark/flattening.js diff --git a/.jshintignore b/.eslintignore similarity index 100% rename from .jshintignore rename to .eslintignore diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..8008362 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,190 @@ +{ + "ecmaFeatures": { + "jsx": true + }, + "parser": "espree", + "env": { + "amd": false, + "jasmine": false, + "node": true, + "mocha": true, + "browser": true, + "builtin": true, + "es6": true + }, + "rules": { + "no-alert": 2, + "no-array-constructor": 2, + "no-bitwise": 2, + "no-caller": 2, + "no-catch-shadow": 2, + "no-cond-assign": [2, "except-parens"], + "no-constant-condition": 2, + "no-continue": 0, + "no-control-regex": 2, + "no-debugger": 2, + "no-delete-var": 2, + "no-div-regex": 0, + "no-dupe-keys": 2, + "no-dupe-args": 2, + "no-duplicate-case": 2, + "no-else-return": 0, + "no-empty": 2, + "no-empty-character-class": 2, + "no-empty-label": 2, + "no-eq-null": 0, + "no-eval": 2, + "no-ex-assign": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": 2, + "no-extra-semi": 2, + "no-fallthrough": 2, + "no-floating-decimal": 0, + "no-func-assign": 2, + "no-implied-eval": 2, + "no-inline-comments": 0, + "no-inner-declarations": [2, "functions"], + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-iterator": 0, + "no-label-var": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-lonely-if": 0, + "no-loop-func": 2, + "no-mixed-requires": [0, false], + "no-mixed-spaces-and-tabs": [2, false], + "linebreak-style": [0, "unix"], + "no-multi-spaces": 2, + "no-multi-str": 2, + "no-multiple-empty-lines": [0, { + "max": 2 + }], + "no-native-reassign": 2, + "no-negated-in-lhs": 2, + "no-nested-ternary": 0, + "no-new": 0, + "no-new-func": 2, + "no-new-object": 2, + "no-new-require": 0, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-param-reassign": 0, + "no-path-concat": 0, + "no-plusplus": 0, + "no-process-env": 0, + "no-process-exit": 0, + "no-proto": 2, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-restricted-modules": 0, + "no-return-assign": 0, + "no-script-url": 2, + "no-self-compare": 0, + "no-sequences": 2, + "no-shadow": 0, + "no-shadow-restricted-names": 2, + "no-spaced-func": 2, + "no-sparse-arrays": 2, + "no-sync": 0, + "no-ternary": 0, + "no-trailing-spaces": 2, + "no-this-before-super": 0, + "no-throw-literal": 0, + "no-undef": 2, + "no-undef-init": 2, + "no-undefined": 0, + "no-unexpected-multiline": 0, + "no-underscore-dangle": 0, + "no-unneeded-ternary": 0, + "no-unreachable": 2, + "no-unused-expressions": 0, + "no-unused-vars": 2, + "no-use-before-define": [2, "nofunc"], + "no-void": 0, + "no-var": 2, + "no-const-assign": 2, + "prefer-const": 2, + "no-warning-comments": [0, { + "terms": ["todo", "fixme", "xxx"], + "location": "start" + }], + "no-with": 2, + "array-bracket-spacing": [0, "never"], + "accessor-pairs": 0, + "block-scoped-var": 0, + "brace-style": [0, "1tbs"], + "camelcase": 0, + "comma-dangle": [2, "always-multiline"], + "comma-spacing": 2, + "comma-style": 0, + "complexity": [0, 11], + "computed-property-spacing": [0, "never"], + "consistent-return": 0, + "consistent-this": [0, "that"], + "constructor-super": 0, + "curly": [2, "multi-line"], + "default-case": 0, + "dot-location": 0, + "dot-notation": 0, + "eol-last": 2, + "eqeqeq": 2, + "func-names": 0, + "func-style": [0, "declaration"], + "generator-star-spacing": 0, + "guard-for-in": 0, + "handle-callback-err": 0, + "indent": [2, 2], + "key-spacing": [2, { + "beforeColon": false, + "afterColon": true + }], + "lines-around-comment": 0, + "max-depth": [0, 4], + "max-len": [0, 80, 4], + "max-nested-callbacks": [0, 2], + "max-params": [0, 3], + "max-statements": [0, 10], + "new-cap": 0, + "new-parens": 2, + "newline-after-var": 0, + "object-curly-spacing": [0, "never"], + "object-shorthand": 0, + "one-var": 0, + "operator-assignment": [0, "always"], + "operator-linebreak": 0, + "padded-blocks": 0, + "quote-props": 0, + "quotes": [2, "single"], + "radix": 0, + "semi": 2, + "semi-spacing": [2, { + "before": false, + "after": true + }], + "sort-vars": 0, + "space-after-keywords": [0, "always"], + "space-before-blocks": [0, "always"], + "space-before-function-paren": [0, "always"], + "space-in-parens": [0, "never"], + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": [2, { + "words": true, + "nonwords": false + }], + "spaced-comment": 0, + "strict": [2, "global"], + "use-isnan": 2, + "valid-jsdoc": 0, + "valid-typeof": 0, + "vars-on-top": 0, + "wrap-iife": 0, + "wrap-regex": 0, + "yoda": [2, "never"] + } +} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index f86f3ed..0000000 --- a/.jshintrc +++ /dev/null @@ -1,95 +0,0 @@ -{ - // JSHint Default Configuration File (as on JSHint website) - // See http://jshint.com/docs/ for more details - - "maxerr" : 50, // {int} Maximum error before stopping - - // Enforcing - "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) - "camelcase" : false, // true: Identifiers must be in camelCase - "curly" : true, // true: Require {} for every new block or scope - "eqeqeq" : true, // true: Require triple equals (===) for comparison - "forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty() - "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` - "indent" : false, // {int} Number of spaces to use for indentation - "latedef" : false, // true: Require variables/functions to be defined before being used - "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` - "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` - "noempty" : true, // true: Prohibit use of empty blocks - "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) - "plusplus" : false, // true: Prohibit use of `++` & `--` - "quotmark" : false, // Quotation mark consistency: - // false : do nothing (default) - // true : ensure whatever is used is consistent - // "single" : require single quotes - // "double" : require double quotes - "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) - "unused" : true, // true: Require all defined variables be used - "strict" : true, // true: Requires all functions run in ES5 Strict Mode - "trailing" : false, // true: Prohibit trailing whitespaces - "maxparams" : false, // {int} Max number of formal params allowed per function - "maxdepth" : false, // {int} Max depth of nested blocks (within functions) - "maxstatements" : false, // {int} Max number statements per function - "maxcomplexity" : false, // {int} Max cyclomatic complexity per function - "maxlen" : false, // {int} Max number of characters per line - - // Relaxing - "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) - "boss" : true, // true: Tolerate assignments where comparisons would be expected - "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. - "eqnull" : false, // true: Tolerate use of `== null` - "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) - "esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`) - "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) - // (ex: `for each`, multiple try/catch, function expression…) - "evil" : false, // true: Tolerate use of `eval` and `new Function()` - "expr" : true, // true: Tolerate `ExpressionStatement` as Programs - "funcscope" : false, // true: Tolerate defining variables inside control statements" - "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') - "iterator" : false, // true: Tolerate using the `__iterator__` property - "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block - "laxbreak" : true, // true: Tolerate possibly unsafe line breakings - "laxcomma" : false, // true: Tolerate comma-first style coding - "loopfunc" : false, // true: Tolerate functions being defined in loops - "multistr" : true, // true: Tolerate multi-line strings - "proto" : false, // true: Tolerate using the `__proto__` property - "scripturl" : false, // true: Tolerate script-targeted URLs - "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment - "shadow" : true, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` - "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation - "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` - "validthis" : false, // true: Tolerate using this in a non-constructor function - - // Environments - "browser" : true, // Web Browser (window, document, etc) - "couch" : false, // CouchDB - "devel" : true, // Development/debugging (alert, confirm, etc) - "dojo" : false, // Dojo Toolkit - "jquery" : false, // jQuery - "mootools" : false, // MooTools - "node" : true, // Node.js - "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) - "prototypejs" : false, // Prototype and Scriptaculous - "rhino" : false, // Rhino - "worker" : false, // Web Workers - "wsh" : false, // Windows Scripting Host - "yui" : false, // Yahoo User Interface - "noyield" : true, // allow generators without a yield - - // Legacy - "nomen" : false, // true: Prohibit dangling `_` in variables - "onevar" : false, // true: Allow only one `var` statement per function - "passfail" : false, // true: Stop on first error - "white" : false, // true: Check against strict whitespace and indentation rules - - // Custom Globals - "globals" : { // additional predefined global variables - // mocha - "describe": true, - "it": true, - "before": true, - "afterEach": true, - "beforeEach": true, - "after": true - } -} diff --git a/README.md b/README.md index 75ce5fe..272812a 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Patch locales functions to koa app. - {Application} app: koa app instance. - {Object} options: optional params - {String} functionName: locale function name patch on koa context. Optional, default is `__`. - - {String} dir: locales resources store directory. Optional, default is `$PWD/locales`. + - {String} dirs: locales resources store directories. Optional, default is `['$PWD/locales']`. - {String} defaultLocale: default locale. Optional, default is `en-US`. - {String} queryField: locale field name on query. Optional, default is `locale`. - {String} cookieField: locale field name on cookie. Optional, default is `locale`. @@ -63,7 +63,7 @@ Patch locales functions to koa app. locales({ app: app, dirs: [__dirname + '/app/locales'], - defaultLocale: 'zh-CN' + defaultLocale: 'zh-CN', })); ``` @@ -74,7 +74,7 @@ Get current request locale text. ```js function* home() { this.body = { - message: this.__('Hello, %s', 'fengmk2') + message: this.__('Hello, %s', 'fengmk2'), }; } ``` diff --git a/benchmark/apply.js b/benchmark/apply.js index 0682a93..2b7b7c5 100644 --- a/benchmark/apply.js +++ b/benchmark/apply.js @@ -1,8 +1,10 @@ -var Benchmark = require('benchmark'); -var benchmarks = require('beautify-benchmark'); -var util = require('util'); +'use strict'; -var suite = new Benchmark.Suite(); +const Benchmark = require('benchmark'); +const benchmarks = require('beautify-benchmark'); +const util = require('util'); + +const suite = new Benchmark.Suite(); function normal(text) { if (arguments.length === 2) { @@ -17,13 +19,13 @@ function normal(text) { } function apply() { - var args = Array.prototype.slice.call(arguments); + const args = Array.prototype.slice.call(arguments); return util.format.apply(util, args); } function apply2() { - var args = new Array(arguments.length); - for (var i = 0, l = arguments.length; i < l; i++) { + const args = new Array(arguments.length); + for (let i = 0, l = arguments.length; i < l; i++) { args[i] = arguments[i]; } return util.format.apply(util, args); diff --git a/benchmark/arguments-to-args.js b/benchmark/arguments-to-args.js index c034414..4a37a91 100644 --- a/benchmark/arguments-to-args.js +++ b/benchmark/arguments-to-args.js @@ -1,7 +1,9 @@ -var Benchmark = require('benchmark'); -var benchmarks = require('beautify-benchmark'); +'use strict'; -var suite = new Benchmark.Suite(); +const Benchmark = require('benchmark'); +const benchmarks = require('beautify-benchmark'); + +const suite = new Benchmark.Suite(); function slice() { return Array.prototype.slice.call(arguments); @@ -12,8 +14,8 @@ function slice0() { } function forLoop() { - var args = new Array(arguments.length); - for(var i = 0; i < args.length; i++) { + const args = new Array(arguments.length); + for(let i = 0; i < args.length; i++) { args[i] = arguments[i]; } return args; @@ -31,7 +33,7 @@ suite .add('Array.prototype.slice.call(arguments, 0)', function() { slice0(0, 1, 2, 3, 4, 5, 6, 7); }) -.add('for(var i = 0; i < args.length; i++) {}', function() { +.add('for(let i = 0; i < args.length; i++) {}', function() { forLoop(0, 1, 2, 3, 4, 5, 6, 7); }) @@ -58,4 +60,4 @@ suite // // Array.prototype.slice.call(arguments) x 4,537,649 ops/sec ±1.18% (94 runs sampled) // Array.prototype.slice.call(arguments, 0) x 4,605,132 ops/sec ±0.87% (96 runs sampled) -// for(var i = 0; i < args.length; i++) {} x 30,435,436 ops/sec ±0.91% (93 runs sampled) +// for(let i = 0; i < args.length; i++) {} x 30,435,436 ops/sec ±0.91% (93 runs sampled) diff --git a/benchmark/ends-with.js b/benchmark/ends-with.js index d3ce6b7..d7e260f 100644 --- a/benchmark/ends-with.js +++ b/benchmark/ends-with.js @@ -1,7 +1,9 @@ -var Benchmark = require('benchmark'); -var benchmarks = require('beautify-benchmark'); +'use strict'; -var suite = new Benchmark.Suite(); +const Benchmark = require('benchmark'); +const benchmarks = require('beautify-benchmark'); + +const suite = new Benchmark.Suite(); function endsWith(str) { return str.endsWith('.properties'); diff --git a/benchmark/flattening.js b/benchmark/flattening.js new file mode 100644 index 0000000..1338c3c --- /dev/null +++ b/benchmark/flattening.js @@ -0,0 +1,203 @@ +'use strict'; + +const Benchmark = require('benchmark'); +const benchmarks = require('beautify-benchmark'); + +const suite = new Benchmark.Suite(); + +function isObject(obj) { + return Object.prototype.toString.call(obj) === '[object Object]'; +} + +function flattening(data) { + + const result = {}; + + function deepFlat (data, keys) { + Object.keys(data).forEach(function(key) { + const value = data[key]; + const k = keys ? key : keys + '.' + key; + if (!isObject(value)) { + return result[k] = String(value); + } + deepFlat(value, k); + }); + } + + deepFlat(data, ''); + + return result; +} + +function flattening_1(data) { + + const result = {}; + + function deepFlat (data, keys) { + Object.keys(data).forEach(function(key) { + const value = data[key]; + const k = keys.concat(key); + if (isObject(value)) { + deepFlat(value, k); + } else { + result[k.join('.')] = String(value); + } + }); + } + + deepFlat(data, []); + + return result; +} + +function flattening_2(data) { + + const result = {}; + + function deepFlat (data, flatKey, key) { + const value = data[key]; + if (isObject(value)) { + Object.keys(value).forEach(function(k) { + deepFlat(value, flatKey + '.' + k, k); + }); + } else { + result[flatKey] = String(value); + } + } + + Object.keys(data).forEach(function(key) { + deepFlat(data, key, key); + }); + return result; +} + +const resource = { + 'model.user.foo.bar.aa': 'Hello', + model: { + user: { + fields: { + name: 'Real Name', + age: 'Age', + a: { + b: { + c: { + d: { + e: { + f: 'fff', + }, + }, + model: { + user: { + fields: { + name: 'Real Name', + age: 'Age', + a: { + b: { + c: { + d: { + e: { + f: 'fff', + }, + }, + }, + }, + }, + }, + }, + post: { + fields: { + title: 'Subject', + }, + }, + }, + }, + }, + }, + }, + model: { + user: { + fields: { + name: 'Real Name', + age: 'Age', + a: { + b: { + c: { + d: { + e: { + f: 'fff', + }, + }, + }, + }, + }, + }, + }, + post: { + fields: { + title: 'Subject', + }, + }, + }, + }, + post: { + fields: { + title: 'Subject', + }, + }, + model: { + user: { + fields: { + name: 'Real Name', + age: 'Age', + a: { + b: { + c: { + d: { + e: { + f: 'fff', + }, + }, + }, + }, + }, + }, + }, + post: { + fields: { + title: 'Subject', + }, + }, + }, + }, +}; + +//console.log('flattening:', flattening(resource)); +//console.log('flattening_1:', flattening_1(resource)); +//console.log('flattening_2:', flattening_2(resource)); + +suite + +.add('flattening', function() { + flattening(resource); +}) +.add('flattening_1', function() { + flattening_1(resource); +}) +.add('flattening_2', function() { + flattening_2(resource); +}) +.on('cycle', function(event) { + benchmarks.add(event.target); +}) +.on('complete', function done() { + benchmarks.log(); +}) +.run({ async: false }); + +//$ node benchmark/flattening.js +// +// 3 tests completed. +// +// flattening x 32,863 ops/sec ±0.83% (98 runs sampled) +// flattening_1 x 10,434 ops/sec ±0.73% (96 runs sampled) +// flattening_2 x 21,734 ops/sec ±1.04% (95 runs sampled) diff --git a/benchmark/nested-value.js b/benchmark/nested-value.js index 8c25b27..26f3ca7 100644 --- a/benchmark/nested-value.js +++ b/benchmark/nested-value.js @@ -1,17 +1,19 @@ -var Benchmark = require('benchmark'); -var benchmarks = require('beautify-benchmark'); +'use strict'; -var suite = new Benchmark.Suite(); +const Benchmark = require('benchmark'); +const benchmarks = require('beautify-benchmark'); + +const suite = new Benchmark.Suite(); function getNestedValue(data, key) { - var keys = key.split('.'); - for (var i = 0; typeof data === 'object' && i < keys.length; i++) { + const keys = key.split('.'); + for (let i = 0; typeof data === 'object' && i < keys.length; i++) { data = data[keys[i]]; } return data; } -var resource = { +const resource = { 'model.user.foo.bar.aa': 'Hello', model: { user: { @@ -23,8 +25,8 @@ var resource = { c: { d: { e: { - f: "fff" - } + f: 'fff', + }, }, model: { user: { @@ -36,23 +38,23 @@ var resource = { c: { d: { e: { - f: "fff" - } - } - } - } - } - } + f: 'fff', + }, + }, + }, + }, + }, + }, }, post: { fields: { - title: 'Subject' - } - } - } - } - } - } + title: 'Subject', + }, + }, + }, + }, + }, + }, }, model: { user: { @@ -64,25 +66,25 @@ var resource = { c: { d: { e: { - f: "fff" - } - } - } - } - } - } + f: 'fff', + }, + }, + }, + }, + }, + }, }, post: { fields: { - title: 'Subject' - } - } - } + title: 'Subject', + }, + }, + }, }, post: { fields: { - title: 'Subject' - } + title: 'Subject', + }, }, model: { user: { @@ -94,24 +96,24 @@ var resource = { c: { d: { e: { - f: "fff" - } - } - } - } - } - } + f: 'fff', + }, + }, + }, + }, + }, + }, }, post: { fields: { - title: 'Subject' - } - } - } - } + title: 'Subject', + }, + }, + }, + }, }; -var fullKey = 'model.user.fields.a.b.c.d.e.f'; +const fullKey = 'model.user.fields.a.b.c.d.e.f'; console.log('Deeps: ', fullKey.split('.').length); diff --git a/index.js b/index.js index 05a0582..a8ff5b9 100644 --- a/index.js +++ b/index.js @@ -14,43 +14,56 @@ * Module dependencies. */ -var debug = require('debug')('koa-locales'); -var ini = require('ini'); -var util = require('util'); -var fs = require('fs'); -var path = require('path'); -var ms = require('humanize-ms'); -var merge = require('merge-descriptors'); +const debug = require('debug')('koa-locales'); +const ini = require('ini'); +const util = require('util'); +const fs = require('fs'); +const path = require('path'); +const ms = require('humanize-ms'); +const assign = require('object-assign'); + +const DEFAULT_OPTIONS = { + defaultLocale: 'en-US', + queryField: 'locale', + cookieField: 'locale', + cookieMaxAge: '1y', + dir: undefined, + dirs: [path.join(process.cwd(), 'locales')], + functionName: '__', +}; module.exports = function (app, options) { - options = options || {}; - var defaultLocale = formatLocale(options.defaultLocale || 'en-US'); - var queryField = options.queryField || 'locale'; - var cookieField = options.cookieField || 'locale'; - var cookieMaxAge = ms(options.cookieMaxAge || '1y'); - var localeDir = options.dir; - var localeDirs = options.dirs || [path.join(process.cwd(), 'locales')]; - var functionName = options.functionName || '__'; - var resources = {}; + options = assign({}, DEFAULT_OPTIONS, options); + const defaultLocale = formatLocale(options.defaultLocale); + const queryField = options.queryField; + const cookieField = options.cookieField; + const cookieMaxAge = ms(options.cookieMaxAge); + const localeDir = options.dir; + const localeDirs = options.dirs; + const functionName = options.functionName; + const resources = {}; + /** + * @Deprecated Use options.dirs instead. + */ if (localeDir && localeDirs.indexOf(localeDir) === -1) { localeDirs.push(localeDir); } - for (var i = 0; i < localeDirs.length; i ++) { - var dir = localeDirs[i]; + for (let i = 0; i < localeDirs.length; i++) { + const dir = localeDirs[i]; if (!fs.existsSync(dir)) { continue; } - var names = fs.readdirSync(dir); - for (var j = 0; j < names.length; j++) { - var name = names[j]; - var filepath = path.join(dir, name); + const names = fs.readdirSync(dir); + for (let j = 0; j < names.length; j++) { + const name = names[j]; + const filepath = path.join(dir, name); // support en_US.js => en-US.js - var locale = formatLocale(name.split('.')[0]); - var resource = {}; + const locale = formatLocale(name.split('.')[0]); + let resource = {}; if (name.endsWith('.js') || name.endsWith('.json')) { resource = flattening(require(filepath)); @@ -59,46 +72,22 @@ module.exports = function (app, options) { } resources[locale] = resources[locale] || {}; - merge(resources[locale], resource); + assign(resources[locale], resource); } } debug('init locales with %j, got %j resources', options, Object.keys(resources)); - var ARRAY_INDEX_RE = /\{(\d+)\}/g; - function formatWithArray(text, values) { - return text.replace(ARRAY_INDEX_RE, function (orignal, matched) { - var index = parseInt(matched); - if (index < values.length) { - return values[index]; - } - // not match index, return orignal text - return orignal; - }); - } - - var Object_INDEX_RE = /\{(.+?)\}/g; - function formatWithObject(text, values) { - return text.replace(Object_INDEX_RE, function (orignal, matched) { - var value = values[matched]; - if (value) { - return value; - } - // not match index, return orignal text - return orignal; - }); - } - app.context[functionName] = function (key, value) { if (arguments.length === 0) { // __() return ''; } - var locale = this.__getLocale(); - var resource = resources[locale] || {}; + const locale = this.__getLocale(); + const resource = resources[locale] || {}; - var text = resource[key] || key; + const text = resource[key] || key; debug('%s: %j => %j', locale, key, text); if (!text) { return ''; @@ -130,9 +119,9 @@ module.exports = function (app, options) { } // __(key, value1, ...) - var args = new Array(arguments.length); + const args = new Array(arguments.length); args[0] = text; - for(var i = 1; i < args.length; i++) { + for(let i = 1; i < args.length; i++) { args[i] = arguments[i]; } return util.format.apply(util, args); @@ -146,20 +135,20 @@ module.exports = function (app, options) { return this.__locale; } - var cookieLocale = this.cookies.get(cookieField); - var locale = this.query[queryField] || cookieLocale; + const cookieLocale = this.cookies.get(cookieField); + let locale = this.query[queryField] || cookieLocale; if (!locale) { // Accept-Language: zh-CN,zh;q=0.5 // Accept-Language: zh-CN - var languages = this.acceptsLanguages(); + let languages = this.acceptsLanguages(); if (languages) { if (Array.isArray(languages)) { if (languages[0] === '*') { languages = languages.slice(1); } if (languages.length > 0) { - for (var i = 0; i < languages.length; i++) { - var lang = formatLocale(languages[i]); + for (let i = 0; i < languages.length; i++) { + const lang = formatLocale(languages[i]); if (resources[lang]) { locale = lang; break; @@ -199,25 +188,49 @@ module.exports = function (app, options) { this.__locale = locale; return locale; }; - - function formatLocale(locale) { - // support zh_CN, en_US => zh-CN, en-US - return locale.replace('_', '-').toLowerCase(); - } }; function isObject(obj) { return Object.prototype.toString.call(obj) === '[object Object]'; } +const ARRAY_INDEX_RE = /\{(\d+)\}/g; +function formatWithArray(text, values) { + return text.replace(ARRAY_INDEX_RE, function (orignal, matched) { + const index = parseInt(matched); + if (index < values.length) { + return values[index]; + } + // not match index, return orignal text + return orignal; + }); +} + +const Object_INDEX_RE = /\{(.+?)\}/g; +function formatWithObject(text, values) { + return text.replace(Object_INDEX_RE, function (orignal, matched) { + const value = values[matched]; + if (value) { + return value; + } + // not match index, return orignal text + return orignal; + }); +} + +function formatLocale(locale) { + // support zh_CN, en_US => zh-CN, en-US + return locale.replace('_', '-').toLowerCase(); +} + function flattening(data) { - var result = {}; + const result = {}; function deepFlat (data, keys) { Object.keys(data).forEach(function(key) { - var value = data[key]; - var k = keys ? keys + '.' + key : key; + const value = data[key]; + const k = keys ? keys + '.' + key : key; if (isObject(value)) { deepFlat(value, k); } else { diff --git a/package.json b/package.json index 42039db..3bc97f6 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,9 @@ "index.js" ], "scripts": { - "test": "mocha --harmony --check-leaks -R spec -t 5000 test/*.test.js", + "test": "eslint . && mocha --harmony --check-leaks -R spec -t 5000 test/*.test.js", "test-cov": "node --harmony node_modules/.bin/istanbul cover node_modules/.bin/_mocha -- --check-leaks -t 5000 test/*.test.js", - "jshint": "jshint .", + "lint": "eslint .", "autod": "autod -w --prefix '~'", "cnpm": "npm install --registry=https://registry.npm.taobao.org", "contributors": "contributors -f plain -o AUTHORS" @@ -18,15 +18,15 @@ "debug": "~2.2.0", "humanize-ms": "~1.0.1", "ini": "~1.3.4", - "merge-descriptors": "~1.0.0" + "object-assign": "~4.0.1" }, "devDependencies": { "autod": "*", "beautify-benchmark": "0", "benchmark": "1", "contributors": "*", + "eslint": "~1.5.0", "istanbul-harmony": "*", - "jshint": "*", "koa": "1", "mm": "1", "mocha": "*", diff --git a/test/index.test.js b/test/index.test.js index 3a2a614..5937d92 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -14,19 +14,19 @@ * Module dependencies. */ -var assert = require('assert'); -var koa = require('koa'); -var request = require('supertest'); -var pedding = require('pedding'); -var mm = require('mm'); -var locales = require('../'); +const assert = require('assert'); +const koa = require('koa'); +const request = require('supertest'); +const pedding = require('pedding'); +const mm = require('mm'); +const locales = require('../'); describe('koa-locales.test.js', function () { afterEach(mm.restore); describe('default options', function () { - var app = createApp(); + const app = createApp(); it('should use default locale: en-US', function (done) { request(app.callback()) @@ -45,8 +45,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "model.user.fields.gender", - "name": "model.user.fields.name" + 'gender': 'model.user.fields.gender', + 'name': 'model.user.fields.name', }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -54,8 +54,8 @@ describe('koa-locales.test.js', function () { }); describe('custom options', function () { - var app = createApp({ - dirs: [__dirname + '/locales', __dirname + '/other-locales'] + const app = createApp({ + dirs: [__dirname + '/locales', __dirname + '/other-locales'], }); it('should use default locale: en-US', function (done) { @@ -75,8 +75,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "model.user.fields.gender", - "name": "model.user.fields.name" + 'gender': 'model.user.fields.gender', + 'name': 'model.user.fields.name', }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -100,8 +100,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "性别", - "name": "姓名" + 'gender': '性别', + 'name': '姓名', }) .expect('Set-Cookie', /^locale=zh\-cn; path=\/; expires=\w+/) .expect(200, done); @@ -124,8 +124,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "model.user.fields.gender", - "name": "model.user.fields.name" + 'gender': 'model.user.fields.gender', + 'name': 'model.user.fields.name', }) .expect('Set-Cookie', /^locale=de; path=\/; expires=\w+/) .expect(200, done); @@ -149,8 +149,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "性别", - "name": "姓名" + 'gender': '性别', + 'name': '姓名', }) .expect('Set-Cookie', /^locale=zh\-cn; path=\/; expires=\w+/) .expect(200, done); @@ -173,8 +173,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "model.user.fields.gender", - "name": "model.user.fields.name" + 'gender': 'model.user.fields.gender', + 'name': 'model.user.fields.name', }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -200,8 +200,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "性别", - "name": "姓名" + 'gender': '性别', + 'name': '姓名', }) .expect(function (res) { assert(!res.headers['set-cookie']); @@ -231,8 +231,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "性别", - "name": "姓名" + 'gender': '性别', + 'name': '姓名', }) .expect('Set-Cookie', /^locale=zh\-cn; path=\/; expires=\w+/) .expect(200, done); @@ -254,8 +254,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "性别", - "name": "姓名" + 'gender': '性别', + 'name': '姓名', }) .expect('Set-Cookie', /^locale=zh\-cn; path=\/; expires=\w+/) .expect(200, done); @@ -277,8 +277,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "性别", - "name": "姓名" + 'gender': '性别', + 'name': '姓名', }) .expect('Set-Cookie', /^locale=zh\-cn; path=\/; expires=\w+/) .expect(200, done); @@ -302,8 +302,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "model.user.fields.gender", - "name": "model.user.fields.name" + 'gender': 'model.user.fields.gender', + 'name': 'model.user.fields.name', }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -327,8 +327,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "model.user.fields.gender", - "name": "model.user.fields.name" + 'gender': 'model.user.fields.gender', + 'name': 'model.user.fields.name', }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -354,8 +354,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "性別", - "name": "姓名" + 'gender': '性別', + 'name': '姓名', }) .expect('Set-Cookie', /^locale=zh\-tw; path=\/; expires=\w+/) .expect(200, done); @@ -381,8 +381,8 @@ describe('koa-locales.test.js', function () { arguments6: '1 2 3 4 5. 6', values: 'foo bar foo bar {2} {100}', object: 'foo bar foo bar {z}', - "gender": "model.user.fields.gender", - "name": "model.user.fields.name" + 'gender': 'model.user.fields.gender', + 'name': 'model.user.fields.name', }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -392,9 +392,9 @@ describe('koa-locales.test.js', function () { }); function createApp(options) { - var app = koa(); + const app = koa(); locales(app, options); - var fname = options && options.functionName || '__'; + const fname = options && options.functionName || '__'; app.use(function* () { this.body = { diff --git a/test/locales/zh-CN.js b/test/locales/zh-CN.js index 1e88f0c..edf2843 100644 --- a/test/locales/zh-CN.js +++ b/test/locales/zh-CN.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = { Email: '邮箱', 'Hello %s, how are you today?': '%s,今天过得如何?', @@ -5,8 +7,8 @@ module.exports = { user: { fields: { name: '姓名', - gender: '性别' - } - } - } + gender: '性别', + }, + }, + }, }; diff --git a/test/other-locales/zh-CN.js b/test/other-locales/zh-CN.js index 61ca2c0..8522df8 100644 --- a/test/other-locales/zh-CN.js +++ b/test/other-locales/zh-CN.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = { - Email: '邮箱1' -} + Email: '邮箱1', +};