Skip to content

Commit 94f230d

Browse files
authored
feat: compatible Vue3 vapor mode (#2299)
* feat: support Vue3 vapor mode * fix: extend GenericComponentInstance interface * fix: composer instance keeping * fix: forgot type annotions
1 parent 72f63b7 commit 94f230d

File tree

10 files changed

+346
-218
lines changed

10 files changed

+346
-218
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@
162162
"pnpm": {
163163
"overrides": {
164164
"vite": "^6.0.0",
165-
"vue": "3.5.22"
165+
"vue": "3.6.0-alpha.2"
166166
},
167167
"onlyBuiltDependencies": [
168168
"@parcel/watcher",

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {
3737
isString,
3838
warn
3939
} from '@intlify/shared'
40-
import { computed, getCurrentInstance, ref, shallowRef, watch } from 'vue'
40+
import { computed, ref, shallowRef, watch } from 'vue'
4141
import { I18nErrorCodes, createI18nError } from './errors'
4242
import { VERSION } from './misc'
4343
import {
@@ -49,7 +49,13 @@ import {
4949
SetPluralRulesSymbol,
5050
TranslateVNodeSymbol
5151
} from './symbols'
52-
import { createTextNode, getComponentOptions, getLocaleMessages, handleFlatJson } from './utils'
52+
import {
53+
createTextNode,
54+
getComponentOptions,
55+
getCurrentInstance,
56+
getLocaleMessages,
57+
handleFlatJson
58+
} from './utils'
5359
import { I18nWarnCodes, getWarnMessage } from './warnings'
5460

5561
import type {
@@ -104,6 +110,7 @@ import type { VueDevToolsEmitter } from '@intlify/devtools-types'
104110
import type {
105111
ComponentInternalInstance,
106112
ComputedRef,
113+
GenericComponentInstance,
107114
VNode,
108115
VNodeArrayChildren,
109116
WritableComputedRef
@@ -221,7 +228,7 @@ export type DefaultNumberFormatSchema<
221228
export type MissingHandler = (
222229
locale: Locale,
223230
key: Path,
224-
instance?: ComponentInternalInstance,
231+
instance?: ComponentInternalInstance | GenericComponentInstance,
225232
type?: string
226233
) => string | void
227234

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

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import type {
1616
Hooks,
1717
InspectedComponentData
1818
} from '@vue/devtools-api'
19-
import type { App, ComponentInternalInstance } from 'vue'
19+
import type { App, ComponentInternalInstance, GenericComponentInstance } from 'vue'
2020
import type { Composer } from './composer'
2121
import type { I18n, I18nInternal } from './i18n'
2222

@@ -61,12 +61,8 @@ export async function enableDevTools(app: App, i18n: _I18n): Promise<boolean> {
6161
})
6262

6363
api.on.inspectComponent(({ componentInstance, instanceData }) => {
64-
if (
65-
componentInstance.vnode.el &&
66-
componentInstance.vnode.el.__VUE_I18N__ &&
67-
instanceData
68-
) {
69-
inspectComposer(instanceData, componentInstance.vnode.el.__VUE_I18N__ as Composer)
64+
if (componentInstance.__VUE_I18N__ && instanceData) {
65+
inspectComposer(instanceData, componentInstance.__VUE_I18N__ as Composer)
7066
}
7167
})
7268

@@ -83,7 +79,7 @@ export async function enableDevTools(app: App, i18n: _I18n): Promise<boolean> {
8379
}
8480
})
8581

86-
const roots = new Map<App, ComponentInternalInstance>()
82+
const roots = new Map<App, ComponentInternalInstance | GenericComponentInstance>()
8783
api.on.getInspectorState(async payload => {
8884
if (payload.app === app && payload.inspectorId === 'vue-i18n-resource-inspector') {
8985
api.unhighlightElement()
@@ -138,9 +134,9 @@ function updateComponentTreeTags(
138134
): void {
139135
// prettier-ignore
140136
const global = i18n.global
141-
if (instance && instance.vnode.el && instance.vnode.el.__VUE_I18N__) {
137+
if (instance && instance.__VUE_I18N__) {
142138
// add custom tags local scope only
143-
if (instance.vnode.el.__VUE_I18N__ !== global) {
139+
if (instance.__VUE_I18N__ !== global) {
144140
const tag = {
145141
label: `i18n (${getI18nScopeLable(instance)} Scope)`,
146142
textColor: 0x000000,
@@ -263,8 +259,11 @@ function registerScope(payload: HookPayloads[Hooks.GET_INSPECTOR_TREE], i18n: _I
263259
}
264260
}
265261

266-
function getComponentInstance(nodeId: string, i18n: _I18n): ComponentInternalInstance | null {
267-
let instance: ComponentInternalInstance | null = null
262+
function getComponentInstance(
263+
nodeId: string,
264+
i18n: _I18n
265+
): ComponentInternalInstance | GenericComponentInstance | null {
266+
let instance: ComponentInternalInstance | GenericComponentInstance | null = null
268267

269268
if (nodeId !== 'global') {
270269
for (const [component, composer] of i18n.__instances.entries()) {

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

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,24 @@ import {
77
makeSymbol,
88
warn
99
} from '@intlify/shared'
10-
import { effectScope, getCurrentInstance, inject, isRef, onMounted, onUnmounted } from 'vue'
10+
import { effectScope, inject, isRef, onMounted, onUnmounted } from 'vue'
1111
import { createComposer } from './composer'
1212
import { addTimelineEvent, enableDevTools } from './devtools'
1313
import { I18nErrorCodes, createI18nError } from './errors'
1414
import { apply as applyPlugin } from './plugin/next'
1515
import { DisableEmitter, DisposeSymbol, EnableEmitter } from './symbols'
16-
import { adjustI18nResources, getComponentOptions } from './utils'
16+
import { adjustI18nResources, getComponentOptions, getCurrentInstance } from './utils'
1717
import { I18nWarnCodes, getWarnMessage } from './warnings'
1818

1919
import type { FallbackLocale, Locale, LocaleParams, SchemaParams } from '@intlify/core-base'
2020
import type { VueDevToolsEmitter, VueDevToolsEmitterEvents } from '@intlify/devtools-types'
21-
import type { App, ComponentInternalInstance, EffectScope, InjectionKey } from 'vue'
21+
import type {
22+
App,
23+
ComponentInternalInstance,
24+
EffectScope,
25+
GenericComponentInstance,
26+
InjectionKey
27+
} from 'vue'
2228
import type {
2329
Composer,
2430
ComposerInternalOptions,
@@ -136,17 +142,17 @@ export interface I18nInternal<
136142
OptionLocale = Locale
137143
> {
138144
__instances: Map<
139-
ComponentInternalInstance,
145+
ComponentInternalInstance | GenericComponentInstance,
140146
Composer<Messages, DateTimeFormats, NumberFormats, OptionLocale>
141147
>
142148
__getInstance<Instance extends Composer<Messages, DateTimeFormats, NumberFormats, OptionLocale>>(
143-
component: ComponentInternalInstance
149+
component: ComponentInternalInstance | GenericComponentInstance
144150
): Instance | null
145151
__setInstance<Instance extends Composer<Messages, DateTimeFormats, NumberFormats, OptionLocale>>(
146-
component: ComponentInternalInstance,
152+
component: ComponentInternalInstance | GenericComponentInstance,
147153
instance: Instance
148154
): void
149-
__deleteInstance(component: ComponentInternalInstance): void
155+
__deleteInstance(component: ComponentInternalInstance | GenericComponentInstance): void
150156
__composerExtend?: ComposerExtender
151157
}
152158

@@ -314,17 +320,22 @@ export function createI18n(options: any = {}): any {
314320
const __globalInjection = isBoolean(options.globalInjection)
315321
? options.globalInjection
316322
: true
317-
const __instances = new Map<ComponentInternalInstance, Composer>()
323+
const __instances = new Map<ComponentInternalInstance | GenericComponentInstance, Composer>()
318324
const [globalScope, __global] = createGlobal(options)
319325
const symbol: InjectionKey<I18n> | string = /* #__PURE__*/ makeSymbol(__DEV__ ? 'vue-i18n' : '')
320326

321-
function __getInstance(component: ComponentInternalInstance): Composer | null {
327+
function __getInstance(
328+
component: ComponentInternalInstance | GenericComponentInstance
329+
): Composer | null {
322330
return __instances.get(component) || null
323331
}
324-
function __setInstance(component: ComponentInternalInstance, instance: Composer): void {
332+
function __setInstance(
333+
component: ComponentInternalInstance | GenericComponentInstance,
334+
instance: Composer
335+
): void {
325336
__instances.set(component, instance)
326337
}
327-
function __deleteInstance(component: ComponentInternalInstance): void {
338+
function __deleteInstance(component: ComponentInternalInstance | GenericComponentInstance): void {
328339
__instances.delete(component)
329340
}
330341

@@ -556,7 +567,7 @@ function createGlobal(options: I18nOptions): [EffectScope, Composer] {
556567
return [scope, obj]
557568
}
558569

559-
function getI18nInstance(instance: ComponentInternalInstance): I18n {
570+
function getI18nInstance(instance: ComponentInternalInstance | GenericComponentInstance): I18n {
560571
const i18n = inject(
561572
!instance.isCE ? instance.appContext.app.__VUE_I18N_SYMBOL__! : I18nInjectionKey
562573
)
@@ -588,12 +599,13 @@ function getGlobalComposer(i18n: I18n): Composer {
588599

589600
function getComposer(
590601
i18n: I18n,
591-
target: ComponentInternalInstance,
602+
target: ComponentInternalInstance | GenericComponentInstance,
592603
useComponent = false
593604
): Composer | null {
594605
let composer: Composer | null = null
595606
const root = target.root
596-
let current: ComponentInternalInstance | null = getParentComponentInstance(target, useComponent)
607+
let current: ComponentInternalInstance | GenericComponentInstance | null =
608+
getParentComponentInstance(target, useComponent)
597609
while (current != null) {
598610
const i18nInternal = i18n as unknown as I18nInternal
599611
composer = i18nInternal.__getInstance(current)
@@ -610,7 +622,7 @@ function getComposer(
610622
}
611623

612624
function getParentComponentInstance(
613-
target: ComponentInternalInstance | null,
625+
target: ComponentInternalInstance | GenericComponentInstance | null,
614626
useComponent = false
615627
) {
616628
if (target == null) {
@@ -622,16 +634,16 @@ function getParentComponentInstance(
622634

623635
function setupLifeCycle(
624636
i18n: I18nInternal,
625-
target: ComponentInternalInstance,
637+
target: ComponentInternalInstance | GenericComponentInstance,
626638
composer: Composer
627639
): void {
628640
let emitter: VueDevToolsEmitter | null = null
629641

630642
// eslint-disable-next-line vue-composable/lifecycle-placement -- NOTE(kazupon): not Vue component
631643
onMounted(() => {
632644
// inject composer instance to DOM for intlify-devtools
633-
if ((__DEV__ || __FEATURE_PROD_VUE_DEVTOOLS__) && !__NODE_JS__ && target.vnode.el) {
634-
target.vnode.el.__VUE_I18N__ = composer
645+
if ((__DEV__ || __FEATURE_PROD_VUE_DEVTOOLS__) && !__NODE_JS__) {
646+
target.__VUE_I18N__ = composer
635647
emitter = createEmitter<VueDevToolsEmitterEvents>()
636648
// eslint-disable-next-line @typescript-eslint/no-explicit-any
637649
const _composer = composer as any
@@ -646,15 +658,10 @@ function setupLifeCycle(
646658
const _composer = composer as any
647659

648660
// remove composer instance from DOM for intlify-devtools
649-
if (
650-
(__DEV__ || __FEATURE_PROD_VUE_DEVTOOLS__) &&
651-
!__NODE_JS__ &&
652-
target.vnode.el &&
653-
target.vnode.el.__VUE_I18N__
654-
) {
661+
if ((__DEV__ || __FEATURE_PROD_VUE_DEVTOOLS__) && !__NODE_JS__ && target.__VUE_I18N__) {
655662
emitter && emitter.off('*', addTimelineEvent)
656663
_composer[DisableEmitter] && _composer[DisableEmitter]()
657-
delete target.vnode.el.__VUE_I18N__
664+
delete target.__VUE_I18N__
658665
}
659666
i18n.__deleteInstance(target)
660667

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@ import {
1010
isString,
1111
warn
1212
} from '@intlify/shared'
13+
import * as Vue from 'vue'
1314
import { Text, createVNode } from 'vue'
1415
import { I18nWarnCodes, getWarnMessage } from './warnings'
1516

1617
import type { Locale, MessageResolver } from '@intlify/core-base'
17-
import type { ComponentInternalInstance, RendererElement, RendererNode } from 'vue'
18+
import type {
19+
ComponentInternalInstance,
20+
GenericComponentInstance,
21+
RendererElement,
22+
RendererNode
23+
} from 'vue'
1824
import type { Composer, ComposerOptions, CustomBlocks, VueMessageType } from './composer'
1925

2026
type GetLocaleMessagesOptions<Messages = {}> = {
@@ -154,7 +160,9 @@ export function getLocaleMessages<Messages = {}>(
154160
return ret as { [K in keyof Messages]: Messages[K] }
155161
}
156162

157-
export function getComponentOptions(instance: ComponentInternalInstance): any {
163+
export function getComponentOptions(
164+
instance: ComponentInternalInstance | GenericComponentInstance
165+
): any {
158166
return instance.type
159167
}
160168

@@ -205,3 +213,8 @@ export function adjustI18nResources(
205213
export function createTextNode(key: string): any {
206214
return createVNode(Text, null, key, 0)
207215
}
216+
217+
export function getCurrentInstance(): GenericComponentInstance | ComponentInternalInstance | null {
218+
// @ts-ignore -- NOTE(kazupon): for Vue 3.6
219+
return Vue.currentInstance || Vue.getCurrentInstance()
220+
}

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { InjectionKey } from 'vue'
2+
import type { Composer } from './composer'
23
import type { I18n, I18nInternal } from './i18n'
34

45
declare module 'vue' {
@@ -14,8 +15,26 @@ declare module 'vue' {
1415
export interface ComponentInternalInstance {
1516
/**
1617
* @internal
17-
* iskk custom element?
18+
* whether target component is custom element
1819
*/
1920
isCE?: boolean
21+
/**
22+
* @internal
23+
* for vue/devtools i18n composer hook
24+
*/
25+
__VUE_I18N__?: Composer
26+
}
27+
28+
export interface GenericComponentInstance {
29+
/**
30+
* @internal
31+
* whether target component is custom element
32+
*/
33+
isCE?: boolean
34+
/**
35+
* @internal
36+
* for vue/devtools i18n composer hook
37+
*/
38+
__VUE_I18N__?: Composer
2039
}
2140
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ import {
2323
setDevToolsHook
2424
} from '@intlify/core-base'
2525
import { createEmitter } from '@intlify/shared'
26-
import { defineComponent, defineCustomElement, getCurrentInstance, h, nextTick, ref } from 'vue'
26+
import { defineComponent, defineCustomElement, h, nextTick, ref } from 'vue'
2727
import { errorMessages, I18nErrorCodes } from '../src/errors'
2828
import { createI18n, useI18n } from '../src/i18n'
29+
import { getCurrentInstance } from '../src/utils'
2930
import { pluralRules as _pluralRules, mount, randStr } from './helper'
3031

3132
import type { IntlifyDevToolsEmitterHooks } from '@intlify/devtools-types'

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ import {
2121
resolveValue,
2222
setDevToolsHook
2323
} from '@intlify/core-base'
24-
import { defineComponent, getCurrentInstance, nextTick, ref } from 'vue'
24+
import { defineComponent, nextTick, ref } from 'vue'
2525
import { createI18n, useI18n } from '../src/i18n'
26+
import { getCurrentInstance } from '../src/utils'
2627
import { ast } from './fixtures/ast'
2728
import { mount } from './helper'
2829

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@
22
* @vitest-environment jsdom
33
*/
44

5-
import { h, provide, nextTick, defineCustomElement, getCurrentInstance } from 'vue'
65
import {
76
compile,
7+
fallbackWithLocaleChain,
8+
registerLocaleFallbacker,
89
registerMessageCompiler,
9-
resolveValue,
1010
registerMessageResolver,
11-
fallbackWithLocaleChain,
12-
registerLocaleFallbacker
11+
resolveValue
1312
} from '@intlify/core-base'
14-
import { createI18n, useI18n, I18nInjectionKey } from '../src/index'
13+
import { defineCustomElement, h, nextTick, provide } from 'vue'
14+
import { createI18n, I18nInjectionKey, useI18n } from '../src/index'
15+
import { getCurrentInstance } from '../src/utils'
1516
import { randStr } from './helper'
1617

17-
import type { VueElement, ComponentOptions } from 'vue'
18+
import type { ComponentOptions, VueElement } from 'vue'
1819

1920
const container = document.createElement('div')
2021
document.body.appendChild(container)

0 commit comments

Comments
 (0)