Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 51 additions & 4 deletions lib/po.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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));
}
}
Expand Down
40 changes: 40 additions & 0 deletions test/fixtures/c-strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -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 <pre> 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"
18 changes: 14 additions & 4 deletions test/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});
});
2 changes: 1 addition & 1 deletion test/write.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"');
});

Expand Down