Traducción

Resumen

Para hacer que un proyecto Django sea traducible, debes agregar un número mínimo de conexiones a tu código y plantillas Python. Estas conexiones se llaman cadenas de traducción. Les dicen a Django: «Este texto debe ser traducido al idioma del usuario final, si existe una traducción para este texto en ese idioma.» Es tu responsabilidad marcar las cadenas de traducción; el sistema solo puede traducir cadenas que conozca.

Django proporciona luego utilidades para extraer las cadenas de traducción en un archivo de mensaje. Este archivo es una forma conveniente para los traductores de proporcionar la equivalencia de las cadenas de traducción en el idioma objetivo. Una vez que los traductores hayan rellenado el archivo de mensajes, debe compilarse. Este proceso depende del conjunto de herramientas GNU gettext.

Una vez hecho esto, Django se encarga de traducir aplicaciones web en vivo en cada idioma disponible, según las preferencias de idioma de los usuarios.

Las conexiones de internacionalización de Django están activadas por defecto y eso significa que hay un poco de sobrecarga i18n en ciertos lugares del marco. Si no utilizas la internacionalización, debes tomar los dos segundos para establecer USE_I18N = False en tu archivo de configuración. Luego Django hará algunas optimizaciones para evitar cargar la maquinaria de internacionalización.

Nota

Asegúrate de que hayas activado la traducción para tu proyecto (la forma más rápida es comprobar si MIDDLEWARE incluye django.middleware.locale.LocaleMiddleware). Si aún no lo has hecho, consulta cómo Django descubre la preferencia del idioma.

Internacionalización: en código Python

Traducción estándar

Specify una cadena de traducción utilizando la función gettext(). Es convención importar esta como un alias más corto, _, para ahorrar tecleado.

Nota

La biblioteca estándar de Python gettext instala _() en el espacio de nombres global, como un alias para gettext(). En Django, hemos elegido no seguir esta práctica por una razón o dos:

  1. A veces, debes usar gettext_lazy() como método de traducción por defecto para un archivo en particular. Sin _() en el espacio de nombres global, el desarrollador tiene que pensar cuál es la función de traducción más adecuada.

  2. El carácter subrayado (_) se utiliza para representar «el resultado anterior» en la consola interactiva de Python y los tests doctest. Instalar una _() global causa interferencia. Importar explícitamente gettext() como _() evita este problema.

¿Qué funciones pueden ser aliasadas como _?

Por cómo funciona xgettext (utilizado por makemessages), solo las funciones que toman un argumento de cadena pueden importarse como _:

En este ejemplo, el texto "Bienvenido a mi sitio." está marcado como una cadena de traducción:

from django.http import HttpResponse
from django.utils.translation import gettext as _


def my_view(request):
    output = _("Welcome to my site.")
    return HttpResponse(output)

Podrías codificar esto sin usar el alias. Este ejemplo es idéntico al anterior:

from django.http import HttpResponse
from django.utils.translation import gettext


def my_view(request):
    output = gettext("Welcome to my site.")
    return HttpResponse(output)

La traducción funciona sobre valores calculados. Este ejemplo es idéntico a los dos anteriores:

def my_view(request):
    words = ["Welcome", "to", "my", "site."]
    output = _(" ".join(words))
    return HttpResponse(output)

La traducción funciona con variables. Aquí tienes un ejemplo idéntico:

def my_view(request):
    sentence = "Welcome to my site."
    output = _(sentence)
    return HttpResponse(output)

La advertencia al utilizar variables o valores calculados, como en los dos ejemplos anteriores, es que la utilidad de Django para detectar cadenas de traducción, django-admin makemessages <makemessages>, no podrá encontrar estas cadenas. Más información sobre makemessages más adelante.

Las cadenas que pasas a _() o gettext() pueden contener marcadores de reemplazo, especificados con la sintaxis estándar de interpolación de cadenas nombradas de Python. Ejemplo:

def my_view(request, m, d):
    output = _("Today is %(month)s %(day)s.") % {"month": m, "day": d}
    return HttpResponse(output)

Esta técnica permite que las traducciones específicas del idioma reordenen el texto de los marcadores de posición. Por ejemplo, una traducción en inglés puede ser "Today is November 26.", mientras que una traducción en español puede ser "Hoy es 26 de noviembre." – con los marcadores de posición del mes y el día intercambiados.

Por esta razón, debes utilizar interpolación de cadena con nombre (por ejemplo, %(day)s) en lugar de la interpolación posicional (por ejemplo, %s o %d) siempre que tengas más de un parámetro. Si utilizaste interpolación posicional, las traducciones no podrían reordenar el texto del placeholder.

Dado que la extracción de cadenas se realiza mediante el comando xgettext, solo se admiten sintaxis soportadas por gettext en Django. Python f-strings no pueden usarse directamente con las funciones de gettext porque las expresiones de f-string se evalúan antes de llegar a gettext. Esto significa que _(f"Welcome {name}") no funcionará como se espera, ya que la variable se sustituye antes de que ocurra la traducción. En su lugar, utilice interpolación de cadenas con nombre:

# Good
_("Welcome %(name)s") % {"name": name}

# Good
_("Welcome {name}").format(name=name)

# Bad
_(f"Welcome {name}")  # f-string evaluated before translation.

Las plantillas de JavaScript necesitan gettext 0.21+.

Comentarios para traductores

Si deseas darle pistas a los traductores sobre una cadena translatable, puedes agregar un comentario precedido por la palabra clave Translators en la línea que antecede la cadena, por ejemplo:

def my_view(request):
    # Translators: This message appears on the home page only
    output = gettext("Welcome to my site.")

La traducción de los textos es la siguiente:

Nota

Por completitud, aquí está el fragmento correspondiente del archivo .po resultante:

#. Translators: This message appears on the home page only
# path/to/python/file.py:123
msgid "Welcome to my site."
msgstr ""

Esto también funciona en plantillas. Consulte Comentarios para traductores en plantillas para obtener más detalles.

Marcando cadenas como no-op

Utilice la función django.utils.translation.gettext_noop() para marcar una cadena como una cadena de traducción sin traducirla. La cadena se traduce luego desde una variable.

Use esto si tiene cadenas constantes que deben almacenarse en el idioma fuente porque son intercambiadas entre sistemas o usuarios – tales como cadenas en una base de datos – pero deben ser traducidas en el último punto posible en tiempo, tal como cuando la cadena se presenta al usuario.

Pluralización

Utilice la función django.utils.translation.ngettext() para especificar mensajes plurales.

ngettext() toma tres argumentos: la cadena de traducción singular, la cadena de traducción plural y el número de objetos.

Esta función es útil cuando necesita que su aplicación Django sea localizable a idiomas donde el número y complejidad de formas plurales es mayor que las dos formas utilizadas en inglés (“objeto” para el singular y “objetos” para todos los casos donde count es diferente de uno, sin importar su valor.)

Por ejemplo:

from django.http import HttpResponse
from django.utils.translation import ngettext


def hello_world(request, count):
    page = ngettext(
        "there is %(count)d object",
        "there are %(count)d objects",
        count,
    ) % {
        "count": count,
    }
    return HttpResponse(page)

En este ejemplo se pasa el número de objetos a las traducciones como la variable count.

Ten en cuenta que la pluralización es complicada y funciona de manera diferente en cada idioma. Comparar count con 1 no siempre es la regla correcta. Este código parece sofisticado, pero producirá resultados incorrectos para algunos idiomas:

from django.utils.translation import ngettext
from myapp.models import Report

count = Report.objects.count()
if count == 1:
    name = Report._meta.verbose_name
else:
    name = Report._meta.verbose_name_plural

text = ngettext(
    "There is %(count)d %(name)s available.",
    "There are %(count)d %(name)s available.",
    count,
) % {"count": count, "name": name}

No intentes implementar tu propia lógica singular-plural; no será correcta. En un caso como este, considera algo como lo siguiente:

text = ngettext(
    "There is %(count)d %(name)s object available.",
    "There are %(count)d %(name)s objects available.",
    count,
) % {
    "count": count,
    "name": Report._meta.verbose_name,
}

Nota

Cuando utilices ngettext(), asegúrate de utilizar un solo nombre para cada variable extrapolada incluida en la literal. En los ejemplos anteriores, nota cómo utilizamos la variable Python name en ambas cadenas de traducción. Este ejemplo, además de ser incorrecto en algunos idiomas como se señaló anteriormente, fallaría:

