The Ruby on Rails I18n core api

posted: July 19th, 2008 · by: Sven

in: Globalization, Programming · tagged as: , , ·  34 comments »

Future versions of Rails will ship with a minimalistic, yet powerful I18n/L10n api baked in.

The following post is about technical api and implementation details. You can read more about the motivation and reasoning behind this work here.

The pivotal point of the new I18n api in Rails is the I18n module which is provided as a gem and shipped with Rails in ActiveSupport’s vendor directory. This module comes with the following features:

  • The main translation method #translate which is used to lookup translations.
  • The #localize method which is used to localize Date, DateTime and Time objects.
  • It stores the current locale in Thread.current.
  • It stores a default locale which is used when no locale has been passed or set.
  • It stores a backend which carries the actual implementation for the translate and localize methods.
  • It comes with a default exception handler which catches exceptions that are raised in the backend.

Both the backend and the exception handler can be swapped with different implementations. Also, #translate is aliased to #t and #localize is aliased to #l for convenience.

#translate supports the following common I18n features which are implemented in the provided Simple backend and should be implemented in all future backends:

  • Lookup of translations by a locale and nested (scoped, namespaced) keys.
  • Defaults that will be used if the lookup does not yield a translation.
  • Interpolation of values to a translation string.
  • Pluralization of translations depending on a :count option.

Lookup, scope and nested keys

Translations are looked up by keys which can be both Symbols or Strings, so these calls are equivalent:


I18n.t :message
I18n.t 'message'

#translate also takes a :scope option which can contain one or many additional keys that will be used to specify a “namespace” or scope for a translation key:


I18n.t :invalid, :scope => [:active_record, :error_messages]

This looks up the :invalid message in the ActiveRecord error messages.

Additionally, both the key and scopes can be specified as dot separated keys as in:


I18n.translate :"active_record.error_messages.invalid"

Thus the following calls are equivalent:


I18n.t 'active_record.error_messages.invalid'
I18n.t 'error_messages.invalid', :scope => :active_record
I18n.t :invalid, :scope => 'active_record.error_messages'
I18n.t :invalid, :scope => [:active_record :error_messages]

Defaults

When a default option is given its value will be returned if the translation is missing:


I18n.t :missing, :default => 'Not here'
# => 'Not here'

If the default value is a Symbol it will be used as a key and translated. One can provide multiple values as default. The first one that results in a value will be returned.

E.g. the following first tries to translate the key :missing and then the key :also_missing. As both do not yield a result the string ‘Not here’ will be returned:


I18n.t :missing, :default => [:also_missing, 'Not here']
# => 'Not here'

Interpolation

All options besides :default and :scope that are passed to #translate will be interpolated to the translation:


I18n.backend.store_translations 'en-US', :thanks => 'Thanks {{name}}!'
I18n.translate :thanks, :name => 'Jeremy'
# => 'Thanks Jeremy!'

If a translation uses :default or :scope as a interpolation variable an I18n::ReservedInterpolationKey exception is raised. If a translation expects an interpolation variable but it has not been passed to #translate an I18n::MissingInterpolationArgument exception is raised.

Pluralization

The :count interpolation variable has a special role in that it both is interpolated to the translation and used to pick a pluralization from the translations according to the pluralization rules defined by CLDR:


I18n.backend.store_translations 'en-US', :inbox => {
  :one => '1 message', 
  :other => '{{count}} messages'
}
I18n.translate :inbox, :count => 2
# => '2 messages'

The algorithm for pluralizations in en-US is as simple as:


entry[count == 1 ? 0 : 1]  

I.e. the translation denoted as :one is regarded as singular, the other is used as plural (including the count being zero).

If the lookup for the key does not return an Hash suitable for pluralization an I18n::InvalidPluralizationData exception is raised.

Bulk and namespace lookup

To lookup multiple translations at once an array of keys can be passed:


I18n.t [:odd, :even], :scope => 'active_record.error_messages'
# => ["must be odd", "must be even"]

