diff --git a/lib/po.js b/lib/po.js index e22db97..b7a6866 100644 --- a/lib/po.js +++ b/lib/po.js @@ -119,8 +119,32 @@ PO.parse = function (data) { function extract(string) { string = trim(string); string = string.replace(/^[^"]*"|"$/g, ''); - string = string.replace(/\\"/g, '"'); - string = string.replace(/\\\\/g, '\\'); + string = string.replace(/\\([abtnvfr'"\\?]|([0-7]{3})|x([0-9a-fA-F]{2}))/g, function (match, esc, oct, hex) { + if (oct) { + return String.fromCharCode(parseInt(oct, 8)); + } + if (hex) { + return String.fromCharCode(parseInt(hex, 16)); + } + switch (esc) { + case 'a': + return '\x07'; + case 'b': + return '\b'; + case 't': + return '\t'; + case 'n': + return '\n'; + case 'v': + return '\v'; + case 'f': + return '\f'; + case 'r': + return '\r'; + default: + return esc; + } + }); return string; } @@ -212,8 +236,27 @@ PO.Item.prototype.toString = function () { // reverse what extract(string) method during PO.parse does var _escape = function (string) { - string = string.replace(/\\/g, '\\\\'); - return string.replace(/"/g, '\\"'); + // don't unescape \n, since string can never contain it + // since split('\n') is called on it + string = string.replace(/[\x07\b\t\v\f\r"\\]/g, function (match) { + switch (match) { + case '\x07': + return '\\a'; + case '\b': + return '\\b'; + case '\t': + return '\\t'; + case '\v': + return '\\v'; + case '\f': + return '\\f'; + case '\r': + return '\\r'; + default: + return '\\' + match; + } + }); + return string; }; var _process = function (keyword, text, i) { @@ -262,6 +305,10 @@ PO.Item.prototype.toString = function () { } else { text = isArray(text) ? text.join() : text; var processed = _process(keyword, text); + //handle \n in single-line texts (can not be handled in _escape) + for (var i = 1; i < processed.length - 1; i++) { + processed[i] = processed[i].slice(0, -1) + '\\n"'; + } lines = lines.concat(mkObsolete + processed.join('\n' + mkObsolete)); } } diff --git a/test/fixtures/c-strings.po b/test/fixtures/c-strings.po index 597951e..d05b8e3 100644 --- a/test/fixtures/c-strings.po +++ b/test/fixtures/c-strings.po @@ -17,3 +17,43 @@ msgstr "" msgid "The name field must not contain characters like \" or \\" msgstr "" + +# possibility to reorder items depending on locale +#. Format of addresses +#. %1$s is the street +#. %2$s is the postal code +#. %3$s is the city +#. %4$s is the state +#. %5$s is the country +msgid "" +"%1$s\n" +"%2$s %3$s\n" +"%4$s\n" +"%5$s" +msgstr "" + +# "i18"ned code +#. used in
environment, so don't remove any control sequences +msgid "" +"define('some/test/module', function () {\n" +"\t'use strict';\n" +"\treturn {};\n" +"});\n" +"" +msgstr "" +"define('random/test/file', function () {\n" +"\t'use strict';\n" +"\treturn {};\n" +"});\n" +"" + +# all one-letter escape characters +# be aware, that \a, \b, \v, \f and \r should not be used +# in i18ned messages (according to gettext tools) +# however, they should be properly parsed, anyway +msgid "" +"\a\b\t\n" +"\v\f\r" +msgstr "" +"\a\b\t\n" +"\v\f\r" diff --git a/test/parse.js b/test/parse.js index d12ee10..f17c7af 100644 --- a/test/parse.js +++ b/test/parse.js @@ -133,19 +133,29 @@ describe('Parse', function () { }); describe('C-Strings', function () { + var po = PO.parse(fs.readFileSync(__dirname + '/fixtures/c-strings.po', 'utf8')); 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 \\'); }); + + it('should handle \n characters', function () { + var item = po.items[1]; + assert.equal(item.msgid, '%1$s\n%2$s %3$s\n%4$s\n%5$s'); + }); + + it('should handle \t characters', function () { + var item = po.items[2]; + assert.equal(item.msgid, 'define(\'some/test/module\', function () {\n' + + '\t\'use strict\';\n' + + '\treturn {};\n' + + '});\n'); + }); }); }); diff --git a/test/write.js b/test/write.js index dd319ad..06edb97 100644 --- a/test/write.js +++ b/test/write.js @@ -97,7 +97,7 @@ describe('Write', function () { item.msgid = '\n should be written escaped'; assertHasLine(item.toString(), 'msgid ""'); - assertHasLine(item.toString(), '""'); + assertHasLine(item.toString(), '"\\n"'); assertHasLine(item.toString(), '" should be written escaped"'); });