In-depth Next.js guides covering App Router, RSC, ISR, and deployment. Get code examples, optimization checklists, and prompts to accelerate development.
If you upgraded to Next.js 16 and suddenly your locale routing stopped working, you're in the right place. No redirects to /en, wrong locale in server components, or this in your logs:
code
Error: Unable to find `next-intl` locale because the middleware didn't run on this request.
See https://next-intl.dev/docs/routing/middleware#unable-to-find-locale
The cause is a single breaking change in Next.js 16: middleware.ts was renamed to proxy.ts. Your next-intl setup is fine — the file Next.js is looking for just no longer exists.
This guide covers the exact fix, why Next.js made this change, and two additional next-intl 4.0 breaking changes that tend to surface in the same upgrade.
Why Next.js 16 Renamed middleware.ts to proxy.ts
Next.js renamed the file to clarify what it actually does. From the release notes:
"proxy.ts replaces middleware.ts and makes the app's network boundary explicit. proxy.ts runs on the Node.js runtime."
The old name caused confusion with Express-style middleware and made the feature sound more general-purpose than it is. The new name makes it clear: this file sits at the network boundary and proxies requests before they reach your app. The logic inside hasn't changed at all — it's a rename, not a rewrite.
Next.js 16 still accepts middleware.ts for now but treats it as deprecated and logs a warning:
code
You are using the `middleware` file convention, which is deprecated and has been renamed to `proxy`.
When next-intl's locale negotiation depends on that file running — and it does — a deprecated or ignored file means no locale gets set, which is why you see the "middleware didn't run" error.
The Fix: Three Steps
Step 1: Rename the file
code
src/middleware.ts → src/proxy.ts
That's it for most next-intl setups. If you're using createMiddleware with a default export, the file contents stay identical:
The next-intl docs confirm it: "proxy.ts was called middleware.ts up until Next.js 16." No changes to imports, no changes to createMiddleware, no changes to your routing config.
Step 2: Rename the exported function (custom setups only)
If you wrote a custom wrapper around createMiddleware rather than using a default export, Next.js 16 expects the named export to be called proxy, not middleware:
If you want to automate this, Next.js ships a codemod that handles both the file rename and the function rename in one command:
bash
npx @next/codemod@canary middleware-to-proxy .
Step 3: Verify nothing else needs changing
No changes required to next.config.ts, src/i18n/routing.ts, src/i18n/navigation.ts, or src/i18n/request.ts. The proxy file is the only thing that moves.
Two More Breaking Changes Worth Checking
If you're upgrading to Next.js 16 you're likely also moving from next-intl v3 to v4. Two other changes in that upgrade break silently in ways that look similar to the proxy issue.
NextIntlClientProvider is now required
In next-intl v3, NextIntlClientProvider was optional in many setups. In v4, if any client component in your tree calls useTranslations, a provider must exist above it or you'll hit:
code
Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found.
The fix is straightforward — add it to your locale layout with no props:
In v4, NextIntlClientProvider automatically inherits messages and formats from your i18n/request.ts — you don't need to pass them as props. If you were previously passing them manually, you can remove those props entirely.
getRequestConfig must return locale explicitly
In v3, returning locale from getRequestConfig was optional. In v4 it's required. If you're missing it, next-intl can't determine which locale is active, which compounds the proxy issue:
The locale field in the return object is what next-intl uses downstream. Without it, you'll see the same "Unable to find next-intl locale" error even after correctly renaming middleware.ts to proxy.ts.
Common Mistakes After Renaming
Renaming the file but not the exported function. If you wrapped createMiddleware with a named export, the function must be renamed from middleware to proxy. Renaming only the file leaves the named export broken.
Assuming v3 behavior for NextIntlClientProvider. In next-intl v3, the provider was optional in many setups. In v4, any client component using useTranslations requires it. This breaks silently — no build error, just a runtime crash.
Fixing the proxy rename but missing locale in getRequestConfig. Both are required in v4. Fixing one without the other still produces the "Unable to find next-intl locale" error.
Wrapping Up
The Next.js 16 upgrade breaks next-intl locale routing through a combination of three changes: the middleware.ts → proxy.ts rename (Next.js-level), the required NextIntlClientProvider wrapper (next-intl 4.0), and the mandatory locale return from getRequestConfig (next-intl 4.0). Any one of them is enough to produce the "Unable to find next-intl locale" error, which is why the upgrade can feel hard to diagnose.
The fixes are all mechanical — rename a file, add a provider, add one field to a return object. Nothing about how next-intl works has changed, and your routing configuration stays exactly as it was.