App Router setup without i18n routing
Setting up an app without the i18n routing integration can be useful in the following cases:
- You’d like to provide a locale to
next-intl
, e.g. based on user settings - Your app only supports a single language
This is the easiest way to get started with next-intl
and requires no changes to the structure of your app.
Getting started
If you haven’t done so already, create a Next.js app that uses the App Router and run:
npm install next-intl
Now, we’re going to create the following file structure:
├── messages
│ ├── en.json (1)
│ └── ...
├── next.config.mjs (2)
└── src
├── i18n
│ └── request.ts (3)
└── app
├── layout.tsx (4)
└── page.tsx (5)
Let’s set up the files:
messages/en.json
Messages represent the translations that are available per language and can be provided either locally or loaded from a remote data source.
The simplest option is to add JSON files in your local project folder:
{
"HomePage": {
"title": "Hello world!"
}
}
next.config.mjs
Now, set up the plugin which creates an alias to provide a request-specific i18n configuration to Server Components (specified in the next step).
import createNextIntlPlugin from 'next-intl/plugin';
const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default withNextIntl(nextConfig);
i18n/request.ts
next-intl
creates a request-scoped configuration object, which you can use to provide messages and other options based on the user’s locale to Server Components.
import {getRequestConfig} from 'next-intl/server';
export default getRequestConfig(async () => {
// Provide a static locale, fetch a user setting,
// read from `cookies()`, `headers()`, etc.
const locale = 'en';
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default
};
});
Can I move this file somewhere else?
This file is supported out-of-the-box as ./i18n/request.ts
both in the src
folder as well as in the project root with the extensions .ts
, .tsx
, .js
and .jsx
.
If you prefer to move this file somewhere else, you can optionally provide a path to the plugin:
const withNextIntl = createNextIntlPlugin(
// Specify a custom path here
'./somewhere/else/request.ts'
);
app/layout.tsx
The locale
that was provided in i18n/request.ts
is available via getLocale
and can be used to configure the document language. Additionally, we can use this place to pass configuration from i18n/request.ts
to Client Components via NextIntlClientProvider
.
import {NextIntlClientProvider} from 'next-intl';
import {getLocale, getMessages} from 'next-intl/server';
export default async function RootLayout({
children
}: {
children: React.ReactNode;
}) {
const locale = await getLocale();
// Providing all messages to the client
// side is the easiest way to get started
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
Note that NextIntlClientProvider
automatically inherits configuration from i18n/request.ts
here, but messages
need to be passed explicitly.
app/page.tsx
Use translations in your page components or anywhere else!
import {useTranslations} from 'next-intl';
export default function HomePage() {
const t = useTranslations('HomePage');
return <h1>{t('title')}</h1>;
}
That’s all it takes!
In case you ran into an issue, have a look at a working example:
Next steps:
Usage guide: Learn how to format messages, dates and times
Workflows: Integrate deeply with TypeScript and other tools