//*************************************************************************************************************************************************************************** *//
//* 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\to\folder" folder with .\Plugins, .\Weather subfolders and =CORE=.txt file                                             *//
//* Usage:    cscript /nologo translate.js /dupes:"path\=dupes=.txt" use dupes file                                                                                           *//
//* Usage:    cscript /nologo translate.js /sourcelang:"language" instead of /path param if your .\Plugins, .\Weather, =CORE=.txt and langpack_%lang%.txt in trunk .\langpacks*//
//* Usage:    cscript /nologo translate.js /out:"path\folder" 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-only strings output to separated files                                                      *//
//* Usage:    cscript /nologo translate.js /popuntranslated:"yes"  remove untranslated string and empty line below from output files                                          *//
//* Usage:    cscript /nologo translate.js /sourcelang:"russian" /release:"path\file"                                                                                         *//
//* 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=.txt file                                                                                                         *//
//* 4) Still no luck? Well, check a /langpack:"/path/lang.txt" as a last place.                                                                                               *//
//* Example1:  cscript /nologo translate.js /langpack:"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 & out langpack in path\file *//
//* Example4:  cscript /nologo translate.js /sourcelang="Russian" /outfile:"path\file" will translate all /english/* tempaltes using files =CORE=.txt, =DUPES=.txt,           *//
//* lanpack_Russian.txt from ./langpacks/Russian/ folder, including /langpacks/Russian/Plugins/*                                                                              *//
//* Example5:  cscript translate.js /sourcelang:"Russian" /release:"Langpack_rusian.txt" will output a "release" version of langpack, using files in \langpacks\russian\ with *//
//* =HEAD=.txt, but "clean" - no file reference and no untranslated strings inside                                                                                            *//
//*****************************************************************************************************************************************************************************//

//Init Variables
//Create FileSystemObject FSO
var FSO = WScript.CreateObject("Scripting.FileSystemObject");
//disabling log by default
var log = false;
//output translated templates in separated files by default
var outfile = false;
//do not remove reference to source file, where we found a translation string
var noref = false;
//disable output untranslated_* files by default
var untranslated = false;
//include untranslated strings and empty line below in output files by default
var popuntranslated = false;
//disable release output by default
var release = false;
//Path variables
var scriptpath = FSO.GetParentFolderName(WScript.ScriptFullName);
var UnTranslatedPath = "";
//get path two layers upper "\tools\lpgen\"
var trunkPath = FSO.GetParentFolderName(FSO.GetParentFolderName(scriptpath));
//path to "English" langpack
var langpackenglish = "\\langpacks\\english\\";
//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
var CoreTranslateDict = WScript.CreateObject("Scripting.Dictionary");
var DupesTranslateDict = WScript.CreateObject("Scripting.Dictionary");
var LangpackTranslateDict = WScript.CreateObject("Scripting.Dictionary");
//init arrays
var Translated_Core_Array = [];
var UnTranslated_Core_Array = [];
var VariousTranslate_Array = [];
var full_langpack_array = [];
var release_array = [];

//*********************************************************************************//
//                         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 ref's to files, specify /noref:"yes"
if (WScript.Arguments.Named.Item("noref")) {
    noref = true;
}

// if console param /untranslated: specified, put it to var untranslated.
// To output untranslated_* files, specify /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") {
        CreateFldr(UnTranslatedPath);
    }
}

//if console param /popuntranslated: specified, put it to var popuntranslated
if (WScript.Arguments.Named.Exists("popuntranslated")) {
    popuntranslated = true;
}

// if console pararm /outpfile:"\path\filename.txt" given, put it to var outfile.
if (WScript.Arguments.Named.Exists("outfile")) {
    outfile = true;
    //path to full langpack file
    var full_langpack_file = WScript.Arguments.Named.Item("outfile");
}
// if console pararm /release:"\path\filename.txt" given, put it to var release.
if (WScript.Arguments.Named.Exists("release")) {
    release = true;
    //path to full langpack file
    var release_langpack_file = WScript.Arguments.Named.Item("release");
}

// if param /out specified, build a path and put it into var.
if (WScript.Arguments.Named.Exists("out")) {
    var out = WScript.Arguments.Named.Item("out");
    var OutPlugins = FSO.BuildPath(out, "Plugins");
    var OutWeather = FSO.BuildPath(out, "Weather");
    CreateFldr(out);
    CreateFldr(OutPlugins);
    CreateFldr(OutWeather);
}

//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 /sourcelang specified, setup all source files already existed in trunk.
//Useful for running translate.js from trunk.
// Currently seldom languages have same files structure,
//thus it is much more easier to just specify a language folder name,
//instead of specifying /path, /dupes, /langpack.
if (WScript.Arguments.Named.Item("sourcelang")) {
    var sourcelang = WScript.Arguments.Named.Item("sourcelang");
    var langpack_path = FSO.BuildPath(FSO.BuildPath(trunkPath, "langpacks"), sourcelang);
    var translated_plugins = FSO.BuildPath(langpack_path, "Plugins");
    var translated_weather = FSO.BuildPath(langpack_path, "Weather");
    var translated_core = FSO.BuildPath(langpack_path, "=CORE=.txt");
    var translated_var = FSO.BuildPath(langpack_path, "Plugins\\_Various.txt");
    var translated_dupes = FSO.BuildPath(langpack_path, "=DUPES=.txt");
    var langpack_head = FSO.BuildPath(langpack_path, "=HEAD=.txt");
    var translated_langpack = FSO.BuildPath(langpack_path, ("langpack_" + sourcelang + ".txt"));
    if (log) {
        WScript.Echo("Translating to " + sourcelang);
    }
}

