FAQ
Misc
i18next is awesome. How can I support the project?
There are a lot of ways to support us. Make a PR for a feature requested. Improve the documentation. Help others to get started. Spread the word.
You can support the future of i18next by using Locize. Itโs our 'Localization as a Service' offering that funds the development of this open-source ecosystem. With our Free plan, you can now support the project even on smaller hobby sites at no cost, while benefiting from a professional translation workflow.
Loading issues
I don't see my translations!!!
Try setting debug: true on init and check the console log. There is rather sure a warning for unable to resolve the loadPath or invalid json. Check if the translation files are accessible via browser.
Translation
How to translate the resource files?
For a quick and dirty machine translation you may have a look at this free translator. But in general we suggest to use a smart Translation Management Service like locize to translate your i18next resources.
For professional translations we advice you to work with human translators. Or at least proofread the results coming from machine translations.
How do i know which plural suffix i have to use?
On the plural page there is a tool to get them.
Or try translation-check, it shows an overview of your translations in a nice UI. It shows also the appropriate plural forms.
Or you use a smart translation management system, like locize.

Why are my plural keys not working?
Are you seeing this error in the development console?
No Intl support, please use an Intl polyfill!
i18next uses the Intl.PluralRules API to resolve plural forms. Since v24 the Intl API is mandatory: there is no fallback to the old JSON v3 plural handling anymore. In an environment without Intl.PluralRules, i18next logs the error above and degrades to a minimal English-style rule (_one/_other only), so languages with more plural forms (e.g. Russian, Arabic, Polish) will show wrong or missing plurals.
The fix is a 2-line polyfill:
React Native: the Hermes engine still does not implement Intl.PluralRules, so the polyfill above is required in every React Native app that uses plurals.
Historical note: on i18next v21-v23 the same situation produced the warning "Your environment seems not to be Intl API compatible ... Will fallback to the compatibilityJSON v3 format handling", and compatibilityJSON: 'v3' could be used as an escape hatch. That option was removed in v24; the polyfill is the only fix on current versions.
How should the language codes be formatted?
Theoretically, you're not bound to any specific language code format, but if you want to make use of all the in built language features, like proper pluralization and correct fallback resolution, we strongly suggest to use the following iso norm (BCP 47 language tag):
lng-(script)-REGION-(extensions)
&#xNAN;i.e.
en, en-US or en-GB
zh, zh-HK or zh-Hant-HK
Other examples are listed here: https://www.iana.org/assignments/language-tags/language-tags.xhtml
And more information about the format can be found here: https://www.w3.org/International/articles/language-tags/
As soon as you use the dash character - the language codes are tried to be formatted with Intl.getCanonicalLocales.
How do I get the current language?
Use i18next.resolvedLanguage:
The three related properties differ:
i18next.language: the language as set/detected, e.g.de-CHi18next.resolvedLanguage: the language actually used for the loaded translations after fallback resolution, e.g.deโ this is usually what you want for UI (language switchers,langattributes)i18next.languages: the full fallback chain, e.g.['de-CH', 'de', 'en']
In react-i18next you get the instance from the hook: const { i18n } = useTranslation().
Why does my app keep switching back to a previously detected language?
i18next-browser-languageDetector caches the detected language in localStorage (and optionally cookies) by default. A language detected or chosen once will win over a changed fallbackLng or a different browser setting on the next visit.
To change this, configure the detector's caches option:
During development you can simply clear the i18nextLng entry from localStorage. Calling i18next.changeLanguage(lng) updates the cache; i18next.changeLanguage(undefined) re-runs detection.
Is i18next overkill for a small site?
i18next can start as small as a naive "fetch a JSON file" approach, without any plugins:
That is the whole setup: no backend, no detector, no framework binding. What you get over a hand-rolled fetch-JSON helper, without writing it yourself: correct plural rules for every language (not just count === 1), a fallback chain so missing keys never render blank, interpolation with escaping, and Intl-based formatting. When the project grows, the plugin ecosystem (lazy loading, detection, framework bindings) attaches to the same setup instead of a rewrite.
Can I use my source text as the key (gettext style)?
Yes. Nothing forces abstract keys like header.title; natural-language keys work:
Combined with saveMissing (or i18next-cli extract with defaultValue), the English text fills the source catalog automatically, and translators translate from the key itself. Trade-off: changing the source wording changes the key (invalidating existing translations), and very long texts make unwieldy keys โ which is why abstract keys remain the default recommendation for larger apps.
Process
How do I keep overview over my translation progress?
Try translation-check, it shows an overview of your translations in a nice UI. Check which keys are not yet translated.
If you need more, it might be time to use a translation management tool.
How to handle with changes in e2e tests?
For e2e tests a good tactic is to set language to cimode on init. This will set i18next to always return the key on calling i18next.t. Want to add the namespace to returned value change appendNamespaceToCIMode: true on init.
How to use i18next in serverless environments?
Due to how serverless functions work, you cannot guarantee that a cached version of your data is available. Serverless functions are short-lived, and can shut down at any time, purging any in-memory or filesystem cache. This may be an acceptable trade-off, but sometimes it isn't acceptable.
Because of this we suggest to not use a remote backend and to download the translations and package them with your serverless function.
Read more about this topic, here.

Last updated
