Skip to content

fix: i18n key path errors, locale drift, and TUI/CLI correctness fixes - #2

Merged
7723qqq merged 1 commit into
7723qqq:mainfrom
h2o502:fix/i18n-and-ui-bugs
Jul 21, 2026
Merged

fix: i18n key path errors, locale drift, and TUI/CLI correctness fixes#2
7723qqq merged 1 commit into
7723qqq:mainfrom
h2o502:fix/i18n-and-ui-bugs

Conversation

@h2o502

@h2o502 h2o502 commented Jul 21, 2026

Copy link
Copy Markdown

fix: i18n key path errors, locale drift, and TUI/CLI correctness fixes

Summary

Static analysis of the fork found a cluster of user-facing bugs: several TUI
screens render raw i18n key paths instead of text, the Astron settings panel
is completely untranslated (and gets wiped by unrelated config saves), the
/workflow help prints undefined, and an 80 ms footer timer runs forever
once thinking effort is enabled.

All fixes are minimal, behavior-preserving except where the current behavior
is objectively wrong. Every t() key used in the touched files was
machine-verified to resolve in both en and zh runtime locales
(locales/*.ts), with placeholder parity.

Bugs fixed

1. i18n key paths pointing at non-existent locations (raw keys shown in UI)

File Wrong prefix Correct prefix Count
tui/commands/config.ts tui.dialogs.config.configXxx tui.messages.configXxx 41
migration/migration-screen.ts, migration/badge.ts migration.X tui.migration.X 34
tui/components/dialogs/coding-plan-config.ts codingPlan.X tui.codingPlan.X 12

The Rust translation engine falls back to returning the key itself, so the
config/migration/coding-plan screens currently display strings like
tui.dialogs.config.configThemeSet to users.

2. Astron settings panel untranslated + locale drift

astron-settings.ts references 14 keys under tui.dialogs.astronSettings.*
that exist in locales/*.json but were never synced into the runtime
locales/*.ts sources — the whole panel renders raw key paths. Also missing
in *.ts: settingsSelector.astron/astronDesc and
messages.configAstronSaveFailed (17 keys total, EN+ZH). Reverse drift:
tui.chrome.footer.swarmPlan existed only in *.ts; added to *.json.
en.json/zh.jsonen.ts/zh.ts are now fully in sync.

3. Malformed placeholder in zh locale

zh unsupportedEffort contained {{arg} (missing closing }}) — the value
would render literally. Fixed in both zh.ts and zh.json.

4. /workflow usage prints undefined

workflow.ts did t('tui.slashCommands.workflowHelp') as Record<string, string>t() returns a string, so every wf.* access was undefined.
Replaced with individual leaf-key calls (workflowHelp.usage, .list, ...).

5. /discuss prompt-structure injection

User-supplied roles/stances were interpolated into the quoted
roleDescription/assignedStance strings unescaped, so a " in a role name
corrupts the generated prompt. Now escapes \ and " (and removes the dead
safeName variable that was computed but never used).

6. Changing theme/locale/editor wipes Astron settings

currentTuiConfig() hardcoded astron: DEFAULT_TUI_CONFIG.astron, so every
saveTuiConfig from the theme/locale/editor/update-preference flows reset
the user's Astron parameters to defaults. It is now async and seeds astron
from the persisted tui.toml (matching how AstronSettingsComponent itself
does load-modify-save).

7. createI18n ignores initialLocale when noDetect is set

options.noDetect ? 'en' : (options.initialLocale ?? detectLocaleNode())
made an explicit initialLocale lose to noDetect. Priority is now
initialLocale ?? (noDetect ? 'en' : detectLocaleNode()). Also exports the
previously instance-only getMessages/translateBatch at module level, and
fixes a JSDoc example referencing a non-existent key.

8. Footer pulse timer runs 12.5×/s forever

syncPulseTimer(state.thinkingEffort !== 'off') kept an 80 ms interval alive
permanently once thinking effort was enabled — constant re-renders while
idle. Now tied to actual activity: thinkingEffort !== 'off' && streamingPhase !== 'idle'.

9. Coding-plan editor accepts control keys as text

The input handler appended any non-Enter/Escape data to the field value, so
arrow keys / Tab / Ctrl combos leaked escape bytes (e.g. \x1b[C) into
config values. Now uses the codebase-standard printableChar filter (paste
still works).

10. Misleading error when resuming a session from another directory

run-v2-print.ts printed a "created under a different directory" hint on
stderr and then threw Session "..." not found — contradictory. It now
throws the sessionDifferentDir message (which carries the remediation
hint) directly.

Verification

  • Scripted check: every t('<key>') in the touched files resolves to a
    string in both runtime locales — 0 missing.
  • en.json/zh.json vs en.ts/zh.ts drift: 0 entries.
  • EN/ZH key parity: identical key sets; placeholder sets equal (except the
    intentional {{plural}} omissions in zh, Chinese having no plural form).
  • No malformed {{...}} placeholders remain in either locale.
  • All touched .ts files parse cleanly (node --experimental-strip-types).
  • Patch verified with git apply --check against current main.

# fix: i18n key path errors, locale drift, and TUI/CLI correctness fixes

## Summary

Static analysis of the fork found a cluster of user-facing bugs: several TUI
screens render raw i18n key paths instead of text, the Astron settings panel
is completely untranslated (and gets wiped by unrelated config saves), the
`/workflow` help prints `undefined`, and an 80 ms footer timer runs forever
once thinking effort is enabled.

All fixes are minimal, behavior-preserving except where the current behavior
is objectively wrong. Every `t()` key used in the touched files was
machine-verified to resolve in **both** `en` and `zh` runtime locales
(`locales/*.ts`), with placeholder parity.

## Bugs fixed

### 1. i18n key paths pointing at non-existent locations (raw keys shown in UI)

| File | Wrong prefix | Correct prefix | Count |
|---|---|---|---|
| `tui/commands/config.ts` | `tui.dialogs.config.configXxx` | `tui.messages.configXxx` | 41 |
| `migration/migration-screen.ts`, `migration/badge.ts` | `migration.X` | `tui.migration.X` | 34 |
| `tui/components/dialogs/coding-plan-config.ts` | `codingPlan.X` | `tui.codingPlan.X` | 12 |

The Rust translation engine falls back to returning the key itself, so the
config/migration/coding-plan screens currently display strings like
`tui.dialogs.config.configThemeSet` to users.

### 2. Astron settings panel untranslated + locale drift

`astron-settings.ts` references 14 keys under `tui.dialogs.astronSettings.*`
that exist in `locales/*.json` but were never synced into the runtime
`locales/*.ts` sources — the whole panel renders raw key paths. Also missing
in `*.ts`: `settingsSelector.astron/astronDesc` and
`messages.configAstronSaveFailed` (17 keys total, EN+ZH). Reverse drift:
`tui.chrome.footer.swarmPlan` existed only in `*.ts`; added to `*.json`.
`en.json/zh.json` ↔ `en.ts/zh.ts` are now fully in sync.

### 3. Malformed placeholder in zh locale

`zh unsupportedEffort` contained `{{arg}` (missing closing `}}`) — the value
would render literally. Fixed in both `zh.ts` and `zh.json`.

### 4. `/workflow` usage prints `undefined`

`workflow.ts` did `t('tui.slashCommands.workflowHelp') as Record<string,
string>` — `t()` returns a string, so every `wf.*` access was `undefined`.
Replaced with individual leaf-key calls (`workflowHelp.usage`, `.list`, ...).

### 5. `/discuss` prompt-structure injection

User-supplied roles/stances were interpolated into the quoted
`roleDescription`/`assignedStance` strings unescaped, so a `"` in a role name
corrupts the generated prompt. Now escapes `\` and `"` (and removes the dead
`safeName` variable that was computed but never used).

### 6. Changing theme/locale/editor wipes Astron settings

`currentTuiConfig()` hardcoded `astron: DEFAULT_TUI_CONFIG.astron`, so every
`saveTuiConfig` from the theme/locale/editor/update-preference flows reset
the user's Astron parameters to defaults. It is now async and seeds `astron`
from the persisted `tui.toml` (matching how `AstronSettingsComponent` itself
does load-modify-save).

### 7. `createI18n` ignores `initialLocale` when `noDetect` is set

`options.noDetect ? 'en' : (options.initialLocale ?? detectLocaleNode())`
made an explicit `initialLocale` lose to `noDetect`. Priority is now
`initialLocale ?? (noDetect ? 'en' : detectLocaleNode())`. Also exports the
previously instance-only `getMessages`/`translateBatch` at module level, and
fixes a JSDoc example referencing a non-existent key.

### 8. Footer pulse timer runs 12.5×/s forever

`syncPulseTimer(state.thinkingEffort !== 'off')` kept an 80 ms interval alive
permanently once thinking effort was enabled — constant re-renders while
idle. Now tied to actual activity: `thinkingEffort !== 'off' &&
streamingPhase !== 'idle'`.

### 9. Coding-plan editor accepts control keys as text

The input handler appended any non-Enter/Escape data to the field value, so
arrow keys / Tab / Ctrl combos leaked escape bytes (e.g. `\x1b[C`) into
config values. Now uses the codebase-standard `printableCha
@7723qqq
7723qqq merged commit 3ad8463 into 7723qqq:main Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants