diff --git a/README.md b/README.md index 687811f..a8ea099 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,13 @@ A node module with a CLI that extracts gettext strings from JavaScript, EJS, Jad ```javascript gettext("Hello world!"); -gettext("Hello" + ' world!'); +gettext("Hello " + 'world!'); myModule.gettext("Hello " + 'world!'); gettext.call(myObj, "Hello " + 'world!'); +ngettext("Here's an apple for you", "Here are %s apples for you", 3); +ngettext("Here's an apple" + ' for you', "Here are %s apples" + ' for you', 3); +myModule.ngettext("Here's an apple" + ' for you', "Here are %s apples" + ' for you', 3); +ngettext.call(myObj, "Here's an apple" + ' for you', "Here are %s apples" + ' for you', 3); ``` It also extracts comments that begin with "L10n:" when they appear above or next to a `gettext` call: diff --git a/lib/jsxgettext.js b/lib/jsxgettext.js index 7a400bc..ccc27d5 100644 --- a/lib/jsxgettext.js +++ b/lib/jsxgettext.js @@ -44,7 +44,8 @@ function getTranslatable(node, options) { if (callee.property.name === 'call') { prop = callee.object.property; funcName = callee.object.name || prop && (prop.name || prop.value); - arg = node.arguments[1]; // skip context object + node.arguments = node.arguments.slice( 1 ); // skip context object + arg = node.arguments[0]; } else { funcName = callee.property.name; } @@ -53,6 +54,10 @@ function getTranslatable(node, options) { if (options.keyword.indexOf(funcName) === -1) return false; + // If the gettext function's name starts with "n" (i.e. ngettext or n_) and its first 2 arguments are strings, we regard it as a plural function + if (arg && funcName.substr(0, 1) === "n" && (isStrConcatExpr(arg) || isStringLiteral(arg)) && node.arguments[1] && (isStrConcatExpr(node.arguments[1]) || isStringLiteral(node.arguments[1]))) + return [arg, node.arguments[1]]; + if (arg && (isStrConcatExpr(arg) || isStringLiteral(arg))) return arg; @@ -116,7 +121,14 @@ function parse(sources, options) { throw err; } - options.keyword = options.keyword || ['gettext']; + if( options.keyword ) { + Object.keys(options.keyword).forEach(function (index) { + options.keyword.push('n' + options.keyword[index]); + }); + } + else { + options.keyword = ['gettext', 'ngettext']; + } var tagName = options.addComments || "L10n:"; var commentRegex = new RegExp([ "^\\s*" + regExpEscape(tagName), // The "TAG" provided externally or "L10n:" by default @@ -156,7 +168,10 @@ function parse(sources, options) { if (!arg) return; - var str = extractStr(arg); + var msgid = arg; + if( arg.constructor === Array ) + msgid = arg[0]; + var str = extractStr(msgid); var line = node.loc.start.line; var comments = findComments(astComments, line); @@ -170,6 +185,10 @@ function parse(sources, options) { reference: ref } }; + if( arg.constructor === Array ) { + translations[str].msgid_plural = extractStr(arg[1]); + translations[str].msgstr = ['', '']; + } } else { if(translations[str].comments) { translations[str].comments.reference += '\n' + ref; diff --git a/test/inputs/anonymous_functions.js b/test/inputs/anonymous_functions.js index 76ab6e3..1e31187 100644 --- a/test/inputs/anonymous_functions.js +++ b/test/inputs/anonymous_functions.js @@ -2,8 +2,10 @@ var testObj = { somemethod: function () {}, - gettext: function () {} + gettext: function () {}, + ngettext: function () {} }; testObj.somemethod('I shall not pass'); testObj.gettext("I'm gonna get translated, yay!"); +testObj.ngettext("I'm also gonna get translated!", "I'm the plural form!", 2); diff --git a/test/inputs/concat.js b/test/inputs/concat.js index ca09289..d6ffeaf 100644 --- a/test/inputs/concat.js +++ b/test/inputs/concat.js @@ -3,3 +3,13 @@ gettext( "and we'd like to concatenate it in our source "+ "code to avoid really wide files." ); + +ngettext( + "This is another quite long sentence "+ + "and we'd like to concatenate it in our source "+ + "code to avoid really wide files.", + "This is the plural version of our quite long sentence "+ + "that we'd like to concatenate it in our source "+ + "code to avoid really wide files.", + 3 +); \ No newline at end of file diff --git a/test/inputs/expressions.js b/test/inputs/expressions.js index 96fa12d..a6266a3 100644 --- a/test/inputs/expressions.js +++ b/test/inputs/expressions.js @@ -3,7 +3,10 @@ var templates = { landing: 'pin_verification', subject: test.gettext("Confirm email address for Persona"), subject2: test.gettext.call(test, "Confirm email address for Persona 2"), - subject3: test.something.someotherthing['gettext'].call(test, "Confirm email address for Persona 3", somethingelse) + subject3: test.something.someotherthing['gettext'].call(test, "Confirm email address for Persona 3", somethingelse), + subject4: test.ngettext("Confirm email address for Persona 4", "Confirm email address for Persona 4 plural", 4), + subject5: test.ngettext.call(test, "Confirm email address for Persona 5", "Confirm email address for Persona 5 plural", 5), + subject6: test.something.someotherthing['ngettext'].call(test, "Confirm email address for Persona 6", "Confirm email address for Persona 6 plural", 6, somethingelse) } }; diff --git a/test/inputs/filter.ejs b/test/inputs/filter.ejs index 1ea4db0..292ea1c 100644 --- a/test/inputs/filter.ejs +++ b/test/inputs/filter.ejs @@ -1 +1,2 @@ <%=: gettext("this is a localizable string") | capitalize %> +<%=: ngettext("this is a localizable singular string", "this is a localizable plural string", 2) | capitalize %> diff --git a/test/inputs/first.js b/test/inputs/first.js index 903e507..6609ce7 100644 --- a/test/inputs/first.js +++ b/test/inputs/first.js @@ -1,2 +1,3 @@ var msg = gettext('Hello World'); -var dup = gettext('This message is used twice.'); \ No newline at end of file +var dup = gettext('This message is used twice.'); +var dup2 = ngettext('This other message is used twice.', 'This other message is used twice. - plural', 2); \ No newline at end of file diff --git a/test/inputs/include.ejs b/test/inputs/include.ejs index b7c8685..de37201 100644 --- a/test/inputs/include.ejs +++ b/test/inputs/include.ejs @@ -1,2 +1,3 @@ <% include this/include/syntax/is/kinda/dumb %> <%= gettext("this is a localizable string") %> +<%= ngettext("this is a localizable singular string", "this is a localizable plural string", 2) %> diff --git a/test/inputs/multiple_keywords.js b/test/inputs/multiple_keywords.js index 8d41eb8..517ef0e 100644 --- a/test/inputs/multiple_keywords.js +++ b/test/inputs/multiple_keywords.js @@ -1,3 +1,7 @@ _('should be translatable'); t('should also be translatable'); gettext('should NOT be translatable since we did not define it as keyword'); + +n_('should be translatable - singular', 'should be translatable - plural', 2); +nt('should also be translatable - singular', 'should also be translatable - plural', 2); +ngettext('should NOT be translatable since we did not define it as keyword - singular', 'should NOT be translatable since we did not define it as keyword - plural', 2); diff --git a/test/inputs/raw.ejs b/test/inputs/raw.ejs index 066d5af..de4b752 100644 --- a/test/inputs/raw.ejs +++ b/test/inputs/raw.ejs @@ -1 +1,3 @@ <%== gettext("this is a raw localizable string") %> +<%== gettext("this is a raw localizable string") %> +<%== ngettext("this is a raw localizable singular string", "this is a raw localizable plural string", 2) %> diff --git a/test/inputs/second.js b/test/inputs/second.js index 8e877f3..a74eae7 100644 --- a/test/inputs/second.js +++ b/test/inputs/second.js @@ -1 +1,2 @@ -var dup = gettext('This message is used twice.'); \ No newline at end of file +var dup = gettext('This message is used twice.'); +var dup2 = ngettext('This other message is used twice.', 'This other message is used twice. - plural', 2); \ No newline at end of file diff --git a/test/outputs/anonymous_functions.pot b/test/outputs/anonymous_functions.pot index 1094a39..dc9cf5b 100644 --- a/test/outputs/anonymous_functions.pot +++ b/test/outputs/anonymous_functions.pot @@ -1,3 +1,9 @@ -#: inputs/anonymous_functions.js:9 +#: inputs/anonymous_functions.js:10 msgid "I'm gonna get translated, yay!" -msgstr "" \ No newline at end of file +msgstr "" + +#: inputs/anonymous_functions.js:11 +msgid "I'm also gonna get translated!" +msgid_plural "I'm the plural form!" +msgstr[0] "" +msgstr[1] "" \ No newline at end of file diff --git a/test/outputs/concat.pot b/test/outputs/concat.pot index 6451f24..40ea673 100644 --- a/test/outputs/concat.pot +++ b/test/outputs/concat.pot @@ -2,4 +2,14 @@ msgid "" "The second string is significantly longer and we'd like to concatenate it " "in our source code to avoid really wide files." -msgstr "" \ No newline at end of file +msgstr "" + +#: inputs/concat.js:7 +msgid "" +"This is another quite long sentence and we'd like to concatenate it in our " +"source code to avoid really wide files." +msgid_plural "" +"This is the plural version of our quite long sentence that we'd like to " +"concatenate it in our source code to avoid really wide files." +msgstr[0] "" +msgstr[1] "" \ No newline at end of file diff --git a/test/outputs/expressions.pot b/test/outputs/expressions.pot index d049601..6785087 100644 --- a/test/outputs/expressions.pot +++ b/test/outputs/expressions.pot @@ -8,4 +8,22 @@ msgstr "" #: inputs/expressions.js:6 msgid "Confirm email address for Persona 3" -msgstr "" \ No newline at end of file +msgstr "" + +#: inputs/expressions.js:7 +msgid "Confirm email address for Persona 4" +msgid_plural "Confirm email address for Persona 4 plural" +msgstr[0] "" +msgstr[1] "" + +#: inputs/expressions.js:8 +msgid "Confirm email address for Persona 5" +msgid_plural "Confirm email address for Persona 5 plural" +msgstr[0] "" +msgstr[1] "" + +#: inputs/expressions.js:9 +msgid "Confirm email address for Persona 6" +msgid_plural "Confirm email address for Persona 6 plural" +msgstr[0] "" +msgstr[1] "" \ No newline at end of file diff --git a/test/outputs/messages_firstpass.pot b/test/outputs/messages_firstpass.pot index de2d6dd..9035ae0 100644 --- a/test/outputs/messages_firstpass.pot +++ b/test/outputs/messages_firstpass.pot @@ -4,4 +4,10 @@ msgstr "" #: inputs/first.js:2 msgid "This message is used twice." -msgstr "" \ No newline at end of file +msgstr "" + +#: inputs/first.js:3 +msgid "This other message is used twice." +msgid_plural "This other message is used twice. - plural" +msgstr[0] "" +msgstr[1] "" \ No newline at end of file diff --git a/test/outputs/messages_secondpass.pot b/test/outputs/messages_secondpass.pot index ae52f9e..9f80fc7 100644 --- a/test/outputs/messages_secondpass.pot +++ b/test/outputs/messages_secondpass.pot @@ -5,4 +5,11 @@ msgstr "" #: inputs/first.js:2 #: inputs/second.js:1 msgid "This message is used twice." -msgstr "" \ No newline at end of file +msgstr "" + +#: inputs/first.js:3 +#: inputs/second.js:2 +msgid "This other message is used twice." +msgid_plural "This other message is used twice. - plural" +msgstr[0] "" +msgstr[1] "" \ No newline at end of file diff --git a/test/outputs/multiple_keywords.pot b/test/outputs/multiple_keywords.pot index 4630f6a..b835c1a 100644 --- a/test/outputs/multiple_keywords.pot +++ b/test/outputs/multiple_keywords.pot @@ -4,4 +4,16 @@ msgstr "" #: inputs/multiple_keywords.js:2 msgid "should also be translatable" -msgstr "" \ No newline at end of file +msgstr "" + +#: inputs/multiple_keywords.js:5 +msgid "should be translatable - singular" +msgid_plural "should be translatable - plural" +msgstr[0] "" +msgstr[1] "" + +#: inputs/multiple_keywords.js:6 +msgid "should also be translatable - singular" +msgid_plural "should also be translatable - plural" +msgstr[0] "" +msgstr[1] "" \ No newline at end of file diff --git a/test/tests/ejs.js b/test/tests/ejs.js index 63d70ef..2cc6126 100644 --- a/test/tests/ejs.js +++ b/test/tests/ejs.js @@ -18,6 +18,8 @@ exports['test ejs'] = function (assert, cb) { assert.ok(result.length > 1, 'result is not empty'); assert.ok(result.indexOf('this is a localizable string') !== -1, 'localizable strings are extracted'); + assert.ok(result.indexOf('this is a localizable plural string') !== -1, + 'localizable plural strings are extracted'); cb(); }); }; diff --git a/test/tests/ejs_filter.js b/test/tests/ejs_filter.js index 792f8b3..1227619 100644 --- a/test/tests/ejs_filter.js +++ b/test/tests/ejs_filter.js @@ -18,6 +18,8 @@ exports['test ejs'] = function (assert, cb) { assert.ok(result.length > 1, 'result is not empty'); assert.ok(result.indexOf('this is a localizable string') !== -1, 'localizable strings are extracted'); + assert.ok(result.indexOf('this is a localizable plural string') !== -1, + 'localizable plural strings are extracted'); cb(); }); }; diff --git a/test/tests/ejs_raw.js b/test/tests/ejs_raw.js index 1936f43..79d9b56 100644 --- a/test/tests/ejs_raw.js +++ b/test/tests/ejs_raw.js @@ -18,6 +18,8 @@ exports['test ejs'] = function (assert, cb) { assert.ok(result.length > 1, 'raw result is not empty'); assert.ok(result.indexOf('this is a raw localizable string') !== -1, 'raw localizable strings are extracted'); + assert.ok(result.indexOf('this is a raw localizable plural string') !== -1, + 'raw localizable plural strings are extracted'); cb(); }); };