wandres.dev
DE LA IDEA A LA APP STORE · el panorama completo

Accesibilidad y localización

Una app de nivel dios funciona para todos y en todos los idiomas. VoiceOver, Dynamic Type y catálogos de strings: hazla accesible y global casi gratis.

⏱ 12 min

La diferencia entre una app buena y una excelente muchas veces no se ve: es que una persona ciega puede usarla con VoiceOver, que el texto crece para quien lo necesita, y que se siente nativa en cinco idiomas. SwiftUI te da mucho de esto gratis; el resto, con poco esfuerzo.

🎯 Al terminar esta lección sabrás
  • Accesibilidad básica: labels, hints y traits.
  • Dynamic Type: texto que se adapta.
  • Localizar tu app a varios idiomas.
  • Catálogos de strings modernos.

Accesibilidad: SwiftUI ayuda de serie

SwiftUI genera información de accesibilidad automáticamente: un Button("Guardar") ya lo lee VoiceOver. Pero cuando usas iconos o vistas personalizadas, ayúdale con contexto:

Image(systemName: "trash")
    .accessibilityLabel("Eliminar")            // qué es
    .accessibilityHint("Borra la tarea seleccionada")   // qué hace

// agrupar elementos como uno solo para VoiceOver
VStack {
    Text("Total"); Text("42 €")
}
.accessibilityElement(children: .combine)      // se lee "Total, 42 euros"
Accesibilidad no es una función extra: es respeto

Es fácil pensar en la accesibilidad como un “por si acaso”, pero millones de personas dependen de VoiceOver, del texto grande y del alto contraste para usar su teléfono. Una app inaccesible sencillamente no existe para ellas. Y lo mejor: SwiftUI hace el 80% del trabajo solo, así que el esfuerzo de cubrir el 20% restante —etiquetar tus iconos, agrupar lo que va junto— es mínimo comparado con el impacto. Probar tu app con VoiceOver activado una vez te abre los ojos a cómo la vive otra gente. Hazlo.

Dynamic Type: texto que respira

Usa los estilos de fuente semánticos (.body, .headline, .title) en vez de tamaños fijos, y tu texto escalará con la preferencia del usuario:

Text("Título").font(.title)        // ✅ escala con Dynamic Type
Text("Cuerpo").font(.body)
// Text("X").font(.system(size: 14))  // ❌ tamaño fijo, no se adapta

Alguien con baja visión que sube el tamaño del texto del sistema verá tu app crecer con él, sin que hagas nada más.

Localización: una app, muchos idiomas

Todo texto literal en SwiftUI es, por defecto, una LocalizedStringKey. Eso significa que ya está listo para traducirse:

Text("Bienvenido")      // esto ya es una clave localizable

Catálogos de strings

En Xcode moderno, añades un String Catalog (.xcstrings): Xcode recopila automáticamente todos los textos de tu app y te da una tabla para traducir cada uno a cada idioma, con seguimiento de qué falta:

🗂️

Recolección automática

Xcode extrae todos los Text("…") de tu código al catálogo. No mantienes listas a mano.

🌍

Un idioma por columna

Añades idiomas y rellenas las traducciones en una tabla, con estado (pendiente, hecho, revisar).

🔢

Plurales y variantes

Maneja “1 mensaje” vs “3 mensajes” correctamente en cada idioma, con sus reglas.

💡
Localiza desde el principio, no al final

Retrofit de localización a una app terminada es doloroso: textos concatenados a mano, formatos de fecha y número cableados, capturas de pantalla que hay que rehacer. Si desde el inicio usas Text con literales (ya localizables), formateas fechas y números con las APIs del sistema (.formatted()), y evitas construir frases pegando trozos, cuando quieras añadir un idioma será casi solo rellenar el catálogo. La localización barata empieza con buenos hábitos, no con una fase final.

⚔️ Para todos y en todas partes
  1. Añade .accessibilityLabel a un botón de solo icono.
  2. Agrupa un par de textos con .accessibilityElement(children: .combine).
  3. Activa VoiceOver en el simulador y navega tu app con él.
  4. Usa fuentes semánticas (.body, .title) y sube el Dynamic Type para ver el texto crecer.
  5. Añade un String Catalog y una segunda lengua a un proyecto de prueba.