Global configuration
Configuration properties that you use across your Next.js app can be set globally.
Server & Client Components
Depending on if you handle internationalization in Server- or Client Components, the configuration from i18n/request.ts
or NextIntlClientProvider
will be applied respectively.
i18n/request.ts
& getRequestConfig
i18n/request.ts
can be used to provide configuration for server-only code, i.e. Server Components, Server Actions & friends. The configuration is provided via the getRequestConfig
function and needs to be set up based on whether you’re using i18n routing or not.
import {getRequestConfig} from 'next-intl/server';
import {routing} from '@/i18n/routing';
export default getRequestConfig(async ({requestLocale}) => {
// ...
return {
locale,
messages
// ...
};
});
The configuration object is created once for each request by internally using React’s cache
. The first component to use internationalization will call the function defined with getRequestConfig
.
Since this function is executed during the Server Components render pass, you can call functions like cookies()
and headers()
to return configuration that is request-specific.
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'
);
NextIntlClientProvider
NextIntlClientProvider
can be used to provide configuration for Client Components.
import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';
export default async function RootLayout(/* ... */) {
// ...
return (
<html lang={locale}>
<body>
<NextIntlClientProvider>{children}</NextIntlClientProvider>
</body>
</html>
);
}
These props are inherited if you’re rendering NextIntlClientProvider
from a Server Component:
locale
messages
now
timeZone
formats
In contrast, these props can be provided as necessary:
onError
getMessageFallback
Additionally, nested instances of NextIntlClientProvider
will inherit configuration from their respective ancestors. Note however that individual props are treated as atomic, therefore e.g. messages
need to be merged manually—if necessary.
How can I provide non-serializable props like onError
to NextIntlClientProvider
?
React limits the types of props that can be passed to Client Components to the ones that are serializable. Since onError
and getMessageFallback
can receive functions, these configuration options can’t be automatically inherited by the client side.
In order to define these values on the client side, you can add a provider that defines these props:
'use client';
import {NextIntlClientProvider} from 'next-intl';
export default function IntlErrorHandlingProvider({children}) {
return (
<NextIntlClientProvider
onError={(error) => console.error(error)}
getMessageFallback={({namespace, key}) => `${namespace}.${key}`}
>
{children}
</NextIntlClientProvider>
);
}
Once you have defined your client-side provider component, you can use it in a Server Component:
import {NextIntlClientProvider} from 'next-intl';
import {getLocale} from 'next-intl/server';
import IntlErrorHandlingProvider from './IntlErrorHandlingProvider';
export default async function RootLayout({children}) {
const locale = await getLocale();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider>
<IntlErrorHandlingProvider>{children}</IntlErrorHandlingProvider>
</NextIntlClientProvider>
</body>
</html>
);
}
By doing this, your provider component will already be part of the client-side bundle and can therefore define and pass functions as props.
Note that the inner NextIntlClientProvider
inherits the configuration from the outer one, only the onError
and getMessageFallback
functions are added.
Locale
The locale
represents an identifier that contains the language and formatting preferences of users, optionally including regional information (e.g. en-US
). Locales are specified as IETF BCP 47 language tags.
Depending on if you’re using i18n routing, you can read the locale from the requestLocale
parameter or provide a value on your own:
With i18n routing:
export default getRequestConfig(async ({requestLocale}) => {
// Typically corresponds to the `[locale]` segment
const requested = await requestLocale;
const locale = hasLocale(routing.locales, requested)
? requested
: routing.defaultLocale;
return {
locale
// ...
};
});
Without i18n routing:
export default getRequestConfig(async () => {
// Provide a static locale, fetch a user setting,
// read from `cookies()`, `headers()`, etc.
const locale = 'en';
return {
locale
// ...
};
});
Which values can the requestLocale
parameter hold?
While the requestLocale
parameter typically corresponds to the [locale]
segment that was matched by the middleware, there are three special cases to consider:
- Overrides: When an explicit
locale
is passed to awaitable functions likegetTranslations({locale: 'en'})
, then this value will be used instead of the segment. undefined
: The value can beundefined
when a page outside of the[locale]
segment renders (e.g. a language selection page atapp/page.tsx
).- Invalid values: Since the
[locale]
segment effectively acts like a catch-all for unknown routes (e.g./unknown.txt
), invalid values should be replaced with a valid locale. In addition to this, you might want to callnotFound()
in the root layout to abort the render in this case.
How can I change the locale?
Depending on if you’re using i18n routing, the locale can be changed as follows:
- With i18n routing: The locale is managed by the router and can be changed by using navigation APIs from
next-intl
likeLink
oruseRouter
. - Without i18n routing: You can change the locale by updating the value where the locale is read from (e.g. a cookie, a user setting, etc.). If you’re looking for inspiration, you can have a look at the App Router without i18n routing example that manages the locale via a cookie.
useLocale
& getLocale
The current locale of your app is automatically incorporated into hooks like useTranslations
& useFormatter
and will affect the rendered output.
In case you need to use this value in other places of your app, e.g. to implement a locale switcher or to pass it to API calls, you can read it via useLocale
or getLocale
:
// Regular components
import {useLocale} from 'next-intl';
const locale = useLocale();
// Async Server Components
import {getLocale} from 'next-intl/server';
const locale = await getLocale();
Which value is returned from useLocale
?
Depending on how a component renders, the returned locale corresponds to:
- Server Components: The locale represents the value returned in
i18n/request.ts
. - Client Components: The locale is received from
NextIntlClientProvider
.
Note that NextIntlClientProvider
automatically inherits the locale if it is rendered by a Server Component, therefore you rarely need to pass a locale to NextIntlClientProvider
yourself.
I’m using the Pages Router, how can I access the locale?
If you use internationalized routing with the Pages Router, you can receive the locale from the router in order to pass it to NextIntlClientProvider
:
import {useRouter} from 'next/router';
// ...
const router = useRouter();
return (
<NextIntlClientProvider locale={router.locale}>
...
</NextIntlClientProvider>;
);
Locale
type
When passing a locale
to another function, you can use the Locale
type for the receiving parameter:
import {Locale} from 'next-intl';
async function getPosts(locale: Locale) {
// ...
}
By default, Locale
is typed as string
. However, you can optionally provide
a strict union based on your supported locales for this type by augmenting
the Locale
type.
Messages
The most crucial aspect of internationalization is providing labels based on the user’s language. The recommended workflow is to store your messages in your repository along with the code.
├── messages
│ ├── en.json
│ ├── de-AT.json
│ └── ...
...
Colocating your messages with app code is beneficial because it allows developers to make changes quickly and additionally, you can use the shape of your local messages for type checking. Translators can collaborate on messages by using CI tools, such as Crowdin’s GitHub integration, which allows changes to be synchronized directly into your code repository.
That being said, next-intl
is agnostic to how you store messages and allows you to freely define an async function that fetches them while your app renders:
import {getRequestConfig} from 'next-intl/server';
export default getRequestConfig(async () => {
return {
messages: (await import(`../../messages/${locale}.json`)).default
// ...
};
});
After messages are configured, they can be used via useTranslations
.
How can I load messages from remote sources?
While it’s recommended to colocate at least the messages for the default locale, you can also load messages from remote sources, e.g. with the Crowdin OTA JS Client.
import OtaClient from '@crowdin/ota-client';
const defaultLocale = 'en';
const client = new OtaClient('<distribution-hash>');
const messages =
locale === defaultLocale
? (await import(`../../messages/en.json`)).default
: await client.getStringsByLocale(locale);
How can I use messages from another locale as fallbacks?
If you have incomplete messages for a given locale and would like to use messages from another locale as a fallback, you can merge the two accordingly.
import deepmerge from 'deepmerge';
const userMessages = (await import(`../../messages/${locale}.json`)).default;
const defaultMessages = (await import(`../../messages/en.json`)).default;
const messages = deepmerge(defaultMessages, userMessages);
How can I split my messages into multiple files?
Since messages can be freely defined and loaded, you can split them into multiple files and merge them later at runtime if you prefer:
const messages = {
...(await import(`../../messages/${locale}/login.json`)).default,
...(await import(`../../messages/${locale}/dashboard.json`)).default
};
Note that the VSCode integration for next-intl
can help you manage messages within a single, large file. If you’re splitting messages purely for organizational reasons, you might want to consider using this instead.
Do I need separate messages for each locale that my app supports?
Since you have full control over how messages are loaded, you can choose to load messages for example merely based on the overall language, ignoring any regional variants:
import {getRequestConfig} from 'next-intl/server';
export default getRequestConfig(async () => {
// E.g. "en-US", "en-CA", …
const locale = 'en-US';
// E.g. "en"
const language = new Intl.Locale(locale).language;
// Load messages based on the language
const messages = (await import(`../../messages/${language}.json`)).default;
// ...
});
useMessages
& getMessages
In case you require access to messages in a component, you can read them via useMessages()
or getMessages()
from your configuration:
// Regular components
import {useMessages} from 'next-intl';
const messages = useMessages();
// Async Server Components
import {getMessages} from 'next-intl/server';
const messages = await getMessages();
Time zone
Specifying a time zone affects the rendering of dates and times. By default, the time zone of the server runtime will be used, but can be customized as necessary.
import {getRequestConfig} from 'next-intl/server';
export default getRequestConfig(async () => {
return {
// The time zone can either be statically defined, read from the
// user profile if you store such a setting, or based on dynamic
// request information like the locale or a cookie.
timeZone: 'Europe/Vienna'
// ...
};
});
The available time zone names can be looked up in the tz database.
The time zone in Client Components is automatically inherited from the server side if you wrap the relevant components in a NextIntlClientProvider
that is rendered by a Server Component. For all other cases, you can specify the value explicitly on a wrapping NextIntlClientProvider
.
useTimeZone
& getTimeZone
The configured time zone can be read via useTimeZone
or getTimeZone
in components:
// Regular components
import {useTimeZone} from 'next-intl';
const timeZone = useTimeZone();
// Async Server Components
import {getTimeZone} from 'next-intl/server';
const timeZone = await getTimeZone();
Now value
When formatting relative dates and times, next-intl
will format times in relation to a reference point in time that is referred to as “now”. While it can be beneficial in terms of caching to provide this value where necessary, you can provide a global value for now
, e.g. to ensure consistency when running tests.
import {getRequestConfig} from 'next-intl/server';
export default getRequestConfig(async () => {
return {
now: new Date('2024-11-14T10:36:01.516Z')
// ...
};
});
If a now
value is provided in i18n/request.ts
, this will automatically be inherited by Client Components if you wrap them in a NextIntlClientProvider
that is rendered by a Server Component.
useNow
& getNow
The configured now
value can be read in components via useNow
or getNow
:
// Regular components
import {useNow} from 'next-intl';
const now = useNow();
// Async Server Components
import {getNow} from 'next-intl/server';
const now = await getNow();
Note that the returned value defaults to the current date and time, therefore making this hook useful when providing now
for format.relativeTime
even when you haven’t configured a global now
value.
Formats
To achieve consistent date, time, number and list formatting, you can define a set of global formats.
import {getRequestConfig} from 'next-intl/server';
export default getRequestConfig(async () => {
return {
formats: {
dateTime: {
short: {
day: 'numeric',
month: 'short',
year: 'numeric'
}
},
number: {
precise: {
maximumFractionDigits: 5
}
},
list: {
enumeration: {
style: 'long',
type: 'conjunction'
}
}
}
// ...
};
});
Once you have formats
set up, you can use them in your components via useFormatter
:
import {useFormatter} from 'next-intl';
function Component() {
const format = useFormatter();
format.dateTime(new Date('2020-11-20T10:36:01.516Z'), 'short');
format.number(47.414329182, 'precise');
format.list(['HTML', 'CSS', 'JavaScript'], 'enumeration');
}
By default, format names are loosely typed as string
. However, you can
optionally use strict types by augmenting the Formats
type.
Global formats for numbers, dates and times can be referenced in messages too:
{
"ordered": "You've ordered this product on {orderDate, date, short}",
"latitude": "Latitude: {latitude, number, precise}"
}
import {useTranslations} from 'next-intl';
function Component() {
const t = useTranslations();
t('ordered', {orderDate: new Date('2020-11-20T10:36:01.516Z')});
t('latitude', {latitude: 47.414329182});
}
Formats are automatically inherited from the server side if you wrap the relevant components in a NextIntlClientProvider
that is rendered by a Server Component.
Error handling (onError
& getMessageFallback
)
By default, when a message fails to resolve or when the formatting failed, an error will be printed on the console. In this case ${namespace}.${key}
will be rendered instead to keep your app running.
This behavior can be customized with the onError
and getMessageFallback
configuration option.
import {getRequestConfig} from 'next-intl/server';
import {IntlErrorCode} from 'next-intl';
export default getRequestConfig(async () => {
return {
onError(error) {
if (error.code === IntlErrorCode.MISSING_MESSAGE) {
// Missing translations are expected and should only log an error
console.error(error);
} else {
// Other errors indicate a bug in the app and should be reported
reportToErrorTracking(error);
}
},
getMessageFallback({namespace, key, error}) {
const path = [namespace, key].filter((part) => part != null).join('.');
if (error.code === IntlErrorCode.MISSING_MESSAGE) {
return path + ' is not yet translated';
} else {
return 'Dear developer, please fix this message: ' + path;
}
}
// ...
};
});
Note that onError
and getMessageFallback
are not automatically inherited by Client Components. If you want to make this functionality available in Client Components too, you can however create a client-side provider that defines these props.