Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow pluralization customization via constructor options (closes #464) #482

Merged
merged 27 commits into from
Dec 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2437de9
improvement(getChoiceIndex): make getChoiceIndex overridable
Raiondesu Oct 25, 2018
6261e8b
update(docs): fit the new functionality
Raiondesu Oct 25, 2018
99fcb3e
build(dist): generate dist files
Raiondesu Oct 25, 2018
a40143e
revert(dist): unbuild files to correspond with guidelines
Raiondesu Oct 25, 2018
4bebc6d
docs(pluralization): fix typo
Raiondesu Oct 25, 2018
f85f439
improvement(test/unit): add test case for custom pluralization
Raiondesu Oct 25, 2018
50de8b4
docs(pluralization): remove unnecessary code from new pluralization e…
Raiondesu Oct 25, 2018
a2945e0
update(types): add types for the new pluralization feature
Raiondesu Oct 25, 2018
cf52cc1
Merge branch 'dev' of github.com:Raiondesu/vue-i18n into dev
Raiondesu Oct 25, 2018
43e02cc
Merge from original
Raiondesu Dec 14, 2018
286bc2e
⚡improvement(types): remove duplicate type definitions
Raiondesu Dec 14, 2018
2e3a263
⚡improvement(tests): Let types allow to pass getChoiceIndex into options
Raiondesu Dec 14, 2018
2879896
⚡improvement(index): Set getChoiceIndex for current instance from opts
Raiondesu Dec 14, 2018
1d89b46
🐛fix(types): fix type aliases for format options
Raiondesu Dec 14, 2018
60bef91
⚡improvement(index): allow to pass a pluralization rules map instead
Raiondesu Dec 14, 2018
a6e6585
📃docs(api): fix md typo
Raiondesu Dec 14, 2018
d7e1492
⚡improvement(types/flow): add `pluralizationRules` to the instance types
Raiondesu Dec 14, 2018
0680891
📃docs(pluralization): add the documentation for #464 functionality
Raiondesu Dec 14, 2018
84b1fec
📃docs(pluralization): fix typo
Raiondesu Dec 14, 2018
00da386
⭐️new(test): add a test case for #464
Raiondesu Dec 14, 2018
0a7b092
📃docs(pluralization): improve custom pluralization definitions
Raiondesu Dec 14, 2018
659ff42
⭐new(test): add a test for backward compatibility with #451 and 8.4.0
Raiondesu Dec 14, 2018
5d405a4
improvement(index): apply the pluralization rule to the instance
Raiondesu Dec 14, 2018
bc8207d
📃docs(api): improve `pluralizationRules` property definition
Raiondesu Dec 14, 2018
6e6ab97
docs(pluralization): fix jsdoc comment misplacement
Raiondesu Dec 16, 2018
cd46df6
Revert "⚡improvement(types): remove duplicate type definitions"
Raiondesu Dec 16, 2018
5abac75
⚡revert(types): Bring back original VueI18n aliases for format options
Raiondesu Dec 16, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions decls/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ declare type I18nOptions = {
root?: I18n, // for internal
fallbackRoot?: boolean,
sync?: boolean,
silentTranslationWarn?: boolean
silentTranslationWarn?: boolean,
pluralizationRules?: {
[lang: string]: (choice: number, choicesLength: number) => number,
},
};

declare type IntlAvailability = {
Expand Down Expand Up @@ -100,7 +103,10 @@ declare interface I18n {
getNumberFormat (locale: Locale): NumberFormat,
setNumberFormat (locale: Locale, format: NumberFormat): void,
mergeNumberFormat (locale: Locale, format: NumberFormat): void,
n (value: number, ...args: any): NumberFormatResult
n (value: number, ...args: any): NumberFormatResult,
pluralizationRules: {
[lang: string]: (choice: number, choicesLength: number) => number
}
};

declare interface Formatter {
Expand Down
34 changes: 33 additions & 1 deletion gitbook/en/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

Localize the locale message of `key` with pluralization. Localize in preferentially component locale messages than global locale messages. If not specified component locale messages, localize with global locale messages. If you specified `locale`, localize the locale messages of `locale`. If you will specify string value to `values`, localize the locale messages of value. If you will specify Array or Object value to `values`, you must specify with `values` of [$t](#t).

If default pluralization does not suit your needs, see [pluralization rules in constructor options](#pluralizationrules) and [custom pluralization](pluralization.md).

#### getChoiceIndex

- **Arguments:**
Expand Down Expand Up @@ -237,7 +239,7 @@ You can specify the below some options of `I18nOptions` constructor options of [

If `false`, regardless of the root level locale, localize for each component locale.

### silentTranslationWarn
#### silentTranslationWarn

> 6.1+

Expand All @@ -249,6 +251,26 @@ You can specify the below some options of `I18nOptions` constructor options of [

If `true`, supress localization fail warnings.

#### pluralizationRules

> 8.5+

- **Type:** `Object`

- **Default:** `{}`

A set of rules for word pluralization in a following format:
```js
{
// Key - locale for the rule to be applied to.
// Value - mapping function that maps a choice index from `$tc` to the actual choice of the plural word.

'ru': function (choice, choiceIndex) => Number/* index of the plural word */;
'en': function (choice, choiceIndex) => Number/* index of the plural word */;
'jp': function (choice, choiceIndex) => Number/* index of the plural word */;
}
```

### Properties

#### locale
Expand Down Expand Up @@ -321,6 +343,16 @@ You can specify the below some options of `I18nOptions` constructor options of [

Whether suppress warnings outputted when localization fails.

#### pluralizationRules

> 8.5+

- **Type:** `Object`

- **Default:** `{}`

A set of rules for word pluralization. Key is a locale, value is the rule function for that locale.

### Methods

#### getLocaleMessage( locale )
Expand Down
65 changes: 36 additions & 29 deletions gitbook/en/pluralization.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,43 +43,46 @@ This will output the following HTML:

Such pluralization, however, does not apply to all languages (Slavic languages, for example, have different pluralization rules).

In order to implement these rules you can override the `VueI18n.prototype.getChoiceIndex` function.
In order to implement these rules you can pass an optional `pluralizationRules` object into `VueI18n` constructor options.

Very simplified example using rules for Slavic languages (Russian, Ukrainian, etc.):
```js
/**
* @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
* @param choicesLength {number} an overall amount of available choices
* @returns a final choice index to select plural word by
**/
VueI18n.prototype.getChoiceIndex = function (choice, choicesLength) {
// this === VueI18n instance, so the locale property also exists here
if (this.locale !== 'ru') {
// proceed to the default implementation
new VueI18n({
pluralizationRules: {
/** Key - language to use the rule for, 'ru', in this case */
/** Value - function
* @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
* @param choicesLength {number} an overall amount of available choices
* @returns a final choice index to select plural word by
**/
'ru': function (choice, choicesLength) {
// this === VueI18n instance, so the locale property also exists here

if (choice === 0) {
return 0;
}

const teen = choice > 10 && choice < 20;
const endsWithOne = choice % 10 === 1;

if (choicesLength < 4) {
return (!teen && endsWithOne) ? 1 : 2;
}
if (!teen && endsWithOne) {
return 1;
}
if (!teen && choice % 10 >= 2 && choice % 10 <= 4) {
return 2;
}

return (choicesLength < 4) ? 2 : 3;
}
}

if (choice === 0) {
return 0;
}

const teen = choice > 10 && choice < 20;
const endsWithOne = choice % 10 === 1;

if (!teen && endsWithOne) {
return 1;
}

if (!teen && choice % 10 >= 2 && choice % 10 <= 4) {
return 2;
}

return (choicesLength < 4) ? 2 : 3;
}
});
```

This would effectively give this:


```javascript
const messages = {
ru: {
Expand Down Expand Up @@ -119,3 +122,7 @@ Which results in:
<p>11 бананов</p>
<p>31 банан</p>
```

### Default pluralization

If your current locale is not found in a pluralization map, the [default](#pluralization) rule of the english langugage will be used.
32 changes: 23 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ export default class VueI18n {
_numberFormatters: Object
_path: I18nPath
_dataListeners: Array<any>
pluralizationRules: {
[lang: string]: (choice: number, choicesLength: number) => number
}

constructor (options: I18nOptions = {}) {
// Auto install if it is not done yet and `window` has `Vue`.
Expand Down Expand Up @@ -90,6 +93,8 @@ export default class VueI18n {
this._path = new I18nPath()
this._dataListeners = []

this.pluralizationRules = options.pluralizationRules || {}

this._exist = (message: Object, key: Path): boolean => {
if (!message || !key) { return false }
return !isNull(this._path.getPathValue(message, key))
Expand Down Expand Up @@ -437,17 +442,26 @@ export default class VueI18n {
* @returns a final choice index
*/
getChoiceIndex (choice: number, choicesLength: number): number {
choice = Math.abs(choice)

if (choicesLength === 2) {
return choice
? choice > 1
? 1
: 0
: 1
// Default (old) getChoiceIndex implementation - english-compatible
const defaultImpl = (_choice: number, _choicesLength: number) => {
_choice = Math.abs(_choice)

if (_choicesLength === 2) {
return _choice
? _choice > 1
? 1
: 0
: 1
}

return _choice ? Math.min(_choice, 2) : 0
}

return choice ? Math.min(choice, 2) : 0
if (this.locale in this.pluralizationRules) {
return this.pluralizationRules[this.locale].apply(this, [choice, choicesLength])
} else {
return defaultImpl(choice, choicesLength)
}
}

tc (key: Path, choice?: number, ...values: any): TranslateResult {
Expand Down
Loading