Palūdziet Cursor vai Claude Code pievienot i18n Next.js lietotnei šodien, un ļoti iespējams, ka iegūsiet middleware.ts, setRequestLocale katrā layout un useTranslations, izsauktu no async lapas — iestatījumu, kas bija pareizs Next.js 15, bet Next.js 16 jau ir par vienu paaudzi novecojis. globalize.now ir AI darbināta lokalizācijas infrastruktūra, tāpēc šis ir slānis, ko mēs sekojam: maršrutēšanas savienojumi mainījās, bet katalogs, ko tie apkalpo, palika nemainīgs. Zemāk ir aprakstīts, kas mainījās, kā noteikt, no kura laikmeta nāk jūsu ģenerētais kods, un trīs labojumi, kas prasa pēcpusdienu, nevis pārrakstīšanu.

Neviena no izmaiņām nav lūzumaina (breaking change). Tieši tāpēc to ir viegli palaist garām.

Kas Next.js 16 mainījās attiecībā uz i18n?

Fails, kas apstrādā lokāles noteikšanu, mainīja savu nosaukumu. Next.js dokumentācijā middleware faila konvencija ir atzīmēta kā novecojusi un pārdēvēta par proxy, sākot ar v16.0.0, un skaidrojums ir saistīts ar terminoloģiju — vārds „middleware“ pastāvīgi tika sajaukts ar Express middleware, tāpēc funkcija tika pārdēvēta, precīzāk aprakstot to tīkla robežu, ko tā faktiski attēlo.

Kopā ar pārdēvēšanu v16 nāk arī divas uzvedības izmaiņas. Proxy pēc noklusējuma izmanto Node.js izpildlaiku, un atsevišķa faila runtime konfigurācijas opcija tur vairs nav pieejama — mēģinot to iestatīt, Next.js izmet kļūdu. Turklāt proxy netiek atbalstīts statiskajā eksportā, kas ir svarīgi, ja lokāles noteikšana bija vienīgais iemesls, kāpēc jūs neizmantojāt eksportu.

Runājot konkrēti par lokāles maršrutēšanu, next-intl iestatījumu rokasgrāmata tagad rāda failu kā src/proxy.ts un skaidri norāda, ka līdz pat Next.js 16 tas tika saukts middleware.ts. Imports faila iekšienē paliek nemainīgs:

// src/proxy.ts
import createMiddleware from 'next-intl/middleware';
import {routing} from './i18n/routing';

export default createMiddleware(routing);

export const config = {
  matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)'
};

Ievērojiet asimetriju, jo tā mēdz mulsināt: fails tagad ir proxy.ts, bet importa ceļš joprojām ir next-intl/middleware. Importa pārdēvēšana ir izplatīta pārmērīga korekcija.

Kāpēc AI ģenerētais i18n kods nāk par vienu versiju atpaliekot?

Tāpēc, ka kodēšanas aģents prognozē visbiežāk sastopamo paraugu, bet visbiežāk sastopamais paraugs ir tas, kuram bija gadi laika, lai uzkrātos. Katrs emuāra ieraksts, Stack Overflow atbilde un GitHub piemērs par App Router lokāles maršrutēšanu, kas rakstīts pirms 2026. gada beigām, izmanto middleware.ts. Modelis, kas salīdzina šo korpusu ar dažu nedēļu veco v16 dokumentāciju, pārliecinoši izvēlēsies veco veidolu, nebrīdinot par to, ka nosaukums ir mainīts.

Šis ir tas pats mehānisms, kas stāv aiz problēmas, par kuru esam rakstījuši jau agrāk, kad Cursor turpina pievienot burtiskus tekstus pēc i18n iestatīšanas — aģents atkārto statistiski normālu kodabāzi, nevis jūsējo. Tas ir arī iemesls, kāpēc instrukciju faili GitHub Copilot gadījumā šo problēmu novērš tikai daļēji: noteikumi, ko lasa čata saskarne, ne vienmēr tiek ievēroti inline pabeigšanas funkcijā.

Praktiskās sekas ir šauras, bet reālas. Jūsu ģenerētie iestatījumi darbojas, tāpēc nekas skaļi nesalūzt. Tad jūs sastopaties ar kļūdu, meklējat to, un visas aktuālās atbildes apraksta failus, kādu jums nav.

Kā noteikt, no kura laikmeta nāk mans ģenerētais iestatījums?

Četri grep pieprasījumi. Palaidiet tos no projekta saknes mapes, un pēc apmēram minūtes jūs zināsiet atbildi.

