From 94f5f4a83e23c24b15e3e55e23ca870647c456ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20B=C3=A4ume?= Date: Wed, 5 Mar 2014 10:13:30 +0100 Subject: [PATCH 1/2] add reverse method of extract(string) during PO.parse, an extract(string) method is called on each string to unescape some characters (like " and \). This process should be reverted in the toString method. The PO spec says, that all strings should be C-Strings. Otherwise tools like msgmerge (from the gettext package) will fail parsing po files written by this library. --- lib/po.js | 10 ++++++++-- test/write.js | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/po.js b/lib/po.js index 82d13f1..0d7a82f 100644 --- a/lib/po.js +++ b/lib/po.js @@ -181,6 +181,12 @@ PO.Item.prototype.toString = function () { var lines = [], that = this; + // reverse what extract(string) method during PO.parse does + var _escape = function (string) { + string = string.replace(/\\/g, '\\\\'); + return string.replace(/"/g, '\\"'); + }; + var _process = function (keyword, text, i) { var lines = [], parts = text.split(/\n/), @@ -188,11 +194,11 @@ PO.Item.prototype.toString = function () { if (parts.length > 1) { lines.push(keyword + index + ' ""'); parts.forEach(function (part) { - lines.push('"' + part + '"'); + lines.push('"' + _escape(part) + '"'); }); } else { - lines.push(keyword + index + ' "' + text + '"'); + lines.push(keyword + index + ' "' + _escape(text) + '"'); } return lines; }; diff --git a/test/write.js b/test/write.js index a1ccd11..b0e3fee 100644 --- a/test/write.js +++ b/test/write.js @@ -52,6 +52,22 @@ describe('Write', function () { assertHasLine(str, '#. Extracted comment'); }); + describe('C-Strings', function () { + it('should escape "', function () { + var item = new PO.Item(); + + item.msgid = '" should be written escaped'; + assertHasLine(item.toString(), 'msgid "\\" should be written escaped"'); + }); + + it('shoudl escape \\', function () { + var item = new PO.Item(); + + item.msgid = '\\ should be written escaped'; + assertHasLine(item.toString(), 'msgid "\\\\ should be written escaped"'); + }); + }); + describe('msgctxt', function () { it('should write context field to file', function () { var input = fs.readFileSync(__dirname + '/fixtures/big.po', 'utf8'); From 851a87ebf284d168fc4bc1fe89a3b0f47ef6d40d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20B=C3=A4ume?= Date: Wed, 5 Mar 2014 11:37:48 +0100 Subject: [PATCH 2/2] add some real world examples for c-strings in po files make sure, if a file containing c-string messages is identical after being parsed and written afterwards. --- test/fixtures/c-strings.po | 19 +++++++++++++++++++ test/parse.js | 17 +++++++++++++++++ test/write.js | 8 ++++++++ 3 files changed, 44 insertions(+) create mode 100644 test/fixtures/c-strings.po diff --git a/test/fixtures/c-strings.po b/test/fixtures/c-strings.po new file mode 100644 index 0000000..597951e --- /dev/null +++ b/test/fixtures/c-strings.po @@ -0,0 +1,19 @@ +# French translation of Link (6.x-2.9) +# Copyright (c) 2011 by the French translation team +msgid "" +msgstr "" +"Project-Id-Version: Link (6.x-2.9)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-12-31 23:39+0000\n" +"PO-Revision-Date: 2013-12-17 14:59+0100\n" +"Last-Translator: Ruben Vermeersch \n" +"Language: fr\n" +"Language-Team: French\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"MIME-Version: 1.0\n" +"X-Generator: Poedit 1.6.2\n" + +msgid "The name field must not contain characters like \" or \\" +msgstr "" diff --git a/test/parse.js b/test/parse.js index e252b08..4547775 100644 --- a/test/parse.js +++ b/test/parse.js @@ -84,4 +84,21 @@ describe('Parse', function () { assert.equal(ambiguousItems[0].msgctxt, 'folder display'); assert.equal(ambiguousItems[1].msgctxt, 'folder action'); }); + + describe('C-Strings', function () { + it('should parse the c-strings.po file', function () { + var po = PO.parse(fs.readFileSync(__dirname + '/fixtures/c-strings.po', 'utf8')); + + assert.notEqual(po, null); + }); + + it('should extract strings containing " and \\ characters', function () { + var po = PO.parse(fs.readFileSync(__dirname + '/fixtures/c-strings.po', 'utf8')); + + var items = po.items.filter(function (item) { + return (/^The name field must not contain/).test(item.msgid); + }); + assert.equal(items[0].msgid, 'The name field must not contain characters like " or \\'); + }); + }); }); diff --git a/test/write.js b/test/write.js index b0e3fee..2f33f3d 100644 --- a/test/write.js +++ b/test/write.js @@ -66,6 +66,14 @@ describe('Write', function () { item.msgid = '\\ should be written escaped'; assertHasLine(item.toString(), 'msgid "\\\\ should be written escaped"'); }); + + it('should write identical file after parsing a file', function () { + var input = fs.readFileSync(__dirname + '/fixtures/c-strings.po', 'utf8'); + var po = PO.parse(input); + var str = po.toString(); + + assert.equal(str, input); + }); }); describe('msgctxt', function () {