The easiest way to translate your NextJs apps.
If you are using next-i18next in production, please consider sponsoring the package with any amount you think appropriate.
next-i18next
is a plugin for Next.js projects that allows you to get translations up and running quickly and easily, while fully supporting SSR, multiple namespaces with 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.
yarn add next-i18next
You need to also have react
and next
installed.
By default, next-i18next
expects your translations to be organised as such:
.
└── public
└── static
└── 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.
The default export of next-i18next
is a class constructor, into which you pass your config options. The resulting class has all the methods you will need to translate your app:
const NextI18Next = require('next-i18next').default
const { localeSubpaths } = require('next/config').default().publicRuntimeConfig
const path = require('path')
module.exports = new NextI18Next({
otherLanguages: ['de'],
localeSubpaths,
localePath: path.resolve('./public/static/locales')
})
Note that localePath
is required, and must be an absolute path.
A full list of options can be seen here.
It's recommended to export this NextI18Next
instance from a single file in your project, where you can continually import it from to use the class methods as needed. You can see this approach in the examples/simple/i18n.js file.
After creating and exporting your NextI18Next
instance, you need to take the following steps to get things working:
- Create an
_app.js
file inside yourpages
directory, and wrap it with theNextI18Next.appWithTranslation
higher order component (HOC). You can see this approach in the examples/simple/pages/_app.js. Your app component must either extendApp
if it's a class component or define agetInitialProps
if it's a functional component (explanation here). - Create a
next.config.js
file inside your root directory if you want to use locale subpaths. You can see this approach in the examples/simple/next.config.js (Next.js 9.5+ required).
Note: You can pass shallowRender: true
into config options to avoid triggering getInitialProps when changeLanguage
method is invoked.
That's it! Your app is ready to go. You can now use the NextI18Next.withTranslation
HOC to make your components or pages translatable, based on namespaces:
// This is our initialised `NextI18Next` instance
import { withTranslation } from '../i18n'
const Footer = ({ t }) => (
<footer>
<p>
{t('description')}
</p>
</footer>
)
export default withTranslation('footer')(Footer)
The withTranslation
HOC is responsible for passing the t
function to your component. It enables all the translation functionality provided by i18next
. Further, it asserts your component gets re-rendered on language change or changes to the translation catalog itself (loaded translations). More info can be found here.
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 need to return a namespacesRequired
array via getInitialProps
on your page-level component. You can see this approach in examples/simple/pages/index.js.
Note: withTranslation
provides namespaces to the component that it wraps. However, namespacesRequired
provides the total available namespaces to the entire React tree and belongs on the page level. Both are required (although you can use Trans
instead of withTranslation
if desired).
One of the main features of this package, besides translation itself, are locale subpaths. It's easiest to explain by example:
myapp.com ---> Homepage in default lang
myapp.com/de ---> Homepage in German
This functionality is not enabled by default, and must be passed as an option into the NextI18Next
constructor as a config option:
new NextI18Next({
localeSubpaths: {
de: 'de'
}
})
The localeSubpaths
object must also be passed into next.config.js
, via the nextI18NextRewrites
util, which you can import from next-i18next/rewrites
.
The localeSubpaths
option is a key/value mapping, where keys are the locale itself (case sensitive) and values are the subpath without slashes.
Now, all your page routes will be duplicated across all your locale subpaths. Here's an example:
----- Config -----
new NextI18Next({
localeSubpaths: {
fr: 'fr',
de: 'german',
en: 'eng',
}
})
----- Output -----
myapp.com/fr
myapp.com/german
myapp.com/eng
When using the localeSubpaths option, our middleware will redirect as needed in the wrapped getInitialProps
one level above your _app
, so none of your code will be called.
The main "gotcha" with locale subpaths is routing. We want to be able to route to "naked" routes, and not have to worry about the locale subpath part of the route:
<Link href='/some-page'>
With this link, we would expect someone whose language is set to French to automatically be directed to /fr/some-page
.
To do that, we must import Link
from your NextI18Next
instance, not next/router:
// This is our initialised `NextI18Next` instance
import { Link } from '../i18n'
const SomeLink = () => (
<Link href='/some-page'>
This will magically prepend locale subpaths
</Link>
)
We can also navigate imperatively with locale subpaths by importing Router
from your NextI18Next
instance. The exported Router shares the same API as the native Next Router. The push, replace, and prefetch functions will automatically prepend locale subpaths.
// This is our initialised `NextI18Next` instance
import { Router } from '../i18n'
const SomeButton = () => (
<button
onClick={() => Router.push('/some-page')}
>
This will magically prepend locale subpaths
</button>
)
In many cases, you'll need to know the currently active language. Most of the time, to accomplish this, you should use the withTranslation
HOC, which will pass an i18n
prop to the wrapped component and further asserts your component will get re-rendered on language change or changes to the translation catalog itself (loaded translations). More info can be found here.
If for some reason you need to access the current language and withTranslation
doesn't suit your needs, you can use the I18nContext
:
import { I18nContext } from 'next-i18next'
const { i18n: { language } } = useContext(I18nContext)
Key | Default value |
---|---|
browserLanguageDetection |
true |
defaultNS |
'common' |
defaultLanguage |
'en' |
ignoreRoutes |
['/_next/', '/static/', '/public/', '/api/'] |
otherLanguages (required) |
[] |
localeExtension |
'json' |
localePath (required) |
'/public/static/locales' |
localeStructure |
'{{lng}}/{{ns}}' |
localeSubpaths |
{} |
serverLanguageDetection |
true |
strictMode |
true |
use (for plugins) |
[] |
customDetectors |
[] |
shallowRender |
false |
This table contains options which are specific to next-i18next. All other i18next options can be passed in as well.
next export
is not supported.- To add a
lang
attribute to your top-level html DOM node, you must create a_document.js
file. - Localising
next/head
requires special consideration due to NextJs internals. - How to use multiple namespaces in the same component
next-i18next
is written in TypeScript and has full support for it. Refer to the usage guide here.
Thanks goes to these wonderful people (emoji key):
Rob Capellini 💻 | Alexander Kachkaev 📢 💬 🤔 💻 | Mathias Wøbbe 💻 🤔 | Lucas Feliciano 🤔 👀 | Ryan Leung 💻 | Nathan Friemel 💻 📖 💡 🤔 |
This project follows the all-contributors specification. Contributions of any kind welcome!
Thanks to BrowserStack for their support of this open-source project.