refactor(i18n): consolidate i18n logic into pkg/i18n, fix TOML loading issue, and migrate translation files

This commit is contained in:
keven1024
2026-05-24 11:29:11 +08:00
parent af6ca85ff5
commit ac8b6f642b
5 changed files with 119 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-01

View File

@@ -0,0 +1,49 @@
## Context
项目使用 Go workspacego.work`pkg/` 下有多个共享包models、utils、services、i18n。worker 模块通过 workspace 引用这些包。
当前 `pkg/i18n` 存在两个问题:
1. `Init()` 中注册的是 JSON 格式解析器,但 locales 目录下的文件是 `.toml`,导致实际加载翻译时什么都加载不到
2. 只有英文一种翻译,缺少其他语言
worker 独立维护了 7 种语言的翻译文件和一套完整的 i18n 初始化/加载逻辑,但这套逻辑使用动态路径查找(尝试多个相对路径),在容器化部署中脆弱。
## Goals / Non-Goals
**Goals:**
- 修复 `pkg/i18n` 的 TOML 加载 bug
- 将所有翻译文件集中到 `pkg/i18n/locales/`,通过 `embed.FS` 编译进二进制,消除运行时路径依赖
- worker 移除本地 i18n 实现,改用 `pkg/i18n`
**Non-Goals:**
- 不增加新的翻译 key
- 不修改 `Init()` 函数签名
- 不影响任何 HTTP API 或前端行为
## Decisions
### 1. 使用 embed.FS 而非动态路径
worker 当前通过 `filepath.Glob` 查找翻译文件,需要尝试 4 个不同路径。`pkg/i18n` 已使用 `//go:embed` 将文件编译进二进制,更可靠。翻译文件全部迁移到 `pkg/i18n/locales/`worker 不再携带翻译文件。
### 2. 保留 Init() 函数不变
`Init()` 函数对外保持现有签名,只修改内部实现:将 `bundle.RegisterUnmarshalFunc("json", json.Unmarshal)` 改为 `bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)`,并将文件后缀过滤从 `.json` 改为 `.toml`
### 3. worker 调用方式
worker 在 `main.go` 中调用 `pkgi18n.Init()``notify.go` 中将原来的三个本地函数替换为两次 `pkgi18n.TWithData()` 调用。
### 4. go.mod 依赖调整
`pkg/i18n/go.mod` 新增:
- `github.com/BurntSushi/toml`
- `github.com/nicksnyder/go-i18n/v2`
- `golang.org/x/text`
worker/go.mod 新增 `pkg/i18n`,如果 `go-i18n``BurntSushi/toml``golang.org/x/text` 不再被 worker 直接使用则移至 indirect。
## Risks / Trade-offs
- [翻译文件删除] 删除 `worker/internal/i18n/` 不可逆 → 迁移前确认 pkg/i18n/locales/ 中所有文件正确
- [go.mod 变动] worker 依赖关系变化可能引起 `go mod tidy` 意外移除仍需要的包 → 执行后检查 go.sum 和编译结果

View File

@@ -0,0 +1,29 @@
## Why
worker 模块自己实现了一套独立的 i18n 加载逻辑(动态文件路径查找、手动注册 TOML 解析),而 `pkg/i18n` 已经提供了通用的 i18n 封装但从未被使用,且其实现存在 bug注册的是 JSON 格式,实际文件是 TOML。统一到 `pkg/i18n` 可以消除重复、修复 bug、让后续模块复用。
## What Changes
- 修复 `pkg/i18n`:将格式从 JSON 改为 TOML更新 `go.mod` 添加缺失依赖
- 将 worker/internal/i18n/ 的 7 种语言翻译文件迁移到 `pkg/i18n/locales/`
-`pkg/i18n` 加入 `go.work` 工作区
- worker/go.mod 中添加 `pkg/i18n` 依赖,移除对 `go-i18n``BurntSushi/toml``golang.org/x/text/language` 的直接引用
- 删除 worker 中的 `loadI18nBundle``mustLocalize``localizeEmail` 函数,改用 `pkg/i18n.TWithData`
- 删除 `worker/internal/i18n/` 目录
## Capabilities
### New Capabilities
无新能力,纯重构。
### Modified Capabilities
- `share-download-notify`i18n 实现层迁移到 `pkg/i18n`,接口行为(邮件本地化主题和正文)不变
## Impact
- **pkg/i18n**go.mod 新增 `github.com/BurntSushi/toml``github.com/nicksnyder/go-i18n/v2``golang.org/x/text` 依赖locales 目录新增 6 种语言文件
- **go.work**:新增 `./pkg/i18n` 模块
- **worker**notify.go 逻辑简化go.mod 依赖变化,`worker/internal/i18n/` 目录删除
- 不影响任何 API 或前端

