diff options
Diffstat (limited to 'node_modules/gettext-parser/lib/moparser.js')
-rw-r--r-- | node_modules/gettext-parser/lib/moparser.js | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/node_modules/gettext-parser/lib/moparser.js b/node_modules/gettext-parser/lib/moparser.js new file mode 100644 index 000000000..8c204716b --- /dev/null +++ b/node_modules/gettext-parser/lib/moparser.js @@ -0,0 +1,202 @@ +'use strict'; + +var encoding = require('encoding'); +var sharedFuncs = require('./shared'); + +/** + * Parses a binary MO object into translation table + * + * @param {Buffer} buffer Binary MO object + * @param {String} [defaultCharset] Default charset to use + * @return {Object} Translation object + */ +module.exports = function(buffer, defaultCharset) { + var parser = new Parser(buffer, defaultCharset); + return parser.parse(); +}; + +/** + * Creates a MO parser object. + * + * @constructor + * @param {Buffer} fileContents Binary MO object + * @param {String} [defaultCharset] Default charset to use + */ +function Parser(fileContents, defaultCharset) { + + this._fileContents = fileContents; + + /** + * Method name for writing int32 values, default littleendian + */ + this._writeFunc = 'writeUInt32LE'; + + /** + * Method name for reading int32 values, default littleendian + */ + this._readFunc = 'readUInt32LE'; + + this._charset = defaultCharset || 'iso-8859-1'; + + this._table = { + charset: this._charset, + headers: undefined, + translations: {} + }; +} + +/** + * Magic constant to check the endianness of the input file + */ +Parser.prototype.MAGIC = 0x950412de; + +/** + * Checks if number values in the input file are in big- or littleendian format. + * + * @return {Boolean} Return true if magic was detected + */ +Parser.prototype._checkMagick = function() { + if (this._fileContents.readUInt32LE(0) === this.MAGIC) { + this._readFunc = 'readUInt32LE'; + this._writeFunc = 'writeUInt32LE'; + return true; + } else if (this._fileContents.readUInt32BE(0) === this.MAGIC) { + this._readFunc = 'readUInt32BE'; + this._writeFunc = 'writeUInt32BE'; + return true; + } else { + return false; + } +}; + +/** + * Read the original strings and translations from the input MO file. Use the + * first translation string in the file as the header. + */ +Parser.prototype._loadTranslationTable = function() { + var offsetOriginals = this._offsetOriginals, + offsetTranslations = this._offsetTranslations, + position, length, + msgid, msgstr; + + for (var i = 0; i < this._total; i++) { + // msgid string + length = this._fileContents[this._readFunc](offsetOriginals); + offsetOriginals += 4; + position = this._fileContents[this._readFunc](offsetOriginals); + offsetOriginals += 4; + msgid = this._fileContents.slice(position, position + length); + + // matching msgstr + length = this._fileContents[this._readFunc](offsetTranslations); + offsetTranslations += 4; + position = this._fileContents[this._readFunc](offsetTranslations); + offsetTranslations += 4; + msgstr = this._fileContents.slice(position, position + length); + + if (!i && !msgid.toString()) { + this._handleCharset(msgstr); + } + + msgid = encoding.convert(msgid, 'utf-8', this._charset).toString('utf-8'); + msgstr = encoding.convert(msgstr, 'utf-8', this._charset).toString('utf-8'); + + this._addString(msgid, msgstr); + } + + // dump the file contents object + this._fileContents = null; +}; + +/** + * Detects charset for MO strings from the header + * + * @param {Buffer} headers Header value + */ +Parser.prototype._handleCharset = function(headers) { + + var headersStr = headers.toString(), + match; + + if ((match = headersStr.match(/[; ]charset\s*=\s*([\w\-]+)/i))) { + this._charset = this._table.charset = sharedFuncs.formatCharset(match[1], this._charset); + } + + headers = encoding.convert(headers, 'utf-8', this._charset).toString('utf-8'); + + this._table.headers = sharedFuncs.parseHeader(headers); +}; + +/** + * Adds a translation to the translation object + * + * @param {String} msgid Original string + * @params {String} msgstr Translation for the original string + */ +Parser.prototype._addString = function(msgid, msgstr) { + var translation = {}, + parts, msgctxt, msgid_plural; + + msgid = msgid.split('\u0004'); + if (msgid.length > 1) { + msgctxt = msgid.shift(); + translation.msgctxt = msgctxt; + } else { + msgctxt = ''; + } + msgid = msgid.join('\u0004'); + + parts = msgid.split('\u0000'); + msgid = parts.shift(); + + translation.msgid = msgid; + + if ((msgid_plural = parts.join('\u0000'))) { + translation.msgid_plural = msgid_plural; + } + + msgstr = msgstr.split('\u0000'); + translation.msgstr = [].concat(msgstr || []); + + if (!this._table.translations[msgctxt]) { + this._table.translations[msgctxt] = {}; + } + + this._table.translations[msgctxt][msgid] = translation; +}; + +/** + * Parses the MO object and returns translation table + * + * @return {Object} Translation table + */ +Parser.prototype.parse = function() { + if (!this._checkMagick()) { + return false; + } + + /** + * GetText revision nr, usually 0 + */ + this._revision = this._fileContents[this._readFunc](4); + + /** + * Total count of translated strings + */ + this._total = this._fileContents[this._readFunc](8); + + /** + * Offset position for original strings table + */ + this._offsetOriginals = this._fileContents[this._readFunc](12); + + /** + * Offset position for translation strings table + */ + this._offsetTranslations = this._fileContents[this._readFunc](16); + + // Load translations into this._translationTable + this._loadTranslationTable(); + + return this._table; +};
\ No newline at end of file |