/files/i18next/1.6.0/i18next-1.6.0.js
JavaScript | 1426 lines | 921 code | 126 blank | 379 comment | 236 complexity | 7797d8d8a63740443ca95ff8495ec0ce MD5 | raw file
- // i18next, v1.6.0
- // Copyright (c)2013 Jan Mühlemann (jamuhl).
- // Distributed under MIT license
- // http://i18next.com
- (function() {
- // add indexOf to non ECMA-262 standard compliant browsers
- if (!Array.prototype.indexOf) {
- Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
- "use strict";
- if (this == null) {
- throw new TypeError();
- }
- var t = Object(this);
- var len = t.length >>> 0;
- if (len === 0) {
- return -1;
- }
- var n = 0;
- if (arguments.length > 0) {
- n = Number(arguments[1]);
- if (n != n) { // shortcut for verifying if it's NaN
- n = 0;
- } else if (n != 0 && n != Infinity && n != -Infinity) {
- n = (n > 0 || -1) * Math.floor(Math.abs(n));
- }
- }
- if (n >= len) {
- return -1;
- }
- var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
- for (; k < len; k++) {
- if (k in t && t[k] === searchElement) {
- return k;
- }
- }
- return -1;
- }
- }
- var root = this
- , $ = root.jQuery
- , i18n = {}
- , resStore = {}
- , currentLng
- , replacementCounter = 0
- , languages = [];
- // Export the i18next object for **CommonJS**.
- // If we're not in CommonJS, add `i18n` to the
- // global object or to jquery.
- if (typeof module !== 'undefined' && module.exports) {
- module.exports = i18n;
- } else {
- if ($) {
- $.i18n = $.i18n || i18n;
- }
-
- root.i18n = root.i18n || i18n;
- }
- // defaults
- var o = {
- lng: undefined,
- load: 'all',
- preload: [],
- lowerCaseLng: false,
- returnObjectTrees: false,
- fallbackLng: 'dev',
- detectLngQS: 'setLng',
- ns: 'translation',
- fallbackToDefaultNS: false,
- nsseparator: ':',
- keyseparator: '.',
- selectorAttr: 'data-i18n',
- debug: false,
-
- resGetPath: 'locales/__lng__/__ns__.json',
- resPostPath: 'locales/add/__lng__/__ns__',
-
- getAsync: true,
- postAsync: true,
-
- resStore: undefined,
- useLocalStorage: false,
- localStorageExpirationTime: 7*24*60*60*1000,
-
- dynamicLoad: false,
- sendMissing: false,
- sendMissingTo: 'fallback', // current | all
- sendType: 'POST',
-
- interpolationPrefix: '__',
- interpolationSuffix: '__',
- reusePrefix: '$t(',
- reuseSuffix: ')',
- pluralSuffix: '_plural',
- pluralNotFound: ['plural_not_found', Math.random()].join(''),
- contextNotFound: ['context_not_found', Math.random()].join(''),
-
- setJqueryExt: true,
- defaultValueFromContent: true,
- useDataAttrOptions: false,
- cookieExpirationTime: undefined,
- useCookie: true,
- cookieName: 'i18next',
-
- postProcess: undefined
- };
-
- function _extend(target, source) {
- if (!source || typeof source === 'function') {
- return target;
- }
-
- for (var attr in source) { target[attr] = source[attr]; }
- return target;
- }
-
- function _each(object, callback, args) {
- var name, i = 0,
- length = object.length,
- isObj = length === undefined || typeof object === "function";
-
- if (args) {
- if (isObj) {
- for (name in object) {
- if (callback.apply(object[name], args) === false) {
- break;
- }
- }
- } else {
- for ( ; i < length; ) {
- if (callback.apply(object[i++], args) === false) {
- break;
- }
- }
- }
-
- // A special, fast, case for the most common use of each
- } else {
- if (isObj) {
- for (name in object) {
- if (callback.call(object[name], name, object[name]) === false) {
- break;
- }
- }
- } else {
- for ( ; i < length; ) {
- if (callback.call(object[i], i, object[i++]) === false) {
- break;
- }
- }
- }
- }
-
- return object;
- }
-
- function _ajax(options) {
-
- // v0.5.0 of https://github.com/goloroden/http.js
- var getXhr = function (callback) {
- // Use the native XHR object if the browser supports it.
- if (window.XMLHttpRequest) {
- return callback(null, new XMLHttpRequest());
- } else if (window.ActiveXObject) {
- // In Internet Explorer check for ActiveX versions of the XHR object.
- try {
- return callback(null, new ActiveXObject("Msxml2.XMLHTTP"));
- } catch (e) {
- return callback(null, new ActiveXObject("Microsoft.XMLHTTP"));
- }
- }
-
- // If no XHR support was found, throw an error.
- return callback(new Error());
- };
-
- var encodeUsingUrlEncoding = function (data) {
- if(typeof data === 'string') {
- return data;
- }
-
- var result = [];
- for(var dataItem in data) {
- if(data.hasOwnProperty(dataItem)) {
- result.push(encodeURIComponent(dataItem) + '=' + encodeURIComponent(data[dataItem]));
- }
- }
-
- return result.join('&');
- };
-
- var utf8 = function (text) {
- text = text.replace(/\r\n/g, '\n');
- var result = '';
-
- for(var i = 0; i < text.length; i++) {
- var c = text.charCodeAt(i);
-
- if(c < 128) {
- result += String.fromCharCode(c);
- } else if((c > 127) && (c < 2048)) {
- result += String.fromCharCode((c >> 6) | 192);
- result += String.fromCharCode((c & 63) | 128);
- } else {
- result += String.fromCharCode((c >> 12) | 224);
- result += String.fromCharCode(((c >> 6) & 63) | 128);
- result += String.fromCharCode((c & 63) | 128);
- }
- }
-
- return result;
- };
-
- var base64 = function (text) {
- var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
-
- text = utf8(text);
- var result = '',
- chr1, chr2, chr3,
- enc1, enc2, enc3, enc4,
- i = 0;
-
- do {
- chr1 = text.charCodeAt(i++);
- chr2 = text.charCodeAt(i++);
- chr3 = text.charCodeAt(i++);
-
- enc1 = chr1 >> 2;
- enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
- enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
- enc4 = chr3 & 63;
-
- if(isNaN(chr2)) {
- enc3 = enc4 = 64;
- } else if(isNaN(chr3)) {
- enc4 = 64;
- }
-
- result +=
- keyStr.charAt(enc1) +
- keyStr.charAt(enc2) +
- keyStr.charAt(enc3) +
- keyStr.charAt(enc4);
- chr1 = chr2 = chr3 = '';
- enc1 = enc2 = enc3 = enc4 = '';
- } while(i < text.length);
-
- return result;
- };
-
- var mergeHeaders = function () {
- // Use the first header object as base.
- var result = arguments[0];
-
- // Iterate through the remaining header objects and add them.
- for(var i = 1; i < arguments.length; i++) {
- var currentHeaders = arguments[i];
- for(var header in currentHeaders) {
- if(currentHeaders.hasOwnProperty(header)) {
- result[header] = currentHeaders[header];
- }
- }
- }
-
- // Return the merged headers.
- return result;
- };
-
- var ajax = function (method, url, options, callback) {
- // Adjust parameters.
- if(typeof options === 'function') {
- callback = options;
- options = {};
- }
-
- // Set default parameter values.
- options.cache = options.cache || false;
- options.data = options.data || {};
- options.headers = options.headers || {};
- options.jsonp = options.jsonp || false;
- options.async = options.async === undefined ? true : options.async;
-
- // Merge the various header objects.
- var headers = mergeHeaders({
- 'accept': '*/*',
- 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8'
- }, ajax.headers, options.headers);
-
- // Encode the data according to the content-type.
- var payload;
- if (headers['content-type'] === 'application/json') {
- payload = JSON.stringify(options.data);
- } else {
- payload = encodeUsingUrlEncoding(options.data);
- }
-
- // Specially prepare GET requests: Setup the query string, handle caching and make a JSONP call
- // if neccessary.
- if(method === 'GET') {
- // Setup the query string.
- var queryString = [];
- if(payload) {
- queryString.push(payload);
- payload = null;
- }
-
- // Handle caching.
- if(!options.cache) {
- queryString.push('_=' + (new Date()).getTime());
- }
-
- // If neccessary prepare the query string for a JSONP call.
- if(options.jsonp) {
- queryString.push('callback=' + options.jsonp);
- queryString.push('jsonp=' + options.jsonp);
- }
-
- // Merge the query string and attach it to the url.
- queryString = queryString.join('&');
- if (queryString.length > 1) {
- if (url.indexOf('?') > -1) {
- url += '&' + queryString;
- } else {
- url += '?' + queryString;
- }
- }
-
- // Make a JSONP call if neccessary.
- if(options.jsonp) {
- var head = document.getElementsByTagName('head')[0];
- var script = document.createElement('script');
- script.type = 'text/javascript';
- script.src = url;
- head.appendChild(script);
- return;
- }
- }
-
- // Since we got here, it is no JSONP request, so make a normal XHR request.
- getXhr(function (err, xhr) {
- if(err) return callback(err);
-
- // Open the request.
- xhr.open(method, url, options.async);
-
- // Set the request headers.
- for(var header in headers) {
- if(headers.hasOwnProperty(header)) {
- xhr.setRequestHeader(header, headers[header]);
- }
- }
-
- // Handle the request events.
- xhr.onreadystatechange = function () {
- if(xhr.readyState === 4) {
- var data = xhr.responseText || '';
-
- // If no callback is given, return.
- if(!callback) {
- return;
- }
-
- // Return an object that provides access to the data as text and JSON.
- callback(xhr.status, {
- text: function () {
- return data;
- },
-
- json: function () {
- return JSON.parse(data);
- }
- });
- }
- };
-
- // Actually send the XHR request.
- xhr.send(payload);
- });
- };
-
- // Define the external interface.
- var http = {
- authBasic: function (username, password) {
- ajax.headers['Authorization'] = 'Basic ' + base64(username + ':' + password);
- },
-
- connect: function (url, options, callback) {
- return ajax('CONNECT', url, options, callback);
- },
-
- del: function (url, options, callback) {
- return ajax('DELETE', url, options, callback);
- },
-
- get: function (url, options, callback) {
- return ajax('GET', url, options, callback);
- },
-
- head: function (url, options, callback) {
- return ajax('HEAD', url, options, callback);
- },
-
- headers: function (headers) {
- ajax.headers = headers || {};
- },
-
- isAllowed: function (url, verb, callback) {
- this.options(url, function (status, data) {
- callback(data.text().indexOf(verb) !== -1);
- });
- },
-
- options: function (url, options, callback) {
- return ajax('OPTIONS', url, options, callback);
- },
-
- patch: function (url, options, callback) {
- return ajax('PATCH', url, options, callback);
- },
-
- post: function (url, options, callback) {
- return ajax('POST', url, options, callback);
- },
-
- put: function (url, options, callback) {
- return ajax('PUT', url, options, callback);
- },
-
- trace: function (url, options, callback) {
- return ajax('TRACE', url, options, callback);
- }
- };
-
-
- var methode = options.type ? options.type.toLowerCase() : 'get';
-
- http[methode](options.url, options, function (status, data) {
- if (status === 200) {
- options.success(data.json(), status, null);
- } else {
- options.error(data.text(), status, null);
- }
- });
- }
-
- var _cookie = {
- create: function(name,value,minutes) {
- var expires;
- if (minutes) {
- var date = new Date();
- date.setTime(date.getTime()+(minutes*60*1000));
- expires = "; expires="+date.toGMTString();
- }
- else expires = "";
- document.cookie = name+"="+value+expires+"; path=/";
- },
-
- read: function(name) {
- var nameEQ = name + "=";
- var ca = document.cookie.split(';');
- for(var i=0;i < ca.length;i++) {
- var c = ca[i];
- while (c.charAt(0)==' ') c = c.substring(1,c.length);
- if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length,c.length);
- }
- return null;
- },
-
- remove: function(name) {
- this.create(name,"",-1);
- }
- };
-
- var cookie_noop = {
- create: function(name,value,minutes) {},
- read: function(name) { return null; },
- remove: function(name) {}
- };
-
-
-
- // move dependent functions to a container so that
- // they can be overriden easier in no jquery environment (node.js)
- var f = {
- extend: $ ? $.extend : _extend,
- each: $ ? $.each : _each,
- ajax: $ ? $.ajax : _ajax,
- cookie: typeof document !== 'undefined' ? _cookie : cookie_noop,
- detectLanguage: detectLanguage,
- log: function(str) {
- if (o.debug && typeof console !== "undefined") console.log(str);
- },
- toLanguages: function(lng) {
- var languages = [];
- if (typeof lng === 'string' && lng.indexOf('-') > -1) {
- var parts = lng.split('-');
-
- lng = o.lowerCaseLng ?
- parts[0].toLowerCase() + '-' + parts[1].toLowerCase() :
- parts[0].toLowerCase() + '-' + parts[1].toUpperCase();
-
- if (o.load !== 'unspecific') languages.push(lng);
- if (o.load !== 'current') languages.push(parts[0]);
- } else {
- languages.push(lng);
- }
-
- if (languages.indexOf(o.fallbackLng) === -1 && o.fallbackLng) languages.push(o.fallbackLng);
-
- return languages;
- },
- regexEscape: function(str) {
- return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
- }
- };
- function init(options, cb) {
-
- if (typeof options === 'function') {
- cb = options;
- options = {};
- }
- options = options || {};
-
- // override defaults with passed in options
- f.extend(o, options);
-
- // create namespace object if namespace is passed in as string
- if (typeof o.ns == 'string') {
- o.ns = { namespaces: [o.ns], defaultNs: o.ns};
- }
-
- // escape prefix/suffix
- o.interpolationPrefixEscaped = f.regexEscape(o.interpolationPrefix);
- o.interpolationSuffixEscaped = f.regexEscape(o.interpolationSuffix);
-
- if (!o.lng) o.lng = f.detectLanguage();
- if (o.lng) {
- // set cookie with lng set (as detectLanguage will set cookie on need)
- if (o.useCookie) f.cookie.create(o.cookieName, o.lng, o.cookieExpirationTime);
- } else {
- o.lng = o.fallbackLng;
- if (o.useCookie) f.cookie.remove(o.cookieName);
- }
-
- languages = f.toLanguages(o.lng);
- currentLng = languages[0];
- f.log('currentLng set to: ' + currentLng);
-
- pluralExtensions.setCurrentLng(currentLng);
-
- // add JQuery extensions
- if ($ && o.setJqueryExt) addJqueryFunct();
-
- // jQuery deferred
- var deferred;
- if ($ && $.Deferred) {
- deferred = $.Deferred();
- }
-
- // return immidiatly if res are passed in
- if (o.resStore) {
- resStore = o.resStore;
- if (cb) cb(translate);
- if (deferred) deferred.resolve();
- if (deferred) return deferred.promise();
- return;
- }
-
- // languages to load
- var lngsToLoad = f.toLanguages(o.lng);
- if (typeof o.preload === 'string') o.preload = [o.preload];
- for (var i = 0, l = o.preload.length; i < l; i++) {
- var pres = f.toLanguages(o.preload[i]);
- for (var y = 0, len = pres.length; y < len; y++) {
- if (lngsToLoad.indexOf(pres[y]) < 0) {
- lngsToLoad.push(pres[y]);
- }
- }
- }
-
- // else load them
- i18n.sync.load(lngsToLoad, o, function(err, store) {
- resStore = store;
-
- if (cb) cb(translate);
- if (deferred) deferred.resolve();
- });
-
- if (deferred) return deferred.promise();
- }
- function preload(lngs, cb) {
- if (typeof lngs === 'string') lngs = [lngs];
- for (var i = 0, l = lngs.length; i < l; i++) {
- if (o.preload.indexOf(lngs[i]) < 0) {
- o.preload.push(lngs[i]);
- }
- }
- return init(cb);
- }
-
- function addResourceBundle(lng, ns, resources) {
- if (typeof ns !== 'string') {
- resources = ns;
- ns = o.ns.defaultNs;
- }
-
- resStore[lng] = resStore[lng] || {};
- resStore[lng][ns] = resStore[lng][ns] || {};
-
- f.extend(resStore[lng][ns], resources);
- }
-
- function setDefaultNamespace(ns) {
- o.ns.defaultNs = ns;
- }
-
- function loadNamespace(namespace, cb) {
- loadNamespaces([namespace], cb);
- }
-
- function loadNamespaces(namespaces, cb) {
- var opts = {
- dynamicLoad: o.dynamicLoad,
- resGetPath: o.resGetPath,
- getAsync: o.getAsync,
- ns: { namespaces: namespaces, defaultNs: ''} /* new namespaces to load */
- };
-
- // languages to load
- var lngsToLoad = f.toLanguages(o.lng);
- if (typeof o.preload === 'string') o.preload = [o.preload];
- for (var i = 0, l = o.preload.length; i < l; i++) {
- var pres = f.toLanguages(o.preload[i]);
- for (var y = 0, len = pres.length; y < len; y++) {
- if (lngsToLoad.indexOf(pres[y]) < 0) {
- lngsToLoad.push(pres[y]);
- }
- }
- }
-
- // check if we have to load
- var lngNeedLoad = [];
- for (var a = 0, lenA = lngsToLoad.length; a < lenA; a++) {
- var needLoad = false;
- var resSet = resStore[lngsToLoad[a]];
- if (resSet) {
- for (var b = 0, lenB = namespaces.length; b < lenB; b++) {
- if (!resSet[namespaces[b]]) needLoad = true;
- }
- } else {
- needLoad = true;
- }
-
- if (needLoad) lngNeedLoad.push(lngsToLoad[a]);
- }
-
- if (lngNeedLoad.length) {
- i18n.sync._fetch(lngNeedLoad, opts, function(err, store) {
- var todo = namespaces.length * lngNeedLoad.length;
-
- // load each file individual
- f.each(namespaces, function(nsIndex, nsValue) {
- f.each(lngNeedLoad, function(lngIndex, lngValue) {
- resStore[lngValue] = resStore[lngValue] || {};
- resStore[lngValue][nsValue] = store[lngValue][nsValue];
-
- todo--; // wait for all done befor callback
- if (todo === 0 && cb) {
- if (o.useLocalStorage) i18n.sync._storeLocal(resStore);
- cb();
- }
- });
- });
- });
- } else {
- if (cb) cb();
- }
- }
-
- function setLng(lng, cb) {
- return init({lng: lng}, cb);
- }
-
- function lng() {
- return currentLng;
- }
- function addJqueryFunct() {
- // $.t shortcut
- $.t = $.t || translate;
-
- function parse(ele, key, options) {
- if (key.length === 0) return;
-
- var attr = 'text';
-
- if (key.indexOf('[') === 0) {
- var parts = key.split(']');
- key = parts[1];
- attr = parts[0].substr(1, parts[0].length-1);
- }
-
- if (key.indexOf(';') === key.length-1) {
- key = key.substr(0, key.length-2);
- }
-
- var optionsToUse;
- if (attr === 'html') {
- optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
- ele.html($.t(key, optionsToUse));
- }
- else if (attr === 'text') {
- optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.text() }, options) : options;
- ele.text($.t(key, optionsToUse));
- } else {
- optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.attr(attr) }, options) : options;
- ele.attr(attr, $.t(key, optionsToUse));
- }
- }
-
- function localize(ele, options) {
- var key = ele.attr(o.selectorAttr);
- if (!key) return;
-
- var target = ele
- , targetSelector = ele.data("i18n-target");
- if (targetSelector) {
- target = ele.find(targetSelector) || ele;
- }
-
- if (!options && o.useDataAttrOptions === true) {
- options = ele.data("i18n-options");
- }
- options = options || {};
-
- if (key.indexOf(';') <= key.length-1) {
- var keys = key.split(';');
-
- $.each(keys, function(m, k) {
- parse(target, k, options);
- });
-
- } else {
- parse(target, k, options);
- }
-
- if (o.useDataAttrOptions === true) ele.data("i18n-options", options);
- }
-
- // fn
- $.fn.i18n = function (options) {
- return this.each(function() {
- // localize element itself
- localize($(this), options);
-
- // localize childs
- var elements = $(this).find('[' + o.selectorAttr + ']');
- elements.each(function() {
- localize($(this), options);
- });
- });
- };
- }
-
- function applyReplacement(str, replacementHash, nestedKey, options) {
- options = options || replacementHash; // first call uses replacement hash combined with options
- if (str.indexOf(options.interpolationPrefix || o.interpolationPrefix) < 0) return str;
-
- var prefix = options.interpolationPrefix ? f.regexEscape(options.interpolationPrefix) : o.interpolationPrefixEscaped
- , suffix = options.interpolationSuffix ? f.regexEscape(options.interpolationSuffix) : o.interpolationSuffixEscaped;
-
- f.each(replacementHash, function(key, value) {
- if (typeof value === 'object' && value !== null) {
- str = applyReplacement(str, value, nestedKey ? nestedKey + o.keyseparator + key : key, options);
- } else {
- str = str.replace(new RegExp([prefix, nestedKey ? nestedKey + o.keyseparator + key : key, suffix].join(''), 'g'), value);
- }
- });
- return str;
- }
-
- function applyReuse(translated, options) {
- var comma = ',';
- var options_open = '{';
- var options_close = '}';
-
- var opts = f.extend({}, options);
- delete opts.postProcess;
-
- while (translated.indexOf(o.reusePrefix) != -1) {
- replacementCounter++;
- if (replacementCounter > o.maxRecursion) { break; } // safety net for too much recursion
- var index_of_opening = translated.indexOf(o.reusePrefix);
- var index_of_end_of_closing = translated.indexOf(o.reuseSuffix, index_of_opening) + o.reuseSuffix.length;
- var token = translated.substring(index_of_opening, index_of_end_of_closing);
- var token_without_symbols = token.replace(o.reusePrefix, '').replace(o.reuseSuffix, '');
-
-
- if (token_without_symbols.indexOf(comma) != -1) {
- var index_of_token_end_of_closing = token_without_symbols.indexOf(comma);
- if (token_without_symbols.indexOf(options_open, index_of_token_end_of_closing) != -1 && token_without_symbols.indexOf(options_close, index_of_token_end_of_closing) != -1) {
- var index_of_opts_opening = token_without_symbols.indexOf(options_open, index_of_token_end_of_closing);
- var index_of_opts_end_of_closing = token_without_symbols.indexOf(options_close, index_of_opts_opening) + options_close.length;
- try {
- opts = f.extend(opts, JSON.parse(token_without_symbols.substring(index_of_opts_opening, index_of_opts_end_of_closing)));
- token_without_symbols = token_without_symbols.substring(0, index_of_token_end_of_closing);
- } catch (e) {
- }
- }
- }
-
- var translated_token = _translate(token_without_symbols, opts);
- translated = translated.replace(token, translated_token);
- }
- return translated;
- }
-
- function hasContext(options) {
- return (options.context && typeof options.context == 'string');
- }
-
- function needsPlural(options) {
- return (options.count !== undefined && typeof options.count != 'string' && options.count !== 1);
- }
-
- function translate(key, options){
- replacementCounter = 0;
- return _translate(key, options);
- }
-
- function _translate(key, options){
- options = options || {};
-
- if (!resStore) { return notfound; } // no resStore to translate from
-
- var optionWithoutCount, translated
- , notfound = options.defaultValue || key
- , lngs = languages;
-
- if (options.lng) {
- lngs = f.toLanguages(options.lng);
-
- if (!resStore[lngs[0]]) {
- var oldAsync = o.getAsync;
- o.getAsync = false;
-
- i18n.sync.load(lngs, o, function(err, store) {
- f.extend(resStore, store);
- o.getAsync = oldAsync;
- });
- }
- }
-
- var ns = options.ns || o.ns.defaultNs;
- if (key.indexOf(o.nsseparator) > -1) {
- var parts = key.split(o.nsseparator);
- ns = parts[0];
- key = parts[1];
- }
-
- if (hasContext(options)) {
- optionWithoutCount = f.extend({}, options);
- delete optionWithoutCount.context;
- optionWithoutCount.defaultValue = o.contextNotFound;
-
- var contextKey = ns + o.nsseparator + key + '_' + options.context;
-
- translated = translate(contextKey, optionWithoutCount);
- if (translated != o.contextNotFound) {
- return applyReplacement(translated, { context: options.context }); // apply replacement for context only
- } // else continue translation with original/nonContext key
- }
-
- if (needsPlural(options)) {
- optionWithoutCount = f.extend({}, options);
- delete optionWithoutCount.count;
- optionWithoutCount.defaultValue = o.pluralNotFound;
-
- var pluralKey = ns + o.nsseparator + key + o.pluralSuffix;
- var pluralExtension = pluralExtensions.get(currentLng, options.count);
- if (pluralExtension >= 0) {
- pluralKey = pluralKey + '_' + pluralExtension;
- } else if (pluralExtension === 1) {
- pluralKey = ns + o.nsseparator + key; // singular
- }
-
- translated = translate(pluralKey, optionWithoutCount);
- if (translated != o.pluralNotFound) {
- return applyReplacement(translated, { count: options.count }); // apply replacement for count only
- } // else continue translation with original/singular key
- }
-
- var found;
- var keys = key.split(o.keyseparator);
- for (var i = 0, len = lngs.length; i < len; i++ ) {
- if (found) break;
-
- var l = lngs[i];
-
- var x = 0;
- var value = resStore[l] && resStore[l][ns];
- while (keys[x]) {
- value = value && value[keys[x]];
- x++;
- }
- if (value !== undefined) {
- if (typeof value === 'string') {
- value = applyReplacement(value, options);
- value = applyReuse(value, options);
- } else if (Object.prototype.toString.apply(value) === '[object Array]' && !o.returnObjectTrees && !options.returnObjectTrees) {
- value = value.join('\n');
- value = applyReplacement(value, options);
- value = applyReuse(value, options);
- } else {
- if (!o.returnObjectTrees && !options.returnObjectTrees) {
- value = 'key \'' + ns + ':' + key + ' (' + l + ')\' ' +
- 'returned a object instead of string.';
- f.log(value);
- } else {
- var copy = {}; // apply child translation on a copy
- for (var m in value) {
- // apply translation on childs
- copy[m] = _translate(ns + o.nsseparator + key + o.keyseparator + m, options);
- }
- value = copy;
- }
- }
- found = value;
- }
- }
-
- if (found === undefined && o.fallbackToDefaultNS) {
- found = _translate(key, options);
- }
-
- if (found === undefined && o.sendMissing) {
- if (options.lng) {
- sync.postMissing(lngs[0], ns, key, notfound, lngs);
- } else {
- sync.postMissing(o.lng, ns, key, notfound, lngs);
- }
- }
-
- var postProcessor = options.postProcess || o.postProcess;
- if (found !== undefined && postProcessor) {
- if (postProcessors[postProcessor]) {
- found = postProcessors[postProcessor](found, key, options);
- }
- }
-
- if (found === undefined) {
- notfound = applyReplacement(notfound, options);
- notfound = applyReuse(notfound, options);
- }
-
- return (found !== undefined) ? found : notfound;
- }
-
- function detectLanguage() {
- var detectedLng;
-
- // get from qs
- var qsParm = [];
- if (typeof window !== 'undefined') {
- (function() {
- var query = window.location.search.substring(1);
- var parms = query.split('&');
- for (var i=0; i<parms.length; i++) {
- var pos = parms[i].indexOf('=');
- if (pos > 0) {
- var key = parms[i].substring(0,pos);
- var val = parms[i].substring(pos+1);
- qsParm[key] = val;
- }
- }
- })();
- if (qsParm[o.detectLngQS]) {
- detectedLng = qsParm[o.detectLngQS];
- }
- }
-
- // get from cookie
- if (!detectedLng && typeof document !== 'undefined' && o.useCookie ) {
- var c = f.cookie.read(o.cookieName);
- if (c) detectedLng = c;
- }
-
- // get from navigator
- if (!detectedLng && typeof navigator !== 'undefined') {
- detectedLng = (navigator.language) ? navigator.language : navigator.userLanguage;
- }
-
- return detectedLng;
- }
- var sync = {
-
- load: function(lngs, options, cb) {
- if (options.useLocalStorage) {
- sync._loadLocal(lngs, options, function(err, store) {
- var missingLngs = [];
- for (var i = 0, len = lngs.length; i < len; i++) {
- if (!store[lngs[i]]) missingLngs.push(lngs[i]);
- }
-
- if (missingLngs.length > 0) {
- sync._fetch(missingLngs, options, function(err, fetched) {
- f.extend(store, fetched);
- sync._storeLocal(fetched);
-
- cb(null, store);
- });
- } else {
- cb(null, store);
- }
- });
- } else {
- sync._fetch(lngs, options, function(err, store){
- cb(null, store);
- });
- }
- },
-
- _loadLocal: function(lngs, options, cb) {
- var store = {}
- , nowMS = new Date().getTime();
-
- if(window.localStorage) {
-
- var todo = lngs.length;
-
- f.each(lngs, function(key, lng) {
- var local = window.localStorage.getItem('res_' + lng);
-
- if (local) {
- local = JSON.parse(local);
-
- if (local.i18nStamp && local.i18nStamp + options.localStorageExpirationTime > nowMS) {
- store[lng] = local;
- }
- }
-
- todo--; // wait for all done befor callback
- if (todo === 0) cb(null, store);
- });
- }
- },
-
- _storeLocal: function(store) {
- if(window.localStorage) {
- for (var m in store) {
- store[m].i18nStamp = new Date().getTime();
- window.localStorage.setItem('res_' + m, JSON.stringify(store[m]));
- }
- }
- return;
- },
-
- _fetch: function(lngs, options, cb) {
- var ns = options.ns
- , store = {};
-
- if (!options.dynamicLoad) {
- var todo = ns.namespaces.length * lngs.length
- , errors;
-
- // load each file individual
- f.each(ns.namespaces, function(nsIndex, nsValue) {
- f.each(lngs, function(lngIndex, lngValue) {
-
- // Call this once our translation has returned.
- var loadComplete = function(err, data) {
- if (err) {
- errors = errors || [];
- errors.push(err);
- }
- store[lngValue] = store[lngValue] || {};
- store[lngValue][nsValue] = data;
-
- todo--; // wait for all done befor callback
- if (todo === 0) cb(errors, store);
- };
-
- if(typeof options.customLoad == 'function'){
- // Use the specified custom callback.
- options.customLoad(lngValue, nsValue, options, loadComplete);
- } else {
- //~ // Use our inbuilt sync.
- sync._fetchOne(lngValue, nsValue, options, loadComplete);
- }
- });
- });
- } else {
- var url = applyReplacement(options.resGetPath, { lng: lngs.join('+'), ns: ns.namespaces.join('+') });
- // load all needed stuff once
- f.ajax({
- url: url,
- success: function(data, status, xhr) {
- f.log('loaded: ' + url);
- cb(null, data);
- },
- error : function(xhr, status, error) {
- f.log('failed loading: ' + url);
- cb('failed loading resource.json error: ' + error);
- },
- dataType: "json",
- async : options.getAsync
- });
- }
- },
-
- _fetchOne: function(lng, ns, options, done) {
- var url = applyReplacement(options.resGetPath, { lng: lng, ns: ns });
- f.ajax({
- url: url,
- success: function(data, status, xhr) {
- f.log('loaded: ' + url);
- done(null, data);
- },
- error : function(xhr, status, error) {
- f.log('failed loading: ' + url);
- done(error, {});
- },
- dataType: "json",
- async : options.getAsync
- });
- },
-
- postMissing: function(lng, ns, key, defaultValue, lngs) {
- var payload = {};
- payload[key] = defaultValue;
-
- var urls = [];
-
- if (o.sendMissingTo === 'fallback') {
- urls.push({lng: o.fallbackLng, url: applyReplacement(o.resPostPath, { lng: o.fallbackLng, ns: ns })});
- } else if (o.sendMissingTo === 'current') {
- urls.push({lng: lng, url: applyReplacement(o.resPostPath, { lng: lng, ns: ns })});
- } else if (o.sendMissingTo === 'all') {
- for (var i = 0, l = lngs.length; i < l; i++) {
- urls.push({lng: lngs[i], url: applyReplacement(o.resPostPath, { lng: lngs[i], ns: ns })});
- }
- }
-
- for (var y = 0, len = urls.length; y < len; y++) {
- var item = urls[y];
- f.ajax({
- url: item.url,
- type: o.sendType,
- data: payload,
- success: function(data, status, xhr) {
- f.log('posted missing key \'' + key + '\' to: ' + item.url);
-
- // add key to resStore
- var keys = key.split('.');
- var x = 0;
- var value = resStore[item.lng][ns];
- while (keys[x]) {
- if (x === keys.length - 1) {
- value = value[keys[x]] = defaultValue;
- } else {
- value = value[keys[x]] = value[keys[x]] || {};
- }
- x++;
- }
- },
- error : function(xhr, status, error) {
- f.log('failed posting missing key \'' + key + '\' to: ' + item.url);
- },
- dataType: "json",
- async : o.postAsync
- });
- }
- }
- };
-
- // definition http://translate.sourceforge.net/wiki/l10n/pluralforms
- var pluralExtensions = {
-
- rules: {
- "ach": {
- "name": "Acholi",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n > 1); }
- },
- "af": {
- "name": "Afrikaans",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "ak": {
- "name": "Akan",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n > 1); }
- },
- "am": {
- "name": "Amharic",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n > 1); }
- },
- "an": {
- "name": "Aragonese",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "ar": {
- "name": "Arabic",
- "numbers": [
- 0,
- 1,
- 2,
- 3,
- 11,
- 100
- ],
- "plurals": function(n) { return Number(n===0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5); }
- },
- "arn": {
- "name": "Mapudungun",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n > 1); }
- },
- "ast": {
- "name": "Asturian",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "ay": {
- "name": "Aymar\u00e1",
- "numbers": [
- 1
- ],
- "plurals": function(n) { return 0; }
- },
- "az": {
- "name": "Azerbaijani",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "be": {
- "name": "Belarusian",
- "numbers": [
- 1,
- 2,
- 5
- ],
- "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
- },
- "bg": {
- "name": "Bulgarian",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "bn": {
- "name": "Bengali",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "bo": {
- "name": "Tibetan",
- "numbers": [
- 1
- ],
- "plurals": function(n) { return 0; }
- },
- "br": {
- "name": "Breton",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n > 1); }
- },
- "bs": {
- "name": "Bosnian",
- "numbers": [
- 1,
- 2,
- 5
- ],
- "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
- },
- "ca": {
- "name": "Catalan",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "cgg": {
- "name": "Chiga",
- "numbers": [
- 1
- ],
- "plurals": function(n) { return 0; }
- },
- "cs": {
- "name": "Czech",
- "numbers": [
- 1,
- 2,
- 5
- ],
- "plurals": function(n) { return Number((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2); }
- },
- "csb": {
- "name": "Kashubian",
- "numbers": [
- 1,
- 2,
- 5
- ],
- "plurals": function(n) { return Number(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
- },
- "cy": {
- "name": "Welsh",
- "numbers": [
- 1,
- 2,
- 3,
- 8
- ],
- "plurals": function(n) { return Number((n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3); }
- },
- "da": {
- "name": "Danish",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "de": {
- "name": "German",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "dz": {
- "name": "Dzongkha",
- "numbers": [
- 1
- ],
- "plurals": function(n) { return 0; }
- },
- "el": {
- "name": "Greek",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "en": {
- "name": "English",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "eo": {
- "name": "Esperanto",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "es": {
- "name": "Spanish",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "es_ar": {
- "name": "Argentinean Spanish",
- "numbers": [
- 1,
- 2
- ],
- "plurals": function(n) { return Number(n != 1); }
- },
- "et": {
- "name": "Estonian",
- "numbers": [
- 1,
- 2
- ],
- "plura