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 preview version to early adopters, who are already building apps with Server Components.
Support for React Server Components is currently available as a release candidate. Please use it at your own risk, knowing that you may have to migrate upon a stable release.
Current version
npm install next-intl@3.0.0-rc.1
This 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
By using APIs like useTranslations
from next-intl
in Server Components, your pages will currently opt 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.
As a stopgap solution, next-intl
provides a temporary API that can be used to enable static rendering:
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
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
.
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).