Make ActiveSupport::Inflector locale aware and multilingual #7197

Merged
merged 1 commit into from Jul 31, 2012

Projects

None yet

5 participants

@davidcelis
Contributor

The Inflector is currently not very supportive of internationalized
websites. If a user wants to singularize and/or pluralize words based on
any locale other than English, they must define each case in locale
files. Rather than create large locale files with mappings between
singular and plural words, why not allow the Inflector to accept a
locale?

This patch makes ActiveSupport::Inflector locale aware and uses the
application's I18n.default_locale unless otherwise specified. Users
will still be provided a list of English (:en) inflections, but they may
additionally define inflection rules for other locales. Each list is
kept separately and permanently. There is no reason to limit users to
one list of inflections:

ActiveSupport::Inflector.inflections(:es) do |inflect|
  inflect.plural(/$/, 's')
  inflect.plural(/([^aeéiou])$/i, '\1es')
  inflect.plural(/([aeiou]s)$/i, '\1')
  inflect.plural(/z$/i, 'ces')
  inflect.plural(/á([sn])$/i, 'a\1es')
  inflect.plural(/é([sn])$/i, 'e\1es')
  inflect.plural(/í([sn])$/i, 'i\1es')
  inflect.plural(/ó([sn])$/i, 'o\1es')
  inflect.plural(/ú([sn])$/i, 'u\1es')

  inflect.singular(/s$/, '')
  inflect.singular(/es$/, '')

  inflect.irregular('el', 'los')
end

'ley'.pluralize(:es)   # => "leyes"
'ley'.pluralize(:en)   # => "leys"
'avión'.pluralize(:es) # => "aviones"
'avión'.pluralize(:en) # => "avións"

A multilingual Inflector should be of use to anybody that is tasked with
internationalizing their Rails application.

Signed-off-by: David Celis david@davidcelis.com

Owner

This seems cool.

cc/ @NZKoz @fxn

@tenderlove tenderlove and 1 other commented on an outdated diff Jul 30, 2012
...vesupport/lib/active_support/inflector/inflections.rb
@@ -20,8 +22,12 @@ module Inflector
# pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
# already have been loaded.
class Inflections
- def self.instance
- @__instance__ ||= new
+ def self.instance(locale = I18n.default_locale)
+ unless instance_variable_defined?("@__#{locale}_instance__")
+ instance_variable_set("@__#{locale}_instance__", new)
+ end
+
+ instance_variable_get("@__#{locale}_instance__")
tenderlove
tenderlove Jul 30, 2012 Owner

Can we just keep this in a hash or something? I'm really against dynamic class instance variables.

davidcelis
davidcelis Jul 30, 2012 Contributor

Yeah, I wasn't terribly happy about this either; this sounds much better. Done!

davidcelis
davidcelis Jul 30, 2012 Contributor

Actually, it looks like that's made a ton of tests fail. Investigating that now.

EDIT: Fixed. All tests passing!

Member

👍 I think this is a great idea, thanks! There are a few things to discuss though:

  1. Today, we make the default I18n.default_locale, I think this is a bad idea because it can lead to your tables and other internal information to be inflected using another locale than :en. I believe we should default to :en, even more because I18n.default_locale doesn't mean anything, if your application has more than one locale, it will be at least half of the time the wrong one (and if it has only one locale, you simply don't care).

  2. underscore, camelize, titleize, etc do not accept a locale. I am not sure if this is a deliberate decision, but I believe it should be discussed. Although I can see how some would never be used with a locale, like underscore, titleize and humanize seem expected to accept a locale.

  3. We should probably update config/initializers/inflections.rb file that ships with Rails with the new info

@thedarkone thedarkone referenced this pull request Jul 30, 2012
Closed

Thread safety #6917

Contributor

Sure, I can go over my reasoning for those things:

  1. I figured that this might actually be a nicety for Rails devs whose first languages aren't English or are working on a team that is primarily speaking another language. While it's true that the keywords in Ruby are all in English, I didn't really see any reason to restrict their class constants to be in English. Since the default_locale doesn't change after initialization, I saw it as a safe decision because the table mappings will be kept consistent. If you think that Rails internally should always use :en, however, I'm all ears.

  2. Singularization/Pluralization were the only methods that really made sense to me to take a locale. Underscore, camelize and titleize seem to behave consistently based on casing rather than anything dependent on language. Actually, tableize does not take a locale and, depending on your answer to question 1, it should or shouldn't (since it uses pluralize internally). Only the singularization and pluralization rules seemed to differ between languages. I can certainly make those methods locale aware as well, I just wasn't sure why I should.

  3. Definitely. I see a couple possibilities with this... Add (:en) to the commented out ActiveSupport::Inflector.inflections block, turn the existing block into a short declaration of rules for some other locale (such as the rules for :es that I gave above), or maybe just add a short block at the bottom for some other locale to give an example. Either way, I'll definitely update the commented note above it with an additional note that a locale can be specified and that multiple lists of inflections can be maintained.

