登录

Nextjs+I18n多语言国际化最佳实践(搜索引擎友好)

作者:neo yang 时间:2024/01/13 读: 9118
注:这个最佳实践是基于next的pages路由。并不适合app路由。 目录 基本思路 使用next-i18ne […]

注:这个最佳实践是基于next的pages路由。并不适合app路由。

目录

基本思路

使用next-i18next。

在“pages”目录中增加[locales]目录,将主要业务逻辑放到这个目录中。

pages目录中的index等页面用于判断语言,然后调用相应本地化页面逻辑

public目录中增加locales目录,用于翻译文件。

安装

pnpm add next-i18next react-i18next i18nex

配置

文件目录

|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" },


       
      ],
    },
  }

对于locales的配置,考虑到对用户展示语言选择的场景,我没有按照网上大多数的配置方式。

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),
  });

这是用来获取语言翻译文件和在服务端翻译的逻辑。

_app.js

import { appWithTranslation } from "next-i18next";
import Layout from '../components/layout'
import { NextPage } from 'next';

const MyApp = ({ Component, pageProps }) => <Layout> <Component {...pageProps} /> </Layout>;

export default appWithTranslation(MyApp);

注意:如果不使用<Layout>可以直接将这个标签去掉。

_docoument.js

import i18nextConfig from "@/next-i18next.config";
// 其他代码
import Document, {
  Html,
  Head,
  Main,
  NextScript,
} from 'next/document'

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]文件夹

用来实现类似www.xxx.com/es的国际化路由

public/locales文件夹

用来存放翻译文件。

组件

语言选择器:LangSwitcher.js

代码


'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

使用方法:

引入组件,直接使用<LangSwitcher>,并且lang参数设置为当前的语言。

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

 <LangSwitcher lang={i18n.language}></LangSwitcher>

本地化Link组件:LocLink.js

import React from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import i18nextConfig from '@/next-i18next.config'

const LinkComponent = ({ children, skipLocaleHandling, ...rest }) => {
  const router = useRouter();
  const locale = rest.locale || router.query.locale || "";
console.log(router)
  let href = rest.href || router.asPath;
  console.log(href)
  if (href.indexOf("http") === 0) skipLocaleHandling = true;
  if (locale && !skipLocaleHandling) {
   // href = href
   console.log(i18nextConfig.i18n.defaultLocale)
   console.log(locale)
    locale==i18nextConfig.i18n.defaultLocale
      ? href
      : router.pathname.replace("[locale]", locale);
  }
console.log(href)
  return (
    <>
      <Link href={href} legacyBehavior>
        <a {...rest}>{children}</a>
      </Link>
    </>
  );
};

export default LinkComponent;

本地化Markdown文件获取和渲染组件: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 (
        <article className="prose lg:prose-xl">
            {a && (<ReactMarkdown>{a}
            </ReactMarkdown>)}
        </article>)
}

使用方法

t函数

  • 引入“useTranslation”
  • 定义t
  • 使用{t(‘xxx’)},即可
import { useTranslation } from "next-i18next";
const { t } = useTranslation(["common"]);//common.json为翻译文件
{t("xxx")}

如何获取当前的语言

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

如何循环获取语言列表

获取语言列表,可以自定义语言导航。

import i18nextConfig from '@/next-i18next.config'
 {i18nextConfig.i18n.locales.map(locale => {
          if (locale.code === i18nextConfig.i18n.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>)
        })} 

内容段落的翻译

如果内容是从后端获取的,那么,这个就不需要了。

但,如果内容存在前端,存在json里非常麻烦,所以,可以存在markdown中,然后在需要展示这个内容的地方获取相应语言版本的markdown文件,然后解析展示出来。

import MdRenderer from '../../components/MdRenderer'
<MdRenderer  path={"locales/"+i18n.language+ "/index.md"} /> 

总结

多语言,可以让一个网站获取更多的流量。这个nextjs的多语言最佳实践,充分考虑了SEO的需要。搜索引擎友好。

适合做工具站、关键词站、新闻和内容站。



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