transifexで多言語化する場合のめんどくささを改善する方法
対応等
わけがあって、tx.exeを配布できないため、txファイルを取得して、所定の箇所に配布するという仕組みを作り上げた。
手順としては以下のとおり。
- transifexからtxクライアントをダウンロード(MSXML2.XMLHTTPからADODB.Stream経由でファイルを吸い出す)
- txクライアントの設定を行う(cmd経由でのバッチ処理)
- txクライアントで翻訳ファイル取得を行う(cmd経由でのバッチ処理)
- 所定の箇所へ配置する(FileSystemObjectでの処理)
PowerShell→断念
これらを手っ取り早く実装しようと思った場合、Windows PowerShellか...と思い、実装したところ、
署名等を行っていない場合、実行されない事実が判明。
集合の扱い等、取り回しがなれないので断念
WSH→できそう→できた
検索すると、adodb - Windows Script Host (jscript): how do i download a binary file? - Stack Overflowという質問があり、WSHでファイルをダウンロードしているコードを発見。
適当に関数に切り出し(後述のdownloadFile関数を参照)。
本体プログラム
- fetch_translations.jsとか名前をつけてプロジェクト直下に配置
- そのjsを実行する
- 黒い画面が表示されるので、必要に応じて情報を入力する。
- DONE!が表示されたら、処理完了。
なお、以下のプログラムはいかなる状態であっても無保証です。責任取りません。
stackoverflowで回答のあったdownloadFile関数以外についてはWTFPLライセンスとします。
//各種設定を行う(プロジェクト名は必ず設定すること!) var settingTransifexProjectUrl = "https://www.transifex.com/projects/p/プロジェクト名"; //txクライアントのダウンロード用URL var settingTransifexClient = "http://files.transifex.com/transifex-client/0.10/tx.exe"; //transifexのホストURL(基本的に変わることはない認識) var settingTransifexHost = "https://www.transifex.com"; //現在のディレクトリを取得する var objWS = WScript.CreateObject("WScript.Shell"); var pathRoot = objWS.CurrentDirectory; d("CurrentDirectory",objWS.CurrentDirectory); //作業用ディレクトリ(transifex)を作成する var objFSO = WScript.CreateObject('Scripting.FileSystemObject'); var pathWork = objFSO.BuildPath(pathRoot,"transifex"); if(!objFSO.FolderExists(pathWork)){ d("CreateDirectory",pathWork); objFSO.CreateFolder(pathWork); } //txクライアントをダウンロードする(transifex/tx.exe) var pathTxClient = objFSO.BuildPath(pathWork,"tx.exe"); if(!objFSO.FileExists(pathTxClient)){ d("Download",settingTransifexClient); if(!downloadFile(objFSO, settingTransifexClient ,pathTxClient)){ e("Cannot download client. Abort",settingTransifexClient); } d("Download done",pathTxClient); } //transifexをセットアップするバッチファイルを作成する(transifex/fetch.bat) var pathBatch = objFSO.BuildPath(pathWork,"fetch.bat"); if (objFSO.FileExists(pathBatch)){ d("Delete file",pathBatch); objFSO.DeleteFile(pathBatch); } //.tx フォルダがある場合は一部処理を省略する var pathTxConfig = objFSO.BuildPath(pathWork,".tx"); var isSkipInit = ""; if(objFSO.FolderExists(pathTxConfig)){ isSkipInit = "rem "; } var objTextFile = objFSO.CreateTextFile(pathBatch, true); objTextFile.WriteLine("@echo off"); objTextFile.WriteLine("cd " + pathWork); objTextFile.WriteLine(isSkipInit + "echo transifex initalize..."); objTextFile.WriteLine(isSkipInit + "tx init --host=" + settingTransifexHost); objTextFile.WriteLine(isSkipInit + "echo."); objTextFile.WriteLine(isSkipInit + "echo."); objTextFile.WriteLine("echo setup transifex: " + settingTransifexProjectUrl); objTextFile.WriteLine("tx set --auto-remote " + settingTransifexProjectUrl); objTextFile.WriteLine("echo."); objTextFile.WriteLine("echo."); objTextFile.WriteLine("echo fetch data..."); objTextFile.WriteLine("tx pull -a -s"); objTextFile.WriteLine("echo done. Press ANY key to continue"); objTextFile.WriteLine("pause"); objTextFile.WriteLine(""); objTextFile.Close(); //ファイルを取得する前に取得するフォルダがない状態にする(transifex/translationsを削除) var pathTranslation = objFSO.BuildPath(pathWork,"translations"); if(objFSO.FolderExists(pathTranslation)){ d("Delete folder force",pathTranslation); objFSO.DeleteFolder(pathTranslation,true); } //バッチファイルを実行する(transifex/fetch.bat) d("run batch",pathBatch); objWS.Run("cmd /C " + pathBatch,1,true); d("done batch",pathBatch); //結果を確認し、フォルダができていない(取得できていない)場合終了する if(!objFSO.FolderExists(pathTranslation)){ e("Folder is not exists. Failed to fetch data",pathTranslation); } //取得したファイルを配布する //最初は以下の処理を組み込まずに実行して、transifex/translationsに何ができるのかを確認したほうがよい。 //確認したら、次のように配置するものを設定する。(!!必ず設定すること!!) //例: // map(objFSO,pathTranslation+"/androidproject.strings",pathRoot+"/AndroidProject/src/main/res/values-XX","strings.xml"); // 上記設定の場合、以下のとおりファイルを配置します。(フォルダがない場合は自動的に作成します) // XXとなっている箇所をそれぞれのロケール(en/ja)に置き換えます。(ファイル名の箇所にXXがあっても同様。大文字小文字は区別されます) // デフォルトでenとしているので、変更する必要がある場合はmap関数のところでreplaceしているのでその箇所を修正してください。 // transifex/translations/androidproject.strings/en.xml → AndroidProject/src/main/res/values/string.xml // transifex/translations/androidproject.strings/ja.xml → AndroidProject/src/main/res/values-ja/string.xml // WScript.Echo("DONE!"); //処理終了 //////////////////////////////////////////////// /* Download file * inherit from http://stackoverflow.com/questions/4164400/windows-script-host-jscript-how-do-i-download-a-binary-file */ function downloadFile(File,Source,Target){ var Object = WScript.CreateObject('MSXML2.XMLHTTP'); Object.Open('GET', Source, false); Object.Send(); if (Object.Status != 200) { return false; } // Create the Data Stream var Stream = WScript.CreateObject('ADODB.Stream'); // Establish the Stream Stream.Open(); Stream.Type = 1; // adTypeBinary Stream.Write(Object.ResponseBody); Stream.Position = 0; // Create an Empty Target File if (File.FileExists(Target)) { File.DeleteFile(Target); } // Write the Data Stream to the File Stream.SaveToFile(Target, 2); // adSaveCreateOverWrite Stream.Close(); Object = null; Stream = null; return true; } function d(title,str){ //WScript.Echo(title + ": " + str); } function e(title,str){ WScript.Echo("ERROR! " + title + ": " + str); WScript.Quit(); } function map(fso,pathFrom,pathToFoler,pathToFile){ var pathTargetFolder = parsePath(fso, pathToFoler); var folder = fso.GetFolder(parsePath(fso, pathFrom)); var folderfiles = new Enumerator(folder.Files); for (; !folderfiles.atEnd(); folderfiles.moveNext()){ var pathFromFile = folderfiles.item(); var itemname = fso.GetBaseName(pathFromFile); var pathSendFolder = pathTargetFolder.replace("XX",itemname).replace("values-en","values"); var pathSendFile = pathToFile.replace("XX",itemname); if(!fso.FolderExists(pathSendFolder)){ fso.CreateFolder(pathSendFolder); } d("copy",pathFromFile); fso.CopyFile (pathFromFile,fso.BuildPath(pathSendFolder,pathSendFile), true); } } //単純に説明すると、(Windowsにおいては、)「/」区切りを「\」区切りにする function parsePath(fso, pathToFolder){ var pathTarget = ""; var arrayPath = pathToFolder.split("/"); for (var idx in arrayPath){ var item = arrayPath[idx]; if(item == ""){ } else if(pathTarget == ""){ pathTarget = item; } else { pathTarget = fso.BuildPath(pathTarget,item); } } return pathTarget; }