svenfuchs/i18n — Issue #123: Better handling of InvalidPluralizationData
Author: nikosd
Created: 2011-11-11 11:17:54+00:00
State: open

I'm having some hard times trying to handle errors related to missing translations for languages with non 'germanic' pluralization forms (for example Polish, Russian, etc).

## The problem / current behaviour

The problem comes up when the following scenario is true :

```ruby
# Given the locale is `ru`
I18n.locale = :ru

# And the pluralization key for `2` is :few
pluralizer.call(2) # => :few

# And the ru messages are missing (ru.po)
#
# msgid "singular text"
# msgid_plural "plural text"
# msgstr[0] ""
# msgstr[1] ""
# msgstr[2] ""

# Asking for translations with count => 2 raises exception
n_('singular text', 'plural text', 2) # => raises InvalidPluralizationData
```

## Wanted behaviour

Instead I would like the following :

```ruby
# Asking for translations with count => 2 should return the default plural form
n_('singular text', 'plural text', 2) # => 'plural text'
```

## Current (hack) workaround

To actually make this work in our own applications I have made the following monkey patch on the pluralization module :

```ruby
module I18n
module Backend
module Pluralization
# Overriding the pluralization method so if the proper plural form is missing we will try
# to fallback to the default gettext plural form (which is the `germanic` one).
def pluralize(locale, entry, count)
return entry unless entry.is_a?(Hash) and count

pluralizer = pluralizer(locale)
if pluralizer.respond_to?(:call)
return entry[:zero] if count == 0 && entry.has_key?(:zero)

plural_key = pluralizer.call(count)
return entry[plural_key] if entry.has_key?(plural_key)

# fallback to the default gettext plural forms if real entry is missing (for example :few)
default_gettext_key = count == 1 ? :one : :other
return entry[default_gettext_key] if entry.has_key?(default_gettext_key)

# If nothing is found throw the classic exception
raise InvalidPluralizationData.new(entry, count)
else
super
end
end
end
end
end
```

but this is *not* the right way to do it since it's way too opinionated.

## Possible approches / Ideal solutions

Ideally, any of the following would be _good_ solutions :

### A. Using the generic I18n.exception_handler

```ruby
# config/initializers/i18n.rb
module I18n
def self.handle_invalid_pluralization_data(*args)
exception = args.first
if exception.is_a?(InvalidPluralizationData)
# do your thing here ...
else
super
end
end
end

I18n.exception_handler = :handle_invalid_pluralization_data
```

to make this work something InvalidPluralizationData should become a MissingTranslation which makes sense to me also in terms of semantics, since it's not the case that the localization data are invalid, it's that a specialized localization datum is missing.

alternatively :

### B. Using a specialized I18n::Backend::Pluralization.invalid_pluralization_handler

```ruby
# config/initializers/i18n.rb
I18n::Backend::Pluralization.invalid_pluralization_handler = Proc.new do |locale, key, count|
# to the handling here
end
```
which means that the pluralization backend should be changed to something _like_ :

```ruby
module I18n
module Backend
module Pluralization
# ...
def pluralize(locale, entry, count)
return entry unless entry.is_a?(Hash) and count

pluralizer = pluralizer(locale)
if pluralizer.respond_to?(:call)
key = count == 0 && entry.has_key?(:zero) ? :zero : pluralizer.call(count)
entry.has_key?(key) ? entry[key] : pluralization_error_proc.call(locale, entry, count)
else
super
end
end

# ...
end
end
end
```