socialcast-i18n-js 4.0.0.rc1

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