Docs
Next.js 13
Client Components

Next.js 13: Internationalization (i18n) in Client Components

next-intl is available in the app directory from version 2.10.0 onwards.

Getting started

If you haven't done so already, create a Next.js 13 app that uses the app directory (opens in a new tab). The goal is to prefix all routes with the locale, so that we can retrieve it as a dynamic segment (opens in a new tab) and use it to configure next-intl.

Start by creating the following file structure:

├── messages (1)
│   ├── en.json
│   └── ...
├── app
│   └── [locale]
│       ├── layout.tsx (2)
│       └── page.tsx (3)
└── middleware.ts (4)

Now, set up these files as follows:

  1. Set up messages for a language, e.g. in messages/en.json.
messages/en.json
{
  "Index": {
    "title": "Hello world!"
  }
}
  1. Provide the document layout and set up NextIntlClientProvider in app/[locale]/layout.tsx.
app/[locale]/layout.tsx
import {NextIntlClientProvider} from 'next-intl/client';
import {notFound} from 'next/navigation';
 
export function generateStaticParams() {
  return [{locale: 'en'}, {locale: 'de'}];
}
 
export default async function LocaleLayout({children, params: {locale}}) {
  let messages;
  try {
    messages = (await import(`../../messages/${locale}.json`)).default;
  } catch (error) {
    notFound();
  }
 
  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider locale={locale} messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}
  1. Turn your component in app/[locale]/page.tsx into a Client Component to be able to use translations.
app/[locale]/page.tsx
'use client';
 
import {useTranslations} from 'next-intl';
 
export default function Index() {
  const t = useTranslations('Index');
  return <h1>{t('title')}</h1>;
}
  1. Set up the i18n routing middleware in middleware.ts. The middleware matches the locale of the request and handles redirects and rewrites accordingly.
middleware.ts
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
  matcher: ['/((?!api|_next|.*\\..*).*)']
};

That's all you need to do to start using translations in the app directory!

If you've encountered an issue, you can explore the code for a working example (opens in a new tab) (demo (opens in a new tab)).

💡

Note that you have to mark all components that use features from next-intl with 'use client'; at the top of the module if you use this approach. Support for next-intl APIs in Server Components is available in a beta version.

Linking between pages

In the pages folder, Next.js automatically considers the current locale for next/link. Since this is no longer the case for the app directory, you can add a component that wraps next/link accordingly as a drop-in replacement.

Link.tsx
import {useLocale} from 'next-intl';
import NextLink from 'next/link';
import {ComponentProps, forwardRef} from 'react';
 
type Props = ComponentProps<typeof NextLink>;
 
function Link({href, ...rest}: Props, ref: Props['ref']) {
  const locale = useLocale();
 
  // Turn this off, to avoid updating the locale cookie for prefetch requests
  const prefetch = false;
 
  function getLocalizedHref(originalHref: string) {
    return originalHref.replace(/^\//, '/' + locale + '/');
  }
 
  const localizedHref =
    typeof href === 'string'
      ? getLocalizedHref(href)
      : href.pathname != null
      ? {...href, pathname: getLocalizedHref(href.pathname)}
      : href;
 
  return (
    <NextLink ref={ref} href={localizedHref} prefetch={prefetch} {...rest} />
  );
}
 
export default forwardRef(Link);
// Usage while being on `/en`
<Link href="/">Goes to `/en`</Link>
<Link href="/nested">Goes to `/en/nested`</Link>
<Link href={{pathname: "/nested"}}>Goes to `/en/nested`</Link>
💡

Built-in support for i18n routing APIs is being considered (see the Server Components beta docs).

Usage with the Metadata API

To provide metadata for a route, you can use the core library from next-intl.

app/[locale]/layout.tsx
import {createTranslator} from 'next-intl';
 
export async function generateMetadata({params: {locale}}) {
  const messages = (await import(`../../../messages/${locale}.json`)).default;
 
  // You can use the core (non-React) APIs when you have to use next-intl
  // outside of components. Potentially this will be simplified in the future
  // (see https://next-intl-docs.vercel.app/docs/next-13/server-components).
  const t = createTranslator({locale, messages});
 
  return {
    title: t('LocaleLayout.title')
  };
}
💡

Potentially, this will be simplified in the future (see the Server Components beta docs).