How to Create a Multilingual site in Next.js With next-i18next

How to Create a Multilingual site in Next.js With next-i18next

Crafting Multilingual Sites in Next.js with next-i18next: A Step-by-Step Guide to Implementing Multiple Languages.

Introduction

In an increasingly interconnected world, multilingual websites serve as indispensable tools for bridging language gaps and connecting with a global audience. By providing content in multiple languages, businesses and organizations elevate user experience, promote accessibility, and embrace inclusivity. Moreover, multilingual websites empower better SEO, broaden market outreach, and exemplify cultural sensitivity, resulting in enhanced engagement and seamless communication with diverse audiences worldwide.

As a striking reminder, only 10% of the world's population speaks English, emphasizing the critical importance of catering to other languages to truly connect with a vast majority of potential users.

To illustrate this significance further, even influential figures like MrBeast discuss the value of translation, underlining its impact on effective communication across linguistic boundaries, shorts video on YouTube where mrBeast talks about Spanish.

Install dependencies

To unlock the full potential of next-i18next and enable seamless language localization in your Next.js project, install the necessary dependencies. Simply execute the following command in your terminal:

Using npm:

npm install next-i18next

Alternatively, if you prefer using yarn:

yarn add next-i18next

Setting up next-i18next.config.js configuration

To configure next-i18next for your project, create a file named next-i18next.config.js in the root directory. Inside this file, define the internationalization settings as follows:

// next-i18next.config.js

module.exports = {
  i18n: {
    locales: ["sr", "en"],
    defaultLocale: "sr",
    ns: ["common", "home"],
  },
};

In this configuration, we specify the supported locales, where sr stands for Serbian and en for English. The defaultLocale is set to sr, meaning the Serbian language will be the fallback if no translation is available in the user's preferred language. Additionally, we define the namespaces ("common" and "home") for organizing and managing translations efficiently.

With this setup, your multilingual Next.js project is now equipped to cater to a diverse audience and provide seamless language localization across different sections of your website.

next.config.js for i18n setup

Directly reference the i18n settings from the next-i18next.config.js file. Update your next.config.js as follows:

// next.config.js

/** @type {import('next').NextConfig} */
const {i18n} = require('./next-i18next.config')
const nextConfig = {
  reactStrictMode: true,
  i18n,
}

module.exports = nextConfig;

By importing the i18n object from the next-i18next.config.js file, your next.config.js becomes more concise and easier to manage. This configuration ensures that your Next.js project is set up to handle internationalization seamlessly.

_app

// _app.js

import "@/styles/globals.css";
import Navbar from "../components/NavBar";
import { useRouter } from "next/router";
import {appWithTranslation} from 'next-i18next'
import { useTranslation } from "next-i18next";
import {serverSideTranslations} from 'next-i18next/serverSideTranslations'
function App({ Component, pageProps }) {
  const {t:translate} = useTranslation('home')
  const {locale, locales, push} = useRouter();
  return (
    <main>
    {/* Like this, you are sending all the languages that you specified */}
    {/* inside your config files earlier. */}      
      <Navbar locales={locales} translate={translate}/>}
      <Component {...pageProps} />
    </main>
  );
}

// embrace app with appWithTranslation
export default appWithTranslation(App);

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

Implementing Language Selection for Site Translation

// Navbar.js

const Navbar = ({locales, translate}) => {

  return (
    <div>
        <div>
            {translate("languages")}
            {locales?.map((l:any)=> (
                <Link href={'/'} locale={l}>
                    {l}
                </Link>
            ))}
        </div>
    </div>       
  );
};

export default Navbar;

Public folder

In the "public" folder, create a "locales" folder with two subfolders for the languages you want on your website. For example, "en" for English and "sr" for Serbian.

Inside each language folder, create "home.json" (can be named whatever you want) and "common.json" files. The "common.json" file is essential as it acts as a fallback for translations. If a translation is not found in the "home.json" file, it will look in the "common.json" file. You can leave the "common.json" file empty.

Code inside the functional component

Inside your functional component, you can use the following code:

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

export default function Home( ) {
const {t:translate} = useTranslation('home')
  return (
    <main>
      {/* Rename "some text key" whatever you want */}
      {translate("some text key")}

      {/* You can pass 'translate' to other components and then use it */}
      <Component translate={translate} />

      {/* You can pass the translated prop like this */}
      <Component title={translate("title")} />
    </main>
  );
}

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

Place this code inside the root index file of every page where you need translations. To avoid errors, pass the translations to other components via props instead of calling this code inside the index.js and another component on the same page. If you do that, you will encounter an error like this:

