Cree automáticamente archivos de localización de Android e iOS a partir de una hoja de cálculo de Excel

imagen


Hola, mi nombre es Alexander y soy desarrollador de aplicaciones de Android. Una vez que llegué a un proyecto que tenía 11 idiomas de interfaz y más de 600 líneas. No había programadores en el lado del cliente, por lo que almacenaron todo en una hoja de cálculo de Excel. Cuando cambiaron algo en él, nos enviaron esta tabla con las palabras "Destacamos las celdas con cambios en amarillo, actualizamos las aplicaciones de Android e iOS, respectivamente". Después de eso, dos desarrolladores perdieron un par de horas, haciendo cambios manualmente. Y luego resultó que alguien había olvidado algo en algún lugar, cometió un error o no lo terminó, hubo discrepancias entre las plataformas, el cliente estaba nervioso, los desarrolladores estaban volviéndose locos. Esta situación no me convenía en absoluto, comencé a buscar formas de automatizar la descarga de filas desde Excel. El resultado fue un excelente código VBScript, que todavía disfrutamos usando. Ahora te presentaré este guión. Debajo del corte, una cierta cantidad de imágenes y código de script.


Entonces, primero eche un vistazo a la tabla en sí y evalúe la escala del problema:


imagen


¡Ahí está ella, belleza! Como podemos ver, hay varias columnas de servicio, nombres de líneas globales y sus traducciones. Además, algunas líneas se presentan solo en inglés y alemán, porque en la versión 2.0 de la aplicación, el cliente decidió dejar solo dos idiomas por ahora y agregar el resto más adelante. O tal vez se compadece del dinero para los traductores. Pero este es su negocio, pero tenemos que tener esto en cuenta. Es decir, el script debe omitir celdas vacías y no crear líneas vacías para dicho lenguaje. Además, se deben considerar los caracteres de formato como "% s" en la celda F5. Tendrán que trabajar, porque lo que Android es bueno en iOS debería reemplazarse con "% @". Te contaré sobre los otros matices en el camino.


Para no languidecer y no jalar al gato por la cola, publicaré todo el guión en este momento:


