26 Commits

Author SHA1 Message Date
Ruben Vermeersch
04fe915389 Release v0.2.8 2014-03-21 11:08:59 +01:00
Ruben Vermeersch
2fb07fb1d0 Recompile browser versions for #8. 2014-03-21 11:08:30 +01:00
Ruben Vermeersch
06571c89fc Merge pull request #8 from Open-Xchange-Frontend/multiline_headers
add support for multiline string in headers
2014-03-21 11:08:05 +01:00
Julian Bäume
73b267b3e8 add support for multiline string in headers
Some languages (such as Polish, Russian or Romanian) do have more
complicated plural forms. Those are still expressible by a more
complicated mathematical expression. However, the msgmerge tool of
gettext will in these cases write multiline header fields. When parsing
such files with this lib, the headers get screwed up, so this patch
provides an example (from a pl_PL po file) and fixes this by joining the
lines in the header, before doing the actual parsing.
2014-03-21 10:54:32 +01:00
Ruben Vermeersch
42d6df5373 Release v0.2.7 2014-03-07 10:18:30 +01:00
Ruben Vermeersch
dba7465ba7 Remove unneeded if-clauses. 2014-03-07 10:17:52 +01:00
Ruben Vermeersch
d426c114c7 Add support for obsolete items to fix broken parsing. 2014-03-07 10:15:50 +01:00
Ruben Vermeersch
5a49a3400b Release v0.2.6 2014-03-05 11:48:45 +01:00
Ruben Vermeersch
c103f45002 Recompile for #7. 2014-03-05 11:48:14 +01:00
Ruben Vermeersch
cb12e69ef4 Merge pull request #7 from Open-Xchange-Frontend/write_escaped
add reverse method of extract(string)
2014-03-05 11:47:56 +01:00
Julian Bäume
851a87ebf2 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.
2014-03-05 11:37:48 +01:00
Julian Bäume
94f5f4a83e 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.
2014-03-05 10:13:30 +01:00
Ruben Vermeersch
6903bbf967 Fix build. 2014-03-03 16:20:49 +01:00
Ruben Vermeersch
b82dea6e42 Update contributors. 2014-03-03 16:17:22 +01:00
Ruben Vermeersch
1d74ea4a7a Release v0.2.5 2014-03-03 16:11:50 +01:00
Ruben Vermeersch
783a4129d5 Merge pull request #6 from gabegorelick/serialize-comments
Include comments in `PO.Item` output
2014-03-03 16:11:24 +01:00
Gabe Gorelick
8dba54b095 Document extractedComments 2014-03-02 13:20:18 -05:00
Gabe Gorelick
ac85ba0b9c Handle extracted comments 2014-03-02 13:17:20 -05:00
Gabe Gorelick
ada76116b0 Include comments in PO.Item output
Fixes #5
2014-03-02 12:41:40 -05:00
Ruben Vermeersch
c5515d6128 Add Julian Bäume to contributors. 2014-01-22 14:25:10 +01:00
Ruben Vermeersch
c58333c4d1 Fix capital and punctuation. 2014-01-22 14:22:03 +01:00
Ruben Vermeersch
136b969adb Release v0.2.4 2014-01-22 14:19:50 +01:00
Ruben Vermeersch
02471be49f Recompile browserified version. 2014-01-22 14:19:26 +01:00
Ruben Vermeersch
13283fedbe Merge pull request #4 from Open-Xchange-Frontend/fix_msgctxt_default
fix default value of msgctxt field
2014-01-22 05:19:14 -08:00
Julian Bäume
a5f3059661 fix default value of msgctxt field
I added a few more edge-cases to the tests for the new msgctxt field and
revealed a bug during that. The default value has been set to empty
string, but should have been null, since the spec says, this field is
optional.
2014-01-22 14:14:03 +01:00
Ruben Vermeersch
abca810905 Upgrade copyright years. 2014-01-21 16:05:01 +01:00
14 changed files with 307 additions and 45 deletions

View File

@@ -3,5 +3,5 @@ node_js:
- "0.8"
- "0.10"
- "0.11"
before_script:
before_install:
- npm install -g grunt-cli

View File

