From 7e02f3485ee77591668a2578b2760110507dc237 Mon Sep 17 00:00:00 2001 From: Mathieu Acthernoene Date: Wed, 15 Mar 2017 17:45:35 +0100 Subject: [PATCH] Update to i18n-js v3.0.0.rc16 --- vendor/i18n.js | 245 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 201 insertions(+), 44 deletions(-) mode change 100644 => 100755 vendor/i18n.js diff --git a/vendor/i18n.js b/vendor/i18n.js old mode 100644 new mode 100755 index bcd7807..7955a01 --- a/vendor/i18n.js +++ b/vendor/i18n.js @@ -12,19 +12,23 @@ // See tests for specific formatting like numbers and dates. // -;(function(factory) { - if (typeof module !== 'undefined' && module.exports) { - // Node/CommonJS - module.exports = factory(this); - } else if (typeof define === 'function' && define.amd) { - // AMD - var global=this; - define('i18n', function(){ return factory(global);}); +// Using UMD pattern from +// https://github.com/umdjs/umd#regular-module +// `returnExports.js` version +;(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define("i18n", function(){ return factory(root);}); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(root); } else { - // Browser globals - this.I18n = factory(this); + // Browser globals (root is window) + root.I18n = factory(root); } -}(function(global) { +}(this, function(global) { "use strict"; // Use previously defined object if exists in current scope @@ -38,6 +42,73 @@ return ("0" + number.toString()).substr(-2); }; + // Improved toFixed number rounding function with support for unprecise floating points + // JavaScript's standard toFixed function does not round certain numbers correctly (for example 0.105 with precision 2). + var toFixed = function(number, precision) { + return decimalAdjust('round', number, -precision).toFixed(precision); + }; + + // Is a given variable an object? + // Borrowed from Underscore.js + var isObject = function(obj) { + var type = typeof obj; + return type === 'function' || type === 'object' && !!obj; + }; + + // Is a given value an array? + // Borrowed from Underscore.js + var isArray = function(val) { + if (Array.isArray) { + return Array.isArray(val); + }; + return Object.prototype.toString.call(val) === '[object Array]'; + }; + + var isString = function(val) { + return typeof value == 'string' || Object.prototype.toString.call(val) === '[object String]'; + }; + + var isNumber = function(val) { + return typeof val == 'number' || Object.prototype.toString.call(val) === '[object Number]'; + }; + + var isBoolean = function(val) { + return val === true || val === false; + }; + + var decimalAdjust = function(type, value, exp) { + // If the exp is undefined or zero... + if (typeof exp === 'undefined' || +exp === 0) { + return Math[type](value); + } + value = +value; + exp = +exp; + // If the value is not a number or the exp is not an integer... + if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) { + return NaN; + } + // Shift + value = value.toString().split('e'); + value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp))); + // Shift back + value = value.toString().split('e'); + return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)); + } + + var merge = function (dest, obj) { + var key, value; + for (key in obj) if (obj.hasOwnProperty(key)) { + value = obj[key]; + if (isString(value) || isNumber(value) || isBoolean(value)) { + dest[key] = value; + } else { + if (dest[key] == null) dest[key] = {}; + merge(dest[key], value); + } + } + return dest; + }; + // Set default days/months translations. var DATE = { day_names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] @@ -89,7 +160,7 @@ , locale: "en" // Set the translation key separator. , defaultSeparator: "." - // Set the placeholder format. Accepts `{placeholder}}` and `%{placeholder}`.} + // Set the placeholder format. Accepts `{{placeholder}}` and `%{placeholder}`. , placeholder: /(?:\{\{|%\{)(.*?)(?:\}\}?)/gm // Set if engine should fallback to the default locale when a translation // is missing. @@ -160,6 +231,12 @@ if (typeof(this.translations) === "undefined" && this.translations !== null) this.translations = DEFAULT_OPTIONS.translations; + + if (typeof(this.missingBehaviour) === "undefined" && this.missingBehaviour !== null) + this.missingBehaviour = DEFAULT_OPTIONS.missingBehaviour; + + if (typeof(this.missingTranslationPrefix) === "undefined" && this.missingTranslationPrefix !== null) + this.missingTranslationPrefix = DEFAULT_OPTIONS.missingTranslationPrefix; }; I18n.initializeOptions(); @@ -189,7 +266,7 @@ result = result(locale); } - if (result instanceof Array === false) { + if (isArray(result) === false) { result = [result]; } @@ -301,7 +378,6 @@ if (!translations) { continue; } - while (scopes.length) { translations = translations[scopes.shift()]; @@ -320,6 +396,75 @@ } }; + // lookup pluralization rule key into translations + I18n.pluralizationLookupWithoutFallback = function(count, locale, translations) { + var pluralizer = this.pluralization.get(locale) + , pluralizerKeys = pluralizer(count) + , pluralizerKey + , message; + + if (isObject(translations)) { + while (pluralizerKeys.length) { + pluralizerKey = pluralizerKeys.shift(); + if (this.isSet(translations[pluralizerKey])) { + message = translations[pluralizerKey]; + break; + } + } + } + + return message; + }; + + // Lookup dedicated to pluralization + I18n.pluralizationLookup = function(count, scope, options) { + options = this.prepareOptions(options); + var locales = this.locales.get(options.locale).slice() + , requestedLocale = locales[0] + , locale + , scopes + , translations + , message + ; + scope = this.getFullScope(scope, options); + + while (locales.length) { + locale = locales.shift(); + scopes = scope.split(this.defaultSeparator); + translations = this.translations[locale]; + + if (!translations) { + continue; + } + + while (scopes.length) { + translations = translations[scopes.shift()]; + if (!isObject(translations)) { + break; + } + if (scopes.length == 0) { + message = this.pluralizationLookupWithoutFallback(count, locale, translations); + } + } + if (message != null && message != undefined) { + break; + } + } + + if (message == null || message == undefined) { + if (this.isSet(options.defaultValue)) { + if (isObject(options.defaultValue)) { + message = this.pluralizationLookupWithoutFallback(count, options.locale, options.defaultValue); + } else { + message = options.defaultValue; + } + translations = options.defaultValue; + } + } + + return { message: message, translations: translations }; + }; + // Rails changed the way the meridian is stored. // It started with `date.meridian` returning an array, // then it switched to `time.am` and `time.pm`. @@ -399,6 +544,7 @@ I18n.translate = function(scope, options) { options = this.prepareOptions(options); + var copiedOptions = this.prepareOptions(options); var translationOptions = this.createTranslationOptions(scope, options); var translation; @@ -423,8 +569,8 @@ if (typeof(translation) === "string") { translation = this.interpolate(translation, options); - } else if (translation instanceof Object && this.isSet(options.count)) { - translation = this.pluralize(options.count, translation, options); + } else if (isObject(translation) && this.isSet(options.count)) { + translation = this.pluralize(options.count, scope, copiedOptions); } return translation; @@ -453,9 +599,9 @@ if (this.isSet(options[name])) { value = options[name].toString().replace(/\$/gm, "_#$#_"); } else if (name in options) { - value = this.nullPlaceholder(placeholder, message); + value = this.nullPlaceholder(placeholder, message, options); } else { - value = this.missingPlaceholder(placeholder, message); + value = this.missingPlaceholder(placeholder, message, options); } regex = new RegExp(placeholder.replace(/\{/gm, "\\{").replace(/\}/gm, "\\}")); @@ -470,32 +616,22 @@ // which will be retrieved from `options`. I18n.pluralize = function(count, scope, options) { options = this.prepareOptions(options); - var translations, pluralizer, keys, key, message; + var pluralizer, message, result; - if (scope instanceof Object) { - translations = scope; - } else { - translations = this.lookup(scope, options); - } - - if (!translations) { + result = this.pluralizationLookup(count, scope, options); + if (result.translations == undefined || result.translations == null) { return this.missingTranslation(scope, options); } - pluralizer = this.pluralization.get(options.locale); - keys = pluralizer(count); - - while (keys.length) { - key = keys.shift(); - - if (this.isSet(translations[key])) { - message = translations[key]; - break; - } - } - options.count = String(count); - return this.interpolate(message, options); + + if (result.message != undefined && result.message != null) { + return this.interpolate(result.message, options); + } + else { + pluralizer = this.pluralization.get(options.locale); + return this.missingTranslation(scope + '.' + pluralizer(count)[0], options); + } }; // Return a missing translation message for the given parameters. @@ -510,14 +646,15 @@ function(match, p1, p2) {return p1 + ' ' + p2.toLowerCase()} ); } + var localeForTranslation = (options != null && options.locale != null) ? options.locale : this.currentLocale(); var fullScope = this.getFullScope(scope, options); - var fullScopeWithLocale = [this.currentLocale(), fullScope].join(this.defaultSeparator); + var fullScopeWithLocale = [localeForTranslation, fullScope].join(this.defaultSeparator); return '[missing "' + fullScopeWithLocale + '" translation]'; }; // Return a missing placeholder message for given parameters - I18n.missingPlaceholder = function(placeholder, message) { + I18n.missingPlaceholder = function(placeholder, message, options) { return "[missing " + placeholder + " value]"; }; @@ -544,7 +681,7 @@ ); var negative = number < 0 - , string = Math.abs(number).toFixed(options.precision).toString() + , string = toFixed(Math.abs(number), options.precision).toString() , parts = string.split(".") , precision , buffer = [] @@ -746,6 +883,10 @@ options = this.prepareOptions(options, DATE); + if (isNaN(date.getTime())) { + throw new Error('I18n.strftime() requires a valid date object, but received an invalid date.'); + } + var weekDay = date.getDay() , day = date.getDate() , year = date.getFullYear() @@ -872,7 +1013,23 @@ } return scope; - } + }; + /** + * Merge obj1 with obj2 (shallow merge), without modifying inputs + * @param {Object} obj1 + * @param {Object} obj2 + * @returns {Object} Merged values of obj1 and obj2 + * + * In order to support ES3, `Object.prototype.hasOwnProperty.call` is used + * Idea is from: + * https://stackoverflow.com/questions/8157700/object-has-no-hasownproperty-method-i-e-its-undefined-ie8 + */ + I18n.extend = function ( obj1, obj2 ) { + if (typeof(obj1) === "undefined" && typeof(obj2) === "undefined") { + return {}; + } + return merge(obj1, obj2); + }; // Set aliases, so we can save some typing. I18n.t = I18n.translate; @@ -880,4 +1037,4 @@ I18n.p = I18n.pluralize; return I18n; -})); \ No newline at end of file +}));