Skip to content

feat(i18n): localize ToolFamily labels (10 MessageIds)#2901

Open
gordonlu wants to merge 3 commits into
Hmbown:mainfrom
gordonlu:feat/i18n-tool-family
Open

feat(i18n): localize ToolFamily labels (10 MessageIds)#2901
gordonlu wants to merge 3 commits into
Hmbown:mainfrom
gordonlu:feat/i18n-tool-family

Conversation

@gordonlu

@gordonlu gordonlu commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Localize tool family verb labels (read, patch, run, find, delegate, fanout, rlm, verify, think, tool) used in tool card headers, sidebar, and footer status lines. 10 new MessageId variants covering all 7 shipped locales.

Changes

  • localization.rs: 10 ToolFamily* MessageId variants, ALL_MESSAGE_IDS entry, translations for En, Vi, ZhHant, Ja, ZhHans, PtBr, Es419.
  • tool_card.rs: tool_activity_label_for_name() takes locale and uses tr() for localized labels. family_label() and tool_display_label_for_name() remain English-only (internal helpers).
  • footer_ui.rs, ui.rs: Pass app.ui_locale to tool_activity_label_for_name() callers.
  • Tests: 2 negative i18n tests (no exact English leak in non-English locales).

Notes

  • family_label() stays English-only — used in card headers alongside glyphs, matching the vocabulary-label pattern from SubAgentType kind column.
  • tool_activity_label_for_name() is the user-facing function used in footer status labels and activity detail.

Greptile Summary

