Blog
Feature
14 de febrero de 20269 min

i18n en solitario: 1993 claves, 6 idiomas, 9 pasadas de LLM

Puntos clave a recordar: Internacionalizar una aplicación móvil con 1993 claves de traducción en 6 idiomas es factible en solitario gracias a la automatización LLM. El script npm run translate detecta el delta, traduce solo las nuevas claves a través de OpenRouter y mantiene la sincronización casi en tiempo real por unos pocos céntimos por pasada. Resultado: un canal de adquisición multiplicado por 6 sin esfuerzo humano continuo.

TAMSIV nació en francés. Cada cadena, cada mensaje de error, cada marcador de posición, todo estaba codificado en el JSX. Cuando decidí soportar 6 idiomas (FR, EN, DE, ES, IT, PT), no me imaginaba que representaría 1993 claves de traducción repartidas en 35 archivos. Y mucho menos que necesitaría 9 pasadas sucesivas para llegar a un resultado estable.

Aquí tienes el relato completo de esta aventura i18n, desde el primer t('key') hasta el pipeline automatizado que funciona hoy.

Espacio de trabajo de desarrollador con varias pantallas mostrando archivos de traducción en diferentes idiomas
La internacionalización implica muchas pantallas y muchos archivos JSON abiertos en paralelo.

¿Por qué internacionalizar una aplicación indie en solitario?

La respuesta corta: la adquisición de usuarios. Una aplicación disponible solo en francés se priva del 95% del mercado mundial. Incluso en Europa, limitar tu audiencia al francés significa ignorar Alemania (83M de habitantes), España (47M), Italia (60M), Portugal (10M) y, por supuesto, todo el mundo anglófono.

Pero más allá del mercado, hay una razón técnica: las tiendas (Google Play, App Store) indexan los metadatos en cada idioma. Una ficha traducida a 6 idiomas significa 6 veces más superficie de descubrimiento orgánico. Es una palanca recomendada por el propio Google.

Para un desarrollador en solitario como yo, también es una ventaja competitiva frente a las grandes aplicaciones que descuidan la localización más allá del inglés. Si un usuario alemán busca "Aufgaben-App mit Spracheingabe" y TAMSIV es la única que tiene una ficha en alemán, ya está ganado.

¿Cómo extraer 1993 claves de traducción sin perder la cabeza?

Primer paso: reemplazar cada cadena francesa codificada por una llamada t('key'). En 35 archivos. A mano. No existe un atajo fiable para esta etapa: cada cadena tiene un contexto, y el nombre de la clave debe reflejar ese contexto.

He estructurado las claves por pantalla y por sección:

  • feed.empty_state — el mensaje cuando el feed está vacío
  • agenda.filter.participating — el filtro "Participa" en la agenda
  • profile.settings.language — el selector de idioma en la configuración
  • dictaphone.recording.title — el título de la pantalla de grabación
  • groups.hierarchy.depth_limit — el mensaje de límite de profundidad de los grupos

Esta convención pantalla.sección.elemento es crucial. Sin ella, uno se encuentra rápidamente con claves del tipo button_text_3 que no significan nada tres meses después. Seguí las recomendaciones de i18next sobre el nombramiento jerárquico.

Consejo práctico: empecé por las pantallas más visitadas (Dictáfono, Feed, Agenda) antes de abordar la configuración y las pantallas secundarias. Esto permite probar la traducción en los flujos principales rápidamente.

¿Qué herramienta de traducción automática elegir cuando estás solo?

¿Traducir 1993 claves manualmente a 5 idiomas? Eso son casi 10.000 traducciones individuales. Imposible para un desarrollador en solitario. Los servicios clásicos como Google Translate API o DeepL carecen de contexto de aplicación: "Memo vocal" se convertiría en "Vocal memo" en lugar de "Voice memo".

Escribí un script npm run translate que envía las claves francesas a OpenRouter (Gemini 2.5 Flash como modelo principal). El LLM comprende el contexto de la aplicación y produce traducciones naturales:

  • "Memo vocal" → "Voice memo" (EN), "Sprachnotiz" (DE), "Nota de voz" (ES)
  • "Ajouter une tache" → "Add a task", "Aufgabe hinzufugen", "Agregar una tarea"
  • "Tout voir" → "See all", "Alle anzeigen", "Ver todo"

El script envía las claves en lotes de 50, con el contexto de la aplicación en el prompt del sistema: "Estás traduciendo las cadenas de una aplicación móvil de gestión de tareas por voz con IA." Este contexto marca la diferencia en la calidad.

