i18n-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +249 -0
- data/Rakefile +42 -0
- data/init.rb +1 -0
- data/install.rb +7 -0
- data/lib/i18n-js.rb +138 -0
- data/lib/i18n-js/railtie.rb +9 -0
- data/lib/i18n-js/version.rb +10 -0
- data/lib/tasks/i18n-js_tasks.rake +16 -0
- data/source/i18n-js.yml +4 -0
- data/source/i18n.js +341 -0
- data/test/i18n-test.html +50 -0
- data/test/i18n-test.js +668 -0
- data/test/i18n_js_test.rb +168 -0
- data/test/jsunittest/jsunittest.js +1017 -0
- data/test/jsunittest/unittest.css +54 -0
- data/test/resources/custom_path.yml +4 -0
- data/test/resources/default.yml +4 -0
- data/test/resources/locales.yml +76 -0
- data/test/resources/multiple_files.yml +6 -0
- data/test/resources/no_scope.yml +3 -0
- data/test/resources/simple_scope.yml +4 -0
- data/test/test_helper.rb +25 -0
- metadata +84 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
namespace :i18n do
|
2
|
+
desc "Copy i18n.js and configuration file"
|
3
|
+
task :setup => :environment do
|
4
|
+
SimplesIdeias::I18n.setup!
|
5
|
+
end
|
6
|
+
|
7
|
+
desc "Export the messages files"
|
8
|
+
task :export => :environment do
|
9
|
+
SimplesIdeias::I18n.export!
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Update the JavaScript library"
|
13
|
+
task :update => :environment do
|
14
|
+
SimplesIdeias::I18n.update!
|
15
|
+
end
|
16
|
+
end
|
data/source/i18n-js.yml
ADDED
data/source/i18n.js
ADDED
@@ -0,0 +1,341 @@
|
|
1
|
+
// Instantiate the object
|
2
|
+
var I18n = I18n || {};
|
3
|
+
|
4
|
+
// Set default locale to english
|
5
|
+
I18n.defaultLocale = "en";
|
6
|
+
|
7
|
+
// Set current locale to null
|
8
|
+
I18n.locale = null;
|
9
|
+
|
10
|
+
// Set the placeholder format. Accepts `{{placeholder}}` and `%{placeholder}`.
|
11
|
+
I18n.PLACEHOLDER = /(?:\{\{|%\{)(.*?)(?:\}\}?)/gm;
|
12
|
+
|
13
|
+
I18n.lookup = function(scope, options) {
|
14
|
+
var translations = this.prepareOptions(I18n.translations);
|
15
|
+
var messages = translations[I18n.currentLocale()];
|
16
|
+
options = this.prepareOptions(options);
|
17
|
+
|
18
|
+
if (!messages) {
|
19
|
+
return;
|
20
|
+
}
|
21
|
+
|
22
|
+
if (typeof(scope) == "object") {
|
23
|
+
scope = scope.join(".");
|
24
|
+
}
|
25
|
+
|
26
|
+
if (options.scope) {
|
27
|
+
scope = options.scope.toString() + "." + scope;
|
28
|
+
}
|
29
|
+
|
30
|
+
scope = scope.split(".");
|
31
|
+
|
32
|
+
while (scope.length > 0) {
|
33
|
+
var currentScope = scope.shift();
|
34
|
+
messages = messages[currentScope];
|
35
|
+
|
36
|
+
if (!messages) {
|
37
|
+
break;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
if (!messages && options.defaultValue != null && options.defaultValue != undefined) {
|
42
|
+
messages = options.defaultValue;
|
43
|
+
}
|
44
|
+
|
45
|
+
return messages;
|
46
|
+
};
|
47
|
+
|
48
|
+
// Merge serveral hash options, checking if value is set before
|
49
|
+
// overwriting any value. The precedence is from left to right.
|
50
|
+
//
|
51
|
+
// I18n.prepareOptions({name: "John Doe"}, {name: "Mary Doe", role: "user"});
|
52
|
+
// #=> {name: "John Doe", role: "user"}
|
53
|
+
//
|
54
|
+
I18n.prepareOptions = function() {
|
55
|
+
var options = {};
|
56
|
+
var opts;
|
57
|
+
var count = arguments.length;
|
58
|
+
|
59
|
+
for (var i = 0; i < count; i++) {
|
60
|
+
opts = arguments[i];
|
61
|
+
|
62
|
+
if (!opts) {
|
63
|
+
continue;
|
64
|
+
}
|
65
|
+
|
66
|
+
for (var key in opts) {
|
67
|
+
if (options[key] == undefined || options[key] == null) {
|
68
|
+
options[key] = opts[key];
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
return options;
|
74
|
+
};
|
75
|
+
|
76
|
+
I18n.interpolate = function(message, options) {
|
77
|
+
options = this.prepareOptions(options);
|
78
|
+
var matches = message.match(this.PLACEHOLDER);
|
79
|
+
|
80
|
+
if (!matches) {
|
81
|
+
return message;
|
82
|
+
}
|
83
|
+
|
84
|
+
var placeholder, value, name;
|
85
|
+
|
86
|
+
for (var i = 0; placeholder = matches[i]; i++) {
|
87
|
+
console.debug(placeholder)
|
88
|
+
name = placeholder.replace(this.PLACEHOLDER, "$1");
|
89
|
+
console.debug(name)
|
90
|
+
|
91
|
+
value = options[name];
|
92
|
+
|
93
|
+
if (options[name] == null || options[name] == undefined) {
|
94
|
+
value = "[missing " + placeholder + " value]";
|
95
|
+
}
|
96
|
+
|
97
|
+
regex = new RegExp(placeholder.replace(/\{/gm, "\\{").replace(/\}/gm, "\\}"));
|
98
|
+
message = message.replace(regex, value);
|
99
|
+
}
|
100
|
+
|
101
|
+
return message;
|
102
|
+
};
|
103
|
+
|
104
|
+
I18n.translate = function(scope, options) {
|
105
|
+
options = this.prepareOptions(options);
|
106
|
+
var translation = this.lookup(scope, options);
|
107
|
+
|
108
|
+
try {
|
109
|
+
if (typeof(translation) == "object") {
|
110
|
+
if (typeof(options.count) == "number") {
|
111
|
+
return this.pluralize(options.count, scope, options);
|
112
|
+
} else {
|
113
|
+
return translation;
|
114
|
+
}
|
115
|
+
} else {
|
116
|
+
return this.interpolate(translation, options);
|
117
|
+
}
|
118
|
+
} catch(err) {
|
119
|
+
return this.missingTranslation(scope);
|
120
|
+
}
|
121
|
+
};
|
122
|
+
|
123
|
+
I18n.localize = function(scope, value) {
|
124
|
+
switch (scope) {
|
125
|
+
case "currency":
|
126
|
+
return this.toCurrency(value);
|
127
|
+
case "number":
|
128
|
+
scope = this.lookup("number.format");
|
129
|
+
return this.toNumber(value, scope);
|
130
|
+
case "percentage":
|
131
|
+
return this.toPercentage(value);
|
132
|
+
default:
|
133
|
+
if (scope.match(/^(date|time)/)) {
|
134
|
+
return this.toTime(scope, value);
|
135
|
+
} else {
|
136
|
+
return value.toString();
|
137
|
+
}
|
138
|
+
}
|
139
|
+
};
|
140
|
+
|
141
|
+
I18n.parseDate = function(d) {
|
142
|
+
var matches, date;
|
143
|
+
|
144
|
+
if (matches = d.toString().match(/(\d{4})-(\d{2})-(\d{2})(?:[ |T](\d{2}):(\d{2}):(\d{2}))?(Z)?/)) {
|
145
|
+
// date/time strings: yyyy-mm-dd hh:mm:ss or yyyy-mm-dd or yyyy-mm-ddThh:mm:ssZ
|
146
|
+
for (var i = 1; i <= 6; i++) {
|
147
|
+
matches[i] = parseInt(matches[i], 10) || 0;
|
148
|
+
}
|
149
|
+
|
150
|
+
// month starts on 0
|
151
|
+
matches[2] -= 1;
|
152
|
+
|
153
|
+
if (matches[7]) {
|
154
|
+
date = new Date(Date.UTC(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]));
|
155
|
+
} else {
|
156
|
+
date = new Date(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]);
|
157
|
+
}
|
158
|
+
} else if (typeof(d) == "number") {
|
159
|
+
// UNIX timestamp
|
160
|
+
date = new Date();
|
161
|
+
date.setTime(d);
|
162
|
+
} else {
|
163
|
+
// an arbitrary javascript string
|
164
|
+
date = new Date();
|
165
|
+
date.setTime(Date.parse(d));
|
166
|
+
}
|
167
|
+
|
168
|
+
return date;
|
169
|
+
};
|
170
|
+
|
171
|
+
I18n.toTime = function(scope, d) {
|
172
|
+
var date = this.parseDate(d);
|
173
|
+
var format = this.lookup(scope);
|
174
|
+
|
175
|
+
if (date.toString().match(/invalid/i)) {
|
176
|
+
return date.toString();
|
177
|
+
}
|
178
|
+
|
179
|
+
if (!format) {
|
180
|
+
return date.toString();
|
181
|
+
}
|
182
|
+
|
183
|
+
return this.strftime(date, format);
|
184
|
+
};
|
185
|
+
|
186
|
+
I18n.strftime = function(date, format) {
|
187
|
+
var options = this.lookup("date");
|
188
|
+
|
189
|
+
if (!options) {
|
190
|
+
return date.toString();
|
191
|
+
}
|
192
|
+
|
193
|
+
var weekDay = date.getDay();
|
194
|
+
var day = date.getDate();
|
195
|
+
var year = date.getFullYear();
|
196
|
+
var month = date.getMonth() + 1;
|
197
|
+
var hour = date.getHours();
|
198
|
+
var hour12 = hour;
|
199
|
+
var meridian = hour > 12? "PM" : "AM";
|
200
|
+
var secs = date.getSeconds();
|
201
|
+
var mins = date.getMinutes();
|
202
|
+
var offset = date.getTimezoneOffset();
|
203
|
+
var absOffsetHours = Math.floor(Math.abs(offset / 60));
|
204
|
+
var absOffsetMinutes = Math.abs(offset) - (absOffsetHours * 60);
|
205
|
+
var timezoneoffset = (offset > 0 ? "-" : "+") + (absOffsetHours.toString().length < 2 ? "0" + absOffsetHours : absOffsetHours) + (absOffsetMinutes.toString().length < 2 ? "0" + absOffsetMinutes : absOffsetMinutes);
|
206
|
+
|
207
|
+
if (hour12 > 12) {
|
208
|
+
hour12 = hour12 - 12;
|
209
|
+
}
|
210
|
+
|
211
|
+
var padding = function(n) {
|
212
|
+
var s = "0" + n.toString();
|
213
|
+
return s.substr(s.length - 2);
|
214
|
+
}
|
215
|
+
|
216
|
+
var f = format;
|
217
|
+
f = f.replace("%a", options["abbr_day_names"][weekDay]);
|
218
|
+
f = f.replace("%A", options["day_names"][weekDay]);
|
219
|
+
f = f.replace("%b", options["abbr_month_names"][month]);
|
220
|
+
f = f.replace("%B", options["month_names"][month]);
|
221
|
+
f = f.replace("%d", padding(day));
|
222
|
+
f = f.replace("%-d", day);
|
223
|
+
f = f.replace("%H", padding(hour));
|
224
|
+
f = f.replace("%-H", hour);
|
225
|
+
f = f.replace("%I", padding(hour12));
|
226
|
+
f = f.replace("%-I", hour12);
|
227
|
+
f = f.replace("%m", padding(month));
|
228
|
+
f = f.replace("%-m", month);
|
229
|
+
f = f.replace("%M", padding(mins));
|
230
|
+
f = f.replace("%-M", mins);
|
231
|
+
f = f.replace("%p", meridian);
|
232
|
+
f = f.replace("%S", padding(secs));
|
233
|
+
f = f.replace("%-S", secs);
|
234
|
+
f = f.replace("%w", weekDay);
|
235
|
+
f = f.replace("%y", padding(year));
|
236
|
+
f = f.replace("%-y", padding(year).replace(/^0+/, ""));
|
237
|
+
f = f.replace("%Y", year);
|
238
|
+
f = f.replace("%z", timezoneoffset);
|
239
|
+
|
240
|
+
return f;
|
241
|
+
};
|
242
|
+
|
243
|
+
I18n.toNumber = function(number, options) {
|
244
|
+
options = this.prepareOptions(
|
245
|
+
options,
|
246
|
+
this.lookup("number.format"),
|
247
|
+
{precision: 3, separator: ".", delimiter: ","}
|
248
|
+
);
|
249
|
+
|
250
|
+
var string = number.toFixed(options["precision"]).toString();
|
251
|
+
var parts = string.split(".");
|
252
|
+
|
253
|
+
number = parts[0];
|
254
|
+
var precision = parts[1];
|
255
|
+
|
256
|
+
var n = [];
|
257
|
+
|
258
|
+
while (number.length > 0) {
|
259
|
+
n.unshift(number.substr(Math.max(0, number.length - 3), 3));
|
260
|
+
number = number.substr(0, number.length -3);
|
261
|
+
}
|
262
|
+
|
263
|
+
var formattedNumber = n.join(options["delimiter"]);
|
264
|
+
|
265
|
+
if (options["precision"] > 0) {
|
266
|
+
formattedNumber += options["separator"] + parts[1];
|
267
|
+
}
|
268
|
+
|
269
|
+
return formattedNumber;
|
270
|
+
};
|
271
|
+
|
272
|
+
I18n.toCurrency = function(number, options) {
|
273
|
+
options = this.prepareOptions(
|
274
|
+
options,
|
275
|
+
this.lookup("number.currency.format"),
|
276
|
+
this.lookup("number.format"),
|
277
|
+
{unit: "$", precision: 2, format: "%u%n", delimiter: ",", separator: "."}
|
278
|
+
);
|
279
|
+
|
280
|
+
number = this.toNumber(number, options);
|
281
|
+
number = options["format"]
|
282
|
+
.replace("%u", options["unit"])
|
283
|
+
.replace("%n", number);
|
284
|
+
|
285
|
+
return number;
|
286
|
+
};
|
287
|
+
|
288
|
+
I18n.toPercentage = function(number, options) {
|
289
|
+
options = this.prepareOptions(
|
290
|
+
options,
|
291
|
+
this.lookup("number.percentage.format"),
|
292
|
+
this.lookup("number.format"),
|
293
|
+
{precision: 3, separator: ".", delimiter: ""}
|
294
|
+
);
|
295
|
+
|
296
|
+
number = this.toNumber(number, options);
|
297
|
+
return number + "%";
|
298
|
+
};
|
299
|
+
|
300
|
+
I18n.pluralize = function(count, scope, options) {
|
301
|
+
var translation = this.lookup(scope, options);
|
302
|
+
|
303
|
+
var message;
|
304
|
+
options = this.prepareOptions(options);
|
305
|
+
options["count"] = count.toString();
|
306
|
+
|
307
|
+
switch(Math.abs(count)) {
|
308
|
+
case 0:
|
309
|
+
message = translation["zero"] || translation["none"] || translation["other"] || this.missingTranslation(scope, "zero");
|
310
|
+
break;
|
311
|
+
case 1:
|
312
|
+
message = translation["one"] || this.missingTranslation(scope, "one");;
|
313
|
+
break;
|
314
|
+
default:
|
315
|
+
message = translation["other"] || this.missingTranslation(scope, "other");;
|
316
|
+
}
|
317
|
+
|
318
|
+
return this.interpolate(message, options);
|
319
|
+
};
|
320
|
+
|
321
|
+
I18n.missingTranslation = function() {
|
322
|
+
var message = '[missing "' + this.currentLocale();
|
323
|
+
var count = arguments.length;
|
324
|
+
|
325
|
+
for (var i = 0; i < count; i++) {
|
326
|
+
message += "." + arguments[i];
|
327
|
+
}
|
328
|
+
|
329
|
+
message += '" translation]';
|
330
|
+
|
331
|
+
return message;
|
332
|
+
};
|
333
|
+
|
334
|
+
I18n.currentLocale = function() {
|
335
|
+
return (I18n.locale || I18n.defaultLocale);
|
336
|
+
};
|
337
|
+
|
338
|
+
// shortcuts
|
339
|
+
I18n.t = I18n.translate;
|
340
|
+
I18n.l = I18n.localize;
|
341
|
+
I18n.p = I18n.pluralize;
|
data/test/i18n-test.html
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<title>JavaScript unit test file</title>
|
6
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
7
|
+
<script src="jsunittest/jsunittest.js" type="text/javascript"></script>
|
8
|
+
<link rel="stylesheet" href="jsunittest/unittest.css" type="text/css" />
|
9
|
+
|
10
|
+
<style type="text/css" media="screen">
|
11
|
+
#logger p {
|
12
|
+
background: #ffc;
|
13
|
+
padding: 5px;
|
14
|
+
}
|
15
|
+
</style>
|
16
|
+
<script type="text/javascript" charset="utf-8">
|
17
|
+
function log(name, message) {
|
18
|
+
var tag = document.getElementById("logger");
|
19
|
+
message = message.toString();
|
20
|
+
message = message.replace(/&/gm, "&");
|
21
|
+
message = message.replace(/</gm, "<");
|
22
|
+
message = message.replace(/>/gm, ">");
|
23
|
+
tag.innerHTML += "<p><strong>" + name + ":</strong> " + message + "</p>";
|
24
|
+
}
|
25
|
+
</script>
|
26
|
+
<script src="../source/i18n.js" type="text/javascript"></script>
|
27
|
+
</head>
|
28
|
+
<body>
|
29
|
+
|
30
|
+
<div id="content">
|
31
|
+
<div id="header">
|
32
|
+
<h1>JavaScript unit test file</h1>
|
33
|
+
<p>
|
34
|
+
This file tests <strong>i18n.js</strong>.
|
35
|
+
</p>
|
36
|
+
</div>
|
37
|
+
|
38
|
+
<!-- Log output (one per Runner, via {testLog: "testlog"} option)-->
|
39
|
+
<div id="testlog"></div>
|
40
|
+
|
41
|
+
<!-- General debugger -->
|
42
|
+
<div id="logger"></div>
|
43
|
+
|
44
|
+
<!-- Put sample/test html here -->
|
45
|
+
<div id="sample">
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
<script src="i18n-test.js" type="text/javascript" charset="utf-8"></script>
|
49
|
+
</body>
|
50
|
+
</html>
|
data/test/i18n-test.js
ADDED
@@ -0,0 +1,668 @@
|
|
1
|
+
new Test.Unit.Runner({
|
2
|
+
setup: function() {
|
3
|
+
I18n.defaultLocale = "en";
|
4
|
+
I18n.locale = null;
|
5
|
+
|
6
|
+
I18n.translations = {
|
7
|
+
en: {
|
8
|
+
hello: "Hello World!",
|
9
|
+
greetings: {
|
10
|
+
stranger: "Hello stranger!",
|
11
|
+
name: "Hello {{name}}!"
|
12
|
+
},
|
13
|
+
profile: {
|
14
|
+
details: "{{name}} is {{age}}-years old"
|
15
|
+
},
|
16
|
+
inbox: {
|
17
|
+
one: "You have {{count}} message",
|
18
|
+
other: "You have {{count}} messages",
|
19
|
+
zero: "You have no messages"
|
20
|
+
},
|
21
|
+
unread: {
|
22
|
+
one: "You have 1 new message ({{unread}} unread)",
|
23
|
+
other: "You have {{count}} new messages ({{unread}} unread)",
|
24
|
+
zero: "You have no new messages ({{unread}} unread)"
|
25
|
+
},
|
26
|
+
number: null
|
27
|
+
},
|
28
|
+
|
29
|
+
pt: {
|
30
|
+
hello: "Olá Mundo!",
|
31
|
+
date: {
|
32
|
+
formats: {
|
33
|
+
"default": "%d/%m/%Y",
|
34
|
+
"short": "%d de %B",
|
35
|
+
"long": "%d de %B de %Y"
|
36
|
+
},
|
37
|
+
day_names: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"],
|
38
|
+
abbr_day_names: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"],
|
39
|
+
month_names: [null, "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
|
40
|
+
abbr_month_names: [null, "Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"]
|
41
|
+
},
|
42
|
+
number: {
|
43
|
+
percentage: {
|
44
|
+
format: {
|
45
|
+
delimiter: "",
|
46
|
+
separator: ",",
|
47
|
+
precision: 2
|
48
|
+
}
|
49
|
+
}
|
50
|
+
},
|
51
|
+
time: {
|
52
|
+
formats: {
|
53
|
+
"default": "%A, %d de %B de %Y, %H:%M h",
|
54
|
+
"short": "%d/%m, %H:%M h",
|
55
|
+
"long": "%A, %d de %B de %Y, %H:%M h"
|
56
|
+
},
|
57
|
+
am: "AM",
|
58
|
+
pm: "PM"
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
},
|
63
|
+
|
64
|
+
teardown: function() {
|
65
|
+
},
|
66
|
+
|
67
|
+
// Defaults
|
68
|
+
testDefaults: function() { with(this) {
|
69
|
+
assertEqual("en", I18n.defaultLocale);
|
70
|
+
assertEqual(null, I18n.locale);
|
71
|
+
assertEqual("en", I18n.currentLocale());
|
72
|
+
}},
|
73
|
+
|
74
|
+
// Custom locale
|
75
|
+
testCustomLocale: function() { with(this) {
|
76
|
+
I18n.locale = "pt";
|
77
|
+
assertEqual("pt", I18n.currentLocale());
|
78
|
+
}},
|
79
|
+
|
80
|
+
// Aliases methods
|
81
|
+
testAliasesMethods: function() { with(this) {
|
82
|
+
assertEqual(I18n.translate, I18n.t);
|
83
|
+
assertEqual(I18n.localize, I18n.l);
|
84
|
+
assertEqual(I18n.pluralize, I18n.p);
|
85
|
+
}},
|
86
|
+
|
87
|
+
// Translation for single scope
|
88
|
+
testTranslationForSingleScope: function() { with(this) {
|
89
|
+
assertEqual("Hello World!", I18n.translate("hello"));
|
90
|
+
}},
|
91
|
+
|
92
|
+
// Translation as object
|
93
|
+
testTranslationAsObject: function() { with(this) {
|
94
|
+
assertEqual("object", typeof I18n.translate("greetings"));
|
95
|
+
}},
|
96
|
+
|
97
|
+
// Translation with invalid scope shall not block
|
98
|
+
testTranslationWithInvalidScope: function() { with(this) {
|
99
|
+
assertEqual('[missing "en.invalid.scope.shall.not.block" translation]', I18n.translate("invalid.scope.shall.not.block"));
|
100
|
+
}},
|
101
|
+
|
102
|
+
// Translation for single scope on a custom locale
|
103
|
+
testTranslationForSingleScopeOnACustomLocale: function() { with(this) {
|
104
|
+
I18n.locale = "pt";
|
105
|
+
assertEqual("Olá Mundo!", I18n.translate("hello"));
|
106
|
+
}},
|
107
|
+
|
108
|
+
// Translation for multiple scopes
|
109
|
+
testTranslationForMultipleScopes: function() { with(this) {
|
110
|
+
assertEqual("Hello stranger!", I18n.translate("greetings.stranger"));
|
111
|
+
}},
|
112
|
+
|
113
|
+
// Single interpolation
|
114
|
+
testSingleInterpolation: function() { with(this) {
|
115
|
+
actual = I18n.translate("greetings.name", {name: "John Doe"});
|
116
|
+
assertEqual("Hello John Doe!", actual);
|
117
|
+
}},
|
118
|
+
|
119
|
+
// Multiple interpolations
|
120
|
+
testMultipleInterpolations: function() { with(this) {
|
121
|
+
actual = I18n.translate("profile.details", {name: "John Doe", age: 27});
|
122
|
+
assertEqual("John Doe is 27-years old", actual);
|
123
|
+
}},
|
124
|
+
|
125
|
+
// Translation with count option
|
126
|
+
testTranslationWithCountOption: function() { with(this) {
|
127
|
+
assertEqual("You have 1 message", I18n.translate("inbox", {count: 1}));
|
128
|
+
assertEqual("You have 5 messages", I18n.translate("inbox", {count: 5}));
|
129
|
+
assertEqual("You have no messages", I18n.translate("inbox", {count: 0}));
|
130
|
+
}},
|
131
|
+
|
132
|
+
// Translation with count option and multiple placeholders
|
133
|
+
testTranslationWithCountOptionAndMultiplePlaceholders: function() { with(this) {
|
134
|
+
actual = I18n.translate("unread", {unread: 5, count: 1});
|
135
|
+
assertEqual("You have 1 new message (5 unread)", actual);
|
136
|
+
|
137
|
+
actual = I18n.translate("unread", {unread: 2, count: 10});
|
138
|
+
assertEqual("You have 10 new messages (2 unread)", actual);
|
139
|
+
|
140
|
+
actual = I18n.translate("unread", {unread: 5, count: 0});
|
141
|
+
assertEqual("You have no new messages (5 unread)", actual);
|
142
|
+
}},
|
143
|
+
|
144
|
+
// Missing translation with count option
|
145
|
+
testMissingTranslationWithCountOption: function() { with(this) {
|
146
|
+
actual = I18n.translate("invalid", {count: 1});
|
147
|
+
assertEqual('[missing "en.invalid" translation]', actual);
|
148
|
+
|
149
|
+
I18n.translations.en.inbox.one = null;
|
150
|
+
actual = I18n.translate("inbox", {count: 1});
|
151
|
+
assertEqual('[missing "en.inbox.one" translation]', actual);
|
152
|
+
}},
|
153
|
+
|
154
|
+
// Pluralization
|
155
|
+
testPluralization: function() { with(this) {
|
156
|
+
assertEqual("You have 1 message", I18n.pluralize(1, "inbox"));
|
157
|
+
assertEqual("You have 5 messages", I18n.pluralize(5, "inbox"));
|
158
|
+
assertEqual("You have no messages", I18n.pluralize(0, "inbox"));
|
159
|
+
}},
|
160
|
+
|
161
|
+
// Pluralize should return "other" scope
|
162
|
+
testPlurationShouldReturnOtherScope: function() { with(this) {
|
163
|
+
I18n.translations["en"]["inbox"]["zero"] = null;
|
164
|
+
assertEqual("You have 0 messages", I18n.pluralize(0, "inbox"));
|
165
|
+
}},
|
166
|
+
|
167
|
+
// Pluralize should return "zero" scope
|
168
|
+
testPlurationShouldReturnZeroScope: function() { with(this) {
|
169
|
+
I18n.translations["en"]["inbox"]["zero"] = "No messages (zero)";
|
170
|
+
I18n.translations["en"]["inbox"]["none"] = "No messages (none)";
|
171
|
+
|
172
|
+
assertEqual("No messages (zero)", I18n.pluralize(0, "inbox"));
|
173
|
+
}},
|
174
|
+
|
175
|
+
// Pluralize should return "none" scope
|
176
|
+
testPlurationShouldReturnNoneScope: function() { with(this) {
|
177
|
+
I18n.translations["en"]["inbox"]["zero"] = null;
|
178
|
+
I18n.translations["en"]["inbox"]["none"] = "No messages (none)";
|
179
|
+
|
180
|
+
assertEqual("No messages (none)", I18n.pluralize(0, "inbox"));
|
181
|
+
}},
|
182
|
+
|
183
|
+
// Pluralize with negative values
|
184
|
+
testPluralizeWithNegativeValues: function() { with(this) {
|
185
|
+
assertEqual("You have -1 message", I18n.pluralize(-1, "inbox"));
|
186
|
+
assertEqual("You have -5 messages", I18n.pluralize(-5, "inbox"));
|
187
|
+
}},
|
188
|
+
|
189
|
+
// Pluralize with multiple placeholders
|
190
|
+
testPluralizeWithMultiplePlaceholders: function() { with(this) {
|
191
|
+
actual = I18n.pluralize(1, "unread", {unread: 5});
|
192
|
+
assertEqual("You have 1 new message (5 unread)", actual);
|
193
|
+
|
194
|
+
actual = I18n.pluralize(10, "unread", {unread: 2});
|
195
|
+
assertEqual("You have 10 new messages (2 unread)", actual);
|
196
|
+
|
197
|
+
actual = I18n.pluralize(0, "unread", {unread: 5});
|
198
|
+
assertEqual("You have no new messages (5 unread)", actual);
|
199
|
+
}},
|
200
|
+
|
201
|
+
// Numbers with default settings
|
202
|
+
testNumbersWithDefaultSettings: function() { with(this) {
|
203
|
+
assertEqual("1.000", I18n.toNumber(1));
|
204
|
+
assertEqual("12.000", I18n.toNumber(12));
|
205
|
+
assertEqual("123.000", I18n.toNumber(123));
|
206
|
+
assertEqual("1,234.000", I18n.toNumber(1234));
|
207
|
+
assertEqual("123,456.000", I18n.toNumber(123456));
|
208
|
+
assertEqual("1,234,567.000", I18n.toNumber(1234567));
|
209
|
+
assertEqual("12,345,678.000", I18n.toNumber(12345678));
|
210
|
+
}},
|
211
|
+
|
212
|
+
// Numbers with partial translation and default options
|
213
|
+
testNumbersWithPartialTranslationAndDefaultOptions: function() { with(this) {
|
214
|
+
I18n.translations.en.number = {
|
215
|
+
format: {
|
216
|
+
precision: 2
|
217
|
+
}
|
218
|
+
}
|
219
|
+
|
220
|
+
assertEqual("1,234.00", I18n.toNumber(1234));
|
221
|
+
}},
|
222
|
+
|
223
|
+
// Numbers with full translation and default options
|
224
|
+
testNumbersWithFullTranslationAndDefaultOptions: function() { with(this) {
|
225
|
+
I18n.translations.en.number = {
|
226
|
+
format: {
|
227
|
+
delimiter: ".",
|
228
|
+
separator: ",",
|
229
|
+
precision: 2
|
230
|
+
}
|
231
|
+
}
|
232
|
+
|
233
|
+
assertEqual("1.234,00", I18n.toNumber(1234));
|
234
|
+
}},
|
235
|
+
|
236
|
+
// Numbers with some custom options that should be merged with default options
|
237
|
+
testNumbersWithSomeCustomOptionsThatShouldBeMergedWithDefaultOptions: function() { with(this) {
|
238
|
+
assertEqual("1,234", I18n.toNumber(1234, {precision: 0}));
|
239
|
+
assertEqual("1,234-000", I18n.toNumber(1234, {separator: "-"}));
|
240
|
+
assertEqual("1-234.000", I18n.toNumber(1234, {delimiter: "-"}));
|
241
|
+
}},
|
242
|
+
|
243
|
+
// Numbers considering options
|
244
|
+
testNumbersConsideringOptions: function() { with(this) {
|
245
|
+
options = {
|
246
|
+
precision: 2,
|
247
|
+
separator: ",",
|
248
|
+
delimiter: "."
|
249
|
+
};
|
250
|
+
|
251
|
+
assertEqual("1,00", I18n.toNumber(1, options));
|
252
|
+
assertEqual("12,00", I18n.toNumber(12, options));
|
253
|
+
assertEqual("123,00", I18n.toNumber(123, options));
|
254
|
+
assertEqual("1.234,00", I18n.toNumber(1234, options));
|
255
|
+
assertEqual("123.456,00", I18n.toNumber(123456, options));
|
256
|
+
assertEqual("1.234.567,00", I18n.toNumber(1234567, options));
|
257
|
+
assertEqual("12.345.678,00", I18n.toNumber(12345678, options));
|
258
|
+
}},
|
259
|
+
|
260
|
+
// Numbers with different precisions
|
261
|
+
testNumbersWithDifferentPrecisions: function() { with(this) {
|
262
|
+
options = {separator: ".", delimiter: ","};
|
263
|
+
|
264
|
+
options["precision"] = 2;
|
265
|
+
assertEqual("1.98", I18n.toNumber(1.98, options));
|
266
|
+
|
267
|
+
options["precision"] = 3;
|
268
|
+
assertEqual("1.980", I18n.toNumber(1.98, options));
|
269
|
+
|
270
|
+
options["precision"] = 2;
|
271
|
+
assertEqual("1.99", I18n.toNumber(1.987, options));
|
272
|
+
|
273
|
+
options["precision"] = 1;
|
274
|
+
assertEqual("2.0", I18n.toNumber(1.98, options));
|
275
|
+
|
276
|
+
options["precision"] = 0;
|
277
|
+
assertEqual("2", I18n.toNumber(1.98, options));
|
278
|
+
}},
|
279
|
+
|
280
|
+
// Currency with default settings
|
281
|
+
testCurrencyWithDefaultSettings: function() { with(this) {
|
282
|
+
assertEqual("$100.99", I18n.toCurrency(100.99));
|
283
|
+
assertEqual("$1,000.99", I18n.toCurrency(1000.99));
|
284
|
+
}},
|
285
|
+
|
286
|
+
// Current with custom settings
|
287
|
+
testCurrencyWithCustomSettings: function() { with(this) {
|
288
|
+
I18n.translations.en.number = {
|
289
|
+
currency: {
|
290
|
+
format: {
|
291
|
+
format: "%n %u",
|
292
|
+
unit: "USD",
|
293
|
+
delimiter: ".",
|
294
|
+
separator: ",",
|
295
|
+
precision: 2
|
296
|
+
}
|
297
|
+
}
|
298
|
+
};
|
299
|
+
|
300
|
+
assertEqual("12,00 USD", I18n.toCurrency(12));
|
301
|
+
assertEqual("123,00 USD", I18n.toCurrency(123));
|
302
|
+
assertEqual("1.234,56 USD", I18n.toCurrency(1234.56));
|
303
|
+
}},
|
304
|
+
|
305
|
+
// Currency with custom settings and partial overriding
|
306
|
+
testCurrencyWithCustomSettingsAndPartialOverriding: function() { with(this) {
|
307
|
+
I18n.translations.en.number = {
|
308
|
+
currency: {
|
309
|
+
format: {
|
310
|
+
format: "%n %u",
|
311
|
+
unit: "USD",
|
312
|
+
delimiter: ".",
|
313
|
+
separator: ",",
|
314
|
+
precision: 2
|
315
|
+
}
|
316
|
+
}
|
317
|
+
};
|
318
|
+
|
319
|
+
assertEqual("12 USD", I18n.toCurrency(12, {precision: 0}));
|
320
|
+
assertEqual("123,00 bucks", I18n.toCurrency(123, {unit: "bucks"}));
|
321
|
+
}},
|
322
|
+
|
323
|
+
// Currency with some custom options that should be merged with default options
|
324
|
+
testCurrencyWithSomeCustomOptionsThatShouldBeMergedWithDefaultOptions: function() { with(this) {
|
325
|
+
assertEqual("$1,234", I18n.toCurrency(1234, {precision: 0}));
|
326
|
+
assertEqual("º1,234.00", I18n.toCurrency(1234, {unit: "º"}));
|
327
|
+
assertEqual("$1,234-00", I18n.toCurrency(1234, {separator: "-"}));
|
328
|
+
assertEqual("$1-234.00", I18n.toCurrency(1234, {delimiter: "-"}));
|
329
|
+
assertEqual("$ 1,234.00", I18n.toCurrency(1234, {format: "%u %n"}));
|
330
|
+
}},
|
331
|
+
|
332
|
+
// Localize numbers
|
333
|
+
testLocalizeNumbers: function() { with(this) {
|
334
|
+
assertEqual("1,234,567.000", I18n.localize("number", 1234567));
|
335
|
+
}},
|
336
|
+
|
337
|
+
// Localize currency
|
338
|
+
testLocalizeCurrency: function() { with(this) {
|
339
|
+
assertEqual("$1,234,567.00", I18n.localize("currency", 1234567));
|
340
|
+
}},
|
341
|
+
|
342
|
+
// Parse date
|
343
|
+
testParseDate: function() { with(this) {
|
344
|
+
expected = new Date(2009, 0, 24, 0, 0, 0);
|
345
|
+
assertEqual(expected.toString(), I18n.parseDate("2009-01-24").toString());
|
346
|
+
|
347
|
+
expected = new Date(2009, 0, 24, 0, 15, 0);
|
348
|
+
assertEqual(expected.toString(), I18n.parseDate("2009-01-24 00:15:00").toString());
|
349
|
+
|
350
|
+
expected = new Date(2009, 0, 24, 0, 0, 15);
|
351
|
+
assertEqual(expected.toString(), I18n.parseDate("2009-01-24 00:00:15").toString());
|
352
|
+
|
353
|
+
expected = new Date(2009, 0, 24, 15, 33, 44);
|
354
|
+
assertEqual(expected.toString(), I18n.parseDate("2009-01-24 15:33:44").toString());
|
355
|
+
|
356
|
+
expected = new Date(2009, 0, 24, 0, 0, 0);
|
357
|
+
assertEqual(expected.toString(), I18n.parseDate(expected.getTime()).toString());
|
358
|
+
|
359
|
+
expected = new Date(2009, 0, 24, 0, 0, 0);
|
360
|
+
assertEqual(expected.toString(), I18n.parseDate("01/24/2009").toString());
|
361
|
+
|
362
|
+
expected = new Date(2009, 0, 24, 14, 33, 55);
|
363
|
+
assertEqual(expected.toString(), I18n.parseDate(expected).toString());
|
364
|
+
|
365
|
+
expected = new Date(2009, 0, 24, 15, 33, 44);
|
366
|
+
assertEqual(expected.toString(), I18n.parseDate("2009-01-24T15:33:44").toString());
|
367
|
+
|
368
|
+
expected = new Date(Date.UTC(2009, 0, 24, 15, 33, 44));
|
369
|
+
assertEqual(expected.toString(), I18n.parseDate("2009-01-24T15:33:44Z").toString());
|
370
|
+
}},
|
371
|
+
|
372
|
+
// Date formatting
|
373
|
+
testDateFormatting: function() { with(this) {
|
374
|
+
I18n.locale = "pt";
|
375
|
+
|
376
|
+
// 2009-04-26 19:35:44 (Sunday)
|
377
|
+
var date = new Date(2009, 3, 26, 19, 35, 44);
|
378
|
+
|
379
|
+
// short week day
|
380
|
+
assertEqual("Dom", I18n.strftime(date, "%a"));
|
381
|
+
|
382
|
+
// full week day
|
383
|
+
assertEqual("Domingo", I18n.strftime(date, "%A"));
|
384
|
+
|
385
|
+
// short month
|
386
|
+
assertEqual("Abr", I18n.strftime(date, "%b"));
|
387
|
+
|
388
|
+
// full month
|
389
|
+
assertEqual("Abril", I18n.strftime(date, "%B"));
|
390
|
+
|
391
|
+
// day
|
392
|
+
assertEqual("26", I18n.strftime(date, "%d"));
|
393
|
+
|
394
|
+
// 24-hour
|
395
|
+
assertEqual("19", I18n.strftime(date, "%H"));
|
396
|
+
|
397
|
+
// 12-hour
|
398
|
+
assertEqual("07", I18n.strftime(date, "%I"));
|
399
|
+
|
400
|
+
// month
|
401
|
+
assertEqual("04", I18n.strftime(date, "%m"));
|
402
|
+
|
403
|
+
// minutes
|
404
|
+
assertEqual("35", I18n.strftime(date, "%M"));
|
405
|
+
|
406
|
+
// meridian
|
407
|
+
assertEqual("PM", I18n.strftime(date, "%p"));
|
408
|
+
|
409
|
+
// seconds
|
410
|
+
assertEqual("44", I18n.strftime(date, "%S"));
|
411
|
+
|
412
|
+
// week day
|
413
|
+
assertEqual("0", I18n.strftime(date, "%w"));
|
414
|
+
|
415
|
+
// short year
|
416
|
+
assertEqual("09", I18n.strftime(date, "%y"));
|
417
|
+
|
418
|
+
// full year
|
419
|
+
assertEqual("2009", I18n.strftime(date, "%Y"));
|
420
|
+
}},
|
421
|
+
|
422
|
+
// Date formatting without padding
|
423
|
+
testDateFormattingWithoutPadding: function() { with(this) {
|
424
|
+
I18n.locale = "pt";
|
425
|
+
|
426
|
+
// 2009-04-26 19:35:44 (Sunday)
|
427
|
+
var date = new Date(2009, 3, 9, 7, 8, 9);
|
428
|
+
|
429
|
+
// 24-hour without padding
|
430
|
+
assertEqual("7", I18n.strftime(date, "%-H"));
|
431
|
+
|
432
|
+
// 12-hour without padding
|
433
|
+
assertEqual("7", I18n.strftime(date, "%-I"));
|
434
|
+
|
435
|
+
// minutes without padding
|
436
|
+
assertEqual("8", I18n.strftime(date, "%-M"));
|
437
|
+
|
438
|
+
// seconds without padding
|
439
|
+
assertEqual("9", I18n.strftime(date, "%-S"));
|
440
|
+
|
441
|
+
// short year without padding
|
442
|
+
assertEqual("9", I18n.strftime(date, "%-y"));
|
443
|
+
|
444
|
+
// month without padding
|
445
|
+
assertEqual("4", I18n.strftime(date, "%-m"));
|
446
|
+
|
447
|
+
// day without padding
|
448
|
+
assertEqual("9", I18n.strftime(date, "%-d"));
|
449
|
+
}},
|
450
|
+
|
451
|
+
// Date formatting with padding
|
452
|
+
testDateFormattingWithPadding: function() { with(this) {
|
453
|
+
I18n.locale = "pt";
|
454
|
+
|
455
|
+
// 2009-04-26 19:35:44 (Sunday)
|
456
|
+
var date = new Date(2009, 3, 9, 7, 8, 9);
|
457
|
+
|
458
|
+
// 24-hour
|
459
|
+
assertEqual("07", I18n.strftime(date, "%H"));
|
460
|
+
|
461
|
+
// 12-hour
|
462
|
+
assertEqual("07", I18n.strftime(date, "%I"));
|
463
|
+
|
464
|
+
// minutes
|
465
|
+
assertEqual("08", I18n.strftime(date, "%M"));
|
466
|
+
|
467
|
+
// seconds
|
468
|
+
assertEqual("09", I18n.strftime(date, "%S"));
|
469
|
+
|
470
|
+
// short year
|
471
|
+
assertEqual("09", I18n.strftime(date, "%y"));
|
472
|
+
|
473
|
+
// month
|
474
|
+
assertEqual("04", I18n.strftime(date, "%m"));
|
475
|
+
|
476
|
+
// day
|
477
|
+
assertEqual("09", I18n.strftime(date, "%d"));
|
478
|
+
}},
|
479
|
+
|
480
|
+
// Date formatting with negative Timezone
|
481
|
+
testDateFormattingWithNegativeTimezone: function() { with(this) {
|
482
|
+
I18n.locale = "pt";
|
483
|
+
|
484
|
+
var date = new Date(2009, 3, 26, 19, 35, 44);
|
485
|
+
|
486
|
+
date.getTimezoneOffset = function() {
|
487
|
+
return 345;
|
488
|
+
};
|
489
|
+
|
490
|
+
assertMatch(/^(\+|-)[\d]{4}$/, I18n.strftime(date, "%z"));
|
491
|
+
assertEqual("-0545", I18n.strftime(date, "%z"));
|
492
|
+
}},
|
493
|
+
|
494
|
+
// Date formatting with positive Timezone
|
495
|
+
testDateFormattingWithPositiveTimezone: function() { with(this) {
|
496
|
+
I18n.locale = "pt";
|
497
|
+
|
498
|
+
var date = new Date(2009, 3, 26, 19, 35, 44);
|
499
|
+
|
500
|
+
date.getTimezoneOffset = function() {
|
501
|
+
return -345;
|
502
|
+
};
|
503
|
+
|
504
|
+
assertMatch(/^(\+|-)[\d]{4}$/, I18n.strftime(date, "%z"));
|
505
|
+
assertEqual("+0545", I18n.strftime(date, "%z"));
|
506
|
+
}},
|
507
|
+
|
508
|
+
// Localize date strings
|
509
|
+
testLocalizeDateStrings: function() { with(this) {
|
510
|
+
I18n.locale = "pt";
|
511
|
+
|
512
|
+
assertEqual("29/11/2009", I18n.localize("date.formats.default", "2009-11-29"));
|
513
|
+
assertEqual("07 de Janeiro", I18n.localize("date.formats.short", "2009-01-07"));
|
514
|
+
assertEqual("07 de Janeiro de 2009", I18n.localize("date.formats.long", "2009-01-07"));
|
515
|
+
}},
|
516
|
+
|
517
|
+
// Localize time strings
|
518
|
+
testLocalizeTimeStrings: function() { with(this) {
|
519
|
+
I18n.locale = "pt";
|
520
|
+
assertEqual("Domingo, 29 de Novembro de 2009, 15:07 h", I18n.localize("time.formats.default", "2009-11-29 15:07:59"));
|
521
|
+
assertEqual("07/01, 09:12 h", I18n.localize("time.formats.short", "2009-01-07 09:12:35"));
|
522
|
+
assertEqual("Domingo, 29 de Novembro de 2009, 15:07 h", I18n.localize("time.formats.long", "2009-11-29 15:07:59"));
|
523
|
+
}},
|
524
|
+
|
525
|
+
// Localize percentage
|
526
|
+
testLocalizePercentage: function() { with(this) {
|
527
|
+
I18n.locale = "pt";
|
528
|
+
assertEqual("123,45%", I18n.localize("percentage", 123.45));
|
529
|
+
}},
|
530
|
+
|
531
|
+
|
532
|
+
|
533
|
+
// Default value for simple translation
|
534
|
+
testDefaultValueForSimpleTranslation: function() { with(this) {
|
535
|
+
actual = I18n.translate("warning", {defaultValue: "Warning!"});
|
536
|
+
assertEqual("Warning!", actual);
|
537
|
+
}},
|
538
|
+
|
539
|
+
// Default value with interpolation
|
540
|
+
testDefaultValueWithInterpolation: function() { with(this) {
|
541
|
+
actual = I18n.translate("alert", {defaultValue: "Attention! {{message}}", message: "You're out of quota!"});
|
542
|
+
assertEqual("Attention! You're out of quota!", actual);
|
543
|
+
}},
|
544
|
+
|
545
|
+
// Default value should not be used when scope exist
|
546
|
+
testDefaultValueShouldNotBeUsedWhenScopeExist: function() { with(this) {
|
547
|
+
actual = I18n.translate("hello", {defaultValue: "What's up?"});
|
548
|
+
assertEqual("Hello World!", actual);
|
549
|
+
}},
|
550
|
+
|
551
|
+
// Default value for pluralize
|
552
|
+
testDefaultValueForPluralize: function() { with(this) {
|
553
|
+
options = {defaultValue: {
|
554
|
+
none: "No things here!",
|
555
|
+
one: "There is {{count}} thing here!",
|
556
|
+
other: "There are {{count}} things here!"
|
557
|
+
}};
|
558
|
+
|
559
|
+
assertEqual("No things here!", I18n.pluralize(0, "things", options));
|
560
|
+
assertEqual("There is 1 thing here!", I18n.pluralize(1, "things", options));
|
561
|
+
assertEqual("There are 5 things here!", I18n.pluralize(5, "things", options));
|
562
|
+
}},
|
563
|
+
|
564
|
+
// Default value for pluralize should not be used when scope exist
|
565
|
+
testDefaultValueForPluralizeShouldNotBeUsedWhenScopeExist: function() { with(this) {
|
566
|
+
options = {defaultValue: {
|
567
|
+
none: "No things here!",
|
568
|
+
one: "There is {{count}} thing here!",
|
569
|
+
other: "There are {{count}} things here!"
|
570
|
+
}};
|
571
|
+
|
572
|
+
assertEqual("You have no messages", I18n.pluralize(0, "inbox", options));
|
573
|
+
assertEqual("You have 1 message", I18n.pluralize(1, "inbox", options));
|
574
|
+
assertEqual("You have 5 messages", I18n.pluralize(5, "inbox", options));
|
575
|
+
}},
|
576
|
+
|
577
|
+
// Prepare options
|
578
|
+
testPrepareOptions: function() { with(this) {
|
579
|
+
options = I18n.prepareOptions(
|
580
|
+
{name: "Mary Doe"},
|
581
|
+
{name: "John Doe", role: "user"}
|
582
|
+
);
|
583
|
+
|
584
|
+
assertEqual("Mary Doe", options["name"]);
|
585
|
+
assertEqual("user", options["role"]);
|
586
|
+
}},
|
587
|
+
|
588
|
+
// Prepare options with multiple options
|
589
|
+
testPrepareOptionsWithMultipleOptions: function() { with(this) {
|
590
|
+
options = I18n.prepareOptions(
|
591
|
+
{name: "Mary Doe"},
|
592
|
+
{name: "John Doe", role: "user"},
|
593
|
+
{age: 33},
|
594
|
+
{email: "mary@doe.com", url: "http://marydoe.com"},
|
595
|
+
{role: "admin", email: "john@doe.com"}
|
596
|
+
);
|
597
|
+
|
598
|
+
assertEqual("Mary Doe", options["name"]);
|
599
|
+
assertEqual("user", options["role"]);
|
600
|
+
assertEqual(33, options["age"]);
|
601
|
+
assertEqual("mary@doe.com", options["email"]);
|
602
|
+
assertEqual("http://marydoe.com", options["url"]);
|
603
|
+
}},
|
604
|
+
|
605
|
+
// Prepare options should return an empty hash when values are null
|
606
|
+
testPrepareOptionsShouldReturnAnEmptyHashWhenValuesAreNull: function() { with(this) {
|
607
|
+
assertNotNullOrUndefined(I18n.prepareOptions(null, null));
|
608
|
+
}},
|
609
|
+
|
610
|
+
// Percentage with defaults
|
611
|
+
testPercentageWithDefaults: function() { with(this) {
|
612
|
+
assertEqual("1234.000%", I18n.toPercentage(1234));
|
613
|
+
}},
|
614
|
+
|
615
|
+
// Percentage with custom options
|
616
|
+
testPercentageWithCustomOptions: function() { with(this) {
|
617
|
+
assertEqual("1_234%", I18n.toPercentage(1234, {delimiter: "_", precision: 0}));
|
618
|
+
}},
|
619
|
+
|
620
|
+
// Percentage with translation
|
621
|
+
testPercentageWithTranslation: function() { with(this) {
|
622
|
+
I18n.translations.en.number = {
|
623
|
+
percentage: {
|
624
|
+
format: {
|
625
|
+
precision: 2,
|
626
|
+
delimiter: ".",
|
627
|
+
separator: ","
|
628
|
+
}
|
629
|
+
}
|
630
|
+
}
|
631
|
+
|
632
|
+
assertEqual("1.234,00%", I18n.toPercentage(1234));
|
633
|
+
}},
|
634
|
+
|
635
|
+
// Percentage with translation and custom options
|
636
|
+
testPercentageWithTranslationAndCustomOptions: function() { with(this) {
|
637
|
+
I18n.translations.en.number = {
|
638
|
+
percentage: {
|
639
|
+
format: {
|
640
|
+
precision: 2,
|
641
|
+
delimiter: ".",
|
642
|
+
separator: ","
|
643
|
+
}
|
644
|
+
}
|
645
|
+
}
|
646
|
+
|
647
|
+
assertEqual("1-234+0000%", I18n.toPercentage(1234, {precision: 4, delimiter: "-", separator: "+"}));
|
648
|
+
}},
|
649
|
+
|
650
|
+
// Scope option as string
|
651
|
+
testScopeOptionAsString: function() { with(this) {
|
652
|
+
actual = I18n.translate("stranger", {scope: "greetings"});
|
653
|
+
assertEqual("Hello stranger!", actual);
|
654
|
+
}},
|
655
|
+
|
656
|
+
// Scope as array
|
657
|
+
testScopeAsArray: function() { with(this) {
|
658
|
+
actual = I18n.translate(["greetings", "stranger"]);
|
659
|
+
assertEqual("Hello stranger!", actual);
|
660
|
+
}},
|
661
|
+
|
662
|
+
// New placeholder syntax
|
663
|
+
testNewPlaceholderSyntax: function() { with(this) {
|
664
|
+
I18n.translations["en"]["new_syntax"] = "Hi %{name}!";
|
665
|
+
actual = I18n.translate("new_syntax", {name: "John"});
|
666
|
+
assertEqual("Hi John!", actual);
|
667
|
+
}}
|
668
|
+
});
|