Skip to content

Commit 281fc44

Browse files
committed
feat: add support for compiling big-endian .mo files
This commit adds support for compiling big-endian .mo files. This facilitates the use of .mo files in big-endian systems, the ability to parser big-endian .mo files was already present. This commit also does the following: - Adds a new mo parser options type, used to indicate `endian = 'le' | 'be'` - Adds tests and fixtures for big-endian .mo files. - Renames the existing .mo fixtures to indicate that they are little-endian. - Fixes direct usages of `writeUInt32LE`. Fixes #97
1 parent f360d08 commit 281fc44

10 files changed

Lines changed: 111 additions & 23 deletions

File tree

src/mocompiler.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,22 @@ import contentType from 'content-type';
2121
* @typedef {{ msgid: Buffer, msgstr: Buffer }} TranslationBuffers A translation object partially parsed.
2222
*/
2323

24+
/**
25+
*
26+
* @typedef {Object} CompilerOptions MO compiler options
27+
* @property {'be'|'le'} [endian='le'] Endianness of the output buffer. Default is 'le'
28+
*/
29+
2430
/**
2531
* Exposes general compiler function. Takes a translation
2632
* object as a parameter and returns binary MO object
2733
*
2834
* @param {GetTextTranslations} table Translation object
35+
* @param {CompilerOptions} [options] MO compiler options
2936
* @return {Buffer} Compiled binary MO object
3037
*/
31-
export default function (table) {
32-
const compiler = new Compiler(table);
38+
export default function (table, options = { endian: 'le' }) {
39+
const compiler = new Compiler(table, options);
3340

3441
return compiler.compile();
3542
}
@@ -91,8 +98,9 @@ function prepareTranslations (translations) {
9198
* @this {Compiler & Transform}
9299
*
93100
* @param {GetTextTranslations} [table] Translation table as defined in the README
101+
* @param {CompilerOptions} [options] MO compiler options
94102
*/
95-
function Compiler (table) {
103+
function Compiler (table, options = { endian: 'le' }) {
96104
/** @type {GetTextTranslations} _table The translation table */
97105
this._table = {
98106
charset: undefined,
@@ -101,10 +109,11 @@ function Compiler (table) {
101109
};
102110

103111
this._translations = [];
112+
104113
/**
105114
* @type {WriteFunc}
106115
*/
107-
this._writeFunc = 'writeUInt32LE';
116+
this._writeFunc = options?.endian === 'le' ? 'writeUInt32LE' : 'writeUInt32BE';
108117

109118
this._handleCharset();
110119

@@ -258,8 +267,8 @@ Compiler.prototype._build = function (list, size) {
258267
for (i = 0, len = list.length; i < len; i++) {
259268
const msgidLength = /** @type {Buffer} */(/** @type {unknown} */(list[i].msgid));
260269
msgidLength.copy(returnBuffer, curPosition);
261-
returnBuffer.writeUInt32LE(list[i].msgid.length, 28 + i * 8);
262-
returnBuffer.writeUInt32LE(curPosition, 28 + i * 8 + 4);
270+
returnBuffer[this._writeFunc](list[i].msgid.length, 28 + i * 8);
271+
returnBuffer[this._writeFunc](curPosition, 28 + i * 8 + 4);
263272
returnBuffer[curPosition + list[i].msgid.length] = 0x00;
264273
curPosition += list[i].msgid.length + 1;
265274
}
@@ -268,8 +277,8 @@ Compiler.prototype._build = function (list, size) {
268277
for (i = 0, len = list.length; i < len; i++) {
269278
const msgstrLength = /** @type {Buffer} */(/** @type {unknown} */(list[i].msgstr));
270279
msgstrLength.copy(returnBuffer, curPosition);
271-
returnBuffer.writeUInt32LE(list[i].msgstr.length, 28 + (4 + 4) * list.length + i * 8);
272-
returnBuffer.writeUInt32LE(curPosition, 28 + (4 + 4) * list.length + i * 8 + 4);
280+
returnBuffer[this._writeFunc](list[i].msgstr.length, 28 + (4 + 4) * list.length + i * 8);
281+
returnBuffer[this._writeFunc](curPosition, 28 + (4 + 4) * list.length + i * 8 + 4);
273282
returnBuffer[curPosition + list[i].msgstr.length] = 0x00;
274283
curPosition += list[i].msgstr.length + 1;
275284
}

test/fixtures/latin13-be.mo

678 Bytes
Binary file not shown.

test/fixtures/obsolete-be.mo

125 Bytes
Binary file not shown.

test/fixtures/utf8-be.mo

851 Bytes
Binary file not shown.

test/mo-compiler-test.js

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,85 @@ const __dirname = path.dirname(__filename);
1111
const readFile = promisify(fsReadFile);
1212

1313
const expect = chai.expect;
14+
15+
const littleEndianMagic = [0xde, 0x12, 0x04, 0x95];
16+
const bigEndianMagic = [0x95, 0x04, 0x12, 0xde];
17+
1418
chai.config.includeStack = true;
1519

1620
describe('MO Compiler', () => {
17-
describe('UTF-8', () => {
21+
describe('UTF-8 LE', async () => {
22+
const [json, moData] = await Promise.all([
23+
readFile(path.join(__dirname, 'fixtures/utf8-po.json'), 'utf8'),
24+
readFile(path.join(__dirname, 'fixtures/utf8-le.mo'))
25+
]);
26+
1827
it('should compile', async () => {
19-
const [json, moData] = await Promise.all([
20-
readFile(path.join(__dirname, 'fixtures/utf8-po.json'), 'utf8'),
21-
readFile(path.join(__dirname, 'fixtures/utf8.mo'))
22-
]);
28+
const compiled = mo.compile(JSON.parse(json));
2329

30+
expect(compiled.toString('utf8')).to.deep.equal(moData.toString('utf8'));
31+
});
32+
33+
it('should have the correct magic number', () => {
2434
const compiled = mo.compile(JSON.parse(json));
2535

36+
expect(Array.from(compiled.subarray(0, 4))).to.eql(littleEndianMagic);
37+
});
38+
});
39+
40+
describe('UTF-8 BE', async () => {
41+
const [json, moData] = await Promise.all([
42+
readFile(path.join(__dirname, 'fixtures/utf8-po.json'), 'utf8'),
43+
readFile(path.join(__dirname, 'fixtures/utf8-be.mo'))
44+
]);
45+
46+
it('should compile', async () => {
47+
const compiled = mo.compile(JSON.parse(json), { endian: 'be' });
48+
2649
expect(compiled.toString('utf8')).to.deep.equal(moData.toString('utf8'));
2750
});
51+
52+
it('should have the correct magic number', () => {
53+
const compiled = mo.compile(JSON.parse(json), { endian: 'be' });
54+
55+
expect(Array.from(compiled.subarray(0, 4))).to.eql(bigEndianMagic);
56+
});
2857
});
2958

30-
describe('Latin-13', () => {
59+
describe('Latin-13 LE', async () => {
60+
const [json, moData] = await Promise.all([
61+
readFile(path.join(__dirname, 'fixtures/latin13-po.json'), 'utf8'),
62+
readFile(path.join(__dirname, 'fixtures/latin13-le.mo'))
63+
]);
64+
3165
it('should compile', async () => {
32-
const [json, moData] = await Promise.all([
33-
readFile(path.join(__dirname, 'fixtures/latin13-po.json'), 'utf8'),
34-
readFile(path.join(__dirname, 'fixtures/latin13.mo'))
35-
]);
66+
const compiled = mo.compile(JSON.parse(json));
3667

68+
expect(compiled.toString('utf8')).to.equal(moData.toString('utf8'));
69+
});
70+
71+
it('should have the correct magic number', () => {
3772
const compiled = mo.compile(JSON.parse(json));
3873

74+
expect(Array.from(compiled.subarray(0, 4))).to.eql(littleEndianMagic);
75+
});
76+
});
77+
78+
describe('Latin-13 BE', async () => {
79+
const [json, moData] = await Promise.all([
80+
readFile(path.join(__dirname, 'fixtures/latin13-po.json'), 'utf8'),
81+
readFile(path.join(__dirname, 'fixtures/latin13-be.mo'))
82+
]);
83+
84+
it('should compile', async () => {
85+
const compiled = mo.compile(JSON.parse(json), { endian: 'be' });
86+
3987
expect(compiled.toString('utf8')).to.equal(moData.toString('utf8'));
4088
});
89+
90+
it('should have the correct magic number', () => {
91+
const compiled = mo.compile(JSON.parse(json), { endian: 'be' });
92+
expect(Array.from(compiled.subarray(0, 4))).to.eql(bigEndianMagic);
93+
});
4194
});
4295
});

test/mo-parser-test.js

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ const expect = chai.expect;
1414
chai.config.includeStack = true;
1515

1616
describe('MO Parser', () => {
17-
describe('UTF-8', () => {
17+
describe('UTF-8 LE', () => {
1818
it('should parse', async () => {
1919
const [moData, json] = await Promise.all([
20-
readFile(path.join(__dirname, 'fixtures/utf8.mo')),
20+
readFile(path.join(__dirname, 'fixtures/utf8-le.mo')),
2121
readFile(path.join(__dirname, 'fixtures/utf8-mo.json'), 'utf8')
2222
]);
2323

@@ -27,10 +27,36 @@ describe('MO Parser', () => {
2727
});
2828
});
2929

30-
describe('Latin-13', () => {
30+
describe('UTF-8 BE', () => {
3131
it('should parse', async () => {
3232
const [moData, json] = await Promise.all([
33-
readFile(path.join(__dirname, 'fixtures/latin13.mo')),
33+
readFile(path.join(__dirname, 'fixtures/utf8-be.mo')),
34+
readFile(path.join(__dirname, 'fixtures/utf8-mo.json'), 'utf8')
35+
]);
36+
37+
const parsed = mo.parse(moData);
38+
39+
expect(parsed).to.deep.equal(JSON.parse(json));
40+
});
41+
});
42+
43+
describe('Latin-13 LE', () => {
44+
it('should parse', async () => {
45+
const [moData, json] = await Promise.all([
46+
readFile(path.join(__dirname, 'fixtures/latin13-le.mo')),
47+
readFile(path.join(__dirname, 'fixtures/latin13-mo.json'), 'utf8')
48+
]);
49+
50+
const parsed = mo.parse(moData);
51+
52+
expect(parsed).to.deep.equal(JSON.parse(json));
53+
});
54+
});
55+
56+
describe('Latin-13 BE', () => {
57+
it('should parse', async () => {
58+
const [moData, json] = await Promise.all([
59+
readFile(path.join(__dirname, 'fixtures/latin13-be.mo')),
3460
readFile(path.join(__dirname, 'fixtures/latin13-mo.json'), 'utf8')
3561
]);
3662

test/po-obsolete-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ chai.config.includeStack = true;
1717
describe('Obsolete', async () => {
1818
const [po, mo, jsonString] = await Promise.all([
1919
readFile(path.join(__dirname, 'fixtures/obsolete.po')),
20-
readFile(path.join(__dirname, 'fixtures/obsolete.mo')),
20+
readFile(path.join(__dirname, 'fixtures/obsolete-le.mo')),
2121
readFile(path.join(__dirname, 'fixtures/obsolete.json'), 'utf8')
2222
]);
2323

0 commit comments

Comments
 (0)