123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- /*
- The MIT License (MIT)
- Copyright (c) 2014 Irrelon Software Limited
- http://www.irrelon.com
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice, url and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- Source: https://github.com/coolbloke1324/jquery-lang-js
- Changelog:
- Version 2.0.0 - Complete re-write.
- */
- var Lang = (function () {
- var Lang = function (defaultLang, currentLang, allowCookieOverride) {
- var self = this,
- cookieLang;
-
- // Enable firing events
- this._fireEvents = true;
-
- // Allow storage of dynamic language pack data
- this._dynamic = {};
-
- // Store existing mutation methods so we can auto-run
- // translations when new data is added to the page
- this._mutationCopies = {
- append: $.fn.append,
- appendTo: $.fn.appendTo,
- prepend: $.fn.prepend,
- before: $.fn.before,
- after: $.fn.after,
- html: $.fn.html
- };
-
- // Now override the existing mutation methods with our own
- $.fn.append = function () { return self._mutation(this, 'append', arguments) };
- $.fn.appendTo = function () { return self._mutation(this, 'appendTo', arguments) };
- $.fn.prepend = function () { return self._mutation(this, 'prepend', arguments) };
- $.fn.before = function () { return self._mutation(this, 'before', arguments) };
- $.fn.after = function () { return self._mutation(this, 'after', arguments) };
- $.fn.html = function () { return self._mutation(this, 'html', arguments) };
-
- // Set default and current language to the default one
- // to start with
- this.defaultLang = defaultLang || 'en';
- this.currentLang = defaultLang || 'en';
-
- // Check for cookie support when no current language is specified
- if ((allowCookieOverride || !currentLang) && $.cookie) {
- // Check for an existing language cookie
- cookieLang = $.cookie('langCookie');
-
- if (cookieLang) {
- // We have a cookie language, set the current language
- currentLang = cookieLang;
- }
- }
- $(function () {
- // Setup data on the language items
- self._start();
-
- // Check if the current language is not the same as our default
- if (currentLang && currentLang !== self.defaultLang) {
- // Switch to the current language
- self.change(currentLang);
- }
- })
- };
- /**
- * Object that holds the language packs.
- * @type {{}}
- */
- Lang.prototype.pack = {};
- /**
- * Array of translatable attributes to check for on elements.
- * @type {string[]}
- */
- Lang.prototype.attrList = [
- 'title',
- 'alt',
- 'placeholder'
- ];
- /**
- * Defines a language pack that can be dynamically loaded and the
- * path to use when doing so.
- * @param {String} lang The language two-letter iso-code.
- * @param {String} path The path to the language pack js file.
- */
- Lang.prototype.dynamic = function (lang, path) {
- if (lang !== undefined && path !== undefined) {
- this._dynamic[lang] = path;
- }
- };
- /**
- * Loads a new language pack for the given language.
- * @param {string} lang The language to load the pack for.
- * @param {Function=} callback Optional callback when the file has loaded.
- */
- Lang.prototype.loadPack = function (lang, callback) {
- var self = this;
-
- if (lang && self._dynamic[lang]) {
- $.ajax({
- dataType: "json",
- url: self._dynamic[lang],
- success: function (data) {
- self.pack[lang] = data;
-
- // Process the regex list
- if (self.pack[lang].regex) {
- var packRegex = self.pack[lang].regex,
- regex,
- i;
-
- for (i = 0; i < packRegex.length; i++) {
- regex = packRegex[i];
- if (regex.length === 2) {
- // String, value
- regex[0] = new RegExp(regex[0]);
- } else if (regex.length === 3) {
- // String, modifiers, value
- regex[0] = new RegExp(regex[0], regex[1]);
-
- // Remove modifier
- regex.splice(1, 1);
- }
- }
- }
-
- console.log('Loaded language pack: ' + self._dynamic[lang]);
- if (callback) { callback(false, lang, self._dynamic[lang]); }
- },
- error: function () {
- console.log('Error loading language pack' + self._dynamic[lang]);
- if (callback) { callback(true, lang, self._dynamic[lang]); }
- }
- });
- } else {
- throw('Cannot load language pack, no file path specified!');
- }
- };
- /**
- * Scans the DOM for elements with [lang] selector and saves translate data
- * for them for later use.
- * @private
- */
- Lang.prototype._start = function (selector) {
- // Get the page HTML
- var arr = selector !== undefined ? $(selector).find('[lang]') : $(':not(html)[lang]'),
- arrCount = arr.length,
- elem;
- while (arrCount--) {
- elem = $(arr[arrCount]);
- this._processElement(elem);
- }
- };
-
- Lang.prototype._processElement = function (elem) {
- // Only store data if the element is set to our default language
- if (elem.attr('lang') === this.defaultLang) {
- // Store translatable attributes
- this._storeAttribs(elem);
-
- // Store translatable content
- this._storeContent(elem);
- }
- };
- /**
- * Stores the translatable attribute values in their default language.
- * @param {object} elem The jQuery selected element.
- * @private
- */
- Lang.prototype._storeAttribs = function (elem) {
- var attrIndex,
- attr,
- attrObj;
- for (attrIndex = 0; attrIndex < this.attrList.length; attrIndex++) {
- attr = this.attrList[attrIndex];
- if (elem.attr(attr)) {
- // Grab the existing attribute store or create a new object
- attrObj = elem.data('lang-attr') || {};
- // Add the attribute and value to the store
- attrObj[attr] = elem.attr(attr);
- // Save the attribute data to the store
- elem.data('lang-attr', attrObj);
- }
- }
- };
- /**
- * Reads the existing content from the element and stores it for
- * later use in translation.
- * @param elem
- * @private
- */
- Lang.prototype._storeContent = function (elem) {
- // Check if the element is an input element
- if (elem.is('input')) {
- switch (elem.attr('type')) {
- case 'button':
- case 'submit':
- case 'reset':
- elem.data('lang-val', elem.val());
- break;
- }
- } else {
- // Get the text nodes immediately inside this element
- var nodes = this._getTextNodes(elem);
- if (nodes) {
- elem.data('lang-text', nodes);
- }
- }
- };
- /**
- * Retrieves the text nodes from an element and returns them.
- * @param elem
- * @returns {Array|*}
- * @private
- */
- Lang.prototype._getTextNodes = function (elem) {
- var nodes = elem.contents(),
- nodeArr;
-
- nodeArr = nodes.filter(function () {
- this.langDefaultText = this.data;
- return this.nodeType === 3;
- });
-
- return nodeArr;
- };
- /**
- * Sets text nodes of an element translated based on the passed language.
- * @param elem
- * @param nodes
- * @param lang
- * @private
- */
- Lang.prototype._setTextNodes = function (elem, nodes, lang) {
- var index,
- textNode,
- defaultText,
- translation,
- langNotDefault = lang !== this.defaultLang;
-
- for (index = 0; index < nodes.length; index++) {
- textNode = nodes[index];
-
- if (langNotDefault) {
- defaultText = $.trim(textNode.langDefaultText);
-
- if (defaultText) {
- // Translate the langDefaultText
- translation = this.translate(defaultText, lang);
-
- if (translation) {
- try {
- // Replace the text with the translated version
- textNode.data = textNode.data.split($.trim(textNode.data)).join(translation);
- } catch (e) {
-
- }
- } else {
- console.log('Translation for "' + defaultText + '" not found!');
- }
- }
- } else {
- // Replace with original text
- try {
- textNode.data = textNode.langDefaultText;
- } catch (e) {
-
- }
- }
- }
- };
- /**
- * Translates and sets the attributes of an element to the passed language.
- * @param elem
- * @param lang
- * @private
- */
- Lang.prototype._translateAttribs = function (elem, lang) {
- var attr,
- attrObj = elem.data('lang-attr') || {},
- translation;
- for (attr in attrObj) {
- if (attrObj.hasOwnProperty(attr)) {
- // Check the element still has the attribute
- if (elem.attr(attr)) {
- if (lang !== this.defaultLang) {
- // Get the translated value
- translation = this.translate(attrObj[attr], lang);
- // Check we actually HAVE a translation
- if (translation) {
- // Change the attribute to the translated value
- elem.attr(attr, translation);
- }
- } else {
- // Set default language value
- elem.attr(attr, attrObj[attr]);
- }
- }
- }
- }
- };
- /**
- * Translates and sets the contents of an element to the passed language.
- * @param elem
- * @param lang
- * @private
- */
- Lang.prototype._translateContent = function (elem, lang) {
- var langNotDefault = lang !== this.defaultLang,
- translation,
- nodes;
- // Check if the element is an input element
- if (elem.is('input')) {
- switch (elem.attr('type')) {
- case 'button':
- case 'submit':
- case 'reset':
- if (langNotDefault) {
- // Get the translated value
- translation = this.translate(elem.data('lang-val'), lang);
- // Check we actually HAVE a translation
- if (translation) {
- // Set translated value
- elem.val(translation);
- }
- } else {
- // Set default language value
- elem.val(elem.data('lang-val'));
- }
- break;
- }
- } else {
- // Set text node translated text
- nodes = elem.data('lang-text');
- if (nodes) {
- this._setTextNodes(elem, nodes, lang);
- }
- }
- };
- /**
- * Call this to change the current language on the page.
- * @param {String} lang The new two-letter language code to change to.
- * @param {String=} selector Optional selector to find language-based
- * elements for updating.
- * @param {Function=} callback Optional callback function that will be
- * called once the language change has been successfully processed. This
- * is especially useful if you are using dynamic language pack loading
- * since you will get a callback once it has been loaded and changed.
- * Your callback will be passed three arguments, a boolean to denote if
- * there was an error (true if error), the second will be the language
- * you passed in the change call (the lang argument) and the third will
- * be the selector used in the change update.
- */
- Lang.prototype.change = function (lang, selector, callback) {
- var self = this;
-
- if (lang === this.defaultLang || this.pack[lang] || this._dynamic[lang]) {
- // Check if the language pack is currently loaded
- if (lang !== this.defaultLang) {
- if (!this.pack[lang] && this._dynamic[lang]) {
- // The language pack needs loading first
- console.log('Loading dynamic language pack: ' + this._dynamic[lang] + '...');
- this.loadPack(lang, function (err, loadingLang, fromUrl) {
- if (!err) {
- // Process the change language request
- self.change.call(self, lang, selector, callback);
- } else {
- // Call the callback with the error
- if (callback) { callback('Language pack could not load from: ' + fromUrl, lang, selector); }
- }
- });
-
- return;
- } else if (!this.pack[lang] && !this._dynamic[lang]) {
- // Pack not loaded and no dynamic entry
- console.log('Could not change language to ' + lang + ' because no language pack for this language exists!');
- if (callback) { callback('Language pack not defined for: ' + lang, lang, selector); }
- }
- }
-
- var fireAfterUpdate = false,
- currLang = this.currentLang;
-
- if (this.currentLang != lang) {
- this.beforeUpdate(currLang, lang);
- fireAfterUpdate = true;
- }
-
- this.currentLang = lang;
-
- // Get the page HTML
- var arr = selector !== undefined ? $(selector).find('[lang]') : $(':not(html)[lang]'),
- arrCount = arr.length,
- elem;
-
- while (arrCount--) {
- elem = $(arr[arrCount]);
-
- if (elem.attr('lang') !== lang) {
- this._translateElement(elem, lang);
- }
- }
-
- if (fireAfterUpdate) {
- this.afterUpdate(currLang, lang);
- }
-
- // Check for cookie support
- if ($.cookie) {
- // Set a cookie to remember this language setting with 1 year expiry
- $.cookie('langCookie', lang, {
- expires: 365,
- path: '/'
- });
- }
-
- if (callback) { callback(false, lang, selector); }
- } else {
- console.log('Attempt to change language to "' + lang + '" but no language pack for that language is loaded!');
- if (callback) { callback('No language pack defined for: ' + lang, lang, selector); }
- }
- };
-
- Lang.prototype._translateElement = function (elem, lang) {
- // Translate attributes
- this._translateAttribs(elem, lang);
- // Translate content
- if (elem.attr('data-lang-content') != 'false') {
- this._translateContent(elem, lang);
- }
- // Update the element's current language
- elem.attr('lang', lang);
- };
- /**
- * Translates text from the default language into the passed language.
- * @param {String} text The text to translate.
- * @param {String} lang The two-letter language code to translate to.
- * @returns {*}
- */
- Lang.prototype.translate = function (text, lang) {
- lang = lang || this.currentLang;
-
- if (this.pack[lang]) {
- var translation = '';
-
- if (lang != this.defaultLang) {
- // Check for a direct token translation
- translation = this.pack[lang].token[text];
-
- if (!translation) {
- // No token translation was found, test for regex match
- translation = this._regexMatch(text, lang);
- }
-
- if (!translation) {
- console.log('Translation for "' + text + '" not found in language pack: ' + lang);
- }
-
- return translation || text;
- } else {
- return text;
- }
- } else {
- return text;
- }
- };
- /**
- * Checks the regex items for a match against the passed text and
- * if a match is made, translates to the given replacement.
- * @param {String} text The text to test regex matches against.
- * @param {String} lang The two-letter language code to translate to.
- * @returns {string}
- * @private
- */
- Lang.prototype._regexMatch = function (text, lang) {
- // Loop the regex array and test them against the text
- var arr,
- arrCount,
- arrIndex,
- item,
- regex,
- expressionResult;
-
- arr = this.pack[lang].regex;
-
- if (arr) {
- arrCount = arr.length;
-
- for (arrIndex = 0; arrIndex < arrCount; arrIndex++) {
- item = arr[arrIndex];
- regex = item[0];
-
- // Test regex
- expressionResult = regex.exec(text);
-
- if (expressionResult && expressionResult[0]) {
- return text.split(expressionResult[0]).join(item[1]);
- }
- }
- }
- return '';
- };
- Lang.prototype.beforeUpdate = function (currentLang, newLang) {
- if (this._fireEvents) {
- $(this).triggerHandler('beforeUpdate', [currentLang, newLang, this.pack[currentLang], this.pack[newLang]]);
- }
- };
-
- Lang.prototype.afterUpdate = function (currentLang, newLang) {
- if (this._fireEvents) {
- $(this).triggerHandler('afterUpdate', [currentLang, newLang, this.pack[currentLang], this.pack[newLang]]);
- }
- };
-
- Lang.prototype.refresh = function () {
- // Process refresh on the page
- this._fireEvents = false;
- this.change(this.currentLang);
- this._fireEvents = true;
- };
-
- ////////////////////////////////////////////////////
- // Mutation overrides
- ////////////////////////////////////////////////////
- Lang.prototype._mutation = function (context, method, args) {
- var result = this._mutationCopies[method].apply(context, args),
- currLang = this.currentLang,
- rootElem = $(context);
-
- if (rootElem.attr('lang')) {
- // Switch off events for the moment
- this._fireEvents = false;
-
- // Check if the root element is currently set to another language from current
- //if (rootElem.attr('lang') !== this.currentLang) {
- this._translateElement(rootElem, this.defaultLang);
- this.change(this.defaultLang, rootElem);
-
- // Calling change above sets the global currentLang but this is supposed to be
- // an isolated change so reset the global value back to what it was before
- this.currentLang = currLang;
-
- // Record data on the default language from the root element
- this._processElement(rootElem);
-
- // Translate the root element
- this._translateElement(rootElem, this.currentLang);
- //}
- }
-
- // Record data on the default language from the root's children
- this._start(rootElem);
-
- // Process translation on any child elements of this element
- this.change(this.currentLang, rootElem);
-
- // Switch events back on
- this._fireEvents = true;
-
- return result;
- };
- return Lang;
- })();
|