You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2280 lines
84 KiB

  1. // i18next, v1.10.1
  2. // Copyright (c)2015 Jan Mühlemann (jamuhl).
  3. // Distributed under MIT license
  4. // http://i18next.com
  5. (function(root) {
  6. // add indexOf to non ECMA-262 standard compliant browsers
  7. if (!Array.prototype.indexOf) {
  8. Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
  9. "use strict";
  10. if (this == null) {
  11. throw new TypeError();
  12. }
  13. var t = Object(this);
  14. var len = t.length >>> 0;
  15. if (len === 0) {
  16. return -1;
  17. }
  18. var n = 0;
  19. if (arguments.length > 0) {
  20. n = Number(arguments[1]);
  21. if (n != n) { // shortcut for verifying if it's NaN
  22. n = 0;
  23. } else if (n != 0 && n != Infinity && n != -Infinity) {
  24. n = (n > 0 || -1) * Math.floor(Math.abs(n));
  25. }
  26. }
  27. if (n >= len) {
  28. return -1;
  29. }
  30. var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
  31. for (; k < len; k++) {
  32. if (k in t && t[k] === searchElement) {
  33. return k;
  34. }
  35. }
  36. return -1;
  37. }
  38. }
  39. // add lastIndexOf to non ECMA-262 standard compliant browsers
  40. if (!Array.prototype.lastIndexOf) {
  41. Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/) {
  42. "use strict";
  43. if (this == null) {
  44. throw new TypeError();
  45. }
  46. var t = Object(this);
  47. var len = t.length >>> 0;
  48. if (len === 0) {
  49. return -1;
  50. }
  51. var n = len;
  52. if (arguments.length > 1) {
  53. n = Number(arguments[1]);
  54. if (n != n) {
  55. n = 0;
  56. } else if (n != 0 && n != (1 / 0) && n != -(1 / 0)) {
  57. n = (n > 0 || -1) * Math.floor(Math.abs(n));
  58. }
  59. }
  60. var k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n);
  61. for (; k >= 0; k--) {
  62. if (k in t && t[k] === searchElement) {
  63. return k;
  64. }
  65. }
  66. return -1;
  67. };
  68. }
  69. // Add string trim for IE8.
  70. if (typeof String.prototype.trim !== 'function') {
  71. String.prototype.trim = function() {
  72. return this.replace(/^\s+|\s+$/g, '');
  73. }
  74. }
  75. var $ = root.jQuery || root.Zepto
  76. , i18n = {}
  77. , resStore = {}
  78. , currentLng
  79. , replacementCounter = 0
  80. , languages = []
  81. , initialized = false
  82. , sync = {}
  83. , conflictReference = null;
  84. // Export the i18next object for **CommonJS**.
  85. // If we're not in CommonJS, add `i18n` to the
  86. // global object or to jquery.
  87. if (typeof module !== 'undefined' && module.exports) {
  88. module.exports = i18n;
  89. } else {
  90. if ($) {
  91. $.i18n = $.i18n || i18n;
  92. }
  93. if (root.i18n) {
  94. conflictReference = root.i18n;
  95. }
  96. root.i18n = i18n;
  97. }
  98. sync = {
  99. load: function(lngs, options, cb) {
  100. if (options.useLocalStorage) {
  101. sync._loadLocal(lngs, options, function(err, store) {
  102. var missingLngs = [];
  103. for (var i = 0, len = lngs.length; i < len; i++) {
  104. if (!store[lngs[i]]) missingLngs.push(lngs[i]);
  105. }
  106. if (missingLngs.length > 0) {
  107. sync._fetch(missingLngs, options, function(err, fetched) {
  108. f.extend(store, fetched);
  109. sync._storeLocal(fetched);
  110. cb(err, store);
  111. });
  112. } else {
  113. cb(err, store);
  114. }
  115. });
  116. } else {
  117. sync._fetch(lngs, options, function(err, store){
  118. cb(err, store);
  119. });
  120. }
  121. },
  122. _loadLocal: function(lngs, options, cb) {
  123. var store = {}
  124. , nowMS = new Date().getTime();
  125. if(window.localStorage) {
  126. var todo = lngs.length;
  127. f.each(lngs, function(key, lng) {
  128. var local = f.localStorage.getItem('res_' + lng);
  129. if (local) {
  130. local = JSON.parse(local);
  131. if (local.i18nStamp && local.i18nStamp + options.localStorageExpirationTime > nowMS) {
  132. store[lng] = local;
  133. }
  134. }
  135. todo--; // wait for all done befor callback
  136. if (todo === 0) cb(null, store);
  137. });
  138. }
  139. },
  140. _storeLocal: function(store) {
  141. if(window.localStorage) {
  142. for (var m in store) {
  143. store[m].i18nStamp = new Date().getTime();
  144. f.localStorage.setItem('res_' + m, JSON.stringify(store[m]));
  145. }
  146. }
  147. return;
  148. },
  149. _fetch: function(lngs, options, cb) {
  150. var ns = options.ns
  151. , store = {};
  152. if (!options.dynamicLoad) {
  153. var todo = ns.namespaces.length * lngs.length
  154. , errors;
  155. // load each file individual
  156. f.each(ns.namespaces, function(nsIndex, nsValue) {
  157. f.each(lngs, function(lngIndex, lngValue) {
  158. // Call this once our translation has returned.
  159. var loadComplete = function(err, data) {
  160. if (err) {
  161. errors = errors || [];
  162. errors.push(err);
  163. }
  164. store[lngValue] = store[lngValue] || {};
  165. store[lngValue][nsValue] = data;
  166. todo--; // wait for all done befor callback
  167. if (todo === 0) cb(errors, store);
  168. };
  169. if(typeof options.customLoad == 'function'){
  170. // Use the specified custom callback.
  171. options.customLoad(lngValue, nsValue, options, loadComplete);
  172. } else {
  173. //~ // Use our inbuilt sync.
  174. sync._fetchOne(lngValue, nsValue, options, loadComplete);
  175. }
  176. });
  177. });
  178. } else {
  179. // Call this once our translation has returned.
  180. var loadComplete = function(err, data) {
  181. cb(err, data);
  182. };
  183. if(typeof options.customLoad == 'function'){
  184. // Use the specified custom callback.
  185. options.customLoad(lngs, ns.namespaces, options, loadComplete);
  186. } else {
  187. var url = applyReplacement(options.resGetPath, { lng: lngs.join('+'), ns: ns.namespaces.join('+') });
  188. // load all needed stuff once
  189. f.ajax({
  190. url: url,
  191. cache: options.cache,
  192. success: function(data, status, xhr) {
  193. f.log('loaded: ' + url);
  194. loadComplete(null, data);
  195. },
  196. error : function(xhr, status, error) {
  197. f.log('failed loading: ' + url);
  198. loadComplete('failed loading resource.json error: ' + error);
  199. },
  200. dataType: "json",
  201. async : options.getAsync,
  202. timeout: options.ajaxTimeout
  203. });
  204. }
  205. }
  206. },
  207. _fetchOne: function(lng, ns, options, done) {
  208. var url = applyReplacement(options.resGetPath, { lng: lng, ns: ns });
  209. f.ajax({
  210. url: url,
  211. cache: options.cache,
  212. success: function(data, status, xhr) {
  213. f.log('loaded: ' + url);
  214. done(null, data);
  215. },
  216. error : function(xhr, status, error) {
  217. if ((status && status == 200) || (xhr && xhr.status && xhr.status == 200)) {
  218. // file loaded but invalid json, stop waste time !
  219. f.error('There is a typo in: ' + url);
  220. } else if ((status && status == 404) || (xhr && xhr.status && xhr.status == 404)) {
  221. f.log('Does not exist: ' + url);
  222. } else {
  223. var theStatus = status ? status : ((xhr && xhr.status) ? xhr.status : null);
  224. f.log(theStatus + ' when loading ' + url);
  225. }
  226. done(error, {});
  227. },
  228. dataType: "json",
  229. async : options.getAsync,
  230. timeout: options.ajaxTimeout
  231. });
  232. },
  233. postMissing: function(lng, ns, key, defaultValue, lngs) {
  234. var payload = {};
  235. payload[key] = defaultValue;
  236. var urls = [];
  237. if (o.sendMissingTo === 'fallback' && o.fallbackLng[0] !== false) {
  238. for (var i = 0; i < o.fallbackLng.length; i++) {
  239. urls.push({lng: o.fallbackLng[i], url: applyReplacement(o.resPostPath, { lng: o.fallbackLng[i], ns: ns })});
  240. }
  241. } else if (o.sendMissingTo === 'current' || (o.sendMissingTo === 'fallback' && o.fallbackLng[0] === false) ) {
  242. urls.push({lng: lng, url: applyReplacement(o.resPostPath, { lng: lng, ns: ns })});
  243. } else if (o.sendMissingTo === 'all') {
  244. for (var i = 0, l = lngs.length; i < l; i++) {
  245. urls.push({lng: lngs[i], url: applyReplacement(o.resPostPath, { lng: lngs[i], ns: ns })});
  246. }
  247. }
  248. for (var y = 0, len = urls.length; y < len; y++) {
  249. var item = urls[y];
  250. f.ajax({
  251. url: item.url,
  252. type: o.sendType,
  253. data: payload,
  254. success: function(data, status, xhr) {
  255. f.log('posted missing key \'' + key + '\' to: ' + item.url);
  256. // add key to resStore
  257. var keys = key.split('.');
  258. var x = 0;
  259. var value = resStore[item.lng][ns];
  260. while (keys[x]) {
  261. if (x === keys.length - 1) {
  262. value = value[keys[x]] = defaultValue;
  263. } else {
  264. value = value[keys[x]] = value[keys[x]] || {};
  265. }
  266. x++;
  267. }
  268. },
  269. error : function(xhr, status, error) {
  270. f.log('failed posting missing key \'' + key + '\' to: ' + item.url);
  271. },
  272. dataType: "json",
  273. async : o.postAsync,
  274. timeout: o.ajaxTimeout
  275. });
  276. }
  277. },
  278. reload: reload
  279. };
  280. // defaults
  281. var o = {
  282. lng: undefined,
  283. load: 'all',
  284. preload: [],
  285. lowerCaseLng: false,
  286. returnObjectTrees: false,
  287. fallbackLng: ['dev'],
  288. fallbackNS: [],
  289. detectLngQS: 'setLng',
  290. detectLngFromLocalStorage: false,
  291. ns: {
  292. namespaces: ['translation'],
  293. defaultNs: 'translation'
  294. },
  295. fallbackOnNull: true,
  296. fallbackOnEmpty: false,
  297. fallbackToDefaultNS: false,
  298. showKeyIfEmpty: false,
  299. nsseparator: ':',
  300. keyseparator: '.',
  301. selectorAttr: 'data-i18n',
  302. debug: false,
  303. resGetPath: 'locales/__lng__/__ns__.json',
  304. resPostPath: 'locales/add/__lng__/__ns__',
  305. getAsync: true,
  306. postAsync: true,
  307. resStore: undefined,
  308. useLocalStorage: false,
  309. localStorageExpirationTime: 7*24*60*60*1000,
  310. dynamicLoad: false,
  311. sendMissing: false,
  312. sendMissingTo: 'fallback', // current | all
  313. sendType: 'POST',
  314. interpolationPrefix: '__',
  315. interpolationSuffix: '__',
  316. defaultVariables: false,
  317. reusePrefix: '$t(',
  318. reuseSuffix: ')',
  319. pluralSuffix: '_plural',
  320. pluralNotFound: ['plural_not_found', Math.random()].join(''),
  321. contextNotFound: ['context_not_found', Math.random()].join(''),
  322. escapeInterpolation: false,
  323. indefiniteSuffix: '_indefinite',
  324. indefiniteNotFound: ['indefinite_not_found', Math.random()].join(''),
  325. setJqueryExt: true,
  326. defaultValueFromContent: true,
  327. useDataAttrOptions: false,
  328. cookieExpirationTime: undefined,
  329. useCookie: true,
  330. cookieName: 'i18next',
  331. cookieDomain: undefined,
  332. objectTreeKeyHandler: undefined,
  333. postProcess: undefined,
  334. parseMissingKey: undefined,
  335. missingKeyHandler: sync.postMissing,
  336. ajaxTimeout: 0,
  337. shortcutFunction: 'sprintf' // or: defaultValue
  338. };
  339. function _extend(target, source) {
  340. if (!source || typeof source === 'function') {
  341. return target;
  342. }
  343. for (var attr in source) { target[attr] = source[attr]; }
  344. return target;
  345. }
  346. function _deepExtend(target, source) {
  347. for (var prop in source)
  348. if (prop in target)
  349. _deepExtend(target[prop], source[prop]);
  350. else
  351. target[prop] = source[prop];
  352. return target;
  353. }
  354. function _each(object, callback, args) {
  355. var name, i = 0,
  356. length = object.length,
  357. isObj = length === undefined || Object.prototype.toString.apply(object) !== '[object Array]' || typeof object === "function";
  358. if (args) {
  359. if (isObj) {
  360. for (name in object) {
  361. if (callback.apply(object[name], args) === false) {
  362. break;
  363. }
  364. }
  365. } else {
  366. for ( ; i < length; ) {
  367. if (callback.apply(object[i++], args) === false) {
  368. break;
  369. }
  370. }
  371. }
  372. // A special, fast, case for the most common use of each
  373. } else {
  374. if (isObj) {
  375. for (name in object) {
  376. if (callback.call(object[name], name, object[name]) === false) {
  377. break;
  378. }
  379. }
  380. } else {
  381. for ( ; i < length; ) {
  382. if (callback.call(object[i], i, object[i++]) === false) {
  383. break;
  384. }
  385. }
  386. }
  387. }
  388. return object;
  389. }
  390. var _entityMap = {
  391. "&": "&amp;",
  392. "<": "&lt;",
  393. ">": "&gt;",
  394. '"': '&quot;',
  395. "'": '&#39;',
  396. "/": '&#x2F;'
  397. };
  398. function _escape(data) {
  399. if (typeof data === 'string') {
  400. return data.replace(/[&<>"'\/]/g, function (s) {
  401. return _entityMap[s];
  402. });
  403. }else{
  404. return data;
  405. }
  406. }
  407. function _ajax(options) {
  408. // v0.5.0 of https://github.com/goloroden/http.js
  409. var getXhr = function (callback) {
  410. // Use the native XHR object if the browser supports it.
  411. if (window.XMLHttpRequest) {
  412. return callback(null, new XMLHttpRequest());
  413. } else if (window.ActiveXObject) {
  414. // In Internet Explorer check for ActiveX versions of the XHR object.
  415. try {
  416. return callback(null, new ActiveXObject("Msxml2.XMLHTTP"));
  417. } catch (e) {
  418. return callback(null, new ActiveXObject("Microsoft.XMLHTTP"));
  419. }
  420. }
  421. // If no XHR support was found, throw an error.
  422. return callback(new Error());
  423. };
  424. var encodeUsingUrlEncoding = function (data) {
  425. if(typeof data === 'string') {
  426. return data;
  427. }
  428. var result = [];
  429. for(var dataItem in data) {
  430. if(data.hasOwnProperty(dataItem)) {
  431. result.push(encodeURIComponent(dataItem) + '=' + encodeURIComponent(data[dataItem]));
  432. }
  433. }
  434. return result.join('&');
  435. };
  436. var utf8 = function (text) {
  437. text = text.replace(/\r\n/g, '\n');
  438. var result = '';
  439. for(var i = 0; i < text.length; i++) {
  440. var c = text.charCodeAt(i);
  441. if(c < 128) {
  442. result += String.fromCharCode(c);
  443. } else if((c > 127) && (c < 2048)) {
  444. result += String.fromCharCode((c >> 6) | 192);
  445. result += String.fromCharCode((c & 63) | 128);
  446. } else {
  447. result += String.fromCharCode((c >> 12) | 224);
  448. result += String.fromCharCode(((c >> 6) & 63) | 128);
  449. result += String.fromCharCode((c & 63) | 128);
  450. }
  451. }
  452. return result;
  453. };
  454. var base64 = function (text) {
  455. var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  456. text = utf8(text);
  457. var result = '',
  458. chr1, chr2, chr3,
  459. enc1, enc2, enc3, enc4,
  460. i = 0;
  461. do {
  462. chr1 = text.charCodeAt(i++);
  463. chr2 = text.charCodeAt(i++);
  464. chr3 = text.charCodeAt(i++);
  465. enc1 = chr1 >> 2;
  466. enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
  467. enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
  468. enc4 = chr3 & 63;
  469. if(isNaN(chr2)) {
  470. enc3 = enc4 = 64;
  471. } else if(isNaN(chr3)) {
  472. enc4 = 64;
  473. }
  474. result +=
  475. keyStr.charAt(enc1) +
  476. keyStr.charAt(enc2) +
  477. keyStr.charAt(enc3) +
  478. keyStr.charAt(enc4);
  479. chr1 = chr2 = chr3 = '';
  480. enc1 = enc2 = enc3 = enc4 = '';
  481. } while(i < text.length);
  482. return result;
  483. };
  484. var mergeHeaders = function () {
  485. // Use the first header object as base.
  486. var result = arguments[0];
  487. // Iterate through the remaining header objects and add them.
  488. for(var i = 1; i < arguments.length; i++) {
  489. var currentHeaders = arguments[i];
  490. for(var header in currentHeaders) {
  491. if(currentHeaders.hasOwnProperty(header)) {
  492. result[header] = currentHeaders[header];
  493. }
  494. }
  495. }
  496. // Return the merged headers.
  497. return result;
  498. };
  499. var ajax = function (method, url, options, callback) {
  500. // Adjust parameters.
  501. if(typeof options === 'function') {
  502. callback = options;
  503. options = {};
  504. }
  505. // Set default parameter values.
  506. options.cache = options.cache || false;
  507. options.data = options.data || {};
  508. options.headers = options.headers || {};
  509. options.jsonp = options.jsonp || false;
  510. options.async = options.async === undefined ? true : options.async;
  511. // Merge the various header objects.
  512. var headers = mergeHeaders({
  513. 'accept': '*/*',
  514. 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8'
  515. }, ajax.headers, options.headers);
  516. // Encode the data according to the content-type.
  517. var payload;
  518. if (headers['content-type'] === 'application/json') {
  519. payload = JSON.stringify(options.data);
  520. } else {
  521. payload = encodeUsingUrlEncoding(options.data);
  522. }
  523. // Specially prepare GET requests: Setup the query string, handle caching and make a JSONP call
  524. // if neccessary.
  525. if(method === 'GET') {
  526. // Setup the query string.
  527. var queryString = [];
  528. if(payload) {
  529. queryString.push(payload);
  530. payload = null;
  531. }
  532. // Handle caching.
  533. if(!options.cache) {
  534. queryString.push('_=' + (new Date()).getTime());
  535. }
  536. // If neccessary prepare the query string for a JSONP call.
  537. if(options.jsonp) {
  538. queryString.push('callback=' + options.jsonp);
  539. queryString.push('jsonp=' + options.jsonp);
  540. }
  541. // Merge the query string and attach it to the url.
  542. queryString = queryString.join('&');
  543. if (queryString.length > 1) {
  544. if (url.indexOf('?') > -1) {
  545. url += '&' + queryString;
  546. } else {
  547. url += '?' + queryString;
  548. }
  549. }
  550. // Make a JSONP call if neccessary.
  551. if(options.jsonp) {
  552. var head = document.getElementsByTagName('head')[0];
  553. var script = document.createElement('script');
  554. script.type = 'text/javascript';
  555. script.src = url;
  556. head.appendChild(script);
  557. return;
  558. }
  559. }
  560. // Since we got here, it is no JSONP request, so make a normal XHR request.
  561. getXhr(function (err, xhr) {
  562. if(err) return callback(err);
  563. // Open the request.
  564. xhr.open(method, url, options.async);
  565. // Set the request headers.
  566. for(var header in headers) {
  567. if(headers.hasOwnProperty(header)) {
  568. xhr.setRequestHeader(header, headers[header]);
  569. }
  570. }
  571. // Handle the request events.
  572. xhr.onreadystatechange = function () {
  573. if(xhr.readyState === 4) {
  574. var data = xhr.responseText || '';
  575. // If no callback is given, return.
  576. if(!callback) {
  577. return;
  578. }
  579. // Return an object that provides access to the data as text and JSON.
  580. callback(xhr.status, {
  581. text: function () {
  582. return data;
  583. },
  584. json: function () {
  585. try {
  586. return JSON.parse(data)
  587. } catch (e) {
  588. f.error('Can not parse JSON. URL: ' + url);
  589. return {};
  590. }
  591. }
  592. });
  593. }
  594. };
  595. // Actually send the XHR request.
  596. xhr.send(payload);
  597. });
  598. };
  599. // Define the external interface.
  600. var http = {
  601. authBasic: function (username, password) {
  602. ajax.headers['Authorization'] = 'Basic ' + base64(username + ':' + password);
  603. },
  604. connect: function (url, options, callback) {
  605. return ajax('CONNECT', url, options, callback);
  606. },
  607. del: function (url, options, callback) {
  608. return ajax('DELETE', url, options, callback);
  609. },
  610. get: function (url, options, callback) {
  611. return ajax('GET', url, options, callback);
  612. },
  613. head: function (url, options, callback) {
  614. return ajax('HEAD', url, options, callback);
  615. },
  616. headers: function (headers) {
  617. ajax.headers = headers || {};
  618. },
  619. isAllowed: function (url, verb, callback) {
  620. this.options(url, function (status, data) {
  621. callback(data.text().indexOf(verb) !== -1);
  622. });
  623. },
  624. options: function (url, options, callback) {
  625. return ajax('OPTIONS', url, options, callback);
  626. },
  627. patch: function (url, options, callback) {
  628. return ajax('PATCH', url, options, callback);
  629. },
  630. post: function (url, options, callback) {
  631. return ajax('POST', url, options, callback);
  632. },
  633. put: function (url, options, callback) {
  634. return ajax('PUT', url, options, callback);
  635. },
  636. trace: function (url, options, callback) {
  637. return ajax('TRACE', url, options, callback);
  638. }
  639. };
  640. var methode = options.type ? options.type.toLowerCase() : 'get';
  641. http[methode](options.url, options, function (status, data) {
  642. // file: protocol always gives status code 0, so check for data
  643. if (status === 200 || (status === 0 && data.text())) {
  644. options.success(data.json(), status, null);
  645. } else {
  646. options.error(data.text(), status, null);
  647. }
  648. });
  649. }
  650. var _cookie = {
  651. create: function(name,value,minutes,domain) {
  652. var expires;
  653. if (minutes) {
  654. var date = new Date();
  655. date.setTime(date.getTime()+(minutes*60*1000));
  656. expires = "; expires="+date.toGMTString();
  657. }
  658. else expires = "";
  659. domain = (domain)? "domain="+domain+";" : "";
  660. document.cookie = name+"="+value+expires+";"+domain+"path=/";
  661. },
  662. read: function(name) {
  663. var nameEQ = name + "=";
  664. var ca = document.cookie.split(';');
  665. for(var i=0;i < ca.length;i++) {
  666. var c = ca[i];
  667. while (c.charAt(0)==' ') c = c.substring(1,c.length);
  668. if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length,c.length);
  669. }
  670. return null;
  671. },
  672. remove: function(name) {
  673. this.create(name,"",-1);
  674. }
  675. };
  676. var cookie_noop = {
  677. create: function(name,value,minutes,domain) {},
  678. read: function(name) { return null; },
  679. remove: function(name) {}
  680. };
  681. // move dependent functions to a container so that
  682. // they can be overriden easier in no jquery environment (node.js)
  683. var f = {
  684. extend: $ ? $.extend : _extend,
  685. deepExtend: _deepExtend,
  686. each: $ ? $.each : _each,
  687. ajax: $ ? $.ajax : (typeof document !== 'undefined' ? _ajax : function() {}),
  688. cookie: typeof document !== 'undefined' ? _cookie : cookie_noop,
  689. detectLanguage: detectLanguage,
  690. escape: _escape,
  691. log: function(str) {
  692. if (o.debug && typeof console !== "undefined") console.log(str);
  693. },
  694. error: function(str) {
  695. if (typeof console !== "undefined") console.error(str);
  696. },
  697. getCountyIndexOfLng: function(lng) {
  698. var lng_index = 0;
  699. if (lng === 'nb-NO' || lng === 'nn-NO' || lng === 'nb-no' || lng === 'nn-no') lng_index = 1;
  700. return lng_index;
  701. },
  702. toLanguages: function(lng, fallbackLng) {
  703. var log = this.log;
  704. fallbackLng = fallbackLng || o.fallbackLng;
  705. if (typeof fallbackLng === 'string')
  706. fallbackLng = [fallbackLng];
  707. function applyCase(l) {
  708. var ret = l;
  709. if (typeof l === 'string' && l.indexOf('-') > -1) {
  710. var parts = l.split('-');
  711. ret = o.lowerCaseLng ?
  712. parts[0].toLowerCase() + '-' + parts[1].toLowerCase() :
  713. parts[0].toLowerCase() + '-' + parts[1].toUpperCase();
  714. } else {
  715. ret = o.lowerCaseLng ? l.toLowerCase() : l;
  716. }
  717. return ret;
  718. }
  719. var languages = [];
  720. var whitelist = o.lngWhitelist || false;
  721. var addLanguage = function(language){
  722. //reject langs not whitelisted
  723. if(!whitelist || whitelist.indexOf(language) > -1){
  724. languages.push(language);
  725. }else{
  726. log('rejecting non-whitelisted language: ' + language);
  727. }
  728. };
  729. if (typeof lng === 'string' && lng.indexOf('-') > -1) {
  730. var parts = lng.split('-');
  731. if (o.load !== 'unspecific') addLanguage(applyCase(lng));
  732. if (o.load !== 'current') addLanguage(applyCase(parts[this.getCountyIndexOfLng(lng)]));
  733. } else {
  734. addLanguage(applyCase(lng));
  735. }
  736. for (var i = 0; i < fallbackLng.length; i++) {
  737. if (languages.indexOf(fallbackLng[i]) === -1 && fallbackLng[i]) languages.push(applyCase(fallbackLng[i]));
  738. }
  739. return languages;
  740. },
  741. regexEscape: function(str) {
  742. return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
  743. },
  744. regexReplacementEscape: function(strOrFn) {
  745. if (typeof strOrFn === 'string') {
  746. return strOrFn.replace(/\$/g, "$$$$");
  747. } else {
  748. return strOrFn;
  749. }
  750. },
  751. localStorage: {
  752. setItem: function(key, value) {
  753. if (window.localStorage) {
  754. try {
  755. window.localStorage.setItem(key, value);
  756. } catch (e) {
  757. f.log('failed to set value for key "' + key + '" to localStorage.');
  758. }
  759. }
  760. },
  761. getItem: function(key, value) {
  762. if (window.localStorage) {
  763. try {
  764. return window.localStorage.getItem(key, value);
  765. } catch (e) {
  766. f.log('failed to get value for key "' + key + '" from localStorage.');
  767. return undefined;
  768. }
  769. }
  770. }
  771. }
  772. };
  773. function init(options, cb) {
  774. if (typeof options === 'function') {
  775. cb = options;
  776. options = {};
  777. }
  778. options = options || {};
  779. // override defaults with passed in options
  780. f.extend(o, options);
  781. delete o.fixLng; /* passed in each time */
  782. // override functions: .log(), .detectLanguage(), etc
  783. if (o.functions) {
  784. delete o.functions;
  785. f.extend(f, options.functions);
  786. }
  787. // create namespace object if namespace is passed in as string
  788. if (typeof o.ns == 'string') {
  789. o.ns = { namespaces: [o.ns], defaultNs: o.ns};
  790. }
  791. // fallback namespaces
  792. if (typeof o.fallbackNS == 'string') {
  793. o.fallbackNS = [o.fallbackNS];
  794. }
  795. // fallback languages
  796. if (typeof o.fallbackLng == 'string' || typeof o.fallbackLng == 'boolean') {
  797. o.fallbackLng = [o.fallbackLng];
  798. }
  799. // escape prefix/suffix
  800. o.interpolationPrefixEscaped = f.regexEscape(o.interpolationPrefix);
  801. o.interpolationSuffixEscaped = f.regexEscape(o.interpolationSuffix);
  802. if (!o.lng) o.lng = f.detectLanguage();
  803. languages = f.toLanguages(o.lng);
  804. currentLng = languages[0];
  805. f.log('currentLng set to: ' + currentLng);
  806. if (o.useCookie && f.cookie.read(o.cookieName) !== currentLng){ //cookie is unset or invalid
  807. f.cookie.create(o.cookieName, currentLng, o.cookieExpirationTime, o.cookieDomain);
  808. }
  809. if (o.detectLngFromLocalStorage && typeof document !== 'undefined' && window.localStorage) {
  810. f.localStorage.setItem('i18next_lng', currentLng);
  811. }
  812. var lngTranslate = translate;
  813. if (options.fixLng) {
  814. lngTranslate = function(key, options) {
  815. options = options || {};
  816. options.lng = options.lng || lngTranslate.lng;
  817. return translate(key, options);
  818. };
  819. lngTranslate.lng = currentLng;
  820. }
  821. pluralExtensions.setCurrentLng(currentLng);
  822. // add JQuery extensions
  823. if ($ && o.setJqueryExt) {
  824. addJqueryFunct && addJqueryFunct();
  825. } else {
  826. addJqueryLikeFunctionality && addJqueryLikeFunctionality();
  827. }
  828. // jQuery deferred
  829. var deferred;
  830. if ($ && $.Deferred) {
  831. deferred = $.Deferred();
  832. }
  833. // return immidiatly if res are passed in
  834. if (o.resStore) {
  835. resStore = o.resStore;
  836. initialized = true;
  837. if (cb) cb(lngTranslate);
  838. if (deferred) deferred.resolve(lngTranslate);
  839. if (deferred) return deferred.promise();
  840. return;
  841. }
  842. // languages to load
  843. var lngsToLoad = f.toLanguages(o.lng);
  844. if (typeof o.preload === 'string') o.preload = [o.preload];
  845. for (var i = 0, l = o.preload.length; i < l; i++) {
  846. var pres = f.toLanguages(o.preload[i]);
  847. for (var y = 0, len = pres.length; y < len; y++) {
  848. if (lngsToLoad.indexOf(pres[y]) < 0) {
  849. lngsToLoad.push(pres[y]);
  850. }
  851. }
  852. }
  853. // else load them
  854. i18n.sync.load(lngsToLoad, o, function(err, store) {
  855. resStore = store;
  856. initialized = true;
  857. if (cb) cb(err, lngTranslate);
  858. if (deferred) (!err ? deferred.resolve : deferred.reject)(err || lngTranslate);
  859. });
  860. if (deferred) return deferred.promise();
  861. }
  862. function isInitialized() {
  863. return initialized;
  864. }
  865. function preload(lngs, cb) {
  866. if (typeof lngs === 'string') lngs = [lngs];
  867. for (var i = 0, l = lngs.length; i < l; i++) {
  868. if (o.preload.indexOf(lngs[i]) < 0) {
  869. o.preload.push(lngs[i]);
  870. }
  871. }
  872. return init(cb);
  873. }
  874. function addResourceBundle(lng, ns, resources, deep) {
  875. if (typeof ns !== 'string') {
  876. resources = ns;
  877. ns = o.ns.defaultNs;
  878. } else if (o.ns.namespaces.indexOf(ns) < 0) {
  879. o.ns.namespaces.push(ns);
  880. }
  881. resStore[lng] = resStore[lng] || {};
  882. resStore[lng][ns] = resStore[lng][ns] || {};
  883. if (deep) {
  884. f.deepExtend(resStore[lng][ns], resources);
  885. } else {
  886. f.extend(resStore[lng][ns], resources);
  887. }
  888. if (o.useLocalStorage) {
  889. sync._storeLocal(resStore);
  890. }
  891. }
  892. function hasResourceBundle(lng, ns) {
  893. if (typeof ns !== 'string') {
  894. ns = o.ns.defaultNs;
  895. }
  896. resStore[lng] = resStore[lng] || {};
  897. var res = resStore[lng][ns] || {};
  898. var hasValues = false;
  899. for(var prop in res) {
  900. if (res.hasOwnProperty(prop)) {
  901. hasValues = true;
  902. }
  903. }
  904. return hasValues;
  905. }
  906. function getResourceBundle(lng, ns) {
  907. if (typeof ns !== 'string') {
  908. ns = o.ns.defaultNs;
  909. }
  910. resStore[lng] = resStore[lng] || {};
  911. return f.extend({}, resStore[lng][ns]);
  912. }
  913. function removeResourceBundle(lng, ns) {
  914. if (typeof ns !== 'string') {
  915. ns = o.ns.defaultNs;
  916. }
  917. resStore[lng] = resStore[lng] || {};
  918. resStore[lng][ns] = {};
  919. if (o.useLocalStorage) {
  920. sync._storeLocal(resStore);
  921. }
  922. }
  923. function addResource(lng, ns, key, value) {
  924. if (typeof ns !== 'string') {
  925. resource = ns;
  926. ns = o.ns.defaultNs;
  927. } else if (o.ns.namespaces.indexOf(ns) < 0) {
  928. o.ns.namespaces.push(ns);
  929. }
  930. resStore[lng] = resStore[lng] || {};
  931. resStore[lng][ns] = resStore[lng][ns] || {};
  932. var keys = key.split(o.keyseparator);
  933. var x = 0;
  934. var node = resStore[lng][ns];
  935. var origRef = node;
  936. while (keys[x]) {
  937. if (x == keys.length - 1)
  938. node[keys[x]] = value;
  939. else {
  940. if (node[keys[x]] == null)
  941. node[keys[x]] = {};
  942. node = node[keys[x]];
  943. }
  944. x++;
  945. }
  946. if (o.useLocalStorage) {
  947. sync._storeLocal(resStore);
  948. }
  949. }
  950. function addResources(lng, ns, resources) {
  951. if (typeof ns !== 'string') {
  952. resource = ns;
  953. ns = o.ns.defaultNs;
  954. } else if (o.ns.namespaces.indexOf(ns) < 0) {
  955. o.ns.namespaces.push(ns);
  956. }
  957. for (var m in resources) {
  958. if (typeof resources[m] === 'string') addResource(lng, ns, m, resources[m]);
  959. }
  960. }
  961. function setDefaultNamespace(ns) {
  962. o.ns.defaultNs = ns;
  963. }
  964. function loadNamespace(namespace, cb) {
  965. loadNamespaces([namespace], cb);
  966. }
  967. function loadNamespaces(namespaces, cb) {
  968. var opts = {
  969. dynamicLoad: o.dynamicLoad,
  970. resGetPath: o.resGetPath,
  971. getAsync: o.getAsync,
  972. customLoad: o.customLoad,
  973. ns: { namespaces: namespaces, defaultNs: ''} /* new namespaces to load */
  974. };
  975. // languages to load
  976. var lngsToLoad = f.toLanguages(o.lng);
  977. if (typeof o.preload === 'string') o.preload = [o.preload];
  978. for (var i = 0, l = o.preload.length; i < l; i++) {
  979. var pres = f.toLanguages(o.preload[i]);
  980. for (var y = 0, len = pres.length; y < len; y++) {
  981. if (lngsToLoad.indexOf(pres[y]) < 0) {
  982. lngsToLoad.push(pres[y]);
  983. }
  984. }
  985. }
  986. // check if we have to load
  987. var lngNeedLoad = [];
  988. for (var a = 0, lenA = lngsToLoad.length; a < lenA; a++) {
  989. var needLoad = false;
  990. var resSet = resStore[lngsToLoad[a]];
  991. if (resSet) {
  992. for (var b = 0, lenB = namespaces.length; b < lenB; b++) {
  993. if (!resSet[namespaces[b]]) needLoad = true;
  994. }
  995. } else {
  996. needLoad = true;
  997. }
  998. if (needLoad) lngNeedLoad.push(lngsToLoad[a]);
  999. }
  1000. if (lngNeedLoad.length) {
  1001. i18n.sync._fetch(lngNeedLoad, opts, function(err, store) {
  1002. var todo = namespaces.length * lngNeedLoad.length;
  1003. // load each file individual
  1004. f.each(namespaces, function(nsIndex, nsValue) {
  1005. // append namespace to namespace array
  1006. if (o.ns.namespaces.indexOf(nsValue) < 0) {
  1007. o.ns.namespaces.push(nsValue);
  1008. }
  1009. f.each(lngNeedLoad, function(lngIndex, lngValue) {
  1010. resStore[lngValue] = resStore[lngValue] || {};
  1011. resStore[lngValue][nsValue] = store[lngValue][nsValue];
  1012. todo--; // wait for all done befor callback
  1013. if (todo === 0 && cb) {
  1014. if (o.useLocalStorage) i18n.sync._storeLocal(resStore);
  1015. cb();
  1016. }
  1017. });
  1018. });
  1019. });
  1020. } else {
  1021. if (cb) cb();
  1022. }
  1023. }
  1024. function setLng(lng, options, cb) {
  1025. if (typeof options === 'function') {
  1026. cb = options;
  1027. options = {};
  1028. } else if (!options) {
  1029. options = {};
  1030. }
  1031. options.lng = lng;
  1032. return init(options, cb);
  1033. }
  1034. function lng() {
  1035. return currentLng;
  1036. }
  1037. function reload(cb) {
  1038. resStore = {};
  1039. setLng(currentLng, cb);
  1040. }
  1041. function noConflict() {
  1042. window.i18next = window.i18n;
  1043. if (conflictReference) {
  1044. window.i18n = conflictReference;
  1045. } else {
  1046. delete window.i18n;
  1047. }
  1048. }
  1049. function addJqueryFunct() {
  1050. // $.t shortcut
  1051. $.t = $.t || translate;
  1052. function parse(ele, key, options) {
  1053. if (key.length === 0) return;
  1054. var attr = 'text';
  1055. if (key.indexOf('[') === 0) {
  1056. var parts = key.split(']');
  1057. key = parts[1];
  1058. attr = parts[0].substr(1, parts[0].length-1);
  1059. }
  1060. if (key.indexOf(';') === key.length-1) {
  1061. key = key.substr(0, key.length-2);
  1062. }
  1063. var optionsToUse;
  1064. if (attr === 'html') {
  1065. optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
  1066. ele.html($.t(key, optionsToUse));
  1067. } else if (attr === 'text') {
  1068. optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.text() }, options) : options;
  1069. ele.text($.t(key, optionsToUse));
  1070. } else if (attr === 'prepend') {
  1071. optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
  1072. ele.prepend($.t(key, optionsToUse));
  1073. } else if (attr === 'append') {
  1074. optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
  1075. ele.append($.t(key, optionsToUse));
  1076. } else if (attr.indexOf("data-") === 0) {
  1077. var dataAttr = attr.substr(("data-").length);
  1078. optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.data(dataAttr) }, options) : options;
  1079. var translated = $.t(key, optionsToUse);
  1080. //we change into the data cache
  1081. ele.data(dataAttr, translated);
  1082. //we change into the dom
  1083. ele.attr(attr, translated);
  1084. } else {
  1085. optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.attr(attr) }, options) : options;
  1086. ele.attr(attr, $.t(key, optionsToUse));
  1087. }
  1088. }
  1089. function localize(ele, options) {
  1090. var key = ele.attr(o.selectorAttr);
  1091. if (!key && typeof key !== 'undefined' && key !== false) key = ele.text() || ele.val();
  1092. if (!key) return;
  1093. var target = ele
  1094. , targetSelector = ele.data("i18n-target");
  1095. if (targetSelector) {
  1096. target = ele.find(targetSelector) || ele;
  1097. }
  1098. if (!options && o.useDataAttrOptions === true) {
  1099. options = ele.data("i18n-options");
  1100. }
  1101. options = options || {};
  1102. if (key.indexOf(';') >= 0) {
  1103. var keys = key.split(';');
  1104. $.each(keys, function(m, k) {
  1105. if (k !== '') parse(target, k, options);
  1106. });
  1107. } else {
  1108. parse(target, key, options);
  1109. }
  1110. if (o.useDataAttrOptions === true) ele.data("i18n-options", options);
  1111. }
  1112. // fn
  1113. $.fn.i18n = function (options) {
  1114. return this.each(function() {
  1115. // localize element itself
  1116. localize($(this), options);
  1117. // localize childs
  1118. var elements = $(this).find('[' + o.selectorAttr + ']');
  1119. elements.each(function() {
  1120. localize($(this), options);
  1121. });
  1122. });
  1123. };
  1124. }
  1125. function addJqueryLikeFunctionality() {
  1126. function parse(ele, key, options) {
  1127. if (key.length === 0) return;
  1128. var attr = 'text';
  1129. if (key.indexOf('[') === 0) {
  1130. var parts = key.split(']');
  1131. key = parts[1];
  1132. attr = parts[0].substr(1, parts[0].length-1);
  1133. }
  1134. if (key.indexOf(';') === key.length-1) {
  1135. key = key.substr(0, key.length-2);
  1136. }
  1137. if (attr === 'html') {
  1138. ele.innerHTML = translate(key, options);
  1139. } else if (attr === 'text') {
  1140. ele.textContent = translate(key, options);
  1141. } else if (attr === 'prepend') {
  1142. ele.insertAdjacentHTML(translate(key, options), 'afterbegin');
  1143. } else if (attr === 'append') {
  1144. ele.insertAdjacentHTML(translate(key, options), 'beforeend');
  1145. } else {
  1146. ele.setAttribute(attr, translate(key, options));
  1147. }
  1148. }
  1149. function localize(ele, options) {
  1150. var key = ele.getAttribute(o.selectorAttr);
  1151. if (!key && typeof key !== 'undefined' && key !== false) key = ele.textContent || ele.value;
  1152. if (!key) return;
  1153. var target = ele
  1154. , targetSelector = ele.getAttribute("i18n-target");
  1155. if (targetSelector) {
  1156. target = ele.querySelector(targetSelector) || ele;
  1157. }
  1158. if (key.indexOf(';') >= 0) {
  1159. var keys = key.split(';'), index = 0, length = keys.length;
  1160. for ( ; index < length; index++) {
  1161. if (keys[index] !== '') parse(target, keys[index], options);
  1162. }
  1163. } else {
  1164. parse(target, key, options);
  1165. }
  1166. }
  1167. // fn
  1168. i18n.translateObject = function (object, options) {
  1169. // localize childs
  1170. var elements = object.querySelectorAll('[' + o.selectorAttr + ']');
  1171. var index = 0, length = elements.length;
  1172. for ( ; index < length; index++) {
  1173. localize(elements[index], options);
  1174. }
  1175. };
  1176. }
  1177. function applyReplacement(str, replacementHash, nestedKey, options) {
  1178. if (!str) return str;
  1179. options = options || replacementHash; // first call uses replacement hash combined with options
  1180. if (str.indexOf(options.interpolationPrefix || o.interpolationPrefix) < 0) return str;
  1181. var prefix = options.interpolationPrefix ? f.regexEscape(options.interpolationPrefix) : o.interpolationPrefixEscaped
  1182. , suffix = options.interpolationSuffix ? f.regexEscape(options.interpolationSuffix) : o.interpolationSuffixEscaped
  1183. , unEscapingSuffix = 'HTML'+suffix;
  1184. var hash = replacementHash.replace && typeof replacementHash.replace === 'object' ? replacementHash.replace : replacementHash;
  1185. f.each(hash, function(key, value) {
  1186. var nextKey = nestedKey ? nestedKey + o.keyseparator + key : key;
  1187. if (typeof value === 'object' && value !== null) {
  1188. str = applyReplacement(str, value, nextKey, options);
  1189. } else {
  1190. if (options.escapeInterpolation || o.escapeInterpolation) {
  1191. str = str.replace(new RegExp([prefix, nextKey, unEscapingSuffix].join(''), 'g'), f.regexReplacementEscape(value));
  1192. str = str.replace(new RegExp([prefix, nextKey, suffix].join(''), 'g'), f.regexReplacementEscape(f.escape(value)));
  1193. } else {
  1194. str = str.replace(new RegExp([prefix, nextKey, suffix].join(''), 'g'), f.regexReplacementEscape(value));
  1195. }
  1196. // str = options.escapeInterpolation;
  1197. }
  1198. });
  1199. return str;
  1200. }
  1201. // append it to functions
  1202. f.applyReplacement = applyReplacement;
  1203. function applyReuse(translated, options) {
  1204. var comma = ',';
  1205. var options_open = '{';
  1206. var options_close = '}';
  1207. var opts = f.extend({}, options);
  1208. delete opts.postProcess;
  1209. while (translated.indexOf(o.reusePrefix) != -1) {
  1210. replacementCounter++;
  1211. if (replacementCounter > o.maxRecursion) { break; } // safety net for too much recursion
  1212. var index_of_opening = translated.lastIndexOf(o.reusePrefix);
  1213. var index_of_end_of_closing = translated.indexOf(o.reuseSuffix, index_of_opening) + o.reuseSuffix.length;
  1214. var token = translated.substring(index_of_opening, index_of_end_of_closing);
  1215. var token_without_symbols = token.replace(o.reusePrefix, '').replace(o.reuseSuffix, '');
  1216. if (index_of_end_of_closing <= index_of_opening) {
  1217. f.error('there is an missing closing in following translation value', translated);
  1218. return '';
  1219. }
  1220. if (token_without_symbols.indexOf(comma) != -1) {
  1221. var index_of_token_end_of_closing = token_without_symbols.indexOf(comma);
  1222. 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) {
  1223. var index_of_opts_opening = token_without_symbols.indexOf(options_open, index_of_token_end_of_closing);
  1224. var index_of_opts_end_of_closing = token_without_symbols.indexOf(options_close, index_of_opts_opening) + options_close.length;
  1225. try {
  1226. opts = f.extend(opts, JSON.parse(token_without_symbols.substring(index_of_opts_opening, index_of_opts_end_of_closing)));
  1227. token_without_symbols = token_without_symbols.substring(0, index_of_token_end_of_closing);
  1228. } catch (e) {
  1229. }
  1230. }
  1231. }
  1232. var translated_token = _translate(token_without_symbols, opts);
  1233. translated = translated.replace(token, f.regexReplacementEscape(translated_token));
  1234. }
  1235. return translated;
  1236. }
  1237. function hasContext(options) {
  1238. return (options.context && (typeof options.context == 'string' || typeof options.context == 'number'));
  1239. }
  1240. function needsPlural(options, lng) {
  1241. return (options.count !== undefined && typeof options.count != 'string'/* && pluralExtensions.needsPlural(lng, options.count)*/);
  1242. }
  1243. function needsIndefiniteArticle(options) {
  1244. return (options.indefinite_article !== undefined && typeof options.indefinite_article != 'string' && options.indefinite_article);
  1245. }
  1246. function exists(key, options) {
  1247. options = options || {};
  1248. var notFound = _getDefaultValue(key, options)
  1249. , found = _find(key, options);
  1250. return found !== undefined || found === notFound;
  1251. }
  1252. function translate(key, options) {
  1253. options = options || {};
  1254. if (!initialized) {
  1255. f.log('i18next not finished initialization. you might have called t function before loading resources finished.')
  1256. return options.defaultValue || '';
  1257. };
  1258. replacementCounter = 0;
  1259. return _translate.apply(null, arguments);
  1260. }
  1261. function _getDefaultValue(key, options) {
  1262. return (options.defaultValue !== undefined) ? options.defaultValue : key;
  1263. }
  1264. function _injectSprintfProcessor() {
  1265. var values = [];
  1266. // mh: build array from second argument onwards
  1267. for (var i = 1; i < arguments.length; i++) {
  1268. values.push(arguments[i]);
  1269. }
  1270. return {
  1271. postProcess: 'sprintf',
  1272. sprintf: values
  1273. };
  1274. }
  1275. function _translate(potentialKeys, options) {
  1276. if (options && typeof options !== 'object') {
  1277. if (o.shortcutFunction === 'sprintf') {
  1278. // mh: gettext like sprintf syntax found, automatically create sprintf processor
  1279. options = _injectSprintfProcessor.apply(null, arguments);
  1280. } else if (o.shortcutFunction === 'defaultValue') {
  1281. options = {
  1282. defaultValue: options
  1283. }
  1284. }
  1285. } else {
  1286. options = options || {};
  1287. }
  1288. if (typeof o.defaultVariables === 'object') {
  1289. options = f.extend({}, o.defaultVariables, options);
  1290. }
  1291. if (potentialKeys === undefined || potentialKeys === null || potentialKeys === '') return '';
  1292. if (typeof potentialKeys === 'number') {
  1293. potentialKeys = String(potentialKeys);
  1294. }
  1295. if (typeof potentialKeys === 'string') {
  1296. potentialKeys = [potentialKeys];
  1297. }
  1298. var key = potentialKeys[0];
  1299. if (potentialKeys.length > 1) {
  1300. for (var i = 0; i < potentialKeys.length; i++) {
  1301. key = potentialKeys[i];
  1302. if (exists(key, options)) {
  1303. break;
  1304. }
  1305. }
  1306. }
  1307. var notFound = _getDefaultValue(key, options)
  1308. , found = _find(key, options)
  1309. , lngs = options.lng ? f.toLanguages(options.lng, options.fallbackLng) : languages
  1310. , ns = options.ns || o.ns.defaultNs
  1311. , parts;
  1312. // split ns and key
  1313. if (key.indexOf(o.nsseparator) > -1) {
  1314. parts = key.split(o.nsseparator);
  1315. ns = parts[0];
  1316. key = parts[1];
  1317. }
  1318. if (found === undefined && o.sendMissing && typeof o.missingKeyHandler === 'function') {
  1319. if (options.lng) {
  1320. o.missingKeyHandler(lngs[0], ns, key, notFound, lngs);
  1321. } else {
  1322. o.missingKeyHandler(o.lng, ns, key, notFound, lngs);
  1323. }
  1324. }
  1325. var postProcessorsToApply;
  1326. if (typeof o.postProcess === 'string' && o.postProcess !== '') {
  1327. postProcessorsToApply = [o.postProcess];
  1328. } else if (typeof o.postProcess === 'array' || typeof o.postProcess === 'object') {
  1329. postProcessorsToApply = o.postProcess;
  1330. } else {
  1331. postProcessorsToApply = [];
  1332. }
  1333. if (typeof options.postProcess === 'string' && options.postProcess !== '') {
  1334. postProcessorsToApply = postProcessorsToApply.concat([options.postProcess]);
  1335. } else if (typeof options.postProcess === 'array' || typeof options.postProcess === 'object') {
  1336. postProcessorsToApply = postProcessorsToApply.concat(options.postProcess);
  1337. }
  1338. if (found !== undefined && postProcessorsToApply.length) {
  1339. postProcessorsToApply.forEach(function(postProcessor) {
  1340. if (postProcessors[postProcessor]) {
  1341. found = postProcessors[postProcessor](found, key, options);
  1342. }
  1343. });
  1344. }
  1345. // process notFound if function exists
  1346. var splitNotFound = notFound;
  1347. if (notFound.indexOf(o.nsseparator) > -1) {
  1348. parts = notFound.split(o.nsseparator);
  1349. splitNotFound = parts[1];
  1350. }
  1351. if (splitNotFound === key && o.parseMissingKey) {
  1352. notFound = o.parseMissingKey(notFound);
  1353. }
  1354. if (found === undefined) {
  1355. notFound = applyReplacement(notFound, options);
  1356. notFound = applyReuse(notFound, options);
  1357. if (postProcessorsToApply.length) {
  1358. var val = _getDefaultValue(key, options);
  1359. postProcessorsToApply.forEach(function(postProcessor) {
  1360. if (postProcessors[postProcessor]) {
  1361. found = postProcessors[postProcessor](val, key, options);
  1362. }
  1363. });
  1364. }
  1365. }
  1366. return (found !== undefined) ? found : notFound;
  1367. }
  1368. function _find(key, options) {
  1369. options = options || {};
  1370. var optionWithoutCount, translated
  1371. , notFound = _getDefaultValue(key, options)
  1372. , lngs = languages;
  1373. if (!resStore) { return notFound; } // no resStore to translate from
  1374. // CI mode
  1375. if (lngs[0].toLowerCase() === 'cimode') return notFound;
  1376. // passed in lng
  1377. if (options.lngs) lngs = options.lngs;
  1378. if (options.lng) {
  1379. lngs = f.toLanguages(options.lng, options.fallbackLng);
  1380. if (!resStore[lngs[0]]) {
  1381. var oldAsync = o.getAsync;
  1382. o.getAsync = false;
  1383. i18n.sync.load(lngs, o, function(err, store) {
  1384. f.extend(resStore, store);
  1385. o.getAsync = oldAsync;
  1386. });
  1387. }
  1388. }
  1389. var ns = options.ns || o.ns.defaultNs;
  1390. if (key.indexOf(o.nsseparator) > -1) {
  1391. var parts = key.split(o.nsseparator);
  1392. ns = parts[0];
  1393. key = parts[1];
  1394. }
  1395. if (hasContext(options)) {
  1396. optionWithoutCount = f.extend({}, options);
  1397. delete optionWithoutCount.context;
  1398. optionWithoutCount.defaultValue = o.contextNotFound;
  1399. var contextKey = ns + o.nsseparator + key + '_' + options.context;
  1400. translated = translate(contextKey, optionWithoutCount);
  1401. if (translated != o.contextNotFound) {
  1402. return applyReplacement(translated, { context: options.context }); // apply replacement for context only
  1403. } // else continue translation with original/nonContext key
  1404. }
  1405. if (needsPlural(options, lngs[0])) {
  1406. optionWithoutCount = f.extend({ lngs: [lngs[0]]}, options);
  1407. delete optionWithoutCount.count;
  1408. optionWithoutCount._origLng = optionWithoutCount._origLng || optionWithoutCount.lng || lngs[0];
  1409. delete optionWithoutCount.lng;
  1410. optionWithoutCount.defaultValue = o.pluralNotFound;
  1411. var pluralKey;
  1412. if (!pluralExtensions.needsPlural(lngs[0], options.count)) {
  1413. pluralKey = ns + o.nsseparator + key;
  1414. } else {
  1415. pluralKey = ns + o.nsseparator + key + o.pluralSuffix;
  1416. var pluralExtension = pluralExtensions.get(lngs[0], options.count);
  1417. if (pluralExtension >= 0) {
  1418. pluralKey = pluralKey + '_' + pluralExtension;
  1419. } else if (pluralExtension === 1) {
  1420. pluralKey = ns + o.nsseparator + key; // singular
  1421. }
  1422. }
  1423. translated = translate(pluralKey, optionWithoutCount);
  1424. if (translated != o.pluralNotFound) {
  1425. return applyReplacement(translated, {
  1426. count: options.count,
  1427. interpolationPrefix: options.interpolationPrefix,
  1428. interpolationSuffix: options.interpolationSuffix
  1429. }); // apply replacement for count only
  1430. } else if (lngs.length > 1) {
  1431. // remove failed lng
  1432. var clone = lngs.slice();
  1433. clone.shift();
  1434. options = f.extend(options, { lngs: clone });
  1435. options._origLng = optionWithoutCount._origLng;
  1436. delete options.lng;
  1437. // retry with fallbacks
  1438. translated = translate(ns + o.nsseparator + key, options);
  1439. if (translated != o.pluralNotFound) return translated;
  1440. } else {
  1441. optionWithoutCount.lng = optionWithoutCount._origLng;
  1442. delete optionWithoutCount._origLng;
  1443. translated = translate(ns + o.nsseparator + key, optionWithoutCount);
  1444. return applyReplacement(translated, {
  1445. count: options.count,
  1446. interpolationPrefix: options.interpolationPrefix,
  1447. interpolationSuffix: options.interpolationSuffix
  1448. });
  1449. }
  1450. }
  1451. if (needsIndefiniteArticle(options)) {
  1452. var optionsWithoutIndef = f.extend({}, options);
  1453. delete optionsWithoutIndef.indefinite_article;
  1454. optionsWithoutIndef.defaultValue = o.indefiniteNotFound;
  1455. // If we don't have a count, we want the indefinite, if we do have a count, and needsPlural is false
  1456. var indefiniteKey = ns + o.nsseparator + key + (((options.count && !needsPlural(options, lngs[0])) || !options.count) ? o.indefiniteSuffix : "");
  1457. translated = translate(indefiniteKey, optionsWithoutIndef);
  1458. if (translated != o.indefiniteNotFound) {
  1459. return translated;
  1460. }
  1461. }
  1462. var found;
  1463. var keys = key.split(o.keyseparator);
  1464. for (var i = 0, len = lngs.length; i < len; i++ ) {
  1465. if (found !== undefined) break;
  1466. var l = lngs[i];
  1467. var x = 0;
  1468. var value = resStore[l] && resStore[l][ns];
  1469. while (keys[x]) {
  1470. value = value && value[keys[x]];
  1471. x++;
  1472. }
  1473. if (value !== undefined && (!o.showKeyIfEmpty || value !== '')) {
  1474. var valueType = Object.prototype.toString.apply(value);
  1475. if (typeof value === 'string') {
  1476. value = applyReplacement(value, options);
  1477. value = applyReuse(value, options);
  1478. } else if (valueType === '[object Array]' && !o.returnObjectTrees && !options.returnObjectTrees) {
  1479. value = value.join('\n');
  1480. value = applyReplacement(value, options);
  1481. value = applyReuse(value, options);
  1482. } else if (value === null && o.fallbackOnNull === true) {
  1483. value = undefined;
  1484. } else if (value !== null) {
  1485. if (!o.returnObjectTrees && !options.returnObjectTrees) {
  1486. if (o.objectTreeKeyHandler && typeof o.objectTreeKeyHandler == 'function') {
  1487. value = o.objectTreeKeyHandler(key, value, l, ns, options);
  1488. } else {
  1489. value = 'key \'' + ns + ':' + key + ' (' + l + ')\' ' +
  1490. 'returned an object instead of string.';
  1491. f.log(value);
  1492. }
  1493. } else if (valueType !== '[object Number]' && valueType !== '[object Function]' && valueType !== '[object RegExp]') {
  1494. var copy = (valueType === '[object Array]') ? [] : {}; // apply child translation on a copy
  1495. f.each(value, function(m) {
  1496. copy[m] = _translate(ns + o.nsseparator + key + o.keyseparator + m, options);
  1497. });
  1498. value = copy;
  1499. }
  1500. }
  1501. if (typeof value === 'string' && value.trim() === '' && o.fallbackOnEmpty === true)
  1502. value = undefined;
  1503. found = value;
  1504. }
  1505. }
  1506. if (found === undefined && !options.isFallbackLookup && (o.fallbackToDefaultNS === true || (o.fallbackNS && o.fallbackNS.length > 0))) {
  1507. // set flag for fallback lookup - avoid recursion
  1508. options.isFallbackLookup = true;
  1509. if (o.fallbackNS.length) {
  1510. for (var y = 0, lenY = o.fallbackNS.length; y < lenY; y++) {
  1511. found = _find(o.fallbackNS[y] + o.nsseparator + key, options);
  1512. if (found || (found==="" && o.fallbackOnEmpty === false)) {
  1513. /* compare value without namespace */
  1514. var foundValue = found.indexOf(o.nsseparator) > -1 ? found.split(o.nsseparator)[1] : found
  1515. , notFoundValue = notFound.indexOf(o.nsseparator) > -1 ? notFound.split(o.nsseparator)[1] : notFound;
  1516. if (foundValue !== notFoundValue) break;
  1517. }
  1518. }
  1519. } else {
  1520. options.ns = o.ns.defaultNs;
  1521. found = _find(key, options); // fallback to default NS
  1522. }
  1523. options.isFallbackLookup = false;
  1524. }
  1525. return found;
  1526. }
  1527. function detectLanguage() {
  1528. var detectedLng;
  1529. var whitelist = o.lngWhitelist || [];
  1530. var userLngChoices = [];
  1531. // get from qs
  1532. var qsParm = [];
  1533. if (typeof window !== 'undefined') {
  1534. (function() {
  1535. var query = window.location.search.substring(1);
  1536. var params = query.split('&');
  1537. for (var i=0; i<params.length; i++) {
  1538. var pos = params[i].indexOf('=');
  1539. if (pos > 0) {
  1540. var key = params[i].substring(0,pos);
  1541. if (key == o.detectLngQS) {
  1542. userLngChoices.push(params[i].substring(pos+1));
  1543. }
  1544. }
  1545. }
  1546. })();
  1547. }
  1548. // get from cookie
  1549. if (o.useCookie && typeof document !== 'undefined') {
  1550. var c = f.cookie.read(o.cookieName);
  1551. if (c) userLngChoices.push(c);
  1552. }
  1553. // get from localStorage
  1554. if (o.detectLngFromLocalStorage && typeof window !== 'undefined' && window.localStorage) {
  1555. var lang = f.localStorage.getItem('i18next_lng');
  1556. if (lang) {
  1557. userLngChoices.push(lang);
  1558. }
  1559. }
  1560. // get from navigator
  1561. if (typeof navigator !== 'undefined') {
  1562. if (navigator.languages) { // chrome only; not an array, so can't use .push.apply instead of iterating
  1563. for (var i=0;i<navigator.languages.length;i++) {
  1564. userLngChoices.push(navigator.languages[i]);
  1565. }
  1566. }
  1567. if (navigator.userLanguage) {
  1568. userLngChoices.push(navigator.userLanguage);
  1569. }
  1570. if (navigator.language) {
  1571. userLngChoices.push(navigator.language);
  1572. }
  1573. }
  1574. (function() {
  1575. for (var i=0;i<userLngChoices.length;i++) {
  1576. var lng = userLngChoices[i];
  1577. if (lng.indexOf('-') > -1) {
  1578. var parts = lng.split('-');
  1579. lng = o.lowerCaseLng ?
  1580. parts[0].toLowerCase() + '-' + parts[1].toLowerCase() :
  1581. parts[0].toLowerCase() + '-' + parts[1].toUpperCase();
  1582. }
  1583. if (whitelist.length === 0 || whitelist.indexOf(lng) > -1) {
  1584. detectedLng = lng;
  1585. break;
  1586. }
  1587. }
  1588. })();
  1589. //fallback
  1590. if (!detectedLng){
  1591. detectedLng = o.fallbackLng[0];
  1592. }
  1593. return detectedLng;
  1594. }
  1595. // definition http://translate.sourceforge.net/wiki/l10n/pluralforms
  1596. /* [code, name, numbers, pluralsType] */
  1597. var _rules = [
  1598. ["ach", "Acholi", [1,2], 1],
  1599. ["af", "Afrikaans",[1,2], 2],
  1600. ["ak", "Akan", [1,2], 1],
  1601. ["am", "Amharic", [1,2], 1],
  1602. ["an", "Aragonese",[1,2], 2],
  1603. ["ar", "Arabic", [0,1,2,3,11,100],5],
  1604. ["arn", "Mapudungun",[1,2], 1],
  1605. ["ast", "Asturian", [1,2], 2],
  1606. ["ay", "Aymará", [1], 3],
  1607. ["az", "Azerbaijani",[1,2],2],
  1608. ["be", "Belarusian",[1,2,5],4],
  1609. ["bg", "Bulgarian",[1,2], 2],
  1610. ["bn", "Bengali", [1,2], 2],
  1611. ["bo", "Tibetan", [1], 3],
  1612. ["br", "Breton", [1,2], 1],
  1613. ["bs", "Bosnian", [1,2,5],4],
  1614. ["ca", "Catalan", [1,2], 2],
  1615. ["cgg", "Chiga", [1], 3],
  1616. ["cs", "Czech", [1,2,5],6],
  1617. ["csb", "Kashubian",[1,2,5],7],
  1618. ["cy", "Welsh", [1,2,3,8],8],
  1619. ["da", "Danish", [1,2], 2],
  1620. ["de", "German", [1,2], 2],
  1621. ["dev", "Development Fallback", [1,2], 2],
  1622. ["dz", "Dzongkha", [1], 3],
  1623. ["el", "Greek", [1,2], 2],
  1624. ["en", "English", [1,2], 2],
  1625. ["eo", "Esperanto",[1,2], 2],
  1626. ["es", "Spanish", [1,2], 2],
  1627. ["es_ar","Argentinean Spanish", [1,2], 2],
  1628. ["et", "Estonian", [1,2], 2],
  1629. ["eu", "Basque", [1,2], 2],
  1630. ["fa", "Persian", [1], 3],
  1631. ["fi", "Finnish", [1,2], 2],
  1632. ["fil", "Filipino", [1,2], 1],
  1633. ["fo", "Faroese", [1,2], 2],
  1634. ["fr", "French", [1,2], 9],
  1635. ["fur", "Friulian", [1,2], 2],
  1636. ["fy", "Frisian", [1,2], 2],
  1637. ["ga", "Irish", [1,2,3,7,11],10],
  1638. ["gd", "Scottish Gaelic",[1,2,3,20],11],
  1639. ["gl", "Galician", [1,2], 2],
  1640. ["gu", "Gujarati", [1,2], 2],
  1641. ["gun", "Gun", [1,2], 1],
  1642. ["ha", "Hausa", [1,2], 2],
  1643. ["he", "Hebrew", [1,2], 2],
  1644. ["hi", "Hindi", [1,2], 2],
  1645. ["hr", "Croatian", [1,2,5],4],
  1646. ["hu", "Hungarian",[1,2], 2],
  1647. ["hy", "Armenian", [1,2], 2],
  1648. ["ia", "Interlingua",[1,2],2],
  1649. ["id", "Indonesian",[1], 3],
  1650. ["is", "Icelandic",[1,2], 12],
  1651. ["it", "Italian", [1,2], 2],
  1652. ["ja", "Japanese", [1], 3],
  1653. ["jbo", "Lojban", [1], 3],
  1654. ["jv", "Javanese", [0,1], 13],
  1655. ["ka", "Georgian", [1], 3],
  1656. ["kk", "Kazakh", [1], 3],
  1657. ["km", "Khmer", [1], 3],
  1658. ["kn", "Kannada", [1,2], 2],
  1659. ["ko", "Korean", [1], 3],
  1660. ["ku", "Kurdish", [1,2], 2],
  1661. ["kw", "Cornish", [1,2,3,4],14],
  1662. ["ky", "Kyrgyz", [1], 3],
  1663. ["lb", "Letzeburgesch",[1,2],2],
  1664. ["ln", "Lingala", [1,2], 1],
  1665. ["lo", "Lao", [1], 3],
  1666. ["lt", "Lithuanian",[1,2,10],15],
  1667. ["lv", "Latvian", [1,2,0],16],
  1668. ["mai", "Maithili", [1,2], 2],
  1669. ["mfe", "Mauritian Creole",[1,2],1],
  1670. ["mg", "Malagasy", [1,2], 1],
  1671. ["mi", "Maori", [1,2], 1],
  1672. ["mk", "Macedonian",[1,2],17],
  1673. ["ml", "Malayalam",[1,2], 2],
  1674. ["mn", "Mongolian",[1,2], 2],
  1675. ["mnk", "Mandinka", [0,1,2],18],
  1676. ["mr", "Marathi", [1,2], 2],
  1677. ["ms", "Malay", [1], 3],
  1678. ["mt", "Maltese", [1,2,11,20],19],
  1679. ["nah", "Nahuatl", [1,2], 2],
  1680. ["nap", "Neapolitan",[1,2], 2],
  1681. ["nb", "Norwegian Bokmal",[1,2],2],
  1682. ["ne", "Nepali", [1,2], 2],
  1683. ["nl", "Dutch", [1,2], 2],
  1684. ["nn", "Norwegian Nynorsk",[1,2],2],
  1685. ["no", "Norwegian",[1,2], 2],
  1686. ["nso", "Northern Sotho",[1,2],2],
  1687. ["oc", "Occitan", [1,2], 1],
  1688. ["or", "Oriya", [2,1], 2],
  1689. ["pa", "Punjabi", [1,2], 2],
  1690. ["pap", "Papiamento",[1,2], 2],
  1691. ["pl", "Polish", [1,2,5],7],
  1692. ["pms", "Piemontese",[1,2], 2],
  1693. ["ps", "Pashto", [1,2], 2],
  1694. ["pt", "Portuguese",[1,2], 2],
  1695. ["pt_br","Brazilian Portuguese",[1,2], 2],
  1696. ["rm", "Romansh", [1,2], 2],
  1697. ["ro", "Romanian", [1,2,20],20],
  1698. ["ru", "Russian", [1,2,5],4],
  1699. ["sah", "Yakut", [1], 3],
  1700. ["sco", "Scots", [1,2], 2],
  1701. ["se", "Northern Sami",[1,2], 2],
  1702. ["si", "Sinhala", [1,2], 2],
  1703. ["sk", "Slovak", [1,2,5],6],
  1704. ["sl", "Slovenian",[5,1,2,3],21],
  1705. ["so", "Somali", [1,2], 2],
  1706. ["son", "Songhay", [1,2], 2],
  1707. ["sq", "Albanian", [1,2], 2],
  1708. ["sr", "Serbian", [1,2,5],4],
  1709. ["su", "Sundanese",[1], 3],
  1710. ["sv", "Swedish", [1,2], 2],
  1711. ["sw", "Swahili", [1,2], 2],
  1712. ["ta", "Tamil", [1,2], 2],
  1713. ["te", "Telugu", [1,2], 2],
  1714. ["tg", "Tajik", [1,2], 1],
  1715. ["th", "Thai", [1], 3],
  1716. ["ti", "Tigrinya", [1,2], 1],
  1717. ["tk", "Turkmen", [1,2], 2],
  1718. ["tr", "Turkish", [1,2], 1],
  1719. ["tt", "Tatar", [1], 3],
  1720. ["ug", "Uyghur", [1], 3],
  1721. ["uk", "Ukrainian",[1,2,5],4],
  1722. ["ur", "Urdu", [1,2], 2],
  1723. ["uz", "Uzbek", [1,2], 1],
  1724. ["vi", "Vietnamese",[1], 3],
  1725. ["wa", "Walloon", [1,2], 1],
  1726. ["wo", "Wolof", [1], 3],
  1727. ["yo", "Yoruba", [1,2], 2],
  1728. ["zh", "Chinese", [1], 3]
  1729. ];
  1730. var _rulesPluralsTypes = {
  1731. 1: function(n) {return Number(n > 1);},
  1732. 2: function(n) {return Number(n != 1);},
  1733. 3: function(n) {return 0;},
  1734. 4: 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);},
  1735. 5: 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);},
  1736. 6: function(n) {return Number((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);},
  1737. 7: function(n) {return Number(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);},
  1738. 8: function(n) {return Number((n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3);},
  1739. 9: function(n) {return Number(n >= 2);},
  1740. 10: function(n) {return Number(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4) ;},
  1741. 11: function(n) {return Number((n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3);},
  1742. 12: function(n) {return Number(n%10!=1 || n%100==11);},
  1743. 13: function(n) {return Number(n !== 0);},
  1744. 14: function(n) {return Number((n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : 3);},
  1745. 15: function(n) {return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);},
  1746. 16: function(n) {return Number(n%10==1 && n%100!=11 ? 0 : n !== 0 ? 1 : 2);},
  1747. 17: function(n) {return Number(n==1 || n%10==1 ? 0 : 1);},
  1748. 18: function(n) {return Number(0 ? 0 : n==1 ? 1 : 2);},
  1749. 19: function(n) {return Number(n==1 ? 0 : n===0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3);},
  1750. 20: function(n) {return Number(n==1 ? 0 : (n===0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2);},
  1751. 21: function(n) {return Number(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0); }
  1752. };
  1753. var pluralExtensions = {
  1754. rules: (function () {
  1755. var l, rules = {};
  1756. for (l=_rules.length; l-- ;) {
  1757. rules[_rules[l][0]] = {
  1758. name: _rules[l][1],
  1759. numbers: _rules[l][2],
  1760. plurals: _rulesPluralsTypes[_rules[l][3]]
  1761. }
  1762. }
  1763. return rules;
  1764. }()),
  1765. // you can add your own pluralExtensions
  1766. addRule: function(lng, obj) {
  1767. pluralExtensions.rules[lng] = obj;
  1768. },
  1769. setCurrentLng: function(lng) {
  1770. if (!pluralExtensions.currentRule || pluralExtensions.currentRule.lng !== lng) {
  1771. var parts = lng.split('-');
  1772. pluralExtensions.currentRule = {
  1773. lng: lng,
  1774. rule: pluralExtensions.rules[parts[0]]
  1775. };
  1776. }
  1777. },
  1778. needsPlural: function(lng, count) {
  1779. var parts = lng.split('-');
  1780. var ext;
  1781. if (pluralExtensions.currentRule && pluralExtensions.currentRule.lng === lng) {
  1782. ext = pluralExtensions.currentRule.rule;
  1783. } else {
  1784. ext = pluralExtensions.rules[parts[f.getCountyIndexOfLng(lng)]];
  1785. }
  1786. if (ext && ext.numbers.length <= 1) {
  1787. return false;
  1788. } else {
  1789. return this.get(lng, count) !== 1;
  1790. }
  1791. },
  1792. get: function(lng, count) {
  1793. var parts = lng.split('-');
  1794. function getResult(l, c) {
  1795. var ext;
  1796. if (pluralExtensions.currentRule && pluralExtensions.currentRule.lng === lng) {
  1797. ext = pluralExtensions.currentRule.rule;
  1798. } else {
  1799. ext = pluralExtensions.rules[l];
  1800. }
  1801. if (ext) {
  1802. var i;
  1803. if (ext.noAbs) {
  1804. i = ext.plurals(c);
  1805. } else {
  1806. i = ext.plurals(Math.abs(c));
  1807. }
  1808. var number = ext.numbers[i];
  1809. if (ext.numbers.length === 2 && ext.numbers[0] === 1) {
  1810. if (number === 2) {
  1811. number = -1; // regular plural
  1812. } else if (number === 1) {
  1813. number = 1; // singular
  1814. }
  1815. }//console.log(count + '-' + number);
  1816. return number;
  1817. } else {
  1818. return c === 1 ? '1' : '-1';
  1819. }
  1820. }
  1821. return getResult(parts[f.getCountyIndexOfLng(lng)], count);
  1822. }
  1823. };
  1824. var postProcessors = {};
  1825. var addPostProcessor = function(name, fc) {
  1826. postProcessors[name] = fc;
  1827. };
  1828. // sprintf support
  1829. var sprintf = (function() {
  1830. function get_type(variable) {
  1831. return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
  1832. }
  1833. function str_repeat(input, multiplier) {
  1834. for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
  1835. return output.join('');
  1836. }
  1837. var str_format = function() {
  1838. if (!str_format.cache.hasOwnProperty(arguments[0])) {
  1839. str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
  1840. }
  1841. return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
  1842. };
  1843. str_format.format = function(parse_tree, argv) {
  1844. var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
  1845. for (i = 0; i < tree_length; i++) {
  1846. node_type = get_type(parse_tree[i]);
  1847. if (node_type === 'string') {
  1848. output.push(parse_tree[i]);
  1849. }
  1850. else if (node_type === 'array') {
  1851. match = parse_tree[i]; // convenience purposes only
  1852. if (match[2]) { // keyword argument
  1853. arg = argv[cursor];
  1854. for (k = 0; k < match[2].length; k++) {
  1855. if (!arg.hasOwnProperty(match[2][k])) {
  1856. throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
  1857. }
  1858. arg = arg[match[2][k]];
  1859. }
  1860. }
  1861. else if (match[1]) { // positional argument (explicit)
  1862. arg = argv[match[1]];
  1863. }
  1864. else { // positional argument (implicit)
  1865. arg = argv[cursor++];
  1866. }
  1867. if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
  1868. throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
  1869. }
  1870. switch (match[8]) {
  1871. case 'b': arg = arg.toString(2); break;
  1872. case 'c': arg = String.fromCharCode(arg); break;
  1873. case 'd': arg = parseInt(arg, 10); break;
  1874. case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
  1875. case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
  1876. case 'o': arg = arg.toString(8); break;
  1877. case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
  1878. case 'u': arg = Math.abs(arg); break;
  1879. case 'x': arg = arg.toString(16); break;
  1880. case 'X': arg = arg.toString(16).toUpperCase(); break;
  1881. }
  1882. arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
  1883. pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
  1884. pad_length = match[6] - String(arg).length;
  1885. pad = match[6] ? str_repeat(pad_character, pad_length) : '';
  1886. output.push(match[5] ? arg + pad : pad + arg);
  1887. }
  1888. }
  1889. return output.join('');
  1890. };
  1891. str_format.cache = {};
  1892. str_format.parse = function(fmt) {
  1893. var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
  1894. while (_fmt) {
  1895. if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
  1896. parse_tree.push(match[0]);
  1897. }
  1898. else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
  1899. parse_tree.push('%');
  1900. }
  1901. else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
  1902. if (match[2]) {
  1903. arg_names |= 1;
  1904. var field_list = [], replacement_field = match[2], field_match = [];
  1905. if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
  1906. field_list.push(field_match[1]);
  1907. while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
  1908. if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
  1909. field_list.push(field_match[1]);
  1910. }
  1911. else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
  1912. field_list.push(field_match[1]);
  1913. }
  1914. else {
  1915. throw('[sprintf] huh?');
  1916. }
  1917. }
  1918. }
  1919. else {
  1920. throw('[sprintf] huh?');
  1921. }
  1922. match[2] = field_list;
  1923. }
  1924. else {
  1925. arg_names |= 2;
  1926. }
  1927. if (arg_names === 3) {
  1928. throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
  1929. }
  1930. parse_tree.push(match);
  1931. }
  1932. else {
  1933. throw('[sprintf] huh?');
  1934. }
  1935. _fmt = _fmt.substring(match[0].length);
  1936. }
  1937. return parse_tree;
  1938. };
  1939. return str_format;
  1940. })();
  1941. var vsprintf = function(fmt, argv) {
  1942. argv.unshift(fmt);
  1943. return sprintf.apply(null, argv);
  1944. };
  1945. addPostProcessor("sprintf", function(val, key, opts) {
  1946. if (!opts.sprintf) return val;
  1947. if (Object.prototype.toString.apply(opts.sprintf) === '[object Array]') {
  1948. return vsprintf(val, opts.sprintf);
  1949. } else if (typeof opts.sprintf === 'object') {
  1950. return sprintf(val, opts.sprintf);
  1951. }
  1952. return val;
  1953. });
  1954. // public api interface
  1955. i18n.init = init;
  1956. i18n.isInitialized = isInitialized;
  1957. i18n.setLng = setLng;
  1958. i18n.preload = preload;
  1959. i18n.addResourceBundle = addResourceBundle;
  1960. i18n.hasResourceBundle = hasResourceBundle;
  1961. i18n.getResourceBundle = getResourceBundle;
  1962. i18n.addResource = addResource;
  1963. i18n.addResources = addResources;
  1964. i18n.removeResourceBundle = removeResourceBundle;
  1965. i18n.loadNamespace = loadNamespace;
  1966. i18n.loadNamespaces = loadNamespaces;
  1967. i18n.setDefaultNamespace = setDefaultNamespace;
  1968. i18n.t = translate;
  1969. i18n.translate = translate;
  1970. i18n.exists = exists;
  1971. i18n.detectLanguage = f.detectLanguage;
  1972. i18n.pluralExtensions = pluralExtensions;
  1973. i18n.sync = sync;
  1974. i18n.functions = f;
  1975. i18n.lng = lng;
  1976. i18n.addPostProcessor = addPostProcessor;
  1977. i18n.applyReplacement = f.applyReplacement;
  1978. i18n.options = o;
  1979. i18n.noConflict = noConflict;
  1980. })(typeof exports === 'undefined' ? window : exports);