Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/vue-i18n-routing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"vite-plugin-dts": "^3.5.1",
"vitest": "^0.34.1",
"vue": "^3.2.27",
"vue-i18n": "npm:vue-i18n@next",
"vue-i18n": "npm:vue-i18n@9.3.0-beta.26",
"vue-i18n-bridge": "next",
"vue-i18n-legacy": "npm:vue-i18n@8",
"vue-router": "^4.1.5",
Expand Down
33 changes: 27 additions & 6 deletions packages/vue-i18n-routing/src/extends/__test__/i18n.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,29 @@ describe('extendI18n', () => {
localeCodes: ['en', 'ja']
})

useSetup(() => {}, [i18n])
const vm = useSetup(() => {}, [i18n])
const composer = i18n.global as unknown as Composer
assert.deepEqual(composer.locales!.value, [{ code: 'en' }, { code: 'ja' }])
assert.deepEqual(composer.localeCodes!.value, ['en', 'ja'])
assert.deepEqual(composer.locales.value, [{ code: 'en' }, { code: 'ja' }])
assert.deepEqual(composer.localeCodes.value, ['en', 'ja'])

vm.unmount()
})
})

describe('vue-i18n v9: legacy mode', () => {
it('should be extended', () => {
const i18n = createI18n({ legacy: true, locale: 'en' })
extendI18n(i18n, {
locales: [{ code: 'en' }, { code: 'ja' }],
localeCodes: ['en', 'ja']
})

const vm = useSetup(() => {}, [i18n])
const vueI18n = i18n.global as unknown as VueI18n
assert.deepEqual(vueI18n.locales, [{ code: 'en' }, { code: 'ja' }])
assert.deepEqual(vueI18n.localeCodes, ['en', 'ja'])

vm.unmount()
})
})

Expand Down Expand Up @@ -52,8 +71,9 @@ describe('extendI18n', () => {
})
const vm = useSetup(() => {}, [i18n])
const $i18n = (vm as any).$i18n

const composer = i18n.global as unknown as Composer

