Skip to content

Commit ea7f768

Browse files
authored
fix: Resolve the message in components which was rendered with slots, for about <i18n-t>, <i18n-n> and <i18n-d> (#1416)
* fix: Resolve the message in the render component in which the slot was rendered, for `<i18n-t>`, `<i18n-n>` and `<i18n-d>` * fix: for bridge
1 parent 59e5452 commit ea7f768

File tree

3 files changed

+181
-4
lines changed

3 files changed

+181
-4
lines changed

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1021,7 +1021,10 @@ function getComposer(
10211021
): Composer | null {
10221022
let composer: Composer | null = null
10231023
const root = target.root
1024-
let current: ComponentInternalInstance | null = target.parent
1024+
let current: ComponentInternalInstance | null = getParentComponentInstance(
1025+
target,
1026+
useComponent
1027+
)
10251028
while (current != null) {
10261029
const i18nInternal = i18n as unknown as I18nInternal
10271030
if (i18n.mode === 'composition') {
@@ -1053,6 +1056,23 @@ function getComposer(
10531056
return composer
10541057
}
10551058

1059+
function getParentComponentInstance(
1060+
target: ComponentInternalInstance | null,
1061+
useComponent = false
1062+
) {
1063+
if (target == null) {
1064+
return null
1065+
}
1066+
if (!__BRIDGE__) {
1067+
// if `useComponent: true` will be specified, we get lexical scope owner instance for use-case slots
1068+
return !useComponent
1069+
? target.parent
1070+
: (target.vnode as any).ctx || target.parent // eslint-disable-line @typescript-eslint/no-explicit-any
1071+
} else {
1072+
return target.parent
1073+
}
1074+
}
1075+
10561076
function setupLifeCycle(
10571077
i18n: I18nInternal,
10581078
target: ComponentInternalInstance,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ test('scope', async () => {
189189
`
190190
})
191191

192-
const Root = defineComponent({
192+
const App = defineComponent({
193193
components: {
194194
Container
195195
},
@@ -208,7 +208,7 @@ test('scope', async () => {
208208
template: `<Container>`
209209
})
210210

211-
const wrapper = await mount(Root, i18n)
211+
const wrapper = await mount(App, i18n)
212212

213213
expect(wrapper.html()).toEqual(`this is rootthis is global`)
214214
})

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

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import {
1818
h,
1919
withDirectives,
2020
resolveDirective,
21-
nextTick
21+
nextTick,
22+
getCurrentInstance
2223
} from 'vue'
2324
import {
2425
setDevToolsHook,
@@ -32,6 +33,8 @@ import {
3233
import { createI18n, useI18n } from '../src/i18n'
3334
import { mount } from './helper'
3435

36+
import type { ComponentOptions } from 'vue'
37+
3538
const container = document.createElement('div')
3639
document.body.appendChild(container)
3740

@@ -292,6 +295,92 @@ describe('issue #722', () => {
292295
})
293296
})
294297

298+
test('issue #729', async () => {
299+
const i18n = createI18n({
300+
legacy: true,
301+
locale: 'en',
302+
messages
303+
})
304+
305+
const C3 = defineComponent({
306+
template: `<div>C3 slot: <slot></slot></div>`,
307+
i18n: {
308+
messages: {
309+
en: {
310+
hello: 'Hello {world} - C3',
311+
world: 'world! - C3'
312+
}
313+
}
314+
}
315+
})
316+
317+
const C2 = defineComponent({
318+
template: `<div>C2 slot: <slot></slot></div>`,
319+
i18n: {
320+
messages: {
321+
en: {
322+
goodbuy: 'Goodbuy!'
323+
}
324+
}
325+
}
326+
})
327+
328+
const C1 = defineComponent({
329+
components: {
330+
C2,
331+
C3
332+
},
333+
template: `<div>
334+
C1:
335+
<div>{{ $t("hello", { world: $t("world") }) }}</div>
336+
<i18n-t keypath="hello" tag="div">
337+
<template #world>
338+
<strong>{{ $t("world") }}</strong>
339+
</template>
340+
</i18n-t>
341+
342+
<br />
343+
344+
<C2>
345+
<div>{{ $t("hello", { world: $t("world") }) }}</div>
346+
<i18n-t keypath="hello" tag="div">
347+
<template #world>
348+
<strong>{{ $t("world") }}</strong>
349+
</template>
350+
</i18n-t>
351+
</C2>
352+
<C3>
353+
<div>{{ $t("hello", { world: $t("world") }) }}</div>
354+
<i18n-t keypath="hello" tag="div">
355+
<template #world>
356+
<strong>{{ $t("world") }}</strong>
357+
</template>
358+
</i18n-t>
359+
</C3>
360+
</div>`,
361+
i18n: {
362+
messages: {
363+
en: {
364+
hello: 'Hello {world}',
365+
world: 'world!'
366+
}
367+
}
368+
}
369+
})
370+
371+
const App = defineComponent({
372+
components: {
373+
C1
374+
},
375+
template: `<C1 />`
376+
})
377+
const wrapper = await mount(App, i18n)
378+
379+
expect(wrapper.html()).toEqual(
380+
`<div> C1: <div>Hello world!</div><div>Hello <strong>world!</strong></div><br><div>C2 slot: <div>Hello world!</div><div>Hello <strong>world!</strong></div></div><div>C3 slot: <div>Hello world!</div><div>Hello <strong>world!</strong></div></div></div>`
381+
)
382+
})
383+
295384
test('issue #819: v-for', async () => {
296385
const i18n = createI18n({
297386
legacy: false,
@@ -722,3 +811,71 @@ test('issue #1123', async () => {
722811
`hello, <span>Hello</span>! Do you like <a><strong>Vue </strong> I18n </a>?`
723812
)
724813
})
814+
815+
test('issue #1392', async () => {
816+
const i18n = createI18n({
817+
legacy: false,
818+
locale: 'en',
819+
messages: {
820+
en: { hello: 'world' }
821+
}
822+
})
823+
824+
const Test = defineComponent({
825+
setup() {
826+
const instance = getCurrentInstance()
827+
if (instance == null) {
828+
throw new Error()
829+
}
830+
// emulate i18n custom block
831+
const options = instance.type as ComponentOptions
832+
options.__i18n = [
833+
{
834+
locale: 'en',
835+
resource: {
836+
any: 'thing'
837+
}
838+
}
839+
]
840+
const { t } = useI18n()
841+
return { t }
842+
},
843+
template: `<slot />`
844+
})
845+
846+
const App = defineComponent({
847+
components: {
848+
Test
849+
},
850+
setup() {
851+
const instance = getCurrentInstance()
852+
if (instance == null) {
853+
throw new Error()
854+
}
855+
// emulate i18n custom block
856+
const options = instance.type as ComponentOptions
857+
options.__i18n = [
858+
{
859+
locale: 'en',
860+
resource: {
861+
doesNotWork: 'works'
862+
}
863+
}
864+
]
865+
const { t } = useI18n()
866+
867+
return { t }
868+
},
869+
template: `<div>
870+
<Test>
871+
component: <i18n-t keypath="doesNotWork" />
872+
<br />
873+
t: {{ t('doesNotWork') }}
874+
</Test>
875+
</div>`
876+
})
877+
878+
const wrapper = await mount(App, i18n)
879+
880+
expect(wrapper.html()).toEqual(`<div> component: works<br> t: works</div>`)
881+
})

0 commit comments

Comments
 (0)