## Context

项目已使用手写的 `AppLocalizations` 类（`lib/core/localization/app_localizations.dart`），通过 `_localizedValues` 嵌套 Map 存储中英文翻译，并由 `LocaleProvider` 控制当前语言。基础设施完备，但实际 UI 代码大量使用中文字面量，覆盖率不足。

i18n 审计结果：
- 53 个 `.dart` 文件包含中文字符
- 约 456 处硬编码字符串
- 现有 `AppLocalizations` 已定义约 100 个键，但许多 UI 文本（如 SnackBar、Dialog、表单标签、占位符、错误消息）仍未通过翻译函数获取

## Goals / Non-Goals

**Goals:**
- 用户可见的所有 UI 文本均通过 `AppLocalizations` 获取
- 切换语言后所有页面即时刷新
- 翻译键命名统一、易于维护
- 保留现有手写翻译方案，避免引入新依赖（如 `intl_utils`、`slang`）

**Non-Goals:**
- 不改造日志/调试输出中的中文（仅面向开发者）
- 不本地化数据库种子数据（属于业务数据，由用户编辑）
- 不引入 `.arb` 文件 + 代码生成方案
- 不新增除中英文之外的第三种语言（架构需支持，但本次不实现）

## Decisions

### 决策 1：沿用手写 `AppLocalizations`，不迁移到 `flutter gen-l10n`

**选择**：保留现有 `Map<String, Map<String, String>>` 结构。

**理由**：
- 项目已具备完整的代理（`Delegate`）和 `LocaleProvider` 设置，迁移成本高
- 翻译规模有限（预估最终 250 个左右键），手写可维护
- 避免引入 `build_runner` 代码生成步骤

**已考虑替代方案**：
- `flutter_localizations + .arb` 文件：标准方案但需引入代码生成，对小项目过度
- `slang` 包：类型安全但需新增依赖和构建步骤

### 决策 2：参数化文本统一使用方法形式

**选择**：将带参数的翻译从「getter + 拼接」改为「方法返回完整字符串」。

示例：
```dart
// 旧（错误）
String get importSuccess => '成功导入';
String get programsImported => '个程序';
// 调用：'${l.importSuccess} $count ${l.programsImported}'

// 新（正确）
String importedPrograms(int count) =>
  locale.languageCode == 'en'
    ? 'Successfully imported $count programs'
    : '成功导入 $count 个程序';
```

**理由**：英文语序与中文不同，拼接会产生不自然的文本（如 "Successfully imported 5 programs" 才正确，而非 "Successfully imported 5 programs"）。

### 决策 3：分模块按 feature 推进

将替换工作按 feature 拆分，每个 feature 一个原子提交：
1. `core` 模块（router、theme）
2. `features/home`
3. `features/programs`
4. `features/program_detail`
5. `features/device`（仅 UI 层，service 层错误消息走错误码）
6. `features/settings`
7. `shared/widgets`

每个 feature 完成后运行 `flutter analyze` + 手动切换语言验证。

### 决策 4：Service 层错误使用错误码而非翻译

`SerialPortService`、`DeviceMessageService` 等返回 `Exception` 时使用稳定的英文错误码或自定义枚举，由 UI 层调用 `AppLocalizations` 的方法转换为本地化消息。

**理由**：Service 层无法持有 `BuildContext`，强行注入 `Locale` 会污染领域层职责。

### 决策 5：新增翻译键的命名约定

- 采用 `camelCase`
- 按模块前缀分组：`home.*`、`program.*`、`step.*`、`device.*`、`settings.*`、`common.*`
- 但为了兼容现有键，新增键先采用相同的平铺命名风格，待整体替换完成后视情况重构

## Risks / Trade-offs

- **[风险] 漏改字符串**：456 处替换易遗漏 → **缓解**：完成后用 grep `[一-鿿]` 全量扫描 `lib/features/**/*.dart`、`lib/shared/**/*.dart`，确保 UI 层无中文字面量
- **[风险] 英文翻译质量参差不齐**：开发者非母语翻译 → **缓解**：保持术语表统一（device、program、step、position、volume 等），由审阅时统一校对
- **[风险] 现有 Dialog/Snackbar 使用了拼接字符串**：英文语序错乱 → **缓解**：通过参数化方法重构（决策 2）
- **[风险] 修改面广，潜在 UI 回归**：可能误改逻辑代码 → **缓解**：严格只替换字符串字面量，每个 feature 完成后人工运行验证
- **[权衡] 不引入代码生成**：可读性 vs. 类型安全 → 接受手写翻译表的维护成本以换取零构建步骤

## Migration Plan

1. **准备**：扩充 `AppLocalizations` 翻译表，补齐所有需要的新键（中英文）
2. **逐模块替换**：按决策 3 顺序，每个 feature 一次提交
3. **回归验证**：每次提交后启动应用，切换中英文，确认无遗漏文本
4. **最终扫描**：grep 全量检查 `lib/features/`、`lib/shared/`、`lib/core/router/`、`lib/core/theme/` 中是否仍有中文字面量（排除注释）
5. **回滚策略**：每个 feature 独立提交，可单独 revert

## Open Questions

- Service 层抛出的 `Exception` 是否需要定义统一的错误码枚举？（待实施时根据现有错误类型决定）
- 程序名称（用户在数据库中输入的中文）是否需要支持英文模式下显示英文别名？（**默认否**，用户数据原样展示）
