DEV Community

Cover image for Next.js — The Scalable Way to Internationalize Using next-i18next
i18nexus
i18nexus

Posted on

Next.js — The Scalable Way to Internationalize Using next-i18next

In this tutorial, we’re going to learn how to internationalize a Next.js app using next-i18next. To make things even easier, we’re going to use i18nexus to auto-translate and manage our strings for us.

Let’s go! ⚡️

Getting Started

Let’s bootstrap together a simple Next.js application using create-next-app:

npx create-next-app

After typing in a title for our app, we will cd into the app directory and install next-i18next:

npm install next-i18next --save

Time to internationalize! 🌐

Configuration

With the release of Next.js v10, we received an awesome built-in solution for handling internationalized routing and locale detection. next-i18next is designed to work hand-in-hand with this new internationalization support.

To use next-i18next, we need to create an i18next config file at the root of our app called next-i18next.config.js. This will define the languages we want our app to use. Let’s support English and Swedish.

We need to initialize our defaultLocale (the default language for our app) and locales (a list of languages that we want our app to support):

next-i18next.config.js

module.exports = {
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'sv']
  }
}
Enter fullscreen mode Exit fullscreen mode

We also need to add the i18n property to next.config.js by simply importing the i18n object from the i18next config:

next.config.js

const { i18n } = require('./next-i18next.config');

module.exports = {
  i18n,
  reactStrictMode: true
}
Enter fullscreen mode Exit fullscreen mode

The next-i18next library uses the same i18n config structure that Next requires. So instead of having to manage the same settings in two places, we simply import the next-i18next config into next.config.js as recommended in the next-i18next docs.

appWithTranslation

One last step for setting up next-i18next is to wrap our app with the appWithTranslation HOC (higher-order component). This component will provide our i18next context to all of our pages. Our _app.js file should look like this:

_app.js

import '../styles/globals.css';
import { appWithTranslation } from 'next-i18next';

const MyApp = ({ Component, pageProps }) => <Component {...pageProps} />

export default appWithTranslation(MyApp);
Enter fullscreen mode Exit fullscreen mode

And now we’re ready to go! 🚀

i18nexus Integration

If you’ve used i18next before but have never used it with i18nexus, you’re in for a treat.

i18nexus makes managing our translations a million times easier by storing our app texts in the cloud. It even Google translates our strings to as many languages as we need. Whenever we’re ready to hire professional translators, we just invite them to our i18nexus project and let them edit the translations!

Let’s do it!

If you don’t already have an i18nexus account, go to i18nexus.com and sign up for a free account. After naming our project we’ll be directed to our language dashboard:

i18nexus project dashboardi18nexus project dashboard

The first language tile is our base language, which should match the language we set for the defaultLocale property in next-i18next.config.js.

Next, we’ll click Add Language to select the locales that we want our app to support. Since we already added sv (Swedish) to our locales list in next-i18next.config.js, we’ll select Swedish:

Adding Swedish as a supported languageAdding Swedish as a supported language

Now let’s click Open Project in the top right corner to go to the Strings Management page where we will be adding our strings.

Namespaces

At the top of the page, there is a dropdown labeled namespaces, which contains one namespace already created for us called “default”.

It is convention to have one namespace for each Page in your app, as well as a namespace called “common” for common strings that appear on more than one page.

Let’s rename the “default” namespace to “common”, and then create a namespace called “home” to be used for our Home page:

Managing namespacesManaging namespaces

Important: Even though we will not be using the “common” namespace in this walkthrough, it is required. We will discuss why shortly. 🙂

To add our first string, click Add String. I’m going to add a string in my “home” namespace that says “Hello, and welcome to my app!”:

Adding a new string with key “welcome_msg”Adding a new string with key “welcome_msg”

The key is how we will reference this string in our code.

The value is the text that will be rendered in our app.

The details field is optional. It is meant to provide any extra information about the context of our string for when we’re ready to bring in professional translators. We can even add an image here for more context.

After adding the string, we can expand the row to see our automatic Google translations:

New strings are automatically translated to all of our supported languagesNew strings are automatically translated to all of our supported languages

Connecting our Translations

