Translate Your NextJS App with I18next

next-i18next market itself as the easiest way to translate your NextJS app. There are properly simpler translations libraries but none as advanced and widespread. I18next is the market leader with around 3,3 million weekly downloads on NPM. Its primary translation package is i18next
. It then offers a wide range of framework specific sub packages. This is where next-i18next
comes in. The official package for integration with NextJS.
One advantage next-i18next
has over other simpler translation solutions is that you can namespace it so that each page only loads the required translations needed for this page. For apps with many translations and/or languages this is an advantage as it avoids loading all translations and slowing the page.
Enable build-in NextJS localization configuration
NextJS has build-in localization capabilities. It handles multilingual routes and a hook that returns current locale const { locale } = useRouter()
. To enable multilingual routing update the configuration in next-i18next.config.js
(this is a i18next
specific file that can then be imported into next.config.js
):
module.exports = { // https://www.i18next.com/overview/configuration-options#logging debug: process.env.NODE_ENV === 'development', i18n: { defaultLocale: 'en', locales: ['en', 'de'], }, reloadOnPrerender: process.env.NODE_ENV === 'development', };
Now import the i18n
into next.config.js
(this is just done to keep all localization configuration in one place):
const { i18n } = require('./next-i18next.config'); module.exports = { i18n, };
NextJS will now beside the previous default URL routes have paths with the prefix /en/
and /de/
. Routes without either /en/
or /de/
will render the defaultLocale
.
Install and configure next-i18next
Run yarn add next-i18next
and add the localization files:
|-- public | `-- locales | |-- de | | |-- common.json | | |-- footer.json | | `-- second-page.json | `-- en | |-- common.json | |-- footer.json | `-- second-page.json
Each language specific folder (de
or en
) contains the namespaced files to allow for optimized builds embedded only the required translations. It further makes translations more manageable.
Wrap your app in the higher-order component provided by next-i18next
to access the localization functionality. Add it to the pages/_app.js
:
import { appWithTranslation } from 'next-i18next'; const MyApp = ({ Component, pageProps }) => <Component {...pageProps} />; export default appWithTranslation(MyApp);
To improve SEO of your app, you can add localization to the HTML document in pages/_document.js
:
import Document, { Html, Head, Main, NextScript } from 'next/document'; import i18nextConfig from '../next-i18next.config'; class MyDocument extends Document { render() { const currentLocale = this.props.__NEXT_DATA__.locale || i18nextConfig.i18n.defaultLocale; return ( <Html lang={currentLocale}> <Head /> <body> <Main /> <NextScript /> </body> </Html> ); } } export default MyDocument;
Add translation to pages
In pages/index.js
you can the translation files used for this page via the getStaticProps
function provided by next
:
export const getStaticProps = async ({ locale }) => ({ props: { ...(await serverSideTranslations(locale, ['common', 'footer'])), }, });
This load 2 translation files, common.json
and footer.json
. If a <Header />
component is added to this specific page, its translations must likewise be added loaded: serverSideTranslations(locale, ['common', 'footer', 'header'])
.
In the React component, the translation can be used like:
import { useTranslation, Trans } from 'next-i18next' const Homepage = () => { const { t } = useTranslation('common') return ( <main> <Header heading={t('h1')} title={t('title')} /> <p> <Trans i18nKey='blog.optimized.answer'> Then you may have a look at <a href='https://locize.com/blog/next-i18next/'>this blog post</a>. </Trans> </p> </main> ) }
The t
function returns the raw translated string, in this case from the common.json
translation file. The <Trans />
allows for complex translations containing HTML
markup. See full example.
Add another page with translation
Adding another translation follows the same pattern. Let's say you wanted to add a custom 404 page. First add a translation file containing error translations in public/locales/en/errors.json
containing:
{ "404": { "title": "Ups, content not found", "help": "Please ensure the url is correct." } }
Then add pages/404.js
:
import React from 'react'; import { useTranslation } from 'next-i18next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; const Custom404 = () => { const { t } = useTranslation('errors'); return ( <div> <h1>{t('404.title')}</h1> <p>{t('404.help')}</p> </div> ); }; export const getStaticProps = async ({ locale }) => ({ props: { ...(await serverSideTranslations(locale, ['errors'])), }, }); export default Custom404;
Now to add the translation for this, you can either retrieve them from translator and add them manually to the public/locales/de/errors.json
file or simply use git18n.com. Using git18n.com the developer can request a translation when the feature is ready and the translator can directly add translations to the pull request via git18n.com.
For the above example is available on: git18n.com/repos/lassegit/next-i18next/4. The pull request is also on Github.
On git18n.com you can find the pull request and request translation:
The translator will retrieve an email containing a link where the translations can be added via git18n.com and then pushed directly to the pull request (ensuring CI pipeline has the latest translations):
This makes it a lot easier for developers, since they don't need to fiddle around with translations. Everything is maintained by git18n.com (it also removes unused translation keys to make maintenance easier).
For the translators it saves lot of complexity by keeping all translations in one place (no more need to manage spreadsheets besides the translations in code public/locales/**/*
). And also makes changing and improving translations a lot easier since developers doesn't have to be instructed for every little change. The translators can actually do it themselves.