[MultiLang] Switch Different Languages On The Webpage - jQuery multilang (ok)

https://github.com/Irrelon/jquery-lang-js

C:\xampp\htdocs\windows\index.html

<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<title>jQuery Lang JS Module Test Page</title>
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" type="text/javascript"></script>
	<script src="js/js.cookie.js" charset="utf-8" type="text/javascript"></script>
	<script src="js/jquery-lang.js" charset="utf-8" type="text/javascript"></script>
	<!--<script src="js/langpack/nonDynamic.js" charset="utf-8" type="text/javascript"></script>-->
	<script type="text/javascript">
		var lang = new Lang();
		lang.dynamic('en', 'js/langpack/en.json');
		lang.dynamic('th', 'js/langpack/th.json');
		lang.init({
			defaultLang: 'en'
		});
	</script>
	<style>
		body {
			font-family: Verdana, Geneva, sans-serif;
			font-size: 12px;
		}
		#langChanger {
			margin-bottom: 10px;
		}
		.sectionTitle {
			font-weight: bold;
			font-size: 14px;
			margin-bottom: 10px;
		}
	</style>
</head>
<body>
<a href="http://flattr.com/thing/352558/jQuery-Multi-Language-Site-Plugin" target="_blank">
	<img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this"
			border="0" /></a><BR /><BR />
<div id="langChanger"><a href="#" onclick="window.lang.change('en'); return false;">Switch to English</a> | <a href="#" onclick="window.lang.change('th'); return false;">Switch to Thai</a></div>
<div id="testHtml">
	<div id="searchTitle" class="sectionTitle" lang="en" data-lang-token="propertySearch"></div>
	<div lang="en" data-lang-token="translationWithDataObject" data-lang-default-data='{"firstName": "Amelia", "lastName": "Earhart"}'></div>
	<div id="searchForm">
		<form action="/api/search" method="post" enctype="multipart/form-data">
			<table>
				<tr>
					<td><span lang="en">Location</span>:</td>
				</tr>
				<tr>
					<td><input type="text" name="location" /></td>
				</tr>
				<tr>
					<td>
						<hr />
					</td>
				</tr>
				<tr>
					<td><span lang="en">Budget</span> (with placeholder text):</td>
				</tr>
				<tr>
					<td><input lang="en" type="text" name="budgetFrom" placeholder="Budget" /></td>
				</tr>
				<tr>
					<td><input lang="en" type="text" name="budgetTo" placeholder="Budget" /></td>
				</tr>
				<tr>
					<td>
						<hr />
					</td>
				</tr>
				<tr>
					<td><span lang="en">Min Beds</span>:</td>
				</tr>
				<tr>
					<td>
						<select name="minBeds">
							<option value="0">0</option>
							<option value="1">1</option>
							<option value="2">2</option>
							<option value="3">3</option>
							<option value="4">4</option>
							<option value="5">5</option>
							<option value="6+">6+</option>
						</select>
					</td>
				</tr>
				<tr>
					<td>
						<hr />
					</td>
				</tr>
				<tr>
					<td><span lang="en">Property Type</span>:</td>
				</tr>
				<tr>
					<td>
						<select name="type">
							<option value="all" lang="en">Show All</option>
							<option value="house" lang="en">House</option>
							<option value="townhouse" lang="en">Town House</option>
							<option value="apartment" lang="en">Apartment</option>
							<option value="condo" lang="en">Condominium</option>
							<option value="commercial" lang="en">Commercial</option>
							<option value="office" lang="en">Office</option>
							<option value="storage" lang="en">Storage</option>
							<option value="land" lang="en">Land</option>
						</select>
					</td>
				</tr>
				<tr>
					<td>
						<hr />
					</td>
				</tr>
				<tr>
					<td><span lang="en">Added In</span>:</td>
				</tr>
				<tr>
					<td>
						<select name="added">
							<option value="anytime" lang="en">Anytime</option>
							<option value="24hours" lang="en">Last 24 Hours</option>
							<option value="3days" lang="en">Last 3 Days</option>
							<option value="week" lang="en">Last Week</option>
							<option value="month" lang="en">Last Month</option>
						</select>
					</td>
				</tr>
				<tr>
					<td>
						<textarea placeholder="Anytime" lang="en"></textarea>
					</td>
				</tr>
				<tr>
					<td>
						&lt;div lang=&quot;en&quot;&gt;Last Month &lt;b&gt;Moo&lt;/b&gt; Anytime&lt;/div&gt;:<br><BR>
						<div lang="en">Last Month <b>Moo</b> Anytime</div>
					</td>
				</tr>
				<tr>
					<td>
						&lt;input type=&quot;button&quot;&gt;:
						<input type="button" value="Search..." lang="en" />
					</td>
				</tr>
				<tr>
					<td>
						&lt;button&gt;:
						<button type="button" lang="en" />
						Search...</button>
					</td>
				</tr>
				<tr>
					<td>
						&lt;reset&gt;:
						<button type="reset" lang="en" />
						Search...</button>
					</td>
				</tr>
				<tr>
					<td>
						&lt;img&gt;:
						<img lang="en" data-lang-token="flagImage" src="https://upload.wikimedia.org/wikipedia/en/a/ae/Flag_of_the_United_Kingdom.svg" height="150" />
					</td>
				</tr>
				<tr>
					<td>
						&lt;a&gt;:
						<a lang="en" href="http://www.google.com/">http://www.google.com/</a>
					</td>
				</tr>
				<tr>
					<td>
						Click to test alert text:
						<input type="button" value="Alert!" onclick="alert(window.lang.translate('propertySearch'));" />
					</td>
				</tr>
				<tr>
					<td>
						Click to test loading dynamic content:
						<input type="button" value="Load Dynamic..."
								onclick="$('#dyn-target').append('Something Budget <span lang=\'en\'>Something Budget</span>');" />
						<div id="dyn-target" lang="en"></div>
					</td>
				</tr>
				<tr>
					<td>
						Long text translation with a custom defined token (data-lang-token attribute):<br>
						<div lang="en" data-lang-token="lorem">Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in ...</div>
					</td>
				</tr>
			</table>
		</form>
	</div>
