Índice
Django i18n con Traducción por IA: Guía Completa de Configuración
Django incluye un framework de internacionalización (i18n) maduro construido sobre GNU gettext. Cuando combinas esa base con traducción impulsada por IA, obtienes un flujo de trabajo que escala desde un proyecto individual hasta una aplicación de producción con múltiples locales sin ahogarte en trabajo de traducción manual.
Esta guía te lleva a través de todo el proceso: configurar Django para i18n, marcar cadenas, gestionar archivos .po, automatizar la traducción con IA y conectar todo en un pipeline CI/CD.
Puntos Clave
- El sistema i18n de Django usa GNU gettext — las cadenas se marcan en Python y plantillas, se extraen a archivos
.poy se compilan a archivos binarios.mopara su uso en tiempo de ejecución. - La traducción por IA puede automatizar la traducción de archivos
.po— las API de traducción automática procesan entradas sin traducir en lote, reduciendo días de trabajo manual a minutos. - Un paso de revisión de calidad es esencial — las traducciones generadas por IA deben ser revisadas por hablantes nativos antes de la implementación en producción, especialmente para contenido orientado al usuario.
- La integración CI/CD cierra el ciclo — los pipelines automatizados pueden extraer nuevas cadenas, traducirlas, compilar archivos de mensajes e implementar sin intervención manual.
- Better i18n proporciona un flujo de trabajo gestionado — en lugar de construir scripts personalizados, puedes sincronizar archivos
.po, gestionar traducciones y automatizar todo el ciclo de vida desde una sola plataforma.
¿Qué es Django i18n?
Django i18n es el framework de internacionalización integrado de Django que te permite traducir tu aplicación web a múltiples idiomas. Envuelve GNU gettext para proporcionar extracción de cadenas, gestión de archivos de traducción y cambio de idioma en tiempo de ejecución — todo integrado en las plantillas, formularios y enrutamiento URL de Django.
Django separa la internacionalización (i18n) de la localización (l10n). La internacionalización es el proceso de preparar tu código para soportar múltiples idiomas. La localización es el proceso de traducir realmente el contenido y adaptar los formatos para un locale específico. Django maneja ambos a través de su módulo django.utils.translation y la cadena de herramientas gettext.
El framework soporta la traducción de:
- Cadenas Python en vistas, modelos y formularios
- Contenido de plantillas usando etiquetas de plantilla integradas
- Patrones URL para enrutamiento con prefijo de locale
- Formato de fechas, horas, números y monedas mediante
django.utils.formats
Para la referencia oficial, consulta la documentación de internacionalización de Django.
Configurar Django para Internacionalización
Antes de marcar cadenas para traducción, debes configurar los ajustes de Django e instalar el middleware requerido.
Configurar Ajustes
Abre tu settings.py y establece lo siguiente:
# settings.py
from django.utils.translation import gettext_lazy as _
# Idioma predeterminado para la aplicación
LANGUAGE_CODE = "en"
# Habilitar el framework de internacionalización
USE_I18N = True
# Habilitar el formato localizado de fechas, números, etc.
USE_L10N = True
# Habilitar datetimes con conocimiento de zona horaria
USE_TZ = True
# Idiomas que soporta tu aplicación
LANGUAGES = [
("en", _("English")),
("es", _("Spanish")),
("fr", _("French")),
("de", _("German")),
("ja", _("Japanese")),
]
# Dónde Django busca los archivos de traducción
LOCALE_PATHS = [
BASE_DIR / "locale",
]
También necesitas LocaleMiddleware en tu stack de middleware. Debe venir después de SessionMiddleware y antes de CommonMiddleware:
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware", # Debe estar aquí
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
Crea la estructura de directorios locale:
mkdir -p locale/{es,fr,de,ja}/LC_MESSAGES
Marcar Cadenas para Traducción
Django proporciona dos funciones principales para marcar cadenas traducibles:
gettext()(aliasado como_()) — traduce la cadena inmediatamente en tiempo de ejecucióngettext_lazy()(aliasado como_()) — retrasa la traducción hasta que se renderiza la cadena, lo que es necesario para código a nivel de módulo como campos de modelo y etiquetas de formulario
En vistas (usar gettext):
from django.utils.translation import gettext as _
def dashboard_view(request):
welcome_message = _("Welcome to your dashboard")
context = {
"title": _("Dashboard"),
"welcome": welcome_message,
}
return render(request, "dashboard.html", context)
En modelos (usar gettext_lazy):
from django.db import models
from django.utils.translation import gettext_lazy as _
class Article(models.Model):
title = models.CharField(
max_length=200,
verbose_name=_("Title"),
)
body = models.TextField(
verbose_name=_("Body"),
help_text=_("The main content of the article."),
)
created_at = models.DateTimeField(
auto_now_add=True,
verbose_name=_("Created at"),
)
class Meta:
verbose_name = _("Article")
verbose_name_plural = _("Articles")
def __str__(self):
return self.title
La distinción importa: gettext_lazy devuelve un proxy de cadena perezosa que se resuelve al idioma correcto cuando la cadena se muestra realmente. Las definiciones de modelo se evalúan una vez en el momento de la importación, por lo que usar gettext allí fijaría el idioma que estaba activo durante la importación.
Etiquetas de Traducción en Plantillas
Las plantillas de Django usan {% load i18n %} para acceder a las etiquetas de traducción:
Traducción simple de cadenas con {% trans %}:
{% load i18n %}
<h1>{% trans "Welcome to our site" %}</h1>
<p>{% trans "This content will be translated." %}</p>
Traducción de bloque con {% blocktrans %} para cadenas que contienen variables:
{% load i18n %}
{% blocktrans with username=user.username %}
Hello, {{ username }}! You have new notifications.
{% endblocktrans %}
Pluralización:
{% load i18n %}
{% blocktrans count counter=item_count %}
You have {{ counter }} item in your cart.
{% plural %}
You have {{ counter }} items in your cart.
{% endblocktrans %}
Establecer el contexto de idioma:
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
<html lang="{{ LANGUAGE_CODE }}">
Internacionalización de URL
La función i18n_patterns de Django añade el código de idioma activo como prefijo a tus patrones URL:
# urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path, include
urlpatterns = [
# URLs no localizadas (admin, API, etc.)
path("api/", include("api.urls")),
]
urlpatterns += i18n_patterns(
path("", include("pages.urls")),
path("blog/", include("blog.urls")),
path("accounts/", include("accounts.urls")),
prefix_default_language=True,
)
Con esta configuración, tus URLs se convierten en:
/en/blog/— Blog en inglés/es/blog/— Blog en español/fr/blog/— Blog en francés
También deberías incluir la vista de cambio de idioma integrada de Django:
from django.conf.urls.i18n import i18n_patterns
from django.urls import path, include
urlpatterns = [
path("i18n/", include("django.conf.urls.i18n")),
]
urlpatterns += i18n_patterns(
# ... tus patrones
)
Esto habilita la etiqueta de plantilla {% url 'set_language' %} que permite a los usuarios cambiar de idioma mediante un POST de formulario.
Trabajar con Archivos .po y .mo
El sistema de traducción de Django se basa en el formato de archivo de GNU gettext. Comprender este pipeline es esencial antes de automatizarlo.
Cómo funciona gettext en Django
El flujo de trabajo sigue tres pasos:
- Extraer —
makemessagesescanea tus archivos Python y plantillas en busca de cadenas traducibles y las escribe en archivos.po(Portable Object). - Traducir — Los traductores (o IA) rellenan el campo
msgstrpara cadamsgiden los archivos.po. - Compilar —
compilemessagesconvierte los archivos.poen archivos binarios.mo(Machine Object) que Django lee en tiempo de ejecución para búsquedas rápidas.
Extraer Mensajes
Ejecuta el comando de extracción desde la raíz de tu proyecto:
# Extraer mensajes para todos los idiomas configurados python manage.py makemessages --all --no-obsolete # Extraer para un idioma específico python manage.py makemessages -l es # Incluir cadenas JavaScript (para el catálogo JS i18n de Django) python manage.py makemessages -d djangojs --all
La bandera --no-obsolete elimina entradas para cadenas que ya no existen en tu código, manteniendo limpios tus archivos .po.
La Estructura del Archivo .po
Después de la extracción, cada locale obtiene un archivo .po:
locale/
├── es/
│ └── LC_MESSAGES/
│ └── django.po
├── fr/
│ └── LC_MESSAGES/
│ └── django.po
└── de/
└── LC_MESSAGES/
└── django.po
Una entrada de archivo .po tiene este aspecto:
#: templates/dashboard.html:5 msgid "Welcome to your dashboard" msgstr "" #: myapp/models.py:12 msgid "Article" msgstr "" #. Translators: This is a button label #: templates/base.html:42 msgid "Submit" msgstr ""
Cada entrada tiene:
- Comentarios
#:— referencias de archivo fuente y línea msgid— la cadena original (en tu idioma fuente)msgstr— la cadena traducida (vacía hasta que se traduce)- Comentarios
#.— notas para traductores (añadidas mediante comentariosTranslators:en tu código)
Compilar Mensajes
Una vez completadas las traducciones, compílalas:
python manage.py compilemessages
Esto crea archivos .mo junto a los archivos .po. Django carga estos archivos binarios al inicio para búsquedas rápidas de traducción. Debes reiniciar tu servidor Django (o procesos worker) después de compilar nuevas traducciones.
Añadir Traducción Impulsada por IA a Django
La traducción manual de archivos .po es precisa pero lenta. La traducción impulsada por IA puede procesar cientos de entradas en segundos, dándote un primer borrador funcional que los revisores humanos pueden refinar.
Analizar y Traducir Archivos .po con Python
La librería polib proporciona una API limpia para leer y escribir archivos .po programáticamente:
pip install polib openai
Aquí hay un script que traduce entradas sin traducir en un archivo .po usando una API de traducción por IA:
# scripts/translate_po.py
import sys
import polib
from openai import OpenAI
client = OpenAI() # Usa la variable de entorno OPENAI_API_KEY
TARGET_LANGUAGES = {
"es": "Spanish",
"fr": "French",
"de": "German",
"ja": "Japanese",
}
def translate_text(text: str, target_language: str) -> str:
"""Traduce una sola cadena usando un modelo de IA."""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": (
f"You are a professional translator. Translate the following "
f"text to {target_language}. Preserve any Python format strings "
f"like %(name)s or {{variable}} exactly as they are. "
f"Return only the translated text, nothing else."
),
},
{"role": "user", "content": text},
],
temperature=0.3,
)
return response.choices[0].message.content.strip()
def translate_po_file(po_path: str, lang_code: str) -> int:
"""Traduce todas las entradas sin traducir en un archivo .po."""
language_name = TARGET_LANGUAGES.get(lang_code)
if not language_name:
print(f"Código de idioma no soportado: {lang_code}")
return 0
po = polib.pofile(po_path)
untranslated = po.untranslated_entries()
if not untranslated:
print(f"No hay entradas sin traducir en {po_path}")
return 0
print(f"Traduciendo {len(untranslated)} entradas a {language_name}...")
translated_count = 0
for entry in untranslated:
try:
entry.msgstr = translate_text(entry.msgid, language_name)
entry.flags.append("fuzzy") # Marcar como que necesita revisión
translated_count += 1
except Exception as e:
print(f" Error al traducir '{entry.msgid[:50]}...': {e}")
po.save()
print(f"Traducidas {translated_count} entradas en {po_path}")
return translated_count
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Uso: python translate_po.py <ruta/django.po> <código_idioma>")
sys.exit(1)
translate_po_file(sys.argv[1], sys.argv[2])
Ejecútalo:
python scripts/translate_po.py locale/es/LC_MESSAGES/django.po es python scripts/translate_po.py locale/fr/LC_MESSAGES/django.po fr
Traducción por Lotes en Todos los Locales
Para proyectos más grandes, un script de lotes procesa cada locale a la vez:
# scripts/translate_all.py
from pathlib import Path
from translate_po import translate_po_file, TARGET_LANGUAGES
LOCALE_DIR = Path("locale")
def translate_all_locales():
"""Traduce entradas sin traducir para todos los locales configurados."""
total = 0
for lang_code in TARGET_LANGUAGES:
po_path = LOCALE_DIR / lang_code / "LC_MESSAGES" / "django.po"
if po_path.exists():
count = translate_po_file(str(po_path), lang_code)
total += count
else:
print(f"No se encontró archivo .po en {po_path}")
print(f"\nTotal de traducciones: {total}")
if __name__ == "__main__":
translate_all_locales()
Flujo de Trabajo de Revisión de Calidad
Las entradas traducidas por IA se marcan con la bandera fuzzy, que indica a Django (y a los revisores humanos) que la traducción necesita verificación. Esto es intencional e importante:
- IA traduce — todos los valores
msgstrsin traducir se rellenan y se marcan comofuzzy - Los revisores verifican — los hablantes nativos comprueban las entradas fuzzy y eliminan la bandera una vez aprobadas
- Compilar — por defecto, solo las entradas no fuzzy se incluyen en los archivos
.mocompilados
Para revisar entradas fuzzy:
# Contar entradas fuzzy por locale
for lang in es fr de ja; do
count=$(grep -c "^#, fuzzy" locale/$lang/LC_MESSAGES/django.po 2>/dev/null || echo 0)
echo "$lang: $count entradas fuzzy"
done
También puedes usar editores de archivos .po como Poedit o plataformas web para el paso de revisión.
Pipeline CI/CD para Localización de Django
Automatizar el ciclo extraer-traducir-compilar en tu pipeline CI/CD garantiza que las traducciones se mantengan sincronizadas con tu código.
Flujo de Trabajo de GitHub Actions
Aquí hay un flujo de trabajo de GitHub Actions que se ejecuta en cada push a main:
# .github/workflows/i18n.yml
name: i18n Translation Pipeline
on:
push:
branches: [main]
paths:
- "**.py"
- "**.html"
- "locale/**"
jobs:
translate:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install polib openai
- name: Install gettext
run: sudo apt-get install -y gettext
- name: Extract messages
run: python manage.py makemessages --all --no-obsolete
- name: AI-translate new strings
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: python scripts/translate_all.py
- name: Compile messages
run: python manage.py compilemessages
- name: Commit translation updates
run: |
git config user.name "i18n-bot"
git config user.email "i18n-bot@users.noreply.github.com"
git add locale/
git diff --staged --quiet || git commit -m "chore(i18n): update translations"
git push
Desglose del Pipeline
El pipeline sigue cuatro etapas:
- Extraer —
makemessagesescanea todos los archivos Python y plantillas en busca de cadenas traducibles nuevas o modificadas - Traducir — el script de traducción por IA rellena cualquier entrada sin traducir y las marca como
fuzzy - Compilar —
compilemessagesgenera los archivos binarios.mo - Commit — los cambios se envían de vuelta al repositorio para que estén disponibles en el próximo despliegue
Para aplicaciones críticas de producción, añade una puerta de aprobación manual entre los pasos de traducir y compilar. Esto da a tu equipo una ventana para revisar las traducciones fuzzy antes de que se publiquen.
Pre-commit Hook para Desarrollo Local
También puedes añadir un pre-commit hook para detectar traducciones faltantes durante el desarrollo:
#!/bin/bash
# .git/hooks/pre-commit
# Comprobar cadenas sin traducir
python manage.py makemessages --all --no-obsolete 2>/dev/null
if git diff --name-only locale/ | grep -q ".po$"; then
echo "Advertencia: Se detectaron nuevas cadenas traducibles."
echo "Ejecuta 'python scripts/translate_all.py' para traducirlas."
fi
Cómo Better i18n se Integra con Django
Los scripts anteriores funcionan, pero requieren que mantengas código de traducción personalizado, gestiones claves API, manejes límites de velocidad y construyas flujos de trabajo de revisión desde cero. Better i18n proporciona una plataforma gestionada que maneja todo el ciclo de vida de traducción de Django.
Así es como Better i18n encaja en un proyecto Django:
1. Sincronizar archivos .po con Better i18n
En lugar de escribir scripts de análisis personalizados, puedes sincronizar tus archivos .po directamente con Better i18n. La plataforma lee el formato gettext de forma nativa, mapeando cada par msgid/msgstr a claves de traducción.
2. Traducción por IA con flujo de trabajo de revisión
Better i18n proporciona traducción por IA integrada específicamente ajustada para la localización de software. Las traducciones pasan por un flujo de trabajo de revisión gestionado donde los miembros del equipo pueden aprobar, editar o rechazar sugerencias — sin necesidad de herramientas personalizadas.
3. Publicar y obtener traducciones
Una vez aprobadas las traducciones, puedes obtenerlas de vuelta en tu proyecto Django como archivos .po actualizados. El paso de publicación asegura que solo las traducciones revisadas lleguen a tu base de código.
4. Integración CI/CD
El CLI de Better i18n puede reemplazar los scripts personalizados en tu pipeline CI. El ciclo sync-translate-pull se convierte en un único comando en tu flujo de trabajo de GitHub Actions.
Para un análisis más profundo de las capacidades i18n de Django, consulta nuestra guía del framework Django i18n. Si estás evaluando herramientas de traducción para tu flujo de trabajo, nuestra guía de herramientas de traducción por IA cubre el panorama actual.
FAQ
¿Cuál es la diferencia entre i18n y l10n en Django?
La internacionalización (i18n) es el proceso de hacer que tu aplicación Django sea traducible — marcar cadenas, configurar middleware, configurar patrones URL. La localización (l10n) es el proceso de proporcionar realmente traducciones y formato específico del locale para un idioma y región particular. En Django, USE_I18N = True habilita el framework de traducción, mientras que USE_L10N = True habilita el formato localizado de fechas, números y calendarios.
¿Cómo manejo la pluralización en Django?
Django maneja la pluralización a través de la función ngettext() en código Python y la etiqueta de plantilla {% blocktrans count %}. Gettext soporta reglas de plural complejas — a diferencia del inglés que tiene dos formas (singular/plural), idiomas como el árabe tienen seis formas plurales y el polaco tiene tres. La integración gettext de Django maneja todas estas a través de las definiciones de forma plural en el encabezado del archivo .po.
Ejemplo en Python:
from django.utils.translation import ngettext
def item_count_message(count):
return ngettext(
"You have %(count)d item.",
"You have %(count)d items.",
count,
) % {"count": count}
Ejemplo en plantilla:
{% load i18n %}
{% blocktrans count counter=notifications %}
You have {{ counter }} new notification.
{% plural %}
You have {{ counter }} new notifications.
{% endblocktrans %}
¿Puedo usar IA para traducir archivos .po de Django?
Sí. Los modelos de traducción por IA pueden analizar archivos .po y traducir entradas msgstr en lote. El enfoque recomendado es usar una librería como polib para leer archivos .po programáticamente, enviar cadenas sin traducir a una API de traducción, escribir los resultados de vuelta con una bandera fuzzy y luego hacer que hablantes nativos revisen el resultado. Herramientas como Better i18n automatizan todo este pipeline, incluido el flujo de trabajo de revisión, para que no necesites mantener scripts personalizados. La consideración clave es siempre marcar las traducciones de IA para revisión humana antes de desplegarlas en producción.