Implementación de la herencia en archivos de localización de iOS





¡Saludos queridos hackers!



Hoy quiero compartir una experiencia interesante en la solución del problema de localización. En iOS, la localización se organiza de manera bastante conveniente desde el punto de vista de un objetivo, o varios objetivos, en los que las claves en localizable.strings no están muy repetidas. Pero todo se vuelve más complicado cuando tienes una docena de objetivos, en los que se repiten más de la mitad de las claves, pero al mismo tiempo tienen significados diferentes, y también hay un conjunto de claves que son únicas para un objetivo en particular.



Para aquellos que aún no se han encontrado con esto, explicaré el problema con más detalle con un ejemplo.



Digamos que tenemos un gran proyecto en el que el 90% del código general y 3 destinos : MyApp1 , MyApp2 , MyApp3 , que tienen una serie de pantallas específicas, además de que cada una tiene su propio nombre y textos. De hecho, un objetivo es una aplicación independiente. Cada uno de ellos debe estar traducido a 10 idiomas. Sin embargo, NO queremos agregar claves de localización como app1_localizable_key1 , app2_localizable_key1 , etc. Queremos que todo sea hermoso en el código y la localización en una línea.



NSLocalizedString(@"localizable_key1", nil)


Sin if y ifdef , de modo que al agregar un nuevo objetivo, no tengamos que buscar en todo el código de un gran proyecto lugares con NSLocalizedString y registrar nuevas claves allí. También queremos que algunas de las claves estén vinculadas a pantallas de destino específicas, es decir, había claves app2_screen1_key , app3_screen2_key .



Ahora puede hacer lo siguiente con las herramientas estándar de Xcode:



  • Copie la parte común localizable.strings a cada objetivo y obtendremos 3 copias de estos archivos.
  • Agregue claves específicas de destino a las cadenas localizables correspondientes.


Que problemas tenemos:



  • Es bastante caro agregar una nueva clave pública a un proyecto. El número de asientos es igual al número de destinos multiplicado por el número de idiomas. En nuestro ejemplo, son 30 lugares.
  • Existe la posibilidad de un error cuando agregamos una línea a 1-2 objetivos actuales con los que estamos trabajando activamente, y un año después decidieron resucitar uno o más objetivos. Tendrá que sincronizar manualmente las localizaciones entre sí o escribir un script para esto. Y si se mostró algo de descuido al agregar o fusionar ramas, y las claves generales se mezclan con las específicas, entonces habrá una búsqueda real.
  • El volumen de archivos de localización. Todos están en constante crecimiento, lo que dificulta trabajar con ellos y aumenta las posibilidades de conflicto al fusionar sucursales.


Lo que me gustaría:



  • Mantener todas las claves públicas en un archivo separado.
  • Para cada objetivo, había un archivo en el que solo se almacenaban las claves específicas para él, así como claves generales con valores para este objetivo.


Para nuestro ejemplo, tener un archivo común localizable.strings con cadenas



"shared_localizable_key1" = "MyApp title"
"shared_localizable_key2" = "MyApp description"
"shared_localizable_key3" = "Shared text1"
"shared_localizable_key4" = "Shared text2"


Me gustaría tener un archivo localizable_app2.strings con las claves



"shared_localizable_key1" = "MyApp2 another title"
"shared_localizable_key2" = "MyApp2 another description"
"app2_screen1_key" = "Profile screen title"


Aquellos. organizar el principio de herencia en archivos de localización .



Desafortunadamente, Xcode no está hecho a medida para esto, así que tuve que reinventar mi propia "bicicleta", que no quise ir durante mucho tiempo debido al hecho de que Xcode aquí y allá ponía un radio en las ruedas.



Tenemos un proyecto con 18 destinatarios y 12 idiomas. Y esto no es una broma, el proyecto es realmente grande y se necesitan muchos objetivos allí. Cada vez que necesitamos agregar una nueva clave pública para la traducción, estamos tratando con 216 archivos de localización. Se tarda mucho tiempo. Y agregar un nuevo objetivo conduce al hecho de que necesita copiar 12 cadenas más localizables en él . En general, en algún momento nos dimos cuenta de que ya era imposible vivir así y tuvimos que buscar una solución.