We’re going to use the i18nexus CLI to import our i18nexus translations in our Next.js app:

npm install i18nexus-cli -g

If we go to the Export tab in i18nexus, we’ll be able to find our project API key:

i18nexus Export tabi18nexus Export tab

In our app directory, all we have to do is run i18nexus pull with our API key and all of our latest translations will be downloaded to our project directory!

$ i18nexus pull --api-key <YOUR_API_KEY>

Now all our translations will be located in public/locales, which is where next-i18next expects them.

We can also add our API key as an environment variable named I18NEXUS_API_KEY so that we can just use i18nexus pull without typing your API key every time.

To do this, we simply create an environment variable file called .env at the root of our app that contains I18NEXUS_API_KEY=YOUR_API_KEY.

Bonus Points:

If we want to automatically pull our latest translations every time we start up our dev server or build our app, all we have to do is update our scripts in package.json:

package.json

...
"scripts": {
   "dev": "i18nexus pull && next dev",
   "build": "i18nexus pull && next build",
   "start": "i18nexus pull && next start"
 }
...
Enter fullscreen mode Exit fullscreen mode

If you do this, you should also install the i18nexus-cli as a dev dependency:

npm install i18nexus-cli --save-dev

Rendering our Translations

Right now our app is just using the boilerplate create-next-app home page:

Boilerplate create-next-appBoilerplate create-next-app

serverSideTranslations

In our home page we need to import a function called serverSideTranslations from next-i18next. This function needs to be run in getStaticProps on each page-level component. It provides our page with our translations and configuration options as props. We’ll add this to our pages/index.js:

pages/index.js

import { serverSideTranslations } from 'next-i18next/serverSideTranslations';

export async function getStaticProps({ locale }) {
  return {
    props: {
      ...(await serverSideTranslations(locale, ['home'])),
    }
  } 
}

...
Enter fullscreen mode Exit fullscreen mode

The severSideTranslations function accepts a locale as the first argument and the namespaces required for this page as the second argument. This ensures that our app only has to load the namespaces needed for the page. The locale is passed down from getStaticProps by Next.

useTranslation

Lastly, let’s import the useTranslation hook from next-i18next. The useTranslation hook contains a function called t that takes a key as an argument and renders the proper translation.

I’m going to clear out most of the boilerplate that was generated by create-next-app and just render a single line of text on my Home page. Here’s my entire home page using useTranslation to render my welcome_msg string:

pages/index.js

import Head from "next/head";
import styles from "../styles/Home.module.css";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { useTranslation } from "next-i18next";

export async function getStaticProps({ locale }) {
  return {
    props: {
      ...(await serverSideTranslations(locale, ["home"]))
    }
  };
}

export default function Home() {
  const { t } = useTranslation();

  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>{t("home:welcome_msg")}</h1>
      </main>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Notice that when we use useTranslation, we need to specify the namespace with the key like so: home:welcome_msg.

The only time we do not need to specify the namespace in useTranslation is when the key we are referencing is in our default namespace. By default, i18next sets our default namespace to “common”. If you’d like, you can change your default namespace via the defaultNS config option in next-i18next.config.js.

Now lets run npm run dev, and check it out!

http://localhost:3000

Rendering English translationRendering English translation

We did it! 🎉

Using Other Languages

Right now, our app is using en because that is what we set as our defaultLocale in next.config.js.

If you recall, we added Swedish (sv) to our list of locales. To see our app in Swedish, all we have to do is add /sv to the end of the URL. If a user’s browser language is set to Swedish, Next will automatically redirect them to the /sv route. Let’s see what out app looks like in Swedish:

http://localhost:3000/sv

Rendering Swedish translationRendering Swedish translation

Looks like we’re internationalization pros! 😎

To learn more about internationalized routing and changing to different languages, take a quick read of the Next.js docs here.

That’s all folks!

You now have a Next.js app fully set up with internationalized routing, i18next integration, and automated translation management with i18nexus!

Discussion (1)

Collapse
aleattorium profile image
Jean Lucas ⚡️

Just a warning for those reading, this is an ad. You have to pay for this solution, so be careful because even if it is scalable, the price also scales with your application.