Adds localized labels for all 10 ToolFamily variants across 7 locales (En, Vi, ZhHant, Ja, ZhHans, PtBr, Es419), surfacing them in footer status lines and activity-detail labels. tool_activity_label_for_name now accepts a Locale and routes through tr(); family_label intentionally stays English-only for card-header glyphs.

  • localization.rs gains 10 new MessageId variants with complete translations for every shipped locale, including intentional loan-words (\"fanout\", \"rlm\") where no native term exists.
  • tool_card.rs introduces family_message_id(), restricts tool_display_label_for_name to #[cfg(test)], and adds two negative i18n tests verifying non-English locales don't echo English strings.
  • footer_ui.rs and ui.rs thread app.ui_locale through to all Generic-branch call-sites.

Confidence Score: 5/5

Safe to merge — all production paths correctly thread locale through to tr(), and the translation tables are complete for every shipped locale.

The change is additive: new enum variants, new translation entries, and updated call-sites. Every production call-site now passes locale correctly. The only gap is that three MessageIds (Delegate, Think, Generic) are absent from the no-English-leak test, but this does not affect runtime behaviour.

The two new tests in tool_card.rs are worth a second look: ToolFamilyDelegate, ToolFamilyThink, and ToolFamilyGeneric are missing from the tool_family_labels_localized_no_english_leak coverage loop.

Important Files Changed

Filename Overview
crates/tui/src/localization.rs Adds 10 ToolFamily* MessageId variants with translations for all 7 locales; ALL_MESSAGE_IDS updated; English and all non-English locale functions complete.
crates/tui/src/tui/widgets/tool_card.rs Adds family_message_id(), updates tool_activity_label_for_name() to accept a Locale, and gates tool_display_label_for_name to #[cfg(test)]; two new i18n tests cover 7 of 10 MessageIds (Delegate, Think, Generic omitted from leak check).
crates/tui/src/tui/footer_ui.rs Threads app.ui_locale into collect_active_tool_status and passes it to tool_activity_label_for_name for the Generic branch; other branches remain English-only as before.
crates/tui/src/tui/ui.rs Passes app.ui_locale to both activity_cell_label and detail_target_label call-sites for ToolCell::Generic.
.gitignore Adds .claude/ to the ignore list.

Sequence Diagram

sequenceDiagram
    participant App
    participant footer_ui
    participant ui
    participant tool_card
    participant localization

    App->>footer_ui: active_tool_status_label(app)
    footer_ui->>footer_ui: collect_active_tool_status(cell, snapshot, app.ui_locale)
    footer_ui->>tool_card: tool_activity_label_for_name(name, locale)
    tool_card->>tool_card: tool_family_for_name(name)
    tool_card->>tool_card: family_message_id(family)
    tool_card->>localization: tr(locale, mid)
    localization-->>tool_card: "&'static str"
    tool_card-->>footer_ui: String label

    App->>ui: activity_cell_label(app, idx, cell)
    ui->>tool_card: tool_activity_label_for_name(name, app.ui_locale)
    tool_card->>localization: tr(locale, mid)
    localization-->>tool_card: "&'static str"
    tool_card-->>ui: String label
Loading

Comments Outside Diff (2)

  1. crates/tui/src/tui/widgets/tool_card.rs, line 102-111 (link)

    P2 pub function with #[allow(dead_code)] creates an ambiguous API contract

    tool_display_label_for_name is pub but is now only called inside the test module — the production path (tool_activity_label_for_name) was refactored to call tr() directly without delegating to it. Adding #[allow(dead_code)] to a public symbol is unusual; it signals the function has no production callers while still advertising it as part of the module API. A future contributor could call it expecting localized output and get English-only labels. Consider whether the function should be pub(crate) or scoped to #[cfg(test)], or whether it should be removed if it genuinely has no callers outside tests.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

    Fix in Codex Fix in Claude Code Fix in Cursor

  2. crates/tui/src/tui/footer_ui.rs, line 362-396 (link)

    P2 locale is threaded in but not used for several tool cell branches

    collect_active_tool_status now receives locale and correctly passes it to tool_activity_label_for_name for ToolCell::Generic, but the Exploring ("read {…}"), PatchSummary ("patch {…}"), and Mcp ("tool {…}") branches still hardcode the English verb. The Mcp branch in particular uses the identical "tool {name}" pattern that was just localized for the Generic path. In a non-English locale the footer will show mixed-language labels: localized verbs for generic tools, English verbs for MCP tools and patch/explore cells side-by-side in the same status line. This was pre-existing behaviour, but threading locale all the way in makes the remaining gaps visible. Worth a follow-up issue if out of scope here.

    Fix in Codex Fix in Claude Code Fix in Cursor

Fix All in Codex Fix All in Claude Code Fix All in Cursor

Reviews (2): Last reviewed commit: "fixup: make tool_display_label_for_name ..." | Re-trigger Greptile

gordonlu added 2 commits June 8, 2026 17:35
- localization.rs: Add 10 ToolFamily* MessageId variants + ALL_MESSAGE_IDS + all 7 locales
- tool_card.rs: tool_activity_label_for_name() accepts locale, uses tr() for labels
- footer_ui.rs, ui.rs: thread locale to tool_activity_label_for_name() callers
- Tests: 2 negative i18n tests + updated existing tests for new signatures
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

Thanks @gordonlu for taking the time to contribute.

This repository is currently observing a maintainer-managed contribution gate in dry-run mode, so this pull request is staying open. When enforcement is enabled, pull requests from contributors who are not listed in .github/APPROVED_CONTRIBUTORS will be closed automatically.

Please read CONTRIBUTING.md for the expected contribution shape. A maintainer can grant PR access by commenting /lgtm on a pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces localization support for tool family labels (such as read, patch, run, find, etc.) across multiple languages in the TUI. It updates the relevant UI and footer components to pass the current UI locale when resolving tool activity labels, and adds unit tests to verify that localized labels do not leak English values. A review comment suggests simplifying the translation logic in tool_activity_label_for_name by mapping the ToolFamily to its corresponding MessageId first, thereby reducing code duplication.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread crates/tui/src/tui/widgets/tool_card.rs Outdated
Comment on lines +124 to +154
let label = match family {
ToolFamily::Read => {
crate::localization::tr(locale, crate::localization::MessageId::ToolFamilyRead)
}
ToolFamily::Patch => {
crate::localization::tr(locale, crate::localization::MessageId::ToolFamilyPatch)
}
ToolFamily::Run => {
crate::localization::tr(locale, crate::localization::MessageId::ToolFamilyRun)
}
ToolFamily::Find => {
crate::localization::tr(locale, crate::localization::MessageId::ToolFamilyFind)
}
ToolFamily::Delegate => {
crate::localization::tr(locale, crate::localization::MessageId::ToolFamilyDelegate)
}
ToolFamily::Fanout => {
crate::localization::tr(locale, crate::localization::MessageId::ToolFamilyFanout)
}
ToolFamily::Rlm => {
crate::localization::tr(locale, crate::localization::MessageId::ToolFamilyRlm)
}
ToolFamily::Verify => {
crate::localization::tr(locale, crate::localization::MessageId::ToolFamilyVerify)
}
ToolFamily::Think => {
crate::localization::tr(locale, crate::localization::MessageId::ToolFamilyThink)
}
ToolFamily::Generic => unreachable!(),
};
label.to_string()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of calling crate::localization::tr(locale, ...) in every single match arm, you can map the ToolFamily to its corresponding MessageId first, and then perform a single translation call. This significantly reduces code duplication and improves maintainability.

        let message_id = match family {
            ToolFamily::Read => crate::localization::MessageId::ToolFamilyRead,
            ToolFamily::Patch => crate::localization::MessageId::ToolFamilyPatch,
            ToolFamily::Run => crate::localization::MessageId::ToolFamilyRun,
            ToolFamily::Find => crate::localization::MessageId::ToolFamilyFind,
            ToolFamily::Delegate => crate::localization::MessageId::ToolFamilyDelegate,
            ToolFamily::Fanout => crate::localization::MessageId::ToolFamilyFanout,
            ToolFamily::Rlm => crate::localization::MessageId::ToolFamilyRlm,
            ToolFamily::Verify => crate::localization::MessageId::ToolFamilyVerify,
            ToolFamily::Think => crate::localization::MessageId::ToolFamilyThink,
            ToolFamily::Generic => unreachable!(),
        };
        crate::localization::tr(locale, message_id).to_string()

Comment on lines +393 to +434
let checks: &[(MessageId, &str, &str)] = &[
(MessageId::ToolFamilyRead, "read", "đọc,读,読,读取,ler,leer"),
(
MessageId::ToolFamilyPatch,
"patch",
"vá,補,パ,修补,corrigir,parchear",
),
(
MessageId::ToolFamilyRun,
"run",
"chạy,執,実,运行,executar,ejecutar",
),
(
MessageId::ToolFamilyFind,
"find",
"tìm,搜,検,搜索,buscar,buscar",
),
(
MessageId::ToolFamilyVerify,
"verify",
"xác minh,驗,検,验,verificar,verificar",
),
];
for locale in [
Locale::Ja,
Locale::ZhHans,
Locale::ZhHant,
Locale::PtBr,
Locale::Es419,
Locale::Vi,
] {
for (id, eng, _) in checks {
let msg = tr(locale, *id);
assert!(
!msg.eq_ignore_ascii_case(eng),
"{} leaked exact English '{}' for '{:?}': {msg}",
locale.tag(),
eng,
id
);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Expected-translation data is never asserted

The third element of each checks tuple (e.g., "đọc,读,読,读取,ler,leer") is bound to _ and discarded. The test only proves each translation differs from the English baseline (eq_ignore_ascii_case), so a completely incorrect translation — for example, Vietnamese ToolFamilyRead accidentally set to "sai" instead of "đọc" — would pass silently. The third string reads as if it were the expected value to verify, making the intent ambiguous. Consider either dropping the column entirely (it's documentation at best) or asserting against it per locale so the test actually validates correctness.

Fix in Codex Fix in Claude Code Fix in Cursor

@Hmbown

Hmbown commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Thanks @gordonlu — we merged #2891 and #2896 from your i18n batch tonight, which moved localization.rs on main, so this slice now shows as conflicting. Could you rebase it onto current main when you get a chance?

Since the slices all touch localization.rs, the smoothest path is to rebase and push them one at a time, oldest-first (#2892#2901#2918#2919#2921#2926#2929#2932#2940) — we'll merge each promptly so the next rebase stays small. Sorry for the churn; the batch is exactly the right shape and we want all of it.

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