Login

Nextjs+next-intl multi-language internationalization best practice (APP router)

Author:neo yang Time:2024/02/19 Read: 13284
Nextjs offers two routers: APP and Page, with Page being phased out. The author previously used Page router internationalization, but has since implemented internationalization based on the APP router. They evaluated several solutions and found that next-intl is the simplest and most successful . The post outlines the directory structure, routing, middleware setup, how to load translation files, and how to implement translations, emphasizing that regardless of the internationalization solution chosen, routing, file structure, and translation implementation are key aspects.

Nextjs has two routers: APP router and Page router.

Some time ago, under the Page router, nextjs was internationalized and summarized:

But the Page router will be the router gradually phased out by Nextjs. Therefore, recently, Nextjs has been internationalized based on APP router.

1. Selection of Nextjs internationalization solution

1. i18next + react-i18next + i18next-resources-to-backend + next-i18n-router

It seems that most people are using this. I tried it and it was quite complicated. I didn't succeed yet, so I gave up.

2. next-i18n-router react-intl

It looked quite complicated, but because of the shadow of 1, I gave up.

3. next-intl

next-intl is a complete internationalization solution for nextjs and does not require other software packages. And the configuration is relatively simple. More importantly, I configured it successfully and encountered no inexplicable problems.

So, this is it.

2. Directory structure

|app ..[locale] ...layout.js ...page.js |components ..LangSwitcher.js |public ..locales ...en ....common.json ...es .... common.json |i18n.js |navigation.js |middleware.js |next.config.js

3. International routing

1. navigation.js

This is a configuration file, and the items configured in this file will be called in some other files.

import { createSharedPathnamesNavigation } from 'next-intl/navigation'; export const locales = ['en', 'de', 'es', 'ja','ko','it','pt']; export const localePrefix = 'as-needed'; export const defaultLocale= "en"; export const localeItems= [ { name: "English",code: "en", iso: "en-US", dir: "ltr" }, { name : "español",code: "es", iso: "es-ES", dir: "ltr" }, { name: "中文",code: "zh_cn", iso: "zh-CN", dir: " ltr" }, { name: "Deutsch",code: "de", iso: "de-DE", dir: "ltr" }, { name: "Italiano",code: "it", iso: "it- IT", dir: "ltr" }, { name: "日本语",code: "ja", iso: "ja-JP", dir: "ltr" }, { name: "한국인",code: "ko ", iso: "ko-KR", dir: "ltr" }, { name: "Português",code: "pt", iso: "pt-PT", dir: "ltr" }, ] export const { Link , redirect, usePathname, useRouter } = createSharedPathnamesNavigation({ locales, localePrefix });

2. [locale] directory

All page files in the app directory are placed in the [locale] directory.

3. Middleware: middleware.js

illustrate:

Implement the following style of URL

Default language URL:

The homepage is: www.xxx.com

The inner page is: www.xxx.com/about

URLs in other languages: (Take Spanish as an example)

The homepage is: www.xxx.com/es

The inner page is: www.xxx.com/es/about

In addition, if the default language is entered in the URL, for example, the default language is English, the user enters the URL: www.xxx.com/en

Then, it will automatically go to www.xxx.com

Code:

import createMiddleware from "next-intl/middleware"; import { defaultLocale, localePrefix, locales } from '@/navigation'; export default createMiddleware({ locales, localePrefix , defaultLocale, localeDetection: false, }); export const config = { / / Skip all paths that should not be internationalized. // This skips the folders "api", "_next" and all files // with an extension (eg favicon.ico) matcher: ["/((?!api|_next| .*\\..*).*)"], };

4. Load translation files

1. Translate files

illustrate

My translation files are placed in the "public" directory, but in fact, the translation files can also be placed in other directories, as long as the corresponding path is configured in the i18n.js file.

The translation file is a json file, and the json file can be in a nested format or not. The two, ultimately, will be referenced slightly differently.

Code (not nested)

{ "aaa": "hi", }

Code (nested)

{ "bbb":{ "aaa":"hi", } }

2. i18n.js file

illustrate:

This file is used to import translation files. The key is to configure the path of the translation file, which must be consistent with the path where your translation file is located.

${locale} in the path indicates the language.

code

import { getRequestConfig } from "next-intl/server"; // Create this configuration once per request and // make it available to all Server Components. export default getRequestConfig(async ({ locale }) => ({ // Load translations for the active locale. messages: (await import(`./public/locales/${locale}/common.json`)).default, }));

3. Configuration of next.config.js

/** @type {import('next').NextConfig} */ const nextConfig = {} const withNextIntl = require("next-intl/plugin")("./i18n.js"); module.exports =withNextIntl (nextConfig)

5. Implement translation

Code in layout.js:

import "./globals.css"; import { NextIntlClientProvider, useMessages } from 'next-intl'; import { locales } from '@/navigation'; import Header from '@/components/Header' import Footer from '@/components /Footer' export default function RootLayout({ children, params: { locale } }) { if (!locales.includes(locale)) { notFound(); } const messages = useMessages(); return (
    <html lang="{locale}">
      <nextintlclientprovider locale="{locale}" messages="{messages}">
      <body><header />{children}<footer /></body>
      </nextintlclientprovider>
    </html>
  );
}

Code in page.js:

The parameter locale represents the current language.

If the translation file is not nested, then const t = useTranslations();

If the translation file is nested, use something like: const t = useTranslations("bbb"); based on the file structure and actual needs.

import { useTranslations } from &#039;next-intl&#039;; import { Link } from &#039;@/navigation&#039;; export default function Home({ params: { locale } }) { const t = useTranslations(); return (<>   
<link href="{"/"}"><h1>{t(&quot;aaa&quot;)}</h1></Link>
</>)

