Un sitio multilíngüe puede parecer algo complicado. Sin embargo, existen varios paquetes para Laravel que simplifican enormemente nuestra labor a punto tal de permitirnos poner en funcionamiento un sitio multilenguaje en cuestión de minutos.

Recientemente he creado un paquete que quisiera presentarte. Desde ya, puedes decirme que hay decenas de paquetes disponibles que facilitan las tareas de traducción de un sitio web. Una búsqueda rápida por Packagist puede brindarte un gran abanico de posibilidades. ¿Para qué uno nuevo? Lo primero que tengo para decirte, obviamente, es que puedes elegir el paquete que más te convega; no tienes por qué creer que esta guía es la indicada para tu proyecto. Pero, por otro lado, en lo personal, te confieso que ninguno de estos paquetes ha logrado facilitarme las tareas plenamente: no he encontrado ninguno que pudiera hacer exactamente todo lo que yo necesitaba. Por ello, comparto Laravel Translator.

Para comenzar, debemos aclarar algo importante. En lo que aquí nos convoca, la traducción está íntimamente relacionada con la localización. Un usuario de España visitará, probablemente, nuestra versión en español al igual que todos los usuarios de latinoamérica. Habrá, entonces, una única versión en español para todos los hispanohablantes del mundo. Sin embargo, a veces puede resultar útil distinguir versiones regionales o locales dentro de un mismo idioma. En ese caso, por ejemplo, podría haber una versión de nuestro sitio web para españoles (es-es), para argentinos (es-ar) o para mexicanos (es-mx). En resumen, cuando hablamos de idiomas o locales estamos hablando más o menos de las mismas cosas.

Características

¿Qué debemos buscar, entonces, en un paquete? ¿Por qué Laravel Translator?

Enrutamiento. Es necesario seleccionar las rutas que queremos traducir: no todas serán traducidas y muchas, probablemente, no estarán disponibles en todos los idiomas Y. además, esto debe ser hecho de una manera sencilla y cercana a los usos de Laravel. Desde la hoja de rutas, deberemos poder seleccionar cuáles rutas traducir como cuando queremos seleccionar las rutas que caen bajo un conjunto de middlewares. El objetivo es simplificar y reducir el número de reglas. Si tenemos cinco idiomas, no queremos escribir cinco veces lo mismo. Observemos este ejemplo:

Route::get('articles/{slug}', [
     'locales' =>  ['en', 'es', 'fr', 'it', 'br'] , 
     'as' =>  'article_show_path',  
     'uses' => 'ArticlesController@show' 
]);

Esta regla multiplicará cinco veces (por cada idioma) una misma ruta, cambiando solo el prefijo. Sin embargo, es posible que no querramos un prefijo para el idioma principal del sitio web. Ya veremos cómo configurar el paquete para ocultar el prefijo.

URL. Al multiplicarse las rutas debemos tener una forma de acceder a cada versión de cada ruta.

Traducción de textos. Luego, en cada vista es necesario imprimir el texto que corresponda al idioma elegido. Supongamos que hemos accedido a 'es/about-us' y queremos imprimir en nuestra vista el mensaje de presentación

 {{ tt('messages.about') }}

Traducción de modelos. Muchas veces en nuestros mismos modelos vamos a querer tener acceso a propiedades traducidas a los distintos idiomas. Vamos a querer que $article->title arroje el título en español o en inglés dependiendo de la versión a la que nuestros usuarios hayan tenido acceso.

Instalación

Puedes seguir los pasos de instalación que encontrarás en la documentación de Instalación de Laravel Translator. Es muy sencillo, debes correr tres líneas en la consola y añadir tres líneas a config/app.php. No tendrás problemas. Además, deberás añadir una línea de código a la clase App\Http\Kernel tal como lo indica la documentación a fin de extender el Router de Laravel.

Configuración

Una vez que hemos instalado adecuadamente el paquete, hará falta definir qué idiomas manejará nuestra aplicación. Debemos recordar que en el archivo de configuración de Laravel config/app.php se encuentra la propiedad locale. Ella definirá el idioma principal de nuestro sitio web:

'locale' => 'es',

Ahora sí, vayamos al archivo de configuración config/translator.php para definir los idiomas que estarán disponibles en la aplicación. En la propiedad locales_available añadiremos cuatro idiomas: español (nuestro idioma principal), inglés, francés y portugués de Brasil. Entonces, la propiedad debería quedar así:

