Conversation
There was a problem hiding this comment.
Hey - 我发现了 4 个问题,并给出了一些整体性的反馈:
- 新的
i18n.ToMatchAPILocale/getLocaleFromState逻辑忽略了RunState.InputLanguage,只使用全局 PI 语言,这会改变 essencefilter 的行为,并可能让之前依赖「按运行设置语言」的用户感到意外;建议要么保留之前的覆盖逻辑,要么在文档中明确说明 matchapi locale 现在与PI_CLIENT_LANGUAGE绑定。 i18n.Init函数通过os.Getwd()加相对路径来构建localeDir,如果二进制从不同的工作目录启动,这里就会失败;建议基于可执行文件路径或可配置的根路径,而不是进程当前工作目录(CWD)。- 移除
InitAbortReasonCatalog并将 abort reason 文案查找改为i18n.T之后,就失去了对所有AbortReason值在启动时进行「非空消息」校验的能力;如果这个保证仍然重要,建议加一个轻量级的启动检查,遍历已知的原因并在键缺失时记录日志或直接报错。
给 AI Agents 的提示词
请根据这次代码评审中的评论进行修改:
## 总体评论
- 新的 `i18n.ToMatchAPILocale` / `getLocaleFromState` 逻辑忽略了 `RunState.InputLanguage`,只使用全局 PI 语言,这会改变 essencefilter 的行为,并可能让之前依赖「按运行设置语言」的用户感到意外;建议要么保留之前的覆盖逻辑,要么在文档中明确说明 matchapi locale 现在与 `PI_CLIENT_LANGUAGE` 绑定。
- `i18n.Init` 函数通过 `os.Getwd()` 加相对路径来构建 `localeDir`,如果二进制从不同的工作目录启动,这里就会失败;建议基于可执行文件路径或可配置的根路径,而不是进程当前工作目录(CWD)。
- 移除 `InitAbortReasonCatalog` 并将 abort reason 文案查找改为 `i18n.T` 之后,就失去了对所有 `AbortReason` 值在启动时进行「非空消息」校验的能力;如果这个保证仍然重要,建议加一个轻量级的启动检查,遍历已知的原因并在键缺失时记录日志或直接报错。
## 单独评论
### 评论 1
<location path="agent/go-service/pkg/i18n/i18n.go" line_range="23" />
<code_context>
+
+ DefaultLang = LangZhCN
+ envKey = "PI_CLIENT_LANGUAGE"
+ localeRelDir = "misc/locales/go-service"
+ fileRefPrefix = "@"
+)
</code_context>
<issue_to_address>
**issue (bug_risk):** Locale 目录常量与新的资源路径不匹配,导致永远无法找到 HTML/JSON 文件。
新的 locale HTML/JSON 文件位于 `assets/misc/locales/go-service/...` 下,但 `localeRelDir` 仍然是`
</issue_to_address>
### 评论 2
<location path="agent/go-service/essencefilter/integration.go" line_range="23-24" />
<code_context>
- return matchapi.LocaleCN
- }
- return loc
+func getLocaleFromState(_ *RunState) string {
+ return i18n.ToMatchAPILocale()
}
</code_context>
<issue_to_address>
**question (bug_risk):** Essencefilter 的 locale 不再从 RunState 推导,现会忽略按运行设置的输入语言。
之前的 `getLocaleFromState` 会通过 `matchapi.NormalizeInputLocale` 使用 `st.InputLanguage`,因此 essencefilter 可以使用独立于全局 UI 语言的 locale。现在它忽略了 `RunState`,始终通过 `i18n.ToMatchAPILocale()` 使用 `PI_CLIENT_LANGUAGE`,这会破坏按运行或按过滤器配置语言的能力,并导致错误的 matchapi locale。如果目标就是始终使用 `PI_CLIENT_LANGUAGE`,建议移除 `st *RunState` 参数,或者在文档中明确说明不再支持按运行覆盖。
</issue_to_address>
### 评论 3
<location path="agent/go-service/pkg/i18n/i18n.go" line_range="37-44" />
<code_context>
+)
+
+func Init() {
+ lang := strings.ToLower(strings.TrimSpace(os.Getenv(envKey)))
+ if lang == "" {
+ lang = DefaultLang
+ }
+ lang = NormalizeLang(lang)
+
+ cwd, _ := os.Getwd()
</code_context>
<issue_to_address>
**suggestion:** 语言标准化没有处理常见变体(例如 `zh-CN`),会静默回退到默认语言。
环境变量中像 `zh-CN` 和 `en-US` 这样的值很常见。由于 `NormalizeLang` 只做小写和 trim,`zh-CN` 会变成 `zh-cn`,这与允许的常量(`zh_cn`、`en_us` 等)不匹配,因此这些值会被视作未知并静默回退到 `DefaultLang`。建议把 `-` 规范化为 `_` 并/或显式支持 `xx-YY` 形式,或者至少在提供了无法识别的语言时输出日志,以便更容易发现配置错误。
```suggestion
func Init() {
rawEnv := os.Getenv(envKey)
lang := strings.ToLower(strings.TrimSpace(rawEnv))
if lang == "" {
lang = DefaultLang
}
// Normalize common variants like "zh-CN" or "en-US" to "zh_cn" / "en_us"
langCandidate := strings.ReplaceAll(lang, "-", "_")
normalized := NormalizeLang(langCandidate)
// If a non-empty, non-default value was provided but NormalizeLang
// mapped it back to DefaultLang, emit a warning to surface misconfigurations.
if rawEnv != "" && langCandidate != "" && langCandidate != DefaultLang && normalized == DefaultLang {
log.Warn().
Str("env_key", envKey).
Str("env_value", rawEnv).
Str("normalized_value", langCandidate).
Str("effective_lang", normalized).
Msg("unrecognized language configured in environment, falling back to default")
}
lang = normalized
cwd, _ := os.Getwd()
```
</issue_to_address>
### 评论 4
<location path="agent/go-service/autostockpile/abort_reason.go" line_range="19-23" />
<code_context>
-// LookupAbortReasonZHCN 返回指定原因键对应的中文文案。
-func LookupAbortReasonZHCN(reason AbortReason) (string, error) {
+// LookupAbortReason 返回指定原因键对应的本地化文案。
+func LookupAbortReason(reason AbortReason) (string, error) {
if err := ValidateAbortReason(reason); err != nil {
return "", err
}
-
- catalog, err := abortReasonCatalogOnce()
- if err != nil {
- return "", err
- }
-
- message, ok := catalog.ZHCN[reason]
- if !ok {
- return "", fmt.Errorf("abort reason %q missing zh_cn message", reason)
- }
-
- return message, nil
+ return i18n.T("autostockpile.abort." + string(reason)), nil
}
</code_context>
<issue_to_address>
**question (bug_risk):** Abort reason 查找不再校验翻译是否存在,并且总是返回 nil 错误。
之前会在启动时对每个 abort reason 做校验,以保证存在非空的 `zh_cn` 条目,并在缺失时通过查找错误暴露问题。现在该函数总是返回 `nil` 和 `i18n.T(...)`,而 `i18n.T(...)` 在翻译缺失时会静默回退到 key 本身。这移除了翻译与 `AbortReason` 漂移时的早期失败信号,也让调用方(例如 `lookupAbortReasonText`)无法区分「缺失翻译」与「合法值」。如果仍然希望保留这种行为,建议要么在启动时做一次验证(例如对所有 reason 断言 `i18n.T(...) != key`),要么在 `T` 回退到 key 时,让 `LookupAbortReason` 返回一个错误。
</issue_to_address>帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据这些反馈改进之后的评审。
Original comment in English
Hey - I've found 4 issues, and left some high level feedback:
- The new
i18n.ToMatchAPILocale/getLocaleFromStatelogic ignoresRunState.InputLanguageand uses only the global PI language, which changes behavior for essencefilter and may surprise users who previously relied on per-run language selection; consider either preserving the old override semantics or documenting that matchapi locale is now tied toPI_CLIENT_LANGUAGE. - The
i18n.Initfunction buildslocaleDirfromos.Getwd()and a relative path, which will fail if the binary is started from a different working directory; consider basing this on the executable path or a configurable root instead of the process CWD. - By removing
InitAbortReasonCatalogand switching abort reason text lookup toi18n.T, you’ve lost the upfront validation that allAbortReasonvalues have non-empty messages; if that guarantee is still important, consider adding a lightweight startup check that scans known reasons and logs or errors on missing keys.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new `i18n.ToMatchAPILocale` / `getLocaleFromState` logic ignores `RunState.InputLanguage` and uses only the global PI language, which changes behavior for essencefilter and may surprise users who previously relied on per-run language selection; consider either preserving the old override semantics or documenting that matchapi locale is now tied to `PI_CLIENT_LANGUAGE`.
- The `i18n.Init` function builds `localeDir` from `os.Getwd()` and a relative path, which will fail if the binary is started from a different working directory; consider basing this on the executable path or a configurable root instead of the process CWD.
- By removing `InitAbortReasonCatalog` and switching abort reason text lookup to `i18n.T`, you’ve lost the upfront validation that all `AbortReason` values have non-empty messages; if that guarantee is still important, consider adding a lightweight startup check that scans known reasons and logs or errors on missing keys.
## Individual Comments
### Comment 1
<location path="agent/go-service/pkg/i18n/i18n.go" line_range="23" />
<code_context>
+
+ DefaultLang = LangZhCN
+ envKey = "PI_CLIENT_LANGUAGE"
+ localeRelDir = "misc/locales/go-service"
+ fileRefPrefix = "@"
+)
</code_context>
<issue_to_address>
**issue (bug_risk):** Locale directory constant does not match the new assets path and will never find the HTML/JSON files.
The new locale HTML/JSON files live under `assets/misc/locales/go-service/...`, but `localeRelDir` is still `
</issue_to_address>
### Comment 2
<location path="agent/go-service/essencefilter/integration.go" line_range="23-24" />
<code_context>
- return matchapi.LocaleCN
- }
- return loc
+func getLocaleFromState(_ *RunState) string {
+ return i18n.ToMatchAPILocale()
}
</code_context>
<issue_to_address>
**question (bug_risk):** Essencefilter locale is no longer derived from RunState and now ignores per-run input language settings.
Previously `getLocaleFromState` used `st.InputLanguage` via `matchapi.NormalizeInputLocale`, so essencefilter could use a locale independent of the global UI language. Now it ignores `RunState` and always uses `PI_CLIENT_LANGUAGE` via `i18n.ToMatchAPILocale()`, which can break per-run or per-filter language configuration and lead to incorrect matchapi locales. If the goal is to always use `PI_CLIENT_LANGUAGE`, consider removing the `st *RunState` parameter or explicitly documenting that per-run overrides are no longer supported.
</issue_to_address>
### Comment 3
<location path="agent/go-service/pkg/i18n/i18n.go" line_range="37-44" />
<code_context>
+)
+
+func Init() {
+ lang := strings.ToLower(strings.TrimSpace(os.Getenv(envKey)))
+ if lang == "" {
+ lang = DefaultLang
+ }
+ lang = NormalizeLang(lang)
+
+ cwd, _ := os.Getwd()
</code_context>
<issue_to_address>
**suggestion:** Language normalization does not handle common variants like `zh-CN`, which will silently fall back to the default language.
Environment values like `zh-CN` and `en-US` are common. Since `NormalizeLang` only lowercases and trims, `zh-CN` becomes `zh-cn`, which doesn’t match the allowed constants (`zh_cn`, `en_us`, etc.), so these values are treated as unknown and silently fall back to `DefaultLang`. Please normalize `-` to `_` and/or support `xx-YY` forms explicitly, or at least log when an unrecognized language is provided to make misconfiguration easier to detect.
```suggestion
func Init() {
rawEnv := os.Getenv(envKey)
lang := strings.ToLower(strings.TrimSpace(rawEnv))
if lang == "" {
lang = DefaultLang
}
// Normalize common variants like "zh-CN" or "en-US" to "zh_cn" / "en_us"
langCandidate := strings.ReplaceAll(lang, "-", "_")
normalized := NormalizeLang(langCandidate)
// If a non-empty, non-default value was provided but NormalizeLang
// mapped it back to DefaultLang, emit a warning to surface misconfigurations.
if rawEnv != "" && langCandidate != "" && langCandidate != DefaultLang && normalized == DefaultLang {
log.Warn().
Str("env_key", envKey).
Str("env_value", rawEnv).
Str("normalized_value", langCandidate).
Str("effective_lang", normalized).
Msg("unrecognized language configured in environment, falling back to default")
}
lang = normalized
cwd, _ := os.Getwd()
```
</issue_to_address>
### Comment 4
<location path="agent/go-service/autostockpile/abort_reason.go" line_range="19-23" />
<code_context>
-// LookupAbortReasonZHCN 返回指定原因键对应的中文文案。
-func LookupAbortReasonZHCN(reason AbortReason) (string, error) {
+// LookupAbortReason 返回指定原因键对应的本地化文案。
+func LookupAbortReason(reason AbortReason) (string, error) {
if err := ValidateAbortReason(reason); err != nil {
return "", err
}
-
- catalog, err := abortReasonCatalogOnce()
- if err != nil {
- return "", err
- }
-
- message, ok := catalog.ZHCN[reason]
- if !ok {
- return "", fmt.Errorf("abort reason %q missing zh_cn message", reason)
- }
-
- return message, nil
+ return i18n.T("autostockpile.abort." + string(reason)), nil
}
</code_context>
<issue_to_address>
**question (bug_risk):** Abort reason lookups no longer validate translation presence and always return nil error.
Previously, every abort reason was validated at startup to ensure a non-empty `zh_cn` entry, and lookup errors surfaced when a reason was missing. Now the function always returns `nil` and `i18n.T(...)`, which silently falls back to the key if the translation is absent. This removes the early-fail signal when translations drift from `AbortReason` and prevents callers (e.g. `lookupAbortReasonText`) from distinguishing a missing translation from a valid value. If that behavior is still desired, consider either a startup validation pass (e.g. asserting `i18n.T(...) != key` for all reasons) or returning an error from `LookupAbortReason` when `T` falls back to the key.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
agent/go-service/pkg/i18n/i18n.go
Outdated
| func Init() { | ||
| lang := strings.ToLower(strings.TrimSpace(os.Getenv(envKey))) | ||
| if lang == "" { | ||
| lang = DefaultLang | ||
| } | ||
| lang = NormalizeLang(lang) | ||
|
|
||
| cwd, _ := os.Getwd() |
There was a problem hiding this comment.
suggestion: 语言标准化没有处理常见变体(例如 zh-CN),会静默回退到默认语言。
环境变量中像 zh-CN 和 en-US 这样的值很常见。由于 NormalizeLang 只做小写和 trim,zh-CN 会变成 zh-cn,这与允许的常量(zh_cn、en_us 等)不匹配,因此这些值会被视作未知并静默回退到 DefaultLang。建议把 - 规范化为 _ 并/或显式支持 xx-YY 形式,或者至少在提供了无法识别的语言时输出日志,以便更容易发现配置错误。
| func Init() { | |
| lang := strings.ToLower(strings.TrimSpace(os.Getenv(envKey))) | |
| if lang == "" { | |
| lang = DefaultLang | |
| } | |
| lang = NormalizeLang(lang) | |
| cwd, _ := os.Getwd() | |
| func Init() { | |
| rawEnv := os.Getenv(envKey) | |
| lang := strings.ToLower(strings.TrimSpace(rawEnv)) | |
| if lang == "" { | |
| lang = DefaultLang | |
| } | |
| // Normalize common variants like "zh-CN" or "en-US" to "zh_cn" / "en_us" | |
| langCandidate := strings.ReplaceAll(lang, "-", "_") | |
| normalized := NormalizeLang(langCandidate) | |
| // If a non-empty, non-default value was provided but NormalizeLang | |
| // mapped it back to DefaultLang, emit a warning to surface misconfigurations. | |
| if rawEnv != "" && langCandidate != "" && langCandidate != DefaultLang && normalized == DefaultLang { | |
| log.Warn(). | |
| Str("env_key", envKey). | |
| Str("env_value", rawEnv). | |
| Str("normalized_value", langCandidate). | |
| Str("effective_lang", normalized). | |
| Msg("unrecognized language configured in environment, falling back to default") | |
| } | |
| lang = normalized | |
| cwd, _ := os.Getwd() |
Original comment in English
suggestion: Language normalization does not handle common variants like zh-CN, which will silently fall back to the default language.
Environment values like zh-CN and en-US are common. Since NormalizeLang only lowercases and trims, zh-CN becomes zh-cn, which doesn’t match the allowed constants (zh_cn, en_us, etc.), so these values are treated as unknown and silently fall back to DefaultLang. Please normalize - to _ and/or support xx-YY forms explicitly, or at least log when an unrecognized language is provided to make misconfiguration easier to detect.
| func Init() { | |
| lang := strings.ToLower(strings.TrimSpace(os.Getenv(envKey))) | |
| if lang == "" { | |
| lang = DefaultLang | |
| } | |
| lang = NormalizeLang(lang) | |
| cwd, _ := os.Getwd() | |
| func Init() { | |
| rawEnv := os.Getenv(envKey) | |
| lang := strings.ToLower(strings.TrimSpace(rawEnv)) | |
| if lang == "" { | |
| lang = DefaultLang | |
| } | |
| // Normalize common variants like "zh-CN" or "en-US" to "zh_cn" / "en_us" | |
| langCandidate := strings.ReplaceAll(lang, "-", "_") | |
| normalized := NormalizeLang(langCandidate) | |
| // If a non-empty, non-default value was provided but NormalizeLang | |
| // mapped it back to DefaultLang, emit a warning to surface misconfigurations. | |
| if rawEnv != "" && langCandidate != "" && langCandidate != DefaultLang && normalized == DefaultLang { | |
| log.Warn(). | |
| Str("env_key", envKey). | |
| Str("env_value", rawEnv). | |
| Str("normalized_value", langCandidate). | |
| Str("effective_lang", normalized). | |
| Msg("unrecognized language configured in environment, falling back to default") | |
| } | |
| lang = normalized | |
| cwd, _ := os.Getwd() |
| func LookupAbortReason(reason AbortReason) (string, error) { | ||
| if err := ValidateAbortReason(reason); err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| catalog, err := abortReasonCatalogOnce() | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| message, ok := catalog.ZHCN[reason] | ||
| if !ok { | ||
| return "", fmt.Errorf("abort reason %q missing zh_cn message", reason) | ||
| } | ||
|
|
||
| return message, nil | ||
| return i18n.T("autostockpile.abort." + string(reason)), nil |
There was a problem hiding this comment.
question (bug_risk): Abort reason 查找不再校验翻译是否存在,并且总是返回 nil 错误。
之前会在启动时对每个 abort reason 做校验,以保证存在非空的 zh_cn 条目,并在缺失时通过查找错误暴露问题。现在该函数总是返回 nil 和 i18n.T(...),而 i18n.T(...) 在翻译缺失时会静默回退到 key 本身。这移除了翻译与 AbortReason 漂移时的早期失败信号,也让调用方(例如 lookupAbortReasonText)无法区分「缺失翻译」与「合法值」。如果仍然希望保留这种行为,建议要么在启动时做一次验证(例如对所有 reason 断言 i18n.T(...) != key),要么在 T 回退到 key 时,让 LookupAbortReason 返回一个错误。
Original comment in English
question (bug_risk): Abort reason lookups no longer validate translation presence and always return nil error.
Previously, every abort reason was validated at startup to ensure a non-empty zh_cn entry, and lookup errors surfaced when a reason was missing. Now the function always returns nil and i18n.T(...), which silently falls back to the key if the translation is absent. This removes the early-fail signal when translations drift from AbortReason and prevents callers (e.g. lookupAbortReasonText) from distinguishing a missing translation from a valid value. If that behavior is still desired, consider either a startup validation pass (e.g. asserting i18n.T(...) != key for all reasons) or returning an error from LookupAbortReason when T falls back to the key.
There was a problem hiding this comment.
Pull request overview
该 PR 为 Go service 引入一套轻量 i18n 机制,将原先硬编码/内嵌(embed)的提示文案与 HTML 消息抽离到 assets/misc/locales/go-service/,并改造多个功能模块在运行时按语言加载对应文本/HTML。
Changes:
- 新增
agent/go-service/pkg/i18n,基于PI_CLIENT_LANGUAGE加载各语言 JSON,并支持@xxx.html文件引用渲染。 - 新增/补齐 go-service 的多语言 locale JSON(zh_cn/zh_tw/en_us/ja_jp/ko_kr)与若干 HTML 模板文件。
- 改造 resell / visitfriends / batchaddfriends / autostockpile / map-tracker / taskersink / essencefilter 等模块使用 i18n 输出用户可见信息,并移除部分 embed 资源。
Reviewed changes
Copilot reviewed 62 out of 65 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| assets/misc/locales/go-service/zh_tw.json | 新增 go-service 繁中翻译 key/value(含部分 HTML 文件引用) |
| assets/misc/locales/go-service/zh_cn.json | 新增 go-service 简中翻译 key/value(含部分 HTML 文件引用) |
| assets/misc/locales/go-service/ko_kr.json | 新增 go-service 韩语翻译 key/value(含部分 HTML 文件引用) |
| assets/misc/locales/go-service/ja_jp.json | 新增 go-service 日语翻译 key/value(含部分 HTML 文件引用) |
| assets/misc/locales/go-service/en_us.json | 新增 go-service 英文翻译 key/value(含部分 HTML 文件引用) |
| assets/misc/locales/go-service/process-warning-zh_tw.html | 新增“黑名单进程”警告 HTML(繁中) |
| assets/misc/locales/go-service/process-warning-zh_cn.html | 新增“黑名单进程”警告 HTML(简中) |
| assets/misc/locales/go-service/process-warning-ko_kr.html | 新增“黑名单进程”警告 HTML(韩语) |
| assets/misc/locales/go-service/process-warning-ja_jp.html | 新增“黑名单进程”警告 HTML(日语) |
| assets/misc/locales/go-service/process-warning-en_us.html | 新增“黑名单进程”警告 HTML(英文) |
| assets/misc/locales/go-service/navigation-moving-zh_tw.html | 新增 map-tracker 导航移动提示 HTML(繁中) |
| assets/misc/locales/go-service/navigation-moving-zh_cn.html | 新增 map-tracker 导航移动提示 HTML(简中) |
| assets/misc/locales/go-service/navigation-moving-ko_kr.html | 新增 map-tracker 导航移动提示 HTML(韩语) |
| assets/misc/locales/go-service/navigation-moving-ja_jp.html | 新增 map-tracker 导航移动提示 HTML(日语) |
| assets/misc/locales/go-service/navigation-moving-en_us.html | 新增 map-tracker 导航移动提示 HTML(英文) |
| assets/misc/locales/go-service/navigation-finished-zh_tw.html | 新增 map-tracker 导航完成提示 HTML(繁中) |
| assets/misc/locales/go-service/navigation-finished-zh_cn.html | 新增 map-tracker 导航完成提示 HTML(简中) |
| assets/misc/locales/go-service/navigation-finished-ko_kr.html | 新增 map-tracker 导航完成提示 HTML(韩语) |
| assets/misc/locales/go-service/navigation-finished-ja_jp.html | 新增 map-tracker 导航完成提示 HTML(日语) |
| assets/misc/locales/go-service/navigation-finished-en_us.html | 新增 map-tracker 导航完成提示 HTML(英文) |
| assets/misc/locales/go-service/inference-finished-zh_tw.html | 新增 map-tracker 推理完成提示 HTML(繁中) |
| assets/misc/locales/go-service/inference-finished-zh_cn.html | 新增 map-tracker 推理完成提示 HTML(简中) |
| assets/misc/locales/go-service/inference-finished-ko_kr.html | 新增 map-tracker 推理完成提示 HTML(韩语) |
| assets/misc/locales/go-service/inference-finished-ja_jp.html | 新增 map-tracker 推理完成提示 HTML(日语) |
| assets/misc/locales/go-service/inference-finished-en_us.html | 新增 map-tracker 推理完成提示 HTML(英文) |
| assets/misc/locales/go-service/inference-failed-zh_tw.html | 新增 map-tracker 推理失败提示 HTML(繁中) |
| assets/misc/locales/go-service/inference-failed-zh_cn.html | 新增 map-tracker 推理失败提示 HTML(简中) |
| assets/misc/locales/go-service/inference-failed-ko_kr.html | 新增 map-tracker 推理失败提示 HTML(韩语) |
| assets/misc/locales/go-service/inference-failed-ja_jp.html | 新增 map-tracker 推理失败提示 HTML(日语) |
| assets/misc/locales/go-service/inference-failed-en_us.html | 新增 map-tracker 推理失败提示 HTML(英文) |
| assets/misc/locales/go-service/hdr-warning-zh_tw.html | 新增 HDR 警告 HTML(繁中) |
| assets/misc/locales/go-service/hdr-warning-zh_cn.html | 新增 HDR 警告 HTML(简中) |
| assets/misc/locales/go-service/hdr-warning-ko_kr.html | 新增 HDR 警告 HTML(韩语) |
| assets/misc/locales/go-service/hdr-warning-ja_jp.html | 新增 HDR 警告 HTML(日语) |
| assets/misc/locales/go-service/hdr-warning-en_us.html | 新增 HDR 警告 HTML(英文) |
| assets/misc/locales/go-service/emergency-stop-zh_tw.html | 新增紧急停止提示 HTML(繁中) |
| assets/misc/locales/go-service/emergency-stop-zh_cn.html | 新增紧急停止提示 HTML(简中) |
| assets/misc/locales/go-service/emergency-stop-ko_kr.html | 新增紧急停止提示 HTML(韩语) |
| assets/misc/locales/go-service/emergency-stop-ja_jp.html | 新增紧急停止提示 HTML(日语) |
| assets/misc/locales/go-service/emergency-stop-en_us.html | 新增紧急停止提示 HTML(英文) |
| assets/misc/locales/go-service/aspect-ratio-warning-zh_tw.html | 新增分辨率比例警告 HTML(繁中) |
| assets/misc/locales/go-service/aspect-ratio-warning-zh_cn.html | 调整分辨率比例警告 HTML(简中,行号/格式微调) |
| assets/misc/locales/go-service/aspect-ratio-warning-ko_kr.html | 新增分辨率比例警告 HTML(韩语) |
| assets/misc/locales/go-service/aspect-ratio-warning-ja_jp.html | 新增分辨率比例警告 HTML(日语) |
| assets/misc/locales/go-service/aspect-ratio-warning-en_us.html | 新增分辨率比例警告 HTML(英文) |
| agent/go-service/visitfriends/visitfriends.go | 将节点提示文案改为 i18n key + 组合输出 |
| agent/go-service/taskersink/processcheck/checker.go | 用 i18n.TF 替代 embed HTML 输出黑名单进程警告 |
| agent/go-service/taskersink/hdrcheck/checker.go | 用 i18n.TF 替代 embed HTML 输出 HDR 警告 |
| agent/go-service/taskersink/aspectratio/checker.go | 用 i18n.TF 替代 embed HTML 输出比例不匹配警告 |
| agent/go-service/resell/decide.go | 将购买建议/提示改为 i18n 文案 |
| agent/go-service/pkg/i18n/i18n.go | 新增 i18n 初始化、文本与文件引用渲染(T/TF)等实现 |
| agent/go-service/map-tracker/move.go | 移除 embed HTML,改为按语言读取并渲染导航 HTML |
| agent/go-service/map-tracker/infer.go | 移除 embed HTML,改为按语言读取并渲染推理结果 HTML |
| agent/go-service/main.go | 启动时初始化 i18n |
| agent/go-service/essencefilter/ui.go | UI 输出文案与拼接分隔符改为 i18n |
| agent/go-service/essencefilter/options.go | 稀有度/列表拼接等文案改为 i18n |
| agent/go-service/essencefilter/integration.go | matchapi locale 选择改为从 i18n 推导 |
| agent/go-service/batchaddfriends/batchaddfriends.go | 进度/结果提示文案改为 i18n |
| agent/go-service/autostockpile/selector.go | 多处 focus 文案与模式文本改为 i18n,并切换 abort reason lookup |
| agent/go-service/autostockpile/register.go | 移除 abort reason catalog 初始化流程 |
| agent/go-service/autostockpile/recognition.go | 识别完成提示改为 i18n |
| agent/go-service/autostockpile/quantity.go | quantity 决策原因/数量文本改为 i18n |
| agent/go-service/autostockpile/abort_reason.json | 删除原先仅 zh_cn 的 abort reason 文案表 |
| agent/go-service/autostockpile/abort_reason.go | abort reason lookup 改为使用 i18n key |
| if !ok { | ||
| return key | ||
| } |
There was a problem hiding this comment.
i18n.T/TF 在 key 不存在时直接返回 key 字符串(这里的 return key)。这样当非默认语言缺少个别翻译时,最终 UI/日志会直接暴露内部 key(如 maptracker.xxx),而不是自动回退到默认语言。建议在加载时合并 DefaultLang 的 messages(默认语言作为 base,当前语言覆盖),或在查找失败时按 DefaultLang 再查一次。
agent/go-service/pkg/i18n/i18n.go
Outdated
| data, err := os.ReadFile(path) | ||
| if err != nil && lang != DefaultLang { | ||
| fallback := resolveFilePath(fileName, DefaultLang) | ||
| data, err = os.ReadFile(fallback) | ||
| path = fallback | ||
| } |
There was a problem hiding this comment.
readFileContent 在读取当前语言文件失败后会回退到 DefaultLang,并把 cache key 设为 fallback 的实际路径;但下一次同样请求仍会先查当前语言路径缓存(查不到)再触发一次失败 + 回退读取,导致重复 I/O。建议在发生回退时,同时把“原始请求路径”的缓存也填充为 fallback 内容(或用 fileName+lang 作为缓存 key)。
| } | ||
|
|
||
| return message, nil | ||
| return i18n.T("autostockpile.abort." + string(reason)), nil |
There was a problem hiding this comment.
LookupAbortReason 现在直接返回 i18n.T(...),即使对应翻译 key 缺失也会返回 key 本身且不报错;这会让上层继续把 autostockpile.abort.Xxx 这种内部 key 展示给用户,同时这里的 error 返回值也基本失去意义。建议在这里检测 i18n 是否命中(例如返回值等于 key 时认为缺失)并返回明确错误,或至少回退到 DefaultLang 的文案。
| return i18n.T("autostockpile.abort." + string(reason)), nil | |
| key := "autostockpile.abort." + string(reason) | |
| translated := i18n.T(key) | |
| if translated == key { | |
| return "", fmt.Errorf("missing i18n translation for key %q", key) | |
| } | |
| return translated, nil |
| log.Info().Msgf("[Resell]配额溢出:建议购买%d件,推荐第%d行第%d列(利润:%d)", | ||
| overflowAmount, showMaxRecord.Row, showMaxRecord.Col, showMaxRecord.Profit) | ||
| message := fmt.Sprintf("⚠️ 配额溢出提醒\n剩余配额明天将超出上限,建议购买%d件商品\n推荐购买: 第%d行第%d列 (最高利润: %d)", | ||
| overflowAmount, showMaxRecord.Row, showMaxRecord.Col, showMaxRecord.Profit) | ||
| maafocus.NodeActionStarting(ctx, message) | ||
| maafocus.NodeActionStarting(ctx, i18n.T("resell.quota_overflow", overflowAmount, showMaxRecord.Row, showMaxRecord.Col, showMaxRecord.Profit)) |
There was a problem hiding this comment.
这里继续使用了 Msgf 且把组件前缀(如 [Resell])拼进 Msg 文本里,不符合仓库对 go-service 的 zerolog 约定(应使用链式字段承载上下文,Msg 保持简短)。建议改为 log.Info().Str("component","resell").Int(...).Msg("quota overflow") 这类结构化日志,避免 Msgf/字符串拼接。参考:.github/instructions/go-service.instructions.md 中 LogStyleZerolog 段落。
|
@sourcery-ai summary |
|
@sourcery-ai review |
There was a problem hiding this comment.
Hey - 我发现了 3 个问题,并留下了一些整体层面的反馈:
- EssenceFilter 之前是通过
getLocaleFromState从RunState.InputLanguage推导出 locale 的,但现在所有用户可见的字符串都走全局的i18n语言;请确认取消按运行 / 按任务选择 locale 是有意为之,并且不会导致混合语言场景的回归。 - 旧的
autostockpileabort_reason.json加载逻辑会校验每个AbortReason是否有非空 message 且没有多余字段;现在查找直接走i18n.T,建议加一个小的启动检查,遍历knownAbortReasons并验证对应的autostockpile.abort.*key 是否存在,以保留原先的安全网。 - 新的
pkg/i18n中的 HTML 模板注册表是手动定义 key 的(例如essencefilter.no_match_discard、maptracker.navigation_moving);为避免静默降级为返回 key 字符串本身,可能值得增加一个单元测试或初始化时的健全性检查,确认代码中引用的所有模板 key 都存在于htmlTemplates中,且底层文件可以被加载。
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- EssenceFilter previously derived the locale from `RunState.InputLanguage` via `getLocaleFromState`, but now all user‑visible strings go through the global `i18n` language; please confirm that dropping per-run/Per-task locale selection is intentional and won’t regress mixed-locale scenarios.
- The old `autostockpile` abort_reason.json loader validated that every `AbortReason` had a non-empty message and no extra keys; now that lookup goes straight to `i18n.T`, consider adding a small startup check that iterates `knownAbortReasons` and verifies the corresponding `autostockpile.abort.*` keys exist to keep the same safety net.
- The new HTML template registry in `pkg/i18n` is keyed manually (e.g., `essencefilter.no_match_discard`, `maptracker.navigation_moving`); to avoid silent fallbacks like returning the key string, it may be worth adding a unit or init-time sanity check that all referenced template keys in code are present in `htmlTemplates` and the underlying files can be loaded.
## Individual Comments
### Comment 1
<location path="agent/go-service/pkg/i18n/i18n.go" line_range="26" />
<code_context>
+
+ DefaultLang = LangZhCN
+ envKey = "PI_CLIENT_LANGUAGE"
+ localeRelDir = "locales/go-service"
+)
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Locale directory base path likely doesn’t match where assets are actually stored
`localeRelDir` is set to `locales/go-service`, but the locale JSON/HTML files in this repo live under `assets/locales/go-service/...`. That means `Init` will resolve `<cwd>/locales/go-service`, while the actual files are at `<cwd>/assets/locales/go-service`, so `loadMessages` and `readTemplateFile` won’t find them unless something copies/remaps assets at build/deploy time.
Recommend either updating `localeRelDir` to `"assets/locales/go-service"`, or making the base path configurable (env/injected) and pointing it at the real asset directory. Otherwise, message loading will fail and `RenderHTML` will only return template keys instead of localized content.
</issue_to_address>
### Comment 2
<location path="agent/go-service/autostockpile/abort_reason.go" line_range="6-10" />
<code_context>
"encoding/json"
"fmt"
+ "github.com/MaaXYZ/MaaEnd/agent/go-service/pkg/i18n"
"github.com/MaaXYZ/MaaEnd/agent/go-service/pkg/maafocus"
maa "github.com/MaaXYZ/maa-framework-go/v4"
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Removing the JSON-backed catalog loses upfront validation of abort reason coverage
The old `abortReasonCatalogOnce` path validated at startup that every `knownAbortReasons` entry had a non-empty message and that the JSON had no unknown keys. The new `i18n.T("autostockpile.abort." + string(reason))` path will just return the key for missing translations, so new/renamed abort reasons can ship without a real user-facing message and we won’t notice until runtime.
To keep the i18n approach but retain that safety net, consider a validation step (e.g., during registration) that:
- Iterates over `knownAbortReasons`, calls `i18n.T(...)`, and warns/errors if the result equals the key.
- Optionally checks for unexpected `autostockpile.abort.*` entries if you track them.
That would restore the robustness of the previous JSON-backed validation while using the new i18n system.
</issue_to_address>
### Comment 3
<location path="agent/go-service/essencefilter/integration.go" line_range="42-47" />
<code_context>
- if engine == nil {
- return
- }
+func reportOCRSkills(ctx *maa.Context, skills []string, levels [3]int, matched bool) {
color := "#00bfff"
if matched {
color = "#064d7c"
}
- LogMXUSimpleHTMLWithColor(ctx, engine.FocusOCRSkills(skills, levels), color)
+ text := i18n.T("essencefilter.focus.ocr_skills",
+ skills[0], levels[0], skills[1], levels[1], skills[2], levels[2])
+ LogMXUSimpleHTMLWithColor(ctx, text, color)
</code_context>
<issue_to_address>
**issue:** reportOCRSkills assumes exactly 3 skills, which can panic if the slice is shorter
This now indexes `skills[0]`, `skills[1]`, and `skills[2]` unconditionally, whereas `engine.FocusOCRSkills` could internally handle unexpected lengths. If `skills` is ever shorter than 3 (e.g., partial OCR results or a failed slot), this will panic on slice bounds.
Please either:
- Guard the access: `if len(skills) < 3 { /* fallback or skip */ }`, or
- Ensure callers always pass a 3-element slice (e.g., pad with empty strings) before calling `reportOCRSkills`.
That preserves the new i18n behavior while avoiding a runtime panic in short-slice cases.
</issue_to_address>Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Original comment in English
Hey - I've found 3 issues, and left some high level feedback:
- EssenceFilter previously derived the locale from
RunState.InputLanguageviagetLocaleFromState, but now all user‑visible strings go through the globali18nlanguage; please confirm that dropping per-run/Per-task locale selection is intentional and won’t regress mixed-locale scenarios. - The old
autostockpileabort_reason.json loader validated that everyAbortReasonhad a non-empty message and no extra keys; now that lookup goes straight toi18n.T, consider adding a small startup check that iteratesknownAbortReasonsand verifies the correspondingautostockpile.abort.*keys exist to keep the same safety net. - The new HTML template registry in
pkg/i18nis keyed manually (e.g.,essencefilter.no_match_discard,maptracker.navigation_moving); to avoid silent fallbacks like returning the key string, it may be worth adding a unit or init-time sanity check that all referenced template keys in code are present inhtmlTemplatesand the underlying files can be loaded.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- EssenceFilter previously derived the locale from `RunState.InputLanguage` via `getLocaleFromState`, but now all user‑visible strings go through the global `i18n` language; please confirm that dropping per-run/Per-task locale selection is intentional and won’t regress mixed-locale scenarios.
- The old `autostockpile` abort_reason.json loader validated that every `AbortReason` had a non-empty message and no extra keys; now that lookup goes straight to `i18n.T`, consider adding a small startup check that iterates `knownAbortReasons` and verifies the corresponding `autostockpile.abort.*` keys exist to keep the same safety net.
- The new HTML template registry in `pkg/i18n` is keyed manually (e.g., `essencefilter.no_match_discard`, `maptracker.navigation_moving`); to avoid silent fallbacks like returning the key string, it may be worth adding a unit or init-time sanity check that all referenced template keys in code are present in `htmlTemplates` and the underlying files can be loaded.
## Individual Comments
### Comment 1
<location path="agent/go-service/pkg/i18n/i18n.go" line_range="26" />
<code_context>
+
+ DefaultLang = LangZhCN
+ envKey = "PI_CLIENT_LANGUAGE"
+ localeRelDir = "locales/go-service"
+)
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Locale directory base path likely doesn’t match where assets are actually stored
`localeRelDir` is set to `locales/go-service`, but the locale JSON/HTML files in this repo live under `assets/locales/go-service/...`. That means `Init` will resolve `<cwd>/locales/go-service`, while the actual files are at `<cwd>/assets/locales/go-service`, so `loadMessages` and `readTemplateFile` won’t find them unless something copies/remaps assets at build/deploy time.
Recommend either updating `localeRelDir` to `"assets/locales/go-service"`, or making the base path configurable (env/injected) and pointing it at the real asset directory. Otherwise, message loading will fail and `RenderHTML` will only return template keys instead of localized content.
</issue_to_address>
### Comment 2
<location path="agent/go-service/autostockpile/abort_reason.go" line_range="6-10" />
<code_context>
"encoding/json"
"fmt"
+ "github.com/MaaXYZ/MaaEnd/agent/go-service/pkg/i18n"
"github.com/MaaXYZ/MaaEnd/agent/go-service/pkg/maafocus"
maa "github.com/MaaXYZ/maa-framework-go/v4"
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Removing the JSON-backed catalog loses upfront validation of abort reason coverage
The old `abortReasonCatalogOnce` path validated at startup that every `knownAbortReasons` entry had a non-empty message and that the JSON had no unknown keys. The new `i18n.T("autostockpile.abort." + string(reason))` path will just return the key for missing translations, so new/renamed abort reasons can ship without a real user-facing message and we won’t notice until runtime.
To keep the i18n approach but retain that safety net, consider a validation step (e.g., during registration) that:
- Iterates over `knownAbortReasons`, calls `i18n.T(...)`, and warns/errors if the result equals the key.
- Optionally checks for unexpected `autostockpile.abort.*` entries if you track them.
That would restore the robustness of the previous JSON-backed validation while using the new i18n system.
</issue_to_address>
### Comment 3
<location path="agent/go-service/essencefilter/integration.go" line_range="42-47" />
<code_context>
- if engine == nil {
- return
- }
+func reportOCRSkills(ctx *maa.Context, skills []string, levels [3]int, matched bool) {
color := "#00bfff"
if matched {
color = "#064d7c"
}
- LogMXUSimpleHTMLWithColor(ctx, engine.FocusOCRSkills(skills, levels), color)
+ text := i18n.T("essencefilter.focus.ocr_skills",
+ skills[0], levels[0], skills[1], levels[1], skills[2], levels[2])
+ LogMXUSimpleHTMLWithColor(ctx, text, color)
</code_context>
<issue_to_address>
**issue:** reportOCRSkills assumes exactly 3 skills, which can panic if the slice is shorter
This now indexes `skills[0]`, `skills[1]`, and `skills[2]` unconditionally, whereas `engine.FocusOCRSkills` could internally handle unexpected lengths. If `skills` is ever shorter than 3 (e.g., partial OCR results or a failed slot), this will panic on slice bounds.
Please either:
- Guard the access: `if len(skills) < 3 { /* fallback or skip */ }`, or
- Ensure callers always pass a 3-element slice (e.g., pad with empty strings) before calling `reportOCRSkills`.
That preserves the new i18n behavior while avoiding a runtime panic in short-slice cases.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| "github.com/MaaXYZ/MaaEnd/agent/go-service/pkg/i18n" | ||
| ) | ||
|
|
||
| // ValidateAbortReason 校验给定原因键是否合法。 | ||
| func ValidateAbortReason(reason AbortReason) error { |
There was a problem hiding this comment.
suggestion (bug_risk): 移除基于 JSON 的目录会失去对中止原因覆盖率的前置校验
旧的 abortReasonCatalogOnce 流程会在启动时校验每个 knownAbortReasons 条目都有非空的 message,并且 JSON 中没有未知字段。新的 i18n.T("autostockpile.abort." + string(reason)) 路径在缺少翻译时只会返回 key 本身,因此新增或重命名的中止原因可能在没有真正面向用户的信息的情况下上线,而且直到运行期我们才会发现。
为了继续使用 i18n 的方式同时保留这层安全网,可以考虑在注册阶段增加一个校验步骤,例如:
- 遍历
knownAbortReasons,调用i18n.T(...),如果结果与 key 相同则发出警告/报错; - 如果有追踪相关信息,可选地检查是否存在意外的
autostockpile.abort.*条目。
这样可以在使用新 i18n 系统的同时,恢复之前基于 JSON 校验所提供的健壮性。
Original comment in English
suggestion (bug_risk): Removing the JSON-backed catalog loses upfront validation of abort reason coverage
The old abortReasonCatalogOnce path validated at startup that every knownAbortReasons entry had a non-empty message and that the JSON had no unknown keys. The new i18n.T("autostockpile.abort." + string(reason)) path will just return the key for missing translations, so new/renamed abort reasons can ship without a real user-facing message and we won’t notice until runtime.
To keep the i18n approach but retain that safety net, consider a validation step (e.g., during registration) that:
- Iterates over
knownAbortReasons, callsi18n.T(...), and warns/errors if the result equals the key. - Optionally checks for unexpected
autostockpile.abort.*entries if you track them.
That would restore the robustness of the previous JSON-backed validation while using the new i18n system.
| func reportOCRSkills(ctx *maa.Context, skills []string, levels [3]int, matched bool) { | ||
| color := "#00bfff" | ||
| if matched { | ||
| color = "#064d7c" | ||
| } | ||
| LogMXUSimpleHTMLWithColor(ctx, engine.FocusOCRSkills(skills, levels), color) | ||
| text := i18n.T("essencefilter.focus.ocr_skills", |
There was a problem hiding this comment.
issue: reportOCRSkills 假定一定有 3 个技能,如果切片更短会导致 panic
现在无条件访问 skills[0]、skills[1] 和 skills[2],而 engine.FocusOCRSkills 之前可能在内部处理了长度异常的情况。如果 skills 长度小于 3(例如 OCR 结果不完整或某个槽位识别失败),就会在切片越界时产生 panic。
请考虑:
- 添加访问保护:
if len(skills) < 3 { /* fallback or skip */ };或者 - 确保调用方在调用
reportOCRSkills之前总是传入长度为 3 的切片(例如用空字符串填充)。
这样可以保留新的 i18n 行为,同时避免在切片过短时出现运行期 panic。
Original comment in English
issue: reportOCRSkills assumes exactly 3 skills, which can panic if the slice is shorter
This now indexes skills[0], skills[1], and skills[2] unconditionally, whereas engine.FocusOCRSkills could internally handle unexpected lengths. If skills is ever shorter than 3 (e.g., partial OCR results or a failed slot), this will panic on slice bounds.
Please either:
- Guard the access:
if len(skills) < 3 { /* fallback or skip */ }, or - Ensure callers always pass a 3-element slice (e.g., pad with empty strings) before calling
reportOCRSkills.
That preserves the new i18n behavior while avoiding a runtime panic in short-slice cases.
assets/locales/go-service/ko_kr.json
Outdated
| { | ||
| "resell.stock_empty": "⚠️ 庫存已售罄,無可購買商品", | ||
| "resell.quota_overflow": "⚠️ 配額溢出提醒\n剩餘配額明天將超出上限,建議購買%d件商品\n推薦購買: 第%d行第%d列 (最高利潤: %d)", | ||
| "resell.auto_buy_disabled": "💡 已禁用自動購買/出售\n推薦購買: 第%d行第%d列 (利潤: %d)", | ||
| "resell.below_min_profit": "💡 沒有達到最低利潤的商品,建議把配額留至明天\n推薦購買: 第%d行第%d列 (利潤: %d)", |
There was a problem hiding this comment.
该 ko_kr.json 文件开头多条文案仍为中文(看起来更像从 zh_tw 复制),会导致韩语界面显示错误语言。建议补齐韩语翻译;如果短期无法翻译,至少避免提交明显错误语言的内容(例如先用英文/占位,并在后续补翻译)。
Summary by Sourcery
引入可复用的 Go 服务 i18n 子系统,并迁移多个 agents 和 tasker 检查以使用集中管理的本地化消息和 HTML 模板。
Enhancements:
Build:
Documentation:
Chores:
Original summary in English
Summary by Sourcery
Introduce a reusable Go service i18n subsystem and migrate multiple agents and tasker checks to use centralized localized messages and HTML templates.
Enhancements:
Build:
Documentation:
Chores:
新功能:
增强:
构建:
文档:
assets/locales结构。杂项:
assets/misc/locales移动到新的assets/locales/interface目录树中,并新增assets/locales/go-service目录树,用于 Go 端消息和 HTML 模板。Original summary in English
Summary by Sourcery
引入可复用的 Go 服务 i18n 子系统,并迁移多个 agents 和 tasker 检查以使用集中管理的本地化消息和 HTML 模板。
Enhancements:
Build:
Documentation:
Chores:
Original summary in English
Summary by Sourcery
Introduce a reusable Go service i18n subsystem and migrate multiple agents and tasker checks to use centralized localized messages and HTML templates.
Enhancements:
Build:
Documentation:
Chores:
Original summary in English
Summary by Sourcery
引入可复用的 Go 服务 i18n 子系统,并迁移多个 agents 和 tasker 检查以使用集中管理的本地化消息和 HTML 模板。
Enhancements:
Build:
Documentation:
Chores:
Original summary in English
Summary by Sourcery
Introduce a reusable Go service i18n subsystem and migrate multiple agents and tasker checks to use centralized localized messages and HTML templates.
Enhancements:
Build:
Documentation:
Chores:
新功能:
增强:
构建:
文档:
assets/locales结构。杂项:
assets/misc/locales移动到新的assets/locales/interface目录树中,并新增assets/locales/go-service目录树,用于 Go 端消息和 HTML 模板。Original summary in English
Summary by Sourcery
引入可复用的 Go 服务 i18n 子系统,并迁移多个 agents 和 tasker 检查以使用集中管理的本地化消息和 HTML 模板。
Enhancements:
Build:
Documentation:
Chores:
Original summary in English
Summary by Sourcery
Introduce a reusable Go service i18n subsystem and migrate multiple agents and tasker checks to use centralized localized messages and HTML templates.
Enhancements:
Build:
Documentation:
Chores: