// i18next, v1.10.1
// Copyright (c)2015 Jan Mühlemann (jamuhl).
// Distributed under MIT license
// http://i18next.com
(function (root) {
  // 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;
    };
  }

  // Add string trim for IE8.
  if (typeof String.prototype.trim !== "function") {
    String.prototype.trim = function () {
      return this.replace(/^\s+|\s+$/g, "");
    };
  }

  var $ = root.jQuery || root.Zepto,
    i18n = {},
    resStore = {},
    currentLng,
    replacementCounter = 0,
    languages = [],
    initialized = false,
    sync = {},
    conflictReference = null;

  // 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;
    }

    if (root.i18n) {
      conflictReference = root.i18n;
    }
    root.i18n = i18n;
  }
  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(err, store);
            });
          } else {
            cb(err, store);
          }
        });
      } else {
        sync._fetch(lngs, options, function (err, store) {
          cb(err, 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 = f.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();
          f.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(err, 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,
            cache: options.cache,
            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,
            timeout: options.ajaxTimeout,
          });
        }
      }
    },

    _fetchOne: function (lng, ns, options, done) {
      var url = applyReplacement(options.resGetPath, { lng: lng, ns: ns });
      f.ajax({
        url: url,
        cache: options.cache,
        success: function (data, status, xhr) {
          f.log("loaded: " + url);
          done(null, data);
        },
        error: function (xhr, status, error) {
          if (
            (status && status == 200) ||
            (xhr && xhr.status && xhr.status == 200)
          ) {
            // file loaded but invalid json, stop waste time !
            f.error("There is a typo in: " + url);
          } else if (
            (status && status == 404) ||
            (xhr && xhr.status && xhr.status == 404)
          ) {
            f.log("Does not exist: " + url);
          } else {
            var theStatus = status
              ? status
              : xhr && xhr.status
              ? xhr.status
              : null;
            f.log(theStatus + " when loading " + url);
          }

          done(error, {});
        },
        dataType: "json",
        async: options.getAsync,
        timeout: options.ajaxTimeout,
      });
    },

    postMissing: function (lng, ns, key, defaultValue, lngs) {
      var payload = {};
      payload[key] = defaultValue;

      var urls = [];

      if (o.sendMissingTo === "fallback" && o.fallbackLng[0] !== false) {
        for (var i = 0; i < o.fallbackLng.length; i++) {
          urls.push({
            lng: o.fallbackLng[i],
            url: applyReplacement(o.resPostPath, {
              lng: o.fallbackLng[i],
              ns: ns,
            }),
          });
        }
      } else if (
        o.sendMissingTo === "current" ||
        (o.sendMissingTo === "fallback" && o.fallbackLng[0] === 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,
          timeout: o.ajaxTimeout,
        });
      }
    },

    reload: reload,
  };
  // defaults
  var o = {
    lng: undefined,
    load: "all",
    preload: [],
    lowerCaseLng: false,
    returnObjectTrees: false,
    fallbackLng: ["dev"],
    fallbackNS: [],
    detectLngQS: "setLng",
    detectLngFromLocalStorage: false,
    ns: {
      namespaces: ["translation"],
      defaultNs: "translation",
    },
    fallbackOnNull: true,
    fallbackOnEmpty: false,
    fallbackToDefaultNS: false,
    showKeyIfEmpty: 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: "__",
    defaultVariables: false,
    reusePrefix: "$t(",
    reuseSuffix: ")",
    pluralSuffix: "_plural",
    pluralNotFound: ["plural_not_found", Math.random()].join(""),
    contextNotFound: ["context_not_found", Math.random()].join(""),
    escapeInterpolation: false,
    indefiniteSuffix: "_indefinite",
    indefiniteNotFound: ["indefinite_not_found", Math.random()].join(""),

    setJqueryExt: true,
    defaultValueFromContent: true,
    useDataAttrOptions: false,
    cookieExpirationTime: undefined,
    useCookie: true,
    cookieName: "i18next",
    cookieDomain: undefined,

    objectTreeKeyHandler: undefined,
    postProcess: undefined,
    parseMissingKey: undefined,
    missingKeyHandler: sync.postMissing,
    ajaxTimeout: 0,

    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 _deepExtend(target, source) {
    for (var prop in source)
      if (prop in target) _deepExtend(target[prop], source[prop]);
      else target[prop] = source[prop];
    return target;
  }

  function _each(object, callback, args) {
    var name,
      i = 0,
      length = object.length,
      isObj =
        length === undefined ||
        Object.prototype.toString.apply(object) !== "[object Array]" ||
        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 () {
                try {
                  return JSON.parse(data);
                } catch (e) {
                  f.error("Can not parse JSON. URL: " + url);
                  return {};
                }
              },
            });
          }
        };

        // 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) {
      // file: protocol always gives status code 0, so check for data
      if (status === 200 || (status === 0 && data.text())) {
        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,
    deepExtend: _deepExtend,
    each: $ ? $.each : _each,
    ajax: $ ? $.ajax : typeof document !== "undefined" ? _ajax : function () {},
    cookie: typeof document !== "undefined" ? _cookie : cookie_noop,
    detectLanguage: detectLanguage,
    escape: _escape,
    log: function (str) {
      if (o.debug && typeof console !== "undefined") console.log(str);
    },
    error: function (str) {
      if (typeof console !== "undefined") console.error(str);
    },
    getCountyIndexOfLng: function (lng) {
      var lng_index = 0;
      if (
        lng === "nb-NO" ||
        lng === "nn-NO" ||
        lng === "nb-no" ||
        lng === "nn-no"
      )
        lng_index = 1;
      return lng_index;
    },
    toLanguages: function (lng, fallbackLng) {
      var log = this.log;

      fallbackLng = fallbackLng || o.fallbackLng;
      if (typeof fallbackLng === "string") fallbackLng = [fallbackLng];

      function applyCase(l) {
        var ret = l;

        if (typeof l === "string" && l.indexOf("-") > -1) {
          var parts = l.split("-");

          ret = o.lowerCaseLng
            ? parts[0].toLowerCase() + "-" + parts[1].toLowerCase()
            : parts[0].toLowerCase() + "-" + parts[1].toUpperCase();
        } else {
          ret = o.lowerCaseLng ? l.toLowerCase() : l;
        }

        return ret;
      }

      var languages = [];
      var whitelist = o.lngWhitelist || false;
      var addLanguage = function (language) {
        //reject langs not whitelisted
        if (!whitelist || whitelist.indexOf(language) > -1) {
          languages.push(language);
        } else {
          log("rejecting non-whitelisted language: " + language);
        }
      };
      if (typeof lng === "string" && lng.indexOf("-") > -1) {
        var parts = lng.split("-");

        if (o.load !== "unspecific") addLanguage(applyCase(lng));
        if (o.load !== "current")
          addLanguage(applyCase(parts[this.getCountyIndexOfLng(lng)]));
      } else {
        addLanguage(applyCase(lng));
      }

      for (var i = 0; i < fallbackLng.length; i++) {
        if (languages.indexOf(fallbackLng[i]) === -1 && fallbackLng[i])
          languages.push(applyCase(fallbackLng[i]));
      }
      return languages;
    },
    regexEscape: function (str) {
      return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
    },
    regexReplacementEscape: function (strOrFn) {
      if (typeof strOrFn === "string") {
        return strOrFn.replace(/\$/g, "$$$$");
      } else {
        return strOrFn;
      }
    },
    localStorage: {
      setItem: function (key, value) {
        if (window.localStorage) {
          try {
            window.localStorage.setItem(key, value);
          } catch (e) {
            f.log('failed to set value for key "' + key + '" to localStorage.');
          }
        }
      },
      getItem: function (key, value) {
        if (window.localStorage) {
          try {
            return window.localStorage.getItem(key, value);
          } catch (e) {
            f.log(
              'failed to get value for key "' + key + '" from localStorage.',
            );
            return undefined;
          }
        }
      },
    },
  };
  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 */

    // override functions: .log(), .detectLanguage(), etc
    if (o.functions) {
      delete o.functions;
      f.extend(f, options.functions);
    }

    // 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];
    }

    // fallback languages
    if (typeof o.fallbackLng == "string" || typeof o.fallbackLng == "boolean") {
      o.fallbackLng = [o.fallbackLng];
    }

    // escape prefix/suffix
    o.interpolationPrefixEscaped = f.regexEscape(o.interpolationPrefix);
    o.interpolationSuffixEscaped = f.regexEscape(o.interpolationSuffix);

    if (!o.lng) o.lng = f.detectLanguage();

    languages = f.toLanguages(o.lng);
    currentLng = languages[0];
    f.log("currentLng set to: " + currentLng);

    if (o.useCookie && f.cookie.read(o.cookieName) !== currentLng) {
      //cookie is unset or invalid
      f.cookie.create(
        o.cookieName,
        currentLng,
        o.cookieExpirationTime,
        o.cookieDomain,
      );
    }
    if (
      o.detectLngFromLocalStorage &&
      typeof document !== "undefined" &&
      window.localStorage
    ) {
      f.localStorage.setItem("i18next_lng", 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 && addJqueryFunct();
    } else {
      addJqueryLikeFunctionality && addJqueryLikeFunctionality();
    }

    // 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(err, lngTranslate);
      if (deferred)
        (!err ? deferred.resolve : deferred.reject)(err || lngTranslate);
    });

    if (deferred) return deferred.promise();
  }

  function isInitialized() {
    return initialized;
  }
  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, deep) {
    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] || {};

    if (deep) {
      f.deepExtend(resStore[lng][ns], resources);
    } else {
      f.extend(resStore[lng][ns], resources);
    }
    if (o.useLocalStorage) {
      sync._storeLocal(resStore);
    }
  }

  function hasResourceBundle(lng, ns) {
    if (typeof ns !== "string") {
      ns = o.ns.defaultNs;
    }

    resStore[lng] = resStore[lng] || {};
    var res = resStore[lng][ns] || {};

    var hasValues = false;
    for (var prop in res) {
      if (res.hasOwnProperty(prop)) {
        hasValues = true;
      }
    }

    return hasValues;
  }

  function getResourceBundle(lng, ns) {
    if (typeof ns !== "string") {
      ns = o.ns.defaultNs;
    }

    resStore[lng] = resStore[lng] || {};
    return f.extend({}, resStore[lng][ns]);
  }

  function removeResourceBundle(lng, ns) {
    if (typeof ns !== "string") {
      ns = o.ns.defaultNs;
    }

    resStore[lng] = resStore[lng] || {};
    resStore[lng][ns] = {};
    if (o.useLocalStorage) {
      sync._storeLocal(resStore);
    }
  }

  function addResource(lng, ns, key, value) {
    if (typeof ns !== "string") {
      resource = 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] || {};

    var keys = key.split(o.keyseparator);
    var x = 0;
    var node = resStore[lng][ns];
    var origRef = node;

    while (keys[x]) {
      if (x == keys.length - 1) node[keys[x]] = value;
      else {
        if (node[keys[x]] == null) node[keys[x]] = {};

        node = node[keys[x]];
      }
      x++;
    }
    if (o.useLocalStorage) {
      sync._storeLocal(resStore);
    }
  }

  function addResources(lng, ns, resources) {
    if (typeof ns !== "string") {
      resource = ns;
      ns = o.ns.defaultNs;
    } else if (o.ns.namespaces.indexOf(ns) < 0) {
      o.ns.namespaces.push(ns);
    }

    for (var m in resources) {
      if (typeof resources[m] === "string")
        addResource(lng, ns, m, resources[m]);
    }
  }

  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 = {};
    } else if (!options) {
      options = {};
    }

    options.lng = lng;
    return init(options, cb);
  }

  function lng() {
    return currentLng;
  }

  function reload(cb) {
    resStore = {};
    setLng(currentLng, cb);
  }

  function noConflict() {
    window.i18next = window.i18n;

    if (conflictReference) {
      window.i18n = conflictReference;
    } else {
      delete window.i18n;
    }
  }
  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 if (attr === "prepend") {
        optionsToUse = o.defaultValueFromContent
          ? $.extend({ defaultValue: ele.html() }, options)
          : options;
        ele.prepend($.t(key, optionsToUse));
      } else if (attr === "append") {
        optionsToUse = o.defaultValueFromContent
          ? $.extend({ defaultValue: ele.html() }, options)
          : options;
        ele.append($.t(key, optionsToUse));
      } else if (attr.indexOf("data-") === 0) {
        var dataAttr = attr.substr("data-".length);
        optionsToUse = o.defaultValueFromContent
          ? $.extend({ defaultValue: ele.data(dataAttr) }, options)
          : options;
        var translated = $.t(key, optionsToUse);
        //we change into the data cache
        ele.data(dataAttr, translated);
        //we change into the dom
        ele.attr(attr, translated);
      } 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 addJqueryLikeFunctionality() {
    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);
      }

      if (attr === "html") {
        ele.innerHTML = translate(key, options);
      } else if (attr === "text") {
        ele.textContent = translate(key, options);
      } else if (attr === "prepend") {
        ele.insertAdjacentHTML(translate(key, options), "afterbegin");
      } else if (attr === "append") {
        ele.insertAdjacentHTML(translate(key, options), "beforeend");
      } else {
        ele.setAttribute(attr, translate(key, options));
      }
    }

    function localize(ele, options) {
      var key = ele.getAttribute(o.selectorAttr);
      if (!key && typeof key !== "undefined" && key !== false)
        key = ele.textContent || ele.value;
      if (!key) return;

      var target = ele,
        targetSelector = ele.getAttribute("i18n-target");
      if (targetSelector) {
        target = ele.querySelector(targetSelector) || ele;
      }

      if (key.indexOf(";") >= 0) {
        var keys = key.split(";"),
          index = 0,
          length = keys.length;

        for (; index < length; index++) {
          if (keys[index] !== "") parse(target, keys[index], options);
        }
      } else {
        parse(target, key, options);
      }
    }

    // fn
    i18n.translateObject = function (object, options) {
      // localize childs
      var elements = object.querySelectorAll("[" + o.selectorAttr + "]");
      var index = 0,
        length = elements.length;
      for (; index < length; index++) {
        localize(elements[index], 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;

    var hash =
      replacementHash.replace && typeof replacementHash.replace === "object"
        ? replacementHash.replace
        : replacementHash;
    f.each(hash, 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"),
            f.regexReplacementEscape(value),
          );
          str = str.replace(
            new RegExp([prefix, nextKey, suffix].join(""), "g"),
            f.regexReplacementEscape(f.escape(value)),
          );
        } else {
          str = str.replace(
            new RegExp([prefix, nextKey, suffix].join(""), "g"),
            f.regexReplacementEscape(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 (index_of_end_of_closing <= index_of_opening) {
        f.error(
          "there is an missing closing in following translation value",
          translated,
        );
        return "";
      }

      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,
        f.regexReplacementEscape(translated_token),
      );
    }
    return translated;
  }

  function hasContext(options) {
    return (
      options.context &&
      (typeof options.context == "string" || typeof options.context == "number")
    );
  }

  function needsPlural(options, lng) {
    return (
      options.count !== undefined &&
      typeof options.count !=
        "string" /* && pluralExtensions.needsPlural(lng, options.count)*/
    );
  }

  function needsIndefiniteArticle(options) {
    return (
      options.indefinite_article !== undefined &&
      typeof options.indefinite_article != "string" &&
      options.indefinite_article
    );
  }

  function exists(key, options) {
    options = options || {};

    var notFound = _getDefaultValue(key, options),
      found = _find(key, options);

    return found !== undefined || found === notFound;
  }

  function translate(key, options) {
    options = 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 (options && typeof options !== "object") {
      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 o.defaultVariables === "object") {
      options = f.extend({}, o.defaultVariables, options);
    }

    if (
      potentialKeys === undefined ||
      potentialKeys === null ||
      potentialKeys === ""
    )
      return "";

    if (typeof potentialKeys === "number") {
      potentialKeys = String(potentialKeys);
    }

    if (typeof potentialKeys === "string") {
      potentialKeys = [potentialKeys];
    }

    var key = potentialKeys[0];

    if (potentialKeys.length > 1) {
      for (var i = 0; i < potentialKeys.length; i++) {
        key = potentialKeys[i];
        if (exists(key, options)) {
          break;
        }
      }
    }

    var notFound = _getDefaultValue(key, options),
      found = _find(key, options),
      lngs = options.lng
        ? f.toLanguages(options.lng, options.fallbackLng)
        : 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 &&
      typeof o.missingKeyHandler === "function"
    ) {
      if (options.lng) {
        o.missingKeyHandler(lngs[0], ns, key, notFound, lngs);
      } else {
        o.missingKeyHandler(o.lng, ns, key, notFound, lngs);
      }
    }

    var postProcessorsToApply;
    if (typeof o.postProcess === "string" && o.postProcess !== "") {
      postProcessorsToApply = [o.postProcess];
    } else if (
      typeof o.postProcess === "array" ||
      typeof o.postProcess === "object"
    ) {
      postProcessorsToApply = o.postProcess;
    } else {
      postProcessorsToApply = [];
    }

    if (typeof options.postProcess === "string" && options.postProcess !== "") {
      postProcessorsToApply = postProcessorsToApply.concat([
        options.postProcess,
      ]);
    } else if (
      typeof options.postProcess === "array" ||
      typeof options.postProcess === "object"
    ) {
      postProcessorsToApply = postProcessorsToApply.concat(options.postProcess);
    }

    if (found !== undefined && postProcessorsToApply.length) {
      postProcessorsToApply.forEach(function (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 (postProcessorsToApply.length) {
        var val = _getDefaultValue(key, options);
        postProcessorsToApply.forEach(function (postProcessor) {
          if (postProcessors[postProcessor]) {
            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

    // CI mode
    if (lngs[0].toLowerCase() === "cimode") return notFound;

    // passed in lng
    if (options.lngs) lngs = options.lngs;
    if (options.lng) {
      lngs = f.toLanguages(options.lng, options.fallbackLng);

      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, lngs[0])) {
      optionWithoutCount = f.extend({ lngs: [lngs[0]] }, options);
      delete optionWithoutCount.count;
      optionWithoutCount._origLng =
        optionWithoutCount._origLng || optionWithoutCount.lng || lngs[0];
      delete optionWithoutCount.lng;
      optionWithoutCount.defaultValue = o.pluralNotFound;

      var pluralKey;
      if (!pluralExtensions.needsPlural(lngs[0], options.count)) {
        pluralKey = ns + o.nsseparator + key;
      } else {
        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 if (lngs.length > 1) {
        // remove failed lng
        var clone = lngs.slice();
        clone.shift();
        options = f.extend(options, { lngs: clone });
        options._origLng = optionWithoutCount._origLng;
        delete options.lng;
        // retry with fallbacks
        translated = translate(ns + o.nsseparator + key, options);
        if (translated != o.pluralNotFound) return translated;
      } else {
        optionWithoutCount.lng = optionWithoutCount._origLng;
        delete optionWithoutCount._origLng;
        translated = translate(ns + o.nsseparator + key, optionWithoutCount);

        return applyReplacement(translated, {
          count: options.count,
          interpolationPrefix: options.interpolationPrefix,
          interpolationSuffix: options.interpolationSuffix,
        });
      }
    }

    if (needsIndefiniteArticle(options)) {
      var optionsWithoutIndef = f.extend({}, options);
      delete optionsWithoutIndef.indefinite_article;
      optionsWithoutIndef.defaultValue = o.indefiniteNotFound;
      // If we don't have a count, we want the indefinite, if we do have a count, and needsPlural is false
      var indefiniteKey =
        ns +
        o.nsseparator +
        key +
        ((options.count && !needsPlural(options, lngs[0])) || !options.count
          ? o.indefiniteSuffix
          : "");
      translated = translate(indefiniteKey, optionsWithoutIndef);
      if (translated != o.indefiniteNotFound) {
        return translated;
      }
    }

    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 && (!o.showKeyIfEmpty || value !== "")) {
        var valueType = Object.prototype.toString.apply(value);
        if (typeof value === "string") {
          value = applyReplacement(value, options);
          value = applyReuse(value, options);
        } else if (
          valueType === "[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) {
            if (
              o.objectTreeKeyHandler &&
              typeof o.objectTreeKeyHandler == "function"
            ) {
              value = o.objectTreeKeyHandler(key, value, l, ns, options);
            } else {
              value =
                "key '" +
                ns +
                ":" +
                key +
                " (" +
                l +
                ")' " +
                "returned an object instead of string.";
              f.log(value);
            }
          } else if (
            valueType !== "[object Number]" &&
            valueType !== "[object Function]" &&
            valueType !== "[object RegExp]"
          ) {
            var copy = valueType === "[object Array]" ? [] : {}; // apply child translation on a copy
            f.each(value, function (m) {
              copy[m] = _translate(
                ns + o.nsseparator + key + o.keyseparator + m,
                options,
              );
            });
            value = copy;
          }
        }

        if (
          typeof value === "string" &&
          value.trim() === "" &&
          o.fallbackOnEmpty === true
        )
          value = undefined;

        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 || (found === "" && o.fallbackOnEmpty === false)) {
            /* 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 {
        options.ns = o.ns.defaultNs;
        found = _find(key, options); // fallback to default NS
      }
      options.isFallbackLookup = false;
    }

    return found;
  }
  function detectLanguage() {
    var detectedLng;
    var whitelist = o.lngWhitelist || [];
    var userLngChoices = [];

    // get from qs
    var qsParm = [];
    if (typeof window !== "undefined") {
      (function () {
        var query = window.location.search.substring(1);
        var params = query.split("&");
        for (var i = 0; i < params.length; i++) {
          var pos = params[i].indexOf("=");
          if (pos > 0) {
            var key = params[i].substring(0, pos);
            if (key == o.detectLngQS) {
              userLngChoices.push(params[i].substring(pos + 1));
            }
          }
        }
      })();
    }

    // get from cookie
    if (o.useCookie && typeof document !== "undefined") {
      var c = f.cookie.read(o.cookieName);
      if (c) userLngChoices.push(c);
    }

    // get from localStorage
    if (
      o.detectLngFromLocalStorage &&
      typeof window !== "undefined" &&
      window.localStorage
    ) {
      var lang = f.localStorage.getItem("i18next_lng");
      if (lang) {
        userLngChoices.push(lang);
      }
    }

    // get from navigator
    if (typeof navigator !== "undefined") {
      if (navigator.languages) {
        // chrome only; not an array, so can't use .push.apply instead of iterating
        for (var i = 0; i < navigator.languages.length; i++) {
          userLngChoices.push(navigator.languages[i]);
        }
      }
      if (navigator.userLanguage) {
        userLngChoices.push(navigator.userLanguage);
      }
      if (navigator.language) {
        userLngChoices.push(navigator.language);
      }
    }

    (function () {
      for (var i = 0; i < userLngChoices.length; i++) {
        var lng = userLngChoices[i];

        if (lng.indexOf("-") > -1) {
          var parts = lng.split("-");
          lng = o.lowerCaseLng
            ? parts[0].toLowerCase() + "-" + parts[1].toLowerCase()
            : parts[0].toLowerCase() + "-" + parts[1].toUpperCase();
        }

        if (whitelist.length === 0 || whitelist.indexOf(lng) > -1) {
          detectedLng = lng;
          break;
        }
      }
    })();

    //fallback
    if (!detectedLng) {
      detectedLng = o.fallbackLng[0];
    }

    return detectedLng;
  }
  // definition http://translate.sourceforge.net/wiki/l10n/pluralforms

  /* [code, name, numbers, pluralsType] */
  var _rules = [
    ["ach", "Acholi", [1, 2], 1],
    ["af", "Afrikaans", [1, 2], 2],
    ["ak", "Akan", [1, 2], 1],
    ["am", "Amharic", [1, 2], 1],
    ["an", "Aragonese", [1, 2], 2],
    ["ar", "Arabic", [0, 1, 2, 3, 11, 100], 5],
    ["arn", "Mapudungun", [1, 2], 1],
    ["ast", "Asturian", [1, 2], 2],
    ["ay", "Aymará", [1], 3],
    ["az", "Azerbaijani", [1, 2], 2],
    ["be", "Belarusian", [1, 2, 5], 4],
    ["bg", "Bulgarian", [1, 2], 2],
    ["bn", "Bengali", [1, 2], 2],
    ["bo", "Tibetan", [1], 3],
    ["br", "Breton", [1, 2], 1],
    ["bs", "Bosnian", [1, 2, 5], 4],
    ["ca", "Catalan", [1, 2], 2],
    ["cgg", "Chiga", [1], 3],
    ["cs", "Czech", [1, 2, 5], 6],
    ["csb", "Kashubian", [1, 2, 5], 7],
    ["cy", "Welsh", [1, 2, 3, 8], 8],
    ["da", "Danish", [1, 2], 2],
    ["de", "German", [1, 2], 2],
    ["dev", "Development Fallback", [1, 2], 2],
    ["dz", "Dzongkha", [1], 3],
    ["el", "Greek", [1, 2], 2],
    ["en", "English", [1, 2], 2],
    ["eo", "Esperanto", [1, 2], 2],
    ["es", "Spanish", [1, 2], 2],
    ["es_ar", "Argentinean Spanish", [1, 2], 2],
    ["et", "Estonian", [1, 2], 2],
    ["eu", "Basque", [1, 2], 2],
    ["fa", "Persian", [1], 3],
    ["fi", "Finnish", [1, 2], 2],
    ["fil", "Filipino", [1, 2], 1],
    ["fo", "Faroese", [1, 2], 2],
    ["fr", "French", [1, 2], 9],
    ["fur", "Friulian", [1, 2], 2],
    ["fy", "Frisian", [1, 2], 2],
    ["ga", "Irish", [1, 2, 3, 7, 11], 10],
    ["gd", "Scottish Gaelic", [1, 2, 3, 20], 11],
    ["gl", "Galician", [1, 2], 2],
    ["gu", "Gujarati", [1, 2], 2],
    ["gun", "Gun", [1, 2], 1],
    ["ha", "Hausa", [1, 2], 2],
    ["he", "Hebrew", [1, 2], 2],
    ["hi", "Hindi", [1, 2], 2],
    ["hr", "Croatian", [1, 2, 5], 4],
    ["hu", "Hungarian", [1, 2], 2],
    ["hy", "Armenian", [1, 2], 2],
    ["ia", "Interlingua", [1, 2], 2],
    ["id", "Indonesian", [1], 3],
    ["is", "Icelandic", [1, 2], 12],
    ["it", "Italian", [1, 2], 2],
    ["ja", "Japanese", [1], 3],
    ["jbo", "Lojban", [1], 3],
    ["jv", "Javanese", [0, 1], 13],
    ["ka", "Georgian", [1], 3],
    ["kk", "Kazakh", [1], 3],
    ["km", "Khmer", [1], 3],
    ["kn", "Kannada", [1, 2], 2],
    ["ko", "Korean", [1], 3],
    ["ku", "Kurdish", [1, 2], 2],
    ["kw", "Cornish", [1, 2, 3, 4], 14],
    ["ky", "Kyrgyz", [1], 3],
    ["lb", "Letzeburgesch", [1, 2], 2],
    ["ln", "Lingala", [1, 2], 1],
    ["lo", "Lao", [1], 3],
    ["lt", "Lithuanian", [1, 2, 10], 15],
    ["lv", "Latvian", [1, 2, 0], 16],
    ["mai", "Maithili", [1, 2], 2],
    ["mfe", "Mauritian Creole", [1, 2], 1],
    ["mg", "Malagasy", [1, 2], 1],
    ["mi", "Maori", [1, 2], 1],
    ["mk", "Macedonian", [1, 2], 17],
    ["ml", "Malayalam", [1, 2], 2],
    ["mn", "Mongolian", [1, 2], 2],
    ["mnk", "Mandinka", [0, 1, 2], 18],
    ["mr", "Marathi", [1, 2], 2],
    ["ms", "Malay", [1], 3],
    ["mt", "Maltese", [1, 2, 11, 20], 19],
    ["nah", "Nahuatl", [1, 2], 2],
    ["nap", "Neapolitan", [1, 2], 2],
    ["nb", "Norwegian Bokmal", [1, 2], 2],
    ["ne", "Nepali", [1, 2], 2],
    ["nl", "Dutch", [1, 2], 2],
    ["nn", "Norwegian Nynorsk", [1, 2], 2],
    ["no", "Norwegian", [1, 2], 2],
    ["nso", "Northern Sotho", [1, 2], 2],
    ["oc", "Occitan", [1, 2], 1],
    ["or", "Oriya", [2, 1], 2],
    ["pa", "Punjabi", [1, 2], 2],
    ["pap", "Papiamento", [1, 2], 2],
    ["pl", "Polish", [1, 2, 5], 7],
    ["pms", "Piemontese", [1, 2], 2],
    ["ps", "Pashto", [1, 2], 2],
    ["pt", "Portuguese", [1, 2], 2],
    ["pt_br", "Brazilian Portuguese", [1, 2], 2],
    ["rm", "Romansh", [1, 2], 2],
    ["ro", "Romanian", [1, 2, 20], 20],
    ["ru", "Russian", [1, 2, 5], 4],
    ["sah", "Yakut", [1], 3],
    ["sco", "Scots", [1, 2], 2],
    ["se", "Northern Sami", [1, 2], 2],
    ["si", "Sinhala", [1, 2], 2],
    ["sk", "Slovak", [1, 2, 5], 6],
    ["sl", "Slovenian", [5, 1, 2, 3], 21],
    ["so", "Somali", [1, 2], 2],
    ["son", "Songhay", [1, 2], 2],
    ["sq", "Albanian", [1, 2], 2],
    ["sr", "Serbian", [1, 2, 5], 4],
    ["su", "Sundanese", [1], 3],
    ["sv", "Swedish", [1, 2], 2],
    ["sw", "Swahili", [1, 2], 2],
    ["ta", "Tamil", [1, 2], 2],
    ["te", "Telugu", [1, 2], 2],
    ["tg", "Tajik", [1, 2], 1],
    ["th", "Thai", [1], 3],
    ["ti", "Tigrinya", [1, 2], 1],
    ["tk", "Turkmen", [1, 2], 2],
    ["tr", "Turkish", [1, 2], 1],
    ["tt", "Tatar", [1], 3],
    ["ug", "Uyghur", [1], 3],
    ["uk", "Ukrainian", [1, 2, 5], 4],
    ["ur", "Urdu", [1, 2], 2],
    ["uz", "Uzbek", [1, 2], 1],
    ["vi", "Vietnamese", [1], 3],
    ["wa", "Walloon", [1, 2], 1],
    ["wo", "Wolof", [1], 3],
    ["yo", "Yoruba", [1, 2], 2],
    ["zh", "Chinese", [1], 3],
  ];

  var _rulesPluralsTypes = {
    1: function (n) {
      return Number(n > 1);
    },
    2: function (n) {
      return Number(n != 1);
    },
    3: function (n) {
      return 0;
    },
    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,
      );
    },
    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,
      );
    },
    6: function (n) {
      return Number(n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2);
    },
    7: function (n) {
      return Number(
        n == 1
          ? 0
          : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)
          ? 1
          : 2,
      );
    },
    8: function (n) {
      return Number(n == 1 ? 0 : n == 2 ? 1 : n != 8 && n != 11 ? 2 : 3);
    },
    9: function (n) {
      return Number(n >= 2);
    },
    10: function (n) {
      return Number(n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4);
    },
    11: function (n) {
      return Number(
        n == 1 || n == 11 ? 0 : n == 2 || n == 12 ? 1 : n > 2 && n < 20 ? 2 : 3,
      );
    },
    12: function (n) {
      return Number(n % 10 != 1 || n % 100 == 11);
    },
    13: function (n) {
      return Number(n !== 0);
    },
    14: function (n) {
      return Number(n == 1 ? 0 : n == 2 ? 1 : n == 3 ? 2 : 3);
    },
    15: function (n) {
      return Number(
        n % 10 == 1 && n % 100 != 11
          ? 0
          : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20)
          ? 1
          : 2,
      );
    },
    16: function (n) {
      return Number(n % 10 == 1 && n % 100 != 11 ? 0 : n !== 0 ? 1 : 2);
    },
    17: function (n) {
      return Number(n == 1 || n % 10 == 1 ? 0 : 1);
    },
    18: function (n) {
      return Number(0 ? 0 : n == 1 ? 1 : 2);
    },
    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,
      );
    },
    20: function (n) {
      return Number(
        n == 1 ? 0 : n === 0 || (n % 100 > 0 && n % 100 < 20) ? 1 : 2,
      );
    },
    21: function (n) {
      return Number(
        n % 100 == 1
          ? 1
          : n % 100 == 2
          ? 2
          : n % 100 == 3 || n % 100 == 4
          ? 3
          : 0,
      );
    },
  };

  var pluralExtensions = {
    rules: (function () {
      var l,
        rules = {};
      for (l = _rules.length; l--; ) {
        rules[_rules[l][0]] = {
          name: _rules[l][1],
          numbers: _rules[l][2],
          plurals: _rulesPluralsTypes[_rules[l][3]],
        };
      }
      return rules;
    })(),

    // 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]],
        };
      }
    },

    needsPlural: function (lng, count) {
      var parts = lng.split("-");

      var ext;
      if (
        pluralExtensions.currentRule &&
        pluralExtensions.currentRule.lng === lng
      ) {
        ext = pluralExtensions.currentRule.rule;
      } else {
        ext = pluralExtensions.rules[parts[f.getCountyIndexOfLng(lng)]];
      }

      if (ext && ext.numbers.length <= 1) {
        return false;
      } else {
        return this.get(lng, count) !== 1;
      }
    },

    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;
          if (ext.noAbs) {
            i = ext.plurals(c);
          } else {
            i = ext.plurals(Math.abs(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[f.getCountyIndexOfLng(lng)], 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.isInitialized = isInitialized;
  i18n.setLng = setLng;
  i18n.preload = preload;
  i18n.addResourceBundle = addResourceBundle;
  i18n.hasResourceBundle = hasResourceBundle;
  i18n.getResourceBundle = getResourceBundle;
  i18n.addResource = addResource;
  i18n.addResources = addResources;
  i18n.removeResourceBundle = removeResourceBundle;
  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.applyReplacement = f.applyReplacement;
  i18n.options = o;
  i18n.noConflict = noConflict;
})(typeof exports === "undefined" ? window : exports);
