From 58324a11fffd80f59debb3f22d275983d4c94883 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 17 Sep 2015 18:20:10 +0800 Subject: [PATCH] feat: Support nested locale keys. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example: ```js { model: { user: { name: 'Real Name', createdAt: 'Joined At' } } } ``` ```js ctx.__('model.user.name'); ctx.__('model.user.createdAt'); ``` The before: ```js { 'model.user.name': 'Real Name', 'model.user.createdAt', 'Joined At' } ``` Benchmarks: ``` Deeps: 9 2 tests completed. direct read a key x 85,993,593 ops/sec ±1.89% (96 runs sampled) by nested x 4,203,837 ops/sec ±0.98% (93 runs sampled) ``` --- README.md | 2 +- benchmark/nested-value.js | 134 ++++++++++++++++++++++++++++++++++++++ index.js | 12 +++- test/index.test.js | 30 +++++++++ test/locales/zh-CN.js | 8 +++ test/locales/zh_TW.json | 10 ++- 6 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 benchmark/nested-value.js diff --git a/README.md b/README.md index 8d0bc95..7352959 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ koa-locales koa locales, i18n solution for koa: 1. All locales resources location on `options.dir`. -2. resources file supports: `*.js`, `*.json` and `*.properties` +2. resources file supports: `*.js`, `*.json` and `*.properties`, [examples](https://github.com/koajs/locales/tree/master/test/locales) 3. One api: `__(key[, value, ...])` 4. Auto detect request locale from `query`, `cookie` and `header: Accept-Language` diff --git a/benchmark/nested-value.js b/benchmark/nested-value.js new file mode 100644 index 0000000..8c25b27 --- /dev/null +++ b/benchmark/nested-value.js @@ -0,0 +1,134 @@ +var Benchmark = require('benchmark'); +var benchmarks = require('beautify-benchmark'); + +var suite = new Benchmark.Suite(); + +function getNestedValue(data, key) { + var keys = key.split('.'); + for (var i = 0; typeof data === 'object' && i < keys.length; i++) { + data = data[keys[i]]; + } + return data; +} + +var 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' + } + } + } + } +}; + +var fullKey = 'model.user.fields.a.b.c.d.e.f'; + +console.log('Deeps: ', fullKey.split('.').length); + +// console.log('getNestedValue:', getNestedValue(resource, fullKey)); + +suite + +.add('direct read a key', function() { + resource['model.user.foo.bar.aa']; +}) +.add('by nested', function() { + getNestedValue(resource, fullKey); +}) +.on('cycle', function(event) { + benchmarks.add(event.target); +}) +.on('complete', function done() { + benchmarks.log(); +}) +.run({ async: false }); diff --git a/index.js b/index.js index d1fde27..6e25853 100644 --- a/index.js +++ b/index.js @@ -103,7 +103,8 @@ module.exports = function (app, options) { var locale = this.__getLocale(); var resource = resources[locale] || {}; - var text = resource[key] || key; + + var text = resource[key] || getNestedValue(resource, key) || key; debug('%s: %j => %j', locale, key, text); if (!text) { return ''; @@ -209,4 +210,13 @@ module.exports = function (app, options) { // support zh_CN, en_US => zh-CN, en-US return locale.replace('_', '-').toLowerCase(); } + + // fetch nested key, example: model.user.fields.title + function getNestedValue(data, key) { + var keys = key.split('.'); + for (var i = 0; typeof data === 'object' && i < keys.length; i++) { + data = data[keys[i]]; + } + return data; + } }; diff --git a/test/index.test.js b/test/index.test.js index 86fa387..3a2a614 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -45,6 +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" }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -73,6 +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" }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -96,6 +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": "姓名" }) .expect('Set-Cookie', /^locale=zh\-cn; path=\/; expires=\w+/) .expect(200, done); @@ -118,6 +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" }) .expect('Set-Cookie', /^locale=de; path=\/; expires=\w+/) .expect(200, done); @@ -141,6 +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": "姓名" }) .expect('Set-Cookie', /^locale=zh\-cn; path=\/; expires=\w+/) .expect(200, done); @@ -163,6 +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" }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -188,6 +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": "姓名" }) .expect(function (res) { assert(!res.headers['set-cookie']); @@ -217,6 +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": "姓名" }) .expect('Set-Cookie', /^locale=zh\-cn; path=\/; expires=\w+/) .expect(200, done); @@ -238,6 +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": "姓名" }) .expect('Set-Cookie', /^locale=zh\-cn; path=\/; expires=\w+/) .expect(200, done); @@ -259,6 +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": "姓名" }) .expect('Set-Cookie', /^locale=zh\-cn; path=\/; expires=\w+/) .expect(200, done); @@ -282,6 +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" }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -305,6 +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" }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -330,6 +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": "姓名" }) .expect('Set-Cookie', /^locale=zh\-tw; path=\/; expires=\w+/) .expect(200, done); @@ -355,6 +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" }) .expect('Set-Cookie', /^locale=en\-us; path=\/; expires=\w+/) .expect(200, done); @@ -371,6 +399,8 @@ function createApp(options) { app.use(function* () { this.body = { email: this[fname]('Email'), + name: this[fname]('model.user.fields.name'), + gender: this[fname]('model.user.fields.gender'), hello: this[fname]('Hello %s, how are you today?', 'fengmk2'), message: this[fname]('Hello %s, how are you today? How was your %s.', 'fengmk2', 18), empty: this[fname](), diff --git a/test/locales/zh-CN.js b/test/locales/zh-CN.js index 4995a0c..1e88f0c 100644 --- a/test/locales/zh-CN.js +++ b/test/locales/zh-CN.js @@ -1,4 +1,12 @@ module.exports = { Email: '邮箱', 'Hello %s, how are you today?': '%s,今天过得如何?', + model: { + user: { + fields: { + name: '姓名', + gender: '性别' + } + } + } }; diff --git a/test/locales/zh_TW.json b/test/locales/zh_TW.json index 6bb200b..d3484f0 100644 --- a/test/locales/zh_TW.json +++ b/test/locales/zh_TW.json @@ -1,4 +1,12 @@ { "Email": "郵箱", - "Hello %s, how are you today?": "%s,今天過得如何?" + "Hello %s, how are you today?": "%s,今天過得如何?", + "model": { + "user": { + "fields": { + "name": "姓名", + "gender": "性別" + } + } + } }