vue-i18n icon indicating copy to clipboard operation
vue-i18n copied to clipboard

tc decimal handling

Open Rosadojonathan opened this issue 5 years ago • 7 comments

vue & vue-i18n version vue 2.6.8 vue-i18n 8.11.2

Steps to reproduce

  {{ $tc('daysCounter', days, {count: days}) }}


  i18n: {
    messages: {
   daysCounter: ' 0 jour | {count} jour | {count} jours'
   }
}

What is Expected? if days is 0.5 or 1.5 the singular option should be selected and output '0.5 jour' or '1.5 jour'

What is the result? vue-i18n displays '0 jours | 0.5 jour | 0.5 jours'

Rosadojonathan avatar Jun 13 '19 08:06 Rosadojonathan

This is because $tc's second argument is supposed to be a index to choose message from case of 0 | 1 | 2 and more, so it must be int right now.

I think you may get expcted result if you round or ceil before passing days to $tc I made an example of that: http://jsfiddle.net/5mxgauwf/1/

exoego avatar Aug 02 '19 02:08 exoego

I think $tc should warn if it received a non-int value (float, string, array or whatever). Warning like $tc's second argument should be int but ... given. If you want to pass non-int number to the chosen message, consider $tc(messageKey, round(f), {count: f}) so that we can avoid confusing like this situation.

I think we can add int-ness check and warning around inside the below https://github.com/kazupon/vue-i18n/blob/6ffc60190d4d7910964186fd3904b77fd422a399/src/index.js#L517-L525

exoego avatar Aug 02 '19 21:08 exoego

I have same issue about decimals and I think it could be a good idea to let this situation documented.

jerrybendy avatar Dec 11 '19 03:12 jerrybendy

Same issue here.

jonalxh avatar Feb 26 '21 15:02 jonalxh

Same issue here too.

0xTheOldOne avatar Mar 09 '22 12:03 0xTheOldOne

Keep in mind that pluralization rules are vastly different in different languages. Since I'm not fluent in French, I don't know the rules here, but I can safely say that e.g. both English and German treat 1.5 as a plural form (=> "1.5 days" / "1,5 Tage"), not singular.

There's nothing preventing you from defining your own plural rule and I think this would be the safest way to handle the problem described here – not artificially limiting counts to integers.

clemens avatar May 12 '22 09:05 clemens

in french, plural is when number >= 2 so 0.5 and 1.5 are singular, 2.5 is plural

you could take inspiration on rails :

  • here are the different common pluralization rules
  • here is where its defined for each langage
  • here is a config that is not common (with 6 different translations each time [:zero, :one, :two, :few, :many, :other], thats crazy :D) (example)

MathieuDerelle avatar Jan 10 '23 09:01 MathieuDerelle

👋 I just faced this issue and found a clean solution by applying this logic on pluralizationRules when creating i18n instance. 🎉 🎉 🎉

// `choiceOptions` relates to the number of options for the specified translation
// `i18n.tc()` accepts 2 or 3 options, so the code is covering both cases
// In case of translation having only 2 options, empty and single options will be the same
// `choiceOptions` value is the length of options for that translation
const getOptionsByTranslationChoices = (choiceOptions) => choiceOptions === 3 ? {
  returnAsEmpty: 0,
  returnAsSingle: 1,
  returnAsPlural: 2,
} :
  {
    returnAsEmpty: 0,
    returnAsSingle: 0,
    returnAsPlural: 1,
  };

/**
 * A helper function for applying decimals as plural for english speakers
 * @param {number} [choice] - Number passed to `i18n.tc()` function as value to be resolved
 * @param {number} [choiceOptions] - `3` or `2`: Number of options available on the translation file to that specific key
 * @returns {number} 0: `empty`, 1: `single`, 2: `plural`
 */
const applyingDecimalsAsPluralForEnglishSpeakers = (choice, choiceOptions) => {
  // These codes are related to `vue-i18n` internals to pluralize resolution
  //  - 0: it should return empty value as result
  //  - 1: it should return single value as result
  //  - 2: it should return plural value as result
  const { returnAsEmpty, returnAsSingle, returnAsPlural } = getOptionsByTranslationChoices(choiceOptions);

  // Returns empty if receives `0` as value
  if (choice === 0) {
    return returnAsEmpty;
  }

  // NOTE: In english, decimals are also plural
  // This function always receives value as `number`, so it's safe to assume
  // decimal values as result of `!Number.isInteger()` and return as plural
  if (!Number.isInteger(choice)) {
    return returnAsPlural;
  }

  // Otherwise, check if number is more than 1 to return as plural or single
  return choice > 1 ? returnAsPlural : returnAsSingle;
};

// In this example, I'm applying this rule only for `en`
const pluralizationRules = {
  'en':applyingDecimalsAsPluralForEnglish,
};

const _i18n = createI18n({
  // ...
  // Applying the rule
  pluralizationRules,
});

willmendesneto avatar Aug 01 '23 16:08 willmendesneto