Skip to content

feat(tui): harvest zh-Hans locale bundle#3559

Merged
Hmbown merged 4 commits into
mainfrom
codex/pr3549-i18n-finish
Jun 24, 2026
Merged

feat(tui): harvest zh-Hans locale bundle#3559
Hmbown merged 4 commits into
mainfrom
codex/pr3549-i18n-finish

Conversation

@Hmbown

@Hmbown Hmbown commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

  • harvest feat(i18n): extract Chinese translations into dedicated JSON file #3549 by @cy2311 as a clean current-main merge with contributor credit
  • move Simplified Chinese UI strings into crates/tui/locales/zh-Hans.json without reducing shipped locale coverage
  • keep the useful details-shortcut cleanup from the branch: visible help/footer/context copy now points to bare v; the old modified key path remains only as a compatibility alias

Verification

  • cargo fmt --all
  • cargo test -p codewhale-tui --bin codewhale-tui --locked localization::tests
  • cargo test -p codewhale-tui --bin codewhale-tui --locked tool_details_help_documents_bare_v_without_alt_v
  • cargo test -p codewhale-tui --bin codewhale-tui --locked activity_footer_hint
  • cargo test -p codewhale-tui --bin codewhale-tui --locked activity_detail
  • cargo test -p codewhale-tui --bin codewhale-tui --locked open_tool_details_pager

Harvested from PR #3549 by @cy2311.
Refs #3537.

cy2311 and others added 2 commits June 24, 2026 20:12
Harvested from PR #3549 by @cy2311.

Refs #3537.

Preserves the dedicated zh-Hans JSON extraction while resolving current-main localization coverage and moving the visible details shortcut copy to bare v.

Co-authored-by: CY <29836092+cy2311@users.noreply.github.com>
@claude

claude Bot commented Jun 24, 2026

Copy link
Copy Markdown

Claude finished @Hmbown's task in 3m 54s —— View job


Code Review

  • Gather context and read changed files
  • Review correctness and logic
  • Check localization loading and coverage
  • Check keybinding changes
  • Summarize findings

Summary

Clean harvest. The JSON-loading approach for zh-Hans is architecturally sound, coverage is enforced by the existing test, and the bare-v shortcut restoration is correctly guarded. A few minor observations below.


Correctness

chinese_simplified lifetime — OK.
MAP.get(&format!("{id:?}")) returns Option<&'static String> (because MAP is a static LazyLock), and .map(String::as_str) correctly lifts that to Option<&'static str>. The return type matches.

.expect("valid zh-Hans.json translations") — acceptable.
This panics on first zh-Hans access if the JSON is malformed. For a compile-time-embedded asset that is gated by shipped_first_pack_has_no_missing_core_messages, this is the right failure mode (loudly wrong, not silently English). The alternative — unwrap_or_default() — would mask a corrupted bundle.

Bare v guard — tight.

KeyCode::Char('v')
    if key.modifiers == KeyModifiers::NONE
        && app.input.is_empty()
        && detail_target_cell_index(app).is_some() =>

All three conditions must hold before a keystroke is consumed, so typing v into a non-empty composer is unaffected. Alt+V at line 4911 and the macOS Option+V (√) legacy path at line 3901 are both preserved, maintaining backward compat.

Vim normal mode interaction — worth knowing.
The bare v arm appears before the vim-normal-mode arm in the match. If vim normal mode is active, input is empty, and a tool cell is selected, pressing v opens the details pager instead of entering visual mode. Empty-buffer visual selection does nothing meaningful anyway, so this is likely intentional — but documenting it would help a future reader who wonders why vim's v seems to misfire here.


Potential fragility

JSON key format coupling.
MAP.get(&format!("{id:?}")) keys are the Debug-derived variant names ("ComposerPlaceholder", etc.). If MessageId ever gets a custom Debug impl or per-variant #[doc(hidden)]-style renaming, the JSON keys silently stop matching. This is a known tradeoff of the translator-friendly JSON approach and is caught by shipped_first_pack_has_no_missing_core_messages on next CI run — acceptable, but worth a code-comment in chinese_simplified so future contributors don't reach for {id:?} without knowing why it works.


Minor / nits

