The easiest way to translate your Next.js apps (with pages setup).
If you are using next-i18next (pages directory) in production and like to unleash some super powers, you may have a look at this blog post.
If you're using Next.js 13/14 with app directory, there is no need for next-i18next, you can directly use i18next and react-i18next, like described in this blog post.
Although Next.js provides internationalised routing directly, it does not handle any management of translation content, or the actual translation functionality itself. All Next.js does is keep your locales and URLs in sync.
To complement this, next-i18next
provides the remaining functionality βΒ management of translation content, and components/hooks to translate your React components β while fully supporting SSG/SSR, multiple namespaces, codesplitting, etc.
While next-i18next
uses i18next and react-i18next under the hood, users of next-i18next
simply need to include their translation content as JSON files and don't have to worry about much else.
A live demo is available here. This demo app is the simple example - nothing more, nothing less.
Easy to set up, easy to use: setup only takes a few steps, and configuration is simple.
No other requirements: next-i18next
simplifies internationalisation for your Next.js app without extra dependencies.
Production ready: next-i18next
supports passing translations and configuration options into pages as props with SSG/SSR support.
Your next-i18next.config.js
file will provide configuration for next-i18next
.
After configuration, appWithTranslation
allows us to use the t
(translate) function in our components via hooks.
Then we add serverSideTranslation
to getStaticProps or getServerSideProps (depending on your case) in our page-level components.
Now our Next.js app is fully translatable!
yarn add next-i18next react-i18next i18next
You need to also have react
and next
installed.
By default, next-i18next
expects your translations to be organised as such:
.
βββ public
βββ locales
βββ en
| βββ common.json
βββ de
βββ common.json
This structure can also be seen in the simple example.
If you want to structure your translations/namespaces in a custom way, you will need to pass modified localePath
and localeStructure
values into the initialisation config.
First, create a next-i18next.config.js
file in the root of your project. The syntax for the nested i18n
object comes from Next.js directly.
This tells next-i18next
what your defaultLocale
and other locales are, so that it can preload translations on the server:
/** @type {import('next-i18next').UserConfig} */
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'de'],
},
}
Now, create or modify your next.config.js
file, by passing the i18n
object into your next.config.js
file, to enable localised URL routing:
const { i18n } = require('./next-i18next.config')
module.exports = {
i18n,
}
There are three functions that next-i18next
exports, which you will need to use to translate your project:
This is a HOC which wraps your _app
:
import { appWithTranslation } from 'next-i18next'
const MyApp = ({ Component, pageProps }) => (
<Component {...pageProps} />
)
export default appWithTranslation(MyApp)
The appWithTranslation
HOC is primarily responsible for adding a I18nextProvider
.
This is an async function that you need to include on your page-level components, via either getStaticProps
or getServerSideProps
(depending on your use case):
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale, [
'common',
'footer',
])),
// Will be passed to the page component as props
},
}
}
Note that serverSideTranslations
must be imported from next-i18next/serverSideTranslations
β this is a separate module that contains NodeJs-specific code.
Also, note that serverSideTranslations
is not compatible with getInitialProps
, as it only can execute in a server environment, whereas getInitialProps
is called on the client side when navigating between pages.
The serverSideTranslations
HOC is primarily responsible for passing translations and configuration options into pages, as props β you need to add it to any page that has translations.
This is the hook which you'll actually use to do the translation itself. The useTranslation
hook comes from react-i18next
, but needs to be imported from next-i18next
directly.
Do NOT use the useTranslation
export of react-i18next
, but ONLY use the one from next-i18next
!
import { useTranslation } from 'next-i18next'
export const Footer = () => {
const { t } = useTranslation('footer')
return (
<footer>
<p>{t('description')}</p>
</footer>
)
}
By default, next-i18next
will send all your namespaces down to the client on each initial request. This can be an appropriate approach for smaller apps with less content, but a lot of apps will benefit from splitting namespaces based on route.
To do that, you can pass an array of required namespaces for each page into serverSideTranslations
. You can see this approach in examples/simple/pages/index.tsx. Passing in an empty array of required namespaces will send no namespaces.
Note: useTranslation
provides namespaces to the component that you use it in. However, serverSideTranslations
provides the total available namespaces to the entire React tree and belongs on the page level. Both are required.
By default, next-i18next
will send only the active locale down to the client on each request. This helps reduce the size of the
initial payload sent to the client. However in some cases one may need the translations for other languages at runtime too. For example
when using getFixedT of useTranslation
hook.
To change the behavior and load extra locales just pass in an array of locales as the last argument to serverSideTranslations
.
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
export async function getStaticProps({ locale }) {
return {
props: {
- ...(await serverSideTranslations(locale, ['common', 'footer'])),
+ ...(await serverSideTranslations(locale, ['common', 'footer'], null, ['en', 'no'])),
},
};
}
As a result the translations for both no
and en
locales will always be loaded regardless of the current language.
Note: The extra argument should be added to all pages that use
getFixedT
function.
By default, next-i18next
will add the defaultLocale
as fallback. To change this, you can set fallbackLng
. All values supported by i18next
(string
, array
, object
and function
) are supported by next-i18next
too.
Additionally nonExplicitSupportedLngs
can be set to true
to support all variants of a language, without the need to provide JSON files for each of them. Notice that all variants still must be included in locales
to enable routing within next.js
.
Note:
fallbackLng
andnonExplicitSupportedLngs
can be used at once. There is only one exception: You can not use a function forfallbackLng
whennonExplicitSupportedLngs
istrue
,
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'de-AT', 'de-DE', 'de-CH'],
},
fallbackLng: {
default: ['en'],
'de-CH': ['fr'],
},
nonExplicitSupportedLngs: true,
// de, fr and en will be loaded as fallback languages for de-CH
}
Be aware that using fallbackLng
and nonExplicitSupportedLngs
can easily increase the initial size of the page.
fyi: Setting fallbackLng
to false
will NOT serialize your fallback language (usually defaultLocale
). This will decrease the size of your initial page load.
If you need to modify more advanced configuration options, you can pass them via next-i18next.config.js
. For example:
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'de'],
},
localePath:
typeof window === 'undefined'
? require('path').resolve('./my-custom/path')
: '/public/my-custom/path',
ns: ['common'],
}
Some i18next
plugins (which you can pass into config.use
) are unserializable, as they contain functions and other JavaScript primitives.
You may run into this if your use case is more advanced. You'll see Next.js throw an error like:
Error: Error serializing `._nextI18Next.userConfig.use[0].process` returned from `getStaticProps` in "/my-page".
Reason: `function` cannot be serialized as JSON. Please only return JSON serializable data types.
To fix this, you'll need to set config.serializeConfig
to false
, and manually pass your config into appWithTranslation
:
import { appWithTranslation } from 'next-i18next'
import nextI18NextConfig from '../next-i18next.config.js'
const MyApp = ({ Component, pageProps }) => (
<Component {...pageProps} />
)
export default appWithTranslation(MyApp, nextI18NextConfig)
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import nextI18NextConfig from '../next-i18next.config.js'
export const getStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(
locale,
['common', 'footer'],
nextI18NextConfig
)),
},
})
When using on server-side generated pages with getStaticPaths
and fallback: true
or fallback: 'blocking'
, the default setup indicated above will cause the app to be unmounted and remounted on every load, causing various adverse consequences like calling every useEffect(() => {...}, [])
hook twice and slight performance degradation.
This is due to the fact that, for those pages, Next.js does a first render with empty serverSideProps
and then a second render with the serverSideProps
that include the next-i18next
translations. With the default setup, the i18n
instance is initially undefined
when serverSideProps
is empty
, causing the unmount-remount.
To mitigate this issue, you can do the following:
import { UserConfig } from 'next-i18next';
import nextI18NextConfig from '../next-i18next.config.js'
const emptyInitialI18NextConfig: UserConfig = {
i18n: {
defaultLocale: nextI18NextConfig.i18n.defaultLocale,
locales: nextI18NextConfig.i18n.locales,
},
};
const MyApp = ({ Component, pageProps }) => (
<Component {...pageProps} />
)
export default appWithTranslation(MyApp, emptyInitialI18NextConfig) // Makes sure the initial i18n instance is not undefined
This will work as long as you make sure that, in the fallback page state, your client-side code is not trying to display any translation since otherwise you will get a "server-client mismatch" error from Next.js (due to the fact that the server has an actual translation in its html while the client html has the translation key in the same place).
This is normal and fine: you shouldn't be displaying a translation key to your user anyway!
Since v11.0.0 next-i18next also provides support for client side loading of translations.
In some use cases, you might want to load a translation file dynamically without having to use serverSideTranslations
. This can be especially useful for lazy-loaded components that you don't want slowing down pages.
More information about that can be found here.
Because resources are loaded once when the server is started, any changes made to your translation JSON files in development will not be loaded until the server is restarted.
In production this does not tend to be an issue, but in development you may want to see updates to your translation JSON files without having to restart your development server each time. To do this, set the reloadOnPrerender
config option to true
.
This option will reload your translations whenever serverSideTranslations
is called (in getStaticProps
or getServerSideProps
). If you are using serverSideTranslations
in getServerSideProps
, it is recommended to disable reloadOnPrerender
in production environments as to avoid reloading resources on each server call.
Key | Default value | Note |
---|---|---|
defaultNS |
'common' |
|
localePath |
'./public/locales' |
Can be a function, see note below. (can also be null, if passing resources option directly via config, like here) |
localeExtension |
'json' |
Ignored if localePath is a function. |
localeStructure |
'{{lng}}/{{ns}}' |
Ignored if localePath is a function. |
reloadOnPrerender |
false |
|
serializeConfig |
true |
|
use (for plugins) |
[] |
|
onPreInitI18next |
undefined |
i.e. (i18n) => i18n.on('failedLoading', handleFailedLoading)
|
localePath
as a function is of the form (locale: string, namespace: string, missing: boolean) => string
returning the entire path including filename and extension. When missing
is true, return the path for the addPath
option of i18next-fs-backend
, when false, return the path for the loadPath
option. More info at the i18next-fs-backend
repo.
If the localePath is a function, make sure you also define the ns option, because on server side we're not able to preload the namespaces then.
All other i18next options and react-i18next options can be passed in as well.
You can also pass in the resources
directly in combination with setting localePath
to null
.
By default, i18next uses {{
as prefix and }}
as suffix for interpolation.
If you want/need to override these interpolation settings, you must also specify an alternative localeStructure
setting that matches your custom prefix and suffix.
For example, if you want to use {
and }
the config would look like this:
{
i18n: {
defaultLocale: 'en',
locales: ['en', 'nl'],
},
interpolation: {
prefix: '{',
suffix: '}',
},
localeStructure: '{lng}/{ns}',
}
If you want to change the default config path, you can set the environment variable I18NEXT_DEFAULT_CONFIG_PATH
.
For example, inside the .env
file you can set a static path:
I18NEXT_DEFAULT_CONFIG_PATH=/path/to/project/apps/my-app/next-i18next.config.js
Or you can use a trick for dynamic path and set the following inside next.config.js
:
process.env.I18NEXT_DEFAULT_CONFIG_PATH = `${__dirname}/next-i18next.config.js`;
// ... Some other imports
const { i18n } = require('./next-i18next.config');
// ... Some other code
module.exports = {
i18n,
...
};
This means that the i18n configuration file will be in the same directory as next.config.js
and it doesn't matter where your current working directory is. This helps for example for nx
when you have monorepo and start your application from project root but the application is in apps/{appName}
.
Notice If your config next-i18next.config.js
is not in the same directory as next.config.js
, you must copy it manually (or by custom script).
If you are planning on incrementally add next-i18next to you project we recommended that you will pass your next-i18next.config
to appWithTranslation
to avoid any issues.
i.e
import nextI18nextConfig from '../../next-i18next.config';
//...
export default appWithTranslation(MyApp, nextI18nextConfig);
See Issue #2259 for more information.
Some serverless PaaS may not be able to locate the path of your translations and require additional configuration. If you have filesystem issues using serverSideTranslations
, set config.localePath
to use path.resolve
. An example can be found here.
For Docker deployment, note that if you use the Dockerfile
from Next.js docs do not forget to copy next.config.js
and next-i18next.config.js
into the Docker image.
COPY --from=builder /app/next.config.js ./next.config.js
COPY --from=builder /app/next-i18next.config.js ./next-i18next.config.js
If you choose to use an i18next backend different to the built-in i18next-fs-backend, you will need to ensure the translation resources are loaded before you call the t
function.
Since React suspense is not yet supported for SSR, this can be solved in 2 different ways:
1) Preload the namespaces:
Set the ns
option, like in this example. Doing this will ensure all translation resources are loaded on initialization.
2) Check the ready flag:
If you cannot or do not want to provide the ns
array, calls to the t
function will cause namespaces to be loaded on the fly. This means you'll need to handle the "not ready" state by checking ready === true
or props.tReady === true
. Not doing so will result in rendering your translations before they loaded, which will cause "save missing" be called despite the translations actually existing (just yet not loaded).
This can be done with the useTranslation hook or the withTranslation HOC.
Are you trying to generate a static HTML export by executing next export
and are getting this error?
Error: i18n support is not compatible with next export. See here for more info on deploying: https://nextjs.org/docs/deployment
But there's a way to workaround that with the help of next-language-detector. Check out this blog post and this example project.
You have multiple ways to use the t function in your child component:
- Pass the
t
function via props down to the children - Pass the translated text via props down to the children, like in this example: https://github.com/i18next/next-i18next/blob/master/examples/simple/components/Header.tsx#L12
- Use the
useTranslation
function, like in this example: - Use the
withTranslation
function
And in general, you always needs to be sure serverSideTranslations contains all namespaces you need in the tree.
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!
localization as a service - locize.com
Needing a translation management? Want to edit your translations with an InContext Editor? Use the original provided to you by the maintainers of i18next!
With using locize you directly support the future of i18next and next-i18next.