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