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

improve i18n instance management #111

Merged
merged 1 commit into from
Sep 13, 2020
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
9 changes: 0 additions & 9 deletions src/core/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@ import {
NumberFormats as NumberFormatsType
} from './types'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never

export type Locale = string

export type FallbackLocale =
Expand All @@ -57,8 +50,6 @@ export type LocaleMessages<Message = string> = Record<
LocaleMessageDictionary<Message>
>

type NestedPath<T> = { [K in keyof T]: T[K] }

export type RuntimeMissingType = 'translate' | 'datetime' | 'number'
export type RuntimeMissingHandler<Message = string> = (
context: RuntimeCommonContext<Message>,
Expand Down
23 changes: 10 additions & 13 deletions src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ declare module '@vue/runtime-core' {
// eslint-disable-next-line
interface App<HostElement = any> {
__VUE_I18N__?: I18n & I18nInternal
__VUE_I18N_SYMBOL__?: InjectionKey<I18n>
}
}

Expand Down Expand Up @@ -145,12 +146,6 @@ export interface ComposerAdditionalOptions {
useScope?: I18nScope
}

/**
* I18n instance injectin key
* @internal
*/
export const I18nSymbol: InjectionKey<I18n> = Symbol.for('vue-i18n')

/**
* I18n factory function
*
Expand Down Expand Up @@ -250,6 +245,7 @@ export function createI18n<
const __global = __legacyMode
? createVueI18n(options)
: createComposer(options)
const symbol: InjectionKey<I18n> = Symbol(__DEV__ ? 'vue-i18n' : '')

const i18n = {
// mode
Expand All @@ -261,6 +257,7 @@ export function createI18n<
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
app.__VUE_I18N__ = i18n as I18n & I18nInternal
}
app.__VUE_I18N_SYMBOL__ = symbol
apply<Messages, DateTimeFormats, NumberFormats>(app, i18n, ...options)
if (__legacyMode) {
app.mixin(
Expand Down Expand Up @@ -384,7 +381,13 @@ export function useI18n<
Options['datetimeFormats'],
Options['numberFormats']
> {
const i18n = inject(I18nSymbol) as I18n<
const instance = getCurrentInstance()
/* istanbul ignore if */
if (instance == null || !instance.appContext.app.__VUE_I18N_SYMBOL__) {
throw createI18nError(I18nErrorCodes.UNEXPECTED_ERROR)
}

const i18n = inject(instance.appContext.app.__VUE_I18N_SYMBOL__) as I18n<
Messages,
DateTimeFormats,
NumberFormats
Expand All @@ -406,12 +409,6 @@ export function useI18n<
return global
}

const instance = getCurrentInstance()
/* istanbul ignore if */
if (instance == null) {
throw createI18nError(I18nErrorCodes.UNEXPECTED_ERROR)
}

if (scope === 'parent') {
let composer = getComposer(i18n, instance)
if (composer == null) {
Expand Down
8 changes: 6 additions & 2 deletions src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { App, Component } from 'vue'
import { I18nSymbol, I18n } from './i18n'
import { I18n } from './i18n'
import { Translation, NumberFormat, DatetimeFormat } from './components'
import { vTDirective } from './directive'
import { I18nErrorCodes, createI18nError } from './errors'
import { I18nWarnCodes, getWarnMessage } from './warnings'
import { isPlainObject, warn, isBoolean } from './utils'

Expand Down Expand Up @@ -54,5 +55,8 @@ export function apply<Messages, DateTimeFormats, NumberFormats>(
)

// setup global provider
app.provide(I18nSymbol, i18n)
if (!app.__VUE_I18N_SYMBOL__) {
throw createI18nError(I18nErrorCodes.UNEXPECTED_ERROR)
}
app.provide(app.__VUE_I18N_SYMBOL__, i18n)
}
45 changes: 39 additions & 6 deletions test/i18n.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,6 @@ describe('useI18n', () => {
expect(composer.locale.value).toEqual('ja')
})

test(errorMessages[I18nErrorCodes.NOT_INSLALLED], () => {
expect(() => {
useI18n()
}).toThrowError(errorMessages[I18nErrorCodes.NOT_INSLALLED])
})

test(errorMessages[I18nErrorCodes.NOT_AVAILABLE_IN_LEGACY_MODE], async () => {
const i18n = createI18n({
legacy: true,
Expand Down Expand Up @@ -423,3 +417,42 @@ describe('slot reactivity', () => {
expect(html()).toMatchSnapshot('en')
})
})

test('multi instance', async () => {
const i18n1 = createI18n({
locale: 'ja',
messages: {
en: {
hello: 'hello!'
}
}
})
const i18n2 = createI18n({
locale: 'en',
messages: {
ja: {
hello: 'こんにちは!'
}
}
})

const App = defineComponent({
setup() {
const i18n = useI18n({
locale: 'en',
messages: {
en: {
hello: 'hello!'
}
}
})
return { ...i18n }
},
template: `<p>foo</p>`
})
const { app: app1 } = await mount(App, i18n1)
const { app: app2 } = await mount(App, i18n2)

expect(app1.__VUE_I18N_SYMBOL__ !== app2.__VUE_I18N_SYMBOL__).toEqual(true)
expect(i18n1.global).not.toEqual(i18n2.global)
})
4 changes: 4 additions & 0 deletions test/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('useI18nComponentName option', () => {
mockWarn.mockImplementation(() => {})

const app = createApp({})
app.__VUE_I18N_SYMBOL__ = Symbol()
const i18n = {} as I18n & I18nInternal

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

const app = createApp({})
app.__VUE_I18N_SYMBOL__ = Symbol()
const i18n = {} as I18n & I18nInternal

apply(app, i18n, { useI18nComponentName: true })
Expand All @@ -47,6 +49,7 @@ describe('useI18nComponentName option', () => {
describe('globalInstall option', () => {
test('default', () => {
const app = createApp({})
app.__VUE_I18N_SYMBOL__ = Symbol()
const i18n = {} as I18n & I18nInternal
const spy = jest.spyOn(app, 'component')

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

test('false', () => {
const app = createApp({})
app.__VUE_I18N_SYMBOL__ = Symbol()
const i18n = {} as I18n & I18nInternal
const spy = jest.spyOn(app, 'component')

Expand Down