'locales_available' => ['es', 'en', 'fr', 'pt-br'],

Cada código definido en esta propiedad podrá ser añadido como prefijo en las URLs que definamos en nuestras hojas de rutas. Por ejemplo, podremos acceder a www.misitio.com/es/blog. Pero si quisiéramos eliminar el prefijo del idioma principal, deberemos modificar el archivo de configuración. En nuestra guía, eliminaremos ese prefijo:

'remove_prefix' => true,

Por último, es útil saber que los prefijos en las URLs no tienen por qué coincidir con los códigos de localización. Por ejemplo, para el código 'pt-br' correspondiente al portugués de brasil, el prefijo podría ser 'br'. En ese caso, definámoslo en la propiedad correspondiente:

'alias_URL' => [
        'pt-br' => 'br'
]

Hoja de rutas

Una vez que hemos instalado y configurado correctamente la aplicación, veamos cómo "multiplicar" las rutas para cada idioma (en realidad, técnicamente las rutas no se multiplican por una cuestión de optimización). Veamos un ejemplo:


//este grupo estará disponible para nuestro idioma principal, el español
Route::group([ 'locales' => 'es' ], function(){

   //esta ruta estará disponible en todos los idiomas que habíamos definido en el archivo de configuración:
    Route::get('/', ['locales' => 'all', 'as' =>  'home_path',  'uses' => 'PagesController@showHome' ]);

    //esta ruta será traducida al español, al inglés y al francés:
    Route::get('about', ['locales' => ['fr', 'en'], 'as' =>  'about_path',  'uses' => 'PagesController@showAbout' ]);

});

De este modo, éstas serán algunas de las rutas a las que tendremos acceso:

  • www.misitio.com
  • www.misitio.com/br
  • www.misitio.com/fr/about

Y éstas serán algunas de las rutas que arrojarán un error 404:

  • www.misitio.com/es
  • www.misitio.com/pt-br
  • www.misitio.com/br/about

Traducción de los contenidos.

Muy bien, ya tenemos las rutas definidas. Cuando alguien intente acceder a www.misitio.com/en/about la aplicación definirá el locale actual como en y delegará el resto del trabajo al controlador y al método correspondiente. Nuestro controlador no tendrá nada de especial: sólo devolverá la vista correspondiente. Obviémoslo y pasemos al template blade. Supongamos que éste es el archivo about.blade.php sin traducción:

<html>
    <head>
         <meta http-equiv="content-language" content="es">
         <title>Sobre nosotros</title>

    </head>
    <body>

                Somos una empresa que desarrolla aplicaciones web.

    </body>
</html>

Cuando accedamos a /about, a /en/about o a /fr/about siempre devolverá la misma vista sin cambio alguno. Debemos abstraer el texto del template. Para ello, ejecutemos el siguiente código:

Translator::create('about.title', [
  'es' => 'Sobre nosotros', 
  'en' => 'About us',
  'fr' => 'À propos de nous'
]);

Translator::create('about.message', [
  'es' => 'Somos una empresa que desarrolla aplicaciones web.', 
  'en' => 'We are a company that develops Web applications.',
  'fr' => 'Nous sommes une société qui développe des applications Web.'
]);

Con ello hemos creado un grupo ('about') y dos agujas ('title' y 'message'). Ahora estamos en condiciones de traer estos textos desde el template blade:

<html>
    <head>
         <meta http-equiv="content-language" content="{!! App::getLocale() !!}">
         <title>{{ tt('about.title') }}</title>

    </head>
    <body>

                {{ tt('about.message') }}

    </body>
</html>

Con el helper tt('group.needle') estamos trayendo el texto que habíamos guardado. Este helper es una abreviatura para Translator::text('group.needle'). Por último, habrás notado la meta etiqueta http-equiv="content-language" en donde llamamos al método App::getLocale(). Este método nativo de Laravel nos devuelve el locale que ha sido automáticamente definido por la aplicación al acceder a la ruta especificada. Por ejemplo, el resultado para /en/about sería:

<html>
    <head>
         <meta http-equiv="content-language" content="en">
         <title>About us</title>

    </head>
    <body>

                We are a company that develops Web applications.

    </body>
</html>

Ya tenemos funcionando un sitio web multilïngüe. ¿Fácil, no? Dejaremos para la próxima entrega cómo trabajar con la extensión de URLGenerator de Laravel Translator y con la traducción de modelos incluida en el paquete.