Skip to content

Commit

Permalink
Add support for explicit "0" and "1" keys
Browse files Browse the repository at this point in the history
  • Loading branch information
movermeyer committed Jan 25, 2023
1 parent ef1c234 commit 4fe1371
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/two-clouds-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/react-i18n': minor
---

Allow CLDR explicit "0" and "1" pluralization keys
20 changes: 16 additions & 4 deletions packages/react-i18n/docs/react_i18n_schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ React I18n is a schema comprised of a structure of nested key-value pairs (KVPs)
<!-- Created using "Markdown All in One" extension for VS Code -->

- [History](#history)
- [Version 3.0](#version-30)
- [Version 2.0](#version-20)
- [Version 1.0](#version-10)
- [Versioning](#versioning)
Expand All @@ -28,6 +29,10 @@ React I18n is a schema comprised of a structure of nested key-value pairs (KVPs)

# History

## Version 3.0

- Expands the list of reserved cardinal plural keys to include the explicit `"0"` and `"1"` keys.

## Version 2.0

- Use the more precise language: `"Pluralization" -> "Cardinal Pluralization"`
Expand Down Expand Up @@ -126,7 +131,14 @@ In order to avoid key collisions, the following keys are reserved for use as car

These keys are derived from the names given to the cardinal pluralization rules of all languages, as defined in the [Unicode Consortium's Common Locale Data Repository (CLDR)](https://github.com/unicode-org/cldr).

This list is current as of version `37` of the CLDR. In the unlikely event that new cardinal pluralization rule names are added, they will be added to this reserved list in a future version of this specification.
This list is current as of version `42` of the CLDR. In the unlikely event that new cardinal pluralization rule names are added, they will be added to this reserved list in a future version of this specification.

Further, the following keys are reserved for use as cardinal pluralization keys and must not be used as keys of leaf KVPs outside of a cardinal pluralization context:

- `"0"`
- `"1"`

These keys are derived from the [CLDR's explicit 0 and 1 rules](https://unicode-org.github.io/cldr/ldml/tr35-numbers.html#Explicit_0_1_rules)

**Example INVALID English file:**

Expand Down Expand Up @@ -308,7 +320,7 @@ Comments follow the [JSON5 syntax for comments](https://spec.json5.org/#comments
React I18n's schema is loosely based on the [Rails I18n](https://guides.rubyonrails.org/i18n.html) schema.
This section compares the two schemas, in order to point out the ways in which they differ from one another.

### Root locale key
## Root locale key

Rails I18n has a locale root key, while React I18n doesn't:

Expand All @@ -330,7 +342,7 @@ en:
Note the `en` (for English) locale key that is the root key for the Rails I18n file.
This key is not present in the React I18n file. The locale of a React I18n file is stored outside of the file contents. By convention, the locale is typically stored within the filename (eg. `en.json` for an English locale file).

### Pluralization
## Pluralization

Rails I18n (when using the default backend) allows for the use of a `zero` key (regardless of the source language), while React I18n doesn't.

Expand Down Expand Up @@ -363,7 +375,7 @@ en:
}
```

### Interpolation
## Interpolation

While neither schema defines an interpolation syntax, the most common syntax used in Rails I18n is `%{}`, while in React I18n, the most common syntax is `{}`.

Expand Down
13 changes: 12 additions & 1 deletion packages/react-i18n/src/utilities/tests/translate.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ describe('translate()', () => {
// Lots of spaces to make sure we're taking the whole match length into account.
[id]: 'foo { bar } baz',
pluralizations: {
'0': 'I have no car!', // eslint-disable-line @typescript-eslint/naming-convention
'1': 'I have a single car!', // eslint-disable-line @typescript-eslint/naming-convention
one: 'I have {count} car!',
other: 'I have {count} cars!',
ordinal: {
Expand All @@ -194,13 +196,22 @@ describe('translate()', () => {
});

it('returns selects the proper values for cardinal pluralization', () => {
const translationNone = translate(
'pluralizations',
{replacements: {count: 0}},
translations,
locale,
);

expect(translationNone).toBe('I have no car!');

const translationOne = translate(
'pluralizations',
{replacements: {count: 1}},
translations,
locale,
);
expect(translationOne).toBe('I have 1 car!');
expect(translationOne).toBe('I have a single car!');

const translationOther = translate(
'pluralizations',
Expand Down
13 changes: 10 additions & 3 deletions packages/react-i18n/src/utilities/translate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,16 @@ function translateWithDictionary(
const count = replacements[CARDINAL_PLURALIZATION_KEY_NAME];

if (typeof count === 'number') {
const group = memoizedPluralRules(locale).select(count);
result = result[group] || result.other;

// Explicit 0 and 1 rules take precedence over the pluralization rules
// https://unicode-org.github.io/cldr/ldml/tr35-numbers.html#Explicit_0_1_rules
if (count === 0 && result['0'] !== undefined) {
result = result['0'];
} else if (count === 1 && result['1'] !== undefined) {
result = result['1'];
} else {
const group = memoizedPluralRules(locale).select(count);
result = result[group] || result.other;
}
additionalReplacements[CARDINAL_PLURALIZATION_KEY_NAME] =
memoizedNumberFormatter(locale).format(count);
}
Expand Down

0 comments on commit 4fe1371

Please sign in to comment.