Repository navigation
Releases: i18next/next-i18next
Releases · i18next/next-i18next
Release list
v16.4.2
- Pages Router: translations load on the server again with a custom
interpolation.prefix/suffix-localeStructureand the serverloadPathare built from the configured prefix and suffix (e.g.{lng}/{ns}), but the file loader that replaced i18next-fs-backend in 16.0.0 only replaced{{lng}}and{{ns}}. It tried to read a literalpublic/locales/{lng}/{ns}.json, the error only showed withdebug: true, andserverSideTranslationsreturned empty namespaces, so pages rendered the raw keys. The loader now uses the same prefix and suffix as the config. The default config and a functionlocalePathare unchanged. Thanks @nk-batman21 (#2350)
v16.4.1
- Docs: typed ICU and MF2 arguments - the README section "ICU and MF2 with the App Router" now describes
parseInterpolation: 'icu'/'mf2'(i18next 26.5.0): the options oftfromgetTanduseTand thevaluesofIcuTransandIcuTransWithoutContext(react-i18next 17.1.0) are type-checked against the message's arguments, with the resources file fromnpx i18next-cli typesfor literal message types.parseInterpolation: falsestays the setting for i18next 26.2.0 to 26.4.x. No code changes:getTanduseTreturn i18next'sTFunction, so they get the checks as is. - Example
app-router-icuusesparseInterpolation: 'icu'with ani18next.config.tsand a generated@types/resources.d.ts.
v16.4.0
- App Router: ICU and MF2 without a
'use client'wrapper -next-i18next/icuandnext-i18next/mf2export anI18nProviderwith the same props as the one fromnext-i18next/client, with i18next-icu or i18next-mf2 registered on the client instance. Render it from the root layout and keepuse: [ICU](or[MF2]) in the server config. A Server Component can not pass a plugin class to a Client Component, so<I18nProvider use={[ICU]}>in a layout failed the build with "Functions cannot be passed directly to Client Components", and ICU needed a hand-written'use client'wrapper around the provider. Both plugins are optional peer dependencies: apps that never import these entry points do not resolve or bundle them. New README section "ICU and MF2 with the App Router" (setup,getT/useT, rich text withIcuTransWithoutContextfrom react-i18next 17.0.21 in Server Components,parseInterpolation: falsefor TypeScript,extract.messageFormatin i18next-cli 1.76.0) and a new example,app-router-icu.
v16.3.2
- App Router:
useTno longer re-renders on every URL change — it calleduseParams()to sync the language from a[lng]/[locale]segment, and that subscribes the caller to the router's params, which Next replaces on every URL change, includinghistory.replaceState/pushStateand search-param updates. Every component usinguseTre-rendered each time, even inlocaleInPath: falsemode where there is no such segment. The sync now lives in a single component insideI18nProviderthat renders nothing, anduseTis a typeduseTranslation. #2349 - App Router:
useTno longer breaksnext buildwithcacheComponents: true— that sync component only mounts in the browser, souseParams()is no longer called during server rendering. AuseTcaller on a route whose params are not all covered bygenerateStaticParamsused to fail the prerender with "useParams()in a Client Component outside of<Suspense>"; it now renders into the static shell. #2349
v16.3.1
- App Router: changing the language on the client works with
i18nextOptions: { preload: [] }— with languages loaded on demand the server ships only the ones it has loaded, andI18nProviderpinnedsupportedLngsto whateverresourceshappened to contain, so i18next resolved every other language back to the fallback and client components stayed in the old language.supportedLngsis no longer derived fromresources(pass the prop to restrict it), and each newresourcesprop is merged into the client instance —router.refresh()delivers the language the server just loaded — re-resolving the language when its bundles only just arrived.useChangeLanguageleaves that switch to the provider when the store holds nothing for the target language yet, soi18n.resolvedLanguageno longer lags a switch behind in client components (react-i18next 17.0.14 fixes the same staleness at its source, for the case where an app switches the language itself). #2348
v16.3.0
- App Router:
createServerI18next(config)— returns{ getT, getResources, generateI18nStaticParams }bound to the config, so the server API no longer depends oninitServerI18nexthaving run first in the same module instance. Create it once in ani18n.server.tsand import from there: every caller then imports the module that holds the config, and neither module evaluation order nor a second bundling layer (Route Handlers live in their own) can reachgetTuninitialized.initServerI18nextand the module-level functions keep working. #2347 - App Router: default server state is keyed on
globalThis— a second copy ofnext-i18next/serverin the same process (another bundling layer, or the CJS build next to the ESM one) now sees the sameinitServerI18nextcall instead of throwing. The "not initialized" error also names the real requirement (before the firstgetT()in the process) and points atcreateServerI18next. #2347
v16.2.0
- App Router: the proxy only writes the language cookie when the language changed — every response used to re-set the cookie: internal-mode rewrites on every request, the default and
hideDefaultLocaleredirects, and the referer branch on every navigation from a locale-prefixed page. Next merges a cookie set in middleware into the request's mutable cookies and treats any modified cookie as a revalidation, so every Server Action (its POST carries the page as referer) made the client router drop its cache and refetch the page, resetting remounted forms along the way. The write is now skipped when the incoming cookie already holds the resolved language. Trade-off: the cookie'smaxAgeis refreshed only on change. #2346 - App Router:
persistCookieandcookieOptions—persistCookie: falsemakes the proxy read the language cookie without ever writing it, for apps where another system owns it.cookieOptions(domain,secure,sameSite,path,maxAge) is applied on top of the defaults when the proxy does write, anduseChangeLanguage(cookieName?, cookieOptions?)accepts the same options so both writers agree on the cookie's scope. #2346
v16.1.1
- App Router: proxy rewrites and redirects keep Next's
basePath— targets were built withnew URL(path, req.url), which drops the base path that Next keeps insidereq.nextUrl, so withbasePath: '/nxt'innext.configevery locale redirect, and every rewrite inhideDefaultLocaleand'internal'mode, landed outside the app and 404'd. Targets are now built fromreq.nextUrl.clone(), which also preservestrailingSlash, and the referer-based cookie persistence strips the base path before looking for a locale. ThebasePathoption in the i18n config is unrelated: it is the route-folder prefix for mixed-router setups (app/app-router/[lng]/…). #2345 - App Router:
ssrBackendprop onI18nProvider— opt in to applying the backends fromuseduring the server render pass as well. Withi18nextOptions: { react: { useSuspense: true } }a namespace missing fromresourcessuspends into the nearest<Suspense>boundary and streams in, so only the namespaces a route renders need to be in the HTML and the rest load on demand without hydration mismatches. Only for timer-free backends such asi18next-resources-to-backendwith dynamic imports; the 16.0.11 reasoning still applies to http/locize backends. #2345
v16.1.0
- App Router:
localeInPath: 'internal'— a third routing mode for clean URLs and static prerendering per locale. Routes stay underapp/[lng]/, but the proxy rewrites/aboutto/{lng}/aboutinternally (cookie → Accept-Language → fallback), so the public URL never shows a locale while every locale is still prerendered viagenerateStaticParams. Explicit locale paths (/de/about) are served as-is, never redirected, so<Link>prefetches cannot flip the language cookie; switch languages withuseChangeLanguage(). This is the mode for Next.js Cache Components (cacheComponents: true), wherelocaleInPath: falsecannot be used because readingheaders()/cookies()makes every route dynamic. #2345 - App Router:
getT()resolves the language fromnext/root-paramsfirst (Next.js 16.3+) — root params are part of the route key, so unlikeheaders()/cookies()they do not opt a route out of prerendering.await getT('home')in a prerendered[lng]route no longer needs{ lng }threaded through every call site. Falls back to header/cookie detection on older Next.js, in Route Handlers and Server Actions (which have no root params), or when the root layout has no locale segment. #2345 - App Router: languages load on demand —
getT()now callsloadLanguages(lng)and checkshasLoadedNamespace(ns, { lng })for the requested language instead of the instance's own. The default still preloads every supported language; seti18nextOptions: { preload: [] }to load each language on its first request, keeping memory proportional to the locales actually served. #2345 - App Router:
localeParamNameoption — name of the locale route segment (default'lng').generateI18nStaticParams()keys its params by it, so anapp/[locale]segment now prerenders (previously the returned{ lng }silently matched nothing); root-param detection uses the same name.generateI18nStaticParams<'locale'>()narrows the return type. #2345
v16.0.11
- App Router: backends are no longer attached during SSR —
I18nProvideris a Client Component, but the App Router also renders it on the server, once per request, and itsuseStateinitializer therefore built a throwaway i18next instance in Node on every render. With a backend passed viause, that instance got a Node-side backend, and backends that refresh on a timer never let it go:i18next-locize-backenddefaultsreloadIntervalto 1 hour wheneverwindowis undefined, so every render left behind a live timer that kept refetching its namespaces forever. In production this showed up as a request floor that ratcheted upwards between deploys (measured on one app: ~90 requests/hour climbing to ~8,500 before a pod restart, ~190k downloads/day against a ~10k baseline). Backend plugins, including the built-in fetch loader, are now applied only in the browser, andpartialBundledLanguagesis off during the server pass, so SSR renders purely fromresources. Non-backend plugins (detectors, post-processors) still apply in both passes. getResourcesaccepts a language filter —getResources(i18n, namespaces?, languages?). The shared server instance preloads every supported language, so the default return value serialized all of them into the HTML; on a four-language app that was ~440 KB per page. Pass[lng, fallbackLng]to ship only what the client renders.