This commit is contained in:
ray zhou
2026-06-29 14:51:55 +08:00
parent 225fb2bd28
commit 2dd9f17da9
319 changed files with 29461 additions and 9412 deletions

View File

@@ -0,0 +1,92 @@
<!-- 22f17591-d664-4371-a1b6-9e4769285ad8 -->
---
todos:
- id: "middleware"
content: "新建 app/middleware/Language.php:读 X-Lang、归一化、每请求 locale()、设 $request->language"
status: pending
- id: "register"
content: "config/middleware.php 全局组在 TraceId 前注册 Language 中间件"
status: pending
- id: "config"
content: "config/translation.php 增加 supported=['zh_CN','en'],保留默认 zh_CN"
status: pending
- id: "trans-files"
content: "新建 resource/translations/{zh_CN,en}/messages.php,含 errors(按 OtaErrorCode)+ validation + common.ok"
status: pending
- id: "exception"
content: "BusinessException message 改可选;Handler 渲染时 trans('errors.'+code);Logic 7 处抛出去掉中文只传 code"
status: pending
- id: "result-auth"
content: "Result::fail 支持空 message 回退 trans;AdminAuth/InternalTokenAuth 去掉中文消息"
status: pending
- id: "validate"
content: "3 个 validator 的 $message 改翻译 key;AdminController::validate 返回前 trans();补全 validation.* 译文"
status: pending
- id: "verify"
content: "internal/admin 带 X-Lang 验证消息随 locale 变化 + 连续请求无串台,跑收尾检测脚本"
status: pending
isProject: false
---
# OTA 接口国际化(i18n)
## 结论先行
「中间件解析 header」思路正确,但在 Webman(常驻进程)里**核心动作是 `locale($lang)`**,不是把语言塞进 `$request->language``$request->language` 只作附带标记(给 Logic/日志用,非必须)。已具备:`config/translation.php``symfony/translation`。缺:语言中间件、翻译文件、把硬编码消息接到 `trans()`
约定:自定义头 `X-Lang`(值 `zh_CN` / `en`),默认 `zh_CN`,覆盖 internal + admin。
## 数据流
```mermaid
flowchart LR
req["HTTP 请求 X-Lang: en"] --> lang["Language 中间件 locale(en) + request->language"]
lang --> auth["Auth 中间件 Result::fail(code)"]
auth --> ctrl["Controller / Validate"]
ctrl --> logic["Logic throw BusinessException(code)"]
logic --> handler["Handler trans(code) 渲染"]
handler --> resp["{code, message(已按 locale 翻译), data}"]
```
## 改动点
### 1. 语言中间件(新增)
新建 `app/middleware/Language.php`(`MiddlewareInterface`):
-`X-Lang`(可兼容 `X-Language`),归一化(`zh`/`zh-CN`/`zh_cn``zh_CN`;`en`/`en-US``en`),不在支持列表则用默认。
- **每个请求都调 `locale($lang)`**(进程级全局状态,必须每请求重置,否则串语言)。
-`$request->language = $lang`(附带)。
- 支持列表 + 默认值读 `config('translation')`(见第 5 步)。
### 2. 注册为全局中间件
[config/middleware.php](config/middleware.php) 的 `''` 组在 `TraceId` 前加入 `app\middleware\Language::class`,确保 admin/internal 鉴权失败消息也按 locale 翻译。
### 3. 翻译文件(新增)
新建 `resource/translations/zh_CN/messages.php``resource/translations/en/messages.php`,返回两段:
- `errors` => 以 `OtaErrorCode` 各常量字符串为 key 的消息(对应现 Logic / Auth 里的中文)。
- `validation` => 校验消息 key(见第 6 步)。
调用形如 `trans('errors.' . $code)``trans($validationKey)`
### 4. 消息改为 trans() 取值
- [app/exception/Handler.php](app/exception/Handler.php):渲染 `BusinessException``message = trans('errors.' . $code)`(为空回退 code)。
- [app/exception/BusinessException.php](app/exception/BusinessException.php):`message` 改为可选;Logic 抛出时只传 code,去掉硬编码中文(`FirmwareLogic` 5 处、`OtaReportLogic`/`OtaCheckLogic`/`UpgradeRecordQueryLogic` 各 1 处)。需要动态文案的保留可选 message 入参。
- [app/support/Result.php](app/support/Result.php):`fail(string $code, ?string $message = null, ...)`,`$message` 为空时取 `trans('errors.' . $code)`;成功 `message``trans('common.ok')`
- 两个 Auth 中间件(`AdminAuth``InternalTokenAuth`)`Result::fail` 去掉中文,只传 code。
### 5. 配置补充
[config/translation.php](config/translation.php) 增加 `supported => ['zh_CN','en']`,`locale` 维持 `zh_CN` 作默认。中间件据此校验/回退。
### 6. 校验消息国际化(think\Validate)
为统一到单一 i18n(trans),不引入 think\Lang 第二套:
- 3 个 validator(`FirmwareCreateValidate``FirmwareListValidate``UpgradeRecordListValidate`)的 `$message` 值改为翻译 key(如 `validation.firmware.product_key_required`)。
- [app/admin/controller/AdminController.php](app/admin/controller/AdminController.php) `validate()` 返回前对错误信息做 `trans()`(key 命中则翻译,未命中原样返回)。
- 两个 locale 的 `messages.php` 补全这些 `validation.*` key。
## 验证
- internal:`POST /internal/ota/check` 带/不带 `X-Lang: en`,断言鉴权失败、业务异常、参数错误的 message 随 locale 变化。
- admin:固件创建非法参数,`X-Lang: en` 返回英文校验消息。
- 连续两请求(en 后 zh_CN)验证无 locale 串台(常驻进程回归)。
- 收尾跑 `~/.cursor/skills/slot-backend-completion-report/scripts/report.sh` 并附检测结果。
## 不做 / 待确认
- 不替换为 Accept-Language(已选自定义头)。
- 英文译文先给准确直译,文案润色后续可调。