socialcast-i18n-js 4.0.0.rc1 → 4.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,661 +0,0 @@
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
- }