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

14 KiB
Raw Blame History

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 中指定 localesdefaultLocale (这些通常是集成特定 i18n 库或中间件时的配置),但我们将遵循一种通用的模式,即通过目录结构来管理多语言内容。

目前,我们不需要修改 astro.config.mjs 来显式声明语言Astro 会根据 src/pages/ 下的目录结构自动处理多语言路由。

1.2 组织多语言页面目录

  1. src/pages/ 目录下,为项目支持的每种语言创建一个子目录。根据我们的约定,您需要创建以下目录:

    • src/pages/en/
    • src/pages/zh/
  2. 将您现有的页面文件(例如 index.astro, about.astro 等)复制到每个语言目录下。例如:

    src/
    └── pages/
        ├── en/
        │   ├── index.astro
        │   └── about.astro (如果存在)
        ├── zh/
        │   ├── index.astro
        │   └── about.astro (如果存在)
        └── index.astro (根目录的索引页,将用于重定向)
    

1.3 设置默认语言重定向

为了让访问网站根路径 (/) 的用户自动跳转到默认语言(我们约定为英语 en)的首页,请修改位于 src/pages/index.astro 的文件,内容如下 0

---
// src/pages/index.astro
// This page will redirect to the default language's home page.
--- 
<meta http-equiv="refresh" content="0;url=/en/" />

关键点 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 的文件。

    src/
    ├── i18n/
    │   └── ui.ts
    └── pages/
        ...
    
  3. 将以下内容添加到 src/i18n/ui.ts 文件中 0

    // 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.enui.zh 对象中添加或修改相应的翻译键值对。 例如,导航栏链接、页脚文本、按钮文本等。

完成后,请与指导者确认,然后再进行下一步。


步骤 3创建翻译辅助函数

此步骤的目标是创建一些辅助函数,以便在您的 Astro 页面和组件中更轻松地获取和使用翻译字符串。

3.1 创建 utils.ts 文件

我们将在 src/i18n/ 目录下创建一个 utils.ts 文件来存放这些辅助函数 0。

  1. src/i18n/ 文件夹中创建一个名为 utils.ts 的新文件。

    src/
    ├── i18n/
    │   ├── ui.ts
    │   └── utils.ts
    └── pages/
        ...
    
  2. 将以下内容添加到 src/i18n/utils.ts 文件中 0

    // 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 };
    

关键点 0

  • LangUiKeys: 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 中的 languagesdefaultLang,方便在其他地方统一从 utils.ts 导入。

完成后,请与指导者确认,然后再进行下一步。


步骤 4在 Astro 页面和组件中使用翻译

此步骤将演示如何在您的 Astro 页面 (.astro 文件) 和 React 组件 (.tsx 文件) 中使用我们创建的翻译辅助函数来显示本地化文本。

4.1 在 Astro 页面中使用翻译

在您的 Astro 页面中,您可以导入并使用 getLangFromUrluseTranslations 函数来获取当前语言并翻译文本。

示例:修改 src/pages/en/index.astrosrc/pages/zh/index.astro

假设您的首页需要显示一个欢迎标题。您可以像这样修改您的语言特定首页文件:

src/pages/en/index.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

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

关键点 0

  • 我们从 ../../i18n/utils 导入了 getLangFromUrluseTranslations
  • 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

    src/
    ├── components/
    │   └── Footer.tsx
    ├── i18n/
    │   ...
    └── pages/
        ...
    
  2. 将以下内容添加到 src/components/Footer.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 中:

    ---
    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 定义了接口,明确了 langt 的类型。
  • langt 函数从 Astro 页面作为 props 传递给 Footer 组件。
  • 在 React 组件内部,我们直接使用传递进来的 t 函数进行翻译。
  • client:visible (或其他 Astro 客户端指令) 是必需的,以使 React 组件在客户端进行交互式渲染。

请根据您的项目结构和需求,在相应的 Astro 页面和 React 组件中应用这些翻译方法。

完成后,请与指导者确认,然后再进行下一步。