diff --git a/decls/i18n.js b/decls/i18n.js index aff10335c..b192da890 100644 --- a/decls/i18n.js +++ b/decls/i18n.js @@ -82,6 +82,7 @@ declare type I18nOptions = { fallbackLocale?: FallbackLocale, messages?: LocaleMessages, dateTimeFormats?: DateTimeFormats, + datetimeFormats?: DateTimeFormats, numberFormats?: NumberFormats, formatter?: Formatter, missing?: MissingHandler, diff --git a/package.json b/package.json index f314e1e86..0a8c5e575 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "babel-plugin-istanbul": "^6.0.0", "babel-preset-power-assert": "^3.0.0", "buble": "^0.19.3", - "chromedriver": "^92.0.0", + "chromedriver": "^93.0.0", "core-js": "^3.6.5", "cross-env": "^7.0.2", "cross-spawn": "^7.0.3", diff --git a/src/index.js b/src/index.js index e381af34e..1e39c4978 100644 --- a/src/index.js +++ b/src/index.js @@ -82,7 +82,7 @@ export default class VueI18n { ? false : options.fallbackLocale || 'en-US' const messages: LocaleMessages = options.messages || {} - const dateTimeFormats = options.dateTimeFormats || {} + const dateTimeFormats = options.dateTimeFormats || options.datetimeFormats || {} const numberFormats = options.numberFormats || {} this._vm = null @@ -232,7 +232,7 @@ export default class VueI18n { }): void { const silent = Vue.config.silent Vue.config.silent = true - this._vm = new Vue({ data }) + this._vm = new Vue({ data, __VUE18N__INSTANCE__: true }) Vue.config.silent = silent } diff --git a/src/install.js b/src/install.js index 62edbc2db..198ecb670 100644 --- a/src/install.js +++ b/src/install.js @@ -1,13 +1,13 @@ import { warn } from './util' import extend from './extend' -import mixin from './mixin' +import defineMixin from './mixin' import interpolationComponent from './components/interpolation' import numberComponent from './components/number' import { bind, update, unbind } from './directive' export let Vue -export function install (_Vue) { +export function install (_Vue, options = { bridge: false }) { /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && install.installed && _Vue === Vue) { warn('already installed.') @@ -25,7 +25,7 @@ export function install (_Vue) { } extend(Vue) - Vue.mixin(mixin) + Vue.mixin(defineMixin(options.bridge)) Vue.directive('t', { bind, update, unbind }) Vue.component(interpolationComponent.name, interpolationComponent) Vue.component(numberComponent.name, numberComponent) diff --git a/src/mixin.js b/src/mixin.js index 79eaebcb1..697313d5e 100644 --- a/src/mixin.js +++ b/src/mixin.js @@ -3,143 +3,155 @@ import VueI18n from './index' import { isPlainObject, warn, error, 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 && options.i18n.messages ? options.i18n.messages : {} - 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') { - error(`Cannot parse locale messages via custom blocks.`, e) +/** + * Mixin + * + * If `bridge` mode, empty mixin is returned, + * else regulary mixin implementation is returned. + */ +export default function defineMixin (bridge: boolean = false) { + function mounted (): void { + if (this !== this.$root && this.$options.__INTLIFY_META__ && this.$el) { + this.$el.setAttribute('data-intlify', this.$options.__INTLIFY_META__) + } + } + + return bridge + ? { mounted } // delegate `vue-i18n-bridge` mixin implementation + : { // regulary + 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 && options.i18n.messages ? options.i18n.messages : {} + 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') { + error(`Cannot parse locale messages via custom blocks.`, e) + } } } - } - this._i18n = options.i18n - this._i18nWatcher = this._i18n.watchI18nData() - } else if (isPlainObject(options.i18n)) { - const rootI18n = this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n - ? this.$root.$i18n - : null - // component local i18n - if (rootI18n) { - options.i18n.root = this.$root - options.i18n.formatter = rootI18n.formatter - options.i18n.fallbackLocale = rootI18n.fallbackLocale - options.i18n.formatFallbackMessages = rootI18n.formatFallbackMessages - options.i18n.silentTranslationWarn = rootI18n.silentTranslationWarn - options.i18n.silentFallbackWarn = rootI18n.silentFallbackWarn - options.i18n.pluralizationRules = rootI18n.pluralizationRules - options.i18n.preserveDirectiveContent = rootI18n.preserveDirectiveContent - } + this._i18n = options.i18n + this._i18nWatcher = this._i18n.watchI18nData() + } else if (isPlainObject(options.i18n)) { + const rootI18n = this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n + ? this.$root.$i18n + : null + // component local i18n + if (rootI18n) { + options.i18n.root = this.$root + options.i18n.formatter = rootI18n.formatter + options.i18n.fallbackLocale = rootI18n.fallbackLocale + options.i18n.formatFallbackMessages = rootI18n.formatFallbackMessages + options.i18n.silentTranslationWarn = rootI18n.silentTranslationWarn + options.i18n.silentFallbackWarn = rootI18n.silentFallbackWarn + options.i18n.pluralizationRules = rootI18n.pluralizationRules + options.i18n.preserveDirectiveContent = rootI18n.preserveDirectiveContent + } - // init locale messages via custom blocks - if (options.__i18n) { - try { - let localeMessages = options.i18n && options.i18n.messages ? options.i18n.messages : {} - 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) + // init locale messages via custom blocks + if (options.__i18n) { + try { + let localeMessages = options.i18n && options.i18n.messages ? options.i18n.messages : {} + 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) + } } } - } - const { sharedMessages } = options.i18n - if (sharedMessages && isPlainObject(sharedMessages)) { - options.i18n.messages = merge(options.i18n.messages, sharedMessages) - } + const { sharedMessages } = options.i18n + if (sharedMessages && isPlainObject(sharedMessages)) { + options.i18n.messages = merge(options.i18n.messages, sharedMessages) + } - this._i18n = new VueI18n(options.i18n) - this._i18nWatcher = this._i18n.watchI18nData() + this._i18n = new VueI18n(options.i18n) + this._i18nWatcher = this._i18n.watchI18nData() - if (options.i18n.sync === undefined || !!options.i18n.sync) { - this._localeWatcher = this.$i18n.watchLocale() - } + if (options.i18n.sync === undefined || !!options.i18n.sync) { + this._localeWatcher = this.$i18n.watchLocale() + } - if (rootI18n) { - rootI18n.onComponentInstanceCreated(this._i18n) - } - } else { - if (process.env.NODE_ENV !== 'production') { - warn(`Cannot be interpreted 'i18n' option.`) + if (rootI18n) { + rootI18n.onComponentInstanceCreated(this._i18n) + } + } 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 + } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) { + // parent i18n + this._i18n = options.parent.$i18n } - } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) { - // root i18n - this._i18n = this.$root.$i18n - } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) { - // parent i18n - this._i18n = options.parent.$i18n - } - }, + }, - beforeMount (): void { - const options: any = this.$options - options.i18n = options.i18n || (options.__i18n ? {} : null) + beforeMount (): 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) { + if (options.i18n instanceof VueI18n) { + // init locale messages via custom blocks + this._i18n.subscribeDataChanging(this) + this._subscribing = true + } else if (isPlainObject(options.i18n)) { + this._i18n.subscribeDataChanging(this) + this._subscribing = true + } 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) { this._i18n.subscribeDataChanging(this) this._subscribing = true - } else if (isPlainObject(options.i18n)) { + } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) { this._i18n.subscribeDataChanging(this) this._subscribing = true - } 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) { - this._i18n.subscribeDataChanging(this) - this._subscribing = true - } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) { - this._i18n.subscribeDataChanging(this) - this._subscribing = true - } - }, + }, - mounted (): void { - if (this !== this.$root && this.$options.__INTLIFY_META__ && this.$el) { - this.$el.setAttribute('data-intlify', this.$options.__INTLIFY_META__) - } - }, + mounted, - beforeDestroy (): void { - if (!this._i18n) { return } + beforeDestroy (): void { + if (!this._i18n) { return } - const self = this - this.$nextTick(() => { - if (self._subscribing) { - self._i18n.unsubscribeDataChanging(self) - delete self._subscribing - } + const self = this + this.$nextTick(() => { + if (self._subscribing) { + self._i18n.unsubscribeDataChanging(self) + delete self._subscribing + } - if (self._i18nWatcher) { - self._i18nWatcher() - self._i18n.destroyVM() - delete self._i18nWatcher - } + if (self._i18nWatcher) { + self._i18nWatcher() + self._i18n.destroyVM() + delete self._i18nWatcher + } - if (self._localeWatcher) { - self._localeWatcher() - delete self._localeWatcher - } - }) + if (self._localeWatcher) { + self._localeWatcher() + delete self._localeWatcher + } + }) + } } } diff --git a/test/unit/mixin.test.js b/test/unit/mixin.test.js index c4d900534..3290b0aac 100644 --- a/test/unit/mixin.test.js +++ b/test/unit/mixin.test.js @@ -1,4 +1,4 @@ -import mixin from '../../src/mixin' +import defineMixin from '../../src/mixin' describe('mixin', () => { describe('beforeCreate', () => { @@ -19,7 +19,7 @@ describe('mixin', () => { describe('beforeDestroy', () => { describe('not assign VueI18n instance', () => { it('should be succeeded', () => { - assert(mixin.beforeDestroy() === undefined) + assert(defineMixin().beforeDestroy() === undefined) }) }) }) diff --git a/types/index.d.ts b/types/index.d.ts index 43c63ed02..8a73c22ce 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -157,6 +157,9 @@ export type MissingHandler = VueI18n.MissingHandler; export type PostTranslationHandler = VueI18n.PostTranslationHandler; export type IntlAvailability = VueI18n.IntlAvailability; export type I18nOptions = VueI18n.I18nOptions; +export type PluignOptions = { + bridge?: boolean +} export declare interface IVueI18n { readonly messages: VueI18n.LocaleMessages; @@ -257,7 +260,7 @@ declare class VueI18n { */ getChoiceIndex: (choice: number, choicesLength: number) => number; - static install: PluginFunction; + static install: PluginFunction; static version: string; static availabilities: VueI18n.IntlAvailability; } diff --git a/yarn.lock b/yarn.lock index fc50e1095..c31fcc5bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2214,12 +2214,12 @@ axios@^0.19.0: dependencies: follow-redirects "1.5.10" -axios@^0.21.1: - version "0.21.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" - integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== +axios@^0.21.2: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== dependencies: - follow-redirects "^1.10.0" + follow-redirects "^1.14.0" babel-eslint@^10.0.1, babel-eslint@^10.1.0: version "10.1.0" @@ -3136,13 +3136,13 @@ chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" -chromedriver@^92.0.0: - version "92.0.1" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-92.0.1.tgz#3e28b7e0c9fb94d693cf74d51af0c29d57f18dca" - integrity sha512-LptlDVCs1GgyFNVbRoHzzy948JDVzTgGiVPXjNj385qXKQP3hjAVBIgyvb/Hco0xSEW8fjwJfsm1eQRmu6t4pQ== +chromedriver@^93.0.0: + version "93.0.1" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-93.0.1.tgz#3ed1f7baa98a754fc1788c42ac8e4bb1ab27db32" + integrity sha512-KDzbW34CvQLF5aTkm3b5VdlTrvdIt4wEpCzT2p4XJIQWQZEPco5pNce7Lu9UqZQGkhQ4mpZt4Ky6NKVyIS2N8A== dependencies: "@testim/chrome-version" "^1.0.7" - axios "^0.21.1" + axios "^0.21.2" del "^6.0.0" extract-zip "^2.0.1" https-proxy-agent "^5.0.0" @@ -5664,10 +5664,10 @@ follow-redirects@^1.0.0: dependencies: debug "^3.0.0" -follow-redirects@^1.10.0: - version "1.13.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz#dd73c8effc12728ba5cf4259d760ea5fb83e3147" - integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA== +follow-redirects@^1.14.0: + version "1.14.4" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" + integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== for-in@^1.0.2: version "1.0.2"