Skip to content

Commit 53b2272

Browse files
authored
fix: improve vue-i18n extending (#44)
1 parent c0b988f commit 53b2272

File tree

4 files changed

+117
-68
lines changed

4 files changed

+117
-68
lines changed

packages/vue-i18n-routing/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"vite-plugin-dts": "^3.5.1",
2929
"vitest": "^0.34.1",
3030
"vue": "^3.2.27",
31-
"vue-i18n": "npm:vue-i18n@next",
31+
"vue-i18n": "npm:vue-i18n@9.3.0-beta.26",
3232
"vue-i18n-bridge": "next",
3333
"vue-i18n-legacy": "npm:vue-i18n@8",
3434
"vue-router": "^4.1.5",

packages/vue-i18n-routing/src/extends/__test__/i18n.test.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,29 @@ describe('extendI18n', () => {
1919
localeCodes: ['en', 'ja']
2020
})
2121

22-
useSetup(() => {}, [i18n])
22+
const vm = useSetup(() => {}, [i18n])
2323
const composer = i18n.global as unknown as Composer
24-
assert.deepEqual(composer.locales!.value, [{ code: 'en' }, { code: 'ja' }])
25-
assert.deepEqual(composer.localeCodes!.value, ['en', 'ja'])
24+
assert.deepEqual(composer.locales.value, [{ code: 'en' }, { code: 'ja' }])
25+
assert.deepEqual(composer.localeCodes.value, ['en', 'ja'])
26+
27+
vm.unmount()
28+
})
29+
})
30+
31+
describe('vue-i18n v9: legacy mode', () => {
32+
it('should be extended', () => {
33+
const i18n = createI18n({ legacy: true, locale: 'en' })
34+
extendI18n(i18n, {
35+
locales: [{ code: 'en' }, { code: 'ja' }],
36+
localeCodes: ['en', 'ja']
37+
})
38+
39+
const vm = useSetup(() => {}, [i18n])
40+
const vueI18n = i18n.global as unknown as VueI18n
41+
assert.deepEqual(vueI18n.locales, [{ code: 'en' }, { code: 'ja' }])
42+
assert.deepEqual(vueI18n.localeCodes, ['en', 'ja'])
43+
44+
vm.unmount()
2645
})
2746
})
2847

