Next.js 13: Internationalization (i18n) in Server Components
Next.js 13 introduces support for React Server Components (opens in a new tab) with the App Router and unlocks many benefits when handling internationalization entirely on the server side. next-intl
is adopting the new capabilities and is currently offering a beta version to early adopters, who are already building apps with Server Components.
Support for React Server Components is currently in beta. Please use it at your own risk, knowing that you may have to migrate upon a stable release.
Current beta version
npm install next-intl@3.0.0-beta.19
This beta version was tested with next@13.5.1
.
Roadmap
Feature | Status |
---|---|
Usage of all next-intl APIs in Server Components | ✅ |
Dynamic rendering | ✅ |
Static rendering (i.e. generateStaticParams ) | 🏗️ |
Support for static rendering is currently available via a stopgap solution (see static rendering.
Getting started
If you haven't done so already, create a Next.js 13 app that uses the App Router (opens in a new tab). All pages should be moved within a [locale]
folder so that we can use this segment to provide content in different languages (e.g. /en
, /en/about
, etc.).
Start by running npm install next-intl
and create the following file structure:
├── messages (1)
│ ├── en.json
│ └── ...
├── i18n.ts (2)
├── next.config.js (3)
├── middleware.ts (4)
└── app
└── [locale]
├── layout.tsx (5)
└── page.tsx (6)
Now, set up the files as follows:
messages/en.json
Messages can be provided locally or loaded from a remote data source (e.g. a translation management system). Use whatever suits your workflow best.
The simplest option is to create JSON files locally based on locales, e.g. en.json
.
{
"Index": {
"title": "Hello world!"
}
}
i18n.ts
next-intl
creates a configuration once per request and makes it available to all Server Components. Here you can provide messages and other options depending the locale of the user.
import {getRequestConfig} from 'next-intl/server';
export default getRequestConfig(async ({locale}) => ({
messages: (await import(`./messages/${locale}.json`)).default
}));
next.config.js
Now, set up the plugin and provide the path to your i18n.ts
file.
const withNextIntl = require('next-intl/plugin')(
// This is the default (also the `src` folder is supported out of the box)
'./i18n.ts'
);
module.exports = withNextIntl({
// Other Next.js configuration ...
});
middleware.ts
The middleware matches a locale for the request and handles redirects and rewrites accordingly.
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
// A list of all locales that are supported
locales: ['en', 'de'],
// If this locale is matched, pathnames work without a prefix (e.g. `/about`)
defaultLocale: 'en'
});
export const config = {
// Skip all paths that should not be internationalized. This example skips
// certain folders and all pathnames with a dot (e.g. favicon.ico)
matcher: ['/((?!api|_next|_vercel|.*\\..*).*)']
};
Note: If you have pages that contain the character .
in the pathname (e.g. /users/jane.doe
), you might want to consider them in your matcher config.
app/[locale]/layout.tsx
The locale
that was matched by the middleware is available via the locale
param and can be used to configure the document language.
import {useLocale} from 'next-intl';
import {notFound} from 'next/navigation';
const locales = ['en', 'de'];
export default function LocaleLayout({children, params: {locale}}) {
// Validate that the incoming `locale` parameter is valid
const isValidLocale = locales.some((cur) => cur === locale);
if (!isValidLocale) notFound();
return (
<html lang={locale}>
<body>{children}</body>
</html>
);
}
app/[locale]/page.tsx
Use translations in your page components or anywhere else!
import {useTranslations} from 'next-intl';
export default function Index() {
const t = useTranslations('Index');
return <h1>{t('title')}</h1>;
}
That's all it takes! Now you can internationalize your apps on the server side.
Next steps:
Ran into an issue? Have a look at the Server Components example (opens in a new tab) (source (opens in a new tab)).
- Exploring
next-intl
? Check out the usage guide. Decided you're sticking with
next-intl
? Consider the steps of the checklist for production.Interested to learn more about the advantages of using
next-intl
in Server Components? Check out the Server & Client Components guide.Are you transitioning from the
pages
directory toapp
? Check out the migration example (opens in a new tab).
Static rendering
The support for using next-intl
in React Server Components currently opts your pages into dynamic rendering. This is a limitation that will eventually be lifted once createServerContext
(opens in a new tab) is available and integrated in Next.js.
If you have a strong need for static rendering, you can enable static rendering as follows:
Add generateStaticParams
to app/[locale]/layout.tsx
Since we use a dynamic route segment for the [locale]
param, we need to provide all possible values via generateStaticParams
(opens in a new tab) to Next.js, so the routes can be rendered at build time.
const locales = ['en', 'de'];
export function generateStaticParams() {
return locales.map((locale) => ({locale}));
}
Add unstable_setRequestLocale
to all layouts and pages
As a stopgap solution, next-intl
provides a temporary API that can be used to distribute the locale that is received via params
in a layout or page for usage in all Server Components that are rendered as part of the request.
import {unstable_setRequestLocale} from 'next-intl/server';
const locales = ['en', 'de'];
export default async function LocaleLayout({
children,
params: {locale}
}) {
// Validate that the incoming `locale` parameter is valid
const isValidLocale = locales.some((cur) => cur === locale);
if (!isValidLocale) notFound();
unstable_setRequestLocale(locale);
return (
// ...
);
}
import {unstable_setRequestLocale} from 'next-intl/server';
import {locales} from '..';
export default async function IndexPage({
params: {locale}
}) {
unstable_setRequestLocale(locale);
return (
// ...
);
}
What does "unstable" mean?
unstable_setRequestLocale
is meant to be used as a stopgap solution and will eventually be replaced by an API that's based on createServerContext
. When that time comes, you'll get a deprecation notice in a minor version and the API will be removed as part of a major version.
Note that Next.js can render layouts and pages indepently. This means that e.g. when you navigate from /settings/profile
to /settings/privacy
, the /settings
segment might not re-render as part of the request. Due to this, it's important that unstable_setRequestLocale
is called not only in the parent settings/layout.tsx
, but also in the individual pages profile/page.tsx
and settings/page.tsx
.
Apart from that, the API can only be used for pages that receive params
(i.e. not not-found.tsx
).
That being said, the API is expected to work reliably if you're cautious to apply it in all relevant places.
Providing feedback
If you have feedback about using next-intl
in the app
directory, feel free to leave feedback in the PR that implements the React Server Components support (opens in a new tab).