mirror of
https://github.com/jarrodwatts/claude-hud.git
synced 2026-04-20 01:32:38 +00:00
fix: address code review findings for i18n
- Fix detectLanguage() to follow POSIX priority: LC_ALL > LC_MESSAGES > LANG
- Fix Chinese format.resetsIn from broken wrap-around grammar to prefix form
- Fix loadConfig() to call mergeConfig({}) when no config file exists,
ensuring detectLanguage() runs for auto-detection
- Add setLanguage('en') guard to render.test.js for locale-independent tests
- Add dedicated i18n test suite (tests/i18n.test.js) covering t(),
detectLanguage(), and mergeConfig language handling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -96,8 +96,9 @@ export interface HudConfig {
|
||||
}
|
||||
|
||||
export function detectLanguage(): Language {
|
||||
// POSIX priority: LC_ALL overrides everything, then LC_MESSAGES, then LANG
|
||||
const envLang =
|
||||
process.env.LANG || process.env.LC_ALL || process.env.LC_MESSAGES || "";
|
||||
process.env.LC_ALL || process.env.LC_MESSAGES || process.env.LANG || "";
|
||||
if (envLang.startsWith("zh")) return "zh";
|
||||
return "en";
|
||||
}
|
||||
@@ -458,13 +459,13 @@ export async function loadConfig(): Promise<HudConfig> {
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(configPath)) {
|
||||
return DEFAULT_CONFIG;
|
||||
return mergeConfig({});
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(configPath, "utf-8");
|
||||
const userConfig = JSON.parse(content) as Partial<HudConfig>;
|
||||
return mergeConfig(userConfig);
|
||||
} catch {
|
||||
return DEFAULT_CONFIG;
|
||||
return mergeConfig({});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export const zh: Messages = {
|
||||
|
||||
// Format
|
||||
"format.resets": "重置于",
|
||||
"format.resetsIn": "将在...后重置",
|
||||
"format.resetsIn": "重置剩余",
|
||||
"format.in": "输入",
|
||||
"format.cache": "缓存",
|
||||
"format.out": "输出",
|
||||
|
||||
126
tests/i18n.test.js
Normal file
126
tests/i18n.test.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { setLanguage, getLanguage, t } from "../dist/i18n/index.js";
|
||||
import { detectLanguage, mergeConfig } from "../dist/config.js";
|
||||
|
||||
test("t() returns English strings by default", () => {
|
||||
setLanguage("en");
|
||||
assert.equal(t("label.context"), "Context");
|
||||
assert.equal(t("label.usage"), "Usage");
|
||||
assert.equal(t("label.approxRam"), "Approx RAM");
|
||||
assert.equal(t("status.limitReached"), "Limit reached");
|
||||
assert.equal(t("status.allTodosComplete"), "All todos complete");
|
||||
});
|
||||
|
||||
test("t() returns Chinese strings when language is zh", () => {
|
||||
setLanguage("zh");
|
||||
assert.equal(t("label.context"), "上下文");
|
||||
assert.equal(t("label.usage"), "用量");
|
||||
assert.equal(t("label.approxRam"), "内存");
|
||||
assert.equal(t("label.rules"), "规则");
|
||||
assert.equal(t("label.hooks"), "钩子");
|
||||
assert.equal(t("status.limitReached"), "已达上限");
|
||||
assert.equal(t("status.allTodosComplete"), "全部完成");
|
||||
assert.equal(t("format.in"), "输入");
|
||||
assert.equal(t("format.cache"), "缓存");
|
||||
assert.equal(t("format.out"), "输出");
|
||||
// Restore
|
||||
setLanguage("en");
|
||||
});
|
||||
|
||||
test("setLanguage and getLanguage round-trip", () => {
|
||||
setLanguage("zh");
|
||||
assert.equal(getLanguage(), "zh");
|
||||
setLanguage("en");
|
||||
assert.equal(getLanguage(), "en");
|
||||
});
|
||||
|
||||
test("detectLanguage respects LC_ALL over LANG (POSIX priority)", () => {
|
||||
const origLang = process.env.LANG;
|
||||
const origLcAll = process.env.LC_ALL;
|
||||
const origLcMsg = process.env.LC_MESSAGES;
|
||||
|
||||
try {
|
||||
process.env.LANG = "en_US.UTF-8";
|
||||
process.env.LC_ALL = "zh_CN.UTF-8";
|
||||
delete process.env.LC_MESSAGES;
|
||||
assert.equal(detectLanguage(), "zh");
|
||||
|
||||
process.env.LC_ALL = "en_US.UTF-8";
|
||||
process.env.LANG = "zh_CN.UTF-8";
|
||||
assert.equal(detectLanguage(), "en");
|
||||
|
||||
delete process.env.LC_ALL;
|
||||
assert.equal(detectLanguage(), "zh");
|
||||
} finally {
|
||||
process.env.LANG = origLang;
|
||||
process.env.LC_ALL = origLcAll;
|
||||
process.env.LC_MESSAGES = origLcMsg;
|
||||
}
|
||||
});
|
||||
|
||||
test("detectLanguage returns en for unknown locales", () => {
|
||||
const origLang = process.env.LANG;
|
||||
const origLcAll = process.env.LC_ALL;
|
||||
const origLcMsg = process.env.LC_MESSAGES;
|
||||
|
||||
try {
|
||||
delete process.env.LC_ALL;
|
||||
delete process.env.LC_MESSAGES;
|
||||
process.env.LANG = "fr_FR.UTF-8";
|
||||
assert.equal(detectLanguage(), "en");
|
||||
|
||||
process.env.LANG = "C";
|
||||
assert.equal(detectLanguage(), "en");
|
||||
|
||||
delete process.env.LANG;
|
||||
assert.equal(detectLanguage(), "en");
|
||||
} finally {
|
||||
process.env.LANG = origLang;
|
||||
process.env.LC_ALL = origLcAll;
|
||||
process.env.LC_MESSAGES = origLcMsg;
|
||||
}
|
||||
});
|
||||
|
||||
test("mergeConfig uses detectLanguage when no language specified", () => {
|
||||
const origLcAll = process.env.LC_ALL;
|
||||
const origLang = process.env.LANG;
|
||||
const origLcMsg = process.env.LC_MESSAGES;
|
||||
|
||||
try {
|
||||
process.env.LC_ALL = "zh_CN.UTF-8";
|
||||
delete process.env.LC_MESSAGES;
|
||||
const config = mergeConfig({});
|
||||
assert.equal(config.language, "zh");
|
||||
} finally {
|
||||
process.env.LC_ALL = origLcAll;
|
||||
process.env.LANG = origLang;
|
||||
process.env.LC_MESSAGES = origLcMsg;
|
||||
}
|
||||
});
|
||||
|
||||
test("mergeConfig preserves explicit language from config", () => {
|
||||
const config = mergeConfig({ language: "zh" });
|
||||
assert.equal(config.language, "zh");
|
||||
|
||||
const config2 = mergeConfig({ language: "en" });
|
||||
assert.equal(config2.language, "en");
|
||||
});
|
||||
|
||||
test("mergeConfig falls back to detection for invalid language", () => {
|
||||
const origLcAll = process.env.LC_ALL;
|
||||
const origLang = process.env.LANG;
|
||||
const origLcMsg = process.env.LC_MESSAGES;
|
||||
|
||||
try {
|
||||
delete process.env.LC_ALL;
|
||||
delete process.env.LC_MESSAGES;
|
||||
process.env.LANG = "C";
|
||||
const config = mergeConfig({ language: "invalid" });
|
||||
assert.equal(config.language, "en");
|
||||
} finally {
|
||||
process.env.LC_ALL = origLcAll;
|
||||
process.env.LANG = origLang;
|
||||
process.env.LC_MESSAGES = origLcMsg;
|
||||
}
|
||||
});
|
||||
1417
tests/render.test.js
1417
tests/render.test.js
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user