//*****************************************************************************************// //* Name: translate.js *// //* Language: JScript *// //* Function: Parse Miranda-NG translation templates and get translated strings *// //* Author: BasiL *// //* Usage: cscript /nologo translate.js to run generation in batches *// //* Usage: cscript /nologo translate.js /log:"yes" to enable console logging *// //* Usage: cscript /nologo translate.js /plugin:"path\file" for one template *// //* Usage: cscript /nologo translate.js /path:"path\plugins" translated plugins folder*// //* Usage: cscript /nologo translate.js /core:"path\=core=.txt" use core file *// //* Usage: cscript /nologo translate.js /dupes:"path\=dupes=.txt" use dupes file *// //* Usage: cscript /nologo translate.js /out:"path\plugins" output result to folder *// //* Usage: cscript /nologo translate.js /outfile:"path\file" output result to one file*// //* Usage: cscript /nologo translate.js /langpack:"path\lang.txt" - Full langpack *// //* Usage: cscript /nologo translate.js /noref:"yes" - remove ref. ";file path\file" *// //* Usage: cscript /nologo translate.js /untranslated:"yes|path" untranslated strings*// //* Note: script will use following sequense to find a translation for string: *// //* 1) Try to get translation from a same file name. Example: /langpack/english/plugin/ *// //* /TabSRMM.txt strings will be checked in file named TabSRMM.txt in folder from /path: *// //* if you specify a "path" - /path:"path\folder", so look in path\folder\TabSRMM.txt *// //* 2) If not find in step 1), check a string in file DUPES, specified in /dupes parameter*// //* 3) If still not found, try to find trasnlation in /core file *// //* 4) Still no luck? Well, check a /langpack:"/path/lang.txt" as a last place. *// //* Example1: cscript /nologo translate.js /core:"path\lang.txt" /path:"path/german" *// //* will translate english templates using plugins translation from path\german and if *// //* translation not found, try to find translation in path\lang.txt *// //* Example2: cscript /nologo translate.js /plugin:"path\file" /langpack:"path\lang.txt" *// //* will translate path\file using translation from path\lang.txt *// //* Example3: cscript /nologo translate.js /langpack:"path\lang.txt" /outfile:"path\file"*// //* will translate all /english/* templates using lang.txt & output langpack in path\file *// //*****************************************************************************************// //Init Variables //Create FileSystemObject FSO var FSO=WScript.CreateObject("Scripting.FileSystemObject"); //FileSystemObject vars var ForReading=1; var TristateUseDefault=-2; var overwritefile=true; var unicode=false; //disabling log by default var log=false; //output translated templates in separated files by default var outfile=false; //do not remove refference to source file, where we found a translation string var noref=false; //disable output untranslated_* files by default var untranslated=false; //Path variables var scriptpath=FSO.GetParentFolderName(WScript.ScriptFullName); //crazy way to get path two layers upper "\tools\lpgen\" var trunk=FSO.GetFolder(FSO.GetParentFolderName(FSO.GetParentFolderName(scriptpath))); //stream - our variable for output UTF-8 files with BOM var stream= new ActiveXObject("ADODB.Stream"); //stream var tune stream.Type = 2; // text mode stream.Charset = "utf-8"; //init translate dictionaries CoreTranslateDict=WScript.CreateObject("Scripting.Dictionary"); DupesTranslateDict=WScript.CreateObject("Scripting.Dictionary"); LangpackTranslateDict=WScript.CreateObject("Scripting.Dictionary"); //*********************************************************************************// // Checking command line parameters *// //*********************************************************************************// // if console param /log: specified, put it to var log. To enable log, specify /log:"yes" if (WScript.Arguments.Named.Item("log")) log=true; // if console param /noref: specified, put it to var noref. To remove reff's to files, specifry /noref:"yes" if (WScript.Arguments.Named.Item("noref")) noref=true; // if console param /untranslated: specified, put it to var untranslated. To output untranslated_* files, specifry /untranslated:"yes", or specify a path to output untranslated files folder if (WScript.Arguments.Named.Item("untranslated")) { untranslated=true; UnTranslatedPath=WScript.Arguments.Named.Item("untranslated") if (WScript.Arguments.Named.Item("untranslated").toLowerCase()!="yes") { if (!FSO.FolderExists(UnTranslatedPath)) { WScript.Echo("Output folder "+UnTranslatedPath+" does not exist!\nPlease, create it first!"); WScript.Quit();} } }; // if console pararm /outpfile:"\path\filename.txt" given, put it to var outfile. if (WScript.Arguments.Named.Item("outfile")) { outfile=true; full_langpack_file=WScript.Arguments.Named.Item("outfile"); } // if param /out specified, build a path and put it into var. if (WScript.Arguments.Named.Item("out")) { var out=WScript.Arguments.Named.Item("out"); if (!FSO.FolderExists(out)) { WScript.Echo("Output folder "+out+" does not exist!\nPlease, create it first!"); WScript.Quit();} }; //If script run by double click, open choose folder dialog to choose plugin folder to parse. If Cancel pressed, quit script. if (WScript.FullName.toLowerCase().charAt(WScript.FullName.length - 11)=="w") { WScript.Echo("Please run from command line!"); WScript.Quit(); } //when /plugin: specified, parse only this path and quit if (WScript.Arguments.Named.Item("plugin")) { //First, generate DB of translations from Core and Dupes files checkparams(); GenerateDictionaries(); //plugin from command line: var cmdline_file=new String(WScript.Arguments.Named.Item("plugin")); //init array for our file translation and untranslated strings var cmdline_file_array=new Array; var cmdline_untranslated_array=new Array; //Output filename variable var traslated_cmdline_file=new String(FSO.BuildPath(scriptpath,FSO.GetFileName(cmdline_file))); var untranslated_cmdline_file=new String(FSO.BuildPath(scriptpath,"untranslated_"+FSO.GetFileName(cmdline_file))); //logging if (log) WScript.Echo("translating "+cmdline_file); //Call TranslateTemplateFile for path specified in command line argument /path:"path/to/template", output result to "scriptpath" TranslateTemplateFile(WScript.Arguments.Named.Item("plugin"),cmdline_file_array,cmdline_untranslated_array); //Output results to scriptpath folder. WriteToUnicodeFile(cmdline_file_array,traslated_cmdline_file); if (log) WScript.Echo("Translated file: "+traslated_cmdline_file); //if there is something untranslated in cmdline_untranslated_array, output to file if (cmdline_untranslated_array.length>0) { WriteToUnicodeFile(cmdline_untranslated_array,untranslated_cmdline_file); if (log) WScript.Echo("Untranslated file: "+traslated_cmdline_file); } //We are done, quit. WScript.Quit(); } //*********************************************************************************// // Main part *// //*********************************************************************************// //first, check we have files with translated stirngs specified. checkparams(); if (log) WScript.Echo("Translation begin"); //Generate translation dictionaries from /core, /dupes and /langpack files. GenerateDictionaries (); //Array for translated core & full langpack file Translated_Core_Array=new Array; UnTranslated_Core_Array=new Array; full_langpack_array=new Array; if (log) WScript.Echo("Translating Core"); //Call function for translate core template TranslateTemplateFile(FSO.BuildPath(trunk,"langpacks\\english\\=CORE=.txt"),Translated_Core_Array,UnTranslated_Core_Array); //output core file, if needed. OutputFiles(Translated_Core_Array,UnTranslated_Core_Array,"=CORE=.txt") //Init array of template files TemplateFilesArray=new Array; //Find all template files and put them to array FindFiles(FSO.BuildPath(trunk,"langpacks\\english\\plugins\\"),"\\.txt$",TemplateFilesArray) //create enumerator filesenum from filelist filesenum=new Enumerator(TemplateFilesArray); //cycle through file list while (!filesenum.atEnd()) { //intit Array with translated strings and untranslated stings TranslatedTemplate=new Array; UnTranslatedStrings=new Array; //curfile is our current file in files enumerator curfile=filesenum.item(); //Log output to console if (log) WScript.Echo("translating: "+curfile); //now put strings from template and translations into array TranslateTemplateFile(curfile,TranslatedTemplate,UnTranslatedStrings); //output files, if need OutputFiles(TranslatedTemplate,UnTranslatedStrings,FSO.GetFileName(curfile)) //move to next file filesenum.moveNext(); }; //if output to one langpack file, write a final array Translated_Core_Array into UTF-8 file with BOM if (outfile) { WriteToUnicodeFile(full_langpack_array,full_langpack_file); WScript.Echo("Langpack file in "+full_langpack_file); } if (log) WScript.Echo("Translation end"); //*********************************************************************************// // Functions *// //*********************************************************************************// //output to files. Checking params, and output file(s). function OutputFiles(TranslatedArray,UntranslatedArray,FileName) { //define default path to files in "langpacks\english\plugins" TraslatedTemplateFile=trunk+"\\langpacks\\english\\plugins\\translated_"+FileName UnTranslatedFile=trunk+"\\langpacks\\english\\plugins\\untranslated_"+FileName //redefine path to files, if /out specified if (out) { TraslatedTemplateFile=out+"\\"+FileName; UnTranslatedFile=out+"\\untranslated_"+FileName; } //redefine path to files, if FileName is a =CORE=.txt if (FileName=="=CORE=.txt") { TraslatedTemplateFile=trunk+"\\langpacks\\english\\translated_"+FileName; UnTranslatedFile=trunk+"\\langpacks\\english\\untranslated_"+FileName; if (out) { //if /out:"/path/plugins" specified redefine path of translated and untranslated =CORE=.txt file to parent folder of specified path TraslatedTemplateFile=FSO.BuildPath(FSO.GetParentFolderName(out),FileName); //if /untranslated:"yes" specified, redefine untranslated core to parent folder, same as above. UnTranslatedFile=FSO.BuildPath(FSO.GetParentFolderName(out),"untranslated_"+FileName); } //if /untralsated:"path/plugins" specified, redefine to parent folder with name "=CORE=.txt" if (UnTranslatedPath!="yes") UnTranslatedFile=FSO.BuildPath(FSO.GetParentFolderName(UnTranslatedPath),FileName); } // output translated file if /out and /outfile ommited, or if /out specified if ((!out && !outfile) || out) { if (log) WScript.Echo("Output to file: "+TraslatedTemplateFile); WriteToUnicodeFile(TranslatedArray,TraslatedTemplateFile); } //Write untranslated array into file, if /untranslated specified and there is something in array if (untranslated & UntranslatedArray.length>0) { //redefine Untranslated file path and name, if /untranslated:"/path/plugins" specified and this is not a =CORE=.txt file if (UnTranslatedPath!="yes" && FileName!="=CORE=.txt") UnTranslatedFile=UnTranslatedPath+"\\"+FileName; if (log) WScript.Echo("Untranslated in: "+UnTranslatedFile); WriteToUnicodeFile(UntranslatedArray,UnTranslatedFile); } //if we will output one file only, concatenate array if (outfile) full_langpack_array=full_langpack_array.concat(TranslatedArray); } //when /core: and /path: are NOT specified, thus we don't have any langpack file(s) to get translated strings. Thus all other job are uselessg function checkparams() { if (!WScript.Arguments.Named.Item("langpack") && !WScript.Arguments.Named.Item("path")) { WScript.Echo("you didn't specify /langpack: or /path: parameter, there is no files with translated strings!"); WScript.Quit(); }; } //Check file existense. If file not found, quit. function CheckFileExist(file) { if (!FSO.FileExists(file)) { WScript.Echo("Can't find "+file) }; } //Generate DB with translations from Core and Dupes files function GenerateDictionaries () { //path variables if (WScript.Arguments.Named.Item("core")) { //Check file /core:"path" exist or not CheckFileExist(WScript.Arguments.Named.Item("core")); //Generate dictionanry GenerateTransalteDict(WScript.Arguments.Named.Item("core"),CoreTranslateDict); } if (WScript.Arguments.Named.Item("dupes")) { CheckFileExist(WScript.Arguments.Named.Item("dupes")); GenerateTransalteDict(WScript.Arguments.Named.Item("dupes"),DupesTranslateDict); } if (WScript.Arguments.Named.Item("langpack")) { CheckFileExist(WScript.Arguments.Named.Item("langpack")); GenerateTransalteDict(WScript.Arguments.Named.Item("langpack"),LangpackTranslateDict); } } //Generate Dictionary with english sting + translated string from file function GenerateTransalteDict (file,dictionary) { //if file does not exist, it's a core, we do not need do the job again, so return. if (!FSO.FileExists(file)) return; //open file stream.Open(); stream.LoadFromFile(file); //read file into var var translatefiletext=stream.ReadText(); //"find" - RegularExpression, first string have to start with [ and end with]. Next string - translation var find=/(^\[.+?\](?=$))\r\n(^(?!\[|;file)[^\r\n]+?(?=$))/mg; //While our "find" RegExp return a results, add strings into dictionary. while ((string = find.exec(translatefiletext)) != null) { //first, init empty var var string; //first match as original string [....], is a key of dictionary, second match is a translation - item of key in dictionary var key=string[1]; var item=string[2]; //add key-item pair into dictionary, if not exists already if (!dictionary.Exists(key)) dictionary.Add(key,item); //use also different variants of Miranda name for translations from old langpacks key = key.replace("Miranda IM", "Miranda NG"); item = item.replace("Miranda IM", "Miranda NG"); if (!dictionary.Exists(key)) dictionary.Add(key,item); key = key.replace("Miranda NG", "Miranda"); item = item.replace("Miranda NG", "Miranda"); if (!dictionary.Exists(key)) dictionary.Add(key,item); } //close file stream.Close(); } //Generate array with stirngs from translation template, adding founded translation, if exist. function TranslateTemplateFile(Template_file,translated_array,untranslated_array) { //Init PluginTranslate Dictionary from plugins translate file var PluginTranslateDict=WScript.CreateObject("Scripting.Dictionary"); // if /path:"" specified, this is a folder with plugin translations, use it to find out our translation. if (WScript.Arguments.Named.Item("path")) { //Generate PluginTranslate Dictionary GenerateTransalteDict(FSO.BuildPath(WScript.Arguments.Named.Item("path"),FSO.GetFileName(Template_file)),PluginTranslateDict); } //If file zero size, return; if (FSO.GetFile(Template_file).Size==0) return; //access file stream.Open(); stream.LoadFromFile(Template_file); //Reading line-by-line while (!stream.EOS) { //clear up variable englishstring=""; //read on line var line=stream.ReadText(-2); //If we need refference to "; file source\file\path" in template or langpack, put into array every line if (!noref) translated_array.push(line); //If we need "clean" file, remove stirngs, starting from ";file" if (noref) { //RegExp matching strings, starting from ";file" reffline=line.match(/^;file.+/); //RegExp for match a =CORE=.txt header line "Miranda Language Pack Version 1". If /noref specified, remove this line as well. headerline=line.match(/^Miranda Language Pack Version 1$/) //if RegExp not matched, push line into array if (!reffline && !headerline) translated_array.push(line); } //find string covered by[] using regexp englishstring=line.match(/\[.+\]/); //If current line is english string covered by [], try to find translation in global db if (englishstring) { //uncomment next string for more verbose log output //if (log) WScript.Echo("lookin' for "+englishstring); //firstly find our string exist in Plugin translate DB dictionary if (PluginTranslateDict.Exists(line)) { //yes, we have translation, put translation into array translated_array.push(PluginTranslateDict.Item(line)); } else { //If we do not foud sting in plugin translation, check Dupes and if found, put to array if (DupesTranslateDict.Exists(line)) { translated_array.push(DupesTranslateDict.Item(line)); } else { //not found in dupes? Check CORE if (CoreTranslateDict.Exists(line)) { translated_array.push(CoreTranslateDict.Item(line)); } else { //Sill no luck? Check Langpack... if (LangpackTranslateDict.Exists(line)) { translated_array.push(LangpackTranslateDict.Item(line)); } else { //no translation found, put empty line translated_array.push(""); untranslated_array.push(line); } } } } } } //closing file stream.Close(); }; //Recourse find all files in "path" with file RegExp mask "name" and return file list into filelistarray function FindFiles (path,name,filelistarray) { //Init vars var Folder, Folders, Files, file, filename; // second param "name" is our case insensive RegExp var filemask=new RegExp(name,"i"); //Put path into var Folder Folder=FSO.GetFolder(path); //put subFolders into var Folders=new Enumerator(Folder.SubFolders); //Create Enumerator with Folder files inside Files=new Enumerator(Folder.Files); //Cycle by files in Folder while (!Files.atEnd()) { //file is a next file file=Files.item(); //put file name into filename filename=FSO.GetFileName(file); //if filename is our name mask, do the job. if (filemask.test(filename)) filelistarray.push(file); //move to next file Files.moveNext(); }; //Cycle by subfolders while (!Folders.atEnd()) { FindFiles(Folders.item().Path,name,filelistarray); //WScript.Echo(Folders.item().Path); Folders.moveNext(); }; }; //Write UTF-8 file function WriteToUnicodeFile(array,langpack) { stream.Open(); for (i=0;i<=array.length-1;i++) stream.WriteText(array[i]+"\r\n"); stream.SaveToFile(langpack, 2); stream.Close(); }