VBScript en todo su esplendor
option explicit ' Start it with: cscript ConvertExcelToTXTandXML.vbs If UCASE(right(wscript.fullname,11)) <> "CSCRIPT.EXE" Then Msgbox "Please enter: cscript ConvertExcelToTXTandXML.vbs <filename>.xlsx" WScript.Quit 255 End If ' The column of the key is 4 Const KeyColumn = 4 ' Names of destination files to create Const outFileiOS="\Localizable.strings" Const outFileiOSLocale="\InfoPlist.strings" Const outFileAndroid="\stringsToFormat.xml" Const NsCameraUsageDescription = "NsCameraUsageDescription" Const NSLocationAlwaysAndWhenInUseUsageDescription = "NSLocationAlwaysAndWhenInUseUsageDescription" Const NSLocationAlwaysUsageDescription = "NSLocationAlwaysUsageDescription" Const NSLocationWhenInUseUsageDescription = "NSLocationWhenInUseUsageDescription" Const NSPhotoLibraryAddUsageDescription = "NSPhotoLibraryAddUsageDescription" Const NSPhotoLibraryUsageDescription = "NSPhotoLibraryUsageDescription" Dim oExcel Dim oTranslations Dim objOutputFileiOS Dim objOutputFileiOSLocale Dim objFSO Dim objFSOandroid Dim objFSOios Dim myArgs Dim myParameter Dim sName Dim CompletePath Dim WorkingDir Dim WorkingDirAndroid Dim WorkingDirIos Dim LanguageColumnIndex Dim UsedRows Dim nCounter Dim xmlDoc Dim objIntro Dim objRoot Dim objHdr Dim objHdrAtt Dim theText Dim AndroidString Dim iOSString ' **************************************** ' MAKE PRETTY XML ' **************************************** Const strOutputFile = "\strings.xml" ' **************************************** Dim objInputFile, objOutputFile, strXML Dim objXML : Set objXML = WScript.CreateObject("Msxml2.DOMDocument") Dim objXSL : Set objXSL = WScript.CreateObject("Msxml2.DOMDocument") ' Create interface to Excel Set oExcel = CreateObject("Excel.application") ' Create the file interface Set objFSO=CreateObject("Scripting.FileSystemObject") ' Get the commandline parameter Set myArgs = WScript.Arguments.Unnamed If myArgs.count > 0 Then If (not objFSO.FileExists(myArgs.item(0))) Then Wscript.Echo "Error: '" & myArgs.item(0) & "' not found" WScript.Quit 255 End If Set myParameter = objFSO.GetFile(myArgs.item(0)) sName = myParameter.Name CompletePath = myParameter.Path WorkingDir = myParameter.Path WorkingDir = Left(WorkingDir, Len(WorkingDir)-Len(sName)) WorkingDirAndroid = "res\" WorkingDirIos = "ios\" If Not objFSO.FolderExists(WorkingDir & WorkingDirAndroid) Then ' Create folder if not exists' objFSO.CreateFolder(WorkingDir & WorkingDirAndroid) End If If Not objFSO.FolderExists(WorkingDir & WorkingDirIos) Then ' Create folder if not exists' objFSO.CreateFolder(WorkingDir & WorkingDirIos) End If Else Wscript.Echo "Error: A filename is needed" WScript.Quit 255 End If ' Source file Set oTranslations = oExcel.Workbooks.Open(CompletePath) ' Get the maximum number of rows in the sheet UsedRows = oTranslations.Sheets(1).UsedRange.Rows.Count ' In this column start the start the languages' LanguageColumnIndex = 6 ' stop the processing when the cell is empty --> end of languages while oTranslations.Sheets(1).Cells(3, LanguageColumnIndex) <> "" WScript.stdout.Write "Create files for: " WScript.stdout.Write oTranslations.Sheets(1).Cells(3, LanguageColumnIndex) WScript.stdout.Write " " objFSOandroid = oTranslations.Sheets(1).Cells(3, LanguageColumnIndex) If objFSOandroid = "values" Then objFSOios = "en" & ".lproj" ElseIf objFSOandroid = "values-ru" Then objFSOios = "ru-RU" & ".lproj" Else objFSOios = Right(objFSOandroid,2) & ".lproj" End If WScript.stdout.Write "; iOs folder: " WScript.stdout.Write objFSOios If Not objFSO.FolderExists(WorkingDir & WorkingDirAndroid & objFSOandroid) Then ' Create folder if not exists' objFSO.CreateFolder(WorkingDir & WorkingDirAndroid & objFSOandroid) End If If Not objFSO.FolderExists(WorkingDir & WorkingDirIos & objFSOios) Then ' Create folder if not exists' objFSO.CreateFolder(WorkingDir & WorkingDirIos & objFSOios) End If ' Create the destination files Set objOutputFileiOS = CreateObject("ADODB.Stream") objOutputFileiOS.CharSet = "utf-8" objOutputFileiOS.Open Set objOutputFileiOSLocale = CreateObject("ADODB.Stream") objOutputFileiOSLocale.CharSet = "utf-8" objOutputFileiOSLocale.Open Set xmlDoc = CreateObject("Msxml2.DOMDocument") ' NOTE: chr(34) is " ' NOTE: vbCrLf is <CR><LF> ' Create the XML header Set objIntro = xmlDoc.createProcessingInstruction("xml","version='1.0' encoding='UTF-8' standalone='yes'") xmlDoc.insertBefore objIntro,xmlDoc.childNodes(0) Set objRoot = xmlDoc.createElement("resources") xmlDoc.appendChild objRoot ' keys start in row 3!!! For nCounter = 3 To UsedRows WScript.stdout.Write "." If oTranslations.Sheets(1).Cells(nCounter, KeyColumn).Value <> "" Then ' Write to iOS file If Not oTranslations.Sheets(1).Cells(nCounter, LanguageColumnIndex).Value = "" Then objOutputFileiOS.WriteText chr(34) objOutputFileiOS.WriteText oTranslations.Sheets(1).Cells(nCounter, KeyColumn).Value objOutputFileiOS.WriteText chr(34) objOutputFileiOS.WriteText " = " objOutputFileiOS.WriteText chr(34) iOSString = Replace(oTranslations.Sheets(1).Cells(nCounter, LanguageColumnIndex).Value, "%s", "%@") iOSString = Replace(iOSString, "'", "\'") iOSString = Replace(iOSString, chr(34), "\" & chr(34)) objOutputFileiOS.WriteText iOSString objOutputFileiOS.WriteText chr(34) objOutputFileiOS.WriteText ";" & vbCrLf If ( (oTranslations.Sheets(1).Cells(nCounter, KeyColumn).Value = NsCameraUsageDescription) _ or (oTranslations.Sheets(1).Cells(nCounter, KeyColumn).Value = NSLocationAlwaysAndWhenInUseUsageDescription) _ or (oTranslations.Sheets(1).Cells(nCounter, KeyColumn).Value = NSLocationAlwaysUsageDescription) _ or (oTranslations.Sheets(1).Cells(nCounter, KeyColumn).Value = NSLocationWhenInUseUsageDescription) _ or (oTranslations.Sheets(1).Cells(nCounter, KeyColumn).Value = NSPhotoLibraryAddUsageDescription) _ or (oTranslations.Sheets(1).Cells(nCounter, KeyColumn).Value = NSPhotoLibraryUsageDescription) _ ) Then objOutputFileiOSLocale.WriteText chr(34) objOutputFileiOSLocale.WriteText oTranslations.Sheets(1).Cells(nCounter, KeyColumn).Value objOutputFileiOSLocale.WriteText chr(34) objOutputFileiOSLocale.WriteText " = " objOutputFileiOSLocale.WriteText chr(34) objOutputFileiOSLocale.WriteText iOSString objOutputFileiOSLocale.WriteText chr(34) objOutputFileiOSLocale.WriteText ";" & vbCrLf End If End If ' Write to Android file Set objHdr = xmlDoc.createElement("string") Set objHdrAtt = xmlDoc.createAttribute("name") objHdrAtt.text = oTranslations.Sheets(1).Cells(nCounter, KeyColumn).Value AndroidString =Replace (oTranslations.Sheets(1).Cells(nCounter, LanguageColumnIndex).Value, "'", "\'") AndroidString =Replace (oTranslations.Sheets(1).Cells(nCounter, LanguageColumnIndex).Value, Chr(10), "\n") If Not AndroidString = "" Then Set theText=xmlDoc.createTextNode(AndroidString) objHdr.setAttributeNode objHdrAtt objHdr.appendChild theText objRoot.appendChild objHdr End If End If Next ' Save the files xmlDoc.Save WorkingDir & WorkingDirAndroid & objFSOandroid & outFileAndroid ' **************************************** ' Put whitespace between tags. (Required for XSL transformation.) ' **************************************** Set objInputFile = objFSO.OpenTextFile(WorkingDir & WorkingDirAndroid & objFSOandroid & outFileAndroid,1,False,-2) Set objOutputFile = objFSO.CreateTextFile(WorkingDir & WorkingDirAndroid & objFSOandroid & strOutputFile,True,False) strXML = objInputFile.ReadAll strXML = Replace(strXML,"><",">" & vbCrLf & "<") objOutputFile.Write strXML objInputFile.Close objFSO.DeleteFile(WorkingDir & WorkingDirAndroid & objFSOandroid & outFileAndroid) objOutputFile.Close ' **************************************** ' Create an XSL stylesheet for transformation. ' **************************************** Dim strStylesheet : strStylesheet = _ "<xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"">" & _ "<xsl:output method=""xml"" indent=""yes""/>" & _ "<xsl:template match=""/"">" & _ "<xsl:copy-of select="".""/>" & _ "</xsl:template>" & _ "</xsl:stylesheet>" ' **************************************** ' Transform the XML. ' **************************************** objXSL.loadXML strStylesheet objXML.load WorkingDir & WorkingDirAndroid & objFSOandroid & strOutputFile objXML.transformNode objXSL objXML.save WorkingDir & WorkingDirAndroid & objFSOandroid & strOutputFile ' **************************************** ' End transformation. ' **************************************** objOutputFileiOSLocale.SaveToFile WorkingDir & WorkingDirIos & objFSOios & outFileiOSLocale, 2 objOutputFileiOS.SaveToFile WorkingDir & WorkingDirIos & objFSOios & outFileiOS, 2 LanguageColumnIndex = LanguageColumnIndex + 1 WScript.stdout.Write vbCrLf wend oTranslations.Close oExcel.Quit WScript.Echo "With success done" WScript.Quit(0) 

Ahora es el momento de pasar por los matices.


Nuestra aplicación requiere varios permisos de usuario. En iOS, las líneas para solicitar estos permisos se almacenan no como de costumbre en Localizable.strings, sino en InfoPlist.strings, por lo que al comienzo de nuestro script definimos los nombres de esas líneas que se enviarán a InfoPlist:


 Const NsCameraUsageDescription = "NsCameraUsageDescription" Const NSLocationAlwaysAndWhenInUseUsageDescription = "NSLocationAlwaysAndWhenInUseUsageDescription" Const NSLocationAlwaysUsageDescription = "NSLocationAlwaysUsageDescription" Const NSLocationWhenInUseUsageDescription = "NSLocationWhenInUseUsageDescription" Const NSPhotoLibraryAddUsageDescription = "NSPhotoLibraryAddUsageDescription" Const NSPhotoLibraryUsageDescription = "NSPhotoLibraryUsageDescription" 

El siguiente fragmento digno de mención son los nombres de las carpetas donde se guardarán todos los archivos. En iOS, todas las carpetas se nombran con una designación de idioma de dos letras, como "en.lproj", "de.lproj". Todo excepto ruso, aquí es "ru-RU". Y en la tabla misma, las columnas están en notación de Android. Por lo tanto, parsim:


 If objFSOandroid = "values" Then objFSOios = "en" & ".lproj" ElseIf objFSOandroid = "values-ru" Then objFSOios = "ru-RU" & ".lproj" Else objFSOios = Right(objFSOandroid,2) & ".lproj" End If 

Y la última tarea, reemplazar y escapar de los personajes. Para iOS, cambiaremos, como dije, % s a % @ y escaparemos de comillas y apóstrofes:


 iOSString = Replace(oTranslations.Sheets(1).Cells(nCounter, LanguageColumnIndex).Value, "%s", "%@") iOSString = Replace(iOSString, "'", "\'") iOSString = Replace(iOSString, chr(34), "\" & chr(34)) 

Para Android, también escapamos de los apóstrofes y reemplazamos el llamado carácter de avance de línea ( Chr (10) ) con el carácter habitual de nueva línea \ n . Y hay una razón para esto. En una de las celdas tenemos un texto bastante grande, compilado por el cliente en MS Word y colocado en la celda de Excel mediante el uso técnico de copiar y pegar. Y aunque no encontramos el reemplazo correcto por prueba y error, en iOS el texto se mostraba en los párrafos necesarios, y en Android se fusionó en un párrafo.


Conclusión


Como probablemente ya haya adivinado, el script se ejecuta en la línea de comandos de Windows. Para simplificar, colocamos el script y el archivo .xlsx en una carpeta, vamos a la línea de comando y escribimos el comando:


cscript ConvertExcelToTXTandXML.vbs <filename>.xlsx


A continuación, presione Entrar y disfrute de una hermosa visualización del guión en forma de puntos que aparecen en la ventana de comandos para cada paso del programa. El resultado del trabajo titánico de nuestro script son dos carpetas, "ios" y "res", cuyo contenido queda por copiar al proyecto iOS y Android, respectivamente.


Eso es todo Espero que este script sea útil para alguien y ahorre mucho tiempo.

Source: https://habr.com/ru/post/446100/


All Articles