Volver al blog

Cómo automatizar el flujo de trabajo de traducción en Django

2026-07-11 10 min de lectura

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:

  1. Marcar cadenas con gettext o {% trans %} en su código y plantillas
  2. Ejecutar makemessages para extraerlas en archivos .po
  3. Traducir las entradas msgstr en cada archivo .po
  4. Ejecutar compilemessages para producir los archivos binarios .mo que 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_plural y múltiples entradas msgstr[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 .po correctamente (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 %}. Ejecute makemessages y busque cadenas que haya omitido.
  • Su configuración de LOCALE_PATHS es correcta y la estructura del directorio locale/ está en su lugar.
  • compilemessages se ejecuta sin errores en sus archivos .po existentes.
  • 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 makemessages con --no-obsolete para mantener los archivos .po limpios. Las entradas muertas confunden tanto a revisores humanos como a herramientas automatizadas.
  • Use msgctxt cuando 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.md con 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 .po y .mo juntos. Si compila mensajes en CI, asegúrese de que los archivos .mo esté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.

Deja de editar archivos .po manualmente

TranslateBot automatiza las traducciones de Django con IA. Un comando, todos tus idiomas, centavos por traducción.