diff --git a/src/mocompiler.js b/src/mocompiler.js index f538a6d..049ab62 100644 --- a/src/mocompiler.js +++ b/src/mocompiler.js @@ -21,15 +21,22 @@ import contentType from 'content-type'; * @typedef {{ msgid: Buffer, msgstr: Buffer }} TranslationBuffers A translation object partially parsed. */ +/** + * + * @typedef {Object} CompilerOptions MO compiler options + * @property {'be'|'le'} [endian='le'] Endianness of the output buffer. Default is 'le' + */ + /** * Exposes general compiler function. Takes a translation * object as a parameter and returns binary MO object * * @param {GetTextTranslations} table Translation object + * @param {CompilerOptions} [options] MO compiler options * @return {Buffer} Compiled binary MO object */ -export default function (table) { - const compiler = new Compiler(table); +export default function (table, options = { endian: 'le' }) { + const compiler = new Compiler(table, options); return compiler.compile(); } @@ -91,8 +98,9 @@ function prepareTranslations (translations) { * @this {Compiler & Transform} * * @param {GetTextTranslations} [table] Translation table as defined in the README + * @param {CompilerOptions} [options] MO compiler options */ -function Compiler (table) { +function Compiler (table, options = { endian: 'le' }) { /** @type {GetTextTranslations} _table The translation table */ this._table = { charset: undefined, @@ -101,10 +109,11 @@ function Compiler (table) { }; this._translations = []; + /** * @type {WriteFunc} */ - this._writeFunc = 'writeUInt32LE'; + this._writeFunc = options?.endian === 'be' ? 'writeUInt32BE' : 'writeUInt32LE'; this._handleCharset(); @@ -258,8 +267,8 @@ Compiler.prototype._build = function (list, size) { for (i = 0, len = list.length; i < len; i++) { const msgidLength = /** @type {Buffer} */(/** @type {unknown} */(list[i].msgid)); msgidLength.copy(returnBuffer, curPosition); - returnBuffer.writeUInt32LE(list[i].msgid.length, 28 + i * 8); - returnBuffer.writeUInt32LE(curPosition, 28 + i * 8 + 4); + returnBuffer[this._writeFunc](list[i].msgid.length, 28 + i * 8); + returnBuffer[this._writeFunc](curPosition, 28 + i * 8 + 4); returnBuffer[curPosition + list[i].msgid.length] = 0x00; curPosition += list[i].msgid.length + 1; } @@ -268,8 +277,8 @@ Compiler.prototype._build = function (list, size) { for (i = 0, len = list.length; i < len; i++) { const msgstrLength = /** @type {Buffer} */(/** @type {unknown} */(list[i].msgstr)); msgstrLength.copy(returnBuffer, curPosition); - returnBuffer.writeUInt32LE(list[i].msgstr.length, 28 + (4 + 4) * list.length + i * 8); - returnBuffer.writeUInt32LE(curPosition, 28 + (4 + 4) * list.length + i * 8 + 4); + returnBuffer[this._writeFunc](list[i].msgstr.length, 28 + (4 + 4) * list.length + i * 8); + returnBuffer[this._writeFunc](curPosition, 28 + (4 + 4) * list.length + i * 8 + 4); returnBuffer[curPosition + list[i].msgstr.length] = 0x00; curPosition += list[i].msgstr.length + 1; } diff --git a/test/fixtures/latin13-be.mo b/test/fixtures/latin13-be.mo new file mode 100644 index 0000000..aa12185 Binary files /dev/null and b/test/fixtures/latin13-be.mo differ diff --git a/test/fixtures/latin13.mo b/test/fixtures/latin13-le.mo similarity index 100% rename from test/fixtures/latin13.mo rename to test/fixtures/latin13-le.mo diff --git a/test/fixtures/obsolete-be.mo b/test/fixtures/obsolete-be.mo new file mode 100644 index 0000000..b70aacd Binary files /dev/null and b/test/fixtures/obsolete-be.mo differ diff --git a/test/fixtures/obsolete.mo b/test/fixtures/obsolete-le.mo similarity index 100% rename from test/fixtures/obsolete.mo rename to test/fixtures/obsolete-le.mo diff --git a/test/fixtures/utf8-be.mo b/test/fixtures/utf8-be.mo new file mode 100644 index 0000000..7c5023e Binary files /dev/null and b/test/fixtures/utf8-be.mo differ diff --git a/test/fixtures/utf8.mo b/test/fixtures/utf8-le.mo similarity index 100% rename from test/fixtures/utf8.mo rename to test/fixtures/utf8-le.mo diff --git a/test/mo-compiler-test.js b/test/mo-compiler-test.js index 3b0a8b6..896dc8c 100644 --- a/test/mo-compiler-test.js +++ b/test/mo-compiler-test.js @@ -11,32 +11,94 @@ const __dirname = path.dirname(__filename); const readFile = promisify(fsReadFile); const expect = chai.expect; + +const littleEndianMagic = [0xde, 0x12, 0x04, 0x95]; +const bigEndianMagic = [0x95, 0x04, 0x12, 0xde]; + chai.config.includeStack = true; describe('MO Compiler', () => { - describe('UTF-8', () => { + describe('UTF-8 LE', async () => { it('should compile', async () => { const [json, moData] = await Promise.all([ readFile(path.join(__dirname, 'fixtures/utf8-po.json'), 'utf8'), - readFile(path.join(__dirname, 'fixtures/utf8.mo')) + readFile(path.join(__dirname, 'fixtures/utf8-le.mo')) ]); const compiled = mo.compile(JSON.parse(json)); expect(compiled.toString('utf8')).to.deep.equal(moData.toString('utf8')); }); + + it('should have the correct magic number', async () => { + const json = await readFile(path.join(__dirname, 'fixtures/utf8-po.json'), 'utf8'); + + const compiled = mo.compile(JSON.parse(json)); + + expect(Array.from(compiled.subarray(0, 4))).to.eql(littleEndianMagic); + }); + }); + + describe('UTF-8 BE', () => { + it('should compile', async () => { + const [json, moData] = await Promise.all([ + readFile(path.join(__dirname, 'fixtures/utf8-po.json'), 'utf8'), + readFile(path.join(__dirname, 'fixtures/utf8-be.mo')) + ]); + + const compiled = mo.compile(JSON.parse(json), { endian: 'be' }); + + expect(compiled.toString('utf8')).to.deep.equal(moData.toString('utf8')); + }); + + it('should have the correct magic number', async () => { + const json = await readFile(path.join(__dirname, 'fixtures/utf8-po.json'), 'utf8'); + + const compiled = mo.compile(JSON.parse(json), { endian: 'be' }); + + expect(Array.from(compiled.subarray(0, 4))).to.eql(bigEndianMagic); + }); }); - describe('Latin-13', () => { + describe('Latin-13 LE', () => { it('should compile', async () => { const [json, moData] = await Promise.all([ readFile(path.join(__dirname, 'fixtures/latin13-po.json'), 'utf8'), - readFile(path.join(__dirname, 'fixtures/latin13.mo')) + readFile(path.join(__dirname, 'fixtures/latin13-le.mo')) ]); const compiled = mo.compile(JSON.parse(json)); expect(compiled.toString('utf8')).to.equal(moData.toString('utf8')); }); + + it('should have the correct magic number', async () => { + const json = await readFile(path.join(__dirname, 'fixtures/latin13-po.json'), 'utf8'); + + const compiled = mo.compile(JSON.parse(json)); + + expect(Array.from(compiled.subarray(0, 4))).to.eql(littleEndianMagic); + }); + }); + + describe('Latin-13 BE', () => { + it('should compile', async () => { + const [json, moData] = await Promise.all([ + readFile(path.join(__dirname, 'fixtures/latin13-po.json'), 'utf8'), + readFile(path.join(__dirname, 'fixtures/latin13-be.mo')) + ]); + + const compiled = mo.compile(JSON.parse(json), { endian: 'be' }); + + expect(compiled.toString('utf8')).to.equal(moData.toString('utf8')); + }); + + it('should have the correct magic number', async () => { + const json = await readFile(path.join(__dirname, 'fixtures/latin13-po.json'), 'utf8'); + + const compiled = mo.compile(JSON.parse(json), { endian: 'be' }); + + expect(Array.from(compiled.subarray(0, 4))).to.eql(bigEndianMagic); + }); }); }); diff --git a/test/mo-parser-test.js b/test/mo-parser-test.js index 4391471..2c463c6 100644 --- a/test/mo-parser-test.js +++ b/test/mo-parser-test.js @@ -14,10 +14,10 @@ const expect = chai.expect; chai.config.includeStack = true; describe('MO Parser', () => { - describe('UTF-8', () => { + describe('UTF-8 LE', () => { it('should parse', async () => { const [moData, json] = await Promise.all([ - readFile(path.join(__dirname, 'fixtures/utf8.mo')), + readFile(path.join(__dirname, 'fixtures/utf8-le.mo')), readFile(path.join(__dirname, 'fixtures/utf8-mo.json'), 'utf8') ]); @@ -27,10 +27,36 @@ describe('MO Parser', () => { }); }); - describe('Latin-13', () => { + describe('UTF-8 BE', () => { it('should parse', async () => { const [moData, json] = await Promise.all([ - readFile(path.join(__dirname, 'fixtures/latin13.mo')), + readFile(path.join(__dirname, 'fixtures/utf8-be.mo')), + readFile(path.join(__dirname, 'fixtures/utf8-mo.json'), 'utf8') + ]); + + const parsed = mo.parse(moData); + + expect(parsed).to.deep.equal(JSON.parse(json)); + }); + }); + + describe('Latin-13 LE', () => { + it('should parse', async () => { + const [moData, json] = await Promise.all([ + readFile(path.join(__dirname, 'fixtures/latin13-le.mo')), + readFile(path.join(__dirname, 'fixtures/latin13-mo.json'), 'utf8') + ]); + + const parsed = mo.parse(moData); + + expect(parsed).to.deep.equal(JSON.parse(json)); + }); + }); + + describe('Latin-13 BE', () => { + it('should parse', async () => { + const [moData, json] = await Promise.all([ + readFile(path.join(__dirname, 'fixtures/latin13-be.mo')), readFile(path.join(__dirname, 'fixtures/latin13-mo.json'), 'utf8') ]); diff --git a/test/po-obsolete-test.js b/test/po-obsolete-test.js index d8a4a18..aa7390d 100644 --- a/test/po-obsolete-test.js +++ b/test/po-obsolete-test.js @@ -17,7 +17,7 @@ chai.config.includeStack = true; describe('Obsolete', async () => { const [po, mo, jsonString] = await Promise.all([ readFile(path.join(__dirname, 'fixtures/obsolete.po')), - readFile(path.join(__dirname, 'fixtures/obsolete.mo')), + readFile(path.join(__dirname, 'fixtures/obsolete-le.mo')), readFile(path.join(__dirname, 'fixtures/obsolete.json'), 'utf8') ]);