From 6790bfb466b056fd7f6f9dc2e27561feeaaf99b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20B=C3=A4ume?= Date: Tue, 21 Jan 2014 13:44:52 +0100 Subject: [PATCH 1/4] add parsing support for msgctxt field of poitems The gettext format specifies a msgctxt field for translated items. An example of how to use this field has been added to the tests and the parsing of this attribute has been implemented. --- lib/po.js | 5 +++++ test/fixtures/big.po | 10 ++++++++++ test/parse.js | 13 ++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/po.js b/lib/po.js index 2e0c586..0068547 100644 --- a/lib/po.js +++ b/lib/po.js @@ -139,6 +139,10 @@ PO.parse = function (data) { item.msgstr[plural] = extract(line); context = 'msgstr'; } + else if (line.match(/^msgctxt/)) { // Context + finish(); + item.msgctxt = extract(line); + } else { // Probably multiline string or blank if (line.length > 0) { if (context === 'msgstr') { @@ -160,6 +164,7 @@ PO.parse = function (data) { PO.Item = function () { this.msgid = ''; + this.msgctxt = ''; this.references = []; this.msgid_plural = null; this.msgstr = []; diff --git a/test/fixtures/big.po b/test/fixtures/big.po index d2a37a7..4638b40 100644 --- a/test/fixtures/big.po +++ b/test/fixtures/big.po @@ -285,3 +285,13 @@ msgstr "Attribut 'title' du lien" # Comment msgid "Title, as plain text" msgstr "Attribut title, en tant que texte brut" + +# Empty should be adjective +msgctxt "folder display" +msgid "Empty folder" +msgstr "This folder is empty." + +# Empty should be verb +msgctxt "folder action" +msgid "Empty folder" +msgstr "Make this folder empty." diff --git a/test/parse.js b/test/parse.js index 3996326..483f303 100644 --- a/test/parse.js +++ b/test/parse.js @@ -6,7 +6,7 @@ describe('Parse', function () { it('Parses the big po file', function () { var po = PO.parse(fs.readFileSync(__dirname + '/fixtures/big.po', 'utf8')); assert.notEqual(po, null); - assert.equal(po.items.length, 67); + assert.equal(po.items.length, 69); var item = po.items[0]; assert.equal(item.msgid, "Title"); @@ -62,4 +62,15 @@ describe('Parse', function () { assert.notEqual(item.flags, null); assert.equal(item.flags.fuzzy, true); }); + + it('Parses item context', function () { + var po = PO.parse(fs.readFileSync(__dirname + '/fixtures/big.po', 'utf8')); + + var ambiguousItems = po.items.filter(function (item) { + return item.msgid === 'Empty folder'; + }); + + assert.equal(ambiguousItems[0].msgctxt, 'folder display'); + assert.equal(ambiguousItems[1].msgctxt, 'folder action'); + }); }); From c046b62873b5c932fe071b07374867a3255e0ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20B=C3=A4ume?= Date: Tue, 21 Jan 2014 15:06:11 +0100 Subject: [PATCH 2/4] implement writing msgctxt field since the parser can extract this information, now, the writer should also be able to write it back to a po file --- lib/po.js | 2 +- test/write.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/po.js b/lib/po.js index 0068547..3825b65 100644 --- a/lib/po.js +++ b/lib/po.js @@ -203,7 +203,7 @@ PO.Item.prototype.toString = function () { lines.push('#, ' + flags.join(",")); } - ['msgid', 'msgid_plural', 'msgstr'].forEach(function (keyword) { + ['msgctxt', 'msgid', 'msgid_plural', 'msgstr'].forEach(function (keyword) { var text = that[keyword]; if (text != null) { if (isArray(text) && text.length > 1) { diff --git a/test/write.js b/test/write.js index 9aece24..3a04376 100644 --- a/test/write.js +++ b/test/write.js @@ -37,4 +37,11 @@ describe('Write', function () { var str = po.toString(); assertHasLine(str, "msgstr \"Source\""); }); + + it('write msgctxt', function () { + var input = fs.readFileSync(__dirname + '/fixtures/big.po', 'utf8'); + var po = PO.parse(input); + var str = po.toString(); + assertHasLine(str, 'msgctxt "folder action"'); + }); }); From 4ba2cf1cefb4944282cc6a96c58a610d9c7df43d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20B=C3=A4ume?= Date: Tue, 21 Jan 2014 15:10:02 +0100 Subject: [PATCH 3/4] add distfiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit as compiled by the grunt task. These come in an own commit, since I didn’t want them to clutter the actual implementation commits. --- dist/pofile.js | 13 +++++++++---- dist/pofile.min.js | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/dist/pofile.js b/dist/pofile.js index 9c2126e..2d2ea87 100644 --- a/dist/pofile.js +++ b/dist/pofile.js @@ -1,4 +1,4 @@ -require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) { if (context === 'msgstr') { @@ -161,6 +165,7 @@ PO.parse = function (data) { PO.Item = function () { this.msgid = ''; + this.msgctxt = ''; this.references = []; this.msgid_plural = null; this.msgstr = []; @@ -199,7 +204,7 @@ PO.Item.prototype.toString = function () { lines.push('#, ' + flags.join(",")); } - ['msgid', 'msgid_plural', 'msgstr'].forEach(function (keyword) { + ['msgctxt', 'msgid', 'msgid_plural', 'msgstr'].forEach(function (keyword) { var text = that[keyword]; if (text != null) { if (isArray(text) && text.length > 1) { @@ -220,7 +225,7 @@ PO.Item.prototype.toString = function () { module.exports = PO; },{"fs":3,"lodash.isarray":4}],"pofile":[function(require,module,exports){ -module.exports=require('W8CkM0'); +module.exports=require('+dPpep'); },{}],3:[function(require,module,exports){ },{}],4:[function(require,module,exports){ @@ -306,4 +311,4 @@ function isNative(value) { module.exports = isNative; -},{}]},{},["W8CkM0"]) \ No newline at end of file +},{}]},{},["+dPpep"]) \ No newline at end of file diff --git a/dist/pofile.min.js b/dist/pofile.min.js index 24489c3..f27f71e 100644 --- a/dist/pofile.min.js +++ b/dist/pofile.min.js @@ -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;g0&&(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;o0&&("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.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(",")),["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"]); \ No newline at end of file +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;g0&&(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;o0&&("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("+dPpep")},{}],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},{}]},{},["+dPpep"]); \ No newline at end of file From 87e29b6c2161d7bece1da1d7fbd5965e04f78669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20B=C3=A4ume?= Date: Tue, 21 Jan 2014 15:46:31 +0100 Subject: [PATCH 4/4] add documentation for the new field possibly makes developers happy to have this field documented --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7558e35..6f0d7a3 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ The `PO` class exposes the following members: * `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 ## Contributing