使用您最喜欢的AI助手总结文档,并引用此页面和AI提供商
版本历史
- "使用 remix-intlayer 中间件和钩子"v9.5.52026/9/19
- "Remix 3 初始文档"v9.5.02026/9/9
如果您有改善此文档的想法,请随时通过在GitHub上提交拉取请求来贡献。
文档的 GitHub 链接复制文档 Markdown 到剪贴板
使用 Intlayer 翻译您的 Remix 3 网站 | 国际化 (i18n)
本指南演示了如何将 Intlayer 集成到 Remix 3 应用中以实现无缝的国际化,涵盖基于语言的路由、类型安全的内容声明、服务端渲染的 JSX 组件以及对 Node.js、Bun、Deno 和 Cloudflare Workers 的跨运行时支持。
什么是 Remix 3?
Remix 3 代表了一次根本性的架构演进,转向完全构建在 Web 标准之上、可组合且与运行时解耦的 Web 框架。Remix 3 不再与特定的打包器或专有服务器 API 绑定,而是以单一职责的可组合包形式发布:
remix/fetch-router(或remix/router): 基于 Fetch API (Request与Response) 构建的轻量且符合规范的路由。remix/ui: JSX 组件模型 (jsxImportSource: "remix/ui")。组件是一个接收 Handle 并返回渲染函数的设置函数,外观类似 React,但状态保存在纯 JavaScript 闭包中。remix/middleware/render: 为每个请求挂载context.render(<Page />),将 JSX 树以流式传输转换为 HTMLResponse。remix/node-fetch-server: Node.js 服务器适配器,原生支持 Bun、Deno 与边缘运行时。remix/cookie: 具备加密安全性的 Cookie 解析与序列化工具。
结合 Intlayer 和 remix-intlayer 软件包(包含语言环境中间件以及与 react-intlayer 相同的 useIntlayer / useDictionary / useLocale 钩子,绑定到 Remix 请求上下文),你将获得一个完整的国际化系统,提供编译时安全性、自动化 AI 翻译、零开销服务端渲染以及流畅的语言环境路由。
目录
为什么选择 Intlayer 而不是其他方案?
与 i18next 等传统方案或自定义翻译加载器相比,Intlayer 提供了专为现代 Web 架构优化的集成化开发者体验:
Intlayer 专为与 Web 标准(Request、Response、Headers 和 URL)无缝协作而构建。remix-intlayer 作为轻量级中间件插入 Remix 3 的 Fetch 路由器中,从 URL 路径、Cookie 或 Accept-Language 请求头中提取语言环境,并将其暴露给请求的其余部分、处理程序、视图和 remix/ui 组件,无需手动传递参数,也不会将你锁定在特定运行时。
彻底告别零散的 JSON 键和运行时的缺失键崩溃。Intlayer 在所有声明的语言中强制执行 TypeScript 静态类型检查,如果缺少翻译或内容不合法,会在构建时立即发出警告。
Remix 3 在服务端渲染 JSX 组件并将 HTML 流式传输至客户端。仅会将对应请求语言解析后的纯文本写入输出流。除非组件被显式标记为 clientEntry,否则无需客户端注水包或笨重的翻译字典。
Intlayer 将内容声明 (.content.ts) 与路由业务逻辑就近同构,大幅减少了大语言模型 (LLM) 所需的 Token 上下文。内置的 CLI 命令如 intlayer fill 和 intlayer test 允许您在 CI/CD 流水线中以自有 AI 服务商的原始成本实现自动化翻译。
分步指南
在 GitHub 上查看 应用模板。
安装依赖
使用你喜欢的包管理器安装
intlayer、remix-intlayer和remix(版本 3):bash复制代码复制代码到剪贴板
intlayer: 核心国际化引擎,负责配置管理、字典声明 (t(),Dictionary)、CLI 工具和运行时解释器。remix-intlayer:Remix 3 集成:解析每个请求语言环境的intlayer()路由器中间件,以及在下游任何位置读取它的useIntlayer、useDictionary和useLocale钩子。remix-intlayer:Remix 3 集成:解析每个请求语言环境的intlayer()路由器中间件,以及在下游任何位置读取它的useIntlayer、useDictionary和useLocale钩子。remix: 统一的 Remix 3 框架包,导出remix/router、remix/routes、remix/ui、remix/middleware/render以及remix/node-fetch-server。
配置 Intlayer
架构
在此架构中,
remix-intlayer的intlayer()中间件在render()中间件之前注册到createRouter()中。它会在路由匹配前去除语言环境前缀,因此路由只需在src/routes.ts中声明一次,无需:locale段,并且它会在AsyncLocalStorage作用域内运行请求的其余部分,从而让useIntlayer/useLocale能够在路由处理函数和remix/ui视图中无需参数读取语言环境。内容声明文件与你的视图一起放置在src/中:bash复制代码复制代码到剪贴板
配置
在项目根目录下创建
intlayer.config.ts,声明支持的语言及国际化设置:intlayer.config.ts复制代码复制代码到剪贴板
import { Locales, type IntlayerConfig } from "intlayer"; const config: IntlayerConfig = { internationalization: { locales: [ Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH, Locales.CHINESE, ], defaultLocale: Locales.ENGLISH, }, }; export default config;更多配置项说明请参阅 配置文档。
声明多语言内容
在
.content.ts文件中声明本地化内容:src/home.content.ts复制代码复制代码到剪贴板
import { t, type Dictionary } from "intlayer"; const homeContent = { key: "home", content: { title: t({ zh: "欢迎使用 Remix 3", en: "Welcome to Remix 3", fr: "Bienvenue sur Remix 3", es: "Bienvenido a Remix 3", }), description: t({ zh: "基于 Web 标准构建并具备原生多语言支持的组合式应用。", en: "A composable, web-standard application with native i18n.", fr: "Une application composable basée sur les standards web avec i18n native.", es: "Una aplicación componible basada en estándares web con i18n nativa.", }), switchLanguage: t({ zh: "切换语言:", en: "Switch language:", fr: "Changer de langue :", es: "Cambiar idioma:", }), }, } satisfies Dictionary; export default homeContent;Intlayer 还支持 JSON、YAML 和 CommonJS 声明格式。请参阅 内容声明文档。
构建 Intlayer 字典
编译字典声明以生成 TypeScript 类型与运行时定义:
bash复制代码复制代码到剪贴板
此操作会将内容编译至
.intlayer产物目录中,提供完整的 TypeScript 自动补全和快速字典查询。添加 Intlayer 中间件
Remix 3 通过
createRouter({ middleware: [...] })提供可组合的中间件管道。remix-intlayer提供了intlayer()中间件。对于每个传入请求,它使用以下内容解析语言环境:- 除
no-prefix之外的所有路由模式下的 URL:路径前缀(例如/zh或/en)或?locale=查询参数。 - 客户端持久化的语言环境:存储 Cookie(
INTLAYER_LOCALE)或自定义标头(x-intlayer-locale)。 - 标准
Accept-Language协商,回退到配置的defaultLocale。
结果作为
context.intlayer(或context.get(Intlayer))存储在 Remix 请求上下文中,包含locale、defaultLocale和availableLocales。中间件随后在绑定到该上下文的AsyncLocalStorage作用域内运行请求的其余部分,使得该包的钩子无需传递参数即可读取语言环境,无论是在路由处理程序、视图还是remix/ui组件中:typescript复制代码复制代码到剪贴板
useIntlayer("home", "fr")或useIntlayer("faq", { item: 2 })可在单次调用中覆盖请求语言环境,而useDictionary(homeContent)读取导入的字典而不是键。在请求之外,钩子会回退到默认语言环境。中间件还会在服务器启动时准备 Intlayer 字典,因此即使缺少
intlayer build也不会导致注册表为空。- 除
定义类型安全路由
使用
remix/routes中的route()定义应用路由:src/routes.ts复制代码复制代码到剪贴板
import { route } from "remix/routes"; export const routes = route({ // 默认语言路由 home: "/", // 带有动态 :locale 片段的本地化路由 localizedHome: "/:locale", });使用
route()可在整个应用中提供类型安全的 URL 生成支持:typescript复制代码复制代码到剪贴板
使用 JSX 渲染本地化页面
Remix 3 使用来自
remix/ui的 JSX 组件渲染 UI。组件是一个接收Handle并返回渲染函数的设置函数。设置函数每个实例仅执行一次,渲染函数在每次更新时执行,并通过handle.props读取属性。从一个共享的
Document外壳开始,它根据中间件解析的语言环境设置<html lang="..." dir="...">属性:src/views/document.tsx复制代码复制代码到剪贴板
import { getHTMLTextDir } from "intlayer"; import { useLocale } from "remix-intlayer"; import type { Handle, RemixNode } from "remix/ui"; type DocumentProps = { title: string; children?: RemixNode; }; export const Document = (handle: Handle<DocumentProps>) => () => { const { title, children } = handle.props; const { locale } = useLocale(); return ( <html lang={locale} dir={getHTMLTextDir(locale)}> <head> <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>{title}</title> </head> <body>{children}</body> </html> ); };然后创建主页。它使用
useIntlayer读取本地化字典并渲染语言切换器:src/views/home.tsx复制代码复制代码到剪贴板
import { getLocaleName, getLocalizedUrl, getPathWithoutLocale } from "intlayer"; import { useIntlayer, useLocale } from "remix-intlayer"; import { Document } from "./document"; export const HomePage = () => () => { const { locale, availableLocales } = useLocale(); const home = useIntlayer("home"); const pathWithoutLocale = getPathWithoutLocale(); return ( <Document title={home.title}> <header> <nav aria-label="Languages"> <span>{home.switchLanguage}</span> <ul> {availableLocales.map((localeItem) => { const isActive = localeItem === locale; return ( <li key={localeItem} class="p-1"> <a href={getLocalizedUrl(pathWithoutLocale, localeItem)} class={isActive ? "active" : undefined} aria-current={isActive ? "page" : undefined} > {getLocaleName(localeItem, locale)} </a> </li> ); })} </ul> </nav> </header> <main> <h1>{home.title}</h1> <p>{home.description}</p> </main> </Document> ); };Remix JSX 不是 React:
class原样书写(也接受className),并且通过handle.update()显式触发重新渲染。插值会自动转义。Intlayer 钩子是读取请求作用域的普通函数,因此可以从 setup 函数或 render 函数中调用。串联路由器与服务器
在 Intlayer 中间件旁添加来自
remix/middleware/render的render()中间件。它会在每个请求上挂载context.render(node, init),将 JSX 树流式转换为 HTMLResponse(在最前添加<!DOCTYPE html>并设置Content-Type请求头):src/router.tsx复制代码复制代码到剪贴板
import { isDeclaredLocale } from "intlayer"; import { intlayer } from "remix-intlayer"; import { render } from "remix/middleware/render"; import { createRouter } from "remix/router"; import { routes } from "./routes"; import { HomePage } from "./views/home"; // 1. Initialize router with Intlayer + render middleware export const router = createRouter({ middleware: [intlayer(), render()], }); // 2. Map route handlers router.map(routes, { actions: { // Default locale route home(context) { return context.render(<HomePage />); }, // Localized route localizedHome(context) { if (!isDeclaredLocale(context.params.locale)) { return new Response("Not Found", { status: 404 }); } return context.render(<HomePage />); }, }, });context.render接受可选的ResponseInit作为第二个参数,例如context.render(<NotFoundPage />, { status: 404 })。解析后的语言环境仍可作为context.intlayer.locale从处理程序访问,例如用于构建Response.json响应体。最后,通过标准
fetch处理函数暴露路由器。同一个路由器可无缝运行在 Node.js、Bun、Deno 及 Cloudflare Workers 上:src/server.ts复制代码复制代码到剪贴板
import * as http from "node:http"; import { createRequestListener } from "remix/node-fetch-server"; import { router } from "./router"; const PORT = Number(process.env.PORT || 3000); // Node.js const server = http.createServer( createRequestListener((request) => router.fetch(request)) ); server.listen(PORT, () => { console.log(`服务器运行在 http://localhost:${PORT}`); }); // Bun / Deno / Cloudflare Workers export default { port: PORT, fetch(request: Request) { return router.fetch(request); }, };审计并自动填充翻译
Intlayer 提供 CLI 工具来审计缺失的翻译并借助 AI 自动补充:
bash复制代码复制代码到剪贴板
TypeScript 配置
将 JSX 指向 remix/ui 运行时,并确保您的 tsconfig.json 包含生成的 .intlayer 类型:
复制代码到剪贴板
jsxImportSource: "remix/ui"使得<HomePage />会被解析为 Remix 的createElement而非 React 的。
结论
借助 Remix 3 与 Intlayer,您拥有了一个精简、完全类型安全且具备跨运行时可移植性的现代化技术栈,严格契合开放 Web 标准。无论构建简单的本地化营销页,还是部署在边缘网络的全球分布式服务,您的应用都能轻松从容扩展。
