2
0
Эх сурвалжийг харах

feat(app): spanish translations

Adam 1 сар өмнө
parent
commit
09a9556c70

+ 8 - 2
packages/app/src/context/language.tsx

@@ -7,17 +7,19 @@ import { dict as en } from "@/i18n/en"
 import { dict as zh } from "@/i18n/zh"
 import { dict as ko } from "@/i18n/ko"
 import { dict as de } from "@/i18n/de"
+import { dict as es } from "@/i18n/es"
 import { dict as uiEn } from "@opencode-ai/ui/i18n/en"
 import { dict as uiZh } from "@opencode-ai/ui/i18n/zh"
 import { dict as uiKo } from "@opencode-ai/ui/i18n/ko"
 import { dict as uiDe } from "@opencode-ai/ui/i18n/de"
+import { dict as uiEs } from "@opencode-ai/ui/i18n/es"
 
-export type Locale = "en" | "zh" | "ko" | "de"
+export type Locale = "en" | "zh" | "ko" | "de" | "es"
 
 type RawDictionary = typeof en & typeof uiEn
 type Dictionary = i18n.Flatten<RawDictionary>
 
-const LOCALES: readonly Locale[] = ["en", "zh", "ko", "de"]
+const LOCALES: readonly Locale[] = ["en", "zh", "ko", "de", "es"]
 
 function detectLocale(): Locale {
   if (typeof navigator !== "object") return "en"
@@ -28,6 +30,7 @@ function detectLocale(): Locale {
     if (language.toLowerCase().startsWith("zh")) return "zh"
     if (language.toLowerCase().startsWith("ko")) return "ko"
     if (language.toLowerCase().startsWith("de")) return "de"
+    if (language.toLowerCase().startsWith("es")) return "es"
   }
 
   return "en"
@@ -47,6 +50,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
       if (store.locale === "zh") return "zh"
       if (store.locale === "ko") return "ko"
       if (store.locale === "de") return "de"
+      if (store.locale === "es") return "es"
       return "en"
     })
 
@@ -61,6 +65,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
       if (locale() === "en") return base
       if (locale() === "zh") return { ...base, ...i18n.flatten({ ...zh, ...uiZh }) }
       if (locale() === "de") return { ...base, ...i18n.flatten({ ...de, ...uiDe }) }
+      if (locale() === "es") return { ...base, ...i18n.flatten({ ...es, ...uiEs }) }
       return { ...base, ...i18n.flatten({ ...ko, ...uiKo }) }
     })
 
@@ -71,6 +76,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
       zh: "language.zh",
       ko: "language.ko",
       de: "language.de",
+      es: "language.es",
     }
 
     const label = (value: Locale) => t(labelKey[value])

+ 1 - 0
packages/app/src/i18n/de.ts

