Files
zhaoguiyang.site/.dev_docs/i18n_guide.md
joyzhao 21c337a040 docs: move i18n guide to .dev_docs directory
Relocate the internationalization guide from project root to .dev_docs folder for better documentation organization
2025-06-15 09:09:44 +08:00

335 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Astro + React 项目国际化 (i18n) 实施步骤
本文档将严格按照 Astro 官方文档的推荐实践,指导您一步步为您的 Astro + React 项目添加国际化功能。请在完成每个主要步骤后,与指导者确认,然后再进行下一步。
## 基本配置与约定
- **默认语言**: `en` (英语)
- **支持语言**: `en` (英语), `zh` (简体中文)
- **URL 结构**: 不同语言的内容将通过 URL 子路径区分,例如 `example.com/en/``example.com/zh/`
- **翻译文件格式**: JSON
## 基本术语
- **i18n**: Internationalization 的缩写,意为国际化。
- **L10n**: Localization 的缩写,意为本地化。
- **UI Strings**: 用户界面中需要翻译的文本,例如按钮标签、导航链接文本等。
---
## 步骤 1配置 Astro i18n 并组织页面
此步骤的目标是配置 Astro 以支持多种语言,并相应地组织您的页面文件结构。
### 1.1 配置 `astro.config.mjs`
您需要在 `astro.config.mjs` 文件中定义国际化相关的配置。虽然 Astro 的核心 i18n 功能不直接在 `defineConfig` 中指定 `locales``defaultLocale` (这些通常是集成特定 i18n 库或中间件时的配置),但我们将遵循一种通用的模式,即通过目录结构来管理多语言内容。
目前,我们不需要修改 `astro.config.mjs` 来显式声明语言Astro 会根据 `src/pages/` 下的目录结构自动处理多语言路由。
### 1.2 组织多语言页面目录
1.`src/pages/` 目录下,为项目支持的每种语言创建一个子目录。根据我们的约定,您需要创建以下目录:
* `src/pages/en/`
* `src/pages/zh/`
2. 将您现有的页面文件(例如 `index.astro`, `about.astro` 等)复制到每个语言目录下。例如:
```tree
src/
└── pages/
├── en/
│ ├── index.astro
│ └── about.astro (如果存在)
├── zh/
│ ├── index.astro
│ └── about.astro (如果存在)
└── index.astro (根目录的索引页,将用于重定向)
```
### 1.3 设置默认语言重定向
为了让访问网站根路径 (`/`) 的用户自动跳转到默认语言(我们约定为英语 `en`)的首页,请修改位于 `src/pages/index.astro` 的文件,内容如下 <mcreference index="0" link="https://docs.astro.build/zh-cn/recipes/i18n/">0</mcreference>
```astro
---
// src/pages/index.astro
// This page will redirect to the default language's home page.
---
<meta http-equiv="refresh" content="0;url=/en/" />
```
**关键点** <mcreference index="0" link="https://docs.astro.build/zh-cn/recipes/i18n/">0</mcreference>
* 这种 `meta refresh` 重定向方法适用于各种部署环境。
* 确保您的默认语言首页(例如 `src/pages/en/index.astro`)已创建并包含实际内容。
**完成后,请与指导者确认,然后再进行下一步。**
---
## 步骤 2创建翻译文件 (UI Strings)
此步骤的目标是为网站用户界面 (UI) 元素创建翻译字典。这些字典将存储不同语言的文本字符串。
### 2.1 创建 `ui.ts` 文件
根据 Astro 官方文档的建议,我们将在 `src/i18n/` 目录下创建一个 `ui.ts` 文件来存储翻译字符串 <mcreference index="0" link="https://docs.astro.build/zh-cn/recipes/i18n/">0</mcreference>。
1. 在 `src/` 目录下创建一个名为 `i18n` 的新文件夹。
2. 在 `src/i18n/` 文件夹中创建一个名为 `ui.ts` 的文件。
```tree
src/
├── i18n/
│ └── ui.ts
└── pages/
...
```
3. 将以下内容添加到 `src/i18n/ui.ts` 文件中 <mcreference index="0" link="https://docs.astro.build/zh-cn/recipes/i18n/">0</mcreference>
```typescript
// src/i18n/ui.ts
export const languages = {
en: 'English',
zh: '简体中文',
} as const;
export const defaultLang = 'en';
export const ui = {
en: {
'nav.home': 'Home',
'nav.projects': 'Projects',
'nav.experience': 'Experience',
'nav.skills': 'Skills',
'nav.awards': 'Awards',
'nav.education': 'Education',
'footer.rights': 'All rights reserved.',
// 根据您的项目实际情况添加更多翻译键值对
},
zh: {
'nav.home': '首页',
'nav.projects': '项目经历',
'nav.experience': '工作经历',
'nav.skills': '专业技能',
'nav.awards': '奖项荣誉',
'nav.education': '教育背景',
'footer.rights': '版权所有。',
// 根据您的项目实际情况添加更多翻译键值对
},
} as const;
```
**关键点** <mcreference index="0" link="https://docs.astro.build/zh-cn/recipes/i18n/">0</mcreference>
* `languages`: 一个对象,键是语言代码,值是该语言的人类可读名称。这对于构建语言切换器非常有用。
* `defaultLang`: 指定项目的默认语言代码。
* `ui`: 一个对象,其键是语言代码。每个语言代码下又是一个对象,包含该语言的翻译键和对应的翻译文本。
* 我们使用点号 (`.`) 来组织翻译键的层级,例如 `nav.home`。
* `as const` 用于确保 TypeScript 将这些对象视为常量,提供更好的类型推断和自动完成。
* **请根据您项目中的实际 UI 文本,在 `ui.en` 和 `ui.zh` 对象中添加或修改相应的翻译键值对。** 例如,导航栏链接、页脚文本、按钮文本等。
**完成后,请与指导者确认,然后再进行下一步。**
---
## 步骤 3创建翻译辅助函数
此步骤的目标是创建一些辅助函数,以便在您的 Astro 页面和组件中更轻松地获取和使用翻译字符串。
### 3.1 创建 `utils.ts` 文件
我们将在 `src/i18n/` 目录下创建一个 `utils.ts` 文件来存放这些辅助函数 <mcreference index="0" link="https://docs.astro.build/zh-cn/recipes/i18n/">0</mcreference>。
1. 在 `src/i18n/` 文件夹中创建一个名为 `utils.ts` 的新文件。
```tree
src/
├── i18n/
│ ├── ui.ts
│ └── utils.ts
└── pages/
...
```
2. 将以下内容添加到 `src/i18n/utils.ts` 文件中 <mcreference index="0" link="https://docs.astro.build/zh-cn/recipes/i18n/">0</mcreference>
```typescript
// src/i18n/utils.ts
import { ui, defaultLang, languages } from './ui';
export type Lang = keyof typeof ui;
export type UiKeys = keyof typeof ui[typeof defaultLang];
export function getLangFromUrl(url: URL): Lang {
const [, lang] = url.pathname.split('/');
if (lang in ui) return lang as Lang;
return defaultLang;
}
export function useTranslations(lang: Lang | undefined) {
const currentLang = lang || defaultLang;
return function t(key: UiKeys, params?: Record<string, string | number>): string {
let translation = ui[currentLang][key] || ui[defaultLang][key];
if (params) {
Object.keys(params).forEach(paramKey => {
const regex = new RegExp(`\\{\\s*${paramKey}\\s*\\}`, 'g');
translation = translation.replace(regex, String(params[paramKey]));
});
}
return translation;
}
}
export function getLocalizedPath(path: string, lang: Lang | undefined, base: string | URL = import.meta.env.BASE_URL): string {
const currentLang = lang || defaultLang;
const baseWithSlash = typeof base === 'string' && !base.endsWith('/') ? `${base}/` : String(base);
const langPath = currentLang === defaultLang ? '' : `${currentLang}/`;
const normalizedPath = path.startsWith('/') ? path.substring(1) : path;
return `${baseWithSlash}${langPath}${normalizedPath}`.replace(/\/\/+$/, '/'); // Ensure no double slashes at the end
}
export { languages, defaultLang };
```
**关键点** <mcreference index="0" link="https://docs.astro.build/zh-cn/recipes/i18n/">0</mcreference>
* `Lang` 和 `UiKeys`: TypeScript 类型定义,用于增强代码的类型安全。
* `Lang`: 表示支持的语言代码的类型 (例如 `'en' | 'zh'`)。
* `UiKeys`: 表示翻译字典中所有键的类型 (例如 `'nav.home' | 'nav.projects'`)。
* `getLangFromUrl(url: URL)`: 一个函数,用于从当前页面的 URL 中提取语言代码。如果 URL 中没有指定语言或者指定的语言无效,则返回默认语言。
* `useTranslations(lang: Lang | undefined)`: 一个高阶函数,它接收一个语言代码作为参数,并返回另一个函数 `t`。
* 返回的 `t(key: UiKeys, params?: Record<string, string | number>)` 函数用于获取特定键的翻译文本。
* 它会首先尝试获取当前语言的翻译,如果找不到,则回退到默认语言的翻译。
* 支持可选的 `params` 参数,用于在翻译字符串中插入动态值。例如,如果翻译字符串是 `Hello {name}`,你可以通过 `t('greeting', { name: 'World' })` 来得到 `Hello World`。
* `getLocalizedPath(path: string, lang: Lang | undefined, base: string | URL = import.meta.env.BASE_URL)`: 一个函数,用于生成本地化的 URL 路径。它会根据传入的语言和基础路径,自动添加语言子路径(如果不是默认语言)。
* `export { languages, defaultLang };`: 重新导出了 `ui.ts` 中的 `languages` 和 `defaultLang`,方便在其他地方统一从 `utils.ts` 导入。
**完成后,请与指导者确认,然后再进行下一步。**
---
## 步骤 4在 Astro 页面和组件中使用翻译
此步骤将演示如何在您的 Astro 页面 (`.astro` 文件) 和 React 组件 (`.tsx` 文件) 中使用我们创建的翻译辅助函数来显示本地化文本。
### 4.1 在 Astro 页面中使用翻译
在您的 Astro 页面中,您可以导入并使用 `getLangFromUrl` 和 `useTranslations` 函数来获取当前语言并翻译文本。
**示例:修改 `src/pages/en/index.astro` 和 `src/pages/zh/index.astro`**
假设您的首页需要显示一个欢迎标题。您可以像这样修改您的语言特定首页文件:
**`src/pages/en/index.astro`**
```astro
---
import Layout from '../../layouts/Layout.astro';
import { getLangFromUrl, useTranslations } from '../../i18n/utils';
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<Layout title={t('nav.home')} lang={lang}>
<h1>{t('nav.home')}</h1>
<p>Welcome to the English version of the site!</p>
<p>{t('footer.rights')}</p>
</Layout>
```
**`src/pages/zh/index.astro`**
```astro
---
import Layout from '../../layouts/Layout.astro';
import { getLangFromUrl, useTranslations } from '../../i18n/utils';
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<Layout title={t('nav.home')} lang={lang}>
<h1>{t('nav.home')}</h1>
<p>欢迎来到本站的中文版本!</p>
<p>{t('footer.rights')}</p>
</Layout>
```
**关键点** <mcreference index="0" link="https://docs.astro.build/zh-cn/recipes/i18n/">0</mcreference>
* 我们从 `../../i18n/utils` 导入了 `getLangFromUrl` 和 `useTranslations`。
* `getLangFromUrl(Astro.url)` 用于从当前页面的 URL 获取语言代码。
* `useTranslations(lang)` 返回了翻译函数 `t`。
* 我们使用 `t('key_name')` 来获取翻译后的字符串,例如 `t('nav.home')` 和 `t('footer.rights')`。
* 确保您的 `Layout.astro` 组件能够接收并使用 `lang` prop 来设置 HTML 的 `lang` 属性,例如 `<html lang={Astro.props.lang}>`。
### 4.2 在 React 组件中使用翻译
要在 React 组件中使用翻译,您需要将翻译函数 `t` 和当前语言 `lang` 作为 props 传递给组件。
**示例:创建一个简单的 React 页脚组件 `src/components/Footer.tsx`**
1. 创建文件 `src/components/Footer.tsx`
```tree
src/
├── components/
│ └── Footer.tsx
├── i18n/
│ ...
└── pages/
...
```
2. 将以下内容添加到 `src/components/Footer.tsx`
```tsx
// src/components/Footer.tsx
import type { Lang, UiKeys } from '../i18n/utils'; // 导入类型
interface FooterProps {
lang: Lang;
t: (key: UiKeys, params?: Record<string, string | number>) => string;
}
export default function Footer({ lang, t }: FooterProps) {
return (
<footer>
<p>{t('footer.rights')}</p>
<p>Current language: {lang}</p>
</footer>
);
}
```
3. 在您的 Astro 页面中使用此组件,例如在 `src/pages/en/index.astro` 中:
```astro
---
import Layout from '../../layouts/Layout.astro';
import { getLangFromUrl, useTranslations } from '../../i18n/utils';
import Footer from '../../components/Footer.tsx'; // 导入 React 组件
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<Layout title={t('nav.home')} lang={lang}>
<h1>{t('nav.home')}</h1>
<p>Welcome to the English version of the site!</p>
<Footer lang={lang} t={t} client:visible />
</Layout>
```
**关键点**:
* 我们为 `FooterProps` 定义了接口,明确了 `lang` 和 `t` 的类型。
* `lang` 和 `t` 函数从 Astro 页面作为 props 传递给 `Footer` 组件。
* 在 React 组件内部,我们直接使用传递进来的 `t` 函数进行翻译。
* `client:visible` (或其他 Astro 客户端指令) 是必需的,以使 React 组件在客户端进行交互式渲染。
**请根据您的项目结构和需求,在相应的 Astro 页面和 React 组件中应用这些翻译方法。**
**完成后,请与指导者确认,然后再进行下一步。**
---