88 shallowRef ,
99 isRef ,
1010 ref ,
11- computed
11+ computed ,
12+ effectScope
1213} from 'vue'
1314import {
1415 inBrowser ,
@@ -47,7 +48,7 @@ import {
4748 adjustI18nResources
4849} from './utils'
4950
50- import type { ComponentInternalInstance , App } from 'vue'
51+ import type { ComponentInternalInstance , App , EffectScope } from 'vue'
5152import type {
5253 Locale ,
5354 Path ,
@@ -234,6 +235,10 @@ export interface I18n<
234235 * @param options - An install options
235236 */
236237 install ( app : App , ...options : unknown [ ] ) : void
238+ /**
239+ * Release global scope resource
240+ */
241+ dispose ( ) : void
237242}
238243
239244/**
@@ -492,7 +497,11 @@ export function createI18n(options: any = {}, VueI18nLegacy?: any): any {
492497 ? ! ! options . allowComposition
493498 : true
494499 const __instances = new Map < ComponentInternalInstance , VueI18n | Composer > ( )
495- const __global = createGlobal ( options , __legacyMode , VueI18nLegacy )
500+ const [ globalScope , __global ] = createGlobal (
501+ options ,
502+ __legacyMode ,
503+ VueI18nLegacy
504+ )
496505 const symbol : InjectionKey < I18n > | string = /* #__PURE__*/ makeSymbol (
497506 __DEV__ ? 'vue-i18n' : ''
498507 )
@@ -559,6 +568,13 @@ export function createI18n(options: any = {}, VueI18nLegacy?: any): any {
559568 )
560569 }
561570
571+ // release global scope
572+ const unmountApp = app . unmount
573+ app . unmount = ( ) => {
574+ i18n . dispose ( )
575+ unmountApp ( )
576+ }
577+
562578 // setup vue-devtools plugin
563579 if ( ( __DEV__ || __FEATURE_PROD_VUE_DEVTOOLS__ ) && ! __NODE_JS__ ) {
564580 const ret = await enableDevTools ( app , i18n as _I18n )
@@ -584,6 +600,9 @@ export function createI18n(options: any = {}, VueI18nLegacy?: any): any {
584600 get global ( ) {
585601 return __global
586602 } ,
603+ dispose ( ) : void {
604+ globalScope . stop ( )
605+ } ,
587606 // @internal
588607 __instances,
589608 // @internal
@@ -598,6 +617,7 @@ export function createI18n(options: any = {}, VueI18nLegacy?: any): any {
598617 // extend legacy VueI18n instance
599618
600619 const i18n = ( __global as any ) [ LegacyInstanceSymbol ] // eslint-disable-line @typescript-eslint/no-explicit-any
620+ let _localeWatcher : Function | null = null
601621 Object . defineProperty ( i18n , 'global' , {
602622 get ( ) {
603623 return __global
@@ -631,11 +651,21 @@ export function createI18n(options: any = {}, VueI18nLegacy?: any): any {
631651 __FEATURE_FULL_INSTALL__ && applyBridge ( Vue , ...options )
632652
633653 if ( ! __legacyMode && __globalInjection ) {
634- injectGlobalFieldsForBridge ( Vue , i18n , __global as Composer )
654+ _localeWatcher = injectGlobalFieldsForBridge (
655+ Vue ,
656+ i18n ,
657+ __global as Composer
658+ )
635659 }
636660 Vue . mixin ( defineMixinBridge ( i18n , _legacyVueI18n ) )
637661 }
638662 } )
663+ Object . defineProperty ( i18n , 'dispose' , {
664+ value : ( ) : void => {
665+ _localeWatcher && _localeWatcher ( )
666+ globalScope . stop ( )
667+ }
668+ } )
639669 const methodMap = {
640670 __getInstance,
641671 __setInstance,
@@ -861,16 +891,26 @@ function createGlobal(
861891 options : I18nOptions ,
862892 legacyMode : boolean ,
863893 VueI18nLegacy : any // eslint-disable-line @typescript-eslint/no-explicit-any
864- ) : VueI18n | Composer {
894+ ) : [ EffectScope , VueI18n | Composer ] {
895+ const scope = effectScope ( )
865896 if ( ! __BRIDGE__ ) {
866- return ! __LITE__ && __FEATURE_LEGACY_API__ && legacyMode
867- ? createVueI18n ( options , VueI18nLegacy )
868- : createComposer ( options , VueI18nLegacy )
897+ const obj =
898+ ! __LITE__ && __FEATURE_LEGACY_API__ && legacyMode
899+ ? scope . run ( ( ) => createVueI18n ( options , VueI18nLegacy ) )
900+ : scope . run ( ( ) => createComposer ( options , VueI18nLegacy ) )
901+ if ( obj == null ) {
902+ throw createI18nError ( I18nErrorCodes . UNEXPECTED_ERROR )
903+ }
904+ return [ scope , obj ]
869905 } else {
870906 if ( ! isLegacyVueI18n ( VueI18nLegacy ) ) {
871907 throw createI18nError ( I18nErrorCodes . NOT_COMPATIBLE_LEGACY_VUE_I18N )
872908 }
873- return createComposer ( options , VueI18nLegacy )
909+ const obj = scope . run ( ( ) => createComposer ( options , VueI18nLegacy ) )
910+ if ( obj == null ) {
911+ throw createI18nError ( I18nErrorCodes . UNEXPECTED_ERROR )
912+ }
913+ return [ scope , obj ]
874914 }
875915}
876916
@@ -1578,11 +1618,11 @@ function injectGlobalFieldsForBridge(
15781618 Vue : any , // eslint-disable-line @typescript-eslint/no-explicit-any
15791619 i18n : any , // eslint-disable-line @typescript-eslint/no-explicit-any
15801620 composer : Composer
1581- ) : void {
1621+ ) : Function {
15821622 // The composition mode in vue-i18n-bridge is `$18n` is the VueI18n instance.
15831623 // so we need to tell composer to change the locale.
15841624 // If we don't do, things like `$t` that are injected will not be reacted.
1585- i18n . watchLocale ( composer )
1625+ const watcher = i18n . watchLocale ( composer ) as Function
15861626
15871627 // define fowardcompatible vue-i18n-next inject fields with `globalInjection`
15881628 Vue . prototype . $t = function ( ...args : unknown [ ] ) {
@@ -1596,4 +1636,6 @@ function injectGlobalFieldsForBridge(
15961636 Vue . prototype . $n = function ( ...args : unknown [ ] ) {
15971637 return Reflect . apply ( composer . n , composer , [ ...args ] )
15981638 }
1639+
1640+ return watcher
15991641}
0 commit comments