Ingeniería//8 min de lectura

Controles de salud i18n: detecta las traducciones faltantes antes de que lleguen a producción

Eray Gündoğmuş
Compartir

Las traducciones faltantes son el tipo de error que se cuela por cada etapa de tu pipeline. Los tests unitarios pasan porque no comprueban los archivos de traducción. Los tests de integración pasan porque se ejecutan en el idioma por defecto. El QA pasa porque las pruebas manuales raramente cubren los doce idiomas. Y entonces un usuario en Brasil ve checkout.confirm_button donde debería aparecer una etiqueta de botón, y recibes un reporte de error que hace quedar a tu equipo como descuidado.

El problema no es que los equipos se olviden de las traducciones. Es que no existe ninguna comprobación automatizada que detecte las brechas de traducción del mismo modo que un verificador de tipos detecta errores de tipo o un linter detecta problemas de estilo de código. Tu código tiene ESLint, Prettier, TypeScript y un pipeline de CI completo. Tus traducciones tienen... un archivo JSON que alguien con suerte recordó actualizar.

Esta entrada cubre cómo implementar controles de salud i18n automatizados que detecten traducciones faltantes, discrepancias en marcadores de posición, claves huérfanas y cadenas de texto codificadas directamente antes de que lleguen a producción.


¿Qué comprueba realmente un control de salud i18n?

Un control de salud i18n exhaustivo evalúa cuatro dimensiones de tu configuración de traducciones:

1. Cobertura: ¿Están traducidas todas las claves?

La cobertura es la comprobación más directa y la de mayor impacto. Para cada clave de traducción utilizada en tu código fuente, ¿existe una traducción en cada idioma destino?

Referencias en el código fuente: 1.247 claves
Inglés (origen):      1.247/1.247 (100%)
Español:               1.235/1.247 (99%)
Francés:                1.198/1.247 (96%)
Japonés:              1.150/1.247 (92%)
Coreano:                1.089/1.247 (87%)

Un control de cobertura detecta el escenario más común: un desarrollador añade una nueva funcionalidad, escribe las cadenas en inglés y pasa a la siguiente tarea. Las claves van al archivo JSON en inglés pero nunca se envían para traducción. Sin un control de cobertura, la brecha es invisible hasta que un usuario la encuentra.

Los controles de cobertura también detectan discrepancias de namespace. Si tu código referencia t('checkout.confirm') pero el namespace checkout no existe para el coreano, eso es una brecha de cobertura que mostrará una clave en bruto a los usuarios de ese idioma.

2. Calidad: ¿Son las traducciones estructuralmente correctas?

La cobertura solo te dice si existe una traducción. La calidad te dice si funcionará correctamente en tiempo de ejecución.

La comprobación de calidad más crítica es la validación de marcadores de posición. Si tu cadena en inglés es:

"You have {count} items in your cart, {name}."

Entonces cada traducción de esa cadena debe contener exactamente {count} y {name}. Un traductor francés que escriba {nombre} en lugar de {count} crea un error en tiempo de ejecución: el motor de interpolación no encontrará un valor para {nombre} y mostrará el marcador en bruto o lanzará un error.

Otros controles de calidad incluyen:

  • Valores vacíos: Claves que existen en un archivo de idioma pero tienen cadenas vacías. Normalmente indican creación programática de claves sin traducción real.
  • Cadenas idénticas a la fuente: Traducciones que son carácter a carácter idénticas al idioma fuente. Algunas cadenas (nombres de marca, URLs) son legítimamente idénticas, pero un recuento elevado suele indicar contenido sin traducir.
  • Longitud excesiva: Traducciones significativamente más largas que la fuente, que pueden desbordar los contenedores de la interfaz. Las traducciones al alemán son notoriamente un 30-40% más largas que las del inglés.

3. Estructura: ¿Están limpios los archivos de traducción?