# 1. Pre-16 locale negotiation file
ls middleware.ts src/middleware.ts 2>/dev/null

# 2. Legacy static-rendering API
grep -rn "setRequestLocale" app src 2>/dev/null

# 3. Hooks called inside async components
grep -rn -B3 "useTranslations" app | grep -n "async function"

# 4. Which next-intl era the config reads from
grep -rn "root-params\|await params" src/i18n/request.ts 2>/dev/null

Atradumi 1. un 2. gadījumā nozīmē, ka tas ir pirms 16 versijas veidols. Atradums 3. gadījumā ir reāla izpildlaika kļūda, kas gaida pirmo pieprasījumu, kas renderēs šo komponenti. Ja 4. gadījumā nav atraduma, tas nozīmē, ka jūsu pieprasījuma konfigurācija joprojām nolasa lokāli vecajā veidā.

Vai man jāpārdēvē middleware.ts par proxy.ts?

Nav steidzami, un jums to nevajadzētu darīt manuāli. Next.js piedāvā codemod, kas pārdēvē gan failu, gan eksportēto funkciju:

npx @next/codemod@canary middleware-to-proxy .

Pārdēvēšana ir novecošanas process, nevis izņemšana, tāpēc esošs middleware.ts turpina darboties. Iemesls to tomēr veikt ir uzturēšanas izmaksas: kad jūsu failu nosaukumi atbilst aktuālajai dokumentācijai, katrs turpmākais meklēšanas rezultāts attiecas uz jūsu repozitoriju. Ja atstāsiet to novecojušu, jūs maksāsiet nelielu „nodokli“ katrā atkļūdošanas sesijā — mūžīgi.

Vai setRequestLocale ir novecojis (deprecated) next-intl?

Tas ir apzīmēts kā legacy — tas ir maigāks apgalvojums nekā „novecojis“ un ir vērts izlasīt precīzi. next-intl dokumentācija apraksta setRequestLocale kā API, kas pastāvēja, līdz tika ieviests next/root-params, un norāda, ka tas joprojām tiek atbalstīts atpakaļejošas saderības dēļ, taču ieteikums ir izmantot next/root-params.

Jaunākais veidols nolasa saskaņoto lokāli tieši pieprasījuma konfigurācijā, nevis manuāli pārnesot to caur katru layout un lapu:

// src/i18n/request.ts
import * as rootParams from 'next/root-params';
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';
import {hasLocale} from 'next-intl';
import {routing} from './routing';

export default getRequestConfig(async ({locale}) => {
  if (!locale) {
    const paramValue = await rootParams.locale();
    if (hasLocale(routing.locales, paramValue)) {
      locale = paramValue;
    } else {
      notFound();
    }
  }

  return {locale};
});

next/root-params ir pieejams pēc noklusējuma Next.js 16.3 versijā un jaunākā, savukārt agrākās versijās tas ir jāiespējo caur experimental.rootParams. Ja seko šim iestatījumam, statiskā renderēšana darbojas automātiski, ja vien joprojām eksportējat generateStaticParams segmentam [locale].

Vecā pieeja pieprasīja izsaukt setRequestLocale katrā lapā un layout, kuru vēlējāties renderēt statiski, pirms jebkura cita next-intl izsaukuma, jo Next.js renderē layout un lapas neatkarīgi. Tas ir noteikums, kuru AI aģents aizmirst līdz piektajam uzrakstītajam failam. Šī prasība tagad ir zudusi, un līdz ar to arī visa šī kļūdu kategorija.

Kāpēc useTranslations izraisa kļūmi manā asinhronajā Server Component?

Tāpēc, ka hooks nevar izsaukt no async komponentēm, un useTranslations ir hook. Šis ir React Server Components ierobežojums, nevis next-intl savdabība, un next-intl risinājums ir paralēls gaidāmu (awaitable) funkciju kopums:

// Async component — await the server API
import {getTranslations} from 'next-intl/server';

export default async function ProfilePage() {
  const user = await fetchUser();
  const t = await getTranslations('ProfilePage');
  return <h1>{t('title', {username: user.name})}</h1>;
}
// Non-async component — the hook is correct here
import {useTranslations} from 'next-intl';

export default function UserDetails({user}) {
  const t = useTranslations('UserProfile');
  return <h2>{t('title')}</h2>;
}

getFormatter, getNow, getTimeZone, getMessages un getLocale seko tam pašam principam. Otro piemēru ir vērts aplūkot rūpīgāk: neasinhrona komponente bez interaktīvām funkcijām ir koplietota komponente, un next-intl izvēlas pareizo implementāciju atkarībā no tā, vai tā renderējas serverī vai klientā. Tātad useTranslations izsaukšana servera komponentē nav kļūda — kļūda ir to izsaukt no async komponentes.