Mapa del mundo con pines de colores marcando los países cubiertos por la traducción
Seis idiomas, seis mercados europeos: la traducción como estrategia de adquisición.

¿Por qué 9 pasadas y no una sola?

La primera pasada tradujo la mayor parte del corpus, unas 1600 claves. Pero una aplicación viva evoluciona. Cada nueva característica añade claves:

  • Pasada 2: gamificación — 47 nuevas claves (niveles, insignias, rachas)
  • Pasada 3: agenda — 62 claves (eventos, filtros, recurrencia)
  • Pasada 4: grupos colaborativos — 89 claves (jerarquía, permisos, asignaciones)
  • Pasada 5-7: correcciones contextuales y ajustes después de los comentarios de los usuarios
  • Pasada 8: onboarding y pantallas de primer uso
  • Pasada 9: sitio web (tamsiv.com) — un segundo corpus de traducción separado

El script detecta las claves faltantes comparando fr.json (la referencia) con cada archivo de destino. Solo retraduce el delta, las claves nuevas o modificadas. Sin desperdicio de tokens, sin riesgo de sobrescribir una traducción ya validada.

¿Cómo funciona el pipeline de traducción en la práctica?

El pipeline completo sigue 4 etapas:

  1. Diff: el script compara fr.json con en.json, de.json, es.json, it.json, pt.json. Cada clave presente en FR pero ausente en un destino se marca como "a traducir".
  2. Lote: las claves a traducir se agrupan en paquetes de 50 (para mantenerse dentro de los límites de tokens del LLM).
  3. Traducción: cada lote se envía a OpenRouter con un prompt estructurado. El LLM devuelve un JSON válido con las traducciones.
  4. Fusión: las traducciones se fusionan en los archivos de destino, conservando el orden alfabético y la estructura existente.

¿El coste? Aproximadamente 0.02 a 0.05 EUR por pasada con Gemini 2.5 Flash a través de OpenRouter. Para 5 idiomas. Es casi gratuito en comparación con los servicios de traducción profesional (que cobran 0.10-0.20 EUR por palabra).

¿Cómo gestionar la detección automática de idioma en el primer lanzamiento?

En el primer lanzamiento, TAMSIV detecta el idioma del sistema a través de getLocales() de React Native. Si el idioma es compatible (FR, EN, DE, ES, IT, PT), se utiliza directamente. De lo contrario, se recurre al inglés.

La elección se guarda luego en la base de datos (Supabase) en el perfil del usuario. Esto permite recuperar la preferencia incluso al cambiar de teléfono. Y evita el problema clásico: el usuario cambia el idioma de su sistema operativo por una razón X, y todas sus aplicaciones cambian sin previo aviso.

También he añadido un selector de idioma en la configuración, para que el usuario pueda elegir independientemente del idioma del sistema. Es un detalle, pero marca la diferencia para los bilingües o los expatriados.

¿Cuáles son las trampas de la traducción automática por LLM?

La traducción por LLM no es perfecta. Aquí están las trampas que encontré:

  • Los falsos amigos: "Classeur" traducido como "Binder" en lugar de "Folder" — el LLM carecía de contexto de aplicación al principio.
  • El género gramatical: en alemán, "die Aufgabe" (femenino) vs "der Memo" (masculino) — los artículos deben coincidir.
  • Los plurales: algunos idiomas tienen reglas de plural complejas (portugués, alemán). Hay que proporcionar las formas singular/plural explícitamente.
  • La longitud: el alemán produce palabras un 30-50% más largas que el francés. "Einstellungen" vs "Parametres". Esto rompe los diseños si el diseño de la interfaz de usuario no es flexible.
  • Los caracteres especiales: las comillas varían según los idiomas (" " en inglés, « » en francés, „ “ en alemán).

La solución: un prompt de sistema detallado que especifique el dominio de la aplicación, ejemplos de traducciones validadas en few-shot, y una pasada de relectura automática donde el LLM verifica la coherencia de sus propias traducciones.

Cadena de producción automatizada simbolizando el pipeline de traducción continua
La automatización transforma un tedioso proceso de traducción en un pipeline casi instantáneo.

¿Cómo mantener la sincronización a largo plazo?

Lo más difícil no es la traducción inicial, sino el mantenimiento. Cada modificación en francés debe propagarse. Si cambio "Ajouter une tache" por "Creer une tache", los otros 5 idiomas deben seguir el mismo camino.

