Cómo traducir un sitio web con Django

Traducir una aplicación en Django a varios idiomas y permitir que este sea detectado dependiendo de las peticiones de tus usuarios es una característica muy importante el día de hoy, además, es más sencillo de lo que crees. En este artículo exploraremos un poco cómo hacerlo.

Para efectos prácticos, en este artículo asumiré que tienes instalado gettext en tu sistema y que tienes una aplicación de Django funcional, si no es así, por favor instala gettext (Dependiendo de tu sistema operativo el proceso puede ser diferente) y crea un pequeño proyecto en Django.

Configuraciones iniciales…

Supongamos que tienes un proyecto en Django llamado some_project con una aplicación llamada some_app. La estructura del proyecto debería lucir así:

/
    manage.py
    templates/
    some_project/
        __init__.py
        settings.py
        urls.py
        wsgi.py
    some_app/
        migrations/
        __init__.py
        admin.py
        models.py
        tests.py
        views.py

El primer paso, es asegurarnos de que tenemos activadas las traducciones en nuestra configuración. Para hacer esto, debemos hacer los siguientes cambios en some_project/settings.py:

# some_project/settings.py

LANGUAGE_CODE = 'es'
TIME_ZONE = 'America/Bogota'

USE_I18N = True
USE_L10N = True
USE_TZ = True

Para este caso hemos definido que nuestro proyecto se encuentra en español por defecto

Marcar lo que vamos a traducir

El siguiente paso es marcar los textos que queremos traducir dentro de nuestro proyecto, para esto existen diferentes formas dependiendo del texto que vayamos a traducir.

Traducir en templates

Para explicar cómo traducir templates, supongamos que tenemos el siguiente templates/some_app/index.html

<h1>¡Bienvenido a nuestro sitio!</h1>
<p>Es un placer para nosotros tenerte aquí.</p>
<p>
    Este contenido fue escrito por {{ author }}
</p>

Es necesario que adaptemos este archivo para que luzca de la siguiente forma:

{% load i18n %}

<h1>{% trans '¡Bienvenido a nuestro sitio!' %}</h1>
<p>{% trans 'Es un placer para nosotros tenerte aquí.' %}</p>
<p>
    {% blocktrans trimmed %}
        Este contenido fue escrito por {{ author }}
    {% endblocktrans %}
</p>

Notemos que en este caso importamos los templatetags de i18n con {% load i18n %} y que para traducir una línea sencilla utilizamos el templatetag trans, pero si tenemos contenido un poco más complejo, por ejemplo que incluya multiples elementos HTML o variables, utilizaremos los templatetag blocktrans / endblocktrans, el flag trimmed lo utilizo para que en el archivo de traducción queden los strings más limpios (Sin saltos de línea por ejemplo), puedes profundizar sobre estos u otros templatetags de i18n en el siguiente enlace

Traducir en código Python

También es posible que los textos que queramos traducir se encuentren en el código Python, es posible marcar dichos strings, para esto supongamos que tenemos una vista que renderiza el template anterior en some_app/views.py:

from django.views.generic import TemplateView
from django.utils.translation import ugettext as _


class HomeView(TemplateView):
    template_name = 'some_app/index.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['author'] = _('Milton Lenis')
        return context

Observemos que para este caso necesitamos importar ugettext para marcar nuestros strings, una convención popular (Podría decirse que estándar) es nombrarlo _ para que no estorbe mucho en nuestro código. En este caso estamos marcando como traducible la variable author la cual estamos pasando al contexto del template. En el caso de traducir código Python también existen expresiones más complejas y muchos otros recursos que podemos utilizar, puedes profundizar más sobre esto en este enlace.

Crear archivos de traducción

El siguiente paso es crear los archivos de traducción para cada uno de los lenguajes que queremos soportar, para esto agregaremos unas cuantas líneas más a la configuración some_project/settings.py:

# some_project/settings.py

from django.utils.translation import ugettext_lazy as _

...

MIDDLEWARE_CLASSES = (
    ...,
    'django.middleware.locale.LocaleMiddleware',
    ...,
)

...

LANGUAGES = [
    ('es', _('Español')),
    ('en-us', _('English'))
]

LOCALE_PATHS = [
    os.path.join(BASE_DIR, 'locale')
]
...

