diff --git a/crates/tui/src/commands/change.rs b/crates/tui/src/commands/change.rs index 5c0b3418..e8448a48 100644 --- a/crates/tui/src/commands/change.rs +++ b/crates/tui/src/commands/change.rs @@ -100,6 +100,7 @@ pub fn change(app: &mut App, version: Option<&str>) -> CommandResult { Locale::ZhHant => "Traditional Chinese (繁體中文)", Locale::Ja => "Japanese (日本語)", Locale::PtBr => "Brazilian Portuguese (Português)", + Locale::Es419 => "Latin American Spanish (Español latinoamericano)", // Fallback — should never reach here since we check En above. Locale::En => "English", }; diff --git a/crates/tui/src/commands/config.rs b/crates/tui/src/commands/config.rs index 0ed018ce..82b4e5c4 100644 --- a/crates/tui/src/commands/config.rs +++ b/crates/tui/src/commands/config.rs @@ -88,6 +88,7 @@ fn show_single_setting(app: &App, key: &str) -> CommandResult { crate::localization::Locale::ZhHant => "zh-Hant", crate::localization::Locale::Ja => "ja", crate::localization::Locale::PtBr => "pt-BR", + crate::localization::Locale::Es419 => "es-419", } } fn density_display(d: crate::tui::app::ComposerDensity) -> &'static str { @@ -419,6 +420,7 @@ pub fn set_config_value(app: &mut App, key: &str, value: &str, persist: bool) -> } "locale" | "language" => { app.ui_locale = resolve_locale(&settings.locale); + app.mark_history_updated(); app.needs_redraw = true; } "theme" | "ui_theme" | "background_color" | "background" | "bg" => { diff --git a/crates/tui/src/localization.rs b/crates/tui/src/localization.rs index 477d9973..673ac543 100644 --- a/crates/tui/src/localization.rs +++ b/crates/tui/src/localization.rs @@ -38,6 +38,7 @@ pub enum Locale { ZhHans, ZhHant, PtBr, + Es419, } impl Locale { @@ -48,6 +49,7 @@ impl Locale { Self::ZhHans => "zh-Hans", Self::ZhHant => "zh-Hant", Self::PtBr => "pt-BR", + Self::Es419 => "es-419", } } @@ -58,6 +60,7 @@ impl Locale { Self::ZhHans => "Simplified Chinese (简体中文)", Self::ZhHant => "Traditional Chinese (繁體中文)", Self::PtBr => "Brazilian Portuguese (Português do Brasil)", + Self::Es419 => "Latin American Spanish (Español latinoamericano)", } } @@ -104,12 +107,27 @@ impl Locale { fallback: "en", coverage: LocaleCoverage::V076Core, }, + Self::Es419 => LocaleSpec { + tag: "es-419", + display_name: "Spanish (Latin America)", + script: "Latin", + direction: TextDirection::Ltr, + fallback: "en", + coverage: LocaleCoverage::V076Core, + }, } } #[allow(dead_code)] pub fn shipped() -> &'static [Self] { - &[Self::En, Self::Ja, Self::ZhHans, Self::ZhHant, Self::PtBr] + &[ + Self::En, + Self::Ja, + Self::ZhHans, + Self::ZhHant, + Self::PtBr, + Self::Es419, + ] } } @@ -179,14 +197,6 @@ pub const PLANNED_QA_LOCALES: &[LocaleSpec] = &[ fallback: "en", coverage: LocaleCoverage::PlannedQa, }, - LocaleSpec { - tag: "es-419", - display_name: "Spanish (Latin America)", - script: "Latin", - direction: TextDirection::Ltr, - fallback: "en", - coverage: LocaleCoverage::PlannedQa, - }, LocaleSpec { tag: "fr", display_name: "French", @@ -684,6 +694,7 @@ pub fn thinking_translation_placeholder(locale: Locale) -> &'static str { Locale::ZhHans => "正在思考,完成后翻译为简体中文...", Locale::ZhHant => "正在思考,完成後翻譯為繁體中文...", Locale::PtBr => "Pensando; traduzindo ao concluir...", + Locale::Es419 => "Pensando; traduciendo al finalizar...", } } @@ -694,6 +705,7 @@ pub fn thinking_translation_in_progress(locale: Locale) -> &'static str { Locale::ZhHans => "正在翻译思考内容...", Locale::ZhHant => "正在翻譯思考內容...", Locale::PtBr => "Traduzindo o conteúdo de raciocínio...", + Locale::Es419 => "Traduciendo el contenido de razonamiento...", } } @@ -704,6 +716,7 @@ pub fn thinking_translation_complete(locale: Locale) -> &'static str { Locale::ZhHans => "思考内容翻译完成", Locale::ZhHant => "思考內容翻譯完成", Locale::PtBr => "Tradução do raciocínio concluída", + Locale::Es419 => "Traducción del razonamiento completada", } } @@ -714,6 +727,7 @@ pub fn thinking_translation_failed(locale: Locale) -> &'static str { Locale::ZhHans => "思考内容翻译失败", Locale::ZhHant => "思考內容翻譯失敗", Locale::PtBr => "Falha ao traduzir o raciocínio", + Locale::Es419 => "Falló la traducción del razonamiento", } } @@ -724,6 +738,7 @@ pub fn hidden_translation_failed(locale: Locale) -> &'static str { Locale::ZhHans => "翻译失败,原文已隐藏。", Locale::ZhHant => "翻譯失敗,原文已隱藏。", Locale::PtBr => "A tradução falhou; o texto original está oculto.", + Locale::Es419 => "La traducción falló; el texto original está oculto.", } } @@ -830,6 +845,9 @@ fn parse_locale(value: &str) -> Option { if value.starts_with("pt") || value == "br" { return Some(Locale::PtBr); } + if value.starts_with("es") { + return Some(Locale::Es419); + } None } @@ -1194,6 +1212,7 @@ fn translation(locale: Locale, id: MessageId) -> Option<&'static str> { Locale::ZhHans => chinese_simplified(id), Locale::ZhHant => traditional_chinese(id), Locale::PtBr => portuguese_brazil(id), + Locale::Es419 => spanish_latin_america(id), } } @@ -2251,6 +2270,391 @@ fn portuguese_brazil(id: MessageId) -> Option<&'static str> { }) } +fn spanish_latin_america(id: MessageId) -> Option<&'static str> { + Some(match id { + MessageId::ComposerPlaceholder => "Escribe una tarea o usa /.", + MessageId::HistorySearchPlaceholder => "Buscar en el historial de prompts...", + MessageId::HistorySearchTitle => "Búsqueda en el historial", + MessageId::HistoryHintMove => "Arriba/Abajo mover", + MessageId::HistoryHintAccept => "Enter aceptar", + MessageId::HistoryHintRestore => "Esc restaurar", + MessageId::HistoryNoMatches => " Sin resultados", + MessageId::ConfigTitle => "Configuración de la sesión", + MessageId::ConfigModalTitle => " Config ", + MessageId::ConfigSearchPlaceholder => "escribe para filtrar", + MessageId::ConfigNoSettings => " No hay configuraciones disponibles.", + MessageId::ConfigNoMatchesPrefix => " Ninguna configuración coincide con ", + MessageId::ConfigFilteredSettings => " Configuraciones filtradas", + MessageId::ConfigShowing => " Mostrando", + MessageId::ConfigFooterDefault => { + " escribir=filtrar, Arriba/Abajo=seleccionar, Enter/e=editar, Esc/q=cerrar " + } + MessageId::ConfigFooterScrollable => { + " escribir=filtrar, Arriba/Abajo=seleccionar, Enter/e=editar, PgUp/PgDn=desplazar, Esc/q=cerrar " + } + MessageId::ConfigFooterFiltered => { + " escribir=filtrar, Backspace=borrar, Ctrl+U/Esc=limpiar, Enter=editar " + } + MessageId::HelpTitle => "Ayuda", + MessageId::HelpFilterPlaceholder => "Escribe para filtrar", + MessageId::HelpFilterPrefix => "Filtro: ", + MessageId::HelpNoMatches => " Sin resultados.", + MessageId::HelpSlashCommands => "Comandos con barra", + MessageId::HelpKeybindings => "Atajos de teclado", + MessageId::HelpFooterTypeFilter => " escribir para filtrar ", + MessageId::HelpFooterMove => " Arriba/Abajo mover ", + MessageId::HelpFooterJump => " PgUp/PgDn saltar ", + MessageId::HelpFooterClose => " Esc cerrar ", + MessageId::CmdAnchorDescription => { + "Fijar un dato que sobrevive a la compactación (inyectado automáticamente en el contexto)" + } + MessageId::CmdAttachDescription => { + "Adjuntar imagen o video; usa @ruta para archivos de texto o directorios" + } + MessageId::CmdCacheDescription => { + "Mostrar estadísticas de hit/miss del caché de prefijo DeepSeek en las últimas N rondas" + } + MessageId::CmdChangeDescription => "Mostrar la entrada más reciente del changelog", + MessageId::CmdChangeHeader => "Changelog más reciente", + MessageId::CmdChangeTranslationQueued => { + "Las notas de la versión en inglés se muestran abajo. Se solicitará una versión traducida a continuación; si el proveedor no está disponible, este texto en inglés será el fallback." + } + MessageId::CmdChangeTranslationUnavailable => { + "Las notas de la versión en inglés se muestran abajo. La traducción no está disponible porque la sesión actual no tiene clave de API o está offline." + } + MessageId::CmdChangePreviousVersion => { + "Versión anterior: {version} — ejecuta `/change {version}` para verla" + } + MessageId::CmdClearDescription => "Limpiar el historial de la conversación", + MessageId::CmdCompactDescription => { + "Compactar el contexto para liberar espacio (heredado; v0.6.6 prefiere reinicio de ciclo)" + } + MessageId::CmdConfigDescription => "Abrir el editor interactivo de configuración", + MessageId::CmdContextDescription => "Abrir el inspector compacto de contexto de la sesión", + MessageId::CmdCostDescription => "Mostrar el desglose de costo de la sesión", + MessageId::CmdCycleDescription => { + "Mostrar el resumen de continuidad de un ciclo específico" + } + MessageId::CmdCyclesDescription => { + "Listar las transferencias de checkpoint-restart de esta sesión" + } + MessageId::CmdDiffDescription => "Mostrar cambios en archivos desde el inicio de la sesión", + MessageId::CmdEditDescription => "Revisar y reenviar el último mensaje", + MessageId::CmdExitDescription => "Salir de la aplicación", + MessageId::CmdExportDescription => "Exportar la conversación a markdown", + MessageId::CmdFeedbackDescription => "Generar una URL de feedback en GitHub", + MessageId::CmdHelpDescription => "Mostrar información de ayuda", + MessageId::CmdHomeDescription => { + "Mostrar el panel inicial con estadísticas y acciones rápidas" + } + MessageId::CmdHooksDescription => { + "Listar hooks de ciclo de vida configurados (solo lectura)" + } + MessageId::CmdAgentDescription => { + "Abrir una sesión persistente de sub-agente: /agent [0-3] " + } + MessageId::CmdGoalDescription => { + "Definir una meta de sesión con presupuesto de tokens opcional" + } + MessageId::CmdInitDescription => "Generar AGENTS.md para el proyecto", + MessageId::CmdLspDescription => "Alternar diagnóstico LSP encendido o apagado", + MessageId::CmdShareDescription => "Exportar la sesión actual como una URL web compartible", + MessageId::CmdJobsDescription => { + "Inspeccionar y controlar trabajos de shell en segundo plano" + } + MessageId::CmdLinksDescription => "Mostrar enlaces del panel y documentación de DeepSeek", + MessageId::CmdLoadDescription => "Cargar la sesión desde un archivo", + MessageId::CmdLogoutDescription => "Limpiar la clave de API y volver a la configuración", + MessageId::CmdMcpDescription => "Abrir o gestionar servidores MCP", + MessageId::CmdMemoryDescription => { + "Inspeccionar o gestionar el archivo persistente de memoria del usuario" + } + MessageId::CmdModeDescription => { + "Alternar modo o abrir selector: /mode [agent|plan|yolo|1|2|3]" + } + MessageId::CmdModelDescription => "Cambiar o mostrar el modelo actual", + MessageId::CmdModelsDescription => "Listar los modelos disponibles por la API", + MessageId::CmdNetworkDescription => "Gestionar reglas de red permitidas y bloqueadas", + MessageId::CmdNoteDescription => "Agregar nota al archivo persistente (.deepseek/notes.md)", + MessageId::CmdThemeDescription => "Alternar entre tema claro y oscuro", + MessageId::CmdProviderDescription => { + "Cambiar o mostrar el backend LLM activo (deepseek | nvidia-nim | ollama)" + } + MessageId::CmdQueueDescription => "Ver o editar mensajes en cola", + MessageId::CmdRecallDescription => { + "Buscar archivos de ciclos anteriores (BM25 sobre el texto de los mensajes)" + } + MessageId::CmdRelayDescription => "Crear un relay de sesión (接力) para un hilo nuevo", + MessageId::CmdRenameDescription => "Renombrar la sesión actual", + MessageId::CmdRestoreDescription => { + "Revertir el workspace a un snapshot pre/post-turno anterior. Sin argumento, lista los snapshots recientes." + } + MessageId::CmdRetryDescription => "Repetir la última solicitud", + MessageId::CmdReviewDescription => { + "Ejecutar una revisión de código estructurada en un archivo, diff o PR" + } + MessageId::CmdRlmDescription => { + "Turno del Recursive Language Model (RLM) — guarda el prompt en un REPL Python y deja que el modelo escriba el código que lo procesa; usa `llm_query()` / `sub_rlm()` para llamadas a sub-LLMs." + } + MessageId::CmdSaveDescription => "Guardar la sesión en archivo", + MessageId::CmdSessionsDescription => "Abrir el selector de sesiones", + MessageId::CmdSettingsDescription => "Mostrar las configuraciones persistidas", + MessageId::CmdSkillDescription => { + "Activar una skill, o instalar/actualizar/desinstalar/confiar en una skill de la comunidad" + } + MessageId::CmdSkillsDescription => { + "Listar skills locales (filtra con `/skills `; --remote navega el registro curado)" + } + MessageId::CmdStashDescription => { + "Estacionar o restaurar borrador del compositor (Ctrl+S estaciona, /stash list|pop)" + } + MessageId::CmdStatusDescription => "Mostrar el estado de la sesión en ejecución", + MessageId::CmdStatuslineDescription => { + "Configurar qué elementos aparecen en el pie de página" + } + MessageId::CmdSubagentsDescription => "Listar el estado de los sub-agentes", + MessageId::CmdSwarmDescription => { + "Ejecutar turno fanout multi-agente (sequential | mixture | distill | deliberate)" + } + MessageId::CmdSystemDescription => "Mostrar el prompt de sistema actual", + MessageId::CmdTaskDescription => "Gestionar tareas en segundo plano", + MessageId::CmdTokensDescription => "Mostrar el uso de tokens de la sesión", + MessageId::CmdTranslateDescription => { + "Activar o desactivar la traducción de salida al idioma actual del sistema" + } + MessageId::CmdTranslateOff => { + "Traducción de salida desactivada (se muestra la salida original del modelo)" + } + MessageId::CmdTranslateOn => { + "Traducción de salida activada: las respuestas del modelo se mostrarán en el idioma del sistema" + } + MessageId::TranslationInProgress => "Traduciendo la salida del asistente...", + MessageId::TranslationComplete => "Traducción completada", + MessageId::TranslationFailed => "Traducción fallida", + MessageId::CmdTrustDescription => { + "Gestionar la confianza del workspace y la lista de paths permitidos (`/trust add `, `/trust list`, `/trust on|off`)" + } + MessageId::CmdUndoDescription => "Eliminar el último par de mensajes", + MessageId::CmdVerboseDescription => { + "Alternar pensamiento en vivo completo en la transcripción" + } + MessageId::CmdCacheAdvice => { + "Tasas de hit/miss arriba del ~70% a partir del tercer turno indican un prefijo de caché estable;\n\ + valores menores en sesiones largas sugieren inestabilidad en el prefijo, vale investigar (#263)." + } + MessageId::CmdCacheFootnote => { + "* miss inferido a partir de entrada − hit cuando el proveedor no lo reporta por separado.\n" + } + MessageId::CmdCacheHeader => { + "Telemetría del caché — últimos {count} de {total} turno(s) (modelo: {model})\n" + } + MessageId::CmdCacheNoData => { + "Historial del caché: ningún turno registrado todavía.\n\n\ + DeepSeek expone `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens` en cada turno \ + de la API donde el modelo lo soporta (familia V4). Ejecuta un turno y prueba /cache de nuevo." + } + MessageId::CmdCacheTotals => { + "Σ entrada: {sum_in} Σ hit: {sum_hit} Σ miss: {sum_miss} tasa promedio de hit: {avg}\n" + } + MessageId::CmdCostReport => { + "Costo de la sesión:\n\ + ─────────────────────────────\n\ + Total aproximado: {cost}\n\n\ + Las estimaciones de costo son aproximadas y usan la telemetría de uso del proveedor cuando está disponible.\n\n\ + Precios de la API DeepSeek:\n\ + ─────────────────────────────\n\ + Los detalles de precio no están configurados en esta CLI." + } + MessageId::CmdTokensCacheBoth => "{hit} hit / {miss} miss", + MessageId::CmdTokensCacheHitOnly => "{hit} hit / miss no reportado", + MessageId::CmdTokensCacheMissOnly => "hit no reportado / {miss} miss", + MessageId::CmdTokensContextUnknownWindow => "~{estimated} / ventana desconocida", + MessageId::CmdTokensContextWithWindow => "~{used} / {window} ({percent}%)", + MessageId::FooterAgentSingular => "1 sub-agente", + MessageId::FooterAgentsPlural => "{count} sub-agentes", + MessageId::FooterPressCtrlCAgain => "Presiona Ctrl+C de nuevo para salir", + MessageId::FooterWorking => "trabajando", + MessageId::HelpSectionActions => "Acciones", + MessageId::HelpSectionClipboard => "Portapapeles", + MessageId::HelpSectionEditing => "Edición de entrada", + MessageId::HelpSectionHelp => "Ayuda", + MessageId::HelpSectionModes => "Modos", + MessageId::HelpSectionNavigation => "Navegación", + MessageId::HelpSectionSessions => "Sesiones", + MessageId::CmdTokensNotReported => "no reportado", + MessageId::CmdTokensReport => { + "Uso de tokens:\n\ + ─────────────────────────────\n\ + Contexto activo: {active}\n\ + Última entrada de API: {input} (telemetría por turno; puede contar el mismo prefijo varias veces en rondas con herramientas)\n\ + Última salida de API: {output}\n\ + Hit/miss del caché: {cache} (solo para telemetría/costo)\n\ + Tokens acumulados: {total} (telemetría de uso de la sesión)\n\ + Costo aproximado: {cost}\n\ + Mensajes de API: {api_messages}\n\ + Mensajes del chat: {chat_messages}\n\ + Modelo: {model}" + } + MessageId::KbScrollTranscript => { + "Desplazar transcripción, navegar historial de entrada o seleccionar adjuntos del compositor" + } + MessageId::KbNavigateHistory => "Navegar historial de entrada", + MessageId::KbBrowseHistory => "Explorar historial de conversación", + MessageId::KbScrollTranscriptAlt => "Desplazar transcripción", + MessageId::KbScrollPage => "Desplazar transcripción por página", + MessageId::KbJumpTopBottom => "Saltar al inicio / fin de la transcripción", + MessageId::KbJumpTopBottomEmpty => "Saltar al inicio / fin (cuando la entrada está vacía)", + MessageId::KbJumpToolBlocks => "Saltar entre bloques de salida de herramientas", + MessageId::KbMoveCursor => "Mover cursor en el compositor", + MessageId::KbJumpLineStartEnd => "Saltar al inicio / fin de la línea", + MessageId::KbDeleteChar => { + "Eliminar carácter antes / después del cursor, o quitar adjunto seleccionado" + } + MessageId::KbClearDraft => "Limpiar borrador actual", + MessageId::KbStashDraft => "Estacionar borrador actual (`/stash pop` restaura)", + MessageId::KbSearchHistory => "Buscar historial de prompts y recuperar borradores locales", + MessageId::KbInsertNewline => "Insertar nueva línea en el compositor", + MessageId::KbSendDraft => "Enviar borrador actual", + MessageId::KbCloseMenu => { + "Cerrar menú, cancelar solicitud, descartar borrador o limpiar entrada" + } + MessageId::KbCancelOrExit => "Cancelar solicitud o salir cuando está inactivo", + MessageId::KbShellControls => "Abrir controles de shell para comando en primer plano", + MessageId::KbExitEmpty => "Salir cuando la entrada está vacía", + MessageId::KbCommandPalette => "Abrir paleta de comandos", + MessageId::KbFuzzyFilePicker => { + "Abrir selector de archivo fuzzy (inserta @ruta al presionar Enter)" + } + MessageId::KbCompactInspector => "Abrir inspector compacto de contexto de la sesión", + MessageId::KbLastMessagePager => { + "Abrir paginador para el último mensaje (cuando la entrada está vacía)" + } + MessageId::KbSelectedDetails => { + "Abrir detalles de la herramienta o mensaje seleccionado (cuando la entrada está vacía)" + } + MessageId::KbToolDetailsPager => "Abrir paginador de detalles de la herramienta", + MessageId::KbThinkingPager => "Abrir paginador de razonamiento", + MessageId::KbLiveTranscript => "Abrir superposición de transcripción en vivo (auto-scroll)", + MessageId::KbBacktrackMessage => { + "Retroceder al mensaje anterior del usuario (izquierda/derecha, Enter para rebobinar)" + } + MessageId::KbCompleteCycleModes => { + "Completar /command, encolar follow-up, ciclar modos; Shift+Tab cicla esfuerzo de razonamiento" + } + MessageId::KbJumpPlanAgentYolo => "Saltar directo a modo Plan / Agent / YOLO", + MessageId::KbAltJumpPlanAgentYolo => "Salto alternativo a modo Plan / Agent / YOLO", + MessageId::KbFocusSidebar => "Enfocar barra lateral Plan / Todos / Tasks / Agents / Auto", + MessageId::KbTogglePlanAgent => "Alternar entre modos Plan y Agent", + MessageId::KbSessionPicker => "Abrir selector de sesiones", + MessageId::KbPasteAttach => "Pegar texto o adjuntar imagen del portapapeles", + MessageId::KbCopySelection => "Copiar selección actual (Cmd+C en macOS)", + MessageId::KbContextMenu => { + "Abrir acciones de contexto para pegar, selección, detalles, contexto y ayuda" + } + MessageId::KbAttachPath => "Agregar archivo o directorio local al contexto", + MessageId::KbHelpOverlay => { + "Abrir esta superposición de ayuda (cuando la entrada está vacía)" + } + MessageId::KbToggleHelp => "Alternar superposición de ayuda", + MessageId::KbToggleHelpSlash => "Alternar superposición de ayuda", + MessageId::HelpUsageLabel => "Uso:", + MessageId::HelpAliasesLabel => "Alias:", + MessageId::SettingsTitle => "Configuraciones:", + MessageId::SettingsConfigFile => "Archivo de configuración:", + MessageId::ClearConversation => "Conversación limpia", + MessageId::ClearConversationBusy => { + "Conversación limpia (estado del plan ocupado; ejecuta /clear de nuevo si es necesario)" + } + MessageId::ModelChanged => "Modelo cambiado: {old} \u{2192} {new}", + MessageId::LinksTitle => "Enlaces de DeepSeek:", + MessageId::LinksDashboard => "Panel:", + MessageId::LinksDocs => "Documentación:", + MessageId::LinksTip => "Tip: las claves de API están disponibles en la consola del panel.", + MessageId::SubagentsFetching => "Obteniendo estado de los sub-agentes...", + MessageId::HelpUnknownCommand => "Comando desconocido: {topic}", + MessageId::HomeDashboardTitle => "Panel Inicial de DeepSeek TUI", + MessageId::HomeModel => "Modelo:", + MessageId::HomeMode => "Modo:", + MessageId::HomeWorkspace => "Workspace:", + MessageId::HomeHistory => "Historial:", + MessageId::HomeTokens => "Tokens:", + MessageId::HomeQueued => "En cola:", + MessageId::HomeSubagents => "Sub-agentes:", + MessageId::HomeSkill => "Skill:", + MessageId::HomeQuickActions => "Acciones Rápidas", + MessageId::HomeQuickLinks => "/links - Enlaces del panel y API", + MessageId::HomeQuickSkills => "/skills - Listar skills disponibles", + MessageId::HomeQuickConfig => "/config - Abrir editor interactivo de configuración", + MessageId::HomeQuickSettings => "/settings - Mostrar configuraciones persistentes", + MessageId::HomeQuickModel => "/model - Alternar o visualizar modelo", + MessageId::HomeQuickSubagents => "/subagents - Listar estado de los sub-agentes", + MessageId::HomeQuickTaskList => "/task list - Mostrar fila de tareas en segundo plano", + MessageId::HomeQuickHelp => "/help - Mostrar ayuda", + MessageId::HomeModeTips => "Tips de Modo", + MessageId::HomeAgentModeTip => "Modo Agent - Usar herramientas para tareas autónomas", + MessageId::HomeAgentModeReviewTip => { + " Usa Ctrl+X para revisar en modo Plan antes de ejecutar" + } + MessageId::HomeAgentModeYoloTip => { + " Escribe /mode yolo para habilitar acceso total a las herramientas" + } + MessageId::HomeYoloModeTip => "Modo YOLO - Acceso total a herramientas, sin aprobaciones", + MessageId::HomeYoloModeCaution => " ¡Ten cuidado con operaciones destructivas!", + MessageId::HomePlanModeTip => "Modo Plan - Planear antes de implementar", + MessageId::HomePlanModeChecklistTip => { + " Usa /mode plan para crear checklists estructurados" + } + MessageId::OnboardLanguageTitle => "Elige el idioma", + MessageId::OnboardLanguageBlurb => { + "Elige el idioma de la interfaz. Puedes cambiarlo en cualquier momento con `/settings set locale `." + } + MessageId::OnboardLanguageFooter => { + "Presiona 1-5 para elegir, o Enter para mantener la configuración actual" + } + MessageId::OnboardApiKeyTitle => "Conecta tu clave de API DeepSeek", + MessageId::OnboardApiKeyStep1 => { + "Paso 1. Abre https://platform.deepseek.com/api_keys y crea una clave." + } + MessageId::OnboardApiKeyStep2 => "Paso 2. Pégala abajo y presiona Enter.", + MessageId::OnboardApiKeySavedHint => { + "Guardada en ~/.deepseek/config.toml para funcionar en cualquier carpeta." + } + MessageId::OnboardApiKeyFormatHint => { + "Pega la clave completa tal como fue emitida (sin espacios ni saltos de línea)." + } + MessageId::OnboardApiKeyPlaceholder => "(pega la clave acá)", + MessageId::OnboardApiKeyLabel => "Clave: ", + MessageId::OnboardApiKeyFooter => "Enter para guardar, Esc para volver.", + MessageId::OnboardTrustTitle => "Confiar en el directorio", + MessageId::OnboardTrustQuestion => "¿Confías en el contenido de este directorio?", + MessageId::OnboardTrustLocationPrefix => "Estás en ", + MessageId::OnboardTrustRiskHint => { + "Trabajar con contenido no confiable aumenta el riesgo de inyección de prompt." + } + MessageId::OnboardTrustEffectHint => { + "Confiar en este directorio lo registra en la configuración global y habilita el modo workspace confiable." + } + MessageId::OnboardTrustFooterPrefix => "Presiona ", + MessageId::OnboardTrustFooterMiddle => " para confiar y continuar, ", + MessageId::OnboardTrustFooterSuffix => " para salir", + MessageId::OnboardTipsTitle => "Empieza simple", + MessageId::OnboardTipsLine1 => { + "Escribe la tarea en lenguaje natural. Usa /help o Ctrl+K para comandos." + } + MessageId::OnboardTipsLine2 => { + "El composer inferior es multilínea: Enter envía, Alt+Enter o Ctrl+J agrega una nueva línea." + } + MessageId::OnboardTipsLine3 => { + "Cambia de modo solo cuando el trabajo cambie: Plan para revisar antes, Agent para ejecución, YOLO para auto-aprobación." + } + MessageId::OnboardTipsLine4 => { + "Ctrl+R retoma sesiones anteriores, y Esc cancela el borrador o superposición actual." + } + MessageId::OnboardTipsFooterEnter => "Presiona Enter", + MessageId::OnboardTipsFooterAction => " para abrir el workspace", + }) +} + #[cfg(test)] mod tests { use super::*; @@ -2269,6 +2673,8 @@ mod tests { assert_eq!(normalize_configured_locale("zh_HK.UTF-8"), Some("zh-Hant")); assert_eq!(normalize_configured_locale("pt"), Some("pt-BR")); assert_eq!(normalize_configured_locale("pt-PT"), Some("pt-BR")); + assert_eq!(normalize_configured_locale("es"), Some("es-419")); + assert_eq!(normalize_configured_locale("es-MX"), Some("es-419")); } #[test] diff --git a/crates/tui/src/settings.rs b/crates/tui/src/settings.rs index e36e1327..9dc879b2 100644 --- a/crates/tui/src/settings.rs +++ b/crates/tui/src/settings.rs @@ -193,7 +193,7 @@ pub struct Settings { pub show_thinking: bool, /// Show detailed tool output pub show_tool_details: bool, - /// UI locale: auto, en, ja, zh-Hans, pt-BR + /// UI locale: auto, en, ja, zh-Hans, pt-BR, es-419 pub locale: String, /// UI theme: system, dark, light, grayscale pub theme: String, @@ -475,7 +475,7 @@ impl Settings { "locale" | "language" => { let Some(locale) = normalize_configured_locale(value) else { anyhow::bail!( - "Failed to update setting: invalid locale '{value}'. Expected: auto, en, ja, zh-Hans, pt-BR." + "Failed to update setting: invalid locale '{value}'. Expected: auto, en, ja, zh-Hans, pt-BR, es-419." ); }; self.locale = locale.to_string(); @@ -713,7 +713,7 @@ impl Settings { ("show_tool_details", "Show detailed tool output: on/off"), ( "locale", - "UI locale and default model language: auto, en, ja, zh-Hans, pt-BR", + "UI locale and default model language: auto, en, ja, zh-Hans, pt-BR, es-419", ), ("theme", "UI theme: system, dark, light, grayscale"), (