Skip to content

Commit 5df6aee

Browse files
committed
first commit
1 parent 7079dbe commit 5df6aee

File tree

8 files changed

+366
-2
lines changed

8 files changed

+366
-2
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bower_components
2+
node_modules

README.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,50 @@
1-
# gettext-translator
2-
Javascript gettext translator
1+
# Gettext translator
2+
Javascript gettext translator. Use [gettext/gettext](https://github.com/oscarotero/Gettext) PHP library to generate and modify the messages.
3+
4+
## Usage
5+
6+
Use the Json generator of the [gettext/gettext](https://github.com/oscarotero/Gettext) library:
7+
8+
```php
9+
use Gettext\Translations;
10+
11+
//Scan the po file with the translations
12+
$translations = Translations::fromPoFile('locales/gl.po');
13+
14+
//Export to a json file
15+
$translations->toJsonFile('locales/gl.json');
16+
```
17+
18+
Load the json file in your javascript (for example, using webpack) and use it
19+
20+
```js
21+
var Translator = require('gettext-translator');
22+
var translations = require('locales/gl.json');
23+
24+
var i18n = new Translator(translations);
25+
26+
27+
console.log(i18n.gettext('hello world')); //ola mundo
28+
```
29+
30+
## API
31+
32+
method | short | description
33+
------ | ----- | -----------
34+
gettext | __ | Returns a translation
35+
ngettext | n__ | Returns a translation with singular/plural variations
36+
dngettext | dn__ | Returns a translation with domain and singular/plural variations
37+
npgettext | np__ | Returns a translation with context and singular/plural variations
38+
pgettext | p__ | Returns a translation with a specific context
39+
dgettext | d__ | Returns a translation with a specific domain
40+
dpgettext | dp__ | Returns a translation with a specific domain and context
41+
dnpgettext | dnp__ | Returns a translation with a specific domain, context and singular/plural variations
42+
43+
## Sprintf
44+
45+
This library includes [sprintf](https://github.com/alexei/sprintf.js) dependency implemented in the short methods:
46+
47+
```js
48+
i18n.__('Hello %s', 'world'); //Hello world
49+
i18n.n__('One comment', '%s comments', 12, 12); //12 comments
50+
```

bower.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "gettext-translator",
3+
"authors": [
4+
"oscarotero <oom@oscarotero.com>"
5+
],
6+
"description": "Javascript gettext translator",
7+
"main": "src/translator.js",
8+
"keywords": [
9+
"gettext",
10+
"i18n",
11+
"translator"
12+
],
13+
"license": "MIT",
14+
"homepage": "",
15+
"dependencies": {
16+
"sprintf": "^1.0.3"
17+
}
18+
}

package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "gettext-translator",
3+
"version": "1.0.0",
4+
"description": "Javascript gettext translator",
5+
"main": "src/translator.js",
6+
"directories": {
7+
"test": "tests"
8+
},
9+
"scripts": {
10+
"test": "mocha tests/test.js"
11+
},
12+
"keywords": [
13+
"gettext",
14+
"i18n",
15+
"translator"
16+
],
17+
"author": "oscarotero <oom@oscarotero.com>",
18+
"license": "MIT",
19+
"dependencies": {
20+
"sprintf-js": "^1.0.3"
21+
},
22+
"devDependencies": {
23+
"mocha": "^2.5.3"
24+
}
25+
}

src/translator.js

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
(function (root, factory) {
2+
//amd
3+
if (typeof define === "function" && define.amd) {
4+
define(['sprintf'], function (sprintf) {
5+
return factory(sprintf.vsprintf);
6+
});
7+
8+
//commonjs
9+
} else if (typeof module === "object" && module.exports) {
10+
module.exports = factory(require('sprintf-js').vsprintf);
11+
12+
//global
13+
} else {
14+
root.gettext = factory(window.vsprintf);
15+
}
16+
}(this, function (vsprintf) {
17+
18+
function Translator (translations) {
19+
this.dictionary = {};
20+
this.plurals = {};
21+
this.domain = null;
22+
23+
if (translations) {
24+
this.loadTranslations(translations);
25+
}
26+
}
27+
28+
Translator.prototype = {
29+
loadTranslations: function (translations) {
30+
var domain = translations.domain || '';
31+
32+
if (this.domain === null) {
33+
this.domain = domain;
34+
}
35+
36+
if (this.dictionary[domain]) {
37+
mergeTranslations(this.dictionary[domain], translations.messages);
38+
return this;
39+
}
40+
41+
var plural = translations['plural-forms'].split(';', 2);
42+
43+
this.plurals[domain] = {
44+
count: parseInt(plural[0].replace('nplurals=', '')),
45+
code: plural[1].replace('plural=', 'return ') + ';'
46+
};
47+
48+
this.dictionary[domain] = translations.messages;
49+
50+
return this;
51+
},
52+
53+
defaultDomain: function (domain) {
54+
this.domain = domain;
55+
56+
return this;
57+
},
58+
59+
gettext: function (original) {
60+
return this.dpgettext(this.domain, null, original);
61+
},
62+
63+
ngettext: function (original, plural, value) {
64+
return this.dnpgettext(this.domain, null, original, plural, value);
65+
},
66+
67+
dngettext: function (domain, original, plural, value) {
68+
return this.dnpgettext(domain, null, original, plural, value);
69+
},
70+
71+
npgettext: function (context, original, plural, value) {
72+
return this.dnpgettext(this.domain, context, original, plural, value);
73+
},
74+
75+
pgettext: function (context, original) {
76+
return this.dpgettext(this.domain, context, original);
77+
},
78+
79+
dgettext: function (domain, original) {
80+
return this.dpgettext(domain, null, original);
81+
},
82+
83+
dpgettext: function (domain, context, original) {
84+
var translation = getTranslation(this.dictionary, domain, context, original);
85+
86+
if (translation !== false && translation[0] !== '') {
87+
return translation[0];
88+
}
89+
90+
return original;
91+
},
92+
93+
dnpgettext: function (domain, context, original, plural, value) {
94+
var index = getPluralIndex(this.plurals, domain, value);
95+
var translation = getTranslation(this.dictionary, domain, context, original);
96+
97+
if (translation[index] && translation[index] !== '') {
98+
return translation[index];
99+
}
100+
101+
return (index === 0) ? original : plural;
102+
},
103+
104+
__: function (original) {
105+
return format(
106+
this.gettext(original),
107+
Array.prototype.slice.call(arguments, 1)
108+
);
109+
},
110+
111+
n__: function (original, plural, value) {
112+
return format(
113+
this.ngettext(original, plural, value),
114+
Array.prototype.slice.call(arguments, 3)
115+
);
116+
},
117+
118+
p__: function (context, original) {
119+
return format(
120+
this.pgettext(context, original),
121+
Array.prototype.slice.call(arguments, 2)
122+
);
123+
},
124+
125+
d__: function (domain, original) {
126+
return format(
127+
this.dgettext(domain, original),
128+
Array.prototype.slice.call(arguments, 2)
129+
);
130+
},
131+
132+
dp__: function (domain, context, original) {
133+
return format(
134+
this.dgettext(domain, context, original),
135+
Array.prototype.slice.call(arguments, 3)
136+
);
137+
},
138+
139+
np__: function (context, original, plural, value) {
140+
return format(
141+
this.npgettext(context, original, plural, value),
142+
Array.prototype.slice.call(arguments, 4)
143+
);
144+
},
145+
146+
dnp__: function (domain, context, original, plural, value) {
147+
return format(
148+
this.dnpgettext(domain, context, original, plural, value),
149+
Array.prototype.slice.call(arguments, 5)
150+
);
151+
}
152+
};
153+
154+
function getTranslation(dictionary, domain, context, original) {
155+
context = context || '';
156+
157+
if (!dictionary[domain] || !dictionary[domain][context] || !dictionary[domain][context][original]) {
158+
return false;
159+
}
160+
161+
return dictionary[domain][context][original];
162+
}
163+
164+
function getPluralIndex(plurals, domain, value) {
165+
if (!plurals[domain]) {
166+
return value == 1 ? 0 : 1;
167+
}
168+
169+
if (!plurals[domain].fn) {
170+
plurals[domain].fn = new Function('n', plurals[domain].code);
171+
}
172+
173+
return plurals[domain].fn.call(this, value);
174+
}
175+
176+
function mergeTranslations(translations, newTranslations) {
177+
for (var context in newTranslations) {
178+
if (!translations[context]) {
179+
translations[context] = newTranslations[context];
180+
continue;
181+
}
182+
183+
for (var original in newTranslations[context]) {
184+
translations[context][original] = newTranslations[context][original];
185+
}
186+
}
187+
}
188+
189+
function format (text, args) {
190+
if (!args.length) {
191+
return text;
192+
}
193+
194+
if (args[0] instanceof Array) {
195+
return vsprintf(text, args[0]);
196+
}
197+
198+
return vsprintf(text, args);
199+
}
200+
201+
return Translator;
202+
}));

tests/test.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
var assert = require('assert');
2+
var Translator = require(__dirname + '/../src/translator');
3+
4+
var i18n = new Translator(require(__dirname + '/translations.json'));
5+
i18n.loadTranslations(require(__dirname + '/translations2.json'));
6+
7+
describe('Basic functions', function() {
8+
it('gettext', function () {
9+
assert.equal('vaca', i18n.gettext('cow'));
10+
});
11+
12+
it('ngettext', function () {
13+
assert.equal('un arquivo', i18n.ngettext('one file', '%s files', 1));
14+
assert.equal('%s arquivos', i18n.ngettext('one file', '%s files', 0));
15+
assert.equal('%s arquivos', i18n.ngettext('one file', '%s files', 2));
16+
});
17+
});
18+
19+
describe('vsprintf functions', function() {
20+
it('__', function () {
21+
assert.equal('vaca', i18n.__('cow'));
22+
});
23+
24+
it('n__', function () {
25+
assert.equal('un arquivo', i18n.n__('one file', '%s files', 1, 1));
26+
assert.equal('0 arquivos', i18n.n__('one file', '%s files', 0, 0));
27+
assert.equal('2 arquivos', i18n.n__('one file', '%s files', 2, 2));
28+
});
29+
});

tests/translations.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"domain": null,
3+
"plural-forms": "nplurals=3; plural=n == 1 ? 0 : 1;",
4+
"messages": {
5+
"": {
6+
"": [
7+
"Content-Transfer-Encoding: 8bit\nContent-Type: text\/plain; charset=UTF-8\nLanguage: \nLanguage-Team: \nLast-Translator: \nMIME-Version: 1.0\nPlural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\nProject-Id-Version: \nReport-Msgid-Bugs-To: \n"
8+
],
9+
"one file": [
10+
"un arquivo",
11+
"%s arquivos"
12+
],
13+
"cow": [
14+
"vaca"
15+
]
16+
}
17+
}
18+
}

tests/translations2.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"domain": null,
3+
"plural-forms": "nplurals=3; plural=n == 1 ? 0 : 1;",
4+
"messages": {
5+
"": {
6+
"": [
7+
"Content-Transfer-Encoding: 8bit\nContent-Type: text\/plain; charset=UTF-8\nLanguage: \nLanguage-Team: \nLast-Translator: \nMIME-Version: 1.0\nPlural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\nProject-Id-Version: \nReport-Msgid-Bugs-To: \n"
8+
],
9+
"one file": [
10+
"un arquivo",
11+
"%s arquivos"
12+
],
13+
"one comment": [
14+
"un comentario",
15+
"%s comentarios"
16+
],
17+
"cow": [
18+
"vaca"
19+
]
20+
}
21+
}
22+
}

0 commit comments

Comments
 (0)