Also, a key can translate to a (potentially nested) hash as grouped translations. E.g. one can receive all ActiveRecord error messages as a Hash with:


I18n.t 'active_record.error_messages'
# => { :inclusion => "is not included in the list", :exclusion => ... }

Setting and passing a locale

The locale can be either set pseudo-globally to I18n.locale (which uses Thread.current like, e.g., Time.zone) or can be passed as an option to #translate and #localize.

If no locale is passed I18n.locale is used:


I18n.locale = 'de-DE'
I18n.t :foo 
I18n.l Time.now

Explicitely passing a locale:


I18n.t :foo, :locale => 'de-DE'
I18n.l Time.now, :locale => 'de-DE'

I18n.locale defaults to I18n.default_locale which defaults to 'en-US'. The default locale can be set:


I18n.default_locale = 'de-DE'

Using a different backend

A different backend can be set on the I18n module:


I18n.backend = I18n::Backend::Gettext

The I18n gem (and thus Rails) only ships with the Simple backend which is tailored to Rails’ needs. Other backends can be provided as external solutions.

Exceptions and exception handlers

The #translate method catches exceptions that are thrown in the backend and passes them to the exception handler that is defined as I18n.exception_handler and defaults to I18n#default_exception_handler.

In #default_exception_handler all exceptions are re-raised except for MissingTranslationData exceptions. When a MissingTranslationData exception has been caught the #default_exception_handler will return the exception’s error message.

This behaviour is particulary useful during, e.g., view developement when the developer does not want to switch contexts (add a translated string to the view, define the key and translation somewhere else, go back to the view, …) because otherwise the application would break because of a missing translation.

On the other hand in different contexts a different exception handling might be useful. E.g. the default exception handling does not allow to catch missing translations during automated tests easily. For this purpose a different exception handler can be specified. The specified exception handler must be a method on the I18n module:


module I18n
  def just_raise_that_exception(*args)
    raise args.first
  end
end

I18n.exception_handler = :just_raise_that_exception

This would re-raise all caught exceptions including MissingTranslationData.

Another example where the default behaviour is less desirable is the Rails TranslationHelper which provides a method #translate. When a MissingTranslationData exception occurs in this context the helper wraps the message into a span with the css class translation_missing.

To do so the helper forces I18n#translate to raise exceptions no matter what exception handler is defined by setting the :raise option:


I18n.t :foo, :raise => true # always re-raises exceptions from the backend

Populating the translations storage

Libraries can use I18n.load_translations to populate the translations storage:


# in active_support.rb
I18n.load_translations 'active_support/locale/en-US.yml'

# in active_support/locale/en-US.yml
en-US:
  date:
    formats:
      default: "%Y-%m-%d"
      # ...

The Simple backend can load YAML files as well as plain Ruby files. If you want to store translations in Ruby make sure the file evaluates to a plain Ruby Hash like so:


# translations in Ruby
{ :'en-US' => {
  :date => { 
    :formats => {
      :default => "%Y-%m-%d"
      # ...
    }
  }
}}

Other backends might add capabilities to load from different sources like, e.g., SQL for a database backend or PO/MO files for a gettext backend.

Get involved!

If you’d like to join us working on Ruby on Rails’s future I18n support, provide feedback or ask questions please do so! You can find our Google Group over at http://groups.google.com/group/rails-i18n.

Leave a comment