@@ -1,4 +1,4 @@
Copyright (C) 2013 Ruben Vermeersch
Copyright (C) 2013-2014 Ruben Vermeersch
Copyright (C) 2012 Michael Holly
Permission is hereby granted, free of charge, to any person obtaining a copy of

View File

@@ -88,11 +88,12 @@ The `PO` class exposes the following members:
* `msgstr`: An array of translated strings. Items that have no plural msgid
only have one element in this array.
* `references`: An array of reference strings.
* `comments`: An array of string comments.
* `comments`: An array of string translator comments.
* `extractedComments`: An array of string extracted comments.
* `flags`: A dictionary of the string flags. Each flag is mapped to a key with
value true. For instance, a string with the fuzzy flag set will have
`item.flags.fuzzy == true`.
* `msgctxt`: context of the message, an arbitrary string, can be used for disambiguation
* `msgctxt`: Context of the message, an arbitrary string, can be used for disambiguation.
## Contributing
@@ -142,7 +143,7 @@ PO.load('text.po', function (err, po) {
(The MIT License)
Copyright (C) 2013 by Ruben Vermeersch <ruben@rocketeer.be>
Copyright (C) 2013-2014 by Ruben Vermeersch <ruben@rocketeer.be>
Copyright (C) 2012 by Michael Holly
Permission is hereby granted, free of charge, to any person obtaining a copy

View File

@@ -1,6 +1,6 @@
{
"name": "pofile",
"version": "0.2.3",
"version": "0.2.8",
"authors": [
"Ruben Vermeersch <ruben@rocketeer.be>"
],

75
dist/pofile.js vendored
View File

@@ -75,7 +75,18 @@ PO.parse = function (data) {
'Plural-Forms': '',
};
headers.split(/\n/).forEach(function (header) {
headers.split(/\n/).reduce(function (acc, line) {
if (acc.merge) {
//join lines, remove last resp. first "
line = acc.pop().slice(0, -1) + line.slice(1);
delete acc.merge;
}
if (/^".*"$/.test(line) && !/^".*\\n"$/.test(line)) {
acc.merge = true;
}
acc.push(line);
return acc;
}, []).forEach(function (header) {
if (header.match(/^#/)) {
po.comments.push(header.replace(/^#\s*/, ''));
}
@@ -90,12 +101,15 @@ PO.parse = function (data) {
var item = new PO.Item(),
context = null,
plural = 0;
plural = 0,
obsolete = false;
function finish() {
if (item.msgid.length > 0) {
po.items.push(item);
item = new PO.Item();
item.obsolete = obsolete;
obsolete = false;
}
}
@@ -110,6 +124,14 @@ PO.parse = function (data) {
while (lines.length > 0) {
var line = trim(lines.shift()),
add = false;
if (line.match(/^#\~/)) { // Obsolete item
obsolete = true;
line = trim(line.substring(2));
} else {
obsolete = false;
}
if (line.match(/^#:/)) { // Reference
finish();
item.references.push(trim(line.replace(/^#:/, '')));
@@ -121,9 +143,13 @@ PO.parse = function (data) {
item.flags[flags[i]] = true;
}
}
else if (line.match(/^#/)) { // Comment
else if (line.match(/^#\s+/)) { // Translator comment
finish();
item.comments.push(trim(line.replace(/^#/, '')));
item.comments.push(trim(line.replace(/^#\s+/, '')));
}
else if (line.match(/^#\./)) { // Extracted comment
finish();
item.extractedComments.push(trim(line.replace(/^#\./, '')));
}
else if (line.match(/^msgid_plural/)) { // Plural form
item.msgid_plural = extract(line);
@@ -165,18 +191,26 @@ PO.parse = function (data) {
PO.Item = function () {
this.msgid = '';
this.msgctxt = '';
this.msgctxt = null;
this.references = [];
this.msgid_plural = null;
this.msgstr = [];
this.comments = [];
this.comments = []; // translator comments
this.extractedComments = [];
this.flags = {};
this.obsolete = false;
};
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/),
@@ -184,20 +218,29 @@ 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;
};
if (this.references.length > 0) {
this.references.forEach(function (ref) {
lines.push('#: ' + ref);
});
}
// https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
// says order is translator-comments, extracted-comments, references, flags
this.comments.forEach(function (c) {
lines.push('# ' + c);
});
this.extractedComments.forEach(function (c) {
lines.push('#. ' + c);
});
this.references.forEach(function (ref) {
lines.push('#: ' + ref);
});
var flags = Object.keys(this.flags);
if (flags.length > 0) {
@@ -219,7 +262,11 @@ PO.Item.prototype.toString = function () {
}
});
return lines.join("\n");
if (this.obsolete) {
return "#~ " + lines.join("\n#~ ");
} else {
return lines.join("\n");
}
};
module.exports = PO;

2
dist/pofile.min.js vendored
View File

@@ -1 +1 @@
require=function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({W8CkM0:[function(a,b){function c(a){return a.replace(/^\s+|\s+$/g,"")}var d=a("fs"),e=a("lodash.isarray"),f=function(){this.comments=[],this.headers={},this.items=[]};f.prototype.save=function(a,b){d.writeFile(a,this.toString(),b)},f.prototype.toString=function(){var a=[],b=this;this.comments&&this.comments.forEach(function(b){a.push("# "+b)}),a.push('msgid ""'),a.push('msgstr ""');var c=Object.keys(this.headers);return c.forEach(function(c){a.push('"'+c+": "+b.headers[c]+'\\n"')}),a.push(""),this.items.forEach(function(b){a.push(b.toString()),a.push("")}),a.join("\n")},f.load=function(a,b){d.readFile(a,"utf-8",function(a,c){if(a)return b(a);var d=f.parse(c);b(null,d)})},f.parse=function(a){function b(){j.msgid.length>0&&(e.items.push(j),j=new f.Item)}function d(a){return a=c(a),a=a.replace(/^[^"]*"|"$/g,""),a=a.replace(/\\"/g,'"'),a=a.replace(/\\\\/g,"\\")}a=a.replace(/\r\n/g,"\n");var e=new f,g=a.split(/\n\n/),h=g.shift(),i=g.join("\n").split(/\n/);e.headers={"Project-Id-Version":"","Report-Msgid-Bugs-To":"","POT-Creation-Date":"","PO-Revision-Date":"","Last-Translator":"",Language:"","Language-Team":"","Content-Type":"","Content-Transfer-Encoding":"","Plural-Forms":""},h.split(/\n/).forEach(function(a){if(a.match(/^#/)&&e.comments.push(a.replace(/^#\s*/,"")),a.match(/^"/)){a=a.trim().replace(/^"/,"").replace(/\\n"$/,"");var b=a.split(/:/),c=b.shift().trim(),d=b.join(":").trim();e.headers[c]=d}});for(var j=new f.Item,k=null,l=0;i.length>0;){var m=c(i.shift());if(m.match(/^#:/))b(),j.references.push(c(m.replace(/^#:/,"")));else if(m.match(/^#,/)){b();for(var n=c(m.replace(/^#,/,"")).split(","),o=0;o<n.length;o++)j.flags[n[o]]=!0}else if(m.match(/^#/))b(),j.comments.push(c(m.replace(/^#/,"")));else if(m.match(/^msgid_plural/))j.msgid_plural=d(m),k="msgid_plural";else if(m.match(/^msgid/))b(),j.msgid=d(m),k="msgid";else if(m.match(/^msgstr/)){var p=m.match(/^msgstr\[(\d+)\]/);l=p&&p[1]?parseInt(p[1]):0,j.msgstr[l]=d(m),k="msgstr"}else m.match(/^msgctxt/)?(b(),j.msgctxt=d(m)):m.length>0&&("msgstr"===k?j.msgstr[l]+=d(m):"msgid"===k?j.msgid+=d(m):"msgid_plural"===k&&(j.msgid_plural+=d(m)))}return b(),e},f.Item=function(){this.msgid="",this.msgctxt="",this.references=[],this.msgid_plural=null,this.msgstr=[],this.comments=[],this.flags={}},f.Item.prototype.toString=function(){var a=[],b=this,c=function(a,b,c){var d=[],e=b.split(/\n/),f="undefined"!=typeof c?"["+c+"]":"";return e.length>1?(d.push(a+f+' ""'),e.forEach(function(a){d.push('"'+a+'"')})):d.push(a+f+' "'+b+'"'),d};this.references.length>0&&this.references.forEach(function(b){a.push("#: "+b)});var d=Object.keys(this.flags);return d.length>0&&a.push("#, "+d.join(",")),["msgctxt","msgid","msgid_plural","msgstr"].forEach(function(d){var f=b[d];null!=f&&(e(f)&&f.length>1?f.forEach(function(b,e){a=a.concat(c(d,b,e))}):(f=e(f)?f.join():f,a=a.concat(c(d,f))))}),a.join("\n")},b.exports=f},{fs:3,"lodash.isarray":4}],pofile:[function(a,b){b.exports=a("W8CkM0")},{}],3:[function(){},{}],4:[function(a,b){var c=a("lodash._isnative"),d="[object Array]",e=Object.prototype,f=e.toString,g=c(g=Array.isArray)&&g,h=g||function(a){return a&&"object"==typeof a&&"number"==typeof a.length&&f.call(a)==d||!1};b.exports=h},{"lodash._isnative":5}],5:[function(a,b){function c(a){return"function"==typeof a&&f.test(a)}var d=Object.prototype,e=d.toString,f=RegExp("^"+String(e).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$");b.exports=c},{}]},{},["W8CkM0"]);
require=function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({W8CkM0:[function(a,b){function c(a){return a.replace(/^\s+|\s+$/g,"")}var d=a("fs"),e=a("lodash.isarray"),f=function(){this.comments=[],this.headers={},this.items=[]};f.prototype.save=function(a,b){d.writeFile(a,this.toString(),b)},f.prototype.toString=function(){var a=[],b=this;this.comments&&this.comments.forEach(function(b){a.push("# "+b)}),a.push('msgid ""'),a.push('msgstr ""');var c=Object.keys(this.headers);return c.forEach(function(c){a.push('"'+c+": "+b.headers[c]+'\\n"')}),a.push(""),this.items.forEach(function(b){a.push(b.toString()),a.push("")}),a.join("\n")},f.load=function(a,b){d.readFile(a,"utf-8",function(a,c){if(a)return b(a);var d=f.parse(c);b(null,d)})},f.parse=function(a){function b(){j.msgid.length>0&&(e.items.push(j),j=new f.Item,j.obsolete=m,m=!1)}function d(a){return a=c(a),a=a.replace(/^[^"]*"|"$/g,""),a=a.replace(/\\"/g,'"'),a=a.replace(/\\\\/g,"\\")}a=a.replace(/\r\n/g,"\n");var e=new f,g=a.split(/\n\n/),h=g.shift(),i=g.join("\n").split(/\n/);e.headers={"Project-Id-Version":"","Report-Msgid-Bugs-To":"","POT-Creation-Date":"","PO-Revision-Date":"","Last-Translator":"",Language:"","Language-Team":"","Content-Type":"","Content-Transfer-Encoding":"","Plural-Forms":""},h.split(/\n/).reduce(function(a,b){return a.merge&&(b=a.pop().slice(0,-1)+b.slice(1),delete a.merge),/^".*"$/.test(b)&&!/^".*\\n"$/.test(b)&&(a.merge=!0),a.push(b),a},[]).forEach(function(a){if(a.match(/^#/)&&e.comments.push(a.replace(/^#\s*/,"")),a.match(/^"/)){a=a.trim().replace(/^"/,"").replace(/\\n"$/,"");var b=a.split(/:/),c=b.shift().trim(),d=b.join(":").trim();e.headers[c]=d}});for(var j=new f.Item,k=null,l=0,m=!1;i.length>0;){var n=c(i.shift());if(n.match(/^#\~/)?(m=!0,n=c(n.substring(2))):m=!1,n.match(/^#:/))b(),j.references.push(c(n.replace(/^#:/,"")));else if(n.match(/^#,/)){b();for(var o=c(n.replace(/^#,/,"")).split(","),p=0;p<o.length;p++)j.flags[o[p]]=!0}else if(n.match(/^#\s+/))b(),j.comments.push(c(n.replace(/^#\s+/,"")));else if(n.match(/^#\./))b(),j.extractedComments.push(c(n.replace(/^#\./,"")));else if(n.match(/^msgid_plural/))j.msgid_plural=d(n),k="msgid_plural";else if(n.match(/^msgid/))b(),j.msgid=d(n),k="msgid";else if(n.match(/^msgstr/)){var q=n.match(/^msgstr\[(\d+)\]/);l=q&&q[1]?parseInt(q[1]):0,j.msgstr[l]=d(n),k="msgstr"}else n.match(/^msgctxt/)?(b(),j.msgctxt=d(n)):n.length>0&&("msgstr"===k?j.msgstr[l]+=d(n):"msgid"===k?j.msgid+=d(n):"msgid_plural"===k&&(j.msgid_plural+=d(n)))}return b(),e},f.Item=function(){this.msgid="",this.msgctxt=null,this.references=[],this.msgid_plural=null,this.msgstr=[],this.comments=[],this.extractedComments=[],this.flags={},this.obsolete=!1},f.Item.prototype.toString=function(){var a=[],b=this,c=function(a){return a=a.replace(/\\/g,"\\\\"),a.replace(/"/g,'\\"')},d=function(a,b,d){var e=[],f=b.split(/\n/),g="undefined"!=typeof d?"["+d+"]":"";return f.length>1?(e.push(a+g+' ""'),f.forEach(function(a){e.push('"'+c(a)+'"')})):e.push(a+g+' "'+c(b)+'"'),e};this.comments.forEach(function(b){a.push("# "+b)}),this.extractedComments.forEach(function(b){a.push("#. "+b)}),this.references.forEach(function(b){a.push("#: "+b)});var f=Object.keys(this.flags);return f.length>0&&a.push("#, "+f.join(",")),["msgctxt","msgid","msgid_plural","msgstr"].forEach(function(c){var f=b[c];null!=f&&(e(f)&&f.length>1?f.forEach(function(b,e){a=a.concat(d(c,b,e))}):(f=e(f)?f.join():f,a=a.concat(d(c,f))))}),this.obsolete?"#~ "+a.join("\n#~ "):a.join("\n")},b.exports=f},{fs:3,"lodash.isarray":4}],pofile:[function(a,b){b.exports=a("W8CkM0")},{}],3:[function(){},{}],4:[function(a,b){var c=a("lodash._isnative"),d="[object Array]",e=Object.prototype,f=e.toString,g=c(g=Array.isArray)&&g,h=g||function(a){return a&&"object"==typeof a&&"number"==typeof a.length&&f.call(a)==d||!1};b.exports=h},{"lodash._isnative":5}],5:[function(a,b){function c(a){return"function"==typeof a&&f.test(a)}var d=Object.prototype,e=d.toString,f=RegExp("^"+String(e).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$");b.exports=c},{}]},{},["W8CkM0"]);

View File

@@ -74,7 +74,18 @@ PO.parse = function (data) {
'Plural-Forms': '',
};
headers.split(/\n/).forEach(function (header) {
headers.split(/\n/).reduce(function (acc, line) {
if (acc.merge) {
//join lines, remove last resp. first "
line = acc.pop().slice(0, -1) + line.slice(1);
delete acc.merge;
}
if (/^".*"$/.test(line) && !/^".*\\n"$/.test(line)) {
acc.merge = true;
}
acc.push(line);
return acc;
}, []).forEach(function (header) {
if (header.match(/^#/)) {
po.comments.push(header.replace(/^#\s*/, ''));
}
@@ -89,12 +100,15 @@ PO.parse = function (data) {
var item = new PO.Item(),
context = null,
plural = 0;
plural = 0,
obsolete = false;
function finish() {
if (item.msgid.length > 0) {
po.items.push(item);
item = new PO.Item();
item.obsolete = obsolete;
obsolete = false;
}
}
@@ -109,6 +123,14 @@ PO.parse = function (data) {
while (lines.length > 0) {
var line = trim(lines.shift()),
add = false;
if (line.match(/^#\~/)) { // Obsolete item
obsolete = true;
line = trim(line.substring(2));
} else {
obsolete = false;
}
if (line.match(/^#:/)) { // Reference
finish();
item.references.push(trim(line.replace(/^#:/, '')));
@@ -120,9 +142,13 @@ PO.parse = function (data) {
item.flags[flags[i]] = true;
}
}
else if (line.match(/^#/)) { // Comment
else if (line.match(/^#\s+/)) { // Translator comment
finish();
item.comments.push(trim(line.replace(/^#/, '')));
item.comments.push(trim(line.replace(/^#\s+/, '')));
}
else if (line.match(/^#\./)) { // Extracted comment
finish();
item.extractedComments.push(trim(line.replace(/^#\./, '')));
}
else if (line.match(/^msgid_plural/)) { // Plural form
item.msgid_plural = extract(line);
@@ -164,18 +190,26 @@ PO.parse = function (data) {
PO.Item = function () {
this.msgid = '';
this.msgctxt = '';
this.msgctxt = null;
this.references = [];
this.msgid_plural = null;
this.msgstr = [];
this.comments = [];
this.comments = []; // translator comments
this.extractedComments = [];
this.flags = {};
this.obsolete = false;
};
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/),
@@ -183,20 +217,29 @@ 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;
};
if (this.references.length > 0) {
this.references.forEach(function (ref) {
lines.push('#: ' + ref);
});
}
// https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
// says order is translator-comments, extracted-comments, references, flags
this.comments.forEach(function (c) {
lines.push('# ' + c);
});
this.extractedComments.forEach(function (c) {
lines.push('#. ' + c);
});
this.references.forEach(function (ref) {
lines.push('#: ' + ref);
});
var flags = Object.keys(this.flags);
if (flags.length > 0) {
@@ -218,7 +261,11 @@ PO.Item.prototype.toString = function () {
}
});
return lines.join("\n");
if (this.obsolete) {
return "#~ " + lines.join("\n#~ ");
} else {
return lines.join("\n");
}
};
module.exports = PO;

View File

@@ -1,14 +1,18 @@
{
"name": "pofile",
"description": "Parse and serialize Gettext PO files.",
"version": "0.2.3",
"version": "0.2.8",
"author": {
"name": "Ruben Vermeersch",
"email": "ruben@savanne.be",
"url": "http://savanne.be/"
},
"contributors": [
"Mike Holly"
"Eyal Lewinsohn",
"Gabe Gorelick",
"Julian Bäume",
"Mike Holly",
"Sander Houttekier"
],
"homepage": "http://github.com/rubenv/pofile",
"repository": {
@@ -24,7 +28,8 @@
"po"
],
"scripts": {
"test": "grunt test"
"test": "grunt test",
"prepublish": "grunt build"
},
"directories": {
"test": "test"

19
test/fixtures/c-strings.po vendored Normal file
View File

@@ -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 <ruben@rocketeer.be>\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 ""

View File

@@ -15,6 +15,7 @@ msgstr ""
"Language: fr\n"
"X-Generator: Poedit 1.6.2\n"
# Comment
# Translator comment
#. Extracted comment
msgid "Title, as plain text"
msgstr "Attribut title, en tant que texte brut"

23
test/fixtures/commented.po vendored Normal file
View File

@@ -0,0 +1,23 @@
msgid ""
msgstr ""
"Project-Id-Version: Test\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2014-02-17 14:11+0100\n"
"Last-Translator: Ruben Vermeersch <ruben@rocketeer.be>\n"
"Language-Team: \n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.6.4\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-POOTLE-MTIME: 1390921449.000000\n"
#: .tmp/ui/settings/views/console-modal.html
msgid "{{dataLoader.data.length}} results"
msgstr "{{dataLoader.data.length}} resultaten"
#~ msgid "Add order"
#~ msgstr "Order toevoegen"

View File

@@ -1,6 +1,8 @@
# French translation of Link (6.x-2.9)
# Copyright (c) 2011 by the French translation team
#
## Plural-Forms by polish translation team to demonstrate multi-line ##
#
msgid ""
msgstr ""
"Project-Id-Version: Link (6.x-2.9)\n"
@@ -10,7 +12,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2;\n"
"Last-Translator: Ruben Vermeersch <ruben@rocketeer.be>\n"
"Language: fr\n"
"X-Generator: Poedit 1.6.2\n"

View File

@@ -23,7 +23,15 @@ describe('Parse', function () {
assert.equal(item.msgstr, "Les ébauches de jetons suivantes peuvent être utilisées à la fois dans les chemins et dans les titres. Lorsqu'elles sont utilisées dans un chemin ou un titre, elles seront remplacées par les valeurs appropriées.");
});
it('Handles string comments', function () {
it('Handles multi-line headers', function () {
var po = PO.parse(fs.readFileSync(__dirname + '/fixtures/multi-line.po', 'utf8'));
assert.notEqual(po, null);
assert.equal(po.items.length, 1);
assert.equal(po.headers['Plural-Forms'], 'nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;');
});
it('Handles translator comments', function () {
var po = PO.parse(fs.readFileSync(__dirname + '/fixtures/comment.po', 'utf8'));
assert.notEqual(po, null);
assert.equal(po.items.length, 1);
@@ -31,7 +39,18 @@ describe('Parse', function () {
var item = po.items[0];
assert.equal(item.msgid, "Title, as plain text");
assert.equal(item.msgstr, "Attribut title, en tant que texte brut");
assert.deepEqual(item.comments, ["Comment"]);
assert.deepEqual(item.comments, ["Translator comment"]);
});
it('Handles extracted comments', function () {
var po = PO.parse(fs.readFileSync(__dirname + '/fixtures/comment.po', 'utf8'));
assert.notEqual(po, null);
assert.equal(po.items.length, 1);
var item = po.items[0];
assert.equal(item.msgid, "Title, as plain text");
assert.equal(item.msgstr, "Attribut title, en tant que texte brut");
assert.deepEqual(item.extractedComments, ["Extracted comment"]);
});
it('Handles string references', function () {
@@ -73,4 +92,36 @@ describe('Parse', function () {
assert.equal(ambiguousItems[0].msgctxt, 'folder display');
assert.equal(ambiguousItems[1].msgctxt, 'folder action');
});
it('Handles obsolete items', function () {
var po = PO.parse(fs.readFileSync(__dirname + '/fixtures/commented.po', 'utf8'));
assert.equal(po.items.length, 2);
var item = po.items[0];
assert.equal(item.obsolete, false);
assert.equal(item.msgid, "{{dataLoader.data.length}} results");
assert.equal(item.msgstr, "{{dataLoader.data.length}} resultaten");
item = po.items[1];
assert.equal(item.obsolete, true);
assert.equal(item.msgid, "Add order");
assert.equal(item.msgstr, "Order toevoegen");
});
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 \\');
});
});
});

View File

@@ -38,10 +38,75 @@ describe('Write', function () {
assertHasLine(str, "msgstr \"Source\"");
});
it('write msgctxt', function () {
var input = fs.readFileSync(__dirname + '/fixtures/big.po', 'utf8');
it('write translator comment', function () {
var input = fs.readFileSync(__dirname + '/fixtures/comment.po', 'utf8');
var po = PO.parse(input);
var str = po.toString();
assertHasLine(str, 'msgctxt "folder action"');
assertHasLine(str, "# Translator comment");
});
it('write extracted comment', function () {
var input = fs.readFileSync(__dirname + '/fixtures/comment.po', 'utf8');
var po = PO.parse(input);
var str = po.toString();
assertHasLine(str, '#. Extracted comment');
});
it('write obsolete items', function () {
var input = fs.readFileSync(__dirname + '/fixtures/commented.po', 'utf8');
var po = PO.parse(input);
var str = po.toString();
assertHasLine(str, '#~ msgid "Add order"');
assertHasLine(str, '#~ msgstr "Order toevoegen"');
});
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"');
});
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 () {
it('should write context field to file', function () {
var input = fs.readFileSync(__dirname + '/fixtures/big.po', 'utf8');
var po = PO.parse(input);
var str = po.toString();
assertHasLine(str, 'msgctxt "folder action"');
});
it('should ignore omitted context field', function () {
var po = new PO();
var item = new PO.Item();
po.items.push(item);
assert.ok(po.toString().indexOf('msgctxt') < 0);
});
it('should write empty context field', function () {
var po = new PO();
var item = new PO.Item();
item.msgctxt = '';
po.items.push(item);
assert.ok(po.toString().indexOf('msgctxt') >= 0);
});
});
});