diff --git a/lib/extract.js b/lib/extract.js index c33c67f..e94aadf 100644 --- a/lib/extract.js +++ b/lib/extract.js @@ -13,7 +13,7 @@ var mkAttrRegex = function (startDelim, endDelim) { start = '^'; } - return new RegExp(start + '\\s*(\'|"|")(.*?)\\1\\s*\\|\\s*translate\\s*(' + end + '|\\|)', 'g'); + return new RegExp(start + '\\s*(\'|"|")(.*?)\\1\\s*\\|\\s*translate(Plural:.*:\\1(.*?)\\1)?\\s*(' + end + '|\\|)', 'g'); }; var noDelimRegex = mkAttrRegex('', ''); @@ -205,8 +205,13 @@ var Extractor = (function () { self.addString(filename, str, plural, extractedComment); } else if (matches = noDelimRegex.exec(node.attr(attr))) { str = matches[2].replace(/\\\'/g, '\''); - self.addString(filename, str); + plural = (typeof matches[4] === 'string') ? matches[4].replace(/\\\'/g, '\'') : undefined; + self.addString(filename, str, plural); } + + // Regex with global modifier keeps lastIndex + // Reset it to make sure all strings are checked from index 0 + noDelimRegex.lastIndex = 0; } if (typeof node.attr('data-translate') !== 'undefined') { @@ -219,7 +224,8 @@ var Extractor = (function () { var matches; while (matches = this.attrRegex.exec(src)) { var str = matches[2].replace(/\\\'/g, '\''); - this.addString(filename, str); + var plural = (typeof matches[4] === 'string') ? matches[4].replace(/\\\'/g, '\'') : undefined; + this.addString(filename, str, plural); } }; diff --git a/test/extract.coffee b/test/extract.coffee index 2c26acc..43b5bcf 100644 --- a/test/extract.coffee +++ b/test/extract.coffee @@ -506,4 +506,30 @@ describe 'Extract', -> ] catalog = testExtract(files) - assert.equal(catalog.items.length, 0) \ No newline at end of file + assert.equal(catalog.items.length, 0) + + it 'Extracts filter strings for translatePlural', -> + files = [ + 'test/fixtures/filter-plural.html' + ] + catalog = testExtract(files) + + # Notes on the ordering: Strings seem to be sorted alphabetically + assert.equal(catalog.items.length, 3) + assert.equal(catalog.items[0].msgid, '1 coin') + assert.equal(catalog.items[0].msgid_plural, '{} coins') + assert.equal(catalog.items[0].msgstr.length, 2) + assert.equal(catalog.items[0].references.length, 1) + assert.equal(catalog.items[0].references[0], 'test/fixtures/filter-plural.html') + + assert.equal(catalog.items[1].msgid, 'One file selected') + assert.equal(catalog.items[1].msgid_plural, '{} files selected') + assert.equal(catalog.items[1].msgstr.length, 2) + assert.equal(catalog.items[1].references.length, 1) + assert.equal(catalog.items[1].references[0], 'test/fixtures/filter-plural.html') + + assert.equal(catalog.items[2].msgid, 'You have an unread message') + assert.equal(catalog.items[2].msgid_plural, 'You have {} unread messages') + assert.equal(catalog.items[2].msgstr.length, 2) + assert.equal(catalog.items[2].references.length, 1) + assert.equal(catalog.items[2].references[0], 'test/fixtures/filter-plural.html') diff --git a/test/extract_regex.js b/test/extract_regex.js index 0910901..b22d11d 100644 --- a/test/extract_regex.js +++ b/test/extract_regex.js @@ -13,8 +13,11 @@ describe('Extract: Filter regex', function () { var hit = false; while (matches = regex.exec('{{\'Hello\'|translate}}')) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); hit = true; } assert(hit); @@ -25,8 +28,11 @@ describe('Extract: Filter regex', function () { var hit = false; while (matches = regex.exec('{{\'Hello\'|translate|lowercase}}')) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); hit = true; } assert(hit); @@ -37,8 +43,11 @@ describe('Extract: Filter regex', function () { var hit = false; while (matches = regex.exec('{{"Hello"|translate}}')) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); hit = true; } assert(hit); @@ -49,8 +58,11 @@ describe('Extract: Filter regex', function () { var hit = false; while (matches = regex.exec('{{\'Hello\'|translate|lowercase}}')) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); hit = true; } assert(hit); @@ -62,11 +74,17 @@ describe('Extract: Filter regex', function () { while (matches = regex.exec('{{\'Hello\'|translate}} {{"Second"|translate}}')) { if (hit === 0) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); } else if (hit === 1) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Second'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); } hit++; } @@ -79,11 +97,17 @@ describe('Extract: Filter regex', function () { while (matches = regex.exec('{{\'Hello\'|translate|lowercase}} {{"Second"|translate|uppercase}}')) { if (hit === 0) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); } else if (hit === 1) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Second'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); } hit++; } @@ -96,11 +120,17 @@ describe('Extract: Filter regex', function () { while (matches = regex.exec('{{\'Hello\'|translate}} {{"Second"|translate}}')) { if (hit === 0) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); } else if (hit === 1) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Second'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); } hit++; } @@ -113,11 +143,17 @@ describe('Extract: Filter regex', function () { while (matches = regex.exec('{{\'Hello\'|translate}} {{"Second"|translate|lowercase}}')) { if (hit === 0) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); } else if (hit === 1) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Second'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); } hit++; } @@ -129,8 +165,11 @@ describe('Extract: Filter regex', function () { var hit = false; while (matches = regex.exec('{{ "Hello" | translate }}')) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); hit = true; } assert(hit); @@ -141,8 +180,11 @@ describe('Extract: Filter regex', function () { var hit = false; while (matches = regex.exec('{{ "Hello" | translate | lowercase }}')) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); hit = true; } assert(hit); @@ -154,8 +196,11 @@ describe('Extract: Filter regex', function () { var hit = false; while (matches = regex.exec('[[\'Hello\'|translate]]')) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); hit = true; } assert(hit); @@ -167,8 +212,11 @@ describe('Extract: Filter regex', function () { var hit = false; while (matches = regex.exec('[[\'Hello\'|translate|lowercase]]')) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); hit = true; } assert(hit); @@ -180,8 +228,11 @@ describe('Extract: Filter regex', function () { var hit = false; while (matches = regex.exec('\'Hello\' | translate')) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); hit = true; } assert(hit); @@ -193,8 +244,11 @@ describe('Extract: Filter regex', function () { var hit = false; while (matches = regex.exec('\'Hello\' | translate | lowercase')) { - assert.equal(matches.length, 4); + assert.equal(matches.length, 6); assert.equal(matches[2], 'Hello'); + // Assert no plural is matched + assert.equal(matches[3], undefined); + assert.equal(matches[4], undefined); hit = true; } assert(hit); @@ -202,3 +256,223 @@ describe('Extract: Filter regex', function () { assert.equal(matches, null); }); }); + +describe('Extract: Filter regex for translatePlural', function () { + var regex = null; + + beforeEach(function () { + regex = mkAttrRegex('{{', '}}'); + }); + + it('Matches a simple string', function () { + var matches; + var hit = false; + + while (matches = regex.exec('{{\'1 cat\'|translatePlural:n:\'{} cats\'}}')) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + hit = true; + } + assert(hit); + }); + + it('Matches a simple string with multiple filters', function () { + var matches; + var hit = false; + + while (matches = regex.exec('{{\'1 cat\'|translatePlural:n:\'{} cats\'|lowercase}}')) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + hit = true; + } + assert(hit); + }); + + it('Matches double quotes', function () { + var matches; + var hit = false; + + while (matches = regex.exec('{{"1 cat"|translatePlural:n:"{} cats"}}')) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + hit = true; + } + assert(hit); + }); + + it('Matches double quotes with multiple filters', function () { + var matches; + var hit = false; + + while (matches = regex.exec('{{"1 cat"|translatePlural:n:"{} cats"|lowercase}}')) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + hit = true; + } + assert(hit); + }); + + it('Matches multiple strings', function () { + var matches; + var hit = 0; + + while (matches = regex.exec('{{\'1 cat\'|translatePlural:n:\'{} cats\'}} {{"1 dog"|translatePlural:n:"{} dogs"}}')) { + if (hit === 0) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + } else if (hit === 1) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 dog'); + assert.equal(matches[4], '{} dogs'); + } + hit++; + } + assert.equal(hit, 2); + }); + + it('Matches multiple strings with multiple filters', function () { + var matches; + var hit = 0; + + while (matches = regex.exec('{{\'1 cat\'|translatePlural:n:\'{} cats\'|lowercase}} {{"1 dog"|translatePlural:n:"{} dogs"|uppercase}}')) { + if (hit === 0) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + } else if (hit === 1) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 dog'); + assert.equal(matches[4], '{} dogs'); + } + hit++; + } + assert.equal(hit, 2); + }); + + it('Matches encoded quotes', function () { + var matches; + var hit = 0; + + while (matches = regex.exec('{{\'1 cat\'|translatePlural:n:\'{} cats\'}} {{"1 dog"|translatePlural:n:"{} dogs"}}')) { + if (hit === 0) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + } else if (hit === 1) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 dog'); + assert.equal(matches[4], '{} dogs'); + } + hit++; + } + assert.equal(hit, 2); + }); + + it('Matches encoded quotes with multiple filters', function () { + var matches; + var hit = 0; + + while (matches = regex.exec('{{\'1 cat\'|translatePlural:n:\'{} cats\'}} {{"1 dog"|translatePlural:n:"{} dogs"|lowercase}}')) { + if (hit === 0) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + } else if (hit === 1) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 dog'); + assert.equal(matches[4], '{} dogs'); + } + hit++; + } + assert.equal(hit, 2); + }); + + it('Matches spaces', function () { + var matches; + var hit = false; + + while (matches = regex.exec('{{ \'1 cat\' | translatePlural:n:\'{} cats\' }}')) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + hit = true; + } + assert(hit); + }); + + it('Matches spaces with multiple filters', function () { + var matches; + var hit = false; + + while (matches = regex.exec('{{ \'1 cat\' | translatePlural:n:\'{} cats\' | lowercase }}')) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + hit = true; + } + assert(hit); + }); + + it('Can customize delimiters', function () { + var matches; + var regex = mkAttrRegex('[[', ']]'); + var hit = false; + + while (matches = regex.exec('[[\'1 cat\'|translatePlural:n:\'{} cats\']]')) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + hit = true; + } + assert(hit); + }); + + it('Can customize delimiters with multiple filters', function () { + var matches; + var regex = mkAttrRegex('[[', ']]'); + var hit = false; + + while (matches = regex.exec('[[\'1 cat\'|translatePlural:n:\'{} cats\'|lowercase]]')) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + hit = true; + } + assert(hit); + }); + + it('Can be used without delimiters', function () { + var matches; + var regex = mkAttrRegex('', ''); + var hit = false; + + while (matches = regex.exec('\'1 cat\' | translatePlural:n:\'{} cats\'')) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + hit = true; + } + assert(hit); + }); + + it('Can be used without delimiters with multiple filters', function () { + var matches; + var regex = mkAttrRegex('', ''); + var hit = false; + + while (matches = regex.exec('\'1 cat\' | translatePlural:n:\'{} cats\' | lowercase')) { + assert.equal(matches.length, 6); + assert.equal(matches[2], '1 cat'); + assert.equal(matches[4], '{} cats'); + hit = true; + } + assert(hit); + matches = regex.exec('{{\'1 cat\' | translatePlural:n:\'{} cats\'}}'); + assert.equal(matches, null); + }); +}); diff --git a/test/fixtures/filter-plural.html b/test/fixtures/filter-plural.html new file mode 100644 index 0000000..e18bf2f --- /dev/null +++ b/test/fixtures/filter-plural.html @@ -0,0 +1,7 @@ + +
+ + +{{'You have an unread message' | translatePlural:apples.length:'You have {} unread messages'}}
+ +