tva
← Artículos

Banderas en widgets: por qué usar códigos ISO de país en lugar de nombres localizados

El error era sutil. Un widget de iOS mostraba el nombre correcto de un país en inglés, pero un emoji de bandera incorrecto en alemán. Con la configuración regional en francés, otro país mostraba una bandera incorrecta. Las banderas no eran aleatorias: fallaban siguiendo un patrón que tardamos un momento en reconocer. La búsqueda devolvía la bandera del país cuyo nombre, en la configuración regional actual, se parecía al nombre guardado para mostrar.

La causa era una búsqueda de países que utilizaba nombres localizados como claves de diccionario. Los nombres que son cadenas estables en una configuración regional se convierten en cadenas diferentes en otra. La solución exigió reconstruir toda la búsqueda en torno a los códigos ISO 3166-1 alfa-2, y la lección va mucho más allá de los emojis de banderas.


Cómo apareció el error

El widget mostraba una lista de países con sus emojis de bandera junto a otros datos. La implementación original guardaba los nombres visibles de los países como identificadores —las cadenas «Germany» o «France»— y utilizaba un diccionario con esos nombres como claves para buscar el emoji correspondiente.

Con la configuración regional en inglés, funcionaba correctamente. El nombre guardado era «Germany», la clave del diccionario era «Germany» y la búsqueda devolvía la bandera correcta. Pero al configurar el dispositivo en alemán, NSLocale.current.localizedString(forRegionCode: "DE") devuelve «Deutschland». El widget llamaba a este método para resolver dinámicamente los nombres visibles. El nombre mostrado pasaba a ser «Deutschland», correcto en ese contexto, pero la búsqueda en el diccionario con claves en inglés fallaba, recurría a un valor predeterminado y mostraba la bandera incorrecta.

La variante más difícil de detectar eran las coincidencias parciales. En algunas configuraciones regionales, el nombre localizado de un país comparte un prefijo o se parece al nombre en inglés de otro. Sin comprobaciones de igualdad estricta, las búsquedas aproximadas devolvían resultados aparentemente plausibles, pero incorrectos. Un proceso de control de calidad ejecutado solo con la configuración regional en inglés nunca lo detectaría.

Por qué los nombres dependientes de la configuración regional son una trampa

El problema fundamental es usar como identificador una cadena que depende de la configuración regional. Los identificadores deben ser estables: tener el mismo valor con independencia de quién los lea, cuándo o en qué idioma. El nombre de un país en la configuración regional actual es un valor de presentación. Es correcto y adecuado mostrarlo al usuario. Es incorrecto y frágil utilizarlo como clave de búsqueda, identificador de base de datos, clave de caché o cualquier otra referencia estable.

Esto no se limita a los nombres de países. Los nombres de monedas e idiomas, los nombres visibles de configuraciones regionales y las descripciones de zonas horarias comparten la misma propiedad: son cadenas dependientes de la configuración regional que el sistema genera para mostrar, no para identificar. NSLocale.current.localizedString(forCurrencyCode: "EUR") devuelve «Euro» en inglés y «Euro» en alemán, una coincidencia casual; pero localizedString(forCurrencyCode: "GBP") devuelve «British Pound» en inglés y «Britisches Pfund» en alemán. Usar cualquiera de ellas como clave produce la misma clase de error.

La regla es sencilla: utilice el código ISO como identificador en toda la lógica y llame al método de localización solo al construir una cadena que se mostrará al usuario. Nunca guarde la cadena localizada. Nunca la utilice como clave. Nunca la compare mediante código entre configuraciones regionales.

Generación de emojis de banderas a partir de códigos ISO

Unicode codifica los emojis de banderas como pares de símbolos indicadores regionales. La secuencia de caracteres de la bandera de un país corresponde a su código ISO 3166-1 alfa-2, con cada letra sustituida por su símbolo indicador regional equivalente. Estos símbolos van de U+1F1E6 (para A) a U+1F1FF (para Z).

En Swift, generar un emoji de bandera a partir de un código ISO requiere una función auxiliar breve:

func flagEmoji(for isoCode: String) -> String {
    let base: UInt32 = 0x1F1E6 - 65 // offset from A
    return isoCode.uppercased().unicodeScalars.compactMap {
        UnicodeScalar(base + $0.value)
    }.map { String($0) }.joined()
}

Esta función recibe «DE» y devuelve 🇩🇪. Recibe «SG» y devuelve 🇸🇬. No requiere diccionario, archivo de correspondencias ni mantenimiento. La correspondencia está definida por el estándar Unicode y sigue directamente los códigos ISO 3166-1 alfa-2.

El único caso límite que merece atención es que no todos los códigos de dos letras tienen un emoji de bandera correspondiente. Códigos como UN y EU, que están reservados en ISO 3166-1 en lugar de estar asignados a países, producen emojis de bandera que no todas las plataformas representan de forma consistente. La función anterior tampoco valida la entrada: convierte cualquier carácter que reciba, por lo que, antes de la conversión, se debe comprobar la entrada con la lista de códigos ISO 3166-1 alfa-2 asignados (exactamente dos letras de la A a la Z). Limitar la entrada a los códigos asignados a países y territorios y probar en la plataforma de destino cubre este caso.

La tabla de más de 150 países que seguíamos necesitando

