jueves, 1 de octubre de 2009

Internacionalización y localización en rails

Lo reconozco: soy un maniático de la localización e internacionalización. Tanto, que casi rozo un trastorno obsesivo compulsivo. En rails considero una práctica casi obligatoria incluir la localización desde el primer momento en que empezamos un nuevo desarrollo.

Pero como Rails está pensado para vagos, nos incluye, al menos desde la versión 2.3.2, el plugin I18n desde el principio. Con esto, simplemente tendremos que configurar nuestra aplicación para que utilice dicha funcionalidad (un punto más para rails!).

Para esto, lo primero es decidir dónde se van a colocar los ficheros yml de traducción. En mi caso siempre opto por ponerlos dentro del directorio config/locales/ de forma que crearé dentro de este directorio, otro más por cada idioma que quiera añadir a la aplicación. Por ejemplo:


config/locales/es-ES
config/locales/en-US


Después hay que decirle a la aplicación que cargue los diferentes archivos de idioma que tengamos en estos directorios. Para esto suelo crear el archivo de inicialización config/initializers/locale.rb


I18n.load_path += Dir[ File.join(RAILS_ROOT, 'config', 'locales', '*', '*.{rb,yml}') ]


Y por último hay que seleccionar el idioma con que queramos que se muestre nuestra aplicación. Para esto tenemos la variable I18n.locale. En el último proyecto en el que estoy, la aplicación sólo va a tener un idioma, por lo tanto bastaría con añadir la siguiente línea en el application_controller:


I18n.locale = 'es-ES'


Esta línea puede ser tratada para que coja la preferencia del usuario actual, por ejemplo:


I18n.locale = current_user.locale


Ahora necesitamos nada más crear los archivos de traducción en el directorio que hemos creado al principio. Estos archivos serán unos archivos yml. Por ejemplo un archivo con las cadenas de texto generales para toda la aplicación podría ser la siguiente:

config/locales/es-ES/general.yml


es-ES:
  general:
    add: Añadir
    are_you_sure: ¿ Estas seguro ?
    accept: Aceptar
    actions: Acciones
    back: Volver
    cancel: Cancelar
    delete: Borrar
    edit: Editar
    exit: Salir
    "no": "No"


Con esto ya tenemos configurada la aplicación para utilizar I18n.

Ahora para mostrar una cadena de texto traducida, podemos utilizar el helper I18n.translate o su alias t, de la siguiente forma:


t('general.accept')


Nos devolverá la cadena "Aceptar".

Esto es útil hacerlo hasta cuando vayamos a tener una aplicación con un solo idioma porque:

  1. Es más fácil mantener los textos de la aplicación si estos están contenidos en unos cuantos ficheros de traducción, que si estuvieran dispersos a lo largo y ancho de toda nuestra aplicación
  2. Siempre es posible que en un futuro nuestra aplicación tenga que ser traducida a otro idioma. De esta forma, simplemente tendríamos que replicar los archivos de traducción y traducir las etiquetas.
  3. Si no internacionalizamos, tendríamos que conformarnos con que cierta información se muestre como viene por defecto: por ejemplo, que el separador de miles sea la coma y no el punto, que la moneda sea el dolar, etc
Estos son los conceptos básicos para internacionalizar una aplicación. En otro momento, cuando tenga más tiempo y me encuentre con humor, intentaré explicar cómo traducir ciertos componentes de rails como los modelos, errores, unidades, etc. Eso sí, para el que le pique la curiosidad siempre estará nuestra amiga la web para resolver todas nuestras dudas.

Y como última recomendación uno de los mejores punteos bajo mi humilde punto de vista: Ram It Down de Judas Priest

Si el cerebro del ser humano fuera tan sencillo que lo pudiéramos entender, entonces seríamos tan estúpidos que tampoco lo entenderíamos -- Jostein Gaardner (El mundo de Sofía)

