node_modules

This commit is contained in:
saeid
2019-07-15 13:07:31 +04:30
parent d3735c6d30
commit ce1a3ebf9c
35713 changed files with 3639219 additions and 1 deletions
+78
View File
@@ -0,0 +1,78 @@
/* @flow */
import { warn } from './util'
export default {
name: 'i18n',
functional: true,
props: {
tag: {
type: String,
default: 'span'
},
path: {
type: String,
required: true
},
locale: {
type: String
},
places: {
type: [Array, Object]
}
},
render (h: Function, { props, data, children, parent }: Object) {
const i18n = parent.$i18n
children = (children || []).filter(child => {
return child.tag || (child.text = child.text.trim())
})
if (!i18n) {
if (process.env.NODE_ENV !== 'production') {
warn('Cannot find VueI18n instance!')
}
return children
}
const path: Path = props.path
const locale: ?Locale = props.locale
const params: Object = {}
const places: Array<any> | Object = props.places || {}
const hasPlaces: boolean = Array.isArray(places)
? places.length > 0
: Object.keys(places).length > 0
const everyPlace: boolean = children.every(child => {
if (child.data && child.data.attrs) {
const place = child.data.attrs.place
return (typeof place !== 'undefined') && place !== ''
}
})
if (hasPlaces && children.length > 0 && !everyPlace) {
warn('If places prop is set, all child elements must have place prop set.')
}
if (Array.isArray(places)) {
places.forEach((el, i) => {
params[i] = el
})
} else {
Object.keys(places).forEach(key => {
params[key] = places[key]
})
}
children.forEach((child, i: number) => {
const key: string = everyPlace
? `${child.data.attrs.place}`
: `${i}`
params[key] = child
})
return h(props.tag, data, i18n.i(path, locale, params))
}
}
+99
View File
@@ -0,0 +1,99 @@
/* @flow */
import { warn, isPlainObject, looseEqual } from './util'
export function bind (el: any, binding: Object, vnode: any): void {
if (!assert(el, vnode)) { return }
t(el, binding, vnode)
}
export function update (el: any, binding: Object, vnode: any, oldVNode: any): void {
if (!assert(el, vnode)) { return }
if (localeEqual(el, vnode) && looseEqual(binding.value, binding.oldValue)) { return }
t(el, binding, vnode)
}
export function unbind (el: any, binding: Object, vnode: any, oldVNode: any): void {
if (!assert(el, vnode)) { return }
el.textContent = ''
el._vt = undefined
delete el['_vt']
el._locale = undefined
delete el['_locale']
}
function assert (el: any, vnode: any): boolean {
const vm: any = vnode.context
if (!vm) {
warn('not exist Vue instance in VNode context')
return false
}
if (!vm.$i18n) {
warn('not exist VueI18n instance in Vue instance')
return false
}
return true
}
function localeEqual (el: any, vnode: any): boolean {
const vm: any = vnode.context
return el._locale === vm.$i18n.locale
}
function t (el: any, binding: Object, vnode: any): void {
const value: any = binding.value
const { path, locale, args, choice } = parseValue(value)
if (!path && !locale && !args) {
warn('not support value type')
return
}
if (!path) {
warn('required `path` in v-t directive')
return
}
const vm: any = vnode.context
if (choice) {
el._vt = el.textContent = vm.$i18n.tc(path, choice, ...makeParams(locale, args))
} else {
el._vt = el.textContent = vm.$i18n.t(path, ...makeParams(locale, args))
}
el._locale = vm.$i18n.locale
}
function parseValue (value: any): Object {
let path: ?string
let locale: ?Locale
let args: any
let choice: ?number
if (typeof value === 'string') {
path = value
} else if (isPlainObject(value)) {
path = value.path
locale = value.locale
args = value.args
choice = value.choice
}
return { path, locale, args, choice }
}
function makeParams (locale: Locale, args: any): Array<any> {
const params: Array<any> = []
locale && params.push(locale)
if (args && (Array.isArray(args) || isPlainObject(args))) {
params.push(args)
}
return params
}
+47
View File
@@ -0,0 +1,47 @@
/* @flow */
export default function extend (Vue: any): void {
// $FlowFixMe
Object.defineProperty(Vue.prototype, '$t', {
get () {
return (key: Path, ...values: any): TranslateResult => {
const i18n = this.$i18n
return i18n._t(key, i18n.locale, i18n._getMessages(), this, ...values)
}
}
})
// $FlowFixMe
Object.defineProperty(Vue.prototype, '$tc', {
get () {
return (key: Path, choice?: number, ...values: any): TranslateResult => {
const i18n = this.$i18n
return i18n._tc(key, i18n.locale, i18n._getMessages(), this, choice, ...values)
}
}
})
// $FlowFixMe
Object.defineProperty(Vue.prototype, '$te', {
get () {
return (key: Path, locale?: Locale): boolean => {
const i18n = this.$i18n
return i18n._te(key, i18n.locale, i18n._getMessages(), locale)
}
}
})
// $FlowFixMe
Object.defineProperty(Vue.prototype, '$d', {
get () {
return (value: number | Date, ...args: any): DateTimeFormatResult => {
return this.$i18n.d(value, ...args)
}
}
})
// $FlowFixMe
Object.defineProperty(Vue.prototype, '$n', {
get () {
return (value: number, ...args: any): NumberFormatResult => {
return this.$i18n.n(value, ...args)
}
}
})
}
+113
View File
@@ -0,0 +1,113 @@
/* @flow */
import { warn, isObject } from './util'
export default class BaseFormatter {
_caches: { [key: string]: Array<Token> }
constructor () {
this._caches = Object.create(null)
}
interpolate (message: string, values: any): Array<any> {
if (!values) {
return [message]
}
let tokens: Array<Token> = this._caches[message]
if (!tokens) {
tokens = parse(message)
this._caches[message] = tokens
}
return compile(tokens, values)
}
}
type Token = {
type: 'text' | 'named' | 'list' | 'unknown',
value: string
}
const RE_TOKEN_LIST_VALUE: RegExp = /^(\d)+/
const RE_TOKEN_NAMED_VALUE: RegExp = /^(\w)+/
export function parse (format: string): Array<Token> {
const tokens: Array<Token> = []
let position: number = 0
let text: string = ''
while (position < format.length) {
let char: string = format[position++]
if (char === '{') {
if (text) {
tokens.push({ type: 'text', value: text })
}
text = ''
let sub: string = ''
char = format[position++]
while (char !== '}') {
sub += char
char = format[position++]
}
const type = RE_TOKEN_LIST_VALUE.test(sub)
? 'list'
: RE_TOKEN_NAMED_VALUE.test(sub)
? 'named'
: 'unknown'
tokens.push({ value: sub, type })
} else if (char === '%') {
// when found rails i18n syntax, skip text capture
if (format[(position)] !== '{') {
text += char
}
} else {
text += char
}
}
text && tokens.push({ type: 'text', value: text })
return tokens
}
export function compile (tokens: Array<Token>, values: Object | Array<any>): Array<any> {
const compiled: Array<any> = []
let index: number = 0
const mode: string = Array.isArray(values)
? 'list'
: isObject(values)
? 'named'
: 'unknown'
if (mode === 'unknown') { return compiled }
while (index < tokens.length) {
const token: Token = tokens[index]
switch (token.type) {
case 'text':
compiled.push(token.value)
break
case 'list':
compiled.push(values[parseInt(token.value, 10)])
break
case 'named':
if (mode === 'named') {
compiled.push((values: any)[token.value])
} else {
if (process.env.NODE_ENV !== 'production') {
warn(`Type of token '${token.type}' and format of value '${mode}' don't match!`)
}
}
break
case 'unknown':
if (process.env.NODE_ENV !== 'production') {
warn(`Detect 'unknown' type of token!`)
}
break
}
index++
}
return compiled
}
+633
View File
@@ -0,0 +1,633 @@
/* @flow */
import { install, Vue } from './install'
import {
warn,
isNull,
parseArgs,
fetchChoice,
isPlainObject,
isObject,
looseClone,
remove,
canUseDateTimeFormat,
canUseNumberFormat
} from './util'
import BaseFormatter from './format'
import I18nPath from './path'
import type { PathValue } from './path'
const numberFormatKeys = [
'style',
'currency',
'currencyDisplay',
'useGrouping',
'minimumIntegerDigits',
'minimumFractionDigits',
'maximumFractionDigits',
'minimumSignificantDigits',
'maximumSignificantDigits',
'localeMatcher',
'formatMatcher'
]
export default class VueI18n {
static install: () => void
static version: string
static availabilities: IntlAvailability
_vm: any
_formatter: Formatter
_root: ?I18n
_sync: boolean
_fallbackRoot: boolean
_missing: ?MissingHandler
_exist: Function
_watcher: any
_i18nWatcher: Function
_silentTranslationWarn: boolean
_dateTimeFormatters: Object
_numberFormatters: Object
_path: I18nPath
_dataListeners: Array<any>
constructor (options: I18nOptions = {}) {
// Auto install if it is not done yet and `window` has `Vue`.
// To allow users to avoid auto-installation in some cases,
// this code should be placed here. See #290
/* istanbul ignore if */
if (!Vue && typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
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
this._root = options.root || null
this._sync = options.sync === undefined ? true : !!options.sync
this._fallbackRoot = options.fallbackRoot === undefined
? true
: !!options.fallbackRoot
this._silentTranslationWarn = options.silentTranslationWarn === undefined
? false
: !!options.silentTranslationWarn
this._dateTimeFormatters = {}
this._numberFormatters = {}
this._path = new I18nPath()
this._dataListeners = []
this._exist = (message: Object, key: Path): boolean => {
if (!message || !key) { return false }
return !isNull(this._path.getPathValue(message, key))
}
this._initVM({
locale,
fallbackLocale,
messages,
dateTimeFormats,
numberFormats
})
}
_initVM (data: {
locale: Locale,
fallbackLocale: Locale,
messages: LocaleMessages,
dateTimeFormats: DateTimeFormats,
numberFormats: NumberFormats
}): void {
const silent = Vue.config.silent
Vue.config.silent = true
this._vm = new Vue({ data })
Vue.config.silent = silent
}
subscribeDataChanging (vm: any): void {
this._dataListeners.push(vm)
}
unsubscribeDataChanging (vm: any): void {
remove(this._dataListeners, vm)
}
watchI18nData (): Function {
const self = this
return this._vm.$watch('$data', () => {
let i = self._dataListeners.length
while (i--) {
Vue.nextTick(() => {
self._dataListeners[i] && self._dataListeners[i].$forceUpdate()
})
}
}, { deep: true })
}
watchLocale (): ?Function {
/* istanbul ignore if */
if (!this._sync || !this._root) { return null }
const target: any = this._vm
return this._root.vm.$watch('locale', (val) => {
target.$set(target, 'locale', val)
target.$forceUpdate()
}, { immediate: true })
}
get vm (): any { return this._vm }
get messages (): LocaleMessages { return looseClone(this._getMessages()) }
get dateTimeFormats (): DateTimeFormats { return looseClone(this._getDateTimeFormats()) }
get numberFormats (): NumberFormats { return looseClone(this._getNumberFormats()) }
get locale (): Locale { return this._vm.locale }
set locale (locale: Locale): void {
this._vm.$set(this._vm, 'locale', locale)
}
get fallbackLocale (): Locale { return this._vm.fallbackLocale }
set fallbackLocale (locale: Locale): void {
this._vm.$set(this._vm, 'fallbackLocale', locale)
}
get missing (): ?MissingHandler { return this._missing }
set missing (handler: MissingHandler): void { this._missing = handler }
get formatter (): Formatter { return this._formatter }
set formatter (formatter: Formatter): void { this._formatter = formatter }
get silentTranslationWarn (): boolean { return this._silentTranslationWarn }
set silentTranslationWarn (silent: boolean): void { this._silentTranslationWarn = silent }
_getMessages (): LocaleMessages { return this._vm.messages }
_getDateTimeFormats (): DateTimeFormats { return this._vm.dateTimeFormats }
_getNumberFormats (): NumberFormats { return this._vm.numberFormats }
_warnDefault (locale: Locale, key: Path, result: ?any, vm: ?any, values: any): ?string {
if (!isNull(result)) { return result }
if (this._missing) {
const missingRet = this._missing.apply(null, [locale, key, vm, values])
if (typeof missingRet === 'string') {
return missingRet
}
} else {
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
warn(
`Cannot translate the value of keypath '${key}'. ` +
'Use the value of keypath as default.'
)
}
}
return key
}
_isFallbackRoot (val: any): boolean {
return !val && !isNull(this._root) && this._fallbackRoot
}
_interpolate (
locale: Locale,
message: LocaleMessageObject,
key: Path,
host: any,
interpolateMode: string,
values: any
): any {
if (!message) { return null }
const pathRet: PathValue = this._path.getPathValue(message, key)
if (Array.isArray(pathRet) || isPlainObject(pathRet)) { return pathRet }
let ret: mixed
if (isNull(pathRet)) {
/* istanbul ignore else */
if (isPlainObject(message)) {
ret = message[key]
if (typeof ret !== 'string') {
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
warn(`Value of key '${key}' is not a string!`)
}
return null
}
} else {
return null
}
} else {
/* istanbul ignore else */
if (typeof pathRet === 'string') {
ret = pathRet
} else {
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
warn(`Value of key '${key}' is not a string!`)
}
return null
}
}
// Check for the existance of links within the translated string
if (ret.indexOf('@:') >= 0) {
ret = this._link(locale, message, ret, host, interpolateMode, values)
}
return this._render(ret, interpolateMode, values)
}
_link (
locale: Locale,
message: LocaleMessageObject,
str: string,
host: any,
interpolateMode: string,
values: any
): any {
let ret: string = str
// Match all the links within the local
// We are going to replace each of
// them with its translation
const matches: any = ret.match(/(@:[\w\-_|.]+)/g)
for (const idx in matches) {
// ie compatible: filter custom array
// prototype method
if (!matches.hasOwnProperty(idx)) {
continue
}
const link: string = matches[idx]
// Remove the leading @:
const linkPlaceholder: string = link.substr(2)
// Translate the link
let translated: any = this._interpolate(
locale, message, linkPlaceholder, host,
interpolateMode === 'raw' ? 'string' : interpolateMode,
interpolateMode === 'raw' ? undefined : values
)
if (this._isFallbackRoot(translated)) {
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
warn(`Fall back to translate the link placeholder '${linkPlaceholder}' with root locale.`)
}
/* istanbul ignore if */
if (!this._root) { throw Error('unexpected error') }
const root: any = this._root
translated = root._translate(
root._getMessages(), root.locale, root.fallbackLocale,
linkPlaceholder, host, interpolateMode, values
)
}
translated = this._warnDefault(
locale, linkPlaceholder, translated, host,
Array.isArray(values) ? values : [values]
)
// Replace the link with the translated
ret = !translated ? ret : ret.replace(link, translated)
}
return ret
}
_render (message: string, interpolateMode: string, values: any): any {
const ret = this._formatter.interpolate(message, values)
// if interpolateMode is **not** 'string' ('row'),
// return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter
return interpolateMode === 'string' ? ret.join('') : ret
}
_translate (
messages: LocaleMessages,
locale: Locale,
fallback: Locale,
key: Path,
host: any,
interpolateMode: string,
args: any
): any {
let res: any =
this._interpolate(locale, messages[locale], key, host, interpolateMode, args)
if (!isNull(res)) { return res }
res = this._interpolate(fallback, messages[fallback], key, host, interpolateMode, args)
if (!isNull(res)) {
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
warn(`Fall back to translate the keypath '${key}' with '${fallback}' locale.`)
}
return res
} else {
return null
}
}
_t (key: Path, _locale: Locale, messages: LocaleMessages, host: any, ...values: any): any {
if (!key) { return '' }
const parsedArgs = parseArgs(...values)
const locale: Locale = parsedArgs.locale || _locale
const ret: any = this._translate(
messages, locale, this.fallbackLocale, key,
host, 'string', parsedArgs.params
)
if (this._isFallbackRoot(ret)) {
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
warn(`Fall back to translate the keypath '${key}' with root locale.`)
}
/* istanbul ignore if */
if (!this._root) { throw Error('unexpected error') }
return this._root.t(key, ...values)
} else {
return this._warnDefault(locale, key, ret, host, values)
}
}
t (key: Path, ...values: any): TranslateResult {
return this._t(key, this.locale, this._getMessages(), null, ...values)
}
_i (key: Path, locale: Locale, messages: LocaleMessages, host: any, values: Object): any {
const ret: any =
this._translate(messages, locale, this.fallbackLocale, key, host, 'raw', values)
if (this._isFallbackRoot(ret)) {
if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
warn(`Fall back to interpolate the keypath '${key}' with root locale.`)
}
if (!this._root) { throw Error('unexpected error') }
return this._root.i(key, locale, values)
} else {
return this._warnDefault(locale, key, ret, host, [values])
}
}
i (key: Path, locale: Locale, values: Object): TranslateResult {
/* istanbul ignore if */
if (!key) { return '' }
if (typeof locale !== 'string') {
locale = this.locale
}
return this._i(key, locale, this._getMessages(), null, values)
}
_tc (
key: Path,
_locale: Locale,
messages: LocaleMessages,
host: any,
choice?: number,
...values: any
): any {
if (!key) { return '' }
if (choice === undefined) {
choice = 1
}
return fetchChoice(this._t(key, _locale, messages, host, ...values), choice)
}
tc (key: Path, choice?: number, ...values: any): TranslateResult {
return this._tc(key, this.locale, this._getMessages(), null, choice, ...values)
}
_te (key: Path, locale: Locale, messages: LocaleMessages, ...args: any): boolean {
const _locale: Locale = parseArgs(...args).locale || locale
return this._exist(messages[_locale], key)
}
te (key: Path, locale?: Locale): boolean {
return this._te(key, this.locale, this._getMessages(), locale)
}
getLocaleMessage (locale: Locale): LocaleMessageObject {
return looseClone(this._vm.messages[locale] || {})
}
setLocaleMessage (locale: Locale, message: LocaleMessageObject): void {
this._vm.$set(this._vm.messages, locale, message)
}
mergeLocaleMessage (locale: Locale, message: LocaleMessageObject): void {
this._vm.$set(this._vm.messages, locale, Vue.util.extend(this._vm.messages[locale] || {}, message))
}
getDateTimeFormat (locale: Locale): DateTimeFormat {
return looseClone(this._vm.dateTimeFormats[locale] || {})
}
setDateTimeFormat (locale: Locale, format: DateTimeFormat): void {
this._vm.$set(this._vm.dateTimeFormats, locale, format)
}
mergeDateTimeFormat (locale: Locale, format: DateTimeFormat): void {
this._vm.$set(this._vm.dateTimeFormats, locale, Vue.util.extend(this._vm.dateTimeFormats[locale] || {}, format))
}
_localizeDateTime (
value: number | Date,
locale: Locale,
fallback: Locale,
dateTimeFormats: DateTimeFormats,
key: string
): ?DateTimeFormatResult {
let _locale: Locale = locale
let formats: DateTimeFormat = dateTimeFormats[_locale]
// fallback locale
if (isNull(formats) || isNull(formats[key])) {
if (process.env.NODE_ENV !== 'production') {
warn(`Fall back to '${fallback}' datetime formats from '${locale} datetime formats.`)
}
_locale = fallback
formats = dateTimeFormats[_locale]
}
if (isNull(formats) || isNull(formats[key])) {
return null
} else {
const format: ?DateTimeFormatOptions = formats[key]
const id = `${_locale}__${key}`
let formatter = this._dateTimeFormatters[id]
if (!formatter) {
formatter = this._dateTimeFormatters[id] = new Intl.DateTimeFormat(_locale, format)
}
return formatter.format(value)
}
}
_d (value: number | Date, locale: Locale, key: ?string): DateTimeFormatResult {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !VueI18n.availabilities.dateTimeFormat) {
warn('Cannot format a Date value due to not supported Intl.DateTimeFormat.')
return ''
}
if (!key) {
return new Intl.DateTimeFormat(locale).format(value)
}
const ret: ?DateTimeFormatResult =
this._localizeDateTime(value, locale, this.fallbackLocale, this._getDateTimeFormats(), key)
if (this._isFallbackRoot(ret)) {
if (process.env.NODE_ENV !== 'production') {
warn(`Fall back to datetime localization of root: key '${key}' .`)
}
/* istanbul ignore if */
if (!this._root) { throw Error('unexpected error') }
return this._root.d(value, key, locale)
} else {
return ret || ''
}
}
d (value: number | Date, ...args: any): DateTimeFormatResult {
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._d(value, locale, key)
}
getNumberFormat (locale: Locale): NumberFormat {
return looseClone(this._vm.numberFormats[locale] || {})
}
setNumberFormat (locale: Locale, format: NumberFormat): void {
this._vm.$set(this._vm.numberFormats, locale, format)
}
mergeNumberFormat (locale: Locale, format: NumberFormat): void {
this._vm.$set(this._vm.numberFormats, locale, Vue.util.extend(this._vm.numberFormats[locale] || {}, format))
}
_localizeNumber (
value: number,
locale: Locale,
fallback: Locale,
numberFormats: NumberFormats,
key: string,
options: ?NumberFormatOptions
): ?NumberFormatResult {
let _locale: Locale = locale
let formats: NumberFormat = numberFormats[_locale]
// fallback locale
if (isNull(formats) || isNull(formats[key])) {
if (process.env.NODE_ENV !== 'production') {
warn(`Fall back to '${fallback}' number formats from '${locale} number formats.`)
}
_locale = fallback
formats = numberFormats[_locale]
}
if (isNull(formats) || isNull(formats[key])) {
return null
} else {
const format: ?NumberFormatOptions = formats[key]
let formatter
if (options) {
// If options specified - create one time number formatter
formatter = new Intl.NumberFormat(_locale, Object.assign({}, format, options))
} else {
const id = `${_locale}__${key}`
formatter = this._numberFormatters[id]
if (!formatter) {
formatter = this._numberFormatters[id] = new Intl.NumberFormat(_locale, format)
}
}
return formatter.format(value)
}
}
_n (value: number, locale: Locale, key: ?string, options: ?NumberFormatOptions): NumberFormatResult {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !VueI18n.availabilities.numberFormat) {
warn('Cannot format a Number value due to not supported Intl.NumberFormat.')
return ''
}
if (!key) {
const nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options)
return nf.format(value)
}
const ret: ?NumberFormatResult =
this._localizeNumber(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options)
if (this._isFallbackRoot(ret)) {
if (process.env.NODE_ENV !== 'production') {
warn(`Fall back to number localization of root: key '${key}' .`)
}
/* istanbul ignore if */
if (!this._root) { throw Error('unexpected error') }
return this._root.n(value, Object.assign({}, { key, locale }, options))
} else {
return ret || ''
}
}
n (value: number, ...args: any): NumberFormatResult {
let locale: Locale = this.locale
let key: ?string = null
let options: ?NumberFormatOptions = 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
}
// Filter out number format options only
options = Object.keys(args[0]).reduce((acc, key) => {
if (numberFormatKeys.includes(key)) {
return Object.assign({}, acc, { [key]: args[0][key] })
}
return acc
}, null)
}
} 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, options)
}
}
VueI18n.availabilities = {
dateTimeFormat: canUseDateTimeFormat,
numberFormat: canUseNumberFormat
}
VueI18n.install = install
VueI18n.version = '__VERSION__'
+38
View File
@@ -0,0 +1,38 @@
import { warn } from './util'
import extend from './extend'
import mixin from './mixin'
import component from './component'
import { bind, update, unbind } from './directive'
export let Vue
export function install (_Vue) {
Vue = _Vue
const version = (Vue.version && Number(Vue.version.split('.')[0])) || -1
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && install.installed) {
warn('already installed.')
return
}
install.installed = true
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && version < 2) {
warn(`vue-i18n (${install.version}) need to use Vue 2.0 or later (Vue: ${Vue.version}).`)
return
}
Object.defineProperty(Vue.prototype, '$i18n', {
get () { return this._i18n }
})
extend(Vue)
Vue.mixin(mixin)
Vue.directive('t', { bind, update, unbind })
Vue.component(component.name, component)
// use object-based merge strategy
const strats = Vue.config.optionMergeStrategies
strats.i18n = strats.methods
}
+103
View File
@@ -0,0 +1,103 @@
/* @flow */
import VueI18n from './index'
import { isPlainObject, warn, merge } from './util'
export default {
beforeCreate (): void {
const options: any = this.$options
options.i18n = options.i18n || (options.__i18n ? {} : null)
if (options.i18n) {
if (options.i18n instanceof VueI18n) {
// init locale messages via custom blocks
if (options.__i18n) {
try {
let localeMessages = {}
options.__i18n.forEach(resource => {
localeMessages = merge(localeMessages, JSON.parse(resource))
})
Object.keys(localeMessages).forEach((locale: Locale) => {
options.i18n.mergeLocaleMessage(locale, localeMessages[locale])
})
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
warn(`Cannot parse locale messages via custom blocks.`, e)
}
}
}
this._i18n = options.i18n
this._i18nWatcher = this._i18n.watchI18nData()
this._i18n.subscribeDataChanging(this)
this._subscribing = true
} else if (isPlainObject(options.i18n)) {
// component local i18n
if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
options.i18n.root = this.$root.$i18n
options.i18n.formatter = this.$root.$i18n.formatter
options.i18n.fallbackLocale = this.$root.$i18n.fallbackLocale
options.i18n.silentTranslationWarn = this.$root.$i18n.silentTranslationWarn
}
// init locale messages via custom blocks
if (options.__i18n) {
try {
let localeMessages = {}
options.__i18n.forEach(resource => {
localeMessages = merge(localeMessages, JSON.parse(resource))
})
options.i18n.messages = localeMessages
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
warn(`Cannot parse locale messages via custom blocks.`, e)
}
}
}
this._i18n = new VueI18n(options.i18n)
this._i18nWatcher = this._i18n.watchI18nData()
this._i18n.subscribeDataChanging(this)
this._subscribing = true
if (options.i18n.sync === undefined || !!options.i18n.sync) {
this._localeWatcher = this.$i18n.watchLocale()
}
} else {
if (process.env.NODE_ENV !== 'production') {
warn(`Cannot be interpreted 'i18n' option.`)
}
}
} else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
// root i18n
this._i18n = this.$root.$i18n
this._i18n.subscribeDataChanging(this)
this._subscribing = true
} else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
// parent i18n
this._i18n = options.parent.$i18n
this._i18n.subscribeDataChanging(this)
this._subscribing = true
}
},
beforeDestroy (): void {
if (!this._i18n) { return }
if (this._subscribing) {
this._i18n.unsubscribeDataChanging(this)
delete this._subscribing
}
if (this._i18nWatcher) {
this._i18nWatcher()
delete this._i18nWatcher
}
if (this._localeWatcher) {
this._localeWatcher()
delete this._localeWatcher
}
this._i18n = null
}
}
+26
View File
@@ -0,0 +1,26 @@
export default {
staticKeys: ['staticI18n'],
preTransformNode (el, options) {
console.log('preTransformNode', el.tag)
},
transformNode (el, options) {
console.log('transformNode', el.tag)
},
postTransformNode (el, options) {
console.log('postTransformNode', el.tag, el.parent ? el.parent.children : '')
},
genData (el) {
console.log('genData', el.tag)
return ''
},
transformCode (el, code) {
debugger
console.log('transformCode', el.tag, code)
return code
}
}
+323
View File
@@ -0,0 +1,323 @@
/* @flow */
import { isObject } from './util'
/**
* Path paerser
* - Inspired:
* Vue.js Path parser
*/
// actions
const APPEND = 0
const PUSH = 1
const INC_SUB_PATH_DEPTH = 2
const PUSH_SUB_PATH = 3
// states
const BEFORE_PATH = 0
const IN_PATH = 1
const BEFORE_IDENT = 2
const IN_IDENT = 3
const IN_SUB_PATH = 4
const IN_SINGLE_QUOTE = 5
const IN_DOUBLE_QUOTE = 6
const AFTER_PATH = 7
const ERROR = 8
const pathStateMachine: any = []
pathStateMachine[BEFORE_PATH] = {
'ws': [BEFORE_PATH],
'ident': [IN_IDENT, APPEND],
'[': [IN_SUB_PATH],
'eof': [AFTER_PATH]
}
pathStateMachine[IN_PATH] = {
'ws': [IN_PATH],
'.': [BEFORE_IDENT],
'[': [IN_SUB_PATH],
'eof': [AFTER_PATH]
}
pathStateMachine[BEFORE_IDENT] = {
'ws': [BEFORE_IDENT],
'ident': [IN_IDENT, APPEND],
'0': [IN_IDENT, APPEND],
'number': [IN_IDENT, APPEND]
}
pathStateMachine[IN_IDENT] = {
'ident': [IN_IDENT, APPEND],
'0': [IN_IDENT, APPEND],
'number': [IN_IDENT, APPEND],
'ws': [IN_PATH, PUSH],
'.': [BEFORE_IDENT, PUSH],
'[': [IN_SUB_PATH, PUSH],
'eof': [AFTER_PATH, PUSH]
}
pathStateMachine[IN_SUB_PATH] = {
"'": [IN_SINGLE_QUOTE, APPEND],
'"': [IN_DOUBLE_QUOTE, APPEND],
'[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
']': [IN_PATH, PUSH_SUB_PATH],
'eof': ERROR,
'else': [IN_SUB_PATH, APPEND]
}
pathStateMachine[IN_SINGLE_QUOTE] = {
"'": [IN_SUB_PATH, APPEND],
'eof': ERROR,
'else': [IN_SINGLE_QUOTE, APPEND]
}
pathStateMachine[IN_DOUBLE_QUOTE] = {
'"': [IN_SUB_PATH, APPEND],
'eof': ERROR,
'else': [IN_DOUBLE_QUOTE, APPEND]
}
/**
* Check if an expression is a literal value.
*/
const literalValueRE: RegExp = /^\s?(true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/
function isLiteral (exp: string): boolean {
return literalValueRE.test(exp)
}
/**
* Strip quotes from a string
*/
function stripQuotes (str: string): string | boolean {
const a: number = str.charCodeAt(0)
const b: number = str.charCodeAt(str.length - 1)
return a === b && (a === 0x22 || a === 0x27)
? str.slice(1, -1)
: str
}
/**
* Determine the type of a character in a keypath.
*/
function getPathCharType (ch: ?string): string {
if (ch === undefined || ch === null) { return 'eof' }
const code: number = ch.charCodeAt(0)
switch (code) {
case 0x5B: // [
case 0x5D: // ]
case 0x2E: // .
case 0x22: // "
case 0x27: // '
case 0x30: // 0
return ch
case 0x5F: // _
case 0x24: // $
case 0x2D: // -
return 'ident'
case 0x20: // Space
case 0x09: // Tab
case 0x0A: // Newline
case 0x0D: // Return
case 0xA0: // No-break space
case 0xFEFF: // Byte Order Mark
case 0x2028: // Line Separator
case 0x2029: // Paragraph Separator
return 'ws'
}
// a-z, A-Z
if ((code >= 0x61 && code <= 0x7A) || (code >= 0x41 && code <= 0x5A)) {
return 'ident'
}
// 1-9
if (code >= 0x31 && code <= 0x39) { return 'number' }
return 'else'
}
/**
* Format a subPath, return its plain form if it is
* a literal string or number. Otherwise prepend the
* dynamic indicator (*).
*/
function formatSubPath (path: string): boolean | string {
const trimmed: string = path.trim()
// invalid leading 0
if (path.charAt(0) === '0' && isNaN(path)) { return false }
return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed
}
/**
* Parse a string path into an array of segments
*/
function parse (path: Path): ?Array<string> {
const keys: Array<string> = []
let index: number = -1
let mode: number = BEFORE_PATH
let subPathDepth: number = 0
let c: ?string
let key: any
let newChar: any
let type: string
let transition: number
let action: Function
let typeMap: any
const actions: Array<Function> = []
actions[PUSH] = function () {
if (key !== undefined) {
keys.push(key)
key = undefined
}
}
actions[APPEND] = function () {
if (key === undefined) {
key = newChar
} else {
key += newChar
}
}
actions[INC_SUB_PATH_DEPTH] = function () {
actions[APPEND]()
subPathDepth++
}
actions[PUSH_SUB_PATH] = function () {
if (subPathDepth > 0) {
subPathDepth--
mode = IN_SUB_PATH
actions[APPEND]()
} else {
subPathDepth = 0
key = formatSubPath(key)
if (key === false) {
return false
} else {
actions[PUSH]()
}
}
}
function maybeUnescapeQuote (): ?boolean {
const nextChar: string = path[index + 1]
if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
(mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
index++
newChar = '\\' + nextChar
actions[APPEND]()
return true
}
}
while (mode !== null) {
index++
c = path[index]
if (c === '\\' && maybeUnescapeQuote()) {
continue
}
type = getPathCharType(c)
typeMap = pathStateMachine[mode]
transition = typeMap[type] || typeMap['else'] || ERROR
if (transition === ERROR) {
return // parse error
}
mode = transition[0]
action = actions[transition[1]]
if (action) {
newChar = transition[2]
newChar = newChar === undefined
? c
: newChar
if (action() === false) {
return
}
}
if (mode === AFTER_PATH) {
return keys
}
}
}
export type PathValue = PathValueObject | PathValueArray | string | number | boolean | null
export type PathValueObject = { [key: string]: PathValue }
export type PathValueArray = Array<PathValue>
function empty (target: any): boolean {
/* istanbul ignore else */
if (Array.isArray(target)) {
return target.length === 0
} else {
return false
}
}
export default class I18nPath {
_cache: Object
constructor () {
this._cache = Object.create(null)
}
/**
* External parse that check for a cache hit first
*/
parsePath (path: Path): Array<string> {
let hit: ?Array<string> = this._cache[path]
if (!hit) {
hit = parse(path)
if (hit) {
this._cache[path] = hit
}
}
return hit || []
}
/**
* Get path value from path string
*/
getPathValue (obj: mixed, path: Path): PathValue {
if (!isObject(obj)) { return null }
const paths: Array<string> = this.parsePath(path)
if (empty(paths)) {
return null
} else {
const length: number = paths.length
let ret: any = null
let last: any = obj
let i: number = 0
while (i < length) {
const value: any = last[paths[i]]
if (value === undefined) {
last = null
break
}
last = value
i++
}
ret = last
return ret
}
}
}
+154
View File
@@ -0,0 +1,154 @@
/* @flow */
/**
* utilites
*/
export function warn (msg: string, err: ?Error): void {
if (typeof console !== 'undefined') {
console.warn('[vue-i18n] ' + msg)
/* istanbul ignore if */
if (err) {
console.warn(err.stack)
}
}
}
export function isObject (obj: mixed): boolean %checks {
return obj !== null && typeof obj === 'object'
}
const toString: Function = Object.prototype.toString
const OBJECT_STRING: string = '[object Object]'
export function isPlainObject (obj: any): boolean {
return toString.call(obj) === OBJECT_STRING
}
export function isNull (val: mixed): boolean {
return val === null || val === undefined
}
export function parseArgs (...args: Array<mixed>): Object {
let locale: ?string = null
let params: mixed = null
if (args.length === 1) {
if (isObject(args[0]) || Array.isArray(args[0])) {
params = args[0]
} else if (typeof args[0] === 'string') {
locale = args[0]
}
} else if (args.length === 2) {
if (typeof args[0] === 'string') {
locale = args[0]
}
/* istanbul ignore if */
if (isObject(args[1]) || Array.isArray(args[1])) {
params = args[1]
}
}
return { locale, params }
}
function getOldChoiceIndexFixed (choice: number): number {
return choice
? choice > 1
? 1
: 0
: 1
}
function getChoiceIndex (choice: number, choicesLength: number): number {
choice = Math.abs(choice)
if (choicesLength === 2) { return getOldChoiceIndexFixed(choice) }
return choice ? Math.min(choice, 2) : 0
}
export function fetchChoice (message: string, choice: number): ?string {
/* istanbul ignore if */
if (!message && typeof message !== 'string') { return null }
const choices: Array<string> = message.split('|')
choice = getChoiceIndex(choice, choices.length)
if (!choices[choice]) { return message }
return choices[choice].trim()
}
export function looseClone (obj: Object): Object {
return JSON.parse(JSON.stringify(obj))
}
export function remove (arr: Array<any>, item: any): Array<any> | void {
if (arr.length) {
const index = arr.indexOf(item)
if (index > -1) {
return arr.splice(index, 1)
}
}
}
const hasOwnProperty = Object.prototype.hasOwnProperty
export function hasOwn (obj: Object | Array<*>, key: string): boolean {
return hasOwnProperty.call(obj, key)
}
export function merge (target: Object): Object {
const output = Object(target)
for (let i = 1; i < arguments.length; i++) {
const source = arguments[i]
if (source !== undefined && source !== null) {
let key
for (key in source) {
if (hasOwn(source, key)) {
if (isObject(source[key])) {
output[key] = merge(output[key], source[key])
} else {
output[key] = source[key]
}
}
}
}
}
return output
}
export function looseEqual (a: any, b: any): boolean {
if (a === b) { return true }
const isObjectA: boolean = isObject(a)
const isObjectB: boolean = isObject(b)
if (isObjectA && isObjectB) {
try {
const isArrayA: boolean = Array.isArray(a)
const isArrayB: boolean = Array.isArray(b)
if (isArrayA && isArrayB) {
return a.length === b.length && a.every((e: any, i: number): boolean => {
return looseEqual(e, b[i])
})
} else if (!isArrayA && !isArrayB) {
const keysA: Array<string> = Object.keys(a)
const keysB: Array<string> = Object.keys(b)
return keysA.length === keysB.length && keysA.every((key: string): boolean => {
return looseEqual(a[key], b[key])
})
} else {
/* istanbul ignore next */
return false
}
} catch (e) {
/* istanbul ignore next */
return false
}
} else if (!isObjectA && !isObjectB) {
return String(a) === String(b)
} else {
return false
}
}
export const canUseDateTimeFormat: boolean =
typeof Intl !== 'undefined' && typeof Intl.DateTimeFormat !== 'undefined'
export const canUseNumberFormat: boolean =
typeof Intl !== 'undefined' && typeof Intl.NumberFormat !== 'undefined'