From 5c08bb801d7ae8d98dde12e57fd0b98a27a77ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Thu, 28 Sep 2023 13:17:14 +0200 Subject: [PATCH 1/4] make sure zero, one, two works --- .../__tests__/use-i18n.test.tsx | 41 +++++++++++++++++++ .../next-international/__tests__/utils/en.ts | 4 ++ .../next-international/src/common/create-t.ts | 14 ++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/packages/next-international/__tests__/use-i18n.test.tsx b/packages/next-international/__tests__/use-i18n.test.tsx index c6c7b44..80792e8 100644 --- a/packages/next-international/__tests__/use-i18n.test.tsx +++ b/packages/next-international/__tests__/use-i18n.test.tsx @@ -315,4 +315,45 @@ describe('useI18n', () => { expect(Array.isArray(result.current)).toBe(true); }); + + const pluralTestCases = [ + { + count: 0, + expected: 'No cows (#zero)', + }, + { + count: 1, + expected: 'One cow (#one)', + }, + { + count: 2, + expected: 'Two cows (#two)', + }, + ...[...new Array(30).fill(0)].map((_, i) => ({ count: i + 3, expected: `${i + 3} cows (#other)` })), + ]; + + it.each(pluralTestCases)('should return correct plural: $count ($expected)', async ({ count, expected }) => { + const { useI18n, I18nProvider } = createI18n({ + en: () => import('./utils/en'), + fr: () => import('./utils/fr'), + }); + + const App = ({ children }: { children: React.ReactNode }) => { + return {children}; + }; + + const { result } = renderHook( + () => { + const t = useI18n(); + return t('cow', { + count, + }); + }, + { + wrapper: App, + }, + ); + + expect(result.current).toBe(expected); + }); }); diff --git a/packages/next-international/__tests__/utils/en.ts b/packages/next-international/__tests__/utils/en.ts index d356c8e..d52dee6 100644 --- a/packages/next-international/__tests__/utils/en.ts +++ b/packages/next-international/__tests__/utils/en.ts @@ -10,4 +10,8 @@ export default { 'namespace.subnamespace.user.description': '{name} is {years} years old', 'only.exists.in.en': 'EN locale', 'double.param': 'This {param} is used twice ({param})', + 'cow#zero': 'No cows (#zero)', + 'cow#one': 'One cow (#one)', + 'cow#two': 'Two cows (#two)', + 'cow#other': '{count} cows (#other)', } as const; diff --git a/packages/next-international/src/common/create-t.ts b/packages/next-international/src/common/create-t.ts index 5c8aee4..c5ce819 100644 --- a/packages/next-international/src/common/create-t.ts +++ b/packages/next-international/src/common/create-t.ts @@ -27,6 +27,18 @@ export function createT .filter(key => key.includes('#')) .map(key => key.split('#')[0]), ); + + function getPluralKey(count: number) { + switch (count) { + case 0: + return 'zero'; + case 1: + return 'one'; + case 2: + return 'two'; + } + return pluralRules.select(count); + } const pluralRules = new Intl.PluralRules(context.locale); function t, Value extends LocaleValue = ScopedValue>( @@ -48,7 +60,7 @@ export function createT const isPluralKey = scope ? pluralKeys.has(`${scope}.${key}`) : pluralKeys.has(key); if (isPluralKey) { - key = `${key}#${pluralRules.select(paramObject.count)}` as Key; + key = `${key}#${getPluralKey(paramObject.count)}` as Key; isPlural = true; } } From 5bb2c0309ac1b90a5c7e15fc4585c11e47e2b198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Wed, 4 Oct 2023 13:17:36 +0200 Subject: [PATCH 2/4] add plural key for only "zero" --- .../__tests__/use-i18n.test.tsx | 26 ++++++++++++++++++- .../next-international/__tests__/utils/en.ts | 3 ++- .../next-international/src/common/create-t.ts | 9 +------ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/packages/next-international/__tests__/use-i18n.test.tsx b/packages/next-international/__tests__/use-i18n.test.tsx index 80792e8..ca2006b 100644 --- a/packages/next-international/__tests__/use-i18n.test.tsx +++ b/packages/next-international/__tests__/use-i18n.test.tsx @@ -327,7 +327,7 @@ describe('useI18n', () => { }, { count: 2, - expected: 'Two cows (#two)', + expected: '2 cows (#other)', }, ...[...new Array(30).fill(0)].map((_, i) => ({ count: i + 3, expected: `${i + 3} cows (#other)` })), ]; @@ -356,4 +356,28 @@ describe('useI18n', () => { expect(result.current).toBe(expected); }); + it('should fallback on #other if #zero is not defined', async () => { + const { useI18n, I18nProvider } = createI18n({ + en: () => import('./utils/en'), + fr: () => import('./utils/fr'), + }); + + const App = ({ children }: { children: React.ReactNode }) => { + return {children}; + }; + + const { result } = renderHook( + () => { + const t = useI18n(); + return t('horse', { + count: 0, + }); + }, + { + wrapper: App, + }, + ); + + expect(result.current).toBe('0 horses (#other)'); + }); }); diff --git a/packages/next-international/__tests__/utils/en.ts b/packages/next-international/__tests__/utils/en.ts index d52dee6..089169c 100644 --- a/packages/next-international/__tests__/utils/en.ts +++ b/packages/next-international/__tests__/utils/en.ts @@ -12,6 +12,7 @@ export default { 'double.param': 'This {param} is used twice ({param})', 'cow#zero': 'No cows (#zero)', 'cow#one': 'One cow (#one)', - 'cow#two': 'Two cows (#two)', 'cow#other': '{count} cows (#other)', + 'horse#one': 'One horse (#one)', + 'horse#other': '{count} horses (#other)', } as const; diff --git a/packages/next-international/src/common/create-t.ts b/packages/next-international/src/common/create-t.ts index c5ce819..ecf4c75 100644 --- a/packages/next-international/src/common/create-t.ts +++ b/packages/next-international/src/common/create-t.ts @@ -29,14 +29,7 @@ export function createT ); function getPluralKey(count: number) { - switch (count) { - case 0: - return 'zero'; - case 1: - return 'one'; - case 2: - return 'two'; - } + if (count === 0) return 'zero'; return pluralRules.select(count); } const pluralRules = new Intl.PluralRules(context.locale); From 2815959a5e8f9bf42ff76e3a6f0bdc486fd6694e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Wed, 4 Oct 2023 19:12:17 +0200 Subject: [PATCH 3/4] move pluralRules --- packages/next-international/src/common/create-t.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/next-international/src/common/create-t.ts b/packages/next-international/src/common/create-t.ts index ecf4c75..6516b25 100644 --- a/packages/next-international/src/common/create-t.ts +++ b/packages/next-international/src/common/create-t.ts @@ -28,11 +28,12 @@ export function createT .map(key => key.split('#')[0]), ); + const pluralRules = new Intl.PluralRules(context.locale); + function getPluralKey(count: number) { if (count === 0) return 'zero'; return pluralRules.select(count); } - const pluralRules = new Intl.PluralRules(context.locale); function t, Value extends LocaleValue = ScopedValue>( key: Key, From af715180641654ddf24703bb11cf468d6623ee45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Wed, 4 Oct 2023 19:51:48 +0200 Subject: [PATCH 4/4] update docs --- docs/pages/docs/app-plurals.md | 3 +++ docs/pages/docs/pages-plurals.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/docs/pages/docs/app-plurals.md b/docs/pages/docs/app-plurals.md index ea91afe..bd8cb6a 100644 --- a/docs/pages/docs/app-plurals.md +++ b/docs/pages/docs/app-plurals.md @@ -7,6 +7,7 @@ To declare plural translations, append `#` followed by `zero`, `one`, `two`, `fe ```ts {3-4} // locales/en.ts export default { + 'cows#zero': 'No cows', 'cows#one': 'A cow', 'cows#other': '{count} cows' } as const @@ -27,6 +28,8 @@ export default function Page() { return (
+ {/* Output: No cows */} +

{t('cows', { count: 0 })}

{/* Output: A cow */}

{t('cows', { count: 1 })}

{/* Output: 3 cows */} diff --git a/docs/pages/docs/pages-plurals.md b/docs/pages/docs/pages-plurals.md index 3420f91..55a1621 100644 --- a/docs/pages/docs/pages-plurals.md +++ b/docs/pages/docs/pages-plurals.md @@ -7,6 +7,7 @@ To declare plural translations, append `#` followed by `zero`, `one`, `two`, `fe ```ts {3-4} // locales/en.ts export default { + 'cows#zero': 'No cows', 'cows#one': 'A cow', 'cows#other': '{count} cows' } as const @@ -27,6 +28,8 @@ export default function Page() { return (
+ {/* Output: No cows */} +

{t('cows', { count: 0 })}

{/* Output: A cow */}

{t('cows', { count: 1 })}

{/* Output: 3 cows */}