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

Revert "Add support for CLDR data in I18n::Backend::Pluralization" #633

Merged
merged 1 commit into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
69 changes: 17 additions & 52 deletions lib/i18n/backend/pluralization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,75 +16,40 @@ module Backend
module Pluralization
# Overwrites the Base backend translate method so that it will check the
# translation meta data space (:i18n) for a locale specific pluralization
# rule and use it to pluralize the given entry. I.e., the library expects
# rule and use it to pluralize the given entry. I.e. the library expects
# pluralization rules to be stored at I18n.t(:'i18n.plural.rule')
#
# Pluralization rules are expected to respond to #call(count) and
# return a pluralization key. Valid keys depend on the pluralization
# rules for the locale, as defined in the CLDR.
# As of v41, 6 locale-specific plural categories are defined:
# :few, :many, :one, :other, :two, :zero
# return a pluralization key. Valid keys depend on the translation data
# hash (entry) but it is generally recommended to follow CLDR's style,
# i.e., return one of the keys :zero, :one, :few, :many, :other.
#
# n.b., The :one plural category does not imply the number 1.
# Instead, :one is a category for any number that behaves like 1 in
# that locale. For example, in some locales, :one is used for numbers
# that end in "1" (like 1, 21, 151) but that don't end in
# 11 (like 11, 111, 10311).
# Similar notes apply to the :two, and :zero plural categories.
#
# If you want to have different strings for the categories of count == 0
# (e.g. "I don't have any cars") or count == 1 (e.g. "I have a single car")
# use the explicit `"0"` and `"1"` keys.
# https://unicode-org.github.io/cldr/ldml/tr35-numbers.html#Explicit_0_1_rules
# The :zero key is always picked directly when count equals 0 AND the
# translation data has the key :zero. This way translators are free to
# either pick a special :zero translation even for languages where the
# pluralizer does not return a :zero key.
def pluralize(locale, entry, count)
return entry unless entry.is_a?(Hash) && count

pluralizer = pluralizer(locale)
if pluralizer.respond_to?(:call)
# "0" and "1" are special cases
# https://unicode-org.github.io/cldr/ldml/tr35-numbers.html#Explicit_0_1_rules
if count == 0 || count == 1
value = entry[symbolic_count(count)]
return value if value
end

# Lateral Inheritance of "count" attribute (http://www.unicode.org/reports/tr35/#Lateral_Inheritance):
# > If there is no value for a path, and that path has a [@count="x"] attribute and value, then:
# > 1. If "x" is numeric, the path falls back to the path with [@count=«the plural rules category for x for that locale»], within that the same locale.
# > 2. If "x" is anything but "other", it falls back to a path [@count="other"], within that the same locale.
# > 3. If "x" is "other", it falls back to the path that is completely missing the count item, within that the same locale.
# Note: We don't yet implement #3 above, since we haven't decided how lateral inheritance attributes should be represented.
plural_rule_category = pluralizer.call(count)

value = if entry.has_key?(plural_rule_category) || entry.has_key?(:other)
entry[plural_rule_category] || entry[:other]
else
raise InvalidPluralizationData.new(entry, count, plural_rule_category)
end
key = count == 0 && entry.has_key?(:zero) ? :zero : pluralizer.call(count)
raise InvalidPluralizationData.new(entry, count, key) unless entry.has_key?(key)
entry[key]
else
super
end
end

protected

def pluralizers
@pluralizers ||= {}
end

def pluralizer(locale)
pluralizers[locale] ||= I18n.t(:'i18n.plural.rule', :locale => locale, :resolve => false)
end

private
def pluralizers
@pluralizers ||= {}
end

# Normalizes categories of 0.0 and 1.0
# and returns the symbolic version
def symbolic_count(count)
count = 0 if count == 0
count = 1 if count == 1
count.to_s.to_sym
end
def pluralizer(locale)
pluralizers[locale] ||= I18n.t(:'i18n.plural.rule', :locale => locale, :resolve => false)
end
end
end
end
27 changes: 7 additions & 20 deletions test/backend/pluralization_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,16 @@ class Backend < I18n::Backend::Simple
def setup
super
I18n.backend = Backend.new
@rule = lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : n == 0 || (2..10).include?(n % 100) ? :few : (11..19).include?(n % 100) ? :many : :other }
@rule = lambda { |n| n == 1 ? :one : n == 0 || (2..10).include?(n % 100) ? :few : (11..19).include?(n % 100) ? :many : :other }
store_translations(:xx, :i18n => { :plural => { :rule => @rule } })
@entry = { :"0" => 'none', :"1" => 'single', :one => 'one', :few => 'few', :many => 'many', :other => 'other' }
@entry = { :zero => 'zero', :one => 'one', :few => 'few', :many => 'many', :other => 'other' }
end

test "pluralization picks a pluralizer from :'i18n.pluralize'" do
assert_equal @rule, I18n.backend.send(:pluralizer, :xx)
end

test "pluralization picks the explicit 1 rule for count == 1, the explicit rule takes priority over the matching :one rule" do
assert_equal 'single', I18n.t(:count => 1, :default => @entry, :locale => :xx)
assert_equal 'single', I18n.t(:count => 1.0, :default => @entry, :locale => :xx)
end

test "pluralization picks :one for 1, since in this case that is the matching rule for 1 (when there is no explicit 1 rule)" do
@entry.delete(:"1")
test "pluralization picks :one for 1" do
assert_equal 'one', I18n.t(:count => 1, :default => @entry, :locale => :xx)
end

Expand All @@ -36,22 +30,15 @@ def setup
assert_equal 'many', I18n.t(:count => 11, :default => @entry, :locale => :xx)
end

test "pluralization picks explicit 0 rule for count == 0, since the explicit rule takes priority over the matching :few rule" do
assert_equal 'none', I18n.t(:count => 0, :default => @entry, :locale => :xx)
assert_equal 'none', I18n.t(:count => 0.0, :default => @entry, :locale => :xx)
assert_equal 'none', I18n.t(:count => -0, :default => @entry, :locale => :xx)
test "pluralization picks zero for 0 if the key is contained in the data" do
assert_equal 'zero', I18n.t(:count => 0, :default => @entry, :locale => :xx)
end

test "pluralization picks :few for 0 (when there is no explicit 0 rule)" do
@entry.delete(:"0")
test "pluralization picks few for 0 if the key is not contained in the data" do
@entry.delete(:zero)
assert_equal 'few', I18n.t(:count => 0, :default => @entry, :locale => :xx)
end

test "pluralization does Lateral Inheritance to :other to cover missing data" do
@entry.delete(:many)
assert_equal 'other', I18n.t(:count => 11, :default => @entry, :locale => :xx)
end

test "pluralization picks one for 1 if the entry has attributes hash on unknown locale" do
@entry[:attributes] = { :field => 'field', :second => 'second' }
assert_equal 'one', I18n.t(:count => 1, :default => @entry, :locale => :pirate)
Expand Down