preface

  • This article written by the big guy is very good, I will carry over to everyone to see
  • Do you want your software/website to be used only by English-speaking users? I don’t think so; you want your application to be language friendly. You don’t want your users to be restricted by language. Language shouldn’t be a barrier to reaching a billion users, so I’ll show you how to build Nextjs applications to accommodate a variety of languages here.

internationalization

Internationalization, wow, that’s a 20-letter word. Typing is too long, and even longer when you have to repeat it multiple times in text. That’s why it’s often abbreviated to i18n :). So for most of this article, I’ll use i18N instead of internationalization.

What is I18N?

According to Wikipedia, internationalization is the process of designing a software application so that it can adapt to a variety of languages and locales without engineering changes. Developers build software application content in a language they understand. Chinese developers build apps with Chinese content, English developers build apps with English, and again, it works in all the languages of the world. The content of the application is in the developer’s language. For example, a news blog application built by a Spanish developer will publish its news content in Spanish.

Nowadays, i18n

Is one of the most popular features in software applications. Most modern software supports the most popular languages you can find. Supporting I18N raises the bar for you internationally, because your application is not only applicable to a specific region, but to the entire world. According to the official nex.js page: 72% of consumers are more likely to stay on your site if it’s translated, and 55% of consumers say they only buy from e-commerce sites in their native language.

Applications that embody i18N support multiple languages. It can support as many languages as possible. These I18N-enabled applications can convert their content from one language to another. The supported languages are displayed in the drop-down list for you to select. Most applications do also support I18N by domain. For example, lit.com can support its Spanish language version in es.lit.com. This is another form of i18n.

Locale is a specific language supported by i18N-Apps. The standard format for defining a locale is a UTS locale identifier, while the general format for a locale is a locale script. For French in France, it’s fr-fr. American English is en-US, British English is en-en.

In the next section, we’ll look at how to build i18N-apps in Nextjs.

Internationalization and nex.js

Nex.js is an open source Web framework for building both server-side rendered and statically generated Web applications using React.js. Nex.js has always supported internationalization V10.0.0, which allows us to set the default language, the language currently in use, and the supported language. To set up the locale in the nex.js application, we first create a nex.config.js file in the root directory of the application. This is where we define the locale:

module.exports = { i18n: {/** * Provide locales you want to support in your application */ locales: ["en-US", "fr-FR", "es-ES"], /** * This is the default locale you want to be used when visiting * A non-locale prefixed path. */  defaultLocale: "en-US", }, };Copy the code

I18n tells nex.js that we want to activate/use the i18N feature in the nex.js application. The Locales array property is where we add the locale code for the locale we want to support. DefaultLocale saves the application’s defaultLocale code, which is the locale that will be used if no locale is active. Next. Js has two strategies for handling i18N in our application. The policies are: subpath routing and domain routing.

Subpath routing

Subpath routing policies involve adding the locale as part of the URL path without affecting the components when they are rendered.

Suppose we have this pages/users.js and we have the locale “en-us”, “es-es”, “fr-fr”, and “en-us” is the default locale.

Next. Js will map fr-fr and es-es to /users:

  • / fr – fr/users
  • / es – es/users

See locale prefixed with the /users path. Both routes point to pages/users.js. The default locale is not prefixed with a path. We can then access the locale information in Pages /users.js via the nex.js router. Using the useRouter() hook, we can get the currently active locales in the locale, all the locales in the locale, and the default locales in the defaultLocale.

import { useRouter } from "next/router";

const { locale, locales, defaultLocale } = useRouter();
Copy the code

See, we’ve refactored locale, locales, and defaultLocale from useRouter(). We can then use the locale value to know the language version of what we should render.

For a statically generated page or a rendered page on the server side, we can pass locale information via getServerSideProps or getStaticProps. These methods pass the item to the component from the object containing the item key they return. These getServerSideProps or getStaticProps methods take a context parameter, which is an object containing the locale, the currently active locale, all the locales in our application, and the defaultLocale, defaultLocale. We then pass this context to the props returned by the method so that we can access it from the component’s props:

export default function Users(props) {
const { locale, locales, defaultLocale } = props;
console.log(locale, locales, defaultLocale);

return (
<div>
<span>Users Component</span>
</div>
);
}

export const getStaticProps = async (context) => {
return {
props: { context },
};
};
Copy the code

So we can get locale information from the router and the getServerSideProps or getStaticProps method. Let’s demonstrate the use of both in the following sections.

Through the useRouter() hook

Build the Next. Js application:

yarn create next-app i18n-next
# OR
npx create-next-app i18n-next
Copy the code

