# 国际化 (i18n) 规则

凡是涉及用户可见文案、提示信息、表单标签、错误消息或 UI 文本时，都应用本文件。

<!-- 请根据项目实际 i18n 方案修改 locales 路径、库与 key 前缀 -->

## 适用范围

- **现代栈（纯 React / Vue 等）**：以下「核心原则」「文案规范」「实现指导」与通用检查清单优先适用。
- **遗留 HTML/JS、渐进迁移或新旧并存**：新代码路径仍须满足核心原则；遗留层额外遵守「遗留代码与迁移场景」，避免扩大硬编码与混乱 key。

下文用 **`src/locales/`** 指代项目实际文案资源目录，请按仓库约定替换（如 `public/locales`、`lang/`、`messages/` 等）。

## 核心原则

- 在新应用代码（组件、路由页、hooks、composables）中，用户可见文案须使用 i18n，**禁止**硬编码中文或英文字符串
- i18n key 按模块组织，使用点分命名：`module.component.label`
- 相同语义的文案复用同一个 key，避免重复定义

## 文案规范

### 命名约定

```
common.confirm        → 通用确认
common.cancel         → 通用取消
user.login.title      → 用户登录标题
order.list.empty      → 订单列表空状态
```

### 分类

- `common.*` — 跨模块通用文案（确认、取消、提交、加载中等）
- `module.component.*` — 模块级文案
- `validation.*` — 表单校验提示
- `error.*` — 错误信息

### 语言文件

- **每种自然语言对应独立资源文件**（命名依项目约定，如 `en.json` 与 `zh-CN.json`，或 `en.ts` / `zh-CN.ts`），置于 `src/locales/`（或项目约定目录）下；**禁止**将多种语言的译文混写在同一文件或同一顶层多语言对象内，以免难以 diff、评审与按需加载。若仓库历史结构暂为混排，新增语言或新模块仍应优先采用分文件约定。
- **编码**：文案文件须以 **UTF-8** 保存（团队有约定时优先 **UTF-8 无 BOM**），确保在 IDE 中打开时中文、日文等非 ASCII 字符**不出现乱码**；禁止用系统默认 ANSI、GBK 等与 UTF-8 混用导致编码误判。若仓库已有 `.editorconfig`，与其中 `charset = utf-8` 等声明保持一致。

## 遗留代码与迁移场景

在渐进式迁移、微前端或新旧代码并存时：

- **遗留层**：不在 legacy HTML 或 JS 中**新写**用户可见的中/英文硬编码，除非该段为纯静态展示且仓库**尚未**建立可接入的国际化路径（避免扩大技术债）。
- **新代码路径**：React / Vue 等侧新增、修改的提示、错误、按钮、表单标签等，须走 i18n；新增子应用或模块时，须与现有语言文件结构、命名空间、懒加载策略一致，**不破坏**既有语言策略。
- **复用优先**：同一语义优先复用已有 key 或已有资源项，避免同一含义多份翻译。
- **多语言同步**：新增或修改条目后，至少核对当前功能已覆盖的**主要语言文件**是否一并更新。
- **改动前检索**：改提示、错误、按钮、表单标签前，先在 `src/locales/`（或项目约定目录）中搜索是否已有对应文案。
- **Key 与文案治理**：
  - key 按**页面 / 模块 / 语义**组织，避免继续沿用历史缩写或无意义分段。
  - 先按当前界面真实含义定 key，再写各语言译文。
  - 旧文案若仅「能用但不准」，优先**重命名 key 并重写译文**，不为劣质命名长期保留并行兼容。
  - 仅当某旧 key 已成为**对外契约**（后端、埋点、外部文档、第三方集成等固定引用）时，才引入映射层或别名过渡；否则不把历史包袱复制进新结构。

## 实现指导

### Vue（vue-i18n）

```vue
<template>
  <!-- 正确 -->
  <span>{{ $t('order.status.pending') }}</span>
  <!-- 错误：硬编码 -->
  <span>待处理</span>
</template>
```

### React（react-intl / react-i18next）

```tsx
// 正确
<FormattedMessage id="order.status.pending" />
// 或
const { t } = useTranslation();
<span>{t('order.status.pending')}</span>

// 错误：硬编码
<span>待处理</span>
```

## 检查清单

### 通用

- [ ] 所有新增用户可见文案使用 i18n key（新代码路径）
- [ ] 新增 key 已添加到所有**应覆盖**的语言文件，且各语言仍分文件维护（未混写）
- [ ] 语言资源文件为 UTF-8（无 BOM 或按仓库约定），在 IDE 中打开无乱码
- [ ] key 命名符合模块.组件.用途 的约定
- [ ] 包含变量的文案使用插值而非拼接
- [ ] 复数形式使用 i18n 复数规则而非条件判断
- [ ] 日期、数字、货币使用 i18n 格式化

### 迁移或混合栈时补充

- [ ] 已检索 `src/locales/`（或项目约定目录）中是否已有可复用文案
- [ ] 相关语言文件已同步更新
- [ ] 未在可国际化路径上新增用户可见硬编码（遗留静态例外见「遗留代码与迁移场景」）
- [ ] 新代码未破坏现有语言策略（命名空间、加载方式、子应用边界）

## 反模式

- 在模板或 JSX 中直接写中文字符串（新代码路径）
- 使用字符串拼接代替 i18n 插值（`"共" + count + "条"` → `t('common.total', { count })`）
- 在 JS/TS 逻辑中硬编码提示信息（新代码路径）
- 不同模块为相同含义的文案创建不同的 key
- 多种语言混在同一资源文件或同一顶层对象内，导致 diff、评审与按需加载困难
- 用非 UTF-8 或错误编码保存文案文件，在 IDE / CI / 协作环境下出现乱码
- 为迁就历史缩写或劣质命名长期保留多套并行 key，而不做治理
- 在非契约场景下继续沿用无意义旧 key，把混乱结构复制到新模块
