diff --git a/lang.js b/lang.js index 7f33dac3ef..cd96505c2a 100644 --- a/lang.js +++ b/lang.js @@ -24,7 +24,7 @@ * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. - ************************************************************************/ + ************************************************************************/ // node lang.js espocrm-nl_NL.po lastRelease nl_NL if (process.argv.length < 2) { @@ -35,6 +35,7 @@ var path = require('path'); var fs = require('fs'); var nodePath = require('path'); var os = require('os'); +var PO = require('pofile'); var isWin = /^win/.test(os.platform()); var espoPath = path.dirname(fs.realpathSync(__filename)) + ''; @@ -100,84 +101,139 @@ Lang.prototype.escape = function(s) { }; Lang.prototype.run = function () { - - var translationHash = {}; + var translationData = {}; var dirs = this.dirs; - var contents = fs.readFileSync(this.poPath, 'utf8'); - var matches = contents.match(new RegExp('msgid (\"(.*)\".*\n)+.*msgstr (\"(.*)\"(\n)?)+', 'g')); + PO.load(this.poPath, function (err, po) { + if (err) throw new Error("Could not parse " + this.poPath); - matches.forEach(function (part) { + po.items.forEach(function (item) { + if (!item.msgctxt) return; + var key = item.msgctxt + '__' + item.msgid; + var file = item.msgctxt.split('.')[0]; + var path = item.msgctxt.split('.').slice(1); - //remove line break "\n" - part = part.replace(/"\n"/g, ''); + var o = { + stringOriginal: item.msgid, + stringTranslated: item.msgstr[0], + context: item.msgctxt, + file: file, + path: path + }; + translationData[file] = translationData[file] || []; + translationData[file].push(o); + }); - var res = part.match(new RegExp('msgid \"(.*)\".*\n.*msgstr \"(.*)\"')); - translationHash[res[1]] = res[2]; - }, this); - dirs.forEach(function (path) { - var resDirPath = this.dirNames[path]; + dirs.forEach(function (path) { + var resDirPath = this.dirNames[path]; + var resPath = this.currentPath + 'build/' + resLang + '/' + resDirPath; - 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] + '"'); + 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); + } + }); } - for (var key in translationHash) { - if (key.substr(0, 2) == "\\\"") { + var list = fs.readdirSync(path); + list.forEach(function (fileName) { - if (!translationHash[key].trim()) { - continue; + var filePath = path + fileName; + var resFilePath = resPath + '/' + fileName; + + var contents = fs.readFileSync(filePath, 'utf8'); + + var fileKey = fileName.split('.')[0]; + + var fileObject = JSON.parse(contents); + var targetFileObject = {}; + + if (!(fileKey in translationData)) return; + + translationData[fileKey].forEach(function (item) { + var isArray = false; + var isMet = true; + var c = fileObject; + var path = item.path.slice(0); + + for (var i in item.path) { + var key = item.path[i]; + if (key in c) { + c = c[key]; + if (Array.isArray(c)) { + isArray = true; + break; + } + } else { + isMet = false; + } } - var escapedKey = this.escape(key); - contents = contents.replace(new RegExp('(' + escapedKey.replace(/\\"/g, '"') + ')', 'g'), '' + translationHash[key].replace(/\\"/g, '"') + ''); - } - } - if (fs.existsSync(resFilePath)) { - fs.unlinkSync(resFilePath); - } - fs.writeFileSync(resFilePath , contents); + if (isMet) { + if (!isArray) { + var isMet = false; + for (var k in c) { + if (c[k] === item.stringOriginal) { + path.push(k); + isMet = true; + } + } + } + } + + if (!isMet) return; + + var targetValue = item.stringTranslated; + if (targetValue === '') { + targetValue = item.stringOriginal; + } + if (isArray) { + try { + var targetValue = JSON.parse('[' + targetValue + ']'); + } catch (e) { + targetValue = null; + } + } + if (targetValue == null) return; + + var c = targetFileObject; + path.forEach(function (pathKey, i) { + if (i < path.length - 1) { + c[pathKey] = c[pathKey] || {}; + c = c[pathKey]; + } else { + c[pathKey] = targetValue; + } + }, this); + }, this); + + var contents = JSON.stringify(targetFileObject, null, ' '); + + if (fs.existsSync(resFilePath)) { + fs.unlinkSync(resFilePath); + } + fs.writeFileSync(resFilePath, contents); + + return; + }, this); }, this); - }, this); + }.bind(this)) + + }; var lang = new Lang(poPath, espoPath); diff --git a/package.json b/package.json index c4d788af4e..d6c1289add 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "grunt-contrib-less": "~0.7.0", "grunt-mkdir": "~0.1.1", "grunt-contrib-compress": "~0.8.0", - "grunt-chmod": "~1.0.3" + "grunt-chmod": "~1.0.3", + "pofile": "~1.0.2" } } diff --git a/po.js b/po.js index 4cd890c2ba..a80768e86f 100644 --- a/po.js +++ b/po.js @@ -75,8 +75,9 @@ function PO (espoPath, language) { PO.prototype.run = function () { var dirs = this.dirs; - var messageList = []; - var langMessageList = []; + var messageData = {}; + var targetMessageData = {} + var self = this; var poContents = this.poContentHeader; @@ -86,29 +87,27 @@ PO.prototype.run = function () { var list = fs.readdirSync(dirPath); list.forEach(function (fileName) { - var filePath = this.getDirPath(path, self.baseLanguage) + fileName; - messageList = this.getMessageList(filePath, messageList); + this.populateMessageDataFromFile(filePath, messageData); if (self.language != self.baseLanguage) { var langFilePath = this.getDirPath(path, self.language) + fileName; - langMessageList = this.getMessageList(langFilePath, langMessageList); + this.populateMessageDataFromFile(langFilePath, targetMessageData); } - }, this); }, this); if (self.language == self.baseLanguage) { - langMessageList = messageList; + targetMessageData = messageData; } - for (var index in messageList) { - poContents += 'msgid "' + messageList[index] + '"\n'; - - var langMessage = langMessageList[index] || ""; - poContents += 'msgstr "' + langMessage + '"\n\n'; + for (var key in messageData) { + poContents += 'msgid "' + messageData[key].value + '"\n'; + poContents += 'msgctxt "' + messageData[key].context + '"\n'; + var translatedValue = (targetMessageData[key] || {}).value || ""; + poContents += 'msgstr "' + translatedValue + '"\n\n'; } var resFilePath = this.currentPath + 'build/' + this.outputFileName; @@ -120,17 +119,17 @@ PO.prototype.run = function () { fs.writeFileSync(resFilePath, poContents); }; -PO.prototype.getMessageList = function (filePath, currentMessageList) { +PO.prototype.populateMessageDataFromFile = function (filePath, messageData) { if (!fs.existsSync(filePath)) { - return currentMessageList; + return messageData; } var data = fs.readFileSync(filePath, 'utf8'); data = JSON.parse(data); - currentMessageList = this.convertToSigleObject(data, '', currentMessageList); + var fileName = filePath.split('\/').slice(-1).pop().split('.')[0]; - return currentMessageList; + this.populateMessageData(fileName, data, '', messageData); } PO.prototype.getDirPath = function (path, language) { @@ -138,8 +137,7 @@ PO.prototype.getDirPath = function (path, language) { return dirPath; } -PO.prototype.convertToSigleObject = function (dataObject, prefix, currentMessageList) { - +PO.prototype.populateMessageData = function (fileName, dataObject, prefix, messageData) { prefix = prefix || ''; for (var index in dataObject) { @@ -147,51 +145,26 @@ PO.prototype.convertToSigleObject = function (dataObject, prefix, currentMessage 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); + if (typeof dataObject[index] === 'object' && !Array.isArray(dataObject[index])) { + var nextPrefix = prefix + (prefix ? '.' : '') + index; + this.populateMessageData(fileName, dataObject[index], nextPrefix, messageData); } else { - if (!this.objectIndexOf(dataObject[index], currentMessageList)) { - var key = prefix + index; - key = this.checkFixDuplicateKey(key, currentMessageList); - var savedString = this.fixString(dataObject[index]); + var path = fileName + '.' + prefix; - currentMessageList[key] = savedString; + var key = path + '.' + index; + + var value = dataObject[index]; + if (Array.isArray(value)) { + value = '"' + value.join('", "') + '"'; + path = path + '.' + index; } + + messageData[key] = { + context: path, + value: this.fixString(value) + }; } } - - 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) {