File tree Expand file tree Collapse file tree 3 files changed +70
-2
lines changed Expand file tree Collapse file tree 3 files changed +70
-2
lines changed Original file line number Diff line number Diff line change @@ -22,7 +22,9 @@ export const I18nErrorCodes = {
22
22
CANNOT_SETUP_VUE_DEVTOOLS_PLUGIN : 30 ,
23
23
NOT_INSTALLED_WITH_PROVIDE : 31 ,
24
24
// unexpected error
25
- UNEXPECTED_ERROR : 32
25
+ UNEXPECTED_ERROR : 32 ,
26
+ // duplicate `useI18n` calling
27
+ DUPLICATE_USE_I18N_CALLING : 33
26
28
} as const
27
29
28
30
type I18nErrorCodes = ( typeof I18nErrorCodes ) [ keyof typeof I18nErrorCodes ]
@@ -49,5 +51,7 @@ export const errorMessages: { [code: number]: string } = {
49
51
[ I18nErrorCodes . INVALID_VALUE ] : `Invalid value` ,
50
52
[ I18nErrorCodes . CANNOT_SETUP_VUE_DEVTOOLS_PLUGIN ] : `Cannot setup vue-devtools plugin` ,
51
53
[ 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.'
53
57
}
Original file line number Diff line number Diff line change @@ -610,6 +610,10 @@ export function useI18n<
610
610
setupLifeCycle ( i18nInternal , instance , composer )
611
611
612
612
i18nInternal . __setInstance ( instance , composer )
613
+ } else {
614
+ if ( __DEV__ && scope === 'local' ) {
615
+ throw createI18nError ( I18nErrorCodes . DUPLICATE_USE_I18N_CALLING )
616
+ }
613
617
}
614
618
615
619
return composer as unknown as Composer <
Original file line number Diff line number Diff line change @@ -344,6 +344,66 @@ describe('useI18n', () => {
344
344
errorMessages [ I18nErrorCodes . NOT_INSTALLED_WITH_PROVIDE ]
345
345
)
346
346
} )
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
+ } )
347
407
} )
348
408
349
409
test ( 'slot reactivity' , async ( ) => {
You can’t perform that action at this time.
0 commit comments