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
@@ -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