您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
jquery.localizationTool
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyforks.org/scripts/403927/808323/jquerylocalizationTooljs.js
/** * @fileOverview Contains the code for jQuery.localizationTool * * @author Savio Dimatteo <[email protected]> */ (function($) { var _keyboardPressed = false; var methods = { /** * Returns the ordinal number corresponding to the given language code, * or throws in case the given language code is not defined. * NOTE: this method operates on the active languages, therefore * $this.data('activeLanguageCodeArray') must be available when the * method is called. * * @name _languageCodeToOrdinal * @function * @access private * @param {string} lanuageCode - the language code to convert to ordinal * @returns {number} ordinal - the converted ordinal */ '_languageCodeToOrdinal' : function (languageCode) { var $this = this, activeLanguageCodes = $this.data('activeLanguageCodeArray'); var ordinal = $.inArray(languageCode, activeLanguageCodes); if (ordinal === -1) { $.error('Cannot convert ' + languageCode + ' into an ordinal number'); } return ordinal; }, /** * Returns the language code corresponding to the given ordinal number. * It throws in case the given ordinal number does not correspond to any * language code. * NOTE: this method operates on the active languages, therefore * $this.data('activeLanguageCodeArray') must be available when the * method is called. * * @name _ordinalToLanguageCode * @function * @access private * @param {number} ordinal - the ordinal number to convert into a language code * @returns {string} languageCode - the converted language code */ '_ordinalToLanguageCode' : function (ordinal) { var $this = this, activeLanguageCodes = $this.data('activeLanguageCodeArray'); if (activeLanguageCodes.length <= ordinal || ordinal < 0) { $.error('Cannot convert ' + ordinal + ' into a language code.'); } return activeLanguageCodes[ordinal]; }, /** * Returns the html representation for the given language code. * @name _languageCodeToHtml * @function * @param {string} languageCode - the language code as defined in the settings object */ '_languageCodeToHtml': function (languageCode) { var $this = this, settings = $this.data('settings'), languagesObj = settings.languages, languageDefinitionObj = languagesObj[languageCode]; var htmlClass = ''; if (languageDefinitionObj.flag.hasOwnProperty('class')) { htmlClass = ' ' + languageDefinitionObj.flag['class']; } var htmlImage = ''; if (languageDefinitionObj.flag.hasOwnProperty('url')) { htmlImage = '<img src="' + languageDefinitionObj.flag.url + '" />'; } var languageName = languageDefinitionObj.language; var haveCountry = languageDefinitionObj.hasOwnProperty('country'); /* * Build up the html */ var html = []; html.push('<li class="ltool-language ', languageCode, '">'); if (settings.showFlag) { html.push( '<div class="ltool-language-flag', htmlClass, '"></div>' ); html.push( htmlImage ); } var interpolatedTemplate = methods._interpolateTemplate.call($this, haveCountry ? languageDefinitionObj.country : undefined, languageName ); html.push(interpolatedTemplate); html.push('</li>'); return html.join(''); }, /** * Interpolates the given country name and language name to the * labelTemplate specified in the settings. * * @param {string} countryName * the country name * @param {string} languageName * the language name * * @returns {string} * the interpolated template */ _interpolateTemplate: function (countryName, languageName) { var $this = this, settings = $this.data('settings'), template = settings.labelTemplate, countryReplacement = '', languageReplacement = '', haveCountry = typeof countryName === 'string'; if (settings.showCountry && haveCountry) { countryReplacement = [ '<span class="ltool-language-country">', '$1' + countryName.replace(/[$]/g, '$') + '$2', '</span>' ].join(''); } if (settings.showLanguage) { var hasCountryClass = haveCountry ? 'ltool-has-country ' : ""; languageReplacement = [ '<span class="', hasCountryClass, 'ltool-language-name">', '$1' + languageName.replace(/[$]/g, '$') + '$2' , '</span>' ].join(''); } return '<span class="ltool-language-countryname">' + template .replace(/{{([^{]*)language([^}]*)}}/g, languageReplacement) .replace(/{{([^{]*)country([^}]*)}}/g, countryReplacement) + '</span>'; }, /** * Displays the given language in the dropdown menu. 在下拉菜单中显示给定的语言。 * @name _selectLanguage * @function * @access private * @param {string} languageCode - the language code */ '_selectLanguage': function (languageCode) { var $this = this; $this.find('.ltool-dropdown-label').html( $('.ltool-language.' + languageCode).html() ); $this.data('selectedLanguageCode', languageCode); }, /** * Initializes the localization tool widget. 初始化本地化工具小部件。 * @name _initializeWidget * @function * @access private * @param {array} languageCodeArray - the language code array of the languages to be displayed */ '_initializeWidget': function (languageCodeArray) { var $this = this, settings = $this.data('settings'), languagesObj = settings.languages; var markupArray = []; markupArray.push('<span tabindex="0" class="ltool-dropdown-label">Change Language</span><div class="ltool-dropdown-label-arrow"></div>'); markupArray.push('<ul class="ltool-dropdown-items">'); var languageCode, i; for (i=0;languageCode=languageCodeArray[i++];) { if ( languagesObj.hasOwnProperty(languageCode)) { markupArray.push( methods._languageCodeToHtml.call($this, languageCode) ); } else { $.error('The language \'' + languageCode + '\' must be defined'); } } markupArray.push('</ul>'); $(markupArray.join('')).appendTo($this); return $this; }, /** * Handles dropdown click event. 处理下拉单击事件。 * @name _onDropdownClicked * @function * @access private */ '_onDropdownClicked' : function (/*e*/) { var $this = this; var selectedLanguageCode = $this.data('selectedLanguageCode'); $this.find('.ltool-language').removeClass('ltool-is-selected'); $this.find('.' + selectedLanguageCode).addClass('ltool-is-selected'); $this.toggleClass('ltool-is-visible'); return $this; }, '_closeDropdown' : function () { var $this = this; $this.removeClass('ltool-is-visible'); }, /** * Handles mouseout on dropdown items. * @name _onMouseout * @function * @access private */ '_onMouseout': function (e) { var $this = this; if ($this.find(e.relatedTarget).length > 0) { // get rid of the current selected item! $this.find('.ltool-is-selected') .removeClass('ltool-is-selected'); // we will be over an element of ours e.preventDefault(); return $this; } /* We will be over another element that doesn't belong to us */ $this.removeClass('ltool-is-visible'); }, /** * Handles user clicks on a certain dropdown item. 处理用户单击某个下拉项。 * @name _onLanguageSelected * @function * @param {$element} $item - the jquery item clicked * @access private */ '_onLanguageSelected': function ($item) { var $this = this; // extract language code from the $item var languageCode = $item.attr('class') .replace('ltool-language', '') .replace('ltool-is-selected', '') .replace(/ /g, ''); //存储当前选择的语言 console.log("存储! languageCode: ",languageCode); gc_multiLanguage.saveConfig(languageCode); methods._selectLanguage.call($this, languageCode); methods._mayTranslate.call($this, languageCode); }, /** * Select the language before the current language in the list. 在列表中选择当前语言之前的语言。 * @name _selectPreviousLanguage * @function * @access private */ '_selectPreviousLanguage' : function () { var $this = this; var currentLanguageCode = $this.data('selectedLanguageCode'); var currentLanguageCodeOrdinal = methods._languageCodeToOrdinal.call($this, currentLanguageCode); if (currentLanguageCodeOrdinal === 0) { return; // cannot go before the first language } var nextLanguageCode = methods._ordinalToLanguageCode.call($this, currentLanguageCodeOrdinal-1); // peform the selection $this.find('.ltool-is-selected').removeClass('ltool-is-selected'); methods._selectLanguage.call($this, nextLanguageCode); methods._mayTranslate.call($this, nextLanguageCode); $this.find('.' + nextLanguageCode).addClass('ltool-is-selected'); return $this; }, /** * Select the language after the current language in the list. 选择列表中当前语言之后的语言。 * @name _selectPreviousLanguage * @function * @access private */ '_selectNextLanguage' : function () { var $this = this, activeLanguageCodes = $this.data('activeLanguageCodeArray'); var currentLanguageCode = $this.data('selectedLanguageCode'); var currentLanguageCodeOrdinal = methods._languageCodeToOrdinal.call($this, currentLanguageCode); if (currentLanguageCodeOrdinal + 1 >= activeLanguageCodes.length) { return; } var nextLanguageCode = methods._ordinalToLanguageCode.call($this, currentLanguageCodeOrdinal+1); // peform the selection $this.find('.ltool-is-selected').removeClass('ltool-is-selected'); methods._selectLanguage.call($this, nextLanguageCode); methods._mayTranslate.call($this, nextLanguageCode); $this.find('.' + nextLanguageCode).addClass('ltool-is-selected'); return $this; }, /** * Handles keydown event * @name _onKeydown * @function * @param {event} e - the keydown event * @access private */ '_onKeydown': function (e) { var $this = this; switch (e.keyCode) { case 13: /* enter (open-close menu) */ methods._onDropdownClicked.call($this); e.preventDefault(); break; case 40: /* down (select next) */ methods._selectNextLanguage.call($this); e.preventDefault(); break; case 38: /* up (select previous) */ methods._selectPreviousLanguage.call($this); e.preventDefault(); break; case 27: methods._closeDropdown.call($this); e.preventDefault(); break; } return $this; }, /** * Binds events to the localization tool widget. * @name _bindEvents * @function * @access private */ '_bindEvents': function () { var $this = this; $this .bind('mousedown.localizationTool', function (e) { _keyboardPressed = false; methods._onKeydown.call($this, e); }) .bind('click.localizationTool', function (e) { methods._onDropdownClicked.call($this, e); }) .bind('keydown.localizationTool', function (e){ _keyboardPressed = true; methods._onKeydown.call($this, e); }) .bind('mouseout.localizationTool', function (e) { methods._onMouseout.call($this, e); }) .bind('focusout.localizationTool', function () { if (_keyboardPressed) { methods._closeDropdown.call($this); } }); $this.find('.ltool-language') .bind('click.localizationTool', function (/*e*/) { methods._onLanguageSelected.call($this, $(this)); }); return $this; }, /** * Analizes the input strings object and decomposes its keys in * sections: text strings, id strings, class strings, element strings, * attribute strings. * @name _decomposeStringsForReferenceMapping * @function * @access private * @returns {object} the decomposition object. */ '_decomposeStringsForReferenceMapping' : function () { var decompositionObj = { 'idStrings' : [], 'classStrings' : [], 'elementStrings' : [], 'textStrings' : [], 'attributeStrings' : [] }; var $this = this, stringsObj = $this.data('settings').strings; // regexp for attributes matching var attrRegexp = new RegExp('^[a-zA-Z-]+?::'); var stringKey; for (stringKey in stringsObj) { if (stringsObj.hasOwnProperty(stringKey)) { if (stringKey.match(attrRegexp)) { // NOTE: check first! decompositionObj.attributeStrings.push(stringKey); } else if (stringKey.indexOf('id:') === 0) { decompositionObj.idStrings.push(stringKey); } else if (stringKey.indexOf('class:') === 0) { decompositionObj.classStrings.push(stringKey); } else if (stringKey.indexOf('element:') === 0) { decompositionObj.elementStrings.push(stringKey); } else { decompositionObj.textStrings.push(stringKey); } } } return decompositionObj; }, /** * Goes through each text node and builds a string reference mapping. * It is a mapping (an object) * STRING_IDENTIFIER -> <IS_ATTRIBUTE?, ORIGINAL_HTML, [DOM_NODES]> * used later for the translation. See init method for a * reference. The resulting object is stored internally in * $this.data('refMappingObj') as refMapping. * @name _buildStringReferenceMapping * @function * @access private */ '_buildStringReferenceMapping': function () { var $this = this, refMapping = {}, settings = $this.data('settings'), stringsObj = settings.strings; // decompose the initial strings in various bits var decompositionObj = methods._decomposeStringsForReferenceMapping.call($this); /* * First go through each id */ var idString, i; for (i=0; idString = decompositionObj.idStrings[i++];) { var idStringName = idString.substring('id:'.length); var $idNode = $('#' + idStringName); var contents = $idNode.contents(); if (settings.ignoreUnmatchedSelectors === true && contents.length === 0) { continue; } if (contents.length !== 1) { $.error(idString + ' must contain exactly one text node, found ' + contents.length + ' instead'); } else if (contents[0].nodeType !== 3) { $.error(idString + ' does not contain a #text node (i.e., type 3)'); } else { // add this to the refMapping refMapping[idString] = { isAttribute : false, // it's an id: selector originalText : $idNode.text(), domNodes : [ $idNode ] }; } } /* * Helper function to not write the same code over again... */ var processMultipleElements = function (prefix, jqueryPrefix, checkForIds, checkForClasses) { var string; var decompositionKeyPrefix = prefix.replace(':',''); for (i=0; string = decompositionObj[decompositionKeyPrefix + 'Strings'][i++];) { var stringName = string.substring(prefix.length); // keeps the text of the first dom node in the loop below var domNodeText; var domNodesArray = []; var allNodeTextsAreEqual = true; domNodeText = undefined; // note: assigns undefined var k=0, node; NODE: for (; node = $(jqueryPrefix + stringName)[k++];) { var $node = $(node); if (checkForIds) { var nodeId = $node.attr('id'); // skip any node that was previously translated via an id if (typeof nodeId === 'string' && stringsObj.hasOwnProperty('id:' + nodeId)) { continue NODE; } } if (checkForClasses) { // skip any node that was previously translated via a class var nodeClasses = $node.attr('class'); if (typeof nodeClasses === 'string') { var nodeClassArray = nodeClasses.split(' '), nodeClass, j = 0; for(;nodeClass = nodeClassArray[j++];) { if (typeof nodeClass === 'string' && stringsObj.hasOwnProperty('class:' + nodeClass)) { continue NODE; } } } } // make sure this node contains only one text content var nodeContents = $node.contents(); if (nodeContents.length === 0 || nodeContents.length > 1) { $.error('A \'' + string + '\' node was found to contain ' + nodeContents.length + ' child nodes. This node must contain exactly one text node!'); continue; } if (nodeContents[0].nodeType !== 3) { $.error('A \'' + string + '\' node does not contain a #text node (i.e., type 3)'); continue; } // this node is pushable at this point... domNodesArray.push($node); // also check the text is the same across the nodes considered if (typeof domNodeText === 'undefined') { // ... the first time we store the text of the node domNodeText = $node.text(); } else if (domNodeText !== $node.text()) { // ... then we keep checking if the text node is the same allNodeTextsAreEqual = false; } } // end for k loop // make sure that the remaining classes contain the same text if (!allNodeTextsAreEqual) { $.error('Not all text content of elements with ' + string + ' were found to be \'' + domNodeText + '\'. So these elements will be ignored.'); } else { // all good refMapping[string] = { isAttribute : false, // it's a class: or an element: selector originalText : domNodeText, domNodes : domNodesArray }; } } }; // end of processMultipleElements /* * Then go through classes */ processMultipleElements('class:', '.', true, false); /* * Then go through elements */ processMultipleElements('element:', '', true, true); /* * Time to process the attributes */ var firstSelectorStringRegex = new RegExp('(class|id|element):[^:]'); var attrString; for (i=0; attrString = decompositionObj.attributeStrings[i++];) { // let's extract the attribute name from the element selector var splitStringArray = attrString.split("::"); var attributeString = splitStringArray.shift(); // sanity check on the format if (splitStringArray.length === 0) { $.error('sorry, you need to specify class:, element: or id: selectors in ' + attrString); } var selectorString = splitStringArray.join('::'); if (!splitStringArray[0].match(firstSelectorStringRegex)) { $.error(attrString + "Doesn't look right. Perhaps you've added extra semicolons?"); } // turn selector into jQuery selector selectorString = selectorString.replace('id:', '#'); selectorString = selectorString.replace('class:', '.'); selectorString = selectorString.replace('element:', ''); // find DOM nodes var $domNodes = $(selectorString + '[' + attributeString + ']'); if ($domNodes.length === 0) { $.error('The selector "' + attrString + '" does not point to an existing DOM element'); } // avoid using Array.prototype.reduce as it's supported in IE9+ var j = 0, allSameAttributeValue = true; var attributeText = $($domNodes[0]).attr(attributeString); var domNodesToAdd = []; for (j=0; j<$domNodes.length; j++) { // check the placeholder text is all the same var $dom = $($domNodes[j]); if (attributeText !== $dom.attr(attributeString)) { allSameAttributeValue = false; } // also add for later... domNodesToAdd.push($dom); } if (!allSameAttributeValue) { $.error('Not all the attribute values selected via ' + attrString + ' are the same'); } // now we have everything in place, we just add it to the rest! refMapping[attrString] = { isAttribute : true, // yes, we are dealing with an attribute here originalText : attributeText, domNodes : domNodesToAdd }; } /* * Finally find the dom nodes associated to any text searched */ var textString; for (i=0; textString = decompositionObj.textStrings[i++];) { // nodes that will contain the text to translate var textNodesToAdd = []; var allParentNodes = $(':contains(' + textString + ')'); var k, parentNode; for (k=0; parentNode = allParentNodes[k++];) { var nodeContents = $(parentNode).contents(); if (nodeContents.length === 1 && nodeContents[0].nodeType === 3) { textNodesToAdd.push($(parentNode)); } } if (textNodesToAdd.length > 0) { // all good refMapping[textString] = { isAttribute: false, // no it's just another dom element originalText : textString, domNodes : textNodesToAdd }; } } $this.data('refMappingObj', refMapping); return $this; }, /** * Calls the user specified callback (if any), then translates the page. * If the user returned 'false' in his/her callback, the translation is * not performed. * @name _mayTranslate * @function * @access private * @param {string} [languageCode] - the language code to translate to */ '_mayTranslate': function (languageCode) { var $this = this, settings = $this.data('settings'); if (false !== settings.onLanguageSelected(languageCode)) { methods._translate.call($this, languageCode); } }, /** * Returns the code of the language currently selected * @name getSelectedLanguageCode * @function * @access public * @returns {string} [languageCode] - the language code currently selected */ 'getSelectedLanguageCode' : function () { var $this = this; return $this.data('selectedLanguageCode'); }, /** * Translates the current page. * @name translate * @function * @access public * @param {string} [languageCode] - the language to translate to. */ '_translate': function (languageCode) { var $this = this, settings = $this.data('settings'), stringsObj = settings.strings, refMappingObj = $this.data('refMappingObj'); var cssDirection = 'ltr'; if (typeof languageCode !== 'undefined') { // check if the language code exists actually if (!settings.languages.hasOwnProperty(languageCode)) { $.error('The language code ' + languageCode + ' is not defined'); return $this; } // check if we are dealing with a right to left language if (settings.languages[languageCode].hasOwnProperty('cssDirection')) { cssDirection = settings.languages[languageCode].cssDirection; } } // translate everything according to the reference mapping var string; for (string in refMappingObj) { if (refMappingObj.hasOwnProperty(string)) { // get the translation for this string... var translation; if (typeof languageCode === 'undefined' || languageCode === settings.defaultLanguage) { translation = refMappingObj[string].originalText; } else { translation = stringsObj[string][languageCode]; } //console.log("翻译的文本:",translation); //console.log("语言代码:",languageCode); var domNodes = refMappingObj[string].domNodes; var $domNode, i; // attribute case if (refMappingObj[string].isAttribute === true) { var attributeName = string.split("::", 1)[0]; for (i=0; $domNode = domNodes[i++];) { $domNode.attr(attributeName, translation); $domNode.css('direction', cssDirection); } } else { // all other cases for (i=0; $domNode = domNodes[i++];) { $domNode.html(translation); $domNode.css('direction', cssDirection); } } } } return $this; }, /** * Translates according to the widget configuration programmatically. * This is meant to be called by the user. The difference with the * private counterpart _translate method is that the language is * selected in the widget. */ 'translate' : function (languageCode) { var $this = this; methods._translate.call($this, languageCode); // must also select the language when translating via the public method methods._selectLanguage.call($this, languageCode); return $this; }, /** * Destroys the dropdown widget. * * @name destroy * @function * @access public **/ 'destroy' : function () { var $this = this; // remove all data set with .data() $this.removeData(); // unbind events $this.unbind('click.localizationTool', function (e) { methods._onDropdownClicked.call($this, e); }); $this.find('.ltool-language') .unbind('click.localizationTool', function (/*e*/) { methods._onLanguageSelected.call($this, $(this)); }); $this .unbind('mouseout.localizationTool', function (e) { methods._onMouseout.call($this, e); }); // remove markup $this.empty(); return $this; }, /** * Sorts the given array of countryLanguageCodes by country name. * If a language has no name goes to the bottom of the list. * * @name _sortCountryLanguagesByCountryName * @function * @access private * @param {object} languagesDefinition - the array countryLanguageCodes defined during initialization. * @param {array} arrayOfCountryLanguageCodes - the input array countryLanguageCodes. * @returns {array} sortedArrayOfCountryLanguageCodes - the sorted array countryLanguageCodes. */ '_sortCountryLanguagesByCountryName': function (languagesDefinition, arrayOfCountryLanguageCodes) { return arrayOfCountryLanguageCodes.sort(function (a, b) { if (languagesDefinition[a].hasOwnProperty('country') && languagesDefinition[b].hasOwnProperty('country')) { return languagesDefinition[a].country.localeCompare( languagesDefinition[b].country ); } else if (languagesDefinition[a].hasOwnProperty('country')) { return languagesDefinition[a].country.localeCompare( languagesDefinition[b].language ); } // else if (languagesDefinition[b].hasOwnProperty('country')) { return languagesDefinition[a].language.localeCompare( languagesDefinition[b].country ); // } }); }, /** * Goes through each string defined and extracts the common subset of * languages that actually used. The default language is added to this * subset a priori. The resulting list is sorted by country name. * * @name _findSubsetOfUsedLanguages * @function * @access private * @param {object} stringsObj - the strings to translate * @returns {array} usedLanguageCodes - an array of country codes sorted based on country names. */ '_findSubsetOfUsedLanguages' : function (stringsObj) { var $this = this; var string; var settings = $this.data('settings'); // build an histogram of all the used languages in strings var usedLanguagesHistogram = {}; var howManyDifferentStrings = 0; for (string in stringsObj) { if (stringsObj.hasOwnProperty(string)) { var languages = stringsObj[string], language; for (language in languages) { if (languages.hasOwnProperty(language)) { if (!usedLanguagesHistogram.hasOwnProperty(language)) { usedLanguagesHistogram[language] = 0; } } usedLanguagesHistogram[language]++; } howManyDifferentStrings++; } } // find languages that are guaranteed to appear in all strings var guaranteedLanguages = [], languageCode; for (languageCode in usedLanguagesHistogram) { if (usedLanguagesHistogram.hasOwnProperty(languageCode) && usedLanguagesHistogram[languageCode] === howManyDifferentStrings ) { guaranteedLanguages.push(languageCode); } } // delete the default language if it's in the guaranteed languages var defaultIdx = $.inArray(settings.defaultLanguage, guaranteedLanguages); if (defaultIdx > -1) { // delete the default language from the array guaranteedLanguages.splice(defaultIdx, 1); } // add the default language in front guaranteedLanguages.unshift(settings.defaultLanguage); return methods._sortCountryLanguagesByCountryName.call( this, settings.languages, guaranteedLanguages ); }, /** * Initialises the localization tool plugin. * @name init * @function * @param {object} [options] - the user options * @access public * @returns jqueryObject */ 'init' : function(options) { // NOTE: "country" is optional var knownLanguages = { 'en_GB' : { 'country' : 'United Kingdom', 'language': 'English', 'countryTranslated' : 'United Kingdom', 'languageTranslated': 'English', 'flag': { 'class' : 'flag flag-gb' } }, 'de_DE' : { 'country' : 'Germany', 'language' : 'German', 'countryTranslated' : 'Deutschland', 'languageTranslated' : 'Deutsch', 'flag' : { 'class' : 'flag flag-de' } }, 'es_ES' : { 'country' : 'Spain', 'language' : 'Spanish', 'countryTranslated': 'España', 'languageTranslated' : 'Español', 'flag' : { 'class' : 'flag flag-es' } }, 'fr_FR' : { 'country' : 'France', 'language' : 'French', 'countryTranslated' : 'France', 'languageTranslated' : 'Français', 'flag' : { 'class' : 'flag flag-fr' } }, 'ko_KR' : { 'country' : 'Korea, Republic of.', 'language' : 'Korean', 'countryTranslated' : '대한민국', 'languageTranslated' : '한국어', 'flag' : { 'class' : 'flag flag-kr' } }, 'pt_BR' : { 'country' : 'Brazil', 'language' : 'Portuguese', 'countryTranslated': 'Brasil', 'languageTranslated' : 'Português', 'flag' : { 'class' : 'flag flag-br' } }, 'en_AU' : { 'country' : 'Australia', 'language' : 'English', 'countryTranslated' : 'Australia', 'languageTranslated' : 'English', 'flag' : { 'class' : 'flag flag-au' } }, 'en_IN' : { 'country' : 'India', 'language' : 'English', 'countryTranslated': 'India', 'languageTranslated': 'English', 'flag': { 'class' : 'flag flag-in' } }, 'it_IT' : { 'country' : 'Italy', 'language': 'Italian', 'countryTranslated': 'Italia', 'languageTranslated': 'Italiano', 'flag' : { 'class' : 'flag flag-it' } }, 'jp_JP' : { 'country' : 'Japan', 'language': 'Japanese', 'countryTranslated': '日本', 'languageTranslated': '日本語', 'flag' : { 'class' : 'flag flag-jp' } }, 'ar_TN' : { 'country' : 'Tunisia', 'language' : 'Arabic', 'countryTranslated': 'تونس', 'languageTranslated': 'عربي', 'cssDirection': 'rtl', 'flag' : { 'class' : 'flag flag-tn' } }, 'en_IE' : { 'country': 'Ireland', 'language': 'English', 'countryTranslated': 'Ireland', 'languageTranslated' : 'English', 'flag' : { 'class' : 'flag flag-ie' } }, 'nl_NL': { 'country' : 'Netherlands', 'language': 'Dutch', 'countryTranslated' : 'Nederland', 'languageTranslated' : 'Nederlands', 'flag' : { 'class' : 'flag flag-nl' } }, 'zh_CN': { 'country' : 'China', 'language' : 'Simplified Chinese', 'countryTranslated': '中国', 'languageTranslated': '简体中文', 'flag' : { 'class' : 'flag flag-cn' } }, 'zh_TW': { 'country' : 'Taiwan', 'language' : 'Traditional Chinese', 'countryTranslated': '臺灣', 'languageTranslated': '繁體中文', 'flag' : { 'class' : 'flag flag-tw' } }, 'fi_FI': { 'country' : 'Finland', 'language' : 'Finnish', 'countryTranslated' : 'Suomi', 'languageTranslated' : 'Suomi', 'flag' : { 'class' : 'flag flag-fi' } }, 'pt_PT' : { 'country' : 'Portugal', 'language' : 'Portuguese', 'countryTranslated': 'Portugal', 'languageTranslated' : 'Português', 'flag' : { 'class' : 'flag flag-pt' } }, 'pl_PL': { 'country' : 'Poland', 'language': 'Polish', 'countryTranslated' : 'Polska', 'languageTranslated': 'Polski', 'flag' : { 'class' : 'flag flag-pl' } }, 'ru_RU': { 'country' : 'Russia', 'language' : 'Russian', 'languageTranslated': 'Русский', 'countryTranslated' : 'Россия', 'flag': { 'class': 'flag flag-ru' } }, 'hi_IN': { 'country' : 'India', 'language': 'Hindi', 'countryTranslated': 'भारत', 'languageTranslated': 'हिन्द', 'flag': { 'class': 'flag flag-in' } }, 'ta_IN': { 'country' : 'India', 'language': 'Tamil', 'countryTranslated': 'இந்தியா', 'languageTranslated': 'தமிழ்', 'flag': { 'class': 'flag flag-in' } }, 'tr_TR': { 'country' : 'Turkey', 'language' : 'Turkish', 'countryTranslated': 'Türkiye', 'languageTranslated': 'Türkçe', 'flag': { 'class': 'flag flag-tr' } }, 'he_IL': { 'country' : 'Israel', 'language' : 'Hebrew', 'countryTranslated' : 'מדינת ישראל', 'languageTranslated': 'עברית', 'cssDirection': 'rtl', 'flag': { 'class': 'flag flag-il' } }, 'da_DK' : { 'country' : 'Denmark', 'language' : 'Danish', 'countryTranslated': 'Danmark', 'languageTranslated': 'Dansk', 'flag' : { 'class': 'flag flag-dk' } }, 'ro_RO': { 'country' : 'Romania', 'language' : 'Romanian', 'countryTranslated': 'România', 'languageTranslated': 'Român', 'flag' : { 'class': 'flag flag-ro' } }, 'eo' : { // NOTE: no country 'language' : 'Esperanto', 'languageTranslated' : 'Esperanto', 'flag' : { 'class': 'flag flag-esperanto' } } }; var settings = $.extend({ 'defaultLanguage' : 'en_GB', /* do not throw error if a selector doesn't match */ 'ignoreUnmatchedSelectors': false, /* show the flag on the widget */ 'showFlag' : true, /* show the language on the widget */ 'showLanguage': true, /* show the country on the widget */ 'showCountry': true, /* format of the language/country label */ 'labelTemplate': '{{country}} {{(language)}}', 'languages' : { /* * The format here is <country code>_<language code>. * - list of country codes: http://www.gnu.org/software/gettext/manual/html_node/Country-Codes.html * - list of language codes: http://www.gnu.org/software/gettext/manual/html_node/Usual-Language-Codes.html#Usual-Language-Codes */ }, /* * Strings are provided by the user of the plugin. Each entry * in the dictionary has the form: * * [STRING_IDENTIFIER] : { * [LANGUAGE] : [TRANSLATION] * } * * STRING_IDENTIFIER: * id:<html-id-name> OR * class:<html-class-name> OR * element:<html-element-name> OR * <string> * * LANGUAGE: one of the languages defined above (e.g., it_IT) * * TRANSLATION: <string> * */ 'strings' : {}, /* * A callback called whenever the user selects the language * from the dropdown menu. If false is returned, the * translation will not be performed (but just the language * will be selected from the widget). * * The countryLanguageCode is a string representing the * selected language identifier like 'en_GB' */ 'onLanguageSelected' : function (/*countryLanguageCode*/) { return true; } }, options); // add more languages settings.languages = $.extend(knownLanguages, settings.languages); // check that the default language is defined if (!settings.languages.hasOwnProperty(settings.defaultLanguage)) { $.error('FATAL: the default language ' + settings.defaultLanguage + ' is not defined in the \'languages\' parameter!'); } return this.each(function() { // save settings var $this = $(this); $this.data('settings', settings); // language codes common to all translations var activeLanguageCodeArray = methods._findSubsetOfUsedLanguages.call( $this, settings.strings ); $this.data('activeLanguageCodeArray', activeLanguageCodeArray); methods._initializeWidget.call($this, activeLanguageCodeArray); methods._selectLanguage.call($this, settings.defaultLanguage); methods._bindEvents.call($this); methods._buildStringReferenceMapping.call($this); }); } }; var __name__ = 'localizationTool'; /** * jQuery Localization Tool - a jQuery widget to translate web pages * * @memberOf jQuery.fn */ $.fn[__name__] = function(method) { /* * Just a router for method calls */ if (methods[method]) { if (this.data('initialized') === true) { // call a method return methods[method].apply(this, Array.prototype.slice.call(arguments, 1) ); } else { throw new Error('method ' + method + ' called on an uninitialized instance of ' + __name__); } } else if (typeof method === 'object' || !method) { // call init, user passed the settings as parameters this.data('initialized', true); return methods.init.apply(this, arguments); } else { $.error('Cannot call method ' + method); } }; })(jQuery);