text = ngettext(
    "There is %(count)d %(name)s available.",
    "There are %(count)d %(plural_name)s available.",
    count,
) % {
    "count": Report.objects.count(),
    "name": Report._meta.verbose_name,
    "plural_name": Report._meta.verbose_name_plural,
}

Obtendrías un error al ejecutar django-admin compilemessages:

a format specification for argument 'name', as in 'msgstr[0]', doesn't exist in 'msgid'

Marcadores de contexto

A veces las palabras tienen varios significados, como "May" en inglés, que se refiere a un nombre de mes y a un verbo. Para permitir a los traductores traducir estas palabras correctamente en diferentes contextos, puedes utilizar la función django.utils.translation.pgettext(), o la función django.utils.translation.npgettext() si la cadena necesita pluralización. Ambas toman una cadena de contexto como la primera variable.

En el archivo .po resultante, la cadena aparecerá tantas veces como haya diferentes marcadores de contexto para la misma cadena (el contexto aparecerá en la línea msgctxt), lo que permite al traductor dar una traducción diferente para cada uno de ellos.

Por ejemplo:

from django.utils.translation import pgettext

month = pgettext("month name", "May")

o:

from django.db import models
from django.utils.translation import pgettext_lazy


class MyThing(models.Model):
    name = models.CharField(
        help_text=pgettext_lazy("help text for MyThing model", "This is the help text")
    )

aparecería en el archivo .po como:

msgctxt "month name"
msgid "May"
msgstr ""

Los marcadores de contexto también están soportados por los etiquetas de plantilla translate y blocktranslate.

Lazy translation

Utiliza las versiones perezosas de funciones de traducción en django.utils.translation (fácilmente reconocibles por la sufijación lazy en sus nombres) para traducir cadenas de forma perezosa – cuando el valor se accede más que cuando se llaman.

Estas funciones almacenan una referencia perezosa a la cadena – no la traducción real. La traducción misma se realizará cuando la cadena se utilice en un contexto de cadena, como en la renderización de plantillas.

Esto es esencial cuando las llamadas a estas funciones están ubicadas en caminos de código que se ejecutan en el tiempo de carga del módulo.

Esto puede suceder fácilmente cuando se definen modelos, formularios y formularios de modelo, porque Django implementa estos de forma que sus campos sean atributos de nivel de clase. Por esa razón, asegúrate de utilizar traducciones perezosas en los siguientes casos:

Valores de opciones verbose_name y help_text de campos y relaciones de modelos

Por ejemplo, para traducir el texto de ayuda del campo name en el siguiente modelo, haz lo siguiente:

from django.db import models
from django.utils.translation import gettext_lazy as _


class MyThing(models.Model):
    name = models.CharField(help_text=_("This is the help text"))

Puedes marcar los nombres de las ForeignKey, ManyToManyField o OneToOneField relaciones como translatables utilizando sus opciones verbose_name

class MyThing(models.Model):
    kind = models.ForeignKey(
        ThingKind,
        on_delete=models.CASCADE,
        related_name="kinds",
        verbose_name=_("kind"),
    )

Al igual que harías en verbose_name, debes proporcionar un texto de nombre verbose en minúsculas para la relación, ya que Django lo convertirá automáticamente a mayúsculas cuando sea necesario.

Valores de nombres de modelos verbales

Se recomienda siempre proporcionar opciones explícitas verbose_name y verbose_name_plural en lugar de confiar en la determinación ingenua y centrada en inglés que hace Django al mirar el nombre de clase del modelo:

from django.db import models
from django.utils.translation import gettext_lazy as _


class MyThing(models.Model):
    name = models.CharField(_("name"), help_text=_("This is the help text"))

    class Meta:
        verbose_name = _("my thing")
        verbose_name_plural = _("my things")

Los métodos de modelos description argumento para el decorador @display

Para los métodos de modelos, puedes proporcionar traducciones a Django y al sitio administrativo con el argumento description del decorador display():

from django.contrib import admin
from django.db import models
from django.utils.translation import gettext_lazy as _


class MyThing(models.Model):
    kind = models.ForeignKey(
        ThingKind,
        on_delete=models.CASCADE,
        related_name="kinds",
        verbose_name=_("kind"),
    )

    @admin.display(description=_("Is it a mouse?"))
    def is_mouse(self):
        return self.kind.type == MOUSE_TYPE

Trabajando con objetos de traducción perezosos

El resultado de una llamada a gettext_lazy() se puede utilizar en cualquier lugar donde se usaría una cadena (un objeto str) en otros códigos Django, pero no funcionará con código Python arbitrario. Por ejemplo, lo siguiente no funcionará porque la biblioteca requests no maneja objetos gettext_lazy:

body = gettext_lazy("I \u2764 Django")  # (Unicode :heart:)
requests.post("https://example.com/send", data={"body": body})

Puedes evitar estos problemas convirtiendo los objetos gettext_lazy() a cadenas de texto antes de pasarlos al código no Django:

requests.post("https://example.com/send", data={"body": str(body)})

Si no te gusta el largo nombre gettext_lazy, puedes asignarlo como _ (guión bajo), así:

from django.db import models
from django.utils.translation import gettext_lazy as _


class MyThing(models.Model):
    name = models.CharField(help_text=_("This is the help text"))

Usar gettext_lazy() y ngettext_lazy() para marcar cadenas en modelos y funciones de utilidad es una operación común. Cuando estés trabajando con estos objetos en otros lugares de tu código, asegúrate de que no conviertas accidentalmente a cadenas, porque deben convertirse lo más tarde posible (para que el locale correcto esté en efecto). Esto requiere el uso de la función auxiliar descrita a continuación.

Traducciones perezosas y plurales

Cuando se utiliza una traducción perezosa para una cadena plural (n[p]gettext_lazy), generalmente no sabes el argumento number en el momento de la definición de la cadena. Por lo tanto, estás autorizado a pasar un nombre de clave en lugar de un entero como argumento number. Luego number se buscará en el diccionario bajo esa clave durante la interpolación de cadenas. Aquí tienes un ejemplo:

from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import ngettext_lazy


class MyForm(forms.Form):
    error_message = ngettext_lazy(
        "You only provided %(num)d argument",
        "You only provided %(num)d arguments",
        "num",
    )

    def clean(self):
        # ...
        if error:
            raise ValidationError(self.error_message % {"num": number})

Si la cadena contiene exactamente un lugar de reemplazo sin nombre, puedes interpolar directamente con el argumento number:

class MyForm(forms.Form):
    error_message = ngettext_lazy(
        "You provided %d argument",
        "You provided %d arguments",
    )

    def clean(self):
        # ...
        if error:
            raise ValidationError(self.error_message % number)

Formateando cadenas: format_lazy()

El método str.format() de Python no funcionará cuando tanto la cadena de formato como cualquiera de los argumentos a str.format() contengan objetos de traducción perezosa. En su lugar, puedes usar django.utils.text.format_lazy(), que crea un objeto perezoso que ejecuta el método str.format() solo cuando el resultado se incluye en una cadena. Por ejemplo:

from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy

...
name = gettext_lazy("John Lennon")
instrument = gettext_lazy("guitar")
result = format_lazy("{name}: {instrument}", name=name, instrument=instrument)

En este caso, las traducciones perezosas en result solo se convertirán a cadenas cuando result se utilice en una cadena (generalmente en el momento de la renderización del template).

Otros usos de lazy en traducciones retrasadas

Para cualquier otro caso donde desee retrasar la traducción, pero tenga que pasar la cadena translatable como argumento a otra función, puede envolver esta función dentro de una llamada perezosa usted mismo. Por ejemplo:

from django.utils.functional import lazy
from django.utils.translation import gettext_lazy as _


def to_lower(string):
    return string.lower()


to_lower_lazy = lazy(to_lower, str)

Y luego más tarde:

lazy_string = to_lower_lazy(_("My STRING!"))

Nombres localizados de idiomas

get_language_info(lang_code)[fuente]

La función get_language_info() proporciona información detallada sobre los idiomas:

>>> from django.utils.translation import activate, get_language_info
>>> activate("fr")
>>> li = get_language_info("de")
>>> print(li["name"], li["name_local"], li["name_translated"], li["bidi"])
German Deutsch Allemand False

Las atributos name, name_local y name_translated del diccionario contienen el nombre del idioma en inglés, en sí mismo y en su idioma activo actualmente respectivamente. El atributo bidi es True solo para idiomas de escritura bidireccional.

La fuente de la información sobre el idioma es el módulo django.conf.locale. Acceso similar a esta información está disponible para el código de plantilla. Consulte a continuación.