Go to CD i18n-next and run yarn dev. We will create a news page that displays a list of news stories. The language of the news content will vary according to the locale passed to the URL path. So, we have news content in different languages. We will support English (default), French, and Spanish (Spanish 😁). First, we create next. Config.js in the root folder and add the following configuration:

module.exports = { i18n: {/** * Provide locales you want to support in your application */ locales: ["en-US", "fr-FR", "es-ES"], /** * This is the default locale you want to be used when visiting * A non-locale prefixed path e.g. `/hello` */ defaultLocale: "en-US", }, };Copy the code

This will activate i18N in our application. Let’s create a news.js file in the Pages component and paste the following code:

import Head from "next/head"; import Link from "next/link"; import { useRouter } from "next/router"; import styles from ".. /styles/Home.module.css"; import NewsCard from "./.. /components/newscard"; const newsContent = { "en-US": { title: "Your News", content: [ { title: "Otter. Ai's new assistant can automatically transcribe your Zoom meetings", synopsis: "A.I.-powered voice transcription service Otter.ai wants to make it even easier for its business users to record their meetings. The company is today introducing a new feature, Otter Assistant, whic...", imageUrl: "", }, // ... ] , }, "fr-FR": { title: "Vos nouvelles", content: [ { title: "Le Nouvel assistant d 'otter. Ai peut transcrire automatiquement vos reunions Zoom", synopsis: Le service de transcription vocale alimente par A.I. Otter. Ai veut rendre encore plus facile pour ses utilisateurs La Societe presente aujourd'hui une nouvelle fonctionnalite Otter Assistant, qui ...", imageUrl: "", }, // ... ] , }, "es-ES": { title: "Tus noticias", content: [ { title: "El nuevo asistente de Otter. Ai puede transcribir automaticamente SUS reuniones de Zoom", synopsis: "El Servicio de transcripcion de voz con tecnologia de Inteligencia artificial Otter. Ai Quiere facilitar aun mas a SUS La Compania Presenta Hoy una Nueva Funcion, Otter Assistant, que ...", imageUrl: "", }, // ... ] ,}}; export default function News(props) { const { locale, locales, defaultLocale, asPath } = useRouter(); const { title, content } = newsContent[locale]; return ( <div className={styles.container}> <Head> <title>TV</title> <link rel="icon" href="/favicon.ico" /> </Head> <main className={styles.main}> <div className={styles.breadcrumb}> <div style={{ padding: "4px", marginRight: "4px", }} > <span>Current Language: </span> <span style={{ borderRadius: "3px", backgroundColor: "blue", color: "white", padding: "2px", }} > {locale} </span> </div> <Link activeClassName={locale === "es-ES"} href={asPath} locale="es-ES" > es-ES </Link> <Link activeClassName={locale === "en-US"} href={asPath} locale="en-US" > en-US </Link> <Link activeClassName={locale === "fr-FR"} href={asPath} locale="fr-FR" > fr-FR </Link> </div> <div className={styles.newscontainer}> <div className={styles.yournewscasts}> <h3>{title}</h3> </div> <div> {content.map((newsItem, i) => ( <NewsCard key={i} news={newsItem} /> ))} </div> </div> </main> </div> ); }Copy the code

See we first set up our news content. The newsContent object holds our newsContent in French, Spanish, and English. It has “en-us”, “es-ES”, and “fr-fr” attributes, all of which have “title” and “Content” keys. The headline is what we will display at the top of the page and it will show “Your News” so each region has its own version of “your News” in the language. Content is an array that contains the actual news content for our page.

We’ll use the locale in the useRouter() hook to get the language-specific news content. If the locale is FR-fr, we will refer to fr-fr from the newsContent object, as newsContent[” fr-fr]. This is what we do in the News component. We call the useRouter() hook and deconstruct the locale and asPath from it. As we already know, locale holds the current active locale. AsPath gives us a route to the current locale. For example, if the current locale is en-us, asPath is/en-us /news.

Next, we retrieve the specific language object from the newsContent object by doing the following: newsContent[locale]. We deconstructed the title and the content. Now, let’s render them on the UI. In the News component UI, we display the current locale:

<div
  style={
    {
      // ...
    }
  }
>
  <span>Current Language: </span>
  <span
    style={
      {
        //...
      }
    }
  >
    {locale}
  </span>
</div>
Copy the code

Next, we set the conversion between the locales:

<Link
    activeClassName={locale === "es-ES"}
    href={asPath}
    locale="es-ES"
>
    es-ES
</Link>

<Link
    activeClassName={locale === "en-US"}
    href={asPath}
    locale="en-US"
>
    en-US
</Link>
<Link
    activeClassName={locale === "fr-FR"}
    href={asPath}
    locale="fr-FR"
>
    fr-FR
</Link>
Copy the code

This displays links with text ES-ES, en-us, and FR-fr, with href links to/en-us /news, /news, and/fr-fr /news, respectively. If you click es-ES, it will load the route ES-ES /news, the current locale will change to ES-ES, and our component will load the news content for ES-ES. Therefore, the above link is used to change the language of our application. Clicking on any of these will translate the content of the application into the language of the click. Finally, display the contents array:

<div>
  {content.map((newsItem, i) => (
    <NewsCard key={i} news={newsItem} />
  ))}
</div>
Copy the code

The NewsCard component renders the news for each news item. Our application will look like this:

Our English news website

Our news site in Spain (In Spanish)

Our French news site we clicked on the link to change the language. If we decide to use next/ Router programmatically to navigate our route:

const router = useRouter();

router.push("/news", "/news");
Copy the code

We must pass a locale option in the object of the third parameter to navigate to the locale route:

const router = useRouter();

router.push("/news", "/news", { locale: "es-ES" });
Copy the code

This will load the /fr-FR/news route, so the locale will be French.

By getServerSideProps or getStaticProps now we can use getServerSideProps or getStaticProps to demonstrate i18n. This will be a small editor in our news component:

// ...
export default function News(props) {
const { locale, locales, defaultLocale } = props.context;
const { title, content } = newsContent[locale];
return (
<div className={styles.container}>
<Head>
<title>TV</title>
<link rel="icon" href="/favicon.ico" />
</Head>

      <main className={styles.main}>
        <div className={styles.breadcrumb}>
          <div
            style={{
              padding: "4px",
              marginRight: "4px",
            }}
          >
            <span>Current Language: </span>
            <span
              style={{
                borderRadius: "3px",
                backgroundColor: "blue",
                color: "white",
                padding: "2px",
              }}
            >
              {locale}
            </span>
          </div>
          <Link
            activeClassName={locale === "es-ES"}
            href={`/es-ES/news`}
            locale="es-ES"
          >
            es-ES
          </Link>

          <Link
            activeClassName={locale === "en-US"}
            href={`/en-US/news`}
            locale="en-US"
          >
            en-US
          </Link>
          <Link
            activeClassName={locale === "fr-FR"}
            href={`/fr-FR/news`}
            locale="fr-FR"
          >
            fr-FR
          </Link>
        </div>

        <div className={styles.newscontainer}>
          <div className={styles.yournewscasts}>
            <h3>{title}</h3>
          </div>

          <div>
            {content.map((newsItem, i) => (
              <NewsCard key={i} news={newsItem} />
            ))}
          </div>
        </div>
      </main>
    </div>
);
}

export async function getStaticProps(context) {
return {
props: {
context,
},
};
}
Copy the code

We exported a getStaticProps method from the news.js file, which will cause the News component to be generated statically at build time. If we use getServerSideProps instead of getStaticProps, the page will be generated from the server.

The main thing here is that we pass the context parameter to the props object and return that object from the getStaticProps method. Now we can access the context in the News component through its props parameter; Props. Context. And then we deconstruct the linguistic context from there. Everything else holds, we just need to refactor our transformation links because pros. Context doesn’t have asPath, so we set them up manually.

We can do this programmatically in an efficient way, and I’ll leave it up to the reader to make the above better. 😁 So our application will run the same way as when we used useRouter().

Domain routing

Domain routing policies involve mapping the locale to the top-level domain. For example, we set the locale:

module.exports = {
i18n: {
locales: ["en-US", "fr-FR", "es-ES"],
defaultLocale: "en-US",
},
};
Copy the code

We can set the locale to be provided depending on the domain.


module.exports = {
  i18n: {
    locales: ["en-US", "fr-FR", "es-ES"],
    defaultLocale: "en-US",
    domains: [
      {
        domain: "mynews.com",
        defaultLocale: "en-US",
      },
      {
        domain: "mynews.es",
        defaultLocale: "es-ES",
      },
      {
        domain: "mynews.fr",
        defaultLocale: "fr-FR",
      },
    ],
  },
};
Copy the code

Next. Js will set the URL path according to the domain creation region we set in the domain array property above. For now, the French version of our website mynews.com/news will be mynews.fr/news. The Spanish version will be Mynews.es /news.

Other I18N implementations

  • There are some great libraries that bring i18N to next-js and make it very easy to use. Check them out below:
  • react-intl
  • react-i18next
  • lingui
  • rosetta
  • next-intl

conclusion

Next. Js makes i18N very easy and simple. All you have to do is set up your locale in the nex.config.js file and the multilingual data, and you’re ready to go. You just need to get the locale information from the useRouter() hook or through the getStaticProps or getServerSideProps methods.

The source code

See the full code here