How to build multi-language website with Next.js (PART 1)

Building from scratch an application that supports multiple languages.

April 06, 2021

React

Next

Javascript

A lot of people, like me, are trying to get jobs outside their own country so to be able to do that we need to learn and master other languages. In the programming world (and to be honest, in almost all areas that involve technology in some degree), the universal language is the english.

With that, beyond mastering communicating (which I mean, talking and listening) in those languages, we need to write our CV, blog posts and copies in this universal language. So, today I'll be writing about how to manage a multi-language website using Next.js.

Setting up the Project

Let's set up our Next.js project using the create-next-app lib, which give us a pretty good template (you can follow up to see options in here)

npx create-next-app@latest
# or
yarn create next-app
# or
pnpm create next-app

Altought Next.js providers internationalised routing, it doesn't handle management of translation content, so to be able to achieve that we're going to install next-i18next dependency for our project:

yarn add next-i18next react-i18next i18next
# or
npm install --save next-i18next react-i18next i18next

Configuring Next.js with I18n

Following the next-i18next documentation, we need set up the project for it:

  1. First, create a next-i18next.config.js file in the root of your project.
  • Inside it we need to add the locales configuration, based on your locale languages, which in my case is:
module.exports = {
    i18n: {
      defaultLocale: 'en',
      locales: ['en', 'pt-BR'],
    },
  }
  1. Next, we're going to modify the next.config.js file, by passing the i18n config that we previously set up on our next-i18next.config.js file.
const { i18n } = require('./next-i18next.config')

module.exports = {
  {...}
  i18n,
}
  1. Now we need to create a locales folder inside public and create our different languages folder with a common.json file each. It's important to say that the common.json it's basically a set of texts that will be used for all components, but you can create others for specific components)
└── public
    └── locales
        ├── en
        |   └── common.json
        └── pt-BR
            └── common.json
  1. Mostly important and the last step of the configuration, we need to connect the I18n with our app. Open the pages/_app.js directory and wrap the default export with appWithTranslation from next-i18next, following as below:
import '../styles/globals.css'
import { appWithTranslation } from 'next-i18next'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}


export default appWithTranslation(MyApp)

That's all! Now we have all configuration set up and we can start using it.

Implementing different languages in components using SSR

It's important to know that next-i18next added support for using SSR, which basically can be done by using the getStaticProps or getServerSideProps from Next.js. But let's continue:

  1. Let's define some example text in all our different locale folders, I will be creating a title field:
// locale/en/common.json
{
    "title": "Building a multi-language website with Next.js"
}
// locale/pt-BR/common.json
{
    "title": "Construindo um website com suporte a diversas linguagens, utilizando Next.js"
}
  1. As mentioned above, to have Next.js support, we need to use getStaticPropsor getServerSideProps (you can check usage details of each here). With the decision in place, we're going to import serverSideTranslations from next-i18next and add to our choosen method.

getStaticProps

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

export const getStaticProps = async ({ locale }) => {
  return {
    props: {
      ...(await serverSideTranslations(locale, ['common'])),
    }
  }
}

getServerSideProps

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

export const getServerSideProps = async ({ locale }) => {
  return {
    props: {
      ...(await serverSideTranslations(locale, ['common'])),
    }
  }
}

Another really important thing to remember is that we can have specific translations (as I mentioned prior) for each component. Let's say I created a new file in our locales named example.json, and we want to add the translation to the same component. We would do:

export const getStaticProps = async ({ locale }) => {
  return {
    props: {
      ...(await serverSideTranslations(locale, ['common',
      'example']))
    }
  }
}
  1. Last, the display!! Let's add the translation itself to our Component. Let's import useTranslations from react-i18next dependency and destructure it to get property t (which will be then used for translating):
import { useTranslation } from 'react-i18next'

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

  return (
    <div>
      <main>
        <h1>
          {t('title')}
        </h1>
      </main>
    </div>
  )
}

export const getStaticProps = async ({ locale }) => {
  return {
    props: {
      ...(await serverSideTranslations(locale, ['common']))
    }
  }
}

Seeing the Results

Accessing your YOUR_URL/$LANGUAGE, which in my case is http://localhost:3000/${CURRENT_LANGUAGE}.

English - http://localhost:3000/en

MY_WEBSITE_IN_ENGLISH

Portuguese - http://localhost:3000/pt-BR

MY_WEBSITE_IN_PORTUGUESE

Conclusion

There we have it! I hope the post it's clear and you guys were able to follow. Feel free to tag me into any doubts and providing any feedbacks. In part 2, we're going to understand on how to build a simple toggle for changing languages! Stay tuned

To check all the work done here, you can go to the GitHub Repository