Salve,
Há alguns dias eu ví um post no Ruby on Br do Fernando Luizão perguntando sobre conversões de datas em Ruby utilizando o (não tão) novo I18n do Rails, que foi incluído na versão 2.2.2. O problema básico é que se tentarmos utilizar o Ruby padrão para fazer algo como:
>> I18n.locale = :'pt-br' => :'pt-br' >> "20/02/2008".to_date => ArgumentError: Invalid date....
Receberemos um erro de conversão, mas o motivo é bastante simples: o Ruby nem sabe que o I18n existe, uma vez que ele é uma extensão do Rails. Eu acho que ele é uma ferramenta muito legal para fazer traduções e localização, eu o utilizo para fazer as várias versões do meu blog, apesar de alguns probleminhas.
Então eu decidi aceitar o desafio e desenvolver algumas extensões para podermos aproveitar as bençãos da internacionalização no Ruby. Eu as batizei de i18n_localize_core.
O I18n é muito bom para converter objetos Date e Numeric em strings formatadas, mas o inverso não é verdade. Não existe um suporte padrão para converter objetos String e eles seriam muito bem vindos.
Mesmo se tivessemos um mecanismo padrão para fazer esse parse via o I18n, ele não seria muito útil em operações como a criação de novos objetos ActiveRecord quando passamos os valores iniciais como um hash, que é uma construção bastante comum em actions de controllers que lidam com input de usuários, uma vez que esse processo está embutido no Rails and ele não faz qualquer referência ao I18n para fazer o parse das strings recebidas, ele usa as conversões “puras” do Ruby para transformar String no tipo desejado.
Então, como podemos adicionar suporte à localização sem interferir demais no Rails e sem alterar nosso código para utilizar o I18n como intermediário? A solução é utilizar, mais uma vez, a magia da meta-programação para redefinir os métodos necessários.
Basicamente, o i18n_localize_core adiciona algumas extensões ao I18n para facilitar o acesso à suas propriedades e métodos internos, redefinne o comportament do método _parse na classe Date, e dos métodos to_f e to_i da classe String. Só isso.
Com essas alterações nos podemos fazer coisas como:
>> 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"
Agora podemos fazer o Ruby converter datas e números conforme a configuração do I18n. Se utilizarmos as chamadas explicitas, através do I18n.parse_date, receberemos nil se a string não for válida, mas se utilizarmos os métodos básicos, como Date.parse e String.to_date, continuaremos recebendo datas válidas quando tratarmos strings no formato de data padrão do Ruby, formatadas como “yyyy-mm-dd”. Mas qual é a utilidade disso?
Imaginemos que temos um model Programmer com as seguintes propriedades: name:string, birthday:date, commits:integer e salary:float. O código que utilizaríamos nas actions create e edit dos controllers para inicializar um novo objeto seria mais ou menos como esse:
params = {"name" => "José Pedro", "birthday" => "20/01/1980", "commits" => "132.323", "salary" => "534.231,23"}
@programmer = Programmer.new(params)
Programmer<#2323: name:"José Pedro", birthday:nil, commits:132, salary:534.231>
Mas agora que os formatos do I18n são utilizados pelas classes básicas do Ruby durante a conversão de strings, o mesmo code vai gerar o seguinte resultado:
params = {"name" => "José Pedro", "birthday" => "20/01/1980", "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>
O mais legal é que não precisamos mudar nada no ActiveRecord ou no Rails, todo o código padrão deve funcionar corretamente, mas agora nós podemos fazer o parse de strings localizadas de acordo com as configurações do I18n.
O código completo está no GitHub: i18n_localize_core home page
Se você acha que ele é útil para você, faça o download, experimente e me diga o que achou. Aproveite.
Até mais,
Rafael.
Comments
-
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!
-
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 2009As 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!