34 Comments

  1. ste said July 19th, 2008 at 03:57 PM  

    “the first translation is regarded as singular, the second is used as plural (including the count being zero).”

    How do you solve the problem of displaying a different phrase in the case of count being zero? E.g. let’s say I wanted to display the strings “No messages”, “One message” and “3 messages”; is this possible?

  2. Clément Joubert said July 20th, 2008 at 02:39 PM  

    Minimalistic, maybe, but still one of the most (I’d say the most) thought-through implementation of L18n / I10n for use in Rails. Most issues encountered with plugins become a vague souvenir with this !

  3. Stephan Soller said July 20th, 2008 at 03:30 PM  

    Thanks for all the great work and the good overview Sven! It’s really good to see all this stuff getting into Rails.

    Ste, regarding the pluralization: You can change the pluralization algorithm in the locale. E.g. that it maps a count of zero to the first array element of a locale entry, a count of 1 to the second element and all other counts to the third element.

    Algorithm for pluralizations in @en-US@:

    entry[count == 0 || count == 1 ? count : 2]

    Translations:

    I18n.store_translations ‘en-US’, :inbox => [‘no messages’, ‘1 message’, ‘{{count}} messages’] I18n.translate :inbox, :count => 0 # => ‘no messages’ I18n.translate :inbox, :count => 5 # => ‘5 messages’

  4. Sven said July 20th, 2008 at 06:06 PM  

    Hey ste, Stephan,

    actually, Stephan, one can not. We took this out for various reasons, most importantly sticking to what’s necessary for Rails being localized to en-US. The en-US pluralization algorithm is currently hardcoded in #pluralize:

    http://github.com/rails/rails/tree/master/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb

    Obviously it won’t be too hard to implement custom logic in a different backend though: just create your own backend module, provide a method #pluralize and include the Simple backend module. Then use it with I18n.backend = FexiblyPluralizingBackend … or whatever.

  5. Fjan said July 20th, 2008 at 06:53 PM  

    The code probably looks nicer if you add something like:

    class Symbol def t(x);I18n::t(x);end end

    Then in your code you can simply do a more Rails-like: :message.t

  6. Seymour said July 20th, 2008 at 07:28 PM  

    I looked at some localization plugins a while ago, but didn’t like any of them. Instead I started doing my own, which was not as advanced as your, but had some neat ideas imho. I never finished it but maybe you find this useful (maybe you already had these ideas but dumped them):

    My main prio was to make it simple to use and easy to retrofit. So I too introduced the method #t (like h is used).

    <%= t "Information" %>

    <%= link_to t “Show all”, :action => “show” %>

    But I just put it in front of my existing strings. If the the chosen language to translate into was, say Swedish Chef, and if there was no Swedish Chef translation, it would fall back to the default that’s already there (“Show all”).

    If there was a Swedish Chef “Hum Dum Bork” in the structure connected to “Show all”, it would display that instead.

    If “Show all” later was changed in the code into “Show everything”, when looked up it would be added to the translation structure as a new entry since it didn’t exist.

    The old “Show all” would not be used anymore, something that would become obvious because the access counter for “Show all” would stop ticking. Reset the counters, exercise the code with the test suites, observe any new strings or not used strings via a web-gui for handling translations. Correct it.

    Of course, hunting for strings in the code to change could be avoided by adding an English translation stating that “Show all” (in the code) would be translated into “Show everything” (from the structure).

    So what I am missing (afai understand) from I18n is the following:

    • I18n.t :missing …without a translation and without a default, should return “Missing”. And can I include I18n to just use t directly?

    • An exercise mode to add all looked up translation strings (good for retrofit, or if you slip and don’t update like you should).

  7. Sven said July 20th, 2008 at 07:31 PM  

    Hey Fjan,

    we’ve been there before but for various reasons we finally agreed to abstain from anything like that - at least for the I18n gem that would go into Rails core.

    This sort of stuff can still be easily added in plugins though if people think it makes sense.

    Personally, by now I don’t think it does, because you wouldn’t call #t on the current context this way - like a view or controller in Rails. In the end it’s really a preference though and for that reason we sticked to the simplest thing possible for the I18n gem and left both Symbol#t and String#t undefined.

  8. Sven said July 20th, 2008 at 07:39 PM  

    Hey Seymour,

    you can do most of what you’re mentioning with the I18n gem with a slightly different flavor of syntax.

    If I get what you’re saying you are using default strings as keys. This is something which we regard as a bad practice for anything but very simple projects. Plugins building on the I18n api can still implement something like that easily though if they want.

    Also, notice that there’s a #t helper provided with Rails. So you can call <%= t :key %> in your views.

    If you want #t to return something different in case the key is missing and you don’t want to pass a default string you can always hook in a different exception handler (e.g. one that turns the key into a slightly better readable string).

    Stuff like excercise modes, gui translation edit modes etc. would be way to special and heavyweight to include them to Rails directly. Also … if we did it … what would poor plugin devs like us have to do then anymore ;) No, really, this is the sort of stuff we hope to (continue to) happen in plugin-land.

  9. dirk lüsebrink said July 20th, 2008 at 08:05 PM  

    i’m coming from a time where ASCII was all you ever wanted but i know i can’t hold up the progress and so am making friends with I18M and the like.

    Getting a standard I18N API inside rails is way better than any extension/plugin/gem bolted-on later.

    But in one way this goes way to far for my taste. Forcing every exception taking a codepath through the Translation code stack per default looks totally wrong and bloated to me. I would prefer keeping the default behaviour as it is and offer an options for the ones who really think they must support tranlated exception traces.

    Exceptions are communication with the developer, not with the users. And we also don’t write localized methods names and variables.

  10. Sven said July 20th, 2008 at 08:54 PM  

    Hey Dirk,

    not sure how you mean throwing an exception on an unexpected state is bloated, but using exceptions to communicate things like these from a backend to a frontend is a pretty common (and useful) pattern.

    And yes, it’s for communication between developers. That’s what was implemented here, isn’t it?

  11. Anders Engström said July 20th, 2008 at 10:46 PM  

    This looks really nice. While minimalistic, the API covers pretty much anything you need.

    I’ve written an i18n module that behaves similar to this API (but without the scoped keys). One thing that might be useful (but could be implemented in a custom backend) is to be able to execute a Proc when resolving a key.

    This allows me to do stuff like (in an ERB template):

    <%= _(:time, model.created_at) %>
    

    (where “_” is similar to #translate).

    The :time key is declared as:

    Localization.in_context("en") do |l|
        l.instance_eval do 
           define :time, proc{|time|
               if time.today?
                   "Today"
               elsif ....
           }        
    
            # ....
    
        end
    end
    
  12. Peter De Berdt said July 20th, 2008 at 11:45 PM  

    One aspect of l18n that still seems to be unaddressed is number localization. It’s a problem we European face in about every application that deals with numbers with decimal fractions. I’ve invested quite some time trying to find a clean and uninvasive (read: heavy monkeypatching with ugly things going on) to make Rails transparently handle “1.250,52” instead of “1,250.52” notations but haven’t find any option that would fit the bill.

    Although it is true that most bigger localization plugins nowadays just override the default number formatting helpers (numberto_currency and the likes), but when you want comma delimited decimal fraction to automatically be used in f.textfield :field_name, things start to go terribly wrong.

    Maybe I have missed something so blantly obvious, I like to hope so actually, but my fear is that I haven’t. What I came up with aliassing all number related getters and setters, then redefining them to replace dots with comma’s and feed that to the forms, but even there it still didn’t cover all bases.

    Maybe a thinktank like the l18n group can see the light. It just surprised me that no one but us has ever had to deal with this before.

  13. Fjan said July 21st, 2008 at 10:03 AM  

    :message.t

    you wouldn’t call #t on the current context this way - like a view or controller in Rails.

    Actually I do it that way in my own I18n thing (yes, I too didn’t like what was out there and built my own). I simply start each view with a method that sets a global:

     <% set_context(:login_view) %>
    

    And then simply call the t method on any literal in the view:

     <%= :Password.t %>
    

    The t method simply looks this up in a hash of hashes like your module does. The good thing about this is that the views remain very readable.

  14. Fjan said July 21st, 2008 at 10:12 AM  

    @Peter: transparently handle “1.250,52” instead of “1,250.52”

    Yes, I ran into that too. It gets even more complicated if you know that the French apparently like their numbers without thousand separators at all, so you have three versions. I ended up rolling my own there too. The Rails source code uses a clever regexp that takes care of all the work and is easily modified.

  15. KC Jones said July 21st, 2008 at 11:42 PM  

    After reading this and Sven’s blog post, I think I get it. And I appreciate that this fills a huge, gaping hole in a very lighthanded way - especially once a variety of backends have been developed. Can’t wait.

    One question: will this work have any impact on the ability of plugin developers to support per-locale templates? Or was that considered poor practice?

    I haven’t needed it in my nascent, simple localizations. And it seems to create serious maintenance / lifecycle issues, but it also seems pretty useful for some gnarly localizations.

  16. Sven said July 22nd, 2008 at 01:13 AM  

    Hey Fjan, Peter,

    you guys certainly have a point with the transparent number formatting in forms. Mind to register to the Rails I18n group and discuss things over there? http://groups.google.com/group/rails-i18n

    Hey KC Jones,

    thanks for the feedback :) At this point I don’t think the Rails I18n api has any impact on per-locale templates. If you use them in any way, you can continue to do so. Our patch does not touch this matter in any way.

    That said we’re certainly going to reinvestigate this topic in plugin land (can only speak for upcoming Globalize 2 here). At the end of the day it’s definitely something that belongs into the I18n/L10n toolbox.

    Generally speaking with this patch we did not aim to solve each and every problem in Rails core. We aimed to get started with the most important (and most often re-implemented) things and we’re definitely going to have a second iteration of the whole process (i.e.: check out existing implementations, extract + cherrypick common solutions, discuss + review them, finally bring them to core if that really, really makes sense).

  17. Aleksandr said July 23rd, 2008 at 09:00 AM  

    Will the new l18n api have a solution to extract the keys from the application like GetText does (rake updatepo)? Because copy-pasting them manually into the translation file can be a bitch if you’re dealing with a lot of languages.

  18. Mark said July 24th, 2008 at 11:08 AM  

    Is possible to use strings instead of symbols? It doesn’t seems very smart to repeat twice the translations :( :anenglishmessage => “An english message”

    English strings should be default and you want to translate them you could you a gettext-style, example _(‘Translate me’)

  19. Sven said July 24th, 2008 at 08:44 PM  

    Hey Aleksandr,

    no, the api is an api :) What you’re looking for is a tool that scrapes an application for keys (or rather base language translations). Although a useful tool, this certainly belongs into a plugin.

    Hey Mark,

    we’ve gone back and forth and back and forth endlessly discussing default strings and whether to support them in a way as solutions as Gettext, Globalize, Gibberish, … do. We finally decided against it because we really wanted to only rely on “the most simple thing that ever could work” for the Rails core api. And this definitely does not include default strings as keys (which is what you mean by “gettext-style”).

    We think it’s actually a bad practice for anything else than a very small project - backed by lots of experiences with said solutions. Thus, don’t recommend that any more. (Most important reason being default-translations-as-keys getting out of sync with the translation data … resulting in major headaches at times.)

    BUT that does not mean that future Rails plugins can’t implement this approach. They can. Both String#t and Symbol#t are left undefined by the API and plugin developers can implement whatever they think is useful here.

  20. Ben said July 25th, 2008 at 03:13 PM  

    Hey Sven,

    gute arbeit! Ich hoffe wir sehen uns bei BoR! :-)

    Gruss Ben

  21. Sven said July 26th, 2008 at 02:03 PM  

    Hey Ben,

    na klar!

    :)

  22. Glenn said July 29th, 2008 at 03:39 PM  

    I would think that in the simple backend impl, if the entry for a key is an Array (pluralizable), then :count value should default to 1 if not specified. Currently, it will simply return a concatenation of the two values (singular + plural).

    This can be easily fixed by changing the pluralize(entry, count) method in the simple.rb, by removing the “and count” check to the first line and adding a default value to the second line:

    def pluralize(entry, count) return entry unless entry.is_a?(Array) count ||= 1 raise InvalidPluralizationData.new(entry, count) unless entry.size == 2 entry[count == 1 ? 0 : 1] end

    Does anyone see any reason why this shouldn’t be the standard functionality?

  23. Sven said July 31st, 2008 at 01:16 PM  

    Hey Glenn,

    I agree it’s weird that it concatenates the Strings (it really does?). That’s not useful in any case.

    On the other hand I wonder why we would pick a default count here. My first guess would be that it would be more in line with the “spirit” of the rest of the API to return the actual pluralization data array here (like you could also lookup whole chunks of the hash, e.g.)

    It would be up to the user then to know this and do something useful with it. Actually this would even allow to implement pluralization in user land. So, why not?

  24. Glenn said July 31st, 2008 at 02:03 PM  

    Hi Sven,

    Very good points. I have implemented my own custom backend as you described doing, and I added my own pluralization logic. It does work, however, now the Rails Core localizations are all missing from my app. I think the problem is that I’m not setting my I18n backend at the correct time. Probably, the Core is adding all of it’s localizations at some point during the very start of the app’s init. Then after that, I call: I18n.backend = I18n::Backend::Custom which resets the backend and erases all the Core’s localizations.

    I was first setting the I18n.backend in my ApplicationHelper (which is where I also call I18n.backend.populate with my local file.

    Then I tried setting the backend in an config/initializer called I18n.rb This also doesn’t work.

    So, my question is, where do you set this backend, so that the Core strings remain in tact?

  25. Glenn said July 31st, 2008 at 03:06 PM  

    Ok, So I put the call at the end of my custom.rb file where I actually define my Custom backend, and now everything seems to be working ok.

    Sometimes, you just have to stop and sit outside for a bit, and the answer will come to you.

  26. Sven said August 1st, 2008 at 10:10 AM  

    Hey Glenn,

    please register to the Rails I18n Google Group at http://groups.google.com/group/rails-i18n and let people know about your work over there. There’s already another plugin (YAML-based, by Iain) in the oven and people are helping each other trying out their code and fixing issues.

    I guess that’s the way better plattform for discussing such things :)

  27. Andrew Roth said October 23rd, 2008 at 05:07 PM  

    Hey, where can I find the rubydocs?

  28. Peter said October 29th, 2008 at 10:06 AM  

    There is a huge disadvantage, too, associated with choosing short(ish) symbols or strings as translation keys, as opposed to the Globalize “Translate me!”.t approach, and that is that the code becomes very difficult to read, and thus to maintain. In practice, the keys tend to look like I18n.t :accloginrefuse_pt2, which conveys very little to the programmer. This is not something to be ignored, as it reduces the maintainability of the code, incurring higher costs.

    One important point is that Globalize offered translation of base language strings into the base language itself, which covers the case very neatly of when management wants to change strings — something which really is not that common anyway: you are exaggerating that aspect quite considerably. For instance, “Please enter your password”.t can be “translated” to “Please enter your secret password” or whatever management would require. And let’s face it, management is not very likely to tamper with strings like “Cancel”, “Save” and suchlike. And I speak from many years of experience. Dismissing long translation key strings is not necessarily something which will ease development.

    Take a look at an internationalized application, for instance the sample demo app on github. It’s a very small application, and a tutorial one at that — but there is very little relationship between what can be read in the views and what will be seen on screen.

    Another important aspect has to do with legacy applications. I’m currently working with several large applications, in a collaborative environment, based on Globalize. If Globalize 2 doesn’t support the old syntax at least in some measure, well, then it will be almost impossible to upgrade to Rails 2.2 or beyond.

    It doesn’t have to be much more complicated than this:

    ################################################################
    # Define a new backend, inheriting from the default Simple one #
    ################################################################
    
    module I18n
      module Backend
        class GlobalizeErsatz < Simple
          # Add code to load SQL after the usual .yml and .rb files
          # have been loaded. Also code to disable splitting on full stops
          # in keys where necessary.
        end
      end
    end
    
    I18n.backend = I18n::Backend::GlobalizeErsatz.new
    
    
    ################################################################
    # It takes the same .rb and .yml files as Simple, for the very #
    # basic translations.                                          #
    ################################################################
    
    LOCALES_DIRECTORY = File.join(RAILS_ROOT, 'config', 'locales')
    LOCALE_FILES = Dir[ File.join(LOCALES_DIRECTORY, '*.{rb,yml}') ]
    
    LOCALES = LOCALE_FILES.collect do |locale_file|
      File.basename(File.basename(locale_file, ".rb"), ".yml")
    end.uniq.sort
    
    
    I18n.load_path += LOCALE_FILES
    
    
    ################################################################
    # Re-implement the classical Globalize string extensions       #
    ################################################################
    
    class String
    
      def translate
        I18n.t self, :default => self
      end
    
      def t
        translate
      end
    
      def tn(namespace)
        I18n.t self, :scope => namespace, :default => self
      end
    
      def /(val)
        if sub!('%d', '{{count}}')
          I18n.t self, :count => val, :default => self
        elsif sub!('%s', '{{s}}')
          I18n.t self, :s => val, :default => self
        else
          translate
        end
      end
    
    end
    
  29. Sven said October 29th, 2008 at 11:06 AM  

    Hi Peter,

    that “classical” Globalize way of using default strings as keys certainly works for some projects.

    We’ve seen people more and more moving away from it though and using fixed keys instead (that also are easy to recognize as keys) - be it that they’ve set the Globalize base language to some fantasy locale like “dev” or simply move to a different solution.

    For the I18n gem we’ve aimed to provide a foundation for building plugins on top of it. That’s what you can do. It is true that with Globalize we believe we’ve learned something from the past when we’ve settled with separating keys and defaults. That doesn’t mean that your implementation needs to do the same.

    Also, of course the fullstops/dots as key separators are a limitation. We’ve been thinking about making this optional (you could still use arrays of keys), but we want to see how widespread that need really is.

    So if you really want this, why not just implement what you’ve layed out above as a plugin and publish it?

    Having settled with a common API in Rails does not mean that we can not experiment with different solutions any more :)

  30. Sven said October 29th, 2008 at 11:11 AM  

    Oh, one more thing ;)

    Of course instead of <%= “Please enter your password”.t %> you can still use <%= t “Please enter your password” %> and use that as a key. So you can have readable keys if you perfer to.

  31. Seb said December 4th, 2008 at 09:35 PM  

    Hi,

    Thanks for your work !

    There is a little mistake. You wrote well first time: I18n.t :invalid, :scope => [:activerecord, :errormessages] But second time, you miss the ‘,’ I18n.t :invalid, :scope => [:activerecord :errormessages]

    Seb

  32. Miika said December 16th, 2008 at 10:06 PM  

    Hi

    I made an exception handler that tries to find default translation. I’d like to see this kind of feature in the default implementation… module I18n def self.findtranslationfrom_default(*args) if MissingTranslationData === args.first && args[1] != I18n.default_locale return self.translate(args[2], :locale => I18n.default_locale) end defaultexceptionhandler(*args) end

    end

  33. Sam said January 20th, 2009 at 06:07 PM  

    New bug: If you have in file “config/fr.yml” activerecord: models: attributes: article: title: “Le champ Titre” price: “Le champ Prix errors: messages: exclusion: “est déjà pris !” notanumber: “n’est pas un nombre”

    And in view: for exclusion message, you have “Le champ Titre est déja pris !”. So, you aren’t message “Le champ Prix n’est pas un nombre” but “Price n’est pas un nombre”. A bug, missing “t” function before actionview ?

  34. Norstone Wooden said December 24th, 2009 at 09:30 AM  

    Hi,

    Thanks for your work !

Leave a comment

Name required
E-Mail and Website optional

If you can read this, you don't use a typical webbrowser that plays nice with CSS.
Please do not fill in anything here!

Hint: Markdown will be applied to your comment. If you post any code, be sure to escape underscores (like so: \_) if you do not want them to be converted to an <em>phasis.

artweb design
Sven Fuchs
Grünberger Str. 65
10245 Berlin, Germany


http://www.artweb-design.de

Fon +49 (30) 47 98 69 96
Fax +49 (30) 47 98 69 97