UNPKG

next-i18n-router

Version:

Next.js App Router internationalized routing and locale detection.

239 lines (156 loc) 9.7 kB
<div align="center"> <a href="https://i18nexus.com" target="_blank"> <img src="./assets/sponsor.png" alt="i18nexus Logo" width="350" /> </a> </div> # next-i18n-router **Adds internationalized routing for Next.js apps that use the App Router** ## App Router internationalized routing 🎉 ### Why is this library needed? With the release of the App Router, internationalized routing has been removed as a built-in Next.js feature. This library adds back internationalized routing in addition to locale detection and optional cookie usage to set a user's preferred language. This library can be used with any of the popular Javascript i18n libraries such as `react-i18next` and `react-intl`. Tutorials and examples can be found [here](#usage-with-popular-i18n-libraries). ## Installation ```sh npm install next-i18n-router ``` ## Example Usage First, nest all pages and layouts inside of a dynamic segment named `[locale]`: ``` └── app └── [locale] ├── layout.js └── page.js ``` Create a file called `i18nConfig.js` at the root of your project to store your config: ```js const i18nConfig = { locales: ['en', 'de', 'ja'], defaultLocale: 'en' }; module.exports = i18nConfig; ``` Create a `proxy.js` file (or `middleware.js` if you're using Next.js 15 or earlier) in the root of your project. If your app uses a `/src` directory, put it in `/src`. This is where `i18nRouter` will be used to handle internationalized redirects and rewrites: ```js import { i18nRouter } from 'next-i18n-router'; import i18nConfig from './i18nConfig'; // name this function "middleware" in Next 15 or earlier export function proxy(request) { return i18nRouter(request, i18nConfig); } // only applies this logic to files in the app directory export const config = { matcher: '/((?!api|static|.*\\..*|_next).*)' }; ``` In your root layout, add a `notFound` redirect for any unsupported locales: ```js ... import { notFound } from 'next/navigation'; ... export default function RootLayout({ children, params: { locale } }) { if (!i18nConfig.locales.includes(locale)) { notFound(); } return ( ... ); } ``` You now have internationalized routing! ## Config Options | Option | Default value | Type | Required? | | ----------------- | --------------- | --------------------------------- | --------- | | `locales` | | string[] | &#10004; | | `defaultLocale` | | string | &#10004; | | `prefixDefault` | `false` | boolean | | | `localeDetector` | (See below) | function \| false | | | `localeCookie` | `'NEXT_LOCALE'` | string | | | `noPrefix` | `false` | boolean | | | `serverSetCookie` | `'always'` | "always" \| "if-empty" \| "never" | | | `cookieOptions` | (See below) | object | | | `basePath` | `''` | string | | ## Locale Path Prefixing By default, the `defaultLocale`'s path is not prefixed with the locale. For example, if `defaultLocale` is set to `en` and `locales` is set to `['en', 'de']`, the paths will appear as follows: **English**: `/products` **German**: `/de/products` To also include your default locale in the path, set the `prefixDefault` config option to `true`. To hide all locales from the path, set the `noPrefix` config option to `true`. ## Locale Detection By default, this library parses the request's `accept-language` header and determines which of your `locales` is preferred using `@formatjs/intl-localematcher`. This logic can be disabled or customized using the `localeDetector` config option (below). Using the `accept-language` header is the recommended strategy outlined in the [Next.js docs](https://nextjs.org/docs/app/building-your-application/routing/internationalization) and is similar to the previous Pages Router implementation. ### Custom Locale Detection (optional) If you would prefer to handle locale detection yourself, you can set the `localeDetector` option with your own locale detection function: ```js const i18nConfig = { locales: ['en', 'de', 'ja'], defaultLocale: 'en', localeDetector: (request, config) => { // your custom locale detection logic return 'the-locale'; } }; module.exports = i18nConfig; ``` You can also set the `localeDetector` option to `false` if you wish to opt out of any locale detection. ### Locale Cookie (optional) You can override the `localeDetector` using the `NEXT_LOCALE=the-locale` cookie. For example, you can set this cookie when a user opts to change to a different language. When they return to your site, their preferred language will already be set. If you would prefer to use a different cookie key other than `NEXT_LOCALE`, you can set the `localeCookie` option. ### serverSetCookie (optional) The `serverSetCookie` option automatically changes a visitor's preferred locale cookie by simply visiting a pathname that contains a locale. `'always'`(default): When the pathname of a request includes a locale, that locale will be set as the cookie by the middleware. This means that locale detection and any existing locale cookie will be ignored if a locale exists in the request's pathname. Locale detection and the reading of any existing cookie will still be run on pathnames that do not include a locale. `'if-empty'`: Same as `'always'`, except the middleware will not overwrite the cookie if one already exists. `'never'`: The middleware will not automatically set the cookie. If you are using `noPrefix`, the `serverSetCookie` option does not do anything since there is no locale in the pathname to read from. All language changing must be done by setting the cookie manually. ### cookieOptions (optional) The server sets the cookie by setting the `Set-Cookie` HTTP response header on the `NextResponse`. [(Learn More)](https://nextjs.org/docs/app/api-reference/functions/next-response#setname-value) By default, `cookieOptions` is set to: ``` { sameSite: 'lax', maxAge: 31536000, path: {the basePath of the incoming NextRequest} } ``` You can set your own `cookieOptions` object containing any of the valid `Set-Cookie` attributes: [MDN: Set-Cookie](https://nextjs.org/docs/app/api-reference/functions/next-response) ## Using `basePath` (optional) This is only needed if you are using the `basePath` option in `next.config.js`. You will need to also include it as the `basePath` option in your `i18nConfig`. As can be read about [here](https://github.com/vercel/next.js/issues/47085), you will also need to update your `matcher` in your middleware config to include `{ source: '/' }`: ```js export const config = { matcher: ['/((?!api|static|.*\\..*|_next).*)', { source: '/' }] }; ``` ## Getting the current locale ### In a Client Component: The current locale can be retrieved in a Client Component using the `useCurrentLocale` hook: ```js 'use client'; import { useCurrentLocale } from 'next-i18n-router/client'; import i18nConfig from '@/i18nConfig'; function ExampleClientComponent() { const locale = useCurrentLocale(i18nConfig); ... } ``` ### In a Server Component: The current locale should be accessed from the component's `params` props: ```js function ExampleServerComponent({ params: { locale } }) { ... } ``` # Usage with popular i18n libraries ## react-intl The `react-intl` library works great with the App Router. But it does require a litte extra configuration for usage in Server Components. For a full walkthrough on using `react-intl` with `next-i18n-router` (plus Google Translate/DeepL integration), see [this tutorial](https://i18nexus.com/tutorials/nextjs/react-intl). You can also find an example project [here](https://github.com/i18nexus/next-i18n-router/tree/main/examples/react-intl-example). ## react-i18next If you use `i18next`, you can now use either `next-i18next` or `react-i18next` with the App Router. `next-i18next` previously only worked with the Pages Router, but it recently added App Router support in 2026. If you want to use `next-i18next`, see [this `next-i18next` App Router tutorial](https://i18nexus.com/tutorials/nextjs/next-i18next-app-router). `next-i18n-router` is not needed for that setup. If you're interested in using `react-i18next` directly instead, see [this tutorial](https://i18nexus.com/tutorials/nextjs/react-i18next). You can also find an example project [here](https://github.com/i18nexus/next-i18n-router/tree/main/examples/i18next-example). # FAQ ## How do I create a dropdown for a user to change the language? In our [example projects](https://github.com/i18nexus/next-i18n-router/tree/main/examples) you will find a `LanguageChanger` component showing how to do this. Note that `router.refresh` is called after changing languages. This is because Next will not route the request through the middleware if the page happens to be cached on the client. `router.refresh` ensures the middleware is run on language change, allowing the locale cookie to be set properly. ## My not-found page is not working. What's wrong? This is likely because of our use of the `[locale]` dynamic segment. This is not a bug with this library. It is a design choice of NextJS when using a not-found page in a dynamic segment. To solve this, we recommend making the adjustment described [here](https://github.com/i18nexus/next-i18n-router/issues/36#issuecomment-1821887026).