Skip to content

Commit 41ca424

Browse files
committed
Clean up and test plural forms (closes #3)
1 parent df05031 commit 41ca424

File tree

7 files changed

+164
-39
lines changed

7 files changed

+164
-39
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# gettext-pythonic
1+
# gettext-pythonic v1.0.0
22

33
[![Build Status](https://travis-ci.org/vphantom/gettext-pythonic.svg?branch=master)](https://travis-ci.org/vphantom/gettext-pythonic) [![Coverage Status](https://coveralls.io/repos/github/vphantom/gettext-pythonic/badge.svg?branch=master)](https://coveralls.io/github/vphantom/gettext-pythonic?branch=master)
44

@@ -39,7 +39,7 @@ In Node.JS, you can choose whichever name you'd like, although keep in mind that
3939

4040
```js
4141
var gettext = require('gettext-pythonic');
42-
var __ = gettext;
42+
var __ = gettext.gettext;
4343
var n_ = gettext.ngettext;
4444
...
4545
gettext.load(require('locales/fr.json'));

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "gettext-pythonic",
3-
"version": "0.5.2",
3+
"version": "1.0.0",
44
"description": "Tiny po2json-compatible gettext with variable substitution",
55
"main": "gettext-pythonic.js",
66
"authors": [

gettext-pythonic.js

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
/*! gettext-pythonic
2-
* Copyright 2016 Stéphane Lavergne <http://www.imars.com/>
1+
/*! gettext-pythonic v1.0.0
2+
* <https://github.com/vphantom/gettext-pythonic>
3+
* Copyright 2016 Stéphane Lavergne
34
* Free software under <http://www.gnu.org/licenses/lgpl-3.0.txt> */
45

56
/**
@@ -15,32 +16,33 @@
1516

1617
"use strict";
1718

18-
(function(window) {
19+
(function(context) {
1920
var gt;
2021

2122
gt = function(target, args) {
23+
// Browser-side needs this redundancy with gt.gettext()
2224
return gt.ngettext(target, null, null, args);
2325
};
2426

2527
gt._formatRE = /%\{([^}]+)\}/g;
2628
gt._pluralRE = /nplurals=(\d+);\s+plural=([^;]+);/;
27-
gt._lang = null;
28-
gt._nplurals = 2; // Not currently used
29-
gt._plural = function(n) {
30-
return +(n != 1); // eslint-disable-line eqeqeq
31-
};
3229

3330
gt.ngettext = function(singular, plural, count, args) {
31+
// Our target is singular or its translation (possibly an array)
3432
var res = (
3533
gt._lang !== null && singular in gt._lang
3634
? (gt._lang[singular] || singular)
3735
: singular
3836
);
3937

38+
// Plural: choose right form from array or plural argument
4039
if (typeof res === "object") {
4140
res = res[gt._plural(count) + 1] || res[1] || singular;
41+
} else if (typeof plural === "string" && gt._plural(count)) {
42+
res = plural;
4243
}
4344

45+
// Pythonic substitutions
4446
if (args !== null && typeof args === "object") {
4547
res = res.replace(gt._formatRE, function(z, key) {
4648
return (key in args ? args[key] : "");
@@ -50,25 +52,36 @@
5052
return res;
5153
};
5254

55+
gt.gettext = function(target, args) {
56+
return gt.ngettext(target, null, null, args);
57+
};
58+
5359
gt.load = function(newLang) {
5460
var src = [];
5561

56-
gt._lang = newLang;
57-
if ("" in newLang && "plural-forms" in newLang[""]) {
58-
src = newLang[""]["plural-forms"].match(gt._pluralRE);
62+
gt._lang = (typeof newLang === "object" ? newLang : {});
63+
64+
if ("" in gt._lang && "plural-forms" in gt._lang[""]) {
65+
src = gt._lang[""]["plural-forms"].match(gt._pluralRE);
5966
gt._nplurals = src[1];
60-
gt._plural = new Function("n", "return +(" + src[2] + ")");
67+
gt._plural = new Function(
68+
"n",
69+
"n = Math.abs(n); return +(" + src[2] + ")"
70+
);
71+
} else {
72+
gt._nplurals = 2; // Not currently used
73+
gt._plural = function(n) {
74+
return +(Math.abs(n) != 1); // eslint-disable-line eqeqeq
75+
};
6176
}
6277
};
78+
gt.load(); // Initialize implicitly
6379

64-
if (
65-
typeof module === "object"
66-
&& module
67-
&& typeof module.exports === "object"
68-
) {
69-
module.exports = gt;
80+
if (typeof context.exports === "object") {
81+
context.exports = gt;
7082
} else {
71-
window.gettext = (window.__ = gt);
72-
window.ngettext = (window.n_ = gt.ngettext);
83+
// Browser-side, gettext is global so it needs to be gt, not gt.gettext
84+
context.gettext = (context.__ = gt);
85+
context.ngettext = (context.n_ = gt.ngettext);
7386
}
74-
})(this);
87+
})(module || this);

gettext-pythonic.min.js

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gettext-pythonic.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "gettext-pythonic",
3-
"version": "0.5.2",
3+
"version": "1.0.0",
44
"description": "Tiny po2json-compatible gettext with variable substitution",
55
"main": "gettext-pythonic.js",
66
"scripts": {
@@ -18,9 +18,9 @@
1818
"homepage": "https://github.com/vphantom/gettext-pythonic#readme",
1919
"devDependencies": {
2020
"coveralls": "^2.11.9",
21-
"eslint": "^2.6.0",
21+
"eslint": "^2.7.0",
2222
"faucet": "0.0.1",
23-
"istanbul": "^0.4.2",
23+
"istanbul": "^0.4.3",
2424
"tape": "^4.5.1",
2525
"uglifyjs": "^2.4.10"
2626
}

test.js

Lines changed: 119 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
var test = require("tape");
22

3-
var __ = require("./gettext-pythonic.js");
3+
var gettext = require("./gettext-pythonic.js");
4+
var __ = gettext.gettext;
5+
var n_ = gettext.ngettext;
46

57
test("Missing key returns itself", function(t) {
68
t.equal(__("foobar"), "foobar");
@@ -95,10 +97,22 @@ test("Named pythonic variable substitution", function(t) {
9597
"graceful with unknown name"
9698
);
9799

100+
t.equal(
101+
__("...%{foo...", args),
102+
"...%{foo...",
103+
"graceful with missing closing brace"
104+
);
105+
106+
t.equal(
107+
__("...%{weird key+name_also-very.long[indeed]=etc}...", args),
108+
"......",
109+
"graceful with special characters in valid brace"
110+
);
111+
98112
t.equal(
99113
__("Hi, %{one} this is %{three} or %{two}...", args),
100114
"Hi, first this is third or second...",
101-
""
115+
"performs substitution"
102116
);
103117

104118
t.end();
@@ -114,16 +128,30 @@ test("Load a language", function(t) {
114128
"This is convenient" : "Ceci est pratique"
115129
};
116130

117-
__.load(lang);
131+
gettext.load("invalid language data");
118132

119-
t.deepEqual(__._lang, lang, "loaded to _lang property");
133+
t.equal(
134+
__("foobar", {a: 1}),
135+
"foobar",
136+
"unaffected by loading a string instead of a PO object"
137+
);
138+
139+
gettext.load(lang);
140+
141+
t.deepEqual(gettext._lang, lang, "loaded to _lang property");
120142

121143
t.equal(
122144
__("This is a test"),
123145
"Ceci est un test",
124146
"look up key in language"
125147
);
126148

149+
t.equal(
150+
gettext("This is a test"),
151+
"Ceci est un test",
152+
"look up key in language using root object function shortcut"
153+
);
154+
127155
t.equal(
128156
__("Non-existent string"),
129157
"Non-existent string",
@@ -175,26 +203,109 @@ test("Plural forms", function(t) {
175203
]
176204
};
177205

178-
__.load(fr);
206+
t.equal(
207+
n_("%{count} result", "%{count} results", 0, {count: 0}),
208+
"0 results",
209+
"Default with 0"
210+
);
211+
212+
t.equal(
213+
n_("%{count} result", "%{count} results", 1, {count: 1}),
214+
"1 result",
215+
"Default with 1"
216+
);
217+
218+
t.equal(
219+
n_("%{count} result", "%{count} results", 23, {count: 23}),
220+
"23 results",
221+
"Default with 23"
222+
);
223+
224+
t.equal(
225+
n_("%{count} result", "%{count} results", -1, {count: -1}),
226+
"-1 result",
227+
"Default with -1"
228+
);
229+
230+
t.equal(
231+
n_("%{count} result", "%{count} results", -23, {count: -23}),
232+
"-23 results",
233+
"Default with -23"
234+
);
235+
236+
gettext.load(fr);
179237

180238
t.equal(
181-
__.ngettext("%{count} result", "%{count} results", 0, {count: 0}),
239+
n_("%{count} result", "%{count} results", 0, {count: 0}),
182240
"0 résultat",
183241
"French with 0"
184242
);
185243

186244
t.equal(
187-
__.ngettext("%{count} result", "%{count} results", 1, {count: 1}),
245+
n_("%{count} result", "%{count} results", 1, {count: 1}),
188246
"1 résultat",
189247
"French with 1"
190248
);
191249

192250
t.equal(
193-
__.ngettext("%{count} result", "%{count} results", 23, {count: 23}),
251+
n_("%{count} result", "%{count} results", 23, {count: 23}),
194252
"23 résultats",
195253
"French with 23"
196254
);
197255

256+
t.equal(
257+
n_("%{count} result", "%{count} results", -1, {count: -1}),
258+
"-1 résultat",
259+
"French with -1"
260+
);
261+
262+
t.equal(
263+
n_("%{count} result", "%{count} results", -23, {count: -23}),
264+
"-23 résultats",
265+
"French with -23"
266+
);
267+
268+
gettext.load(23);
269+
270+
t.equal(
271+
n_("%{count} result", "%{count} results", 0, {count: 0}),
272+
"0 results",
273+
"Default with 0 after French then invalid language"
274+
);
275+
276+
t.equal(
277+
n_("%{count} result", "%{count} results", 1, {count: 1}),
278+
"1 result",
279+
"Default with 1 after French then invalid language"
280+
);
281+
282+
t.equal(
283+
n_("%{count} result", "%{count} results", 23, {count: 23}),
284+
"23 results",
285+
"Default with 23 after French then invalid language"
286+
);
287+
288+
gettext.load(fr);
289+
gettext.load({});
290+
291+
t.equal(
292+
n_("%{count} result", "%{count} results", 0, {count: 0}),
293+
"0 results",
294+
"Default with 0 after French then empty object"
295+
);
296+
297+
t.equal(
298+
n_("%{count} result", "%{count} results", 1, {count: 1}),
299+
"1 result",
300+
"Default with 1 after French then empty object"
301+
);
302+
303+
t.equal(
304+
n_("%{count} result", "%{count} results", 23, {count: 23}),
305+
"23 results",
306+
"Default with 23 after French then empty object"
307+
);
308+
198309
t.end();
199310
});
200311

0 commit comments

Comments
 (0)