diff --git a/dist/angular-gettext.js b/dist/angular-gettext.js
index b192bc8..3a6738f 100644
--- a/dist/angular-gettext.js
+++ b/dist/angular-gettext.js
@@ -101,6 +101,10 @@ angular.module('gettext').factory('gettextCatalog', ["gettextPlurals", "$http",
},
getPlural: function (n, string, stringPlural, scope, context) {
+ if (!n && n !== 0) {
+ return this.getString(string, scope, context);
+ }
+
var form = gettextPlurals(this.currentLanguage, n);
string = this.getStringForm(string, form, context) || prefixDebug(n === 1 ? string : stringPlural);
if (scope) {
@@ -206,13 +210,63 @@ angular.module('gettext').directive('translate', ["gettextCatalog", "$parse", "$
};
}]);
-angular.module('gettext').filter('translate', ["gettextCatalog", function (gettextCatalog) {
- function filter(input, context) {
- return gettextCatalog.getString(input, null, context);
- }
- filter.$stateful = true;
- return filter;
-}]);
+(function () {
+ var translate = function (gettextCatalog, $gettext) {
+ var message = gettextCatalog.getPlural($gettext.n, $gettext.msgid, $gettext.plural, null, $gettext.context);
+ if ($gettext.n || $gettext.n === 0) {
+ return message.replace(/(^|\s)\$count\b/g, '$1' + $gettext.n);
+ } else {
+ return message;
+ }
+ };
+
+ angular.module('gettext').filter('translate', ["gettextCatalog", function (gettextCatalog) {
+ function filter(msgid) {
+ var $gettext = msgid.$gettext || { msgid: msgid };
+
+ // translate is the only filter that returns a string primitive
+ return translate(gettextCatalog, $gettext);
+ }
+ filter.$stateful = true;
+ return filter;
+ }]);
+
+ angular.module('gettext').filter('translatePlural', ["gettextCatalog", function (gettextCatalog) {
+ function filter(msgid, n, plural) {
+ var $gettext = msgid.$gettext || { msgid: msgid };
+ $gettext.n = n;
+ $gettext.plural = plural;
+
+ /*jshint -W053 */
+ // might as well return the correct String, even if it is a wrapper type
+ var message = new String(translate(gettextCatalog, $gettext));
+ /*jshint +W053 */
+
+ message.$gettext = $gettext;
+ return message;
+ }
+ filter.$stateful = true;
+ return filter;
+ }]);
+
+ angular.module('gettext').filter('translateContext', ["gettextCatalog", function (gettextCatalog) {
+ function filter(msgid, context) {
+ var $gettext = msgid.$gettext || { msgid: msgid };
+ $gettext.context = context;
+
+ var message = translate(gettextCatalog, $gettext);
+
+ /*jshint -W053 */
+ message = new String(message);
+ /*jshint +W053 */
+
+ message.$gettext = $gettext;
+ return message;
+ }
+ filter.$stateful = true;
+ return filter;
+ }]);
+})();
// Do not edit this file, it is autogenerated using genplurals.py!
angular.module("gettext").factory("gettextPlurals", function () {
diff --git a/dist/angular-gettext.min.js b/dist/angular-gettext.min.js
index a7cb823..c1c70d8 100644
--- a/dist/angular-gettext.min.js
+++ b/dist/angular-gettext.min.js
@@ -1 +1 @@
-angular.module("gettext",[]),angular.module("gettext").constant("gettext",function(a){return a}),angular.module("gettext").factory("gettextCatalog",["gettextPlurals","$http","$cacheFactory","$interpolate","$rootScope",function(a,b,c,d,e){function f(){e.$broadcast("gettextLanguageChanged")}var g,h="$$noContext",i="test",j=angular.element(""+i+"").html()!==i,k=function(a){return g.debug&&g.currentLanguage!==g.baseLanguage?g.debugPrefix+a:a},l=function(a){return g.showTranslatedMarkers?g.translatedMarkerPrefix+a+g.translatedMarkerSuffix:a};return g={debug:!1,debugPrefix:"[MISSING]: ",showTranslatedMarkers:!1,translatedMarkerPrefix:"[",translatedMarkerSuffix:"]",strings:{},baseLanguage:"en",currentLanguage:"en",cache:c("strings"),setCurrentLanguage:function(a){this.currentLanguage=a,f()},setStrings:function(a,b){this.strings[a]||(this.strings[a]={});for(var c in b){var d=b[c];if(j&&(c=angular.element(""+c+"").html()),angular.isString(d)||angular.isArray(d)){var e={};e[h]=d,d=e}for(var g in d){var i=d[g];d[g]=angular.isArray(i)?i:[i]}this.strings[a][c]=d}f()},getStringForm:function(a,b,c){var d=this.strings[this.currentLanguage]||{},e=d[a]||{},f=e[c||h]||[];return f[b]},getString:function(a,b,c){return a=this.getStringForm(a,0,c)||k(a),a=b?d(a)(b):a,l(a)},getPlural:function(b,c,e,f,g){var h=a(this.currentLanguage,b);return c=this.getStringForm(c,h,g)||k(1===b?c:e),f&&(f.$count=b,c=d(c)(f)),l(c)},loadRemote:function(a){return b({method:"GET",url:a,cache:g.cache}).success(function(a){for(var b in a)g.setStrings(b,a[b])})}}}]),angular.module("gettext").directive("translate",["gettextCatalog","$parse","$animate","$compile","$window",function(a,b,c,d,e){function f(a,b,c){if(!a)throw new Error("You should add a "+b+" attribute whenever you add a "+c+" attribute.")}var g=function(){return String.prototype.trim?function(a){return"string"==typeof a?a.trim():a}:function(a){return"string"==typeof a?a.replace(/^\s*/,"").replace(/\s*$/,""):a}}(),h=parseInt((/msie (\d+)/.exec(angular.lowercase(e.navigator.userAgent))||[])[1],10);return{restrict:"AE",terminal:!0,compile:function(e,i){f(!i.translatePlural||i.translateN,"translate-n","translate-plural"),f(!i.translateN||i.translatePlural,"translate-plural","translate-n");var j=g(e.html()),k=i.translatePlural,l=i.translateContext;return 8>=h&&""===j.slice(-13)&&(j=j.slice(0,-13)),{post:function(e,f,g){function h(){var b;k?(e=m||(m=e.$new()),e.$count=i(e),b=a.getPlural(e.$count,j,k,null,l)):b=a.getString(j,null,l);var g=angular.element(""+b+"");d(g.contents())(e);var h=f.contents(),n=g.contents();c.enter(n,f),c.leave(h)}var i=b(g.translateN),m=null;g.translateN&&e.$watch(g.translateN,h),e.$on("gettextLanguageChanged",h),h()}}}}}]),angular.module("gettext").filter("translate",["gettextCatalog",function(a){function b(b,c){return a.getString(b,null,c)}return b.$stateful=!0,b}]),angular.module("gettext").factory("gettextPlurals",function(){return function(a,b){switch(a){case"ay":case"bo":case"cgg":case"dz":case"fa":case"id":case"ja":case"jbo":case"ka":case"kk":case"km":case"ko":case"ky":case"lo":case"ms":case"my":case"sah":case"su":case"th":case"tt":case"ug":case"vi":case"wo":case"zh":return 0;case"is":return b%10!=1||b%100==11?1:0;case"jv":return 0!=b?1:0;case"mk":return 1==b||b%10==1?0:1;case"ach":case"ak":case"am":case"arn":case"br":case"fil":case"fr":case"gun":case"ln":case"mfe":case"mg":case"mi":case"oc":case"pt_BR":case"tg":case"ti":case"tr":case"uz":case"wa":case"zh":return b>1?1:0;case"lv":return b%10==1&&b%100!=11?0:0!=b?1:2;case"lt":return b%10==1&&b%100!=11?0:b%10>=2&&(10>b%100||b%100>=20)?1:2;case"be":case"bs":case"hr":case"ru":case"sr":case"uk":return b%10==1&&b%100!=11?0:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?1:2;case"mnk":return 0==b?0:1==b?1:2;case"ro":return 1==b?0:0==b||b%100>0&&20>b%100?1:2;case"pl":return 1==b?0:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?1:2;case"cs":case"sk":return 1==b?0:b>=2&&4>=b?1:2;case"sl":return b%100==1?1:b%100==2?2:b%100==3||b%100==4?3:0;case"mt":return 1==b?0:0==b||b%100>1&&11>b%100?1:b%100>10&&20>b%100?2:3;case"gd":return 1==b||11==b?0:2==b||12==b?1:b>2&&20>b?2:3;case"cy":return 1==b?0:2==b?1:8!=b&&11!=b?2:3;case"kw":return 1==b?0:2==b?1:3==b?2:3;case"ga":return 1==b?0:2==b?1:7>b?2:11>b?3:4;case"ar":return 0==b?0:1==b?1:2==b?2:b%100>=3&&10>=b%100?3:b%100>=11?4:5;default:return 1!=b?1:0}}});
\ No newline at end of file
+angular.module("gettext",[]),angular.module("gettext").constant("gettext",function(a){return a}),angular.module("gettext").factory("gettextCatalog",["gettextPlurals","$http","$cacheFactory","$interpolate","$rootScope",function(a,b,c,d,e){function f(){e.$broadcast("gettextLanguageChanged")}var g,h="$$noContext",i="test",j=angular.element(""+i+"").html()!==i,k=function(a){return g.debug&&g.currentLanguage!==g.baseLanguage?g.debugPrefix+a:a},l=function(a){return g.showTranslatedMarkers?g.translatedMarkerPrefix+a+g.translatedMarkerSuffix:a};return g={debug:!1,debugPrefix:"[MISSING]: ",showTranslatedMarkers:!1,translatedMarkerPrefix:"[",translatedMarkerSuffix:"]",strings:{},baseLanguage:"en",currentLanguage:"en",cache:c("strings"),setCurrentLanguage:function(a){this.currentLanguage=a,f()},setStrings:function(a,b){this.strings[a]||(this.strings[a]={});for(var c in b){var d=b[c];if(j&&(c=angular.element(""+c+"").html()),angular.isString(d)||angular.isArray(d)){var e={};e[h]=d,d=e}for(var g in d){var i=d[g];d[g]=angular.isArray(i)?i:[i]}this.strings[a][c]=d}f()},getStringForm:function(a,b,c){var d=this.strings[this.currentLanguage]||{},e=d[a]||{},f=e[c||h]||[];return f[b]},getString:function(a,b,c){return a=this.getStringForm(a,0,c)||k(a),a=b?d(a)(b):a,l(a)},getPlural:function(b,c,e,f,g){if(!b&&0!==b)return this.getString(c,f,g);var h=a(this.currentLanguage,b);return c=this.getStringForm(c,h,g)||k(1===b?c:e),f&&(f.$count=b,c=d(c)(f)),l(c)},loadRemote:function(a){return b({method:"GET",url:a,cache:g.cache}).success(function(a){for(var b in a)g.setStrings(b,a[b])})}}}]),angular.module("gettext").directive("translate",["gettextCatalog","$parse","$animate","$compile","$window",function(a,b,c,d,e){function f(a,b,c){if(!a)throw new Error("You should add a "+b+" attribute whenever you add a "+c+" attribute.")}var g=function(){return String.prototype.trim?function(a){return"string"==typeof a?a.trim():a}:function(a){return"string"==typeof a?a.replace(/^\s*/,"").replace(/\s*$/,""):a}}(),h=parseInt((/msie (\d+)/.exec(angular.lowercase(e.navigator.userAgent))||[])[1],10);return{restrict:"AE",terminal:!0,compile:function(e,i){f(!i.translatePlural||i.translateN,"translate-n","translate-plural"),f(!i.translateN||i.translatePlural,"translate-plural","translate-n");var j=g(e.html()),k=i.translatePlural,l=i.translateContext;return 8>=h&&""===j.slice(-13)&&(j=j.slice(0,-13)),{post:function(e,f,g){function h(){var b;k?(e=m||(m=e.$new()),e.$count=i(e),b=a.getPlural(e.$count,j,k,null,l)):b=a.getString(j,null,l);var g=angular.element(""+b+"");d(g.contents())(e);var h=f.contents(),n=g.contents();c.enter(n,f),c.leave(h)}var i=b(g.translateN),m=null;g.translateN&&e.$watch(g.translateN,h),e.$on("gettextLanguageChanged",h),h()}}}}}]),function(){var a=function(a,b){var c=a.getPlural(b.n,b.msgid,b.plural,null,b.context);return b.n||0===b.n?c.replace(/(^|\s)\$count\b/g,"$1"+b.n):c};angular.module("gettext").filter("translate",["gettextCatalog",function(b){function c(c){var d=c.$gettext||{msgid:c};return a(b,d)}return c.$stateful=!0,c}]),angular.module("gettext").filter("translatePlural",["gettextCatalog",function(b){function c(c,d,e){var f=c.$gettext||{msgid:c};f.n=d,f.plural=e;var g=new String(a(b,f));return g.$gettext=f,g}return c.$stateful=!0,c}]),angular.module("gettext").filter("translateContext",["gettextCatalog",function(b){function c(c,d){var e=c.$gettext||{msgid:c};e.context=d;var f=a(b,e);return f=new String(f),f.$gettext=e,f}return c.$stateful=!0,c}])}(),angular.module("gettext").factory("gettextPlurals",function(){return function(a,b){switch(a){case"ay":case"bo":case"cgg":case"dz":case"fa":case"id":case"ja":case"jbo":case"ka":case"kk":case"km":case"ko":case"ky":case"lo":case"ms":case"my":case"sah":case"su":case"th":case"tt":case"ug":case"vi":case"wo":case"zh":return 0;case"is":return b%10!=1||b%100==11?1:0;case"jv":return 0!=b?1:0;case"mk":return 1==b||b%10==1?0:1;case"ach":case"ak":case"am":case"arn":case"br":case"fil":case"fr":case"gun":case"ln":case"mfe":case"mg":case"mi":case"oc":case"pt_BR":case"tg":case"ti":case"tr":case"uz":case"wa":case"zh":return b>1?1:0;case"lv":return b%10==1&&b%100!=11?0:0!=b?1:2;case"lt":return b%10==1&&b%100!=11?0:b%10>=2&&(10>b%100||b%100>=20)?1:2;case"be":case"bs":case"hr":case"ru":case"sr":case"uk":return b%10==1&&b%100!=11?0:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?1:2;case"mnk":return 0==b?0:1==b?1:2;case"ro":return 1==b?0:0==b||b%100>0&&20>b%100?1:2;case"pl":return 1==b?0:b%10>=2&&4>=b%10&&(10>b%100||b%100>=20)?1:2;case"cs":case"sk":return 1==b?0:b>=2&&4>=b?1:2;case"sl":return b%100==1?1:b%100==2?2:b%100==3||b%100==4?3:0;case"mt":return 1==b?0:0==b||b%100>1&&11>b%100?1:b%100>10&&20>b%100?2:3;case"gd":return 1==b||11==b?0:2==b||12==b?1:b>2&&20>b?2:3;case"cy":return 1==b?0:2==b?1:8!=b&&11!=b?2:3;case"kw":return 1==b?0:2==b?1:3==b?2:3;case"ga":return 1==b?0:2==b?1:7>b?2:11>b?3:4;case"ar":return 0==b?0:1==b?1:2==b?2:b%100>=3&&10>=b%100?3:b%100>=11?4:5;default:return 1!=b?1:0}}});
\ No newline at end of file
diff --git a/src/catalog.js b/src/catalog.js
index 3782b36..3dceb8d 100644
--- a/src/catalog.js
+++ b/src/catalog.js
@@ -89,6 +89,10 @@ angular.module('gettext').factory('gettextCatalog', function (gettextPlurals, $h
},
getPlural: function (n, string, stringPlural, scope, context) {
+ if (!n && n !== 0) {
+ return this.getString(string, scope, context);
+ }
+
var form = gettextPlurals(this.currentLanguage, n);
string = this.getStringForm(string, form, context) || prefixDebug(n === 1 ? string : stringPlural);
if (scope) {
diff --git a/src/filter.js b/src/filter.js
index db065ee..2a9a24d 100644
--- a/src/filter.js
+++ b/src/filter.js
@@ -1,7 +1,58 @@
-angular.module('gettext').filter('translate', function (gettextCatalog) {
- function filter(input, context) {
- return gettextCatalog.getString(input, null, context);
- }
- filter.$stateful = true;
- return filter;
-});
+(function () {
+ var translate = function (gettextCatalog, $gettext) {
+ var message = gettextCatalog.getPlural($gettext.n, $gettext.msgid, $gettext.plural, null, $gettext.context);
+ if ($gettext.n || $gettext.n === 0) {
+ // replace $count with n, preserving leading whitespace
+ return message.replace(/(^|\s)\$count\b/g, '$1' + $gettext.n);
+ } else {
+ return message;
+ }
+ };
+
+ angular.module('gettext').filter('translate', function (gettextCatalog) {
+ function filter(msgid) {
+ var $gettext = msgid.$gettext || { msgid: msgid };
+
+ // translate is the only filter that returns a string primitive
+ return translate(gettextCatalog, $gettext);
+ }
+ filter.$stateful = true;
+ return filter;
+ });
+
+ angular.module('gettext').filter('translatePlural', function (gettextCatalog) {
+ function filter(msgid, n, plural) {
+ var $gettext = msgid.$gettext || { msgid: msgid };
+ $gettext.n = n;
+ $gettext.plural = plural;
+
+ /*jshint -W053 */
+ // might as well return the correct String, even if it is a wrapper type
+ var message = new String(translate(gettextCatalog, $gettext));
+ /*jshint +W053 */
+
+ message.$gettext = $gettext;
+ return message;
+ }
+ filter.$stateful = true;
+ return filter;
+ });
+
+ angular.module('gettext').filter('translateContext', function (gettextCatalog) {
+ function filter(msgid, context) {
+ var $gettext = msgid.$gettext || { msgid: msgid };
+ $gettext.context = context;
+
+ var message = translate(gettextCatalog, $gettext);
+
+ /*jshint -W053 */
+ message = new String(message);
+ /*jshint +W053 */
+
+ message.$gettext = $gettext;
+ return message;
+ }
+ filter.$stateful = true;
+ return filter;
+ });
+})();
diff --git a/test/unit/filter.js b/test/unit/filter.js
index 0bd36e3..e806673 100644
--- a/test/unit/filter.js
+++ b/test/unit/filter.js
@@ -12,7 +12,7 @@ describe("Filter", function () {
catalog.setStrings("nl", {
Hello: "Hallo",
"Hello {{name}}!": "Hallo {{name}}!",
- "One boat": ["Een boot", "{{count}} boten"],
+ "One boat": ["Een boot", "$count boten"],
Archive: { verb: "Archiveren", noun: "Archief", $$noContext: "Archief (no context)" }
});
}));
@@ -30,23 +30,84 @@ describe("Filter", function () {
assert.equal(el.text(), "Hallo");
});
- it("Should translate known strings according to translate context", function () {
- catalog.setCurrentLanguage("nl");
- var el = $compile("{{\"Archive\"|translate:'verb'}}")($rootScope);
- $rootScope.$digest();
- assert.equal(el.text(), "Archiveren");
- el = $compile("{{\"Archive\"|translate:'noun'}}")($rootScope);
- $rootScope.$digest();
- assert.equal(el.text(), "Archief");
- el = $compile("{{\"Archive\"|translate}}")($rootScope);
- $rootScope.$digest();
- assert.equal(el.text(), "Archief (no context)");
- });
-
it("Can use filter in attribute values", function () {
catalog.setCurrentLanguage("nl");
var el = $compile("")($rootScope);
$rootScope.$digest();
assert.equal(el.attr("placeholder"), "Hallo");
});
+
+ describe("translatePlural", function () {
+
+ // not sure why you'd want to do this, but it's a good test case
+ it("Should work if n is a number", function () {
+ catalog.setCurrentLanguage("nl");
+ var el = $compile("{{'One boat' | translatePlural:2:'$count boten' | translate}}")($rootScope);
+ $rootScope.$digest();
+ assert.equal(el.text(), "2 boten");
+ });
+
+ it("Should work if n is a reference", function () {
+ catalog.setCurrentLanguage("nl");
+ var scope = $rootScope.$new();
+ scope.count = 2;
+ var el = $compile("{{'One boat' | translatePlural:count:'$count boten' | translate}}")(scope);
+ $rootScope.$digest();
+ assert.equal(el.text(), "2 boten");
+
+ scope.count = 1;
+ el = $compile("{{'One boat' | translatePlural:count:'$count boten' | translate}}")(scope);
+ $rootScope.$digest();
+ assert.equal(el.text(), "Een boot");
+ });
+
+ it("Should work if it precedes translateContext", function () {
+ catalog.setCurrentLanguage("nl");
+ catalog.setStrings("nl", {
+ "One boat": { c1: ["Een boot1", "$count boten1"], c2: ["Een boot", "$count boten"] }
+ });
+
+ var scope = $rootScope.$new();
+ scope.count = 2;
+ var el = $compile("{{'One boat' | translatePlural:count:'$count boten' | translateContext:'c2' | translate}}")(scope);
+ $rootScope.$digest();
+ assert.equal(el.text(), "2 boten");
+ });
+ });
+
+ describe("translateContext", function () {
+ it("Should translate known strings according to translateContext", function () {
+ catalog.setCurrentLanguage("nl");
+ var el = $compile("{{'Archive' | translateContext:'verb' | translate}}")($rootScope);
+ $rootScope.$digest();
+ assert.equal(el.text(), "Archiveren");
+ el = $compile("{{'Archive' | translateContext:'noun' | translate}}")($rootScope);
+ $rootScope.$digest();
+ assert.equal(el.text(), "Archief");
+ });
+
+ it("Should work with no args", function () {
+ // translateContext with no args is the same as translate
+ catalog.setCurrentLanguage("nl");
+ var el = $compile("{{'Archive' | translateContext | translate}}")($rootScope);
+ $rootScope.$digest();
+ assert.equal(el.text(), "Archief (no context)");
+ el = $compile("{{'Archive' | translate }}")($rootScope);
+ $rootScope.$digest();
+ assert.equal(el.text(), "Archief (no context)");
+ });
+
+ it("Should work if it precedes translatePlural", function () {
+ catalog.setCurrentLanguage("nl");
+ catalog.setStrings("nl", {
+ "One boat": { c1: ["Een boot1", "$count boten1"], c2: ["Een boot", "$count boten"] }
+ });
+
+ var scope = $rootScope.$new();
+ scope.count = 2;
+ var el = $compile("{{'One boat' | translateContext:'c2' | translatePlural:count:'$count boten' | translate}}")(scope);
+ $rootScope.$digest();
+ assert.equal(el.text(), "2 boten");
+ });
+ });
});