Now, when you have specified keys inside the functional component, you can create JSON files for each language:

In home.json for English (/locales/en/home.json):

{
  "some text key": "Hello World!",
  "title": "Web shop",
    // I included the "languages" key from the Navbar component here
    // to show you that you can translate everything inside your
    // application in this one JSON file.
  "languages": "Languages"
}

In home.json for Serbian (/locales/sr/home.json):

{
  "some text key": "Ćao svete!",
  "title": "Internet prodavnica",
  "languages": "Jezici"
}

What should I do if I have some array of objects with text to translate?

If you have an array of objects with text to translate, you can use the object's properties as keys to fetch the translations from the JSON files. Here's how you can implement it:

const katalozi = [
  {
    id: 1,
    title: "Bosch DIY measuring tools",
    //description: "Discover the latest price list of Bosch DIY measuring tools for June 2023. This comprehensive catalog provides the most current information on available measuring tools for DIY enthusiasts and hobbyists."
  },
  {
    id: 2,
    title: "Bosch accessories",
    //description: "Explore the latest offers in our catalog of Bosch accessories for July 2023. Browse a wide selection of premium Bosch accessories that will enhance your tools and simplify your projects."
  },
  {
    id: 3,
    title: "Bosch Dremel",
    //description: "Explore the latest price list of Dremel tools and accessories for June 2023. In our catalog, you can find a rich assortment of high-quality Dremel tools and accessories to meet all your creative needs."
  },
];

const Card = ({ translate }) => {
  return (
    <div>
        {katalozi.map((catalog) => (
          <div>
            <div>
              {translate(`${catalog.title}`)}
            </div>
            <div>
              {translate(`${catalog.id}`)}
            </div>
          </div>
        ))}
    </div>
  );
};

export default Card;

In this example, we use the title property as the key for translating the title and the id property as the key for translating the description. We pass the catalog.title and catalog.id directly to the translate function to get the translated description.

And in your JSON files, you can map the id and title to their respective translations:

// home.json ...\en
{
  "Bosch DIY measuring tools": "Bosch DIY measuring tools",
  "Bosch accessories": "Bosch accessories",
  "1": "Discover the latest price list of Bosch DIY measuring tools for June 2023. This comprehensive catalog provides the most current information on available measuring tools for DIY enthusiasts and hobbyists.",
  "2": "Explore the latest offers in our catalog of Bosch accessories for July 2023. Browse a wide selection of premium Bosch accessories that will enhance your tools and simplify your projects.",
  "3": "Explore the latest price list of Dremel tools and accessories for June 2023. In our catalog, you can find a rich assortment of high-quality Dremel tools and accessories to meet all your creative needs."
}

// home.json ...\sr
{
  "Bosch DIY measuring tools": "Bosch DIY merni alati",
  "Bosch accessories": "Bosch pribor",
  "1": "Upoznajte se sa najnovijim cenovnikom Bosch DIY mernih alata za jun 2023. Ovaj sveobuhvatni cenovnik donosi najaktuelnije informacije o raspoloživim mernim alatima za kućne majstore i entuzijaste.",
  "2": "Otkrijte najnovije ponude u našem cenovniku Bosch pribora za mesec jul 2023. Pregledajte bogat izbor vrhunskog Bosch pribora koji će unaprediti vaše alate i olakšati vaše projekte.",
  "3": "Istražite najnoviji cenovnik Dremel alata i pribora za jun 2023. U našem cenovniku možete pronaći bogat izbor visokokvalitetnih Dremel alata i pribora koji će ispuniti sve vaše kreativne potrebe."
}

By using the id and title properties as keys in the JSON files, you can easily access the translations for each item in the katalozi array.

Note: If you don't specify translations for the key used in {translate("key")}, it will render the key itself on the screen. In our example, we didn't specify translations for "Bosch Dremel" because it remains the same in both English and Serbian languages.

The defaultLocale is not retaining the default language

Next.js will automatically detect the user's preferred locale based on the Accept-Language header sent in the page request.

In this case, even though the default locale is set to "sr", the "en" locale is detected in the Accept-Language header, which leads to a redirection to the locale-prefixed path.

To disable this behavior, you can set "localeDetection" to false in the i18n options.

// next-i18next.config.js

module.exports = {
  i18n: {
    locales: ["sr", "en"],
    defaultLocale: "sr",
    ns: ["common", "home"],
    localeDetection: false
  },
};

From the Disabling Automatic Locale Detection Next.js documentation:

When localeDetection is set to false Next.js will no longer automatically redirect based on the user's preferred locale and will only provide locale information detected from either the locale based domain or locale path as described above.