</div>
</body>
</html>

C:\xampp\htdocs\windows\js\jquery-lang.js

/*
 The MIT License (MIT)
 Copyright (c) 2014 Irrelon Software Limited
 https://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("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("langAttr") || {};
				// Add the attribute and value to the store
				attrObj[attr] = elem.attr(attr);
				// Save the attribute data to the store
				elem.data("langAttr", 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("langVal", elem.val());
					break;
			}
		} else if (elem.is("img")) {
			elem.data("langSrc", elem.attr("src"));
		} else {
			// Get the text nodes immediately inside this element
			var nodes = this._getTextNodes(elem);
			if (nodes) {
				elem.data("langText", 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 = [];
		$.each(nodes, function (index, node) {
			if (node.nodeType !== 3) {
				// The node is not a text node, ignore it
				return;
			}
			nodeObjArray.push({
				node: node,
				langDefaultText: node.data
			});
		});
		// If element has only one text node and data-lang-token is defined
		// set langToken property to use as a token
		if (nodeObjArray.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, data) {
		var regexCheckForHtmlTag = /<[^>]+>/g,
			index,
			textNode,
			translation,
			defaultContent,
			token;
		for (index = 0; index < nodes.length; index++) {
			textNode = nodes[index];
			token = textNode.langToken;
			defaultContent = $.trim(textNode.langDefaultText);
			if (token) {
				// We have a language token, use this for translation
				translation = this.translate(token, lang, data);
			} else if (defaultContent) {
				// Translate the langDefaultText
				translation = this.translate(defaultContent, lang, data);
			}
			// If the text contains an HTML tag, process it
			if (regexCheckForHtmlTag.test(translation)) {
				// Set the element's innerHTML to the translated HTML markup
				elem[0].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) {
					// Some browsers (IE *cough*) might have issues, just ignore this if we cannot
					// set a text node's data.
				}
			} else {
				if (console && console.log) {
					console.log("Translation for \"" + defaultContent + "\" not found!");
				}
			}
		}
	};
	/**
	 * Translates and sets the attributes of an element to the passed language.
	 * @param elem
	 * @param lang
	 * @private
	 */
	Lang.prototype._translateAttribs = function (elem, lang, data) {
		var attr,
			attrObj = elem.data("langAttr") || {},
			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, data);
						// 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, data) {
		var langIsDefault = lang === this.defaultLang,
			translation,
			originalContent,
			token;
		//console.log("Translating element", elem, data);
		// Check if the element is an input element
		if (elem.is("input")) {
			switch (elem.attr("type")) {
				case "button":
				case "submit":
				case "hidden":
				case "reset":
					originalContent = elem.data("langVal");
					token = elem.data("langToken") || originalContent;
					// Get the translated value
					translation = this.translate(token, lang, data);
					// Set translated value
					elem.val(translation);
					break;
			}
		} else if (elem.is("img")) {
			originalContent = elem.data("langSrc");
			token = elem.data("langToken") || originalContent;
			// Get the translated value
			translation = this.translate(token, lang, data);
			// Set translated value
			elem.attr("src", translation);
		} else {
			originalContent = elem.data("langText");
			token = elem.data("langToken");
			if (token) {
				translation = this.translate(token, lang, data);
				// Check we actually HAVE a translation
				if (translation) {
					// Set translated value
					elem[0].innerHTML = translation;
				}
			} else if (originalContent) {
				this._setTextNodes(elem, originalContent, lang, data);
			}
		}
	};
	Lang.prototype.getLangDataFromElement = function (elem) {
		return elem.data("langDefaultData");
	};
	/**
	 * 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;
		var langIsDefault = lang === this.defaultLang;
		var packIsLoaded = this.pack[lang];
		var packIsDynamic = this._dynamic[lang];
		if (!langIsDefault && !packIsLoaded && !packIsDynamic) {
			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!");
		}
		// I've commented this for now because we still want back-compatibility with previous versions
		// where we used page content as the "default" language content. In the future we should really
		// use tokens instead of page content.
		/*if (!packIsLoaded && !packIsDynamic) {
			// 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!");
		}*/
		// Check if the language pack is currently loaded
		if (!packIsLoaded && packIsDynamic) {
			// The language pack needs loading first
			//console.log('Loading dynamic language pack: ' + packIsDynamic + '...');
			this.loadPack(lang, function (err, loadingLang, fromUrl) {
				if (err) {
					// Call the callback with the error
					if (callback) {
						callback("Language pack could not load from: " + fromUrl, lang, selector);
					}
					return;
				}
				// Process the change language request
				self.change.call(self, lang, selector, callback);
			});
			return;
		}
		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.data("langScanned") || elem.attr("lang") !== lang) {
				elem.data("langScanned", true);
				this._translateElement(elem, lang, this.getLangDataFromElement(elem));
			}
		}
		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);
		}
	};
	Lang.prototype._translateElement = function (elem, lang, data) {
		// Translate attributes
		this._translateAttribs(elem, lang, data);
		// Translate content
		var contentInAttribute = elem.attr("data-lang-content");
		if (contentInAttribute !== "false") {
			this._translateContent(elem, lang, data);
		}
		// Update the element's current language
		elem.attr("lang", lang);
	};
	Lang.prototype.getDataAtPath = function (obj, path, defaultVal) {
		var self = this,
			internalPath = path,
			objPart;
		if (path instanceof Array) {
			// Cannot use map because of IE8
			var returnData = [];
			for (var i = 0; i < path.length; i++) {
				var individualPath = path[i];
				returnData.push(self.getDataAtPath(obj, individualPath));
			}
			return returnData;
		}
		// No object data, return undefined
		if (obj === undefined || obj === null) {
			return defaultVal;
		}
		// No path string, return the base obj
		if (!internalPath) {
			return obj;
		}
		// Path is not a string, throw error
		if (typeof internalPath !== "string") {
			throw new Error("Path argument must be a string");
		}
		// Path has no dot-notation, return key/value
		if (internalPath.indexOf(".") === -1) {
			return obj[internalPath] !== undefined ? obj[internalPath] : defaultVal;
		}
		if (typeof obj !== "object") {
			return defaultVal !== undefined ? defaultVal : undefined;
		}
		const pathParts = internalPath.split(".");
		objPart = obj;
		for (var i = 0; i < pathParts.length; i++) {
			var pathPart = pathParts[i];
			objPart = objPart[pathPart];
			if (!objPart || typeof objPart !== "object") {
				if (i !== pathParts.length - 1) {
					// The path terminated in the object before we reached
					// the end node we wanted so make sure we return undefined
					objPart = undefined;
				}
				break;
			}
		}
		return objPart !== undefined ? objPart : defaultVal;
	};
	Lang.prototype._parseTemplate = function (str, data) {
		if (!str || !data) return str;
		var finalTranslation = str;
		if (str && str.indexOf("${")) {
			// Break out the data requests and render the data in place of tokens
			var regexp = /\${([\s\S]+?)}/g;
			var matches;
			while ((matches = regexp.exec(str))) {
				var matchString = matches[0];
				var dataPath = matches[1];
				finalTranslation = finalTranslation.replace(matchString, this.getDataAtPath(data, dataPath));
			}
		}
		return finalTranslation;
	};
	/**
	 * 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, data) {
		var translation = "",
			langIsDefault = lang === this.defaultLang;
		lang = lang || this.currentLang;
		if (!this.pack[lang]) {
			//console.log("No language pack is loaded for language: ", lang);
			return text;
		}
		// Check for a direct token translation
		translation = this._parseTemplate(this.pack[lang].token[text], {data: data});
		if (!translation) {
			// No token translation was found, test for regex match
			translation = this._regexMatch(text, lang);
		}
		if (!translation && !langIsDefault) {
			if (console && console.log) {
				console.log("Translation for \"" + text + "\" not found in language pack: " + lang);
			}
		}
		return translation || 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),
			data;
		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
			this._translateElement(rootElem, this.defaultLang, this.getLangDataFromElement(rootElem));
			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, this.getLangDataFromElement(rootElem));
		}
		// 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;
}));

C:\xampp\htdocs\windows\js\js.cookie.js

/*!
 * JavaScript Cookie v2.0.4
 * https://github.com/js-cookie/js-cookie
 *
 * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
 * Released under the MIT license
 */
