wallet-core/node_modules/gettext-parser/lib/moparser.js
2016-10-10 03:43:44 +02:00

202 lines
5.3 KiB
JavaScript

'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;
};