Login

Nextjs+I18n multi-language internationalization best practices (search engine friendly)

Author:neo yang Time:2024/01/13 Read: 9866
Note: This best practice is based on next pages routing. Not suitable for app routing. The basic idea of the directory is to use next-i18ne […]

Note: This best practice is based on next pages routing. Not suitable for app routing.

Table of contents

The basic idea

Use next-i18next.

Add the [locales] directory to the "pages" directory and place the main business logic in this directory.

Pages such as index in the pages directory are used to determine the language, and then call the corresponding localized page logic.

Add the locales directory to the public directory for translation files.

Install

pnpm add next-i18next react-i18next i18nex

Configuration

File Directory

|components ..LangSwitcher.js ..LocLink.js ..MdRenderer.js |lib ..getStatic.js |pages ..[locales] ....index.js .._app.js .._document.js .. index.js |public ..locales ....en ...common.json ...index.md ....es ...common.json ..... .index.md |next-i18next.config.js |next.config.js

next.config.js

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

next-i18next.config.js

module.exports = { 'development', i18n: { defaultLocale: 'en', // locales: [ 'en', 'de', 'es',"cn"], locales: [ { 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" }, ], }, }

For the configuration of locales, considering the scenario of displaying language selection to users, I did not follow most configuration methods on the Internet.

lib/getStatic.js

import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import i18nextConfig from "@/next-i18next.config"; export const getI18nPaths = () => i18nextConfig.i18n.locales.map((lng) => ({ params : { locale: lng.code,//add code }, })); export const getStaticPaths = () => ({ fallback: false, paths: getI18nPaths(), }); export const getI18nProps = async (ctx, ns = ["common"]) => { const locale = ctx?.params?.locale || i18nextConfig.i18n.defaultLocale; let props = { ...(await serverSideTranslations(locale, ns)), }; return props; }; export const makeStaticProps = (ns = []) => async (ctx) => ({ props: await getI18nProps(ctx, ns), });

This is the logic used to obtain the language translation file and translate it on the server side.

_app.js

import { appWithTranslation } from "next-i18next"; import Layout from '../components/layout' import { NextPage } from 'next'; const MyApp = ({ Component, pageProps }) => ; export default appWithTranslation(MyApp);

NOTE: If not using This label can be removed directly.

_docoument.js

import i18nextConfig from &quot;@/next-i18next.config&quot;; // Other code import Document, { Html, Head, Main, NextScript, } from &#039;next/document&#039; class MyDocument extends Document { render() { const currentLocale = this. props.__NEXT_DATA__.query.locale || i18nextConfig.i18n.defaultLocale; console.log(currentLocale) return <html lang="{currentLocale}">
      <head>

        <link href="/app.css" />
        <link rel="shortcut icon" href="/icon.png" />
      </head>
      <body>
        <main />
        <nextscript />
      </body>


    </html>; } } export default MyDocument

index.js

import Home, { getStaticProps } from "./[locale]/index"; export default Home; export { getStaticProps }; 

[locales] folder

Used to implement international routing similar to www.xxx.com/es

public/locales folder

Used to store translation files.

components

Language selector: LangSwitcher.js

code


'use client'
import React from "react";
import { useRouter } from "next/router"
import i18nextConfig from '@/next-i18next.config'
console.log(i18nextConfig.i18n.defaultLocale)
 

const LangSwitcher = ({ lang, ...rest}) => {

        const router = useRouter();

const  GetHref=(locale)=> {

    console.log(locale)

    let href = rest.href || router.asPath
    let pName = router.pathname
    Object.keys(router.query).forEach(k => {
      if (k === 'locale') {
        pName = pName.replace(`[${k}]`, locale)
        return
      }
      pName = pName.replace(`[${k}]`, router.query[k])
    })
    if (locale == i18nextConfig.i18n.defaultLocale) {

      if (pName == '/' + i18nextConfig.i18n.defaultLocale) {
        href = '/'
      } else {
        href = `${href}`

      }

    } else {
      if (locale) {
        href = rest.href ? `/${locale}${rest.href}` : pName
      }
      if (href.indexOf(`/${locale}`) < 0) {
        href = `/${locale}${href}`
      }
    }
    return href
  }

  const LocalChanged = (value) => {
    const thehref = GetHref(value)
    router.push(thehref)
  }

  

  return (

    <div>
      🌐
      <select onChange={(e) => LocalChanged(e.target.value)} defaultValue={lang} className="h-8 m-2 p-1 rounded border-current">

        <option value={lang} > {GetLangData(lang).name}</option>

        {i18nextConfig.i18n.locales.map(locale => {
          if (locale.code === lang) return null
          return (<option value={locale.code} key={locale.code}> {locale.name}</option>)
        })}

      </select>
    </div>
  )
}
 //export 

 export function GetLangData(lang) {
    var res = {}
    {
      i18nextConfig.i18n.locales.map(locale => {
        if (locale.code === lang) {
          console.log(locale)
          res = locale
        }
      })
    }
    return res

  }

export default LangSwitcher

Instructions:

Introduce components and use them directly , and the lang parameter is set to the current language.

import LangSwitcher from './LangSwitcher' const { i18n } = useTranslation('home');

Localized Link component: LocLink.js

import React from &quot;react&quot;; import Link from &quot;next/link&quot;; import { useRouter } from &quot;next/router&quot;; import i18nextConfig from &#039;@/next-i18next.config&#039; const LinkComponent = ({ children, skipLocaleHandling, .. .rest }) =&gt; { const router = useRouter(); const locale = rest.locale || router.query.locale || &quot;&quot;; console.log(router) let href = rest.href || router.asPath; console.log(href) if (href.indexOf(&quot;http&quot;) === 0) skipLocaleHandling = true; if (locale &amp;&amp; !skipLocaleHandling) { // href = href console.log(i18nextConfig.i18n.defaultLocale) console. log(locale) locale==i18nextConfig.i18n.defaultLocale ? href : router.pathname.replace(&quot;[locale]&quot;, locale); } console.log(href) return (
    <>
      <link href="{href}" legacybehavior>
        <a {...rest}>{children}</a>
      </Link>
    </>
  ); }; export default LinkComponent;

Localized Markdown file acquisition and rendering components: MdRenderer.js

import ReactMarkdown from 'react-markdown' import { useState, useEffect } from "react"; import { useRouter } from "next/router" export default function MdRenderer({ path }) { const [a, setA] = useState("" ); const router = useRouter() fetch(path, { next: { revalidate: 0 } }) .then(response => response.text()) .then(data => { setA(data) } ).catch( err=>{console.log(err)}) return (
{a && ( {a} )}
) }

Instructions

t function

  • Introducing "useTranslation"
  • define t
  • Use {t('xxx')}, that's it
import { useTranslation } from "next-i18next"; const { t } = useTranslation(["common"]);//common.json is the translation file {t("xxx")}

How to get the current language

import { useTranslation } from "next-i18next"; const { i18n } = useTranslation('home'); console.log(i18n.language)

How to loop through a list of languages

Get the language list and customize the language navigation.

import i18nextConfig from '@/next-i18next.config' {i18nextConfig.i18n.locales.map(locale => { if (locale.code === i18nextConfig.i18n.defaultLocale) return( {locale.name} ) const thehref="/"+locale.code return ( {locale.name} ) })} 

Translation of content paragraphs

If the content is obtained from the backend, then this is not needed.

However, if the content exists on the front end, it is very troublesome to store it in json. Therefore, it can be stored in markdown, and then obtain the markdown file of the corresponding language version where the content needs to be displayed, and then parse and display it.

import MdRenderer from '../../components/MdRenderer' 

Summarize

Multi-language allows a website to get more traffic. This nextjs multi-language best practice fully considers the needs of SEO. Search engine friendly.

Suitable for tool stations, keyword stations, news and content stations.



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