Si ha trabajado con el sistema de traducción de Django durante algún tiempo, conoce el proceso. Marca cadenas con _(), ejecuta makemessages, abre un archivo .po y se encuentra con cientos de campos msgstr vacíos. Luego repite el proceso para cada idioma que su aplicación soporta.
El proceso funciona. Pero consume horas que podrían dedicarse a construir funcionalidades. Un proyecto con 200 cadenas traducibles en cinco idiomas significa 1.000 traducciones que gestionar. Cambie la etiqueta de un botón y necesitará actualizar los cinco archivos .po antes del próximo despliege, o publicar páginas parcialmente traducidas.
A continuación se recorre el flujo de trabajo manual, se analiza dónde falla y se cubren formas prácticas de automatizarlo sin perder el control sobre la calidad.
El flujo de trabajo manual
El pipeline de traduccion integrado de Django tiene cuatro pasos:
- Marcar cadenas con
gettexto{% trans %}en su código y plantillas - Ejecutar
makemessagespara extraerlas en archivos.po - Traducir las entradas
msgstren cada archivo.po - Ejecutar
compilemessagespara producir los archivos binarios.moque Django lee
Los pasos 1 y 2 son rápidos. El paso 4 es un solo comando. El paso 3 es donde todo se ralentiza.
Cómo se ve el paso 3 en la práctica
Abra locale/de/LC_MESSAGES/django.po en su editor. Verá algo como esto:
#: myapp/views.py:42
msgid "Your subscription has been renewed."
msgstr ""
#: myapp/templates/dashboard.html:18
msgid "Welcome back, %(username)s"
msgstr ""
#: myapp/forms.py:15
msgid "This field is required."
msgstr ""
Para cada entrada, necesita una traducción correcta que preserve cualquier variable de marcador de posición como %(username)s o {count}. Si rompe un marcador de posición, Django lanzará un error de formato en tiempo de ejecucion.
Con cinco idiomas y cien cadenas nuevas, tiene por delante una tarde entera de copiar y pegar entre servicios de traducción, corregir formatos de marcadores de posición y esperar no haber olvidado nada. Y en el próximo sprint, lo hace de nuevo.
Dónde falla el enfoque manual
Los proyectos pequeños pueden funcionar con traducción manual. Una vez que un proyecto cruza ciertos umbrales, el flujo de trabajo empieza a ser un problema.
Conteos crecientes de cadenas
Una aplicación Django con una docena de plantillas, algunos formularios y notificaciones por correo electrónico puede alcanzar fácilmente 300 cadenas traducibles. Multiplique por el numero de idiomas soportados y el total crece rápido. En algún momento, nadie quiere ser la persona que abre ese archivo .po.
Traducciones obsoletas
Cuando los desarrolladores cambian cadenas existentes, makemessages marca las traduccones antiguas como fuzzy. Estas entradas fuzzy necesitan revisión manual, y tienden a acumularse porque nadie las nota hasta que un usuario reporta ver inglés en una página en alemán. La lista de pendientes fuzzy crece silenciosamente entre versiones.
Errores de marcadores de posición
El formato .po de Django usa varios estilos de marcadores de posición: %(name)s para el formato % de Python, {name} para .format() y f-strings, y {{ name }} para variables de plantilla. Los traductores (humanos o automáticos) a veces los alteran, y los errores solo aparecen en tiempo de ejecución. Un %(username)s mal formado en una traducción al alemán puede hacer fallar una vista que funciona perfectamente en inglés.
Bloqueo de despliegues
Si las traducciones son un paso manual en su pipeline de despliegue, se convierten en un cuello de botella. O espera a tener las traducciones antes de desplegar (lento), o despliega con traducciones faltantes (mala experiencia de usuario). Ninguna opción es buena cuando publica semanalmente.
Cómo es la automatización
Automatizar las traducciones de Django significa reemplazar el paso 3 (la parte de traducción manual) con un proceso programatico manteniendo todo lo demás igual. Su flujo de trabajo con makemessages y compilemessages no cambia. Simplemente deja de traducir archivos .po a mano.
Opción 1: hágalo usted mismo
Puede escribir un script que lea un archivo .po, envíe las cadenas sin traducir a una API de traducción y escriba los resultados de vuelta. La versión básica es sorprendentemente corta:
import polib
from openai import OpenAI
client = OpenAI()
po = polib.POFile("locale/de/LC_MESSAGES/django.po")
for entry in po.untranslated_entries():
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "Translate to German. Preserve all "
"placeholder variables like %(name)s exactly as they appear."},
{"role": "user", "content": entry.msgid},
],
)
entry.msgstr = response.choices[0].message.content.strip()
po.save()
Esto le lleva al 80% del camino. El 20% restante es donde se complica:
- Las formas plurales (
msgid_pluraly múltiples entradasmsgstr[n]) necesitan manejo especial, porque diferentes idiomas tienen diferentes reglas de pluralización - Las cadenas de contexto (
msgctxt) deben pasarse a la API para que la traducción se ajuste al uso - Necesita validar que los marcadores de posición sobrevivieron la traducción intactos
- Los límites de tasa y errores de API necesitan lógica de reintento
- El script necesita manejar la codificación del archivo
.pocorrectamente (UTF-8 con campos de encabezado específicos) - Las entradas fuzzy necesitan un tratamiento diferente al de las entradas sin traducir
Construir todo esto por su cuenta es factible, pero requiere varios días de trabajo y mantenimeinto continuo. Cada vez que una API de traducción cambia su comportamiento o precios, necesita actualizar su script.
Opción 2: usar una plataforma de gestión de traducciones
Servicios como Crowdin, Transifex y Lokalise se integran con control de versiones y proporcionan una interfaz web para traductores. Manejan archivos .po de forma nativa, soportan formas plurales y pueden configurar flujos automatizados con traducción automática como primer paso.
La contrapartida es costo y complejidad. Estas plataformas están diseñadas para equipos con traductores dedicados, y los precios lo reflejan. Para un desarrollador individual o un equipo pequeño que solo necesita traducir archivos .po, agregan más infraestrucura de la necesaria.
Opción 3: herramientas específicas
Las herramientas construidas especificamente para el flujo de trabajo de archivos .po de Django pueden automatizar la traducción respetando las particularidades del formato. Manejan formas plurales, preservan marcadores de posición, gestionan entradas fuzzy y se integran con su pipeline existente de makemessages/compilemessages.
La ventaja es que están diseñadas exactamente para este caso de uso, así que hay menos código de integración que escribir y mantener. La desventaja es que agrega una dependencia a su flujo de trabajo de traducción.
Configuración de traducciones automatizadas en CI/CD
Independientemente de la herramienta que elija, la integración con su pipeline de despliegue sigue el mismo patrón:
# .github/workflows/translate.yml
name: Update translations
on:
push:
branches: [main]
jobs:
translate:
runs-on: ubuntu-latest
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 django
- name: Extract new strings
run: python manage.py makemessages -a --no-obsolete
- name: Translate .po files
run: python scripts/translate.py # your translation script
- name: Compile messages
run: python manage.py compilemessages
- name: Commit and push
run: |
git config user.name "github-actions"
git config user.email "actions@github.com"
git add locale/
git diff --staged --quiet || git commit -m "chore: update translations"
git push
El flujo extrae nuevas cadenas, las traduce, compila los resultados y hace commit de los archivos actualizados. La verificación git diff --staged --quiet evita crear commits vacíos cuando nada cambió.
Tambien puede ejecutar esto como un hook de pre-commit o un trabajo programado. La idea clave es la misma: la traducción ocurre automáticamente como parte de su proceso normal de desarrollo, no como un paso manual separado.
Validación de traducciones
Las traducciones automatizadas necesitan validación. Incluso los mejores modelos de IA ocasionalmente rompen formatos de marcadores de posición o producen traducciones que no se ajustan al contexto de la interfaz.
Validación de marcadores de posición
Escriba una verificación que compare los marcadores de posición entre msgid y msgstr:
import re
import polib
PLACEHOLDER_RE = re.compile(r"%\(\w+\)[sd]|\{\w+\}|\{\{.*?\}\}")
def validate_po(path):
po = polib.POFile(path)
errors = []
for entry in po.translated_entries():
src = set(PLACEHOLDER_RE.findall(entry.msgid))
dst = set(PLACEHOLDER_RE.findall(entry.msgstr))
if src != dst:
errors.append(f"Mismatch: {entry.msgid!r}")
return errors
Ejecute esto en CI y haga fallar el build si algún marcador de posición está roto. Toma segundos y detecta la clase más peligrosa de errores de traducción antes de que lleguen a producción.
Verificaciones de longitud
Algunas traducciones son significativamente más largas que la cadena original. El texto en alemán es típicamente 20-30% más largo que el inglés, y esto puede romper elementos de interfaz con ancho fijo. Una simple verificación de ratio de longitud puede señalar traducciones que podrían causar problemas de diseño.
Auditoría de entradas fuzzy
Después de la traducción automatizada, verifique que no queden entradas marcadas como fuzzy. Las entradas fuzzy significan que Django no usará la traducción, recurriendo al inglés en su lugar. Su pipeline de CI debería tratar las entradas fuzzy como una condición de fallo, de la misma forma que trata un test fallido.
Mantener las traducciones sincronizadas
La parte más difícil del mantenimiento de traducciones no es la traducción inicial. Es mantener las traducciones actualizadas a medida que su código evoluciona.
Cambios en cadenas
Cuando cambia una cadena traducible existente, makemessages hace algo sutil: busca una cadena similar en el archivo .po y crea una coincidencia fuzzy. La traducción antigua se preserva pero se marca como pendiente de revisión. Esto es útil cuando un traductor humano está revisando cambios, pero significa que los flujos automatizados necesitan manejar las entradas fuzzy de forma específica.
Un enfoque práctico: elimine la marca fuzzy y vuelva a traducir la entrada desde cero. La coincidencia fuzzy ahorra trabajo a los traductores humanos, pero una herramienta automatizada puede simplemente traducir la nueva cadena directamente. El resultado suele ser mejor que intentar parchear una traducción antigua para que se ajuste a una nueva cadena fuente.
Cadenas nuevas
Las cadenas nuevas aparecen como entradas sin traducir después de ejecutar makemessages. Su flujo automatizado debería traducirlas y hacer commit de los resultados. Si ejecuta makemessages en CI, las nuevas cadenas se traducen automáticamente al fusionar.
Cadenas eliminadas
Ejecutar makemessages --no-obsolete elimina las entradas de cadenas que ya no existen en su código. Esto mantiene los archivos .po limpios y evita acumular traducciones muertas con el tiempo.
Qué verificar antes de automatizar
Antes de configurar la traducción automatizada, verifique algunas cosas:
- Todas las cadenas traducibles están correctamente marcadas con
_()o{% trans %}. Ejecutemakemessagesy busque cadenas que haya omitido. - Su configuración de
LOCALE_PATHSes correcta y la estructura del directoriolocale/está en su lugar. compilemessagesse ejecuta sin errores en sus archivos.poexistentes.- Tiene una forma de revisar las traducciones automatizadas antes de que entren en producción, aunque sea solo un entorno de staging donde alguien pueda hacer clic por la aplicación en cada idioma.
La automatización de traducciones funciona mejor cuando el resto de su configuración de i18n es sólida. Automatizar el paso 3 no ayudará si los pasos 1 y 2 están rotos.
Consejos prácticos
Algunas cosas que he aprendido trabajando con traducciones automatizadas de Django:
- Ejecute
makemessagescon--no-obsoletepara mantener los archivos.polimpios. Las entradas muertas confunden tanto a revisores humanos como a herramientas automatizadas. - Use
msgctxtcuando la misma cadena en inglés necesite diferentes traducciones en diferentes contextos. "Post" puede significar una publicación de blog o la acción de publicar, y sin contexto el traductor (humano o automatizado) tiene que adivinar. - Pruebe con idiomas que sean estructuralmente diferentes del inglés. Las palabras más largas del alemán y el sistema de caracteres del japonés detectan errores de diseño que los idiomas europeos no captan.
- Mantenga un archivo
TRANSLATING.mdcon terminología específica del proyecto y guías de tono. Si su proyecto llama "proyecto" a un "workspace" en la interfaz, documéntelo. Algunas herramientas de traducción automatizada pueden leer este archivo y aplicar sus preferencias de forma consistente. - Haga commit de los archivos
.poy.mojuntos. Si compila mensajes en CI, asegúrese de que los archivos.moestén incluidos en su artefacto de despliegue. Django lee archivos.mo, no archivos.po.
Comparación de enfoques
Para un desarrollador individual o equipo pequeño con un proyecto Django que soporta 3-5 idiomas:
Un script personalizado funciona si disfruta manteniéndolo y su conteo de cadenas está por debajo de 200. Más allá de eso, los casos especiales con formas plurales, marcadores de posición y entradas fuzzy empiezan a consumir el tiempo que ahorró.
Las plataformas de gestión de traducciones tienen sentido si tiene un equipo de traductores trabajando en múltiples proyectos. Para una sola aplicación Django, generalmente son excesivas.
Las herramientas específicas cubren el punto medio: manejan las particularidades de los archivos .po sin requerir que construya y mantenga la integración usted mismo. El costo mensual suele ser menor que el tiempo de ingeniería invertido en una solución personalizada.
Elija el enfoque que se ajuste al tamaño de su equipo y su disposición para mantener infraestructura de traducción. Lo importante es que las traducciones dejen de ser un cuello de botella manual y pasen a ser parte de su ciclo normal de desarrollo.