- /**
- * @fileOverview Contains the code for jQuery.localizationTool
- *
- * @author Savio Dimatteo <darksmo@gmail.com>
- */
-
- (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);