From 9757e71a681f712f44107e8439b57527eb674936 Mon Sep 17 00:00:00 2001 From: kirti Date: Wed, 15 Mar 2023 02:25:32 +0530 Subject: [PATCH] Numeric value causes TypeError #176 --- src/jquery.i18n.js | 519 +++++++++++++++++++++++---------------------- 1 file changed, 271 insertions(+), 248 deletions(-) diff --git a/src/jquery.i18n.js b/src/jquery.i18n.js index d6c0bae..5b3a7dc 100644 --- a/src/jquery.i18n.js +++ b/src/jquery.i18n.js @@ -13,278 +13,301 @@ * @licence MIT License */ -( function ( $ ) { - 'use strict'; +(function ($) { + "use strict"; - var I18N, - slice = Array.prototype.slice; - /** - * @constructor - * @param {Object} options - */ - I18N = function ( options ) { - // Load defaults - this.options = $.extend( {}, I18N.defaults, options ); + var I18N, + slice = Array.prototype.slice; + /** + * @constructor + * @param {Object} options + */ + I18N = function (options) { + // Load defaults + this.options = $.extend({}, I18N.defaults, options); - this.parser = this.options.parser; - this.locale = this.options.locale; - this.messageStore = this.options.messageStore; - this.languages = {}; - }; + this.parser = this.options.parser; + this.locale = this.options.locale; + this.messageStore = this.options.messageStore; + this.languages = {}; + }; - I18N.prototype = { - /** - * Localize a given messageKey to a locale. - * @param {string} messageKey - * @return {string} Localized message - */ - localize: function ( messageKey ) { - var localeParts, localePartIndex, locale, fallbackIndex, - tryingLocale, message; + I18N.prototype = { + /** + * Localize a given messageKey to a locale. + * @param {string} messageKey + * @return {string} Localized message + */ + localize: function (messageKey) { + var localeParts, + localePartIndex, + locale, + fallbackIndex, + tryingLocale, + message; - locale = this.locale; - fallbackIndex = 0; + locale = this.locale; + fallbackIndex = 0; - while ( locale ) { - // Iterate through locales starting at most-specific until - // localization is found. As in fi-Latn-FI, fi-Latn and fi. - localeParts = locale.split( '-' ); - localePartIndex = localeParts.length; + while (locale) { + // Iterate through locales starting at most-specific until + // localization is found. As in fi-Latn-FI, fi-Latn and fi. + localeParts = locale.split("-"); + localePartIndex = localeParts.length; - do { - tryingLocale = localeParts.slice( 0, localePartIndex ).join( '-' ); - message = this.messageStore.get( tryingLocale, messageKey ); + do { + tryingLocale = localeParts.slice(0, localePartIndex).join("-"); + message = this.messageStore.get(tryingLocale, messageKey); - if ( message ) { - return message; - } + if (message) { + return message; + } - localePartIndex--; - } while ( localePartIndex ); + localePartIndex--; + } while (localePartIndex); - if ( locale === this.options.fallbackLocale ) { - break; - } + if (locale === this.options.fallbackLocale) { + break; + } - locale = ( $.i18n.fallbacks[ this.locale ] && - $.i18n.fallbacks[ this.locale ][ fallbackIndex ] ) || - this.options.fallbackLocale; - $.i18n.log( 'Trying fallback locale for ' + this.locale + ': ' + locale + ' (' + messageKey + ')' ); + locale = + ($.i18n.fallbacks[this.locale] && + $.i18n.fallbacks[this.locale][fallbackIndex]) || + this.options.fallbackLocale; + $.i18n.log( + "Trying fallback locale for " + + this.locale + + ": " + + locale + + " (" + + messageKey + + ")" + ); - fallbackIndex++; - } + fallbackIndex++; + } - // key not found - return ''; - }, + // key not found + return ""; + }, - /* - * Destroy the i18n instance. - */ - destroy: function () { - $.removeData( document, 'i18n' ); - }, + /* + * Destroy the i18n instance. + */ + destroy: function () { + $.removeData(document, "i18n"); + }, - /** - * General message loading API This can take a URL string for - * the json formatted messages. Example: - * load('path/to/all_localizations.json'); - * - * To load a localization file for a locale: - * - * load('path/to/de-messages.json', 'de' ); - * - * - * To load a localization file from a directory: - * - * load('path/to/i18n/directory', 'de' ); - * - * The above method has the advantage of fallback resolution. - * ie, it will automatically load the fallback locales for de. - * For most usecases, this is the recommended method. - * It is optional to have trailing slash at end. - * - * A data object containing message key- message translation mappings - * can also be passed. Example: - * - * load( { 'hello' : 'Hello' }, optionalLocale ); - * - * - * A source map containing key-value pair of languagename and locations - * can also be passed. Example: - * - * load( { - * bn: 'i18n/bn.json', - * he: 'i18n/he.json', - * en: 'i18n/en.json' - * } ) - * - * - * If the data argument is null/undefined/false, - * all cached messages for the i18n instance will get reset. - * - * @param {string|Object} source - * @param {string} locale Language tag - * @return {jQuery.Promise} - */ - load: function ( source, locale ) { - var fallbackLocales, locIndex, fallbackLocale, sourceMap = {}; - if ( !source && !locale ) { - source = 'i18n/' + $.i18n().locale + '.json'; - locale = $.i18n().locale; - } - if ( typeof source === 'string' && - // source extension should be json, but can have query params after that. - source.split( '?' )[ 0 ].split( '.' ).pop() !== 'json' - ) { - // Load specified locale then check for fallbacks when directory is - // specified in load() - sourceMap[ locale ] = source + '/' + locale + '.json'; - fallbackLocales = ( $.i18n.fallbacks[ locale ] || [] ) - .concat( this.options.fallbackLocale ); - for ( locIndex = 0; locIndex < fallbackLocales.length; locIndex++ ) { - fallbackLocale = fallbackLocales[ locIndex ]; - sourceMap[ fallbackLocale ] = source + '/' + fallbackLocale + '.json'; - } - return this.load( sourceMap ); - } else { - return this.messageStore.load( source, locale ); - } + /** + * General message loading API This can take a URL string for + * the json formatted messages. Example: + * load('path/to/all_localizations.json'); + * + * To load a localization file for a locale: + * + * load('path/to/de-messages.json', 'de' ); + * + * + * To load a localization file from a directory: + * + * load('path/to/i18n/directory', 'de' ); + * + * The above method has the advantage of fallback resolution. + * ie, it will automatically load the fallback locales for de. + * For most usecases, this is the recommended method. + * It is optional to have trailing slash at end. + * + * A data object containing message key- message translation mappings + * can also be passed. Example: + * + * load( { 'hello' : 'Hello' }, optionalLocale ); + * + * + * A source map containing key-value pair of languagename and locations + * can also be passed. Example: + * + * load( { + * bn: 'i18n/bn.json', + * he: 'i18n/he.json', + * en: 'i18n/en.json' + * } ) + * + * + * If the data argument is null/undefined/false, + * all cached messages for the i18n instance will get reset. + * + * @param {string|Object} source + * @param {string} locale Language tag + * @return {jQuery.Promise} + */ + load: function (source, locale) { + var fallbackLocales, + locIndex, + fallbackLocale, + sourceMap = {}; + if (!source && !locale) { + source = "i18n/" + $.i18n().locale + ".json"; + locale = $.i18n().locale; + } + if ( + typeof source === "string" && + // source extension should be json, but can have query params after that. + source.split("?")[0].split(".").pop() !== "json" + ) { + // Load specified locale then check for fallbacks when directory is + // specified in load() + sourceMap[locale] = source + "/" + locale + ".json"; + fallbackLocales = ($.i18n.fallbacks[locale] || []).concat( + this.options.fallbackLocale + ); + for (locIndex = 0; locIndex < fallbackLocales.length; locIndex++) { + fallbackLocale = fallbackLocales[locIndex]; + sourceMap[fallbackLocale] = source + "/" + fallbackLocale + ".json"; + } + return this.load(sourceMap); + } else { + return this.messageStore.load(source, locale); + } + }, - }, + /** + * Does parameter and magic word substitution. + * + * @param {string} key Message key + * @param {Array} parameters Message parameters + * @return {string} + */ + parse: function (key, parameters) { + var message = this.localize(key); + // FIXME: This changes the state of the I18N object, + // should probably not change the 'this.parser' but just + // pass it to the parser. + this.parser.language = + $.i18n.languages[$.i18n().locale] || $.i18n.languages["default"]; + if (message === "") { + message = key; + } + return this.parser.parse(message, parameters); + }, + }; - /** - * Does parameter and magic word substitution. - * - * @param {string} key Message key - * @param {Array} parameters Message parameters - * @return {string} - */ - parse: function ( key, parameters ) { - var message = this.localize( key ); - // FIXME: This changes the state of the I18N object, - // should probably not change the 'this.parser' but just - // pass it to the parser. - this.parser.language = $.i18n.languages[ $.i18n().locale ] || $.i18n.languages[ 'default' ]; - if ( message === '' ) { - message = key; - } - return this.parser.parse( message, parameters ); - } - }; + /** + * Process a message from the $.I18N instance + * for the current document, stored in jQuery.data(document). + * + * @param {string} key Key of the message. + * @param {string} param1 [param...] Variadic list of parameters for {key}. + * @return {string|$.I18N} Parsed message, or if no key was given + * the instance of $.I18N is returned. + */ + $.i18n = function (key, param1) { + var parameters, + i18n = $.data(document, "i18n"), + options = typeof key === "object" && key; - /** - * Process a message from the $.I18N instance - * for the current document, stored in jQuery.data(document). - * - * @param {string} key Key of the message. - * @param {string} param1 [param...] Variadic list of parameters for {key}. - * @return {string|$.I18N} Parsed message, or if no key was given - * the instance of $.I18N is returned. - */ - $.i18n = function ( key, param1 ) { - var parameters, - i18n = $.data( document, 'i18n' ), - options = typeof key === 'object' && key; + // If the locale option for this call is different then the setup so far, + // update it automatically. This doesn't just change the context for this + // call but for all future call as well. + // If there is no i18n setup yet, don't do this. It will be taken care of + // by the `new I18N` construction below. + // NOTE: It should only change language for this one call. + // Then cache instances of I18N somewhere. + if (options && options.locale && i18n && i18n.locale !== options.locale) { + i18n.locale = options.locale; + } - // If the locale option for this call is different then the setup so far, - // update it automatically. This doesn't just change the context for this - // call but for all future call as well. - // If there is no i18n setup yet, don't do this. It will be taken care of - // by the `new I18N` construction below. - // NOTE: It should only change language for this one call. - // Then cache instances of I18N somewhere. - if ( options && options.locale && i18n && i18n.locale !== options.locale ) { - i18n.locale = options.locale; - } + if (!i18n) { + i18n = new I18N(options); + $.data(document, "i18n", i18n); + } - if ( !i18n ) { - i18n = new I18N( options ); - $.data( document, 'i18n', i18n ); - } + if (typeof key === "string") { + if (param1 !== undefined) { + parameters = slice.call(arguments, 1); + } else { + parameters = []; + } - if ( typeof key === 'string' ) { - if ( param1 !== undefined ) { - parameters = slice.call( arguments, 1 ); - } else { - parameters = []; - } + return i18n.parse(key, parameters); + } else { + // FIXME: remove this feature/bug. + return i18n; + } + }; - return i18n.parse( key, parameters ); - } else { - // FIXME: remove this feature/bug. - return i18n; - } - }; + $.fn.i18n = function () { + var i18n = $.data(document, "i18n"); - $.fn.i18n = function () { - var i18n = $.data( document, 'i18n' ); + if (!i18n) { + i18n = new I18N(); + $.data(document, "i18n", i18n); + } - if ( !i18n ) { - i18n = new I18N(); - $.data( document, 'i18n', i18n ); - } + return this.each(function () { + var $this = $(this), + messageKey = $this.data("i18n") + " ", + lBracket, + rBracket, + type, + key; - return this.each( function () { - var $this = $( this ), - messageKey = $this.data( 'i18n' ), - lBracket, rBracket, type, key; + if (messageKey) { + lBracket = messageKey.indexOf("["); + rBracket = messageKey.indexOf("]"); + if (lBracket !== -1 && rBracket !== -1 && lBracket < rBracket) { + type = messageKey.slice(lBracket + 1, rBracket); + key = messageKey.slice(rBracket + 1); + if (type === "html") { + $this.html(i18n.parse(key)); + } else { + $this.attr(type, i18n.parse(key)); + } + } else { + $this.text(i18n.parse()); + } + } else { + $this.find("[data-i18n]").i18n(); + } + }); + }; - if ( messageKey ) { - lBracket = messageKey.indexOf( '[' ); - rBracket = messageKey.indexOf( ']' ); - if ( lBracket !== -1 && rBracket !== -1 && lBracket < rBracket ) { - type = messageKey.slice( lBracket + 1, rBracket ); - key = messageKey.slice( rBracket + 1 ); - if ( type === 'html' ) { - $this.html( i18n.parse( key ) ); - } else { - $this.attr( type, i18n.parse( key ) ); - } - } else { - $this.text( i18n.parse( messageKey ) ); - } - } else { - $this.find( '[data-i18n]' ).i18n(); - } - } ); - }; + function getDefaultLocale() { + var locale = $("html").attr("lang"); + if (!locale) { + locale = navigator.language || navigator.userLanguage || ""; + } + return locale; + } - function getDefaultLocale() { - var locale = $( 'html' ).attr( 'lang' ); - if ( !locale ) { - locale = navigator.language || navigator.userLanguage || ''; - } - return locale; - } + $.i18n.languages = {}; + $.i18n.messageStore = $.i18n.messageStore || {}; + $.i18n.parser = { + // The default parser only handles variable substitution + parse: function (message, parameters) { + return message.replace(/\$(\d+)/g, function (str, match) { + var index = parseInt(match, 10) - 1; + return parameters[index] !== undefined + ? parameters[index] + : "$" + match; + }); + }, + emitter: {}, + }; + $.i18n.fallbacks = {}; + $.i18n.debug = false; + $.i18n.log = function (/* arguments */) { + if (window.console && $.i18n.debug) { + window.console.log.apply(window.console, arguments); + } + }; + /* Static members */ + I18N.defaults = { + locale: getDefaultLocale(), + fallbackLocale: "en", + parser: $.i18n.parser, + messageStore: $.i18n.messageStore, + }; - $.i18n.languages = {}; - $.i18n.messageStore = $.i18n.messageStore || {}; - $.i18n.parser = { - // The default parser only handles variable substitution - parse: function ( message, parameters ) { - return message.replace( /\$(\d+)/g, function ( str, match ) { - var index = parseInt( match, 10 ) - 1; - return parameters[ index ] !== undefined ? parameters[ index ] : '$' + match; - } ); - }, - emitter: {} - }; - $.i18n.fallbacks = {}; - $.i18n.debug = false; - $.i18n.log = function ( /* arguments */ ) { - if ( window.console && $.i18n.debug ) { - window.console.log.apply( window.console, arguments ); - } - }; - /* Static members */ - I18N.defaults = { - locale: getDefaultLocale(), - fallbackLocale: 'en', - parser: $.i18n.parser, - messageStore: $.i18n.messageStore - }; - - // Expose constructor - $.i18n.constructor = I18N; -}( jQuery ) ); + // Expose constructor + $.i18n.constructor = I18N; +})(jQuery);