Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: i18next/i18next-browser-languageDetector
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v7.2.1
Choose a base ref
...
head repository: i18next/i18next-browser-languageDetector
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v8.0.0
Choose a head ref
  • 7 commits
  • 16 files changed
  • 3 contributors

Commits on Apr 21, 2024

  1. Documentation: include supportedLngs in example (#284)

    dgmstuart authored Apr 21, 2024
    Copy the full SHA
    026a335 View commit details
  2. Fix typos and grammar (#285)

    dgmstuart authored Apr 21, 2024
    Copy the full SHA
    f9c9c86 View commit details

Commits on May 12, 2024

  1. chore: add browserslist config "fully support es6" (#286)

    * chore: add browserslist config "fully support es6"
    
    * match react-i18next
    VIKTORVAV99 authored May 12, 2024
    Copy the full SHA
    5b046ce View commit details
  2. prepare changelog

    adrai committed May 12, 2024
    Copy the full SHA
    1affdb8 View commit details
  3. use object deconstruction, optional chaining and hot path optimizatio…

    …ns (#287)
    VIKTORVAV99 authored May 12, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    9a3b014 View commit details
  4. release

    adrai committed May 12, 2024
    Copy the full SHA
    d25f7b5 View commit details
  5. 8.0.0

    adrai committed May 12, 2024
    Copy the full SHA
    0acd7d8 View commit details
11 changes: 10 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
{
"presets": ["@babel/env"]
"presets": [
[
"@babel/env",
{
"targets": {
"browsers": ["defaults"]
}
}
]
]
}
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
### 8.0.0

- chore: set browsers target to defaults [286](https://github.com/i18next/i18next-browser-languageDetector/pull/286)
- perf: use object deconstruction, optional chaining and hot path optimisations [287](https://github.com/i18next/i18next-browser-languageDetector/pull/287)

### 7.2.1

- fix: align addDetector impementation to type definition [282](https://github.com/i18next/i18next-browser-languageDetector/issues/282)
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

[![npm version](https://img.shields.io/npm/v/i18next-browser-languagedetector.svg?style=flat-square)](https://www.npmjs.com/package/i18next-browser-languagedetector)

This is a i18next language detection plugin use to detect user language in the browser with support for:
This is an i18next language detection plugin used to detect user language in the browser, with support for:

- cookie (set cookie i18next=LANGUAGE)
- sessionStorage (set key i18nextLng=LANGUAGE)
@@ -33,10 +33,15 @@ Wiring up:
import i18next from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

i18next.use(LanguageDetector).init(i18nextOptions);
i18next.use(LanguageDetector).init({
supportedLngs: ['de', 'en', 'fr'],
...i18nextOptions
});
```

As with all modules you can either pass the constructor function (class) to the i18next.use or a concrete instance.
As with all modules you can either pass the constructor function (class) to the `i18next.use` or to a concrete instance.

[`supportedLngs`](https://www.i18next.com/overview/configuration-options#languages-namespaces-resources) is optional, but allows i18next to pick the best match from the list of detected languages. If it's not set then [`language`](https://www.i18next.com/overview/api#language) will be set to the first detected language, regardless of whether your application has translations for that language or not.

## Detector Options
*The default options can be found [here](https://github.com/i18next/i18next-browser-languageDetector/blob/9efebe6ca0271c3797bc09b84babf1ba2d9b4dbb/src/index.js#L11).*
@@ -58,7 +63,7 @@ As with all modules you can either pass the constructor function (class) to the
caches: ['localStorage', 'cookie'],
excludeCacheFor: ['cimode'], // languages to not persist (cookie, localStorage)

// optional expire and domain for set cookie
// optional expiry and domain for set cookie
cookieMinutes: 10,
cookieDomain: 'myDomain',

@@ -68,7 +73,7 @@ As with all modules you can either pass the constructor function (class) to the
// optional set cookie options, reference:[MDN Set-Cookie docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie)
cookieOptions: { path: '/', sameSite: 'strict' },

// optional conversion function to use to modify the detected language code
// optional conversion function used to modify the detected language code
convertDetectedLanguage: 'Iso15897',
convertDetectedLanguage: (lng) => lng.replace('-', '_')
}
413 changes: 189 additions & 224 deletions i18nextBrowserLanguageDetector.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion i18nextBrowserLanguageDetector.min.js
4 changes: 2 additions & 2 deletions package-lock.json
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "i18next-browser-languagedetector",
"version": "7.2.1",
"version": "8.0.0",
"description": "language detector used in browser environment for i18next",
"main": "./dist/cjs/i18nextBrowserLanguageDetector.js",
"module": "./dist/esm/i18nextBrowserLanguageDetector.js",
21 changes: 11 additions & 10 deletions src/browserLookups/cookie.js
Original file line number Diff line number Diff line change
@@ -82,20 +82,21 @@ const cookie = {
export default {
name: 'cookie',

lookup(options) {
let found;

if (options.lookupCookie && typeof document !== 'undefined') {
const c = cookie.read(options.lookupCookie);
if (c) found = c;
// Deconstruct the options object and extract the lookupCookie property
lookup({ lookupCookie }) {
if (lookupCookie && typeof document !== 'undefined') {
return cookie.read(lookupCookie) || undefined;
}

return found;
return undefined;
},

cacheUserLanguage(lng, options) {
if (options.lookupCookie && typeof document !== 'undefined') {
cookie.create(options.lookupCookie, lng, options.cookieMinutes, options.cookieDomain, options.cookieOptions);
// Deconstruct the options object and extract the lookupCookie, cookieMinutes, cookieDomain, and cookieOptions properties
cacheUserLanguage(lng, {
lookupCookie, cookieMinutes, cookieDomain, cookieOptions
}) {
if (lookupCookie && typeof document !== 'undefined') {
cookie.create(lookupCookie, lng, cookieMinutes, cookieDomain, cookieOptions);
}
}
};
9 changes: 5 additions & 4 deletions src/browserLookups/htmlTag.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
export default {
name: 'htmlTag',

lookup(options) {
// Deconstruct the options object and extract the htmlTag property
lookup({ htmlTag }) {
let found;
const htmlTag = options.htmlTag || (typeof document !== 'undefined' ? document.documentElement : null);
const internalHtmlTag = htmlTag || (typeof document !== 'undefined' ? document.documentElement : null);

if (htmlTag && typeof htmlTag.getAttribute === 'function') {
found = htmlTag.getAttribute('lang');
if (internalHtmlTag && typeof internalHtmlTag.getAttribute === 'function') {
found = internalHtmlTag.getAttribute('lang');
}

return found;
20 changes: 9 additions & 11 deletions src/browserLookups/localStorage.js
Original file line number Diff line number Diff line change
@@ -17,20 +17,18 @@ const localStorageAvailable = () => {
export default {
name: 'localStorage',

lookup(options) {
let found;

if (options.lookupLocalStorage && localStorageAvailable()) {
const lng = window.localStorage.getItem(options.lookupLocalStorage);
if (lng) found = lng;
// Deconstruct the options object and extract the lookupLocalStorage property
lookup({ lookupLocalStorage }) {
if (lookupLocalStorage && localStorageAvailable()) {
return window.localStorage.getItem(lookupLocalStorage) || undefined; // Undefined ensures type consistency with the previous version of this function
}

return found;
return undefined;
},

cacheUserLanguage(lng, options) {
if (options.lookupLocalStorage && localStorageAvailable()) {
window.localStorage.setItem(options.lookupLocalStorage, lng);
// Deconstruct the options object and extract the lookupLocalStorage property
cacheUserLanguage(lng, { lookupLocalStorage }) {
if (lookupLocalStorage && localStorageAvailable()) {
window.localStorage.setItem(lookupLocalStorage, lng);
}
}
};
16 changes: 9 additions & 7 deletions src/browserLookups/navigator.js
Original file line number Diff line number Diff line change
@@ -5,16 +5,18 @@ export default {
const found = [];

if (typeof navigator !== 'undefined') {
if (navigator.languages) { // chrome only; not an array, so can't use .push.apply instead of iterating
for (let i = 0; i < navigator.languages.length; i++) {
found.push(navigator.languages[i]);
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 (navigator.userLanguage) {
found.push(navigator.userLanguage);
if (userLanguage) {
found.push(userLanguage);
}
if (navigator.language) {
found.push(navigator.language);
if (language) {
found.push(language);
}
}

26 changes: 10 additions & 16 deletions src/browserLookups/path.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
export default {
name: 'path',

lookup(options) {
let found;
if (typeof window !== 'undefined') {
const language = window.location.pathname.match(/\/([a-zA-Z-]*)/g);
if (language instanceof Array) {
if (typeof options.lookupFromPathIndex === 'number') {
if (typeof language[options.lookupFromPathIndex] !== 'string') {
return undefined;
}
found = language[options.lookupFromPathIndex].replace('/', '');
} else {
found = language[0].replace('/', '');
}
}
}
return found;
// Deconstruct the options object and extract the lookupFromPathIndex property
lookup({ lookupFromPathIndex }) {

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('/', '');
}
};
7 changes: 4 additions & 3 deletions src/browserLookups/querystring.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
export default {
name: 'querystring',

lookup(options) {
// Deconstruct the options object and extract the lookupQuerystring property
lookup({ lookupQuerystring }) {
let found;

if (typeof window !== 'undefined') {
let { search } = window.location;
if (!window.location.search && window.location.hash && window.location.hash.indexOf('?') > -1) {
if (!window.location.search && window.location.hash?.indexOf('?') > -1) {
search = window.location.hash.substring(window.location.hash.indexOf('?'));
}
const query = search.substring(1);
@@ -15,7 +16,7 @@ export default {
const pos = params[i].indexOf('=');
if (pos > 0) {
const key = params[i].substring(0, pos);
if (key === options.lookupQuerystring) {
if (key === lookupQuerystring) {
found = params[i].substring(pos + 1);
}
}
18 changes: 7 additions & 11 deletions src/browserLookups/sessionStorage.js
Original file line number Diff line number Diff line change
@@ -17,20 +17,16 @@ const sessionStorageAvailable = () => {
export default {
name: 'sessionStorage',

lookup(options) {
let found;

if (options.lookupSessionStorage && sessionStorageAvailable()) {
const lng = window.sessionStorage.getItem(options.lookupSessionStorage);
if (lng) found = lng;
lookup({ lookupSessionStorage }) {
if (lookupSessionStorage && sessionStorageAvailable()) {
return window.sessionStorage.getItem(lookupSessionStorage) || undefined;
}

return found;
return undefined;
},

cacheUserLanguage(lng, options) {
if (options.lookupSessionStorage && sessionStorageAvailable()) {
window.sessionStorage.setItem(options.lookupSessionStorage, lng);
cacheUserLanguage(lng, { lookupSessionStorage }) {
if (lookupSessionStorage && sessionStorageAvailable()) {
window.sessionStorage.setItem(lookupSessionStorage, lng);
}
}
};
15 changes: 5 additions & 10 deletions src/browserLookups/subdomain.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
export default {
name: 'subdomain',

lookup(options) {
lookup({ lookupFromSubdomainIndex }) {
// If given get the subdomain index else 1
const lookupFromSubdomainIndex = typeof options.lookupFromSubdomainIndex === 'number'
? options.lookupFromSubdomainIndex + 1
: 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 macht which sould be the first subdomain match
// 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
&& window.location.hostname
&& window.location.hostname.match(/^(\w{2,5})\.(([a-z0-9-]{1,63}\.[a-z]{2,6})|localhost)/i);
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[lookupFromSubdomainIndex];
return language[internalLookupFromSubdomainIndex];
}
};
8 changes: 3 additions & 5 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
const arr = [];
const each = arr.forEach;
const { slice } = arr;
const { slice, forEach } = [];

export function defaults(obj) {
each.call(slice.call(arguments, 1), (source) => {
forEach.call(slice.call(arguments, 1), (source) => {
if (source) {
for (const prop in source) {
if (obj[prop] === undefined) obj[prop] = source[prop];
@@ -14,7 +12,7 @@ export function defaults(obj) {
}

export function extend(obj) {
each.call(slice.call(arguments, 1), (source) => {
forEach.call(slice.call(arguments, 1), (source) => {
if (source) {
for (const prop in source) {
obj[prop] = source[prop];