Mi sistema de sincronización:

  1. Detección de cambios: se almacena un hash SHA256 de cada valor FR. Si el hash cambia, la clave se marca para retraducción.
  2. CI automático: a través de GitHub Actions, cada vez que se hace un push a main que modifica fr.json, el script se ejecuta y crea un commit con las traducciones actualizadas.
  3. Verificación manual: reviso las diferencias de traducción en el PR para detectar errores obvios.

6 idiomas mantenidos casi en tiempo real por unos pocos céntimos por pasada. La relación esfuerzo/impacto es inmejorable.

¿Qué impacto concreto tiene en la adquisición de usuarios?

Los números hablan por sí solos. Después de la internacionalización:

  • Superficie de descubrimiento x6: la ficha de Play Store está indexada en 6 idiomas, lo que multiplica las consultas de búsqueda cubiertas.
  • Tasa de conversión en la tienda: un usuario que ve una descripción en su idioma tiene 2 a 3 veces más probabilidades de instalar.
  • Retención: la experiencia in-app en el idioma materno reduce drásticamente la rotación de los primeros 7 días.
  • SEO del sitio web: el sitio tamsiv.com está disponible en 6 idiomas con URLs localizadas (/fr/, /en/, /de/, etc.), lo que impulsa el referenciamiento internacional.

Para un desarrollador en solitario, es la palanca de adquisición con la mejor relación coste/eficacia. Sin presupuesto publicitario, sin growth hacking oscuro, solo traducción automatizada inteligente.

¿Cómo aplicar este enfoque a tu propio proyecto?

Si estás desarrollando una aplicación y dudas en internacionalizarla, aquí tienes mi consejo: hazlo pronto. Cuanto más esperes, más cadenas tendrás que extraer. Aquí están los pasos clave:

  1. Estructura tus claves desde el principio con una convención pantalla.sección.elemento.
  2. Elige tus idiomas objetivo en función de tu mercado. Para Europa, FR/EN/DE/ES/IT/PT cubren la mayoría.
  3. Automatiza la traducción con un LLM a través de API (OpenRouter, OpenAI, etc.). El coste es insignificante.
  4. Integra en tu CI para que la sincronización sea automática en cada push.
  5. Prueba con usuarios nativos reales si es posible; incluso un vistazo rápido es mejor que nada.

El recorrido build in public de TAMSIV demuestra que incluso en solitario, se puede alcanzar un nivel de localización comparable al de grandes equipos.

Preguntas Frecuentes

¿Cuánto tiempo lleva la internacionalización de una aplicación React Native?

Para TAMSIV, la extracción inicial de las 1993 claves tomó aproximadamente 3 días de trabajo concentrado. La implementación del script de traducción automática, medio día. Las pasadas siguientes toman unos pocos minutos cada una gracias a la automatización. La mayor parte del trabajo está en la extracción, no en la traducción.

¿Es OpenRouter fiable para la traducción automática?

Sí, con las precauciones adecuadas. El prompt del sistema debe incluir el contexto de la aplicación, ejemplos de traducciones validadas e instrucciones sobre el tono. Gemini 2.5 Flash a través de OpenRouter produce traducciones de mayor calidad que Google Translate para las cadenas de interfaz, porque comprende el contexto. El fallback entre modelos garantiza la disponibilidad.

¿Hay que traducir el contenido generado por el usuario?

No. Las tareas, notas y eventos permanecen en el idioma del usuario. Solo la interfaz (botones, etiquetas, mensajes del sistema, notificaciones) se traduce. Traducir el contenido del usuario sería costoso y fuente de confusión.

¿Cómo gestionar idiomas con alfabetos diferentes (árabe, japonés)?

TAMSIV se centra en los idiomas latinos por el momento. Los idiomas RTL (árabe, hebreo) o los ideogramas (chino, japonés) requieren adaptaciones de diseño adicionales (dirección del texto, fuentes, espaciado). Esto está previsto para una fase posterior, pero cada alfabeto es un proyecto en sí mismo.

¿Se puede reutilizar el script de traducción en otro proyecto?

Absolutamente. El script es genérico: toma un archivo JSON fuente, una lista de idiomas de destino y un endpoint LLM. Solo tienes que adaptar el prompt del sistema al contexto de tu aplicación. El patrón delta-only (traducir solo las claves nuevas) funciona para cualquier proyecto que utilice archivos JSON de traducción.