i18next Supported Locale Codes: There Is No Fixed List
If you came here looking for a canonical i18next supported locale codes list — a table of every language i18next "knows about" — there's something you should know first: it doesn't exist, and it isn't supposed to. i18next is deliberately locale-agnostic. It doesn't ship a whitelist of valid languages, doesn't validate your codes against a registry, and won't reject en-US, zh-Hant, or a made-up xx-YY unless you tell it to. This guide explains what "supported locales" really means in i18next, how the supportedLngs option works, and which locale code format you should use so translations resolve correctly.
TL;DR: what "supported locales" means in i18next
- i18next has no built-in list of supported locale codes. It accepts any string you use as a language key.
- You define your supported locales with the
supportedLngsinit option (an array like['en', 'de', 'fr']). - Default is
supportedLngs: false, meaning no restriction — every code is accepted. - Use BCP 47 / [Unicode] tags such as
en,en-US,pt-BR,zh-CN(hyphens, not underscores). - The set of "supported" codes is whatever you configure plus the resources you load — nothing more, nothing less.
Why there is no official i18next locale list
Many i18n tools tie you to a predefined catalog of locales. i18next takes the opposite approach. It treats a "language" as just a key into your resource tree:
const resources = { en: { translation: { key: 'value' } }, 'en-US': { translation: { key: 'value' } }, de: { translation: { key: 'value' } }, };Whatever keys you put at the top level are your languages. There is no registry inside i18next enforcing that en is real and klingon isn't. This is by design: it lets teams support regional variants, pseudo-locales for testing, RTL languages, or entirely custom codes without patching the library.
So the honest answer to "what locale codes does i18next support?" is: all of them, and none of them by default. Support is something you declare, not something you look up.
The supportedLngs option (formerly whitelist)
The closest thing to a "supported locales list" in i18next is the supportedLngs configuration option. This is where you restrict i18next to a known set of languages.
import i18n from 'i18next'; i18n.init({ supportedLngs: ['en', 'de', 'fr'], fallbackLng: 'en', });According to the i18next configuration docs, setting an array here stops i18next from resolving or requesting languages outside it — useful when you use a language detector and don't want a browser reporting es-419 to trigger a request for a file you never created.
Key facts about supportedLngs:
- Default value is
false— no restriction, any code passes. - It accepts an array of strings, e.g.
['en', 'en-US', 'de']. - When set and paired with a backend such as
i18next-http-backend, it prevents wasted network requests for unlisted languages. - i18next internally exposes
services.languageUtils.isSupportedCode(code)to test membership (this was renamed fromisWhitelisted).
It used to be called whitelist
If you're reading an older tutorial that mentions whitelist, that's the same feature under its old name. The rename happened in the v20 → v21 era: whitelist became supportedLngs, and nonExplicitWhitelist became nonExplicitSupportedLngs, as recorded in the i18next changelog and migration guide. The change first shipped with backwards-compatible deprecation warnings, and the legacy whitelist names were later removed entirely. On any modern version, use supportedLngs.
As of 2026-07-14, the current major release line is i18next v26 (e.g.
26.3.4on npm). Verify the latest against npmjs.com/package/i18next.
What locale code format should you use?
i18next follows the standard BCP 47 language-tag convention (the same UTS/Unicode locale identifiers used across the web). The docs are explicit on one point: use the en-US form with a hyphen, not underscores like en_US.
A tag is generally built as language, optionally language-REGION, and optionally with a script subtag:
| Pattern | Example | Meaning |
|---|---|---|
language | en, de, ja | Language only |
language-REGION | en-US, pt-BR, fr-CA | Language + country/region |
language-Script | zh-Hant, sr-Cyrl | Language + writing system |
language-Script-REGION | zh-Hant-TW | Fully qualified |
Because i18next doesn't validate against a registry, a typo like en-us or en_US won't throw — it will simply be treated as a distinct language key, and lookups can silently miss. Sticking to canonical BCP 47 casing (language lowercase, Script title case, REGION uppercase) keeps detection, fallback, and plural resolution predictable.
How i18next resolves a locale: load and fallbacks
Even though there's no master list, i18next has clear rules for turning one requested code into the sequence of codes it will actually look up. The load option controls this. Given a set language of en-US:
load: 'all'⇒['en-US', 'en', 'dev']load: 'currentOnly'⇒'en-US'onlyload: 'languageOnly'⇒'en'only
Here dev is the default value of fallbackLng. You can inspect the resolved chain at runtime:
i18next.languages— the ordered array of codes used for lookup (specific → less specific → fallbacks).i18next.language— the current detected or set language.i18next.resolvedLanguage— the primary language actually in effect, ideal for a language switcher.
Regional variants and nonExplicitSupportedLngs
Suppose your supportedLngs contains only ['en', 'de'], but a user's browser reports de-AT. By default that variant isn't "supported." Set nonExplicitSupportedLngs: true and i18next will treat de-AT as valid because its base language de is in the list. Per the docs, note that combining this with an HTTP backend can produce failed requests for variant files that don't exist — so pair it with a sensible load strategy.
Detecting the user's locale
If you want i18next to pick a language automatically rather than hard-coding lng, add the i18next-browser-languageDetector plugin. It looks up the language, in configurable order, from sources such as:
- URL query string (
?lng=de) - Cookie /
localStorage/sessionStorage navigator(browser languages)- The
<html lang>attribute - URL path or subdomain
The detector returns whatever code the environment provides — again, it does not restrict to a fixed list. That's exactly why supportedLngs matters: it's your guardrail against loading files for a locale you never authored. A common, robust setup looks like this:
import i18n from 'i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import HttpApi from 'i18next-http-backend'; import { initReactI18next } from 'react-i18next'; const supportedLngs = { en: 'English', de: 'Deutsch', ar: 'العربية' }; i18n
.use(HttpApi) .use(LanguageDetector) .use(initReactI18next) .init({ fallbackLng: 'en', supportedLngs: Object.keys(supportedLngs), // ['en','de','ar'] });Building your own "supported locales list"
Since the list lives in your app, the practical pattern is to treat supportedLngs as your single source of truth and derive everything else from it — the language switcher, route generation, and validation:
- Declare once: keep a
supportedLngsarray (or a{ code: label }map) in one module. - Feed it to init: pass
Object.keys(...)intosupportedLngs. - Render a switcher: map the same object to
<option>elements and calli18next.changeLanguage(code). - Validate input: use
i18next.services.languageUtils.isSupportedCode(code)before trusting a code from a URL or query string.
This keeps the "list" honest: it's whatever you actually ship translations for.
Common misconceptions, cleared up
- "i18next only supports certain languages." False. It supports any code you use; support is configuration, not a built-in catalog.
- "There's a
getSupportedLngs()that returns all valid locales." There's no global registry to return.i18next.languagesreflects the current resolution chain, andisSupportedCode()tests against yoursupportedLngs. - "Plurals only work for listed locales." Plural rules come from the runtime's
Intl.PluralRules(used since the v21/v22 line), i.e. the JavaScript engine's own Unicode data — not an i18next locale list. - "Underscores are fine." Use hyphens.
en-US, noten_US.
Conclusion
The search for an "i18next supported locale codes list" ends with a useful truth: i18next doesn't ship one, because it's locale-agnostic by design. The codes it supports are the codes you use — declared through the supportedLngs option (once called whitelist), formatted as BCP 47 tags like en-US or zh-Hant, and resolved via load, fallbackLng, and optional language detection. So don't hunt for a hidden catalog. Instead, define your own supported set in one place, feed it to supportedLngs, and let i18next handle the resolution. Next step: open your i18next init() config, add or audit your supportedLngs array, and confirm every listed code has matching translation resources.
References
- i18next — Configuration Options: https://www.i18next.com/overview/configuration-options
- i18next — API (
languages,language,resolvedLanguage): https://www.i18next.com/overview/api - i18next — Migration Guide (
whitelist→supportedLngs): https://www.i18next.com/misc/migration-guide - i18next — CHANGELOG: https://github.com/i18next/i18next/blob/master/CHANGELOG.md
- i18next-browser-languageDetector: https://github.com/i18next/i18next-browser-languageDetector
- IETF BCP 47 (language tags): https://www.rfc-editor.org/info/bcp47
- W3C — Language tags in HTML and XML: https://www.w3.org/International/articles/language-tags/
- i18next on npm (verify latest version): https://www.npmjs.com/package/i18next

- Coding Agent Help - Wiki
- GStreamer + TypeScript: GObject Breaking Changes
- Firebase Auth Token Expiry: How Long ID Tokens Last
- i18next Supported Locale Codes: There Is No Fixed List
- BoringSSL's Latest Stable Release Version, Explained
- c8 Coverage JSON Output Flag: A Practical Guide
- OpenSSL Latest Stable Release: The 2026 Version Guide
- Polars DataFrame Filter: Always-True Predicate
- Latest Stable Version of HashiCorp Terraform (2026)
- GStreamer 1.0: The Major Version That Changed AudioFormat
- GitHub GraphQL API isTotal Field: It Doesn't Exist
