Skip to content

Commit 0fc3540

Browse files
authored
fix: fallback linked message params (#1926)
1 parent 359adbb commit 0fc3540

File tree

4 files changed

+114
-41
lines changed

4 files changed

+114
-41
lines changed

packages/core-base/src/runtime.ts

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ type ExtractToStringKey<T> = Extract<keyof T, 'toString'>
1717
type ExtractToStringFunction<T> = T[ExtractToStringKey<T>]
1818
// prettier-ignore
1919
type StringConvertable<T> = ExtractToStringKey<T> extends never
20-
? unknown
21-
: ExtractToStringFunction<T> extends (...args: any) => string // eslint-disable-line @typescript-eslint/no-explicit-any
22-
? T
23-
: unknown
20+
? unknown
21+
: ExtractToStringFunction<T> extends (...args: any) => string // eslint-disable-line @typescript-eslint/no-explicit-any
22+
? T
23+
: unknown
2424

2525
/**
2626
*
@@ -110,7 +110,8 @@ export type MessageFunction<T = string> =
110110

111111
export type MessageFunctions<T = string> = Record<string, MessageFunction<T>>
112112
export type MessageResolveFunction<T = string> = (
113-
key: string
113+
key: string,
114+
useLinked: boolean
114115
) => MessageFunction<T>
115116

116117
export type MessageNormalize<T = string> = (
@@ -310,27 +311,27 @@ function pluralDefault(choice: number, choicesLength: number): number {
310311
if (choicesLength === 2) {
311312
// prettier-ignore
312313
return choice
313-
? choice > 1
314-
? 1
315-
: 0
316-
: 1
314+
? choice > 1
315+
? 1
316+
: 0
317+
: 1
317318
}
318319
return choice ? Math.min(choice, 2) : 0
319320
}
320321

321322
function getPluralIndex<T, N>(options: MessageContextOptions<T, N>): number {
322323
// prettier-ignore
323324
const index = isNumber(options.pluralIndex)
324-
? options.pluralIndex
325-
: -1
325+
? options.pluralIndex
326+
: -1
326327
// prettier-ignore
327328
return options.named && (isNumber(options.named.count) || isNumber(options.named.n))
328-
? isNumber(options.named.count)
329-
? options.named.count
330-
: isNumber(options.named.n)
331-
? options.named.n
332-
: index
333-
: index
329+
? isNumber(options.named.count)
330+
? options.named.count
331+
: isNumber(options.named.n)
332+
? options.named.n
333+
: index
334+
: index
334335
}
335336

336337
function normalizeNamed(pluralIndex: number, props: PluralizationProps): void {
@@ -371,13 +372,13 @@ export function createMessageContext<T = string, N = {}>(
371372
isNumber(options.pluralIndex) && normalizeNamed(pluralIndex, _named)
372373
const named = (key: string): unknown => _named[key]
373374

374-
function message(key: Path): MessageFunction<T> {
375+
function message(key: Path, useLinked?: boolean): MessageFunction<T> {
375376
// prettier-ignore
376377
const msg = isFunction(options.messages)
377-
? options.messages(key)
378-
: isObject(options.messages)
379-
? options.messages[key]
380-
: false
378+
? options.messages(key, !!useLinked)
379+
: isObject(options.messages)
380+
? options.messages[key]
381+
: false
381382
return !msg
382383
? options.parent
383384
? options.parent.message(key) // resolve from parent messages
@@ -425,7 +426,7 @@ export function createMessageContext<T = string, N = {}>(
425426
type = arg2 || type
426427
}
427428
}
428-
const ret = message(key)(ctx)
429+
const ret = message(key, true)(ctx)
429430
const msg =
430431
// The message in vnode resolved with linked are returned as an array by processor.nomalize
431432
type === 'vnode' && isArray(ret) && modifier

packages/core-base/src/translate.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,13 +1156,16 @@ function getMessageContextOptions<Messages, Message = string>(
11561156
fallbackContext
11571157
} = context
11581158

1159-
const resolveMessage = (key: string): MessageFunction<Message> => {
1159+
const resolveMessage = (
1160+
key: string,
1161+
useLinked: boolean
1162+
): MessageFunction<Message> => {
11601163
let val = resolveValue(message, key)
11611164

1162-
// fallback to root context
1163-
if (val == null && fallbackContext) {
1165+
// fallback
1166+
if (val == null && (fallbackContext || useLinked)) {
11641167
const [, , message] = resolveMessageFormat(
1165-
fallbackContext,
1168+
fallbackContext || context, // NOTE: if has fallbackContext, fallback to root, else if use linked, fallback to local context
11661169
key,
11671170
locale,
11681171
fallbackLocale as FallbackLocale,

packages/core-base/test/translate.test.ts

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -810,23 +810,45 @@ describe('edge cases', () => {
810810
})
811811
})
812812

813-
test('fallback context', () => {
814-
const parent = context({
815-
locale: 'en',
816-
messages: {
817-
en: { hello: 'hello man!', hi: 'hi' }
818-
}
819-
})
813+
describe('fallback context', () => {
814+
test('root (parent context)', () => {
815+
const parent = context({
816+
locale: 'en',
817+
messages: {
818+
en: { hello: 'hello man!', hi: 'hi' }
819+
}
820+
})
820821

821-
const ctx = context({
822-
locale: 'en',
823-
messages: {
824-
en: { hi: 'hi! @:hello' }
825-
}
822+
const ctx = context({
823+
locale: 'en',
824+
messages: {
825+
en: { hi: 'hi! @:hello' }
826+
}
827+
})
828+
ctx.fallbackContext = parent
829+
830+
expect(translate(ctx, 'hi')).toEqual('hi! hello man!')
826831
})
827-
ctx.fallbackContext = parent
828832

829-
expect(translate(ctx, 'hi')).toEqual('hi! hello man!')
833+
test('local (self context)', () => {
834+
const ctx = context({
835+
locale: 'en',
836+
messages: {
837+
en: {
838+
apples: 'Apples',
839+
no_results: 'No @.lower:{0} found'
840+
},
841+
'en-variant': {
842+
no_results: 'No @.lower:{0} found'
843+
}
844+
}
845+
})
846+
847+
expect(translate(ctx, 'no_results', ['apples'])).toEqual('No apples found')
848+
expect(
849+
translate(ctx, 'no_results', ['apples'], { locale: 'en-variant' })
850+
).toEqual('No apples found')
851+
})
830852
})
831853

832854
describe('processor', () => {

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,3 +1359,50 @@ test('#1809', async () => {
13591359
})
13601360
expect(i18n.global.t('hi')).toEqual('hi kazupon')
13611361
})
1362+
1363+
test('#1912', async () => {
1364+
const i18n = createI18n({
1365+
legacy: false,
1366+
locale: 'en',
1367+
messages: {
1368+
en: {
1369+
hello: 'Hello, Vue I18n',
1370+
language: 'Languages',
1371+
apples: 'Apples',
1372+
no_results: 'No @.lower:{0} found'
1373+
},
1374+
'en-variant': {
1375+
no_results: 'No @.lower:{0} found'
1376+
}
1377+
}
1378+
})
1379+
1380+
let loc: ReturnType<typeof useI18n>['locale']
1381+
const App = defineComponent({
1382+
template: `
1383+
<form>
1384+
<select v-model="locale">
1385+
<option value="en">en</option>
1386+
<option value="en-variant">en-variant</option>
1387+
</select>
1388+
</form>
1389+
<p>{{ t('no_results', ['apples']) }}</p>
1390+
`,
1391+
setup() {
1392+
const { t, locale } = useI18n()
1393+
// @ts-ignore
1394+
loc = locale
1395+
return { t, locale }
1396+
}
1397+
})
1398+
const wrapper = await mount(App, i18n)
1399+
await nextTick()
1400+
1401+
const el = wrapper.find('p')
1402+
expect(el?.innerHTML).include(`No apples found`)
1403+
// @ts-ignore
1404+
loc.value = 'en-variant'
1405+
await nextTick()
1406+
1407+
expect(el?.innerHTML).include(`No apples found`)
1408+
})

0 commit comments

Comments
 (0)