Arquitectura de internacionalización para apps creadas por chat: define claves estables, reglas de plural y un flujo de traducción que sea consistente en web y móvil.

Lo primero que se rompe no es el código. Son las palabras.
Las apps creadas por chat suelen empezar como prototipos rápidos: escribes “Añadir un botón que diga Guardar”, la interfaz aparece y sigues con lo siguiente. Semanas después quieres español y alemán, y descubres que esas etiquetas “temporales” están esparcidas por pantallas, componentes, correos y mensajes de error.
Los cambios de copy también son más frecuentes que los cambios de código. Los nombres de producto se renombran, el texto legal cambia, el onboarding se reescribe y soporte pide mensajes de error más claros. Si el texto vive directamente dentro del código UI, cada pequeña modificación de redacción se convierte en un lanzamiento arriesgado y perderás lugares donde la misma idea se expresa de forma distinta.
Aquí están los primeros síntomas que indican que estás acumulando deuda de traducción:
Un ejemplo realista: construyes un CRM en Koder.ai. La app web muestra “Deal stage”, la app móvil “Pipeline step” y un toast de error pone “Invalid status”. Incluso si los tres están traducidos, los usuarios sentirán que la app es inconsistente porque los conceptos no coinciden.
“Consistente” no significa “los mismos caracteres en todas partes”. Significa:
Una vez que tratas el texto como datos de producto, no como decoración, añadir idiomas deja de ser una carrera y se vuelve una rutina de desarrollo.
Internationalization (i18n) es el trabajo para que una app pueda soportar muchos idiomas sin reescrituras. Localization (l10n) es el contenido concreto para un idioma y región, por ejemplo francés (Canadá) con las palabras, formatos de fecha y tono correctos.
Un objetivo simple: cada texto dirigido al usuario se selecciona mediante una clave estable, no se escribe directamente en el código UI. Si puedes cambiar una frase sin abrir un componente React o un widget Flutter, vas por buen camino. Esto es el núcleo de una arquitectura de internacionalización para apps creadas por chat, donde es fácil publicar copy codificado generado durante una sesión de chat.
El texto para el usuario es más amplio de lo que muchos equipos piensan. Incluye botones, etiquetas, errores de validación, estados vacíos, consejos de onboarding, notificaciones push, correos, exportes PDF y cualquier mensaje que un usuario pueda ver o escuchar. Normalmente no incluye logs internos, nombres de columnas de base de datos, IDs de eventos de analítica, feature flags ni salidas de depuración solo para admins.
¿Dónde deben vivir las traducciones? En la práctica, suele estar en frontend y backend, con una frontera clara.
El error a evitar es mezclar responsabilidades. Si el backend devuelve frases en inglés ya escritas para errores UI, el frontend no podrá localizarlas limpiamente. Un patrón mejor es: el backend devuelve un código de error (y quizá parámetros seguros), y el cliente mapea ese código a un mensaje localizado.
La propiedad del copy es una decisión de producto, no un detalle técnico. Decide desde temprano quién puede cambiar palabras y aprobar el tono.
Si producto es dueño del copy, trata las traducciones como contenido: versiona, revisa y da a producto una forma segura de solicitar cambios. Si ingeniería es dueña del copy, establece la regla de que cualquier cadena UI nueva debe venir con una clave y una traducción por defecto antes de poder lanzarse.
Ejemplo: si tu flujo de signup dice “Create account” en tres pantallas diferentes, haz que sea una sola clave usada en todas partes. Eso mantiene el significado consistente, acelera a los traductores y evita que pequeños cambios de redacción se conviertan en una limpieza de múltiples pantallas.
Las claves son el contrato entre tu UI y tus traducciones. Si ese contrato cambia constantemente, tendrás textos faltantes, arreglos rápidos y redacción inconsistente entre web y móvil. Una buena arquitectura de internacionalización para apps creadas por chat empieza con una regla: las claves deben describir el significado, no la frase actual en inglés.
Usa IDs estables como claves (por ejemplo billing.invoice.payNow) en lugar del texto completo (como "Pay now"). Las claves basadas en frases se rompen cuando alguien ajusta la redacción, añade puntuación o cambia mayúsculas.
Un patrón práctico y legible es: pantalla (o dominio) + componente + intención. Manténlo aburrido y predecible.
Ejemplos:
auth.login.titleauth.login.emailLabelbilling.checkout.payButtonnav.settingserrors.network.offlineDecide cuándo reutilizar una clave versus crear una nueva preguntando: “¿El significado es idéntico en cada lugar?” Reutiliza claves para acciones verdaderamente genéricas, pero separa claves cuando el contexto cambia. Por ejemplo, “Save” en la pantalla de perfil puede ser una acción simple, mientras que “Save” en un editor complejo puede necesitar un tono más específico en algunos idiomas.
Mantén el texto UI compartido en namespaces dedicados para que no se duplique entre pantallas. Cubos comunes que funcionan bien:
common.actions.* (save, cancel, delete)common.status.* (loading, success)common.fields.* (search, password)errors.* (validation, network)nav.* (tabs, menu items)Cuando la redacción cambia pero el significado se mantiene, conserva la clave y solo actualiza los valores traducidos. Ese es el sentido de los IDs estables. Si el significado cambia (incluso sutilmente), crea una nueva clave y deja la antigua hasta confirmar que ya no se usa. Esto evita desajustes “silenciosos” donde una traducción antigua sigue presente pero ahora es incorrecta.
Un pequeño ejemplo del estilo Koder.ai: tu chat genera tanto una app web en React como una app móvil en Flutter. Si ambos usan common.actions.save, obtendrás traducciones consistentes. Pero si web usa profile.save y móvil account.saveButton, con el tiempo divergirán, aunque hoy el inglés parezca igual.
Trata tu idioma fuente (a menudo inglés) como la única fuente de verdad. Manténlo en un solo lugar, revísalo como código y evita que las cadenas aparezcan en componentes al azar “por ahora”. Esta es la forma más rápida de evitar copy codificado y retrabajo posterior.
Una regla simple ayuda: la app solo puede mostrar texto desde el sistema i18n. Si alguien necesita copy nuevo, añade una clave y un mensaje por defecto primero, luego usa esa clave en la UI. Esto mantiene tu arquitectura de internacionalización estable incluso cuando las features se mueven.
Si entregas web y móvil, quieres un catálogo compartido de claves, más espacio para que los equipos trabajen sin pisarse. Un layout práctico:
Mantén las claves idénticas entre plataformas, aunque la implementación difiera (React en web, Flutter en móvil). Si usas una plataforma como Koder.ai para generar ambas apps desde chat, exportar código fuente es más fácil cuando ambos proyectos apuntan a los mismos nombres de clave y al mismo formato de mensaje.
Las traducciones cambian con el tiempo. Trata los cambios como cambios de producto: pequeños, revisados y trazables. Una buena revisión se enfoca en el significado y la reutilización, no solo en la ortografía.
Para que las claves no se desvíen entre equipos, haz que las claves sean propiedad de features (billing., auth.) y nunca renombres claves solo porque la redacción cambió. Actualiza el mensaje, conserva la clave. Las claves son identificadores, no copy.
Las reglas de plural cambian según el idioma, así que el patrón simple inglés (1 vs todo lo demás) falla rápido. Algunos idiomas tienen formas separadas para 0, 1, 2-4, y otros cambian toda la oración, no solo el sustantivo. Si integras lógica plural en la UI con if-else, terminarás duplicando copy y perdiendo casos límite.
Un enfoque más seguro es mantener un mensaje flexible por idea y dejar que la capa i18n elija la forma correcta. Los mensajes estilo ICU están hechos para esto. Mantienen las decisiones gramaticales en la traducción, no en tus componentes.
Aquí hay un ejemplo pequeño que cubre casos que la gente olvida:
itemsCount = \"{count, plural, =0 {No items} one {# item} other {# items}}\"
Esa única clave cubre 0, 1 y todo lo demás. Los traductores pueden reemplazarla con las formas plurales adecuadas para su idioma sin que toques código.
Cuando necesitas redacción basada en género o rol, evita crear claves separadas como welcome_male y welcome_female a menos que el producto lo requiera de verdad. Usa select para que la oración sea una sola unidad:
welcomeUser = \"{gender, select, female {Welcome, Ms. {name}} male {Welcome, Mr. {name}} other {Welcome, {name}}}\"
Para evitar problemas con casos gramaticales, mantén las oraciones lo más completas posible. No ensamblas fragmentos como "{count} " + t('items') porque muchos idiomas no pueden reordenar palabras así. Prefiere un solo mensaje que incluya el número, el sustantivo y las palabras alrededor.
Una regla simple que funciona bien en apps creadas por chat (incluyendo proyectos Koder.ai) es: si una oración contiene un número, una persona o un estado, hazla ICU desde el día uno. Cuesta un poco más al principio y ahorra mucha deuda de traducción después.
Si tu app React web y tu app Flutter móvil mantienen archivos de traducción por separado, acabarán divergendo. El mismo botón termina con redacciones distintas, una clave significa una cosa en web y otra en móvil, y los tickets de soporte empiezan a decir “la app dice X pero la web dice Y”.
La solución más simple e importante: elige un formato y una fuente de verdad y trátalos como código. Para la mayoría de equipos eso significa un único conjunto compartido de archivos de locale (por ejemplo, JSON con mensajes estilo ICU) que consumen tanto web como móvil. Cuando generas apps por chat y con generadores, esto importa aún más porque es fácil crear texto nuevo en dos sitios por accidente.
Un setup práctico es un pequeño “paquete i18n” o carpeta que contiene:
React y Flutter pasan a ser consumidores. No deberían inventar claves nuevas localmente. En un flujo estilo Koder.ai (React web, Flutter móvil), puedes generar ambos clientes desde el mismo conjunto de claves y mantener cambios bajo revisión como cualquier otro cambio de código.
La alineación con backend es parte de la misma historia. Errores, notificaciones y correos no deberían estar escritos a mano en inglés en Go. En su lugar, devuelve códigos de error estables (como auth.invalid_password) más parámetros seguros. Entonces los clientes mapean códigos a texto traducido. Para correos enviados por el servidor, el servidor puede renderizar plantillas usando las mismas claves y archivos de locale.
Crea un pequeño manual y aplícalo en las revisiones de código:
Para evitar claves duplicadas con significados distintos, añade un campo “descripción” (o un archivo de comentarios) para traductores y tu yo futuro. Ejemplo: billing.trial_days_left debe explicar si se muestra en un banner, un correo o ambos. Esa frase suele detener el uso “casi adecuado” que crea deuda de traducción.
Esta consistencia es la columna vertebral de una arquitectura de internacionalización para apps creadas por chat: un vocabulario compartido, muchas superficies y sin sorpresas al lanzar el siguiente idioma.
Una buena arquitectura de internacionalización para apps creadas por chat empieza simple: un conjunto de claves, una fuente de la verdad para el copy y las mismas reglas en web y móvil. Si trabajas rápido (por ejemplo, con Koder.ai), esta estructura mantiene la velocidad sin generar deuda de traducción.
Elige tus locales desde temprano y decide qué ocurre cuando falta una traducción. Una opción común: mostrar el idioma preferido del usuario cuando esté disponible, si no, usar inglés como fallback y registrar las claves faltantes para corregirlas antes del próximo release.
Luego implementa esto:
billing.plan_name.pro o auth.error.invalid_password. Mantén las mismas claves en todas partes.t("key") en componentes. En Flutter, usa un wrapper de localización y llama la misma búsqueda por clave en widgets. La meta son las mismas claves, no la misma librería.if (count === 1) por toda la app.Finalmente, prueba un idioma con palabras más largas (alemán es clásico) y otro con puntuación distinta. Esto revela rápido botones que desbordan, títulos que se rompen mal y layouts que asumen inglés.
Si mantienes las traducciones en una carpeta compartida (o paquete generado) y tratas los cambios de copy como cambios de código, tus apps web y móviles seguirán siendo consistentes incluso cuando las features se construyan rápido en chat.
Las cadenas traducidas son solo la mitad del problema. La mayoría de apps muestran valores cambiantes como fechas, precios, cantidades y nombres. Si tratas esos valores como texto plano obtendrás formatos raros, zonas horarias equivocadas y oraciones que suenan “extrañas” en muchos idiomas.
Empieza por formatear números, moneda y fechas con reglas del locale, no con código personalizado. Un usuario en Francia espera “1 234,50 €”, mientras que uno en EE. UU. espera “$1,234.50”. Lo mismo aplica a fechas: “03/04/2026” es ambiguo; el formateo por locale lo deja claro.
Las zonas horarias son otra trampa. Los servidores deben almacenar timestamps en una forma neutral (normalmente UTC), pero los usuarios esperan ver la hora en su zona. Por ejemplo: un pedido creado a las 23:30 UTC puede ser “mañana” para alguien en Tokio. Decide una regla por pantalla: muestra hora del usuario para eventos personales y una zona de negocio fija para ventanas como recogidas (y etiquétala claramente).
Evita construir oraciones concatenando fragmentos traducidos. Rompe la gramática porque el orden de palabras cambia por idioma. En lugar de:
"{count} " + t("items") + " " + t("in_cart")
usa un mensaje único con placeholders, por ejemplo: “{count} items in your cart”. El traductor podrá reordenar palabras de forma segura.
RTL no es solo la dirección del texto. El flujo del layout se invierte, algunos iconos necesitan espejado (como flechas de volver) y contenido mixto (árabe más un código de producto en inglés) puede renderizar en orden sorprendente. Prueba pantallas reales, no solo una etiqueta, y asegúrate de que tus componentes UI soporten cambios de dirección.
Nunca traduzcas lo que el usuario escribió (nombres, direcciones, tickets de soporte, mensajes de chat). Puedes traducir etiquetas alrededor y formatear metadatos (fechas, números), pero el contenido debe permanecer tal cual. Si añades auto-traducción después, hazlo como una función explícita con un conmutador claro “original/traducido”.
Un ejemplo práctico: una app construida con Koder.ai podría mostrar “{name} renewed on {date} for {amount}”. Manténlo como un solo mensaje, formatea {date} y {amount} según locale y muéstralo en la zona horaria del usuario. Este patrón evita mucha deuda de traducción.
Reglas rápidas que previenen errores:
La deuda de traducción suele empezar como “solo una cadena rápida” y se convierte en semanas de limpieza. En proyectos creados por chat puede pasar aún más rápido porque el texto UI se genera dentro de componentes, formularios e incluso mensajes de backend.
Los problemas más costosos son los que se extienden por la app y son difíciles de encontrar.
Imagina una app React y otra Flutter que muestran un banner de facturación: “You have 1 free credit left”. Alguien cambia el texto web a “You have one credit remaining” y mantiene la clave como la frase completa. Móvil sigue usando la clave antigua. Ahora tienes dos claves para un mismo concepto y los traductores ven ambas.
Un patrón mejor es claves estables (por ejemplo billing.creditsRemaining) y pluralización con mensajes ICU para que la gramática sea correcta en todos los idiomas. Si usas una herramienta de vibe-coding como Koder.ai, añade una regla temprana: cualquier texto dirigido al usuario producido en chat debe ir a archivos de traducción, no dentro de componentes o errores del servidor. Este pequeño hábito protege tu arquitectura de internacionalización a medida que el proyecto crece.
Cuando la internacionalización se vuelve un lío, suele ser porque no se documentaron las bases. Una lista de verificación y un ejemplo concreto ayudan a mantener al equipo (y a tu yo futuro) fuera de la deuda de traducción.
Aquí tienes una lista rápida para cada nueva pantalla:
billing.invoice.paidStatus, no billing.greenLabel).Un ejemplo simple: lanzas una pantalla de facturación en inglés, español y japonés. La UI tiene: “Invoice”, “Paid”, “Due in 3 days”, “1 payment method” / “2 payment methods” y un total como “$1,234.50”. Si construyes esto con una arquitectura de internacionalización para apps creadas por chat, defines claves una vez (compartidas entre web y móvil) y cada idioma solo rellena valores. “Due in {days} days” se convierte en un mensaje ICU y el formateo de dinero viene de un formateador consciente del locale, no de comas codificadas.
Despliega soporte de idiomas por feature, no como una gran reescritura:
Documenta dos cosas para que nuevas features sean consistentes: tus reglas de nombrado de claves (con ejemplos) y una “definición de hecho” para cadenas (sin copy codificado, ICU para plurales, formateo de fechas/números, añadido al catálogo compartido).
Siguientes pasos: si construyes en Koder.ai, usa Planning Mode para definir pantallas y claves antes de generar UI. Luego usa snapshots y rollback para iterar de forma segura sobre copy y traducciones en web y móvil sin arriesgar un release roto.