6. Language switcher

1. Effect

Pull down to select a language, and the current page switches to the page in the selected language.

2. Code

"use client";
import { useLocale } from "next-intl";
import { localeItems, useRouter, usePathname, defaultLocale } from '@/navigation';
export default function LangSwitcher() {
  const locale = useLocale();
  const router = useRouter();
  const pathname = usePathname();
  console.log(locale)

  const handleChange = (e) => {
    router.push(pathname, { locale: e.target.value });
  };
 
  return (
    <div>
      <select
      value={locale}
        onChange={handleChange}
        className="h-8 m-2 p-1 rounded border-current"
      >
        <option value={locale} > {GetLangData(locale).name}</option>

        {localeItems.map((item) => {

          if (item.code === locale) return null
          return (<option key={item.code} value={item.code}>
            {item.name}
          </option>)
        })}
      </select>
    </div>
  );
}
export function GetLangData(defaultLocale) {
  var res = {}
  {
    localeItems.map(locale => {
      if (locale.code === defaultLocale) {
        console.log(locale)
        res = locale
      }
    })
  }
  return res

}

7. SEO and search engine friendly

1. Metadata

Nextjs APP router generates the title and Meta data of the page. I tried three methods:

The first way: use it directly in layout.js: expert const metadata={}

The second way: use dynamically generated Meta data in page.js

For reference:Optimizing: Metadata | Next.js (nextjs.org)

In these two ways, I failed for multi-language title and Meta data. Finally, the third method was used, which is also the simplest and most straightforward method.

The third way: write the title tag and Meta tag directly in page.js.

Therefore, the code of page.js was changed to this:

import { useTranslations } from &#039;next-intl&#039;; import { Link } from &#039;@/navigation&#039;; export default function Home({ params: { locale } }) { const t = useTranslations(); return (<>  
          <title>{t(&quot;site-name&quot;)}</title>
        <meta name="keyword" content="{t("site-name")}"></meta>
        <meta name="description" content="{t(&quot;site-description&quot;)}"></meta>
        <meta property="og:type" content="website"></meta>
        <meta property="og:title" content="{t(&quot;site-name&quot;)}"></meta>
        <meta property="og:description" content="{t(&quot;site-description&quot;)}"></meta>
        <meta property="og:site_name" content="{t(&quot;site-name&quot;)}"></meta>
        <meta property="og:image" content="/images/xxx.png"></meta>
        <meta property="og:image:width" content="512"></meta>
        <meta property="og:image:height" content="512"></meta>
        <meta property="og:image:alt" content=""></meta>
 
<link href="{"/"}"><h1>{t(&quot;aaa&quot;)}</h1></Link>
</>)

2. Multilingual navigation: Make it easier for search engines to discover multilingual URLs

illustrate

The language switcher is front-end rendered, so I added back-end rendered multi-language navigation at the bottom of the page, which is the component Footer.js. Clicking on the corresponding language will lead to the homepage of the website in this language.

Footer.js code is as follows:

import Link from 'next/link'
import { localeItems,defaultLocale } from '@/navigation';


export default function Footer(){


  return (<>
    <div className="divider"></div>
    <footer className="footer footer-center p-10 bg-base-200 text-base-content rounded">
      <nav className="flex flex-wrap gap-4">

     {localeItems.map(locale => {
          if (locale.code === defaultLocale) return(<Link className="link link-hover" href="/"  key={locale.code}>{locale.name}</Link>)
          const thehref="/"+locale.code
          return (<Link className="link link-hover" href={thehref} key={locale.code}> {locale.name}</Link>)
        })} 
  </nav> 
      <aside className='p-6 text-center'>
        <p>Copyright © 2024 - All right reserved by xxx.com</p>
      </aside>
     
    </footer>
  </>
    
  )
}

8. Summary

International routing, structure and introduction of translation files, and implementation of translation. No matter what kind of internationalization plan, these three aspects will be involved. Although it is not troublesome, in general, next-intl is relatively simple.

9. Reference

A Deep Dive into Next.js App Router Localization with next-intl

A complete guide for setting up next-intl with the App Router

tags:


copyright © www.lyustu.com all rights reserved.
Theme: TheMoon V3.0. Author:neo yang