Repo .git was damaged (refs/ missing, index truncated, object store near-empty). Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak, then full worktree committed as a rollback baseline.
54 lines
5.1 KiB
Markdown
54 lines
5.1 KiB
Markdown
# 投票页中文提示 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 公开页),我可立即开工。
|