From c330fa110f9375574bce2509270c39fae4b0f1a2 Mon Sep 17 00:00:00 2001 From: gordonlu Date: Tue, 9 Jun 2026 09:06:42 +0800 Subject: [PATCH 1/2] feat(i18n): localize ConfigSection and ConfigScope labels (11 MessageIds) --- crates/tui/src/localization.rs | 99 +++++++++++++++++++++++++++++++++ crates/tui/src/tui/views/mod.rs | 90 +++++++++++++++++------------- 2 files changed, 149 insertions(+), 40 deletions(-) diff --git a/crates/tui/src/localization.rs b/crates/tui/src/localization.rs index 28132c50d..8bcf21a29 100644 --- a/crates/tui/src/localization.rs +++ b/crates/tui/src/localization.rs @@ -238,6 +238,17 @@ pub enum MessageId { ConfigFooterDefault, ConfigFooterScrollable, ConfigFooterFiltered, + ConfigSectionProvider, + ConfigSectionModel, + ConfigSectionPermissions, + ConfigSectionNetwork, + ConfigSectionDisplay, + ConfigSectionComposer, + ConfigSectionSidebar, + ConfigSectionHistory, + ConfigSectionMcp, + ConfigScopeSession, + ConfigScopeSaved, HelpTitle, HelpFilterPlaceholder, HelpFilterPrefix, @@ -571,6 +582,17 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[ MessageId::ConfigFooterDefault, MessageId::ConfigFooterScrollable, MessageId::ConfigFooterFiltered, + MessageId::ConfigSectionProvider, + MessageId::ConfigSectionModel, + MessageId::ConfigSectionPermissions, + MessageId::ConfigSectionNetwork, + MessageId::ConfigSectionDisplay, + MessageId::ConfigSectionComposer, + MessageId::ConfigSectionSidebar, + MessageId::ConfigSectionHistory, + MessageId::ConfigSectionMcp, + MessageId::ConfigScopeSession, + MessageId::ConfigScopeSaved, MessageId::HelpTitle, MessageId::HelpFilterPlaceholder, MessageId::HelpFilterPrefix, @@ -1081,6 +1103,17 @@ fn english(id: MessageId) -> &'static str { MessageId::ConfigFooterFiltered => { " type=filter, Backspace=delete, Ctrl+U/Esc=clear, Enter=edit " } + MessageId::ConfigSectionProvider => "Provider", + MessageId::ConfigSectionModel => "Model", + MessageId::ConfigSectionPermissions => "Permissions", + MessageId::ConfigSectionNetwork => "Network", + MessageId::ConfigSectionDisplay => "Display", + MessageId::ConfigSectionComposer => "Composer", + MessageId::ConfigSectionSidebar => "Sidebar", + MessageId::ConfigSectionHistory => "History", + MessageId::ConfigSectionMcp => "MCP", + MessageId::ConfigScopeSession => "SESSION", + MessageId::ConfigScopeSaved => "SAVED", MessageId::HelpTitle => "Help", MessageId::HelpFilterPlaceholder => "Type to filter", MessageId::HelpFilterPrefix => "Filter: ", @@ -1547,6 +1580,17 @@ fn vietnamese(id: MessageId) -> Option<&'static str> { MessageId::ConfigFooterFiltered => { " gõ=lọc, Backspace=xóa, Ctrl+U/Esc=xóa sạch, Enter=sửa " } + MessageId::ConfigSectionProvider => "Nhà cung cấp", + MessageId::ConfigSectionModel => "Mô hình", + MessageId::ConfigSectionPermissions => "Quyền hạn", + MessageId::ConfigSectionNetwork => "Mạng", + MessageId::ConfigSectionDisplay => "Hiển thị", + MessageId::ConfigSectionComposer => "Soạn thảo", + MessageId::ConfigSectionSidebar => "Thanh bên", + MessageId::ConfigSectionHistory => "Lịch sử", + MessageId::ConfigSectionMcp => "MCP", + MessageId::ConfigScopeSession => "PHIÊN", + MessageId::ConfigScopeSaved => "ĐÃ LƯU", MessageId::HelpTitle => "Trợ giúp", MessageId::HelpFilterPlaceholder => "Nhập để lọc", MessageId::HelpFilterPrefix => "Bộ lọc: ", @@ -2073,6 +2117,17 @@ fn traditional_chinese(id: MessageId) -> Option<&'static str> { MessageId::CtxInspCacheTip => { "提示:穩定前綴區塊符合 DeepSeek V4 前綴快取條件。易變工作集的更改僅會破壞快取尾部。" } + MessageId::ConfigSectionProvider => "提供商", + MessageId::ConfigSectionModel => "模型", + MessageId::ConfigSectionPermissions => "權限", + MessageId::ConfigSectionNetwork => "網路", + MessageId::ConfigSectionDisplay => "顯示", + MessageId::ConfigSectionComposer => "編輯器", + MessageId::ConfigSectionSidebar => "側邊欄", + MessageId::ConfigSectionHistory => "歷史", + MessageId::ConfigSectionMcp => "MCP", + MessageId::ConfigScopeSession => "會話", + MessageId::ConfigScopeSaved => "已儲存", other => chinese_simplified(other)?, }) } @@ -2102,6 +2157,17 @@ fn japanese(id: MessageId) -> Option<&'static str> { MessageId::ConfigFooterFiltered => { " 入力=絞り込み, Backspace=削除, Ctrl+U/Esc=クリア, Enter=編集 " } + MessageId::ConfigSectionProvider => "プロバイダ", + MessageId::ConfigSectionModel => "モデル", + MessageId::ConfigSectionPermissions => "権限", + MessageId::ConfigSectionNetwork => "ネットワーク", + MessageId::ConfigSectionDisplay => "表示", + MessageId::ConfigSectionComposer => "コンポーザー", + MessageId::ConfigSectionSidebar => "サイドバー", + MessageId::ConfigSectionHistory => "履歴", + MessageId::ConfigSectionMcp => "MCP", + MessageId::ConfigScopeSession => "セッション", + MessageId::ConfigScopeSaved => "保存済み", MessageId::HelpTitle => "ヘルプ", MessageId::HelpFilterPlaceholder => "入力して絞り込み", MessageId::HelpFilterPrefix => "絞り込み: ", @@ -2562,6 +2628,17 @@ fn chinese_simplified(id: MessageId) -> Option<&'static str> { MessageId::ConfigFooterFiltered => { " 输入=筛选, Backspace=删除, Ctrl+U/Esc=清除, Enter=编辑 " } + MessageId::ConfigSectionProvider => "提供商", + MessageId::ConfigSectionModel => "模型", + MessageId::ConfigSectionPermissions => "权限", + MessageId::ConfigSectionNetwork => "网络", + MessageId::ConfigSectionDisplay => "显示", + MessageId::ConfigSectionComposer => "编辑器", + MessageId::ConfigSectionSidebar => "侧边栏", + MessageId::ConfigSectionHistory => "历史", + MessageId::ConfigSectionMcp => "MCP", + MessageId::ConfigScopeSession => "会话", + MessageId::ConfigScopeSaved => "已保存", MessageId::HelpTitle => "帮助", MessageId::HelpFilterPlaceholder => "输入以筛选", MessageId::HelpFilterPrefix => "筛选: ", @@ -2968,6 +3045,17 @@ fn portuguese_brazil(id: MessageId) -> Option<&'static str> { MessageId::ConfigFooterFiltered => { " digite=filtrar, Backspace=apagar, Ctrl+U/Esc=limpar, Enter=editar " } + MessageId::ConfigSectionProvider => "Provedor", + MessageId::ConfigSectionModel => "Modelo", + MessageId::ConfigSectionPermissions => "Permissões", + MessageId::ConfigSectionNetwork => "Rede", + MessageId::ConfigSectionDisplay => "Exibição", + MessageId::ConfigSectionComposer => "Compositor", + MessageId::ConfigSectionSidebar => "Barra lateral", + MessageId::ConfigSectionHistory => "Histórico", + MessageId::ConfigSectionMcp => "MCP", + MessageId::ConfigScopeSession => "SESSÃO", + MessageId::ConfigScopeSaved => "SALVO", MessageId::HelpTitle => "Ajuda", MessageId::HelpFilterPlaceholder => "Digite para filtrar", MessageId::HelpFilterPrefix => "Filtro: ", @@ -3454,6 +3542,17 @@ fn spanish_latin_america(id: MessageId) -> Option<&'static str> { MessageId::ConfigFooterFiltered => { " escribir=filtrar, Backspace=borrar, Ctrl+U/Esc=limpiar, Enter=editar " } + MessageId::ConfigSectionProvider => "Proveedor", + MessageId::ConfigSectionModel => "Modelo", + MessageId::ConfigSectionPermissions => "Permisos", + MessageId::ConfigSectionNetwork => "Red", + MessageId::ConfigSectionDisplay => "Pantalla", + MessageId::ConfigSectionComposer => "Compositor", + MessageId::ConfigSectionSidebar => "Barra lateral", + MessageId::ConfigSectionHistory => "Historial", + MessageId::ConfigSectionMcp => "MCP", + MessageId::ConfigScopeSession => "SESIÓN", + MessageId::ConfigScopeSaved => "GUARDADO", MessageId::HelpTitle => "Ayuda", MessageId::HelpFilterPlaceholder => "Escribe para filtrar", MessageId::HelpFilterPrefix => "Filtro: ", diff --git a/crates/tui/src/tui/views/mod.rs b/crates/tui/src/tui/views/mod.rs index d69ccf3ff..d44830dfe 100644 --- a/crates/tui/src/tui/views/mod.rs +++ b/crates/tui/src/tui/views/mod.rs @@ -506,11 +506,14 @@ enum ConfigScope { } impl ConfigScope { - fn label(self) -> &'static str { - match self { - ConfigScope::Session => "SESSION", - ConfigScope::Saved => "SAVED", - } + fn label(self, locale: Locale) -> &'static str { + tr( + locale, + match self { + ConfigScope::Session => MessageId::ConfigScopeSession, + ConfigScope::Saved => MessageId::ConfigScopeSaved, + }, + ) } fn persist(self) -> bool { @@ -541,18 +544,21 @@ enum ConfigSection { } impl ConfigSection { - fn label(self) -> &'static str { - match self { - ConfigSection::Provider => "Provider", - ConfigSection::Model => "Model", - ConfigSection::Permissions => "Permissions", - ConfigSection::Network => "Network", - ConfigSection::Display => "Display", - ConfigSection::Composer => "Composer", - ConfigSection::Sidebar => "Sidebar", - ConfigSection::History => "History", - ConfigSection::Mcp => "MCP", - } + fn label(self, locale: Locale) -> &'static str { + tr( + locale, + match self { + ConfigSection::Provider => MessageId::ConfigSectionProvider, + ConfigSection::Model => MessageId::ConfigSectionModel, + ConfigSection::Permissions => MessageId::ConfigSectionPermissions, + ConfigSection::Network => MessageId::ConfigSectionNetwork, + ConfigSection::Display => MessageId::ConfigSectionDisplay, + ConfigSection::Composer => MessageId::ConfigSectionComposer, + ConfigSection::Sidebar => MessageId::ConfigSectionSidebar, + ConfigSection::History => MessageId::ConfigSectionHistory, + ConfigSection::Mcp => MessageId::ConfigSectionMcp, + }, + ) } } @@ -904,10 +910,10 @@ impl ConfigView { return true; } - let section = row.section.label().to_lowercase(); + let section = row.section.label(self.locale).to_lowercase(); let key = row.key.to_lowercase(); let value = self.row_display_value(row).to_lowercase(); - let scope = row.scope.label().to_lowercase(); + let scope = row.scope.label(self.locale).to_lowercase(); filter.split_whitespace().all(|term| { section.contains(term) @@ -1484,7 +1490,7 @@ impl ModalView for ConfigView { lines.push(Line::from("")); lines.push(Line::from(vec![ Span::styled("Scope: ", Style::default().fg(palette::TEXT_MUTED)), - Span::raw(edit.scope.label()), + Span::raw(edit.scope.label(self.locale)), ])); lines.push(Line::from(vec![ Span::styled("Current: ", Style::default().fg(palette::TEXT_MUTED)), @@ -1566,7 +1572,7 @@ impl ModalView for ConfigView { match item { ConfigListItem::Section(section) => { lines.push(Line::from(Span::styled( - format!(" {}", section.label()), + format!(" {}", section.label(self.locale)), Style::default().fg(palette::DEEPSEEK_SKY).bold(), ))); } @@ -1588,7 +1594,8 @@ impl ModalView for ConfigView { let key = truncate_view_text(&row.key, key_column_width); let value = truncate_view_text(&self.row_display_value(row), value_column_width); - let scope = truncate_view_text(row.scope.label(), scope_column_width); + let scope = + truncate_view_text(row.scope.label(self.locale), scope_column_width); let mut line = Line::from(format!( " {: String { #[cfg(test)] mod tests { use super::{ - ConfigListItem, ConfigSection, ConfigView, ModalKind, ModalView, ShellControlView, - ViewAction, ViewEvent, ViewStack, subagent_view_agents, truncate_view_text, + ConfigListItem, ConfigView, ModalKind, ModalView, ShellControlView, ViewAction, ViewEvent, + ViewStack, subagent_view_agents, truncate_view_text, }; use crate::config::Config; use crate::localization::Locale; @@ -2367,12 +2374,18 @@ mod tests { view.visible_items() .into_iter() .filter_map(|item| match item { - ConfigListItem::Section(section) => Some(section.label()), + ConfigListItem::Section(section) => Some(section.label(view.locale)), ConfigListItem::Row(_) => None, }) .collect() } + fn create_config_view(locale: Locale) -> ConfigView { + let mut app = create_test_app(); + app.ui_locale = locale; + ConfigView::new_for_app(&app) + } + fn visible_row_keys(view: &ConfigView) -> Vec<&str> { view.visible_items() .into_iter() @@ -2395,20 +2408,19 @@ mod tests { #[test] fn config_view_groups_rows_by_expected_sections() { - let app = create_test_app(); - let view = ConfigView::new_for_app(&app); + let view = create_config_view(Locale::En); assert_eq!( visible_section_labels(&view), vec![ - ConfigSection::Provider.label(), - ConfigSection::Model.label(), - ConfigSection::Permissions.label(), - ConfigSection::Network.label(), - ConfigSection::Display.label(), - ConfigSection::Composer.label(), - ConfigSection::Sidebar.label(), - ConfigSection::History.label(), - ConfigSection::Mcp.label(), + "Provider", + "Model", + "Permissions", + "Network", + "Display", + "Composer", + "Sidebar", + "History", + "MCP", ] ); } @@ -2577,8 +2589,7 @@ base_url = "https://api.xiaomimimo.com/v1" #[test] fn config_view_filter_matches_group_and_rows() { - let app = create_test_app(); - let mut view = ConfigView::new_for_app(&app); + let mut view = create_config_view(Locale::En); type_filter(&mut view, "side"); @@ -2628,8 +2639,7 @@ base_url = "https://api.xiaomimimo.com/v1" #[test] fn config_view_keeps_scope_column_aligned_for_long_keys() { - let app = create_test_app(); - let mut view = ConfigView::new_for_app(&app); + let mut view = create_config_view(Locale::En); type_filter(&mut view, "composer"); let area = Rect::new(0, 0, 100, 24); let mut buf = Buffer::empty(area); From d3bea729b7e851c9ccd81f47157c50420909f792 Mon Sep 17 00:00:00 2001 From: gordonlu Date: Tue, 9 Jun 2026 09:29:39 +0800 Subject: [PATCH 2/2] fixup: match both localized and English labels in config filter --- crates/tui/src/tui/views/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/tui/src/tui/views/mod.rs b/crates/tui/src/tui/views/mod.rs index d44830dfe..2144631f7 100644 --- a/crates/tui/src/tui/views/mod.rs +++ b/crates/tui/src/tui/views/mod.rs @@ -911,15 +911,19 @@ impl ConfigView { } let section = row.section.label(self.locale).to_lowercase(); + let section_en = row.section.label(Locale::En).to_lowercase(); let key = row.key.to_lowercase(); let value = self.row_display_value(row).to_lowercase(); let scope = row.scope.label(self.locale).to_lowercase(); + let scope_en = row.scope.label(Locale::En).to_lowercase(); filter.split_whitespace().all(|term| { section.contains(term) + || section_en.contains(term) || key.contains(term) || value.contains(term) || scope.contains(term) + || scope_en.contains(term) }) }