Si alguna vez has publicado una aplicación Django en más de un idioma, conoces el proceso. Envuelves tus cadenas en gettext(), ejecutas makemessages, abres un archivo .po con cientos de entradas y empiezas a traducir línea por línea. Para dos idiomas y cincuenta cadenas, es tolerable. Para seis idiomas y quinientas cadenas, es un día completo de trabajo que nunca recuperarás.
Esta guía cubre el pipeline de internacionalización (i18n) de Django de principio a fin, explica dónde falla y muestra cómo automatizar la parte más dolorosa con traducción impulsada por IA.
El flujo de trabajo estándar de Django i18n
El sistema i18n integrado de Django está bien diseñado. El ciclo principal se ve así:
Paso 1: Marca las cadenas para traducción en tu código Python y plantillas:
from django.utils.translation import gettext as _
def dashboard(request):
welcome = _("Welcome back, %(name)s!") % {"name": request.user.first_name}
return render(request, "dashboard.html", {"welcome": welcome})
{% load i18n %}
<h1>{% trans "Account Settings" %}</h1>
<p>{% blocktrans %}You have {{ count }} unread messages.{% endblocktrans %}</p>
Paso 2: Extrae las cadenas en archivos .po:
python manage.py makemessages -l de -l fr -l nl
Esto escanea todo tu código fuente y genera un archivo .po por idioma, conteniendo cada cadena traducible:
#: myapp/views.py:4
msgid "Welcome back, %(name)s!"
msgstr ""
#: templates/dashboard.html:2
msgid "Account Settings"
msgstr ""
Paso 3: Traduce cada msgstr vacío a mano.
Paso 4: Compila los archivos .po terminados en archivos binarios .mo:
python manage.py compilemessages
Los pasos 1, 2 y 4 son rápidos. El paso 3 es donde el proceso se desmorona.
Por qué la traducción manual no escala
Una aplicación Django típica tiene entre 200 y 2,000 cadenas traducibles. Multiplica eso por el número de idiomas objetivo y estarás ante un compromiso de tiempo considerable.
Esto no es una queja teórica. En un conocido hilo del Django Forum, un desarrollador reportó que dedicó más de 8 horas por archivo .po haciendo traducciones manuales. Un contribuidor principal de Django describió que pasó más de 10 horas incorporando traducciones enviadas por la comunidad en un solo lanzamiento, principalmente en revisión, correcciones de formato y arreglo de marcadores de posición rotos.
Los problemas se acumulan con el tiempo:
- Los marcadores de posición se rompen. Copia una cadena como
Welcome, %(name)s!en Google Translate y a menudo obtendrásWillkommen, %(Name)s!oBienvenue, %(nom)s!. Esa corrupción sutil causa fallos en tiempo de ejecución. - La consistencia se desvía. Sin un glosario, "dashboard" se traduce como "Instrumententafel" en un archivo y "Dashboard" en otro. Tres meses después, nadie recuerda cuál fue intencional.
- Los sprints añaden cadenas. Cada rama de funcionalidad añade nuevas cadenas traducibles. Aunque hayas pagado por una traducción completa el trimestre pasado, ahora tienes 40 entradas sin traducir dispersas en tus archivos
.po. - Los asistentes de IA ayudan una vez. Puedes pegar un archivo
.poen ChatGPT o Claude y obtener resultados decentes. Pero en el siguiente sprint, cuando aparecen 15 cadenas nuevas, estás creando prompts desde cero, retraduciendo todo el archivo y esperando que sea consistente con lo que ya existía.
La causa raíz es que la traducción se trata como un evento único en lugar de un proceso incremental y repetible.
Automatizando la traducción con IA
La idea es directa: en lugar de que un humano abra cada archivo .po y rellene los valores de msgstr, una herramienta lee el archivo, envía las cadenas sin traducir a un modelo de IA o API de traducción, escribe los resultados de vuelta y preserva todo lo demás (comentarios, estructura del archivo, marcadores de posición, formas plurales).
TranslateBot Django es un paquete de código abierto que hace exactamente esto. Se integra en el sistema de comandos de gestión de Django, por lo que encaja en el flujo de trabajo que ya tienes.
Configuración paso a paso
1. Instala el paquete
pip install translatebot-django
O, si usas uv (recomendado):
uv add --dev translatebot-django
Instalarlo como dependencia de desarrollo es intencional. Solo necesitas TranslateBot cuando generas traducciones, no en tiempo de ejecución en producción.
2. Añade a INSTALLED_APPS
# settings.py
INSTALLED_APPS = [
# ...
"translatebot_django",
]
3. Configura tu proveedor de IA
# settings.py
import os
TRANSLATEBOT_API_KEY = os.getenv("OPENAI_API_KEY")
TRANSLATEBOT_MODEL = "gpt-4o-mini"
TranslateBot usa LiteLLM internamente, lo que significa que puedes cambiar a cualquiera de más de 100 modelos modificando una sola cadena:
| Proveedor | Valor de TRANSLATEBOT_MODEL |
|---|---|
| OpenAI | gpt-4o-mini, gpt-4o |
| Anthropic | claude-sonnet-4-5-20250929 |
gemini/gemini-2.5-flash |
|
| Azure OpenAI | azure/gpt-4o-mini |
| DeepL | Usa TRANSLATEBOT_PROVIDER = "deepl" en su lugar |
Para DeepL, instala el extra: pip install translatebot-django[deepl]. El nivel gratuito de DeepL te da 500,000 caracteres por mes sin costo, lo cual es suficiente para la mayoría de proyectos pequeños a medianos.
4. Define tus idiomas
# settings.py
LANGUAGES = [
("en", "English"),
("de", "German"),
("fr", "French"),
("nl", "Dutch"),
("ja", "Japanese"),
]
5. Ejecuta la traducción
python manage.py translate
Eso es todo. TranslateBot escanea tu proyecto en busca de archivos .po, identifica las entradas sin traducir, las envía al modelo de IA configurado en lotes optimizados y escribe los resultados de vuelta. Las traducciones existentes quedan intactas.
Para traducir un solo idioma:
python manage.py translate --target-lang nl
La salida se ve así:
Translating to Dutch (nl)...
Found 42 strings to translate
Translating batch 1/2...
Translating batch 2/2...
Successfully translated 42 strings
6. Compila como siempre
python manage.py compilemessages
Tu flujo de trabajo completo ahora es:
python manage.py makemessages -l de -l fr -l nl -l ja
python manage.py translate
python manage.py compilemessages
Tres comandos. Todos los idiomas. Cada sprint.
Incremental por diseño
La característica más importante para un flujo de trabajo repetible es la traducción incremental. TranslateBot solo traduce las entradas donde msgstr está vacío. Si tienes 500 cadenas y 15 son nuevas este sprint, solo esas 15 se envían a la API.
Esto importa por razones prácticas:
- Costo. Solo pagas por las cadenas nuevas, no por el archivo completo.
- Velocidad. Traducir 15 cadenas toma segundos, no minutos.
- Estabilidad. Las traducciones que ya revisaste y aprobaste nunca se sobrescriben (a menos que pases explícitamente
--overwrite).
Seguridad de marcadores de posición
Django usa varios formatos de marcadores de posición: %(name)s, %s, %d, {0}, {name}, y etiquetas HTML en línea como <strong> o <a href="...">. Si alguno de estos se corrompe en la traducción, obtienes errores en tiempo de ejecución o markup roto.
TranslateBot instruye al modelo de IA para preservar todos los formatos de marcadores de posición y valida la salida. Una cadena como:
Welcome to %(site_name)s! You have <strong>%(count)d</strong> new messages.
Se traduce al neerlandés como:
Welkom bij %(site_name)s! Je hebt <strong>%(count)d</strong> nieuwe berichten.
Cada marcador de posición sobrevive intacto.
Controlando la calidad con TRANSLATING.md
Los modelos de IA traducen mejor cuando entienden el contexto. TranslateBot busca un archivo TRANSLATING.md en la raíz de tu proyecto e incluye su contenido en cada solicitud de traducción.
# Translation Context
## About This Project
A B2B project management tool for construction companies.
## Terminology
- "project" means a construction project, not a software project
- "plan" means a building plan/blueprint, not a subscription plan
- Keep "Gantt chart" as-is in all languages
## Tone
- German: use formal "Sie" form (business context)
- French: use formal "vous" form
- Dutch: use informal "je" form
## Do Not Translate
- Brand name: "BuildFlow"
- Feature names: "SmartSchedule", "CostTracker"
Este archivo tiene control de versiones junto con tu código, así que todo tu equipo comparte el mismo contexto de traducción. También puedes colocar archivos TRANSLATING.md por aplicación para apps con terminología especializada. Un módulo de registros médicos y un módulo de facturación pueden tener cada uno su propio glosario.
Vista previa antes de confirmar
La bandera --dry-run muestra exactamente qué se traduciría sin hacer llamadas a la API ni modificar archivos:
python manage.py translate --target-lang fr --dry-run
Found 15 untranslated entries
Dry run mode: skipping LLM translation
Would translate 'Welcome to our site'
Would translate 'Hello, %(name)s!'
...
Dry run complete: 15 entries would be translated
Esto es útil antes de una ejecución de traducción grande o cuando un nuevo miembro del equipo quiere entender qué hace el comando antes de comprometerse con los costos de la API.
Integración CI/CD
Que las traducciones se queden obsoletas es inevitable sin aplicación. TranslateBot incluye un comando de gestión check_translations diseñado para pipelines de CI:
# .github/workflows/ci.yml
jobs:
translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
with:
enable-cache: true
- run: uv python install
- run: uv sync --frozen
- name: Install gettext
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends gettext
- name: Check translations
run: uv run python manage.py check_translations --makemessages
La bandera --makemessages ejecuta primero makemessages -a --no-obsolete, asegurando que los archivos .po reflejen el código fuente actual antes de verificar. Si alguna entrada está sin traducir o es difusa, el comando sale con código 1 y falla la compilación:
locale/de/LC_MESSAGES/django.po: 2 untranslated, 0 fuzzy
locale/nl/LC_MESSAGES/django.po: 0 untranslated, 1 fuzzy
CommandError: Translation check failed
El flujo de trabajo típico del desarrollador se convierte en:
- Añadir nuevas cadenas traducibles en una rama de funcionalidad.
- CI falla porque esas cadenas están sin traducir.
- Ejecutar
python manage.py translatelocalmente. - Confirmar los archivos
.poactualizados. - CI pasa.
Las traducciones nunca se dessincronizan silenciosamente.
Traduciendo contenido de base de datos
Si tu aplicación almacena contenido traducible en la base de datos (nombres de productos, títulos de publicaciones de blog, etiquetas de categorías), TranslateBot también se integra con django-modeltranslation:
pip install translatebot-django[modeltranslation]
# Translate all registered model fields
python manage.py translate --target-lang de --models
# Translate specific models only
python manage.py translate --target-lang de --models Product Category
La misma lógica incremental aplica: solo se traducen los campos donde el valor del idioma objetivo está vacío.
Comparación de costos
Una de las preguntas más comunes es si la traducción con IA es rentable en comparación con las alternativas. Aquí tienes una comparación aproximada para un proyecto con 500 cadenas traducibles en 5 idiomas:
| Enfoque | Costo estimado | Inversión de tiempo |
|---|---|---|
| Manual (tiempo de desarrollador) | $0 de bolsillo, 20-40+ horas | Muy alto |
| Servicio de traducción profesional | $500-2,000+ | Bajo (pero entrega lenta) |
| Plataforma de localización SaaS | $50-200/mes | Medio |
| TranslateBot + GPT-4o-mini | ~$0.05 (una vez) | Minutos |
| TranslateBot + DeepL Free | $0 (hasta 500k caracteres/mes) | Minutos |
| TranslateBot + Claude/GPT-4o | ~$0.30 (una vez) | Minutos |
Los números varían según la cantidad de cadenas e idiomas objetivo, pero la diferencia en orden de magnitud es consistente. Para el mantenimiento continuo (traducir las 20-50 cadenas nuevas añadidas cada sprint), el costo de IA es esencialmente cero.
Mejores prácticas
Comienza con --dry-run. Antes de tu primera ejecución de traducción real, previsualiza qué sucederá. Esto genera confianza y detecta problemas de configuración tempranamente.
Confirma los archivos .po antes de traducir. Si algo sale mal, git checkout te devuelve a un estado limpio instantáneamente.
Escribe un TRANSLATING.md desde el primer día. Incluso un archivo breve con la descripción de tu proyecto y algunas reglas de terminología mejora mediblemente la calidad de traducción.
Añade check_translations a CI. Este único paso previene el modo de fallo i18n más común: cadenas que fueron marcadas para traducción pero nunca fueron realmente traducidas.
Usa gpt-4o-mini o DeepL para eficiencia de costos. Reserva modelos premium como GPT-4o o Claude para proyectos donde la precisión importa, como textos de marketing, texto legal o terminología específica del dominio.
Revisa las cadenas críticas. Las traducciones de IA son lo suficientemente buenas para la mayoría del texto de UI, pero haz que un hablante nativo revise cualquier cosa legalmente vinculante, crítica para la seguridad o dirigida al cliente en un contexto de alto riesgo.
De horas a segundos
El framework i18n de Django es excelente para extraer y compilar traducciones. La brecha siempre ha estado en el paso de traducción mismo: el trabajo tedioso y propenso a errores de rellenar cientos de valores msgstr en múltiples idiomas.
TranslateBot cierra esa brecha. Instálalo, apúntalo a un proveedor de IA y ejecuta un comando. Las cadenas nuevas se traducen. Las cadenas existentes quedan intactas. Los marcadores de posición permanecen intactos. CI detecta cualquier cosa que se escape.
Tus archivos .po dejan de ser una tarea tediosa y pasan a ser simplemente otra parte del proceso de compilación.
pip install translatebot-django
Comienza en translatebot.dev.