Internacionalización: en el código de plantilla

Las traducciones en las plantillas <doc>`Django templates </ref/templates/language>` utilizan dos etiquetas de plantilla y un sintaxis ligeramente diferente que en el código Python. Para dar a tu plantilla acceso a estas etiquetas, coloca {% load i18n %} hacia la parte superior de tu plantilla. Al igual que todas las etiquetas de plantilla, esta etiqueta necesita ser cargada en todas las plantillas que utilizan traducciones, incluso aquellas plantillas que extienden de otras plantillas que ya han cargado la etiqueta i18n.

Advertencia

Las cadenas traducidas no se escaparán cuando se rendericen en una plantilla. Esto permite incluir HTML en las traducciones, por ejemplo para el énfasis, pero también se renderizarán sin cambios los caracteres potencialmente peligrosos (por ejemplo ").

Etiqueta de plantilla translate

La etiqueta de plantilla {% translate %} traduce una cadena constante (encerrada entre comillas simples o dobles) o contenido variable:

<title>{% translate "This is the title." %}</title>
<title>{% translate myvar %}</title>

Si la opción noop está presente, se realizará el buscado de variables pero se saltará la traducción. Esto es útil cuando «sustituyes» contenido que requerirá traducción en el futuro:

<title>{% translate "myvar" noop %}</title>

Internamente, las traducciones inline utilizan una llamada a la función gettext().

En caso de que se pase una variable de plantilla (myvar anterior) al tag, el tag resolverá dicha variable a una cadena en tiempo de ejecución y luego buscará esa cadena en los catálogos de mensajes.

No es posible mezclar una variable de plantilla dentro de una cadena dentro de {% translate %}. Si tus traducciones requieren cadenas con variables (espacios reservados), utiliza {% blocktranslate %} en su lugar.

Si deseas recuperar una cadena traducida sin mostrarla, puedes utilizar el siguiente sintaxis:

{% translate "This is the title" as the_title %}

<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">

En la práctica, usarás esto para obtener una cadena que puedas utilizar en múltiples lugares de un template o así puedas utilizar el resultado como argumento para otros etiquetas o filtros de plantilla:

{% translate "starting point" as start %}
{% translate "end point" as end %}
{% translate "La Grande Boucle" as race %}

<h1>
  <a href="/" title="{% blocktranslate %}Back to '{{ race }}' homepage{% endblocktranslate %}">{{ race }}</a>
</h1>
<p>
{% for stage in tour_stages %}
    {% cycle start end %}: {{ stage }}{% if forloop.counter|divisibleby:2 %}<br>{% else %}, {% endif %}
{% endfor %}
</p>

{% translate %} también admite marcadores de contexto <contextual-markers> utilizando la palabra clave context:

{% translate "May" context "month name" %}

Etiqueta de bloque de traducción

A diferencia de la etiqueta translate, la etiqueta blocktranslate permite marcar oraciones complejas compuestas por literales y contenido variable para su traducción haciendo uso de reemplazos:

{% blocktranslate %}This string will have {{ value }} inside.{% endblocktranslate %}

Para traducir una expresión de plantilla – digamos, accediendo a atributos de objetos o utilizando filtros de plantilla – necesitarás vincular la expresión a una variable local para utilizar dentro del bloque de traducción. Ejemplos:

{% blocktranslate with amount=article.price %}
That will cost $ {{ amount }}.
{% endblocktranslate %}

{% blocktranslate with myvar=value|filter %}
This will have {{ myvar }} inside.
{% endblocktranslate %}

Puedes utilizar múltiples expresiones dentro de un solo etiqueta blocktranslate:

{% blocktranslate with book_t=book|title author_t=author|title %}
This is {{ book_t }} by {{ author_t }}
{% endblocktranslate %}

Nota

La anterior forma más verbosa todavía está soportada: {% blocktranslate with book|title as book_t and author|title as author_t %}

Las otras etiquetas de bloque (por ejemplo {% for %} o {% if %}) no están permitidas dentro de una etiqueta blocktranslate.

Si falla la resolución de uno de los argumentos del bloque, blocktranslate caerá en el lenguaje por defecto desactivando temporalmente el lenguaje activo actual con la función deactivate_all().

Este tag también proporciona para la pluralización. Para utilizarlo:

  • Designa y vincula un valor de contador con el nombre count. Este valor se utilizará para seleccionar la forma plural correcta.

  • Especifica tanto las formas singular como plural separándolas con el tag {% plural %} dentro de los tags {% blocktranslate %} y {% endblocktranslate %}.

Un ejemplo:

{% blocktranslate count counter=list|length %}
There is {{ counter }} {{ name }} object.
{% plural %}
There are {{ counter }} {{ name }} objects.
{% endblocktranslate %}

Un ejemplo más complejo:

{% blocktranslate with amount=article.price count years=i.length %}
That will cost $ {{ amount }} per year.
{% plural %}
That will cost $ {{ amount }} per {{ years }} years.
{% endblocktranslate %}

Cuando utilices tanto la característica de pluralización como vincula valores a variables locales además del valor del contador, ten en cuenta que la construcción blocktranslate se convierte internamente en una llamada a ngettext. Esto significa que las mismas notas sobre las variables de ngettext <pluralization-var-notes> aplican.

Las consultas inversas de URL no pueden realizarse dentro del blocktranslate y deben recuperarse (y almacenarse) con anterioridad:

{% url 'path.to.view' arg arg2 as the_url %}
{% blocktranslate %}
This is a URL: {{ the_url }}
{% endblocktranslate %}

Si deseas recuperar una cadena traducida sin mostrarla, puedes utilizar el siguiente sintaxis:

{% blocktranslate asvar the_title %}The title is {{ title }}.{% endblocktranslate %}
<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">

En la práctica, utilizarás esto para obtener una cadena que puedas usar en múltiples lugares en un template o así puedas usar el resultado como argumento para otros tags de plantilla o filtros.

{% blocktranslate %} también admite marcadores contextuales <contextual-markers> utilizando la palabra clave context:

{% blocktranslate with name=user.username context "greeting" %}Hi {{ name }}{% endblocktranslate %}

Otra característica que {% blocktranslate %} admite es la opción trimmed. Esta opción eliminará los caracteres de nueva línea del principio y el final del contenido del tag {% blocktranslate %}, reemplazará cualquier espacio en blanco al principio y fin de una línea y fusionará todas las líneas en una utilizando un carácter de espacio para separarlas. Esto es muy útil para indentar el contenido del tag {% blocktranslate %} sin que los caracteres de indentación acaben en la correspondiente entrada en el archivo .po, lo que facilita el proceso de traducción.

Los siguientes ejemplos de la etiqueta {% blocktranslate %}:

{% blocktranslate trimmed %}
  First sentence.
  Second paragraph.
{% endblocktranslate %}

resultarán en la entrada "Primera oración. Segundo párrafo." en el archivo .po, comparado con "\n  Primera oración.\n  Segundo párrafo.\n", si no se hubiera especificado la opción trimmed.

Límites de cadena pasados a etiquetas y filtros

Puedes traducir límites de cadena pasados como argumentos a etiquetas y filtros utilizando el sintaxis familiar _():

{% some_tag _("Page not found") value|yesno:_("yes,no") %}

En este caso, tanto la etiqueta como el filtro verán la cadena traducida, por lo que no necesitan estar al tanto de las traducciones.

Nota

En este ejemplo, la infraestructura de traducción se le pasará la cadena "sí,no", y no las cadenas individuales "sí" y "no". La cadena traducida deberá contener la coma para que el código de filtrado pueda dividir los argumentos correctamente. Por ejemplo, un traductor alemán podría traducir la cadena "sí,no" como "ja,nein" (manteniendo la coma intacta).

Comentarios para traductores en plantillas

Al igual que con el código Python, estos comentarios para traductores se pueden especificar utilizando comentarios, ya sea con la etiqueta comment:

{% comment %}Translators: View verb{% endcomment %}
{% translate "View" %}

{% comment %}Translators: Short intro blurb{% endcomment %}
<p>{% blocktranslate %}A multiline translatable
literal.{% endblocktranslate %}</p>

o con los constructos de comentario de una sola línea {##}: ref:<template-comments>:

{# Translators: Label of a button that triggers search #}
<button type="submit">{% translate "Go" %}</button>

{# Translators: This is a text of the base template #}
{% blocktranslate %}Ambiguous translatable block of text{% endblocktranslate %}

Nota

Solo por completitud, aquí tienes los fragmentos correspondientes del archivo resultante .po:

#. Translators: View verb
# path/to/template/file.html:10
msgid "View"
msgstr ""

#. Translators: Short intro blurb
# path/to/template/file.html:13
msgid ""
"A multiline translatable"
"literal."
msgstr ""

# ...

#. Translators: Label of a button that triggers search
# path/to/template/file.html:100
msgid "Go"
msgstr ""

#. Translators: This is a text of the base template
# path/to/template/file.html:103
msgid "Ambiguous translatable block of text"
msgstr ""

Cambiar el idioma en plantillas

Si deseas seleccionar un idioma dentro de una plantilla, puedes utilizar la etiqueta de plantilla language:

{% load i18n %}

{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<p>{% translate "Welcome to our page" %}</p>

{% language 'en' %}
    {% get_current_language as LANGUAGE_CODE %}
    <!-- Current language: {{ LANGUAGE_CODE }} -->
    <p>{% translate "Welcome to our page" %}</p>
{% endlanguage %}

Mientras que la primera aparición de «Bienvenido a nuestra página» utiliza el idioma actual, la segunda siempre estará en inglés.

Otras etiquetas

Estas etiquetas también requieren una {% load i18n %}.

get_available_languages

{% get_available_languages as LENGUAJES %} devuelve una lista de tuplas en la que el primer elemento es el código de idioma y el segundo es el nombre del idioma (traducido al locale actualmente activo).

get_current_language

{% get_current_language as CODIGO_IDIOMA %} devuelve el idioma preferido del usuario actual como una cadena. Ejemplo: en-us. Consulta cómo Django descubre la preferencia de idioma.

get_current_language_bidi

{% get_current_language_bidi as LANGUAGE_BIDI %} devuelve la dirección del locale actual. Si es True, se trata de un idioma de derecha a izquierda, por ejemplo, hebreo o árabe. Si es False se trata de un idioma de izquierda a derecha, por ejemplo, inglés, francés, alemán, etc.

El procesador de contexto i18n

Si habilitas el procesador de contexto django.template.context_processors.i18n, entonces cada RequestContext tendrá acceso a LANGUAGES, LANGUAGE_CODE y LANGUAGE_BIDI tal como se definen arriba.

get_language_info

También puedes recuperar información sobre cualquier uno de los idiomas disponibles utilizando las etiquetas y filtros de plantilla proporcionados. Para obtener información sobre un solo idioma, utiliza la etiqueta {% get_language_info %}:

{% get_language_info for LANGUAGE_CODE as lang %}
{% get_language_info for "pl" as lang %}

Puedes acceder a la información:

Language code: {{ lang.code }}<br>
Name of language: {{ lang.name_local }}<br>
Name in English: {{ lang.name }}<br>
Bi-directional: {{ lang.bidi }}
Name in the active language: {{ lang.name_translated }}

get_language_info_list

También puedes utilizar la etiqueta de plantilla {% get_language_info_list %} para recuperar información sobre una lista de idiomas (por ejemplo, los idiomas activos tal como se especifican en LANGUAGES). Consulta la sección <set_language-redirect-view> sobre el método de redirección set_language para un ejemplo de cómo mostrar un selector de idioma utilizando {% get_language_info_list %}.

Además del estilo de lista de tuplas LANGUAGES, {% get_language_info_list %} admite listas de códigos de idiomas. Si haces esto en tu vista:

context = {"available_languages": ["en", "es", "fr"]}
return render(request, "mytemplate.html", context)

puedes iterar sobre esos idiomas en la plantilla:

{% get_language_info_list for available_languages as langs %}
{% for lang in langs %} ... {% endfor %}

Filtros de plantilla

Hay también algunos filtros disponibles para conveniencia:

  • {{ LANGUAGE_CODE|language_name }}` («alemán»)

  • {{ LANGUAGE_CODE|language_name_local }}` («alemán»)

  • {{ LANGUAGE_CODE|language_bidi }}` (Falso)

  • {{ LANGUAGE_CODE|language_name_translated }}` («češtinou», cuando el idioma activo es checo»)

Internacionalización: en código JavaScript

Agregar traducciones a JavaScript plantea algunos problemas:

  • El código JavaScript no tiene acceso a una implementación de gettext.

  • El código JavaScript no tiene acceso a archivos .po o .mo; deben ser entregados por el servidor.

  • Los catálogos de traducciones para JavaScript deberían mantenerse lo más pequeños posible.

Django proporciona una solución integrada a estos problemas: Pasa las traducciones al JavaScript, por lo que puedes llamar a gettext, etc., desde dentro de JavaScript.

La solución principal a estos problemas es la siguiente vista JavaScriptCatalog, que genera una biblioteca de código JavaScript con funciones que imitan la interfaz gettext, más un array de cadenas de traducción.

La vista JavaScriptCatalog

class JavaScriptCatalog[fuente]

Una vista que produce una biblioteca de código JavaScript con funciones que imitan la interfaz gettext, más un array de cadenas de traducción.

Atributos

domain

Dominio de traducción que contiene cadenas para agregar en la salida de la vista. Por defecto es 'djangojs'.

packages

Una lista de nombres de aplicaciones entre las aplicaciones instaladas. Aquellas apps deben contener un directorio locale. Todos esos catálogos más todos los catálogos encontrados en LOCALE_PATHS (que siempre se incluyen) se fusionan en uno solo. Por defecto es None, lo que significa que todas las traducciones disponibles de todas las INSTALLED_APPS se proporcionan en la salida del JavaScript.

Ejemplo con valores por defecto:

from django.views.i18n import JavaScriptCatalog

urlpatterns = [
    path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"),
]

Ejemplo con paquetes personalizados:

urlpatterns = [
    path(
        "jsi18n/myapp/",
        JavaScriptCatalog.as_view(packages=["your.app.label"]),
        name="javascript-catalog",
    ),
]

Si tu archivo URLconf raíz utiliza i18n_patterns(), JavaScriptCatalog también debe estar envuelto en i18n_patterns() para que el catálogo se genere correctamente.

Ejemplo con i18n_patterns()

from django.conf.urls.i18n import i18n_patterns

urlpatterns = i18n_patterns(
    path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"),
)

La precedencia de las traducciones es tal que los paquetes que aparecen más tarde en el argumento packages tienen una mayor precedencia que los que aparecen al principio. Esto es importante en el caso de traducciones conflictivas para la misma literal.

Si utilizas más de una vista JavaScriptCatalog en un sitio y algunas de ellas definen las mismas cadenas, las cadenas en el catálogo que se cargó último tienen prioridad.

Usando el catálogo de traducciones de JavaScript

Usar el catálogo, incorpora el script generado dinámicamente de la siguiente manera:

<script src="{% url 'javascript-catalog' %}"></script>

Este utiliza la búsqueda de URLs inversa para encontrar la URL de la vista del catálogo de JavaScript. Cuando se carga el catálogo, tu código JavaScript puede utilizar los siguientes métodos:

  • gettext

  • ngettext

  • interpolate

  • get_format

  • gettext_noop

  • pgettext

  • npgettext

  • pluralidx

gettext

La función gettext se comporta de manera similar a la interfaz estándar gettext dentro de su código Python:

document.write(gettext("this is to be translated"))

ngettext

La función ngettext proporciona una interfaz para pluralizar palabras y frases:

const objectCount = 1 // or 0, or 2, or 3, ...
const string = ngettext(
    'literal for the singular case',
    'literal for the plural case',
    objectCount
);

interpolate

La función interpolate admite la población dinámica de una cadena de formato. El síntoma de interpolación se presta de Python, por lo que la función interpolate admite tanto la interpolación posicional como la interpolación nombrada:

  • Interpolación posicional: obj contiene un objeto JavaScript Array cuyos elementos valores se intercalan secuencialmente en sus correspondientes marcadores de formato fmt en el mismo orden que aparecen. Por ejemplo:

    const formats = ngettext(
      'There is %s object. Remaining: %s',
      'There are %s objects. Remaining: %s',
      11
    );
    const string = interpolate(formats, [11, 20]);
    // string is 'There are 11 objects. Remaining: 20'
    
  • Interpolación nombrada: Este modo se selecciona pasando el parámetro booleano opcional named como true. obj contiene un objeto JavaScript o matriz asociativa. Por ejemplo:

    const data = {
      count: 10,
      total: 50
    };
    
    const formats = ngettext(
        'Total: %(total)s, there is %(count)s object',
        'there are %(count)s of a total of %(total)s objects',
        data.count
    );
    const string = interpolate(formats, data, true);
    

No te excedas con la interpolación de cadenas, aunque: esto todavía es JavaScript, por lo que el código tiene que realizar sustituciones regulares repetidas. Esto no es tan rápido como la interpolación de cadenas en Python, así que manténlo para aquellos casos en los que realmente lo necesites (por ejemplo, en conjunto con ngettext para producir pluralizaciones correctas).

get_format

La get_format función tiene acceso a los ajustes de formato configurados para i18n y puede recuperar la cadena de formato para un nombre de ajuste dado:

document.write(get_format('DATE_FORMAT'));
// 'N j, Y'

Tiene acceso a los siguientes ajustes:

  • FORMATO_DE_FECHA

  • FORMATOS_DE_INGRESO_DE_FECHA

  • FORMATO_DE_HORA_Y_FECHA

  • FORMATOS_DE_INGRESO_DE_HORA_Y_FECHA

  • SEPARADOR_DECIMAL

  • PRIMER_DÍA_DE_LA_SEMANA

  • FORMATO_DE_MES_Y_DÍA

  • AGRUPO_POR_DEFECTO

  • FORMATO DE FECHA CORTA

  • FORMATO DE HORA Y FECHA CORTA

  • SEPARADOR DE MIL MILLONES

  • FORMATO DE HORA

  • FORMATOS DE ENTRADA DE HORA

  • FORMATO DE AÑO Y MES

Es útil para mantener la consistencia en la formación con los valores renderizados por Python.

gettext_noop

Esto simula la función gettext pero no hace nada, devolviendo lo que se le pasa a ella:

document.write(gettext_noop("this will not be translated"))

Es útil para sustituir partes del código que necesitarán traducirse en el futuro.

pgettext

La función pgettext se comporta como su variante de Python (pgettext()), proporcionando una palabra traducida contextualmente:

document.write(pgettext("month name", "May"))

npgettext

La traducción de los textos es la siguiente:

document.write(npgettext('group', 'party', 1));
// party
document.write(npgettext('group', 'party', 2));
// parties

pluralidx

La función pluralidx funciona de manera similar a el filtro de plantilla pluralize, determinando si un dado count debe utilizar una forma plural de una palabra o no:

document.write(pluralidx(0));
// true
document.write(pluralidx(1));
// false
document.write(pluralidx(2));
// true

En el caso más simple, si no se necesita una pluralización personalizada, devuelve falso para el entero 1 y verdadero para todos los demás números.

Sin embargo, la pluralización no es tan sencilla en todos los idiomas. Si el idioma no admite la pluralización, se proporciona un valor vacío.

Además, si hay reglas complejas alrededor de la pluralización, la vista del catálogo renderizará una expresión condicional. Esto evaluará a verdadero (debe pluralizarse) o falso (no debe pluralizarse) valor.

La vista JSONCatalog

class JSONCatalog[fuente]

Para aprovechar la funcionalidad de otra biblioteca cliente para manejar las traducciones, puede querer aprovechar la vista JSONCatalog. Es similar a JavaScriptCatalog pero devuelve una respuesta JSON.

Consulte la documentación de JavaScriptCatalog para obtener información sobre los valores posibles y el uso de las atributos domain y packages.

La forma del formato de respuesta es la siguiente:

{
    "catalog": {
        # Translations catalog
    },
    "formats": {
        # Language formats for date, time, etc.
    },
    "plural": "..."  # Expression for plural forms, or null.
}

Nota sobre rendimiento

Los diversos vistas JavaScript/JSON i18n generan el catálogo a partir de archivos .mo en cada solicitud. Dado que su salida es constante, al menos para una versión dada de un sitio, es un buen candidato para la caché.

La caché del lado del servidor reducirá la carga de CPU. Se puede implementar fácilmente con el decorador cache_page(). Para desencadenar la invalidación de la caché cuando cambien tus traducciones, proporciona un prefijo clave dependiente de la versión, como se muestra en el ejemplo a continuación, o mapea la vista en una URL dependiente de la versión:

from django.views.decorators.cache import cache_page
from django.views.i18n import JavaScriptCatalog

# The value returned by get_version() must change when translations change.
urlpatterns = [
    path(
        "jsi18n/",
        cache_page(86400, key_prefix="jsi18n-%s" % get_version())(
            JavaScriptCatalog.as_view()
        ),
        name="javascript-catalog",
    ),
]

La caché del lado del cliente ahorrará ancho de banda y hará que tu sitio cargue más rápido. Si estás utilizando ETags (ConditionalGetMiddleware), ya tienes cubierto. De lo contrario, puedes aplicar los decoradores condicionales <conditional-decorators> . En el ejemplo siguiente, la caché se invalida cada vez que reinicias tu servidor de aplicación:

from django.utils import timezone
from django.views.decorators.http import last_modified
from django.views.i18n import JavaScriptCatalog

last_modified_date = timezone.now()

urlpatterns = [
    path(
        "jsi18n/",
        last_modified(lambda req, **kw: last_modified_date)(
            JavaScriptCatalog.as_view()
        ),
        name="javascript-catalog",
    ),
]

Puedes incluso generar previamente el catálogo JavaScript como parte del procedimiento de despliegue y servirlo como un archivo estático. Esta técnica radical se implementa en django-statici18n.

Internacionalización: en patrones de URL

Django proporciona dos mecanismos para internacionalizar los patrones de URL:

Advertencia

Usar cualquiera de estas características requiere que se establezca un idioma activo para cada solicitud; en otras palabras, necesitas tener django.middleware.locale.LocaleMiddleware en tu configuración MIDDLEWARE.

Prefijo de idioma en patrones de URL

i18n_patterns(*urls, prefix_default_language=True)[fuente]

Esta función se puede utilizar en una configuración de URL raíz y Django agregará automáticamente el código de idioma activo actual al inicio de todos los patrones de URL definidos dentro de i18n_patterns().

Estableciendo prefix_default_language a False se elimina la prefija del idioma por defecto (LANGUAGE_CODE). Esto puede ser útil cuando se agregan traducciones a un sitio existente para que las URLs actuales no cambien.

Ejemplos de patrones de URL:

from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path

from about import views as about_views
from news import views as news_views
from sitemap.views import sitemap

urlpatterns = [
    path("sitemap.xml", sitemap, name="sitemap-xml"),
]

news_patterns = (
    [
        path("", news_views.index, name="index"),
        path("category/<slug:slug>/", news_views.category, name="category"),
        path("<slug:slug>/", news_views.details, name="detail"),
    ],
    "news",
)

urlpatterns += i18n_patterns(
    path("about/", about_views.main, name="about"),
    path("news/", include(news_patterns, namespace="news")),
)

Después de definir estos patrones de URL, Django agregará automáticamente la prefija del idioma a los patrones de URL que fueron agregados por la función i18n_patterns(). Ejemplo:

>>> from django.urls import reverse
>>> from django.utils.translation import activate

>>> activate("en")
>>> reverse("sitemap-xml")
'/sitemap.xml'
>>> reverse("news:index")
'/en/news/'

>>> activate("nl")
>>> reverse("news:detail", kwargs={"slug": "news-slug"})
'/nl/news/news-slug/'

Con prefix_default_language=False y LANGUAGE_CODE='es', las URLs serán:

>>> activate("en")
>>> reverse("news:index")
'/news/'

>>> activate("nl")
>>> reverse("news:index")
'/nl/news/'

Advertencia

La función i18n_patterns() solo está permitida en una configuración de URL raíz. Utilizarla dentro de una inclusión de URLconf lanzará un excepción ImproperlyConfigured.

Advertencia

Asegúrate de que no tengas patrones de URL sin prefijo que puedan colisionar con la prefija del idioma agregada automáticamente.

Traducir patrones de URL

Los patrones de URL también pueden ser marcados como translatables utilizando la función gettext_lazy(). Ejemplo:

from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
from django.utils.translation import gettext_lazy as _

from about import views as about_views
from news import views as news_views
from sitemaps.views import sitemap

urlpatterns = [
    path("sitemap.xml", sitemap, name="sitemap-xml"),
]

news_patterns = (
    [
        path("", news_views.index, name="index"),
        path(_("category/<slug:slug>/"), news_views.category, name="category"),
        path("<slug:slug>/", news_views.details, name="detail"),
    ],
    "news",
)

urlpatterns += i18n_patterns(
    path(_("about/"), about_views.main, name="about"),
    path(_("news/"), include(news_patterns, namespace="news")),
)

Después de crear las traducciones, la función reverse() devolverá la URL en el idioma activo. Ejemplo:

>>> from django.urls import reverse
>>> from django.utils.translation import activate

>>> activate("en")
>>> reverse("news:category", kwargs={"slug": "recent"})
'/en/news/category/recent/'

>>> activate("nl")
>>> reverse("news:category", kwargs={"slug": "recent"})
'/nl/nieuws/categorie/recent/'

Advertencia

Los textos traducidos son:

Reversión en plantillas

Si las URLs localizadas se invierten en plantillas, siempre utilizan el idioma actual. Para vincular a una URL en otro idioma utilice la etiqueta de plantilla idioma. Esta permite habilitar el idioma dado dentro del bloque de plantilla encerrado:

{% load i18n %}

{% get_available_languages as languages %}

{% translate "View this category in:" %}
{% for lang_code, lang_name in languages %}
    {% language lang_code %}
    <a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>
    {% endlanguage %}
{% endfor %}

La etiqueta idioma espera el código de idioma como único argumento.

Localización: cómo crear archivos de idiomas

Una vez que los literales de cadena de una aplicación han sido marcados para su traducción posterior, las traducciones mismas deben ser escritas (o obtenidas). Aquí está cómo funciona eso.

Archivos de mensajes

El primer paso es crear un archivo de mensaje para un nuevo idioma. Un archivo de mensaje es un archivo de texto plano, que representa un solo idioma, y contiene todas las cadenas de traducción disponibles y cómo deben ser representadas en el idioma dado. Los archivos de mensajes tienen una extensión .po.

Django viene con una herramienta, django-admin makemessages, que automatiza la creación y mantenimiento de estos archivos.

Herramientas Gettext

El comando makemessages (y compilemessages que se discute más adelante) utilizan comandos del conjunto de herramientas GNU gettext: xgettext, msgfmt, msgmerge y msguniq.

La versión mínima de las utilidades gettext soportadas es 0.19.

Para crear o actualizar un archivo de mensajes, ejecuta este comando:

django-admin makemessages -l de

…donde de es el nombre de locale para el archivo de mensajes que deseas crear. Por ejemplo, pt_BR para portugués brasileño, de_AT para alemán austriaco o id para indonesio.

El script debe ejecutarse desde uno de dos lugares:

  • La carpeta raíz de tu proyecto Django (la que contiene manage.py).

  • La carpeta raíz de una de tus aplicaciones Django.

El script se ejecuta sobre el árbol de fuentes de tu proyecto o aplicación y extrae todas las cadenas marcadas para la traducción (consultar Cómo Django descubre las traducciones y asegúrate de que LOCALE_PATHS esté configurado correctamente). Crea (o actualiza) un archivo de mensajes en la carpeta locale/LANG/LC_MESSAGES. En el ejemplo de, el archivo será locale/de/LC_MESSAGES/django.po.

Cuando ejecutes makemessages desde la carpeta raíz de tu proyecto, las cadenas extraídas se distribuirán automáticamente a los archivos de mensajes correspondientes. Es decir, una cadena extraída de un archivo de una aplicación que contenga una carpeta locale irá en un archivo de mensajes bajo esa carpeta. Una cadena extraída de un archivo de una aplicación sin ninguna carpeta locale irá en un archivo de mensajes bajo la carpeta lista primero en LOCALE_PATHS o generará un error si LOCALE_PATHS está vacío.

Por defecto, django-admin makemessages examina todos los archivos con las extensiones .html, .txt o .py. Si deseas sobreescribir ese comportamiento por defecto, utiliza la opción --extension o -e para especificar las extensiones de archivo a examinar:

django-admin makemessages -l de -e txt

Separa múltiples extensiones con comas y/o utiliza varias veces -e o --extension.

django-admin makemessages -l de -e html,txt -e xml

Advertencia

Cuando creando archivos de mensajes a partir del código fuente en JavaScript necesitas utilizar el dominio especial djangojs, no -e js.

¿Usando plantillas de Jinja2?

:makemanager:`makemessages` no entiende el sintaxis de plantillas Jinja2. Para extraer cadenas de un proyecto que contiene plantillas Jinja2, utilice Extracción de mensajes de Babel.

[python] extension = .py input_encoding = utf-8 output_encoding = utf-8 mapping = es_ES.UTF-8 python en_US.UTF-8 python

# Extraction from Python source files
[python: **.py]

# Extraction from Jinja2 templates
[jinja2: **.jinja]
extensions = jinja2.ext.with_

Asegúrate de incluir en la lista todas las extensiones que estás utilizando. De lo contrario, Babel no reconocerá los etiquetas definidas por estas extensiones y ignorará completamente las plantillas Jinja2 que las contengan.

Babel proporciona características similares a makemessages, puede reemplazarla en general y no depende de gettext. Para obtener más información, lee su documentación sobre el trabajo con catálogos de mensajes.

¿No hay gettext?

Si no tienes instaladas las utilidades de gettext, makemessages creará archivos vacíos. Si es así, instala las utilidades de gettext o copia el archivo de mensajes en inglés (locale/en/LC_MESSAGES/django.po) si está disponible y úsalo como punto de partida, que es un archivo de traducción vacío.

Trabajando en Windows?

Si estás utilizando Windows y necesitas instalar las utilidades GNU gettext para que makemessages funcione, consulta la sección gettext en Windows para obtener más información.

Cada archivo .po contiene un pequeño trozo de metadatos, como la información de contacto del mantenedor de traducciones, pero el grueso del archivo es una lista de mensajes – mapeos entre las cadenas de traducción y el texto traducido real para el idioma particular.

Por ejemplo, si tu aplicación Django contenía una cadena de traducción para el texto "Bienvenido a mi sitio.", como se muestra a continuación:

_("Welcome to my site.")

…entonces django-admin makemessages habría creado un archivo .po que contiene la siguiente snippet – un mensaje:

#: path/to/python/module.py:23
msgid "Welcome to my site."
msgstr ""

Una explicación rápida:

  • msgid es la cadena de traducción, que aparece en el código fuente. No lo cambies.

  • msgstr es donde colocas la traducción específica del idioma. Comienza vacío, por lo que es tu responsabilidad cambiarlo. Asegúrate de mantener las comillas alrededor de tu traducción.

  • Como conveniencia, cada mensaje incluye, en forma de línea de comentario precedida con # y ubicada sobre la línea msgid, el nombre del archivo y el número de línea desde los cuales se obtuvo la cadena de traducción.

Los mensajes largos son un caso especial. Allí, la primera cadena directamente después de msgstr (o msgid) es una cadena vacía. Luego el contenido en sí se escribirá sobre las siguientes líneas como una cadena por línea. Aquellas cadenas se concatenan directamente. No olvides los espacios finales dentro de las cadenas; de lo contrario, se pegarán sin espacio en blanco.

Ten cuidado con tu conjunto de caracteres

Due a la forma en que funcionan las herramientas gettext internamente y porque queremos permitir cadenas de origen no ASCII en el núcleo de Django y en tus aplicaciones, debes utilizar UTF-8 como codificación para tus archivos .po (lo cual es el valor por defecto cuando se crean los archivos .po). Esto significa que todos estarán utilizando la misma codificación, lo cual es importante cuando Django procesa los archivos .po.

Entradas difusas

: djadmin:makemessages a veces genera entradas de traducción marcadas como difusas, por ejemplo, cuando las traducciones se infieren de cadenas previamente traducidas. Por defecto, las entradas difusas no se procesan por compilemessages.

Para reexaminar todo el código fuente y plantillas para nuevas cadenas de traducción y actualizar todos los archivos de mensajes para todos los idiomas, ejecuta lo siguiente:

django-admin makemessages -a

Compilación de archivos de mensajes

Después de crear tu archivo de mensajes – y cada vez que hagas cambios en él – necesitarás compilarlo a una forma más eficiente, para su uso por gettext. Haz esto con la utilidad django-admin compilemessages.

Esta herramienta recorre todos los archivos .po disponibles y crea archivos .mo, que son archivos binarios optimizados para el uso de gettext. En el mismo directorio desde el cual ejecutaste django-admin makemessages, ejecuta django-admin compilemessages como se muestra a continuación:

django-admin compilemessages

Eso es todo. Tus traducciones están listas para su uso.

Trabajando en Windows?

Si estás utilizando Windows y necesitas instalar las utilidades GNU gettext para que django-admin compilemessages funcione, consulta gettext en Windows para obtener más información.

Archivos .po: Uso de codificación y BOM.

Django solo admite archivos .po codificados en UTF-8 y sin ningún BOM (Byte Order Mark), por lo que si tu editor de texto agrega tales marcas al principio de los archivos por defecto entonces necesitarás reconfigurarlo.

Solución de problemas: gettext() detecta incorrectamente python-format en cadenas con signos de porcentaje

En algunos casos, como las cadenas con un signo de porcentaje seguido de un espacio y un tipo de conversión de cadena (por ejemplo _("10% interes")), gettext() detecta incorrectamente cadenas con python-format.

Si intentas compilar archivos de mensajes con cadenas marcadas incorrectamente, obtendrás un mensaje de error como el número de especificaciones de formato en 'msgid' y 'msgstr' no coincide o 'msgstr' no es una cadena de formato válida de Python, a diferencia de 'msgid'.

Para solventar esto, puedes escapar los signos de porcentaje agregando un segundo signo de porcentaje:

from django.utils.translation import gettext as _

output = _("10%% interest")

O puedes usar no-python-format para que todos los signos de porcentaje se traten como literales:

# xgettext:no-python-format
output = _("10% interest")

Crear archivos de mensajes a partir del código fuente JavaScript

Crees y actualizas los archivos de mensajes de la misma manera que los otros archivos de Django – con la herramienta django-admin makemessages. La única diferencia es que necesitas especificar explícitamente qué en el lenguaje de gettext se conoce como un dominio, en este caso el djangojs dominio, proporcionando un parámetro -d djangojs, como este:

django-admin makemessages -d djangojs -l de

Esto crearía o actualizaría el archivo de mensajes para JavaScript para alemán. Después de actualizar los archivos de mensajes, ejecuta django-admin compilemessages de la misma manera que lo haces con los archivos de mensajes normales de Django.

gettext en Windows

Este es solo necesario para aquellas personas que deseen extraer IDs de mensajes o compilar archivos de mensajes (.po). El trabajo de traducción en sí mismo implica editar archivos existentes de este tipo, pero si deseas crear tus propios archivos de mensajes, o quieres probar o compilar un archivo de mensajes modificado, descarga un instalador binario precompilado.

También puedes utilizar binarios de gettext que hayas obtenido en otro lugar, siempre y cuando el comando xgettext --version funcione correctamente. No intentes utilizar utilidades de traducción Django con un paquete gettext si el comando xgettext --version introducido en una ventana de comandos de Windows causa una ventana emergente que dice «xgettext.exe ha generado errores y será cerrado por Windows».

Personalizando la orden makemessages

Si deseas pasar parámetros adicionales a xgettext, necesitas crear una orden personalizada makemessages y sobrescribir su atributo xgettext_options:

from django.core.management.commands import makemessages


class Command(makemessages.Command):
    xgettext_options = makemessages.Command.xgettext_options + ["--keyword=mytrans"]

Si necesitas más flexibilidad, también podrías agregar un nuevo argumento a tu orden personalizada makemessages:

from django.core.management.commands import makemessages


class Command(makemessages.Command):
    def add_arguments(self, parser):
        super().add_arguments(parser)
        parser.add_argument(
            "--extra-keyword",
            dest="xgettext_keywords",
            action="append",
        )

    def handle(self, *args, **options):
        xgettext_keywords = options.pop("xgettext_keywords")
        if xgettext_keywords:
            self.xgettext_options = makemessages.Command.xgettext_options[:] + [
                "--keyword=%s" % kwd for kwd in xgettext_keywords
            ]
        super().handle(*args, **options)

Misceláneos

La vista de redirección set_language

set_language(request)[fuente]

Como conveniencia, Django viene con una vista, django.views.i18n.set_language(), que establece la preferencia lingüística del usuario y redirige a una URL dada o, por defecto, hacia la página anterior.

Activa esta vista agregando la siguiente línea a tu archivo de configuración de URLs:

path("i18n/", include("django.conf.urls.i18n")),

(Nota que este ejemplo hace disponible la vista en /i18n/setlang/.)

Advertencia

A continuación se presentan las traducciones de los textos originales.

La vista espera ser llamada mediante el método POST, con un parámetro language establecido en la solicitud. La vista almacena la elección de idioma en una cookie que se llama django_language por defecto (el nombre puede cambiarse a través del LANGUAGE_COOKIE_NAME configuración).

Después de establecer la elección de idioma, Django busca un parámetro next en los datos POST o GET. Si se encuentra y Django lo considera una URL segura (es decir, no apunta a un host diferente y utiliza un esquema seguro), se realizará una redirección a esa URL. De lo contrario, Django puede caer en la cuenta de redirigir al usuario a la URL del encabezado Referer o, si no está configurada, a /, dependiendo de la naturaleza de la solicitud:

  • Si la solicitud acepta contenido HTML (basándose en su encabezado HTTP Accept), el fallback siempre se realizará.

  • Si la solicitud no acepta HTML, el fallback solo se realizará si se estableció el parámetro next. De lo contrario, se devolverá un código de estado 204 (Sin contenido).

Aquí tienes un ejemplo de código de plantilla HTML:

{% load i18n %}

<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
    <input name="next" type="hidden" value="{{ redirect_to }}">
    <select name="language">
        {% get_current_language as LANGUAGE_CODE %}
        {% get_available_languages as LANGUAGES %}
        {% get_language_info_list for LANGUAGES as languages %}
        {% for language in languages %}
            <option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
                {{ language.name_local }} ({{ language.code }})
            </option>
        {% endfor %}
    </select>
    <input type="submit" value="Go">
</form>

En este ejemplo, Django busca la URL de la página a la que se redirigirá al usuario en la variable de contexto redirect_to.

Establecer explícitamente el idioma activo

Quizás desees establecer explícitamente el idioma activo para la sesión actual. Tal vez una preferencia del idioma de un usuario se recupere de otro sistema, por ejemplo. Ya has sido presentado a django.utils.translation.activate(). Eso aplica solo al hilo actual. Para persistir el idioma para toda la sesión en una cookie, establece la cookie LANGUAGE_COOKIE_NAME en la respuesta:

from django.conf import settings
from django.http import HttpResponse
from django.utils import translation

user_language = "fr"
translation.activate(user_language)
response = HttpResponse(...)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)

Normalmente querrías utilizar ambos: django.utils.translation.activate() cambia el idioma para este hilo y configurar la cookie hace que esta preferencia persista en solicitudes futuras.

Usando traducciones fuera de vistas y plantillas

Si bien Django proporciona una rica serie de herramientas i18n para su uso en vistas y plantillas, no restringe el uso a código específico de Django. Los mecanismos de traducción de Django se pueden utilizar para traducir textos arbitrarios a cualquier idioma que sea soportado por Django (a condición de que exista un catálogo de traducciones adecuado, por supuesto). Puedes cargar un catálogo de traducciones, activarlo y traducir texto al idioma de tu elección, pero recuerda cambiar nuevamente a la lengua original, ya que activar un catálogo de traducciones se hace en base a hilos y tal cambio afectará el código ejecutado en el mismo hilo.

Por ejemplo:

from django.utils import translation


def welcome_translated(language):
    cur_language = translation.get_language()
    try:
        translation.activate(language)
        text = translation.gettext("welcome")
    finally:
        translation.activate(cur_language)
    return text

Llamar a esta función con el valor 'de' te dará "Willkommen", independientemente del LANGUAGE_CODE y la lengua establecida por middleware.

Las funciones de particular interés son django.utils.translation.get_language() que devuelve la lengua utilizada en el hilo actual, django.utils.translation.activate() que activa un catálogo de traducciones para el hilo actual y django.utils.translation.check_for_language() que verifica si el idioma dado es soportado por Django.

Para ayudar a escribir código más conciso, también hay un administrador de contexto django.utils.translation.override() que almacena la lengua actual en entrada y la restaura en salida. Con él, el ejemplo anterior se convierte en:

from django.utils import translation


def welcome_translated(language):
    with translation.override(language):
        return translation.gettext("welcome")

Notas de implementación

Características especiales de la traducción en Django

La máquina de traducción de Django utiliza el módulo estándar gettext que viene con Python. Si conoces gettext, podrías notar estas características especiales en la forma en que Django hace la traducción:

  • El dominio de cadena es django o djangojs. Este dominio de cadena se utiliza para diferenciar entre diferentes programas que almacenan sus datos en una biblioteca de archivos de mensajes comunes (generalmente /usr/share/locale/). El dominio django se utiliza para las cadenas de traducción de Python y plantillas, y se carga en los catálogos de traducción globales. El dominio djangojs solo se utiliza para los catálogos de traducción de JavaScript para asegurarse de que estos sean lo más pequeños posible.

  • Django no utiliza xgettext solo. Utiliza envolturas en Python alrededor de xgettext y msgfmt. Esto es principalmente por conveniencia.

Cómo Django descubre la preferencia del idioma

Una vez que hayas preparado tus traducciones – o, si deseas utilizar las traducciones que vienen con Django – necesitarás activar la traducción para tu aplicación.

Detrás de escena, Django tiene un modelo muy flexible para decidir qué idioma debe usarse – de forma general en la instalación, para un usuario en particular o ambas cosas.

Establecer una preferencia de idioma para la instalación completa, establece CÓDIGO DE IDIOMA. Django utiliza este idioma como traducción predeterminada – el intento final si no se encuentra ninguna mejor coincidencia de traducción a través de uno de los métodos empleados por el middleware de localización (consulte a continuación).

Si lo único que deseas es ejecutar Django con tu idioma nativo, todo lo que debes hacer es establecer el código de idioma (LANGUAGE_CODE) y asegurarte de que los archivos correspondientes (archivos de mensaje ) y sus versiones compiladas (.mo) existan.

Si deseas permitir que cada usuario individual especifique el idioma que prefiere, también debes utilizar la LocaleMiddleware. La LocaleMiddleware permite la selección del idioma basada en los datos de la solicitud. Personaliza el contenido para cada usuario.

Para utilizar LocaleMiddleware, agrega “django.middleware.locale.LocaleMiddleware” a tu configuración de MIDDLEWARE. Dado que el orden de las middlewares importa, sigue estas directrices:

  • Asegúrate de que sea uno de los primeros middleware instalados.

  • Debería estar después de SessionMiddleware, porque LocaleMiddleware utiliza datos de sesión. Y debería estar antes de CommonMiddleware porque CommonMiddleware necesita una lengua activada para resolver la URL solicitada.

  • Si utilizas CacheMiddleware, coloca LocaleMiddleware después de él.

Ejemplo: Tu MIDDLEWARE podría verse así:

MIDDLEWARE = [
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.locale.LocaleMiddleware",
    "django.middleware.common.CommonMiddleware",
]

Para más información sobre middleware, consulta la documentación de middleware.

LocaleMiddleware intenta determinar la preferencia lingüística del usuario siguiendo este algoritmo:

  • Primero, busca el prefijo de idioma en la URL solicitada. Esto solo se realiza cuando estás utilizando la función i18n_patterns en tu root URLconf. Consulta url-internacionalización para obtener más información sobre el prefijo de idioma y cómo internacionalizar patrones de URL.

  • Si no lo encuentra, busca una cookie.

    El nombre de la cookie utilizada se establece mediante la configuración LANGUAGE_COOKIE_NAME. (El nombre predeterminado es django_language.)

  • Si no lo encuentra, mira el encabezado HTTP ``Accept-Language””. Este encabezado se envía por tu navegador y le dice al servidor qué idiomas prefieres, en orden de prioridad. Django intenta cada idioma del encabezado hasta encontrar uno con traducciones disponibles.

  • Si no lo encuentra, utiliza la configuración global LANGUAGE_CODE.

Notas:

  • En cada uno de estos lugares, se espera que la preferencia lingüística esté en el formato estándar formato de idioma, como una cadena. Por ejemplo, portugués brasileño es pt-br.

  • Si un idioma base está disponible pero no el subidioma especificado, Django utiliza el idioma base. Por ejemplo, si un usuario especifica ``de-at”” (alemán austriaco) pero Django solo tiene ``de”” disponible, Django utiliza ``de””.

  • Solo se pueden seleccionar los idiomas listados en la configuración LANGUAGES. Si deseas restringir la selección de idioma a un subconjunto de los proporcionados (porque tu aplicación no proporciona todos esos idiomas), establece LANGUAGES en una lista de idiomas. Por ejemplo:

    LANGUAGES = [
        ("de", _("German")),
        ("en", _("English")),
    ]
    

    Este ejemplo restringe los idiomas disponibles para la selección automática a alemán y inglés (y cualquier subidioma, como de-ch o en-us).

  • Si defines una configuración personalizada LANGUAGES, tal como se explica en el punto anterior, puedes marcar los nombres de idioma como cadenas de traducción – pero utiliza gettext_lazy() en lugar de gettext() para evitar un importo circular.

    Aquí tienes un archivo de configuración de ejemplo:

    from django.utils.translation import gettext_lazy as _
    
    LANGUAGES = [
        ("de", _("German")),
        ("en", _("English")),
    ]
    

Una vez que LocaleMiddleware determina la preferencia del usuario, hace esta preferencia disponible como request.LANGUAGE_CODE para cada HttpRequest. Puedes leer este valor en tu código de vista. Aquí tienes un ejemplo:

from django.http import HttpResponse


def hello_world(request, count):
    if request.LANGUAGE_CODE == "de-at":
        return HttpResponse("You prefer to read Austrian German.")
    else:
        return HttpResponse("You prefer to read another language.")

Ten en cuenta que, con traducción estática (sin middleware), el idioma está en settings.LANGUAGE_CODE, mientras que con traducción dinámica (con middleware) está en request.LANGUAGE_CODE.

Cómo Django descubre las traducciones

En tiempo de ejecución, Django construye un catálogo unificado en memoria de literales-traducciones. Para lograr esto sigue este algoritmo respecto a la orden en que examina los diferentes paths de archivo para cargar los archivos de mensajes compilados (.mo) y la precedencia de múltiples traducciones para el mismo literal:

  1. Los directorios listados en LOCALE_PATHS tienen la mayor precedencia, con los que aparecen primero teniendo una mayor precedencia que los que aparecen más tarde.

  2. Luego busca y utiliza si existe un directorio locale en cada una de las aplicaciones instaladas listadas en INSTALLED_APPS. Los que aparecen primero tienen una mayor precedencia que los que aparecen más tarde.

  3. Finalmente, se utiliza la traducción base proporcionada por Django en django/conf/locale como fallback.

Ver también

Los textos traducidos son:

También puedes poner archivos de formato personalizados <custom-format-files> en los directorios de las rutas de localización si también estableces la ruta del módulo de formato.

En todos los casos, se espera que el nombre del directorio que contiene la traducción esté nombrado utilizando la notación de nombre de local. Por ejemplo de, pt_BR, es_AR, etc. Las cadenas no traducidas para variantes de idioma territorial utilizan las traducciones del idioma genérico. Por ejemplo, las cadenas no traducidas pt_BR utilizan traducciones pt.

De esta manera, puedes escribir aplicaciones que incluyan sus propias traducciones y puedes sobreescribir las traducciones base en tu proyecto. O, puedes construir un gran proyecto a partir de varias aplicaciones y poner todas las traducciones en un archivo de mensajes grande común específico del proyecto que estás compuesto.

Todos los repositorios de archivos de mensajes están estructurados de la misma manera. Son:

  • Se buscan todos los directorios listados en LOCALE_PATHS en tu archivo de configuración para <idioma>/LC_MESSAGES/django.(po|mo)

  • $APPPATH/locale/<idioma>/LC_MESSAGES/django.(po|mo)

  • $PYTHONPATH/django/conf/locale/<idioma>/LC_MESSAGES/django.(po|mo)

Para crear archivos de mensajes, utilizas la herramienta django-admin makemessages <makemessages> y para producir los archivos binarios .mo que se utilizan por gettext, utiliza django-admin compilemessages <compilemessages>.

También puedes ejecutar django-admin compilemessages –settings=path.to.settings <compilemessages> para hacer que el compilador procese todos los directorios en tu configuración de LOCALE_PATHS.

Usando un idioma base no inglés

Django asume generalmente que las cadenas originales en un proyecto traducible están escritas en inglés. Puedes elegir otro idioma, pero debes ser consciente de ciertas limitaciones:

  • gettext solo proporciona dos formas plurales para las mensajes originales, por lo que también necesitarás proporcionar una traducción para el idioma base para incluir todas las formas plurales si las reglas plurales del idioma base son diferentes del inglés.

  • Cuando se activa una variante del inglés y faltan cadenas en inglés, el idioma de fallback no será el LANGUAGE_CODE del proyecto, sino las cadenas originales. Por ejemplo, un usuario que habla inglés visitando un sitio con LANGUAGE_CODE configurado para español y cadenas originales escritas en ruso verá texto ruso en lugar de español.