Android 自动将Google工作表转换为strings.xml进行本地化

Android 自动将Google工作表转换为strings.xml进行本地化,android,google-sheets,Android,Google Sheets,我在谷歌上找到了我所有strings.xml资源的翻译表。每种语言都是一列。第一列是字符串资源ID 您知道我可以使用什么工具(或宏或任何工具)将电子表格重新导出为带有适当区域后缀的strings.xml格式吗?(后缀是电子表格的第一行。)以下是步骤: 将电子表格导出为Open Office 解开它 获取content.xml文件 解析和/或翻译它 生成不同的strings.xml文件 或者,您可以使用从OOo文档中提取内容。我提出了一个解决方案,在很多情况下都可以使用。你的可能会有所不同 我将输

我在谷歌上找到了我所有strings.xml资源的翻译表。每种语言都是一列。第一列是字符串资源ID

您知道我可以使用什么工具(或宏或任何工具)将电子表格重新导出为带有适当区域后缀的strings.xml格式吗?(后缀是电子表格的第一行。)

以下是步骤:

  • 将电子表格导出为Open Office
  • 解开它
  • 获取
    content.xml
    文件
  • 解析和/或翻译它
  • 生成不同的
    strings.xml
    文件

  • 或者,您可以使用从OOo文档中提取内容。

    我提出了一个解决方案,在很多情况下都可以使用。你的可能会有所不同

  • 我将输出为CSV(逗号分隔值)
  • 我将CSV作为文本导入到flash电影中
  • 我通过在新行切分文本文件,将CSV的每一行解析为一个数组。“\n”
  • 然后我在行中循环,获取每种语言的值 我的电子表格列标题是

    name,plural value,en,fr,de,it,es,ja
    
    下面是我使用的代码。它有两个缺点,您可能需要将其应用到自己的电子表格中。首先,它不处理复数值。它只生成重复的字符串条目,然后您需要返回并手动编辑这些条目。其次,如果要翻译的文本包含逗号,actionscript将在这些逗号上拆分字符串,而不是在每个部分的末尾。我后来注意到,如果值包含逗号,Excel会用双引号将其包装起来。不过,我不知道如果它们包含逗号和双引号会怎么样。如果文本中有类似的情况,则需要添加一些代码来处理这些情况。但下面是我使用的Actionscript代码

    编译和运行swf时,会出现一个黄色按钮。单击该按钮,它将提供保存其中一种语言的功能。每次单击它都会保存一种不同的语言。如果你一直点击,它就会回到第一种语言

    但是最终输出应该非常接近完美的translation strings.xml

    主类:

    package 
    {
        import flash.display.Sprite;
        import flash.events.Event;
        import flash.events.MouseEvent;
        import flash.net.FileReference;
        import flash.utils.ByteArray;
    
        /**
         * ...
         * @author Plastic Sturgeon
         */
        public class Main extends Sprite 
        {
            private var rows:Array;
            private var en:XML = <resources></resources>;
            private var fr:XML = <resources></resources>;
            private var de:XML = <resources></resources>;
            private var it:XML = <resources></resources>;
            private var es:XML = <resources></resources>;
            private var xmls:Vector.<XML> = new Vector.<XML>;
    
            private var counter:uint = 0;
            public function Main():void 
            {
                xmls.push(en);
                xmls.push(fr);
                xmls.push(de);
                xmls.push(it);
                xmls.push(es);
    
                if (stage) init();
                else addEventListener(Event.ADDED_TO_STAGE, init);
            }
    
            private function init(e:Event = null):void 
            {
                removeEventListener(Event.ADDED_TO_STAGE, init);
                // entry point
                dumpIt();
                graphics.beginFill(0xFFCC00);
                graphics.drawRect(0, 0, 100, 100);
                stage.addEventListener(MouseEvent.CLICK, onMouseClick);
            }
    
            private function onMouseClick(e:MouseEvent):void 
            {
                var language:int = counter % 5;
                var suffixes:Array = new Array("en", "fr", "de", "it", "es");
                var fr:FileReference = new FileReference();
                var ba:ByteArray = new ByteArray();
                ba.writeObject('<?xml version="1.0" encoding="utf-8"?>\r'+xmls[language].toXMLString());    
                fr.save(ba, "strings-"+suffixes[language]+".xml");
                counter++
            }
            public function dumpIt():void
            {
                var obj:EmbeddedText = new EmbeddedText();
                //txt.text = obj.toString();
                rows = obj.toString().split("\r");
                trace(rows.length);
                for (var i:uint = 0; i < rows.length; i++ ) {
                    var row:Array = String(rows[i]).split(",");
                    for (var j:uint = 0; j < xmls.length; j++ ) {
                        var name:String = row[0]
                        name = name.substring(1);
                        var value:String = row[2 + j];                  
                        var node:XML = new XML('<string name="' + name + '">' + value + '</string>');
                        //trace('<string name="' + name + '">' + value + '</string>');
                        xmls[j].appendChild(node);
                    }
                }
                trace("en",en);
            }
        }
    
    }
    
    package  
    {
        import flash.utils.ByteArray;
    
        /**
         * ...
         * @author Plastic Sturgeon
         */
        [Embed(source="translateion_csv.txt", mimeType="application/octet-stream")]
        public class EmbeddedText extends ByteArray
        {
    
            public function EmbeddedText() 
            {
    
            }
    
        }
    }
    
    最后:这里是我翻译的文本样本,供您自己测试:

    name,plural value,en,fr,de,it,es,ja
    homescreen_new_button,,NEW TOURNAMENT,NOUVEAU TOURNOI,NEUES TURNIER,NUOVO TORNEO,NUEVO TORNEO,
    homescreen_current_button,,VIEW CURRENT BRACKET,VOIR LE TABLEAU ACTUEL,AKTUELLER TURNIERSTAND,VISUALIZZA GRIGLIA,VER NIVEL ACTUAL,
    tournament_type_title,,TOURNAMENT SETTINGS,PARAMÈTRES DU TOURNOI,TURNIER-EINSTELLUNGEN,IMPOSTAZIONI TORNEO,CONFIGURACIÓN DE TORNEO,
    tournament_type_gaming_stations,,HOW MANY GAMING STATIONS,COMBIEN DE POSTES,WIE VIELE SPIELGERÄTE,NUMERO DI POSTAZIONI DI GIOCO,NÚMERO DE MÁQUINAS DE JUEGO,
    tournament_type_tournament_type,,TOURNAMENT TYPE,TYPE DE TOURNOI,TURNIERART,TIPO DI TORNEO,TIPO DE TORNEO,
    tournament_type_tournament_type_single,,SINGLE ELIMINATION,ÉLIMINATION DIRECTE,EINFACHE ELIMINIERUNG,ELIMINAZIONE SINGOLA,ELIMINACIÓN INDIVIDUAL,
    tournament_type_tournament_type_double,,DOUBLE ELIMINATION,DOUBLE ÉLIMINATION,DOPPELTE ELIMINIERUNG,ELIMINAZIONE DOPPIA,ELIMINACIÓN DOBLE,
    tournament_type_back_warning_title,,WARNING,AVERTISSEMENT,ACHTUNG,AVVISO,ATENCIÓN,
    tournament_type_back_warning_message,,Going back to the home screen will clear all information.\nDo you want to continue?,Retourner à l\'écran d\'accueil effacera toutes les données.\nVoulez-vous continuer ?,Durch die Rückkehr zum Home-Bildschirm werden alle Informationen gelöscht.\nWirklich fortfahren?,Tornando alla schermata iniziale perderai tutte le informazioni.\nVuoi continuare?,"Si vuelves a la pantalla principal, se borrará toda la información.\n¿Continuar?",
    tournament_type_back_warning_confirm,,OK,OK,OK,OK,Aceptar,
    tournament_type_back_warning_cancel,,Cancel,Annuler,Abbrechen,Annulla,Cancelar,
    tournament_type_help_single,,Players are eliminated after their first loss.,Les joueurs sont éliminés dès la première défaite.,Spieler scheiden nach ihrer ersten Niederlage aus.,I giocatori vengono eliminati dopo la prima sconfitta.,Los jugadores quedan eliminados a la primera derrota.,
    tournament_type_help_double,,"After a player loses once, they enter the Loser\'s bracket.  They are eliminated if they lose a second time.","Après une première défaite, le joueur intègre le tableau des perdants. Il est éliminé s\'il perd une seconde fois.",Nach einer ersten Niederlage nimmt der Spieler an der Verliererrunde teil. Bei einer zweiten Niederlage scheidet er aus.,"Dopo una sconfitta, i giocatori vengono inseriti nella griglia sconfitti. Vengono eliminati se perdono per la seconda volta.","Al perder una vez, pasan al nivel de perdedores. Si pierden una segunda vez, quedan eliminados.",
    add_players_title,,ADD PLAYERS,AJOUTER DES JOUEURS,SPIELER HINZUFÜGEN,AGGIUNGI GIOCATORI,AÑADIR JUGADORES,
    add_players_seed,,SEED,CLASSER,SETZEN,TESTA DI SERIE,CABEZA DE SERIE,
    add_players_delete,,DELETE,SUPPRIMER,LÖSCHEN,CANCELLA,QUITAR,
    add_players_default_value,,PLAYER NAME,NOM DU JOUEUR,SPIELERNAME,NOME GIOCATORE,NOMBRE DE JUGADOR,
    add_players_dialog_title,,ADD PLAYER,AJOUTER UN JOUEUR,SPIELER HINZUFÜGEN,AGGIUNGI GIOCATORE,AÑADIR JUGADOR,
    add_players_dialog_message,,ENTER THE PLAYER NAME,ENTRER LE NOM DU JOUEUR,SPIELERNAMEN EINGEBEN,INSERISCI IL NOME DEL GIOCATORE,ESCRIBE EL NOMBRE DE JUGADOR,
    add_players_dialog_OK,,OK,OK,OK,OK,ACEPTAR,
    add_players_dialog_ok_and_add,,OK + ADD ANOTHER,OK + EN AJOUTER UN AUTRE,OK + MEHR HINZUFÜGEN,OK + AGGIUNGI ALTRO,ACEPTAR Y AÑADIR OTRO,
    add_players_dialog_cancel,,CANCEL,ANNULER,ABBRECHEN,ANNULLA,CANCELAR,
    add_players_dialog_name_taken,,Name Already Taken,Nom déjà utilisé,Name bereits vergeben,Nome già in uso,Nombre ya utilizado,
    add_players_dialog_name_added,,Player Added,Joueur ajouté,Spieler hinzugefügt,Giocatore aggiunto,Jugador añadido,
    add_players_delete_confirm,,DELETE,SUPPRIMER,LÖSCHEN,CANCELLA,QUITAR,
    add_players_delete_cancel,,DONE,TERMINÉ,FERTIG,FATTO,HECHO,
    

    使用公式
    =CONCATENATE(“,E2,”)
    ,其中A2是资源ID,E2是已翻译的语言


    复制该列并将其粘贴到您的
    \u
    标签中

    我担心,第4步的工作可能比手工完成要多。但感谢您花时间回答。我想出的解决方案是将电子表格导出为CSV,然后编写一个actionscript程序,将CSV分解为XML,并将其保存到每个地区的文件中。翻译中有几个问题,包括逗号。但这只剩下大约5个字段需要手工操作,而不是76个。我认为您可以在导出时在excel中选择分隔符。我确信它支持tab作为分隔符,这会让你绕过逗号问题。