登录

Nextjs+next-intl多语言国际化最佳实践(APP路由器)

作者:neo yang 时间:2024/02/19 读: 11098
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有两种路由器:APP路由器和Page路由器。

前段时间,在Page路由器下,对nextjs做了国际化,并做了总结:

但Page路由器将会是Nextjs逐渐淘汰的路由器。所以,最近,基于APP路由器,对Nextjs做了国际化。

一、Nextjs国际化解决方案的选择

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

似乎大多数都在用这个,我试了一下,挺复杂,还没成功,所以放弃了。

2、next-i18n-router react-intl

看着也挺复杂,由于有1的阴影,放弃了。

3、next-intl

next-intl 是一个完整的nextjs的国际化方案,无须其它软件包。而且配置相对简单。更重要的是,我配置成功了,没遇到莫名其妙的问题。

所以,就是它了。

二、目录结构

|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

三、国际化路由

1、novigation.js

这是个配置文件,这个文件中配置的项目会在其它一些文件中调用。

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]目录

app目录中的页面文件全部放到[locale]目录中。

3、中间件:middleware.js

说明:

实现以下样式的URL

默认语言的URL:

首页为:www.xxx.com

内页为:www.xxx.com/about

其它语言的URL:(以西班牙语为例)

首页为:www.xxx.com/es

内页为:www.xxx.com/es/about

另外,如果URL中输入了默认语言,比如默认语言为英语,用户输入URL:www.xxx.com/en

那么,将自动转到www.xxx.com

代码:

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 (e.g. favicon.ico)
    matcher: ["/((?!api|_next|.*\\..*).*)"],
  };

四、加载翻译文件

1、翻译文件

说明

我的翻译文件放在了“public”目录中,但,其实,翻译文件也可以放在其它目录中,只要在i18n.js文件中配置好想应的路径即可。

翻译文件是一个json文件,json文件可以是嵌套格式,也可以不嵌套。这两种,最终,在引用时会稍有不同。

代码(不嵌套)

{
  "aaa": "hi",
}

代码(嵌套)

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

2、i18n.js文件

说明:

这个文件,是导入翻译文件的,关键是配置翻译文件的路径,要和你的翻译文件所在的路径保持一致。

路径中的${locale}表示语言。

代码

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、next.config.js的配置

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

module.exports =withNextIntl( nextConfig)

五、实现翻译

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

page.js中的代码:

参数locale表示当前的语言。

如果翻译文件是不嵌套的方式,则const t = useTranslations();

如果翻译文件是嵌套的方式,则根据文件结构和实际需要,用类似:const t = useTranslations(“bbb“);

import { useTranslations } from 'next-intl';
import { Link } from '@/navigation';
export default function Home({ params: { locale } }) {
  const t = useTranslations();

  return (<>   
<Link href={"/"}><h1>{t("aaa")}</h1></Link>
</>)

六、语言切换器

1、效果

下拉选择语言,当前页面切换到选择的语言的页面。

2、代码

"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

}

七、SEO和搜索引擎友好

1、Meta data

nextjs的APP路由器,生成页面的title和Meta data,我尝试了三种方式:

第一种方式:在layout.js中直接使用:expert const metadata={}

第二种方式:在page.js中使用动态生成Meta data

可参考:Optimizing: Metadata | Next.js (nextjs.org)

第这两种方式,对于多语言的title和Meta data,我都失败了。最后用了第三种方式,也是最简单直白的方式。

第三种方式:直接在page.js中写title标签和Meta标签。

所以,page.js的代码改成了这样:

import { useTranslations } from 'next-intl';
import { Link } from '@/navigation';
export default function Home({ params: { locale } }) {
  const t = useTranslations();

  return (<>  
          <title>{t("site-name")}</title>
        <meta name="keyword" content={t("site-name")}></meta>
        <meta name="description" content={t("site-description")}></meta>
        <meta property="og:type" content="website"></meta>
        <meta property="og:title" content={t("site-name")}></meta>
        <meta property="og:description" content={t("site-description")}></meta>
        <meta property="og:site_name" content={t("site-name")}></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("aaa")}</h1></Link>
</>)

2、多语言导航:让搜索引擎更容易发现多语言URL

说明

语言切换器是前端渲染的,所以,我在页面的底部,也就是组件Footer.js中增加了后端渲染的多语言导航。点击相应的语言就会进入此语言的网站首页。

Footer.js代码如下:

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>
  </>
    
  )
}

八、总结

国际化路由、翻译文件的结构和引入、翻译的实现。无论哪种国际化方案,都会涉及到这三个方面。虽然都不叫麻烦,但总的来说,next-intl相对还是简单一些。

九、参考

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

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

标签:


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