next项目的多语言配置
本文的内容不是全面、通用、详尽的技术资料。 仅是个人实践记录,适用于本人已发布的网站。 供参考。 我发布此文时为2025年10月底,next.js的官方已推出16.0.0,我创建的新网站也是按照最新的版本来。
官方指南
关键词:Internationalization
**我的所有next项目均是基于App Router模式,typescript,pnpm包管理,之后不再说明。
路由
关键词: routing
本地化
关键词: localization
翻译
关键词:translation
学习资料
官方提供的极简路由和翻译案例(Minimal i18n routing and translations)](https://github.com/vercel/next.js/tree/canary/examples/i18n-routing)
案例中只是一个已经完成的项目,没有开发过程,所以我们分析一下步骤,然后结合我最近写的一个网站(next 15.X)照做。
案例中有2个我从未接触的组件:
@formatjs/intl-localematcher, negotiator,都是老项目了。
由于案例中不涉及翻译(t),所以没有用到next-intl等组件。
所以我看一下就决定按照之前项目的做法来。 此案例太老,只能参考。
接下来我们可以直接去看next-intl的官方教程。 为啥选这个呢?可能是因为名称简洁,而且在推荐列表里放在最上面吧,多语言的方案太多了。
步骤
安装next-intl
pnpm add next-intl
建立多语言文件库
在项目根目录上新建一个locale目录(官方案例叫messages)。
下面放语言文件json,如en.json, hans.json等,除了中文比较麻烦外,其他语言按照国际标准tag来写名字就行了。
json的格式大致为:
// en.json
{
"hey": "Hey, ",
"email": "Email",
"auth": {
"sign_in": "Sign in",
...},
...
}
// hans.json 简体中文
{
"hey": "你好!",
"email": "电子邮箱",
"auth": {
"sign_in": "登录",
...},
...
}
request配置文件
新建一个i18n目录,和app平级即可(我是放在/src下)。 在其中建立request.ts,内容:
import {getRequestConfig} from 'next-intl/server';
export default getRequestConfig(async () => {
// Static for now, we'll change this later
const locale = 'en';
return {
locale,
messages: (await import(`../../locale/${locale}.json`)).default
};
});
修改next.config.ts
这个文件在你的项目根目录,默认长这样:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;
现在改成这样:
import {NextConfig} from 'next';
import createNextIntlPlugin from 'next-intl/plugin';
const nextConfig: NextConfig = {};
const withNextIntl = createNextIntlPlugin();
export default withNextIntl(nextConfig);
layout中包裹body
为了使以上request配置对客户端组件生效,可以在根布局文件中,用一个NextIntlClientProvider来包裹children们。
// app/layout.tsx
import {NextIntlClientProvider} from 'next-intl';
type Props = {
children: React.ReactNode;
};
export default async function RootLayout({children}: Props) {
return (
<html>
<body>
<NextIntlClientProvider>{children}</NextIntlClientProvider>
</body>
</html>
);
}
之前我不是这样做的,是靠middleware来引入一个多语言的routing配置。 感觉现在的方法更优雅一些。
在组件中使用
接下来就可以试用一下了,如翻译:
// 客户端组件
import {useTranslations} from 'next-intl';
export default function HomePage() {
const t = useTranslations('HomePage');
return <h1>{t('title')}</h1>;
}
// 服务端组件
import {getTranslations} from 'next-intl/server';
export default async function HomePage() {
const t = await getTranslations('HomePage');
return <h1>{t('title')}</h1>;
}
文件结构
刚才的例子中,文件结构大致是这样:
├── locale
│ ├── en.json
│ └── ...
├── next.config.ts
└── src
├── i18n
│ └── request.ts
└── app
├── layout.tsx
└── page.tsx
本地化路由
这是个重要概念。
如果你要将本地化语言放到路径名称中来实现路由导航,如/en/about or example.de/über-uns的形式,你需要在app目录下设置一个顶级的[locale]动态段。
如果不喜欢这种模式,则可以用cookie等方式来传递本地化选项,修改request.ts:
import {cookies} from 'next/headers';
import {getRequestConfig} from 'next-intl/server';
export default getRequestConfig(async () => {
const store = await cookies();
const locale = store.get('locale')?.value || 'en';
return {
locale
// ...
};
});
之前我也用过这方法,后来还是随大流用动态字段的本地化路由了。 下面就是说这个方法。
进一步完善
概述
next-intl在两个地方集成了Next.js的路由系统:
- Proxy / middleware: 协商本地化信息并处理重定向和重写(如:
/→/en