国际化(i18n)¶
控制台经 next-intl v4 支持英文(en)与中文(zh)。URL 不含语言前缀(localePrefix: "never")。
配置¶
路由(i18n/routing.ts)¶
export const routing = defineRouting({
locales: ["zh", "en"],
defaultLocale: "zh",
localePrefix: "never",
});
语言由中间件 / proxy.ts 从 cookie 或 Accept-Language 解析。
请求配置(i18n/request.ts)¶
请求时加载文案:
- 经
readFileSync读取messages/${locale}.json(避免 Turbopack 动态 import 挂起) - 用
deepMerge合并所有messages/fragments/*.${locale}.json - 向
NextIntlClientProvider返回{ locale, messages }
文案文件布局¶
frontend/messages/
├── en.json # Shared / shell strings
├── zh.json
└── fragments/
├── qa.en.json # namespace: "qa"
├── qa.zh.json
├── ingest.en.json
├── kb.en.json
├── health.en.json
└── …
Fragment 约定¶
每个 fragment 文件在顶层命名空间:
deepMerge 递归已有 key —— fragment 扩展基础文案而不覆盖兄弟节点。
新增页面命名空间¶
- 创建
messages/fragments/mypage.en.json+.zh.json - 无需改
request.ts—— 后缀扫描自动发现 - 组件中使用
useTranslations("mypage")
组件中的用法¶
Client 组件¶
import { useTranslations } from "next-intl";
function Composer() {
const t = useTranslations("qa");
return <span>{t("composer.placeholder")}</span>;
}
嵌套命名空间¶
Server 组件¶
App Router 中的语言¶
app/[locale]/layout.tsx:
generateStaticParams()→ 预渲染两种语言- layout 中
setRequestLocale(locale)以静态优化 - 无效 locale →
notFound()
<html lang={locale}> 用于无障碍与 SEO。
Fragment 清单(问答示例)¶
messages/fragments/qa.en.json 覆盖:
| 命名空间段 | UI 区域 |
|---|---|
qa.composer |
提示、模式、附件 |
qa.sources |
证据栏 |
qa.rail |
结构 / 预览标签 |
qa.history |
会话抽屉 |
qa.scope |
Scope 过滤抽屉 |
qa.error |
Toast 文案 |
并行 qa.zh.json 须保持 key 一致 —— 开发环境缺 key 时回退为 key 路径。
日期 / 数字格式化¶
展示与语言相关的值时用 next-intl formatter:
import { useFormatter } from "next-intl";
const format = useFormatter();
format.dateTime(new Date(session.updated_at));
历史分组(history-utils.ts)用 UTC 日历计算 —— UI 标签来自 qa.history.* key。
API 内容语言¶
后端 LLM 答案遵循模型默认(可能中英混杂)。UI 字符串完全本地化;引用内容来自语料语言,非 UI locale。
测试语言¶
切换语言途径:
- 用户偏好 store(
prefsStore,若已接线) - 中间件设置的 cookie
- 首次访问时浏览器
Accept-Language