//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 = WScript.Arguments.Named.Item("plugin");
    //init array for our file translation and untranslated strings
    var cmdline_file_array = [];
    var cmdline_untranslated_array = [];
    //Output filename variable
    var traslated_cmdline_file = FSO.BuildPath(scriptpath, FSO.GetFileName(cmdline_file));
    var untranslated_cmdline_file = 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.
    WriteToUnicodeFileNoBOM(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) {
        WriteToUnicodeFileNoBOM(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 strngs specified.
checkparams();
if (log) {
    WScript.Echo("Translation begin");
}

//Add a =HEAD=.txt into FullLangpack Array and release array if file exist and /out or /release specified.
if ((outfile || release) && FSO.FileExists(langpack_head)) {
    //open file
    stream.Open();
    stream.LoadFromFile(langpack_head);
    //read file into var
    var headertext = stream.ReadText();
    full_langpack_array.push(headertext);
    release_array.push(headertext);
    stream.Close();
}

//Generate translation dictionaries from /path, /dupes and /langpack files.
GenerateDictionaries();

if (log) {
    WScript.Echo("Translating Core");
}
//Call function for translate core template
TranslateTemplateFile(FSO.BuildPath(trunkPath, langpackenglish + "=CORE=.txt"), Translated_Core_Array, UnTranslated_Core_Array);

full_langpack_array = full_langpack_array.concat(VariousTranslate_Array);
release_array = release_array.concat(VariousTranslate_Array);

//output core file, if /out specified.
if (out) {
    OutputFiles(Translated_Core_Array, UnTranslated_Core_Array, "", "=CORE=.txt");
}


//Init array of template files
var TemplateFilesArray = [];
//Init array of weather.ini translation files
var WeatherFilesArray = [];
//Find all template files and put them to array
FindFiles(FSO.BuildPath(trunkPath, langpackenglish + "plugins\\"), "\\.txt$", TemplateFilesArray);
//Find all weather.ini template files and add them into array
FindFiles(FSO.BuildPath(trunkPath, langpackenglish + "Weather\\"), "\\.txt$", WeatherFilesArray);
//Build enumerator for each file array
var TemplateFilesEnum = new Enumerator(TemplateFilesArray);
var WeatherFilesEnum = new Enumerator(WeatherFilesArray);
//Run processing files one-by-one;
ProcessFiles(TemplateFilesEnum);

//if output to one langpack file, write a final array Translated_Core_Array into UTF-8 file with BOM
if (outfile) {
    WriteToUnicodeFileNoBOM(full_langpack_array, full_langpack_file);
    if (log) {
        WScript.Echo("Langpack file in " + full_langpack_file);
    }
}
//if /release specified, output array into file
if (release) {
    WriteToUnicodeFileNoBOM(release_array, release_langpack_file);
    if (log) {
        WScript.Echo("Release langpack file in " + release_langpack_file);
    }
}
if (log) {
    WScript.Echo("Translation end");
}


//*********************************************************************************//
//                                    Functions                                   *//
//*********************************************************************************//

//Process files one-by-one using enummerator
function ProcessFiles(FilesEnum) {
    var TranslatedTemplate = [],
        UnTranslatedStrings = [],
        curfile;
    //cycle through file list
    while (!FilesEnum.atEnd()) {
        //empty strings
        TranslatedTemplate = [];
        UnTranslatedStrings = [];
        //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 /out specified
        if (out) {
            OutputFiles(TranslatedTemplate, UnTranslatedStrings, FSO.GetBaseName(FSO.GetParentFolderName(curfile)), FSO.GetFileName(curfile));
        }
        //move to next file
        if (FSO.GetBaseName(curfile) == "Weather") {
            ProcessFiles(WeatherFilesEnum);
        }
        FilesEnum.moveNext();
    }
}

//Create Folder function, if folder does not exist.
function CreateFldr(FolderPathName) {
    if (!FSO.FolderExists(FolderPathName)) {
        var CreatedFolder = FSO.CreateFolder(FolderPathName);
        if (log) {
            WScript.Echo("Folder created: " + CreatedFolder);
        }
    }
}
//output to files. Checking params, and output file(s).
function OutputFiles(TranslatedArray, UntranslatedArray, FolderName, FileName) {
    //clear var outpath
    var outpath = "",
        TraslatedTemplateFile = "",
        UnTranslatedFile = "";
    //outpath is a /out:"path" + FolderName
    outpath = FSO.BuildPath(out, FolderName);
    //define default path to files in "langpacks\english\plugins"
    TraslatedTemplateFile = trunkPath + langpackenglish + "plugins\\translated_" + FileName;
    UnTranslatedFile = trunkPath + langpackenglish + "plugins\\untranslated_" + FileName;

    //redefine path to files, if /out specified
    if (out) {
        TraslatedTemplateFile = FSO.BuildPath(outpath, FileName);
        UnTranslatedFile = out + "\\untranslated_" + FileName;
    }

    //redefine path to files, if FileName is a =CORE=.txt
    if (FileName == "=CORE=.txt") {
        TraslatedTemplateFile = trunkPath + langpackenglish + "translated_" + FileName;
        UnTranslatedFile = trunkPath + langpackenglish + "untranslated_" + FileName;
        if (out) {
            // if /out:"/path/folder" specified
            // redefine path of translated and untranslated =CORE=.txt file to parent folder of specified path
            TraslatedTemplateFile = FSO.BuildPath(outpath, FileName);
            // if /untranslated:"yes" specified, redefine untranslated core to parent folder, same as above.
            UnTranslatedFile = FSO.BuildPath(outpath, "untranslated_" + FileName);
        }
        // if /untralsated:"path" specified, redefine path to untranslated "=CORE=.txt"
        if (untranslated && (UnTranslatedPath != "yes")) {
            UnTranslatedFile = FSO.BuildPath(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);
        }
        WriteToUnicodeFileNoBOM(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/" specified
        if (UnTranslatedPath != "yes") {
            UnTranslatedFile = UnTranslatedPath + "\\" + FileName;
        }
        if (log) {
            WScript.Echo("Untranslated in: " + UnTranslatedFile);
        }
        WriteToUnicodeFileNoBOM(UntranslatedArray, UnTranslatedFile);
    }
}

//when /sourcelang: and /path: are NOT specified,
//thus we don't have any langpack file(s) to get translated strings.
//Thus all other job are useless
function checkparams() {
    if (!WScript.Arguments.Named.Exists("langpack") && !WScript.Arguments.Named.Exists("path") && !sourcelang) {
        WScript.Echo("you didn't specify /langpack:\"/path/to/langpack.txt\", /path:\"/path/to/plugnis/\"  or /sourcelang:\"language\" 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) && log) {
        WScript.Echo("Can't find " + file);
    }
}

//Generate DB with translations from Core and Dupes files
function GenerateDictionaries() {
    var PathToCore = "",
        PathToDupes = "";
    //if /sourcelang:"language" specified, use it for generate dicitionaries
    if (sourcelang) {
        CheckFileExist(translated_core);
        CheckFileExist(translated_dupes);
        CheckFileExist(translated_langpack);
        LoadTranslateDict(translated_var, VariousTranslate_Array);
        GenerateTranslateDict(translated_core, CoreTranslateDict);
        GenerateTranslateDict(translated_dupes, DupesTranslateDict);
        GenerateTranslateDict(translated_langpack, LangpackTranslateDict);
    }
    //process a dictionaries creation with switch-specified pathes
    if (WScript.Arguments.Named.Item("path")) {
        //Check file =CORE=.txt and =DUPES=.txt exist in /path:"path" or not
        PathToCore = FSO.BuildPath(WScript.Arguments.Named.Item("path"), "\\=CORE=.txt");
        PathToDupes = FSO.BuildPath(WScript.Arguments.Named.Item("path"), "\\=DUPES=.txt");
        CheckFileExist(PathToCore);
        CheckFileExist(PathToDupes);
        //Generate dictionanries
        GenerateTranslateDict(PathToCore, CoreTranslateDict);
        GenerateTranslateDict(PathToDupes, DupesTranslateDict);
    }
    if (WScript.Arguments.Named.Item("dupes")) {
        CheckFileExist(WScript.Arguments.Named.Item("dupes"));
        GenerateTranslateDict(WScript.Arguments.Named.Item("dupes"), DupesTranslateDict);
    }
    if (WScript.Arguments.Named.Item("langpack")) {
        CheckFileExist(WScript.Arguments.Named.Item("langpack"));
        GenerateTranslateDict(WScript.Arguments.Named.Item("langpack"), LangpackTranslateDict);
    }
}

//Generate Dictionary with english sting + translated string from file
function GenerateTranslateDict(file, dictionary) {
    var string = [],
        key = "",
        item = "",
        lowerKey = "";
    //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
        //first match as original string [....], is a key of dictionary,
        //second match is a translation - item of key in dictionary 
        key = string[1];
        item = string[2];
        //ignore "translations" (wrongly parsed untranslated strings) begining and ending with []
        if (item.match(/^\[.*\]$/)) {
            continue;
        }
        //add key-item pair into dictionary, if not exists already
        if (!dictionary.Exists(key)) {
            dictionary.Add(key, item);
        }
        // add key-item pair for case-insensitive match (with prefix so it won't interfere with real keys)
        lowerKey = "__lower__" + key.toLowerCase();
        if (!dictionary.Exists(lowerKey)) {
            dictionary.Add(lowerKey, 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();
}

function LoadTranslateDict(file, array) {
    var string = [],
        key = "",
        item = "",
        lowerKey = "";
    //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
        //first match as original string [....], is a key of dictionary,
        //second match is a translation - item of key in dictionary 
        array.push(string[1]);
        array.push(string[2]);
    }
    //close file
    stream.Close();
}

//Generate array with stirngs from translation template, adding founded translation, if exist.
function TranslateTemplateFile(Template_file, translated_array, untranslated_array) {
    var englishstring = "",
        refline = "",
        headerline = "";
    //Init PluginTranslate Dictionary from plugins translate file
    var PluginTranslateDict = WScript.CreateObject("Scripting.Dictionary");
    //if /sourcelang specified, use it for search plugin translation.
    if (sourcelang) {
        GenerateTranslateDict(FSO.BuildPath(langpack_path, (FSO.GetBaseName(FSO.GetParentFolderName(Template_file)) + "\\" + FSO.GetFileName(Template_file))), PluginTranslateDict);
    }
    // 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
        GenerateTranslateDict(FSO.BuildPath(WScript.Arguments.Named.Item("path"), (FSO.GetBaseName(FSO.GetParentFolderName(Template_file)) + "\\" + 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 reference to "; file source\file\path" in template or langpack,
        //put into array every line
        if (!noref) {
            translated_array.push(line);
        }
        //RegExp matching strings, starting from ";file"
        refline = 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 /noref enabled, check string and if not matched, add to array
        if (noref && (!refline && !headerline)) {
            translated_array.push(line);
        }
        //same for /release
        if (release && (!refline && !headerline)) {
            release_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) {
            var cycle = -1;
            var found = false;
            var search = line;

            while (cycle < 1 && !found) {
                cycle = cycle + 1;
                if (cycle == 1) {
                    // second cycle, try to be case-insensitive now
                    search = "__lower__" + line.toLowerCase();
                }

                //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(search)) {
                    //yes, we have translation, put translation into array
                    translated_array.push(PluginTranslateDict.Item(search));
                    //add translation to release array
                    release_array.push(PluginTranslateDict.Item(search));
                    found = true;
                } else {
                    //if we do not found string in plugin translation, check Dupes and if found, put to array
                    if (DupesTranslateDict.Exists(search)) {
                        translated_array.push(DupesTranslateDict.Item(search));
                        release_array.push(DupesTranslateDict.Item(search));
                        found = true;
                    } else {
                        //not found in dupes? Check CORE
                        if (CoreTranslateDict.Exists(search)) {
                            translated_array.push(CoreTranslateDict.Item(search));
                            release_array.push(CoreTranslateDict.Item(search));
                            found = true;
                        } else {
                            //still no luck? Check Langpack...
                            if (LangpackTranslateDict.Exists(search)) {
                                translated_array.push(LangpackTranslateDict.Item(search));
                                release_array.push(LangpackTranslateDict.Item(search));
                                found = true;
                            }
                        }
                    }
                }
            }

            if (!found) {
                //no translation found, put empty line if popuntranslated disabled
                if (!popuntranslated) {
                    translated_array.push("");
                }
                //add to untranslated array
                untranslated_array.push(line);
                //remove from release, no translation found.
                release_array.pop();
                //remove from translated array if popuntranslated enabled.
                if (popuntranslated) {
                    translated_array.pop();
                }
            }
        }
    }
    //closing file
    stream.Close();
    //if we will output one file only, concatenate array
    if (outfile) {
        full_langpack_array = full_langpack_array.concat(translated_array);
    }
}

//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) {
    var i = 0,
        len = 0;

    stream.Open();
    len = array.length - 1;
    for (i = 0; i <= len; i++) {
        stream.WriteText(array[i] + "\r\n");
    }
    stream.SaveToFile(langpack, 2);
    stream.Close();
}

//Write file as UTF-8 without BOM
function WriteToUnicodeFileNoBOM(array, filename) {
    var UTFStream = WScript.CreateObject("ADODB.Stream");
    var BinaryStream = WScript.CreateObject("ADODB.Stream");
    var i = 0,
        len = 0,
        adTypeBinary = 1,
        adTypeText = 2,
        adModeReadWrite = 3,
        adSaveCreateOverWrite = 2;

    UTFStream.Type = adTypeText;
    UTFStream.Mode = adModeReadWrite;
    UTFStream.Charset = "utf-8";
    UTFStream.Open();
    
    len = array.length - 1;
    for (i = 0; i <= len; i++) {
        UTFStream.WriteText(array[i] + "\r\n");
    }

    UTFStream.Position = 3; // skip BOM
    BinaryStream.Type = adTypeBinary;
    BinaryStream.Mode = adModeReadWrite;
    BinaryStream.Open();

    // Strips BOM (first 3 bytes)
    UTFStream.CopyTo(BinaryStream);

    BinaryStream.SaveToFile(filename, adSaveCreateOverWrite);
    BinaryStream.Flush();
    BinaryStream.Close();
    UTFStream.Close();
}