Ruby on Rails plugin: Localize your Rails app

Recently I’ve been working on a plugin for Ruby on Rails that help me out when creating new Rails applications in Danish. Previously, when we’ve created Danish prototypes for our customers all the default Rails error messages were still written in English.

I generalized it for use with any language, and currently (with the help from contributors) it’s now available for download on rubyforge.

screenshot-localization-simplified-pirate-talk.png

I gave a short plugin demo at our recent Copenhagen Rails Meetup. Olle and Jakob already wrote briefly about it. You can read more (and vote) on agilewebdevelopment page Localization Simplified.

Currently supported languages:

  • Danish
  • Dutch (thanks Jeroen Houben)
  • German
  • Spanish (thanks Luis Villa)
  • Swedish (thanks Olle Jonsson)
  • Swedish Chef
  • Pirate Talk (thanks Tobias Michaelsen)

screenshot-localization-simplified-sweedish-chef.png
The main reason why I wanted to support Swedish chef and Pirate talk is that I want to remind myself to have fun while coding. And of course it’s a plus that International Talk like a Pirate day is coming up at Sept. 19, arrrh!

What it does

This plugin modifies the following most used helpers for Rails

  • Localized errors and error headings
  • Localized monthnames on date_select etc. (changing the order of Y-M-D, where Rails allows)
  • Localized distance_of_time_in_words
  • Localized to_currency (but not changing the order of unit/currency)
  • Simple pluralization also available in the lang-file (but currently only used for pluralizing “error”=>”errors” in local language)
  • Uses standard Rails methods. In this way, there is no tedious rewrite of localization functions in your view files

There are some FIXME’s in the code and any help on the plugin is appreciated.

As I’ve written in the Rails Core group, this plugin does some work that I hope will be worked into the Rails framework for easier future internationalization.

An example: distance_of_time_in_words() are hardcoded and translated into at least 4 different l10n plugins. If it changes in Rails Core, ALL the plugins must be fixed to ensure that the new code is copied to the plugins.

Seen from the perspective of a localized app, there should be as few dependencies as possible, so making hooks available for localization is preferred. In the example above, the texts should be available as a variable (like the error messages in ActiveRecord).

This would be an ideal solution to some of the most important i18n related issues, and I’d like to know the view of the community on this point.

From my post “i18n-friendly, plugable Rails core” in the Ruby on Rails Core list.

It’s my hope that the Rails Core team will acknowledge that it should be easier to localize your application. (Today, many text strings and date/time/currency formats are hard coded and difficult to overwrite).

Technorati Tags: , , , , , , , , , , , , , ,

