i18next review
i18next 26.4.0 is a runtime translation engine that looks up keys in language resources, chooses plural and context forms, interpolates values, formats through Intl, and follows fallback languages. The core does not depend on React; framework bindings, file or HTTP backends, browser detectors, and translation services are separate packages. Version 26.4.0 caches repeated language-hierarchy resolution and clears that cache when fallbackLng changes. Our install found bundled TypeScript declarations and working CommonJS and ESM entry paths.
i18next 26.4.0 installed as 1 package in 1.1 seconds, but its complete browser import measured 13.7 KB gzipped in our sandbox before any backend or framework adapter. Install it when one translation model must span several JavaScript environments; choose a narrower formatter for a small or ICU-first catalog.
We installed it
| Install | ✓ · 1.1s | 1 package on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package with exports map |
| Browser | 13.7 KB | gzipped (42.8 KB minified), bundled with esbuild |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does i18next install cleanly?
Yes. In a fresh container with an empty cache, npm install i18next finished in 1 seconds, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.
How much does i18next add to a browser bundle?
13.7 KB gzipped (42.8 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.
Does i18next work with both ESM and CommonJS?
Yes. Both import 'i18next' and require('i18next') worked in Node 22 in our run. The package is published as CommonJS with an exports map.
Does i18next include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
i18next or intl-messageformat: which should you use?
intl-messageformat: Use it when ICU MessageFormat is required and only formatting is needed. i18next 26.4.0 installed as 1 package in 1.1 seconds, but its complete browser import measured 13.7 KB gzipped in our sandbox before any backend or framework adapter.
When should you not use i18next?
A small product has a fixed catalog and can pair plain objects with Intl; our full core import was 13.7 KB gzipped before plugins
Use it if
- Translation resources must behave the same in browsers, Node services, and framework bindings
- Plural categories, context variants, nested keys, and fallback chains have outgrown plain object access
- Languages and namespaces should load on demand instead of joining the initial browser bundle
- The team or translation vendor already uses i18next JSON conventions
- A small product has a fixed catalog and can pair plain objects with Intl; our full core import was 13.7 KB gzipped before plugins
- ICU MessageFormat is a hard catalog requirement; native i18next uses different interpolation and plural conventions without an ICU plugin
- Messages must be extracted and compiled at build time; runtime key resolution needs separate extraction and checking tools
- The framework already supplies an accepted translation system; a second resource store creates two fallback and formatting policies
- Several packages cannot share separator, namespace, fallback, and interpolation settings; small differences change the resolved key
- Concurrent server requests would change one singleton language; isolate instances or users can receive another request's locale
Setup reality
We installed i18next 26.4.0 in a clean Node 22 sandbox in 1.1 seconds. The result was 1 package and 1 MB on disk, with 0 known vulnerabilities in npm audit. i18next declares 0 direct dependencies and 1 peer dependency and occupies 588 KB unpacked. The CommonJS package has an exports map; require() and ESM import both worked. TypeScript declarations are bundled.
Our esbuild import measured 42.8 KB minified and 13.7 KB gzipped. Core initialization needs no credentials or configuration file, but init() returns a promise even when resources are embedded. Wait for it before rendering. React users normally disable interpolation escaping because React already escapes text. Catalogs must use the plural suffixes produced by Intl.PluralRules rather than old generic _plural keys.
Runtime loading adds policy and packages. A backend fetches resources, a detector chooses language, and namespaces split catalogs for lazy delivery. supportedLngs prevents arbitrary detected locale codes from causing pointless requests. Decide the authoritative locale, fallback chain, missing-key reporting, and whether any backend may create missing entries. A write-enabled saveMissing setup should be disabled or tightly controlled in production.
Typed key checking requires TypeScript module augmentation for defaultNS and the resource shape. Without it, t() accepts arbitrary strings. Huge inferred resource types can slow an editor, which is where selector mode and its optimize setting help. Version 26.4.0 caches fallback resolution. Changes to other resolution options may require languageUtils.clearCache(), while function fallbacks and per-call arrays or objects bypass that cache.
Patterns
Initialize an embedded catalog initialize-resources
import i18next from 'i18next';
await i18next.init({
lng: 'en', fallbackLng: 'en',
resources: { en: { translation: { greeting: 'Hello {{name}}' } } },
});
console.log(i18next.t('greeting', { name: 'Ada' }));Await init before the first t call; an early lookup can return the untranslated key.
Choose a locale plural form translate-plural
await i18next.init({ lng: 'en', resources: { en: { translation: { item_one: '{{count}} item', item_other: '{{count}} items' } } } });
i18next.t('item', { count: 2 });count activates plural selection, and other locales can require categories that English never uses.
Fetch one namespace when needed load-namespace
import HttpBackend from 'i18next-http-backend';
await i18next.use(HttpBackend).init({ lng: 'en', backend: { loadPath: '/locales/{{lng}}/{{ns}}.json' } });
await i18next.loadNamespaces('checkout');The HTTP backend is a separate package, and rendering must wait until the requested namespace is loaded.
Wait for a language switch change-language
i18next.on('languageChanged', lng => { document.documentElement.lang = lng; });
await i18next.changeLanguage('de');A backend may fetch resources during changeLanguage, so await it before assuming the new catalog is ready.
Limit browser detection to shipped locales detect-supported-language
import LanguageDetector from 'i18next-browser-languagedetector';
await i18next.use(LanguageDetector).init({ supportedLngs: ['en','de','fr'], fallbackLng: 'en' });The detector is another dependency; supportedLngs stops an arbitrary browser locale from becoming a resource URL.
Format currency with Intl format-currency
i18next.t('total', { amount: 42.5, formatParams: { amount: { style: 'currency', currency: 'EUR' } } });The translation string must reference the value with the currency formatter, such as {{amount, currency}}.
Declare the resource type type-keys
import 'i18next';
import common from './locales/en/common.json';
declare module 'i18next' { interface CustomTypeOptions { defaultNS: 'common'; resources: { common: typeof common }; strictKeyChecks: true; } }Keep this declaration inside tsconfig's include set; otherwise t continues accepting arbitrary strings.
Create a translator per request isolate-server-request
import { createInstance } from 'i18next';
const instance = createInstance();
await instance.init({ lng: requestLocale, fallbackLng: 'en', resources });
const t = instance.t.bind(instance);Do not call changeLanguage on one shared instance while concurrent requests use different locales.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| intl-messageformat | npm | Use it when ICU MessageFormat is required and only formatting is needed |
| typesafe-i18n | npm | Use it when generated typed translation functions and compile-time checks lead the design |
| next-intl | npm | Use it for Next.js routing, server components, and ICU messages |
| i18n-js | npm | Use it for a smaller resource store without i18next's plugin system |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

