diff --git a/packages/vue-i18n-core/src/i18n.ts b/packages/vue-i18n-core/src/i18n.ts index 04209a68d..bb01e0481 100644 --- a/packages/vue-i18n-core/src/i18n.ts +++ b/packages/vue-i18n-core/src/i18n.ts @@ -1021,7 +1021,10 @@ function getComposer( ): Composer | null { let composer: Composer | null = null const root = target.root - let current: ComponentInternalInstance | null = target.parent + let current: ComponentInternalInstance | null = getParentComponentInstance( + target, + useComponent + ) while (current != null) { const i18nInternal = i18n as unknown as I18nInternal if (i18n.mode === 'composition') { @@ -1053,6 +1056,23 @@ function getComposer( return composer } +function getParentComponentInstance( + target: ComponentInternalInstance | null, + useComponent = false +) { + if (target == null) { + return null + } + if (!__BRIDGE__) { + // if `useComponent: true` will be specified, we get lexical scope owner instance for use-case slots + return !useComponent + ? target.parent + : (target.vnode as any).ctx || target.parent // eslint-disable-line @typescript-eslint/no-explicit-any + } else { + return target.parent + } +} + function setupLifeCycle( i18n: I18nInternal, target: ComponentInternalInstance, diff --git a/packages/vue-i18n-core/test/components/Translation.test.ts b/packages/vue-i18n-core/test/components/Translation.test.ts index fee7ab180..319a1e544 100644 --- a/packages/vue-i18n-core/test/components/Translation.test.ts +++ b/packages/vue-i18n-core/test/components/Translation.test.ts @@ -189,7 +189,7 @@ test('scope', async () => { ` }) - const Root = defineComponent({ + const App = defineComponent({ components: { Container }, @@ -208,7 +208,7 @@ test('scope', async () => { template: `` }) - const wrapper = await mount(Root, i18n) + const wrapper = await mount(App, i18n) expect(wrapper.html()).toEqual(`this is rootthis is global`) }) diff --git a/packages/vue-i18n-core/test/issues.test.ts b/packages/vue-i18n-core/test/issues.test.ts index e85162562..c58b84c19 100644 --- a/packages/vue-i18n-core/test/issues.test.ts +++ b/packages/vue-i18n-core/test/issues.test.ts @@ -18,7 +18,8 @@ import { h, withDirectives, resolveDirective, - nextTick + nextTick, + getCurrentInstance } from 'vue' import { setDevToolsHook, @@ -32,6 +33,8 @@ import { import { createI18n, useI18n } from '../src/i18n' import { mount } from './helper' +import type { ComponentOptions } from 'vue' + const container = document.createElement('div') document.body.appendChild(container) @@ -292,6 +295,92 @@ describe('issue #722', () => { }) }) +test('issue #729', async () => { + const i18n = createI18n({ + legacy: true, + locale: 'en', + messages + }) + + const C3 = defineComponent({ + template: `
C3 slot:
`, + i18n: { + messages: { + en: { + hello: 'Hello {world} - C3', + world: 'world! - C3' + } + } + } + }) + + const C2 = defineComponent({ + template: `
C2 slot:
`, + i18n: { + messages: { + en: { + goodbuy: 'Goodbuy!' + } + } + } + }) + + const C1 = defineComponent({ + components: { + C2, + C3 + }, + template: `
+ C1: +
{{ $t("hello", { world: $t("world") }) }}
+ + + + +
+ + +
{{ $t("hello", { world: $t("world") }) }}
+ + + +
+ +
{{ $t("hello", { world: $t("world") }) }}
+ + + +
+
`, + i18n: { + messages: { + en: { + hello: 'Hello {world}', + world: 'world!' + } + } + } + }) + + const App = defineComponent({ + components: { + C1 + }, + template: `` + }) + const wrapper = await mount(App, i18n) + + expect(wrapper.html()).toEqual( + `
C1:
Hello world!
Hello world!

C2 slot:
Hello world!
Hello world!
C3 slot:
Hello world!
Hello world!
` + ) +}) + test('issue #819: v-for', async () => { const i18n = createI18n({ legacy: false, @@ -722,3 +811,71 @@ test('issue #1123', async () => { `hello, Hello! Do you like Vue I18n ?` ) }) + +test('issue #1392', async () => { + const i18n = createI18n({ + legacy: false, + locale: 'en', + messages: { + en: { hello: 'world' } + } + }) + + const Test = defineComponent({ + setup() { + const instance = getCurrentInstance() + if (instance == null) { + throw new Error() + } + // emulate i18n custom block + const options = instance.type as ComponentOptions + options.__i18n = [ + { + locale: 'en', + resource: { + any: 'thing' + } + } + ] + const { t } = useI18n() + return { t } + }, + template: `` + }) + + const App = defineComponent({ + components: { + Test + }, + setup() { + const instance = getCurrentInstance() + if (instance == null) { + throw new Error() + } + // emulate i18n custom block + const options = instance.type as ComponentOptions + options.__i18n = [ + { + locale: 'en', + resource: { + doesNotWork: 'works' + } + } + ] + const { t } = useI18n() + + return { t } + }, + template: `
+ + component: +
+ t: {{ t('doesNotWork') }} +
+
` + }) + + const wrapper = await mount(App, i18n) + + expect(wrapper.html()).toEqual(`
component: works
t: works
`) +})