diff --git a/lang.js b/lang.js new file mode 100644 index 0000000000..44f2f8f320 --- /dev/null +++ b/lang.js @@ -0,0 +1,157 @@ +// node lang.js espocrm-nl_NL.po lastRelease nl_NL + +if (process.argv.length < 2) { + throw new Error('No dir argument passed'); +} + +var path = require('path'); +var fs = require('fs'); +var nodePath = require('path'); +var os = require('os'); +var isWin = /^win/.test(os.platform()); + +var espoPath = path.dirname(fs.realpathSync(__filename)) + ''; + +var resLang = process.argv[2] || 'lang_LANG'; + +var poPath = process.argv[3] || espoPath + '/build/' + 'espocrm-' + resLang +'.po'; + + +var deleteFolderRecursive = function (path) { + var files = []; + if( fs.existsSync(path) ) { + files = fs.readdirSync(path); + files.forEach(function(file,index){ + var curPath = path + "/" + file; + if(fs.lstatSync(curPath).isDirectory()) { // recurse + deleteFolderRecursive(curPath); + } else { // delete file + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(path); + } +}; + + +function Lang (poPath, espoPath) { + this.poPath = poPath; + + this.espoPath = espoPath; + if (this.espoPath.substr(-1) != '/') { + this.espoPath += '/'; + } + + this.currentPath = path.dirname(fs.realpathSync(__filename)) + '/'; + + this.moduleList = ['Crm']; + this.baseLanguage = 'en_US'; + + var dirNames = this.dirNames = {}; + + var resDirNames = this.resDirNames = {}; + + var coreDir = this.espoPath + 'application/Espo/Resources/i18n/' + this.baseLanguage + '/'; + var dirs = [coreDir]; + dirNames[coreDir] = 'application/Espo/Resources/i18n/' + resLang + '/'; + + this.moduleList.forEach(function (moduleName) { + var dir = this.espoPath + 'application/Espo/Modules/' + moduleName + '/Resources/i18n/' + this.baseLanguage + '/'; + dirs.push(dir); + dirNames[dir] = 'application/Espo/Modules/' + moduleName + '/Resources/i18n/' + resLang + '/'; + }, this); + + var installDir = this.espoPath + 'install/core/i18n/' + this.baseLanguage + '/'; + dirs.push(installDir); + dirNames[installDir] = 'install/core/i18n/' + resLang + '/'; + + this.dirs = dirs; +}; + +Lang.prototype.escape = function(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); +}; + +Lang.prototype.run = function () { + + var translationHash = {}; + var dirs = this.dirs; + + var contents = fs.readFileSync(this.poPath, 'utf8'); + var matches = contents.match(new RegExp('msgid (\"(.*)\".*\n)+.*msgstr (\"(.*)\"(\n)?)+', 'g')); + + matches.forEach(function (part) { + + //remove line break "\n" + part = part.replace(/"\n"/g, ''); + + var res = part.match(new RegExp('msgid \"(.*)\".*\n.*msgstr \"(.*)\"')); + translationHash[res[1]] = res[2]; + }, this); + + dirs.forEach(function (path) { + var resDirPath = this.dirNames[path]; + + var resPath = this.currentPath + 'build/' + resLang + '/' + resDirPath; + + if (!fs.existsSync(resPath)) { + var d = ''; + resPath.split('/').forEach(function (f) { + + if (!f) { + return; + } + + if (isWin) { + d = nodePath.join(d, f); + } else { + d += '/' + f; + } + + if (!fs.existsSync(d)) { + fs.mkdirSync(d); + } + }); + } + + var list = fs.readdirSync(path); + list.forEach(function (fileName) { + var filePath = path + fileName; + var resFilePath = resPath + '/' + fileName; + + var contents = fs.readFileSync(filePath, 'utf8'); + + for (var key in translationHash) { + + if (!translationHash[key].trim()) { + continue; + } + + var escapedKey = this.escape(key); + contents = contents.replace(new RegExp(': *\"(' + escapedKey + ')\"', 'g'), ': "' + translationHash[key] + '"'); + } + + for (var key in translationHash) { + if (key.substr(0, 2) == "\\\"") { + + if (!translationHash[key].trim()) { + continue; + } + + var escapedKey = this.escape(key); + contents = contents.replace(new RegExp('(' + escapedKey + ')', 'g'), '' + translationHash[key].replace(/\\"/g, '"') + ''); + } + } + + if (fs.existsSync(resFilePath)) { + fs.unlinkSync(resFilePath); + } + fs.writeFileSync(resFilePath , contents); + + }, this); + + }, this); +}; + +var lang = new Lang(poPath, espoPath); +lang.run(); \ No newline at end of file diff --git a/po.js b/po.js new file mode 100644 index 0000000000..465070aae4 --- /dev/null +++ b/po.js @@ -0,0 +1,186 @@ +//to run: node po.js lastRelease es_ES + +if (process.argv.length < 2) { + throw new Error('No dir argument passed'); +} + +var path = require('path'); +var fs = require('fs'); + +var language = process.argv[2]; + +var espoPath = path.dirname(fs.realpathSync(__filename)) + ''; + +function PO (espoPath, language) { + this.moduleList = ['Crm']; + this.baseLanguage = 'en_US'; + this.language = language || this.baseLanguage; + + this.currentPath = path.dirname(fs.realpathSync(__filename)) + '/'; + + this.outputFileName = 'espocrm-' + this.language + '.po'; + + this.path = espoPath; + if (this.path.substr(-1) != '/') { + this.path += '/'; + } + + var dirs = [ + this.path + 'application/Espo/Resources/i18n/', + this.path + 'install/core/i18n/' + ]; + this.moduleList.forEach(function (moduleName) { + dirs.push(this.path + 'application/Espo/Modules/' + moduleName + '/Resources/i18n/'); + }, this); + + this.dirs = dirs; + + this.poContentHeader = 'msgid ""\n' + + 'msgstr ""\n' + + '"Project-Id-Version: \\n"\n' + + '"POT-Creation-Date: \\n"\n' + + '"PO-Revision-Date: \\n"\n' + + '"Last-Translator: \\n"\n' + + '"Language-Team: EspoCRM \\n"\n' + + '"MIME-Version: 1.0\\n"\n' + + '"Content-Type: text/plain; charset=UTF-8\\n"\n' + + '"Content-Transfer-Encoding: 8bit\\n"\n' + + '"Language: ' + this.language + '\\n"\n\n'; +}; + +PO.prototype.run = function () { + var dirs = this.dirs; + var messageList = []; + var langMessageList = []; + var self = this; + var poContents = this.poContentHeader; + + dirs.forEach(function (path) { + + var dirPath = this.getDirPath(path, self.baseLanguage); + + var list = fs.readdirSync(dirPath); + list.forEach(function (fileName) { + + var filePath = this.getDirPath(path, self.baseLanguage) + fileName; + messageList = this.getMessageList(filePath, messageList); + + if (self.language != self.baseLanguage) { + var langFilePath = this.getDirPath(path, self.language) + fileName; + langMessageList = this.getMessageList(langFilePath, langMessageList); + } + + }, this); + + }, this); + + if (self.language == self.baseLanguage) { + langMessageList = messageList; + } + + for(var index in messageList) { + poContents += 'msgid "' + messageList[index] + '"\n'; + + var langMessage = langMessageList[index] || ""; + poContents += 'msgstr "' + langMessage + '"\n\n'; + } + + var resFilePath = this.currentPath + 'build/' + this.outputFileName; + + if (fs.existsSync(resFilePath)) { + fs.unlinkSync(resFilePath); + } + + fs.writeFileSync(resFilePath, poContents); +}; + +PO.prototype.getMessageList = function (filePath, currentMessageList) { + if (!fs.existsSync(filePath)) { + return currentMessageList; + } + + var data = fs.readFileSync(filePath, 'utf8'); + data = JSON.parse(data); + + currentMessageList = this.convertToSigleObject(data, '', currentMessageList); + + return currentMessageList; +} + +PO.prototype.getDirPath = function (path, language) { + var dirPath = path + language + '/'; + return dirPath; +} + +PO.prototype.convertToSigleObject = function (dataObject, prefix, currentMessageList) { + + prefix = prefix || ''; + + for(var index in dataObject) { + if (dataObject[index] === null || dataObject[index] === "") { + continue; + } + + if (Array.isArray(dataObject[index])) { + dataObject[index] = '"' + dataObject[index].join('", "') + '"'; + } + + if (typeof dataObject[index] === 'object') { + var nextPrefix = prefix + index + "."; + currentMessageList = this.convertToSigleObject(dataObject[index], nextPrefix, currentMessageList); + } else { + if (!this.objectIndexOf(dataObject[index], currentMessageList)) { + var key = prefix + index; + key = this.checkFixDuplicateKey(key, currentMessageList); + var savedString = this.fixString(dataObject[index]); + + currentMessageList[key] = savedString; + } + } + } + + return currentMessageList; +} + +PO.prototype.objectIndexOf = function (value, data) { + for(var index in data) { + if (data[index] !== null && data[index] === value) { + return true; + } + } + return false; +} + +PO.prototype.checkFixDuplicateKey = function (key, data) { + if (this.objectIndexOfKey(key, data)) { + key = key + "+"; + key = this.checkFixDuplicateKey(key, data); + } + return key; +} + +PO.prototype.objectIndexOfKey = function (key, data) { + for(var index in data) { + if (index !== null && index === key) { + return true; + } + } + return false; +} + +PO.prototype.replaceAll = function (string, find, replace) { + escapedRegExp = find.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); + return string.replace(new RegExp(escapedRegExp, 'g'), replace); +} + +PO.prototype.fixString = function (savedString) { + savedString = this.replaceAll(savedString, '"', '\\"'); + savedString = this.replaceAll(savedString, "\n", '\\n'); + savedString = this.replaceAll(savedString, "\t", '\\t'); + return savedString; +} + +var po = new PO(espoPath, language); + +po.run(); +