socialcast-i18n-js 4.0.0.rc1

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.
Files changed (66) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +2 -0
  4. data/README.md +324 -0
  5. data/README.rdoc +320 -0
  6. data/Rakefile +13 -0
  7. data/app/assets/javascripts/i18n/translations.js.erb +6 -0
  8. data/app/assets/javascripts/i18n.js +661 -0
  9. data/config/i18n-js.yml +22 -0
  10. data/i18n-js.gemspec +28 -0
  11. data/lib/i18n/js/engine.rb +6 -0
  12. data/lib/i18n/js/file_dependency_processor.rb +17 -0
  13. data/lib/i18n/js/railtie.rb +17 -0
  14. data/lib/i18n/js/translator.rb +56 -0
  15. data/lib/i18n/js/version.rb +10 -0
  16. data/lib/i18n-js/engine.rb +63 -0
  17. data/lib/i18n-js/middleware.rb +59 -0
  18. data/lib/i18n-js/railtie.rb +13 -0
  19. data/lib/i18n-js/rake.rb +16 -0
  20. data/lib/i18n-js/version.rb +10 -0
  21. data/lib/i18n-js.rb +22 -0
  22. data/lib/tasks/i18n-js.rake +17 -0
  23. data/spec/file_dependency_processor_spec.rb +32 -0
  24. data/spec/fixtures/custom_path.yml +2 -0
  25. data/spec/fixtures/default.yml +2 -0
  26. data/spec/fixtures/js_file_per_locale.yml +1 -0
  27. data/spec/fixtures/locales.yml +76 -0
  28. data/spec/fixtures/multiple_conditions.yml +3 -0
  29. data/spec/fixtures/no_config.yml +2 -0
  30. data/spec/fixtures/simple_scope.yml +2 -0
  31. data/spec/i18n_js_spec.rb +11 -0
  32. data/spec/i18n_spec.js +820 -0
  33. data/spec/i18n_spec.rb +205 -0
  34. data/spec/js/currency.spec.js +60 -0
  35. data/spec/js/current_locale.spec.js +19 -0
  36. data/spec/js/dates.spec.js +218 -0
  37. data/spec/js/defaults.spec.js +23 -0
  38. data/spec/js/interpolation.spec.js +28 -0
  39. data/spec/js/jasmine/MIT.LICENSE +20 -0
  40. data/spec/js/jasmine/jasmine-html.js +190 -0
  41. data/spec/js/jasmine/jasmine.css +166 -0
  42. data/spec/js/jasmine/jasmine.js +2476 -0
  43. data/spec/js/jasmine/jasmine_favicon.png +0 -0
  44. data/spec/js/localization.spec.js +41 -0
  45. data/spec/js/numbers.spec.js +124 -0
  46. data/spec/js/placeholder.spec.js +24 -0
  47. data/spec/js/pluralization.spec.js +105 -0
  48. data/spec/js/prepare_options.spec.js +41 -0
  49. data/spec/js/specs.html +46 -0
  50. data/spec/js/translate.spec.js +119 -0
  51. data/spec/js/translations.js +115 -0
  52. data/spec/resources/custom_path.yml +4 -0
  53. data/spec/resources/default.yml +4 -0
  54. data/spec/resources/js_file_per_locale.yml +3 -0
  55. data/spec/resources/locales.yml +76 -0
  56. data/spec/resources/multiple_conditions.yml +6 -0
  57. data/spec/resources/multiple_files.yml +6 -0
  58. data/spec/resources/no_config.yml +2 -0
  59. data/spec/resources/no_scope.yml +3 -0
  60. data/spec/resources/simple_scope.yml +4 -0
  61. data/spec/spec_helper.rb +40 -0
  62. data/spec/translator_spec.rb +45 -0
  63. data/vendor/assets/javascripts/i18n/translations.js.erb +9 -0
  64. data/vendor/assets/javascripts/i18n.js +531 -0
  65. data/vendor/assets/javascripts/underscore.js +1059 -0
  66. metadata +240 -0
