| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720 |
- /*
- 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/irrelon/jquery-lang-js
- Changelog: See readme.md
- */
- (function (factory) {
- 'use strict';
- if (typeof define === 'function' && define.amd) {
- define(['jquery', 'Cookies'], factory);
- } else if (typeof exports === 'object') {
- module.exports = factory();
- } else {
- var _OldLang = window.Lang;
- var api = window.Lang = factory(jQuery, typeof Cookies !== 'undefined' ? Cookies : undefined);
- api.noConflict = function () {
- window.Lang = _OldLang;
- return api;
- };
- }
- }(function ($, Cookies) {
- var Lang = function () {
- // Enable firing events
- this._fireEvents = true;
- // Allow storage of dynamic language pack data
- this._dynamic = {};
- };
- /**
- * Initialise the library with the library options.
- * @param {Object} options The options to init the library with.
- * See the readme.md for the details of the options available.
- */
- Lang.prototype.init = function (options) {
- var self = this,
- cookieLang,
- defaultLang,
- currentLang,
- allowCookieOverride;
- options = options || {};
- options.cookie = options.cookie || {};
- defaultLang = options.defaultLang;
- currentLang = options.currentLang;
- allowCookieOverride = options.allowCookieOverride;
- // Set cookie settings
- this.cookieName = options.cookie.name || 'langCookie';
- this.cookieExpiry = options.cookie.expiry || 365;
- this.cookiePath = options.cookie.path || '/';
- // 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) && typeof Cookies !== 'undefined') {
- // Check for an existing language cookie
- cookieLang = Cookies.get(this.cookieName);
- 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',
- 'href'
- ];
- /**
- * 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 () {
- if (callback) {
- callback(true, lang, self._dynamic[lang]);
- }
- throw('Organizr Translation Function: Error loading language pack ' + 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 'hidden':
- case 'reset':
- elem.data('lang-val', elem.val());
- break;
- }
- } else if (elem.is('img')) {
- elem.data('lang-src', elem.attr('src'));
- } 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 in array wrap into
- * object with two properties:
- * - node - which corresponds to text node,
- * - langDefaultText - which remember current data of text node
- * @param elem
- * @returns {Array|*}
- * @private
- */
- Lang.prototype._getTextNodes = function (elem) {
- var nodes = elem.contents(), nodeObjArray = [], nodeObj = {},
- nodeArr, that = this, map = Array.prototype.map;
- $.each(nodes, function (index, node) {
- if (node.nodeType !== 3) {
- return;
- }
- nodeObj = {
- node: node,
- langDefaultText: node.data
- };
- nodeObjArray.push(nodeObj);
- });
- // If element has only one text node and data-lang-token is defined
- // set langContentKey property to use as a token
- if(nodes.length == 1){
- nodeObjArray[0].langToken = elem.data('langToken');
- }
- return nodeObjArray;
- };
- /**
- * Sets text nodes of an element translated based on the passed language.
- * @param elem
- * @param {Array|*} nodes array of objecs with text node and defaultText returned from _getTextNodes
- * @param lang
- * @private
- */
- Lang.prototype._setTextNodes = function (elem, nodes, lang) {
- var index,
- textNode,
- defaultText,
- translation,
- regex,
- langNotDefault = lang !== this.defaultLang;
- for (index = 0; index < nodes.length; index++) {
- textNode = nodes[index];
- if (langNotDefault) {
- // If langToken is set, use it as a token
- defaultText = textNode.langToken || $.trim(textNode.langDefaultText);
- if (defaultText) {
- // Translate the langDefaultText
- translation = this.translate(defaultText, lang);
- // if the text containing HTML tag, processing it
- regex = /<[^>]+>/g;
- if(regex.test(translation)) {
- elem.context.innerHTML = translation;
- }else if (translation) {
- try {
- // Replace the text with the translated version
- textNode.node.data = textNode.node.data.split($.trim(textNode.node.data)).join(translation);
- } catch (e) {
- }
- } else {
- if (console && console.log) {
- console.warn('Organizr Function: Translation for "' + defaultText + '" not found!');
- }
- }
- }
- } else {
- // Replace with original text
- try {
- textNode.node.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 'hidden':
- 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 if (elem.is('img')) {
- if (langNotDefault) {
- // Get the translated value
- translation = this.translate(elem.data('lang-src'), lang);
- // Check we actually HAVE a translation
- if (translation) {
- // Set translated value
- elem.attr('src', translation);
- }
- } else {
- // Set default language value
- elem.attr('src', elem.data('lang-src'));
- }
- } 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
- if (callback) {
- callback('Language pack not defined for: ' + lang, lang, selector);
- }
- throw('Could not change language to ' + lang + ' because no language pack for this language exists!');
- }
- }
- 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 (typeof Cookies !== "undefined") {
- // Set a cookie to remember this language setting with 1 year expiry
- Cookies.set(self.cookieName, lang, {
- expires: self.cookieExpiry,
- path: self.cookiePath
- });
- }
- if (callback) {
- callback(false, lang, selector);
- }
- } else {
- if (callback) {
- callback('No language pack defined for: ' + lang, lang, selector);
- }
- throw('Attempt to change language to "' + lang + '" but no language pack for that language is loaded!');
- }
- };
- 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) {
- if (console && console.log) {
- //console.log('Translation for "' + text + '" not found in language pack: ' + lang + ' Type or copy this string into this console to get a full strings missing output: getLangStrings()');
- //console.log('Translation not found in language pack: ' + lang + ' Type this command into this console to get a full strings missing output: getLangStrings()');
- langStrings['token'][text] = text;
- }
- }
- 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;
- }));
|