Índice
La gestión de traducciones parece sencilla hasta que intentas mantener tres sistemas sincronizados: un repositorio Git donde los desarrolladores escriben código, una base de datos donde trabajan los traductores y una CDN desde donde tu aplicación obtiene las traducciones en tiempo de ejecución. Cambia una clave en un sistema y los otros dos necesitan saberlo — de manera confiable, rápida y sin pérdida de datos.
Este es el problema que construimos el sync engine para resolver. En este artículo, recorreremos la arquitectura, los tipos de mensajes, el sistema de detección de conflictos y las garantías de confiabilidad que hacen que todo funcione.
El Problema con los Flujos de Trabajo de Traducción Síncronos
En las primeras etapas del desarrollo de Better i18n, las sincronizaciones de traducción eran síncronas. Un desarrollador hacía push de código, nuestro manejador de webhooks procesaba los cambios en línea, actualizaba la base de datos, regeneraba los archivos CDN y devolvía una respuesta. Funcionaba — hasta que dejó de funcionar.
Los modos de fallo eran predecibles:
- Timeouts. Un repositorio con 5.000 claves tarda tiempo en generar el diff. Los webhooks de GitHub tienen un timeout de 10 segundos. Las sincronizaciones fallaban silenciosamente en proyectos grandes.
- Actualizaciones parciales. Si la carga en CDN fallaba después de que la base de datos se actualizaba, las traducciones quedaban desincronizadas. Los usuarios verían contenido desactualizado hasta que alguien activara manualmente una re-sincronización.
- Sin visibilidad. Cuando una sincronización fallaba, no había registro de lo que había ocurrido. Depurar requería leer logs del servidor y correlacionar marcas de tiempo.
Necesitábamos una arquitectura que desacoplara el disparador del trabajo, proporcionara reintentos automáticos y brindara visibilidad completa de cada operación.
Entra Cloudflare Queues
Elegimos Cloudflare Queues como la columna vertebral del sync engine. Queues proporciona entrega de mensajes durable y ordenada con semántica de al-menos-una-vez — exactamente lo que necesitábamos.
La arquitectura es directa:
GitHub Webhook → API Handler → Queue (encolar mensaje) → Worker (procesar mensaje)
↓
Activity Log + Database + CDN
El API handler hace un trabajo mínimo: valida el webhook, encola un mensaje REPO_PUSH_SYNC y devuelve un 200. El procesamiento real ocurre de forma asíncrona en el consumidor de la cola — un Cloudflare Worker que recoge los mensajes y los ejecuta.
Esta separación tiene tres beneficios inmediatos:
- Las respuestas de webhook son rápidas. No más timeouts, incluso para repositorios masivos.
- Los fallos se reintentan automáticamente. Si el worker falla o una llamada a API falla, el mensaje se reenvía con backoff exponencial.
- Las operaciones son observables. Cada mensaje produce un activity log estructurado.
10 Tipos de Mensajes, Un Consumidor
El sync engine procesa 10 tipos de mensajes distintos, cada uno con su propio manejador:
Operaciones de sincronización:
SYNC_START— Sincronización completa o incremental con GitHub. Obtiene archivos, compara claves, actualiza la base de datos y opcionalmente genera un pull request con nuevas traducciones.REPO_PUSH_SYNC— Ruta optimizada para eventos de webhook push. Solo procesa los archivos que cambiaron en el push, haciendo que las sincronizaciones incrementales sean casi instantáneas.
Operaciones CDN:
CDN_SETUP— Crea el manifest inicial y los archivos de idioma vacíos cuando un proyecto conecta su CDN.CDN_UPLOAD— Escribe un único archivo JSON de traducción en el almacenamiento R2.CDN_MERGE— Fusiona nuevas traducciones en un archivo CDN existente. Esto es crítico para publicaciones parciales — quieres agregar nuevas traducciones sin eliminar las que no cambiaron.CDN_CLEANUP— Elimina todos los archivos R2 de un proyecto. Se usa durante la eliminación del proyecto o cuando un usuario quiere empezar de cero.
Operaciones de IA:
AI_CONTEXT_ANALYSIS— Usa Firecrawl para hacer scraping del sitio web del proyecto, luego alimenta el contenido a Gemini para construir un modelo de contexto de traducción. Este contexto ayuda a la traducción automática a comprender terminología específica del sector.REPO_ANALYSIS— Escanea el repositorio de GitHub para detectar el framework (React, Next.js, Flutter, etc.), extraer traducciones existentes y construir un glosario de terminología.
Publicación:
PUBLISH_BATCH— El último paso en el flujo de trabajo de traducción. Toma las traducciones aprobadas y las envía tanto a la CDN (para disponibilidad inmediata) como a GitHub (para control de versiones). Esta es una operación atómica — si cualquiera de las escrituras falla, toda la publicación se reintenta.
Glosario:
GLOSSARY_SYNC— Sincroniza glosarios de terminología con DeepL. Cuando defines que "workspace" siempre debe traducirse como "espace de travail" en francés, este mensaje asegura que el glosario de DeepL se actualice para que todas las futuras traducciones automáticas sean consistentes.
Cada tipo de mensaje está aislado. Un fallo en CDN_UPLOAD no bloquea SYNC_START. Un AI_CONTEXT_ANALYSIS lento no retrasa PUBLISH_BATCH. Este aislamiento es clave para la confiabilidad del engine.
El Sistema de Jobs
Los mensajes son de bajo nivel. Los jobs son los flujos de trabajo de alto nivel con los que interactúan los usuarios y el sistema. El sync engine admite 12 tipos de jobs:
| Tipo de Job | Disparador | Mensajes Producidos |
|---|---|---|
| initial_import | Configuración del proyecto | SYNC_START, CDN_SETUP |
| incremental_sync | Webhook push | REPO_PUSH_SYNC, CDN_MERGE |
| full_sync | Disparador manual | SYNC_START, CDN_UPLOAD (por idioma) |
| source_sync | Cambio de idioma fuente | SYNC_START |
| bulk_translate | Solicitud de traducción masiva | Múltiples CDN_UPLOAD |
| publish | Publicación de un idioma | PUBLISH_BATCH, CDN_UPLOAD |
| batch_publish | Publicación multiidioma | Múltiples PUBLISH_BATCH |
| cdn_upload | Escritura directa en CDN | CDN_UPLOAD |
| cdn_merge | Actualización parcial CDN | CDN_MERGE |
| cdn_setup | Inicialización CDN | CDN_SETUP |
| cdn_cleanup | Limpieza del proyecto | CDN_CLEANUP |
| glossary_sync | Actualización de glosario | GLOSSARY_SYNC |
Un solo job puede producir múltiples mensajes. Por ejemplo, un job full_sync en un proyecto con 8 idiomas producirá 1 mensaje SYNC_START seguido de 8 mensajes CDN_UPLOAD — uno por cada archivo de idioma. El job rastrea el estado agregado a través de todos sus mensajes.
Más de 45 Acciones de Actividad: Observabilidad Estructurada
Cada manejador de mensajes registra acciones de actividad estructuradas a medida que avanza. Estas no son líneas de log en texto libre — son eventos tipados y estructurados que alimentan tanto la experiencia de depuración como la interfaz de usuario en tiempo real.
Un flujo típico de SYNC_START produce esta traza de actividad:
SYNC_STARTED → FETCH_FILES (obteniendo archivos de traducción de GitHub) → FILES_FETCHED (12 archivos encontrados) → COMPARE_KEYS (diff contra la base de datos) → KEYS_ADDED (47 nuevas claves) → KEYS_REMOVED (3 claves obsoletas) → KEYS_UPDATED (12 valores modificados) → UPDATE_DATABASE (persistiendo cambios) → PR_GENERATION_STARTED (creando PR de traducción) → PR_CREATED (PR #142 abierto) → SYNC_COMPLETED (duración: 3.2s)
Con más de 45 tipos de acciones distintas, obtienes visibilidad granular de cada operación. Cuando algo falla, la última acción registrada te dice exactamente dónde se detuvo el pipeline y qué datos ya fueron procesados.
Estas acciones de actividad también alimentan la interfaz de historial de sincronización. Tu equipo puede ver cada sincronización que se ha ejecutado, qué hizo, cuánto tardó y si tuvo éxito — sin tocar los logs del servidor.
Detección y Resolución de Conflictos
Los conflictos son el problema más difícil en cualquier sistema de sincronización. Dos personas editan la misma clave de traducción — una en el código, otra en la interfaz de traducción. ¿Quién gana?
Nuestra respuesta: nadie gana automáticamente. El sync engine detecta conflictos y los expone para resolución humana.
Detección
Durante COMPARE_KEYS, el engine verifica cada clave entrante contra la base de datos. Si una clave ha sido modificada tanto en el repositorio como en la base de datos desde la última sincronización exitosa, se marca como conflicto. El engine almacena ambos valores junto con sus marcas de tiempo de modificación.
Resolución
Los conflictos aparecen en el dashboard con contexto completo:
- El valor fuente (del repositorio)
- El valor de la base de datos (de la interfaz de traducción)
- El último valor sincronizado (el ancestro común)
- Marcas de tiempo para cada modificación
Los usuarios pueden resolver conflictos uno por uno o en masa, eligiendo conservar el valor fuente, conservar el valor de la base de datos o escribir una fusión manual. Cada resolución se registra como una acción de actividad.
Este enfoque previene el escenario más común de pérdida de datos en los flujos de trabajo de traducción: un push de código de un desarrollador sobrescribiendo silenciosamente el trabajo cuidadosamente revisado de un traductor.
Garantías de Confiabilidad
El sync engine está diseñado en torno a cuatro principios de confiabilidad:
Entrega al-menos-una-vez. Cloudflare Queues garantiza que cada mensaje se entrega al menos una vez. Los mensajes sobreviven a reinicios del worker, despliegues y fallos de infraestructura.
Manejadores idempotentes. Dado que los mensajes pueden entregarse más de una vez, cada manejador es idempotente. Re-procesar un CDN_UPLOAD con el mismo contenido produce el mismo resultado. Re-procesar un SYNC_START compara contra el estado actual de la base de datos, por lo que las sincronizaciones duplicadas son efectivamente no-ops.
Procesamiento ordenado. Los mensajes para el mismo proyecto se procesan en orden. Un CDN_MERGE siempre se ejecuta después del SYNC_START que lo produjo. Esto previene condiciones de carrera donde un archivo CDN se actualiza antes de que la base de datos refleje las nuevas claves.
Reintentos automáticos con backoff. Los mensajes fallidos se reintentan con backoff exponencial. Los errores transitorios — límites de tasa de API, interrupciones de red, indisponibilidad temporal de R2 — se resuelven solos sin intervención humana. Los errores permanentes (datos inválidos, permisos faltantes) se registran y se exponen en el dashboard.
Qué Significa Esto para Tu Equipo
El sync engine corre en segundo plano. Conectas tu repositorio de GitHub y las sincronizaciones simplemente funcionan. Haz push de código y tus traducciones se actualizan en segundos. Aprueba traducciones y se publican en tu CDN y se confirman en tu repositorio de forma atómica.
Cuando algo sale mal — y en sistemas distribuidos, siempre sale algo mal — el engine reintenta, registra y expone el problema. Sin fallos silenciosos. Sin estado inconsistente. Sin traducciones perdidas.
Esa es la promesa del procesamiento async bien hecho: tu equipo se enfoca en las traducciones, y la infraestructura se encarga del resto.