9 comentarios:

  1. Muy buen post eLafo!

    Si realizas un segundo post sobre estos temas no estaría de más que incidieras en temas como la estructura de directorios de los ficheros .yml para poner un poco de orden ( /model /view /controller ) que utilizas en tus aplicaciones. Y algo que creo que es muy útil como la interpolación automática para localizar las traducciones, que nos ayuda a ser más dry

    ResponderEliminar
  2. ¿y que opninión te merece Gettext?
    junto con las herramientas existentes para Gettext parece una solución que a la larga facilitaría el mantenimiento ¿no?

    ResponderEliminar
  3. Muy buena introducción. Para saber más de lo que puedes hacer con i18n en rails de serie hay una guía [1] que viene perfecta.

    En cuanto a la estructura de directorios, no hay nada claro, porque al final es un tema complejo. Una cosa que sí está clara es que i18n te da facilidades para que todo lo que sean campos de los modelos (o nombres de los modelos en sí mismos) vaya en un mismo sitio.

    Para el resto, una estructura que a mí me parece interesante es crearse un fichero de localización por controller y dentro de eso directamente meter todas las etiquetas para ese controlador. En el caso de controllers enormes se pueden crear secciones por acciones.

    Me parece fundamental utilizar muchos ficheros .yml en lugar de meterlo todo en uno. En cuanto el proyecto crece y tienes a varias personas modificando etiquetas (incluyendo al usuario final), tener separados los ficheros significa que no vas a tener que hacer merges casi nunca.

    Si utilizas un único fichero, te va a tocar estar resolviendo conflictos todo el tiempo.

    Además, como buena práctica, me gusta que las etiquetas estén en orden alfabético. Es mucho más fácil encontrarlo, y mucho más amigable para cualquier herramienta que automatice el mantenimiento de traducciones.

    Enhorabuena por el inicio del blog.. viene pisando fuerte.. se nota que lleva poco tiempo abierto ;)

    [1] http://guides.rubyonrails.org/i18n.html

    ResponderEliminar
  4. Gracias a todos por los comentarios.

    Acerca del tema de los ficheros de idiomas, suelo hacer prácticamente lo mismo que formatinternet:

    un fichero para los modelos
    un fichero para los errores
    un fichero por controlador
    un fichero de traducciones generales
    y un fichero de localización de moneda, formatos numéricos, etc.

    Además, de lo que comenta acerca de evitarte muchos merges cuando varios usuarios están trabajando con las traducciones, también me resulta mucho más cómodo por el simple hecho de trabajar con archivos más pequeños: lo reconozco, a mí hacer scroll no me gusta ;-)

    ResponderEliminar
  5. Acerca de gettext, todavía no lo he utilizado, así que mis impresiones responden nada más que a una opinión muy particular mía. La idea que tengo yo, es que si tu aplicación web va a tener mucho mantenimiento de idioma, es decir, muchos textos y, sobre todo, muchos idiomas diferentes, sí puede ser rentable utilizar gettext, sobre todo por todas las herramientas que existen para traducir ficheros po/mo.

    Si éste no es el caso, yo soy partidario de utilizar lo que venga en el core, en este caso I18n, siempre y cuando la funcionalidad que te aporte sea suficiente.

    Es, en definitiva, un problema de mantenimiento. Si el mantenimiento va a estar en un alto porcentaje relacionado con la traducción / localización de textos, seguramente sea beneficioso. Si, en cambio, este porcentaje va a ser bajo, igual los problemas que puedan surgir de incompatibilidades con las siguientes versiones de rails, o con otros plugins o gemas, hacen que no merezca la pena optar por gettext.

    Pero repito, esto es mi opinión personal y no está apoyada en una experiencia práctica. A ver si en el próximo proyecto puedo meter gettext y os comento qué tal.

    ResponderEliminar
  6. En realidad gettext e i18n no son excluyentes. Lo "bonito" de la implementación de i18n en rails es que te da un interfaz y una implementación por defecto.

    Pero nada te impide utilizar como backend gettext (o ficheros de texto planos, o una base de datos, o...) respetando el interfaz de i18n.

    Todo el diseño de la librería está pensado para usarse así. Tu aplicación siempre usa los mismos métodos, pero el backend donde se almacenan las traducciones es intercambiable.

    Unos de amigos de spain-rb hicieron un backend i18n para gettext en un par de horas de una tarde de sábado friki ;)

    ResponderEliminar
  7. Hola:

    Gracias por el post porque es justo lo que andaba buscando.

    Yo ya he empleado antes gettext pero ahora en un proyecto nuevo voy a emplear i18n solo porque como bien comentas viene ya en el core y eso hará que los esfuerzos de la comunidad en materia de traducciones se centren allí y pronto haya mejoras, herramientas, etc...

    ResponderEliminar
  8. Hola, veras esta muy bien tu post, pero quisiera pedirte un poco de orientacion acerca de esto, ya que hace poco estoy aprendiendo a usar el famoso Active Scaffold para usar ajax con mis aplicaciones y he seguido tu manual, pero no he podido hacer que me mi aplicacion hecha con el plugin de Active scaffold pase a español, no se si estoy mal o tengo que hacer algo mas para que esto pase, ya que me imagino que si por que es un plugin aparte y no un andamio normal

    ResponderEliminar
  9. Aquí es otro recurso importante para las necesidades de localización: https://poeditor.com, un software de traducción en línea que le dan una gran mano a los desarrolladores.

    ResponderEliminar