Default HUD language to English; Chinese remains switchable

New installs and missing language fall back to en. Settings menu lists
English first; grok-hud lang zh|tw still switches to Chinese variants.
This commit is contained in:
2026-07-17 16:55:18 +08:00
parent ed1a1ade45
commit efc16a8404
15 changed files with 52 additions and 47 deletions
+6
View File
@@ -1,5 +1,11 @@
# Changelog # Changelog
## 0.3.14
### Config
- **Default language is English** (`en`). Switch to 简体中文 / 繁體 via
`grok-hud settings` or `grok-hud lang zh` / `lang tw`.
## 0.3.13 ## 0.3.13
### Fixes ### Fixes
+2 -2
View File
@@ -20,7 +20,7 @@ ctx ██████░░░░ 50% (252k/500k) · i … o … c 99% · use 2
| Activity | Tools, agents, todos | | Activity | Tools, agents, todos |
| Theme | Follows Grok `[ui].theme` (not locked) | | Theme | Follows Grok `[ui].theme` (not locked) |
| Multi-terminal | Independent session per tab | | Multi-terminal | Independent session per tab |
| Language | Default **简体中文**; English / 繁體 available | | Language | Default **English**; 简体中文 / 繁體 available |
Repo: http://172.238.15.154:3000/Redredchen01/grok-build-hud Repo: http://172.238.15.154:3000/Redredchen01/grok-build-hud
Plugin id: `grok-build-hud` · version in [`plugin.json`](./plugin.json) Plugin id: `grok-build-hud` · version in [`plugin.json`](./plugin.json)
@@ -154,7 +154,7 @@ scripts/install.sh # one-shot installer
| **essential** | 2 | Model/git + context/usage + activity | | **essential** | 2 | Model/git + context/usage + activity |
| **minimal** | 1 | Dense single row | | **minimal** | 1 | Dense single row |
`language`: `zh-Hans` (default) / `en` / `zh-Hant` `language`: `en` (default) / `zh-Hans` / `zh-Hant`
Theme always tracks Grok `/theme` unless `GROK_HUD_LOCK=1`. Theme always tracks Grok `/theme` unless `GROK_HUD_LOCK=1`.
+4 -4
View File
@@ -20,7 +20,7 @@ Grok 4.5 · AI FILM SPACE/0717 · ●
| 活动 | 工具、agent、todos | | 活动 | 工具、agent、todos |
| 主题 | 跟随 Grok `[ui].theme`,不锁死 | | 主题 | 跟随 Grok `[ui].theme`,不锁死 |
| 多终端 | 每个标签页**独立** session,互不串线 | | 多终端 | 每个标签页**独立** session,互不串线 |
| 语言 | 默认**简体中文**,可切文 / 繁體 | | 语言 | 默认 **English**,可切简体中文 / 繁體 |
仓库:http://172.238.15.154:3000/Redredchen01/grok-build-hud 仓库:http://172.238.15.154:3000/Redredchen01/grok-build-hud
插件名:`grok-build-hud` · 版本见 [`plugin.json`](./plugin.json) 插件名:`grok-build-hud` · 版本见 [`plugin.json`](./plugin.json)
@@ -216,8 +216,8 @@ grok-hud stop
grok-hud settings grok-hud settings
# 快捷切语言 # 快捷切语言
grok-hud lang zh # 简体中文(默认) grok-hud lang en # English(默认)
grok-hud lang en # English grok-hud lang zh # 简体中文
grok-hud lang tw # 繁體中文 grok-hud lang tw # 繁體中文
``` ```
@@ -257,7 +257,7 @@ tmux source-file ~/.grok/hud/tmux.conf && tmux refresh-client -S
常用字段: 常用字段:
- `language``zh-Hans`(默认)/ `en` / `zh-Hant` - `language``en`(默认)/ `zh-Hans` / `zh-Hant`
- `bold: true` — 粗体数值 - `bold: true` — 粗体数值
- `barWidth: 14` — 进度条宽度 - `barWidth: 14` — 进度条宽度
+2 -2
View File
@@ -13,8 +13,8 @@ grok-build-hud --settings
## Language shortcuts ## Language shortcuts
```bash ```bash
grok-hud lang zh # 简体中文 (default) grok-hud lang en # English (default)
grok-hud lang en # English grok-hud lang zh # 简体中文
grok-hud lang tw # 繁體中文 grok-hud lang tw # 繁體中文
``` ```
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "grok-build-hud", "name": "grok-build-hud",
"version": "0.3.13", "version": "0.3.14",
"description": "第三方 Grok Build 状态条(非 xAI 官方):同窗口上下文/配额/token/工具;plugin + CLI + tmux HUD", "description": "第三方 Grok Build 状态条(非 xAI 官方):同窗口上下文/配额/token/工具;plugin + CLI + tmux HUD",
"type": "module", "type": "module",
"main": "dist/src/index.js", "main": "dist/src/index.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "grok-build-hud", "name": "grok-build-hud",
"version": "0.3.13", "version": "0.3.14",
"description": "第三方 Grok Build 状态条(非官方):同窗口上下文/配额/token/工具/待办;tmux HUD + 会话斜杠命令", "description": "第三方 Grok Build 状态条(非官方):同窗口上下文/配额/token/工具/待办;tmux HUD + 会话斜杠命令",
"author": { "author": {
"name": "Redredchen01" "name": "Redredchen01"
+2 -2
View File
@@ -35,8 +35,8 @@ One-shot: build CLI → install tmux dashboard → `grok plugin install . --trus
```bash ```bash
grok # wrapped: Grok + bottom HUD (same tab) grok # wrapped: Grok + bottom HUD (same tab)
grok-hud status # one-shot print grok-hud status # one-shot print
grok-hud settings # language (中/英), preset, rows grok-hud settings # language (default en; 中文可选), preset, rows
grok-hud lang zh|en|tw grok-hud lang en|zh|tw # en default
grok-build-hud --preset full|essential|minimal grok-build-hud --preset full|essential|minimal
grok-build-hud --theme auto # follow Grok [ui].theme grok-build-hud --theme auto # follow Grok [ui].theme
grok-hud stop # stop dashboard daemon grok-hud stop # stop dashboard daemon
+8 -8
View File
@@ -58,8 +58,8 @@ export const PRESET_FULL: HudDisplayConfig = {
preset: "full", preset: "full",
lineLayout: "expanded", lineLayout: "expanded",
pathLevels: 2, pathLevels: 2,
/** Default UI language: 简体中文 (switch to en in settings) */ /** Default UI language: English (switch to 中文 in settings) */
language: "zh-Hans", language: "en",
statusLines: 3, statusLines: 3,
bold: true, bold: true,
barWidth: 14, barWidth: 14,
@@ -150,7 +150,7 @@ export function loadHudConfig(
if (!fs.existsSync(p)) { if (!fs.existsSync(p)) {
return { return {
...PRESET_FULL, ...PRESET_FULL,
language: "zh-Hans", language: "en",
display: { ...PRESET_FULL.display }, display: { ...PRESET_FULL.display },
}; };
} }
@@ -164,8 +164,8 @@ export function loadHudConfig(
return { return {
...base, ...base,
...raw, ...raw,
// Prefer saved language; default 中文 when missing // Prefer saved language; default English when missing
language: raw.language ?? base.language ?? "zh-Hans", language: raw.language ?? base.language ?? "en",
bold: raw.bold ?? base.bold ?? true, bold: raw.bold ?? base.bold ?? true,
barWidth: raw.barWidth ?? base.barWidth ?? 14, barWidth: raw.barWidth ?? base.barWidth ?? 14,
statusLines: raw.statusLines ?? base.statusLines ?? 3, statusLines: raw.statusLines ?? base.statusLines ?? 3,
@@ -174,13 +174,13 @@ export function loadHudConfig(
} catch { } catch {
return { return {
...PRESET_FULL, ...PRESET_FULL,
language: "zh-Hans", language: "en",
display: { ...PRESET_FULL.display }, display: { ...PRESET_FULL.display },
}; };
} }
} }
/** Ensure config exists with Chinese default (idempotent). */ /** Ensure config exists with English default (idempotent). */
export function ensureDefaultConfig( export function ensureDefaultConfig(
grokHome = path.join(os.homedir(), ".grok"), grokHome = path.join(os.homedir(), ".grok"),
): HudDisplayConfig { ): HudDisplayConfig {
@@ -188,7 +188,7 @@ export function ensureDefaultConfig(
if (!fs.existsSync(p)) { if (!fs.existsSync(p)) {
const cfg = { const cfg = {
...PRESET_FULL, ...PRESET_FULL,
language: "zh-Hans" as const, language: "en" as const,
display: { ...PRESET_FULL.display }, display: { ...PRESET_FULL.display },
}; };
saveHudConfig(cfg, grokHome); saveHudConfig(cfg, grokHome);
+3 -3
View File
@@ -1,6 +1,6 @@
/** /**
* HUD + settings UI strings. * HUD + settings UI strings.
* Default language: zh-Hans (简体中文). Switch via settings → English. * Default language: en (English). Switch via settings → 中文 / 繁體.
*/ */
import type { HudDisplayConfig } from "./hud-config.js"; import type { HudDisplayConfig } from "./hud-config.js";
@@ -190,7 +190,7 @@ const EN: HudStrings = {
export function normalizeLang(raw?: string | null): HudLang { export function normalizeLang(raw?: string | null): HudLang {
const s = (raw || "").trim().toLowerCase(); const s = (raw || "").trim().toLowerCase();
if (!s) return "zh-Hans"; if (!s) return "en";
if (s === "en" || s === "english" || s === "eng") return "en"; if (s === "en" || s === "english" || s === "eng") return "en";
if ( if (
s === "zh-hant" || s === "zh-hant" ||
@@ -216,7 +216,7 @@ export function normalizeLang(raw?: string | null): HudLang {
) { ) {
return "zh-Hans"; return "zh-Hans";
} }
return "zh-Hans"; return "en";
} }
export function t(lang?: string | null): HudStrings { export function t(lang?: string | null): HudStrings {
+6 -5
View File
@@ -269,13 +269,14 @@ Also:
grok-build-hud --theme auto # follow Grok /theme (default, not locked) grok-build-hud --theme auto # follow Grok /theme (default, not locked)
grok-build-hud --preset full|essential|minimal grok-build-hud --preset full|essential|minimal
grok-hud settings # 设定界面(语言 中/英、预设、行数) grok-hud settings # 设定界面(语言 中/英、预设、行数)
grok-hud lang zh # 快捷:简体中文 grok-hud lang en # English (default)
grok-hud lang en # 快捷:English grok-hud lang zh # 简体中文
grok-hud lang tw # 繁體中文
Theme: always tracks Grok [ui].theme (auto → OS light/dark maps). Theme: always tracks Grok [ui].theme (auto → OS light/dark maps).
Change inside Grok with /theme — HUD re-paints in ~1s. Change inside Grok with /theme — HUD re-paints in ~1s.
Language: default 简体中文; switch in settings or: grok-hud lang en Language: default English; switch in settings or: grok-hud lang zh
Lifecycle: Terminal open → ready (not machine boot). Lifecycle: Terminal open → ready (not machine boot).
文档: README.md(中文)· README.en.mdEnglish 文档: README.md(中文)· README.en.mdEnglish
@@ -391,7 +392,7 @@ export async function runCli(
// keep readability defaults + user language // keep readability defaults + user language
cfg.bold = true; cfg.bold = true;
cfg.barWidth = Math.max(cfg.barWidth ?? 12, 12); cfg.barWidth = Math.max(cfg.barWidth ?? 12, 12);
cfg.language = prev.language ?? "zh-Hans"; cfg.language = prev.language ?? "en";
const saved = saveHudConfig(cfg, grokHome); const saved = saveHudConfig(cfg, grokHome);
writeTmuxConfFile(opts.grokHome); writeTmuxConfFile(opts.grokHome);
applyTmuxStatusBar({ grokHome: opts.grokHome }); applyTmuxStatusBar({ grokHome: opts.grokHome });
@@ -485,7 +486,7 @@ export async function runCli(
out(` (plain command — auto HUD; no extra flags)`); out(` (plain command — auto HUD; no extra flags)`);
out(` theme: follows Grok /theme (not locked)`); out(` theme: follows Grok /theme (not locked)`);
out( out(
` language: ${hudCfg.language} (设定: grok-hud settings · 切英文: grok-hud lang en)`, ` language: ${hudCfg.language} (settings: grok-hud settings · 文: grok-hud lang zh)`,
); );
if (r.grokWrap) { if (r.grokWrap) {
out(` wrap: ${r.grokWrap.wrapperPath}`); out(` wrap: ${r.grokWrap.wrapperPath}`);
+1 -1
View File
@@ -182,7 +182,7 @@ if-shell '[ -f "$HOME/.grok/hud/tmux.conf" ]' 'source-file ~/.grok/hud/tmux.conf
// Theme: always follow Grok [ui].theme (clear legacy locks) // Theme: always follow Grok [ui].theme (clear legacy locks)
ensureFollowMode(grokHome); ensureFollowMode(grokHome);
// Default language 简体中文 (user can switch in settings) // Default language English (user can switch to 中文 in settings)
ensureDefaultConfig(grokHome); ensureDefaultConfig(grokHome);
// Terminal-open hook (primary lifecycle): interactive zsh → ensure daemon // Terminal-open hook (primary lifecycle): interactive zsh → ensure daemon
+8 -8
View File
@@ -1,6 +1,6 @@
/** /**
* Interactive settings UI for language / preset / rows. * Interactive settings UI for language / preset / rows.
* Default language: 简体中文; switchable to English / 繁體. * Default language: English; switchable to 简体中文 / 繁體.
*/ */
import * as readline from "node:readline"; import * as readline from "node:readline";
import { import {
@@ -77,14 +77,14 @@ async function pickLanguage(
out: (s: string) => void, out: (s: string) => void,
): Promise<HudLang> { ): Promise<HudLang> {
const s = t(cfg.language); const s = t(cfg.language);
out(` 1) ${s.langZh}`); out(` 1) ${s.langEn} (default)`);
out(` 2) ${s.langZhHant}`); out(` 2) ${s.langZh}`);
out(` 3) ${s.langEn}`); out(` 3) ${s.langZhHant}`);
out(` b) ${s.back}`); out(` b) ${s.back}`);
const ans = await ask(rl, ` ${s.choose}: `); const ans = await ask(rl, ` ${s.choose}: `);
if (ans === "1" || ans === "zh" || ans === "cn") return "zh-Hans"; if (ans === "1" || ans === "en") return "en";
if (ans === "2" || ans === "tw" || ans === "hant") return "zh-Hant"; if (ans === "2" || ans === "zh" || ans === "cn") return "zh-Hans";
if (ans === "3" || ans === "en") return "en"; if (ans === "3" || ans === "tw" || ans === "hant") return "zh-Hant";
return normalizeLang(cfg.language); return normalizeLang(cfg.language);
} }
@@ -152,7 +152,7 @@ export async function runSettingsUi(
// Default to Chinese if somehow empty // Default to Chinese if somehow empty
if (!cfg.language) { if (!cfg.language) {
cfg = { ...cfg, language: "zh-Hans" }; cfg = { ...cfg, language: "en" };
} }
const rl = readline.createInterface({ const rl = readline.createInterface({
+4 -7
View File
@@ -22,12 +22,9 @@ import {
/** Brand-new Terminal status: ctx 0%, no leakage from previous windows. */ /** Brand-new Terminal status: ctx 0%, no leakage from previous windows. */
export function seedIdleTmuxStatus(instDir: string): void { export function seedIdleTmuxStatus(instDir: string): void {
fs.mkdirSync(instDir, { recursive: true }); fs.mkdirSync(instDir, { recursive: true });
const idleSingle = "窗 ░░░░░░░░░░ 0% · —\n"; // English labels (default UI language); live dashboard rewrites with user lang
const idleLines = [ const idleSingle = "ctx ░░░░░░░░░░ 0% · —\n";
"— · —", const idleLines = ["— · —", "ctx ░░░░░░░░░░ 0% · —", ""].join("\n") + "\n";
"窗 ░░░░░░░░░░ 0% · —",
"",
].join("\n") + "\n";
fs.writeFileSync(path.join(instDir, "tmux-status.txt"), idleSingle, "utf8"); fs.writeFileSync(path.join(instDir, "tmux-status.txt"), idleSingle, "utf8");
fs.writeFileSync(path.join(instDir, "tmux-lines.txt"), idleLines, "utf8"); fs.writeFileSync(path.join(instDir, "tmux-lines.txt"), idleLines, "utf8");
fs.writeFileSync(path.join(instDir, "status-line.txt"), "[hud] ctx 0%\n", "utf8"); fs.writeFileSync(path.join(instDir, "status-line.txt"), "[hud] ctx 0%\n", "utf8");
@@ -83,7 +80,7 @@ export function tmuxStatusCommands(grokHome = defaultGrokHome()): string[][] {
const scopedSingle = `${instRoot}/#{session_name}/tmux-status.txt`; const scopedSingle = `${instRoot}/#{session_name}/tmux-status.txt`;
// NEVER fall back to global ~/.grok/hud/tmux-*.txt — that leaked the previous // NEVER fall back to global ~/.grok/hud/tmux-*.txt — that leaked the previous
// Terminal's high ctx% into brand-new windows (should show 0% / idle). // Terminal's high ctx% into brand-new windows (should show 0% / idle).
const idleSingle = " ░░░░░░░░░░ 0% · —"; const idleSingle = "ctx ░░░░░░░░░░ 0% · —";
// Up to 3 status rows in the SAME terminal window // Up to 3 status rows in the SAME terminal window
const statusLines = Math.max( const statusLines = Math.max(
1, 1,
+2 -1
View File
@@ -13,7 +13,8 @@ import type { SessionSnapshot } from "../src/types.js";
describe("i18n + settings language", () => { describe("i18n + settings language", () => {
it("defaults and normalizes languages", () => { it("defaults and normalizes languages", () => {
assert.equal(normalizeLang(undefined), "zh-Hans"); assert.equal(normalizeLang(undefined), "en");
assert.equal(normalizeLang(""), "en");
assert.equal(normalizeLang("zh"), "zh-Hans"); assert.equal(normalizeLang("zh"), "zh-Hans");
assert.equal(normalizeLang("en"), "en"); assert.equal(normalizeLang("en"), "en");
assert.equal(normalizeLang("tw"), "zh-Hant"); assert.equal(normalizeLang("tw"), "zh-Hant");
+2 -2
View File
@@ -106,9 +106,9 @@ describe("layout", () => {
for (const ln of narrow) { for (const ln of narrow) {
assert.ok(visibleLen(ln) <= 50, `line too long: ${visibleLen(ln)} ${stripTmuxStyles(ln)}`); assert.ok(visibleLen(ln) <= 50, `line too long: ${visibleLen(ln)} ${stripTmuxStyles(ln)}`);
} }
// hierarchy styles present on wide (default language 中文 labels) // hierarchy styles present on wide (default language English labels)
const joined = wide.join("\n"); const joined = wide.join("\n");
assert.match(joined, /italics|dim|bold/); assert.match(joined, /italics|dim|bold/);
assert.match(joined, /入 |出 |缓 |窗 /); assert.match(joined, /\bi |\bo |\bc |ctx /);
}); });
}); });