data/spec/i18n_spec.js ADDED
@@ -0,0 +1,820 @@
1
+ load("vendor/assets/javascripts/i18n.js");
2
+
3
+ describe("I18n.js", function(){
4
+ before(function() {
5
+ I18n.defaultLocale = "en";
6
+ I18n.defaultSeparator = ".";
7
+ I18n.locale = null;
8
+
9
+ I18n.translations = {
10
+ en: {
11
+ hello: "Hello World!",
12
+ greetings: {
13
+ stranger: "Hello stranger!",
14
+ name: "Hello {{name}}!"
15
+ },
16
+ profile: {
17
+ details: "{{name}} is {{age}}-years old"
18
+ },
19
+ inbox: {
20
+ one: "You have {{count}} message",
21
+ other: "You have {{count}} messages",
22
+ zero: "You have no messages"
23
+ },
24
+ unread: {
25
+ one: "You have 1 new message ({{unread}} unread)",
26
+ other: "You have {{count}} new messages ({{unread}} unread)",
27
+ zero: "You have no new messages ({{unread}} unread)"
28
+ },
29
+ number: {
30
+ human: {
31
+ storage_units: {
32
+ units: {
33
+ "byte": {
34
+ one: "Byte",
35
+ other: "Bytes"
36
+ },
37
+
38
+ "kb": "KB",
39
+ "mb": "MB",
40
+ "gb": "GB",
41
+ "tb": "TB"
42
+ }
43
+ }
44
+ }
45
+ }
46
+ },
47
+
48
+ "pt-BR": {
49
+ hello: "Olá Mundo!",
50
+ date: {
51
+ formats: {
52
+ "default": "%d/%m/%Y",
53
+ "short": "%d de %B",
54
+ "long": "%d de %B de %Y"
55
+ },
56
+ day_names: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"],
57
+ abbr_day_names: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"],
58
+ month_names: [null, "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
59
+ abbr_month_names: [null, "Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"]
60
+ },
61
+ number: {
62
+ percentage: {
63
+ format: {
64
+ delimiter: "",
65
+ separator: ",",
66
+ precision: 2
67
+ }
68
+ }
69
+ },
70
+ time: {
71
+ formats: {
72
+ "default": "%A, %d de %B de %Y, %H:%M h",
73
+ "short": "%d/%m, %H:%M h",
74
+ "long": "%A, %d de %B de %Y, %H:%M h"
75
+ },
76
+ am: "AM",
77
+ pm: "PM"
78
+ }
79
+ },
80
+
81
+ "en-US": {
82
+ date: {
83
+ formats: {
84
+ "default": "%d/%m/%Y",
85
+ "short": "%d de %B",
86
+ "long": "%d de %B de %Y"
87
+ },
88
+ day_names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
89
+ abbr_day_names: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
90
+ month_names: [null, "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
91
+ abbr_month_names: [null, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"],
92
+ meridian: ["am", "pm"]
93
+ }
94
+ },
95
+
96
+ "de": {
97
+ hello: "Hallo Welt!"
98
+ },
99
+
100
+ "nb": {
101
+ hello: "Hei Verden!"
102
+ }
103
+ };
104
+ });
105
+
106
+ specify("with default options", function(){
107
+ expect(I18n.defaultLocale).toBeEqualTo("en");
108
+ expect(I18n.locale).toBeEqualTo(null);
109
+ expect(I18n.currentLocale()).toBeEqualTo("en");
110
+ });
111
+
112
+ specify("with custom locale", function(){
113
+ I18n.locale = "pt-BR";
114
+ expect(I18n.currentLocale()).toBeEqualTo("pt-BR");
115
+ });
116
+
117
+ specify("aliases", function(){
118
+ expect(I18n.t).toBe(I18n.translate);
119
+ expect(I18n.l).toBe(I18n.localize);
120
+ expect(I18n.p).toBe(I18n.pluralize);
121
+ });
122
+
123
+ specify("translation with single scope", function(){
124
+ expect(I18n.t("hello")).toBeEqualTo("Hello World!");
125
+ });
126
+
127
+ specify("translation as object", function(){
128
+ expect(I18n.t("greetings")).toBeInstanceOf(Object);
129
+ });
130
+
131
+ specify("translation with invalid scope shall not block", function(){
132
+ actual = I18n.t("invalid.scope.shall.not.block");
133
+ expected = '[missing "en.invalid.scope.shall.not.block" translation]';
134
+ expect(actual).toBeEqualTo(expected);
135
+ });
136
+
137
+ specify("translation for single scope on a custom locale", function(){
138
+ I18n.locale = "pt-BR";
139
+ expect(I18n.t("hello")).toBeEqualTo("Olá Mundo!");
140
+ });
141
+
142
+ specify("translation for multiple scopes", function(){
143
+ expect(I18n.t("greetings.stranger")).toBeEqualTo("Hello stranger!");
144
+ });
145
+
146
+ specify("translation with default locale option", function(){
147
+ expect(I18n.t("hello", {locale: "en"})).toBeEqualTo("Hello World!");
148
+ expect(I18n.t("hello", {locale: "pt-BR"})).toBeEqualTo("Olá Mundo!");
149
+ });
150
+
151
+ specify("translation should fall if locale is missing", function(){
152
+ I18n.locale = "pt-BR";
153
+ expect(I18n.t("greetings.stranger")).toBeEqualTo("[missing \"pt-BR.greetings.stranger\" translation]");
154
+ });
155
+
156
+ specify("translation should handle fallback if I18n.fallbacks == true", function(){
157
+ I18n.locale = "pt-BR";
158
+ I18n.fallbacks = true;
159
+ expect(I18n.t("greetings.stranger")).toBeEqualTo("Hello stranger!");
160
+ });
161
+
162
+ specify("translation should handle fallback from unknown locale", function(){
163
+ I18n.locale = "fr";
164
+ I18n.fallbacks = true;
165
+ expect(I18n.t("greetings.stranger")).toBeEqualTo("Hello stranger!");
166
+ });
167
+
168
+ specify("translation should handle fallback to less specific locale", function(){
169
+ I18n.locale = "de-DE";
170
+ I18n.fallbacks = true;
171
+ expect(I18n.t("hello")).toBeEqualTo("Hallo Welt!");
172
+ });
173
+
174
+ specify("translation should handle fallback via custom rules", function(){
175
+ I18n.locale = "no";
176
+ I18n.fallbacks = true;
177
+ I18n.fallbackRules.no = [ "nb" ];
178
+ expect(I18n.t("hello")).toBeEqualTo("Hei Verden!");
179
+ });
180
+
181
+ specify("single interpolation", function(){
182
+ actual = I18n.t("greetings.name", {name: "John Doe"});
183
+ expect(actual).toBeEqualTo("Hello John Doe!");
184
+ });
185
+
186
+ specify("multiple interpolation", function(){
187
+ actual = I18n.t("profile.details", {name: "John Doe", age: 27});
188
+ expect(actual).toBeEqualTo("John Doe is 27-years old");
189
+ });
190
+
191
+ specify("translation with count option", function(){
192
+ expect(I18n.t("inbox", {count: 0})).toBeEqualTo("You have no messages");
193
+ expect(I18n.t("inbox", {count: 1})).toBeEqualTo("You have 1 message");
194
+ expect(I18n.t("inbox", {count: 5})).toBeEqualTo("You have 5 messages");
195
+ });
196
+
197
+ specify("translation with count option and multiple placeholders", function(){
198
+ actual = I18n.t("unread", {unread: 5, count: 1});
199
+ expect(actual).toBeEqualTo("You have 1 new message (5 unread)");
200
+
201
+ actual = I18n.t("unread", {unread: 2, count: 10});
202
+ expect(actual).toBeEqualTo("You have 10 new messages (2 unread)");
203
+
204
+ actual = I18n.t("unread", {unread: 5, count: 0});
205
+ expect(actual).toBeEqualTo("You have no new messages (5 unread)");
206
+ });
207
+
208
+ specify("missing translation with count option", function(){
209
+ actual = I18n.t("invalid", {count: 1});
210
+ expect(actual).toBeEqualTo('[missing "en.invalid" translation]');
211
+
212
+ I18n.translations.en.inbox.one = null;
213
+ actual = I18n.t("inbox", {count: 1});
214
+ expect(actual).toBeEqualTo('[missing "en.inbox.one" translation]');
215
+ });
216
+
217
+ specify("pluralization", function(){
218
+ expect(I18n.p(0, "inbox")).toBeEqualTo("You have no messages");
219
+ expect(I18n.p(1, "inbox")).toBeEqualTo("You have 1 message");
220
+ expect(I18n.p(5, "inbox")).toBeEqualTo("You have 5 messages");
221
+ });
222
+
223
+ specify("pluralize should return 'other' scope", function(){
224
+ I18n.translations["en"]["inbox"]["zero"] = null;
225
+ expect(I18n.p(0, "inbox")).toBeEqualTo("You have 0 messages");
226
+ });
227
+
228
+ specify("pluralize should return 'zero' scope", function(){
229
+ I18n.translations["en"]["inbox"]["zero"] = "No messages (zero)";
230
+ I18n.translations["en"]["inbox"]["none"] = "No messages (none)";
231
+
232
+ expect(I18n.p(0, "inbox")).toBeEqualTo("No messages (zero)");
233
+ });
234
+
235
+ specify("pluralize should return 'none' scope", function(){
236
+ I18n.translations["en"]["inbox"]["zero"] = null;
237
+ I18n.translations["en"]["inbox"]["none"] = "No messages (none)";
238
+
239
+ expect(I18n.p(0, "inbox")).toBeEqualTo("No messages (none)");
240
+ });
241
+
242
+ specify("pluralize with negative values", function(){
243
+ expect(I18n.p(-1, "inbox")).toBeEqualTo("You have -1 message");
244
+ expect(I18n.p(-5, "inbox")).toBeEqualTo("You have -5 messages");
245
+ });
246
+
247
+ specify("pluralize with missing scope", function(){
248
+ expect(I18n.p(-1, "missing")).toBeEqualTo('[missing "en.missing" translation]');
249
+ });
250
+
251
+ specify("pluralize with multiple placeholders", function(){
252
+ actual = I18n.p(1, "unread", {unread: 5});
253
+ expect(actual).toBeEqualTo("You have 1 new message (5 unread)");
254
+
255
+ actual = I18n.p(10, "unread", {unread: 2});
256
+ expect(actual).toBeEqualTo("You have 10 new messages (2 unread)");
257
+
258
+ actual = I18n.p(0, "unread", {unread: 5});
259
+ expect(actual).toBeEqualTo("You have no new messages (5 unread)");
260
+ });
261
+
262
+ specify("pluralize should allow empty strings", function(){
263
+ I18n.translations["en"]["inbox"]["zero"] = "";
264
+
265
+ expect(I18n.p(0, "inbox")).toBeEqualTo("");
266
+ });
267
+
268
+ specify("numbers with default settings", function(){
269
+ expect(I18n.toNumber(1)).toBeEqualTo("1.000");
270
+ expect(I18n.toNumber(12)).toBeEqualTo("12.000");
271
+ expect(I18n.toNumber(123)).toBeEqualTo("123.000");
272
+ expect(I18n.toNumber(1234)).toBeEqualTo("1,234.000");
273
+ expect(I18n.toNumber(12345)).toBeEqualTo("12,345.000");
274
+ expect(I18n.toNumber(123456)).toBeEqualTo("123,456.000");
275
+ expect(I18n.toNumber(1234567)).toBeEqualTo("1,234,567.000");
276
+ expect(I18n.toNumber(12345678)).toBeEqualTo("12,345,678.000");
277
+ expect(I18n.toNumber(123456789)).toBeEqualTo("123,456,789.000");
278
+ });
279
+
280
+ specify("negative numbers with default settings", function(){
281
+ expect(I18n.toNumber(-1)).toBeEqualTo("-1.000");
282
+ expect(I18n.toNumber(-12)).toBeEqualTo("-12.000");
283
+ expect(I18n.toNumber(-123)).toBeEqualTo("-123.000");
284
+ expect(I18n.toNumber(-1234)).toBeEqualTo("-1,234.000");
285
+ expect(I18n.toNumber(-12345)).toBeEqualTo("-12,345.000");
286
+ expect(I18n.toNumber(-123456)).toBeEqualTo("-123,456.000");
287
+ expect(I18n.toNumber(-1234567)).toBeEqualTo("-1,234,567.000");
288
+ expect(I18n.toNumber(-12345678)).toBeEqualTo("-12,345,678.000");
289
+ expect(I18n.toNumber(-123456789)).toBeEqualTo("-123,456,789.000");
290
+ });
291
+
292
+ specify("numbers with partial translation and default options", function(){
293
+ I18n.translations.en.number = {
294
+ format: {
295
+ precision: 2
296
+ }
297
+ };
298
+
299
+ expect(I18n.toNumber(1234)).toBeEqualTo("1,234.00");
300
+ });
301
+
302
+ specify("numbers with full translation and default options", function(){
303
+ I18n.translations.en.number = {
304
+ format: {
305
+ delimiter: ".",
306
+ separator: ",",
307
+ precision: 2
308
+ }
309
+ };
310
+
311
+ expect(I18n.toNumber(1234)).toBeEqualTo("1.234,00");
312
+ });
313
+
314
+ specify("numbers with some custom options that should be merged with default options", function(){
315
+ expect(I18n.toNumber(1234, {precision: 0})).toBeEqualTo("1,234");
316
+ expect(I18n.toNumber(1234, {separator: '-'})).toBeEqualTo("1,234-000");
317
+ expect(I18n.toNumber(1234, {delimiter: '-'})).toBeEqualTo("1-234.000");
318
+ });
319
+
320
+ specify("numbers considering options", function(){
321
+ options = {
322
+ precision: 2,
323
+ separator: ",",
324
+ delimiter: "."
325
+ };
326
+
327
+ expect(I18n.toNumber(1, options)).toBeEqualTo("1,00");
328
+ expect(I18n.toNumber(12, options)).toBeEqualTo("12,00");
329
+ expect(I18n.toNumber(123, options)).toBeEqualTo("123,00");
330
+ expect(I18n.toNumber(1234, options)).toBeEqualTo("1.234,00");
331
+ expect(I18n.toNumber(123456, options)).toBeEqualTo("123.456,00");
332
+ expect(I18n.toNumber(1234567, options)).toBeEqualTo("1.234.567,00");
333
+ expect(I18n.toNumber(12345678, options)).toBeEqualTo("12.345.678,00");
334
+ });
335
+
336
+ specify("numbers with different precisions", function(){
337
+ options = {separator: ".", delimiter: ","};
338
+
339
+ options["precision"] = 2;
340
+ expect(I18n.toNumber(1.98, options)).toBeEqualTo("1.98");
341
+
342
+ options["precision"] = 3;
343
+ expect(I18n.toNumber(1.98, options)).toBeEqualTo("1.980");
344
+
345
+ options["precision"] = 2;
346
+ expect(I18n.toNumber(1.987, options)).toBeEqualTo("1.99");
347
+
348
+ options["precision"] = 1;
349
+ expect(I18n.toNumber(1.98, options)).toBeEqualTo("2.0");
350
+
351
+ options["precision"] = 0;
352
+ expect(I18n.toNumber(1.98, options)).toBeEqualTo("2");
353
+ });
354
+
355
+ specify("currency with default settings", function(){
356
+ expect(I18n.toCurrency(100.99)).toBeEqualTo("$100.99");
357
+ expect(I18n.toCurrency(1000.99)).toBeEqualTo("$1,000.99");
358
+ });
359
+
360
+ specify("currency with custom settings", function(){
361
+ I18n.translations.en.number = {
362
+ currency: {
363
+ format: {
364
+ format: "%n %u",
365
+ unit: "USD",
366
+ delimiter: ".",
367
+ separator: ",",
368
+ precision: 2
369
+ }
370
+ }
371
+ };
372
+
373
+ expect(I18n.toCurrency(12)).toBeEqualTo("12,00 USD");
374
+ expect(I18n.toCurrency(123)).toBeEqualTo("123,00 USD");
375
+ expect(I18n.toCurrency(1234.56)).toBeEqualTo("1.234,56 USD");
376
+ });
377
+
378
+ specify("currency with custom settings and partial overriding", function(){
379
+ I18n.translations.en.number = {
380
+ currency: {
381
+ format: {
382
+ format: "%n %u",
383
+ unit: "USD",
384
+ delimiter: ".",
385
+ separator: ",",
386
+ precision: 2
387
+ }
388
+ }
389
+ };
390
+
391
+ expect(I18n.toCurrency(12, {precision: 0})).toBeEqualTo("12 USD");
392
+ expect(I18n.toCurrency(123, {unit: "bucks"})).toBeEqualTo("123,00 bucks");
393
+ });
394
+
395
+ specify("currency with some custom options that should be merged with default options", function(){
396
+ expect(I18n.toCurrency(1234, {precision: 0})).toBeEqualTo("$1,234");
397
+ expect(I18n.toCurrency(1234, {unit: "º"})).toBeEqualTo("º1,234.00");
398
+ expect(I18n.toCurrency(1234, {separator: "-"})).toBeEqualTo("$1,234-00");
399
+ expect(I18n.toCurrency(1234, {delimiter: "-"})).toBeEqualTo("$1-234.00");
400
+ expect(I18n.toCurrency(1234, {format: "%u %n"})).toBeEqualTo("$ 1,234.00");
401
+ });
402
+
403
+ specify("localize numbers", function(){
404
+ expect(I18n.l("number", 1234567)).toBeEqualTo("1,234,567.000");
405
+ });
406
+
407
+ specify("localize currency", function(){
408
+ expect(I18n.l("currency", 1234567)).toBeEqualTo("$1,234,567.00");
409
+ });
410
+
411
+ specify("parse date", function(){
412
+ expected = new Date(2009, 0, 24, 0, 0, 0);
413
+ actual = I18n.parseDate("2009-01-24");
414
+ expect(actual.toString()).toBeEqualTo(expected.toString());
415
+
416
+ expected = new Date(2009, 0, 24, 0, 15, 0);
417
+ actual = I18n.parseDate("2009-01-24 00:15:00");
418
+ expect(actual.toString()).toBeEqualTo(expected.toString());
419
+
420
+ expected = new Date(2009, 0, 24, 0, 0, 15);
421
+ actual = I18n.parseDate("2009-01-24 00:00:15");
422
+ expect(actual.toString()).toBeEqualTo(expected.toString());
423
+
424
+ expected = new Date(2009, 0, 24, 15, 33, 44);
425
+ actual = I18n.parseDate("2009-01-24 15:33:44");
426
+ expect(actual.toString()).toBeEqualTo(expected.toString());
427
+
428
+ expected = new Date(2009, 0, 24, 0, 0, 0);
429
+ actual = I18n.parseDate(expected.getTime());
430
+ expect(actual.toString()).toBeEqualTo(expected.toString());
431
+
432
+ expected = new Date(2009, 0, 24, 0, 0, 0);
433
+ actual = I18n.parseDate("01/24/2009");
434
+ expect(actual.toString()).toBeEqualTo(expected.toString());
435
+
436
+ expected = new Date(2009, 0, 24, 14, 33, 55);
437
+ actual = I18n.parseDate(expected).toString();
438
+ expect(actual).toBeEqualTo(expected.toString());
439
+
440
+ expected = new Date(2009, 0, 24, 15, 33, 44);
441
+ actual = I18n.parseDate("2009-01-24T15:33:44");
442
+ expect(actual.toString()).toBeEqualTo(expected.toString());
443
+
444
+ expected = new Date(Date.UTC(2011, 6, 20, 12, 51, 55));
445
+ actual = I18n.parseDate("2011-07-20T12:51:55+0000");
446
+ expect(actual.toString()).toBeEqualTo(expected.toString());
447
+
448
+ expected = new Date(Date.UTC(2011, 6, 20, 13, 03, 39));
449
+ actual = I18n.parseDate("Wed Jul 20 13:03:39 +0000 2011");
450
+ expect(actual.toString()).toBeEqualTo(expected.toString());
451
+
452
+ expected = new Date(Date.UTC(2009, 0, 24, 15, 33, 44));
453
+ actual = I18n.parseDate("2009-01-24T15:33:44Z");
454
+ expect(actual.toString()).toBeEqualTo(expected.toString());
455
+ });
456
+
457
+ specify("date formatting", function(){
458
+ I18n.locale = "pt-BR";
459
+
460
+ // 2009-04-26 19:35:44 (Sunday)
461
+ var date = new Date(2009, 3, 26, 19, 35, 44);
462
+
463
+ // short week day
464
+ expect(I18n.strftime(date, "%a")).toBeEqualTo("Dom");
465
+
466
+ // full week day
467
+ expect(I18n.strftime(date, "%A")).toBeEqualTo("Domingo");
468
+
469
+ // short month
470
+ expect(I18n.strftime(date, "%b")).toBeEqualTo("Abr");
471
+
472
+ // full month
473
+ expect(I18n.strftime(date, "%B")).toBeEqualTo("Abril");
474
+
475
+ // day
476
+ expect(I18n.strftime(date, "%d")).toBeEqualTo("26");
477
+
478
+ // 24-hour
479
+ expect(I18n.strftime(date, "%H")).toBeEqualTo("19");
480
+
481
+ // 12-hour
482
+ expect(I18n.strftime(date, "%I")).toBeEqualTo("07");
483
+
484
+ // month
485
+ expect(I18n.strftime(date, "%m")).toBeEqualTo("04");
486
+
487
+ // minutes
488
+ expect(I18n.strftime(date, "%M")).toBeEqualTo("35");
489
+
490
+ // meridian
491
+ expect(I18n.strftime(date, "%p")).toBeEqualTo("PM");
492
+
493
+ // seconds
494
+ expect(I18n.strftime(date, "%S")).toBeEqualTo("44");
495
+
496
+ // week day
497
+ expect(I18n.strftime(date, "%w")).toBeEqualTo("0");
498
+
499
+ // short year
500
+ expect(I18n.strftime(date, "%y")).toBeEqualTo("09");
501
+
502
+ // full year
503
+ expect(I18n.strftime(date, "%Y")).toBeEqualTo("2009");
504
+ });
505
+
506
+ specify("date formatting without padding", function(){
507
+ I18n.locale = "pt-BR";
508
+
509
+ // 2009-04-26 19:35:44 (Sunday)
510
+ var date = new Date(2009, 3, 9, 7, 8, 9);
511
+
512
+ // 24-hour without padding
513
+ expect(I18n.strftime(date, "%-H")).toBeEqualTo("7");
514
+
515
+ // 12-hour without padding
516
+ expect(I18n.strftime(date, "%-I")).toBeEqualTo("7");
517
+
518
+ // minutes without padding
519
+ expect(I18n.strftime(date, "%-M")).toBeEqualTo("8");
520
+
521
+ // seconds without padding
522
+ expect(I18n.strftime(date, "%-S")).toBeEqualTo("9");
523
+
524
+ // short year without padding
525
+ expect(I18n.strftime(date, "%-y")).toBeEqualTo("9");
526
+
527
+ // month without padding
528
+ expect(I18n.strftime(date, "%-m")).toBeEqualTo("4");
529
+
530
+ // day without padding
531
+ expect(I18n.strftime(date, "%-d")).toBeEqualTo("9");
532
+ expect(I18n.strftime(date, "%e")).toBeEqualTo("9");
533
+ });
534
+
535
+ specify("date formatting with padding", function(){
536
+ I18n.locale = "pt-BR";
537
+
538
+ // 2009-04-26 19:35:44 (Sunday)
539
+ var date = new Date(2009, 3, 9, 7, 8, 9);
540
+
541
+ // 24-hour
542
+ expect(I18n.strftime(date, "%H")).toBeEqualTo("07");
543
+
544
+ // 12-hour
545
+ expect(I18n.strftime(date, "%I")).toBeEqualTo("07");
546
+
547
+ // minutes
548
+ expect(I18n.strftime(date, "%M")).toBeEqualTo("08");
549
+
550
+ // seconds
551
+ expect(I18n.strftime(date, "%S")).toBeEqualTo("09");
552
+
553
+ // short year
554
+ expect(I18n.strftime(date, "%y")).toBeEqualTo("09");
555
+
556
+ // month
557
+ expect(I18n.strftime(date, "%m")).toBeEqualTo("04");
558
+
559
+ // day
560
+ expect(I18n.strftime(date, "%d")).toBeEqualTo("09");
561
+ });
562
+
563
+ specify("date formatting with negative time zone", function(){
564
+ I18n.locale = "pt-BR";
565
+ var date = new Date(2009, 3, 26, 19, 35, 44);
566
+ stub(date, "getTimezoneOffset()", 345);
567
+
568
+ expect(I18n.strftime(date, "%z")).toMatch(/^(\+|-)[\d]{4}$/);
569
+ expect(I18n.strftime(date, "%z")).toBeEqualTo("-0545");
570
+ });
571
+
572
+ specify("date formatting with positive time zone", function(){
573
+ I18n.locale = "pt-BR";
574
+ var date = new Date(2009, 3, 26, 19, 35, 44);
575
+ stub(date, "getTimezoneOffset()", -345);
576
+
577
+ expect(I18n.strftime(date, "%z")).toMatch(/^(\+|-)[\d]{4}$/);
578
+ expect(I18n.strftime(date, "%z")).toBeEqualTo("+0545");
579
+ });
580
+
581
+ specify("date formatting with custom meridian", function(){
582
+ I18n.locale = "en-US";
583
+ var date = new Date(2009, 3, 26, 19, 35, 44);
584
+ expect(I18n.strftime(date, "%p")).toBeEqualTo("pm");
585
+ });
586
+
587
+ specify("date formatting meridian boundaries", function(){
588
+ I18n.locale = "en-US";
589
+ var date = new Date(2009, 3, 26, 0, 35, 44);
590
+ expect(I18n.strftime(date, "%p")).toBeEqualTo("am");
591
+
592
+ date = new Date(2009, 3, 26, 12, 35, 44);
593
+ expect(I18n.strftime(date, "%p")).toBeEqualTo("pm");
594
+ });
595
+
596
+ specify("date formatting hour12 values", function(){
597
+ I18n.locale = "pt-BR";
598
+ var date = new Date(2009, 3, 26, 19, 35, 44);
599
+ expect(I18n.strftime(date, "%I")).toBeEqualTo("07");
600
+
601
+ date = new Date(2009, 3, 26, 12, 35, 44);
602
+ expect(I18n.strftime(date, "%I")).toBeEqualTo("12");
603
+
604
+ date = new Date(2009, 3, 26, 0, 35, 44);
605
+ expect(I18n.strftime(date, "%I")).toBeEqualTo("12");
606
+ });
607
+
608
+ specify("localize date strings", function(){
609
+ I18n.locale = "pt-BR";
610
+
611
+ expect(I18n.l("date.formats.default", "2009-11-29")).toBeEqualTo("29/11/2009");
612
+ expect(I18n.l("date.formats.short", "2009-01-07")).toBeEqualTo("07 de Janeiro");
613
+ expect(I18n.l("date.formats.long", "2009-01-07")).toBeEqualTo("07 de Janeiro de 2009");
614
+ });
615
+
616
+ specify("localize time strings", function(){
617
+ I18n.locale = "pt-BR";
618
+
619
+ expect(I18n.l("time.formats.default", "2009-11-29 15:07:59")).toBeEqualTo("Domingo, 29 de Novembro de 2009, 15:07 h");
620
+ expect(I18n.l("time.formats.short", "2009-01-07 09:12:35")).toBeEqualTo("07/01, 09:12 h");
621
+ expect(I18n.l("time.formats.long", "2009-11-29 15:07:59")).toBeEqualTo("Domingo, 29 de Novembro de 2009, 15:07 h");
622
+ });
623
+
624
+ specify("localize percentage", function(){
625
+ I18n.locale = "pt-BR";
626
+ expect(I18n.l("percentage", 123.45)).toBeEqualTo("123,45%");
627
+ });
628
+
629
+ specify("default value for simple translation", function(){
630
+ actual = I18n.t("warning", {defaultValue: "Warning!"});
631
+ expect(actual).toBeEqualTo("Warning!");
632
+ });
633
+
634
+ specify("default value for unknown locale", function(){
635
+ I18n.locale = "fr";
636
+ actual = I18n.t("warning", {defaultValue: "Warning!"});
637
+ expect(actual).toBeEqualTo("Warning!");
638
+ });
639
+
640
+ specify("default value with interpolation", function(){
641
+ actual = I18n.t(
642
+ "alert",
643
+ {defaultValue: "Attention! {{message}}", message: "You're out of quota!"}
644
+ );
645
+
646
+ expect(actual).toBeEqualTo("Attention! You're out of quota!");
647
+ });
648
+
649
+ specify("default value should not be used when scope exist", function(){
650
+ actual = I18n.t("hello", {defaultValue: "What's up?"});
651
+ expect(actual).toBeEqualTo("Hello World!");
652
+ });
653
+
654
+ specify("default value for pluralize", function(){
655
+ options = {defaultValue: {
656
+ none: "No things here!",
657
+ one: "There is {{count}} thing here!",
658
+ other: "There are {{count}} things here!"
659
+ }};
660
+
661
+ expect(I18n.p(0, "things", options)).toBeEqualTo("No things here!");
662
+ expect(I18n.p(1, "things", options)).toBeEqualTo("There is 1 thing here!");
663
+ expect(I18n.p(5, "things", options)).toBeEqualTo("There are 5 things here!");
664
+ });
665
+
666
+ specify("default value for pluralize should not be used when scope exist", function(){
667
+ options = {defaultValue: {
668
+ none: "No things here!",
669
+ one: "There is {{count}} thing here!",
670
+ other: "There are {{count}} things here!"
671
+ }};
672
+
673
+ expect(I18n.pluralize(0, "inbox", options)).toBeEqualTo("You have no messages");
674
+ expect(I18n.pluralize(1, "inbox", options)).toBeEqualTo("You have 1 message");
675
+ expect(I18n.pluralize(5, "inbox", options)).toBeEqualTo("You have 5 messages");
676
+ });
677
+
678
+ specify("prepare options", function(){
679
+ options = I18n.prepareOptions(
680
+ {name: "Mary Doe"},
681
+ {name: "John Doe", role: "user"}
682
+ );
683
+
684
+ expect(options["name"]).toBeEqualTo("Mary Doe");
685
+ expect(options["role"]).toBeEqualTo("user");
686
+ });
687
+
688
+ specify("prepare options with multiple options", function(){
689
+ options = I18n.prepareOptions(
690
+ {name: "Mary Doe"},
691
+ {name: "John Doe", role: "user"},
692
+ {age: 33},
693
+ {email: "mary@doe.com", url: "http://marydoe.com"},
694
+ {role: "admin", email: "john@doe.com"}
695
+ );
696
+
697
+ expect(options["name"]).toBeEqualTo("Mary Doe");
698
+ expect(options["role"]).toBeEqualTo("user");
699
+ expect(options["age"]).toBeEqualTo(33);
700
+ expect(options["email"]).toBeEqualTo("mary@doe.com");
701
+ expect(options["url"]).toBeEqualTo("http://marydoe.com");
702
+ });
703
+
704
+ specify("prepare options should return an empty hash when values are null", function(){
705
+ expect({}).toBeEqualTo(I18n.prepareOptions(null, null));
706
+ });
707
+
708
+ specify("percentage with defaults", function(){
709
+ expect(I18n.toPercentage(1234)).toBeEqualTo("1234.000%");
710
+ });
711
+
712
+ specify("percentage with custom options", function(){
713
+ actual = I18n.toPercentage(1234, {delimiter: "_", precision: 0});
714
+ expect(actual).toBeEqualTo("1_234%");
715
+ });
716
+
717
+ specify("percentage with translation", function(){
718
+ I18n.translations.en.number = {
719
+ percentage: {
720
+ format: {
721
+ precision: 2,
722
+ delimiter: ".",
723
+ separator: ","
724
+ }
725
+ }
726
+ };
727
+
728
+ expect(I18n.toPercentage(1234)).toBeEqualTo("1.234,00%");
729
+ });
730
+
731
+ specify("percentage with translation and custom options", function(){
732
+ I18n.translations.en.number = {
733
+ percentage: {
734
+ format: {
735
+ precision: 2,
736
+ delimiter: ".",
737
+ separator: ","
738
+ }
739
+ }
740
+ };
741
+
742
+ actual = I18n.toPercentage(1234, {precision: 4, delimiter: "-", separator: "+"});
743
+ expect(actual).toBeEqualTo("1-234+0000%");
744
+ });
745
+
746
+ specify("scope option as string", function(){
747
+ actual = I18n.t("stranger", {scope: "greetings"});
748
+ expect(actual).toBeEqualTo("Hello stranger!");
749
+ });
750
+
751
+ specify("scope as array", function(){
752
+ actual = I18n.t(["greetings", "stranger"]);
753
+ expect(actual).toBeEqualTo("Hello stranger!");
754
+ });
755
+
756
+ specify("new placeholder syntax", function(){
757
+ I18n.translations["en"]["new_syntax"] = "Hi %{name}!";
758
+ actual = I18n.t("new_syntax", {name: "John"});
759
+ expect(actual).toBeEqualTo("Hi John!");
760
+ });
761
+
762
+ specify("return translation for custom scope separator", function(){
763
+ I18n.defaultSeparator = "•";
764
+ actual = I18n.t("greetings•stranger");
765
+ expect(actual).toBeEqualTo("Hello stranger!");
766
+ });
767
+
768
+ specify("return number as human size", function(){
769
+ kb = 1024;
770
+
771
+ expect(I18n.toHumanSize(1)).toBeEqualTo("1Byte");
772
+ expect(I18n.toHumanSize(100)).toBeEqualTo("100Bytes");
773
+
774
+ expect(I18n.toHumanSize(kb)).toBeEqualTo("1KB");
775
+ expect(I18n.toHumanSize(kb * 1.5)).toBeEqualTo("1.5KB");
776
+
777
+ expect(I18n.toHumanSize(kb * kb)).toBeEqualTo("1MB");
778
+ expect(I18n.toHumanSize(kb * kb * 1.5)).toBeEqualTo("1.5MB");
779
+
780
+ expect(I18n.toHumanSize(kb * kb * kb)).toBeEqualTo("1GB");
781
+ expect(I18n.toHumanSize(kb * kb * kb * 1.5)).toBeEqualTo("1.5GB");
782
+
783
+ expect(I18n.toHumanSize(kb * kb * kb * kb)).toBeEqualTo("1TB");
784
+ expect(I18n.toHumanSize(kb * kb * kb * kb * 1.5)).toBeEqualTo("1.5TB");
785
+
786
+ expect(I18n.toHumanSize(kb * kb * kb * kb * kb)).toBeEqualTo("1024TB");
787
+ });
788
+
789
+ specify("return number as human size using custom options", function(){
790
+ expect(I18n.toHumanSize(1024 * 1.6, {precision: 0})).toBeEqualTo("2KB");
791
+ });
792
+
793
+ specify("return number without insignificant zeros", function(){
794
+ options = {precision: 4, strip_insignificant_zeros: true};
795
+
796
+ expect(I18n.toNumber(65, options)).toBeEqualTo("65");
797
+ expect(I18n.toNumber(1.2, options)).toBeEqualTo("1.2");
798
+ expect(I18n.toCurrency(1.2, options)).toBeEqualTo("$1.2");
799
+ expect(I18n.toHumanSize(1.2, options)).toBeEqualTo("1.2Bytes");
800
+ });
801
+
802
+ specify("return plural key for given count for english locale", function(){
803
+ expect(I18n.pluralizationRules.en(0)).toBeEqualTo(["zero", "none", "other"]);
804
+ expect(I18n.pluralizationRules.en(1)).toBeEqualTo("one");
805
+ expect(I18n.pluralizationRules.en(2)).toBeEqualTo("other");
806
+ });
807
+
808
+ specify("return given pluralizer", function(){
809
+ I18n.pluralizationRules.en = "en";
810
+ expect(I18n.pluralizer("ru")).toBeEqualTo("en");
811
+ I18n.pluralizationRules.ru = "ru";
812
+ expect(I18n.pluralizer("ru")).toBeEqualTo("ru");
813
+ });
814
+
815
+ specify("find and translate valid node", function(){
816
+ expect(I18n.findAndTranslateValidNode(["one", "other"], {one: "one", other: "other"})).toBeEqualTo("one");
817
+ expect(I18n.findAndTranslateValidNode(["one", "other"], {other: "other"})).toBeEqualTo("other");
818
+ expect(I18n.findAndTranslateValidNode(["one"], {})).toBeEqualTo(null);
819
+ });
820
+ });