@@ -276,6 +276,7 @@ export const dict = {
   "language.zh": "Chinesisch",
   "language.ko": "Koreanisch",
   "language.de": "Deutsch",
+  "language.es": "Spanisch",
 
   "toast.language.title": "Sprache",
   "toast.language.description": "Zu {{language}} gewechselt",

+ 1 - 0
packages/app/src/i18n/en.ts

@@ -270,6 +270,7 @@ export const dict = {
   "language.zh": "Chinese",
   "language.ko": "Korean",
   "language.de": "German",
+  "language.es": "Spanish",
 
   "toast.language.title": "Language",
   "toast.language.description": "Switched to {{language}}",

+ 554 - 0
packages/app/src/i18n/es.ts

@@ -0,0 +1,554 @@
+export const dict = {
+  "command.category.suggested": "Sugerido",
+  "command.category.view": "Ver",
+  "command.category.project": "Proyecto",
+  "command.category.provider": "Proveedor",
+  "command.category.server": "Servidor",
+  "command.category.session": "Sesión",
+  "command.category.theme": "Tema",
+  "command.category.language": "Idioma",
+  "command.category.file": "Archivo",
+  "command.category.terminal": "Terminal",
+  "command.category.model": "Modelo",
+  "command.category.mcp": "MCP",
+  "command.category.agent": "Agente",
+  "command.category.permissions": "Permisos",
+
+  "theme.scheme.system": "Sistema",
+  "theme.scheme.light": "Claro",
+  "theme.scheme.dark": "Oscuro",
+
+  "command.sidebar.toggle": "Alternar barra lateral",
+  "command.project.open": "Abrir proyecto",
+  "command.provider.connect": "Conectar proveedor",
+  "command.server.switch": "Cambiar servidor",
+  "command.session.previous": "Sesión anterior",
+  "command.session.next": "Siguiente sesión",
+  "command.session.archive": "Archivar sesión",
+
+  "command.palette": "Paleta de comandos",
+
+  "command.theme.cycle": "Alternar tema",
+  "command.theme.set": "Usar tema: {{theme}}",
+  "command.theme.scheme.cycle": "Alternar esquema de color",
+  "command.theme.scheme.set": "Usar esquema de color: {{scheme}}",
+
+  "command.language.cycle": "Alternar idioma",
+  "command.language.set": "Usar idioma: {{language}}",
+
+  "command.session.new": "Nueva sesión",
+  "command.file.open": "Abrir archivo",
+  "command.file.open.description": "Buscar archivos y comandos",
+  "command.terminal.toggle": "Alternar terminal",
+  "command.review.toggle": "Alternar revisión",
+  "command.terminal.new": "Nueva terminal",
+  "command.terminal.new.description": "Crear una nueva pestaña de terminal",
+  "command.steps.toggle": "Alternar pasos",
+  "command.steps.toggle.description": "Mostrar u ocultar pasos para el mensaje actual",
+  "command.message.previous": "Mensaje anterior",
+  "command.message.previous.description": "Ir al mensaje de usuario anterior",
+  "command.message.next": "Siguiente mensaje",
+  "command.message.next.description": "Ir al siguiente mensaje de usuario",
+  "command.model.choose": "Elegir modelo",
+  "command.model.choose.description": "Seleccionar un modelo diferente",
+  "command.mcp.toggle": "Alternar MCPs",
+  "command.mcp.toggle.description": "Alternar MCPs",
+  "command.agent.cycle": "Alternar agente",
+  "command.agent.cycle.description": "Cambiar al siguiente agente",
+  "command.agent.cycle.reverse": "Alternar agente hacia atrás",
+  "command.agent.cycle.reverse.description": "Cambiar al agente anterior",
+  "command.model.variant.cycle": "Alternar esfuerzo de pensamiento",
+  "command.model.variant.cycle.description": "Cambiar al siguiente nivel de esfuerzo",
+  "command.permissions.autoaccept.enable": "Aceptar ediciones automáticamente",
+  "command.permissions.autoaccept.disable": "Dejar de aceptar ediciones automáticamente",
+  "command.session.undo": "Deshacer",
+  "command.session.undo.description": "Deshacer el último mensaje",
+  "command.session.redo": "Rehacer",
+  "command.session.redo.description": "Rehacer el último mensaje deshecho",
+  "command.session.compact": "Compactar sesión",
+  "command.session.compact.description": "Resumir la sesión para reducir el tamaño del contexto",
+  "command.session.fork": "Bifurcar desde mensaje",
+  "command.session.fork.description": "Crear una nueva sesión desde un mensaje anterior",
+  "command.session.share": "Compartir sesión",
+  "command.session.share.description": "Compartir esta sesión y copiar la URL al portapapeles",
+  "command.session.unshare": "Dejar de compartir sesión",
+  "command.session.unshare.description": "Dejar de compartir esta sesión",
+
+  "palette.search.placeholder": "Buscar archivos y comandos",
+  "palette.empty": "No se encontraron resultados",
+  "palette.group.commands": "Comandos",
+  "palette.group.files": "Archivos",
+
+  "dialog.provider.search.placeholder": "Buscar proveedores",
+  "dialog.provider.empty": "No se encontraron proveedores",
+  "dialog.provider.group.popular": "Popular",
+  "dialog.provider.group.other": "Otro",
+  "dialog.provider.tag.recommended": "Recomendado",
+  "dialog.provider.anthropic.note": "Conectar con Claude Pro/Max o clave API",
+
+  "dialog.model.select.title": "Seleccionar modelo",
+  "dialog.model.search.placeholder": "Buscar modelos",
+  "dialog.model.empty": "Sin resultados de modelos",
+  "dialog.model.manage": "Gestionar modelos",
+  "dialog.model.manage.description": "Personalizar qué modelos aparecen en el selector de modelos.",
+
+  "dialog.model.unpaid.freeModels.title": "Modelos gratuitos proporcionados por OpenCode",
+  "dialog.model.unpaid.addMore.title": "Añadir más modelos de proveedores populares",
+
+  "dialog.provider.viewAll": "Ver todos los proveedores",
+
+  "provider.connect.title": "Conectar {{provider}}",
+  "provider.connect.title.anthropicProMax": "Iniciar sesión con Claude Pro/Max",
+  "provider.connect.selectMethod": "Seleccionar método de inicio de sesión para {{provider}}.",
+  "provider.connect.method.apiKey": "Clave API",
+  "provider.connect.status.inProgress": "Autorización en progreso...",
+  "provider.connect.status.waiting": "Esperando autorización...",
+  "provider.connect.status.failed": "Autorización fallida: {{error}}",
+  "provider.connect.apiKey.description":
+    "Introduce tu clave API de {{provider}} para conectar tu cuenta y usar modelos de {{provider}} en OpenCode.",
+  "provider.connect.apiKey.label": "Clave API de {{provider}}",
+  "provider.connect.apiKey.placeholder": "Clave API",
+  "provider.connect.apiKey.required": "La clave API es obligatoria",
+  "provider.connect.opencodeZen.line1":
+    "OpenCode Zen te da acceso a un conjunto curado de modelos fiables optimizados para agentes de programación.",
+  "provider.connect.opencodeZen.line2":
+    "Con una sola clave API obtendrás acceso a modelos como Claude, GPT, Gemini, GLM y más.",
+  "provider.connect.opencodeZen.visit.prefix": "Visita ",
+  "provider.connect.opencodeZen.visit.suffix": " para obtener tu clave API.",
+  "provider.connect.oauth.code.visit.prefix": "Visita ",
+  "provider.connect.oauth.code.visit.link": "este enlace",
+  "provider.connect.oauth.code.visit.suffix":
+    " para obtener tu código de autorización para conectar tu cuenta y usar modelos de {{provider}} en OpenCode.",
+  "provider.connect.oauth.code.label": "Código de autorización {{method}}",
+  "provider.connect.oauth.code.placeholder": "Código de autorización",
+  "provider.connect.oauth.code.required": "El código de autorización es obligatorio",
+  "provider.connect.oauth.code.invalid": "Código de autorización inválido",
+  "provider.connect.oauth.auto.visit.prefix": "Visita ",
+  "provider.connect.oauth.auto.visit.link": "este enlace",
+  "provider.connect.oauth.auto.visit.suffix":
+    " e introduce el código a continuación para conectar tu cuenta y usar modelos de {{provider}} en OpenCode.",
+  "provider.connect.oauth.auto.confirmationCode": "Código de confirmación",
+  "provider.connect.toast.connected.title": "{{provider}} conectado",
+  "provider.connect.toast.connected.description": "Los modelos de {{provider}} ahora están disponibles para usar.",
+
+  "model.tag.free": "Gratis",
+  "model.tag.latest": "Último",
+
+  "common.search.placeholder": "Buscar",
+  "common.loading": "Cargando",
+  "common.cancel": "Cancelar",
+  "common.submit": "Enviar",
+  "common.save": "Guardar",
+  "common.saving": "Guardando...",
+  "common.default": "Predeterminado",
+  "common.attachment": "adjunto",
+
+  "prompt.placeholder.shell": "Introduce comando de shell...",
+  "prompt.placeholder.normal": 'Pregunta cualquier cosa... "{{example}}"',
+  "prompt.mode.shell": "Shell",
+  "prompt.mode.shell.exit": "esc para salir",
+
+  "prompt.example.1": "Arreglar un TODO en el código",
+  "prompt.example.2": "¿Cuál es el stack tecnológico de este proyecto?",
+  "prompt.example.3": "Arreglar pruebas rotas",
+  "prompt.example.4": "Explicar cómo funciona la autenticación",
+  "prompt.example.5": "Encontrar y arreglar vulnerabilidades de seguridad",
+  "prompt.example.6": "Añadir pruebas unitarias para el servicio de usuario",
+  "prompt.example.7": "Refactorizar esta función para que sea más legible",
+  "prompt.example.8": "¿Qué significa este error?",
+  "prompt.example.9": "Ayúdame a depurar este problema",
+  "prompt.example.10": "Generar documentación de API",
+  "prompt.example.11": "Optimizar consultas a la base de datos",
+  "prompt.example.12": "Añadir validación de entrada",
+  "prompt.example.13": "Crear un nuevo componente para...",
+  "prompt.example.14": "¿Cómo despliego este proyecto?",
+  "prompt.example.15": "Revisar mi código para mejores prácticas",
+  "prompt.example.16": "Añadir manejo de errores a esta función",
+  "prompt.example.17": "Explicar este patrón de regex",
+  "prompt.example.18": "Convertir esto a TypeScript",
+  "prompt.example.19": "Añadir logging en todo el código",
+  "prompt.example.20": "¿Qué dependencias están desactualizadas?",
+  "prompt.example.21": "Ayúdame a escribir un script de migración",
+  "prompt.example.22": "Implementar caché para este endpoint",
+  "prompt.example.23": "Añadir paginación a esta lista",
+  "prompt.example.24": "Crear un comando CLI para...",
+  "prompt.example.25": "¿Cómo funcionan las variables de entorno aquí?",
+
+  "prompt.popover.emptyResults": "Sin resultados coincidentes",
+  "prompt.popover.emptyCommands": "Sin comandos coincidentes",
+  "prompt.dropzone.label": "Suelta imágenes o PDFs aquí",
+  "prompt.slash.badge.custom": "personalizado",
+  "prompt.context.active": "activo",
+  "prompt.context.includeActiveFile": "Incluir archivo activo",
+  "prompt.action.attachFile": "Adjuntar archivo",
+  "prompt.action.send": "Enviar",
+  "prompt.action.stop": "Detener",
+
+  "prompt.toast.pasteUnsupported.title": "Pegado no soportado",
+  "prompt.toast.pasteUnsupported.description": "Solo se pueden pegar imágenes o PDFs aquí.",
+  "prompt.toast.modelAgentRequired.title": "Selecciona un agente y modelo",
+  "prompt.toast.modelAgentRequired.description": "Elige un agente y modelo antes de enviar un prompt.",
+  "prompt.toast.worktreeCreateFailed.title": "Fallo al crear el árbol de trabajo",
+  "prompt.toast.sessionCreateFailed.title": "Fallo al crear la sesión",
+  "prompt.toast.shellSendFailed.title": "Fallo al enviar comando de shell",
+  "prompt.toast.commandSendFailed.title": "Fallo al enviar comando",
+  "prompt.toast.promptSendFailed.title": "Fallo al enviar prompt",
+
+  "dialog.mcp.title": "MCPs",
+  "dialog.mcp.description": "{{enabled}} de {{total}} habilitados",
+  "dialog.mcp.empty": "No hay MCPs configurados",
+
+  "mcp.status.connected": "conectado",
+  "mcp.status.failed": "fallido",
+  "mcp.status.needs_auth": "necesita auth",
+  "mcp.status.disabled": "deshabilitado",
+
+  "dialog.fork.empty": "No hay mensajes desde donde bifurcar",
+
+  "dialog.directory.search.placeholder": "Buscar carpetas",
+  "dialog.directory.empty": "No se encontraron carpetas",
+
+  "dialog.server.title": "Servidores",
+  "dialog.server.description": "Cambiar a qué servidor de OpenCode se conecta esta app.",
+  "dialog.server.search.placeholder": "Buscar servidores",
+  "dialog.server.empty": "No hay servidores aún",
+  "dialog.server.add.title": "Añadir un servidor",
+  "dialog.server.add.url": "URL del servidor",
+  "dialog.server.add.placeholder": "http://localhost:4096",
+  "dialog.server.add.error": "No se pudo conectar al servidor",
+  "dialog.server.add.checking": "Comprobando...",
+  "dialog.server.add.button": "Añadir",
+  "dialog.server.default.title": "Servidor predeterminado",
+  "dialog.server.default.description":
+    "Conectar a este servidor al iniciar la app en lugar de iniciar un servidor local. Requiere reinicio.",
+  "dialog.server.default.none": "Ningún servidor seleccionado",
+  "dialog.server.default.set": "Establecer servidor actual como predeterminado",
+  "dialog.server.default.clear": "Limpiar",
+
+  "dialog.project.edit.title": "Editar proyecto",
+  "dialog.project.edit.name": "Nombre",
+  "dialog.project.edit.icon": "Icono",
+  "dialog.project.edit.icon.alt": "Icono del proyecto",
+  "dialog.project.edit.icon.hint": "Haz clic o arrastra una imagen",
+  "dialog.project.edit.icon.recommended": "Recomendado: 128x128px",
+  "dialog.project.edit.color": "Color",
+
+  "context.breakdown.title": "Desglose de Contexto",
+  "context.breakdown.note":
+    'Desglose aproximado de tokens de entrada. "Otro" incluye definiciones de herramientas y sobrecarga.',
+  "context.breakdown.system": "Sistema",
+  "context.breakdown.user": "Usuario",
+  "context.breakdown.assistant": "Asistente",
+  "context.breakdown.tool": "Llamadas a herramientas",
+  "context.breakdown.other": "Otro",
+
+  "context.systemPrompt.title": "Prompt del Sistema",
+  "context.rawMessages.title": "Mensajes en bruto",
+
+  "context.stats.session": "Sesión",
+  "context.stats.messages": "Mensajes",
+  "context.stats.provider": "Proveedor",
+  "context.stats.model": "Modelo",
+  "context.stats.limit": "Límite de Contexto",
+  "context.stats.totalTokens": "Tokens Totales",
+  "context.stats.usage": "Uso",
+  "context.stats.inputTokens": "Tokens de Entrada",
+  "context.stats.outputTokens": "Tokens de Salida",
+  "context.stats.reasoningTokens": "Tokens de Razonamiento",
+  "context.stats.cacheTokens": "Tokens de Caché (lectura/escritura)",
+  "context.stats.userMessages": "Mensajes de Usuario",
+  "context.stats.assistantMessages": "Mensajes de Asistente",
+  "context.stats.totalCost": "Costo Total",
+  "context.stats.sessionCreated": "Sesión Creada",
+  "context.stats.lastActivity": "Última Actividad",
+
+  "context.usage.tokens": "Tokens",
+  "context.usage.usage": "Uso",
+  "context.usage.cost": "Costo",
+  "context.usage.clickToView": "Haz clic para ver contexto",
+
+  "language.en": "Inglés",
+  "language.zh": "Chino",
+  "language.ko": "Coreano",
+  "language.de": "Alemán",
+  "language.es": "Español",
+
+  "toast.language.title": "Idioma",
+  "toast.language.description": "Cambiado a {{language}}",
+
+  "toast.theme.title": "Tema cambiado",
+  "toast.scheme.title": "Esquema de color",
+
+  "toast.permissions.autoaccept.on.title": "Aceptando ediciones automáticamente",
+  "toast.permissions.autoaccept.on.description": "Los permisos de edición y escritura serán aprobados automáticamente",
+  "toast.permissions.autoaccept.off.title": "Se dejó de aceptar ediciones automáticamente",
+  "toast.permissions.autoaccept.off.description": "Los permisos de edición y escritura requerirán aprobación",
+
+  "toast.model.none.title": "Ningún modelo seleccionado",
+  "toast.model.none.description": "Conecta un proveedor para resumir esta sesión",
+
+  "toast.file.loadFailed.title": "Fallo al cargar archivo",
+
+  "toast.session.share.copyFailed.title": "Fallo al copiar URL al portapapeles",
+  "toast.session.share.success.title": "Sesión compartida",
+  "toast.session.share.success.description": "¡URL compartida copiada al portapapeles!",
+  "toast.session.share.failed.title": "Fallo al compartir sesión",
+  "toast.session.share.failed.description": "Ocurrió un error al compartir la sesión",
+
+  "toast.session.unshare.success.title": "Sesión dejó de compartirse",
+  "toast.session.unshare.success.description": "¡La sesión dejó de compartirse exitosamente!",
+  "toast.session.unshare.failed.title": "Fallo al dejar de compartir sesión",
+  "toast.session.unshare.failed.description": "Ocurrió un error al dejar de compartir la sesión",
+
+  "toast.session.listFailed.title": "Fallo al cargar sesiones para {{project}}",
+
+  "toast.update.title": "Actualización disponible",
+  "toast.update.description": "Una nueva versión de OpenCode ({{version}}) está disponible para instalar.",
+  "toast.update.action.installRestart": "Instalar y reiniciar",
+  "toast.update.action.notYet": "Todavía no",
+
+  "error.page.title": "Algo salió mal",
+  "error.page.description": "Ocurrió un error al cargar la aplicación.",
+  "error.page.details.label": "Detalles del error",
+  "error.page.action.restart": "Reiniciar",
+  "error.page.action.checking": "Comprobando...",
+  "error.page.action.checkUpdates": "Buscar actualizaciones",
+  "error.page.action.updateTo": "Actualizar a {{version}}",
+  "error.page.report.prefix": "Por favor reporta este error al equipo de OpenCode",
+  "error.page.report.discord": "en Discord",
+  "error.page.version": "Versión: {{version}}",
+
+  "error.dev.rootNotFound":
+    "Elemento raíz no encontrado. ¿Olvidaste añadirlo a tu index.html? ¿O tal vez el atributo id está mal escrito?",
+
+  "error.globalSync.connectFailed": "No se pudo conectar al servidor. ¿Hay un servidor ejecutándose en `{{url}}`?",
+
+  "error.chain.unknown": "Error desconocido",
+  "error.chain.causedBy": "Causado por:",
+  "error.chain.apiError": "Error de API",
+  "error.chain.status": "Estado: {{status}}",
+  "error.chain.retryable": "Reintentable: {{retryable}}",
+  "error.chain.responseBody": "Cuerpo de la respuesta:\n{{body}}",
+  "error.chain.didYouMean": "¿Quisiste decir: {{suggestions}}",
+  "error.chain.modelNotFound": "Modelo no encontrado: {{provider}}/{{model}}",
+  "error.chain.checkConfig": "Comprueba los nombres de proveedor/modelo en tu configuración (opencode.json)",
+  "error.chain.mcpFailed": 'El servidor MCP "{{name}}" falló. Nota, OpenCode no soporta autenticación MCP todavía.',
+  "error.chain.providerAuthFailed": "Autenticación de proveedor fallida ({{provider}}): {{message}}",
+  "error.chain.providerInitFailed":
+    'Fallo al inicializar proveedor "{{provider}}". Comprueba credenciales y configuración.',
+  "error.chain.configJsonInvalid": "El archivo de configuración en {{path}} no es un JSON(C) válido",
+  "error.chain.configJsonInvalidWithMessage":
+    "El archivo de configuración en {{path}} no es un JSON(C) válido: {{message}}",
+  "error.chain.configDirectoryTypo":
+    'El directorio "{{dir}}" en {{path}} no es válido. Renombra el directorio a "{{suggestion}}" o elimínalo. Esto es un error tipográfico común.',
+  "error.chain.configFrontmatterError": "Fallo al analizar frontmatter en {{path}}:\n{{message}}",
+  "error.chain.configInvalid": "El archivo de configuración en {{path}} es inválido",
+  "error.chain.configInvalidWithMessage": "El archivo de configuración en {{path}} es inválido: {{message}}",
+
+  "notification.permission.title": "Permiso requerido",
+  "notification.permission.description": "{{sessionTitle}} en {{projectName}} necesita permiso",
+  "notification.question.title": "Pregunta",
+  "notification.question.description": "{{sessionTitle}} en {{projectName}} tiene una pregunta",
+  "notification.action.goToSession": "Ir a sesión",
+
+  "notification.session.responseReady.title": "Respuesta lista",
+  "notification.session.error.title": "Error de sesión",
+  "notification.session.error.fallbackDescription": "Ocurrió un error",
+
+  "home.recentProjects": "Proyectos recientes",
+  "home.empty.title": "Sin proyectos recientes",
+  "home.empty.description": "Empieza abriendo un proyecto local",
+
+  "session.tab.session": "Sesión",
+  "session.tab.review": "Revisión",
+  "session.tab.context": "Contexto",
+  "session.review.filesChanged": "{{count}} Archivos Cambiados",
+  "session.review.loadingChanges": "Cargando cambios...",
+  "session.review.empty": "No hay cambios en esta sesión aún",
+  "session.messages.renderEarlier": "Renderizar mensajes anteriores",
+  "session.messages.loadingEarlier": "Cargando mensajes anteriores...",
+  "session.messages.loadEarlier": "Cargar mensajes anteriores",
+  "session.messages.loading": "Cargando mensajes...",
+
+  "session.context.addToContext": "Añadir {{selection}} al contexto",
+
+  "session.new.worktree.main": "Rama principal",
+  "session.new.worktree.mainWithBranch": "Rama principal ({{branch}})",
+  "session.new.worktree.create": "Crear nuevo árbol de trabajo",
+  "session.new.lastModified": "Última modificación",
+
+  "session.header.search.placeholder": "Buscar {{project}}",
+
+  "session.share.popover.title": "Publicar en web",
+  "session.share.popover.description.shared":
+    "Esta sesión es pública en la web. Es accesible para cualquiera con el enlace.",
+  "session.share.popover.description.unshared":
+    "Compartir sesión públicamente en la web. Será accesible para cualquiera con el enlace.",
+  "session.share.action.share": "Compartir",
+  "session.share.action.publish": "Publicar",
+  "session.share.action.publishing": "Publicando...",
+  "session.share.action.unpublish": "Despublicar",
+  "session.share.action.unpublishing": "Despublicando...",
+  "session.share.action.view": "Ver",
+  "session.share.copy.copied": "Copiado",
+  "session.share.copy.copyLink": "Copiar enlace",
+
+  "lsp.tooltip.none": "Sin servidores LSP",
+  "lsp.label.connected": "{{count}} LSP",
+
+  "prompt.loading": "Cargando prompt...",
+  "terminal.loading": "Cargando terminal...",
+  "terminal.title": "Terminal",
+  "terminal.title.numbered": "Terminal {{number}}",
+
+  "common.closeTab": "Cerrar pestaña",
+  "common.dismiss": "Descartar",
+  "common.requestFailed": "Solicitud fallida",
+  "common.moreOptions": "Más opciones",
+  "common.learnMore": "Saber más",
+  "common.rename": "Renombrar",
+  "common.reset": "Restablecer",
+  "common.delete": "Eliminar",
+  "common.close": "Cerrar",
+  "common.edit": "Editar",
+  "common.loadMore": "Cargar más",
+
+  "sidebar.settings": "Ajustes",
+  "sidebar.help": "Ayuda",
+  "sidebar.workspaces.enable": "Habilitar espacios de trabajo",
+  "sidebar.workspaces.disable": "Deshabilitar espacios de trabajo",
+  "sidebar.gettingStarted.title": "Empezando",
+  "sidebar.gettingStarted.line1": "OpenCode incluye modelos gratuitos para que puedas empezar inmediatamente.",
+  "sidebar.gettingStarted.line2": "Conecta cualquier proveedor para usar modelos, inc. Claude, GPT, Gemini etc.",
+  "sidebar.project.recentSessions": "Sesiones recientes",
+  "sidebar.project.viewAllSessions": "Ver todas las sesiones",
+
+  "settings.section.desktop": "Escritorio",
+  "settings.tab.general": "General",
+  "settings.tab.shortcuts": "Atajos",
+
+  "settings.general.section.appearance": "Apariencia",
+  "settings.general.section.notifications": "Notificaciones del sistema",
+  "settings.general.section.sounds": "Efectos de sonido",
+
+  "settings.general.row.language.title": "Idioma",
+  "settings.general.row.language.description": "Cambiar el idioma de visualización para OpenCode",
+  "settings.general.row.appearance.title": "Apariencia",
+  "settings.general.row.appearance.description": "Personaliza cómo se ve OpenCode en tu dispositivo",
+  "settings.general.row.theme.title": "Tema",
+  "settings.general.row.theme.description": "Personaliza el tema de OpenCode.",
+  "settings.general.row.font.title": "Fuente",
+  "settings.general.row.font.description": "Personaliza la fuente mono usada en bloques de código",
+
+  "settings.general.notifications.agent.title": "Agente",
+  "settings.general.notifications.agent.description":
+    "Mostrar notificación del sistema cuando el agente termine o necesite atención",
+  "settings.general.notifications.permissions.title": "Permisos",
+  "settings.general.notifications.permissions.description":
+    "Mostrar notificación del sistema cuando se requiera un permiso",
+  "settings.general.notifications.errors.title": "Errores",
+  "settings.general.notifications.errors.description": "Mostrar notificación del sistema cuando ocurra un error",
+
+  "settings.general.sounds.agent.title": "Agente",
+  "settings.general.sounds.agent.description": "Reproducir sonido cuando el agente termine o necesite atención",
+  "settings.general.sounds.permissions.title": "Permisos",
+  "settings.general.sounds.permissions.description": "Reproducir sonido cuando se requiera un permiso",
+  "settings.general.sounds.errors.title": "Errores",
+  "settings.general.sounds.errors.description": "Reproducir sonido cuando ocurra un error",
+
+  "settings.shortcuts.title": "Atajos de teclado",
+  "settings.shortcuts.reset.button": "Restablecer a valores predeterminados",
+  "settings.shortcuts.reset.toast.title": "Atajos restablecidos",
+  "settings.shortcuts.reset.toast.description":
+    "Los atajos de teclado han sido restablecidos a los valores predeterminados.",
+  "settings.shortcuts.conflict.title": "Atajo ya en uso",
+  "settings.shortcuts.conflict.description": "{{keybind}} ya está asignado a {{titles}}.",
+  "settings.shortcuts.unassigned": "Sin asignar",
+  "settings.shortcuts.pressKeys": "Presiona teclas",
+
+  "settings.shortcuts.group.general": "General",
+  "settings.shortcuts.group.session": "Sesión",
+  "settings.shortcuts.group.navigation": "Navegación",
+  "settings.shortcuts.group.modelAndAgent": "Modelo y agente",
+  "settings.shortcuts.group.terminal": "Terminal",
+  "settings.shortcuts.group.prompt": "Prompt",
+
+  "settings.providers.title": "Proveedores",
+  "settings.providers.description": "La configuración de proveedores estará disponible aquí.",
+  "settings.models.title": "Modelos",
+  "settings.models.description": "La configuración de modelos estará disponible aquí.",
+  "settings.agents.title": "Agentes",
+  "settings.agents.description": "La configuración de agentes estará disponible aquí.",
+  "settings.commands.title": "Comandos",
+  "settings.commands.description": "La configuración de comandos estará disponible aquí.",
+  "settings.mcp.title": "MCP",
+  "settings.mcp.description": "La configuración de MCP estará disponible aquí.",
+
+  "settings.permissions.title": "Permisos",
+  "settings.permissions.description": "Controla qué herramientas puede usar el servidor por defecto.",
+  "settings.permissions.section.tools": "Herramientas",
+  "settings.permissions.toast.updateFailed.title": "Fallo al actualizar permisos",
+
+  "settings.permissions.action.allow": "Permitir",
+  "settings.permissions.action.ask": "Preguntar",
+  "settings.permissions.action.deny": "Denegar",
+
+  "settings.permissions.tool.read.title": "Leer",
+  "settings.permissions.tool.read.description": "Leer un archivo (coincide con la ruta del archivo)",
+  "settings.permissions.tool.edit.title": "Editar",
+  "settings.permissions.tool.edit.description":
+    "Modificar archivos, incluyendo ediciones, escrituras, parches y multi-ediciones",
+  "settings.permissions.tool.glob.title": "Glob",
+  "settings.permissions.tool.glob.description": "Coincidir archivos usando patrones glob",
+  "settings.permissions.tool.grep.title": "Grep",
+  "settings.permissions.tool.grep.description": "Buscar contenidos de archivo usando expresiones regulares",
+  "settings.permissions.tool.list.title": "Listar",
+  "settings.permissions.tool.list.description": "Listar archivos dentro de un directorio",
+  "settings.permissions.tool.bash.title": "Bash",
+  "settings.permissions.tool.bash.description": "Ejecutar comandos de shell",
+  "settings.permissions.tool.task.title": "Tarea",
+  "settings.permissions.tool.task.description": "Lanzar sub-agentes",
+  "settings.permissions.tool.skill.title": "Habilidad",
+  "settings.permissions.tool.skill.description": "Cargar una habilidad por nombre",
+  "settings.permissions.tool.lsp.title": "LSP",
+  "settings.permissions.tool.lsp.description": "Ejecutar consultas de servidor de lenguaje",
+  "settings.permissions.tool.todoread.title": "Leer Todo",
+  "settings.permissions.tool.todoread.description": "Leer la lista de tareas",
+  "settings.permissions.tool.todowrite.title": "Escribir Todo",
+  "settings.permissions.tool.todowrite.description": "Actualizar la lista de tareas",
+  "settings.permissions.tool.webfetch.title": "Web Fetch",
+  "settings.permissions.tool.webfetch.description": "Obtener contenido de una URL",
+  "settings.permissions.tool.websearch.title": "Búsqueda Web",
+  "settings.permissions.tool.websearch.description": "Buscar en la web",
+  "settings.permissions.tool.codesearch.title": "Búsqueda de Código",
+  "settings.permissions.tool.codesearch.description": "Buscar código en la web",
+  "settings.permissions.tool.external_directory.title": "Directorio Externo",
+  "settings.permissions.tool.external_directory.description": "Acceder a archivos fuera del directorio del proyecto",
+  "settings.permissions.tool.doom_loop.title": "Bucle Infinito",
+  "settings.permissions.tool.doom_loop.description": "Detectar llamadas a herramientas repetidas con entrada idéntica",
+
+  "workspace.new": "Nuevo espacio de trabajo",
+  "workspace.type.local": "local",
+  "workspace.type.sandbox": "sandbox",
+  "workspace.create.failed.title": "Fallo al crear espacio de trabajo",
+  "workspace.delete.failed.title": "Fallo al eliminar espacio de trabajo",
+  "workspace.resetting.title": "Restableciendo espacio de trabajo",
+  "workspace.resetting.description": "Esto puede tomar un minuto.",
+  "workspace.reset.failed.title": "Fallo al restablecer espacio de trabajo",
+  "workspace.reset.success.title": "Espacio de trabajo restablecido",
+  "workspace.reset.success.description": "El espacio de trabajo ahora coincide con la rama predeterminada.",
+  "workspace.status.checking": "Comprobando cambios no fusionados...",
+  "workspace.status.error": "No se pudo verificar el estado de git.",
+  "workspace.status.clean": "No se detectaron cambios no fusionados.",
+  "workspace.status.dirty": "Cambios no fusionados detectados en este espacio de trabajo.",
+  "workspace.delete.title": "Eliminar espacio de trabajo",
+  "workspace.delete.confirm": '¿Eliminar espacio de trabajo "{{name}}"?',
+  "workspace.delete.button": "Eliminar espacio de trabajo",
+  "workspace.reset.title": "Restablecer espacio de trabajo",
+  "workspace.reset.confirm": '¿Restablecer espacio de trabajo "{{name}}"?',
+  "workspace.reset.button": "Restablecer espacio de trabajo",
+  "workspace.reset.archived.none": "No se archivarán sesiones activas.",
+  "workspace.reset.archived.one": "1 sesión será archivada.",
+  "workspace.reset.archived.many": "{{count}} sesiones serán archivadas.",
+  "workspace.reset.note": "Esto restablecerá el espacio de trabajo para coincidir con la rama predeterminada.",
+}

+ 1 - 0
packages/app/src/i18n/ko.ts

@@ -273,6 +273,7 @@ export const dict = {
   "language.zh": "중국어",
   "language.ko": "한국어",
   "language.de": "독일어",
+  "language.es": "스페인어",
 
   "toast.language.title": "언어",
   "toast.language.description": "{{language}}(으)로 전환됨",

+ 1 - 0
packages/app/src/i18n/zh.ts

@@ -269,6 +269,7 @@ export const dict = {
   "language.zh": "中文",
   "language.ko": "韩语",
   "language.de": "德语",
+  "language.es": "西班牙语",
 
   "toast.language.title": "语言",
   "toast.language.description": "已切换到{{language}}",

+ 90 - 0
packages/ui/src/i18n/es.ts

@@ -0,0 +1,90 @@
+export const dict = {
+  "ui.sessionReview.title": "Cambios de la sesión",
+  "ui.sessionReview.diffStyle.unified": "Unificado",
+  "ui.sessionReview.diffStyle.split": "Dividido",
+  "ui.sessionReview.expandAll": "Expandir todo",
+  "ui.sessionReview.collapseAll": "Colapsar todo",
+
+  "ui.sessionTurn.steps.show": "Mostrar pasos",
+  "ui.sessionTurn.steps.hide": "Ocultar pasos",
+  "ui.sessionTurn.summary.response": "Respuesta",
+  "ui.sessionTurn.diff.showMore": "Mostrar más cambios ({{count}})",
+
+  "ui.sessionTurn.retry.retrying": "reintentando",
+  "ui.sessionTurn.retry.inSeconds": "en {{seconds}}s",
+
+  "ui.sessionTurn.status.delegating": "Delegando trabajo",
+  "ui.sessionTurn.status.planning": "Planificando siguientes pasos",
+  "ui.sessionTurn.status.gatheringContext": "Recopilando contexto",
+  "ui.sessionTurn.status.searchingCodebase": "Buscando en la base de código",
+  "ui.sessionTurn.status.searchingWeb": "Buscando en la web",
+  "ui.sessionTurn.status.makingEdits": "Realizando ediciones",
+  "ui.sessionTurn.status.runningCommands": "Ejecutando comandos",
+  "ui.sessionTurn.status.thinking": "Pensando",
+  "ui.sessionTurn.status.thinkingWithTopic": "Pensando - {{topic}}",
+  "ui.sessionTurn.status.gatheringThoughts": "Recopilando pensamientos",
+  "ui.sessionTurn.status.consideringNextSteps": "Considerando siguientes pasos",
+
+  "ui.messagePart.diagnostic.error": "Error",
+  "ui.messagePart.title.edit": "Editar",
+  "ui.messagePart.title.write": "Escribir",
+  "ui.messagePart.option.typeOwnAnswer": "Escribe tu propia respuesta",
+  "ui.messagePart.review.title": "Revisa tus respuestas",
+
+  "ui.list.loading": "Cargando",
+  "ui.list.empty": "Sin resultados",
+  "ui.list.emptyWithFilter.prefix": "Sin resultados para",
+  "ui.list.emptyWithFilter.suffix": "",
+
+  "ui.messageNav.newMessage": "Nuevo mensaje",
+
+  "ui.textField.copyToClipboard": "Copiar al portapapeles",
+  "ui.textField.copied": "Copiado",
+
+  "ui.imagePreview.alt": "Vista previa de imagen",
+
+  "ui.tool.read": "Leer",
+  "ui.tool.list": "Listar",
+  "ui.tool.glob": "Glob",
+  "ui.tool.grep": "Grep",
+  "ui.tool.webfetch": "Webfetch",
+  "ui.tool.shell": "Shell",
+  "ui.tool.patch": "Parche",
+  "ui.tool.todos": "Tareas",
+  "ui.tool.todos.read": "Leer tareas",
+  "ui.tool.questions": "Preguntas",
+  "ui.tool.agent": "Agente {{type}}",
+
+  "ui.common.file.one": "archivo",
+  "ui.common.file.other": "archivos",
+  "ui.common.question.one": "pregunta",
+  "ui.common.question.other": "preguntas",
+
+  "ui.common.add": "Añadir",
+  "ui.common.cancel": "Cancelar",
+  "ui.common.confirm": "Confirmar",
+  "ui.common.dismiss": "Descartar",
+  "ui.common.next": "Siguiente",
+  "ui.common.submit": "Enviar",
+
+  "ui.permission.deny": "Denegar",
+  "ui.permission.allowAlways": "Permitir siempre",
+  "ui.permission.allowOnce": "Permitir una vez",
+
+  "ui.message.expand": "Expandir mensaje",
+  "ui.message.collapse": "Colapsar mensaje",
+  "ui.message.copy": "Copiar",
+  "ui.message.copied": "¡Copiado!",
+  "ui.message.attachment.alt": "adjunto",
+
+  "ui.patch.action.deleted": "Eliminado",
+  "ui.patch.action.created": "Creado",
+  "ui.patch.action.moved": "Movido",
+  "ui.patch.action.patched": "Parcheado",
+
+  "ui.question.subtitle.answered": "{{count}} respondidas",
+  "ui.question.answer.none": "(sin respuesta)",
+  "ui.question.review.notAnswered": "(no respondida)",
+  "ui.question.multiHint": "(selecciona todas las que correspondan)",
+  "ui.question.custom.placeholder": "Escribe tu respuesta...",
+}