Generar emojis de banderas mediante un algoritmo a partir de códigos ISO resuelve la presentación. Pero también necesitábamos guardar y consultar países de forma estructurada: el widget filtraba por región, ordenaba según distintos criterios y asociaba metadatos adicionales a cada país. Esto requería una tabla de correspondencias adecuada.

La tabla que creamos relaciona los códigos ISO 3166-1 alfa-2 con un nombre canónico en inglés (para uso interno y registros), un código numérico ISO 3166-1 (para sistemas que necesitan identificadores numéricos) y una agrupación regional. El nombre en inglés nunca se utiliza como clave de búsqueda: solo sirve para depuración y visualización administrativa en contextos sin localización. Todos los nombres que ve el usuario se generan al renderizar mediante NSLocale.current.localizedString(forRegionCode:).

Esta separación es la decisión arquitectónica clave. La capa de datos utiliza códigos ISO. La capa de presentación los traduce a la configuración regional actual. No hay una mezcla intermedia. La búsqueda de una bandera, la agrupación regional y el orden de clasificación operan con códigos ISO. Solo el texto final que se muestra al usuario pasa por la localización.

Aplicación de la solución a otros datos

Una vez corregido el error de las banderas, revisamos el resto del código en busca del mismo patrón. El problema aparecía en otros tres lugares: un módulo de visualización de monedas que usaba nombres localizados como claves de caché, un selector de idiomas que guardaba nombres localizados en las preferencias del usuario y un módulo de formato de fechas que comparaba nombres de meses dependientes de la configuración regional.

La solución siguió el mismo patrón en cada caso: sustituir la cadena dependiente de la configuración regional por su equivalente estable ISO o IETF. Códigos de moneda (ISO 4217) en lugar de nombres, etiquetas de idioma BCP 47 en lugar de nombres localizados y valores numéricos de mes en lugar de cadenas con nombres de meses. Las cadenas localizadas pasan a ser responsabilidad exclusiva de la capa de vista.

Valió la pena realizar la revisión de forma sistemática. Todos estos errores habrían sido invisibles en pruebas con la configuración regional en inglés y solo habrían aparecido en producción, en dispositivos configurados para otros idiomas. Como la app se dirigía a un público multilingüe, el impacto habría sido considerable.

Alcance del problema en producción

Una razón por la que persiste esta clase de error es su invisibilidad en entornos de desarrollo con una única configuración regional. Un desarrollador que trabaja con la configuración regional en inglés y crea una app para un público internacional puede escribir y probar toda la función de visualización de países sin activar el error. Este solo aparece cuando la configuración regional del dispositivo no coincide con la utilizada al crear el diccionario o la tabla, precisamente la situación de la mayoría de los usuarios destinatarios.

En un widget, esta diferencia es mayor que en la app principal porque los widgets se ejecutan en el contexto restringido de una extensión. La extensión del widget utiliza la configuración regional actual del sistema, que puede diferir de la que probó el desarrollador. La depuración también es menos directa que en la app principal: se ejecuta el esquema de la extensión del widget en Xcode (o se utiliza Debug > Attach to Process), y el código de la línea de tiempo y de la vista se ejecuta según la programación de WidgetKit, en lugar de responder a las interacciones del desarrollador. Identificar errores de renderizado dependientes de la configuración regional en widgets requiere ejecutar la extensión con las configuraciones regionales pertinentes, utilizar vistas previas de Xcode con entradas de ejemplo de la línea de tiempo y prestar atención al contexto real de renderizado de la extensión, un paso que es fácil omitir hasta que un usuario notifica el problema en producción.

La respuesta práctica es tratar toda la lógica de presentación dependiente de la configuración regional como una categoría de riesgo, no solo como un error aislado. La revisión de código debe comprobar si se utilizan cadenas dependientes de la configuración regional como identificadores. La pregunta es sencilla: ¿pasa esta cadena por algún método de localización antes de utilizarse como clave o valor de comparación? Si es así, hay que cambiar el diseño. Los códigos ISO, los identificadores numéricos y otras referencias independientes de la configuración regional deben sustituir a las cadenas localizadas en todas las capas salvo en la presentación final.

Pruebas con distintas configuraciones regionales

La lección más directa es que las pruebas de configuración regional deben formar parte de la matriz habitual de pruebas y no añadirse al final. iOS lo facilita: el simulador permite cambiar la configuración regional sin cambiar el idioma del sistema y las pruebas de interfaz se pueden parametrizar por configuración regional.

Para la visualización de países y banderas, añadimos pruebas de instantáneas que se ejecutan con un conjunto fijo de configuraciones regionales del dispositivo —inglés, alemán, francés, japonés y árabe— y verifican que el emoji de bandera de un conjunto fijo de códigos ISO sea correcto en todas ellas. Estas pruebas detectan de inmediato las regresiones si alguien introduce una búsqueda dependiente de la configuración regional en la ruta de presentación.

El principio general es que cualquier función relacionada con la localización, la internacionalización o la presentación dependiente de la región debe contar con pruebas automatizadas que cubran varias configuraciones regionales. El control de calidad manual en una sola configuración no las sustituye. Los errores causados por supuestos dependientes de la configuración regional son habituales y sistemáticamente invisibles en pruebas con una única configuración.

Artículos relacionados

Artículos relacionados