Localized parse with I18n

Author: Rafael - Publish date: Fri, 13 Feb 2009 19:40:00 -0300 Feb 13

Hi,

A few days ago I saw a post at Ruby on Br by Fernando Luizão asking about date conversions in Ruby using the (not so) new I18n from Rails, that was included in version 2.2.2. The thing is, if you try to do something like this with standard Ruby:

  >> I18n.locale = :'pt-br'
  => :'pt-br'
  >> "20/02/2008".to_date
  => ArgumentError: Invalid date....

You get a conversion error, but the reason is quite simple: Ruby don’t know that I18n exists, since it’s a Rails extension. But it’s a very nice tool for translations, I use it in this site and like it very much, even though it has its shortcomings.

So I took the challenge and developed some extensions so we can profit from the internationalization blessings. I baptized it i18n_localize_core.

I18n is very good to convert Date and Numeric objects to formated strings, but the inverse is not true. There’s no default support for String conversions and they can be really helpful when dealing with localized input data.

Even if it had some parsing system it wouldn’t help very much in operations like the creation of a new ActiveRecord object with it’s initial values passed as a hash, which is a very common construct in controller actions dealing with user input, since this process is already built into Rails and it doesn’t refers to I18n to parse the strings received, it uses pure Ruby conversions from String to the desired type.

But how can we add this support without interfering a lot with Rails and without changing code to add a intermediary class? The solution is use some more “meta-programming magic” to redefine the necessary methods.

The i18n_localize_core basically add some extensions to the I18n to make it easier to access it’s internals and then redefine the _parse behavior of the Date object and the to_f and to_i from the String object. That’s it.

With this functionalities you can do stuff like this:

  >> I18n.localize_core = true
  => true
  >> I18n.locale = :'pt-br'
  => :'pt-br'
  >> date_br = "20/02/2008"
  => "20/02/2008"
  >> date_br.to_date
  => Fri, 20 Feb 2009
  >> Date.parse date_br
  => Fri, 20 Feb 2009
  >> I18n.parse_date date_br
  => Fri, 20 Feb 2009

  >> date_en = "2009-02-20"
  => "2009-02-20"
  >> date_en.to_date
  => Fri, 20 Feb 2009
  >> Date.parse date_en
  => Fri, 20 Feb 2009
  >> I18n.parse_date date_en
  => nil

  >> float_br = "654.321,88"
  => "654.321,88"
  >> float_br.to_f
  => 654321.88
  >> float_br.to_i
  => 654321
  >> I18n.parse_number float_br
  => 654321.88
  >> float_br.as_delocalized_number
  => "654_321.88"

Now we can make Ruby parse dates as we configured I18n. If we try to explicitly convert them through I18n.parse_date we get nil if the date is not valid, but if we use the more basic methods, like Date.parse and String.to_date we still can make it work with default Ruby dates, formatted as “yyyy-mm-dd”. Why is that useful?

Let’s say we have a model Programmer with the following properties: name:string, birthday:date, commits:integer and salary:float. The code most create and edit actions use to initialize a new object in a controller, looking the params values, would look like this.

  params = {"name" => "José Pedro", "birthday" => "20/01/2009", "commits" => "132.323", "salary" => "534.231,23"}
  @programmer = Programmer.new(params)
  Programmer<#2323: name:"José Pedro", birthday:nil, commits:132, salary:534.231>

But now that the I18n formats are used by Ruby base classes during conversion, the same code will generate the following output:

  params = {"name" => "José Pedro", "birthday" => "20/01/2009", "commits" => "132.323", "salary" => "534.231,23"}
  @programmer = Programmer.new(params)
  Programmer<#2323: name:"José Pedro", birthday:2009-01-1980, commits:132323, salary:534231.23>

The best thing is that we don’t need to change anything on ActiveRecord or Rails, all the standard code should work as usual, but now we can parse the external strings according to our I18n configuration.

You can find the code at GitHub: i18n_localize_core home page

If you think this is useful for you, download it, take it for a spin and tell me what are your impressions. Hope you enjoy it.

Cheers,
Rafael.

Comments: 2 Sections: rails, ruby | Tags: i18n, rails22, ruby18

Comments

  1. Ramon Bispo Mon, 23 Feb 2009 19:20:43 -0300

    Bem legal, Rafael. Porém o último bloco de código resultou “birthday” como “2009-01-1980”.

    Dá uma checada!

    Abraço!

  2. foresth Sat, 21 Mar 2009 15:33:17 -0300

    Hello,
    I hoped it would solve my problem unfortunately there’s a little problem.. :)

    default = “d/m/%Y”
    >> ‘15/3/2009’.to_date
    => Sun, 15 Mar 2009
    >> ‘5/3/2009’.to_date
    => Sun, 03 May 2009

    As you can see, 15/3/2009 is parsed correctly (3rd month – March) but 5/3/2009, since it isn’t invalid input for the m/d/%Y format, is parsed by default.

    Cheers!

Add a commentary

Author*
E-mail*
Website
Commentary*