返回 DeepSeek-TUI-2026
localization.rs
根目录 / crates / tui / src / localization.rs
1 //! Lightweight localization registry for high-visibility TUI strings.
2 //!
3 //! This intentionally covers UI chrome only. It does not change model prompts,
4 //! model output language, provider behavior, or media payload semantics.
5
6 use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
7
8 #[allow(dead_code)]
9 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
10 pub enum TextDirection {
11 Ltr,
12 Rtl,
13 }
14
15 #[allow(dead_code)]
16 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
17 pub enum LocaleCoverage {
18 English,
19 V076Core,
20 PlannedQa,
21 }
22
23 #[allow(dead_code)]
24 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
25 pub struct LocaleSpec {
26 pub tag: &'static str,
27 pub display_name: &'static str,
28 pub script: &'static str,
29 pub direction: TextDirection,
30 pub fallback: &'static str,
31 pub coverage: LocaleCoverage,
32 }
33
34 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35 pub enum Locale {
36 En,
37 Ja,
38 ZhHans,
39 PtBr,
40 }
41
42 impl Locale {
43 pub fn tag(self) -> &'static str {
44 match self {
45 Self::En => "en",
46 Self::Ja => "ja",
47 Self::ZhHans => "zh-Hans",
48 Self::PtBr => "pt-BR",
49 }
50 }
51
52 #[allow(dead_code)]
53 pub fn spec(self) -> LocaleSpec {
54 match self {
55 Self::En => LocaleSpec {
56 tag: "en",
57 display_name: "English",
58 script: "Latin",
59 direction: TextDirection::Ltr,
60 fallback: "en",
61 coverage: LocaleCoverage::English,
62 },
63 Self::Ja => LocaleSpec {
64 tag: "ja",
65 display_name: "Japanese",
66 script: "Jpan",
67 direction: TextDirection::Ltr,
68 fallback: "en",
69 coverage: LocaleCoverage::V076Core,
70 },
71 Self::ZhHans => LocaleSpec {
72 tag: "zh-Hans",
73 display_name: "Chinese Simplified",
74 script: "Hans",
75 direction: TextDirection::Ltr,
76 fallback: "en",
77 coverage: LocaleCoverage::V076Core,
78 },
79 Self::PtBr => LocaleSpec {
80 tag: "pt-BR",
81 display_name: "Portuguese (Brazil)",
82 script: "Latin",
83 direction: TextDirection::Ltr,
84 fallback: "en",
85 coverage: LocaleCoverage::V076Core,
86 },
87 }
88 }
89
90 #[allow(dead_code)]
91 pub fn shipped() -> &'static [Self] {
92 &[Self::En, Self::Ja, Self::ZhHans, Self::PtBr]
93 }
94 }
95
96 #[allow(dead_code)]
97 pub const PLANNED_QA_LOCALES: &[LocaleSpec] = &[
98 LocaleSpec {
99 tag: "ar",
100 display_name: "Arabic",
101 script: "Arab",
102 direction: TextDirection::Rtl,
103 fallback: "en",
104 coverage: LocaleCoverage::PlannedQa,
105 },
106 LocaleSpec {
107 tag: "hi",
108 display_name: "Hindi",
109 script: "Deva",
110 direction: TextDirection::Ltr,
111 fallback: "en",
112 coverage: LocaleCoverage::PlannedQa,
113 },
114 LocaleSpec {
115 tag: "bn",
116 display_name: "Bengali",
117 script: "Beng",
118 direction: TextDirection::Ltr,
119 fallback: "en",
120 coverage: LocaleCoverage::PlannedQa,
121 },
122 LocaleSpec {
123 tag: "id",
124 display_name: "Indonesian",
125 script: "Latin",
126 direction: TextDirection::Ltr,
127 fallback: "en",
128 coverage: LocaleCoverage::PlannedQa,
129 },
130 LocaleSpec {
131 tag: "vi",
132 display_name: "Vietnamese",
133 script: "Latin",
134 direction: TextDirection::Ltr,
135 fallback: "en",
136 coverage: LocaleCoverage::PlannedQa,
137 },
138 LocaleSpec {
139 tag: "sw",
140 display_name: "Swahili",
141 script: "Latin",
142 direction: TextDirection::Ltr,
143 fallback: "en",
144 coverage: LocaleCoverage::PlannedQa,
145 },
146 LocaleSpec {
147 tag: "ha",
148 display_name: "Hausa",
149 script: "Latin",
150 direction: TextDirection::Ltr,
151 fallback: "en",
152 coverage: LocaleCoverage::PlannedQa,
153 },
154 LocaleSpec {
155 tag: "yo",
156 display_name: "Yoruba",
157 script: "Latin",
158 direction: TextDirection::Ltr,
159 fallback: "en",
160 coverage: LocaleCoverage::PlannedQa,
161 },
162 LocaleSpec {
163 tag: "es-419",
164 display_name: "Spanish (Latin America)",
165 script: "Latin",
166 direction: TextDirection::Ltr,
167 fallback: "en",
168 coverage: LocaleCoverage::PlannedQa,
169 },
170 LocaleSpec {
171 tag: "fr",
172 display_name: "French",
173 script: "Latin",
174 direction: TextDirection::Ltr,
175 fallback: "en",
176 coverage: LocaleCoverage::PlannedQa,
177 },
178 LocaleSpec {
179 tag: "fil",
180 display_name: "Filipino/Tagalog",
181 script: "Latin",
182 direction: TextDirection::Ltr,
183 fallback: "en",
184 coverage: LocaleCoverage::PlannedQa,
185 },
186 ];
187
188 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
189 pub enum MessageId {
190 ComposerPlaceholder,
191 HistorySearchPlaceholder,
192 HistorySearchTitle,
193 HistoryHintMove,
194 HistoryHintAccept,
195 HistoryHintRestore,
196 HistoryNoMatches,
197 ConfigTitle,
198 ConfigModalTitle,
199 ConfigSearchPlaceholder,
200 ConfigNoSettings,
201 ConfigNoMatchesPrefix,
202 ConfigFilteredSettings,
203 ConfigShowing,
204 ConfigFooterDefault,
205 ConfigFooterScrollable,
206 ConfigFooterFiltered,
207 HelpTitle,
208 HelpFilterPlaceholder,
209 HelpFilterPrefix,
210 HelpNoMatches,
211 HelpSlashCommands,
212 HelpKeybindings,
213 HelpFooterTypeFilter,
214 HelpFooterMove,
215 HelpFooterJump,
216 HelpFooterClose,
217 CmdAgentDescription,
218 CmdAttachDescription,
219 CmdCacheDescription,
220 CmdClearDescription,
221 CmdCompactDescription,
222 CmdConfigDescription,
223 CmdContextDescription,
224 CmdCostDescription,
225 CmdCycleDescription,
226 CmdCyclesDescription,
227 CmdDiffDescription,
228 CmdEditDescription,
229 CmdExitDescription,
230 CmdExportDescription,
231 CmdHelpDescription,
232 CmdHomeDescription,
233 CmdHooksDescription,
234 CmdGoalDescription,
235 CmdInitDescription,
236 CmdJobsDescription,
237 CmdLinksDescription,
238 CmdLoadDescription,
239 CmdLogoutDescription,
240 CmdMcpDescription,
241 CmdMemoryDescription,
242 CmdModelDescription,
243 CmdModelsDescription,
244 CmdNetworkDescription,
245 CmdNoteDescription,
246 CmdPlanDescription,
247 CmdProviderDescription,
248 CmdQueueDescription,
249 CmdRecallDescription,
250 CmdRenameDescription,
251 CmdRestoreDescription,
252 CmdRetryDescription,
253 CmdReviewDescription,
254 CmdRlmDescription,
255 CmdSaveDescription,
256 CmdSessionsDescription,
257 CmdSettingsDescription,
258 CmdSkillDescription,
259 CmdSkillsDescription,
260 CmdStashDescription,
261 CmdStatuslineDescription,
262 CmdSubagentsDescription,
263 CmdSwarmDescription,
264 CmdSystemDescription,
265 CmdTaskDescription,
266 CmdTokensDescription,
267 CmdTrustDescription,
268 CmdLspDescription,
269 CmdShareDescription,
270 CmdUndoDescription,
271 CmdYoloDescription,
272 CmdCacheAdvice,
273 CmdCacheFootnote,
274 CmdCacheHeader,
275 CmdCacheNoData,
276 CmdCacheTotals,
277 CmdCostReport,
278 CmdTokensCacheBoth,
279 CmdTokensCacheHitOnly,
280 CmdTokensCacheMissOnly,
281 CmdTokensContextUnknownWindow,
282 CmdTokensContextWithWindow,
283 CmdTokensNotReported,
284 CmdTokensReport,
285 FooterAgentSingular,
286 FooterAgentsPlural,
287 FooterPressCtrlCAgain,
288 FooterWorking,
289 HelpSectionActions,
290 HelpSectionClipboard,
291 HelpSectionEditing,
292 HelpSectionHelp,
293 HelpSectionModes,
294 HelpSectionNavigation,
295 HelpSectionSessions,
296 KbScrollTranscript,
297 KbNavigateHistory,
298 KbScrollTranscriptAlt,
299 KbScrollPage,
300 KbJumpTopBottom,
301 KbJumpTopBottomEmpty,
302 KbJumpToolBlocks,
303 KbMoveCursor,
304 KbJumpLineStartEnd,
305 KbDeleteChar,
306 KbClearDraft,
307 KbStashDraft,
308 KbSearchHistory,
309 KbInsertNewline,
310 KbSendDraft,
311 KbCloseMenu,
312 KbCancelOrExit,
313 KbShellControls,
314 KbExitEmpty,
315 KbCommandPalette,
316 KbFuzzyFilePicker,
317 KbCompactInspector,
318 KbLastMessagePager,
319 KbSelectedDetails,
320 KbToolDetailsPager,
321 KbThinkingPager,
322 KbLiveTranscript,
323 KbBacktrackMessage,
324 KbCompleteCycleModes,
325 KbJumpPlanAgentYolo,
326 KbAltJumpPlanAgentYolo,
327 KbFocusSidebar,
328 KbTogglePlanAgent,
329 KbSessionPicker,
330 KbPasteAttach,
331 KbCopySelection,
332 KbContextMenu,
333 KbAttachPath,
334 KbHelpOverlay,
335 KbToggleHelp,
336 KbToggleHelpSlash,
337 HelpUsageLabel,
338 HelpAliasesLabel,
339 SettingsTitle,
340 SettingsConfigFile,
341 ClearConversation,
342 ClearConversationBusy,
343 ModelChanged,
344 LinksTitle,
345 LinksDashboard,
346 LinksDocs,
347 LinksTip,
348 SubagentsFetching,
349 HelpUnknownCommand,
350 HomeDashboardTitle,
351 HomeModel,
352 HomeMode,
353 HomeWorkspace,
354 HomeHistory,
355 HomeTokens,
356 HomeQueued,
357 HomeSubagents,
358 HomeSkill,
359 HomeQuickActions,
360 HomeQuickLinks,
361 HomeQuickSkills,
362 HomeQuickConfig,
363 HomeQuickSettings,
364 HomeQuickModel,
365 HomeQuickSubagents,
366 HomeQuickTaskList,
367 HomeQuickHelp,
368 HomeModeTips,
369 HomeAgentModeTip,
370 HomeAgentModeReviewTip,
371 HomeAgentModeYoloTip,
372 HomeYoloModeTip,
373 HomeYoloModeCaution,
374 HomePlanModeTip,
375 HomePlanModeChecklistTip,
376 }
377
378 #[allow(dead_code)]
379 pub const ALL_MESSAGE_IDS: &[MessageId] = &[
380 MessageId::ComposerPlaceholder,
381 MessageId::HistorySearchPlaceholder,
382 MessageId::HistorySearchTitle,
383 MessageId::HistoryHintMove,
384 MessageId::HistoryHintAccept,
385 MessageId::HistoryHintRestore,
386 MessageId::HistoryNoMatches,
387 MessageId::ConfigTitle,
388 MessageId::ConfigModalTitle,
389 MessageId::ConfigSearchPlaceholder,
390 MessageId::ConfigNoSettings,
391 MessageId::ConfigNoMatchesPrefix,
392 MessageId::ConfigFilteredSettings,
393 MessageId::ConfigShowing,
394 MessageId::ConfigFooterDefault,
395 MessageId::ConfigFooterScrollable,
396 MessageId::ConfigFooterFiltered,
397 MessageId::HelpTitle,
398 MessageId::HelpFilterPlaceholder,
399 MessageId::HelpFilterPrefix,
400 MessageId::HelpNoMatches,
401 MessageId::HelpSlashCommands,
402 MessageId::HelpKeybindings,
403 MessageId::HelpFooterTypeFilter,
404 MessageId::HelpFooterMove,
405 MessageId::HelpFooterJump,
406 MessageId::HelpFooterClose,
407 MessageId::CmdAgentDescription,
408 MessageId::CmdAttachDescription,
409 MessageId::CmdCacheDescription,
410 MessageId::CmdClearDescription,
411 MessageId::CmdCompactDescription,
412 MessageId::CmdConfigDescription,
413 MessageId::CmdContextDescription,
414 MessageId::CmdCostDescription,
415 MessageId::CmdCycleDescription,
416 MessageId::CmdCyclesDescription,
417 MessageId::CmdDiffDescription,
418 MessageId::CmdEditDescription,
419 MessageId::CmdExitDescription,
420 MessageId::CmdExportDescription,
421 MessageId::CmdHelpDescription,
422 MessageId::CmdHomeDescription,
423 MessageId::CmdHooksDescription,
424 MessageId::CmdInitDescription,
425 MessageId::CmdJobsDescription,
426 MessageId::CmdLinksDescription,
427 MessageId::CmdLoadDescription,
428 MessageId::CmdLogoutDescription,
429 MessageId::CmdMcpDescription,
430 MessageId::CmdMemoryDescription,
431 MessageId::CmdModelDescription,
432 MessageId::CmdModelsDescription,
433 MessageId::CmdNetworkDescription,
434 MessageId::CmdNoteDescription,
435 MessageId::CmdPlanDescription,
436 MessageId::CmdProviderDescription,
437 MessageId::CmdQueueDescription,
438 MessageId::CmdRecallDescription,
439 MessageId::CmdRenameDescription,
440 MessageId::CmdRestoreDescription,
441 MessageId::CmdRetryDescription,
442 MessageId::CmdReviewDescription,
443 MessageId::CmdRlmDescription,
444 MessageId::CmdSaveDescription,
445 MessageId::CmdSessionsDescription,
446 MessageId::CmdSettingsDescription,
447 MessageId::CmdSkillDescription,
448 MessageId::CmdSkillsDescription,
449 MessageId::CmdStashDescription,
450 MessageId::CmdStatuslineDescription,
451 MessageId::CmdSubagentsDescription,
452 MessageId::CmdSwarmDescription,
453 MessageId::CmdSystemDescription,
454 MessageId::CmdTaskDescription,
455 MessageId::CmdTokensDescription,
456 MessageId::CmdTrustDescription,
457 MessageId::CmdLspDescription,
458 MessageId::CmdShareDescription,
459 MessageId::CmdUndoDescription,
460 MessageId::CmdYoloDescription,
461 MessageId::CmdCacheAdvice,
462 MessageId::CmdCacheFootnote,
463 MessageId::CmdCacheHeader,
464 MessageId::CmdCacheNoData,
465 MessageId::CmdCacheTotals,
466 MessageId::CmdCostReport,
467 MessageId::CmdTokensCacheBoth,
468 MessageId::CmdTokensCacheHitOnly,
469 MessageId::CmdTokensCacheMissOnly,
470 MessageId::CmdTokensContextUnknownWindow,
471 MessageId::CmdTokensContextWithWindow,
472 MessageId::CmdTokensNotReported,
473 MessageId::CmdTokensReport,
474 MessageId::FooterAgentSingular,
475 MessageId::FooterAgentsPlural,
476 MessageId::FooterPressCtrlCAgain,
477 MessageId::FooterWorking,
478 MessageId::HelpSectionActions,
479 MessageId::HelpSectionClipboard,
480 MessageId::HelpSectionEditing,
481 MessageId::HelpSectionHelp,
482 MessageId::HelpSectionModes,
483 MessageId::HelpSectionNavigation,
484 MessageId::HelpSectionSessions,
485 MessageId::KbScrollTranscript,
486 MessageId::KbNavigateHistory,
487 MessageId::KbScrollTranscriptAlt,
488 MessageId::KbScrollPage,
489 MessageId::KbJumpTopBottom,
490 MessageId::KbJumpTopBottomEmpty,
491 MessageId::KbJumpToolBlocks,
492 MessageId::KbMoveCursor,
493 MessageId::KbJumpLineStartEnd,
494 MessageId::KbDeleteChar,
495 MessageId::KbClearDraft,
496 MessageId::KbStashDraft,
497 MessageId::KbSearchHistory,
498 MessageId::KbInsertNewline,
499 MessageId::KbSendDraft,
500 MessageId::KbCloseMenu,
501 MessageId::KbCancelOrExit,
502 MessageId::KbShellControls,
503 MessageId::KbExitEmpty,
504 MessageId::KbCommandPalette,
505 MessageId::KbFuzzyFilePicker,
506 MessageId::KbCompactInspector,
507 MessageId::KbLastMessagePager,
508 MessageId::KbSelectedDetails,
509 MessageId::KbToolDetailsPager,
510 MessageId::KbThinkingPager,
511 MessageId::KbLiveTranscript,
512 MessageId::KbBacktrackMessage,
513 MessageId::KbCompleteCycleModes,
514 MessageId::KbJumpPlanAgentYolo,
515 MessageId::KbAltJumpPlanAgentYolo,
516 MessageId::KbFocusSidebar,
517 MessageId::KbTogglePlanAgent,
518 MessageId::KbSessionPicker,
519 MessageId::KbPasteAttach,
520 MessageId::KbCopySelection,
521 MessageId::KbContextMenu,
522 MessageId::KbAttachPath,
523 MessageId::KbHelpOverlay,
524 MessageId::KbToggleHelp,
525 MessageId::KbToggleHelpSlash,
526 MessageId::HelpUsageLabel,
527 MessageId::HelpAliasesLabel,
528 MessageId::SettingsTitle,
529 MessageId::SettingsConfigFile,
530 MessageId::ClearConversation,
531 MessageId::ClearConversationBusy,
532 MessageId::ModelChanged,
533 MessageId::LinksTitle,
534 MessageId::LinksDashboard,
535 MessageId::LinksDocs,
536 MessageId::LinksTip,
537 MessageId::SubagentsFetching,
538 MessageId::HelpUnknownCommand,
539 MessageId::HomeDashboardTitle,
540 MessageId::HomeModel,
541 MessageId::HomeMode,
542 MessageId::HomeWorkspace,
543 MessageId::HomeHistory,
544 MessageId::HomeTokens,
545 MessageId::HomeQueued,
546 MessageId::HomeSubagents,
547 MessageId::HomeSkill,
548 MessageId::HomeQuickActions,
549 MessageId::HomeQuickLinks,
550 MessageId::HomeQuickSkills,
551 MessageId::HomeQuickConfig,
552 MessageId::HomeQuickSettings,
553 MessageId::HomeQuickModel,
554 MessageId::HomeQuickSubagents,
555 MessageId::HomeQuickTaskList,
556 MessageId::HomeQuickHelp,
557 MessageId::HomeModeTips,
558 MessageId::HomeAgentModeTip,
559 MessageId::HomeAgentModeReviewTip,
560 MessageId::HomeAgentModeYoloTip,
561 MessageId::HomeYoloModeTip,
562 MessageId::HomeYoloModeCaution,
563 MessageId::HomePlanModeTip,
564 MessageId::HomePlanModeChecklistTip,
565 ];
566
567 pub fn tr(locale: Locale, id: MessageId) -> &'static str {
568 fallback_translation(translation(locale, id), id)
569 }
570
571 #[allow(dead_code)]
572 pub fn missing_message_ids(locale: Locale) -> Vec<MessageId> {
573 ALL_MESSAGE_IDS
574 .iter()
575 .copied()
576 .filter(|id| translation(locale, *id).is_none())
577 .collect()
578 }
579
580 pub fn normalize_configured_locale(input: &str) -> Option<&'static str> {
581 let normalized = normalize_locale_input(input);
582 if matches!(normalized.as_str(), "" | "auto" | "system") {
583 return Some("auto");
584 }
585 parse_locale(&normalized).map(Locale::tag)
586 }
587
588 pub fn resolve_locale(setting: &str) -> Locale {
589 resolve_locale_with_env(setting, |key| std::env::var(key).ok())
590 }
591
592 pub fn resolve_locale_with_env<F>(setting: &str, env: F) -> Locale
593 where
594 F: Fn(&str) -> Option<String>,
595 {
596 let normalized = normalize_locale_input(setting);
597 if !matches!(normalized.as_str(), "" | "auto" | "system") {
598 return parse_locale(&normalized).unwrap_or(Locale::En);
599 }
600
601 for key in ["LC_ALL", "LC_MESSAGES", "LANG"] {
602 if let Some(value) = env(key)
603 && let Some(locale) = parse_locale(&normalize_locale_input(&value))
604 {
605 return locale;
606 }
607 }
608
609 Locale::En
610 }
611
612 #[allow(dead_code)]
613 pub fn truncate_to_width(text: &str, max_width: usize) -> String {
614 if max_width == 0 {
615 return String::new();
616 }
617 if text.width() <= max_width {
618 return text.to_string();
619 }
620
621 let ellipsis_width = '…'.width().unwrap_or(1);
622 if max_width <= ellipsis_width {
623 return "…".to_string();
624 }
625
626 let limit = max_width - ellipsis_width;
627 let mut out = String::new();
628 let mut width = 0usize;
629 for ch in text.chars() {
630 let ch_width = ch.width().unwrap_or(0);
631 if width + ch_width > limit {
632 break;
633 }
634 out.push(ch);
635 width += ch_width;
636 }
637 out.push('…');
638 out
639 }
640
641 fn normalize_locale_input(input: &str) -> String {
642 input
643 .split('.')
644 .next()
645 .unwrap_or(input)
646 .split('@')
647 .next()
648 .unwrap_or(input)
649 .trim()
650 .replace('_', "-")
651 .to_lowercase()
652 }
653
654 fn parse_locale(value: &str) -> Option<Locale> {
655 if value == "c" || value == "posix" || value.starts_with("en") {
656 return Some(Locale::En);
657 }
658 if value.starts_with("ja") {
659 return Some(Locale::Ja);
660 }
661 if value.starts_with("zh") {
662 if value.contains("hant")
663 || value.contains("-tw")
664 || value.contains("-hk")
665 || value.contains("-mo")
666 {
667 return None;
668 }
669 return Some(Locale::ZhHans);
670 }
671 if value.starts_with("pt") || value == "br" {
672 return Some(Locale::PtBr);
673 }
674 None
675 }
676
677 fn fallback_translation(candidate: Option<&'static str>, id: MessageId) -> &'static str {
678 candidate.unwrap_or_else(|| english(id))
679 }
680
681 fn english(id: MessageId) -> &'static str {
682 match id {
683 MessageId::ComposerPlaceholder => "Write a task or use /.",
684 MessageId::HistorySearchPlaceholder => "Search prompt history...",
685 MessageId::HistorySearchTitle => "History Search",
686 MessageId::HistoryHintMove => "Up/Down move",
687 MessageId::HistoryHintAccept => "Enter accept",
688 MessageId::HistoryHintRestore => "Esc restore",
689 MessageId::HistoryNoMatches => " No matches",
690 MessageId::ConfigTitle => "Session Configuration",
691 MessageId::ConfigModalTitle => " Config ",
692 MessageId::ConfigSearchPlaceholder => "type to filter",
693 MessageId::ConfigNoSettings => " No settings available.",
694 MessageId::ConfigNoMatchesPrefix => " No settings match ",
695 MessageId::ConfigFilteredSettings => " Filtered settings",
696 MessageId::ConfigShowing => " Showing",
697 MessageId::ConfigFooterDefault => {
698 " type=filter, Up/Down=select, Enter/e=edit, Esc/q=close "
699 }
700 MessageId::ConfigFooterScrollable => {
701 " type=filter, Up/Down=select, Enter/e=edit, PgUp/PgDn=scroll, Esc/q=close "
702 }
703 MessageId::ConfigFooterFiltered => {
704 " type=filter, Backspace=delete, Ctrl+U/Esc=clear, Enter=edit "
705 }
706 MessageId::HelpTitle => "Help",
707 MessageId::HelpFilterPlaceholder => "Type to filter",
708 MessageId::HelpFilterPrefix => "Filter: ",
709 MessageId::HelpNoMatches => " No matches.",
710 MessageId::HelpSlashCommands => "Slash commands",
711 MessageId::HelpKeybindings => "Keybindings",
712 MessageId::HelpFooterTypeFilter => " type to filter ",
713 MessageId::HelpFooterMove => " Up/Down move ",
714 MessageId::HelpFooterJump => " PgUp/PgDn jump ",
715 MessageId::HelpFooterClose => " Esc close ",
716 MessageId::CmdAgentDescription => "Switch to agent mode",
717 MessageId::CmdAttachDescription => {
718 "Attach image/video media; use @path for text files or directories"
719 }
720 MessageId::CmdCacheDescription => {
721 "Show DeepSeek prefix-cache hit/miss stats for the last N turns"
722 }
723 MessageId::CmdClearDescription => "Clear conversation history",
724 MessageId::CmdCompactDescription => {
725 "Trigger context compaction to free up space (legacy; v0.6.6 prefers cycle restart)"
726 }
727 MessageId::CmdConfigDescription => "Open interactive configuration editor",
728 MessageId::CmdContextDescription => "Open compact session context inspector",
729 MessageId::CmdCostDescription => "Show session cost breakdown",
730 MessageId::CmdCycleDescription => "Show the carry-forward briefing for a specific cycle",
731 MessageId::CmdCyclesDescription => "List checkpoint-restart cycle handoffs in this session",
732 MessageId::CmdDiffDescription => "Show file changes since session start",
733 MessageId::CmdEditDescription => "Revise and resubmit the last message",
734 MessageId::CmdExitDescription => "Exit the application",
735 MessageId::CmdExportDescription => "Export conversation to markdown",
736 MessageId::CmdHelpDescription => "Show help information",
737 MessageId::CmdHomeDescription => "Show home dashboard with stats and quick actions",
738 MessageId::CmdHooksDescription => "List configured lifecycle hooks (read-only)",
739 MessageId::CmdGoalDescription => "Set a session goal with optional token budget",
740 MessageId::CmdInitDescription => "Generate AGENTS.md for project",
741 MessageId::CmdLspDescription => "Toggle LSP diagnostics on or off",
742 MessageId::CmdShareDescription => "Export current session as a shareable web URL",
743 MessageId::CmdJobsDescription => "Inspect and control background shell jobs",
744 MessageId::CmdLinksDescription => "Show DeepSeek dashboard and docs links",
745 MessageId::CmdLoadDescription => "Load session from file",
746 MessageId::CmdLogoutDescription => "Clear API key and return to setup",
747 MessageId::CmdMcpDescription => "Open or manage MCP servers",
748 MessageId::CmdMemoryDescription => "Inspect or manage the persistent user-memory file",
749 MessageId::CmdModelDescription => "Switch or view current model",
750 MessageId::CmdModelsDescription => "List available models from API",
751 MessageId::CmdNetworkDescription => "Manage network allow and deny rules",
752 MessageId::CmdNoteDescription => {
753 "Append note to persistent notes file (.deepseek/notes.md)"
754 }
755 MessageId::CmdPlanDescription => {
756 "Switch to plan mode and review suggested implementation steps"
757 }
758 MessageId::CmdProviderDescription => {
759 "Switch or view the active LLM backend (deepseek | nvidia-nim)"
760 }
761 MessageId::CmdQueueDescription => "View or edit queued messages",
762 MessageId::CmdRecallDescription => "Search prior cycle archives (BM25 over message text)",
763 MessageId::CmdRenameDescription => "Rename the current session",
764 MessageId::CmdRestoreDescription => {
765 "Roll back the workspace to a prior pre/post-turn snapshot. With no arg, lists recent snapshots."
766 }
767 MessageId::CmdRetryDescription => "Retry the last request",
768 MessageId::CmdReviewDescription => "Run a structured code review on a file, diff, or PR",
769 MessageId::CmdRlmDescription => {
770 "Recursive Language Model (RLM) turn — store the prompt in a Python REPL and let the model write code to process it, with `llm_query()` / `sub_rlm()` for sub-LLM calls."
771 }
772 MessageId::CmdSaveDescription => "Save session to file",
773 MessageId::CmdSessionsDescription => "Open session picker",
774 MessageId::CmdSettingsDescription => "Show persistent settings",
775 MessageId::CmdSkillDescription => {
776 "Activate a skill, or install/update/uninstall/trust a community skill"
777 }
778 MessageId::CmdSkillsDescription => {
779 "List local skills (or --remote to browse the curated registry)"
780 }
781 MessageId::CmdStashDescription => {
782 "Park or restore a composer draft (Ctrl+S to push, /stash list/pop)"
783 }
784 MessageId::CmdStatuslineDescription => "Configure which items appear in the footer",
785 MessageId::CmdSubagentsDescription => "List sub-agent status",
786 MessageId::CmdSwarmDescription => {
787 "Run a multi-agent fanout turn (sequential | mixture | distill | deliberate)"
788 }
789 MessageId::CmdSystemDescription => "Show current system prompt",
790 MessageId::CmdTaskDescription => "Manage background tasks",
791 MessageId::CmdTokensDescription => "Show token usage for session",
792 MessageId::CmdTrustDescription => {
793 "Manage workspace trust and per-path allowlist (`/trust add <path>`, `/trust list`, `/trust on|off`)"
794 }
795 MessageId::CmdUndoDescription => "Remove last message pair",
796 MessageId::CmdYoloDescription => "Enable YOLO mode (shell + trust + auto-approve)",
797 MessageId::CmdCacheAdvice => {
798 "Hit/miss ratios over ~70% after the third turn indicate a stable cache prefix; \n\
799 lower than that on long sessions suggests prefix churn worth investigating (#263)."
800 }
801 MessageId::CmdCacheFootnote => {
802 "* miss inferred from input − hit when the provider did not report it explicitly.\n"
803 }
804 MessageId::CmdCacheHeader => {
805 "Cache telemetry — last {count} of {total} turn(s) (model: {model})\n"
806 }
807 MessageId::CmdCacheNoData => {
808 "Cache history: no turns recorded yet.\n\n\
809 DeepSeek surfaces `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens` \
810 on every API turn that the model supports it (V4 family). Run a turn \
811 and try /cache again."
812 }
813 MessageId::CmdCacheTotals => {
814 "Σ in: {sum_in} Σ hit: {sum_hit} Σ miss: {sum_miss} avg hit ratio: {avg}\n"
815 }
816 MessageId::CmdCostReport => {
817 "Session Cost:\n\
818 ─────────────────────────────\n\
819 Approx total spent: {cost}\n\n\
820 Cost estimates are approximate and use provider usage telemetry when available.\n\n\
821 DeepSeek API Pricing:\n\
822 ─────────────────────────────\n\
823 Pricing details are not configured in this CLI."
824 }
825 MessageId::CmdTokensCacheBoth => "{hit} hit / {miss} miss",
826 MessageId::CmdTokensCacheHitOnly => "{hit} hit / miss not reported",
827 MessageId::CmdTokensCacheMissOnly => "hit not reported / {miss} miss",
828 MessageId::CmdTokensContextUnknownWindow => "~{estimated} / unknown window",
829 MessageId::CmdTokensContextWithWindow => "~{used} / {window} ({percent}%)",
830 MessageId::FooterAgentSingular => "1 agent",
831 MessageId::FooterAgentsPlural => "{count} agents",
832 MessageId::FooterPressCtrlCAgain => "Press Ctrl+C again to quit",
833 MessageId::FooterWorking => "working",
834 MessageId::HelpSectionActions => "Actions",
835 MessageId::HelpSectionClipboard => "Clipboard",
836 MessageId::HelpSectionEditing => "Input editing",
837 MessageId::HelpSectionHelp => "Help",
838 MessageId::HelpSectionModes => "Modes",
839 MessageId::HelpSectionNavigation => "Navigation",
840 MessageId::HelpSectionSessions => "Sessions",
841 MessageId::CmdTokensNotReported => "not reported",
842 MessageId::CmdTokensReport => {
843 "Token Usage:\n\
844 ─────────────────────────────\n\
845 Active context: {active}\n\
846 Last API input: {input} (turn telemetry; may count repeated prefix across tool rounds)\n\
847 Last API output: {output}\n\
848 Cache hit/miss: {cache} (telemetry/cost only)\n\
849 Cumulative tokens: {total} (session usage telemetry)\n\
850 Approx session cost: {cost}\n\
851 API messages: {api_messages}\n\
852 Chat messages: {chat_messages}\n\
853 Model: {model}"
854 }
855 MessageId::KbScrollTranscript => {
856 "Scroll transcript, navigate input history, or select composer attachments"
857 }
858 MessageId::KbNavigateHistory => "Navigate input history",
859 MessageId::KbScrollTranscriptAlt => "Scroll transcript",
860 MessageId::KbScrollPage => "Scroll transcript by page",
861 MessageId::KbJumpTopBottom => "Jump to top / bottom of transcript",
862 MessageId::KbJumpTopBottomEmpty => "Jump to top / bottom (when input is empty)",
863 MessageId::KbJumpToolBlocks => "Jump between tool output blocks",
864 MessageId::KbMoveCursor => "Move cursor in composer",
865 MessageId::KbJumpLineStartEnd => "Jump to start / end of line",
866 MessageId::KbDeleteChar => {
867 "Delete character before / after the cursor, or remove selected attachment"
868 }
869 MessageId::KbClearDraft => "Clear the current draft",
870 MessageId::KbStashDraft => "Stash the current draft (`/stash pop` to restore)",
871 MessageId::KbSearchHistory => "Search prompt history and recover local drafts",
872 MessageId::KbInsertNewline => "Insert a newline in the composer",
873 MessageId::KbSendDraft => "Send the current draft",
874 MessageId::KbCloseMenu => "Close menu, cancel request, discard draft, or clear input",
875 MessageId::KbCancelOrExit => "Cancel request, or exit when idle",
876 MessageId::KbShellControls => "Open shell controls for a running foreground command",
877 MessageId::KbExitEmpty => "Exit when input is empty",
878 MessageId::KbCommandPalette => "Open the command palette",
879 MessageId::KbFuzzyFilePicker => "Open the fuzzy file picker (insert @path on Enter)",
880 MessageId::KbCompactInspector => "Open compact session context inspector",
881 MessageId::KbLastMessagePager => "Open pager for the last message (when input is empty)",
882 MessageId::KbSelectedDetails => {
883 "Open details for the selected tool or message (when input is empty)"
884 }
885 MessageId::KbToolDetailsPager => "Open tool-details pager",
886 MessageId::KbThinkingPager => "Open thinking pager",
887 MessageId::KbLiveTranscript => "Open live transcript overlay (sticky-tail auto-scroll)",
888 MessageId::KbBacktrackMessage => {
889 "Backtrack to a previous user message (Left/Right step, Enter to rewind)"
890 }
891 MessageId::KbCompleteCycleModes => {
892 "Complete /command, queue running-turn follow-up, cycle modes; Shift+Tab cycles reasoning effort"
893 }
894 MessageId::KbJumpPlanAgentYolo => "Jump directly to Plan / Agent / YOLO mode",
895 MessageId::KbAltJumpPlanAgentYolo => "Alternative jump to Plan / Agent / YOLO mode",
896 MessageId::KbFocusSidebar => "Focus Plan / Todos / Tasks / Agents / Auto sidebar",
897 MessageId::KbTogglePlanAgent => "Toggle between Plan and Agent modes",
898 MessageId::KbSessionPicker => "Open the session picker",
899 MessageId::KbPasteAttach => "Paste text or attach a clipboard image",
900 MessageId::KbCopySelection => "Copy the current selection (Cmd+C on macOS)",
901 MessageId::KbContextMenu => {
902 "Open context actions for paste, selection, message details, context, and help"
903 }
904 MessageId::KbAttachPath => "Add a local text file or directory to context",
905 MessageId::KbHelpOverlay => "Open this help overlay (when input is empty)",
906 MessageId::KbToggleHelp => "Toggle help overlay",
907 MessageId::KbToggleHelpSlash => "Toggle help overlay",
908 MessageId::HelpUsageLabel => "Usage:",
909 MessageId::HelpAliasesLabel => "Aliases:",
910 MessageId::SettingsTitle => "Settings:",
911 MessageId::SettingsConfigFile => "Config file:",
912 MessageId::ClearConversation => "Conversation cleared",
913 MessageId::ClearConversationBusy => {
914 "Conversation cleared (plan state busy; run /clear again if needed)"
915 }
916 MessageId::ModelChanged => "Model changed: {old} \u{2192} {new}",
917 MessageId::LinksTitle => "DeepSeek Links:",
918 MessageId::LinksDashboard => "Dashboard:",
919 MessageId::LinksDocs => "Docs:",
920 MessageId::LinksTip => "Tip: API keys are available in the dashboard console.",
921 MessageId::SubagentsFetching => "Fetching sub-agent status...",
922 MessageId::HelpUnknownCommand => "Unknown command: {topic}",
923 MessageId::HomeDashboardTitle => "DeepSeek TUI Home Dashboard",
924 MessageId::HomeModel => "Model:",
925 MessageId::HomeMode => "Mode:",
926 MessageId::HomeWorkspace => "Workspace:",
927 MessageId::HomeHistory => "History:",
928 MessageId::HomeTokens => "Tokens:",
929 MessageId::HomeQueued => "Queued:",
930 MessageId::HomeSubagents => "Sub-agents:",
931 MessageId::HomeSkill => "Skill:",
932 MessageId::HomeQuickActions => "Quick Actions",
933 MessageId::HomeQuickLinks => "/links - Dashboard & API links",
934 MessageId::HomeQuickSkills => "/skills - List available skills",
935 MessageId::HomeQuickConfig => "/config - Open interactive configuration editor",
936 MessageId::HomeQuickSettings => "/settings - Show persistent settings",
937 MessageId::HomeQuickModel => "/model - Switch or view model",
938 MessageId::HomeQuickSubagents => "/subagents - List sub-agent status",
939 MessageId::HomeQuickTaskList => "/task list - Show background task queue",
940 MessageId::HomeQuickHelp => "/help - Show help",
941 MessageId::HomeModeTips => "Mode Tips",
942 MessageId::HomeAgentModeTip => "Agent mode - Use tools for autonomous tasks",
943 MessageId::HomeAgentModeReviewTip => " Use Ctrl+X to review in Plan mode before executing",
944 MessageId::HomeAgentModeYoloTip => " Type /yolo to enable full tool access",
945 MessageId::HomeYoloModeTip => "YOLO mode - Full tool access, no approvals",
946 MessageId::HomeYoloModeCaution => " Be careful with destructive operations!",
947 MessageId::HomePlanModeTip => "Plan mode - Design before implementing",
948 MessageId::HomePlanModeChecklistTip => " Use /plan to create structured checklists",
949 }
950 }
951
952 fn translation(locale: Locale, id: MessageId) -> Option<&'static str> {
953 match locale {
954 Locale::En => Some(english(id)),
955 Locale::Ja => japanese(id),
956 Locale::ZhHans => chinese_simplified(id),
957 Locale::PtBr => portuguese_brazil(id),
958 }
959 }
960
961 fn japanese(id: MessageId) -> Option<&'static str> {
962 Some(match id {
963 MessageId::ComposerPlaceholder => "タスクを書くか / を使う。",
964 MessageId::HistorySearchPlaceholder => "プロンプト履歴を検索...",
965 MessageId::HistorySearchTitle => "履歴検索",
966 MessageId::HistoryHintMove => "Up/Down 移動",
967 MessageId::HistoryHintAccept => "Enter 確定",
968 MessageId::HistoryHintRestore => "Esc 復元",
969 MessageId::HistoryNoMatches => " 一致なし",
970 MessageId::ConfigTitle => "セッション設定",
971 MessageId::ConfigModalTitle => " 設定 ",
972 MessageId::ConfigSearchPlaceholder => "入力して絞り込み",
973 MessageId::ConfigNoSettings => " 設定がありません。",
974 MessageId::ConfigNoMatchesPrefix => " 一致する設定なし: ",
975 MessageId::ConfigFilteredSettings => " 絞り込み後の設定",
976 MessageId::ConfigShowing => " 表示",
977 MessageId::ConfigFooterDefault => {
978 " 入力=絞り込み, Up/Down=選択, Enter/e=編集, Esc/q=閉じる "
979 }
980 MessageId::ConfigFooterScrollable => {
981 " 入力=絞り込み, Up/Down=選択, Enter/e=編集, PgUp/PgDn=スクロール, Esc/q=閉じる "
982 }
983 MessageId::ConfigFooterFiltered => {
984 " 入力=絞り込み, Backspace=削除, Ctrl+U/Esc=クリア, Enter=編集 "
985 }
986 MessageId::HelpTitle => "ヘルプ",
987 MessageId::HelpFilterPlaceholder => "入力して絞り込み",
988 MessageId::HelpFilterPrefix => "絞り込み: ",
989 MessageId::HelpNoMatches => " 一致なし。",
990 MessageId::HelpSlashCommands => "スラッシュコマンド",
991 MessageId::HelpKeybindings => "キー操作",
992 MessageId::HelpFooterTypeFilter => " 入力して絞り込み ",
993 MessageId::HelpFooterMove => " Up/Down 移動 ",
994 MessageId::HelpFooterJump => " PgUp/PgDn ジャンプ ",
995 MessageId::HelpFooterClose => " Esc 閉じる ",
996 MessageId::CmdAgentDescription => "Agent モードに切り替え",
997 MessageId::CmdAttachDescription => {
998 "画像・動画メディアを添付(テキストファイルやディレクトリは @path)"
999 }
1000 MessageId::CmdCacheDescription => {
1001 "直近 N ターンの DeepSeek プレフィックスキャッシュのヒット/ミス統計を表示"
1002 }
1003 MessageId::CmdClearDescription => "会話履歴をクリア",
1004 MessageId::CmdCompactDescription => {
1005 "コンテキスト圧縮で容量を確保(旧式:v0.6.6 以降はサイクル再起動を推奨)"
1006 }
1007 MessageId::CmdConfigDescription => "インタラクティブな設定エディタを開く",
1008 MessageId::CmdContextDescription => "コンパクトなセッションコンテキスト検査ツールを開く",
1009 MessageId::CmdCostDescription => "セッションのコスト内訳を表示",
1010 MessageId::CmdCycleDescription => "指定したサイクルの引き継ぎブリーフィングを表示",
1011 MessageId::CmdCyclesDescription => {
1012 "セッション内のチェックポイント再起動サイクルの引き継ぎを一覧表示"
1013 }
1014 MessageId::CmdDiffDescription => "セッション開始以降のファイル変更を表示",
1015 MessageId::CmdEditDescription => "最後のメッセージを編集して再送信",
1016 MessageId::CmdExitDescription => "アプリを終了",
1017 MessageId::CmdExportDescription => "会話を Markdown にエクスポート",
1018 MessageId::CmdHelpDescription => "ヘルプを表示",
1019 MessageId::CmdHomeDescription => "統計とクイックアクション付きのホームダッシュボードを表示",
1020 MessageId::CmdHooksDescription => {
1021 "設定済みのライフサイクルフックを一覧表示(読み取り専用)"
1022 }
1023 MessageId::CmdGoalDescription => "トークンバジェット付きのセッション目標を設定",
1024 MessageId::CmdInitDescription => "プロジェクト用に AGENTS.md を生成",
1025 MessageId::CmdLspDescription => "LSP 診断のオン・オフを切り替え",
1026 MessageId::CmdShareDescription => "現在のセッションを共有可能な Web URL としてエクスポート",
1027 MessageId::CmdJobsDescription => "バックグラウンドのシェルジョブを確認・制御",
1028 MessageId::CmdLinksDescription => "DeepSeek ダッシュボードとドキュメントへのリンクを表示",
1029 MessageId::CmdLoadDescription => "ファイルからセッションを読み込み",
1030 MessageId::CmdLogoutDescription => "API キーを消去してセットアップに戻る",
1031 MessageId::CmdMcpDescription => "MCP サーバを開く・管理する",
1032 MessageId::CmdMemoryDescription => "永続ユーザーメモリファイルを確認・管理",
1033 MessageId::CmdModelDescription => "現在のモデルを切り替え・確認",
1034 MessageId::CmdModelsDescription => "API から利用可能なモデルを一覧表示",
1035 MessageId::CmdNetworkDescription => "ネットワーク許可・拒否ルールを管理",
1036 MessageId::CmdNoteDescription => "永続ノートファイル(.deepseek/notes.md)に追記",
1037 MessageId::CmdPlanDescription => "Plan モードに切り替え、推奨される実装手順を確認",
1038 MessageId::CmdProviderDescription => {
1039 "現在の LLM バックエンドを切り替え・確認(deepseek | nvidia-nim)"
1040 }
1041 MessageId::CmdQueueDescription => "キューされたメッセージを確認・編集",
1042 MessageId::CmdRecallDescription => {
1043 "過去のサイクルアーカイブを検索(メッセージ本文への BM25 検索)"
1044 }
1045 MessageId::CmdRenameDescription => "現在のセッションの名前を変更",
1046 MessageId::CmdRestoreDescription => {
1047 "ワークスペースを以前のターン前/後スナップショットへロールバック。引数なしで最近のスナップショットを一覧表示。"
1048 }
1049 MessageId::CmdRetryDescription => "直前のリクエストを再試行",
1050 MessageId::CmdReviewDescription => "ファイル・diff・PR に対して構造化コードレビューを実行",
1051 MessageId::CmdRlmDescription => {
1052 "再帰言語モデル(RLM)ターン — プロンプトを Python REPL に格納し、モデルが処理コードを記述。サブ LLM 呼び出しは `llm_query()` / `sub_rlm()`。"
1053 }
1054 MessageId::CmdSaveDescription => "セッションをファイルに保存",
1055 MessageId::CmdSessionsDescription => "セッションピッカーを開く",
1056 MessageId::CmdSettingsDescription => "永続化された設定を表示",
1057 MessageId::CmdSkillDescription => {
1058 "スキルを有効化、またはコミュニティスキルをインストール/更新/アンインストール/信頼"
1059 }
1060 MessageId::CmdSkillsDescription => {
1061 "ローカルスキルを一覧表示(--remote で精選レジストリを参照)"
1062 }
1063 MessageId::CmdStashDescription => {
1064 "コンポーザーの下書きを退避/復元(Ctrl+S で退避、/stash list|pop)"
1065 }
1066 MessageId::CmdStatuslineDescription => "フッターに表示する項目を設定",
1067 MessageId::CmdSubagentsDescription => "サブエージェントの状態を一覧表示",
1068 MessageId::CmdSwarmDescription => {
1069 "マルチエージェントのファンアウトターンを実行(sequential | mixture | distill | deliberate)"
1070 }
1071 MessageId::CmdSystemDescription => "現在のシステムプロンプトを表示",
1072 MessageId::CmdTaskDescription => "バックグラウンドタスクを管理",
1073 MessageId::CmdTokensDescription => "セッションのトークン使用量を表示",
1074 MessageId::CmdTrustDescription => {
1075 "ワークスペースの信頼設定とパス別許可リストを管理(`/trust add <path>`、`/trust list`、`/trust on|off`)"
1076 }
1077 MessageId::CmdUndoDescription => "最後のメッセージ対を削除",
1078 MessageId::CmdYoloDescription => "YOLO モードを有効化(shell + 信頼 + 自動承認)",
1079 MessageId::CmdCacheAdvice => {
1080 "3 ターン目以降にヒット率が ~70% 以上で安定していれば、プレフィックスキャッシュは健全。\n\
1081 長いセッションでこれを下回る場合はプレフィックスのドリフトの可能性あり (#263)。"
1082 }
1083 MessageId::CmdCacheFootnote => {
1084 "* プロバイダがミスを単独で報告しない場合は「入力 − ヒット」から推定。\n"
1085 }
1086 MessageId::CmdCacheHeader => {
1087 "キャッシュテレメトリ — 直近 {count} / {total} ターン(モデル: {model})\n"
1088 }
1089 MessageId::CmdCacheNoData => {
1090 "キャッシュ履歴: まだターンを記録していません。\n\n\
1091 DeepSeek は対応モデル (V4 系) の各 API ターンで `prompt_cache_hit_tokens` / \
1092 `prompt_cache_miss_tokens` を返します。1 ターン実行してから /cache を再度試してください。"
1093 }
1094 MessageId::CmdCacheTotals => {
1095 "Σ 入力: {sum_in} Σ ヒット: {sum_hit} Σ ミス: {sum_miss} 平均ヒット率: {avg}\n"
1096 }
1097 MessageId::CmdCostReport => {
1098 "セッション費用:\n\
1099 ─────────────────────────────\n\
1100 累計概算: {cost}\n\n\
1101 費用は概算値。プロバイダの使用量テレメトリがあれば優先して使用します。\n\n\
1102 DeepSeek API 料金:\n\
1103 ─────────────────────────────\n\
1104 本 CLI には詳細な料金表は組み込まれていません。"
1105 }
1106 MessageId::CmdTokensCacheBoth => "ヒット {hit} / ミス {miss}",
1107 MessageId::CmdTokensCacheHitOnly => "ヒット {hit} / ミスは未報告",
1108 MessageId::CmdTokensCacheMissOnly => "ヒットは未報告 / ミス {miss}",
1109 MessageId::CmdTokensContextUnknownWindow => "~{estimated} / コンテキスト窓不明",
1110 MessageId::CmdTokensContextWithWindow => "~{used} / {window} ({percent}%)",
1111 MessageId::FooterAgentSingular => "1 エージェント",
1112 MessageId::FooterAgentsPlural => "{count} エージェント",
1113 MessageId::FooterPressCtrlCAgain => "もう一度 Ctrl+C で終了",
1114 MessageId::FooterWorking => "処理中",
1115 MessageId::HelpSectionActions => "操作",
1116 MessageId::HelpSectionClipboard => "クリップボード",
1117 MessageId::HelpSectionEditing => "入力編集",
1118 MessageId::HelpSectionHelp => "ヘルプ",
1119 MessageId::HelpSectionModes => "モード",
1120 MessageId::HelpSectionNavigation => "ナビゲーション",
1121 MessageId::HelpSectionSessions => "セッション",
1122 MessageId::CmdTokensNotReported => "未報告",
1123 MessageId::CmdTokensReport => {
1124 "トークン使用量:\n\
1125 ─────────────────────────────\n\
1126 アクティブコンテキスト: {active}\n\
1127 直近の API 入力: {input}(ターン単位のテレメトリ。複数回のツール往復で同じプレフィックスが重複してカウントされる場合あり)\n\
1128 直近の API 出力: {output}\n\
1129 キャッシュヒット/ミス: {cache}(テレメトリ/コスト用のみ)\n\
1130 累計トークン: {total}(セッション使用量テレメトリ)\n\
1131 セッション費用概算: {cost}\n\
1132 API メッセージ: {api_messages}\n\
1133 チャットメッセージ: {chat_messages}\n\
1134 モデル: {model}"
1135 }
1136 MessageId::KbScrollTranscript => {
1137 "会話履歴をスクロール、入力履歴を移動、または添付ファイルを選択"
1138 }
1139 MessageId::KbNavigateHistory => "入力履歴を移動",
1140 MessageId::KbScrollTranscriptAlt => "会話履歴をスクロール",
1141 MessageId::KbScrollPage => "ページ単位で会話履歴をスクロール",
1142 MessageId::KbJumpTopBottom => "会話履歴の先頭/末尾へジャンプ",
1143 MessageId::KbJumpTopBottomEmpty => "先頭/末尾へジャンプ(入力が空の時)",
1144 MessageId::KbJumpToolBlocks => "ツール出力ブロック間をジャンプ",
1145 MessageId::KbMoveCursor => "コンポーザー内でカーソルを移動",
1146 MessageId::KbJumpLineStartEnd => "行の先頭/末尾へジャンプ",
1147 MessageId::KbDeleteChar => "カーソル前/後の文字を削除、または選択中の添付を削除",
1148 MessageId::KbClearDraft => "現在の下書きをクリア",
1149 MessageId::KbStashDraft => "現在の下書きをスタッシュ(`/stash pop`で復元)",
1150 MessageId::KbSearchHistory => "プロンプト履歴を検索してローカル下書きを復元",
1151 MessageId::KbInsertNewline => "コンポーザーに改行を挿入",
1152 MessageId::KbSendDraft => "現在の下書きを送信",
1153 MessageId::KbCloseMenu => {
1154 "メニューを閉じる、リクエストをキャンセル、下書きを破棄、または入力をクリア"
1155 }
1156 MessageId::KbCancelOrExit => "リクエストをキャンセル、またはアイドル時に終了",
1157 MessageId::KbShellControls => "実行中のフォアグラウンドコマンドのシェル制御を開く",
1158 MessageId::KbExitEmpty => "入力が空の時に終了",
1159 MessageId::KbCommandPalette => "コマンドパレットを開く",
1160 MessageId::KbFuzzyFilePicker => "ファジーファイルピッカーを開く(Enter で @path を挿入)",
1161 MessageId::KbCompactInspector => "コンパクトなセッションコンテキスト検査ツールを開く",
1162 MessageId::KbLastMessagePager => "最後のメッセージのページャーを開く(入力が空の時)",
1163 MessageId::KbSelectedDetails => {
1164 "選択中のツールまたはメッセージの詳細を開く(入力が空の時)"
1165 }
1166 MessageId::KbToolDetailsPager => "ツール詳細のページャーを開く",
1167 MessageId::KbThinkingPager => "思考内容のページャーを開く",
1168 MessageId::KbLiveTranscript => "ライブ会話履歴オーバーレイを開く(自動追尾スクロール)",
1169 MessageId::KbBacktrackMessage => {
1170 "前のユーザーメッセージに戻る(左右でステップ、Enter で巻き戻し)"
1171 }
1172 MessageId::KbCompleteCycleModes => {
1173 "/command を補完、実行中ターンのフォローアップをキュー、モードを切り替え;Shift+Tab で推論強度を切り替え"
1174 }
1175 MessageId::KbJumpPlanAgentYolo => "Plan / Agent / YOLO モードに直接ジャンプ",
1176 MessageId::KbAltJumpPlanAgentYolo => "Plan / Agent / YOLO モードへの代替ジャンプ",
1177 MessageId::KbFocusSidebar => "Plan / Todos / Tasks / Agents / Auto サイドバーにフォーカス",
1178 MessageId::KbTogglePlanAgent => "Plan モードと Agent モードを切り替え",
1179 MessageId::KbSessionPicker => "セッションピッカーを開く",
1180 MessageId::KbPasteAttach => "テキストを貼り付けまたはクリップボード画像を添付",
1181 MessageId::KbCopySelection => "現在の選択をコピー(macOS は Cmd+C)",
1182 MessageId::KbContextMenu => {
1183 "貼り付け、選択、メッセージ詳細、コンテキスト、ヘルプのコンテキスト操作を開く"
1184 }
1185 MessageId::KbAttachPath => {
1186 "ローカルのテキストファイルまたはディレクトリをコンテキストに追加"
1187 }
1188 MessageId::KbHelpOverlay => "このヘルプオーバーレイを開く(入力が空の時)",
1189 MessageId::KbToggleHelp => "ヘルプオーバーレイを切り替え",
1190 MessageId::KbToggleHelpSlash => "ヘルプオーバーレイを切り替え",
1191 MessageId::HelpUsageLabel => "使い方:",
1192 MessageId::HelpAliasesLabel => "エイリアス:",
1193 MessageId::SettingsTitle => "設定:",
1194 MessageId::SettingsConfigFile => "設定ファイル:",
1195 MessageId::ClearConversation => "会話履歴をクリアしました",
1196 MessageId::ClearConversationBusy => {
1197 "会話履歴をクリアしました(plan 状態が忙しい;必要なら /clear を再度実行)"
1198 }
1199 MessageId::ModelChanged => "モデルを変更しました: {old} → {new}",
1200 MessageId::LinksTitle => "DeepSeek リンク:",
1201 MessageId::LinksDashboard => "ダッシュボード:",
1202 MessageId::LinksDocs => "ドキュメント:",
1203 MessageId::LinksTip => "ヒント: API キーはダッシュボードコンソールで取得できます。",
1204 MessageId::SubagentsFetching => "サブエージェントの状態を取得中...",
1205 MessageId::HelpUnknownCommand => "不明なコマンド: {topic}",
1206 MessageId::HomeDashboardTitle => "DeepSeek TUI ホームダッシュボード",
1207 MessageId::HomeModel => "モデル:",
1208 MessageId::HomeMode => "モード:",
1209 MessageId::HomeWorkspace => "ワークスペース:",
1210 MessageId::HomeHistory => "履歴:",
1211 MessageId::HomeTokens => "トークン:",
1212 MessageId::HomeQueued => "キュー:",
1213 MessageId::HomeSubagents => "サブエージェント:",
1214 MessageId::HomeSkill => "スキル:",
1215 MessageId::HomeQuickActions => "クイックアクション",
1216 MessageId::HomeQuickLinks => "/links - ダッシュボードと API リンク",
1217 MessageId::HomeQuickSkills => "/skills - 利用可能なスキルを一覧",
1218 MessageId::HomeQuickConfig => "/config - インタラクティブな設定エディタを開く",
1219 MessageId::HomeQuickSettings => "/settings - 永続化された設定を表示",
1220 MessageId::HomeQuickModel => "/model - モデルを切り替え・確認",
1221 MessageId::HomeQuickSubagents => "/subagents - サブエージェントの状態を一覧",
1222 MessageId::HomeQuickTaskList => "/task list - バックグラウンドタスクキューを表示",
1223 MessageId::HomeQuickHelp => "/help - ヘルプを表示",
1224 MessageId::HomeModeTips => "モードヒント",
1225 MessageId::HomeAgentModeTip => "Agent モード - ツールを使って自律的なタスクを実行",
1226 MessageId::HomeAgentModeReviewTip => " 実行前に Ctrl+X で Plan モードでレビュー",
1227 MessageId::HomeAgentModeYoloTip => " /yolo と入力して完全なツールアクセスを有効化",
1228 MessageId::HomeYoloModeTip => "YOLO モード - 完全なツールアクセス、承認なし",
1229 MessageId::HomeYoloModeCaution => " 破壊的な操作には注意してください!",
1230 MessageId::HomePlanModeTip => "Plan モード - 実装前に設計",
1231 MessageId::HomePlanModeChecklistTip => " /plan を使って構造化されたチェックリストを作成",
1232 })
1233 }
1234
1235 fn chinese_simplified(id: MessageId) -> Option<&'static str> {
1236 Some(match id {
1237 MessageId::ComposerPlaceholder => "编写任务或使用 /。",
1238 MessageId::HistorySearchPlaceholder => "搜索提示历史...",
1239 MessageId::HistorySearchTitle => "历史搜索",
1240 MessageId::HistoryHintMove => "Up/Down 移动",
1241 MessageId::HistoryHintAccept => "Enter 接受",
1242 MessageId::HistoryHintRestore => "Esc 还原",
1243 MessageId::HistoryNoMatches => " 无匹配",
1244 MessageId::ConfigTitle => "会话配置",
1245 MessageId::ConfigModalTitle => " 配置 ",
1246 MessageId::ConfigSearchPlaceholder => "输入以筛选",
1247 MessageId::ConfigNoSettings => " 没有可用设置。",
1248 MessageId::ConfigNoMatchesPrefix => " 没有匹配设置: ",
1249 MessageId::ConfigFilteredSettings => " 已筛选设置",
1250 MessageId::ConfigShowing => " 显示",
1251 MessageId::ConfigFooterDefault => " 输入=筛选, Up/Down=选择, Enter/e=编辑, Esc/q=关闭 ",
1252 MessageId::ConfigFooterScrollable => {
1253 " 输入=筛选, Up/Down=选择, Enter/e=编辑, PgUp/PgDn=滚动, Esc/q=关闭 "
1254 }
1255 MessageId::ConfigFooterFiltered => {
1256 " 输入=筛选, Backspace=删除, Ctrl+U/Esc=清除, Enter=编辑 "
1257 }
1258 MessageId::HelpTitle => "帮助",
1259 MessageId::HelpFilterPlaceholder => "输入以筛选",
1260 MessageId::HelpFilterPrefix => "筛选: ",
1261 MessageId::HelpNoMatches => " 无匹配。",
1262 MessageId::HelpSlashCommands => "斜杠命令",
1263 MessageId::HelpKeybindings => "快捷键",
1264 MessageId::HelpFooterTypeFilter => " 输入以筛选 ",
1265 MessageId::HelpFooterMove => " Up/Down 移动 ",
1266 MessageId::HelpFooterJump => " PgUp/PgDn 跳转 ",
1267 MessageId::HelpFooterClose => " Esc 关闭 ",
1268 MessageId::CmdAgentDescription => "切换到 Agent 模式",
1269 MessageId::CmdAttachDescription => "附加图片或视频媒体;文本文件或目录请使用 @path",
1270 MessageId::CmdCacheDescription => "显示最近 N 轮的 DeepSeek 前缀缓存命中/未命中统计",
1271 MessageId::CmdClearDescription => "清除对话历史",
1272 MessageId::CmdCompactDescription => {
1273 "触发上下文压缩以释放空间(旧版命令;v0.6.6 起建议改用循环重启)"
1274 }
1275 MessageId::CmdConfigDescription => "打开交互式配置编辑器",
1276 MessageId::CmdContextDescription => "打开紧凑会话上下文检查器",
1277 MessageId::CmdCostDescription => "显示本次会话的费用明细",
1278 MessageId::CmdCycleDescription => "显示指定循环的延续简报",
1279 MessageId::CmdCyclesDescription => "列出本次会话中的检查点重启循环交接",
1280 MessageId::CmdDiffDescription => "显示会话开始以来的文件变更",
1281 MessageId::CmdEditDescription => "修改并重新提交最后一条消息",
1282 MessageId::CmdExitDescription => "退出应用",
1283 MessageId::CmdExportDescription => "将对话导出为 Markdown",
1284 MessageId::CmdHelpDescription => "显示帮助信息",
1285 MessageId::CmdHomeDescription => "显示主页面板,含统计与快捷操作",
1286 MessageId::CmdHooksDescription => "列出已配置的生命周期钩子(只读)",
1287 MessageId::CmdGoalDescription => "设置带有可选令牌预算的会话目标",
1288 MessageId::CmdInitDescription => "为项目生成 AGENTS.md",
1289 MessageId::CmdLspDescription => "切换 LSP 诊断的开启或关闭",
1290 MessageId::CmdShareDescription => "将当前会话导出为可共享的 Web URL",
1291 MessageId::CmdJobsDescription => "查看并管理后台 shell 作业",
1292 MessageId::CmdLinksDescription => "显示 DeepSeek 控制台与文档链接",
1293 MessageId::CmdLoadDescription => "从文件加载会话",
1294 MessageId::CmdLogoutDescription => "清除 API 密钥并返回设置",
1295 MessageId::CmdMcpDescription => "打开或管理 MCP 服务器",
1296 MessageId::CmdMemoryDescription => "查看或管理持久用户记忆文件",
1297 MessageId::CmdModelDescription => "切换或查看当前模型",
1298 MessageId::CmdModelsDescription => "列出 API 中可用的模型",
1299 MessageId::CmdNetworkDescription => "管理网络允许和拒绝规则",
1300 MessageId::CmdNoteDescription => "将笔记追加到持久笔记文件(.deepseek/notes.md)",
1301 MessageId::CmdPlanDescription => "切换到 Plan 模式并查看建议的实现步骤",
1302 MessageId::CmdProviderDescription => "切换或查看当前 LLM 后端(deepseek | nvidia-nim)",
1303 MessageId::CmdQueueDescription => "查看或编辑已排队的消息",
1304 MessageId::CmdRecallDescription => "搜索此前的循环归档(基于消息文本的 BM25 检索)",
1305 MessageId::CmdRenameDescription => "重命名当前会话",
1306 MessageId::CmdRestoreDescription => {
1307 "将工作区回滚到此前的轮次前/后快照。不带参数时列出最近的快照。"
1308 }
1309 MessageId::CmdRetryDescription => "重试上一次请求",
1310 MessageId::CmdReviewDescription => "对文件、diff 或 PR 进行结构化代码审查",
1311 MessageId::CmdRlmDescription => {
1312 "递归语言模型(RLM)轮次 —— 将提示词存入 Python REPL,让模型编写代码进行处理;可用 `llm_query()` / `sub_rlm()` 调用子 LLM。"
1313 }
1314 MessageId::CmdSaveDescription => "将会话保存到文件",
1315 MessageId::CmdSessionsDescription => "打开会话选择器",
1316 MessageId::CmdSettingsDescription => "显示持久化设置",
1317 MessageId::CmdSkillDescription => "激活技能,或安装/更新/卸载/信任社区技能",
1318 MessageId::CmdSkillsDescription => "列出本地技能(或使用 --remote 浏览精选注册表)",
1319 MessageId::CmdStashDescription => "暂存或恢复输入草稿(Ctrl+S 暂存,/stash list|pop)",
1320 MessageId::CmdStatuslineDescription => "配置底栏要显示哪些条目",
1321 MessageId::CmdSubagentsDescription => "列出子代理状态",
1322 MessageId::CmdSwarmDescription => {
1323 "运行多代理扇出轮次(sequential | mixture | distill | deliberate)"
1324 }
1325 MessageId::CmdSystemDescription => "显示当前系统提示词",
1326 MessageId::CmdTaskDescription => "管理后台任务",
1327 MessageId::CmdTokensDescription => "显示本次会话的 token 用量",
1328 MessageId::CmdTrustDescription => {
1329 "管理工作区信任与按路径的白名单(`/trust add <path>`、`/trust list`、`/trust on|off`)"
1330 }
1331 MessageId::CmdUndoDescription => "移除最后一组消息对",
1332 MessageId::CmdYoloDescription => "启用 YOLO 模式(shell + 信任 + 自动批准)",
1333 MessageId::CmdCacheAdvice => {
1334 "第 3 轮起命中率稳定在 ~70% 以上即表示前缀缓存稳定;\n\
1335 长会话中明显偏低则意味着前缀有抖动,值得排查(#263)。"
1336 }
1337 MessageId::CmdCacheFootnote => "* 当提供方未单独上报未命中时,由「输入 − 命中」推算。\n",
1338 MessageId::CmdCacheHeader => "缓存遥测 —— 最近 {count} / {total} 轮(模型:{model})\n",
1339 MessageId::CmdCacheNoData => {
1340 "缓存历史:尚未记录任何轮次。\n\n\
1341 DeepSeek 在受支持的模型(V4 系列)每个 API 轮次都会返回 `prompt_cache_hit_tokens` / \
1342 `prompt_cache_miss_tokens`。请先运行一个轮次再试 /cache。"
1343 }
1344 MessageId::CmdCacheTotals => {
1345 "Σ 输入:{sum_in} Σ 命中:{sum_hit} Σ 未命中:{sum_miss} 平均命中率:{avg}\n"
1346 }
1347 MessageId::CmdCostReport => {
1348 "会话费用:\n\
1349 ─────────────────────────────\n\
1350 预估累计消耗:{cost}\n\n\
1351 费用为估算值;如有提供方用量遥测会优先使用。\n\n\
1352 DeepSeek API 计费:\n\
1353 ─────────────────────────────\n\
1354 此 CLI 中未配置详细计费规则。"
1355 }
1356 MessageId::CmdTokensCacheBoth => "命中 {hit} / 未命中 {miss}",
1357 MessageId::CmdTokensCacheHitOnly => "命中 {hit} / 未命中未上报",
1358 MessageId::CmdTokensCacheMissOnly => "命中未上报 / 未命中 {miss}",
1359 MessageId::CmdTokensContextUnknownWindow => "~{estimated} / 窗口未知",
1360 MessageId::CmdTokensContextWithWindow => "~{used} / {window}({percent}%)",
1361 MessageId::FooterAgentSingular => "1 个子代理",
1362 MessageId::FooterAgentsPlural => "{count} 个子代理",
1363 MessageId::FooterPressCtrlCAgain => "再次按 Ctrl+C 退出",
1364 MessageId::FooterWorking => "工作中",
1365 MessageId::HelpSectionActions => "操作",
1366 MessageId::HelpSectionClipboard => "剪贴板",
1367 MessageId::HelpSectionEditing => "输入编辑",
1368 MessageId::HelpSectionHelp => "帮助",
1369 MessageId::HelpSectionModes => "模式",
1370 MessageId::HelpSectionNavigation => "导航",
1371 MessageId::HelpSectionSessions => "会话",
1372 MessageId::CmdTokensNotReported => "未上报",
1373 MessageId::CmdTokensReport => {
1374 "令牌用量:\n\
1375 ─────────────────────────────\n\
1376 活动上下文: {active}\n\
1377 上次 API 输入: {input}(来自轮次遥测;多轮工具调用中相同前缀可能被重复计入)\n\
1378 上次 API 输出: {output}\n\
1379 缓存命中/未命中: {cache}(仅用于遥测/计费)\n\
1380 累计令牌: {total}(会话用量遥测)\n\
1381 预估会话费用: {cost}\n\
1382 API 消息数: {api_messages}\n\
1383 聊天消息数: {chat_messages}\n\
1384 模型: {model}"
1385 }
1386 MessageId::KbScrollTranscript => "滚动对话记录、浏览输入历史或选择附件",
1387 MessageId::KbNavigateHistory => "浏览输入历史",
1388 MessageId::KbScrollTranscriptAlt => "滚动对话记录",
1389 MessageId::KbScrollPage => "按页滚动对话记录",
1390 MessageId::KbJumpTopBottom => "跳转到对话顶部/底部",
1391 MessageId::KbJumpTopBottomEmpty => "跳转到顶部/底部(输入框为空时)",
1392 MessageId::KbJumpToolBlocks => "在工具输出块之间跳转",
1393 MessageId::KbMoveCursor => "在输入框中移动光标",
1394 MessageId::KbJumpLineStartEnd => "跳转到行首/行尾",
1395 MessageId::KbDeleteChar => "删除光标前/后的字符,或移除已选附件",
1396 MessageId::KbClearDraft => "清空当前草稿",
1397 MessageId::KbStashDraft => "暂存当前草稿(用 `/stash pop` 恢复)",
1398 MessageId::KbSearchHistory => "搜索提示历史并恢复本地草稿",
1399 MessageId::KbInsertNewline => "在输入框中插入换行",
1400 MessageId::KbSendDraft => "发送当前草稿",
1401 MessageId::KbCloseMenu => "关闭菜单、取消请求、丢弃草稿或清空输入",
1402 MessageId::KbCancelOrExit => "取消请求,或空闲时退出",
1403 MessageId::KbShellControls => "打开正在运行的前台命令的 shell 控制",
1404 MessageId::KbExitEmpty => "输入框为空时退出",
1405 MessageId::KbCommandPalette => "打开命令面板",
1406 MessageId::KbFuzzyFilePicker => "打开模糊文件选择器(按 Enter 插入 @path)",
1407 MessageId::KbCompactInspector => "打开紧凑会话上下文检查器",
1408 MessageId::KbLastMessagePager => "打开最后一条消息的分页器(输入框为空时)",
1409 MessageId::KbSelectedDetails => "打开选中工具或消息的详情(输入框为空时)",
1410 MessageId::KbToolDetailsPager => "打开工具详情分页器",
1411 MessageId::KbThinkingPager => "打开思考内容分页器",
1412 MessageId::KbLiveTranscript => "打开实时对话覆盖层(自动滚动尾随)",
1413 MessageId::KbBacktrackMessage => "回退到之前的用户消息(左右键步进,Enter 回退)",
1414 MessageId::KbCompleteCycleModes => {
1415 "补全 /command、排队运行轮次跟进、切换模式;Shift+Tab 切换推理强度"
1416 }
1417 MessageId::KbJumpPlanAgentYolo => "直接跳转到 Plan / Agent / YOLO 模式",
1418 MessageId::KbAltJumpPlanAgentYolo => "替代快捷键跳转到 Plan / Agent / YOLO 模式",
1419 MessageId::KbFocusSidebar => "聚焦 Plan / 待办 / 任务 / 代理 / 代理 / 自动侧边栏",
1420 MessageId::KbTogglePlanAgent => "在 Plan 和 Agent 模式之间切换",
1421 MessageId::KbSessionPicker => "打开会话选择器",
1422 MessageId::KbPasteAttach => "粘贴文本或附加剪贴板图片",
1423 MessageId::KbCopySelection => "复制当前选中内容(macOS 为 Cmd+C)",
1424 MessageId::KbContextMenu => "打开上下文操作菜单,用于粘贴、选择、消息详情、上下文和帮助",
1425 MessageId::KbAttachPath => "添加本地文本文件或目录到上下文",
1426 MessageId::KbHelpOverlay => "打开此帮助覆盖层(输入框为空时)",
1427 MessageId::KbToggleHelp => "切换帮助覆盖层",
1428 MessageId::KbToggleHelpSlash => "切换帮助覆盖层",
1429 MessageId::HelpUsageLabel => "用法:",
1430 MessageId::HelpAliasesLabel => "别名:",
1431 MessageId::SettingsTitle => "设置:",
1432 MessageId::SettingsConfigFile => "配置文件:",
1433 MessageId::ClearConversation => "对话已清空",
1434 MessageId::ClearConversationBusy => {
1435 "对话已清空(Plan 状态忙碌;如需再次清空请运行 /clear)"
1436 }
1437 MessageId::ModelChanged => "模型已切换:{old} \u{2192} {new}",
1438 MessageId::LinksTitle => "DeepSeek 链接:",
1439 MessageId::LinksDashboard => "控制台:",
1440 MessageId::LinksDocs => "文档:",
1441 MessageId::LinksTip => "提示:API 密钥可在控制台中获取。",
1442 MessageId::SubagentsFetching => "正在获取子代理状态...",
1443 MessageId::HelpUnknownCommand => "未知命令:{topic}",
1444 MessageId::HomeDashboardTitle => "DeepSeek TUI 主面板",
1445 MessageId::HomeModel => "模型:",
1446 MessageId::HomeMode => "模式:",
1447 MessageId::HomeWorkspace => "工作区:",
1448 MessageId::HomeHistory => "历史:",
1449 MessageId::HomeTokens => "令牌:",
1450 MessageId::HomeQueued => "队列:",
1451 MessageId::HomeSubagents => "子代理:",
1452 MessageId::HomeSkill => "技能:",
1453 MessageId::HomeQuickActions => "快捷操作",
1454 MessageId::HomeQuickLinks => "/links - 控制台与 API 链接",
1455 MessageId::HomeQuickSkills => "/skills - 列出可用技能",
1456 MessageId::HomeQuickConfig => "/config - 打开交互式配置编辑器",
1457 MessageId::HomeQuickSettings => "/settings - 显示持久化设置",
1458 MessageId::HomeQuickModel => "/model - 切换或查看模型",
1459 MessageId::HomeQuickSubagents => "/subagents - 列出子代理状态",
1460 MessageId::HomeQuickTaskList => "/task list - 显示后台任务队列",
1461 MessageId::HomeQuickHelp => "/help - 显示帮助",
1462 MessageId::HomeModeTips => "模式提示",
1463 MessageId::HomeAgentModeTip => "Agent 模式 - 使用工具执行自主任务",
1464 MessageId::HomeAgentModeReviewTip => " 按 Ctrl+X 可在 Plan 模式下审查后再执行",
1465 MessageId::HomeAgentModeYoloTip => " 输入 /yolo 启用完整工具访问",
1466 MessageId::HomeYoloModeTip => "YOLO 模式 - 完整工具访问,无需审批",
1467 MessageId::HomeYoloModeCaution => " 请小心破坏性操作!",
1468 MessageId::HomePlanModeTip => "Plan 模式 - 先设计再实现",
1469 MessageId::HomePlanModeChecklistTip => " 使用 /plan 创建结构化检查清单",
1470 })
1471 }
1472
1473 fn portuguese_brazil(id: MessageId) -> Option<&'static str> {
1474 Some(match id {
1475 MessageId::ComposerPlaceholder => "Escreva uma tarefa ou use /.",
1476 MessageId::HistorySearchPlaceholder => "Pesquisar histórico de prompts...",
1477 MessageId::HistorySearchTitle => "Busca no histórico",
1478 MessageId::HistoryHintMove => "Up/Down move",
1479 MessageId::HistoryHintAccept => "Enter aceita",
1480 MessageId::HistoryHintRestore => "Esc restaura",
1481 MessageId::HistoryNoMatches => " Sem resultados",
1482 MessageId::ConfigTitle => "Configuração da sessão",
1483 MessageId::ConfigModalTitle => " Config ",
1484 MessageId::ConfigSearchPlaceholder => "digite para filtrar",
1485 MessageId::ConfigNoSettings => " Nenhuma configuração disponível.",
1486 MessageId::ConfigNoMatchesPrefix => " Nenhuma configuração corresponde a ",
1487 MessageId::ConfigFilteredSettings => " Configurações filtradas",
1488 MessageId::ConfigShowing => " Mostrando",
1489 MessageId::ConfigFooterDefault => {
1490 " digite=filtrar, Up/Down=selecionar, Enter/e=editar, Esc/q=fechar "
1491 }
1492 MessageId::ConfigFooterScrollable => {
1493 " digite=filtrar, Up/Down=selecionar, Enter/e=editar, PgUp/PgDn=rolar, Esc/q=fechar "
1494 }
1495 MessageId::ConfigFooterFiltered => {
1496 " digite=filtrar, Backspace=apagar, Ctrl+U/Esc=limpar, Enter=editar "
1497 }
1498 MessageId::HelpTitle => "Ajuda",
1499 MessageId::HelpFilterPlaceholder => "Digite para filtrar",
1500 MessageId::HelpFilterPrefix => "Filtro: ",
1501 MessageId::HelpNoMatches => " Sem resultados.",
1502 MessageId::HelpSlashCommands => "Comandos com barra",
1503 MessageId::HelpKeybindings => "Atalhos",
1504 MessageId::HelpFooterTypeFilter => " digite para filtrar ",
1505 MessageId::HelpFooterMove => " Up/Down move ",
1506 MessageId::HelpFooterJump => " PgUp/PgDn salta ",
1507 MessageId::HelpFooterClose => " Esc fecha ",
1508 MessageId::CmdAgentDescription => "Mudar para o modo agent",
1509 MessageId::CmdAttachDescription => {
1510 "Anexar imagem ou vídeo; use @path para arquivos de texto ou diretórios"
1511 }
1512 MessageId::CmdCacheDescription => {
1513 "Exibir estatísticas de hit/miss do cache de prefixo DeepSeek nas últimas N rodadas"
1514 }
1515 MessageId::CmdClearDescription => "Limpar o histórico da conversa",
1516 MessageId::CmdCompactDescription => {
1517 "Compactar o contexto para liberar espaço (legado; a v0.6.6 prefere o reinício de ciclo)"
1518 }
1519 MessageId::CmdConfigDescription => "Abrir o editor interativo de configuração",
1520 MessageId::CmdContextDescription => "Abrir o inspetor compacto de contexto da sessão",
1521 MessageId::CmdCostDescription => "Exibir o detalhamento de custo da sessão",
1522 MessageId::CmdCycleDescription => {
1523 "Exibir o briefing de continuidade de um ciclo específico"
1524 }
1525 MessageId::CmdCyclesDescription => {
1526 "Listar as transferências dos ciclos checkpoint-restart desta sessão"
1527 }
1528 MessageId::CmdDiffDescription => "Mostrar alterações em arquivos desde o início da sessão",
1529 MessageId::CmdEditDescription => "Revisar e reenviar a última mensagem",
1530 MessageId::CmdExitDescription => "Sair do aplicativo",
1531 MessageId::CmdExportDescription => "Exportar a conversa para markdown",
1532 MessageId::CmdHelpDescription => "Exibir informações de ajuda",
1533 MessageId::CmdHomeDescription => "Exibir o painel inicial com estatísticas e ações rápidas",
1534 MessageId::CmdHooksDescription => {
1535 "Listar hooks de ciclo de vida configurados (somente leitura)"
1536 }
1537 MessageId::CmdGoalDescription => {
1538 "Definir uma meta de sessão com orçamento de tokens opcional"
1539 }
1540 MessageId::CmdInitDescription => "Gerar AGENTS.md para o projeto",
1541 MessageId::CmdLspDescription => "Alternar diagnóstico LSP ligado ou desligado",
1542 MessageId::CmdShareDescription => "Exportar a sessão atual como uma URL web compartilhável",
1543 MessageId::CmdJobsDescription => "Inspecionar e controlar jobs de shell em segundo plano",
1544 MessageId::CmdLinksDescription => "Exibir links do painel e da documentação do DeepSeek",
1545 MessageId::CmdLoadDescription => "Carregar a sessão de um arquivo",
1546 MessageId::CmdLogoutDescription => "Limpar a chave de API e voltar à configuração",
1547 MessageId::CmdMcpDescription => "Abrir ou gerenciar servidores MCP",
1548 MessageId::CmdMemoryDescription => {
1549 "Inspecionar ou gerenciar o arquivo persistente de memória do usuário"
1550 }
1551 MessageId::CmdModelDescription => "Trocar ou exibir o modelo atual",
1552 MessageId::CmdModelsDescription => "Listar os modelos disponíveis pela API",
1553 MessageId::CmdNetworkDescription => "Gerenciar regras de rede permitidas e bloqueadas",
1554 MessageId::CmdNoteDescription => {
1555 "Adicionar nota ao arquivo persistente (.deepseek/notes.md)"
1556 }
1557 MessageId::CmdPlanDescription => {
1558 "Mudar para o modo plan e revisar os passos de implementação sugeridos"
1559 }
1560 MessageId::CmdProviderDescription => {
1561 "Trocar ou exibir o backend LLM ativo (deepseek | nvidia-nim)"
1562 }
1563 MessageId::CmdQueueDescription => "Ver ou editar mensagens enfileiradas",
1564 MessageId::CmdRecallDescription => {
1565 "Buscar arquivos de ciclos anteriores (BM25 sobre o texto das mensagens)"
1566 }
1567 MessageId::CmdRenameDescription => "Renomear a sessão atual",
1568 MessageId::CmdRestoreDescription => {
1569 "Reverter o workspace a um snapshot pré/pós-turno anterior. Sem argumento, lista os snapshots recentes."
1570 }
1571 MessageId::CmdRetryDescription => "Repetir a última requisição",
1572 MessageId::CmdReviewDescription => {
1573 "Executar uma revisão de código estruturada em um arquivo, diff ou PR"
1574 }
1575 MessageId::CmdRlmDescription => {
1576 "Turno do Recursive Language Model (RLM) — guarda o prompt em um REPL Python e deixa o modelo escrever o código que o processa; use `llm_query()` / `sub_rlm()` para chamadas a sub-LLMs."
1577 }
1578 MessageId::CmdSaveDescription => "Salvar a sessão em arquivo",
1579 MessageId::CmdSessionsDescription => "Abrir o seletor de sessões",
1580 MessageId::CmdSettingsDescription => "Exibir as configurações persistidas",
1581 MessageId::CmdSkillDescription => {
1582 "Ativar uma skill, ou instalar/atualizar/desinstalar/confiar em uma skill da comunidade"
1583 }
1584 MessageId::CmdSkillsDescription => {
1585 "Listar skills locais (ou --remote para navegar pelo registro curado)"
1586 }
1587 MessageId::CmdStashDescription => {
1588 "Estacionar ou restaurar rascunho do compositor (Ctrl+S estaciona, /stash list|pop)"
1589 }
1590 MessageId::CmdStatuslineDescription => "Configurar quais itens aparecem no rodapé",
1591 MessageId::CmdSubagentsDescription => "Listar o status dos sub-agentes",
1592 MessageId::CmdSwarmDescription => {
1593 "Executar turno fanout multi-agente (sequential | mixture | distill | deliberate)"
1594 }
1595 MessageId::CmdSystemDescription => "Exibir o prompt de sistema atual",
1596 MessageId::CmdTaskDescription => "Gerenciar tarefas em segundo plano",
1597 MessageId::CmdTokensDescription => "Exibir o uso de tokens da sessão",
1598 MessageId::CmdTrustDescription => {
1599 "Gerenciar a confiança do workspace e a allowlist por caminho (`/trust add <path>`, `/trust list`, `/trust on|off`)"
1600 }
1601 MessageId::CmdUndoDescription => "Remover o último par de mensagens",
1602 MessageId::CmdYoloDescription => {
1603 "Ativar o modo YOLO (shell + confiança + aprovação automática)"
1604 }
1605 MessageId::CmdCacheAdvice => {
1606 "Taxas de hit/miss acima de ~70% a partir do terceiro turno indicam um prefixo de cache estável;\n\
1607 valores menores em sessões longas sugerem instabilidade no prefixo, vale investigar (#263)."
1608 }
1609 MessageId::CmdCacheFootnote => {
1610 "* miss inferido a partir de entrada − hit quando o provedor não o reporta separadamente.\n"
1611 }
1612 MessageId::CmdCacheHeader => {
1613 "Telemetria do cache — últimos {count} de {total} turno(s) (modelo: {model})\n"
1614 }
1615 MessageId::CmdCacheNoData => {
1616 "Histórico do cache: nenhum turno registrado ainda.\n\n\
1617 O DeepSeek expõe `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens` em cada turno \
1618 da API onde o modelo suporta (família V4). Execute um turno e tente /cache de novo."
1619 }
1620 MessageId::CmdCacheTotals => {
1621 "Σ entrada: {sum_in} Σ hit: {sum_hit} Σ miss: {sum_miss} taxa média de hit: {avg}\n"
1622 }
1623 MessageId::CmdCostReport => {
1624 "Custo da sessão:\n\
1625 ─────────────────────────────\n\
1626 Total aproximado: {cost}\n\n\
1627 Estimativas de custo são aproximadas e usam a telemetria de uso do provedor quando disponível.\n\n\
1628 Preços da API DeepSeek:\n\
1629 ─────────────────────────────\n\
1630 Os detalhes de preço não estão configurados nesta CLI."
1631 }
1632 MessageId::CmdTokensCacheBoth => "{hit} hit / {miss} miss",
1633 MessageId::CmdTokensCacheHitOnly => "{hit} hit / miss não reportado",
1634 MessageId::CmdTokensCacheMissOnly => "hit não reportado / {miss} miss",
1635 MessageId::CmdTokensContextUnknownWindow => "~{estimated} / janela desconhecida",
1636 MessageId::CmdTokensContextWithWindow => "~{used} / {window} ({percent}%)",
1637 MessageId::FooterAgentSingular => "1 sub-agente",
1638 MessageId::FooterAgentsPlural => "{count} sub-agentes",
1639 MessageId::FooterPressCtrlCAgain => "Pressione Ctrl+C novamente para sair",
1640 MessageId::FooterWorking => "trabalhando",
1641 MessageId::HelpSectionActions => "Ações",
1642 MessageId::HelpSectionClipboard => "Área de transferência",
1643 MessageId::HelpSectionEditing => "Edição de entrada",
1644 MessageId::HelpSectionHelp => "Ajuda",
1645 MessageId::HelpSectionModes => "Modos",
1646 MessageId::HelpSectionNavigation => "Navegação",
1647 MessageId::HelpSectionSessions => "Sessões",
1648 MessageId::CmdTokensNotReported => "não reportado",
1649 MessageId::CmdTokensReport => {
1650 "Uso de tokens:\n\
1651 ─────────────────────────────\n\
1652 Contexto ativo: {active}\n\
1653 Última entrada da API: {input} (telemetria por turno; pode contar o mesmo prefixo várias vezes em rodadas com ferramentas)\n\
1654 Última saída da API: {output}\n\
1655 Hit/miss do cache: {cache} (apenas para telemetria/custo)\n\
1656 Tokens acumulados: {total} (telemetria de uso da sessão)\n\
1657 Custo aproximado: {cost}\n\
1658 Mensagens da API: {api_messages}\n\
1659 Mensagens do chat: {chat_messages}\n\
1660 Modelo: {model}"
1661 }
1662 MessageId::KbScrollTranscript => {
1663 "Rolar transcrição, navegar histórico de entrada ou selecionar anexos do compositor"
1664 }
1665 MessageId::KbNavigateHistory => "Navegar histórico de entrada",
1666 MessageId::KbScrollTranscriptAlt => "Rolar transcrição",
1667 MessageId::KbScrollPage => "Rolar transcrição por página",
1668 MessageId::KbJumpTopBottom => "Pular para topo / fim da transcrição",
1669 MessageId::KbJumpTopBottomEmpty => "Pular para topo / fim (quando entrada vazia)",
1670 MessageId::KbJumpToolBlocks => "Pular entre blocos de saída de ferramentas",
1671 MessageId::KbMoveCursor => "Mover cursor no compositor",
1672 MessageId::KbJumpLineStartEnd => "Pular para início / fim da linha",
1673 MessageId::KbDeleteChar => {
1674 "Excluir caractere antes / depois do cursor, ou remover anexo selecionado"
1675 }
1676 MessageId::KbClearDraft => "Limpar rascunho atual",
1677 MessageId::KbStashDraft => "Estacionar rascunho atual (`/stash pop` restaura)",
1678 MessageId::KbSearchHistory => "Buscar histórico de prompts e recuperar rascunhos locais",
1679 MessageId::KbInsertNewline => "Inserir nova linha no compositor",
1680 MessageId::KbSendDraft => "Enviar rascunho atual",
1681 MessageId::KbCloseMenu => {
1682 "Fechar menu, cancelar requisição, descartar rascunho ou limpar entrada"
1683 }
1684 MessageId::KbCancelOrExit => "Cancelar requisição ou sair quando ocioso",
1685 MessageId::KbShellControls => "Abrir controles de shell para comando em primeiro plano",
1686 MessageId::KbExitEmpty => "Sair quando entrada vazia",
1687 MessageId::KbCommandPalette => "Abrir paleta de comandos",
1688 MessageId::KbFuzzyFilePicker => {
1689 "Abrir seletor de arquivo fuzzy (insere @path ao pressionar Enter)"
1690 }
1691 MessageId::KbCompactInspector => "Abrir inspetor compacto de contexto da sessão",
1692 MessageId::KbLastMessagePager => {
1693 "Abrir paginador para última mensagem (quando entrada vazia)"
1694 }
1695 MessageId::KbSelectedDetails => {
1696 "Abrir detalhes da ferramenta ou mensagem selecionada (quando entrada vazia)"
1697 }
1698 MessageId::KbToolDetailsPager => "Abrir paginador de detalhes da ferramenta",
1699 MessageId::KbThinkingPager => "Abrir paginador de raciocínio",
1700 MessageId::KbLiveTranscript => "Abrir sobreposição de transcrição ao vivo (auto-scroll)",
1701 MessageId::KbBacktrackMessage => {
1702 "Retroceder para mensagem anterior do usuário (esquerda/direita, Enter para rebobinar)"
1703 }
1704 MessageId::KbCompleteCycleModes => {
1705 "Completar /command, enfileirar follow-up, ciclar modos; Shift+Tab cicla esforço de raciocínio"
1706 }
1707 MessageId::KbJumpPlanAgentYolo => "Pular direto para modo Plan / Agent / YOLO",
1708 MessageId::KbAltJumpPlanAgentYolo => "Salto alternativo para modo Plan / Agent / YOLO",
1709 MessageId::KbFocusSidebar => "Focar barra lateral Plan / Todos / Tasks / Agents / Auto",
1710 MessageId::KbTogglePlanAgent => "Alternar entre modos Plan e Agent",
1711 MessageId::KbSessionPicker => "Abrir seletor de sessões",
1712 MessageId::KbPasteAttach => "Colar texto ou anexar imagem da área de transferência",
1713 MessageId::KbCopySelection => "Copiar seleção atual (Cmd+C no macOS)",
1714 MessageId::KbContextMenu => {
1715 "Abrir ações de contexto para colar, seleção, detalhes, contexto e ajuda"
1716 }
1717 MessageId::KbAttachPath => "Adicionar arquivo ou diretório local ao contexto",
1718 MessageId::KbHelpOverlay => "Abrir esta sobreposição de ajuda (quando entrada vazia)",
1719 MessageId::KbToggleHelp => "Alternar sobreposição de ajuda",
1720 MessageId::KbToggleHelpSlash => "Alternar sobreposição de ajuda",
1721 MessageId::HelpUsageLabel => "Uso:",
1722 MessageId::HelpAliasesLabel => "Apelidos:",
1723 MessageId::SettingsTitle => "Configurações:",
1724 MessageId::SettingsConfigFile => "Arquivo de configuração:",
1725 MessageId::ClearConversation => "Conversa limpa",
1726 MessageId::ClearConversationBusy => {
1727 "Conversa limpa (estado do plano ocupado; execute /clear novamente se necessário)"
1728 }
1729 MessageId::ModelChanged => "Modelo alterado: {old} \u{2192} {new}",
1730 MessageId::LinksTitle => "Links do DeepSeek:",
1731 MessageId::LinksDashboard => "Painel:",
1732 MessageId::LinksDocs => "Documentação:",
1733 MessageId::LinksTip => "Dica: chaves de API estão disponíveis no console do painel.",
1734 MessageId::SubagentsFetching => "Buscando status dos sub-agentes...",
1735 MessageId::HelpUnknownCommand => "Comando desconhecido: {topic}",
1736 MessageId::HomeDashboardTitle => "Painel Inicial do DeepSeek TUI",
1737 MessageId::HomeModel => "Modelo:",
1738 MessageId::HomeMode => "Modo:",
1739 MessageId::HomeWorkspace => "Workspace:",
1740 MessageId::HomeHistory => "Histórico:",
1741 MessageId::HomeTokens => "Tokens:",
1742 MessageId::HomeQueued => "Enfileirado:",
1743 MessageId::HomeSubagents => "Sub-agentes:",
1744 MessageId::HomeSkill => "Skill:",
1745 MessageId::HomeQuickActions => "Ações Rápidas",
1746 MessageId::HomeQuickLinks => "/links - Links do painel e API",
1747 MessageId::HomeQuickSkills => "/skills - Listar skills disponíveis",
1748 MessageId::HomeQuickConfig => "/config - Abrir editor interativo de configuração",
1749 MessageId::HomeQuickSettings => "/settings - Exibir configurações persistentes",
1750 MessageId::HomeQuickModel => "/model - Alternar ou visualizar modelo",
1751 MessageId::HomeQuickSubagents => "/subagents - Listar status dos sub-agentes",
1752 MessageId::HomeQuickTaskList => "/task list - Exibir fila de tarefas em segundo plano",
1753 MessageId::HomeQuickHelp => "/help - Exibir ajuda",
1754 MessageId::HomeModeTips => "Dicas de Modo",
1755 MessageId::HomeAgentModeTip => "Modo Agent - Use ferramentas para tarefas autônomas",
1756 MessageId::HomeAgentModeReviewTip => {
1757 " Use Ctrl+X para revisar no modo Plan antes de executar"
1758 }
1759 MessageId::HomeAgentModeYoloTip => {
1760 " Digite /yolo para habilitar acesso total às ferramentas"
1761 }
1762 MessageId::HomeYoloModeTip => "Modo YOLO - Acesso total a ferramentas, sem aprovações",
1763 MessageId::HomeYoloModeCaution => " Tenha cuidado com operações destrutivas!",
1764 MessageId::HomePlanModeTip => "Modo Plan - Planeje antes de implementar",
1765 MessageId::HomePlanModeChecklistTip => " Use /plan para criar checklists estruturados",
1766 })
1767 }
1768
1769 #[cfg(test)]
1770 mod tests {
1771 use super::*;
1772 use ratatui::{
1773 buffer::Buffer,
1774 layout::Rect,
1775 widgets::{Paragraph, Widget, Wrap},
1776 };
1777
1778 #[test]
1779 fn locale_setting_normalizes_supported_tags() {
1780 assert_eq!(normalize_configured_locale("auto"), Some("auto"));
1781 assert_eq!(normalize_configured_locale("ja_JP.UTF-8"), Some("ja"));
1782 assert_eq!(normalize_configured_locale("zh-CN"), Some("zh-Hans"));
1783 assert_eq!(normalize_configured_locale("pt"), Some("pt-BR"));
1784 assert_eq!(normalize_configured_locale("pt-PT"), Some("pt-BR"));
1785 assert_eq!(normalize_configured_locale("zh-TW"), None);
1786 }
1787
1788 #[test]
1789 fn locale_resolution_uses_config_then_environment_then_english() {
1790 assert_eq!(
1791 resolve_locale_with_env("ja", |_| Some("pt_BR.UTF-8".to_string())),
1792 Locale::Ja
1793 );
1794 assert_eq!(
1795 resolve_locale_with_env("auto", |key| {
1796 (key == "LANG").then(|| "zh_CN.UTF-8".to_string())
1797 }),
1798 Locale::ZhHans
1799 );
1800 assert_eq!(resolve_locale_with_env("auto", |_| None), Locale::En);
1801 }
1802
1803 #[test]
1804 fn shipped_first_pack_has_no_missing_core_messages() {
1805 for locale in Locale::shipped() {
1806 assert!(
1807 missing_message_ids(*locale).is_empty(),
1808 "{} is missing messages",
1809 locale.tag()
1810 );
1811 }
1812 }
1813
1814 #[test]
1815 fn unsupported_locale_falls_back_to_english() {
1816 assert_eq!(
1817 resolve_locale_with_env("ar", |_| None),
1818 Locale::En,
1819 "Arabic is planned for QA but not shipped in the v0.7.6 core pack"
1820 );
1821 }
1822
1823 #[test]
1824 fn missing_translation_falls_back_to_english() {
1825 assert_eq!(
1826 fallback_translation(None, MessageId::ComposerPlaceholder),
1827 english(MessageId::ComposerPlaceholder)
1828 );
1829 }
1830
1831 #[test]
1832 fn width_truncation_handles_cjk_rtl_indic_and_latin_samples() {
1833 let samples = [
1834 ("zh-Hans", "输入以筛选配置"),
1835 ("ar", "تصفية الإعدادات"),
1836 ("hi", "सेटिंग खोजें"),
1837 ("pt-BR", "configurações filtradas"),
1838 ];
1839
1840 for (tag, sample) in samples {
1841 let truncated = truncate_to_width(sample, 12);
1842 assert!(
1843 truncated.width() <= 12,
1844 "{tag} sample overflowed: {truncated:?}"
1845 );
1846 }
1847 }
1848
1849 #[test]
1850 fn planned_script_samples_render_in_narrow_terminal_buffer() {
1851 let samples = [
1852 ("CJK", "输入以筛选配置"),
1853 ("RTL", "تصفية الإعدادات"),
1854 ("Indic", "सेटिंग खोजें"),
1855 ("Latin Global South", "configurações filtradas"),
1856 ];
1857
1858 for (label, sample) in samples {
1859 let area = Rect::new(0, 0, 18, 4);
1860 let mut buf = Buffer::empty(area);
1861 Paragraph::new(sample)
1862 .wrap(Wrap { trim: false })
1863 .render(area, &mut buf);
1864 let dump = buffer_text(&buf, area);
1865
1866 assert!(
1867 dump.chars().any(|ch| !ch.is_whitespace()),
1868 "{label} sample produced an empty render"
1869 );
1870 }
1871 }
1872
1873 fn buffer_text(buf: &Buffer, area: Rect) -> String {
1874 let mut out = String::new();
1875 for y in area.top()..area.bottom() {
1876 for x in area.left()..area.right() {
1877 out.push_str(buf[(x, y)].symbol());
1878 }
1879 out.push('\n');
1880 }
1881 out
1882 }
1883 }
1884
1884 lines RUST