CtxInspAltVHint variant name is stale.
Both the English string ("Open the matching card and press v for full details.") and the zh-Hans translation ("打开对应的卡片并按 v 查看详细信息。") correctly say v, but the MessageId variant is still named CtxInspAltVHint. Not a bug — the name is code-internal only — but it'll confuse a grep for AltV in the future. A follow-up rename is low-priority but worthwhile. Fix this →

open_tool_details_pager return value discarded — preexisting, not introduced here.
The new call site at the v arm is consistent with the existing call sites at lines 3902 and 4914. No #[must_use] annotation, and none of the callers act on success/failure. Not a regression.

ZhHant fallback spec vs. runtime behaviour.
LocaleSpec::fallback: "zh-Hans" for Traditional Chinese is informational only — the runtime fallback_translation() path always falls back to English, not zh-Hans. Pre-existing gap, not introduced here.


Tests

The PR's verification commands (localization::tests, tool_details_help_documents_bare_v_without_alt_v, activity_footer_hint, activity_detail, open_tool_details_pager) cover the main paths. shipped_first_pack_has_no_missing_core_messages acts as exhaustiveness enforcement for the JSON bundle.

LGTM with the note that the CtxInspAltVHint rename is a small follow-up worth filing.

@claude

claude Bot commented Jun 24, 2026

Copy link
Copy Markdown

Claude finished @Hmbown's task in 2m 44s —— View job


Code Review

  • Read changed files and diff
  • Review localization.rs refactor
  • Review zh-Hans.json bundle
  • Review keybinding changes
  • Review UI and test changes
  • Summarize findings

Summary

Clean harvest. The JSON-backed chinese_simplified is architecturally sound, coverage is enforced by shipped_first_pack_has_no_missing_core_messages, and the bare-v restoration is correctly guarded. A few minor observations below — nothing blocking.


Correctness

chinese_simplified lifetime — OK.
MAP is static LazyLock<HashMap<String, String>>, so MAP.get(...) yields Option<&'static String> and .map(String::as_str) correctly produces Option<&'static str>. Return type matches the callers in tr().

.expect("valid zh-Hans.json translations") — appropriate.
This panics on first zh-Hans access if the JSON is malformed. For a compile-time-embedded asset already gated by shipped_first_pack_has_no_missing_core_messages, a loud panic is the right failure mode — unwrap_or_default() would silently fall back to English and mask a corrupted bundle.

zh-Hant fallback path — still works.
traditional_chinese at line 2761 has other => chinese_simplified(other)?. With chinese_simplified now returning MAP.get(&format!("{id:?}")).map(String::as_str), KbSelectedDetails and the updated CtxInspAltVHint both resolve via the JSON map for zh-Hant. The shipped_first_pack_has_no_missing_core_messages test covers this transitively.

Bare v guard — tight.

KeyCode::Char('v')
    if key.modifiers == KeyModifiers::NONE
        && app.input.is_empty()
        && detail_target_cell_index(app).is_some() =>

All three conditions must hold. If no tool/subagent cell is selected, the arm doesn't fire and v falls through to normal text input — better behavior than the old Alt+V arm which consumed the keystroke regardless of context.

Alt+V and macOS Option+V (√) preserved.
Both compat paths remain: macOS Option+V at line 3901 and Alt+V | Alt+Shift+V at line 4911. No backward-compat regression.

KbSelectedDetails coverage — complete.
Added to ALL_MESSAGE_IDS, english(), and all 5 non-zh-Hans locales (vietnamese, japanese, portuguese_brazil, spanish_latin_america, and in zh-Hans.json at line 211). The shipped_first_pack_has_no_missing_core_messages test enforces exhaustiveness at CI time.


Worth noting

Vim normal mode interaction.
The bare v arm (line 3967 in ui.rs) appears before the vim-normal-mode arm (line 4920). If vim normal mode is active, input is empty, and a tool cell is selected, pressing v opens the details pager rather than entering visual mode. Empty-buffer visual mode has no meaningful effect here, so this is likely fine — but a one-line comment in the arm would help a future reader who wonders why vim's v seems to fire early. Fix this →

CtxInspAltVHint variant name is stale.
Both the English string and the zh-Hans translation now correctly say v, but the MessageId variant is still CtxInspAltVHint. Code-internal only — not a user-facing bug — but will confuse a future grep AltV. Low-priority follow-up rename. Fix this →