No hablaré durante mucho tiempo sobre todos los métodos que logré probar en el proceso, iré directamente a la solución de trabajo.



Entonces, primero necesitábamos encontrar todas las claves compartidas. Esto se puede hacer usando un script, no entraré en detalles, esta es una tarea bastante trivial.



Además, cuando recibamos un archivo de localización común (base), o más bien 12 archivos físicos, así como un conjunto de archivos para cada objetivo, vaya a Xcode y agregue todos los archivos allí. Al mismo tiempo, no adjuntamos archivos a ningún objetivo, es decir, el panel derecho debajo de Membresía de destino debe estar desmarcado.







Estableceremos estas marcas solo para el archivo, que será el resultado del trabajo del script para ensamblar archivos.



Entonces comienza la misma "bicicleta":



  • Localization, build_localization.py.
  • Localizable. localizable.strings.
  • Localizable .






Solo lo necesitamos para agregar correctamente un enlace a los archivos en el proyecto, y para que Xcode los reconozca correctamente. De lo contrario, no los utilizará para buscar claves. Por ejemplo, si crea una carpeta localizable con los archivos localizable.strings correctos dentro y la agrega al proyecto como referencias de carpeta , no importa qué Xcode no entenderá que le dimos claves de localización. Por lo tanto, tome la carpeta Localizable , arrástrela como un grupo ( crear grupo ) y desmarque la casilla de verificación Copiar elementos si es necesario , para que se vea como la imagen de abajo.







Eliminar la carpeta localizabley agréguelo a las excepciones del gita. Debido a que no necesitamos el resultado del script en la gita, cambiará para cada objetivo y obstruirá las confirmaciones.



Ahora necesitamos agregar el script a la fase de construcción. Para hacer esto, en Build Phases, haga clic en New Run Script Phase y escriba nuestro script con parámetros. 




python3 ${SRCROOT}/Localization/build_localization.py -b “${SRCROOT}/BaseLocalization" -s "${SRCROOT}/Target1Localization" -d "${SRCROOT}/Localization/Localizable"


b es la carpeta con la localización base, s es la localización del destino actual, d es la carpeta de resultados.



Mueva la nueva fase hacia arriba, no debe ser inferior a la fase Copiar recursos del paquete . Aquellos. primero, el script genera archivos y solo entonces se incluyen en el paquete.







Ahora es importante decirle a Xcode que durante la ejecución del script, los archivos se cambian, de lo contrario, durante la compilación, arrojará un error de que no pudo encontrar los archivos. Además, el error solo estará en un ensamblaje limpio y no quedará claro de inmediato cuál es el problema. En la fase de compilación, agregue todos los archivos de localización a los archivos de salida







Esto debe hacerse para cada objetivo. La forma más fácil de hacerlo es abriendo el proyecto con un editor de texto, porque Xcode no podrá copiar / pegar una fase entre objetivos. En consecuencia, los parámetros del script -s para cada destino serán diferentes.



Ahora, en cada compilación, el script tomará el archivo de localización base, renovará los cambios del archivo de destino (agregar, sobrescribir claves) y generará la localización en la carpeta Localizable , que iOS usará para buscar claves.



En general, obtuvimos lo planeado al implementar el mecanismo de herencia:



  • Las claves compartidas residen en un archivo y no interfieren con otros. ¡El tiempo para el proceso de introducción de nuevas claves se ha reducido en 18! hora.
  • Las claves relacionadas con un objetivo específico se encuentran en el archivo correspondiente.
  • El tamaño del archivo se ha reducido significativamente. Nos deshicimos del desorden de líneas repetidas.
  • El proceso de agregar un nuevo idioma a un proyecto también se simplifica enormemente.
  • Al crear un nuevo destino, no es necesario copiar la localización con un montón de líneas innecesarias. Cree un nuevo archivo llamado localizable.strings y agregue solo lo que se necesita para este objetivo.
  • Si decide reanimar el objetivo anterior, entonces no necesita hacer nada con las líneas, todo se extraerá del archivo base.
  • El guión no ensucia el git, el resultado del trabajo permanece localmente y se puede eliminar sin dolor.


→ El guión terminado se puede tomar aquí



. No pretendo ser un guión perfecto, las solicitudes de extracción son bienvenidas.



All Articles