diff --git a/packages/core-base/src/runtime.ts b/packages/core-base/src/runtime.ts index b0163c2ac..9124a4ad6 100644 --- a/packages/core-base/src/runtime.ts +++ b/packages/core-base/src/runtime.ts @@ -17,10 +17,10 @@ type ExtractToStringKey = Extract type ExtractToStringFunction = T[ExtractToStringKey] // prettier-ignore type StringConvertable = ExtractToStringKey extends never - ? unknown - : ExtractToStringFunction extends (...args: any) => string // eslint-disable-line @typescript-eslint/no-explicit-any - ? T - : unknown + ? unknown + : ExtractToStringFunction extends (...args: any) => string // eslint-disable-line @typescript-eslint/no-explicit-any + ? T + : unknown /** * @@ -110,7 +110,8 @@ export type MessageFunction = export type MessageFunctions = Record> export type MessageResolveFunction = ( - key: string + key: string, + useLinked: boolean ) => MessageFunction export type MessageNormalize = ( @@ -310,10 +311,10 @@ function pluralDefault(choice: number, choicesLength: number): number { if (choicesLength === 2) { // prettier-ignore return choice - ? choice > 1 - ? 1 - : 0 - : 1 + ? choice > 1 + ? 1 + : 0 + : 1 } return choice ? Math.min(choice, 2) : 0 } @@ -321,16 +322,16 @@ function pluralDefault(choice: number, choicesLength: number): number { function getPluralIndex(options: MessageContextOptions): number { // prettier-ignore const index = isNumber(options.pluralIndex) - ? options.pluralIndex - : -1 + ? options.pluralIndex + : -1 // prettier-ignore return options.named && (isNumber(options.named.count) || isNumber(options.named.n)) - ? isNumber(options.named.count) - ? options.named.count - : isNumber(options.named.n) - ? options.named.n - : index - : index + ? isNumber(options.named.count) + ? options.named.count + : isNumber(options.named.n) + ? options.named.n + : index + : index } function normalizeNamed(pluralIndex: number, props: PluralizationProps): void { @@ -371,13 +372,13 @@ export function createMessageContext( isNumber(options.pluralIndex) && normalizeNamed(pluralIndex, _named) const named = (key: string): unknown => _named[key] - function message(key: Path): MessageFunction { + function message(key: Path, useLinked?: boolean): MessageFunction { // prettier-ignore const msg = isFunction(options.messages) - ? options.messages(key) - : isObject(options.messages) - ? options.messages[key] - : false + ? options.messages(key, !!useLinked) + : isObject(options.messages) + ? options.messages[key] + : false return !msg ? options.parent ? options.parent.message(key) // resolve from parent messages @@ -425,7 +426,7 @@ export function createMessageContext( type = arg2 || type } } - const ret = message(key)(ctx) + const ret = message(key, true)(ctx) const msg = // The message in vnode resolved with linked are returned as an array by processor.nomalize type === 'vnode' && isArray(ret) && modifier diff --git a/packages/core-base/src/translate.ts b/packages/core-base/src/translate.ts index da8148706..517cd2eef 100644 --- a/packages/core-base/src/translate.ts +++ b/packages/core-base/src/translate.ts @@ -1156,13 +1156,16 @@ function getMessageContextOptions( fallbackContext } = context - const resolveMessage = (key: string): MessageFunction => { + const resolveMessage = ( + key: string, + useLinked: boolean + ): MessageFunction => { let val = resolveValue(message, key) - // fallback to root context - if (val == null && fallbackContext) { + // fallback + if (val == null && (fallbackContext || useLinked)) { const [, , message] = resolveMessageFormat( - fallbackContext, + fallbackContext || context, // NOTE: if has fallbackContext, fallback to root, else if use linked, fallback to local context key, locale, fallbackLocale as FallbackLocale, diff --git a/packages/core-base/test/translate.test.ts b/packages/core-base/test/translate.test.ts index ea0956d0f..b62cc60eb 100644 --- a/packages/core-base/test/translate.test.ts +++ b/packages/core-base/test/translate.test.ts @@ -810,23 +810,45 @@ describe('edge cases', () => { }) }) -test('fallback context', () => { - const parent = context({ - locale: 'en', - messages: { - en: { hello: 'hello man!', hi: 'hi' } - } - }) +describe('fallback context', () => { + test('root (parent context)', () => { + const parent = context({ + locale: 'en', + messages: { + en: { hello: 'hello man!', hi: 'hi' } + } + }) - const ctx = context({ - locale: 'en', - messages: { - en: { hi: 'hi! @:hello' } - } + const ctx = context({ + locale: 'en', + messages: { + en: { hi: 'hi! @:hello' } + } + }) + ctx.fallbackContext = parent + + expect(translate(ctx, 'hi')).toEqual('hi! hello man!') }) - ctx.fallbackContext = parent - expect(translate(ctx, 'hi')).toEqual('hi! hello man!') + test('local (self context)', () => { + const ctx = context({ + locale: 'en', + messages: { + en: { + apples: 'Apples', + no_results: 'No @.lower:{0} found' + }, + 'en-variant': { + no_results: 'No @.lower:{0} found' + } + } + }) + + expect(translate(ctx, 'no_results', ['apples'])).toEqual('No apples found') + expect( + translate(ctx, 'no_results', ['apples'], { locale: 'en-variant' }) + ).toEqual('No apples found') + }) }) describe('processor', () => { diff --git a/packages/vue-i18n-core/test/issues.test.ts b/packages/vue-i18n-core/test/issues.test.ts index 1e74b16f1..f3db536b7 100644 --- a/packages/vue-i18n-core/test/issues.test.ts +++ b/packages/vue-i18n-core/test/issues.test.ts @@ -1359,3 +1359,50 @@ test('#1809', async () => { }) expect(i18n.global.t('hi')).toEqual('hi kazupon') }) + +test('#1912', async () => { + const i18n = createI18n({ + legacy: false, + locale: 'en', + messages: { + en: { + hello: 'Hello, Vue I18n', + language: 'Languages', + apples: 'Apples', + no_results: 'No @.lower:{0} found' + }, + 'en-variant': { + no_results: 'No @.lower:{0} found' + } + } + }) + + let loc: ReturnType['locale'] + const App = defineComponent({ + template: ` +
+ +
+

{{ t('no_results', ['apples']) }}

+`, + setup() { + const { t, locale } = useI18n() + // @ts-ignore + loc = locale + return { t, locale } + } + }) + const wrapper = await mount(App, i18n) + await nextTick() + + const el = wrapper.find('p') + expect(el?.innerHTML).include(`No apples found`) + // @ts-ignore + loc.value = 'en-variant' + await nextTick() + + expect(el?.innerHTML).include(`No apples found`) +})