app/assets/javascripts/i18n.js in i18n-js-3.0.0.rc11 vs app/assets/javascripts/i18n.js in i18n-js-3.0.0.rc12

- old
+ new

@@ -36,10 +36,51 @@ // Apply number padding. var padding = function(number) { 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(obj) { + if (Array.isArray) { + return Array.isArray(obj); + }; + return Object.prototype.toString.call(obj) === '[object Array]'; + }; + + 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)); + } + // Set default days/months translations. var DATE = { day_names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] , abbr_day_names: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] , month_names: [null, "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] @@ -84,11 +125,11 @@ defaultLocale: "en" // Set the current locale to `en`. , 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. , fallbacks: false // Set the default translation object. @@ -177,11 +218,11 @@ if (typeof(result) === "function") { result = result(locale); } - if (result instanceof Array === false) { + if (isArray(result) === false) { result = [result]; } return result; }; @@ -406,11 +447,11 @@ return this.missingTranslation(scope, options); } if (typeof(translation) === "string") { translation = this.interpolate(translation, options); - } else if (translation instanceof Object && this.isSet(options.count)) { + } else if (isObject(translation) && this.isSet(options.count)) { translation = this.pluralize(options.count, translation, options); } return translation; }; @@ -436,13 +477,13 @@ name = placeholder.replace(this.placeholder, "$1"); 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, "\\}")); message = message.replace(regex, value); } @@ -455,11 +496,11 @@ // which will be retrieved from `options`. I18n.pluralize = function(count, scope, options) { options = this.prepareOptions(options); var translations, pluralizer, keys, key, message; - if (scope instanceof Object) { + if (isObject(scope)) { translations = scope; } else { translations = this.lookup(scope, options); } @@ -500,11 +541,11 @@ 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]"; }; I18n.nullPlaceholder = function() { return I18n.missingPlaceholder.apply(I18n, arguments); @@ -527,11 +568,11 @@ , this.lookup("number.format") , NUMBER_FORMAT ); 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 = [] , formattedNumber , format = options.format || "%n" @@ -855,10 +896,35 @@ if (options.scope) { scope = [options.scope, scope].join(this.defaultSeparator); } 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 ) { + var extended = {}; + var prop; + for (prop in obj1) { + if (Object.prototype.hasOwnProperty.call(obj1, prop)) { + extended[prop] = obj1[prop]; + } + } + for (prop in obj2) { + if (Object.prototype.hasOwnProperty.call(obj2, prop)) { + extended[prop] = obj2[prop]; + } + } + return extended; + }; // Set aliases, so we can save some typing. I18n.t = I18n.translate; I18n.l = I18n.localize; I18n.p = I18n.pluralize;