feat(i18n): localize approval dialog surface across 7 locales

This commit is contained in:
gordonlu
2026-06-08 09:34:58 +08:00
parent 8dff2f7525
commit 7bdc9a8f96
2 changed files with 265 additions and 80 deletions
+227 -2
View File
@@ -503,6 +503,31 @@ pub enum MessageId {
// Agent fanout card.
FanoutCounts,
// Approval dialog — risk badges, category labels, field labels, options.
ApprovalRiskReview,
ApprovalRiskDestructive,
ApprovalCategorySafe,
ApprovalCategoryFileWrite,
ApprovalCategoryShell,
ApprovalCategoryNetwork,
ApprovalCategoryMcpRead,
ApprovalCategoryMcpAction,
ApprovalCategoryUnknown,
ApprovalFieldType,
ApprovalFieldAbout,
ApprovalFieldImpact,
ApprovalFieldParams,
ApprovalOptionApproveOnce,
ApprovalOptionApproveAlways,
ApprovalOptionDeny,
ApprovalOptionAbortTurn,
ApprovalBlockTitle,
ApprovalControlsHint,
ApprovalChooseHint,
ApprovalChooseAction,
ApprovalIntentLabel,
ApprovalMoreLines,
CtxInspTitle,
CtxInspSessionContext,
CtxInspSystemPrompt,
@@ -825,6 +850,29 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[
MessageId::CtxMenuHelp,
MessageId::CtxMenuHelpDesc,
MessageId::FanoutCounts,
MessageId::ApprovalRiskReview,
MessageId::ApprovalRiskDestructive,
MessageId::ApprovalCategorySafe,
MessageId::ApprovalCategoryFileWrite,
MessageId::ApprovalCategoryShell,
MessageId::ApprovalCategoryNetwork,
MessageId::ApprovalCategoryMcpRead,
MessageId::ApprovalCategoryMcpAction,
MessageId::ApprovalCategoryUnknown,
MessageId::ApprovalFieldType,
MessageId::ApprovalFieldAbout,
MessageId::ApprovalFieldImpact,
MessageId::ApprovalFieldParams,
MessageId::ApprovalOptionApproveOnce,
MessageId::ApprovalOptionApproveAlways,
MessageId::ApprovalOptionDeny,
MessageId::ApprovalOptionAbortTurn,
MessageId::ApprovalBlockTitle,
MessageId::ApprovalControlsHint,
MessageId::ApprovalChooseHint,
MessageId::ApprovalChooseAction,
MessageId::ApprovalIntentLabel,
MessageId::ApprovalMoreLines,
MessageId::CtxInspTitle,
MessageId::CtxInspSessionContext,
MessageId::CtxInspSystemPrompt,
@@ -1455,6 +1503,33 @@ fn english(id: MessageId) -> &'static str {
"{done} done · {running} running · {failed} failed · {pending} pending"
}
// Approval dialog.
MessageId::ApprovalRiskReview => "REVIEW",
MessageId::ApprovalRiskDestructive => "DESTRUCTIVE",
MessageId::ApprovalCategorySafe => "Safe",
MessageId::ApprovalCategoryFileWrite => "File Write",
MessageId::ApprovalCategoryShell => "Shell Command",
MessageId::ApprovalCategoryNetwork => "Network",
MessageId::ApprovalCategoryMcpRead => "MCP Read",
MessageId::ApprovalCategoryMcpAction => "MCP Action",
MessageId::ApprovalCategoryUnknown => "Unknown",
MessageId::ApprovalFieldType => "Type:",
MessageId::ApprovalFieldAbout => "About:",
MessageId::ApprovalFieldImpact => "Impact:",
MessageId::ApprovalFieldParams => "Params:",
MessageId::ApprovalOptionApproveOnce => "Approve once",
MessageId::ApprovalOptionApproveAlways => "Approve always for this kind",
MessageId::ApprovalOptionDeny => "Deny this call",
MessageId::ApprovalOptionAbortTurn => "Abort the turn",
MessageId::ApprovalBlockTitle => "approval",
MessageId::ApprovalControlsHint => " · v: full params · Esc: abort",
MessageId::ApprovalChooseHint => "Choose: ",
MessageId::ApprovalChooseAction => {
"Enter selected option, or press y/a/d directly"
}
MessageId::ApprovalIntentLabel => "Intent: ",
MessageId::ApprovalMoreLines => " … (+{count} lines)",
MessageId::CtxInspTitle => "Context inspector",
MessageId::CtxInspSessionContext => "Session Context",
MessageId::CtxInspSystemPrompt => "System Prompt Structure",
@@ -1954,6 +2029,31 @@ fn vietnamese(id: MessageId) -> Option<&'static str> {
"{done} hoàn thành · {running} đang chạy · {failed} thất bại · {pending} chờ"
}
// Approval dialog.
MessageId::ApprovalRiskReview => "XEM XÉT",
MessageId::ApprovalRiskDestructive => "NGUY HẠI",
MessageId::ApprovalCategorySafe => "An toàn",
MessageId::ApprovalCategoryFileWrite => "Ghi Tệp",
MessageId::ApprovalCategoryShell => "Lệnh Shell",
MessageId::ApprovalCategoryNetwork => "Mạng",
MessageId::ApprovalCategoryMcpRead => "Đọc MCP",
MessageId::ApprovalCategoryMcpAction => "Hành động MCP",
MessageId::ApprovalCategoryUnknown => "Không xác định",
MessageId::ApprovalFieldType => "Loại:",
MessageId::ApprovalFieldAbout => "Mô tả:",
MessageId::ApprovalFieldImpact => "Tác động:",
MessageId::ApprovalFieldParams => "Tham số:",
MessageId::ApprovalOptionApproveOnce => "Phê duyệt một lần",
MessageId::ApprovalOptionApproveAlways => "Phê duyệt mọi lần cho loại này",
MessageId::ApprovalOptionDeny => "Từ chối lần gọi này",
MessageId::ApprovalOptionAbortTurn => "Hủy bỏ lượt",
MessageId::ApprovalBlockTitle => "phê duyệt",
MessageId::ApprovalControlsHint => " · v: tham số · Esc: hủy bỏ",
MessageId::ApprovalChooseHint => "Chọn: ",
MessageId::ApprovalChooseAction => "Enter để chọn, hoặc nhấn y/a/d trực tiếp",
MessageId::ApprovalIntentLabel => "Ý định: ",
MessageId::ApprovalMoreLines => " … (+{count} dòng)",
MessageId::CtxInspTitle => "Trình kiểm tra ngữ cảnh",
MessageId::CtxInspSessionContext => "Ngữ cảnh phiên",
MessageId::CtxInspSystemPrompt => "Cấu trúc lời nhắc hệ thống",
@@ -2018,6 +2118,31 @@ fn traditional_chinese(id: MessageId) -> Option<&'static str> {
"{done} 已完成 · {running} 運行中 · {failed} 失敗 · {pending} 等待中"
}
// Approval dialog.
MessageId::ApprovalRiskReview => "審查",
MessageId::ApprovalRiskDestructive => "破壞性",
MessageId::ApprovalCategorySafe => "安全",
MessageId::ApprovalCategoryFileWrite => "檔案寫入",
MessageId::ApprovalCategoryShell => "Shell 命令",
MessageId::ApprovalCategoryNetwork => "網路",
MessageId::ApprovalCategoryMcpRead => "MCP 讀取",
MessageId::ApprovalCategoryMcpAction => "MCP 操作",
MessageId::ApprovalCategoryUnknown => "未分類",
MessageId::ApprovalFieldType => "類型:",
MessageId::ApprovalFieldAbout => "說明:",
MessageId::ApprovalFieldImpact => "影響:",
MessageId::ApprovalFieldParams => "參數:",
MessageId::ApprovalOptionApproveOnce => "僅批准一次",
MessageId::ApprovalOptionApproveAlways => "本會話同類自動批准",
MessageId::ApprovalOptionDeny => "拒絕本次調用",
MessageId::ApprovalOptionAbortTurn => "終止本輪",
MessageId::ApprovalBlockTitle => "審批",
MessageId::ApprovalControlsHint => " · v:完整參數 · Esc:終止",
MessageId::ApprovalChooseHint => "選擇:",
MessageId::ApprovalChooseAction => "Enter 執行選中項,或直接按 y/a/d",
MessageId::ApprovalIntentLabel => "意圖:",
MessageId::ApprovalMoreLines => " … (還有 {count} 行)",
MessageId::CtxInspTitle => "上下文檢查器",
MessageId::CtxInspSessionContext => "會話上下文",
MessageId::CtxInspSystemPrompt => "系統提示結構",
@@ -2474,9 +2599,34 @@ fn japanese(id: MessageId) -> Option<&'static str> {
MessageId::CtxMenuHelp => "ヘルプ",
MessageId::CtxMenuHelpDesc => "キー操作とコマンド",
MessageId::FanoutCounts => {
"{done} 完了 · {running} 実行中 · {failed} 失敗 · {pending} 待機"
"{done} 完了 · {running} 実行中 · {failed} 失敗 · {pending} 保留"
}
// Approval dialog.
MessageId::ApprovalRiskReview => "確認",
MessageId::ApprovalRiskDestructive => "破壊的操作",
MessageId::ApprovalCategorySafe => "安全",
MessageId::ApprovalCategoryFileWrite => "ファイル書き込み",
MessageId::ApprovalCategoryShell => "シェルコマンド",
MessageId::ApprovalCategoryNetwork => "ネットワーク",
MessageId::ApprovalCategoryMcpRead => "MCP読み取り",
MessageId::ApprovalCategoryMcpAction => "MCPアクション",
MessageId::ApprovalCategoryUnknown => "未分類",
MessageId::ApprovalFieldType => "種類:",
MessageId::ApprovalFieldAbout => "詳細:",
MessageId::ApprovalFieldImpact => "影響:",
MessageId::ApprovalFieldParams => "パラメータ:",
MessageId::ApprovalOptionApproveOnce => "1回だけ承認",
MessageId::ApprovalOptionApproveAlways => "常に承認(この種類)",
MessageId::ApprovalOptionDeny => "拒否",
MessageId::ApprovalOptionAbortTurn => "中断",
MessageId::ApprovalBlockTitle => "承認",
MessageId::ApprovalControlsHint => " · v: パラメータ表示 · Esc: 中止",
MessageId::ApprovalChooseHint => "選択:",
MessageId::ApprovalChooseAction => "Enterで選択、または y/a/d を直接入力",
MessageId::ApprovalIntentLabel => "意図:",
MessageId::ApprovalMoreLines => " … (+{count} 行)",
MessageId::CtxInspTitle => "コンテキストインスペクタ",
MessageId::CtxInspSessionContext => "セッションコンテキスト",
MessageId::CtxInspSystemPrompt => "システムプロンプト構造",
@@ -2881,6 +3031,31 @@ fn chinese_simplified(id: MessageId) -> Option<&'static str> {
"{done} 已完成 · {running} 运行中 · {failed} 失败 · {pending} 等待中"
}
// Approval dialog.
MessageId::ApprovalRiskReview => "审查",
MessageId::ApprovalRiskDestructive => "破坏性",
MessageId::ApprovalCategorySafe => "安全",
MessageId::ApprovalCategoryFileWrite => "文件写入",
MessageId::ApprovalCategoryShell => "Shell 命令",
MessageId::ApprovalCategoryNetwork => "网络",
MessageId::ApprovalCategoryMcpRead => "MCP 读取",
MessageId::ApprovalCategoryMcpAction => "MCP 操作",
MessageId::ApprovalCategoryUnknown => "未知",
MessageId::ApprovalFieldType => "类型:",
MessageId::ApprovalFieldAbout => "说明:",
MessageId::ApprovalFieldImpact => "影响:",
MessageId::ApprovalFieldParams => "参数:",
MessageId::ApprovalOptionApproveOnce => "仅本次批准",
MessageId::ApprovalOptionApproveAlways => "本会话同类自动批准",
MessageId::ApprovalOptionDeny => "拒绝本次调用",
MessageId::ApprovalOptionAbortTurn => "终止本轮",
MessageId::ApprovalBlockTitle => "审批",
MessageId::ApprovalControlsHint => " · v:完整参数 · Esc:终止",
MessageId::ApprovalChooseHint => "选择:",
MessageId::ApprovalChooseAction => "Enter 执行选中项,或直接按 y/a/d",
MessageId::ApprovalIntentLabel => "意图:",
MessageId::ApprovalMoreLines => " … (还有 {count} 行)",
MessageId::CtxInspTitle => "上下文检查器",
MessageId::CtxInspSessionContext => "会话上下文",
MessageId::CtxInspSystemPrompt => "系统提示结构",
@@ -3358,9 +3533,34 @@ fn portuguese_brazil(id: MessageId) -> Option<&'static str> {
MessageId::CtxMenuHelp => "Ajuda",
MessageId::CtxMenuHelpDesc => "atalhos de teclado e comandos",
MessageId::FanoutCounts => {
"{done} concluído · {running} executando · {failed} falhou · {pending} pendente"
"{done} concluído · {running} em execução · {failed} falhou · {pending} pendente"
}
// Approval dialog.
MessageId::ApprovalRiskReview => "REVISÃO",
MessageId::ApprovalRiskDestructive => "DESTRUTIVO",
MessageId::ApprovalCategorySafe => "Seguro",
MessageId::ApprovalCategoryFileWrite => "Escrita de Arquivo",
MessageId::ApprovalCategoryShell => "Comando Shell",
MessageId::ApprovalCategoryNetwork => "Rede",
MessageId::ApprovalCategoryMcpRead => "Leitura MCP",
MessageId::ApprovalCategoryMcpAction => "Ação MCP",
MessageId::ApprovalCategoryUnknown => "Desconhecido",
MessageId::ApprovalFieldType => "Tipo:",
MessageId::ApprovalFieldAbout => "Sobre:",
MessageId::ApprovalFieldImpact => "Impacto:",
MessageId::ApprovalFieldParams => "Parâmetros:",
MessageId::ApprovalOptionApproveOnce => "Aprovar uma vez",
MessageId::ApprovalOptionApproveAlways => "Aprovar sempre para este tipo",
MessageId::ApprovalOptionDeny => "Negar esta chamada",
MessageId::ApprovalOptionAbortTurn => "Abortar turno",
MessageId::ApprovalBlockTitle => "aprovação",
MessageId::ApprovalControlsHint => " · v: parâmetros · Esc: abortar",
MessageId::ApprovalChooseHint => "Escolha: ",
MessageId::ApprovalChooseAction => "Enter para selecionar, ou pressione y/a/d diretamente",
MessageId::ApprovalIntentLabel => "Intenção: ",
MessageId::ApprovalMoreLines => " … (+{count} linhas)",
MessageId::CtxInspTitle => "Inspetor de contexto",
MessageId::CtxInspSessionContext => "Contexto da sessão",
MessageId::CtxInspSystemPrompt => "Estrutura do prompt do sistema",
@@ -3855,6 +4055,31 @@ fn spanish_latin_america(id: MessageId) -> Option<&'static str> {
"{done} completado · {running} ejecutando · {failed} falló · {pending} pendiente"
}
// Approval dialog.
MessageId::ApprovalRiskReview => "REVISAR",
MessageId::ApprovalRiskDestructive => "DESTRUCTIVO",
MessageId::ApprovalCategorySafe => "Seguro",
MessageId::ApprovalCategoryFileWrite => "Escritura de Archivo",
MessageId::ApprovalCategoryShell => "Comando Shell",
MessageId::ApprovalCategoryNetwork => "Red",
MessageId::ApprovalCategoryMcpRead => "Lectura MCP",
MessageId::ApprovalCategoryMcpAction => "Acción MCP",
MessageId::ApprovalCategoryUnknown => "Desconocido",
MessageId::ApprovalFieldType => "Tipo:",
MessageId::ApprovalFieldAbout => "Acerca de:",
MessageId::ApprovalFieldImpact => "Impacto:",
MessageId::ApprovalFieldParams => "Parámetros:",
MessageId::ApprovalOptionApproveOnce => "Aprobar una vez",
MessageId::ApprovalOptionApproveAlways => "Aprobar siempre para este tipo",
MessageId::ApprovalOptionDeny => "Denegar esta llamada",
MessageId::ApprovalOptionAbortTurn => "Abortar turno",
MessageId::ApprovalBlockTitle => "aprobación",
MessageId::ApprovalControlsHint => " · v: parámetros · Esc: abortar",
MessageId::ApprovalChooseHint => "Elegir: ",
MessageId::ApprovalChooseAction => "Enter para seleccionar, o presione y/a/d directamente",
MessageId::ApprovalIntentLabel => "Intención: ",
MessageId::ApprovalMoreLines => " … (+{count} líneas)",
MessageId::CtxInspTitle => "Inspector de contexto",
MessageId::CtxInspSessionContext => "Contexto de la sesión",
MessageId::CtxInspSystemPrompt => "Estructura del prompt del sistema",
+38 -78
View File
@@ -24,7 +24,7 @@ pub use renderable::Renderable;
use std::time::Duration;
use crate::localization::Locale;
use crate::localization::{Locale, MessageId, tr};
use crate::palette;
use crate::tui::app::{App, AppMode, ComposerDensity, VimMode};
use crate::tui::approval::{
@@ -1171,10 +1171,7 @@ impl Renderable for ApprovalWidget<'_> {
let max_width = card_area.width.saturating_sub(14) as usize;
if max_width > 0 {
lines.push(Line::from(""));
let intent_label = match locale {
Locale::ZhHans => "意图:",
_ => "Intent: ",
};
let intent_label = tr(locale, MessageId::ApprovalIntentLabel);
let summary_lines: Vec<&str> = summary.lines().collect();
for (i, sline) in summary_lines.iter().take(3).enumerate() {
let prefix = if i == 0 { intent_label } else { " " };
@@ -1193,10 +1190,8 @@ impl Renderable for ApprovalWidget<'_> {
]));
}
if summary_lines.len() > 3 {
let more = match locale {
Locale::ZhHans => format!(" … (还有 {} 行)", summary_lines.len() - 3),
_ => format!(" … (+{} lines)", summary_lines.len() - 3),
};
let more = tr(locale, MessageId::ApprovalMoreLines)
.replace("{count}", &(summary_lines.len() - 3).to_string());
lines.push(Line::from(vec![
Span::raw(" "),
Span::styled(more, Style::default().fg(palette::TEXT_HINT)),
@@ -1381,87 +1376,64 @@ fn approval_option_style(is_selected: bool, color: Color) -> Style {
}
fn risk_badge_text(risk: RiskLevel, locale: Locale) -> &'static str {
match (locale, risk) {
(Locale::ZhHans, RiskLevel::Benign) => "审查",
(Locale::ZhHans, RiskLevel::Destructive) => "破坏性",
(_, RiskLevel::Benign) => "REVIEW",
(_, RiskLevel::Destructive) => "DESTRUCTIVE",
match risk {
RiskLevel::Benign => tr(locale, MessageId::ApprovalRiskReview),
RiskLevel::Destructive => tr(locale, MessageId::ApprovalRiskDestructive),
}
}
fn category_label_for(category: ToolCategory, locale: Locale) -> (&'static str, Color) {
match (locale, category) {
(Locale::ZhHans, ToolCategory::Safe) => ("安全", palette::STATUS_SUCCESS),
(Locale::ZhHans, ToolCategory::FileWrite) => ("文件写入", palette::STATUS_WARNING),
(Locale::ZhHans, ToolCategory::Shell) => ("Shell 命令", palette::STATUS_ERROR),
(Locale::ZhHans, ToolCategory::Network) => ("网络", palette::STATUS_WARNING),
(Locale::ZhHans, ToolCategory::McpRead) => ("MCP 读取", palette::DEEPSEEK_SKY),
(Locale::ZhHans, ToolCategory::McpAction) => ("MCP 操作", palette::STATUS_WARNING),
(Locale::ZhHans, ToolCategory::Unknown) => ("未知", palette::STATUS_ERROR),
(_, ToolCategory::Safe) => ("Safe", palette::STATUS_SUCCESS),
(_, ToolCategory::FileWrite) => ("File Write", palette::STATUS_WARNING),
(_, ToolCategory::Shell) => ("Shell Command", palette::STATUS_ERROR),
(_, ToolCategory::Network) => ("Network", palette::STATUS_WARNING),
(_, ToolCategory::McpRead) => ("MCP Read", palette::DEEPSEEK_SKY),
(_, ToolCategory::McpAction) => ("MCP Action", palette::STATUS_WARNING),
(_, ToolCategory::Unknown) => ("Unknown", palette::STATUS_ERROR),
}
let label = match category {
ToolCategory::Safe => tr(locale, MessageId::ApprovalCategorySafe),
ToolCategory::FileWrite => tr(locale, MessageId::ApprovalCategoryFileWrite),
ToolCategory::Shell => tr(locale, MessageId::ApprovalCategoryShell),
ToolCategory::Network => tr(locale, MessageId::ApprovalCategoryNetwork),
ToolCategory::McpRead => tr(locale, MessageId::ApprovalCategoryMcpRead),
ToolCategory::McpAction => tr(locale, MessageId::ApprovalCategoryMcpAction),
ToolCategory::Unknown => tr(locale, MessageId::ApprovalCategoryUnknown),
};
let color = match category {
ToolCategory::Safe => palette::STATUS_SUCCESS,
ToolCategory::FileWrite => palette::STATUS_WARNING,
ToolCategory::Shell => palette::STATUS_ERROR,
ToolCategory::Network => palette::STATUS_WARNING,
ToolCategory::McpRead => palette::DEEPSEEK_SKY,
ToolCategory::McpAction => palette::STATUS_WARNING,
ToolCategory::Unknown => palette::STATUS_ERROR,
};
(label, color)
}
fn approval_word(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => "审批",
_ => "approval",
}
tr(locale, MessageId::ApprovalBlockTitle)
}
fn label_type(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => "类型:",
_ => "Type: ",
}
tr(locale, MessageId::ApprovalFieldType)
}
fn label_about(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => "说明:",
_ => "About: ",
}
tr(locale, MessageId::ApprovalFieldAbout)
}
fn label_impact(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => "影响:",
_ => "Impact: ",
}
tr(locale, MessageId::ApprovalFieldImpact)
}
fn label_params(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => "参数:",
_ => "Params: ",
}
tr(locale, MessageId::ApprovalFieldParams)
}
fn footer_controls(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => " · v:完整参数 · Esc:终止",
_ => " · v: full params · Esc: abort",
}
tr(locale, MessageId::ApprovalControlsHint)
}
fn selection_hint_prefix(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => "选择:",
_ => "Choose: ",
}
tr(locale, MessageId::ApprovalChooseHint)
}
fn selection_hint_value(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => "Enter 执行选中项,或直接按 y/a/d",
_ => "Enter selected option, or press y/a/d directly",
}
tr(locale, MessageId::ApprovalChooseAction)
}
struct ApprovalOptionRow {
@@ -1497,31 +1469,19 @@ fn approval_options_for(risk: RiskLevel, locale: Locale) -> [ApprovalOptionRow;
}
fn option_approve_once(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => "仅本次批准",
_ => "Approve once",
}
tr(locale, MessageId::ApprovalOptionApproveOnce)
}
fn option_approve_always(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => "本会话同类自动批准",
_ => "Approve always for this kind",
}
tr(locale, MessageId::ApprovalOptionApproveAlways)
}
fn option_deny(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => "拒绝本次调用",
_ => "Deny this call",
}
tr(locale, MessageId::ApprovalOptionDeny)
}
fn option_abort(locale: Locale) -> &'static str {
match locale {
Locale::ZhHans => "终止本轮",
_ => "Abort the turn",
}
tr(locale, MessageId::ApprovalOptionAbortTurn)
}
pub struct ElevationWidget<'a> {