Skip to content

Releases: i18next/next-i18next

v16.4.2

Choose a tag to compare

@adrai adrai released this 23 Oct 20:05
  • Pages Router: translations load on the server again with a custom interpolation.prefix / suffix - localeStructure and the server loadPath are 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 literal public/locales/{lng}/{ns}.json, the error only showed with debug: true, and serverSideTranslations returned 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 function localePath are unchanged. Thanks @nk-batman21 (#2350)

v16.4.1

Choose a tag to compare

@adrai adrai released this 17 Oct 19:04
  • 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 of t from getT and useT and the values of IcuTrans and IcuTransWithoutContext (react-i18next 17.1.0) are type-checked against the message's arguments, with the resources file from npx i18next-cli types for literal message types. parseInterpolation: false stays the setting for i18next 26.2.0 to 26.4.x. No code changes: getT and useT return i18next's TFunction, so they get the checks as is.
  • Example app-router-icu uses parseInterpolation: 'icu' with an i18next.config.ts and a generated @types/resources.d.ts.

v16.4.0

Choose a tag to compare

@adrai adrai released this 17 Oct 17:28
  • App Router: ICU and MF2 without a 'use client' wrapper - next-i18next/icu and next-i18next/mf2 export an I18nProvider with the same props as the one from next-i18next/client, with i18next-icu or i18next-mf2 registered on the client instance. Render it from the root layout and keep use: [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 with IcuTransWithoutContext from react-i18next 17.0.21 in Server Components, parseInterpolation: false for TypeScript, extract.messageFormat in i18next-cli 1.76.0) and a new example, app-router-icu.

v16.3.2

Choose a tag to compare

@adrai adrai released this 15 Oct 14:31
  • App Router: useT no longer re-renders on every URL change — it called useParams() 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, including history.replaceState/pushState and search-param updates. Every component using useT re-rendered each time, even in localeInPath: false mode where there is no such segment. The sync now lives in a single component inside I18nProvider that renders nothing, and useT is a typed useTranslation. #2349
  • App Router: useT no longer breaks next build with cacheComponents: true — that sync component only mounts in the browser, so useParams() is no longer called during server rendering. A useT caller on a route whose params are not all covered by generateStaticParams used to fail the prerender with "useParams() in a Client Component outside of <Suspense>"; it now renders into the static shell. #2349

v16.3.1

Choose a tag to compare

@adrai adrai released this 13 Sep 15:11
  • 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, and I18nProvider pinned supportedLngs to whatever resources happened to contain, so i18next resolved every other language back to the fallback and client components stayed in the old language. supportedLngs is no longer derived from resources (pass the prop to restrict it), and each new resources prop 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. useChangeLanguage leaves that switch to the provider when the store holds nothing for the target language yet, so i18n.resolvedLanguage no 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

Choose a tag to compare

@adrai adrai released this 07 Sep 15:22
  • App Router: createServerI18next(config) — returns { getT, getResources, generateI18nStaticParams } bound to the config, so the server API no longer depends on initServerI18next having run first in the same module instance. Create it once in an i18n.server.ts and 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 reach getT uninitialized. initServerI18next and the module-level functions keep working. #2347
  • App Router: default server state is keyed on globalThis — a second copy of next-i18next/server in the same process (another bundling layer, or the CJS build next to the ESM one) now sees the same initServerI18next call instead of throwing. The "not initialized" error also names the real requirement (before the first getT() in the process) and points at createServerI18next. #2347

v16.2.0

Choose a tag to compare

@adrai adrai released this 04 Sep 10:40
  • 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 hideDefaultLocale redirects, 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's maxAge is refreshed only on change. #2346
  • App Router: persistCookie and cookieOptions — persistCookie: false makes 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, and useChangeLanguage(cookieName?, cookieOptions?) accepts the same options so both writers agree on the cookie's scope. #2346

v16.1.1

Choose a tag to compare

@adrai adrai released this 02 Sep 18:57
  • App Router: proxy rewrites and redirects keep Next's basePath — targets were built with new URL(path, req.url), which drops the base path that Next keeps inside req.nextUrl, so with basePath: '/nxt' in next.config every locale redirect, and every rewrite in hideDefaultLocale and 'internal' mode, landed outside the app and 404'd. Targets are now built from req.nextUrl.clone(), which also preserves trailingSlash, and the referer-based cookie persistence strips the base path before looking for a locale. The basePath option in the i18n config is unrelated: it is the route-folder prefix for mixed-router setups (app/app-router/[lng]/…). #2345
  • App Router: ssrBackend prop on I18nProvider — opt in to applying the backends from use during the server render pass as well. With i18nextOptions: { react: { useSuspense: true } } a namespace missing from resources suspends 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 as i18next-resources-to-backend with dynamic imports; the 16.0.11 reasoning still applies to http/locize backends. #2345

v16.1.0

Choose a tag to compare

@adrai adrai released this 02 Sep 15:51
  • App Router: localeInPath: 'internal' — a third routing mode for clean URLs and static prerendering per locale. Routes stay under app/[lng]/, but the proxy rewrites /about to /{lng}/about internally (cookie → Accept-Language → fallback), so the public URL never shows a locale while every locale is still prerendered via generateStaticParams. Explicit locale paths (/de/about) are served as-is, never redirected, so <Link> prefetches cannot flip the language cookie; switch languages with useChangeLanguage(). This is the mode for Next.js Cache Components (cacheComponents: true), where localeInPath: false cannot be used because reading headers()/cookies() makes every route dynamic. #2345
  • App Router: getT() resolves the language from next/root-params first (Next.js 16.3+) — root params are part of the route key, so unlike headers()/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 calls loadLanguages(lng) and checks hasLoadedNamespace(ns, { lng }) for the requested language instead of the instance's own. The default still preloads every supported language; set i18nextOptions: { preload: [] } to load each language on its first request, keeping memory proportional to the locales actually served. #2345
  • App Router: localeParamName option — name of the locale route segment (default 'lng'). generateI18nStaticParams() keys its params by it, so an app/[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

Choose a tag to compare

@adrai adrai released this 23 Aug 14:11
  • App Router: backends are no longer attached during SSR — I18nProvider is a Client Component, but the App Router also renders it on the server, once per request, and its useState initializer therefore built a throwaway i18next instance in Node on every render. With a backend passed via use, that instance got a Node-side backend, and backends that refresh on a timer never let it go: i18next-locize-backend defaults reloadInterval to 1 hour whenever window is 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, and partialBundledLanguages is off during the server pass, so SSR renders purely from resources. Non-backend plugins (detectors, post-processors) still apply in both passes.
  • getResources accepts 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.