多语言使用与扩展指南
Nexty.dev 使用 next-intl
实现完整的多语言支持,提供了灵活的国际化解决方案,包括路由级别的语言切换、自动语言检测等功能。
通过这篇文档,你将了解 next-intl
的基本用法、新语言和翻译内容的扩展方法以及 Nexty 支持的多语言高级特性。
文件结构
i18n/
├── messages/ # 翻译文件目录
│ ├── en/ # 英语翻译
│ │ └── common.json
│ ├── zh/ # 中文翻译
│ │ └── common.json
│ └── ja/ # 日语翻译
│ └── common.json
├── request.ts # next-intl 请求配置
└── routing.ts # 路由和语言配置
components/
├── LocaleSwitcher.tsx # 语言切换组件
└── LanguageDetectionAlert.tsx # 语言检测提醒组件
stores/
└── localeStore.ts # 语言状态管理
middleware.ts # 中间件配置
next.config.mjs # Next.js 配置
基本用法
在组件中使用翻译
在前端组件中,使用 useLocale
获取当前语言,使用 useTranslations
获取翻译内容;。
import { useLocale, useTranslations } from 'next-intl';
export default function MyComponent() {
const locale = useLocale();
const t = useTranslations('Home');
return (
<div>
<h1>{t('title')}</h1>
<p>{t('description')}</p>
</div>
);
}
提示
事实上,在 APP Router 模式中,
useLocale
和useTranslations
并非只能用于客户端组件,它们可以用于任何没有使用async
定义的组件。如果在没有使用async
定义的服务器组件中使用useLocale
和useTranslations
,那么组件仍然是服务端渲染的。
在服务端组件中使用
在使用 async
定义的服务器组件中,使用 getLocale
获取当前语言,使用 getTranslations
获取翻译内容。
import { getLocale, getTranslations } from 'next-intl/server';
export default async function ServerComponent() {
const locale = await getLocale();
const t = await getTranslations('Home');
return (
<div>
<h1>{t('title')}</h1>
</div>
);
}
使用带参数的翻译
JSON 文件中:
{
"welcome": "Welcome, {name}!",
"countdown": "Closing in {countdown} seconds"
}
组件中:
const message = t('welcome', { name: 'John' });
const timer = t('countdown', { countdown: countdown.toString() });
使用原始数据
对于复杂的数据结构(如数组、对象),使用 t.raw()
:
const headerLinks = t.raw('Header.links') as HeaderLink[];
const features = t.raw('Landing.Features.items');
国际化 Link 组件
使用 @/i18n/routing
暴露出来的 Link
组件,href
不需要写语言前缀,next-intl
会根据当前语言自动处理。
import { Link as I18nLink } from '@/i18n/routing';
<I18nLink href="/about">
{t('aboutUs')}
</I18nLink>
国际化 useRouter
使用 @/i18n/routing
暴露出来的 useRouter
Hook,同样不需要写语言前缀,next-intl
会根据当前语言自动处理。
import { useRouter } from '@/i18n/routing';
const router = useRouter();
const handleClick = () => {
router.push('/dashboard');
};
扩展新语言步骤
步骤1. 修改语言配置
在 i18n/routing.ts
中添加新语言:
export const LOCALES = ['en', 'zh', 'ja', 'ko'] // 添加韩语
export const DEFAULT_LOCALE = 'en'
export const LOCALE_NAMES: Record<string, string> = {
'en': "English",
'zh': "中文",
'ja': "日本語",
'ko': "한국어", // 添加韩语显示名称
};
步骤2. 创建翻译文件
创建新的语言目录和翻译文件:
mkdir i18n/messages/ko
cp i18n/messages/en/common.json i18n/messages/ko/common.json
步骤3. 翻译内容
编辑 i18n/messages/ko/common.json
,将所有英语内容翻译为韩语。
步骤4. 中间件配置
export const config = {
matcher: [
'/',
'/(en|zh|ja|ko)/:path*', // 添加韩语
'/((?!api|_next|_vercel|auth|.*\\.|favicon.ico).*)'
]
};
步骤5. 更新路由重定向(可选)
如果需要,在 next.config.mjs
中添加对应的重定向规则:
{
source: "/ko/dashboard",
destination: "/ko/dashboard/settings",
permanent: true,
}
提示
如果想删除某个语言,按照新增步骤反向操作,删掉对应的内容即可。
新增页面,扩展多语言包
新增页面的时候,推荐新建一个 JSON 文件,独立维护多语言包。
例如:在已有 common.json
的情况下,我们新增了一个 Landing.json
i18n/messages/en/
├── common.json
├── Landing.json # 新增 Landing
导入 Landing.json
import { getRequestConfig } from 'next-intl/server';
import { routing } from './routing';
export default getRequestConfig(async ({ requestLocale }) => {
let locale = await requestLocale;
if (!locale || !routing.locales.includes(locale as any)) {
locale = routing.defaultLocale;
}
const common = (await import(`./messages/${locale}/common.json`)).default;
const Landing = (await import(`./messages/${locale}/Landing.json`)).default; // 导入 Landing
return {
locale,
messages: {
...common,
Landing, // 导入 Landing
}
};
});
使用新增的命名空间
const t = useTranslations('Landing');
SSG(静态生成)的正确做法
提示
- v2.3.0 之前,由于 layout 有动态方法,实际上无法使用 SSG,但仍然是 SSR,不会影响 SEO。
- v2.3.0 及以后,请按照下面的方式实现 SSG。
对于静态内容页面,例如博客详情页、Privacy Policy、Terms of Service 页面,建议使用 SSG 来减少服务器 CPU 消耗,提升页面加载性能。
场景一:仅语言为动态参数的页面
适用页面:URL 格式为 /privacy-policy
或 /zh/privacy-policy
,动态参数仅包含语言前缀。
实现步骤:
- 通过
params
获取locale
参数 - 使用
generateStaticParams
遍历所有支持的语言
export default async function Page({ params }: { params: Params }) {
// ✅通过 params 获取 locale
// ❌通过 getLocale() 获取 locale
const { locale } = await params;
// ... 其他代码 ...
return (
// ... 页面内容 ...
);
}
export async function generateStaticParams() {
return LOCALES.map((locale) => ({
locale,
}));
}
场景二:多层动态路由的页面
适用页面:URL 格式为 /blogs/nexty-dev-stand-out
或 /zh/blogs/nexty-dev-stand-out
,动态参数包含语言前缀和内容标识符(如博客 slug
)。
实现步骤:同样是两步,但 generateStaticParams
方法需要处理更复杂的参数组合。
export default async function BlogPage({ params }: { params: Params }) {
const { locale, slug } = await params;
// ... 其他代码 ...
return (
// ... 页面内容 ...
);
}
export async function generateStaticParams() {
// 遍历所有语言,从数据库获取所有博客数据并提取 slug
// 生成所有语言下所有博客的参数组合
const allParams: { locale: string; slug: string }[] = [];
for (const locale of LOCALES) {
// 从数据库获取当前语言下所有博客的 slug
// ... 数据库查询逻辑 ...
allParams.push({ locale, slug })
}
return allParams;
}
验证 SSG 是否生效
在本地执行 npm run build
命令完成构建后,检查 .next/server/app/en
目录下是否生成了对应的 html
文件,如图所示:

你可以在从 Next.js 文档获得更多动态路由的知识。
Nexty 的高级特性
语言检测与提醒
系统会在用户首次访问时自动检测浏览器语言,如果与当前网站语言不符,会显示切换提醒:
- 10秒倒计时自动关闭
- 用户可手动关闭
- 关闭状态保存30天(通过Cookie)
SEO 优化
每个语言版本都会生成独立的URL和元数据:
import { constructMetadata } from "@/lib/metadata";
export async function generateMetadata({ params }: MetadataProps): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "Home" });
return constructMetadata({
page: "Home",
title: t("title"),
description: t("description"),
locale: locale as Locale,
path: `/`,
});
}