fuel-web/nailgun/static/js/libs/i18next-1.7.1.js

2662 lines
89 KiB
JavaScript

// i18next, v1.7.1
// 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;
}
}
// add lastIndexOf to non ECMA-262 standard compliant browsers
if (!Array.prototype.lastIndexOf) {
Array.prototype.lastIndexOf = 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 = len;
if (arguments.length > 1) {
n = Number(arguments[1]);
if (n != n) {
n = 0;
} else if (n != 0 && n != (1 / 0) && n != -(1 / 0)) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
var k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n);
for (; k >= 0; k--) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
};
}
var root = this
, $ = root.jQuery || root.Zepto
, i18n = {}
, resStore = {}
, currentLng
, replacementCounter = 0
, languages = []
, initialized = false;
// 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',
fallbackNS: [],
detectLngQS: 'setLng',
ns: 'translation',
fallbackOnNull: true,
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(''),
escapeInterpolation: false,
setJqueryExt: true,
defaultValueFromContent: true,
useDataAttrOptions: false,
cookieExpirationTime: undefined,
useCookie: true,
cookieName: 'i18next',
cookieDomain: undefined,
postProcess: undefined,
parseMissingKey: undefined,
shortcutFunction: 'sprintf' // or: defaultValue
};
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;
}
var _entityMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#x2F;'
};
function _escape(data) {
if (typeof data === 'string') {
return data.replace(/[&<>"'\/]/g, function (s) {
return _entityMap[s];
});
}else{
return data;
}
}
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,domain) {
var expires;
if (minutes) {
var date = new Date();
date.setTime(date.getTime()+(minutes*60*1000));
expires = "; expires="+date.toGMTString();
}
else expires = "";
domain = (domain)? "domain="+domain+";" : "";
document.cookie = name+"="+value+expires+";"+domain+"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,domain) {},
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,
escape: _escape,
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);
delete o.fixLng; /* passed in each time */
// create namespace object if namespace is passed in as string
if (typeof o.ns == 'string') {
o.ns = { namespaces: [o.ns], defaultNs: o.ns};
}
// fallback namespaces
if (typeof o.fallbackNS == 'string') {
o.fallbackNS = [o.fallbackNS];
}
// 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, o.cookieDomain);
} 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);
var lngTranslate = translate;
if (options.fixLng) {
lngTranslate = function(key, options) {
options = options || {};
options.lng = options.lng || lngTranslate.lng;
return translate(key, options);
};
lngTranslate.lng = 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;
initialized = true;
if (cb) cb(lngTranslate);
if (deferred) deferred.resolve(lngTranslate);
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;
initialized = true;
if (cb) cb(lngTranslate);
if (deferred) deferred.resolve(lngTranslate);
});
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;
} else if (o.ns.namespaces.indexOf(ns) < 0) {
o.ns.namespaces.push(ns);
}
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,
customLoad: o.customLoad,
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) {
// append namespace to namespace array
if (o.ns.namespaces.indexOf(nsValue) < 0) {
o.ns.namespaces.push(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, options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}
options.lng = lng;
return init(options, 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 && typeof key !== 'undefined' && key !== false) key = ele.text() || ele.val();
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(';') >= 0) {
var keys = key.split(';');
$.each(keys, function(m, k) {
if (k !== '') parse(target, k, options);
});
} else {
parse(target, key, 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) {
if (!str) return str;
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
, unEscapingSuffix = 'HTML'+suffix;
f.each(replacementHash, function(key, value) {
var nextKey = nestedKey ? nestedKey + o.keyseparator + key : key;
if (typeof value === 'object' && value !== null) {
str = applyReplacement(str, value, nextKey, options);
} else {
if (options.escapeInterpolation || o.escapeInterpolation) {
str = str.replace(new RegExp([prefix, nextKey, unEscapingSuffix].join(''), 'g'), value);
str = str.replace(new RegExp([prefix, nextKey, suffix].join(''), 'g'), f.escape(value));
} else {
str = str.replace(new RegExp([prefix, nextKey, suffix].join(''), 'g'), value);
}
// str = options.escapeInterpolation;
}
});
return str;
}
// append it to functions
f.applyReplacement = applyReplacement;
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.lastIndexOf(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 exists(key, options) {
options = options || {};
var notFound = _getDefaultValue(key, options)
, found = _find(key, options);
return found !== undefined || found === notFound;
}
function translate(key, options) {
if (!initialized) {
f.log('i18next not finished initialization. you might have called t function before loading resources finished.')
return options.defaultValue || '';
};
replacementCounter = 0;
return _translate.apply(null, arguments);
}
function _getDefaultValue(key, options) {
return (options.defaultValue !== undefined) ? options.defaultValue : key;
}
function _injectSprintfProcessor() {
var values = [];
// mh: build array from second argument onwards
for (var i = 1; i < arguments.length; i++) {
values.push(arguments[i]);
}
return {
postProcess: 'sprintf',
sprintf: values
};
}
function _translate(potentialKeys, options) {
if (typeof options == 'string') {
if (o.shortcutFunction === 'sprintf') {
// mh: gettext like sprintf syntax found, automatically create sprintf processor
options = _injectSprintfProcessor.apply(null, arguments);
} else if (o.shortcutFunction === 'defaultValue') {
options = {
defaultValue: options
}
}
} else {
options = options || {};
}
if (typeof potentialKeys == 'string') {
potentialKeys = [potentialKeys];
}
var key = null;
for (var i = 0; i < potentialKeys.length; i++) {
key = potentialKeys[i];
if (exists(key)) {
break;
}
}
var notFound = _getDefaultValue(key, options)
, found = _find(key, options)
, lngs = options.lng ? f.toLanguages(options.lng) : languages
, ns = options.ns || o.ns.defaultNs
, parts;
// split ns and key
if (key.indexOf(o.nsseparator) > -1) {
parts = key.split(o.nsseparator);
ns = parts[0];
key = parts[1];
}
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);
}
}
// process notFound if function exists
var splitNotFound = notFound;
if (notFound.indexOf(o.nsseparator) > -1) {
parts = notFound.split(o.nsseparator);
splitNotFound = parts[1];
}
if (splitNotFound === key && o.parseMissingKey) {
notFound = o.parseMissingKey(notFound);
}
if (found === undefined) {
notFound = applyReplacement(notFound, options);
notFound = applyReuse(notFound, options);
if (postProcessor && postProcessors[postProcessor]) {
var val = _getDefaultValue(key, options);
found = postProcessors[postProcessor](val, key, options);
}
}
return (found !== undefined) ? found : notFound;
}
function _find(key, options){
options = options || {};
var optionWithoutCount, translated
, notFound = _getDefaultValue(key, options)
, lngs = languages;
if (!resStore) { return notFound; } // no resStore to translate from
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(lngs[0], 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,
interpolationPrefix: options.interpolationPrefix,
interpolationSuffix: options.interpolationSuffix
}); // 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 !== undefined) 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 (value === null && o.fallbackOnNull === true) {
value = undefined;
} else if (value !== null) {
if (!o.returnObjectTrees && !options.returnObjectTrees) {
value = 'key \'' + ns + ':' + key + ' (' + l + ')\' ' +
'returned a object instead of string.';
f.log(value);
} else if (typeof value !== 'number') {
var copy = {}; // apply child translation on a copy
f.each(value, function(m) {
copy[m] = _translate(ns + o.nsseparator + key + o.keyseparator + m, options);
});
value = copy;
}
}
found = value;
}
}
if (found === undefined && !options.isFallbackLookup && (o.fallbackToDefaultNS === true || (o.fallbackNS && o.fallbackNS.length > 0))) {
// set flag for fallback lookup - avoid recursion
options.isFallbackLookup = true;
if (o.fallbackNS.length) {
for (var y = 0, lenY = o.fallbackNS.length; y < lenY; y++) {
found = _find(o.fallbackNS[y] + o.nsseparator + key, options);
if (found) {
/* compare value without namespace */
var foundValue = found.indexOf(o.nsseparator) > -1 ? found.split(o.nsseparator)[1] : found
, notFoundValue = notFound.indexOf(o.nsseparator) > -1 ? notFound.split(o.nsseparator)[1] : notFound;
if (foundValue !== notFoundValue) break;
}
}
} else {
found = _find(key, options); // fallback to default NS
}
}
return found;
}
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 {
// Call this once our translation has returned.
var loadComplete = function(err, data) {
cb(null, data);
};
if(typeof options.customLoad == 'function'){
// Use the specified custom callback.
options.customLoad(lngs, ns.namespaces, 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);
loadComplete(null, data);
},
error : function(xhr, status, error) {
f.log('failed loading: ' + url);
loadComplete('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' && o.fallbackLng !== false) {
urls.push({lng: o.fallbackLng, url: applyReplacement(o.resPostPath, { lng: o.fallbackLng, ns: ns })});
} else if (o.sendMissingTo === 'current' || (o.sendMissingTo === 'fallback' && o.fallbackLng === false) ) {
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
],
"plurals": function(n) { return Number(n != 1); }
},
"eu": {
"name": "Basque",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"fa": {
"name": "Persian",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"fi": {
"name": "Finnish",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"fil": {
"name": "Filipino",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"fo": {
"name": "Faroese",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"fr": {
"name": "French",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"fur": {
"name": "Friulian",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"fy": {
"name": "Frisian",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"ga": {
"name": "Irish",
"numbers": [
1,
2,
3,
7,
11
],
"plurals": function(n) { return Number(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4) ;}
},
"gd": {
"name": "Scottish Gaelic",
"numbers": [
1,
2,
3,
20
],
"plurals": function(n) { return Number((n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3); }
},
"gl": {
"name": "Galician",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"gu": {
"name": "Gujarati",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"gun": {
"name": "Gun",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"ha": {
"name": "Hausa",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"he": {
"name": "Hebrew",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"hi": {
"name": "Hindi",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"hr": {
"name": "Croatian",
"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); }
},
"hu": {
"name": "Hungarian",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"hy": {
"name": "Armenian",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"ia": {
"name": "Interlingua",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"id": {
"name": "Indonesian",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"is": {
"name": "Icelandic",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n%10!=1 || n%100==11); }
},
"it": {
"name": "Italian",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"ja": {
"name": "Japanese",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"jbo": {
"name": "Lojban",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"jv": {
"name": "Javanese",
"numbers": [
0,
1
],
"plurals": function(n) { return Number(n !== 0); }
},
"ka": {
"name": "Georgian",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"kk": {
"name": "Kazakh",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"km": {
"name": "Khmer",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"kn": {
"name": "Kannada",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"ko": {
"name": "Korean",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"ku": {
"name": "Kurdish",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"kw": {
"name": "Cornish",
"numbers": [
1,
2,
3,
4
],
"plurals": function(n) { return Number((n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : 3); }
},
"ky": {
"name": "Kyrgyz",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"lb": {
"name": "Letzeburgesch",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"ln": {
"name": "Lingala",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"lo": {
"name": "Lao",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"lt": {
"name": "Lithuanian",
"numbers": [
1,
2,
10
],
"plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2); }
},
"lv": {
"name": "Latvian",
"numbers": [
0,
1,
2
],
"plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n !== 0 ? 1 : 2); }
},
"mai": {
"name": "Maithili",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"mfe": {
"name": "Mauritian Creole",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"mg": {
"name": "Malagasy",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"mi": {
"name": "Maori",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"mk": {
"name": "Macedonian",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n==1 || n%10==1 ? 0 : 1); }
},
"ml": {
"name": "Malayalam",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"mn": {
"name": "Mongolian",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"mnk": {
"name": "Mandinka",
"numbers": [
0,
1,
2
],
"plurals": function(n) { return Number(0 ? 0 : n==1 ? 1 : 2); }
},
"mr": {
"name": "Marathi",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"ms": {
"name": "Malay",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"mt": {
"name": "Maltese",
"numbers": [
1,
2,
11,
20
],
"plurals": function(n) { return Number(n==1 ? 0 : n===0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3); }
},
"nah": {
"name": "Nahuatl",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"nap": {
"name": "Neapolitan",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"nb": {
"name": "Norwegian Bokmal",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"ne": {
"name": "Nepali",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"nl": {
"name": "Dutch",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"nn": {
"name": "Norwegian Nynorsk",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"no": {
"name": "Norwegian",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"nso": {
"name": "Northern Sotho",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"oc": {
"name": "Occitan",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"or": {
"name": "Oriya",
"numbers": [
2,
1
],
"plurals": function(n) { return Number(n != 1); }
},
"pa": {
"name": "Punjabi",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"pap": {
"name": "Papiamento",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"pl": {
"name": "Polish",
"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); }
},
"pms": {
"name": "Piemontese",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"ps": {
"name": "Pashto",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"pt": {
"name": "Portuguese",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"pt_br": {
"name": "Brazilian Portuguese",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"rm": {
"name": "Romansh",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"ro": {
"name": "Romanian",
"numbers": [
1,
2,
20
],
"plurals": function(n) { return Number(n==1 ? 0 : (n===0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2); }
},
"ru": {
"name": "Russian",
"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); }
},
"sah": {
"name": "Yakut",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"sco": {
"name": "Scots",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"se": {
"name": "Northern Sami",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"si": {
"name": "Sinhala",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"sk": {
"name": "Slovak",
"numbers": [
1,
2,
5
],
"plurals": function(n) { return Number((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2); }
},
"sl": {
"name": "Slovenian",
"numbers": [
5,
1,
2,
3
],
"plurals": function(n) { return Number(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0); }
},
"so": {
"name": "Somali",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"son": {
"name": "Songhay",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"sq": {
"name": "Albanian",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"sr": {
"name": "Serbian",
"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); }
},
"su": {
"name": "Sundanese",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"sv": {
"name": "Swedish",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"sw": {
"name": "Swahili",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"ta": {
"name": "Tamil",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"te": {
"name": "Telugu",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"tg": {
"name": "Tajik",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"th": {
"name": "Thai",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"ti": {
"name": "Tigrinya",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"tk": {
"name": "Turkmen",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"tr": {
"name": "Turkish",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"tt": {
"name": "Tatar",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"ug": {
"name": "Uyghur",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"uk": {
"name": "Ukrainian",
"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); }
},
"ur": {
"name": "Urdu",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"uz": {
"name": "Uzbek",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"vi": {
"name": "Vietnamese",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"wa": {
"name": "Walloon",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n > 1); }
},
"wo": {
"name": "Wolof",
"numbers": [
1
],
"plurals": function(n) { return 0; }
},
"yo": {
"name": "Yoruba",
"numbers": [
1,
2
],
"plurals": function(n) { return Number(n != 1); }
},
"zh": {
"name": "Chinese",
"numbers": [
1
],
"plurals": function(n) { return 0; }
}
},
// for demonstration only sl and ar is added but you can add your own pluralExtensions
addRule: function(lng, obj) {
pluralExtensions.rules[lng] = obj;
},
setCurrentLng: function(lng) {
if (!pluralExtensions.currentRule || pluralExtensions.currentRule.lng !== lng) {
var parts = lng.split('-');
pluralExtensions.currentRule = {
lng: lng,
rule: pluralExtensions.rules[parts[0]]
};
}
},
get: function(lng, count) {
var parts = lng.split('-');
function getResult(l, c) {
var ext;
if (pluralExtensions.currentRule && pluralExtensions.currentRule.lng === lng) {
ext = pluralExtensions.currentRule.rule;
} else {
ext = pluralExtensions.rules[l];
}
if (ext) {
var i = ext.plurals(c);
var number = ext.numbers[i];
if (ext.numbers.length === 2 && ext.numbers[0] === 1) {
if (number === 2) {
number = -1; // regular plural
} else if (number === 1) {
number = 1; // singular
}
}//console.log(count + '-' + number);
return number;
} else {
return c === 1 ? '1' : '-1';
}
}
return getResult(parts[0], count);
}
};
var postProcessors = {};
var addPostProcessor = function(name, fc) {
postProcessors[name] = fc;
};
// sprintf support
var sprintf = (function() {
function get_type(variable) {
return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
}
function str_repeat(input, multiplier) {
for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
return output.join('');
}
var str_format = function() {
if (!str_format.cache.hasOwnProperty(arguments[0])) {
str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
}
return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
};
str_format.format = function(parse_tree, argv) {
var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
for (i = 0; i < tree_length; i++) {
node_type = get_type(parse_tree[i]);
if (node_type === 'string') {
output.push(parse_tree[i]);
}
else if (node_type === 'array') {
match = parse_tree[i]; // convenience purposes only
if (match[2]) { // keyword argument
arg = argv[cursor];
for (k = 0; k < match[2].length; k++) {
if (!arg.hasOwnProperty(match[2][k])) {
throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
}
arg = arg[match[2][k]];
}
}
else if (match[1]) { // positional argument (explicit)
arg = argv[match[1]];
}
else { // positional argument (implicit)
arg = argv[cursor++];
}
if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
}
switch (match[8]) {
case 'b': arg = arg.toString(2); break;
case 'c': arg = String.fromCharCode(arg); break;
case 'd': arg = parseInt(arg, 10); break;
case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
case 'o': arg = arg.toString(8); break;
case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
case 'u': arg = Math.abs(arg); break;
case 'x': arg = arg.toString(16); break;
case 'X': arg = arg.toString(16).toUpperCase(); break;
}
arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
pad_length = match[6] - String(arg).length;
pad = match[6] ? str_repeat(pad_character, pad_length) : '';
output.push(match[5] ? arg + pad : pad + arg);
}
}
return output.join('');
};
str_format.cache = {};
str_format.parse = function(fmt) {
var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
while (_fmt) {
if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
parse_tree.push(match[0]);
}
else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
parse_tree.push('%');
}
else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
if (match[2]) {
arg_names |= 1;
var field_list = [], replacement_field = match[2], field_match = [];
if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
}
else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
}
else {
throw('[sprintf] huh?');
}
}
}
else {
throw('[sprintf] huh?');
}
match[2] = field_list;
}
else {
arg_names |= 2;
}
if (arg_names === 3) {
throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
}
parse_tree.push(match);
}
else {
throw('[sprintf] huh?');
}
_fmt = _fmt.substring(match[0].length);
}
return parse_tree;
};
return str_format;
})();
var vsprintf = function(fmt, argv) {
argv.unshift(fmt);
return sprintf.apply(null, argv);
};
addPostProcessor("sprintf", function(val, key, opts) {
if (!opts.sprintf) return val;
if (Object.prototype.toString.apply(opts.sprintf) === '[object Array]') {
return vsprintf(val, opts.sprintf);
} else if (typeof opts.sprintf === 'object') {
return sprintf(val, opts.sprintf);
}
return val;
});
// public api interface
i18n.init = init;
i18n.setLng = setLng;
i18n.preload = preload;
i18n.addResourceBundle = addResourceBundle;
i18n.loadNamespace = loadNamespace;
i18n.loadNamespaces = loadNamespaces;
i18n.setDefaultNamespace = setDefaultNamespace;
i18n.t = translate;
i18n.translate = translate;
i18n.exists = exists;
i18n.detectLanguage = f.detectLanguage;
i18n.pluralExtensions = pluralExtensions;
i18n.sync = sync;
i18n.functions = f;
i18n.lng = lng;
i18n.addPostProcessor = addPostProcessor;
i18n.options = o;
})();