Files
tamabox/outputs/vote-i18n-audit-2026-09-01.md
T
2026-09-08 10:10:44 +08:00

54 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 投票页中文提示 Bug 排查 + 同类 i18n 缺口审计(2026-09-01)
## 一、结论先行
- **你报告的 `投票成功!感谢参与` 确属 Bug**:`route/vote/vote.go` 的 `Submit` 之前把成功提示硬编码成了中文,未走 i18n,所以无论访客界面语言设成什么,弹出的都是中文。
- **已修复**:投票页全部用户提示 + 验证码包(共享根因,影响 6 个页面)。
- **还有大量同类问题**:route 层另有 40+ 处硬编码中文 flash(含登录页直接把 db 中文错误弹给用户),以及 db 层的中文错误哨兵,属于系统性 i18n 缺口,建议作为下一批次修复(见第三节)。
## 二、本次已修复(可直接验收)
### 1. `route/vote/vote.go`(投票参与页,访客可见)
- `SetSuccessFlash("投票成功!感谢参与")` → `i18n.T(lang, "vote.flash.success")`。
- `Page` / `Submit` 的全部 `SetErrorFlash("中文")` 改为对应 i18n 键(`vote.flash.notFound/notPublished/archived/ended/notStarted`、`vote.page.loginRequired/alreadyVoted`)。
- 表单解析 `parseSubmission` 内约 25 条校验错误(单选/多选/评分/排名/表单字段必填、超长、自定义选项、序列化失败等)全部改为 `i18n.T(lang, ...)`,动态字段名以 `%s` 占位注入(标签本身是箱主自定的内容,不翻译)。
- `censorCheck` 签名改为 `(ctx, lang, text)`,失败文案走 `censor.unavailable` 键。
- 新增 i18n 键:`vote.err.fieldRequired/fieldCustomRequired/fieldCustomTooLong/fieldInvalidOption/fieldConfigError/fieldRankIncomplete/fieldRankRange/fieldRankDuplicate/fieldTooLong/fieldNotNumber/fieldRating/customTooLong/serialize/unknownType/invalidOption/optionDuplicate`(四语言已补齐)。
### 2. `internal/captcha/math.go`(验证码,共享根因)
- `Verify` / `VerifyMath` 增加 `lang` 参数,6 处硬编码中文错误(不能为空/格式错误/答案格式/已过期/服务异常/答案错误/验证码错误)改为 i18n 键。
- 新增 i18n 键:`captcha.errEmpty/errFormat/errAnswerFormat/errExpired/errService/errAnswerWrong`(四语言已补齐)。
- 副作用:登录、注册、找回密码、提问箱页的验证码错误现在也跟随界面语言(这 6 个调用点已同步传 `ctx.Lang()` 并把 `if !ok` 分支的硬编码 `验证码错误` 改为 `captcha.error` 键)。
- 配套:`internal/context/context.go` 新增 `(*Context).Lang()` 方法(返回中间件注入的界面语言,未注入回退 `i18n.Default`),供后端 flash/错误本地化复用。
**验证**:gofmt 通过;`go build -overlay`(含 webp 屏障包)全绿;`go vet` 全绿;全量可运行测试(templates / i18n / vote / auth / question 等)通过。
## 三、尚未修复的同类问题(建议下一批次)
判定标准:用户可见的 flash / 错误信息硬编码中文、不跟随界面语言。按「访客可见 / 箱主后台」分级。
### P0 — 访客/公开页(与投票页同类,匿名用户可见,优先级最高)
| 文件:行 | 当前硬编码 | 建议键 |
|---|---|---|
| route/auth/login.go:49 | `errors.Cause(err).Error()` → `ErrBadCredential` "邮箱或密码错误" | `auth.errBadCredential` |
| route/auth/recover.go:124 | `用户不存在` | `auth.errUserNotFound` |
| route/auth/recover.go:70 / 86 / 105 / 169 | 邮件频繁/失败/验证码过期/密码修改成功 | `auth.*` |
| route/question/page.go:232 | `密码错误` | `question.errWrongPassword` |
| route/question/page.go:313 | `仅注册用户才能提问…` | `question.errLoginRequired` |
| route/question/page.go:322 / 345 | `不能给自己发起私聊` / `你已被对方拉黑…` | `question.*` |
| route/question/page.go:384 | `检测到重复提交,请稍后再试` | `question.errDuplicate` |
| route/question/follow_up.go:38/45/75/90/149/161/180 | 无权操作/未回答不能追问/等上一条/审核不可用/追问不存在/回答成功 等 | `question.*` |
| route/question/question.go:215/281/299/313/325 | 回答发布成功/操作成功/已读/未读 等 | `question.*` |
| internal/db/users.go:206-208 | `ErrBadCredential`/`ErrDuplicateEmail`/`ErrDuplicateDomain` 中文哨兵 | 改为键或 handler 层映射 |
### P1 — 箱主后台(登录态可见,影响较小)
route/user/vote.go、profile.go、password.go、tags.go、harassment.go 等约 25 处硬编码中文 flash
(如 `投票已发布!分享链接:…`、`更新标签成功`、`页面访问密码已更新`、`私聊设置更新成功` 等)。
### P2 — 内部 API(JS 消费的 JSON,非 flash)
internal/captcha/math.go `VerifyHandler` 返回的 `请求格式错误` / `验证码 ID 不能为空`(JSON message,前端弹通用提示,影响低)。
## 四、建议的下一步
1. **先修 P0(公开页)**:与投票页同类,直接决定匿名访客的语言体验,工作量约 8 个文件 + 约 30 个 i18n 键。
2. 再修 P1(箱主后台)+ P2(可选)。
3. db 层中文错误哨兵建议统一改为「哨兵错误 + handler 层按 `errors.Is` 映射 i18n」,避免翻译逻辑下沉到 db 包。
请确认是否按上述批次继续修复(特别是 P0 公开页),我可立即开工。