Kāpēc man rodas NextIntlClientProvider konteksta kļūda?

next-intl problēmu risināšanas piezīmes min divus cēloņus, un tiem ir pretēji risinājumi. Vai nu komponente patiešām darbojas klienta pusē bez sniedzēja (provider) virs tās — tādā gadījumā to jāietin un jāpadod nepieciešamie ziņojumi — vai arī tā nokļuvusi klienta moduļu grafā, lai gan bija paredzēta servera renderēšanai — tādā gadījumā tā jāpadod caur children no servera komponentes, nevis jāimportē tajā tieši.

Otrais gadījums ir tas, ar ko sastopas AI ģenerētas lietotnes, jo aģenti brīvi izmanto 'use client', lai nodrošinātu interaktivitāti, un šī direktīva izplatās tālāk pa importa grafu. Ieteicamais paraugs ir tulkot servera pusē un padot gatavus tekstus pāri robežai:

import {useTranslations} from 'next-intl';
import Expandable from './Expandable'; // 'use client'

export default function FAQEntry() {
  const t = useTranslations('FAQEntry');
  return <Expandable title={t('title')}>{t('description')}</Expandable>;
}

Ja kādai komponentei patiešām nepieciešami ziņojumi klienta pusē, varat ierobežot sniedzēju (provider) tikai attiecīgajai apakškokam, nevis nosūtīt visus ziņojumus uz pārlūku — messages={null} saknes sniedzējā nenosūta nevienu.

Ko no visa šī nekas neietekmē?

Jūsu lokalizācijas failus. Katrs iepriekš minētais labojums attiecas uz maršrutēšanas un renderēšanas savienojumiem — JSON, ko jūsu lietotne ielādē, ar to visu netiek skarts. Un tieši to ir vērts atcerēties, jo savienojumu labošana ir pēcpusdienas darbs, kas mainās reizi gadā, savukārt katalogs ir tas, kas nolietojas katru nedēļu, ejot līdzi jaunam UI.

Tas ir tas dalījums, uz kuru mēs balstāmies. next-intl un tā līdzinieki apkalpo tulkojumus izpildlaikā — mēs ar tiem nekonkurējam, un, ja joprojām izvēlaties starp tiem, next-intl salīdzinājumā ar react-i18next un Lingui atklāj kompromisus. globalize.now atrodas vienu slāni augstāk — tas izveido atslēgas un lokalizācijas failus, ko šie izpildlaiki nolasa, un tieši tāpēc Next.js integrācija ir vienaldzīga pret to, vai jūsu lokāles noteikšana notiek middleware.ts vai proxy.ts.

Konvertēšana ir vienreizēja un notiek lietotnē: jūs savienojat repozitoriju, globalize.now vienu reizi konvertē kodabāzi un atver pull request ar katalogu. Pēc tam nosūtīšanas uzdevumi tulko jaunās kataloga vienības, tām parādoties — tas ir tieši tas atteices scenārijs, kas aprakstīts kāpēc tulkojumu faili pastāvīgi izkļūst no sinhronizācijas, un tieši tāpēc šo plaisu ir vērts aizvērt pirms trešās valodas, nevis pēc tam.

Ja jūsu projektam jau ir manuāli izveidots next-intl iestatījums, esam rakstījuši tieši par to, ko mēs skaram un ko neskaram. Ja tāda vēl nav, Cursor ceļvedis ir īsākais ceļš — ar atrunu, ka tā ģenerētais fails saglabā veco konvencijas nosaukumu, tāpēc pēc tam palaidiet codemod.

No kā sākt

Palaidiet četrus grep pieprasījumus. Ja iegūstat atradumus pirmajos divos, palaidiet codemod, pārceliet savu pieprasījuma konfigurāciju uz next/root-params, un jūs esat aktuāli. Tad pievērsieties lokalizācijas failiem, jo tieši tā ir daļa, kas nākamajā mēnesī joprojām turpinās novecot. Ja veidojat AI būvētu lietotni un vēlaties, lai katalogs tiktu apkalpots, nevis manuāli uzturēts, vibe kodētāju lapa ir pārskats, bet cenas ir atsevišķā lapā.

globalize.now pārvērš burtiski ierakstītus lietotnes tekstus tulkošanai gatavos lokalizācijas failos un pastāvīgi tos atjaunina līdz ar katru jauno versiju.

Izmēģiniet globalize.now bez maksas