Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support for vue-i18n-bridge #1385

Merged
merged 2 commits into from Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions decls/i18n.js
Expand Up @@ -82,6 +82,7 @@ declare type I18nOptions = {
fallbackLocale?: FallbackLocale,
messages?: LocaleMessages,
dateTimeFormats?: DateTimeFormats,
datetimeFormats?: DateTimeFormats,
numberFormats?: NumberFormats,
formatter?: Formatter,
missing?: MissingHandler,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions src/index.js
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand Down
6 changes: 3 additions & 3 deletions 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.')
Expand All @@ -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)
Expand Down
242 changes: 127 additions & 115 deletions src/mixin.js
Expand Up @@ -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
}
})
}
}
}
4 changes: 2 additions & 2 deletions test/unit/mixin.test.js
@@ -1,4 +1,4 @@
import mixin from '../../src/mixin'
import defineMixin from '../../src/mixin'

describe('mixin', () => {
describe('beforeCreate', () => {
Expand All @@ -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)
})
})
})
Expand Down
5 changes: 4 additions & 1 deletion types/index.d.ts
Expand Up @@ -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;
Expand Down Expand Up @@ -257,7 +260,7 @@ declare class VueI18n {
*/
getChoiceIndex: (choice: number, choicesLength: number) => number;

static install: PluginFunction<never>;
static install: PluginFunction<PluignOptions>;
static version: string;
static availabilities: VueI18n.IntlAvailability;
}
Expand Down