अगर आज आप Cursor या Claude Code से किसी Next.js ऐप में i18n जोड़ने को कहें, तो बहुत संभव है कि आपको हर layout में middleware.ts, setRequestLocale मिलेगा, और useTranslations को एक async पेज से कॉल किया जाएगा — यह सेटअप Next.js 15 के लिए सही था लेकिन Next.js 16 पर एक जनरेशन पुराना है। globalize.now एक AI-पावर्ड लोकलाइज़ेशन इन्फ्रास्ट्रक्चर है, इसलिए यही वह लेयर है जिसे हम बारीकी से देखते हैं: रूटिंग वायरिंग बदल गई, लेकिन जो कैटलॉग यह सर्व करती है वह नहीं बदला। नीचे बताया गया है कि क्या बदला, कैसे पता करें कि आपका जेनरेटेड कोड किस दौर का है, और वे तीन फिक्स जिन्हें लागू करने में एक दोपहर लगती है, पूरी रीराइट नहीं।
इनमें से कोई भी बदलाव ब्रेकिंग नहीं है। और यही वजह है कि इसे नज़रअंदाज़ करना बहुत आसान है।
Next.js 16 में i18n के लिए क्या बदला है?
जो फ़ाइल आपकी locale तय करती है, उसका नाम बदल गया है। Next.js में middleware फ़ाइल कन्वेंशन को डेप्रिकेटेड बताया गया है और इसका नाम बदलकर proxy कर दिया गया है, जो v16.0.0 में आया, और इसकी अपनी वजह शब्दावली से जुड़ी है —
v16 में इस नाम-बदलाव के साथ दो व्यवहार संबंधी बातें भी आई हैं। Proxy डिफ़ॉल्ट रूप से Node.js रनटाइम पर चलता है, और वहाँ प्रति-फ़ाइल runtime कॉन्फ़िग ऑप्शन अब उपलब्ध नहीं है — इसे सेट करने पर Next.js एरर देता है। Proxy स्टैटिक एक्सपोर्ट पर भी सपोर्टेड नहीं है, जो मायने रखता है अगर आपकी locale negotiation ही वह इकलौती वजह है जिसके कारण आप एक्सपोर्ट नहीं कर पा रहे।
खासतौर पर locale routing के लिए, next-intl सेटअप गाइड अब इस फ़ाइल को src/proxy.ts के रूप में दिखाती है और स्पष्ट रूप से बताती है कि Next.js 16 तक इसे middleware.ts कहा जाता था। इसके भीतर का इम्पोर्ट पहले जैसा ही है:
// 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|.*\\..*).*)'
};
इस विषमता (असममिति) पर ध्यान दें, क्योंकि यह लोगों को अक्सर उलझा देती है: फ़ाइल अब proxy.ts है, जबकि इम्पोर्ट पाथ अब भी next-intl/middleware है। इम्पोर्ट को भी रीनेम कर देना एक आम गलती है, जो ज़रूरत से ज़्यादा सुधार करने जैसा है।
AI-जनरेटेड i18n कोड एक वर्ज़न पीछे क्यों आता है?
क्योंकि एक कोडिंग एजेंट वह पैटर्न प्रेडिक्ट करता है जो सबसे ज़्यादा मौजूद हो, और सबसे ज़्यादा मौजूद पैटर्न वही होता है जिसे इकट्ठा होने में सालों लगे। 2026 के अंत से पहले लिखा गया App Router locale routing पर हर ब्लॉग पोस्ट, Stack Overflow जवाब और GitHub उदाहरण middleware.ts ही कहता है। एक मॉडल जब इस विशाल डेटा को v16 की कुछ हफ़्तों पुरानी डॉक्यूमेंटेशन के मुक़ाबले तौलता है, तो वह पुराने ढाँचे को ही आत्मविश्वास से चुनता है, बिना यह बताए कि इसका नाम बदल गया है।
यह वही मेकेनिज़्म है जिसके बारे में हमने पहले भी लिखा है, जहाँ i18n सेटअप के बाद भी Cursor हार्डकोडेड स्ट्रिंग्स जोड़ता रहता है — एजेंट सांख्यिकीय रूप से सामान्य कोडबेस को ही दोहराता है, आपके कोडबेस को नहीं। यही वजह है कि इंस्ट्रक्शन फ़ाइलें GitHub Copilot के लिए इसे केवल आंशिक रूप से ठीक करती हैं: जो नियम चैट सरफ़ेस पढ़ता है, वे इनलाइन कम्प्लीशन द्वारा ज़रूरी नहीं कि पढ़े जाएँ।
इसका व्यावहारिक असर सीमित लेकिन वास्तविक है। आपका जेनरेटेड सेटअप काम करता है, इसलिए कुछ भी तेज़ी से फेल नहीं होता। फिर आपको कोई बग मिलता है, आप उसे सर्च करते हैं, और हर मौजूदा जवाब उन फ़ाइलों की बात करता है जो आपके पास ही नहीं हैं।
मुझे कैसे पता चलेगा कि मेरा जेनरेटेड सेटअप किस दौर का है?
चार grep कमांड्स। इन्हें प्रोजेक्ट रूट से चलाएँ और लगभग एक मिनट में आपको पता चल जाएगा।
# 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
1 और 2 पर हिट मिलना मतलब pre-16 स्कैफ़ोल्डिंग है। 3 पर हिट मिलना एक वास्तविक रनटाइम एरर है जो उस कॉम्पोनेंट को रेंडर करने वाली पहली रिक्वेस्ट का इंतज़ार कर रही है। 4 पर कोई हिट न मिलना मतलब आपका रिक्वेस्ट कॉन्फ़िग locale को पुराने तरीके से पढ़ रहा है।
क्या मुझे middleware.ts को proxy.ts में रीनेम करना ज़रूरी है?
तुरंत ज़रूरी नहीं, और इसे हाथ से भी नहीं करना चाहिए। Next.js एक कोडमॉड देता है जो फ़ाइल और एक्सपोर्ट की गई फंक्शन दोनों को रीनेम कर देता है:
npx @next/codemod@canary middleware-to-proxy .
यह रीनेम एक डेप्रिकेशन है, हटाना नहीं, इसलिए मौजूदा middleware.ts काम करना जारी रखता है। इसे फिर भी चलाने की वजह है मेंटेनेंस लागत: एक बार आपकी फ़ाइलों के नाम मौजूदा डॉक्यूमेंटेशन से मेल खाने लगें, तो भविष्य का हर सर्च रिज़ल्ट आपके रिपो पर लागू होता है। इसे पुराना रहने दें तो हर डीबगिंग सेशन पर आप एक छोटा-सा टैक्स देते रहते हैं, हमेशा के लिए।
क्या next-intl में setRequestLocale डेप्रिकेटेड है?
इसे लेगेसी बताया गया है, जो डेप्रिकेटेड से थोड़ा हल्का दावा है और इसे ध्यान से समझना ज़रूरी है। next-intl की डॉक्यूमेंटेशन setRequestLocale को एक ऐसे API के रूप में बताती है जो next/root-params आने तक इस्तेमाल होता था, कहती है कि यह अब भी बैकवर्ड कम्पैटिबिलिटी के लिए सपोर्टेड है, और इसके बजाय next/root-params इस्तेमाल करने की सलाह देती है।
नया तरीका हर layout और page में मैन्युअल रूप से पास करने के बजाय आपके रिक्वेस्ट कॉन्फ़िग के भीतर ही मैच की गई locale को पढ़ लेता है:
// 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 Next.js 16.3 और उसके बाद के वर्ज़न में डिफ़ॉल्ट रूप से उपलब्ध है; पुराने वर्ज़न्स पर इसे experimental.rootParams के ज़रिए इनेबल करना पड़ता है। इस तरीके से सेटअप करें तो स्टैटिक रेंडरिंग मुफ़्त में मिल जाती है, बस शर्त यह है कि आप [locale] सेगमेंट के लिए generateStaticParams को एक्सपोर्ट करते रहें।
पुराने तरीके में आपको स्टैटिक रूप से रेंडर करने वाले हर page और layout में, किसी भी अन्य next-intl कॉल से पहले, setRequestLocale को कॉल करना ज़रूरी होता था, क्योंकि Next.js layouts और pages को अलग-अलग रेंडर करता है। यह वह नियम है जिसे एक AI एजेंट पाँचवीं फ़ाइल लिखते-लिखते भूल जाता है। इस ज़रूरत को हटाने से यह पूरी श्रेणी की बग खत्म हो जाती है।
मेरे async सर्वर कॉम्पोनेंट में useTranslations क्रैश क्यों करता है?
क्योंकि hooks को async कॉम्पोनेंट्स से कॉल नहीं किया जा सकता, और useTranslations एक hook है। यह React सर्वर कॉम्पोनेंट्स की एक सीमा है, न कि next-intl की कोई खासियत, और next-intl का जवाब है awaitable फंक्शन्स का एक समांतर सेट:
// 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 और getLocale भी इसी पैटर्न का पालन करते हैं। दूसरा उदाहरण ध्यान देने लायक है: कोई इंटरैक्टिव फ़ीचर्स न रखने वाला नॉन-async कॉम्पोनेंट एक शेयर्ड कॉम्पोनेंट होता है, और next-intl यह तय करता है कि इसे सही तरह से कैसे इम्प्लीमेंट करना है, यह इस पर निर्भर करता है कि यह सर्वर पर रेंडर होता है या क्लाइंट पर। तो एक सर्वर कॉम्पोनेंट में useTranslations इस्तेमाल करना गलती नहीं है — इसे किसी async कॉम्पोनेंट से कॉल करना गलती है।
मुझे NextIntlClientProvider कॉन्टेक्स्ट एरर क्यों मिलता है?
next-intl के ट्रबलशूटिंग नोट्स दो कारण बताते हैं, और दोनों के लिए अलग-अलग फिक्स चाहिए होते हैं। या तो कॉम्पोनेंट वास्तव में क्लाइंट पर चल रहा है और उसके ऊपर कोई provider नहीं है, ऐसी स्थिति में इसे रैप करें और ज़रूरी मैसेजेस पास करें, या यह किसी क्लाइंट मॉड्यूल ग्राफ़ में चला गया जबकि आपने सर्वर रेंडरिंग की उम्मीद की थी, ऐसी स्थिति में इसे किसी सर्वर कॉम्पोनेंट के भीतर इम्पोर्ट करने के बजाय children के ज़रिए पास करें।
दूसरी स्थिति वही है जिससे AI-जनरेटेड ऐप्स अक्सर टकराते हैं, क्योंकि एजेंट्स इंटरैक्टिविटी को काम करने के लिए 'use client' को खुलेआम लगा देते हैं, और यह डायरेक्टिव इम्पोर्ट ग्राफ़ में नीचे तक फैल जाता है। पसंदीदा तरीका यह है कि अनुवाद सर्वर पर किया जाए और तैयार स्ट्रिंग्स को बाउंड्री के आर-पार पास किया जाए:
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>;
}
अगर किसी कॉम्पोनेंट को वास्तव में क्लाइंट पर मैसेजेस की ज़रूरत है, तो आप हर मैसेज को ब्राउज़र में भेजने के बजाय सिर्फ उस सबट्री के इर्द-गिर्द provider को सीमित कर सकते हैं — रूट provider पर messages={null} इस्तेमाल करने से कुछ भी पास नहीं होता।
इसमें से क्या नहीं बदलता?
आपकी locale files। ऊपर बताया गया हर फिक्स रूटिंग और रेंडरिंग वायरिंग से जुड़ा है; आपका ऐप जो JSON लोड करता है, वह इस सबसे अछूता रहता है। और यही वह बात है जो याद रखने लायक है, क्योंकि यह वायरिंग एक दोपहर में हो जाने वाला काम है जो साल में एक बार बदलता है, जबकि कैटलॉग वह चीज़ है जो हर हफ़्ते नए UI के साथ पुरानी होती जाती है।
यही वह विभाजन है जिसके लिए हम बनाते हैं। next-intl और उसके जैसी लाइब्रेरीज़ रनटाइम पर अनुवाद सर्व करती हैं — हम उनसे प्रतिस्पर्धा नहीं करते, और अगर आप अब भी इनमें से किसी एक को चुन रहे हैं, तो next-intl बनाम react-i18next बनाम Lingui में सभी ट्रेड-ऑफ़्स समझाए गए हैं। globalize.now एक लेयर ऊपर बैठता है, वे keys और locale files बनाता है जिन्हें ये रनटाइम्स पढ़ते हैं, और इसी वजह से Next.js इंटीग्रेशन इस बात से बेपरवाह है कि आपकी negotiation middleware.ts में रहती है या proxy.ts में।
कन्वर्शन एक बार होता है और ऐप के भीतर ही होता है: आप रिपॉज़िटरी कनेक्ट करते हैं, globalize.now एक बार कोडबेस को कन्वर्ट करता है और कैटलॉग के साथ एक पुल रिक्वेस्ट खोलता है। इसके बाद, जैसे-जैसे नए कैटलॉग यूनिट्स सामने आते हैं, पुश जॉब्स उनका अनुवाद करती हैं — ट्रांसलेशन फ़ाइलें आउट ऑफ़ सिंक क्यों होती रहती हैं में वही फेलियर मोड बताया गया है, और यही वजह है कि इस गैप को अपने तीसरे locale से पहले पाटना बेहतर है, बाद में नहीं।
अगर आपके प्रोजेक्ट में पहले से हाथ से बनाया गया next-intl सेटअप है, तो हमने ठीक-ठीक लिखा है कि हम क्या छूते हैं और क्या नहीं। अगर नहीं है, तो Cursor वॉकथ्रू अंदर आने का सबसे छोटा रास्ता है — बस इस चेतावनी के साथ कि इसकी जेनरेट की गई फ़ाइल पुराने कन्वेंशन के नाम पर है, इसलिए बाद में कोडमॉड चला लें।
कहाँ से शुरुआत करें
चार grep कमांड्स चलाएँ। अगर पहले दो पर हिट मिलती है, तो कोडमॉड चलाएँ, अपने रिक्वेस्ट कॉन्फ़िग को next/root-params पर ले जाएँ, और आप अपडेटेड हो जाएँगे। फिर locale files पर ध्यान दें, क्योंकि यही वह हिस्सा है जो अगले महीने भी ड्रिफ्ट करता रहेगा। अगर आप एक AI-निर्मित ऐप शिप कर रहे हैं और चाहते हैं कि कैटलॉग को मैनेज किया जाए, न कि बनाए रखा जाए, तो वाइब कोडर्स पेज ओवरव्यू है और प्राइसिंग अपने अलग पेज पर है।
globalize.now आपके ऐप्लिकेशन की हार्डकोडेड कॉपी को अनुवाद-तैयार locale फ़ाइलों में बदल देता है और जैसे-जैसे आप नई रिलीज़ करते हैं, उन्हें अपडेट रखता है।
globalize.now को निःशुल्क आजमाएं