Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions crates/tui/src/localization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,17 @@ pub enum MessageId {
ConfigFooterDefault,
ConfigFooterScrollable,
ConfigFooterFiltered,
ConfigSectionProvider,
ConfigSectionModel,
ConfigSectionPermissions,
ConfigSectionNetwork,
ConfigSectionDisplay,
ConfigSectionComposer,
ConfigSectionSidebar,
ConfigSectionHistory,
ConfigSectionMcp,
ConfigScopeSession,
ConfigScopeSaved,
HelpTitle,
HelpFilterPlaceholder,
HelpFilterPrefix,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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: ",
Expand Down Expand Up @@ -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: ",
Expand Down Expand Up @@ -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)?,
})
}
Expand Down Expand Up @@ -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 => "絞り込み: ",
Expand Down Expand Up @@ -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 => "筛选: ",
Expand Down Expand Up @@ -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: ",
Expand Down Expand Up @@ -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: ",
Expand Down
94 changes: 54 additions & 40 deletions crates/tui/src/tui/views/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
},
)
}
}

Expand Down Expand Up @@ -904,16 +910,20 @@ impl ConfigView {
return true;
}

let section = row.section.label().to_lowercase();
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().to_lowercase();
let scope = row.scope.label(self.locale).to_lowercase();
Comment on lines +913 to +917

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

When using a non-English locale, filtering the configuration view using English terms (such as "display" or "session") will no longer match because the section and scope labels are fully localized. Since these are common technical terms, it is highly beneficial to allow filtering by both the localized labels and their English/canonical equivalents.

We can achieve this elegantly by formatting the search strings to include both the localized and English labels.

        let section = format!("{} {}", row.section.label(self.locale), row.section.label(Locale::En)).to_lowercase();
        let key = row.key.to_lowercase();
        let value = self.row_display_value(row).to_lowercase();
        let scope = format!("{} {}", row.scope.label(self.locale), row.scope.label(Locale::En)).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)
})
}

Expand Down Expand Up @@ -1484,7 +1494,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)),
Expand Down Expand Up @@ -1566,7 +1576,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(),
)));
}
Expand All @@ -1588,7 +1598,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!(
" {:<key_width$} {:<value_width$} {:<scope_width$}",
key,
Expand Down Expand Up @@ -2174,8 +2185,8 @@ fn truncate_view_text(text: &str, max_chars: usize) -> 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;
Expand Down Expand Up @@ -2367,12 +2378,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()
Expand All @@ -2395,20 +2412,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",
]
);
}
Expand Down Expand Up @@ -2577,8 +2593,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");

Expand Down Expand Up @@ -2628,8 +2643,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);
Expand Down
Loading