(function(factory) {
  if (typeof define === 'function' && define.amd) {
    define(factory);
  } else if (typeof exports === 'object') {
    module.exports = factory();
  } else {
    var _OldCookies = window.Cookies;
    var api = window.Cookies = factory();
    api.noConflict = function() {
      window.Cookies = _OldCookies;
      return api;
    };
  }
}(function() {
  function extend() {
    var i = 0;
    var result = {};
    for (; i < arguments.length; i++) {
      var attributes = arguments[i];
      for (var key in attributes) {
        result[key] = attributes[key];
      }
    }
    return result;
  }
  function init(converter) {
    function api(key, value, attributes) {
      var result;
      // Write
      if (arguments.length > 1) {
        attributes = extend({
          path: '/'
        }, api.defaults, attributes);
        if (typeof attributes.expires === 'number') {
          var expires = new Date();
          expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
          attributes.expires = expires;
        }
        try {
          result = JSON.stringify(value);
          if (/^[\{\[]/.test(result)) {
            value = result;
          }
        } catch (e) {}
        if (!converter.write) {
          value = encodeURIComponent(String(value))
            .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
        } else {
          value = converter.write(value, key);
        }
        key = encodeURIComponent(String(key));
        key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
        key = key.replace(/[\(\)]/g, escape);
        return (document.cookie = [
          key, '=', value,
          attributes.expires && '; expires=' + attributes.expires.toUTCString(), // use expires attribute, max-age is not supported by IE
          attributes.path && '; path=' + attributes.path,
          attributes.domain && '; domain=' + attributes.domain,
          attributes.secure ? '; secure' : ''
        ].join(''));
      }
      // Read
      if (!key) {
        result = {};
      }
      // To prevent the for loop in the first place assign an empty array
      // in case there are no cookies at all. Also prevents odd result when
      // calling "get()"
      var cookies = document.cookie ? document.cookie.split('; ') : [];
      var rdecode = /(%[0-9A-Z]{2})+/g;
      var i = 0;
      for (; i < cookies.length; i++) {
        var parts = cookies[i].split('=');
        var name = parts[0].replace(rdecode, decodeURIComponent);
        var cookie = parts.slice(1).join('=');
        if (cookie.charAt(0) === '"') {
          cookie = cookie.slice(1, -1);
        }
        try {
          cookie = converter.read ?
            converter.read(cookie, name) : converter(cookie, name) ||
            cookie.replace(rdecode, decodeURIComponent);
          if (this.json) {
            try {
              cookie = JSON.parse(cookie);
            } catch (e) {}
          }
          if (key === name) {
            result = cookie;
            break;
          }
          if (!key) {
            result[name] = cookie;
          }
        } catch (e) {}
      }
      return result;
    }
    api.get = api.set = api;
    api.getJSON = function() {
      return api.apply({
        json: true
      }, [].slice.call(arguments));
    };
    api.defaults = {};
    api.remove = function(key, attributes) {
      api(key, '', extend(attributes, {
        expires: -1
      }));
    };
    api.withConverter = init;
    return api;
  }
  return init(function() {});
}));

C:\xampp\htdocs\windows\js\langpack\en.json

{
	"token": {
		"propertySearch": "Property Search",
		"flagImage": "https://upload.wikimedia.org/wikipedia/en/a/ae/Flag_of_the_United_Kingdom.svg",
		"lorem": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in ...",
		"translationWithDataObject": "My dynamic user data is First Name: ${data.firstName} and Last Name: ${data.lastName}",
		"translationWithDataString": "My dynamic string is ${data}!",
		"translationWithDataArray": "My dynamic string is ${data.0}, ${data.1}!"
	}
}

C:\xampp\htdocs\windows\js\langpack\th.json

{
	"token": {
		"propertySearch": "ค้นหา",
		"Location": "สถานที่ตั้ง",
		"Min Beds": "จำนวนห้องนอน",
		"Property Type": "ชนิดที่อยู่อาศัย",
		"Added In": "ลงประกาศเมื่อ",
		"Show All": "แสดงผลทั้งหมด",
		"House": "บ้าน",
		"Town House": "ทาวน์เฮ้าส์",
		"Apartment": "อพาร์ทเม้นท์",
		"Condominium": "คอนโดมิเนียม",
		"Commercial": "อาคารพาณิชย์",
		"Office": "ออฟฟิศ",
		"Storage": "โกดังเก็บของ",
		"Land": "ที่ดินว่างเปล่า",
		"Anytime": "แสดงผลทั้งหมด",
		"Last 24 Hours": "ชั่วโมงที่ผ่านมา",
		"Last 3 Days": "วันที่ผ่านมา",
		"Last Week": "สัปดาห์ที่ผ่านมา",
		"Last Month": "เดือนที่ผ่านมา",
		"Loading": "กำลังประมวลผล",
		"My Saved Searches": "การค้นหาที่ถูกเก็บไว้",
		"My Listings": "รายการของฉัน",
		"My Analytics": "สถิติผู้เข้าชมประกาศของฉัน",
		"Search Buy": "ซื้อ",
		"Search Rent": "เช่า",
		"Create New Listing": "ลงประกาศ",
		"View My Account": "ดูข้อมูลของฉัน",
		"Property Listing Details": "รายละเอียดประกาศ",
		"Listing Type": "ชนิดของประกาศ",
		"Sell": "ขาย",
		"Let": "เช่า",
		"Lease": "เซ้ง",
		"Floors": "ชั้น",
		"Rooms Total": "จำนวนห้องทั้งหมด",
		"Bedrooms": "ห้องนอน",
		"Bathrooms": "ห้องน้ำ",
		"Kitchens": "ครัว",
		"Property Price and Terms": "ราคาและรูปแบบสัญญา",
		"Price": "ราคา",
		"Term": "สัญญา",
		"Freehold": "มีกรรมสิทธิ์",
		"Leasehold": "มีระยะสัญญา",
		"Possession": "บ้านว่างหรือไม่",
		"Onward Chain": "มีผู้อาศัยอยู่",
		"Vacant Possession": "บ้านว่างพร้อมเข้าอยู่ทันที",
		"Property Location": "สถานที่ตั้ง",
		"Country": "ประเทศ",
		"Post/Zip Code": "รหัสไปรษณีย์",
		"No.": "เลขที่",
		"Road Name": "ชื่อถนน",
		"County/Province": "จังหวัด",
		"Town/City": "อำเภอ",
		"Lease Years Remaining": "จำนวนปีของสัญญาที่เหลือ",
		"day": "วัน",
		"week": "สัปดาห์",
		"month": "เดือน",
		"quarter": "3 เดือน",
		"year": "ปี",
		"Submit Your Listing": "ลงประกาศ",
		"Upload Image": "ใส่รูป",
		"Finish": "เสร็จเรียบร้อย",
		"Search": "ค้นหา",
		"Search...": "ค้นหา",
		"Property Description": "รายละเอียดของประกาศ",
		"Synopsis": "รายละเอียดย่อในประกาศ",
		"Full Description": "รายละเอียดเพิ่มเติมสำหรับผู้สนใจประกาศ",
		"Property Photos": "รูปภาพสถานที่",
		"http://www.google.com/": "http://www.yahoo.com/",
		"flagImage": "https://upload.wikimedia.org/wikipedia/commons/a/a9/Flag_of_Thailand.svg",
		"lorem": "ตรงกันข้ามกับความเชื่อที่นิยมกัน Lorem Ipsum ไม่ได้เป็นเพียงแค่ชุดตัวอักษรที่สุ่มขึ้นมามั่วๆ แต่หากมีที่มาจากวรรณกรรมละตินคลาสสิกชิ้นหนึ่งในยุค  ...",
		"translationWithDataObject": "ชื่อจริง: ${data.firstName} นามสกุล: ${data.lastName}",
		"translationWithDataString": "My dynamic string is {data}!"
	},
	"regex": [
		[
			"Budget",
			"งบประมาณ"
		],
		[
			"^Something at start of text",
			""
		],
		[
			"This will be case insensitive",
			"i",
			""
		]
	]
}

C:\xampp\htdocs\windows\js\langpack\nonDynamic.js

Lang.prototype.pack.th = {
	"token": {
		"Property Search":"ค้นหา",
		"Location":"สถานที่ตั้ง",
		"Min Beds":"จำนวนห้องนอน",
		"Property Type":"ชนิดที่อยู่อาศัย",
		"Added In":"ลงประกาศเมื่อ",
		"Show All":"แสดงผลทั้งหมด",
		"House":"บ้าน",
		"Town House":"ทาวน์เฮ้าส์",
		"Apartment":"อพาร์ทเม้นท์",
		"Condominium":"คอนโดมิเนียม",
		"Commercial":"อาคารพาณิชย์",
		"Office":"ออฟฟิศ",
		"Storage":"โกดังเก็บของ",
		"Land":"ที่ดินว่างเปล่า",
		"Anytime":"แสดงผลทั้งหมด",
		"Last 24 Hours":"ชั่วโมงที่ผ่านมา",
		"Last 3 Days":"วันที่ผ่านมา",
		"Last Week":"สัปดาห์ที่ผ่านมา",
		"Last Month":"เดือนที่ผ่านมา",
		"Loading":"กำลังประมวลผล",
		"My Saved Searches":"การค้นหาที่ถูกเก็บไว้",
		"My Listings":"รายการของฉัน",
		"My Analytics":"สถิติผู้เข้าชมประกาศของฉัน",
		"Search Buy":"ซื้อ",
		"Search Rent":"เช่า",
		"Create New Listing":"ลงประกาศ",
		"View My Account":"ดูข้อมูลของฉัน",
		"Property Listing Details":"รายละเอียดประกาศ",
		"Listing Type":"ชนิดของประกาศ",
		"Sell":"ขาย",
		"Let":"เช่า",
		"Lease":"เซ้ง",
		"Floors":"ชั้น",
		"Rooms Total":"จำนวนห้องทั้งหมด",
		"Bedrooms":"ห้องนอน",
		"Bathrooms":"ห้องน้ำ",
		"Kitchens":"ครัว",
		"Property Price and Terms":"ราคาและรูปแบบสัญญา",
		"Price":"ราคา",
		"Term":"สัญญา",
		"Freehold":"มีกรรมสิทธิ์",
		"Leasehold":"มีระยะสัญญา",
		"Possession":"บ้านว่างหรือไม่",
		"Onward Chain":"มีผู้อาศัยอยู่",
		"Vacant Possession":"บ้านว่างพร้อมเข้าอยู่ทันที",
		"Property Location":"สถานที่ตั้ง",
		"Country":"ประเทศ",
		"Post/Zip Code":"รหัสไปรษณีย์",
		"No.":"เลขที่",
		"Road Name":"ชื่อถนน",
		"County/Province":"จังหวัด",
		"Town/City":"อำเภอ",
		"Lease Years Remaining":"จำนวนปีของสัญญาที่เหลือ",
		"day":"วัน",
		"week":"สัปดาห์",
		"month":"เดือน",
		"quarter":"3 เดือน",
		"year":"ปี",
		"Submit Your Listing":"ลงประกาศ",
		"Upload Image":"ใส่รูป",
		"Finish":"เสร็จเรียบร้อย",
		"Search":"ค้นหา",
		"Search...":"ค้นหา",
		"Property Description":"รายละเอียดของประกาศ",
		"Synopsis":"รายละเอียดย่อในประกาศ",
		"Full Description":"รายละเอียดเพิ่มเติมสำหรับผู้สนใจประกาศ",
		"Property Photos":"รูปภาพสถานที่"
	},
	"regex": [
		[/Budget/, "งบประมาณ"],
		[/^Something at start of text/, ""],
		[/This will be case insensitive/i, ""]
	]
};

Last updated

Navigation

Lionel

@Copyright 2023