diff --git a/packages/intl/.size-limit.json b/packages/intl/.size-limit.json index a9cc86a0..6e2cc0af 100644 --- a/packages/intl/.size-limit.json +++ b/packages/intl/.size-limit.json @@ -4,7 +4,7 @@ "gzip": true, "path": "dist/index.production.js", "import": "*", - "limit": "2 kB" + "limit": "2.05 kB" }, { "name": "All publics (Brotli)", @@ -30,7 +30,7 @@ "gzip": true, "path": "dist/index.production.js", "import": "{ intl, text, number, datetime, params, plural, match, cases }", - "limit": "1 kB" + "limit": "1.05 kB" }, { "name": "Basic set (Brotli)", diff --git a/packages/intl/src/format/match.spec.ts b/packages/intl/src/format/match.spec.ts index 25ba0435..5f4a544e 100644 --- a/packages/intl/src/format/match.spec.ts +++ b/packages/intl/src/format/match.spec.ts @@ -218,6 +218,35 @@ describe('intl', () => { count: 3 })).toBe('She has 3 tasks') }) + + it('should use a string translation for every case', () => { + const format = match('gender') + + expect(format(ctx, 'Invited {gender}.')({ + gender: 'female' + })).toBe('Invited female.') + + expect(format(ctx, 'Invited {gender}.')('male')).toBe('Invited male.') + }) + + it('should use a string translation for every case with cases resolver', () => { + const format = match('status', cases({ + active: text('Active {status}'), + disabled: text('Disabled {status}') + })) + + expect(format(ctx, 'Status {status}')({ + status: 'active' + })).toBe('Status active') + }) + + it('should use a string translation for every case with default key resolver', () => { + const format = match('role', other('user')) + + expect(format(ctx, 'Area of {role}')({ + role: 'guest' + })).toBe('Area of guest') + }) }) }) }) diff --git a/packages/intl/src/format/match.ts b/packages/intl/src/format/match.ts index 5fc1a38b..d195337d 100644 --- a/packages/intl/src/format/match.ts +++ b/packages/intl/src/format/match.ts @@ -94,7 +94,7 @@ export function match< param: K, getKey?: GetKey ): Format< - FormatsInput, + FormatsInput | string, MatchFn > @@ -113,7 +113,7 @@ export function match< cases?: CasesFn, getKey?: GetKey ): Format< - FormatsInput, + FormatsInput | string, MatchFn > @@ -135,7 +135,7 @@ export function match< }, maybeGetKey?: GetKey ): Format< - FormatsInput, + FormatsInput | string, MatchFn > { const valuePattern = new RegExp(`{${param}}`, 'g') @@ -149,11 +149,13 @@ export function match< getKey = maybeGetKey ?? casesOrGetKey ?? identity } - return (ctx, input: FormatsInput | undefined) => { + return (ctx, input: FormatsInput | string | undefined) => { const locale = ctx.$locale() - let resolvedCases: Record> = input ?? {} + const isString = typeof input === 'string' + // A string never reaches the lookups below, so it is stored as is. + let resolvedCases = (input ?? {}) as Record> - if (cases) { + if (cases && !isString) { resolvedCases = { ...resolvedCases } @@ -163,8 +165,7 @@ export function match< return (params) => { const value = $get(typeof params === 'object' ? params[param] : params) - const key = getKey(value, locale, resolvedCases) - const resolvedCase = resolvedCases[key] + const resolvedCase = isString ? input : resolvedCases[getKey(value, locale, resolvedCases)] const message = isFunction(resolvedCase) ? resolvedCase(params) : resolvedCase return message?.replace(valuePattern, value as string) diff --git a/packages/intl/src/format/plural.spec.ts b/packages/intl/src/format/plural.spec.ts index 8ea18693..f5b814f5 100644 --- a/packages/intl/src/format/plural.spec.ts +++ b/packages/intl/src/format/plural.spec.ts @@ -203,6 +203,27 @@ describe('intl', () => { count: 3 })).toBe('She has 3 tasks') }) + + it('should use a string translation for every form', () => { + const format = plural('count') + + expect(format(ctx, '{count} going')({ + count: 1 + })).toBe('1 going') + + expect(format(ctx, '{count} going')(3)).toBe('3 going') + }) + + it('should use a string translation for every form with forms resolver', () => { + const format = plural('count', forms({ + one: text(), + other: text() + })) + + expect(format(ctx, '{count} going')({ + count: 1 + })).toBe('1 going') + }) }) }) }) diff --git a/packages/intl/src/format/plural.ts b/packages/intl/src/format/plural.ts index 46805805..57b14c1d 100644 --- a/packages/intl/src/format/plural.ts +++ b/packages/intl/src/format/plural.ts @@ -31,7 +31,7 @@ export function plural< param: K, forms?: CasesFn ): Format< - FormatsInput, + FormatsInput | string, MatchFn > @@ -50,7 +50,7 @@ export function plural< options: Intl.PluralRulesOptions, forms?: CasesFn ): Format< - FormatsInput, + FormatsInput | string, MatchFn > @@ -63,7 +63,7 @@ export function plural< optionsOrForms?: Intl.PluralRulesOptions | CasesFn, maybeForms?: CasesFn ): Format< - FormatsInput, + FormatsInput | string, MatchFn > { let options: Intl.PluralRulesOptions | undefined diff --git a/website/src/content/docs/intl/api.mdx b/website/src/content/docs/intl/api.mdx index 0e818b8e..78a5b440 100644 --- a/website/src/content/docs/intl/api.mdx +++ b/website/src/content/docs/intl/api.mdx @@ -254,7 +254,7 @@ $t().formatAmount([1, 5]) ### `plural(param, forms?)` -Matches an LDML plural form with `Intl.PluralRules`. If `forms(...)` is omitted, `plural` reads forms directly from the translation input. +Matches an LDML plural form with `Intl.PluralRules`. If `forms(...)` is omitted, `plural` reads forms directly from the translation input. A plain string translation serves every form, which suits locales without plural distinctions. ```ts const [$t] = messages('home', { @@ -306,7 +306,7 @@ $t().guests({ ### `match(param, cases?)` -Matches a case by parameter value. If `cases(...)` is omitted, `match` reads cases directly from the translation input. +Matches a case by parameter value. If `cases(...)` is omitted, `match` reads cases directly from the translation input. A plain string translation serves every case. ```ts const [$t] = messages('invite', {