Internacionalización¶
Django avanzado
En desarrollo web, la internacionalización (i18n)1 es el proceso de preparar una aplicación o sitio web para que pueda adaptarse fácilmente a múltiples idiomas, formatos de fecha, monedas y otras diferencias culturales, sin necesidad de realizar cambios significativos en el código base.
Django ofrece mecanismos para facilitar la tarea de internacionalizar una aplicación web. Básicamente hay dos pasos en este proceso:
Marcado¶
El marcado es el proceso por el cual se le indica a Django qué partes de nuestro software deben ser traducidos. Se diferencian tres posibles contextos:
Marcado en plantillas¶
Podemos «marcar» para traducción cadenas de texto en plantillas. Para ello, Django nos ofrece la etiqueta de plantilla: translate.
Veamos un marcado concreto en el ejemplo del «blog» dentro de la plantilla que añade un «post»:
| posts/templates/posts/add.html | |
|---|---|
- Carga las funcionalidades de internacionalización
{% translate %} - Aplica la etiqueta de plantilla.
Marcado en vistas¶
Podemos «marcar» para traducción cadenas de texto en vistas. Para ello, Django nos ofrece la función: gettext().
Veamos un marcado concreto en el ejemplo del «blog» dentro de la vista que muestra el detalle de un «post»:
from django.http import HttpResponse
from django.shortcuts import render
from django.utils.translation import gettext as _#(1)!
from .models import Post
def post_detail(request, post_slug: str):
try:
post = Post.objects.get(slug=post_slug)
except Post.DoesNotExist:
msg = _('Post {ps} does not exist'.format(ps=post_slug))#(2)!
return HttpResponse(msg, status=404)
return render(request, 'posts/post/detail.html', {'post': post})
-
- Importamos la función de marcado/traducción.
- Aunque no es obligatorio, suele ser habitual definir el alias
_
-
- Pasamos la cadena de texto que queremos marcar para traducción.
- Es totalmente válido que la cadena de texto incluya variables a interpolar.
- Eso sí, es necesario usar el método
format()para que funcione bien el marcado.
Marcado en URLs¶
Podemos «marcar» para traducción cadenas de texto en URLs. Para ello, Django nos ofrece la función: gettext_lazy().
La función gettext_lazy() se diferencia de gettext() en que no traduce en el momento en el que el código se ejecuta. En este contexto utilizamos gettext_lazy() porque cuando se carga el módulo urls.py Django no sabe aún cuál es el idioma activo de cada petición.
Veamos un marcado concreto en el ejemplo del «blog» en la URL de edición de un «post»:
from django.urls import path
from django.utils.translation import gettext_lazy as _#(1)!
from . import views
app_name = 'posts'
urlpatterns = [
path('', views.post_list, name='post-list'),
path('add/', views.add_post, name='add-post'),
path('<slug:post_slug>/', views.post_detail, name='post-detail'),
path(_('<slug:post_slug>/edit/'), views.edit_post, name='edit-post'),#(2)!
]
-
- Importamos la función de marcado/traducción.
- Aunque no es obligatorio, suele ser habitual definir el alias
_
-
- El marcado es igual que lo que se ha visto hasta el momento.
- Es posible incluir en el marcado parámetros del patrón sin ningún problema.
Traducción¶
Una vez marcadas las cadenas de texto podemos pasar a la tarea propiamente de traducción. Pero para ello debemos crear los ficheros de idioma. El proceso es el siguiente:
flowchart LR
m[Marcado] --> mm{{makemessages}}
mm --> po([locale/django.po])
po --> t[Traducción]
t --> cm{{compilemessages}}
cm --> mo([locale/django.mo])
Supongamos por ejemplo que estamos desarrollando el «blog» en inglés pero queremos añadir traducción al español.
Crear ficheros de idioma¶
Django proporciona el comando makemessages para crear los ficheros de idioma. Pero antes de lanzarlo, debemos crear en todas las aplicaciones la subcarpeta locale.
justfile
Consulta la receta makelocale para incluirla en tu justfile.
Ahora ya podremos lanzar el comando que crea los ficheros de idioma:
-
- Se ha utilizado
esporque vamos a generar traducciones para español. Habría que adaptar el código según corresponda. - En el propio código fuente del proyecto Django se puede encontrar el listado con los códigos de idioma existentes.
- Se ha utilizado
-
- Se ha utilizado
esporque vamos a generar traducciones para español. Habría que adaptar el código según corresponda. - En el propio código fuente del proyecto Django se puede encontrar el listado con los códigos de idioma existentes.
- Se ha utilizado
justfile
Consulta la receta makemessages para incluirla en tu justfile.
El comando anterior creará un fichero posts/locale/es/LC_MESSAGES/django.po con aquellas cadenas marcadas para traducir. Ahora tendremos que abrir dicho fichero en algún editor de texto y completar las entradas msgstr con la traducción al español:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-30 18:01+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? "
"1 : 2;\n"
#: posts/templates/posts/post/add.html:8
msgid "Add post"
msgstr "Añadir post"
#: posts/urls.py:13
msgid "<slug:post_slug>/edit/"
msgstr "<slug:post_slug>/editar/"
#: posts/views.py:20
#, python-brace-format
msgid "Post {ps} does not exist"
msgstr "El post {ps} no existe"
Poedit
Una de las herramientas más conocidas para realizar traducciones mediante interfaz gráfica es poedit.
Modo de uso: poedit file.po
justfile
Consulta la receta poedit para incluirla en tu justfile.
Compilar ficheros de idioma¶
Con las traducciones completadas, ahora viene la fase de compilación de los ficheros de idioma. Para ello Django proporciona el comando compilemessages.
Ejecutamos el siguiente comando:
- Ignoramos la carpeta del entorno virtual
.venvya que también contiene marcas de traducción (para el propio sistema base Django).
- Ignoramos la carpeta del entorno virtual
.venvya que también contiene marcas de traducción (para el propio sistema base Django).
justfile
Consulta la receta compilemessages para incluirla en tu justfile.
Este comando creará un fichero posts/locale/es/LC_MESSAGES/django.mo con la versión compilada del fichero de idioma.
Cambio de idioma¶
Ahora que ya tenemos las traducciones completadas y los ficheros de idioma preparados, tendremos que encontrar el modo de permitir al usuario cambiar el idioma a conveniencia.
Lo primero será añadir un «middleware» que permita individualizar la elección de idioma. Para ello añadimos la siguiente línea al fichero settings.py:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.locale.LocaleMiddleware',
]
A continuación habrá que realizar unos pequeños cambios en las URLs de primer nivel:
Línea 9:
- Esta línea proporciona la URL con nombre
set_languageque apunta a/i18n/setlang/ - Dicha URL llama a la vista
django.views.i18n.set_language(). - Esta vista espera ser llamada vía POST.
- Almacena la elección de idioma actual en una «cookie» llamada
django_language.
Línea 12:
- Esta función hace que los patrones de URL pasados como argumento dispongan de un prefijo:
es/oen/según el idioma activo. - Entre otras, tendríamos
/en/posts/add/o/es/posts/add/que si aplicamos marcado y traducción de URLs, podría convertirse en/es/posts/agregar/
Etiqueta personalizada¶
Veamos ahora una posible implementación de una etiqueta personalizada para el cambio de idioma que hace uso de la vista set_language proporcionada por Django i18n:
{% load i18n %}<!--(1)!-->
<form action="{% url 'set_language' %}" method="post"><!--(2)!-->
{% csrf_token %}
<select name="language" onchange="this.form.submit()"><!--(3)!-->
{% for language in languages %}<!--(4)!-->
<option value="{{ language }}" {% if current_language == language %}selected{% endif %}><!--(5)!-->
{{ language|upper }}<!--(6)!-->
</option>
{% endfor %}
</select>
</form>
- Importamos las funcionalidades de internacionalización.
- Preparamos un formulario hacia la URL con nombre
set_languageproporcionada por Django. - Hacemos la petición cuando cambie el desplegable mediante un pequeño fragmento de JavaScript.
- Recorremos la lista de idiomas que nos vienen desde la etiqueta personalizada.
- Mostramos cada opción de idioma dejando seleccionado el idioma activo actualmente.
- Mostramos el idioma en el desplegable pasado a mayúsculas.
from django import template
from django.utils.translation import get_language
register = template.Library()
@register.inclusion_tag('includes/setlang.html')
def setlang():
LANGUAGES = ('en', 'es')#(1)!
current_language = get_language()#(2)!
return {'languages': LANGUAGES, 'current_language': current_language}#(3)!
- Definimos la lista de idiomas disponibles (mediante sus códigos).
- Obtenemos el
idiomacódigo de idioma activo actualmente. - Necesitamos enviar a la plantilal un contexto con los idiomas disponibles y el idioma activo.
{% load shared_extras %}<!--(1)!-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Blog</title>
</head>
<body>
{% setlang %}<!--(2)!-->
{% block content %}{% endblock %}
</body>
</html>
- Cargamos las etiquetas y/o filtros personalizados.
- Simplemente hacemos referencia a la plantilla personalizada de cambio de idioma que hemos implementado.
Con esto conseguimos que aparezca un desplegable de este estilo para seleccionar el idioma:

-
i18n es la abreviatura de «internacionalización»
[i (18 caracteres) n]↩