feat(i18n): localize context-inspector surface across 7 locales

Localize the context-inspector surface across 7 locales for improved internationalization support.
This commit is contained in:
Gordon
2026-06-03 11:47:53 +08:00
committed by GitHub
parent 29acb87a9d
commit b4691bc082
3 changed files with 606 additions and 76 deletions
+438
View File
@@ -502,6 +502,52 @@ pub enum MessageId {
CtxMenuHelpDesc,
// Agent fanout card.
FanoutCounts,
CtxInspTitle,
CtxInspSessionContext,
CtxInspSystemPrompt,
CtxInspReferences,
CtxInspRecentTools,
CtxInspModel,
CtxInspWorkspace,
CtxInspSession,
CtxInspContext,
CtxInspTranscript,
CtxInspWorkspaceStatus,
CtxInspNotSampledYet,
CtxInspOk,
CtxInspHigh,
CtxInspCritical,
CtxInspIncluded,
CtxInspAttached,
CtxInspNotIncluded,
CtxInspOutputCaptured,
CtxInspNoOutputYet,
CtxInspNoSystemPrompt,
CtxInspNoReferences,
CtxInspNoToolActivity,
CtxInspAltVHint,
CtxInspCells,
CtxInspApiMessages,
CtxInspActive,
CtxInspCell,
CtxInspMoreReferences,
CtxInspStablePrefix,
CtxInspVolatileWorkingSet,
CtxInspFirstLine,
CtxInspTotal,
CtxInspTextPromptLayers,
CtxInspSingleTextBlob,
CtxInspBlocks,
CtxInspBlock,
CtxInspTokens,
CtxInspLayers,
CtxInspNone,
CtxInspEmpty,
CtxInspCacheFriendly,
CtxInspChangesByTurn,
CtxInspStablePrefixOnly,
CtxInspCacheTip,
}
#[allow(dead_code)]
@@ -779,6 +825,51 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[
MessageId::CtxMenuHelp,
MessageId::CtxMenuHelpDesc,
MessageId::FanoutCounts,
MessageId::CtxInspTitle,
MessageId::CtxInspSessionContext,
MessageId::CtxInspSystemPrompt,
MessageId::CtxInspReferences,
MessageId::CtxInspRecentTools,
MessageId::CtxInspModel,
MessageId::CtxInspWorkspace,
MessageId::CtxInspSession,
MessageId::CtxInspContext,
MessageId::CtxInspTranscript,
MessageId::CtxInspWorkspaceStatus,
MessageId::CtxInspNotSampledYet,
MessageId::CtxInspOk,
MessageId::CtxInspHigh,
MessageId::CtxInspCritical,
MessageId::CtxInspIncluded,
MessageId::CtxInspAttached,
MessageId::CtxInspNotIncluded,
MessageId::CtxInspOutputCaptured,
MessageId::CtxInspNoOutputYet,
MessageId::CtxInspNoSystemPrompt,
MessageId::CtxInspNoReferences,
MessageId::CtxInspNoToolActivity,
MessageId::CtxInspAltVHint,
MessageId::CtxInspCells,
MessageId::CtxInspApiMessages,
MessageId::CtxInspActive,
MessageId::CtxInspCell,
MessageId::CtxInspMoreReferences,
MessageId::CtxInspStablePrefix,
MessageId::CtxInspVolatileWorkingSet,
MessageId::CtxInspFirstLine,
MessageId::CtxInspTotal,
MessageId::CtxInspTextPromptLayers,
MessageId::CtxInspSingleTextBlob,
MessageId::CtxInspBlocks,
MessageId::CtxInspBlock,
MessageId::CtxInspTokens,
MessageId::CtxInspLayers,
MessageId::CtxInspNone,
MessageId::CtxInspEmpty,
MessageId::CtxInspCacheFriendly,
MessageId::CtxInspChangesByTurn,
MessageId::CtxInspStablePrefixOnly,
MessageId::CtxInspCacheTip,
];
pub fn tr(locale: Locale, id: MessageId) -> &'static str {
@@ -1365,6 +1456,55 @@ fn english(id: MessageId) -> &'static str {
MessageId::FanoutCounts => {
"{done} done · {running} running · {failed} failed · {pending} pending"
}
MessageId::CtxInspTitle => "Context inspector",
MessageId::CtxInspSessionContext => "Session Context",
MessageId::CtxInspSystemPrompt => "System Prompt Structure",
MessageId::CtxInspReferences => "References",
MessageId::CtxInspRecentTools => "Recent Tools",
MessageId::CtxInspModel => "Model",
MessageId::CtxInspWorkspace => "Workspace",
MessageId::CtxInspSession => "Session",
MessageId::CtxInspContext => "Context",
MessageId::CtxInspTranscript => "Transcript",
MessageId::CtxInspWorkspaceStatus => "Workspace status",
MessageId::CtxInspNotSampledYet => "not sampled yet",
MessageId::CtxInspOk => "ok",
MessageId::CtxInspHigh => "high",
MessageId::CtxInspCritical => "critical",
MessageId::CtxInspIncluded => "included",
MessageId::CtxInspAttached => "attached",
MessageId::CtxInspNotIncluded => "not included",
MessageId::CtxInspOutputCaptured => "output captured",
MessageId::CtxInspNoOutputYet => "no output yet",
MessageId::CtxInspNoSystemPrompt => "No system prompt set.",
MessageId::CtxInspNoReferences => "No file, directory, or media references recorded yet.",
MessageId::CtxInspNoToolActivity => "No tool activity recorded yet.",
MessageId::CtxInspAltVHint => "Open the matching card and press Alt+V for full details.",
MessageId::CtxInspCells => "cells",
MessageId::CtxInspApiMessages => "API messages",
MessageId::CtxInspActive => "active",
MessageId::CtxInspCell => "cell",
MessageId::CtxInspMoreReferences => "more reference(s)",
MessageId::CtxInspStablePrefix => "Stable prefix",
MessageId::CtxInspVolatileWorkingSet => "Volatile working set",
MessageId::CtxInspFirstLine => "First line",
MessageId::CtxInspTotal => "Total",
MessageId::CtxInspTextPromptLayers => "Text prompt layers",
MessageId::CtxInspSingleTextBlob => "Single text blob",
MessageId::CtxInspBlocks => "block(s)",
MessageId::CtxInspBlock => "block",
MessageId::CtxInspTokens => "tokens",
MessageId::CtxInspLayers => "layer(s)",
MessageId::CtxInspNone => "none",
MessageId::CtxInspEmpty => "(empty)",
MessageId::CtxInspCacheFriendly => "cache-friendly",
MessageId::CtxInspChangesByTurn => "changes by session/turn",
MessageId::CtxInspStablePrefixOnly => "stable prefix only",
MessageId::CtxInspCacheTip => {
"Tip: Stable prefix blocks are DeepSeek V4 prefix-cache eligible. \
Volatile working-set changes break the cache only for the tail."
}
}
}
@@ -1815,6 +1955,54 @@ fn vietnamese(id: MessageId) -> Option<&'static str> {
MessageId::FanoutCounts => {
"{done} hoàn thành · {running} đang chạy · {failed} thất bại · {pending} chờ"
}
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",
MessageId::CtxInspReferences => "Tham chiếu",
MessageId::CtxInspRecentTools => "Công cụ gần đây",
MessageId::CtxInspModel => "Mô hình",
MessageId::CtxInspWorkspace => "Không gian làm việc",
MessageId::CtxInspSession => "Phiên",
MessageId::CtxInspContext => "Ngữ cảnh",
MessageId::CtxInspTranscript => "Bảng ghi",
MessageId::CtxInspWorkspaceStatus => "Trạng thái không gian làm việc",
MessageId::CtxInspNotSampledYet => "chưa lấy mẫu",
MessageId::CtxInspOk => "ổn",
MessageId::CtxInspHigh => "cao",
MessageId::CtxInspCritical => "nghiêm trọng",
MessageId::CtxInspIncluded => "đã bao gồm",
MessageId::CtxInspAttached => "đã đính kèm",
MessageId::CtxInspNotIncluded => "không bao gồm",
MessageId::CtxInspOutputCaptured => "đã thu được đầu ra",
MessageId::CtxInspNoOutputYet => "chưa có đầu ra",
MessageId::CtxInspNoSystemPrompt => "Chưa có lời nhắc hệ thống.",
MessageId::CtxInspNoReferences => "Chưa có tham chiếu tệp, thư mục hoặc phương tiện nào.",
MessageId::CtxInspNoToolActivity => "Chưa có hoạt động công cụ nào.",
MessageId::CtxInspAltVHint => "Mở thẻ phù hợp và nhấn Alt+V để biết chi tiết.",
MessageId::CtxInspCells => "ô",
MessageId::CtxInspApiMessages => "tin nhắn API",
MessageId::CtxInspActive => "đang hoạt động",
MessageId::CtxInspCell => "ô",
MessageId::CtxInspMoreReferences => "các tham chiếu khác",
MessageId::CtxInspStablePrefix => "Khối ổn định",
MessageId::CtxInspVolatileWorkingSet => "Vùng làm việc thay đổi",
MessageId::CtxInspFirstLine => "Dòng đầu",
MessageId::CtxInspTotal => "Tổng",
MessageId::CtxInspTextPromptLayers => "Lớp văn bản gợi ý",
MessageId::CtxInspSingleTextBlob => "Văn bản khối đơn",
MessageId::CtxInspBlocks => "khối",
MessageId::CtxInspBlock => "khối",
MessageId::CtxInspTokens => "token",
MessageId::CtxInspLayers => "lớp",
MessageId::CtxInspNone => "không",
MessageId::CtxInspEmpty => "(trống)",
MessageId::CtxInspCacheFriendly => "thân thiện với bộ nhớ đệm",
MessageId::CtxInspChangesByTurn => "thay đổi theo phiên/lượt",
MessageId::CtxInspStablePrefixOnly => "chỉ có tiền tố ổn định",
MessageId::CtxInspCacheTip => {
"Gợi ý: Các khối ổn định đủ điều kiện cho bộ nhớ đệm tiền tố DeepSeek V4. Thay đổi vùng làm việc chỉ phá vỡ bộ nhớ đệm ở phần cuối."
}
})
}
@@ -1831,6 +2019,54 @@ fn traditional_chinese(id: MessageId) -> Option<&'static str> {
MessageId::FanoutCounts => {
"{done} 已完成 · {running} 運行中 · {failed} 失敗 · {pending} 等待中"
}
MessageId::CtxInspTitle => "上下文檢查器",
MessageId::CtxInspSessionContext => "會話上下文",
MessageId::CtxInspSystemPrompt => "系統提示結構",
MessageId::CtxInspReferences => "引用",
MessageId::CtxInspRecentTools => "最近使用的工具",
MessageId::CtxInspModel => "模型",
MessageId::CtxInspWorkspace => "工作區",
MessageId::CtxInspSession => "會話",
MessageId::CtxInspContext => "上下文",
MessageId::CtxInspTranscript => "記錄",
MessageId::CtxInspWorkspaceStatus => "工作區狀態",
MessageId::CtxInspNotSampledYet => "尚未取樣",
MessageId::CtxInspOk => "正常",
MessageId::CtxInspHigh => "較高",
MessageId::CtxInspCritical => "嚴重",
MessageId::CtxInspIncluded => "已包含",
MessageId::CtxInspAttached => "已附加",
MessageId::CtxInspNotIncluded => "未包含",
MessageId::CtxInspOutputCaptured => "已捕獲輸出",
MessageId::CtxInspNoOutputYet => "尚無輸出",
MessageId::CtxInspNoSystemPrompt => "未設定系統提示。",
MessageId::CtxInspNoReferences => "尚未記錄任何檔案、目錄或媒體引用。",
MessageId::CtxInspNoToolActivity => "尚未記錄任何工具活動。",
MessageId::CtxInspAltVHint => "開啟對應的卡片並按 Alt+V 檢視詳細資訊。",
MessageId::CtxInspCells => "儲存格",
MessageId::CtxInspApiMessages => "API 訊息",
MessageId::CtxInspActive => "作用中",
MessageId::CtxInspCell => "儲存格",
MessageId::CtxInspMoreReferences => "其他引用",
MessageId::CtxInspStablePrefix => "穩定前綴",
MessageId::CtxInspVolatileWorkingSet => "易變工作集",
MessageId::CtxInspFirstLine => "第一行",
MessageId::CtxInspTotal => "總計",
MessageId::CtxInspTextPromptLayers => "文字提示層",
MessageId::CtxInspSingleTextBlob => "單一文字塊",
MessageId::CtxInspBlocks => "個區塊",
MessageId::CtxInspBlock => "個區塊",
MessageId::CtxInspTokens => "個 token",
MessageId::CtxInspLayers => "個層",
MessageId::CtxInspNone => "",
MessageId::CtxInspEmpty => "(空)",
MessageId::CtxInspCacheFriendly => "快取友好",
MessageId::CtxInspChangesByTurn => "按會話/輪次變化",
MessageId::CtxInspStablePrefixOnly => "僅穩定前綴",
MessageId::CtxInspCacheTip => {
"提示:穩定前綴區塊符合 DeepSeek V4 前綴快取條件。易變工作集的更改僅會破壞快取尾部。"
}
other => chinese_simplified(other)?,
})
}
@@ -2242,6 +2478,56 @@ fn japanese(id: MessageId) -> Option<&'static str> {
MessageId::FanoutCounts => {
"{done} 完了 · {running} 実行中 · {failed} 失敗 · {pending} 待機"
}
MessageId::CtxInspTitle => "コンテキストインスペクタ",
MessageId::CtxInspSessionContext => "セッションコンテキスト",
MessageId::CtxInspSystemPrompt => "システムプロンプト構造",
MessageId::CtxInspReferences => "参照",
MessageId::CtxInspRecentTools => "最近のツール",
MessageId::CtxInspModel => "モデル",
MessageId::CtxInspWorkspace => "ワークスペース",
MessageId::CtxInspSession => "セッション",
MessageId::CtxInspContext => "コンテキスト",
MessageId::CtxInspTranscript => "トランスクリプト",
MessageId::CtxInspWorkspaceStatus => "ワークスペース状態",
MessageId::CtxInspNotSampledYet => "未サンプリング",
MessageId::CtxInspOk => "良好",
MessageId::CtxInspHigh => "高い",
MessageId::CtxInspCritical => "深刻",
MessageId::CtxInspIncluded => "含まれている",
MessageId::CtxInspAttached => "添付済み",
MessageId::CtxInspNotIncluded => "含まれていない",
MessageId::CtxInspOutputCaptured => "出力取得済み",
MessageId::CtxInspNoOutputYet => "未出力",
MessageId::CtxInspNoSystemPrompt => "システムプロンプトが設定されていません。",
MessageId::CtxInspNoReferences => {
"ファイル、ディレクトリ、メディアの参照はまだ記録されていません。"
}
MessageId::CtxInspNoToolActivity => "ツールアクティビティはまだ記録されていません。",
MessageId::CtxInspAltVHint => "該当するカードを開き、Alt+V を押すと詳細が表示されます。",
MessageId::CtxInspCells => "セル",
MessageId::CtxInspApiMessages => "API メッセージ",
MessageId::CtxInspActive => "アクティブ",
MessageId::CtxInspCell => "セル",
MessageId::CtxInspMoreReferences => "その他の参照",
MessageId::CtxInspStablePrefix => "安定プレフィックス",
MessageId::CtxInspVolatileWorkingSet => "揮発性ワーキングセット",
MessageId::CtxInspFirstLine => "最初の行",
MessageId::CtxInspTotal => "合計",
MessageId::CtxInspTextPromptLayers => "テキストプロンプトレイヤー",
MessageId::CtxInspSingleTextBlob => "単一テキストブロブ",
MessageId::CtxInspBlocks => "ブロック",
MessageId::CtxInspBlock => "ブロック",
MessageId::CtxInspTokens => "トークン",
MessageId::CtxInspLayers => "レイヤー",
MessageId::CtxInspNone => "なし",
MessageId::CtxInspEmpty => "(空)",
MessageId::CtxInspCacheFriendly => "キャッシュフレンドリー",
MessageId::CtxInspChangesByTurn => "セッション/ターンごとに変更",
MessageId::CtxInspStablePrefixOnly => "安定プレフィックスのみ",
MessageId::CtxInspCacheTip => {
"ヒント:安定プレフィックスブロックはDeepSeek V4プレフィックスキャッシュの対象です。揮発性ワーキングセットの変更は末尾のキャッシュのみを破壊します。"
}
})
}
@@ -2596,6 +2882,54 @@ fn chinese_simplified(id: MessageId) -> Option<&'static str> {
MessageId::FanoutCounts => {
"{done} 已完成 · {running} 运行中 · {failed} 失败 · {pending} 等待中"
}
MessageId::CtxInspTitle => "上下文检查器",
MessageId::CtxInspSessionContext => "会话上下文",
MessageId::CtxInspSystemPrompt => "系统提示结构",
MessageId::CtxInspReferences => "引用",
MessageId::CtxInspRecentTools => "最近使用的工具",
MessageId::CtxInspModel => "模型",
MessageId::CtxInspWorkspace => "工作区",
MessageId::CtxInspSession => "会话",
MessageId::CtxInspContext => "上下文",
MessageId::CtxInspTranscript => "记录",
MessageId::CtxInspWorkspaceStatus => "工作区状态",
MessageId::CtxInspNotSampledYet => "尚未采样",
MessageId::CtxInspOk => "正常",
MessageId::CtxInspHigh => "较高",
MessageId::CtxInspCritical => "严重",
MessageId::CtxInspIncluded => "已包含",
MessageId::CtxInspAttached => "已附加",
MessageId::CtxInspNotIncluded => "未包含",
MessageId::CtxInspOutputCaptured => "已捕获输出",
MessageId::CtxInspNoOutputYet => "尚无输出",
MessageId::CtxInspNoSystemPrompt => "未设置系统提示。",
MessageId::CtxInspNoReferences => "尚未记录任何文件、目录或媒体引用。",
MessageId::CtxInspNoToolActivity => "尚未记录任何工具活动。",
MessageId::CtxInspAltVHint => "打开对应的卡片并按 Alt+V 查看详细信息。",
MessageId::CtxInspCells => "单元格",
MessageId::CtxInspApiMessages => "API 消息",
MessageId::CtxInspActive => "活动中",
MessageId::CtxInspCell => "单元格",
MessageId::CtxInspMoreReferences => "更多引用",
MessageId::CtxInspStablePrefix => "稳定前缀",
MessageId::CtxInspVolatileWorkingSet => "易变工作集",
MessageId::CtxInspFirstLine => "第一行",
MessageId::CtxInspTotal => "总计",
MessageId::CtxInspTextPromptLayers => "文本提示层",
MessageId::CtxInspSingleTextBlob => "单一文本块",
MessageId::CtxInspBlocks => "个区块",
MessageId::CtxInspBlock => "个区块",
MessageId::CtxInspTokens => "个 token",
MessageId::CtxInspLayers => "个层",
MessageId::CtxInspNone => "",
MessageId::CtxInspEmpty => "(空)",
MessageId::CtxInspCacheFriendly => "缓存友好",
MessageId::CtxInspChangesByTurn => "按会话/轮次变化",
MessageId::CtxInspStablePrefixOnly => "仅稳定前缀",
MessageId::CtxInspCacheTip => {
"提示:稳定前缀区块符合 DeepSeek V4 前缀缓存条件。易变工作集的更改仅会破坏缓存尾部。"
}
})
}
@@ -3028,6 +3362,58 @@ fn portuguese_brazil(id: MessageId) -> Option<&'static str> {
MessageId::FanoutCounts => {
"{done} concluído · {running} executando · {failed} falhou · {pending} pendente"
}
MessageId::CtxInspTitle => "Inspetor de contexto",
MessageId::CtxInspSessionContext => "Contexto da sessão",
MessageId::CtxInspSystemPrompt => "Estrutura do prompt do sistema",
MessageId::CtxInspReferences => "Referências",
MessageId::CtxInspRecentTools => "Ferramentas recentes",
MessageId::CtxInspModel => "Modelo",
MessageId::CtxInspWorkspace => "Espaço de trabalho",
MessageId::CtxInspSession => "Sessão",
MessageId::CtxInspContext => "Contexto",
MessageId::CtxInspTranscript => "Transcrição",
MessageId::CtxInspWorkspaceStatus => "Status do espaço de trabalho",
MessageId::CtxInspNotSampledYet => "ainda não amostrado",
MessageId::CtxInspOk => "ok",
MessageId::CtxInspHigh => "alto",
MessageId::CtxInspCritical => "crítico",
MessageId::CtxInspIncluded => "incluído",
MessageId::CtxInspAttached => "anexado",
MessageId::CtxInspNotIncluded => "não incluído",
MessageId::CtxInspOutputCaptured => "saída capturada",
MessageId::CtxInspNoOutputYet => "nenhuma saída ainda",
MessageId::CtxInspNoSystemPrompt => "Nenhum prompt de sistema definido.",
MessageId::CtxInspNoReferences => {
"Nenhuma referência de arquivo, diretório ou mídia registrada ainda."
}
MessageId::CtxInspNoToolActivity => "Nenhuma atividade de ferramenta registrada ainda.",
MessageId::CtxInspAltVHint => {
"Abra o cartão correspondente e pressione Alt+V para detalhes completos."
}
MessageId::CtxInspCells => "células",
MessageId::CtxInspApiMessages => "mensagens da API",
MessageId::CtxInspActive => "ativo",
MessageId::CtxInspCell => "célula",
MessageId::CtxInspMoreReferences => "mais referência(s)",
MessageId::CtxInspStablePrefix => "Prefixo estável",
MessageId::CtxInspVolatileWorkingSet => "Conjunto de trabalho volátil",
MessageId::CtxInspFirstLine => "Primeira linha",
MessageId::CtxInspTotal => "Total",
MessageId::CtxInspTextPromptLayers => "Camadas de prompt de texto",
MessageId::CtxInspSingleTextBlob => "Bloco de texto único",
MessageId::CtxInspBlocks => "bloco(s)",
MessageId::CtxInspBlock => "bloco",
MessageId::CtxInspTokens => "token(s)",
MessageId::CtxInspLayers => "camada(s)",
MessageId::CtxInspNone => "nenhum",
MessageId::CtxInspEmpty => "(vazio)",
MessageId::CtxInspCacheFriendly => "amigável ao cache",
MessageId::CtxInspChangesByTurn => "muda por sessão/turno",
MessageId::CtxInspStablePrefixOnly => "apenas prefixo estável",
MessageId::CtxInspCacheTip => {
"Dica: Blocos de prefixo estável são elegíveis para cache de prefixo DeepSeek V4. Alterações no conjunto de trabalho volátil quebram o cache apenas no final."
}
})
}
@@ -3470,6 +3856,58 @@ fn spanish_latin_america(id: MessageId) -> Option<&'static str> {
MessageId::FanoutCounts => {
"{done} completado · {running} ejecutando · {failed} falló · {pending} pendiente"
}
MessageId::CtxInspTitle => "Inspector de contexto",
MessageId::CtxInspSessionContext => "Contexto de la sesión",
MessageId::CtxInspSystemPrompt => "Estructura del prompt del sistema",
MessageId::CtxInspReferences => "Referencias",
MessageId::CtxInspRecentTools => "Herramientas recientes",
MessageId::CtxInspModel => "Modelo",
MessageId::CtxInspWorkspace => "Espacio de trabajo",
MessageId::CtxInspSession => "Sesión",
MessageId::CtxInspContext => "Contexto",
MessageId::CtxInspTranscript => "Transcripción",
MessageId::CtxInspWorkspaceStatus => "Estado del espacio de trabajo",
MessageId::CtxInspNotSampledYet => "aún no muestreado",
MessageId::CtxInspOk => "bien",
MessageId::CtxInspHigh => "alto",
MessageId::CtxInspCritical => "crítico",
MessageId::CtxInspIncluded => "incluido",
MessageId::CtxInspAttached => "adjunto",
MessageId::CtxInspNotIncluded => "no incluido",
MessageId::CtxInspOutputCaptured => "salida capturada",
MessageId::CtxInspNoOutputYet => "sin salida aún",
MessageId::CtxInspNoSystemPrompt => "No hay prompt de sistema establecido.",
MessageId::CtxInspNoReferences => {
"Aún no se han registrado referencias de archivos, directorios o medios."
}
MessageId::CtxInspNoToolActivity => "Aún no se ha registrado actividad de herramientas.",
MessageId::CtxInspAltVHint => {
"Abra la tarjeta correspondiente y presione Alt+V para ver los detalles completos."
}
MessageId::CtxInspCells => "celdas",
MessageId::CtxInspApiMessages => "mensajes de API",
MessageId::CtxInspActive => "activo",
MessageId::CtxInspCell => "celda",
MessageId::CtxInspMoreReferences => "más referencia(s)",
MessageId::CtxInspStablePrefix => "Prefijo estable",
MessageId::CtxInspVolatileWorkingSet => "Conjunto de trabajo volátil",
MessageId::CtxInspFirstLine => "Primera línea",
MessageId::CtxInspTotal => "Total",
MessageId::CtxInspTextPromptLayers => "Capas de prompt de texto",
MessageId::CtxInspSingleTextBlob => "Bloque de texto único",
MessageId::CtxInspBlocks => "bloque(s)",
MessageId::CtxInspBlock => "bloque",
MessageId::CtxInspTokens => "token(es)",
MessageId::CtxInspLayers => "capa(s)",
MessageId::CtxInspNone => "ninguno",
MessageId::CtxInspEmpty => "(vacío)",
MessageId::CtxInspCacheFriendly => "amigable con caché",
MessageId::CtxInspChangesByTurn => "cambia por sesión/turno",
MessageId::CtxInspStablePrefixOnly => "solo prefijo estable",
MessageId::CtxInspCacheTip => {
"Consejo: Los bloques de prefijo estable son elegibles para caché de prefijo DeepSeek V4. Los cambios en el conjunto de trabajo volátil solo rompen la caché al final."
}
})
}
+165 -74
View File
@@ -4,6 +4,7 @@ use std::collections::HashSet;
use std::fmt::Write;
use crate::compaction::estimate_input_tokens_conservative;
use crate::localization::{Locale, MessageId, tr};
use crate::models::{
LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS, SystemPrompt, context_window_for_model,
};
@@ -71,10 +72,10 @@ enum PromptLayerKind {
}
impl PromptLayerKind {
fn label(self) -> &'static str {
fn label(self, locale: Locale) -> &'static str {
match self {
Self::Static => "cache-friendly",
Self::Dynamic => "changes by session/turn",
Self::Static => tr(locale, MessageId::CtxInspCacheFriendly),
Self::Dynamic => tr(locale, MessageId::CtxInspChangesByTurn),
}
}
}
@@ -87,47 +88,67 @@ struct PromptTextLayer<'a> {
}
#[must_use]
pub fn build_context_inspector_text(app: &App) -> String {
pub fn build_context_inspector_text(app: &App, locale: Locale) -> String {
let mut out = String::new();
let usage = context_usage(app);
let status = context_status(usage.2);
let (used, max, percent) = usage;
let _ = writeln!(out, "Session Context");
let _ = writeln!(out, "{}", tr(locale, MessageId::CtxInspSessionContext));
let _ = writeln!(out, "---------------");
let _ = writeln!(out, "Model: {}", app.model);
let _ = writeln!(
out,
"Workspace: {}",
"{}: {}",
tr(locale, MessageId::CtxInspModel),
app.model
);
let _ = writeln!(
out,
"{}: {}",
tr(locale, MessageId::CtxInspWorkspace),
crate::utils::display_path(&app.workspace)
);
if let Some(session_id) = app.current_session_id.as_deref() {
let _ = writeln!(out, "Session: {session_id}");
let _ = writeln!(
out,
"{}: {session_id}",
tr(locale, MessageId::CtxInspSession)
);
}
let (used, max, percent) = usage;
let status_label = match context_status(percent) {
ContextPressure::Critical => tr(locale, MessageId::CtxInspCritical),
ContextPressure::High => tr(locale, MessageId::CtxInspHigh),
ContextPressure::Ok => tr(locale, MessageId::CtxInspOk),
};
let tokens_unit = tr(locale, MessageId::CtxInspTokens);
let _ = writeln!(
out,
"Context: {status} - ~{used}/{max} tokens ({percent:.1}%)"
"{ctx_label}: {status_label} - ~{used}/{max} {tokens_unit} ({percent:.1}%)",
ctx_label = tr(locale, MessageId::CtxInspContext),
);
let cells = tr(locale, MessageId::CtxInspCells);
let api_msgs = tr(locale, MessageId::CtxInspApiMessages);
let _ = writeln!(
out,
"Transcript: {} cells, {} API messages",
"{label}: {} {cells}, {} {api_msgs}",
app.history.len(),
app.api_messages.len()
app.api_messages.len(),
label = tr(locale, MessageId::CtxInspTranscript),
);
let _ = writeln!(
out,
"Workspace status: {}",
"{}: {}",
tr(locale, MessageId::CtxInspWorkspaceStatus),
app.workspace_context
.as_deref()
.unwrap_or("not sampled yet")
.unwrap_or(tr(locale, MessageId::CtxInspNotSampledYet))
);
let _ = writeln!(out);
push_system_prompt_structure(&mut out, app);
push_system_prompt_structure(&mut out, app, locale);
let _ = writeln!(out);
push_references(&mut out, &app.session_context_references);
push_references(&mut out, &app.session_context_references, locale);
let _ = writeln!(out);
push_tools(&mut out, app);
push_tools(&mut out, app, locale);
out
}
@@ -143,20 +164,26 @@ fn context_usage(app: &App) -> (usize, u32, f64) {
(used, max, percent)
}
fn context_status(percent: f64) -> &'static str {
enum ContextPressure {
Ok,
High,
Critical,
}
fn context_status(percent: f64) -> ContextPressure {
if percent >= CONTEXT_CRITICAL_THRESHOLD_PERCENT {
"critical"
ContextPressure::Critical
} else if percent >= CONTEXT_WARNING_THRESHOLD_PERCENT {
"high"
ContextPressure::High
} else {
"ok"
ContextPressure::Ok
}
}
/// Inspect the system prompt structure, split into cache-friendly stable
/// prefix blocks and the volatile working-set tail block.
fn push_system_prompt_structure(out: &mut String, app: &App) {
let _ = writeln!(out, "System Prompt Structure");
fn push_system_prompt_structure(out: &mut String, app: &App, locale: Locale) {
let _ = writeln!(out, "{}", tr(locale, MessageId::CtxInspSystemPrompt));
let _ = writeln!(out, "-----------------------");
// Conservative token estimate: ~3 chars per token (consistent with
@@ -170,6 +197,22 @@ fn push_system_prompt_structure(out: &mut String, app: &App) {
None => 0,
};
let stable_lbl = tr(locale, MessageId::CtxInspStablePrefix);
let volatile_lbl = tr(locale, MessageId::CtxInspVolatileWorkingSet);
let first_line_lbl = tr(locale, MessageId::CtxInspFirstLine);
let total_lbl = tr(locale, MessageId::CtxInspTotal);
let text_prompt_lbl = tr(locale, MessageId::CtxInspTextPromptLayers);
let single_blob_lbl = tr(locale, MessageId::CtxInspSingleTextBlob);
let blocks_unit = tr(locale, MessageId::CtxInspBlocks);
let block_unit = tr(locale, MessageId::CtxInspBlock);
let tokens_unit = tr(locale, MessageId::CtxInspTokens);
let layers_unit = tr(locale, MessageId::CtxInspLayers);
let none_lbl = tr(locale, MessageId::CtxInspNone);
let empty_lbl = tr(locale, MessageId::CtxInspEmpty);
let cache_friendly = tr(locale, MessageId::CtxInspCacheFriendly);
let changes_by_turn = tr(locale, MessageId::CtxInspChangesByTurn);
let stable_only = tr(locale, MessageId::CtxInspStablePrefixOnly);
let no_system_prompt = tr(locale, MessageId::CtxInspNoSystemPrompt);
match &app.system_prompt {
Some(SystemPrompt::Blocks(blocks)) => {
let working_set_idx = blocks
@@ -189,24 +232,24 @@ fn push_system_prompt_structure(out: &mut String, app: &App) {
let _ = writeln!(
out,
" Stable prefix: {stable_count} block(s), ~{stable_tokens} tokens [cache-friendly]"
" {stable_lbl}: {stable_count} {blocks_unit}, ~{stable_tokens} {tokens_unit} [{cache_friendly}]"
);
if let Some(block) = working_block {
let _ = writeln!(
out,
" Volatile working set: 1 block, ~{working_tokens} tokens [changes every turn]"
" {volatile_lbl}: 1 {block_unit}, ~{working_tokens} {tokens_unit} [{changes_by_turn}]"
);
let _ = writeln!(
out,
" First line: {}",
block.text.lines().next().unwrap_or("(empty)")
" {first_line_lbl}: {}",
block.text.lines().next().unwrap_or(empty_lbl)
);
} else {
let _ = writeln!(out, " Volatile working set: none");
let _ = writeln!(out, " {volatile_lbl}: {none_lbl}");
}
let _ = writeln!(
out,
" Total: {} block(s), ~{total_est} tokens",
" {total_lbl}: {} {blocks_unit}, ~{total_est} {tokens_unit}",
blocks.len()
);
}
@@ -219,37 +262,32 @@ fn push_system_prompt_structure(out: &mut String, app: &App) {
{
let _ = writeln!(
out,
" Text prompt layers: {} layer(s), ~{total_est} tokens",
" {text_prompt_lbl}: {} {layers_unit}, ~{total_est} {tokens_unit}",
layers.len()
);
for layer in layers {
let tokens = text_tokens(layer.body);
let kind_lbl = layer.kind.label(locale);
let _ = writeln!(
out,
" - {}: ~{} tokens [{}]",
" - {}: ~{tokens} {tokens_unit} [{kind_lbl}]",
layer.name,
tokens,
layer.kind.label()
);
}
} else {
let _ = writeln!(
out,
" Single text blob (~{total_est} tokens) [stable prefix only]"
" {single_blob_lbl} (~{total_est} {tokens_unit}) [{stable_only}]"
);
}
}
None => {
let _ = writeln!(out, " No system prompt set.");
let _ = writeln!(out, " {no_system_prompt}");
}
}
// Cache-economics hint
let _ = writeln!(
out,
" Tip: Stable prefix blocks are DeepSeek V4 prefix-cache eligible. \
Volatile working-set changes break the cache only for the tail."
);
let _ = writeln!(out, " {}", tr(locale, MessageId::CtxInspCacheTip));
}
fn split_text_prompt_layers(text: &str) -> Vec<PromptTextLayer<'_>> {
@@ -288,8 +326,8 @@ fn split_text_prompt_layers(text: &str) -> Vec<PromptTextLayer<'_>> {
layers
}
fn push_references(out: &mut String, references: &[SessionContextReference]) {
let _ = writeln!(out, "References");
fn push_references(out: &mut String, references: &[SessionContextReference], locale: Locale) {
let _ = writeln!(out, "{}", tr(locale, MessageId::CtxInspReferences));
let _ = writeln!(out, "----------");
let mut seen = HashSet::new();
@@ -306,7 +344,11 @@ fn push_references(out: &mut String, references: &[SessionContextReference]) {
if rendered >= MAX_REFERENCE_ROWS {
let remaining = references.len().saturating_sub(rendered);
if remaining > 0 {
let _ = writeln!(out, "- ... {remaining} more reference(s)");
let _ = writeln!(
out,
"- ... {remaining} {}",
tr(locale, MessageId::CtxInspMoreReferences)
);
}
break;
}
@@ -317,12 +359,12 @@ fn push_references(out: &mut String, references: &[SessionContextReference]) {
};
let state = if reference.included {
if reference.expanded {
"included"
tr(locale, MessageId::CtxInspIncluded)
} else {
"attached"
tr(locale, MessageId::CtxInspAttached)
}
} else {
"not included"
tr(locale, MessageId::CtxInspNotIncluded)
};
let detail = reference
.detail
@@ -339,15 +381,12 @@ fn push_references(out: &mut String, references: &[SessionContextReference]) {
}
if rendered == 0 {
let _ = writeln!(
out,
"- No file, directory, or media references recorded yet."
);
let _ = writeln!(out, "- {}", tr(locale, MessageId::CtxInspNoReferences));
}
}
fn push_tools(out: &mut String, app: &App) {
let _ = writeln!(out, "Recent Tools");
fn push_tools(out: &mut String, app: &App, locale: Locale) {
let _ = writeln!(out, "{}", tr(locale, MessageId::CtxInspRecentTools));
let _ = writeln!(out, "------------");
let mut rows: Vec<(usize, &ToolDetailRecord)> = app
@@ -359,7 +398,8 @@ fn push_tools(out: &mut String, app: &App) {
let mut rendered = 0usize;
for detail in app.active_tool_details.values() {
push_tool_row(out, "active", detail);
let location = tr(locale, MessageId::CtxInspActive);
push_tool_row(out, locale, location, detail);
rendered += 1;
if rendered >= MAX_TOOL_ROWS {
return;
@@ -369,26 +409,23 @@ fn push_tools(out: &mut String, app: &App) {
.into_iter()
.take(MAX_TOOL_ROWS.saturating_sub(rendered))
{
let location = format!("cell {cell_idx}");
push_tool_row(out, &location, detail);
let location = format!("{} {cell_idx}", tr(locale, MessageId::CtxInspCell));
push_tool_row(out, locale, &location, detail);
rendered += 1;
}
if rendered == 0 {
let _ = writeln!(out, "- No tool activity recorded yet.");
let _ = writeln!(out, "- {}", tr(locale, MessageId::CtxInspNoToolActivity));
} else {
let _ = writeln!(
out,
"- Open the matching card and press Alt+V for full details."
);
let _ = writeln!(out, "- {}", tr(locale, MessageId::CtxInspAltVHint));
}
}
fn push_tool_row(out: &mut String, location: &str, detail: &ToolDetailRecord) {
fn push_tool_row(out: &mut String, locale: Locale, location: &str, detail: &ToolDetailRecord) {
let output_state = if detail.output.as_deref().is_some_and(|out| !out.is_empty()) {
"output captured"
tr(locale, MessageId::CtxInspOutputCaptured)
} else {
"no output yet"
tr(locale, MessageId::CtxInspNoOutputYet)
};
let _ = writeln!(
out,
@@ -420,6 +457,8 @@ mod tests {
use crate::tui::history::HistoryCell;
use std::path::PathBuf;
use crate::localization::Locale;
fn test_app() -> App {
App::new(
TuiOptions {
@@ -450,7 +489,7 @@ mod tests {
#[test]
fn inspector_formats_empty_state() {
let app = test_app();
let text = build_context_inspector_text(&app);
let text = build_context_inspector_text(&app, Locale::En);
assert!(text.contains("Session Context"));
assert!(text.contains("No file, directory, or media references recorded yet."));
assert!(text.contains("No tool activity recorded yet."));
@@ -477,7 +516,7 @@ mod tests {
},
});
let text = build_context_inspector_text(&app);
let text = build_context_inspector_text(&app, Locale::En);
assert!(text.contains("[file] @src/main.rs -> /tmp/project/src/main.rs"));
}
@@ -492,7 +531,7 @@ mod tests {
}],
});
let text = build_context_inspector_text(&app);
let text = build_context_inspector_text(&app, Locale::En);
assert!(text.contains("Context: critical"), "{text}");
}
@@ -503,7 +542,7 @@ mod tests {
app.auto_model = true;
app.last_effective_model = Some("deepseek-v4-pro".to_string());
let text = build_context_inspector_text(&app);
let text = build_context_inspector_text(&app, Locale::En);
assert!(text.contains("Model: auto"), "{text}");
assert!(text.contains("/1000000 tokens"), "{text}");
}
@@ -511,7 +550,7 @@ mod tests {
#[test]
fn inspector_no_system_prompt_shows_section() {
let app = test_app();
let text = build_context_inspector_text(&app);
let text = build_context_inspector_text(&app, Locale::En);
assert!(text.contains("System Prompt Structure"));
assert!(text.contains("No system prompt set."));
}
@@ -533,7 +572,7 @@ mod tests {
},
]));
let text = build_context_inspector_text(&app);
let text = build_context_inspector_text(&app, Locale::En);
assert!(text.contains("System Prompt Structure"));
assert!(
text.contains("Stable prefix: 1 block"),
@@ -548,7 +587,7 @@ mod tests {
"cache hint for stable: {text}"
);
assert!(
text.contains("[changes every turn]"),
text.contains("[changes by session/turn]"),
"volatile marker: {text}"
);
assert!(
@@ -574,7 +613,7 @@ mod tests {
},
]));
let text = build_context_inspector_text(&app);
let text = build_context_inspector_text(&app, Locale::En);
assert!(text.contains("Stable prefix: 2 block(s)"));
assert!(text.contains("Volatile working set: none"));
}
@@ -586,7 +625,7 @@ mod tests {
"You are CodeWhale.\n\n<project_instructions source=\"AGENTS.md\">\nRules\n</project_instructions>\n\n## Project Context Pack\n{}\n\n## Environment\n- lang: en\n\n## Skills\n- rust\n\n## Context Management\nKeep compact\n\n## Compact\nTemplate\n\n## Repo Working Set\nsrc/".to_string(),
));
let text = build_context_inspector_text(&app);
let text = build_context_inspector_text(&app, Locale::En);
assert!(text.contains("System Prompt Structure"));
assert!(text.contains("Text prompt layers"));
assert!(text.contains("Global system prefix"));
@@ -605,8 +644,60 @@ mod tests {
let mut app = test_app();
app.system_prompt = Some(SystemPrompt::Text("You are CodeWhale.".to_string()));
let text = build_context_inspector_text(&app);
let text = build_context_inspector_text(&app, Locale::En);
assert!(text.contains("Single text blob"));
assert!(text.contains("stable prefix only"));
}
#[test]
fn inspector_localizes_to_zh_hans() {
use crate::models::SystemBlock;
let mut app = test_app();
app.system_prompt = Some(SystemPrompt::Blocks(vec![
SystemBlock {
block_type: "text".to_string(),
text: "## Base\nYou are CodeWhale.".to_string(),
cache_control: None,
},
SystemBlock {
block_type: "text".to_string(),
text: format!("{WORKING_SET_MARKER}\nsrc/main.rs changed"),
cache_control: None,
},
]));
let text = build_context_inspector_text(&app, Locale::ZhHans);
// Positive: key ZhHans labels present
assert!(text.contains("会话上下文"), "session header: {text}");
assert!(text.contains("模型"), "model label: {text}");
assert!(text.contains("工作区"), "workspace: {text}");
assert!(text.contains("系统提示结构"), "sysprompt section: {text}");
assert!(text.contains("稳定前缀"), "stable prefix: {text}");
assert!(text.contains("易变工作集"), "volatile ws: {text}");
assert!(text.contains("第一行"), "first line: {text}");
assert!(text.contains("总计"), "total line: {text}");
assert!(text.contains("引用"), "references: {text}");
assert!(text.contains("最近使用的工具"), "tools: {text}");
assert!(text.contains("个区块"), "blocks unit: {text}");
assert!(text.contains("个 token"), "tokens unit: {text}");
assert!(text.contains("缓存友好"), "cache-friendly: {text}");
assert!(text.contains("提示"), "cache tip: {text}");
// Negative: no English labels leak
assert!(!text.contains("Session Context"), "EN session leaked");
assert!(!text.contains("Model:"), "EN model leaked");
assert!(!text.contains("cells"), "EN cells leaked");
assert!(!text.contains("API messages"), "EN API msgs leaked");
assert!(!text.contains("Stable prefix"), "EN stable prefix leaked");
assert!(
!text.contains("Volatile working set"),
"EN volatile ws leaked"
);
assert!(!text.contains("First line"), "EN first line leaked");
assert!(!text.contains("Total:"), "EN total leaked");
assert!(!text.contains("Text prompt layers"), "EN layers leaked");
assert!(!text.contains("cache-friendly"), "EN cache-friendly leaked");
assert!(!text.contains("more reference"), "EN more refs leaked");
assert!(!text.contains("no output yet"), "EN no output leaked");
}
}
+3 -2
View File
@@ -51,6 +51,7 @@ use crate::core::events::Event as EngineEvent;
use crate::core::ops::{Op, USER_SHELL_TOOL_ID_PREFIX};
use crate::hooks::{HookEvent, HookExecutor};
use crate::llm_client::LlmClient;
use crate::localization::{MessageId, tr};
use crate::models::{
ContentBlock, Message, MessageRequest, SystemPrompt, Usage, context_window_for_model,
};
@@ -5325,9 +5326,9 @@ pub(crate) fn open_context_inspector(app: &mut App) {
.last_transcript_area
.map(|area| area.width)
.unwrap_or(80);
let content = build_context_inspector_text(app);
let content = build_context_inspector_text(app, app.ui_locale);
app.view_stack.push(PagerView::from_text(
"Context inspector",
tr(app.ui_locale, MessageId::CtxInspTitle),
&content,
width.saturating_sub(2),
));