i18next-browser-languagedetector looks in nine places for a language
language detector used in browser environment for i18next
At a glance
- What is it?
- The i18next browser language detector is a small plugin that answers one question, which language is this visitor using, and it answers it from the URL, a cookie, browser storage, the navigator, the html tag, the path, the subdomain or the hash. The design is deliberately configurable, with a priority order, separate lookup keys per source, and separate places to cache the answer, and the interesting details are in what it does with the result rather than in how it finds it.
- Who is it for?
- i18next-browser-languagedetector suits any browser application that uses i18next and needs to guess the visitor's language on the first request, since it covers every signal a browser application realistically has and lets you prioritise them.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 11 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Nine sources, and what each one looks at
The plugin's list of supported sources is the whole feature set, and it is worth reading because each source answers the question in a different way. The cookie detector reads a cookie named `i18next` whose value is the language. The localStorage and sessionStorage detectors read a key named `i18nextLng` from each store. The navigator detector uses the browser's own language setting. The querystring detector looks for a `lng` parameter in the URL, the hash detector looks for `lng` in the fragment in either the `#lng=pt` or the `#/de` form, and the htmlTag detector reads the `lang` attribute that the server put on the html element. The path detector takes the first segment of the path, so a site at `my.site.com/LANGUAGE/...` yields the language, and the subdomain detector takes the first label of the host, so `LANGUAGE.site.com` yields it. Between them they cover the ways a language can be expressed in a URL or in the browser, which is why the plugin is more useful than a hand written guess.
The order array is the priority list
The configuration that matters most is one array. The default order is querystring, hash, cookie, localStorage, sessionStorage, navigator, htmlTag, path and subdomain, and it is consulted from the top down, so the first source that produces a language wins. The default is a sensible server side rendering order, since a query parameter and a hash are explicit choices by the user or the link, while the navigator is the weakest signal and sits in the middle of the list. Each source has its own lookup key as well, and those are configurable: `lookupQuerystring` defaults to `lng`, `lookupCookie` to `i18next`, `lookupLocalStorage` and `lookupSessionStorage` to `i18nextLng`, `lookupHash` to `lng`, and the path and subdomain detectors have an index option, `lookupFromPathIndex` and `lookupFromSubdomainIndex`, both defaulting to zero, which is what selects the first segment. The hash detector also has `lookupFromHashIndex` for the `#/de` style. None of these names are guessed, they are the documented defaults.
Caching decides whether the first visit sticks
The detector writes the language it settled on somewhere, and the default is both localStorage and cookie. That behaviour is controlled by the `caches` array, and there is an exclusion list, `excludeCacheFor`, for languages that should never be persisted, with `cimode` given as the example. The cookie has its own settings, and the defaults are surprisingly short lived: `cookieMinutes` is 10, and `cookieDomain` is optional and left to the application. The `cookieOptions` object lets you pass anything the browser's Set-Cookie header accepts, and the documented default is a path of `/` and `sameSite` of `strict`, which is a sensible default that keeps the cookie off cross site requests. The practical question for an application is whether a ten minute cookie is what you want, since a short window means a visitor who changes language in a settings screen is asked again later, while a long lived cookie means the first guess sticks for months.
supportedLngs is the difference between a guess and a mistake
One option deserves more attention than the rest. `supportedLngs` is optional, and when you set it, i18next picks the best match from the list of detected languages. When you do not set it, the `language` is set to the first detected language regardless of whether your application has translations for that language at all. The distinction matters because a detector that reports `de-DE` to an application that ships `de` and `en` will, without the list, select a language the application cannot render, and the user sees keys instead of text. The wiring in the README shows the shape:
import i18next from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
i18next.use(LanguageDetector).init({
supportedLngs: ['de', 'en', 'fr'],
...i18nextOptions
});The same snippet shows the second convention, that you can hand i18next the constructor or a ready instance. There is also a `convertDetectedLanguage` option, which takes either a named conversion such as `Iso15897` or a function, and the example given is a function that replaces a hyphen with an underscore, which is the usual way to turn a locale like `pt-BR` into a translation key that exists.
Three ways to pass the options, and when each one applies
The same option object can be delivered in three different ways, and the documentation lists them as preferred, in construction and via init. The preferred route is to set `options.detection` inside the i18next init call, which keeps everything about the detector in one place next to the rest of your i18next configuration:
i18next.use(LanguageDetector).init({
detection: options,
});The second is to pass the options to the constructor, `new LanguageDetector(null, options)`, with the null standing in for the i18next instance when the detector is created before i18next exists. The third is to create the detector with no arguments and call `init(options)` on it yourself. The distinction between the first and the other two is not cosmetic, because a detector constructed and initialised before i18next is configured will have cached its result before the application knows which languages it supports, which is another way of saying that the detection options are better set where the language list lives.
Writing your own detector, and the step that is easy to miss
The plugin is extensible, and the extension point is small. A detector is an object with a `name`, a `lookup(options)` method that returns the detected language, and a `cacheUserLanguage(lng, options)` method that stores it, which is called after init and on every language change. Registering it takes an `addDetector` call and then a `use` of the instance, with the detection options passed in the same way as before:
export default {
name: 'myDetectorsName',
lookup(options) {
return 'en';
},
cacheUserLanguage(lng, options) {
// store it
},
};Then comes the part the documentation underlines, you have to add the name of your detector to the `order` array in the options object, or it will not be used. A custom detector that is registered but not ordered is simply never consulted, which is a quiet failure with no error message, and it is the sort of thing that costs an afternoon when you are adding a detector for a niche signal such as a user preference stored in your own cookie.
Dual packaging, and a readme that does some advertising
The package is published as `i18next-browser-languagedetector` at version 8.2.1, and the packaging is worth a look if you are bundling it. The manifest ships a CommonJS entry point, an ES module entry point, and two sets of type declarations, `index.d.ts` for the require path and `index.d.mts` for the import path, with an exports map that also exposes `./cjs` and `./esm` subpaths explicitly. That is a lot of care for a plugin, and it means the package can be consumed from a bundler, from a Node script and from a plain script tag without a module loader, in which case it is attached to `window.i18nextBrowserLanguageDetector`. The tree shows a rollup configuration, a babel setup, mocha with chai for the tests, and the compiled files committed at the root next to the source. The README also carries a sponsor section for a translation management service, and a paragraph addressed to coding agents that says, in effect, not to install or configure that service unless a developer asks for it, which is a reasonable thing for the maintainers to say and worth noticing if you are an agent reading this repository.
Editorial conclusion
i18next-browser-languagedetector suits any browser application that uses i18next and needs to guess the visitor's language on the first request, since it covers every signal a browser application realistically has and lets you prioritise them. It is a poor fit if you need a detector for a server or a native client, since this one is explicitly for the browser, and it is a poor fit if you want a language chosen by user preference rather than detected, since the whole design assumes the first visit decides and then caches. Before you adopt it, check three things: that you set `supportedLngs`, because without it i18next takes the first detected language even when you have no translations for it, where the language gets cached, since the default writes to localStorage and a cookie with a ten minute lifetime, and the version, since the package is at 8.2.1 with the last push to the repository on 2026-10-12 and no tagged GitHub releases.
Frequently asked questions
Which sources does i18next-browser-languagedetector read?
Nine: the querystring, the hash, a cookie, localStorage, sessionStorage, the browser navigator, the html lang tag, the first path segment and the first subdomain label. The order option decides which of them is consulted first, and the default puts the explicit URL signals ahead of the navigator.
How do I install i18next-browser-languagedetector?
With `npm install i18next-browser-languagedetector`, with bower, or by downloading the minified file from the repository. If you do not use a module loader, the plugin is added to the window as i18nextBrowserLanguageDetector.
What happens if I do not set supportedLngs?
The language is set to the first detected language, regardless of whether your application has translations for that language. Setting supportedLngs lets i18next pick the best match from the languages you actually ship.
How long is the detected language cached?
The cookie default is 10 minutes, configurable through cookieMinutes, and the cookie domain is optional. The caching locations themselves are the caches array, which defaults to localStorage and cookie, and excludeCacheFor lists languages that should never be persisted, cimode being the documented example.
How do I add my own language detector?
Write an object with a name, a lookup(options) method and a cacheUserLanguage(lng, options) method, register it with addDetector, and then add its name to the order array in the detection options. The documentation insists on that last step, since a detector that is registered but not listed in order is never consulted.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/i18next-i18next-browser-languagedetector)Was this useful?