JSON key format coupling.
MAP.get(&format!("{id:?}")) keys rely on Debug-derived variant names. If MessageId ever gets a custom Debug impl, JSON keys silently stop matching. Caught by shipped_first_pack_has_no_missing_core_messages on next CI run — acceptable, but a short comment in chinese_simplified would save a future contributor from wondering why {id:?} and not {id}.


Tests

The PR's verification commands cover the main paths. shipped_first_pack_has_no_missing_core_messages acts as exhaustiveness enforcement for the JSON bundle. Keybinding test correctly renamed from tool_details_help_documents_alt_v_without_bare_vtool_details_help_documents_bare_v_without_alt_v with updated assertion that Alt+V no longer appears in the table.

LGTM. The two follow-up items (CtxInspAltVHint rename, vim comment) are low-priority and don't block merge.

Add the cy2311 AUTHOR_MAP entry required by the harvest credit gate, rename the stale context-inspector v hint id, and document the bare-v detail shortcut precedence.

Follow-up for PR #3559.
@claude

claude Bot commented Jun 24, 2026

Copy link
Copy Markdown

Claude finished @Hmbown's task in 2m 17s —— View job


Code Review

  • Read diff and changed files
  • Review correctness and logic
  • Check localization loading and coverage
  • Check keybinding changes
  • Summarize findings

Summary

The follow-up commit (0bfe3f5) completes what the two earlier reviews flagged: the CtxInspAltVHint → CtxInspVHint rename is done, the vim-mode precedence comment is in place, and the cy2311 AUTHOR_MAP entry is added. One small residual item below, otherwise clean.


Correctness

chinese_simplified lifetime — OK.
MAP is static LazyLock<HashMap<String, String>>, so MAP.get(...) yields Option<&'static String> and .map(String::as_str) correctly produces Option<&'static str>. Return type matches all callers.

.expect("valid zh-Hans.json translations") — appropriate.
Panics on first zh-Hans access if the JSON is malformed. For a compile-time-embedded asset gated by shipped_first_pack_has_no_missing_core_messages, a loud panic is the right failure mode.

KbSelectedDetails coverage — complete.
Added to ALL_MESSAGE_IDS, english(), all 5 non-zh-Hans locale functions, and zh-Hans.json (line 211). CtxInspAltVHint stale variant is gone — grep -rn CtxInspAltVHint returns empty.

Bare v guard — tight.

KeyCode::Char('v')
    if key.modifiers == KeyModifiers::NONE
        && app.input.is_empty()
        && detail_target_cell_index(app).is_some() =>

All three conditions must hold. Alt+V | Alt+Shift+V compat arm at line 4914 and macOS Option+V (√) arm at line 3901 are both preserved.

Vim-mode precedence comment — added.
ui.rs line 3967 now documents why the bare v arm intentionally precedes vim-normal-mode. Previous reviews' request addressed.


Residual: KbToolDetailsPager is now orphaned

KbToolDetailsPager was replaced in the keybinding table by KbSelectedDetails, but the variant is still present in the enum, ALL_MESSAGE_IDS, english(), all five locale functions, and zh-Hans.json. It's dead code from the help-display perspective — nothing renders it to the user anymore. shipped_first_pack_has_no_missing_core_messages would need updating if you remove it, but it's not enforcing anything visible.

Not blocking, but worth a follow-up: Fix this →


Tests

Keybinding test correctly renamed tool_details_help_documents_bare_v_without_alt_v with a double assertion: primary chord is "v", and "Alt+V" is absent from the table. ui/tests.rs assertions updated to match the new "v raw" / "v details" string formats.

LGTM. All previous review items are resolved. KbToolDetailsPager cleanup is low-priority follow-up only.

@Hmbown Hmbown merged commit a7285ea into main Jun 24, 2026
15 checks passed
@Hmbown Hmbown deleted the codex/pr3549-i18n-finish branch June 24, 2026 20:56
Hmbown added a commit that referenced this pull request Jun 24, 2026
Record the final PR train, empty v0.8.65 milestone, moved follow-up scope, and verification evidence after the #3559/#3560/#3561 replacements landed.
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