ice/website/docs/guide/advanced/i18n.md

381 lines
9.8 KiB
Markdown
Raw Permalink Normal View History

文档 (#203) * feat: docs directory * docs: 调整结构 * chore: lock * feat: update docs * docs: update assets doc * 新增文档 for 介绍 and 环境变量 (#307) * docs: add about * docs: add docs for env * docs: update about * docs: config (#306) * docs: router (#304) * docs: router * feat: getconfig * docs: get data * docs: ssg * docs: ssr * docs: document * docs: page * docs: enable ssr * fix: title * Doc/rax compat (#303) * docs: add docs of rax-compat * docs: modify doc of rax compat * Docs: app entry (#301) * docs: app entry * docs: app entry * docs: mock (#245) * docs: mock * chore: typo * chore: title * docs: styles (#244) * docs: style * chore: acss title * Docs: contributing (#243) * docs: contributing * chore: typo * chore: update contributing docs * fix: typo * chore: add putteteer install * docs: quick start (#235) * docs: quick start * chore: update IDE * chore: update docs * docs: app directory (#236) * docs: directory * chore: document desc * docs: update website * docs: update about * docs: update docs * chore: update prism * chore: update docs * chore: update Co-authored-by: luhc228 <luhengchang228@126.com> Co-authored-by: ClarkXia <xiawenwu41@gmail.com> Co-authored-by: 逆葵 <xianyong.yxy@alibaba-inc.com> Co-authored-by: ZeroLing <zhuoling.lcl@alibaba-inc.com> Co-authored-by: ZeroLing <i@zeroling.com> Co-authored-by: 水澜 <shuilan.cj@taobao.com> Co-authored-by: 染陌同学 <answershuto@gmail.com> Co-authored-by: luhc228 <44047106+luhc228@users.noreply.github.com>
2022-07-01 16:47:25 +08:00
---
title: 国际化
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
ice.js 官方提供 i18n 国际化插件,支持在应用快速开启国际化能力。核心特性包括:
1. 支持自动处理和生成国际化路由
2. 完美支持 SSR 和 SSG以获得更好的 SEO 优化
3. 支持自动重定向到偏好语言对应的页面
4. 不耦合任何一个 i18n 库(流行的 React i18n 库有 [react-intl](https://formatjs.io/docs/getting-started/installation/)、[react-i18next](https://react.i18next.com/) 等),你可以选择任一国际化的库来为你的应用设置国际化
<details open>
<summary>使用国际化插件的示例</summary>
<ul>
<li>
<a href="https://github.com/alibaba/ice/tree/master/examples/with-i18n" target="_blank" rel="noopener noreferrer">
with-i18n
</a>
</li>
</ul>
</details>
:::tip
如果应用不需要使用国际化路由,你可以参考以下例子来让你的项目支持国际化:
- [with-antd5](https://github.com/alibaba/ice/tree/master/examples/with-antd5)
- [with-fusion](https://github.com/alibaba/ice/tree/master/examples/with-fusion)
:::
## 快速开始
首先,我们需要在终端执行以下命令安装插件:
```bash
$ npm i @ice/plugin-i18n -D
```
然后在 `ice.config.mts` 中添加插件和选项:
```ts
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 语言标识符](https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers)。比如:
- `zh-CN`:中文(中国)
- `zh-HK`:中文(香港)
- `en-US`:英文(美国)
- `de`: 德文
## 国际化路由
国际化路由是指在页面路由地址中包含了当前页面的语言,一个国际化路由对应一个语言。
假设现在插件的选项配置是:
```ts
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()` 用于获取当前应用支持的所有语言。
```ts
import { getAllLocales } from 'ice';
console.log(getAllLocales()); // ['zh-CN', 'en-US']
```
### `getDefaultLocale()`
`getDefaultLocale()` 用于获取应用配置的默认语言。
```ts
import { getDefaultLocale } from 'ice';
console.log(getDefaultLocale()); // 'zh-CN'
```
### `useLocale()`
在 Function 组件中使用 `useLocale()` Hook API它的返回值是一个数组包含两个值
1. 当前页面的语言
2. 一个 set 函数用于更新当前页面的语言。注意,默认情况下调用此 set 函数时候,同时会更新 Cookie 中 `ice_locale` 的值为当前页面的语言。这样,再次访问该页面时,从服务端请求能得知当前用户的之前设置的偏好语言,以便返回对应语言的页面内容。
```tsx
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 会包含 `locale``setLocale()` 函数,可以查看和修改当前页面的语言。注意,默认情况下调用 `setLocale()`,会更新 Cookie 中 `ice_locale` 的值为当前页面的语言。这样,再次访问该页面时,从服务端请求能得知当前用户的之前设置的偏好语言,以便返回对应语言的页面内容。
```tsx
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()` 方法进行语言切换:
<Tabs>
<TabItem value="a" label="使用 <Link />">
```tsx
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>
);
}
```
</TabItem>
<TabItem value="b" label="使用 useNavigate()">
```tsx
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>
);
}
```
</TabItem>
</Tabs>
## 路由自动重定向
路由自动重定向是指,如果当前访问的页面是根路由(`/`),将会根据当前语言环境自动跳转到对应的国际化路由。
默认情况下,路由自动重定向的功能是关闭的。如果需要开启,则需要加入以下内容:
```diff
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 中间件使用才能生效。比如:
```ts
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
在上面的章节中提到,用户设置的偏好语言是存放在 Cookie 中的 `ice_locale`,调用 `setLocale()` 时会更新到 Cookie 中,并且路由重定向和路由跳转的时候依赖 `ice_locale` 的值。
假设有这么一个场景,用户拒绝接受 Cookie为了保护隐私这样就不能把偏好语言写到 Cookie 中了。因此需要做以下的配置来禁用 Cookie
```ts title="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` 对象中显式传入即将要切换的新语言的值:
```tsx
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。
比如我们有以下的目录结构,包含 `about``index` 两个页面:
```md
├── src/pages
| ├── about.tsx
| └── index.tsx
```
假如插件的配置是:
```ts
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 文件:
```md
├── build
| ├── about
| | └── index.html
| ├── en-US
| | ├── about
| | | └── index.html
| | └── index.html
| ├── index.html
```
## 插件选项
### `locales`
- **类型:**`string[]`
用于声明该应用支持的语言。
### `defaultLocale`
- **类型:**`string`
声明该应用默认的语言。需要注意的是, `locales` 数组必须包含 `defaultLocale` 的值。
### `autoRedirect`
- **类型:**`boolean`
- **默认值:**`false`
默认不会自动重定向到用户偏好语言对应的页面。如果设置为 `true`,在生产环境下,一般需要配合 Node 中间件一起使用才能生效。[详见](#路由自动重定向)