Nextjs+next-intl Best Practice für mehrsprachige Internationalisierung (APP-Router)
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';
Exportconst locales = ['en', 'de', 'es', 'ja','ko','it','pt'];
Exportconst localePrefix ='as-needed';
Exportconst defaultLocale="en";
Exportconst 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" },
]
Exportconst { Link, redirect, usePathname, useRouter } =
createSharedPathnamesNavigation({ locales, localePrefix });
2、[locale]目录
app目录中的页面文件全部放到[locale]目录中。
3、中间件:middleware.js
veranschaulichen:
实现以下样式的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
Code:
importierencreateMiddlewarefrom "next-intl/middleware";
importieren{ defaultLocale, localePrefix, locales }from '@/navigation';
Export default createMiddleware({
locales,
localePrefix ,
defaultLocale,localeDetection: false,
});
Export constconfig = {// 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文件
veranschaulichen:
这个文件,是导入翻译文件的,关键是配置翻译文件的路径,要和你的翻译文件所在的路径保持一致。
路径中的${locale}表示语言。
Code
importieren{ 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 importieren(`./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中的代码:
importieren "./globals.css";
importieren { NextIntlClientProvider, useMessages }from 'next-intl';
importieren{ locales }from '@/navigation';
importieren Header from '@/components/Header'
importieren Footer from '@/components/Footer'
Export default function RootLayout({
children, params: { locale }
}) {
if(!locales.includes(locale)) {notFound();
}
constmessages =useMessages();
return (
<html lang="{locale}">
<nextintlclientprovider locale="{locale}" messages="{messages}">
<body><header />{Kinder}<footer /></body>
</nextintlclientprovider>
</html>
);
}
page.js中的代码:
参数locale表示当前的语言。
如果翻译文件是不嵌套的方式,则const t = useTranslations();
如果翻译文件是嵌套的方式,则根据文件结构和实际需要,用类似:const t = useTranslations(“bbb“);
importieren{ useTranslations }from 'next-intl';
importieren { Link } from '@/navigation';
Export default function Home({ params: { locale } }) {
constt =useTranslations();
return (<>
<link href="{"/"}"><h1>{t("aaa")}</h1></Link>
</>)
六、语言切换器
1、效果
下拉选择语言,当前页面切换到选择的语言的页面。

2、代码
"use client";
importieren{ useLocale }from "next-intl";
importieren{ localeItems, useRouter, usePathname, defaultLocale }from '@/navigation';
Export default function LangSwitcher() {
constlocale =useLocale();
constrouter =useRouter();
constpathname =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) {
varres = {}
{
localeItems.map(locale => {
if(locale.code=== defaultLocale) {console.log(locale)
res = locale
}
})
}returnres
}
七、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的代码改成了这样:
importieren{ useTranslations }from 'next-intl';
importieren { Link } from '@/navigation';
Export default function Home({ params: { locale } }) {
constt =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代码如下:
importieren Link from 'next/link'
importieren{ 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

关注我的微信公众号