El proceso: exportar, traducir e importar en cada versión
Una app se traduce una vez y se mantiene traducida siempre, y lo segundo es lo que decide si el esfuerzo valió la pena. Esta lección describe el intercambio con XLIFF a través de los paquetes de localización, qué necesita realmente quien traduce para hacer bien su trabajo, cómo encaja un ciclo de traducción dentro de un calendario de versiones que no lo espera, y cómo se decide con datos hasta dónde llegar.
El fallo más común de la localización no es lingüístico ni técnico: es de planificación. Un equipo internacionaliza bien, contrata traducciones, publica una versión impecable en once idiomas y, tres meses después, la mitad de las pantallas nuevas están en inglés porque nadie previó que traducir tiene un plazo de entrega y que ese plazo no cabe dentro de la última semana antes de publicar. El resultado es una app medio traducida, que comunica algo peor que una app en inglés: comunica abandono. Entender la localización como una cadena de suministro con latencia, y no como una tarea que se hace cuando toca, es lo que separa a los productos que sostienen doce idiomas durante años de los que acumulan idiomas fósiles con el porcentaje bajando en cada versión.
- Exportar e importar localizaciones y entender qué viaja realmente dentro de un paquete
XLIFF. - Preparar el material de contexto que quien traduce necesita: comentarios, glosario y capturas.
- Encajar el plazo de traducción dentro del calendario de una versión sin bloquear el desarrollo.
- Decidir con datos qué idiomas se añaden, cuáles se mantienen y cuándo se retira uno.
El intercambio: paquetes y XLIFF
Xcode exporta las localizaciones como paquetes, uno por idioma de destino, y dentro de cada uno viaja un documento XLIFF, que es el estándar de la industria para el intercambio de traducciones. Ese detalle es más importante de lo que parece porque significa que no dependes de un proveedor concreto: cualquier sistema de gestión de traducciones, cualquier agencia y cualquier herramienta de traducción asistida lee ese formato. El paquete lleva además una carpeta para capturas de pantalla y las notas asociadas a cada cadena.
El documento XLIFF empareja cada unidad de origen con su destino y arrastra las notas, que son los comentarios que escribiste en el catálogo. Lo que no viaja es el contexto que no escribiste: la jerarquía de la pantalla, el ancho disponible, el papel gramatical de la palabra. Por eso el rendimiento de este intercambio depende casi por completo del trabajo hecho antes, en el propio código.
flowchart LR a[String Catalog en el repositorio] --> b[Exportar localizaciones] b --> c[Paquete por idioma con XLIFF y capturas] c --> d[Agencia o sistema de gestion de traducciones] d --> e[Traduccion y revision por hablante nativo] e --> f[Importar localizaciones] f --> g[Revision de la solicitud de cambios] g --> h[Comprobacion en dispositivo antes de publicar]
La importación fusiona por clave y respeta lo que ya existe, de modo que el ciclo es incremental por naturaleza: se exporta el conjunto completo, se traduce solo lo pendiente y se importa el resultado. La fricción aparece cuando alguien edita traducciones a mano en el catálogo mientras hay una exportación en curso, porque entonces conviven dos fuentes de verdad. La disciplina que evita el problema es tratar el catálogo como se trata un archivo generado a medias: los idiomas de destino los escribe la importación, y las correcciones puntuales entran también por ahí.
Una traducción automática de calidad actual produce frases correctas y decide mal exactamente donde importa: el registro, el tratamiento formal o informal, la coherencia terminológica entre pantallas y la longitud. No sabe que en tu producto una palabra concreta es un término técnico que no se traduce, ni que ese botón tiene sesenta puntos de ancho. Usarla como borrador con revisión humana es razonable y ahorra dinero; usarla como resultado final produce una app que suena a máquina en once idiomas y no lo parece en el único que tú lees.
Trabajar con quien traduce
Una persona que traduce interfaces recibe una lista de frases sin aplicación, sin flujo y a menudo sin capturas, y tiene que decidir por cada una qué clase de elemento es y cuánto espacio tiene. Cualquier inversión en reducir esa incertidumbre se paga varias veces, porque un malentendido no cuesta una frase: cuesta una ronda de correcciones multiplicada por el número de idiomas.
Comentarios útiles
Qué elemento es, dónde aparece, qué pasa al pulsarlo y si es verbo o sustantivo. Es el único contexto que viaja con la cadena.
Glosario
Los términos del producto con su traducción fijada y los que no se traducen nunca. Evita que la misma cosa tenga tres nombres.
Capturas
Una imagen por pantalla clave resuelve más dudas que veinte comentarios y hace visible el espacio real disponible.
A esos tres elementos conviene añadir una guía de estilo corta que fije las decisiones transversales: si se trata de usted o de tú, si los títulos van en mayúscula inicial o en estilo de oración, cómo se escriben los números, y qué tono tiene el producto. Son decisiones que se toman una vez y que, si no se toman, cada persona resuelve de forma distinta y el resultado es una app que cambia de voz al cambiar de pestaña.
Hay un papel que casi siempre falta y que rinde más que ningún otro: la revisión en contexto por un hablante nativo que use la app compilada, no la hoja de cálculo. Los defectos que solo aparecen ahí son de otra naturaleza —una etiqueta que no cabe, una frase correcta pero antinatural, un término que en esa región significa otra cosa— y ninguna revisión sobre el XLIFF los detecta. Media jornada por idioma y por versión mayor es un coste modesto para la clase de defecto que evita.
El calendario de una versión
Aquí es donde la mayoría de los procesos se rompen, y el motivo es aritmético. El desarrollo de una versión produce cadenas nuevas hasta el último día; la traducción tarda entre varios días y dos semanas según el volumen y el número de idiomas; y la fecha de publicación no se mueve. Si las tres cosas son ciertas a la vez, algo tiene que ceder, y lo que cede sin que nadie lo decida es la traducción.
La solución conocida es una fecha de congelación de textos anterior a la de congelación de código. A partir de ese momento las cadenas de la versión no cambian, se exporta, y el desarrollo continúa sobre lo que no afecta a los textos. Cualquier texto nuevo que aparezca después entra sabiendo que se publicará en inglés y con una tarea abierta para la siguiente versión. Lo importante de este mecanismo no es que evite textos sin traducir, que no lo hace: es que convierte una sorpresa en una decisión consciente y registrada.
// Un texto que llega tarde puede aislarse tras una bandera
if Funcionalidad.resumenSemanal.activa {
Text("Resumen semanal")
}
Esa bandera cumple una función que se suele pasar por alto: permite publicar la versión con la funcionalidad desactivada en los mercados donde el texto todavía no existe, en lugar de mostrarla en inglés. Es la misma técnica que se usa para desplegar por fases, aplicada al eje del idioma en lugar de al del porcentaje de usuarios.
Conviene además entender la cadena de respaldo, porque determina qué ve exactamente alguien cuando falta una traducción. El sistema recorre los idiomas preferidos de la persona en orden, y si ninguno está disponible en tu paquete recae en la localización de desarrollo. Eso significa que un usuario con catalán y luego español en su lista verá tu app en español si no ofreces catalán, lo que casi siempre es el comportamiento deseable, y que un usuario con un solo idioma no cubierto la verá en el de desarrollo.
// Qué idioma está sirviendo realmente el paquete, útil en diagnósticos
let servido = Bundle.main.preferredLocalizations.first ?? "desconocido"
let disponibles = Bundle.main.localizations
El nombre, el subtítulo, la descripción, las palabras clave y las capturas de App Store Connect no viven en el catálogo ni se exportan con él. Tienen su propio ciclo, su propia revisión y su propio plazo, y son lo primero que ve alguien que no ha instalado nada. Una app traducida con una ficha en inglés pierde la conversión antes de tener oportunidad de demostrar nada.
Cuántos idiomas y hasta cuándo
La decisión de qué idiomas soportar se toma casi siempre con intuición y debería tomarse con dos datos. El primero es dónde están ya tus usuarios, que las herramientas de la tienda desglosan por territorio y por idioma del dispositivo. El segundo es dónde hay mercado sin barrera de entrada, que rara vez coincide con lo anterior. Añadir un idioma donde ya tienes tracción convierte usuarios existentes en usuarios que se quedan; añadir uno nuevo es una apuesta con coste de adquisición aparte.
Lo que casi nadie contabiliza es que un idioma no es un gasto único sino una suscripción. Cada versión futura tendrá cadenas nuevas que habrá que traducir a todos los idiomas activos, con su coste, su plazo y su coordinación. Doce idiomas no son doce traducciones: son doce traducciones por versión durante toda la vida del producto, y ese es el número que hay que poner sobre la mesa antes de decir que sí.
// Una prueba barata que impide que la deuda crezca en silencio
func testTodasLasClavesTienenTraduccion() throws {
for idioma in Bundle.main.localizations where idioma != "Base" {
let paquete = try XCTUnwrap(Bundle(path: Bundle.main.path(
forResource: idioma, ofType: "lproj") ?? ""))
for clave in clavesCriticas {
let valor = paquete.localizedString(forKey: clave, value: nil, table: nil)
XCTAssertNotEqual(valor, clave, "Falta \(clave) en \(idioma)")
}
}
}
Esa prueba explota una propiedad del mecanismo de búsqueda que resulta muy útil: cuando no hay traducción, el sistema devuelve la clave. Comparar el resultado con la clave detecta ausencias sin necesidad de analizar el catálogo, y ejecutarla en integración continua sobre el conjunto de cadenas críticas convierte un porcentaje que nadie mira en una condición que bloquea.
Queda la decisión más incómoda, que es retirar un idioma. Un idioma con el veinte por ciento de las cadenas traducidas no está soportado: está estropeado, y produce pantallas mezcladas que resultan peores que el inglés íntegro. Cuando la deuda supera cierto umbral, las opciones honestas son dos, invertir para recuperarlo o retirarlo, y la peor de todas es dejarlo como está. Retirar tiene además un coste que conviene anticipar: quienes lo usaban pasarán al respaldo, y eso merece un aviso en las notas de la versión.
Merece la pena modelar esto con la herramienta correcta, porque hacerlo explica de golpe por qué tantos equipos competentes fracasan en lo mismo. Piensa en la traducción como en cualquier otra dependencia externa con latencia: una compilación que tarda, un proveedor que tiene que aprobar algo, una revisión de seguridad. Todas comparten una estructura y todas se gestionan igual. Hay un flujo de entrada, las cadenas nuevas que produce el desarrollo; una capacidad de servicio, el volumen que la traducción absorbe por unidad de tiempo; y una cola, que es el conjunto de cadenas en estado pendiente. La teoría elemental de colas dice algo que aquí resulta brutal en su claridad: si la tasa de llegada supera de forma sostenida a la de servicio, la cola crece sin límite, y ninguna cantidad de esfuerzo puntual la vacía. Esa es la descripción exacta de una app cuyo porcentaje de traducción baja versión tras versión pese a que el equipo hace sprints de localización cada cierto tiempo. El sprint es un aumento temporal de capacidad; el problema es estructural. De ahí se siguen las tres únicas palancas reales, y conviene reconocerlas porque son las mismas que en cualquier sistema saturado. Reducir la llegada, es decir, escribir menos cadenas nuevas o reutilizar las existentes en lugar de inventar sinónimos, que además mejora la coherencia del producto. Aumentar la capacidad, contratando más, automatizando el borrador o reduciendo el número de idiomas activos. O amortiguar la variabilidad, que es lo que hace la fecha de congelación de textos: no acelera nada, pero desacopla el ritmo irregular del desarrollo del ritmo regular de la traducción, exactamente como un búfer entre un productor a ráfagas y un consumidor constante. La consecuencia de diseño que conviene llevarse va más allá de esta disciplina: cualquier trabajo derivado que dependa de un ser humano externo debe modelarse como un canal con latencia y capacidad, nunca como una tarea final, porque las tareas finales se planifican al final y los canales con latencia hay que alimentarlos desde el principio. Un equipo que interioriza eso deja de tener campañas de traducción, igual que un equipo con integración continua deja de tener semanas de integración.
El intercambio ocurre mediante paquetes de localización con XLIFF dentro, que es estándar y no te ata a un proveedor; la importación fusiona por clave y el catálogo debe tratarse como generado en sus idiomas de destino. Quien traduce necesita comentarios, glosario, guía de estilo y capturas, y una revisión en contexto por hablante nativo encuentra lo que ninguna hoja de cálculo revela. La fecha de congelación de textos precede a la de código, y las banderas permiten ocultar lo que llegó tarde. Cada idioma es una suscripción por versión, no un gasto único, y un idioma a medias es peor que ninguno.
- Exporta las localizaciones de tu proyecto y abre el
XLIFFde un idioma: comprueba cuántas unidades llegan sin nota de contexto. - Escribe un glosario de veinte términos de tu producto con su traducción fijada y la lista de los que no se traducen nunca.
- Fija una fecha de congelación de textos para tu próxima versión y mide cuántas cadenas nuevas aparecen después de ella.
- Añade una prueba de integración continua que falle cuando falte una traducción de las cadenas críticas y ejecútala sobre todos tus idiomas.
- Calcula el coste anual real de cada idioma activo multiplicando cadenas nuevas por versión, versiones al año y tarifa, y decide con ese número cuáles mantienes.