i18n-locale-routing

多语言路由:用 next-intl 做中英文(en/zh)

YouTeacher 落地页用英文和中文提供同一套页面。它在 Next.js App Router 之上使用 next-intl,并且把语言(locale)放在 URL 路径里,而不是放在 cookie 或请求头里。整套机制挂在一份 locale 定义之上,由五个各司其职的小部件撑起来。

locale 只有一个真相来源

src/i18n/locales.ts 把支持的语言声明为 ['en', 'zh'],把 en 定为默认语言,并导出一个 isValidLocale 类型守卫。其余每个部件都从这里 import,所以「支持哪些语言」只存在于一个地方。

locale 存在于 URL 里

路由是带 locale 前缀、且始终显式的。中间件(src/middleware.ts)用 localePrefix: 'always' 包裹 next-intl/middleware,所以裸的 / 会被重定向到带前缀的路径(/en),每个真实页面都落在 /en/.../zh/... 之下。它的 matcher 只匹配 / 以及以 /en/zh 开头的路径,静态资源和内部路由不受影响。

App Router 用一个 [locale] 动态段与之对应。src/app/[locale]/layout.tsx 读取这一段,如果它不是已知语言,就调用 notFound()——未知前缀是 404,而不是悄悄回退到默认语言。

消息按请求加载

src/i18n/request.ts 是 next-intl 的请求配置。对每个请求,它取解析出的 requestLocale,在缺失或非法时回退到默认语言,然后动态 import 对应的消息目录(src/i18n/messages/<locale>.json)。这些目录是带命名空间的 JSON——metadataheaderhero 等——所以组件是按命名空间和 key 取译文的,而不是按扁平的字符串 id。

按语言预渲染

layout 导出 generateStaticParams(),遍历 locale 列表,因此两种语言变体在构建时就被预渲染,而不是按需渲染。它调用 setRequestLocale(locale) 为该请求钉住当前语言,随后加载消息并交给客户端的 Providers 包装组件。

generateMetadata 通过 getTranslationsmetadata 命名空间取页面标题和描述,并设置双语站点所需的 SEO 管线:按语言的 canonical、连接 /en/zhalternates.languages,以及取值为 zh_CNen_US 的 OpenGraph locale

语言切换器

src/components/Header.tsx 渲染页头里的语言切换。它用 useLocale() 读当前语言、用 usePathname() 读当前路径。切换语言时,它把当前路径上已有的 locale 前缀剥掉,再 push /<新locale><剩余路径>——所以你会停在同一个页面上、只是换成另一种语言,而不会被弹回首页。

为什么是这个形状

把 locale 放进 URL,意味着每种语言变体都是一个真实、可链接、可被索引的 URL——这正是 canonicalalternates 元数据要对外声明的东西。把语言列表收在一个文件里,让中间件 matcher、静态参数和切换按钮都从它派生,意味着加入第三种语言基本上只是改数据,而不是重写。

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →

i18n-locale-routing