En este caso le estamos indicando a Django que utilice el LocaleMiddleware el cual se encargará de escoger el lenguaje adecuado para cada usuario basado en información extraída del request (Es importante ubicar este middleware entre SessionMiddleware y CommonMiddleware si estás usando ambos, de lo contrario podría no funcionar bien). También especificamos mediante la variable LANGUAGES qué lenguajes va a soportar nuestro proyecto y por último, especificamos la ruta de la carpeta en la cual se guardarán los archivos de traducción mediante la variable LOCALE_PATHS. Vale la pena aclarar que debemos crear dicha carpeta some_project/locale.

Teniendo todo configurado, ahora sí, procedemos a crear los archivos de traducción. Para esto utilizamos el siguiente comando desde la ruta del proyecto:

django-admin makemessages -l en_US

Esto creará los archivos de traducción para el lenguaje Inglés que es el que utilizaremos para este ejemplo, es importante saber que los lenguajes que le pasemos como parámetro a este comendo deben seguir la notación de locale name, de lo contrario, Django no reconocerá las traducciones.

Traducir archivos .po

Al crear los archivos de traducción obtuvimos algo como esto:

#: some_project/settings.py:126
msgid "Español"
msgstr ""

#: some_project/settings.py:127
msgid "English"
msgstr ""

#: some_app/views.py:10
msgid "Milton Lenis"
msgstr ""

#: templates/home.html:6
msgid "¡Bienvenido a nuestro sitio!"
msgstr ""

#: templates/home.html:10
msgid "Es un placer para nosotros tenerte aquí."
msgstr ""

#: templates/home.html:13
#, python-format
msgid "Este contenido fue escrito por %(author)s"
msgstr ""

Estos archivos son los que utilizaremos para especificarle a Django cómo traducir cada palabra en determinado idioma, entonces simplemente para cada uno de los strings que aparezcan en msgid podemos especificar su traducción con los strings msgstr.

#: some_project/settings.py:126
msgid "Español"
msgstr "Spanish"

#: some_project/settings.py:127
msgid "English"
msgstr "English"

#: some_app/views.py:10
msgid "Milton Lenis"
msgstr "Milthon Lewis"

#: templates/home.html:6
msgid "¡Bienvenido a nuestro sitio!"
msgstr "¡Welcome to our site!"

#: templates/home.html:10
msgid "Es un placer para nosotros tenerte aquí."
msgstr "It's a pleasure for us having you here"

#: templates/home.html:13
#, python-format
msgid "Este contenido fue escrito por %(author)s"
msgstr "This content was written by %(author)s"

Observemos que para el caso del último mensaje tenemos un string con formato, el cual incluye una variable author vale la pena aclarar que esta es una palabra que no se debe traducir ya que es una variable y no parte del texto.

Compilar archivos de traducción

Una vez tengamos nuestros archivos de traducción completos, procederemos a compilarlos con el siguiente comando:

django-admin compilemessages

Cuando compilemos nuestros archivos, ya podemos utilizar nuestro proyecto en el idioma especificado. Cada que uno de nuestros usuarios consulte nuestro proyecto aparecerá en su idioma. Para efectos de prueba, podemos cambiar el lenguaje del proyecto por inglés y observar los resultados:

LANGUAGE_CODE = 'en-us'

También podríamos simularlo utilizando curl:

curl http://localhost:8000/your-url -H "Accept-Language: en-us"

Finalizando...

Este es todo el proceso para traducir nuestros proyectos en Django, sin embargo, existen algunos otros detalles útiles a tener en cuenta, si quieres profundizar más en este tema, te recomiendo leer la documentación oficial al respecto.

Algo importante a aclarar es que este método sirve para traducir sólo contenido estático, esto quiere decir que si, por ejemplo, estamos trabajando con algún generador de contenido, o el contenido de nuestros sitios está alojado directamente en los modelos, este método no los traducirá, si estás buscando algo así, tal vez te interese revisar django-modeltranslation.

Además de este artículo, he creado un repositorio con un ejemplo funcional el cual puedes probar. En la rama master está el código terminado con traducciones, en la rama base está el código sin las traducciones para que puedas implementarlas siguiendo este tutorial, link al repositorio

Si tienes alguna duda sobre el tema, por favor no dudes en ponerte en contacto conmigo o escribir un comentario y con mucho gusto ayudaré en lo que pueda.