Skip to content

Commit

Permalink
⭐ new(number): add number localization
Browse files Browse the repository at this point in the history
  • Loading branch information
kazupon committed Apr 20, 2017
1 parent bb95130 commit 87ee7b3
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 4 deletions.
4 changes: 4 additions & 0 deletions src/extend.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ export default function extend (Vue: any): void {
const i18n = this.$i18n
return i18n.d(value, ...args)
}

Vue.prototype.$n = function (value: number, ...args: any): NumberFormatResult {
return this.$i18n.n(value, ...args)
}
}
90 changes: 86 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
isPlainObject,
isObject,
looseClone,
canUseDateTimeFormat
canUseDateTimeFormat,
canUseNumberFormat
} from './util'
import BaseFormatter from './format'
import getPathValue from './path'
Expand All @@ -32,12 +33,14 @@ export default class VueI18n {
_i18nWatcher: Function
_silentTranslationWarn: boolean
_dateTimeFormatters: Object
_numberFormatters: Object

constructor (options: I18nOptions = {}) {
const locale: Locale = options.locale || 'en-US'
const fallbackLocale: Locale = options.fallbackLocale || 'en-US'
const messages: LocaleMessages = options.messages || {}
const dateTimeFormats = options.dateTimeFormats || {}
const numberFormats = options.numberFormats || {}
this._vm = null
this._formatter = options.formatter || new BaseFormatter()
this._missing = options.missing || null
Expand All @@ -50,20 +53,28 @@ export default class VueI18n {
? false
: !!options.silentTranslationWarn
this._dateTimeFormatters = {}
this._numberFormatters = {}

this._exist = (message: Object, key: Path): boolean => {
if (!message || !key) { return false }
return !isNull(getPathValue(message, key))
}

this._initVM({ locale, fallbackLocale, messages, dateTimeFormats })
this._initVM({
locale,
fallbackLocale,
messages,
dateTimeFormats,
numberFormats
})
}

_initVM (data: {
locale: Locale,
fallbackLocale: Locale,
messages: LocaleMessages,
dateTimeFormats: DateTimeFormats
dateTimeFormats: DateTimeFormats,
numberFormats: NumberFormats
}): void {
const silent = Vue.config.silent
Vue.config.silent = true
Expand Down Expand Up @@ -109,6 +120,7 @@ export default class VueI18n {

get messages (): LocaleMessages { return looseClone(this._vm.messages) }
get dateTimeFormats (): DateTimeFormats { return looseClone(this._vm.dateTimeFormats) }
get numberFormats (): NumberFormats { return looseClone(this._vm.numberFormats) }

get locale (): Locale { return this._vm.locale }
set locale (locale: Locale): void {
Expand Down Expand Up @@ -342,10 +354,80 @@ export default class VueI18n {

return this._d(value, locale, key)
}

getNumberFormat (locale: Locale): NumberFormat {
return looseClone(this._vm.numberFormats[locale])
}

setNumberFormat (locale: Locale, format: NumberFormat): void {
this._vm.numberFormats[locale] = format
}

mergeNumberFormat (locale: Locale, format: NumberFormat): void {
this._vm.numberFormats[locale] = Vue.util.extend(this.getNumberFormat(locale), format)
}

_n (value: number, _locale: Locale, key: ?string): NumberFormatResult {
if (process.env.NODE_ENV !== 'production' && !VueI18n.availabilities.numberFormat) {
warn('Cannot format a Date value due to not support Intl.NumberFormat.')
return ''
}

let ret = ''
const numberFormats = this.numberFormats
if (key) {
let locale: Locale = _locale
if (isNull(numberFormats[_locale][key])) {
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
warn(`Fall back to the numberFormat of key '${key}' with '${this.fallbackLocale}' locale.`)
}
locale = this.fallbackLocale
}
const id = `${locale}__${key}`
let formatter = this._numberFormatters[id]
const format = numberFormats[locale][key]
if (!formatter) {
formatter = this._numberFormatters[id] = Intl.NumberFormat(locale, format)
}
ret = formatter.format(value)
} else {
ret = Intl.NumberFormat(_locale).format(value)
}

return ret
}

n (value: number, ...args: any): NumberFormatResult {
let locale: Locale = this.locale
let key: ?string = null

if (args.length === 1) {
if (typeof args[0] === 'string') {
key = args[0]
} else if (isObject(args[0])) {
if (args[0].locale) {
locale = args[0].locale
}
if (args[0].key) {
key = args[0].key
}
}
} else if (args.length === 2) {
if (typeof args[0] === 'string') {
key = args[0]
}
if (typeof args[1] === 'string') {
locale = args[1]
}
}

return this._n(value, locale, key)
}
}

VueI18n.availabilities = {
dateTimeFormat: canUseDateTimeFormat
dateTimeFormat: canUseDateTimeFormat,
numberFormat: canUseNumberFormat
}
VueI18n.install = install
VueI18n.version = '__VERSION__'
Expand Down
3 changes: 3 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,6 @@ export function looseClone (obj: Object): Object {

export const canUseDateTimeFormat: boolean =
typeof Intl !== 'undefined' && typeof Intl.DateTimeFormat !== 'undefined'

export const canUseNumberFormat: boolean =
typeof Intl !== 'undefined' && typeof Intl.NumberFormat !== 'undefined'
45 changes: 45 additions & 0 deletions test/unit/basic.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import messages from './fixture/index'
import dateTimeFormats from './fixture/datetime'
import numberFormats from './fixture/number'

describe('basic', () => {
let i18n
Expand Down Expand Up @@ -627,4 +628,48 @@ describe('basic', () => {
})
})
})

desc('i18n#n', () => {
let money
beforeEach(() => {
i18n = new VueI18n({
locale: 'en-US',
fallbackLocale: 'ja-JP',
numberFormats
})
money = 10100
})

describe('arguments nothing', () => {
it('should be formatted', () => {
assert.equal(i18n.n(money), '10,100')
})
})

describe('key argument', () => {
it('should be formatted', () => {
assert.equal(i18n.n(money, 'currency'), '$10,100.00')
})
})

describe('locale argument', () => {
describe('with second argument', () => {
it('should be formatted', () => {
assert.equal(i18n.n(money, 'currency', 'ja-JP'), '¥10,100')
})
})

describe('with object argument', () => {
it('should be formatted', () => {
assert.equal(i18n.n(money, { key: 'currency', locale: 'ja-JP' }), '¥10,100')
})
})
})

describe('fallback', () => {
it('should be formatted', () => {
assert.equal(i18n.n(0.9, 'percent'), '90%')
})
})
})
})
21 changes: 21 additions & 0 deletions test/unit/fixture/number.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default {
'en-US': {
currency: {
style: 'currency', currency: 'USD', currencyDisplay: 'symbol'
},
decimal: {
style: 'decimal', useGrouping: false
}
},
'ja-JP': {
currency: {
style: 'currency', currency: 'JPY', currencyDisplay: 'symbol'
},
numeric: {
style: 'decimal', useGrouping: false
},
percent: {
style: 'percent', useGrouping: false
}
}
}
50 changes: 50 additions & 0 deletions test/unit/number.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import numberFormats from './fixture/number'

