| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- (function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
- typeof define === 'function' && define.amd ? define(factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.i18nextBrowserLanguageDetector = factory());
- })(this, (function () { 'use strict';
- const {
- slice,
- forEach
- } = [];
- function defaults(obj) {
- forEach.call(slice.call(arguments, 1), source => {
- if (source) {
- for (const prop in source) {
- if (obj[prop] === undefined) obj[prop] = source[prop];
- }
- }
- });
- return obj;
- }
- function hasXSS(input) {
- if (typeof input !== 'string') return false;
- // Common XSS attack patterns
- const xssPatterns = [/<\s*script.*?>/i, /<\s*\/\s*script\s*>/i, /<\s*img.*?on\w+\s*=/i, /<\s*\w+\s*on\w+\s*=.*?>/i, /javascript\s*:/i, /vbscript\s*:/i, /expression\s*\(/i, /eval\s*\(/i, /alert\s*\(/i, /document\.cookie/i, /document\.write\s*\(/i, /window\.location/i, /innerHTML/i];
- return xssPatterns.some(pattern => pattern.test(input));
- }
- // eslint-disable-next-line no-control-regex
- const fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
- const serializeCookie = function (name, val) {
- let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
- path: '/'
- };
- const opt = options;
- const value = encodeURIComponent(val);
- let str = `${name}=${value}`;
- if (opt.maxAge > 0) {
- const maxAge = opt.maxAge - 0;
- if (Number.isNaN(maxAge)) throw new Error('maxAge should be a Number');
- str += `; Max-Age=${Math.floor(maxAge)}`;
- }
- if (opt.domain) {
- if (!fieldContentRegExp.test(opt.domain)) {
- throw new TypeError('option domain is invalid');
- }
- str += `; Domain=${opt.domain}`;
- }
- if (opt.path) {
- if (!fieldContentRegExp.test(opt.path)) {
- throw new TypeError('option path is invalid');
- }
- str += `; Path=${opt.path}`;
- }
- if (opt.expires) {
- if (typeof opt.expires.toUTCString !== 'function') {
- throw new TypeError('option expires is invalid');
- }
- str += `; Expires=${opt.expires.toUTCString()}`;
- }
- if (opt.httpOnly) str += '; HttpOnly';
- if (opt.secure) str += '; Secure';
- if (opt.sameSite) {
- const sameSite = typeof opt.sameSite === 'string' ? opt.sameSite.toLowerCase() : opt.sameSite;
- switch (sameSite) {
- case true:
- str += '; SameSite=Strict';
- break;
- case 'lax':
- str += '; SameSite=Lax';
- break;
- case 'strict':
- str += '; SameSite=Strict';
- break;
- case 'none':
- str += '; SameSite=None';
- break;
- default:
- throw new TypeError('option sameSite is invalid');
- }
- }
- if (opt.partitioned) str += '; Partitioned';
- return str;
- };
- const cookie = {
- create(name, value, minutes, domain) {
- let cookieOptions = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {
- path: '/',
- sameSite: 'strict'
- };
- if (minutes) {
- cookieOptions.expires = new Date();
- cookieOptions.expires.setTime(cookieOptions.expires.getTime() + minutes * 60 * 1000);
- }
- if (domain) cookieOptions.domain = domain;
- document.cookie = serializeCookie(name, value, cookieOptions);
- },
- read(name) {
- const nameEQ = `${name}=`;
- const ca = document.cookie.split(';');
- for (let i = 0; i < ca.length; i++) {
- let 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(name, domain) {
- this.create(name, '', -1, domain);
- }
- };
- var cookie$1 = {
- name: 'cookie',
- // Deconstruct the options object and extract the lookupCookie property
- lookup(_ref) {
- let {
- lookupCookie
- } = _ref;
- if (lookupCookie && typeof document !== 'undefined') {
- return cookie.read(lookupCookie) || undefined;
- }
- return undefined;
- },
- // Deconstruct the options object and extract the lookupCookie, cookieMinutes, cookieDomain, and cookieOptions properties
- cacheUserLanguage(lng, _ref2) {
- let {
- lookupCookie,
- cookieMinutes,
- cookieDomain,
- cookieOptions
- } = _ref2;
- if (lookupCookie && typeof document !== 'undefined') {
- cookie.create(lookupCookie, lng, cookieMinutes, cookieDomain, cookieOptions);
- }
- }
- };
- var querystring = {
- name: 'querystring',
- // Deconstruct the options object and extract the lookupQuerystring property
- lookup(_ref) {
- let {
- lookupQuerystring
- } = _ref;
- let found;
- if (typeof window !== 'undefined') {
- let {
- search
- } = window.location;
- if (!window.location.search && window.location.hash?.indexOf('?') > -1) {
- search = window.location.hash.substring(window.location.hash.indexOf('?'));
- }
- const query = search.substring(1);
- const params = query.split('&');
- for (let i = 0; i < params.length; i++) {
- const pos = params[i].indexOf('=');
- if (pos > 0) {
- const key = params[i].substring(0, pos);
- if (key === lookupQuerystring) {
- found = params[i].substring(pos + 1);
- }
- }
- }
- }
- return found;
- }
- };
- var hash = {
- name: 'hash',
- // Deconstruct the options object and extract the lookupHash property and the lookupFromHashIndex property
- lookup(_ref) {
- let {
- lookupHash,
- lookupFromHashIndex
- } = _ref;
- let found;
- if (typeof window !== 'undefined') {
- const {
- hash
- } = window.location;
- if (hash && hash.length > 2) {
- const query = hash.substring(1);
- if (lookupHash) {
- const params = query.split('&');
- for (let i = 0; i < params.length; i++) {
- const pos = params[i].indexOf('=');
- if (pos > 0) {
- const key = params[i].substring(0, pos);
- if (key === lookupHash) {
- found = params[i].substring(pos + 1);
- }
- }
- }
- }
- if (found) return found;
- if (!found && lookupFromHashIndex > -1) {
- const language = hash.match(/\/([a-zA-Z-]*)/g);
- if (!Array.isArray(language)) return undefined;
- const index = typeof lookupFromHashIndex === 'number' ? lookupFromHashIndex : 0;
- return language[index]?.replace('/', '');
- }
- }
- }
- return found;
- }
- };
- let hasLocalStorageSupport = null;
- const localStorageAvailable = () => {
- if (hasLocalStorageSupport !== null) return hasLocalStorageSupport;
- try {
- hasLocalStorageSupport = typeof window !== 'undefined' && window.localStorage !== null;
- if (!hasLocalStorageSupport) {
- return false;
- }
- const testKey = 'i18next.translate.boo';
- window.localStorage.setItem(testKey, 'foo');
- window.localStorage.removeItem(testKey);
- } catch (e) {
- hasLocalStorageSupport = false;
- }
- return hasLocalStorageSupport;
- };
- var localStorage = {
- name: 'localStorage',
- // Deconstruct the options object and extract the lookupLocalStorage property
- lookup(_ref) {
- let {
- lookupLocalStorage
- } = _ref;
- if (lookupLocalStorage && localStorageAvailable()) {
- return window.localStorage.getItem(lookupLocalStorage) || undefined; // Undefined ensures type consistency with the previous version of this function
- }
- return undefined;
- },
- // Deconstruct the options object and extract the lookupLocalStorage property
- cacheUserLanguage(lng, _ref2) {
- let {
- lookupLocalStorage
- } = _ref2;
- if (lookupLocalStorage && localStorageAvailable()) {
- window.localStorage.setItem(lookupLocalStorage, lng);
- }
- }
- };
- let hasSessionStorageSupport = null;
- const sessionStorageAvailable = () => {
- if (hasSessionStorageSupport !== null) return hasSessionStorageSupport;
- try {
- hasSessionStorageSupport = typeof window !== 'undefined' && window.sessionStorage !== null;
- if (!hasSessionStorageSupport) {
- return false;
- }
- const testKey = 'i18next.translate.boo';
- window.sessionStorage.setItem(testKey, 'foo');
- window.sessionStorage.removeItem(testKey);
- } catch (e) {
- hasSessionStorageSupport = false;
- }
- return hasSessionStorageSupport;
- };
- var sessionStorage = {
- name: 'sessionStorage',
- lookup(_ref) {
- let {
- lookupSessionStorage
- } = _ref;
- if (lookupSessionStorage && sessionStorageAvailable()) {
- return window.sessionStorage.getItem(lookupSessionStorage) || undefined;
- }
- return undefined;
- },
- cacheUserLanguage(lng, _ref2) {
- let {
- lookupSessionStorage
- } = _ref2;
- if (lookupSessionStorage && sessionStorageAvailable()) {
- window.sessionStorage.setItem(lookupSessionStorage, lng);
- }
- }
- };
- var navigator$1 = {
- name: 'navigator',
- lookup(options) {
- const found = [];
- if (typeof navigator !== 'undefined') {
- const {
- languages,
- userLanguage,
- language
- } = navigator;
- if (languages) {
- // chrome only; not an array, so can't use .push.apply instead of iterating
- for (let i = 0; i < languages.length; i++) {
- found.push(languages[i]);
- }
- }
- if (userLanguage) {
- found.push(userLanguage);
- }
- if (language) {
- found.push(language);
- }
- }
- return found.length > 0 ? found : undefined;
- }
- };
- var htmlTag = {
- name: 'htmlTag',
- // Deconstruct the options object and extract the htmlTag property
- lookup(_ref) {
- let {
- htmlTag
- } = _ref;
- let found;
- const internalHtmlTag = htmlTag || (typeof document !== 'undefined' ? document.documentElement : null);
- if (internalHtmlTag && typeof internalHtmlTag.getAttribute === 'function') {
- found = internalHtmlTag.getAttribute('lang');
- }
- return found;
- }
- };
- var path = {
- name: 'path',
- // Deconstruct the options object and extract the lookupFromPathIndex property
- lookup(_ref) {
- let {
- lookupFromPathIndex
- } = _ref;
- if (typeof window === 'undefined') return undefined;
- const language = window.location.pathname.match(/\/([a-zA-Z-]*)/g);
- if (!Array.isArray(language)) return undefined;
- const index = typeof lookupFromPathIndex === 'number' ? lookupFromPathIndex : 0;
- return language[index]?.replace('/', '');
- }
- };
- var subdomain = {
- name: 'subdomain',
- lookup(_ref) {
- let {
- lookupFromSubdomainIndex
- } = _ref;
- // If given get the subdomain index else 1
- const internalLookupFromSubdomainIndex = typeof lookupFromSubdomainIndex === 'number' ? lookupFromSubdomainIndex + 1 : 1;
- // get all matches if window.location. is existing
- // first item of match is the match itself and the second is the first group match which should be the first subdomain match
- // is the hostname no public domain get the or option of localhost
- const language = typeof window !== 'undefined' && window.location?.hostname?.match(/^(\w{2,5})\.(([a-z0-9-]{1,63}\.[a-z]{2,6})|localhost)/i);
- // if there is no match (null) return undefined
- if (!language) return undefined;
- // return the given group match
- return language[internalLookupFromSubdomainIndex];
- }
- };
- // some environments, throws when accessing document.cookie
- let canCookies = false;
- try {
- // eslint-disable-next-line no-unused-expressions
- document.cookie;
- canCookies = true;
- // eslint-disable-next-line no-empty
- } catch (e) {}
- const order = ['querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag'];
- if (!canCookies) order.splice(1, 1);
- const getDefaults = () => ({
- order,
- lookupQuerystring: 'lng',
- lookupCookie: 'i18next',
- lookupLocalStorage: 'i18nextLng',
- lookupSessionStorage: 'i18nextLng',
- // cache user language
- caches: ['localStorage'],
- excludeCacheFor: ['cimode'],
- // cookieMinutes: 10,
- // cookieDomain: 'myDomain'
- convertDetectedLanguage: l => l
- });
- class Browser {
- constructor(services) {
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
- this.type = 'languageDetector';
- this.detectors = {};
- this.init(services, options);
- }
- init() {
- let services = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
- languageUtils: {}
- };
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
- let i18nOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
- this.services = services;
- this.options = defaults(options, this.options || {}, getDefaults());
- if (typeof this.options.convertDetectedLanguage === 'string' && this.options.convertDetectedLanguage.indexOf('15897') > -1) {
- this.options.convertDetectedLanguage = l => l.replace('-', '_');
- }
- // backwards compatibility
- if (this.options.lookupFromUrlIndex) this.options.lookupFromPathIndex = this.options.lookupFromUrlIndex;
- this.i18nOptions = i18nOptions;
- this.addDetector(cookie$1);
- this.addDetector(querystring);
- this.addDetector(localStorage);
- this.addDetector(sessionStorage);
- this.addDetector(navigator$1);
- this.addDetector(htmlTag);
- this.addDetector(path);
- this.addDetector(subdomain);
- this.addDetector(hash);
- }
- addDetector(detector) {
- this.detectors[detector.name] = detector;
- return this;
- }
- detect() {
- let detectionOrder = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.order;
- let detected = [];
- detectionOrder.forEach(detectorName => {
- if (this.detectors[detectorName]) {
- let lookup = this.detectors[detectorName].lookup(this.options);
- if (lookup && typeof lookup === 'string') lookup = [lookup];
- if (lookup) detected = detected.concat(lookup);
- }
- });
- detected = detected.filter(d => d !== undefined && d !== null && !hasXSS(d)).map(d => this.options.convertDetectedLanguage(d));
- if (this.services && this.services.languageUtils && this.services.languageUtils.getBestMatchFromCodes) return detected; // new i18next v19.5.0
- return detected.length > 0 ? detected[0] : null; // a little backward compatibility
- }
- cacheUserLanguage(lng) {
- let caches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.options.caches;
- if (!caches) return;
- if (this.options.excludeCacheFor && this.options.excludeCacheFor.indexOf(lng) > -1) return;
- caches.forEach(cacheName => {
- if (this.detectors[cacheName]) this.detectors[cacheName].cacheUserLanguage(lng, this.options);
- });
- }
- }
- Browser.type = 'languageDetector';
- return Browser;
- }));
|