UNPKG

23.8 kBJavaScriptView Raw
1/* @flow */
2
3import { install, Vue } from './install'
4import {
5 warn,
6 isNull,
7 parseArgs,
8 isPlainObject,
9 isObject,
10 looseClone,
11 remove,
12 merge
13} from './util'
14import BaseFormatter from './format'
15import I18nPath from './path'
16
17import type { PathValue } from './path'
18
19const numberFormatKeys = [
20 'style',
21 'currency',
22 'currencyDisplay',
23 'useGrouping',
24 'minimumIntegerDigits',
25 'minimumFractionDigits',
26 'maximumFractionDigits',
27 'minimumSignificantDigits',
28 'maximumSignificantDigits',
29 'localeMatcher',
30 'formatMatcher'
31]
32const linkKeyMatcher = /(?:@(?:\.[a-z]+)?:(?:[\w\-_|.]+|\([\w\-_|.]+\)))/g
33const linkKeyPrefixMatcher = /^@(?:\.([a-z]+))?:/
34const bracketsMatcher = /[()]/g
35const formatters = {
36 'upper': str => str.toLocaleUpperCase(),
37 'lower': str => str.toLocaleLowerCase()
38}
39
40const defaultFormatter = new BaseFormatter()
41
42export default class VueI18n {
43 static install: () => void
44 static version: string
45 static availabilities: IntlAvailability
46
47 _vm: any
48 _formatter: Formatter
49 _root: any
50 _sync: boolean
51 _fallbackRoot: boolean
52 _missing: ?MissingHandler
53 _exist: Function
54 _watcher: any
55 _i18nWatcher: Function
56 _silentTranslationWarn: boolean
57 _silentFallbackWarn: boolean
58 _dateTimeFormatters: Object
59 _numberFormatters: Object
60 _path: I18nPath
61 _dataListeners: Array<any>
62 _preserveDirectiveContent: boolean
63 pluralizationRules: {
64 [lang: string]: (choice: number, choicesLength: number) => number
65 }
66
67 constructor (options: I18nOptions = {}) {
68 // Auto install if it is not done yet and `window` has `Vue`.
69 // To allow users to avoid auto-installation in some cases,
70 // this code should be placed here. See #290
71 /* istanbul ignore if */
72 if (!Vue && typeof window !== 'undefined' && window.Vue) {
73 install(window.Vue)
74 }
75
76 const locale: Locale = options.locale || 'en-US'
77 const fallbackLocale: Locale = options.fallbackLocale || 'en-US'
78 const messages: LocaleMessages = options.messages || {}
79 const dateTimeFormats = options.dateTimeFormats || {}
80 const numberFormats = options.numberFormats || {}
81
82 this._vm = null
83 this._formatter = options.formatter || defaultFormatter
84 this._missing = options.missing || null
85 this._root = options.root || null
86 this._sync = options.sync === undefined ? true : !!options.sync
87 this._fallbackRoot = options.fallbackRoot === undefined
88 ? true
89 : !!options.fallbackRoot
90 this._silentTranslationWarn = options.silentTranslationWarn === undefined
91 ? false
92 : !!options.silentTranslationWarn
93 this._silentFallbackWarn = options.silentFallbackWarn === undefined
94 ? false
95 : !!options.silentFallbackWarn
96 this._dateTimeFormatters = {}
97 this._numberFormatters = {}
98 this._path = new I18nPath()
99 this._dataListeners = []
100 this._preserveDirectiveContent = options.preserveDirectiveContent === undefined
101 ? false
102 : !!options.preserveDirectiveContent
103 this.pluralizationRules = options.pluralizationRules || {}
104
105 this._exist = (message: Object, key: Path): boolean => {
106 if (!message || !key) { return false }
107 if (this._path.getPathValue(message, key)) { return true }
108 // fallback for flat key
109 if (message[key]) { return true }
110 return false
111 }
112
113 this._initVM({
114 locale,
115 fallbackLocale,
116 messages,
117 dateTimeFormats,
118 numberFormats
119 })
120 }
121
122 _initVM (data: {
123 locale: Locale,
124 fallbackLocale: Locale,
125 messages: LocaleMessages,
126 dateTimeFormats: DateTimeFormats,
127 numberFormats: NumberFormats
128 }): void {
129 const silent = Vue.config.silent
130 Vue.config.silent = true
131 this._vm = new Vue({ data })
132 Vue.config.silent = silent
133 }
134
135 subscribeDataChanging (vm: any): void {
136 this._dataListeners.push(vm)
137 }
138
139 unsubscribeDataChanging (vm: any): void {
140 remove(this._dataListeners, vm)
141 }
142
143 watchI18nData (): Function {
144 const self = this
145 return this._vm.$watch('$data', () => {
146 let i = self._dataListeners.length
147 while (i--) {
148 Vue.nextTick(() => {
149 self._dataListeners[i] && self._dataListeners[i].$forceUpdate()
150 })
151 }
152 }, { deep: true })
153 }
154
155 watchLocale (): ?Function {
156 /* istanbul ignore if */
157 if (!this._sync || !this._root) { return null }
158 const target: any = this._vm
159 return this._root.$i18n.vm.$watch('locale', (val) => {
160 target.$set(target, 'locale', val)
161 target.$forceUpdate()
162 }, { immediate: true })
163 }
164
165 get vm (): any { return this._vm }
166
167 get messages (): LocaleMessages { return looseClone(this._getMessages()) }
168 get dateTimeFormats (): DateTimeFormats { return looseClone(this._getDateTimeFormats()) }
169 get numberFormats (): NumberFormats { return looseClone(this._getNumberFormats()) }
170
171 get locale (): Locale { return this._vm.locale }
172 set locale (locale: Locale): void {
173 this._vm.$set(this._vm, 'locale', locale)
174 }
175
176 get fallbackLocale (): Locale { return this._vm.fallbackLocale }
177 set fallbackLocale (locale: Locale): void {
178 this._vm.$set(this._vm, 'fallbackLocale', locale)
179 }
180
181 get missing (): ?MissingHandler { return this._missing }
182 set missing (handler: MissingHandler): void { this._missing = handler }
183
184 get formatter (): Formatter { return this._formatter }
185 set formatter (formatter: Formatter): void { this._formatter = formatter }
186
187 get silentTranslationWarn (): boolean { return this._silentTranslationWarn }
188 set silentTranslationWarn (silent: boolean): void { this._silentTranslationWarn = silent }
189
190 get silentFallbackWarn (): boolean { return this._silentFallbackWarn }
191 set silentFallbackWarn (silent: boolean): void { this._silentFallbackWarn = silent }
192
193 get preserveDirectiveContent (): boolean { return this._preserveDirectiveContent }
194 set preserveDirectiveContent (preserve: boolean): void { this._preserveDirectiveContent = preserve }
195
196 _getMessages (): LocaleMessages { return this._vm.messages }
197 _getDateTimeFormats (): DateTimeFormats { return this._vm.dateTimeFormats }
198 _getNumberFormats (): NumberFormats { return this._vm.numberFormats }
199
200 _warnDefault (locale: Locale, key: Path, result: ?any, vm: ?any, values: any): ?string {
201 if (!isNull(result)) { return result }
202 if (this._missing) {
203 const missingRet = this._missing.apply(null, [locale, key, vm, values])
204 if (typeof missingRet === 'string') {
205 return missingRet
206 }
207 } else {
208 if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
209 warn(
210 `Cannot translate the value of keypath '${key}'. ` +
211 'Use the value of keypath as default.'
212 )
213 }
214 }
215 return key
216 }
217
218 _isFallbackRoot (val: any): boolean {
219 return !val && !isNull(this._root) && this._fallbackRoot
220 }
221
222 _isSilentFallback (locale: Locale): boolean {
223 return this._silentFallbackWarn && (this._isFallbackRoot() || locale !== this.fallbackLocale)
224 }
225
226 _interpolate (
227 locale: Locale,
228 message: LocaleMessageObject,
229 key: Path,
230 host: any,
231 interpolateMode: string,
232 values: any,
233 visitedLinkStack: Array<string>
234 ): any {
235 if (!message) { return null }
236
237 const pathRet: PathValue = this._path.getPathValue(message, key)
238 if (Array.isArray(pathRet) || isPlainObject(pathRet)) { return pathRet }
239
240 let ret: mixed
241 if (isNull(pathRet)) {
242 /* istanbul ignore else */
243 if (isPlainObject(message)) {
244 ret = message[key]
245 if (typeof ret !== 'string') {
246 if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn && !this._isSilentFallback(locale)) {
247 warn(`Value of key '${key}' is not a string!`)
248 }
249 return null
250 }
251 } else {
252 return null
253 }
254 } else {
255 /* istanbul ignore else */
256 if (typeof pathRet === 'string') {
257 ret = pathRet
258 } else {
259 if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn && !this._isSilentFallback(locale)) {
260 warn(`Value of key '${key}' is not a string!`)
261 }
262 return null
263 }
264 }
265
266 // Check for the existence of links within the translated string
267 if (ret.indexOf('@:') >= 0 || ret.indexOf('@.') >= 0) {
268 ret = this._link(locale, message, ret, host, 'raw', values, visitedLinkStack)
269 }
270
271 return this._render(ret, interpolateMode, values, key)
272 }
273
274 _link (
275 locale: Locale,
276 message: LocaleMessageObject,
277 str: string,
278 host: any,
279 interpolateMode: string,
280 values: any,
281 visitedLinkStack: Array<string>
282 ): any {
283 let ret: string = str
284
285 // Match all the links within the local
286 // We are going to replace each of
287 // them with its translation
288 const matches: any = ret.match(linkKeyMatcher)
289 for (const idx in matches) {
290 // ie compatible: filter custom array
291 // prototype method
292 if (!matches.hasOwnProperty(idx)) {
293 continue
294 }
295 const link: string = matches[idx]
296 const linkKeyPrefixMatches: any = link.match(linkKeyPrefixMatcher)
297 const [linkPrefix, formatterName] = linkKeyPrefixMatches
298
299 // Remove the leading @:, @.case: and the brackets
300 const linkPlaceholder: string = link.replace(linkPrefix, '').replace(bracketsMatcher, '')
301
302 if (visitedLinkStack.includes(linkPlaceholder)) {
303 if (process.env.NODE_ENV !== 'production') {
304 warn(`Circular reference found. "${link}" is already visited in the chain of ${visitedLinkStack.reverse().join(' <- ')}`)
305 }
306 return ret
307 }
308 visitedLinkStack.push(linkPlaceholder)
309
310 // Translate the link
311 let translated: any = this._interpolate(
312 locale, message, linkPlaceholder, host,
313 interpolateMode === 'raw' ? 'string' : interpolateMode,
314 interpolateMode === 'raw' ? undefined : values,
315 visitedLinkStack
316 )
317
318 if (this._isFallbackRoot(translated)) {
319 if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
320 warn(`Fall back to translate the link placeholder '${linkPlaceholder}' with root locale.`)
321 }
322 /* istanbul ignore if */
323 if (!this._root) { throw Error('unexpected error') }
324 const root: any = this._root.$i18n
325 translated = root._translate(
326 root._getMessages(), root.locale, root.fallbackLocale,
327 linkPlaceholder, host, interpolateMode, values
328 )
329 }
330 translated = this._warnDefault(
331 locale, linkPlaceholder, translated, host,
332 Array.isArray(values) ? values : [values]
333 )
334 if (formatters.hasOwnProperty(formatterName)) {
335 translated = formatters[formatterName](translated)
336 }
337
338 visitedLinkStack.pop()
339
340 // Replace the link with the translated
341 ret = !translated ? ret : ret.replace(link, translated)
342 }
343
344 return ret
345 }
346
347 _render (message: string, interpolateMode: string, values: any, path: string): any {
348 let ret = this._formatter.interpolate(message, values, path)
349
350 // If the custom formatter refuses to work - apply the default one
351 if (!ret) {
352 ret = defaultFormatter.interpolate(message, values, path)
353 }
354
355 // if interpolateMode is **not** 'string' ('row'),
356 // return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter
357 return interpolateMode === 'string' ? ret.join('') : ret
358 }
359
360 _translate (
361 messages: LocaleMessages,
362 locale: Locale,
363 fallback: Locale,
364 key: Path,
365 host: any,
366 interpolateMode: string,
367 args: any
368 ): any {
369 let res: any =
370 this._interpolate(locale, messages[locale], key, host, interpolateMode, args, [key])
371 if (!isNull(res)) { return res }
372
373 res = this._interpolate(fallback, messages[fallback], key, host, interpolateMode, args, [key])
374 if (!isNull(res)) {
375 if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn && !this._silentFallbackWarn) {
376 warn(`Fall back to translate the keypath '${key}' with '${fallback}' locale.`)
377 }
378 return res
379 } else {
380 return null
381 }
382 }
383
384 _t (key: Path, _locale: Locale, messages: LocaleMessages, host: any, ...values: any): any {
385 if (!key) { return '' }
386
387 const parsedArgs = parseArgs(...values)
388 const locale: Locale = parsedArgs.locale || _locale
389
390 const ret: any = this._translate(
391 messages, locale, this.fallbackLocale, key,
392 host, 'string', parsedArgs.params
393 )
394 if (this._isFallbackRoot(ret)) {
395 if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn && !this._silentFallbackWarn) {
396 warn(`Fall back to translate the keypath '${key}' with root locale.`)
397 }
398 /* istanbul ignore if */
399 if (!this._root) { throw Error('unexpected error') }
400 return this._root.$t(key, ...values)
401 } else {
402 return this._warnDefault(locale, key, ret, host, values)
403 }
404 }
405
406 t (key: Path, ...values: any): TranslateResult {
407 return this._t(key, this.locale, this._getMessages(), null, ...values)
408 }
409
410 _i (key: Path, locale: Locale, messages: LocaleMessages, host: any, values: Object): any {
411 const ret: any =
412 this._translate(messages, locale, this.fallbackLocale, key, host, 'raw', values)
413 if (this._isFallbackRoot(ret)) {
414 if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
415 warn(`Fall back to interpolate the keypath '${key}' with root locale.`)
416 }
417 if (!this._root) { throw Error('unexpected error') }
418 return this._root.$i18n.i(key, locale, values)
419 } else {
420 return this._warnDefault(locale, key, ret, host, [values])
421 }
422 }
423
424 i (key: Path, locale: Locale, values: Object): TranslateResult {
425 /* istanbul ignore if */
426 if (!key) { return '' }
427
428 if (typeof locale !== 'string') {
429 locale = this.locale
430 }
431
432 return this._i(key, locale, this._getMessages(), null, values)
433 }
434
435 _tc (
436 key: Path,
437 _locale: Locale,
438 messages: LocaleMessages,
439 host: any,
440 choice?: number,
441 ...values: any
442 ): any {
443 if (!key) { return '' }
444 if (choice === undefined) {
445 choice = 1
446 }
447
448 const predefined = { 'count': choice, 'n': choice }
449 const parsedArgs = parseArgs(...values)
450 parsedArgs.params = Object.assign(predefined, parsedArgs.params)
451 values = parsedArgs.locale === null ? [parsedArgs.params] : [parsedArgs.locale, parsedArgs.params]
452 return this.fetchChoice(this._t(key, _locale, messages, host, ...values), choice)
453 }
454
455 fetchChoice (message: string, choice: number): ?string {
456 /* istanbul ignore if */
457 if (!message && typeof message !== 'string') { return null }
458 const choices: Array<string> = message.split('|')
459
460 choice = this.getChoiceIndex(choice, choices.length)
461 if (!choices[choice]) { return message }
462 return choices[choice].trim()
463 }
464
465 /**
466 * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
467 * @param choicesLength {number} an overall amount of available choices
468 * @returns a final choice index
469 */
470 getChoiceIndex (choice: number, choicesLength: number): number {
471 // Default (old) getChoiceIndex implementation - english-compatible
472 const defaultImpl = (_choice: number, _choicesLength: number) => {
473 _choice = Math.abs(_choice)
474
475 if (_choicesLength === 2) {
476 return _choice
477 ? _choice > 1
478 ? 1
479 : 0
480 : 1
481 }
482
483 return _choice ? Math.min(_choice, 2) : 0
484 }
485
486 if (this.locale in this.pluralizationRules) {
487 return this.pluralizationRules[this.locale].apply(this, [choice, choicesLength])
488 } else {
489 return defaultImpl(choice, choicesLength)
490 }
491 }
492
493 tc (key: Path, choice?: number, ...values: any): TranslateResult {
494 return this._tc(key, this.locale, this._getMessages(), null, choice, ...values)
495 }
496
497 _te (key: Path, locale: Locale, messages: LocaleMessages, ...args: any): boolean {
498 const _locale: Locale = parseArgs(...args).locale || locale
499 return this._exist(messages[_locale], key)
500 }
501
502 te (key: Path, locale?: Locale): boolean {
503 return this._te(key, this.locale, this._getMessages(), locale)
504 }
505
506 getLocaleMessage (locale: Locale): LocaleMessageObject {
507 return looseClone(this._vm.messages[locale] || {})
508 }
509
510 setLocaleMessage (locale: Locale, message: LocaleMessageObject): void {
511 this._vm.$set(this._vm.messages, locale, message)
512 }
513
514 mergeLocaleMessage (locale: Locale, message: LocaleMessageObject): void {
515 this._vm.$set(this._vm.messages, locale, merge(this._vm.messages[locale] || {}, message))
516 }
517
518 getDateTimeFormat (locale: Locale): DateTimeFormat {
519 return looseClone(this._vm.dateTimeFormats[locale] || {})
520 }
521
522 setDateTimeFormat (locale: Locale, format: DateTimeFormat): void {
523 this._vm.$set(this._vm.dateTimeFormats, locale, format)
524 }
525
526 mergeDateTimeFormat (locale: Locale, format: DateTimeFormat): void {
527 this._vm.$set(this._vm.dateTimeFormats, locale, merge(this._vm.dateTimeFormats[locale] || {}, format))
528 }
529
530 _localizeDateTime (
531 value: number | Date,
532 locale: Locale,
533 fallback: Locale,
534 dateTimeFormats: DateTimeFormats,
535 key: string
536 ): ?DateTimeFormatResult {
537 let _locale: Locale = locale
538 let formats: DateTimeFormat = dateTimeFormats[_locale]
539
540 // fallback locale
541 if (isNull(formats) || isNull(formats[key])) {
542 if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
543 warn(`Fall back to '${fallback}' datetime formats from '${locale} datetime formats.`)
544 }
545 _locale = fallback
546 formats = dateTimeFormats[_locale]
547 }
548
549 if (isNull(formats) || isNull(formats[key])) {
550 return null
551 } else {
552 const format: ?DateTimeFormatOptions = formats[key]
553 const id = `${_locale}__${key}`
554 let formatter = this._dateTimeFormatters[id]
555 if (!formatter) {
556 formatter = this._dateTimeFormatters[id] = new Intl.DateTimeFormat(_locale, format)
557 }
558 return formatter.format(value)
559 }
560 }
561
562 _d (value: number | Date, locale: Locale, key: ?string): DateTimeFormatResult {
563 /* istanbul ignore if */
564 if (process.env.NODE_ENV !== 'production' && !VueI18n.availabilities.dateTimeFormat) {
565 warn('Cannot format a Date value due to not supported Intl.DateTimeFormat.')
566 return ''
567 }
568
569 if (!key) {
570 return new Intl.DateTimeFormat(locale).format(value)
571 }
572
573 const ret: ?DateTimeFormatResult =
574 this._localizeDateTime(value, locale, this.fallbackLocale, this._getDateTimeFormats(), key)
575 if (this._isFallbackRoot(ret)) {
576 if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
577 warn(`Fall back to datetime localization of root: key '${key}' .`)
578 }
579 /* istanbul ignore if */
580 if (!this._root) { throw Error('unexpected error') }
581 return this._root.$i18n.d(value, key, locale)
582 } else {
583 return ret || ''
584 }
585 }
586
587 d (value: number | Date, ...args: any): DateTimeFormatResult {
588 let locale: Locale = this.locale
589 let key: ?string = null
590
591 if (args.length === 1) {
592 if (typeof args[0] === 'string') {
593 key = args[0]
594 } else if (isObject(args[0])) {
595 if (args[0].locale) {
596 locale = args[0].locale
597 }
598 if (args[0].key) {
599 key = args[0].key
600 }
601 }
602 } else if (args.length === 2) {
603 if (typeof args[0] === 'string') {
604 key = args[0]
605 }
606 if (typeof args[1] === 'string') {
607 locale = args[1]
608 }
609 }
610
611 return this._d(value, locale, key)
612 }
613
614 getNumberFormat (locale: Locale): NumberFormat {
615 return looseClone(this._vm.numberFormats[locale] || {})
616 }
617
618 setNumberFormat (locale: Locale, format: NumberFormat): void {
619 this._vm.$set(this._vm.numberFormats, locale, format)
620 }
621
622 mergeNumberFormat (locale: Locale, format: NumberFormat): void {
623 this._vm.$set(this._vm.numberFormats, locale, merge(this._vm.numberFormats[locale] || {}, format))
624 }
625
626 _localizeNumber (
627 value: number,
628 locale: Locale,
629 fallback: Locale,
630 numberFormats: NumberFormats,
631 key: string,
632 options: ?NumberFormatOptions
633 ): ?NumberFormatResult {
634 let _locale: Locale = locale
635 let formats: NumberFormat = numberFormats[_locale]
636
637 // fallback locale
638 if (isNull(formats) || isNull(formats[key])) {
639 if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
640 warn(`Fall back to '${fallback}' number formats from '${locale} number formats.`)
641 }
642 _locale = fallback
643 formats = numberFormats[_locale]
644 }
645
646 if (isNull(formats) || isNull(formats[key])) {
647 return null
648 } else {
649 const format: ?NumberFormatOptions = formats[key]
650
651 let formatter
652 if (options) {
653 // If options specified - create one time number formatter
654 formatter = new Intl.NumberFormat(_locale, Object.assign({}, format, options))
655 } else {
656 const id = `${_locale}__${key}`
657 formatter = this._numberFormatters[id]
658 if (!formatter) {
659 formatter = this._numberFormatters[id] = new Intl.NumberFormat(_locale, format)
660 }
661 }
662 return formatter.format(value)
663 }
664 }
665
666 _n (value: number, locale: Locale, key: ?string, options: ?NumberFormatOptions): NumberFormatResult {
667 /* istanbul ignore if */
668 if (!VueI18n.availabilities.numberFormat) {
669 if (process.env.NODE_ENV !== 'production') {
670 warn('Cannot format a Number value due to not supported Intl.NumberFormat.')
671 }
672 return ''
673 }
674
675 if (!key) {
676 const nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options)
677 return nf.format(value)
678 }
679
680 const ret: ?NumberFormatResult =
681 this._localizeNumber(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options)
682 if (this._isFallbackRoot(ret)) {
683 if (process.env.NODE_ENV !== 'production' && !this._silentTranslationWarn) {
684 warn(`Fall back to number localization of root: key '${key}' .`)
685 }
686 /* istanbul ignore if */
687 if (!this._root) { throw Error('unexpected error') }
688 return this._root.$i18n.n(value, Object.assign({}, { key, locale }, options))
689 } else {
690 return ret || ''
691 }
692 }
693
694 n (value: number, ...args: any): NumberFormatResult {
695 let locale: Locale = this.locale
696 let key: ?string = null
697 let options: ?NumberFormatOptions = null
698
699 if (args.length === 1) {
700 if (typeof args[0] === 'string') {
701 key = args[0]
702 } else if (isObject(args[0])) {
703 if (args[0].locale) {
704 locale = args[0].locale
705 }
706 if (args[0].key) {
707 key = args[0].key
708 }
709
710 // Filter out number format options only
711 options = Object.keys(args[0]).reduce((acc, key) => {
712 if (numberFormatKeys.includes(key)) {
713 return Object.assign({}, acc, { [key]: args[0][key] })
714 }
715 return acc
716 }, null)
717 }
718 } else if (args.length === 2) {
719 if (typeof args[0] === 'string') {
720 key = args[0]
721 }
722 if (typeof args[1] === 'string') {
723 locale = args[1]
724 }
725 }
726
727 return this._n(value, locale, key, options)
728 }
729}
730
731let availabilities: IntlAvailability
732// $FlowFixMe
733Object.defineProperty(VueI18n, 'availabilities', {
734 get () {
735 if (!availabilities) {
736 const intlDefined = typeof Intl !== 'undefined'
737 availabilities = {
738 dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined',
739 numberFormat: intlDefined && typeof Intl.NumberFormat !== 'undefined'
740 }
741 }
742
743 return availabilities
744 }
745})
746
747VueI18n.install = install
748VueI18n.version = '__VERSION__'