View File

@@ -0,0 +1,16 @@
## MODIFIED Requirements
### Requirement: Localized email notification sent on download
`share:notify` Worker 任务 SHALL 对 `notify_emails` 中每个邮件地址通过 SMTP 发送通知邮件,邮件语言使用 `RedisShareInfo.Locale` 对应的翻译,不支持的 locale 回退到英文。支持的语言为:`en``de``fr``ja``ko``zh-CN``zh-TW`。翻译由 `pkg/i18n` 提供,翻译文件以 TOML 格式嵌入二进制。
#### Scenario: Email sent in creator's locale
- **WHEN** SMTP 配置完整(`smtp.host` 非空)且 `shareInfo.Locale="zh-CN"`
- **THEN** Worker 向目标地址发送邮件,邮件 Subject 和 Body 为中文内容
#### Scenario: Email locale fallback to English
- **WHEN** `shareInfo.Locale` 为空或不支持的语言代码
- **THEN** Worker 使用英文模板发送邮件
#### Scenario: SMTP not configured
- **WHEN** `smtp.host` 为空
- **THEN** 邮件通知被跳过(不计入失败数),记录 warn 日志

View File

@@ -0,0 +1,23 @@
## 1. 修复 pkg/i18n
- [x] 1.1 修改 `pkg/i18n/go.mod`:添加 `github.com/BurntSushi/toml``github.com/nicksnyder/go-i18n/v2``golang.org/x/text` 依赖
- [x] 1.2 修改 `pkg/i18n/i18n.go`:将 `Init()` 内部的 JSON 解析器替换为 TOML 解析器,文件后缀过滤从 `.json` 改为 `.toml`
- [x] 1.3 将 `worker/internal/i18n/` 下的所有 `.toml` 文件复制到 `pkg/i18n/locales/`(保留已有的 active.en.toml添加 de/fr/ja/ko/zh-CN/zh-TW
- [x] 1.4 在 `pkg/i18n/` 目录下运行 `go mod tidy`,生成 go.sum
## 2. 更新 go.work
- [x] 2.1 在 `go.work``use` 块中添加 `./pkg/i18n`
- [x] 2.2 在项目根目录运行 `go work sync` 更新 go.work.sum
## 3. 更新 worker
- [x] 3.1 在 `worker/go.mod` 中添加 `pkg/i18n` 依赖
- [x] 3.2 在 `worker/main.go` 中调用 `pkgi18n.Init()`,处理返回的 error
- [x] 3.3 修改 `worker/internal/tasks/notify.go`:删除 `loadI18nBundle``mustLocalize``localizeEmail` 三个函数及其相关 import改用 `pkgi18n.TWithData` 生成邮件主题和正文
- [x] 3.4 在 `worker/` 目录下运行 `go mod tidy`,确认 `go-i18n``BurntSushi/toml``golang.org/x/text` 被正确移至 indirect 或移除
## 4. 清理
- [x] 4.1 删除 `worker/internal/i18n/` 目录
- [x] 4.2 在项目根目录运行 `go build ./...` 确认编译通过