const desc = VueI18n.availabilities.numberFormat ? describe : describe.skip
desc('number format', () => {
describe('getNumberFormat / setNumberFormat', () => {
it('should be worked', done => {
const i18n = new VueI18n({
locale: 'en-US',
numberFormats
})
const el = document.createElement('div')
document.body.appendChild(el)

const money = 101
const vm = new Vue({
i18n,
render (h) {
return h('p', { ref: 'text' }, [this.$n(money, 'currency')])
}
}).$mount(el)

const { text } = vm.$refs
const zhFormat = {
currency: {
style: 'currency', currency: 'CNY', currencyDisplay: 'name'
}
}
nextTick(() => {
assert.equal(text.textContent, '$101.00')
i18n.setNumberFormat('zh-CN', zhFormat)
assert.deepEqual(i18n.getNumberFormat('zh-CN'), zhFormat)
i18n.locale = 'zh-CN'
}).then(() => {
assert.equal(text.textContent, '101.00人民币')
}).then(done)
})
})

describe('mergeNumberFormat', () => {
it('should be merged', () => {
const i18n = new VueI18n({
locale: 'ja-JP',
numberFormats
})
const percent = { style: 'percent' }
i18n.mergeNumberFormat('en-US', { percent })
assert.deepEqual(percent, i18n.getNumberFormat('en-US').percent)
})
})
})

0 comments on commit 87ee7b3

Please sign in to comment.