Skip to content

Commit 9c7573e

Browse files
authored
fix: error on duplicate useI18n calling on local scope (#2204)
* fix: error on duplicate useI18n calling on local scope * fix
1 parent d4c6e3a commit 9c7573e

File tree

3 files changed

+70
-2
lines changed

3 files changed

+70
-2
lines changed

packages/vue-i18n-core/src/errors.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ export const I18nErrorCodes = {
2222
CANNOT_SETUP_VUE_DEVTOOLS_PLUGIN: 30,
2323
NOT_INSTALLED_WITH_PROVIDE: 31,
2424
// unexpected error
25-
UNEXPECTED_ERROR: 32
25+
UNEXPECTED_ERROR: 32,
26+
// duplicate `useI18n` calling
27+
DUPLICATE_USE_I18N_CALLING: 33
2628
} as const
2729

2830
type I18nErrorCodes = (typeof I18nErrorCodes)[keyof typeof I18nErrorCodes]
@@ -49,5 +51,7 @@ export const errorMessages: { [code: number]: string } = {
4951
[I18nErrorCodes.INVALID_VALUE]: `Invalid value`,
5052
[I18nErrorCodes.CANNOT_SETUP_VUE_DEVTOOLS_PLUGIN]: `Cannot setup vue-devtools plugin`,
5153
[I18nErrorCodes.NOT_INSTALLED_WITH_PROVIDE]:
52-
'Need to install with `provide` function'
54+
'Need to install with `provide` function',
55+
[I18nErrorCodes.DUPLICATE_USE_I18N_CALLING]:
56+
'Duplicate local-scope `useI18n` call detected. Call `useI18n` only once per component.'
5357
}

packages/vue-i18n-core/src/i18n.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,10 @@ export function useI18n<
610610
setupLifeCycle(i18nInternal, instance, composer)
611611

612612
i18nInternal.__setInstance(instance, composer)
613+
} else {
614+
if (__DEV__ && scope === 'local') {
615+
throw createI18nError(I18nErrorCodes.DUPLICATE_USE_I18N_CALLING)
616+
}
613617
}
614618

615619
return composer as unknown as Composer<

packages/vue-i18n-core/test/i18n.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,66 @@ describe('useI18n', () => {
344344
errorMessages[I18nErrorCodes.NOT_INSTALLED_WITH_PROVIDE]
345345
)
346346
})
347+
348+
test(errorMessages[I18nErrorCodes.DUPLICATE_USE_I18N_CALLING], async () => {
349+
const i18n = createI18n({
350+
legacy: false,
351+
locale: 'en',
352+
fallbackLocale: ['en'],
353+
messages: {
354+
en: { hello: 'hello!' }
355+
}
356+
})
357+
358+
const useMyComposable = () => {
359+
const count = ref(0)
360+
const { t } = useI18n({
361+
messages: {
362+
en: {
363+
there: 'hi there! {count}'
364+
}
365+
}
366+
})
367+
return { message: t('there', { count: count.value }) }
368+
}
369+
370+
let error = ''
371+
const App = defineComponent({
372+
setup() {
373+
let message: string = ''
374+
let t: any // eslint-disable-line @typescript-eslint/no-explicit-any
375+
try {
376+
const i18n = useI18n({
377+
messages: {
378+
en: {
379+
hi: 'hi!'
380+
}
381+
}
382+
})
383+
t = i18n.t
384+
const ret = useMyComposable()
385+
message = ret.message
386+
} catch (e: any) {
387+
error = e.message
388+
}
389+
return { t, message, error }
390+
},
391+
template: `
392+
<h1>Root</h1>
393+
<form>
394+
<select v-model="locale">
395+
<option value="en">en</option>
396+
<option value="ja">ja</option>
397+
</select>
398+
</form>
399+
<p>{{ t('hi') }}</p>
400+
<p>{{ message }}</p>
401+
<p>{{ error }}</p>
402+
`
403+
})
404+
await mount(App, i18n as any) // eslint-disable-line @typescript-eslint/no-explicit-any
405+
expect(error).toBe(errorMessages[I18nErrorCodes.DUPLICATE_USE_I18N_CALLING])
406+
})
347407
})
348408

349409
test('slot reactivity', async () => {

0 commit comments

Comments
 (0)