Skip to content

Commit 3596461

Browse files
authored
improve i18n instance management (#111)
1 parent 0836e04 commit 3596461

File tree

5 files changed

+59
-30
lines changed

5 files changed

+59
-30
lines changed

src/core/context.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@ import {
2626
NumberFormats as NumberFormatsType
2727
} from './types'
2828

29-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
30-
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
31-
k: infer I
32-
) => void
33-
? I
34-
: never
35-
3629
export type Locale = string
3730

3831
export type FallbackLocale =
@@ -57,8 +50,6 @@ export type LocaleMessages<Message = string> = Record<
5750
LocaleMessageDictionary<Message>
5851
>
5952

60-
type NestedPath<T> = { [K in keyof T]: T[K] }
61-
6253
export type RuntimeMissingType = 'translate' | 'datetime' | 'number'
6354
export type RuntimeMissingHandler<Message = string> = (
6455
context: RuntimeCommonContext<Message>,

src/i18n.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ declare module '@vue/runtime-core' {
3535
// eslint-disable-next-line
3636
interface App<HostElement = any> {
3737
__VUE_I18N__?: I18n & I18nInternal
38+
__VUE_I18N_SYMBOL__?: InjectionKey<I18n>
3839
}
3940
}
4041

@@ -145,12 +146,6 @@ export interface ComposerAdditionalOptions {
145146
useScope?: I18nScope
146147
}
147148

148-
/**
149-
* I18n instance injectin key
150-
* @internal
151-
*/
152-
export const I18nSymbol: InjectionKey<I18n> = Symbol.for('vue-i18n')
153-
154149
/**
155150
* I18n factory function
156151
*
@@ -250,6 +245,7 @@ export function createI18n<
250245
const __global = __legacyMode
251246
? createVueI18n(options)
252247
: createComposer(options)
248+
const symbol: InjectionKey<I18n> = Symbol(__DEV__ ? 'vue-i18n' : '')
253249

254250
const i18n = {
255251
// mode
@@ -261,6 +257,7 @@ export function createI18n<
261257
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
262258
app.__VUE_I18N__ = i18n as I18n & I18nInternal
263259
}
260+
app.__VUE_I18N_SYMBOL__ = symbol
264261
apply<Messages, DateTimeFormats, NumberFormats>(app, i18n, ...options)
265262
if (__legacyMode) {
266263
app.mixin(
@@ -384,7 +381,13 @@ export function useI18n<
384381
Options['datetimeFormats'],
385382
Options['numberFormats']
386383
> {
387-
const i18n = inject(I18nSymbol) as I18n<
384+
const instance = getCurrentInstance()
385+
/* istanbul ignore if */
386+
if (instance == null || !instance.appContext.app.__VUE_I18N_SYMBOL__) {
387+
throw createI18nError(I18nErrorCodes.UNEXPECTED_ERROR)
388+
}
389+
390+
const i18n = inject(instance.appContext.app.__VUE_I18N_SYMBOL__) as I18n<
388391
Messages,
389392
DateTimeFormats,
390393
NumberFormats
@@ -406,12 +409,6 @@ export function useI18n<
406409
return global
407410
}
408411

409-
const instance = getCurrentInstance()
410-
/* istanbul ignore if */
411-
if (instance == null) {
412-
throw createI18nError(I18nErrorCodes.UNEXPECTED_ERROR)
413-
}
414-
415412
if (scope === 'parent') {
416413
let composer = getComposer(i18n, instance)
417414
if (composer == null) {

src/plugin.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { App, Component } from 'vue'
2-
import { I18nSymbol, I18n } from './i18n'
2+
import { I18n } from './i18n'
33
import { Translation, NumberFormat, DatetimeFormat } from './components'
44
import { vTDirective } from './directive'
5+
import { I18nErrorCodes, createI18nError } from './errors'
56
import { I18nWarnCodes, getWarnMessage } from './warnings'
67
import { isPlainObject, warn, isBoolean } from './utils'
78

@@ -54,5 +55,8 @@ export function apply<Messages, DateTimeFormats, NumberFormats>(
5455
)
5556

5657
// setup global provider
57-
app.provide(I18nSymbol, i18n)
58+
if (!app.__VUE_I18N_SYMBOL__) {
59+
throw createI18nError(I18nErrorCodes.UNEXPECTED_ERROR)
60+
}
61+
app.provide(app.__VUE_I18N_SYMBOL__, i18n)
5862
}

test/i18n.test.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,6 @@ describe('useI18n', () => {
191191
expect(composer.locale.value).toEqual('ja')
192192
})
193193

194-
test(errorMessages[I18nErrorCodes.NOT_INSLALLED], () => {
195-
expect(() => {
196-
useI18n()
197-
}).toThrowError(errorMessages[I18nErrorCodes.NOT_INSLALLED])
198-
})
199-
200194
test(errorMessages[I18nErrorCodes.NOT_AVAILABLE_IN_LEGACY_MODE], async () => {
201195
const i18n = createI18n({
202196
legacy: true,
@@ -423,3 +417,42 @@ describe('slot reactivity', () => {
423417
expect(html()).toMatchSnapshot('en')
424418
})
425419
})
420+
421+
test('multi instance', async () => {
422+
const i18n1 = createI18n({
423+
locale: 'ja',
424+
messages: {
425+
en: {
426+
hello: 'hello!'
427+
}
428+
}
429+
})
430+
const i18n2 = createI18n({
431+
locale: 'en',
432+
messages: {
433+
ja: {
434+
hello: 'こんにちは!'
435+
}
436+
}
437+
})
438+
439+
const App = defineComponent({
440+
setup() {
441+
const i18n = useI18n({
442+
locale: 'en',
443+
messages: {
444+
en: {
445+
hello: 'hello!'
446+
}
447+
}
448+
})
449+
return { ...i18n }
450+
},
451+
template: `<p>foo</p>`
452+
})
453+
const { app: app1 } = await mount(App, i18n1)
454+
const { app: app2 } = await mount(App, i18n2)
455+
456+
expect(app1.__VUE_I18N_SYMBOL__ !== app2.__VUE_I18N_SYMBOL__).toEqual(true)
457+
expect(i18n1.global).not.toEqual(i18n2.global)
458+
})

test/plugin.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ describe('useI18nComponentName option', () => {
2121
mockWarn.mockImplementation(() => {})
2222

2323
const app = createApp({})
24+
app.__VUE_I18N_SYMBOL__ = Symbol()
2425
const i18n = {} as I18n & I18nInternal
2526

2627
apply(app, i18n)
@@ -32,6 +33,7 @@ describe('useI18nComponentName option', () => {
3233
mockWarn.mockImplementation(() => {})
3334

3435
const app = createApp({})
36+
app.__VUE_I18N_SYMBOL__ = Symbol()
3537
const i18n = {} as I18n & I18nInternal
3638

3739
apply(app, i18n, { useI18nComponentName: true })
@@ -47,6 +49,7 @@ describe('useI18nComponentName option', () => {
4749
describe('globalInstall option', () => {
4850
test('default', () => {
4951
const app = createApp({})
52+
app.__VUE_I18N_SYMBOL__ = Symbol()
5053
const i18n = {} as I18n & I18nInternal
5154
const spy = jest.spyOn(app, 'component')
5255

@@ -56,6 +59,7 @@ describe('globalInstall option', () => {
5659

5760
test('false', () => {
5861
const app = createApp({})
62+
app.__VUE_I18N_SYMBOL__ = Symbol()
5963
const i18n = {} as I18n & I18nInternal
6064
const spy = jest.spyOn(app, 'component')
6165

0 commit comments

Comments
 (0)