ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

ice.js 国际化(i18n)插件完整实战指南:多语言路由、SSR/SSG 与自动重定向

ice.js 国际化(i18n)插件完整实战指南:多语言路由、SSR/SSG 与自动重定向 前端Web框架SSR前端构建插件系统微前端跨平台【免费下载链接】ice ice.js: The Progressive App Framework Based On React基于 React 的渐进式应用框架项目地址https://gitcode.com/gh_mirrors/ice1/ice点击查看免费下载导读本文围绕 ice.js 官方提供的ice/plugin-i18n国际化插件展开讲解如何在基于 React 的渐进式应用框架 ice.js 3 中快速开启多语言能力。读完本文你将掌握国际化路由的自动生成原理、useLocale()/withLocale()等运行时 API 的用法、偏好语言识别与 Cookie 持久化机制、SSR/SSG 下的多语言 HTML 生成以及禁用 Cookie 等隐私场景的处理方案。插件核心特性一览ice.js 官方提供的 i18n 国际化插件可以在不引入任何第三方 i18n 库的情况下为应用快速开启国际化能力其核心特性包括自动处理和生成国际化路由为每个非默认语言自动生成带语言前缀的路由完美支持 SSR 和 SSG在服务端渲染与构建时静态生成阶段都能输出对应语言的页面以获得更好的 SEO 优化自动重定向到偏好语言页面根据用户偏好Cookie、浏览器语言、Accept-Language自动跳转到对应语言的国际化路由不耦合任何 i18n 库插件只负责路由、语言状态与持久化具体文案翻译交给 react-intl、react-i18next 等任意你熟悉的库来实现。插件在仓库中的实现位于 packages/plugin-i18n完整的可用示例工程是 examples/with-i18n。提示如果应用不需要国际化路由仅想在应用内支持多语言切换可以参考 examples/with-antd5 与 examples/with-fusion 两个示例。快速开始首先在终端执行以下命令安装插件$ npm i ice/plugin-i18n -D然后在ice.config.mts中添加插件和选项import { defineConfig } from ice/app; import i18n from ice/plugin-i18n; export default defineConfig({ plugins: [ i18n({ locales: [zh-CN, en-US, de], defaultLocale: zh-CN, }), ], });上面的en-US、zh-CN是国际化语言的缩写它们均遵循标准的 UTS 语言标识符。比如zh-CN中文中国zh-HK中文香港en-US英文美国de德文插件选项的校验逻辑从源码 packages/plugin-i18n/src/index.ts 可以看到插件在setup阶段会先调用checkPluginOptions对配置做基本校验locales必须是数组否则报错The plugin option locales type should be array...defaultLocale必须是字符串否则报错The plugin option defaultLocale type should be string...。两者任一不合法构建过程都会以process.exit(1)直接终止尽早暴露配置问题。构建时自动生成的内容插件通过generator.addRenderFile将 templates/plugin-i18n.ts.ejs 渲染为应用内的plugin-i18n.ts实际生成的内容大致等价于export function getDefaultLocale() { return zh-CN; } export function getAllLocales() { return [zh-CN, en-US, de]; }随后通过generator.addExport把getDefaultLocale、getAllLocales暴露为ice包导出把withLocale、useLocale暴露为ice/plugin-i18n/runtime导出同时把i18nConfig注入到customRuntimeOptions中供运行时读取。国际化路由国际化路由是指在页面路由地址中包含当前页面的语言一个国际化路由对应一种语言。假设现在插件的选项配置是import { defineConfig } from ice/app; import i18n from ice/plugin-i18n; export default defineConfig({ plugins: [ i18n({ locales: [zh-CN, en-US, nl-NL], defaultLocale: zh-CN, }), ], });假设有一个页面src/pages/home.tsx那么将会一一对应自动生成以下路由/home显示zh-CN语言默认语言对应的路由不包含语言前缀/en-US/home显示en-US语言/nl-NL/home显示nl-NL语言。访问不同的路由将会显示该语言对应的页面内容。路由生成原理从源码 packages/plugin-i18n/src/index.ts 可以看出插件通过addRoutesDefinition钩子注册路由定义。它先将locales中不等于defaultLocale的语言收集为prefixedLocales再遍历框架解析出的nestedRouteManifest即src/pages目录对应的路由清单为每个带前缀的语言生成形如/${locale}/${route.path}的新路由并把嵌套子路由一并递归注册const prefixedLocales locales.filter(locale locale ! defaultLocale); // ... prefixedLocales.forEach(prefixedLocale { options.nestedRouteManifest.forEach(route { const newRoutePath ${prefixedLocale}${route.path ? /${route.path} : }; defineRoute(newRoutePath, route.file, { index: route.index }, () { route.children defineChildrenRoutes(route.children, prefixedLocale); }); }); });也就是说默认语言的页面复用原始路由不带前缀而每个非默认语言会自动复制出一整套带语言前缀的路由树开发者无需手工维护。获取语言信息getAllLocales()用于获取当前应用支持的所有语言import { getAllLocales } from ice; console.log(getAllLocales()); // [zh-CN, en-US]getDefaultLocale()用于获取应用配置的默认语言import { getDefaultLocale } from ice; console.log(getDefaultLocale()); // zh-CNuseLocale()在 Function 组件中使用useLocale()Hook API它的返回值是一个数组包含两个值当前页面的语言一个 set 函数用于更新当前页面的语言。注意默认情况下调用此 set 函数时同时会更新 Cookie 中ice_locale的值为当前页面的语言。这样再次访问该页面时服务端请求能得知当前用户之前设置的偏好语言以便返回对应语言的页面内容。import { useLocale } from ice; export default function Home() { const [locale, setLocale] useLocale(); console.log(locale: , locale); // en-US return ( {/* 切换语言为 zh-CN */} div onClick{() setLocale(zh-CN)}Set zh-CN/div / ) }从实现上看useLocale()的本质是读取 I18nContext 这个 React Context 的值。I18nProvider初始化时会根据当前pathname解析出 URL 中携带的语言通过normalizeLocalePath解析不到时回退到defaultLocalesetLocale内部在非禁用 Cookie 的情况下会先调用setLocaleToCookie写入 Cookie再更新 React 状态从而触发页面重渲染。withLocale()使用withLocale()方法包裹 Class 组件组件的 Props 会包含locale和setLocale()函数可以查看和修改当前页面的语言。注意默认情况下调用setLocale()会更新 Cookie 中ice_locale的值为当前页面的语言。import { withLocale } from ice; function Home({ locale, setLocale }) { console.log(locale: , locale); // en-US return ( {/* 切换语言为 zh-CN */} div onClick{() setLocale(zh-CN)}Set zh-CN/div / ) } export default withLocale(Home);withLocale的实现同样基于 Context源码 中它是一个高阶组件内部调用useLocale()取出[locale, setLocale]后作为 Props 透传给被包裹的组件因此它实际上也可以用于 Function 组件。切换语言推荐使用setLocale()方法配合Link组件或者useNavigate()方法进行语言切换。方式一使用Link /import { useLocale, getAllLocales, Link, useLocation } from ice; export default function Layout() { const location useLocation(); const [activeLocale, setLocale] useLocale(); return ( main pbCurrent locale: /b{activeLocale}/p bChoose language: /b ul { getAllLocales().map((locale: string) { return ( li key{locale} Link to{location.pathname} onClick{() setLocale(locale)} {locale} /Link /li ); }) } /ul /main ); }方式二使用useNavigate()import { useLocale, useNavigate, useLocation } from ice; export default function Layout() { const [, setLocale] useLocale(); const location useLocation(); const navigate useNavigate(); const switchToZHCN () { setLocale(zh-CN); navigate(location.pathname); } return ( main div onClick{switchToZHCN} 点我切换到中文 /div /main ); }路由跳转时自动拼接语言前缀为什么使用Link/useNavigate()而非直接修改地址因为插件在运行时对history的push/replace做了劫持见 packages/plugin-i18n/src/runtime/hijackHistory.tsx。跳转时会自动检测当前偏好语言并为非默认语言拼上语言前缀保证语言切换后访问的路由仍然是正确的国际化路由。examples/with-i18n示例工程就采用了方式一在 layout.tsx 中遍历getAllLocales()渲染语言切换列表并利用react-intl的IntlProvider配合 locales.ts 中的文案映射zh-CN对应普通按钮、en-US对应Normal Button实现真正的翻译展示验证了插件不耦合任何 i18n 库的设计。路由自动重定向路由自动重定向是指如果当前访问的页面是根路由/将会根据当前语言环境自动跳转到对应的国际化路由。默认情况下路由自动重定向的功能是关闭的。如果需要开启则需要加入以下内容import { defineConfig } from ice/app; import i18n from ice/plugin-i18n; export default defineConfig({ plugins: [ i18n({ locales: [zh-CN, en-US, de], defaultLocale: zh-CN, autoRedirect: true, }), ], });其中语言环境的识别顺序如下CSRcookie 中ice_locale的值 window.navigator.languagedefaultLocaleSSRcookie 中ice_locale的值 Request Header中的Accept-LanguagedefaultLocale源码中的重定向实现结合 packages/plugin-i18n/src/runtime/index.tsx当autoRedirect为true时插件注册了一个addResponseHandler响应处理器对每个请求解析 URL调用detectLocale识别偏好语言再调用getLocaleRedirectPath判断是否需要重定向。只有当访问的是根路径/去除 basename 后且检测到的语言不是defaultLocale时才返回302 Found并携带location响应头指向/检测到的语言。这也解释了为什么自动重定向只对根路由生效。语言识别逻辑封装在 detectLocale.ts 中优先级依次是URL 路径中已携带的语言前缀normalizeLocalePathCookie 中的ice_locale值getLocaleFromCookie偏好语言getPreferredLocale客户端取window.navigator.languages服务端解析Accept-Language请求头使用的是accept-language-parser库回退到defaultLocale。部署阶段需要 Node 中间件配合在部署阶段路由自动重定向的功能需要配合 Node 中间件使用才能生效。比如import express from express; import { renderToHTML } from ./build/server/index.mjs; const app express(); app.use(express.static(build, {})); app.use(async (req, res) { const { statusCode, statusText, headers, value: body } await renderToHTML({ req, res }); res.statusCode statusCode; res.statusMessage statusText; Object.entries((headers || {}) as Recordstring, string).forEach(([name, value]) { res.setHeader(name, value); }); if (body req.method ! HEAD) { res.end(body); } else { res.end(); } });完整的可运行服务端示例见 examples/with-i18n/server.mts它监听4000端口并设置了basename为/app。对应地examples/with-i18n/ice.config.mts 中开启了autoRedirect: true和ssr: trueapp.tsx 中通过defineAppConfig配置了router.basename: /app。禁用 Cookie在上面的章节中提到用户设置的偏好语言存放在 Cookie 中的ice_locale调用setLocale()时会更新到 Cookie 中并且路由重定向和路由跳转的时候都依赖ice_locale的值。ice_locale这个 Cookie 名称定义在 packages/plugin-i18n/src/constants.ts 中。假设有这样一个场景用户拒绝接受 Cookie为了保护隐私就不能把偏好语言写到 Cookie 中了。此时需要做以下配置来禁用 Cookieimport { defineI18nConfig } from ice/plugin-i18n/types; export const i18nConfig defineI18nConfig(() ({ // 可以是一个 function disabledCookie: () { if (import.meta.renderer client) { return window.localStorage.getItem(acceptCookie) yes; } return false; }, // 也可以是 boolean 值 // disabledCookie: true, }));这样就禁用了 Cookie 的写入。在切换语言的时候需要在state对象中显式传入即将要切换的新语言的值import { Link, useLocale } from ice; export default function Home() { const [, setLocale] useLocale(); return ( Link to/ onClick{() setLocale(zh-CN)} state{{ locale: zh-CN }} 切换到 zh-CN /Link / ) }禁用 Cookie 后的运行时行为defineI18nConfig的类型定义与说明见 packages/plugin-i18n/src/types.ts它接受I18nAppConfig对象或其工厂函数。运行时插件会读取应用导出的i18nConfig合并默认值{ disableCookie: false }后计算得到最终的disableCookie值。当disableCookie为真时会有两处关键行为变化源码均在 I18nContext.tsx 与 hijackHistory.tsxsetLocale()不再写 Cookie只更新 React 状态路由跳转时不再从 Cookie 探测语言而是优先读取跳转state.locale中显式传入的新语言作为前缀拼接依据——这正是上面示例中state{{ locale: zh-CN }}存在的原因。SSG 多语言静态生成在开启 SSG 功能后ice.js 默认开启 SSG详见 website/docs/guide/basic/ssg.md插件将根据配置的locales的值在build阶段生成不同语言对应的 HTML。比如有以下目录结构包含about和index两个页面├── src/pages │ ├── about.tsx │ └── index.tsx假如插件的配置是import { defineConfig } from ice/app; import i18n from ice/plugin-i18n; export default defineConfig({ plugins: [ i18n({ locales: [zh-CN, en-US], defaultLocale: zh-CN, }), ], });那么将会生成 4 个 HTML 文件├── build │ ├── about │ │ └── index.html │ ├── en-US │ │ ├── about │ │ │ └── index.html │ │ └── index.html │ ├── index.html也就是说默认语言只生成一份不带前缀的 HTML每个非默认语言都会多生成一整套带语言目录的 HTML。结合 SSR/SSG 的能力搜索引擎可以分别抓取各语言版本的页面从而实现更好的 SEO 效果。集成测试的验证仓库中的集成测试 tests/integration/with-i18n.test.ts 对上述行为做了自动化验证构建with-i18n示例后断言build目录下的 HTML 文件列表精确等于[blog.html, blog/a.html, en-US.html, en-US/blog.html, en-US/blog/a.html, index.html]印证了每个非默认语言生成整套 HTML的规则访问/页面断言按钮文案为普通按钮zh-CN访问/en-US.html页面断言按钮文案为Normal Buttonen-US。该示例工程同时开启了ssr: true与autoRedirect: true是研究 SSR、SSG、国际化路由三者协同工作方式的最佳参考。插件选项一览locales类型string[]必填用于声明该应用支持的语言。插件在构建阶段会为除默认语言外的每种语言复制生成一套带前缀的国际化路由并在 SSG 构建时生成对应的语言目录 HTML。defaultLocale类型string必填声明该应用默认的语言。需要注意的是locales数组必须包含defaultLocale的值。默认语言对应的路由不包含语言前缀当访问根路由/且检测到用户偏好语言时自动重定向的目标就是defaultLocale之外的其他语言路径。autoRedirect类型boolean默认值false默认不会自动重定向到用户偏好语言对应的页面。如果设置为true在生产环境下一般需要配合 Node 中间件一起使用才能生效详见上文路由自动重定向一节。开启后插件通过响应处理器对根路由/返回302重定向响应跳转到用户偏好语言对应的国际化路由。与业务代码的组合实践把以上 API 组合起来一个完整的国际化页面通常由三部分组成参考 examples/with-i18n 的工程结构文案资源维护一份locales.ts之类的语言包如{en-US: { buttonText: Normal Button }, zh-CN: { buttonText: 普通按钮 }}语言状态注入在布局组件中调用useLocale()获取当前语言并借助任意 i18n 库如react-intl的 Provider 注入文案语言切换 UI 遍历getAllLocales()渲染所有可用语言点击时调用setLocale()切换路由切换切换语言后通过Link或useNavigate()跳转插件会自动拼接语言前缀并持久化偏好默认写入ice_localeCookie。这样路由层面由插件全权托管翻译与格式化交给任意 i18n 库两者职责清晰、互不耦合。这也是渐进式应用框架理念在国际化能力上的体现需要时引入插件即可获得开箱即用的多语言能力同时保留对具体实现方案的自由选择权。赞分享前端Web框架SSR前端构建插件系统微前端跨平台【免费下载链接】ice ice.js: The Progressive App Framework Based On React基于 React 的渐进式应用框架项目地址https://gitcode.com/gh_mirrors/ice1/ice点击查看免费下载相关推荐purejs-onepage-scroll高级技巧自定义动画与事件回调purejs onepage scroll高级技巧自定义动画与事件回调 purejs onepage scroll是一款轻量级的JavaScript单页滚动库前端Web框架SSR前端构建插件系统微前端跨平台Gutenberg 国际化i18n实战指南让 WordPress 区块插件走向多语言Gutenberg 国际化i18n实战指南让 WordPress 区块插件走向多语言 国际化Internationalization缩写为 i18n后端前端ice.js 国际化i18n最佳实践ice/plugin-i18n 插件完整指南与源码解析ice.js 国际化i18n最佳实践ice/plugin i18n 插件完整指南与源码解析 导读 ice/plugin i18n 是 ice.js 3前端Web框架SSR前端构建插件系统微前端跨平台上一篇攻克LinuxCNC坐标偏移痛点G52/G92参数持久化机制全解析下一篇Corinna AI团队协作功能多人管理销售机器人的方法创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表