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
113 changes: 113 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,
ConfigEditCancelled,
ConfigEditTitlePrefix,
ConfigEditScopeLabel,
ConfigEditCurrentLabel,
ConfigEditHintLabel,
ConfigEditNewLabel,
ConfigEditFooter,
ConfigRowEffective,
ConfigDefaultValue,
ConfigDefaultReasoning,
ConfigUnavailable,
HelpTitle,
HelpFilterPlaceholder,
HelpFilterPrefix,
Expand Down Expand Up @@ -571,6 +582,17 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[
MessageId::ConfigFooterDefault,
MessageId::ConfigFooterScrollable,
MessageId::ConfigFooterFiltered,
MessageId::ConfigEditCancelled,
MessageId::ConfigEditTitlePrefix,
MessageId::ConfigEditScopeLabel,
MessageId::ConfigEditCurrentLabel,
MessageId::ConfigEditHintLabel,
MessageId::ConfigEditNewLabel,
MessageId::ConfigEditFooter,
MessageId::ConfigRowEffective,
MessageId::ConfigDefaultValue,
MessageId::ConfigDefaultReasoning,
MessageId::ConfigUnavailable,
MessageId::HelpTitle,
MessageId::HelpFilterPlaceholder,
MessageId::HelpFilterPrefix,
Expand Down Expand Up @@ -1081,6 +1103,19 @@ fn english(id: MessageId) -> &'static str {
MessageId::ConfigFooterFiltered => {
" type=filter, Backspace=delete, Ctrl+U/Esc=clear, Enter=edit "
}
MessageId::ConfigEditCancelled => "Edit cancelled",
MessageId::ConfigEditTitlePrefix => "Edit ",
MessageId::ConfigEditScopeLabel => "Scope: ",
MessageId::ConfigEditCurrentLabel => "Current: ",
MessageId::ConfigEditHintLabel => "Hint: ",
MessageId::ConfigEditNewLabel => "New: ",
MessageId::ConfigEditFooter => {
" Enter=apply, Esc=cancel, Ctrl+U=clear, Ctrl+A=all, \u{2190}/\u{2192}=move "
}
MessageId::ConfigRowEffective => " (effective {currency})",
MessageId::ConfigDefaultValue => "(default)",
MessageId::ConfigDefaultReasoning => "(config/default)",
MessageId::ConfigUnavailable => "(unavailable)",
MessageId::HelpTitle => "Help",
MessageId::HelpFilterPlaceholder => "Type to filter",
MessageId::HelpFilterPrefix => "Filter: ",
Expand Down Expand Up @@ -1547,6 +1582,19 @@ 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::ConfigEditCancelled => "Đã hủy chỉnh sửa",
MessageId::ConfigEditTitlePrefix => "Sửa ",
MessageId::ConfigEditScopeLabel => "Phạm vi: ",
MessageId::ConfigEditCurrentLabel => "Hiện tại: ",
MessageId::ConfigEditHintLabel => "Gợi ý: ",
MessageId::ConfigEditNewLabel => "Mới: ",
MessageId::ConfigEditFooter => {
" Enter=áp dụng, Esc=hủy, Ctrl+U=xóa, Ctrl+A=tất cả, \u{2190}/\u{2192}=di chuyển "
}
MessageId::ConfigRowEffective => " (hiệu lực {currency})",
MessageId::ConfigDefaultValue => "(mặc định)",
MessageId::ConfigDefaultReasoning => "(cấu hình/mặc định)",
MessageId::ConfigUnavailable => "(không khả dụng)",
MessageId::HelpTitle => "Trợ giúp",
MessageId::HelpFilterPlaceholder => "Nhập để lọc",
MessageId::HelpFilterPrefix => "Bộ lọc: ",
Expand Down Expand Up @@ -2073,6 +2121,19 @@ fn traditional_chinese(id: MessageId) -> Option<&'static str> {
MessageId::CtxInspCacheTip => {
"提示:穩定前綴區塊符合 DeepSeek V4 前綴快取條件。易變工作集的更改僅會破壞快取尾部。"
}
MessageId::ConfigEditCancelled => "編輯已取消",
MessageId::ConfigEditTitlePrefix => "編輯 ",
MessageId::ConfigEditScopeLabel => "範圍: ",
MessageId::ConfigEditCurrentLabel => "目前: ",
MessageId::ConfigEditHintLabel => "提示: ",
MessageId::ConfigEditNewLabel => "新值: ",
MessageId::ConfigEditFooter => {
" Enter=套用, Esc=取消, Ctrl+U=清除, Ctrl+A=全選, \u{2190}/\u{2192}=移動 "
}
MessageId::ConfigRowEffective => " (實際 {currency})",
MessageId::ConfigDefaultValue => "(預設)",
MessageId::ConfigDefaultReasoning => "(設定/預設)",
MessageId::ConfigUnavailable => "(無法使用)",
other => chinese_simplified(other)?,
})
}
Expand Down Expand Up @@ -2102,6 +2163,19 @@ fn japanese(id: MessageId) -> Option<&'static str> {
MessageId::ConfigFooterFiltered => {
" 入力=絞り込み, Backspace=削除, Ctrl+U/Esc=クリア, Enter=編集 "
}
MessageId::ConfigEditCancelled => "編集をキャンセルしました",
MessageId::ConfigEditTitlePrefix => "編集 ",
MessageId::ConfigEditScopeLabel => "スコープ: ",
MessageId::ConfigEditCurrentLabel => "現在: ",
MessageId::ConfigEditHintLabel => "ヒント: ",
MessageId::ConfigEditNewLabel => "新規: ",
MessageId::ConfigEditFooter => {
" Enter=適用, Esc=キャンセル, Ctrl+U=クリア, Ctrl+A=全選択, \u{2190}/\u{2192}=移動 "
}
MessageId::ConfigRowEffective => " (実効 {currency})",
MessageId::ConfigDefaultValue => "(デフォルト)",
MessageId::ConfigDefaultReasoning => "(設定/デフォルト)",
MessageId::ConfigUnavailable => "(利用不可)",
MessageId::HelpTitle => "ヘルプ",
MessageId::HelpFilterPlaceholder => "入力して絞り込み",
MessageId::HelpFilterPrefix => "絞り込み: ",
Expand Down Expand Up @@ -2562,6 +2636,19 @@ fn chinese_simplified(id: MessageId) -> Option<&'static str> {
MessageId::ConfigFooterFiltered => {
" 输入=筛选, Backspace=删除, Ctrl+U/Esc=清除, Enter=编辑 "
}
MessageId::ConfigEditCancelled => "编辑已取消",
MessageId::ConfigEditTitlePrefix => "编辑 ",
MessageId::ConfigEditScopeLabel => "范围: ",
MessageId::ConfigEditCurrentLabel => "当前: ",
MessageId::ConfigEditHintLabel => "提示: ",
MessageId::ConfigEditNewLabel => "新值: ",
MessageId::ConfigEditFooter => {
" Enter=应用, Esc=取消, Ctrl+U=清除, Ctrl+A=全选, \u{2190}/\u{2192}=移动 "
}
MessageId::ConfigRowEffective => " (实际 {currency})",
MessageId::ConfigDefaultValue => "(默认)",
MessageId::ConfigDefaultReasoning => "(配置/默认)",
MessageId::ConfigUnavailable => "(不可用)",
MessageId::HelpTitle => "帮助",
MessageId::HelpFilterPlaceholder => "输入以筛选",
MessageId::HelpFilterPrefix => "筛选: ",
Expand Down Expand Up @@ -2968,6 +3055,19 @@ fn portuguese_brazil(id: MessageId) -> Option<&'static str> {
MessageId::ConfigFooterFiltered => {
" digite=filtrar, Backspace=apagar, Ctrl+U/Esc=limpar, Enter=editar "
}
MessageId::ConfigEditCancelled => "Edição cancelada",
MessageId::ConfigEditTitlePrefix => "Editar ",
MessageId::ConfigEditScopeLabel => "Escopo: ",
MessageId::ConfigEditCurrentLabel => "Atual: ",
MessageId::ConfigEditHintLabel => "Dica: ",
MessageId::ConfigEditNewLabel => "Novo: ",
MessageId::ConfigEditFooter => {
" Enter=aplicar, Esc=cancelar, Ctrl+U=limpar, Ctrl+A=tudo, \u{2190}/\u{2192}=mover "
}
MessageId::ConfigRowEffective => " (efetivo {currency})",
MessageId::ConfigDefaultValue => "(padrão)",
MessageId::ConfigDefaultReasoning => "(config/padrão)",
MessageId::ConfigUnavailable => "(indisponível)",
MessageId::HelpTitle => "Ajuda",
MessageId::HelpFilterPlaceholder => "Digite para filtrar",
MessageId::HelpFilterPrefix => "Filtro: ",
Expand Down Expand Up @@ -3454,6 +3554,19 @@ fn spanish_latin_america(id: MessageId) -> Option<&'static str> {
MessageId::ConfigFooterFiltered => {
" escribir=filtrar, Backspace=borrar, Ctrl+U/Esc=limpiar, Enter=editar "
}
MessageId::ConfigEditCancelled => "Edición cancelada",
MessageId::ConfigEditTitlePrefix => "Editar ",
MessageId::ConfigEditScopeLabel => "Ámbito: ",
MessageId::ConfigEditCurrentLabel => "Actual: ",
MessageId::ConfigEditHintLabel => "Pista: ",
MessageId::ConfigEditNewLabel => "Nuevo: ",
MessageId::ConfigEditFooter => {
" Enter=aplicar, Esc=cancelar, Ctrl+U=limpiar, Ctrl+A=todo, \u{2190}/\u{2192}=mover "
}
MessageId::ConfigRowEffective => " (efectivo {currency})",
MessageId::ConfigDefaultValue => "(predeterminado)",
MessageId::ConfigDefaultReasoning => "(config/predeterminado)",
MessageId::ConfigUnavailable => "(no disponible)",
MessageId::HelpTitle => "Ayuda",
MessageId::HelpFilterPlaceholder => "Escribe para filtrar",
MessageId::HelpFilterPrefix => "Filtro: ",
Expand Down
67 changes: 43 additions & 24 deletions crates/tui/src/tui/views/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ impl ConfigView {
value: settings
.default_model
.as_deref()
.unwrap_or("(default)")
.unwrap_or(tr(app.ui_locale, MessageId::ConfigDefaultValue))
.to_string(),
editable: true,
scope: ConfigScope::Saved,
Expand All @@ -634,7 +634,7 @@ impl ConfigView {
value: settings
.reasoning_effort
.as_deref()
.unwrap_or("(config/default)")
.unwrap_or(tr(app.ui_locale, MessageId::ConfigDefaultReasoning))
.to_string(),
editable: true,
scope: ConfigScope::Saved,
Expand Down Expand Up @@ -684,10 +684,16 @@ impl ConfigView {
ConfigRow {
section: ConfigSection::Display,
key: "background_color".to_string(),
value: settings
.background_color
.clone()
.unwrap_or_else(|| "(default)".to_string()),
value: settings.background_color.clone().unwrap_or_else(|| {
tr(app.ui_locale, MessageId::ConfigDefaultValue).to_string()
}),
editable: true,
scope: ConfigScope::Saved,
},
ConfigRow {
section: ConfigSection::Display,
key: "fancy_animations".to_string(),
value: settings.fancy_animations.to_string(),
editable: true,
scope: ConfigScope::Saved,
},
Expand Down Expand Up @@ -1061,7 +1067,7 @@ impl ConfigView {
match key.code {
KeyCode::Esc => {
self.editing = None;
self.status = Some("Edit cancelled".to_string());
self.status = Some(self.tr(MessageId::ConfigEditCancelled).to_string());
ViewAction::None
}
KeyCode::Enter => {
Expand Down Expand Up @@ -1213,7 +1219,12 @@ impl ConfigView {
let effective_cost_currency =
crate::pricing::CostCurrency::from_setting(&self.effective_cost_currency);
if saved_cost_currency != effective_cost_currency {
return format!("{} (effective {})", row.value, self.effective_cost_currency);
return format!(
"{}{}",
row.value,
self.tr(MessageId::ConfigRowEffective)
.replace("{currency}", &self.effective_cost_currency)
);
}
}

Expand All @@ -1235,7 +1246,7 @@ fn config_base_url_row_value(app: &App) -> String {
config.provider = Some(app.api_provider.as_str().to_string());
config.deepseek_base_url()
})
.unwrap_or_else(|_| "(unavailable)".to_string())
.unwrap_or_else(|_| tr(app.ui_locale, MessageId::ConfigUnavailable).to_string())
}

fn cost_currency_config_value(app: &App) -> String {
Expand Down Expand Up @@ -1281,18 +1292,20 @@ fn config_hint_for_key(key: &str) -> &'static str {
}
}

fn render_config_editor_value_line(edit: &ConfigEdit) -> ratatui::text::Line<'static> {
fn render_config_editor_value_line(
edit: &ConfigEdit,
locale: Locale,
) -> ratatui::text::Line<'static> {
use ratatui::{
style::Style,
text::{Line, Span},
};

let mut spans = Vec::new();
spans.push(Span::styled(
"New: ",
tr(locale, MessageId::ConfigEditNewLabel),
Style::default().fg(palette::TEXT_MUTED),
));

let cursor_style = Style::default()
.fg(palette::DEEPSEEK_INK)
.bg(palette::DEEPSEEK_SKY)
Expand Down Expand Up @@ -1478,33 +1491,38 @@ impl ModalView for ConfigView {
let (lines, footer) = if let Some(edit) = self.editing.as_ref() {
let mut lines: Vec<Line> = Vec::new();
lines.push(Line::from(vec![Span::styled(
format!("Edit {}", edit.key),
format!("{}{}", self.tr(MessageId::ConfigEditTitlePrefix), edit.key),
Style::default().fg(palette::DEEPSEEK_SKY).bold(),
)]));
lines.push(Line::from(""));
lines.push(Line::from(vec![
Span::styled("Scope: ", Style::default().fg(palette::TEXT_MUTED)),
Span::styled(
self.tr(MessageId::ConfigEditScopeLabel),
Style::default().fg(palette::TEXT_MUTED),
),
Span::raw(edit.scope.label()),
]));
lines.push(Line::from(vec![
Span::styled("Current: ", Style::default().fg(palette::TEXT_MUTED)),
Span::styled(
self.tr(MessageId::ConfigEditCurrentLabel),
Style::default().fg(palette::TEXT_MUTED),
),
Span::raw(truncate_view_text(&edit.original_value, 60)),
]));
lines.push(Line::from(""));
lines.push(render_config_editor_value_line(edit));
lines.push(render_config_editor_value_line(edit, self.locale));
lines.push(Line::from(""));
let hint = config_hint_for_key(&edit.key);
if !hint.is_empty() {
lines.push(Line::from(vec![
Span::styled("Hint: ", Style::default().fg(palette::TEXT_MUTED)),
Span::styled(
self.tr(MessageId::ConfigEditHintLabel),
Style::default().fg(palette::TEXT_MUTED),
),
Span::raw(hint),
]));
}
(
lines,
" Enter=apply, Esc=cancel, Ctrl+U=clear, Ctrl+A=all, \u{2190}/\u{2192}=move "
.to_string(),
)
(lines, self.tr(MessageId::ConfigEditFooter).to_string())
} else {
let content_height = usize::from(inner.height);
let header_lines = 5usize;
Expand Down Expand Up @@ -2522,7 +2540,7 @@ base_url = "https://api.xiaomimimo.com/v1"
.expect("cost_currency row");

assert_eq!(row.value, "usd");
assert_eq!(view.row_display_value(row), "usd (effective cny)");
assert_eq!(view.row_display_value(row), "usd (实际 cny)");
assert_eq!(Settings::load().expect("settings").cost_currency, "usd");
}

Expand Down Expand Up @@ -2800,7 +2818,8 @@ base_url = "https://api.xiaomimimo.com/v1"

#[test]
fn config_view_escape_cancels_editing() {
let app = create_test_app();
let mut app = create_test_app();
app.ui_locale = Locale::En;
let mut view = ConfigView::new_for_app(&app);
let _ = view.handle_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
assert!(view.editing.is_some());
Expand Down
Loading