26 Responses to “Ruby on Rails plugin: Localize your Rails app”

  1. Olle Jonsson’s Morningstar » Rails error messages - in your language Says:

    […] Update: Jesper shows the plugin in action at his website. Take a look! […]

  2. Kasper Weibel Says:

    You have probably all heard about the new keyboard layout designed specifically for pirates. It’s all R keys.

  3. MIro Says:

    Cool stuff ! Only thing which i see as missing and really needed is to translate also column names, as I can see from example above “First Name” is not translated – too bad… Any plans to support that?

  4. MIro Says:

    I mean “Last name” of course… ;o)

  5. Edgar Says:

    Nice stuff.

    How you compare your plugin with Ruby-GetText ?

    I ask ’cause, currently I’m localizating rubycorner.com using Ruby-GetText.

  6. Jesper Rønn-Jensen Says:

    MIro: No plans on translating column names or model names. I’m afraid that this will bloat the code more and give difficult dependencies to Rails code. Some of the other l10n/i18n plugins support it, but it usually requires that you add plugin dependent methods in your views.

    If you build a local website using english model and attribute names, one workaround could be to translate model to local language (although I know this is considered bad code style in most cases).

    I have no plans right now for adding translation of model names and attributes, but if somebody can show me how to do it without adding code in views and without giving too many dependencies, I might consider it.

    Edgar: For a comparison of the different localization plugins, see http://wiki.rubyonrails.org/rails/pages/InternationalizationComparison

  7. Daily rumblings Says:

    Polish your Rails…

  8. Jey Says:

    It’s not enough for L10n to support “one-language application” only.

    If you merge your plugin into Rails Core, I hope it’ll support multi-language like other L10n plugins.

    Anyway, if your plugin works with Gettext, I’ll try it.

  9. jeroen Says:

    jesper,

    I like the narrow focus of this plugin (one language per app) – but I really miss being able to reorder date selects. I mean the whole purpose of this plugin is to quickly create a localized form. Would you consider supporting it, I’ll be happy to take a look and see if it can be done with too much hassle.

    Jeroen

  10. Jesper Rønn-Jensen Says:

    Jeroen,
    The newest version already supports reording of date_selects but not datetime_selects (not implemented in Rails). I started a discussion on the Rails Core mailinglist.

    According to Bob Silva, there are patches for datetime_select, so you could start using it right away.

    Reordering of date_selects is starting in version 0.6 of the LocalisationSimplified plugin.

  11. Jesper Rønn-Jensen Says:

    Info: Just released version 0.6.1 with few bugfixes and more new languages.
    http://rubyforge.org/frs/?group_id=2074&release_id=6836

  12. jeroen Says:

    Hmm I just updated the plugin but this still doesn’t work:

    select_date(Date.today, {:order => [:day, :month, :year]})

    Am I overlooking something?

  13. Jesper Rønn-Jensen Says:

    Plugin doesn’t work (yet) for select_date() but only for date_select and datetime_select.

  14. jeroen Says:

    This should fix select_date to use the correct ordering too.

    def select_date(date = Date.today, options = {})
    order = LocalizationSimplified::DateHelper::DateSelectOrder[:order]

    send(“select_#{order[0]}”.to_sym, date, options) +
    send(“select_#{order[1]}”.to_sym, date, options) +
    send(“select_#{order[2]}”.to_sym, date, options)
    end

    Jeroen

  15. justaddwater.dk | See you at RailsConf, Europe this Thursday Says:

    […] As I pointed out while working on my own Localization plugin, there are plenty of loose ends in Rails that could make localization much easier with Rails. […]

  16. benedikt Says:

    Nice plugin – small, easy, exactly what I was looking for.

    I had to remove the UTF8 part though, because it didn’t play with .rjs templates (i.e. remote javascript) for what reason ever.

    For the column names: For my needs it was enough to overwrite ActiveRecord::Base.attribute_human_name (the class method) – this works nice for me in 1.1.6 and Edge.

    Last but not least, the german translation isn’t quite perfect ;) – but that’s ok, you have pirate’s talk. The need for different flections? and articles depending on the context are a problem which seems to be a bit harder to solve within a simple solution.

  17. Jesper Rønn-Jensen Says:

    Benedikt: Thanks for your thoughts and experiments on this. I would love if you’d send me the code examples you ended up using. I will add it to the plugin if it can be of general benefit.

    I would be glad if you modify the german translation: I did some of that, so that explains the quality :) Please send me an updated copy, so that I can give you credit and include in next release

    I have not tried it out yet with RJS, so I’m really glad you noticed. Please let me know what you did and what response you got. I hope that maybe someone else can contribute with a solution.

  18. BT Says:

    I even ran into the .rjs problem. And if the line with the modified header (localization_simplified.rb, line 256) is commented out everything works fine for me. Do you know what kind of side effects could be caused by doing that?

    By the way: Great work!! :)

  19. Jesper Rønn-Jensen Says:

    BT: There is no line 256 in the current version (0.8). Which version are you using?

    I suggest you upgrade to version 0.8 (but beware that it requires Rails 1.2)

    In Rails 1.2, the content header is automatically set, and thus the problem that you run into is solved, as long as the line is commented out.

  20. BT Says:

    Of course you’re right I forgot to upgrade, sorry! Now everything works fine!

    Thanks a lot
    BT

  21. Robert Says:

    Maybe it meight be of interest. I used the human_attribute_override plugin for translating fieldnames in error messages. And I have “translated object name” %> in my views.
    Its not the best solution, but it works very well with this plugin.

    I also made a modification on how :error_translation and :error_header are used. In German the plural of error (Fehler) is the same as the singular, but instad i nead to pluralize the second word in :error_header.
    My dirty workaround:
    (error_messages_for)

    messages = ActiveRecord:: Errors.default_error_messages
    if(count>1 && messages[:error_header_plural]!=nil)
    errorheader=messages[:error_header_plural]
    else
    errorheader=messages[:error_header]
    end
    if(count>1 && messages[:error_translation_plural]!=nil)
    errorcount=format(“%d “+messages[:error_translation_plural],count)
    else
    errorcount=pluralize(count, messages[:error_translation])
    end
    header_message = format( errorheader,
    errorcount,
    (options[:object_name] ||
    params.first).to_s.gsub(“_”, ” “))
    error_messages = objects.map {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }

    With :error_header_plural and :error_translation_plural added to the error messages. This produces “1 Fehler verhinderte…” or “2 Fehler verhinderten…”

    PS: As you see, I’m new to rails and ruby. Sorry

  22. histidin Says:

    Hello,
    I get this Error:
    activesupport-2.0.2/lib/active_support/dependencies.rb:478:in `const_missing’: uninitialized constant Rails::Plugin::L10N_LANG (NameError)

    But the var is set in the environment.rb and/ or init.rb. Is there special dependency to a native library or gem? Or whats going wrong?

    Thanks

  23. Jesper Rønn-Jensen Says:

    @histidin

    I have reproduced your bug by adding the plugin and doing nothing else
    But the bug dissapears the moment I add this line as the first line in my /config/environment.rb

    L10N_LANG = :da

    Unfortunately my time is limited right now to look more into it. But I wonder what happens at the line in /vendor/plugins/l10n-simplified/init.rb:
    L10N_LANG ||= :da

    As the above line should act as a fallback for the plugin. Please let me know anything you find out.

    /Jesper

  24. Simon Rimmons Says:

    We’ve developed a Web 2.0 tool managing translations. It has very good Rails support – allows using Ruby GetText and YML format for your translations.

    Please check it out.

  25. amy Says:

    I used the human_attribute_override plugin for translating fieldnames in error messages. And I have “translated object name” %> in my views.