Traducir Deep Cut Atlas al español, y el bug de binario desactualizado que no era un bug de traducción
153 strings. Esa es la cantidad de claves que había en el String Catalog de Deep Cut Atlas después de el trabajo de retrofit, todas en inglés, todas esperando una pasada de traducción. Esta fue esa pasada: español, específicamente español latinoamericano (es-419), no las variantes de España o Estados Unidos. Traduje las 153 a mano, verifiqué el resultado en una pantalla real, y en el camino pasé unos veinte minutos depurando un bug que resultó no existir.
La parte mecánica
Traducir 153 strings a mano suena como si debiera ser la parte difícil, y en realidad no lo fue. Es mecánico una vez que se tiene la lista: leer el inglés, escribir el español, mantener cada placeholder %@ y %lld exactamente en la misma posición, no traducir “To Check Out” porque es el nombre literal de una lista de reproducción que la app crea en la biblioteca real de Apple Music del usuario, no parte de la interfaz. Las únicas decisiones de criterio reales fueron cosas como si “Comp” (abreviatura de Compilation, una etiqueta de filtro con espacio para unos cuatro caracteres) debía convertirse en “Comp” o en otra cosa en español. Lo dejé igual, ya que no hay un equivalente limpio de cuatro letras y de todas formas se usa como abreviatura.
El bug que no era un bug de traducción
Después fui a verificarlo en el Simulador de iOS, cambié el idioma del simulador a español (Latinoamérica), recompilé y lancé la app. Seguía en inglés. Cada pestaña, cada etiqueta, seguía en inglés, como si nada hubiera cambiado.
Primero probé los caminos obvios. Revisé el bundle compilado de la app directamente, sí, es-419.lproj/Localizable.strings estaba ahí, con el texto traducido correcto. Revisé la preferencia de idioma real a nivel del sistema del simulador, sí, español estaba primero en la lista, de verdad. Intenté escribir la preferencia de idioma directamente en el dominio de preferencias propio de la app, saltándome por completo la configuración del sistema, por si algo relacionado con overrides por app fuera la pieza faltante. Nada. La app seguía renderizándose en inglés sin importar qué cambiara del lado del idioma.
El recurso compilado estaba bien. La configuración del dispositivo estaba bien. Lo único que quedaba era la app en ejecución en sí, y resultó que ese era el problema real: el paso de compilación que había estado usando compila la app pero no la instala. Cada “arreglo” que probé estaba relanzando el mismo binario viejo de antes de haber escrito siquiera una sola traducción. Una vez que instalé explícitamente la app recién compilada, un comando extra que había estado saltándome, todo funcionó de inmediato. Cada pestaña, correctamente traducida, al primer intento.
En retrospectiva es un error muy común, olvidar que una compilación en realidad tiene que llegar al dispositivo antes de poder verse, pero es del tipo de cosas que resultan invisibles hasta que se las busca a propósito, porque cualquier otra señal que normalmente se revisaría (el recurso compilado, la configuración del dispositivo) se ve completamente correcta. El bug no estaba en la traducción. Ni siquiera era realmente un bug. Era un binario desactualizado disfrazado de bug de traducción.
Un bug real que sí salió a la luz
Una vez que la compilación nueva estaba realmente corriendo, casi todo se renderizaba correctamente, excepto tres botones que se quedaron en inglés sin importar qué: “Add to library,” “Add to Playlist,” y “Add album to To Check Out.” Esos tres resultaron ser literales de string crudos devueltos por una computed property de Swift, nunca envueltos en la llamada que hace que un string sea consciente del string catalog. Es exactamente la categoría de hueco que el trabajo de retrofit original señaló por nombre y barrió, y aun así se perdió estos tres, porque los archivos que los contenían todavía no existían en el momento de ese barrido original. Buscar con grep el patrón crudo (un return que arma un string sin envolver) atrapó los tres en una sola pasada, que ahora es la técnica a la que recurriría primero la próxima vez, en lugar de otro repaso manual.
Lo que me llevo de esto
Cuando una traducción parece no estar aplicándose y cada pieza individual que se revisa se ve correcta, conviene verificar si la pieza que se está mirando es en realidad la que está corriendo. Y cuando una afirmación de “ya barrimos esto” se enfrenta a código nuevo que no existía durante el barrido original, un grep mecánico le gana a un segundo repaso manual siempre, sin necesitar inteligencia extra, solo la exhaustividad que un segundo vistazo nunca termina de lograr.
Lecturas relacionadas
Cómo corregí la pluralización, y por qué casi elijo la API equivocada de Apple
La etiqueta visible estaba bien; la etiqueta de accesibilidad decía '1 tracks.' Y la ingeniosa API de inflexión habría publicado un error silencioso en portugués en el propio sistema operativo mínimo que soporta la app.
El error que se reportó dos veces, y la interpolación de cadenas que cambia en silencio el formato de un número
Un año de lanzamiento que se mostraba como "2.026" en un dispositivo con región Costa Rica, String(localized:) tratando un Int como si fuera una cantidad, y la clave del String Catalog que cambió de forma con la corrección.
Cómo localizar el aviso de permiso de Apple Music (y el desvío de pruebas en dispositivo que eso implicó)
Una sola cadena de texto, dos hilos de foro que se contradicen, un iPhone equivocado, y el descubrimiento de que desinstalar una app no reinicia su estado de permisos, solo un reinicio completo de privacidad lo hace.