# 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` 的文件,内容如下 0: ```astro --- // src/pages/index.astro // This page will redirect to the default language's home page. --- ``` **关键点** 0: * 这种 `meta refresh` 重定向方法适用于各种部署环境。 * 确保您的默认语言首页(例如 `src/pages/en/index.astro`)已创建并包含实际内容。 **完成后,请与指导者确认,然后再进行下一步。** --- ## 步骤 2:创建翻译文件 (UI Strings) 此步骤的目标是为网站用户界面 (UI) 元素创建翻译字典。这些字典将存储不同语言的文本字符串。 ### 2.1 创建 `ui.ts` 文件 根据 Astro 官方文档的建议,我们将在 `src/i18n/` 目录下创建一个 `ui.ts` 文件来存储翻译字符串 0。 1. 在 `src/` 目录下创建一个名为 `i18n` 的新文件夹。 2. 在 `src/i18n/` 文件夹中创建一个名为 `ui.ts` 的文件。 ```tree src/ ├── i18n/ │ └── ui.ts └── pages/ ... ``` 3. 将以下内容添加到 `src/i18n/ui.ts` 文件中 0: ```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; ``` **关键点** 0: * `languages`: 一个对象,键是语言代码,值是该语言的人类可读名称。这对于构建语言切换器非常有用。 * `defaultLang`: 指定项目的默认语言代码。 * `ui`: 一个对象,其键是语言代码。每个语言代码下又是一个对象,包含该语言的翻译键和对应的翻译文本。 * 我们使用点号 (`.`) 来组织翻译键的层级,例如 `nav.home`。 * `as const` 用于确保 TypeScript 将这些对象视为常量,提供更好的类型推断和自动完成。 * **请根据您项目中的实际 UI 文本,在 `ui.en` 和 `ui.zh` 对象中添加或修改相应的翻译键值对。** 例如,导航栏链接、页脚文本、按钮文本等。 **完成后,请与指导者确认,然后再进行下一步。** --- ## 步骤 3:创建翻译辅助函数 此步骤的目标是创建一些辅助函数,以便在您的 Astro 页面和组件中更轻松地获取和使用翻译字符串。 ### 3.1 创建 `utils.ts` 文件 我们将在 `src/i18n/` 目录下创建一个 `utils.ts` 文件来存放这些辅助函数 0。 1. 在 `src/i18n/` 文件夹中创建一个名为 `utils.ts` 的新文件。 ```tree src/ ├── i18n/ │ ├── ui.ts │ └── utils.ts └── pages/ ... ``` 2. 将以下内容添加到 `src/i18n/utils.ts` 文件中 0: ```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 { 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 }; ``` **关键点** 0: * `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)` 函数用于获取特定键的翻译文本。 * 它会首先尝试获取当前语言的翻译,如果找不到,则回退到默认语言的翻译。 * 支持可选的 `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); ---

{t('nav.home')}

Welcome to the English version of the site!

{t('footer.rights')}

``` **`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); ---

{t('nav.home')}

欢迎来到本站的中文版本!

{t('footer.rights')}

``` **关键点** 0: * 我们从 `../../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` 属性,例如 ``。 ### 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; } export default function Footer({ lang, t }: FooterProps) { return (

{t('footer.rights')}

Current language: {lang}

); } ``` 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); ---

{t('nav.home')}

Welcome to the English version of the site!