SSR 项目中的国际化和多语言页面如何设计?
简化版
SSR 国际化要解决语言识别、翻译资源加载、URL 结构、SEO 标签和缓存隔离。常见做法是用路径区分语言,例如 /zh/、/en/,服务端根据 URL 加载对应语言包并渲染 HTML,同时输出 lang、hreflang、canonical 等标签。不要只在客户端切语言,否则首屏 HTML、SEO 和缓存都容易出问题。
详细版
多语言 SSR 页面最怕“服务端一套语言,客户端水合后变另一套语言”。这会造成闪烁、水合不一致,也会让搜索引擎拿到错误内容。
规范设计里,语言应该是路由、请求上下文和缓存 key 的一部分。翻译资源可以按语言分包加载,日期、货币、数字格式也要跟 locale 统一。
面试时要强调:国际化不只是文本替换,还包括 URL、SEO、格式化、缓存和兜底策略。
完整版教学
一、语言从哪里来
可以从 URL 路径判断,例如 /en/products。
可以从子域名判断,例如 en.example.com。
可以从用户设置或 Cookie 判断。
也可以参考 Accept-Language,但它更适合作为首次访问的辅助判断。
对 SEO 页面来说,路径或域名通常比纯 Cookie 更稳定。
二、推荐 URL 结构
| 方案 | 示例 | 特点 |
|---|---|---|
| 路径前缀 | /zh/about | 实现直观,SEO 友好 |
| 子域名 | en.example.com | 品牌区隔清晰,运维稍复杂 |
| 参数 | /about?lang=en | 简单但 SEO 表达较弱 |
| Cookie | 无固定 URL | 个性化方便,不适合公开索引页 |
三、SSR 渲染流程
解析 URL 得到 locale。
加载对应语言包。
获取页面数据。
用 locale 格式化日期、金额、数字。
输出 HTML、html lang、SEO 标签和 hydration 初始状态。
多语言页面要让服务端 HTML 就是目标语言,而不是等客户端再替换。
四、代码示例
export async function renderI18nPage(request: Request) {
const locale = getLocaleFromUrl(new URL(request.url).pathname);
const messages = await loadMessages(locale);
return renderHtml({
locale,
messages,
title: messages["home.title"],
});
}
客户端水合时要复用同一个 locale 和 messages,避免重新判断出不同语言。
五、SEO 细节
html lang 要准确。
不同语言页面应有稳定 URL。
可以使用 hreflang 告诉搜索引擎语言版本关系。
canonical 要指向当前语言的规范 URL,避免错误合并。
站点地图也应包含多语言 URL。
六、缓存设计
如果 HTML 按语言不同,CDN 缓存 key 必须包含语言维度。
路径前缀天然能隔离缓存。
如果用 Cookie 或请求头判断语言,要谨慎设置 Vary。
否则用户可能拿到别人语言的 HTML。
七、误区和追问
- 误区:国际化就是把中文替换成英文。 还包括 URL、SEO、格式化、排序、兜底和缓存。
- 误区:用浏览器语言自动跳转一定最好。 自动跳转可能干扰搜索引擎和用户主动选择。
- 误区:客户端切语言就能满足 SSR。 客户端切换太晚,首屏 HTML 和 SEO 已经生成。
- 追问:语言包很大怎么办? 按 locale 和页面拆包,服务端只加载当前页面需要的资源。
- 追问:缺少翻译怎么兜底? 可以回退默认语言,但要记录监控,避免长期隐藏问题。
- 追问:多语言页面怎么防止水合不一致? 服务端和客户端使用同一 locale、同一初始语言包和同一格式化规则。
八、面试收束
回答 SSR 国际化时,不要停在文案替换。
能讲到 URL、SEO、缓存和 hydration,一般就能体现项目经验。