Los controles de estructura evalúan la organización e higiene de tus archivos de traducción:

  • Claves huérfanas: Claves presentes en archivos de traducción pero nunca referenciadas en el código fuente. Se acumulan cuando se eliminan funcionalidades pero no se limpian los archivos de traducción. Malgastan el esfuerzo de los traductores y generan confusión.
  • Claves duplicadas: La misma clave definida dos veces en un mismo archivo. JSON no lanza error ante claves duplicadas — utiliza silenciosamente la última, lo que puede dar lugar a comportamientos confusos.
  • Inconsistencia de nomenclatura: Si el 90% de tus claves usan snake_case pero algunas usan camelCase, la inconsistencia hace que las claves sean más difíciles de encontrar y mantener.

4. Código: ¿Están las cadenas correctamente internacionalizadas?

El análisis de código usa análisis de AST para encontrar cadenas de texto codificadas directamente en tus archivos fuente que deberían estar envueltas en funciones de traducción.

// Marcado: cadena de texto para el usuario codificada directamente
<h1>Welcome to our app</h1>

// No marcado: correctamente internacionalizado
<h1>{t('home.welcome_title')}</h1>

// No marcado: no orientado al usuario (clase CSS, atributo data)
<div className="container" data-testid="home">

Esta comprobación detecta la deuda i18n en el origen. Los desarrolladores nuevos que no están familiarizados con tu configuración i18n escriben cadenas codificadas directamente. Sin una comprobación automatizada, estas cadenas persisten hasta que alguien las detecta durante una auditoría de traducción.


La puntuación de salud: reducir la complejidad a un número

Las comprobaciones individuales producen informes detallados, pero para la integración con CI y el seguimiento de tendencias, necesitas un único número: la puntuación de salud.

Una puntuación de salud bien diseñada pondera las categorías según su impacto en el usuario:

CategoríaPesoJustificación
Cobertura40%Las traducciones faltantes afectan directamente a los usuarios
Calidad30%Los errores en marcadores de posición causan errores en tiempo de ejecución
Estructura20%Las claves huérfanas malgastan esfuerzo pero no rompen la UX
Código10%Las cadenas codificadas son deuda, no una ruptura inmediata

Un proyecto con una puntuación de 87/100 podría desglosarse así:

General: 87/100 APROBADO

Cobertura     92/100  ████████████████████  3 claves faltantes
Calidad      85/100  █████████████████░░░  2 discrepancias en marcadores de posición
Estructura    78/100  ███████████████░░░░░  12 claves huérfanas
Código         90/100  ██████████████████░░  4 cadenas codificadas directamente

El umbral de aprobado/reprobado es configurable. Un umbral de 80 es práctico para la mayoría de los equipos: suficientemente estricto para detectar problemas reales, lo bastante flexible para que las advertencias menores no bloqueen los despliegues.


Configurar controles de salud i18n en CI/CD

El valor real de los controles de salud viene de ejecutarlos automáticamente en cada pull request. Así se configura un flujo de trabajo con GitHub Actions:

# .github/workflows/i18n-doctor.yml
name: i18n Health Check

on:
  pull_request:
    paths:
      - "locales/**"
      - "src/**"
      - "messages/**"

jobs:
  doctor:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Bun
        uses: oven-sh/setup-bun@v2

      - name: Install dependencies
        run: bun install

      - name: Run i18n Doctor
        run: bunx @better-i18n/cli doctor --ci --threshold 80
        env:
          BETTER_I18N_API_KEY: ${{ secrets.BETTER_I18N_API_KEY }}

Qué hace el flag --ci

El flag --ci cambia el comportamiento de Doctor para entornos de CI:

  1. Código de salida: Devuelve el código de salida 1 si el análisis falla, haciendo que el job de GitHub Actions falle
  2. Anotaciones de GitHub: Genera los problemas en formato de anotación de GitHub Actions, para que aparezcan como comentarios en línea en el diff del PR
  3. Resumen: Produce un resumen estructurado para la salida de verificación de GitHub Actions
  4. No interactivo: Suprime las barras de progreso y la salida en color

Los filtros de ruta importan

El filtro paths en la configuración del flujo de trabajo es importante para el rendimiento. Sin él, el control de salud se ejecuta en cada PR, incluidos los que solo cambian documentación o código de backend sin impacto en las traducciones. Filtra hacia tus directorios de archivos de traducción y tus directorios de código fuente.

Reportar resultados a la plataforma

