Skip to content

Commit

Permalink
feat(locale): support dynamic local
Browse files Browse the repository at this point in the history
Closes #6 #21
  • Loading branch information
kazupon committed Apr 15, 2016
1 parent d1c6843 commit 4d61e8d
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 5 deletions.
70 changes: 70 additions & 0 deletions src/asset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { warn, isPromise } from './util'

let locales = Object.create(null) // locales store


export default function (Vue) {
/**
* Register or retrieve a global locale definition.
*
* @param {String} id
* @param {Object | Function | Promise} definition
*/

Vue.locale = (id, definition) => {
if (definition === undefined) { // gettter
return locales[id]
} else { // setter
if (definition === null) {
locales[id] = undefined
delete locales[id]
} else {
setLocale(id, definition, (locale) => {
if (locale) {
locales[id] = locale
} else {
warn('failed set `' + id + '` locale')
}
})
}
}
}
}


function setLocale (id, definition, cb) {
if (typeof definition === 'object') { // sync
cb(definition)
} else {
let future = definition.call(this)
if (typeof future === 'function') {
if (future.resolved) {
// cached
cb(future.resolved)
} else if (future.requested) {
// pool callbacks
future.pendingCallbacks.push(cb)
} else {
future.requested = true
let cbs = future.pendingCallbacks = [cb]
future((locale) => { // resolve
future.resolved = locale
for (let i = 0, l = cbs.length; i < l; i++) {
cbs[i](locale)
}
}, () => { // reject
cb()
})
}
} else if (isPromise(future)) { // promise
future.then((locale) => { // resolve
cb(locale)
}, () => { // reject
cb()
}).catch((err) => {
console.error(err)
cb()
})
}
}
}
6 changes: 3 additions & 3 deletions src/extend.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import compare from './compare'
* extend
*
* @param {Vue} Vue
* @param {Object} locales
* @return {Vue}
*/

export default function (Vue, locales) {
export default function (Vue) {
const getPath = (Vue.version && compare('1.0.8', Vue.version) === -1)
? Vue.parsers.path.getPath
: Vue.parsers.path.get
Expand All @@ -19,7 +18,8 @@ export default function (Vue, locales) {
function getVal (key, lang, args) {
let value = key
try {
let val = getPath(locales[lang], key) || locales[lang][key]
let locale = Vue.locale(lang)
let val = getPath(locale, key) || locale[key]
value = (args ? format(val, args) : val) || key
} catch (e) {
value = key
Expand Down
17 changes: 15 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { warn } from './util'
import util, { warn, empty, each } from './util'
import Asset from './asset'
import Override from './override'
import Config from './config'
import Extend from './extend'
Expand All @@ -19,11 +20,15 @@ function plugin (Vue, opts = { lang: 'en', locales: {} }) {
return
}

util.Vue = Vue
setupLangVM(Vue, opts.lang)

Asset(Vue)
setupLocale(Vue, opts.locales)

Override(Vue, langVM)
Config(Vue, langVM)
Extend(Vue, opts.locales)
Extend(Vue)
}

function setupLangVM (Vue, lang) {
Expand All @@ -35,6 +40,14 @@ function setupLangVM (Vue, lang) {
Vue.config.silent = silent
}

function setupLocale (Vue, locales) {
if (!empty(locales)) {
each(locales, (locale, lang) => {
Vue.locale(lang, locale)
})
}
}

plugin.version = '2.4.1'

export default plugin
56 changes: 56 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,51 @@ export function warn (msg, err) {
}
}

/**
* empty
*
* @param {Array|Object} target
* @return {Boolean}
*/

export function empty (target) {
if (target === null || target === undefined) { return true }

if (Array.isArray(target)) {
if (target.length > 0) { return false }
if (target.length === 0) { return true }
} else if (exports.Vue.util.isPlainObject(target)) {
for (let key in target) {
if (exports.Vue.util.hasOwn(target, key)) { return false }
}
}

return true
}

/**
* each
*
* @param {Array|Object} target
* @param {Function} iterator
* @param {Object} [context]
*/

export function each (target, iterator, context) {
if (Array.isArray(target)) {
for (let i = 0; i < target.length; i++) {
iterator.call(context || target[i], target[i], i)
}
} else if (exports.Vue.util.isPlainObject(target)) {
const hasOwn = exports.Vue.util.hasOwn
for (let key in target) {
if (hasOwn(target, key)) {
iterator.call(context || target[key], target[key], key)
}
}
}
}

/**
* getWatcher
*
Expand Down Expand Up @@ -55,3 +100,14 @@ export function getDep (vm) {
}
return Dep
}

/**
* Forgiving check for a promise
*
* @param {Object} p
* @return {Boolean}
*/

export function isPromise (p) {
return p && typeof p.then === 'function'
}
61 changes: 61 additions & 0 deletions test/specs/asset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import assert from 'power-assert'
import Vue from 'vue'


describe('asset', () => {
const DELAY = 10

describe('sync', () => {
it('should be translated', () => {
Vue.locale('en', {
message: {
foo: 'foo'
}
})
assert(Vue.t('message.foo') === 'foo')
})
})


describe('async', () => {
describe('promise like function', () => {
beforeEach((done) => {
Vue.locale('en', () => {
return (resolve, reject) => {
setTimeout(() => {
resolve({ message: { bar: 'bar' } })
}, DELAY)
}
})
Vue.nextTick(done)
})

it('should be translated', (done) => {
setTimeout(() => {
assert(Vue.t('message.bar') === 'bar')
done()
}, DELAY + 5)
})
})

describe('promise ', () => {
beforeEach((done) => {
Vue.locale('en', () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ message: { buz: 'buz' } })
}, DELAY)
})
})
Vue.nextTick(done)
})

it('should be translated', (done) => {
setTimeout(() => {
assert(Vue.t('message.buz') === 'buz')
done()
}, DELAY + 5)
})
})
})
})
2 changes: 2 additions & 0 deletions test/specs/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Vue from 'vue'
import locales from './fixture/locales'
import plugin from '../../src/index'
import 'babel-polyfill'

require('./format')
require('./compare')
Expand All @@ -11,3 +12,4 @@ Vue.use(plugin, {
})

require('./i18n')
require('./asset')

0 comments on commit 4d61e8d

Please sign in to comment.