Member
  1. Except it isn't backwards compatible. Many people today have their default_locale set to something other than english while keeping their whole application in english. Also I18n.default_locale (and I18n as a whole) focuses mainly on view behavior, having it drastically affect how an application works internally is a bad idea. We could have another option for that, but I would rather see the need before adding it.

  2. Well, I believe some people use titleize for such purpose but let's go with what I just said above: let's see the need before adding it. About different pluralization and singularization rules, I18n already tackles it. I don't think it is in scope with Rails (given their complexity)

  3. Great.

Contributor
  1. Okay, makes sense. I've made it default to :en as such.

  2. Sounds good.

  3. I went ahead and changed the commented block to rules for Spanish. It makes the comment a bit long, but I thought this would be the most useful option. It specifies how to define rules for another locale while also providing (what I believe to be) a good list of rules for Spanish, which would arguably be the most-used second locale. If you think the comment's too long, however, I can change it to one of the other options I mentioned.

  4. Figured I'd ask because the contribution guides don't really mention it: should I also update the internationalization guide? I can also update ActiveSupport's CHANGELOG in this commit and 4.0 release notes, but I don't know if that's typically handled by Rails Core.

Member

Thanks!

About 3), I would say that passing :en as argument by default and a simple note should suffice. I would keep the examples in english, having them in spanish (and many examples) distracts from the main goal which is to show case the API.

/cc @fxn what is your take on 3) ?

David Celis Make ActiveSupport::Inflector locale aware and multilingual
The Inflector is currently not very supportive of internationalized
websites. If a user wants to singularize and/or pluralize words based on
any locale other than English, they must define each case in locale
files. Rather than create large locale files with mappings between
singular and plural words, why not allow the Inflector to accept a
locale?

This patch makes ActiveSupport::Inflector locale aware and uses `:en`` unless
otherwise specified. Users will still be provided a list of English (:en)
inflections, but they may additionally define inflection rules for other
locales. Each list is kept separately and permanently. There is no reason to
limit users to one list of inflections:

    ActiveSupport::Inflector.inflections(:es) do |inflect|
      inflect.plural(/$/, 's')
      inflect.plural(/([^aeéiou])$/i, '\1es')
      inflect.plural(/([aeiou]s)$/i, '\1')
      inflect.plural(/z$/i, 'ces')
      inflect.plural(/á([sn])$/i, 'a\1es')
      inflect.plural(/é([sn])$/i, 'e\1es')
      inflect.plural(/í([sn])$/i, 'i\1es')
      inflect.plural(/ó([sn])$/i, 'o\1es')
      inflect.plural(/ú([sn])$/i, 'u\1es')

      inflect.singular(/s$/, '')
      inflect.singular(/es$/, '')

      inflect.irregular('el', 'los')
    end

    'ley'.pluralize(:es)   # => "leyes"
    'ley'.pluralize(:en)   # => "leys"
    'avión'.pluralize(:es) # => "aviones"
    'avión'.pluralize(:en) # => "avións"

A multilingual Inflector should be of use to anybody that is tasked with
internationalizing their Rails application.

Signed-off-by: David Celis <david@davidcelis.com>
7db0b07
Contributor

Agreed; I reverted the initializer back and just added a note about locales (while showing :en being passed in). Not sure if you wanted @fxn's opinion on that or the updating of the guides. I have an update to the guide stashed, so if you guys decide you want that in, I can easily add it. If not, thanks for taking the time to look through all this!

Member

For me this is ready to go. /cc @tenderlove @rafaelfranca @fxn go?

Owner

:shipit:

Looks good 👍.

I'm just thinking a little bit about how the API looks like:

pluralize('foo', :en)
#vs
pluralize('foo', locale: :en)

Anyway, :shipit:

@josevalim josevalim merged commit 13af5ac into rails:master Jul 31, 2012
@davidcelis davidcelis referenced this pull request in davidcelis/inflections Jan 9, 2014
Closed

default locale not detected when loaded normally #11

@davidcelis davidcelis deleted the davidcelis:i18n_inflector branch Mar 4, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment