跳到主要内容

国际化

ice.js 官方提供 i18n 国际化插件,支持在应用快速开启国际化能力。核心特性包括:

  1. 支持自动处理和生成国际化路由
  2. 完美支持 SSR 和 SSG,以获得更好的 SEO 优化
  3. 支持自动重定向到偏好语言对应的页面
  4. 不耦合任何一个 i18n 库(流行的 React i18n 库有 react-intlreact-i18next 等),你可以选择任一国际化的库来为你的应用设置国际化
使用国际化插件的示例
提示

如果应用不需要使用国际化路由,你可以参考以下例子来让你的项目支持国际化:

快速开始

首先,我们需要在终端执行以下命令安装插件:

$ 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-USzh-CN 是国际化语言的缩写,它们均遵循标准的 UTS 语言标识符。比如:

  • zh-CN:中文(中国)
  • zh-HK:中文(香港)
  • en-US:英文(美国)
  • de: 德文

国际化路由

国际化路由是指在页面路由地址中包含了当前页面的语言,一个国际化路由对应一个语言。

假设现在插件的选项配置是:

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 语言

访问不同的路由,将会显示该语言对应页面内容。

获取语言信息

getLocales()

getAllLocales() 用于获取当前应用支持的所有语言。

import { getAllLocales } from 'ice';

console.log(getAllLocales()); // ['zh-CN', 'en-US']

getDefaultLocale()

getDefaultLocale() 用于获取应用配置的默认语言。

import { getDefaultLocale } from 'ice';

console.log(getDefaultLocale()); // 'zh-CN'

useLocale()

在 Function 组件中使用 useLocale() Hook API,它的返回值是一个数组,包含两个值:

  1. 当前页面的语言
  2. 一个 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>
</>
)
}

withLocale()

使用 withLocale() 方法包裹的 Class 组件,组件的 Props 会包含 localesetLocale() 函数,可以查看和修改当前页面的语言。注意,默认情况下调用 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);

切换语言

推荐使用 setLocale() 方法配合 <Link> 组件或者 useNavigate() 方法进行语言切换:

import { useLocale, getAllLocales, Link, useLocation } from 'ice';

export default function Layout() {
const location = useLocation();
const [activeLocale, setLocale] = useLocale();

return (
<main>
<p><b>Current locale: </b>{activeLocale}</p>

<b>Choose language: </b>
<ul>
{
getAllLocales().map((locale: string) => {
return (
<li key={locale}>
<Link
to={location.pathname}
onClick={() => setLocale(locale)}
>
{locale}
</Link>
</li>
);
})
}
</ul>
</main>
);
}

路由自动重定向

路由自动重定向是指,如果当前访问的页面是根路由(/),将会根据当前语言环境自动跳转到对应的国际化路由。

默认情况下,路由自动重定向的功能是关闭的。如果需要开启,则需要加入以下内容:

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,
}),
],
});

其中,语言环境的识别顺序如下:

  • CSR:cookie 中 ice_locale 的值 > window.navigator.language > defaultLocale
  • SSR:cookie 中 ice_locale 的值 > Request Header 中的 Accept-Language > defaultLocale

在部署阶段,路由自动重定向的功能需要配合 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 Record<string, string>).forEach(([name, value]) => {
res.setHeader(name, value);
});
if (body && req.method !== 'HEAD') {
res.end(body);
} else {
res.end();
}
});

在上面的章节中提到,用户设置的偏好语言是存放在 Cookie 中的 ice_locale,调用 setLocale() 时会更新到 Cookie 中,并且路由重定向和路由跳转的时候依赖 ice_locale 的值。

假设有这么一个场景,用户拒绝接受 Cookie,为了保护隐私,这样就不能把偏好语言写到 Cookie 中了。因此需要做以下的配置来禁用 Cookie:

src/app.ts
import { 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>
</>
)
}

SSG

在开启 SSG 功能后,将根据配置的 locales 的值,在 build 阶段会生成不同语言对应的 HTML。

比如我们有以下的目录结构,包含 aboutindex 两个页面:

├── 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

插件选项

locales

  • 类型:string[]

用于声明该应用支持的语言。

defaultLocale

  • 类型:string

声明该应用默认的语言。需要注意的是, locales 数组必须包含 defaultLocale 的值。

autoRedirect

  • 类型:boolean
  • 默认值:false

默认不会自动重定向到用户偏好语言对应的页面。如果设置为 true,在生产环境下,一般需要配合 Node 中间件一起使用才能生效。详见