i18n-js-pika 3.0.0.rc6

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 (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +13 -0
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +52 -0
  6. data/README.md +326 -0
  7. data/Rakefile +13 -0
  8. data/app/assets/javascripts/i18n/filtered.js.erb +2 -0
  9. data/app/assets/javascripts/i18n/shims.js +93 -0
  10. data/app/assets/javascripts/i18n/translations.js +3 -0
  11. data/app/assets/javascripts/i18n.js +690 -0
  12. data/i18n-js.gemspec +27 -0
  13. data/lib/i18n/js/engine.rb +22 -0
  14. data/lib/i18n/js/middleware.rb +59 -0
  15. data/lib/i18n/js/version.rb +12 -0
  16. data/lib/i18n/js.rb +162 -0
  17. data/lib/i18n-js.rb +1 -0
  18. data/lib/tasks/export.rake +8 -0
  19. data/package.json +11 -0
  20. data/spec/fixtures/custom_path.yml +4 -0
  21. data/spec/fixtures/default.yml +4 -0
  22. data/spec/fixtures/js_file_per_locale.yml +3 -0
  23. data/spec/fixtures/locales.yml +76 -0
  24. data/spec/fixtures/multiple_conditions.yml +5 -0
  25. data/spec/fixtures/multiple_files.yml +6 -0
  26. data/spec/fixtures/no_config.yml +2 -0
  27. data/spec/fixtures/no_scope.yml +3 -0
  28. data/spec/fixtures/simple_scope.yml +4 -0
  29. data/spec/i18n_js_spec.rb +139 -0
  30. data/spec/js/currency.spec.js +60 -0
  31. data/spec/js/current_locale.spec.js +19 -0
  32. data/spec/js/dates.spec.js +222 -0
  33. data/spec/js/defaults.spec.js +23 -0
  34. data/spec/js/interpolation.spec.js +28 -0
  35. data/spec/js/jasmine/MIT.LICENSE +20 -0
  36. data/spec/js/jasmine/jasmine-html.js +190 -0
  37. data/spec/js/jasmine/jasmine.css +166 -0
  38. data/spec/js/jasmine/jasmine.js +2476 -0
  39. data/spec/js/jasmine/jasmine_favicon.png +0 -0
  40. data/spec/js/localization.spec.js +41 -0
  41. data/spec/js/numbers.spec.js +142 -0
  42. data/spec/js/placeholder.spec.js +24 -0
  43. data/spec/js/pluralization.spec.js +105 -0
  44. data/spec/js/prepare_options.spec.js +41 -0
  45. data/spec/js/specs.html +46 -0
  46. data/spec/js/translate.spec.js +120 -0
  47. data/spec/js/translations.js +120 -0
  48. data/spec/spec_helper.rb +41 -0
  49. metadata +196 -0
@@ -0,0 +1,690 @@
1
+ // I18n.js
2
+ // =======
3
+ //
4
+ // This small library provides the Rails I18n API on the Javascript.
5
+ // You don't actually have to use Rails (or even Ruby) to use I18n.js.
6
+ // Just make sure you export all translations in an object like this:
7
+ //
8
+ // I18n.translations.en = {
9
+ // hello: "Hello World"
10
+ // };
11
+ //
12
+ // See tests for specific formatting like numbers and dates.
13
+ //
14
+ ;(function(I18n){
15
+ "use strict";
16
+
17
+ // Just cache the Array#slice function.
18
+ var slice = Array.prototype.slice;
19
+
20
+ // Apply number padding.
21
+ var padding = function(number) {
22
+ return ("0" + number.toString()).substr(-2);
23
+ };
24
+
25
+ // Set default days/months translations.
26
+ var DAYS_AND_MONTHS = {
27
+ day_names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
28
+ , abbr_day_names: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
29
+ , month_names: [null, "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
30
+ , abbr_month_names: [null, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
31
+ };
32
+
33
+ // Set default number format.
34
+ var NUMBER_FORMAT = {
35
+ precision: 3
36
+ , separator: "."
37
+ , delimiter: ","
38
+ , strip_insignificant_zeros: false
39
+ };
40
+
41
+ // Set default currency format.
42
+ var CURRENCY_FORMAT = {
43
+ unit: "$"
44
+ , precision: 2
45
+ , format: "%u%n"
46
+ , delimiter: ","
47
+ , separator: "."
48
+ };
49
+
50
+ // Set default percentage format.
51
+ var PERCENTAGE_FORMAT = {
52
+ precision: 3
53
+ , separator: "."
54
+ , delimiter: ""
55
+ };
56
+
57
+ // Set default size units.
58
+ var SIZE_UNITS = [null, "kb", "mb", "gb", "tb"];
59
+
60
+ // Set meridian.
61
+ var MERIDIAN = ["AM", "PM"];
62
+
63
+ I18n.reset = function() {
64
+ // Set default locale. This locale will be used when fallback is enabled and
65
+ // the translation doesn't exist in a particular locale.
66
+ this.defaultLocale = "en";
67
+
68
+ // Set the current locale to `en`.
69
+ this.locale = "en";
70
+
71
+ // Set the translation key separator.
72
+ this.defaultSeparator = ".";
73
+
74
+ // Set the placeholder format. Accepts `{{placeholder}}` and `%{placeholder}`.
75
+ this.placeholder = /(?:\{\{|%\{)(.*?)(?:\}\}?)/gm;
76
+
77
+ // Set if engine should fallback to the default locale when a translation
78
+ // is missing.
79
+ this.fallbacks = false;
80
+
81
+ // Set the default translation object.
82
+ this.translations = {};
83
+ };
84
+
85
+ // Return a list of all locales that must be tried before returning the
86
+ // missing translation message. By default, this will consider the inline option,
87
+ // current locale and fallback locale.
88
+ //
89
+ // I18n.locales.get("de-DE");
90
+ // // ["de-DE", "de", "en"]
91
+ //
92
+ // You can define custom rules for any locale. Just make sure you return a array
93
+ // containing all locales.
94
+ //
95
+ // // Default the Wookie locale to English.
96
+ // I18n.locales["wk"] = function(locale) {
97
+ // return ["en"];
98
+ // };
99
+ //
100
+ I18n.locales = {};
101
+
102
+ // Retrieve locales based on inline locale, current locale or default to
103
+ // I18n's detection.
104
+ I18n.locales.get = function(locale) {
105
+ var result = this[locale] || this[I18n.locale] || this["default"];
106
+
107
+ if (typeof(result) === "function") {
108
+ result = result(locale);
109
+ }
110
+
111
+ if (result instanceof Array === false) {
112
+ result = [result];
113
+ }
114
+
115
+ return result;
116
+ };
117
+
118
+ // The default locale list.
119
+ I18n.locales["default"] = function(locale) {
120
+ var locales = []
121
+ , list = []
122
+ , countryCode
123
+ , count
124
+ ;
125
+
126
+ // Handle the inline locale option that can be provided to
127
+ // the `I18n.t` options.
128
+ if (locale) {
129
+ locales.push(locale);
130
+ }
131
+
132
+ // Add the current locale to the list.
133
+ if (!locale && I18n.locale) {
134
+ locales.push(I18n.locale);
135
+ }
136
+
137
+ // Add the default locale if fallback strategy is enabled.
138
+ if (I18n.fallbacks && I18n.defaultLocale) {
139
+ locales.push(I18n.defaultLocale);
140
+ }
141
+
142
+ // Compute each locale with its country code.
143
+ // So this will return an array containing both
144
+ // `de-DE` and `de` locales.
145
+ locales.forEach(function(locale){
146
+ countryCode = locale.split("-")[0];
147
+
148
+ if (!~list.indexOf(locale)) {
149
+ list.push(locale);
150
+ }
151
+
152
+ if (I18n.fallbacks && countryCode && countryCode !== locale && !~list.indexOf(countryCode)) {
153
+ list.push(countryCode);
154
+ }
155
+ });
156
+
157
+ // No locales set? English it is.
158
+ if (!locales.length) {
159
+ locales.push("en");
160
+ }
161
+
162
+ return list;
163
+ };
164
+
165
+ // Hold pluralization rules.
166
+ I18n.pluralization = {};
167
+
168
+ // Return the pluralizer for a specific locale.
169
+ // If no specify locale is found, then I18n's default will be used.
170
+ I18n.pluralization.get = function(locale) {
171
+ return this[locale] || this[I18n.locale] || this["default"];
172
+ };
173
+
174
+ // The default pluralizer rule.
175
+ // It detects the `zero`, `one`, and `other` scopes.
176
+ I18n.pluralization["default"] = function(count) {
177
+ switch (count) {
178
+ case 0: return ["zero", "other"];
179
+ case 1: return ["one"];
180
+ default: return ["other"];
181
+ }
182
+ };
183
+
184
+ // Reset all default attributes. This is specially useful
185
+ // while running tests.
186
+ I18n.reset();
187
+
188
+ // Return current locale. If no locale has been set, then
189
+ // the current locale will be the default locale.
190
+ I18n.currentLocale = function() {
191
+ return this.locale || this.defaultLocale;
192
+ };
193
+
194
+ // Check if value is different than undefined and null;
195
+ I18n.isSet = function(value) {
196
+ return value !== undefined && value !== null;
197
+ };
198
+
199
+ // Find and process the translation using the provided scope and options.
200
+ // This is used internally by some functions and should not be used as an
201
+ // public API.
202
+ I18n.lookup = function(scope, options) {
203
+ options = this.prepareOptions(options);
204
+
205
+ var locales = this.locales.get(options.locale)
206
+ , requestedLocale = locales[0]
207
+ , locale
208
+ , scopes
209
+ , translations
210
+ ;
211
+
212
+ while (locales.length) {
213
+ locale = locales.shift();
214
+ scopes = scope.split(this.defaultSeparator);
215
+ translations = this.translations[locale];
216
+
217
+ if (!translations) {
218
+ continue;
219
+ }
220
+
221
+ while (scopes.length) {
222
+ translations = translations[scopes.shift()];
223
+
224
+ if (translations === undefined || translations === null) {
225
+ break;
226
+ }
227
+ }
228
+
229
+ if (translations !== undefined && translations !== null) {
230
+ return translations;
231
+ }
232
+ }
233
+
234
+ if (this.isSet(options.defaultValue)) {
235
+ return options.defaultValue;
236
+ }
237
+ };
238
+
239
+ // Merge serveral hash options, checking if value is set before
240
+ // overwriting any value. The precedence is from left to right.
241
+ //
242
+ // I18n.prepareOptions({name: "John Doe"}, {name: "Mary Doe", role: "user"});
243
+ // #=> {name: "John Doe", role: "user"}
244
+ //
245
+ I18n.prepareOptions = function() {
246
+ var args = slice.call(arguments)
247
+ , options = {}
248
+ , subject
249
+ ;
250
+
251
+ while (args.length) {
252
+ subject = args.shift();
253
+
254
+ if (typeof(subject) != "object") {
255
+ continue;
256
+ }
257
+
258
+ for (var attr in subject) {
259
+ if (!subject.hasOwnProperty(attr)) {
260
+ continue;
261
+ }
262
+
263
+ if (this.isSet(options[attr])) {
264
+ continue;
265
+ }
266
+
267
+ options[attr] = subject[attr];
268
+ }
269
+ }
270
+
271
+ return options;
272
+ };
273
+
274
+ // Translate the given scope with the provided options.
275
+ I18n.translate = function(scope, options) {
276
+ options = this.prepareOptions(options);
277
+ var translation = this.lookup(scope, options);
278
+
279
+ if (translation === undefined || translation === null) {
280
+ return this.missingTranslation(scope);
281
+ }
282
+
283
+ if (typeof(translation) === "string") {
284
+ translation = this.interpolate(translation, options);
285
+ } else if (translation instanceof Object && this.isSet(options.count)) {
286
+ translation = this.pluralize(options.count, translation, options);
287
+ }
288
+
289
+ return translation;
290
+ };
291
+
292
+ // This function interpolates the all variables in the given message.
293
+ I18n.interpolate = function(message, options) {
294
+ options = this.prepareOptions(options);
295
+ var matches = message.match(this.placeholder)
296
+ , placeholder
297
+ , value
298
+ , name
299
+ , regex
300
+ ;
301
+
302
+ if (!matches) {
303
+ return message;
304
+ }
305
+
306
+ while (matches.length) {
307
+ placeholder = matches.shift();
308
+ name = placeholder.replace(this.placeholder, "$1");
309
+ value = options[name];
310
+
311
+ if (!this.isSet(options[name])) {
312
+ value = this.missingPlaceholder(placeholder, message);
313
+ }
314
+
315
+ regex = new RegExp(placeholder.replace(/\{/gm, "\\{").replace(/\}/gm, "\\}"));
316
+ message = message.replace(regex, value);
317
+ }
318
+
319
+ return message;
320
+ };
321
+
322
+ // Pluralize the given scope using the `count` value.
323
+ // The pluralized translation may have other placeholders,
324
+ // which will be retrieved from `options`.
325
+ I18n.pluralize = function(count, scope, options) {
326
+ options = this.prepareOptions(options);
327
+ var translations, pluralizer, keys, key, message;
328
+
329
+ if (scope instanceof Object) {
330
+ translations = scope;
331
+ } else {
332
+ translations = this.lookup(scope, options);
333
+ }
334
+
335
+ if (!translations) {
336
+ return this.missingTranslation(scope);
337
+ }
338
+
339
+ pluralizer = this.pluralization.get(options.locale);
340
+ keys = pluralizer(Math.abs(count));
341
+
342
+ while (keys.length) {
343
+ key = keys.shift();
344
+
345
+ if (this.isSet(translations[key])) {
346
+ message = translations[key];
347
+ break;
348
+ }
349
+ }
350
+
351
+ options.count = String(count);
352
+ return this.interpolate(message, options);
353
+ };
354
+
355
+ // Return a missing translation message for the given parameters.
356
+ I18n.missingTranslation = function(scope) {
357
+ var message = '[missing "';
358
+
359
+ message += this.currentLocale() + ".";
360
+ message += slice.call(arguments).join(".");
361
+ message += '" translation]';
362
+
363
+ return message;
364
+ };
365
+
366
+ // Return a missing placeholder message for given parameters
367
+ I18n.missingPlaceholder = function(placeholder, message) {
368
+ return "[missing " + placeholder + " value]";
369
+ };
370
+
371
+ // Format number using localization rules.
372
+ // The options will be retrieved from the `number.format` scope.
373
+ // If this isn't present, then the following options will be used:
374
+ //
375
+ // - `precision`: `3`
376
+ // - `separator`: `"."`
377
+ // - `delimiter`: `","`
378
+ // - `strip_insignificant_zeros`: `false`
379
+ //
380
+ // You can also override these options by providing the `options` argument.
381
+ //
382
+ I18n.toNumber = function(number, options) {
383
+ options = this.prepareOptions(
384
+ options
385
+ , this.lookup("number.format")
386
+ , NUMBER_FORMAT
387
+ );
388
+
389
+ var negative = number < 0
390
+ , string = Math.abs(number).toFixed(options.precision).toString()
391
+ , parts = string.split(".")
392
+ , precision
393
+ , buffer = []
394
+ , formattedNumber
395
+ ;
396
+
397
+ number = parts[0];
398
+ precision = parts[1];
399
+
400
+ while (number.length > 0) {
401
+ buffer.unshift(number.substr(Math.max(0, number.length - 3), 3));
402
+ number = number.substr(0, number.length -3);
403
+ }
404
+
405
+ formattedNumber = buffer.join(options.delimiter);
406
+
407
+ if (options.strip_insignificant_zeros && precision) {
408
+ precision = precision.replace(/0+$/, "");
409
+ }
410
+
411
+ if (options.precision > 0 && precision) {
412
+ formattedNumber += options.separator + precision;
413
+ }
414
+
415
+ if (negative) {
416
+ formattedNumber = "-" + formattedNumber;
417
+ }
418
+
419
+ return formattedNumber;
420
+ };
421
+
422
+ // Format currency with localization rules.
423
+ // The options will be retrieved from the `number.currency.format` and
424
+ // `number.format` scopes, in that order.
425
+ //
426
+ // Any missing option will be retrieved from the `I18n.toNumber` defaults and
427
+ // the following options:
428
+ //
429
+ // - `unit`: `"$"`
430
+ // - `precision`: `2`
431
+ // - `format`: `"%u%n"`
432
+ // - `delimiter`: `","`
433
+ // - `separator`: `"."`
434
+ //
435
+ // You can also override these options by providing the `options` argument.
436
+ //
437
+ I18n.toCurrency = function(number, options) {
438
+ options = this.prepareOptions(
439
+ options
440
+ , this.lookup("number.currency.format")
441
+ , this.lookup("number.format")
442
+ , CURRENCY_FORMAT
443
+ );
444
+
445
+ number = this.toNumber(number, options);
446
+ number = options.format
447
+ .replace("%u", options.unit)
448
+ .replace("%n", number)
449
+ ;
450
+
451
+ return number;
452
+ };
453
+
454
+ // Localize several values.
455
+ // You can provide the following scopes: `currency`, `number`, or `percentage`.
456
+ // If you provide a scope that matches the `/^(date|time)/` regular expression
457
+ // then the `value` will be converted by using the `I18n.toTime` function.
458
+ //
459
+ // It will default to the value's `toString` function.
460
+ //
461
+ I18n.localize = function(scope, value) {
462
+ switch (scope) {
463
+ case "currency":
464
+ return this.toCurrency(value);
465
+ case "number":
466
+ scope = this.lookup("number.format");
467
+ return this.toNumber(value, scope);
468
+ case "percentage":
469
+ return this.toPercentage(value);
470
+ default:
471
+ if (scope.match(/^(date|time)/)) {
472
+ return this.toTime(scope, value);
473
+ } else {
474
+ return value.toString();
475
+ }
476
+ }
477
+ };
478
+
479
+ // Parse a given `date` string into a JavaScript Date object.
480
+ // This function is time zone aware.
481
+ //
482
+ // The following string formats are recognized:
483
+ //
484
+ // yyyy-mm-dd
485
+ // yyyy-mm-dd[ T]hh:mm::ss
486
+ // yyyy-mm-dd[ T]hh:mm::ss
487
+ // yyyy-mm-dd[ T]hh:mm::ssZ
488
+ // yyyy-mm-dd[ T]hh:mm::ss+0000
489
+ // yyyy-mm-dd[ T]hh:mm::ss+00:00
490
+ //
491
+ I18n.parseDate = function(date) {
492
+ var matches, convertedDate;
493
+
494
+ // we have a date, so just return it.
495
+ if (typeof(date) == "object") {
496
+ return date;
497
+ };
498
+
499
+ matches = date.toString().match(/(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2}))?(Z|\+00:?00)?/);
500
+
501
+ if (matches) {
502
+ for (var i = 1; i <= 6; i++) {
503
+ matches[i] = parseInt(matches[i], 10) || 0;
504
+ }
505
+
506
+ // month starts on 0
507
+ matches[2] -= 1;
508
+
509
+ if (matches[7]) {
510
+ convertedDate = new Date(Date.UTC(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]));
511
+ } else {
512
+ convertedDate = new Date(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]);
513
+ }
514
+ } else if (typeof(date) == "number") {
515
+ // UNIX timestamp
516
+ convertedDate = new Date();
517
+ convertedDate.setTime(date);
518
+ } else if (date.match(/\d+ \d+:\d+:\d+ [+-]\d+ \d+/)) {
519
+ // a valid javascript format with timezone info
520
+ convertedDate = new Date();
521
+ convertedDate.setTime(Date.parse(date))
522
+ } else {
523
+ // an arbitrary javascript string
524
+ convertedDate = new Date();
525
+ convertedDate.setTime(Date.parse(date));
526
+ }
527
+
528
+ return convertedDate;
529
+ };
530
+
531
+ // Formats time according to the directives in the given format string.
532
+ // The directives begins with a percent (%) character. Any text not listed as a
533
+ // directive will be passed through to the output string.
534
+ //
535
+ // The accepted formats are:
536
+ //
537
+ // %a - The abbreviated weekday name (Sun)
538
+ // %A - The full weekday name (Sunday)
539
+ // %b - The abbreviated month name (Jan)
540
+ // %B - The full month name (January)
541
+ // %c - The preferred local date and time representation
542
+ // %d - Day of the month (01..31)
543
+ // %-d - Day of the month (1..31)
544
+ // %H - Hour of the day, 24-hour clock (00..23)
545
+ // %-H - Hour of the day, 24-hour clock (0..23)
546
+ // %I - Hour of the day, 12-hour clock (01..12)
547
+ // %-I - Hour of the day, 12-hour clock (1..12)
548
+ // %m - Month of the year (01..12)
549
+ // %-m - Month of the year (1..12)
550
+ // %M - Minute of the hour (00..59)
551
+ // %-M - Minute of the hour (0..59)
552
+ // %p - Meridian indicator (AM or PM)
553
+ // %S - Second of the minute (00..60)
554
+ // %-S - Second of the minute (0..60)
555
+ // %w - Day of the week (Sunday is 0, 0..6)
556
+ // %y - Year without a century (00..99)
557
+ // %-y - Year without a century (0..99)
558
+ // %Y - Year with century
559
+ // %z - Timezone offset (+0545)
560
+ //
561
+ I18n.strftime = function(date, format) {
562
+ var options = this.lookup("date");
563
+
564
+ if (!options) {
565
+ options = DAYS_AND_MONTHS;
566
+ }
567
+
568
+ if (!options.meridian) {
569
+ options.meridian = MERIDIAN;
570
+ }
571
+
572
+ var weekDay = date.getDay()
573
+ , day = date.getDate()
574
+ , year = date.getFullYear()
575
+ , month = date.getMonth() + 1
576
+ , hour = date.getHours()
577
+ , hour12 = hour
578
+ , meridian = hour > 11 ? 1 : 0
579
+ , secs = date.getSeconds()
580
+ , mins = date.getMinutes()
581
+ , offset = date.getTimezoneOffset()
582
+ , absOffsetHours = Math.floor(Math.abs(offset / 60))
583
+ , absOffsetMinutes = Math.abs(offset) - (absOffsetHours * 60)
584
+ , timezoneoffset = (offset > 0 ? "-" : "+") + (absOffsetHours.toString().length < 2 ? "0" + absOffsetHours : absOffsetHours) + (absOffsetMinutes.toString().length < 2 ? "0" + absOffsetMinutes : absOffsetMinutes)
585
+ ;
586
+
587
+ if (hour12 > 12) {
588
+ hour12 = hour12 - 12;
589
+ } else if (hour12 === 0) {
590
+ hour12 = 12;
591
+ }
592
+
593
+ format = format.replace("%a", options.abbr_day_names[weekDay]);
594
+ format = format.replace("%A", options.day_names[weekDay]);
595
+ format = format.replace("%b", options.abbr_month_names[month]);
596
+ format = format.replace("%B", options.month_names[month]);
597
+ format = format.replace("%d", padding(day));
598
+ format = format.replace("%e", day);
599
+ format = format.replace("%-d", day);
600
+ format = format.replace("%H", padding(hour));
601
+ format = format.replace("%-H", hour);
602
+ format = format.replace("%I", padding(hour12));
603
+ format = format.replace("%-I", hour12);
604
+ format = format.replace("%m", padding(month));
605
+ format = format.replace("%-m", month);
606
+ format = format.replace("%M", padding(mins));
607
+ format = format.replace("%-M", mins);
608
+ format = format.replace("%p", options.meridian[meridian]);
609
+ format = format.replace("%S", padding(secs));
610
+ format = format.replace("%-S", secs);
611
+ format = format.replace("%w", weekDay);
612
+ format = format.replace("%y", padding(year));
613
+ format = format.replace("%-y", padding(year).replace(/^0+/, ""));
614
+ format = format.replace("%Y", year);
615
+ format = format.replace("%z", timezoneoffset);
616
+
617
+ return format;
618
+ };
619
+
620
+ // Convert the given dateString into a formatted date.
621
+ I18n.toTime = function(scope, dateString) {
622
+ var date = this.parseDate(dateString)
623
+ , format = this.lookup(scope)
624
+ ;
625
+
626
+ if (date.toString().match(/invalid/i)) {
627
+ return date.toString();
628
+ }
629
+
630
+ if (!format) {
631
+ return date.toString();
632
+ }
633
+
634
+ return this.strftime(date, format);
635
+ };
636
+
637
+ // Convert a number into a formatted percentage value.
638
+ I18n.toPercentage = function(number, options) {
639
+ options = this.prepareOptions(
640
+ options
641
+ , this.lookup("number.percentage.format")
642
+ , this.lookup("number.format")
643
+ , PERCENTAGE_FORMAT
644
+ );
645
+
646
+ number = this.toNumber(number, options);
647
+ return number + "%";
648
+ };
649
+
650
+ // Convert a number into a readable size representation.
651
+ I18n.toHumanSize = function(number, options) {
652
+ var kb = 1024
653
+ , size = number
654
+ , iterations = 0
655
+ , unit
656
+ , precision
657
+ ;
658
+
659
+ while (size >= kb && iterations < 4) {
660
+ size = size / kb;
661
+ iterations += 1;
662
+ }
663
+
664
+ if (iterations === 0) {
665
+ unit = this.t("number.human.storage_units.units.byte", {count: size});
666
+ precision = 0;
667
+ } else {
668
+ unit = this.t("number.human.storage_units.units." + SIZE_UNITS[iterations]);
669
+ precision = (size - Math.floor(size) === 0) ? 0 : 1;
670
+ }
671
+
672
+ options = this.prepareOptions(
673
+ options
674
+ , {precision: precision, format: "%n%u", delimiter: ""}
675
+ );
676
+
677
+ number = this.toNumber(size, options);
678
+ number = options.format
679
+ .replace("%u", unit)
680
+ .replace("%n", number)
681
+ ;
682
+
683
+ return number;
684
+ };
685
+
686
+ // Set aliases, so we can save some typing.
687
+ I18n.t = I18n.translate;
688
+ I18n.l = I18n.localize;
689
+ I18n.p = I18n.pluralize;
690
+ })(typeof(exports) === "undefined" ? (this.I18n = {}) : exports);
data/i18n-js.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "i18n/js/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "i18n-js-pika"
7
+ s.version = I18n::JS::Version::STRING
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Nando Vieira", "PikachuEXE"]
10
+ s.email = ["fnando.vieira@gmail.com", "pikachuexe@gmail.com"]
11
+ s.homepage = "http://github.com/PikachuEXE/i18n-js-pika"
12
+ s.summary = "Forked version of original i18n-js. It's a small library to provide the Rails I18n translations on the Javascript."
13
+ s.description = "Forked version of original i18n-js. It contains some pull requests that are not pulled yet on the original repo. Switch back to the original one when it's ready."
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.license = 'MIT'
21
+
22
+ s.add_dependency "i18n"
23
+ s.add_development_dependency "activesupport"
24
+ s.add_development_dependency "rspec"
25
+ s.add_development_dependency "rake"
26
+ s.add_development_dependency "pry-meta"
27
+ end