@@ -52,8 +71,9 @@ describe('extendI18n', () => {
5271
})
5372
const vm = useSetup(() => {}, [i18n])
5473
const $i18n = (vm as any).$i18n
55-
5674
const composer = i18n.global as unknown as Composer
75+
76+
// custom extending
5777
const foo = (composer as any).foo as Ref<string>
5878
assert.equal(foo.value, 'foo')
5979
assert.equal($i18n.foo, 'foo')
@@ -74,7 +94,7 @@ describe('extendI18n', () => {
7494
const vueI18nSpy = vi.fn()
7595
vueI18nSpy.mockImplementation(() => 'vue-i18n-foo')
7696

77-
const i18n = createI18n({ locale: 'en' })
97+
const i18n = createI18n({ legacy: true, locale: 'en' })
7898
extendI18n(i18n, {
7999
locales: [{ code: 'en' }, { code: 'ja' }],
80100
localeCodes: ['en', 'ja'],
@@ -101,8 +121,9 @@ describe('extendI18n', () => {
101121
})
102122
const vm = useSetup(() => {}, [i18n])
103123
const $i18n = (vm as any).$i18n
104-
105124
const vueI18n = i18n.global as unknown as VueI18n
125+
126+
// custom extending
106127
const foo = (vueI18n as any).foo as string
107128
assert.equal(foo, 'vue-i18n-foo')
108129
assert.equal($i18n.foo, 'vue-i18n-foo')

packages/vue-i18n-routing/src/extends/i18n.ts

Lines changed: 45 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { DEFAULT_BASE_URL } from '../constants'
1414
import { resolveBaseUrl, isVueI18n, getComposer, inBrowser } from '../utils'
1515

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

2020
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -57,11 +57,11 @@ export interface VueI18nRoutingPluginOptions {
5757
/**
5858
* @internal
5959
*/
60-
__composerExtend?: (composer: Composer) => void
60+
__composerExtend?: ComposerExtender
6161
/**
6262
* @internal
6363
*/
64-
__vueI18nExtend?: (vueI18n: VueI18n) => void
64+
__vueI18nExtend?: VueI18nExtender
6565
}
6666

6767
export interface ExtendProperyDescripters {
@@ -103,35 +103,45 @@ export function extendI18n<Context = unknown, TI18n extends I18n = I18n>(
103103
pluginOptions.inject = true
104104
}
105105
const orgComposerExtend = pluginOptions.__composerExtend
106-
pluginOptions.__composerExtend = (c: Composer) => {
107-
const g = getComposer(i18n)
108-
c.locales = computed(() => g.locales.value)
109-
c.localeCodes = computed(() => g.localeCodes.value)
110-
c.baseUrl = computed(() => g.baseUrl.value)
106+
pluginOptions.__composerExtend = (localComposer: Composer) => {
107+
const globalComposer = getComposer(i18n)
108+
localComposer.locales = computed(() => globalComposer.locales.value)
109+
localComposer.localeCodes = computed(() => globalComposer.localeCodes.value)
110+
localComposer.baseUrl = computed(() => globalComposer.baseUrl.value)
111+
let orgComposerDispose: Disposer | undefined
111112
if (isFunction(orgComposerExtend)) {
112-
Reflect.apply(orgComposerExtend, pluginOptions, [c])
113+
orgComposerDispose = Reflect.apply(orgComposerExtend, pluginOptions, [globalComposer])
114+
}
115+
return () => {
116+
orgComposerDispose && orgComposerDispose()
113117
}
114118
}
115-
if (isVueI18n(i18n.global)) {
119+
if (i18n.mode === 'legacy') {
116120
const orgVueI18nExtend = pluginOptions.__vueI18nExtend
117121
pluginOptions.__vueI18nExtend = (vueI18n: VueI18n) => {
118122
extendVueI18n(vueI18n, hooks.onExtendVueI18n)
123+
let orgVueI18nDispose: Disposer | undefined
119124
if (isFunction(orgVueI18nExtend)) {
120-
Reflect.apply(orgVueI18nExtend, pluginOptions, [vueI18n])
125+
orgVueI18nDispose = Reflect.apply(orgVueI18nExtend, pluginOptions, [vueI18n])
126+
}
127+
return () => {
128+
orgVueI18nDispose && orgVueI18nDispose()
121129
}
122130
}
123131
}
124132

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

128-
const composer = getComposer(i18n)
136+
const globalComposer = getComposer(i18n)
129137

130138
// extend global
131-
scope.run(() => extendComposer(composer, { locales, localeCodes, baseUrl, hooks, context }))
132-
if (isVueI18n(i18n.global)) {
133-
extendVueI18n(i18n.global, hooks.onExtendVueI18n)
134-
}
139+
scope.run(() => {
140+
extendComposer(globalComposer, { locales, localeCodes, baseUrl, hooks, context })
141+
if (i18n.mode === 'legacy' && isVueI18n(i18n.global)) {
142+
extendVueI18n(i18n.global, hooks.onExtendVueI18n)
143+
}
144+
})
135145

136146
// extend vue component instance for Vue 3
137147
const app = vue as App
@@ -140,11 +150,12 @@ export function extendI18n<Context = unknown, TI18n extends I18n = I18n>(
140150
? isVue3
141151
? app.config.globalProperties.$i18n
142152
: i18n
153+
// for legacy mode
143154
: isVue2
144155
? i18n
145156
: null
146157
if (exported) {
147-
extendExportedGlobal(exported, composer, hooks.onExtendExportedGlobal)
158+
extendExportedGlobal(exported, globalComposer, hooks.onExtendExportedGlobal)
148159
}
149160

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

165-
// release scope on unmounting
176+
// dispose when app will be unmounting
166177
if (app.unmount) {
167178
const unmountApp = app.unmount
168179
app.unmount = () => {
@@ -203,37 +214,11 @@ function extendComposer<Context = unknown>(composer: Composer, options: VueI18nE
203214
}
204215
}
205216

206-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
207-
function extendExportedGlobal(exported: any, g: Composer, hook?: ExtendExportedGlobalHook) {
208-
const properties: ExtendProperyDescripters[] = [
209-
{
210-
locales: {
211-
get() {
212-
return g.locales.value
213-
}
214-
},
215-
localeCodes: {
216-
get() {
217-
return g.localeCodes.value
218-
}
219-
},
220-
baseUrl: {
221-
get() {
222-
return g.baseUrl.value
223-
}
224-
}
225-
}
226-
]
227-
hook && properties.push(hook(g))
228-
for (const property of properties) {
229-
for (const [key, descriptor] of Object.entries(property)) {
230-
Object.defineProperty(exported, key, descriptor)
231-
}
232-
}
233-
}
234-
235-
function extendVueI18n(vueI18n: VueI18n, hook?: ExtendVueI18nHook): void {
236-
const composer = getComposer(vueI18n)
217+
function extendProperyDescripters(
218+
composer: Composer,
219+
exported: any, // eslint-disable-line @typescript-eslint/no-explicit-any
220+
hook?: ExtendVueI18nHook | ExtendExportedGlobalHook
221+
): void {
237222
const properties: ExtendProperyDescripters[] = [
238223
{
239224
locales: {
@@ -256,11 +241,21 @@ function extendVueI18n(vueI18n: VueI18n, hook?: ExtendVueI18nHook): void {
256241
hook && properties.push(hook(composer))
257242
for (const property of properties) {
258243
for (const [key, descriptor] of Object.entries(property)) {
259-
Object.defineProperty(vueI18n, key, descriptor)
244+
Object.defineProperty(exported, key, descriptor)
260245
}
261246
}
262247
}
263248

249+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
250+
function extendExportedGlobal(exported: any, g: Composer, hook?: ExtendExportedGlobalHook) {
251+
extendProperyDescripters(g, exported, hook)
252+
}
253+
254+
function extendVueI18n(vueI18n: VueI18n, hook?: ExtendVueI18nHook): void {
255+
const c = getComposer(vueI18n)
256+
extendProperyDescripters(c, vueI18n, hook)
257+
}
258+
264259
// eslint-disable-next-line @typescript-eslint/no-explicit-any
265260
function isPluginOptions(options: any): options is VueI18nRoutingPluginOptions {
266261
return isObject(options) && ('inject' in options || '__composerExtend' in options || '__vueI18nExtend' in options)

pnpm-lock.yaml

Lines changed: 44 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)