#338 Globalize3
- Download:
- source codeProject Files in Zip (93.4 KB)
- mp4Full Size H.264 Video (16.4 MB)
- m4vSmaller H.264 Video (7.84 MB)
- webmFull Size VP8 Video (8.46 MB)
- ogvFull Size Theora Video (17.9 MB)
A continuación se muestra una página de una aplicación de blog que muestra varios registros Article
. La aplicación soporta internacionalización, permitiendo al usuario cambiar el idioma usando los enlaces de la parte superior.
Al hacer clic en el enlace “Wookieespeak” cambia el título en la parte superior de la página pero vemos que el contenido de los artículos sigue saliendo en inglés. ¿Cómo podemos cambiar el contenido de la base de datos de forma que se muestre en el idioma escogido por el usuario?
La internacionalización en las aplicaciones Rails se realiza por lo general mediante ficheros YAML. Colgando del directorio /config/locales
hay uno o más archivos que contienen los textos traducidos en cada idioma soportado por la aplicación. Vimos con detalle este enfoque en el episodio 138 y si bien esto funciona para literales y textos estáticos, no nos servirá para mostrar textos de la base de datos en diferentes idiomas. Para hacer esto nos haría falta almacenar cada traducción en una tabla de la base de datos, y esto es lo que precisamente facilita la gema Globalize3. Globalize3 crea una tabla separada donde se almacenan las traducciones de cada modelo y utilizará la traducción correcta dependiendo del idioma escogido por el usuario. Para utilizar esta gema en nuestra aplicación tenemos que añadirla al Gemfile
y luego ejecutar bundle
para instalarla.
gem 'globalize3'
Ahora ya podemos ir al modelo que queremos traducir y utilizar el método translates
para especificar los nombres de las columnas que queremos que estén disponibles en múltiples idiomas, en nuestro caso las columnas con el título y el contenido de Article
.
class Article < ActiveRecord::Base translates :name, :content end
Tenemos que crear una tabla en la base de datos para guardar las traducciones. El fichero README de la gema dice que podemos utilizar create_translation_table!
dentro de la misma migración en la que creamos la tabla para el modelo pero como ya tenemos una tabla articles
con contenidos tendremos que usar una técnica alternativa que hace una migración separada en la que podemos aprovechar para migrar los datos ya existentes. Primero crearemos una migración llamada create_article_translations
en nuestra aplicación.
$ rails g migration create_article_translations
Luego modificaremos la migración en base a lo que nos dice el fichero README. Tenemos que llamar a create_translations_table!
sobre el modelo al que le queremos añadir traducciones, pasándole los nombres de las columnas que queremos que sean traducibles.
class CreateArticleTranslations < ActiveRecord::Migration def up Article.create_translation_table!({ name: :string, content: :text }, { migrate_data: true }) end def down Article.drop_translation_table! migrate_data: true end end
Los datos de estas dos columnas no quedarán almacenados en la tabla articles
sino en una nueva tabla llamada article_translations
. Lanzaremos la migración de la base de datos para que los cambios surtan efecto.
$ rake db:migrate
Ya podemos demostrar el funcionamiento de Globalize en la consola. Si comprobamos el idioma actual veremos que por defecto es el inglés. Si recuperamos el nombre del primer artículo, se extraerá el valor de la tabla article_translations
.
1.9.3-p125 :001 > I18n.locale => :en 1.9.3-p125 :002 > Article.first.name Article Load (0.2ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 => "Superman"
Si ahora cambiamos el idioma a la lengua wookie y recuperamos el nombre del primer artículo, el valor devuelto será nil
porque el texto traducido aún no existe.
1.9.3-p125 :003 > I18n.locale = :wk => :wk 1.9.3-p125 :004 > Article.first.name Article Load (0.3ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 Article::Translation Load (0.3ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 AND "article_translations"."locale" = 'wk' LIMIT 1 => nil
Globalize3 redefine el comportamiento de los métodos de lectura y escritura para aquellas columnas que queremos traducir y les aplica el lenguaje en uso. Si actualizamos el nombre del primer artículo mientras tenemos el idioma establecido a wk
se insertará el registro en la tabla article_translations
en ese idioma, valor que podremos recuperar si volvemos a pedir el nombre del artículo.
1.9.3-p125 :005 > Article.first.update_attribute(:name, "Ahhyya") 1.9.3-p125 :006 > Article.first.name Article Load (0.3ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 => "Ahhyya"
Si ahora volvemos a cambiar el idioma al inglés veremos que vuelve a aparecer en inglés.
1.9.3-p125 :007 > I18n.locale = :en => :en 1.9.3-p125 :008 > Article.first.name Article Load (0.2ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 => "Superman"
Ya podemos ver esto en acción en nuestro sitio. Tendremos que reiniciar el servidor para que coja los cambios, pero tras hacerlo la versión en inglés de nuestra página debería tener el mismo aspecto porque los contenidos de la tabla original de artículos han sido migrados. Si ahora vemos la página en la lengua wookie veremos que aparece todo casi vacío.
El primer artículo tiene el mismo nombre que le pusimos por la consola pero el resto de atributos que tenemos que traducir siguen estando en blanco. Sin embargo podemos editar uno de estos artículos para establecer el texto traducido.
El artículo quedará disponible en los idiomas, por lo que cuando cambiemos idiomas veremos el artículo en el idioma correcto. Si volvemos al formulario de edición los campos de texto mostrarán el texto en el lenguaje escogido y podemos hacer clic en los enlaces para poder cambiar estos campos en el idioma que queramos. Si cambiamos un atributo que no está configurado para ser traducible (como por ejemplo el nombre del autor) dicho atributo será actualizado para todos los lenguajes porque su valor se estará guardando en la tabla original de articles
, no en article_translations
.
Lenguaje por defecto
Por supuesto traducir todos los registros de la base de datos en ambos lenguajes puede ser una tarea nada desdeñable. Como vimos antes, se devuelve el valor nil
cuando se solicita un atributo que todavía no se ha traducido, pero podemos definir un lenguaje por defecto que será el que se muestre cuando no existe una traducción en el idioma correspondiente. Para hacer esto sólo tenemos quemodificar el fichero de configuración de nuestra aplicación y añadir la opción i18n.fallbacks
.
config.i18n.fallbacks = true
Una vez que hayamos puesto esto las traducciones ausentes en cualquier lenguaje devolverán el contenido cargado en el idioma por defecto, en este caso inglés. Tendremos que reiniciar nuestra aplicación para que el cambio tenga efecto, pero cuando recarguemos la página veremos ahora que las traducciones que faltan en la lengua wookie se muestran en inglés.