Añade el flag --report para enviar los resultados a tu plataforma de gestión de traducciones:

- name: Run i18n Doctor
  run: bunx @better-i18n/cli doctor --ci --report --threshold 80
  env:
    BETTER_I18N_API_KEY: ${{ secrets.BETTER_I18N_API_KEY }}

Los informes incluyen el SHA del commit, el nombre de la rama, el número de archivos y el número de claves. Con el tiempo, esto genera un historial de tu salud i18n que puedes usar para rastrear mejoras, detectar regresiones y establecer objetivos de equipo.

Flujos de trabajo generados automáticamente

Si configurar GitHub Actions manualmente parece una fricción innecesaria, algunas plataformas de traducción (incluida Better i18n) pueden crear el archivo de flujo de trabajo por ti. La plataforma usa la API de GitHub para abrir un PR en tu repositorio con un archivo de flujo de trabajo preconfigurado. Lo revisas, lo fusionas y el control de salud queda activo.


Qué ocurre cuando falla un control

Un control de salud que falla debe proporcionar información accionable, no solo una X roja. Así es como luce un fallo útil:

Claves de traducción faltantes

Error: 12 claves faltantes en idiomas destino

  checkout.confirm_order
    Faltante en: fr, de, ja, ko
    Añadida en el commit: abc1234 (hace 2 días)
    Archivo: src/pages/Checkout.tsx:45

  checkout.payment_method
    Faltante en: fr, de, ja, ko
    Añadida en el commit: abc1234 (hace 2 días)
    Archivo: src/pages/Checkout.tsx:52

El desarrollador ve exactamente qué claves faltan, en qué idiomas, cuándo se añadieron y dónde se usan. La solución es clara: solicitar traducciones para esas claves antes de fusionar.

Discrepancias en marcadores de posición

Error: Discrepancia de marcador de posición en notifications.new_messages (de)
  Fuente:  "You have {count} new messages from {sender}"
  Destino:  "Sie haben {anzahl} neue Nachrichten von {sender}"
  Faltante: {count}
  Extra:   {anzahl}

El desarrollador o el traductor ve la discrepancia exacta y puede corregir la traducción al alemán para usar {count} en lugar de {anzahl}.

Cadenas codificadas directamente

Advertencia: Cadena codificada directamente en JSX (src/components/Header.tsx:23)
  <h1>Welcome back!</h1>
  Sugerencia: <h1>{t('header.welcome_back')}</h1>

Esto es una advertencia, no un error — no bloqueará el PR por defecto. Pero aparece en el informe y contribuye a la puntuación de análisis de código.


Impacto en el mundo real: antes y después

Antes: el proceso manual

  1. El desarrollador añade una nueva funcionalidad con 30 claves nuevas
  2. El desarrollador añade las traducciones en inglés
  3. El desarrollador abre un PR, que se revisa y se fusiona
  4. Dos semanas después, el QA prueba la funcionalidad en francés — encuentra 30 claves en bruto
  5. El QA registra un reporte de error
  6. El desarrollador crea un ticket para las traducciones
  7. El traductor proporciona las traducciones al francés
  8. El desarrollador hace commit del archivo de traducción, abre un nuevo PR
  9. Repetir para alemán, japonés, coreano, etc.

Tiempo desde la fusión del código hasta la funcionalidad completamente traducida: 3-6 semanas.

Después: controles de salud automatizados

  1. El desarrollador añade una nueva funcionalidad con 30 claves nuevas
  2. El desarrollador añade las traducciones en inglés
  3. El desarrollador abre un PR
  4. CI ejecuta i18n Doctor — falla con "30 claves faltantes en fr, de, ja, ko"
  5. El desarrollador solicita traducciones a través de la plataforma
  6. Las traducciones llegan (generadas por IA en minutos, revisadas por humanos en horas)
  7. El desarrollador añade las traducciones al PR
  8. CI vuelve a ejecutarse — pasa
  9. El PR se fusiona con todos los idiomas completos

Tiempo desde la fusión del código hasta la funcionalidad completamente traducida: El mismo día.

