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

Extendable pluralization #451

Merged
merged 9 commits into from
Oct 28, 2018
Merged

Extendable pluralization #451

merged 9 commits into from
Oct 28, 2018

Conversation

Raiondesu
Copy link
Contributor

@Raiondesu Raiondesu commented Oct 25, 2018

Allows custom pluralization handlers via prototype modification.

This is achieved by moving fetchChoice and getChoiceIndex functions directly into VueI18n prototype without any implementation changes. This ensures full backward-compatibility while also providing a possibility for extension to fit specific needs of specific users.

Very simplified example using rules for Slavic langauges (Russian, Ukrainian, etc.):

/**
 * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
 * @param choiceLength {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 exists
  if (this.locale !== 'ru') {
    // proceed to the default implementation
  }

  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 allow this:

const messages = {
 ru: {
   car: '0 машин | 1 машина | {n} машины | {n} машин',
   banana: 'нет бананов | 1 банан | {n} банана | {n} бананов'
 }
}

Where the format is 0 things | 1 thing | few things | multiple things.

Then:

<p>{{ $tc('car', 1) }}</p>
<p>{{ $tc('car', 2) }}</p>
<p>{{ $tc('car', 4) }}</p>
<p>{{ $tc('car', 12) }}</p>
<p>{{ $tc('car', 21) }}</p>
<p>{{ $tc('car', 0) }}</p>
<p>{{ $tc('car', 4) }}</p>
<p>{{ $tc('car', 11) }}</p>
<p>{{ $tc('car', 31) }}</p>

Which results in:

<p>1 машина</p>
<p>2 машины</p>
<p>4 машины</p>
<p>12 машин</p>
<p>21 машина</p>
<p>нет бананов</p>
<p>4 банана</p>
<p>11 бананов</p>
<p>31 банан</p>

Pretty much closes #78.

@codecov-io
Copy link

codecov-io commented Oct 25, 2018

Codecov Report

Merging #451 into dev will not change coverage.
The diff coverage is 100%.

Impacted file tree graph

@@           Coverage Diff           @@
##              dev     #451   +/-   ##
=======================================
  Coverage   95.98%   95.98%           
=======================================
  Files           9        9           
  Lines         647      647           
=======================================
  Hits          621      621           
  Misses         26       26
Impacted Files Coverage Δ
src/util.js 91.22% <ø> (-1.2%) ⬇️
src/index.js 99.61% <100%> (+0.01%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3a57895...cf52cc1. Read the comment docs.

@Raiondesu
Copy link
Contributor Author

The feature is already working in production installed from my fork repo.

Copy link
Owner

@kazupon kazupon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your solution. 👍
In the vue-i18n next major version, I'll plan to adopt the ICU formatting,
In the meantime, this workaround tempolary solution will be useful!!
Thanks!

@kazupon kazupon changed the title Temporary solution for #78 (International pluralization) Extendable pluralization Oct 28, 2018
@kazupon kazupon merged commit bbab90b into kazupon:dev Oct 28, 2018
@Raiondesu
Copy link
Contributor Author

Yay! I think many people would love to see this on npm. ^_^

@mitar
Copy link

mitar commented Oct 30, 2018

Should we have some shared code for a function which supports various languages?

@Raiondesu
Copy link
Contributor Author

@mitar, I personally think this can be left to particular implementations of "getChoiceIndex". Also, many languages do not share any pluralization rules, so there is no need to build this in the lib itself - too many edge-cases to handle.
All the general code is already generalized.

@mitar
Copy link

mitar commented Oct 30, 2018

Hm, this document seems to be able to provide quite extensive list of those formulas, so I just wonder why we could not simply have it in code we all use.

@Raiondesu
Copy link
Contributor Author

Because this would add unnecessary load (for most cases), increasing the size of the default code base. If I use only Russian and English in my project - why would I want to also have specific rules for Arabic and German?

This is why it's better to just define these rules yourself whenever you need them by overloading getChoiceIndex.

@kazupon plans to integrate the ICU formatting, which would allow for declarative definitions for those rules, making them so much easier to describe.

@AndrewBogdanovTSS
Copy link

@Raiondesu is there any chance to improve override for this function so it can be available from options? Could you look into my feature request? Thanks!

@Raiondesu
Copy link
Contributor Author

@AndrewBogdanovTSS, well, this is possible...

Raiondesu added a commit to Raiondesu/vue-i18n that referenced this pull request Dec 14, 2018
kazupon pushed a commit that referenced this pull request Dec 16, 2018
…or options (closes #464) (#482) by @Raiondesu

* improvement(getChoiceIndex): make getChoiceIndex overridable

This provides better pluralization customization. :D

* update(docs): fit the new functionality

* build(dist): generate dist files

* revert(dist): unbuild files to correspond with guidelines

* docs(pluralization): fix typo

* improvement(test/unit): add test case for custom pluralization

* docs(pluralization): remove unnecessary code from new pluralization example

* update(types): add types for the new pluralization feature

* ⚡improvement(types): remove duplicate type definitions

Replace Number and Date format options with standard TS `Intl` types.

* ⚡improvement(tests): Let types allow to pass getChoiceIndex into options

* ⚡improvement(index): Set getChoiceIndex for current instance from opts

Fixes #464

* 🐛fix(types): fix type aliases for format options

* ⚡improvement(index): allow to pass a pluralization rules map instead

Fixes #464

* 📃docs(api): fix md typo

* ⚡improvement(types/flow): add `pluralizationRules` to the instance types

* 📃docs(pluralization): add the documentation for #464 functionality

* 📃docs(pluralization): fix typo

* ⭐️new(test): add a test case for #464

* 📃docs(pluralization): improve custom pluralization definitions

* ⭐new(test): add a test for backward compatibility with #451 and 8.4.0

* improvement(index): apply the pluralization rule to the instance

For better extensibility.

* 📃docs(api): improve `pluralizationRules` property definition

* docs(pluralization): fix jsdoc comment misplacement

* Revert "⚡improvement(types): remove duplicate type definitions"

This reverts commit 286bc2e.

* ⚡revert(types): Bring back original VueI18n aliases for format options
@pete-willard
Copy link

Thank you for your contribution, @Raiondesu!

@kwolniak
Copy link

Hello.

Could you please help me and tell how can I use it in nuxt app? I have all i18n config in nuxt.config.js.

@AndrewBogdanovTSS
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

International pluralization
7 participants