svenfuchs / rails-i18n

Repository for collecting Locale data for Ruby on Rails I18n as well as other interesting, Rails related I18n stuff
http://rails-i18n.org
MIT License
3.97k stars 2.77k forks source link

Why there's "#include?" and no "#between?" methods? #744

Closed sagidM closed 1 year ago

sagidM commented 7 years ago

I don't understand why is used #include? instead of #between? method in conditions for pluralizations, like here /lib/rails_i18n/common_pluralizations/east_slavic.rb

In that example, when count is_a? String there's no errors raised and result is :other. It happens because of String#% method.

Well, count is supposed to be kind of Numeric right? (if not, you may stop reading here)

So, I really like this gem and, wanting to improve it, I simply offer you this solution:

rule: lambda do |n|
  return :other if n.is_a? Float

  n = -n if n < 0  # (-1 % 10) shouldn't turn into 9
  mod10 = n % 10
  mod100 = n % 100

  return :many if mod10.zero? || mod10.between?(5, 9) || mod100.between?(11, 20)
  return :one  if mod10 == 1
  return :few  if mod10.between? 2, 4  # although, this condition isn't necessary
end

It's the most relevant for those locales that has long irregular ranges like /rails/pluralization/ar.rb where methods to_a and include? are called for each time.

I can make all this job and pull request if you give green light.

Eneroth3 commented 6 years ago

I suspect include, in combination with to_a, is to prevent fractional numbers from being regarded as :few or :many. CLDR states fractional in Russian are :other.

Your code returns :other directly for Floats (same as I am doing myself in another project) but the current rails-i18n code doesn't, and have to take these into account for all clauses.

Btw, I think this:

return :one  if mod10 == 1 && mod100 != 11
return :few  if mod10.between?(2, 4) && !mod100.between?(12, 14)
return :many

reads slightly better than this:

return :many if mod10.zero? || mod10.between?(5, 9) || mod100.between?(11, 20)
return :one  if mod10 == 1
return :few  if mod10.between? 2, 4  # although, this condition isn't necessary

I don't speak Russian so I don't really know but I get an impression it is the values ending with 1-4 (exuding those ending with 11-14) that sticks out, and those ending with 0, 5-9 and 11-20 being some sort of default.

sagidM commented 6 years ago

I'm sorry, could you just give an example of different results?

sunny commented 1 year ago

I'm sorry, could you just give an example of different results?

This is in the current implementation, with only round numbers being treated as one, few or many:

> RailsI18n::Pluralization::EastSlavic.rule.call(2.0)
# => :few
> RailsI18n::Pluralization::EastSlavic.rule.call(2.5)
# => :other
sagidM commented 1 year ago

I got it but, as I suggested, all this can be skipped with return :other if n.is_a? Float at the top. Simply execute my solution, it also returns :other for 2.5

It's a little complicated topic on what to use with fractional numbers. Many suggest always using the :few form http://new.gramota.ru/spravka/buro/search-answer/?s=%D1%80%D1%83%D0%B1%D0%BB%D1%8F%20%D0%B8%D0%BB%D0%B8 But notice that specifically in Russian, there're 3 forms, not 4.

sunny commented 1 year ago

I got it but, as I suggested, all this can be skipped with return :other if n.is_a? Float at the top.

I think we would like to keep the current behavior of having 2.0 => :few, though, since some people may rely on that behavior.

sagidM commented 1 year ago

All right, fair enough