La diferencia no es solo velocidad — se trata de detectar el problema en el lugar correcto. Un control de CI detecta las traducciones faltantes en el mismo PR donde se añadieron las claves, cuando el desarrollador todavía tiene el contexto completo sobre la funcionalidad. Un reporte de error tres semanas después requiere que el desarrollador cambie de contexto hacia una funcionalidad en la que ya ha seguido adelante.


Seguimiento de la salud a lo largo del tiempo

Una única puntuación de salud es útil para decidir si aprobar o rechazar. Un historial de puntuaciones de salud es útil para entender las tendencias.

Cuando envías informes de Doctor a un panel de la plataforma, puedes rastrear:

  • Trayectoria de la puntuación: ¿Tu salud i18n está mejorando, estable o deteriorándose?
  • Tendencias por categoría: Quizás tu cobertura es excelente pero las claves huérfanas se están acumulando. El desglose por categorías muestra dónde enfocar los esfuerzos de limpieza.
  • Comparación por rama: Las ramas de funcionalidades suelen tener puntuaciones más bajas (claves nuevas sin traducciones). La rama principal debería mantener una puntuación consistentemente alta.
  • Comparación entre proyectos: Para organizaciones con múltiples productos, compara la salud i18n entre proyectos para identificar cuáles necesitan atención.

Establecer objetivos de equipo

Las puntuaciones de salud hacen posible establecer objetivos i18n medibles:

  • "Mantener una puntuación de salud de 90+ en la rama principal" — un estándar de calidad
  • "Reducir las claves huérfanas de 200 a 50 antes de que acabe el trimestre" — una iniciativa de limpieza
  • "Cero discrepancias en marcadores de posición" — un objetivo de cero defectos para la comprobación más crítica

Objeciones comunes y respuestas

"Solo tenemos dos idiomas, no necesitamos esto." Dos idiomas son suficientes para que las brechas de cobertura y las discrepancias en marcadores de posición causen errores visibles para el usuario. El control de salud es ligero: añade segundos a tu pipeline de CI, no minutos.

"Nuestros traductores se encargan de la calidad." Los traductores garantizan la calidad lingüística. Los controles de salud garantizan la calidad técnica: corrección de marcadores de posición, cobertura de claves, estructura de archivos. Son cosas distintas. Un traductor no puede saber si una clave está referenciada en tu código fuente.

"Lo añadiremos más adelante cuando tengamos más idiomas." La deuda i18n se acumula. Las claves huérfanas, las cadenas codificadas directamente y la nomenclatura inconsistente que acumulas con dos idiomas se vuelven mucho más difíciles de corregir cuando añades un tercero, un cuarto y un quinto idioma. Empezar con controles de salud pronto es más barato que incorporarlos después.

"Nuestro CI ya es lento." Un análisis de Doctor en un proyecto de 10.000 claves con 8 idiomas tarda menos de 10 segundos. Usa --skip-code para eliminar el análisis de AST y reducirlo a menos de 3 segundos. El filtro de ruta en la configuración de GitHub Actions garantiza que el control solo se ejecute en PRs que afecten a archivos relacionados con traducciones.


Primeros pasos

Si usas Better i18n, el comando Doctor está integrado en la CLI:

# Instalar la CLI
bun add -g @better-i18n/cli

# Ejecutar tu primer control de salud
bi18n doctor

# Ejecutar en modo CI con informes
bi18n doctor --ci --report --threshold 80

Si no usas Better i18n, los principios de esta entrada se aplican a cualquier configuración de traducciones. Puedes construir comprobaciones similares con scripts personalizados que:

  1. Analicen tu código fuente en busca de referencias a claves de traducción
  2. Comparen las claves referenciadas con tus archivos de traducción
  3. Validen la consistencia de los marcadores de posición
  4. Generen los resultados en el formato de anotación de tu sistema de CI

Lo importante no es qué herramienta uses. Lo importante es que las traducciones faltantes dejen de ser una sorpresa que encuentran los usuarios y se conviertan en un control de CI que encuentran los desarrolladores.


Las traducciones faltantes son errores prevenibles. Empieza a detectarlos en CI — configura Better i18n Doctor y ejecuta tu primer control de salud hoy mismo.

Comments

Loading comments...