// custom extending
const foo = (composer as any).foo as Ref<string>
assert.equal(foo.value, 'foo')
assert.equal($i18n.foo, 'foo')
Expand All @@ -74,7 +94,7 @@ describe('extendI18n', () => {
const vueI18nSpy = vi.fn()
vueI18nSpy.mockImplementation(() => 'vue-i18n-foo')

const i18n = createI18n({ locale: 'en' })
const i18n = createI18n({ legacy: true, locale: 'en' })
extendI18n(i18n, {
locales: [{ code: 'en' }, { code: 'ja' }],
localeCodes: ['en', 'ja'],
Expand All @@ -101,8 +121,9 @@ describe('extendI18n', () => {
})
const vm = useSetup(() => {}, [i18n])
const $i18n = (vm as any).$i18n

const vueI18n = i18n.global as unknown as VueI18n

// custom extending
const foo = (vueI18n as any).foo as string
assert.equal(foo, 'vue-i18n-foo')
assert.equal($i18n.foo, 'vue-i18n-foo')
Expand Down
95 changes: 45 additions & 50 deletions packages/vue-i18n-routing/src/extends/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { DEFAULT_BASE_URL } from '../constants'
import { resolveBaseUrl, isVueI18n, getComposer, inBrowser } from '../utils'

import type { I18nRoutingOptions, LocaleObject } from '../types'
import type { I18n, Composer, VueI18n } from '@intlify/vue-i18n-bridge'
import type { I18n, Composer, VueI18n, VueI18nExtender, ComposerExtender, Disposer } from '@intlify/vue-i18n-bridge'
import type { App } from 'vue-demi'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -57,11 +57,11 @@ export interface VueI18nRoutingPluginOptions {
/**
* @internal
*/
__composerExtend?: (composer: Composer) => void
__composerExtend?: ComposerExtender
/**
* @internal
*/
__vueI18nExtend?: (vueI18n: VueI18n) => void
__vueI18nExtend?: VueI18nExtender
}

export interface ExtendProperyDescripters {
Expand Down Expand Up @@ -103,35 +103,45 @@ export function extendI18n<Context = unknown, TI18n extends I18n = I18n>(
pluginOptions.inject = true
}
const orgComposerExtend = pluginOptions.__composerExtend
pluginOptions.__composerExtend = (c: Composer) => {
const g = getComposer(i18n)
c.locales = computed(() => g.locales.value)
c.localeCodes = computed(() => g.localeCodes.value)
c.baseUrl = computed(() => g.baseUrl.value)
pluginOptions.__composerExtend = (localComposer: Composer) => {
const globalComposer = getComposer(i18n)
localComposer.locales = computed(() => globalComposer.locales.value)
localComposer.localeCodes = computed(() => globalComposer.localeCodes.value)
localComposer.baseUrl = computed(() => globalComposer.baseUrl.value)
let orgComposerDispose: Disposer | undefined
if (isFunction(orgComposerExtend)) {
Reflect.apply(orgComposerExtend, pluginOptions, [c])
orgComposerDispose = Reflect.apply(orgComposerExtend, pluginOptions, [globalComposer])
}
return () => {
orgComposerDispose && orgComposerDispose()
}
}
if (isVueI18n(i18n.global)) {
if (i18n.mode === 'legacy') {
const orgVueI18nExtend = pluginOptions.__vueI18nExtend
pluginOptions.__vueI18nExtend = (vueI18n: VueI18n) => {
extendVueI18n(vueI18n, hooks.onExtendVueI18n)
let orgVueI18nDispose: Disposer | undefined
if (isFunction(orgVueI18nExtend)) {
Reflect.apply(orgVueI18nExtend, pluginOptions, [vueI18n])
orgVueI18nDispose = Reflect.apply(orgVueI18nExtend, pluginOptions, [vueI18n])
}
return () => {
orgVueI18nDispose && orgVueI18nDispose()
}
}
}

options[0] = pluginOptions
Reflect.apply(orgInstall, i18n, [vue, ...options])

const composer = getComposer(i18n)
const globalComposer = getComposer(i18n)

// extend global
scope.run(() => extendComposer(composer, { locales, localeCodes, baseUrl, hooks, context }))
if (isVueI18n(i18n.global)) {
extendVueI18n(i18n.global, hooks.onExtendVueI18n)
}
scope.run(() => {
extendComposer(globalComposer, { locales, localeCodes, baseUrl, hooks, context })
if (i18n.mode === 'legacy' && isVueI18n(i18n.global)) {
extendVueI18n(i18n.global, hooks.onExtendVueI18n)
}
})

// extend vue component instance for Vue 3
const app = vue as App
Expand All @@ -140,11 +150,12 @@ export function extendI18n<Context = unknown, TI18n extends I18n = I18n>(
? isVue3
? app.config.globalProperties.$i18n
: i18n
// for legacy mode
: isVue2
? i18n
: null
if (exported) {
extendExportedGlobal(exported, composer, hooks.onExtendExportedGlobal)
extendExportedGlobal(exported, globalComposer, hooks.onExtendExportedGlobal)
}

if (pluginOptions.inject) {
Expand All @@ -162,7 +173,7 @@ export function extendI18n<Context = unknown, TI18n extends I18n = I18n>(
})
}

// release scope on unmounting
// dispose when app will be unmounting
if (app.unmount) {
const unmountApp = app.unmount
app.unmount = () => {
Expand Down Expand Up @@ -203,37 +214,11 @@ function extendComposer<Context = unknown>(composer: Composer, options: VueI18nE
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function extendExportedGlobal(exported: any, g: Composer, hook?: ExtendExportedGlobalHook) {
const properties: ExtendProperyDescripters[] = [
{
locales: {
get() {
return g.locales.value
}
},
localeCodes: {
get() {
return g.localeCodes.value
}
},
baseUrl: {
get() {
return g.baseUrl.value
}
}
}
]
hook && properties.push(hook(g))
for (const property of properties) {
for (const [key, descriptor] of Object.entries(property)) {
Object.defineProperty(exported, key, descriptor)
}
}
}

function extendVueI18n(vueI18n: VueI18n, hook?: ExtendVueI18nHook): void {
const composer = getComposer(vueI18n)
function extendProperyDescripters(
composer: Composer,
exported: any, // eslint-disable-line @typescript-eslint/no-explicit-any
hook?: ExtendVueI18nHook | ExtendExportedGlobalHook
): void {
const properties: ExtendProperyDescripters[] = [
{
locales: {
Expand All @@ -256,11 +241,21 @@ function extendVueI18n(vueI18n: VueI18n, hook?: ExtendVueI18nHook): void {
hook && properties.push(hook(composer))
for (const property of properties) {
for (const [key, descriptor] of Object.entries(property)) {
Object.defineProperty(vueI18n, key, descriptor)
Object.defineProperty(exported, key, descriptor)
}
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function extendExportedGlobal(exported: any, g: Composer, hook?: ExtendExportedGlobalHook) {
extendProperyDescripters(g, exported, hook)
}

function extendVueI18n(vueI18n: VueI18n, hook?: ExtendVueI18nHook): void {
const c = getComposer(vueI18n)
extendProperyDescripters(c, vueI18n, hook)
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isPluginOptions(options: any): options is VueI18nRoutingPluginOptions {
return isObject(options) && ('inject' in options || '__composerExtend' in options || '__vueI18nExtend' in options)
Expand Down
55 changes: 44 additions & 11 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.