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 (opens in a new tab) 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.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.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.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.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.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.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