Rails en español
Existen muchas opciones para hacer localización e internacionalización en Ruby on Rails. Entre todas ellas, la que me parece más sencilla es el plugin Localization Simplified. Sin embargo, me interesaban principalmente dos cosas: (1) agregar código sólo para español a mi aplicación, y (2) meterle la mano en los bolsillos a Rails y averiguar bien qué se debía hacer y por qué. En menos palabras: porque soy un maniático y un idiota ;-)
Hay diferentes cosas que hay que ajustar en Rails para que toda la información que colocamos en nuestras aplicaciones salga correctamente en español. Buscando en internet, encontré bastantes sitios donde aparecen consejos sobre cómo hacer muchas de estas cosas. Pero cada consejo, cada nueva solución, generaba un nuevo error, o advertencia, y tenía que seguir buscando. Con un poco de lectura y trabajo, fui uniendo y ajustando las diferentes soluciones que iba encontrando. A continuación les muestro la solución final que logré.
En primer lugar creamos un subdirectorio app\overrides
, y allí colocamos los siguientes archivos:
- errores.rb
module ActiveRecord class Errors begin @@default_error_messages = { :inclusion => "no está incluido en la lista", :exclusion => "está reservado", :invalid => "no es válido", :confirmation => "no es una confirmación", :accepted => "debe ser aceptado", :empty => "no puede estar vacío", :blank => "no puede estar en blanco", :too_long => "es demasiado largo (máximo %d caracteres)", :too_short => "es demasiado corto (mínimo %d caracteres)", :wrong_length => "no tiene la longitud correcta (debería tener %d caracteres)", :taken => "ya ha sido incluido", :not_a_number => "debe ser un número" } end end end module ActionView #nodoc module Helpers module ActiveRecordHelper def error_messages_for(object_name, options = {}) options = options.symbolize_keys object = instance_variable_get("@#{object_name}") unless object.errors.empty? content_tag("div", content_tag(options[:header_tag] || "h2", "Hay errores que impiden guardar el registro") + content_tag("p", "Compruebe los siguientes campos:") + content_tag("ul", object.errors.full_messages.collect { |msg| content_tag("li", msg) }), "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation" ) end end end end end
- date.rb
require 'date' class Date remove_const(:MONTHNAMES) MONTHNAMES = [nil] + %w(Enero Febrero Marzo Abril Mayo Junio Julio Agosto Septiembre Octubre Noviembre Diciembre) remove_const(:ABBR_MONTHNAMES) ABBR_MONTHNAMES = [nil] + %w(Ene Feb Mar Abr May Jun Jul Ago Sep Oct Nov Dic) remove_const(:DAYNAMES) DAYNAMES = %w(Domingo Lunes Martes Miercoles Jueves Viernes Sábado) remove_const(:ABBR_DAYNAMES) ABBR_DAYNAMES = %w(Dom Lun Mar Mié Jue Vie Sáb) end class Time alias :strftime_nolocale :strftime def strftime(format) format = format.dup format.gsub!(/%a/, Date::ABBR_DAYNAMES[self.wday]) format.gsub!(/%A/, Date::DAYNAMES[self.wday]) format.gsub!(/%b/, Date::ABBR_MONTHNAMES[self.mon]) format.gsub!(/%B/, Date::MONTHNAMES[self.mon]) self.strftime_nolocale(format) end end module ActionView module Helpers module DateHelper def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false) from_time = from_time.to_time if from_time.respond_to?(:to_time) to_time = to_time.to_time if to_time.respond_to?(:to_time) distance_in_minutes = (((to_time - from_time).abs)/60).round distance_in_seconds = ((to_time - from_time).abs).round case distance_in_minutes when 0..1 return (distance_in_minutes == 0) ? 'menos de un minuto' : '1 minuto' unless include_seconds case distance_in_seconds when 0..4 then 'menos de 5 segundos' when 5..9 then 'menos de 10 segundos' when 10..19 then 'menos de 20 segundos' when 20..39 then 'medio minuto' when 40..59 then 'menos de un minuto' else '1 minuto' end when 2..44 then "#{distance_in_minutes} minutos" when 45..89 then 'aprox. 1 hora' when 90..1439 then "aprox. #{(distance_in_minutes.to_f / 60.0).round} horas" when 1440..2879 then '1 día' when 2880..43199 then "#{(distance_in_minutes / 1440).round} días" when 43200..86399 then 'aprox. 1 mes' when 86400..525599 then "#{(distance_in_minutes / 43200).round} months" when 525600..1051199 then 'aprox. 1 año' else "over #{(distance_in_minutes / 525600).round} years" end end end end end
- all.rb
Dir[File.dirname(__FILE__) + "/**/*.rb"].each { |file| require(file) }
Luego colocamos, al final del archivo config\environment.rb
:
require "#{RAILS_ROOT}/app/overrides/all"
Por último, en el archivo config\initializers\inflections.rb
:
Inflector.inflections do |inflect| inflect.plural /([aeiou])([A-Z]|_|$)/, '\1s\2' inflect.plural /([rlnd])([A-Z]|_|$)/, '\1es\2' inflect.singular /([aeiou])s([A-Z]|_|$)/, '\1\2' inflect.singular /([rlnd])es([A-Z]|_|$)/, '\1\2' inflect.irregular 'user', 'users' inflect.irregular 'account', 'accounts' inflect.irregular 'password', 'passwords' inflect.irregular 'session', 'sessions' end
Noten que el final del código de pluralizaciones agrego cuatro líneas para user, account, password y session. Esto se debe a que quiero que todas las pluralizaciones que haga Rails en mi aplicación, las haga con las reglas del español, así que tengo que agregar excepciones para los modelos que están en inglés (en este caso, los modelos para el manejo de usuarios, que vienen del plugin Restful Authentication). Si tienen algún modelo en inglés, pueden agregar la regla para pluralizarlo de la misma manera allí.
Todo esto se debe encargar de colocar en español los mensajes de error, las fechas (meses, días), y las pluralizaciones de Rails.
ACTUALIZACIÓN (12/04/2008): Agregué la traducción de la función distance_of_time_in_words
al archivo app\overrides\date.rb
. También es recomendable traducir las plantillas del generador scaffold
que se encuentran en {RUBY}\lib\ruby\gems\{VERSION DE RUBY}\gems\rails-{VERSION DE RAILS}\lib\rails_generator\generators\components\scaffold\templates
Fuentes del código:
- [Ror-es] Errores personalizados
- Better error_messages_for (I)
- How to localize ActiveRecord errors
- Overriding Rails messages in another language
- Re: Errores personalizados: msg#00029
- Re: Errores personalizados: msg#00032
- Rails-ES > Traducción mensajes de validación ActiveRecord
- [Ror-es] Idioma de las fechas! (1)
- [Ror-es] Idioma de las fechas! (2)
- Bug Reports: Date::MONTHNAMES - can't modify frozen array
- Rails-ES > Fechas
- Localization for Ruby's Time#strftime
- Translating dates in ruby (on rails)
- Rails en español: msg#00129
- Re: Scaffold en español: msg#00081
12 comentarios:
Pana que alegría encontrar un Venezolano que le guste llegar al fondo de los problemas con soluciones en mano. Voy a probar todo este código que de entrada se ve muy bien.
Estoy iniciando un proyecto con rails 2.x.x y la creación de los modelos se me ha hecho dificil por este tipo de problemas.
Gracias por tan buen aporte!
Mis felicitaciones
Cristian
http://z-nexus.net
Gracias por el post. Sólo un detalle para los que utilicen Ruby on Rails 2.2.2; hay que hacer una pequeña modificación. Siguiendo con el ejemplo citado arriba, quedaría:
ActiveSupport::Inflector.inflections do |inflect|
inflect.plural /([aeiou])([A-Z]|_|$)/, '\1s\2'
inflect.plural /([rlnd])([A-Z]|_|$)/, '\1es\2'
inflect.singular /([aeiou])s([A-Z]|_|$)/, '\1\2'
inflect.singular /([rlnd])es([A-Z]|_|$)/, '\1\2'
inflect.irregular 'user', 'users'
inflect.irregular 'account', 'accounts'
inflect.irregular 'password', 'passwords'
inflect.irregular 'session', 'sessions'
end
Saludos.
coye muchísimas gracias chamo! que bueno tu post, y sin tener que instalar plugins ni nada. Te felicito por lo fajado y por compartir tu fajación!
Saludos,
tengo dos problemillas:
1.- me funciona a medias todo esto. es decir, me salen algunos mensajes pero otros no. ejemplo:
"Nombre can't be blank"
para "validates_presence_of :nombre"
2.- la modificación a config\initializers\inflections.rb, me genera un error. será porque previamente ya tengo modelos pluralizados con las reglas en inglés (ej. institucions, en vez de instituciones)? o será porque estoy con ror 2.3.2?
como sea, si lo agrego se cae WEBrick con el siguiente error:
=> Rails 2.3.2 application starting on http://0.0.0.0:3001
/home/vladimir/.gem/ruby/1.8/gems/activesupport-2.3.2/lib/active_support/dependencies.rb:443:in `load_missing_constant': uninitialized constant Inflector (NameError)...(sigue)
cualquier ayuda se las agradecerpé.
saludos!
el 2° link está roto. el correcto es:
http://www.jorgebernal.info/dev-random/better-error_messages_for-i
Fantástica aportación. Muchas gracias a Jesús y también al anónimo por el matiz para los que usamos rails 2.2.2+
Para Vladimir Prieto.
No sé si ya resolvistes el problema con el load_missing.. Pero si aún lo tienes verifica que la línea require "#{RAILS_ROOT}/app/overrides/all"
se encuentra después del "end"
en el archivo inflections.rb
Probablemente tengas:
.
.
.
require "#{RAILS_ROOT}/app/overrides/all"
end
Y debe ser:
.
.
.
end
require "#{RAILS_ROOT}/app/overrides/all"
Hola.. Uno de mis socios creo este archivo de pluralizació para nombres de tablas con 1 o 2 palabras conformando el nombre. Si alguien lo puede hacer para cualaquier cantidad de palabras bienvenido es..
ActiveSupport::Inflector.inflections do |inflect|
inflect.plural /([aeiou])([A-Z]|_|$)/, '\1s\2'
inflect.plural /([rlnd])([A-Z]|_|$)/, '\1es\2'
inflect.plural /([aeiou])([A-Z]|_|$)([a-z]+)([rlnd])($)/, '\1s\2\3\4es\5'
inflect.plural /([rlnd])([A-Z]|_|$)([a-z]+)([aeiou])($)/, '\1es\2\3\4s\5'
inflect.singular /([aeiou])s([A-Z]|_|$)/, '\1\2'
inflect.singular /([rlnd])es([A-Z]|_|$)/, '\1\2'
inflect.singular /([aeiou])s([A-Z]|_)([a-z]+)([rlnd])es($)/, '\1\2\3\4\5'
inflect.singular /([rlnd])es([A-Z]|_)([a-z]+)([aeiou])s($)/, '\1\2\3\4\5'
inflect.irregular 'user', 'users'
inflect.irregular 'account', 'accounts'
inflect.irregular 'password', 'passwords'
inflect.irregular 'session', 'sessions'
end
hola, tu código me da un error en la linea 11 del date.rb:
el error es :
date.rb:11: unterminated string meets end of file (syntaxError)
te agradezco ayuda
saludos
Hola, yo utilizo esto y me funciona bien tanto con una o mas palabras, underscore y CamelCase. Espero que os sirva de ayuda :)
ActiveSupport::Inflector.inflections do |inflect|
inflect.plural /([^djlnrs])([A-Z]|_|$)/, '\1s\2'
inflect.plural /([djlnrs])([A-Z]|_|$)/, '\1es\2'
inflect.plural /(.*)z([A-Z]|_|$)$/i, '\1ces\2'
inflect.singular /([^djlnrs])s([A-Z]|_|$)/, '\1\2'
inflect.singular /([djlnrs])es([A-Z]|_|$)/, '\1\2'
inflect.singular /(.*)ces([A-Z]|_|$)$/i, '\1z\2'
inflect.irregular 'user', 'users'
inflect.irregular 'account', 'accounts'
inflect.irregular 'password', 'passwords'
inflect.irregular 'session', 'sessions'
inflect.irregular 'ud', 'uds'
end
Pana te debo una! Te invito a revisar la página que estoy migrando de php a rails www.joincic.com.ve
Publicar un comentario