diff --git a/webui/src/components/thread/ThreadMessages.tsx b/webui/src/components/thread/ThreadMessages.tsx index 9fd01e9c6..1f5b2de9c 100644 --- a/webui/src/components/thread/ThreadMessages.tsx +++ b/webui/src/components/thread/ThreadMessages.tsx @@ -137,10 +137,6 @@ function currentActivityClusterIndices(units: DisplayUnit[]): Set { if (!markedCurrentActivity) { indices.add(i); markedCurrentActivity = true; - continue; - } - if (activityHasLiveFileEdit(unit)) { - indices.add(i); } continue; } @@ -150,13 +146,6 @@ function currentActivityClusterIndices(units: DisplayUnit[]): Set { return indices; } -function activityHasLiveFileEdit(unit: Extract): boolean { - return unit.messages.some((message) => ( - message.kind === "trace" - && message.fileEdits?.some((edit) => edit.status === "editing" || edit.pending || !edit.path) - )); -} - function unitKey(unit: DisplayUnit, index: number): string { if (unit.type === "activity") { const anchor = unit.messages[0]?.id; diff --git a/webui/src/hooks/useNanobotStream.ts b/webui/src/hooks/useNanobotStream.ts index 48c00ca98..9bdc43b76 100644 --- a/webui/src/hooks/useNanobotStream.ts +++ b/webui/src/hooks/useNanobotStream.ts @@ -58,11 +58,10 @@ function findStreamingAssistantIndex( /** * Append a reasoning chunk to the last open reasoning stream in ``prev``. * - * Lookup rule: prefer the most recent assistant turn in the active UI tail. - * Most providers emit reasoning before answer text, but some only expose - * ``reasoning_content`` after the answer stream completes. In that post-hoc - * case the reasoning still belongs to the same assistant turn and must render - * above the answer, not as a new row below it. + * Lookup rule: reasoning can only extend the current reasoning placeholder. + * Once ordinary answer text has appeared, the next reasoning chunk starts a + * fresh Thought block so streamed output stays in arrival order: + * Thought -> answer -> Thought -> answer. */ function attachReasoningChunk( prev: UIMessage[], @@ -83,10 +82,10 @@ function attachReasoningChunk( if (candidate.role !== "assistant") continue; const activitySegmentId = candidate.activitySegmentId ?? segments?.ensure(); const hasAnswer = candidate.content.length > 0; + if (hasAnswer) break; if ( candidate.reasoningStreaming || candidate.reasoning !== undefined - || hasAnswer || candidate.isStreaming ) { const merged: UIMessage = { @@ -97,15 +96,6 @@ function attachReasoningChunk( }; return [...prev.slice(0, i), merged, ...prev.slice(i + 1)]; } - if (!hasAnswer && candidate.isStreaming) { - const merged: UIMessage = { - ...candidate, - reasoning: chunk, - reasoningStreaming: true, - ...(activitySegmentId ? { activitySegmentId } : {}), - }; - return [...prev.slice(0, i), merged, ...prev.slice(i + 1)]; - } break; } const activitySegmentId = segments?.ensure(); @@ -293,38 +283,6 @@ function stripCoveredFileEditToolHints(message: UIMessage, edits: UIFileEdit[]): }; } -function demoteInterruptedAssistantToActivity( - prev: UIMessage[], - segmentId: string, -): UIMessage[] { - for (let i = prev.length - 1; i >= 0; i -= 1) { - const message = prev[i]; - if (message.role === "user") break; - if ( - message.role !== "assistant" - || message.kind === "trace" - || !message.isStreaming - || !message.content.trim() - || message.media?.length - ) { - continue; - } - const reasoning = [message.reasoning, message.content] - .filter((part): part is string => typeof part === "string" && part.trim().length > 0) - .join("\n\n"); - const demoted: UIMessage = { - ...message, - content: "", - reasoning, - reasoningStreaming: false, - isStreaming: false, - activitySegmentId: message.activitySegmentId ?? segmentId, - }; - return replaceMessageAt(prev, i, demoted); - } - return prev; -} - function normalizeFileEdit(edit: UIFileEdit): UIFileEdit | null { if (!edit || !edit.tool || (!edit.path && !edit.pending)) return null; const inferredStatus = @@ -511,6 +469,7 @@ export function useNanobotStream( if (closedStreamId) closedAssistantStreamIdsRef.current.add(closedStreamId); buffer.current = null; activeAssistantRef.current = null; + return !!closedStreamId; }, []); const resolveActiveAssistantIndex = useCallback((prev: UIMessage[]): number | null => { @@ -589,15 +548,18 @@ export function useNanobotStream( text += events[i].text; i += 1; } - next = kind === "delta" - ? appendAnswerChunk(next, text) - : attachReasoningChunk(next, text, { - ensure: ensureActivitySegmentId, - }); + if (kind === "delta") { + next = appendAnswerChunk(next, text); + } else { + if (closeActiveAssistantStream()) clearActivitySegment(); + next = attachReasoningChunk(next, text, { + ensure: ensureActivitySegmentId, + }); + } } return next; }, - [appendAnswerChunk, ensureActivitySegmentId], + [appendAnswerChunk, clearActivitySegment, closeActiveAssistantStream, ensureActivitySegmentId], ); const flushPendingStreamEvents = useCallback((options?: { @@ -735,7 +697,13 @@ export function useNanobotStream( return; } - flushPendingStreamEvents(); + const shouldCloseAnswerBeforeEvent = + ev.event === "file_edit" + || ( + ev.event === "message" + && (ev.kind === "tool_hint" || ev.kind === "progress") + ); + flushPendingStreamEvents({ closeAnswerSegment: shouldCloseAnswerBeforeEvent }); if (ev.event === "reasoning_end") { if (suppressStreamUntilTurnEndRef.current) return; @@ -812,7 +780,7 @@ export function useNanobotStream( const structuredEvents = normalizeToolProgressEvents(ev.tool_events); setMessages((prev) => { const segmentId = ensureActivitySegmentId(); - const base = demoteInterruptedAssistantToActivity(prev, segmentId); + const base = prev; const visibleStructuredEvents = filterCoveredFileEditToolEvents(base, structuredEvents); const structuredLines = toolTraceLinesFromEvents(visibleStructuredEvents); const lines = structuredLines.length > 0 @@ -914,7 +882,7 @@ export function useNanobotStream( } setMessages((prev) => { let segmentId = eventSegmentId; - const base = segmentId ? demoteInterruptedAssistantToActivity(prev, segmentId) : prev; + const base = prev; const targetIndex = findFileEditTraceIndex(base, segmentId, normalized); if (targetIndex !== null) { const target = base[targetIndex]; diff --git a/webui/src/i18n/locales/es/common.json b/webui/src/i18n/locales/es/common.json index 4f742daa2..6b234aae1 100644 --- a/webui/src/i18n/locales/es/common.json +++ b/webui/src/i18n/locales/es/common.json @@ -59,43 +59,43 @@ "settings": { "backToChat": "Volver al chat", "sidebar": { - "title": "Configuración", - "ariaLabel": "Secciones de configuración" + "title": "Ajustes", + "ariaLabel": "Secciones de ajustes" }, "nav": { "general": "General", "byok": "BYOK", - "overview": "Overview", - "appearance": "Appearance", - "models": "Models", - "providers": "Providers", - "image": "Image", - "browser": "Web", + "overview": "Resumen", + "appearance": "Apariencia", + "models": "Modelos", + "providers": "Proveedores", + "image": "Imagen", + "browser": "Internet", "runtime": "Sistema", - "advanced": "Security", + "advanced": "Seguridad", "cliApps": "Apps CLI", "mcp": "MCP", - "apps": "Apps" + "apps": "Aplicaciones" }, "sections": { "interface": "Interfaz", - "ai": "IA", + "ai": "AI", "system": "Sistema", - "status": "Status", - "localPreferences": "Local preferences", - "presets": "Presets", + "status": "Estado", + "localPreferences": "Preferencias locales", + "presets": "Preajustes", "imageGeneration": "Generación de imágenes", "imageDefaults": "Valores predeterminados", - "webSearch": "Web search", - "webBehavior": "Behavior", - "identity": "Identity", - "webuiSafety": "Web safety", + "webSearch": "Búsqueda web", + "webBehavior": "Comportamiento", + "identity": "Identidad", + "webuiSafety": "Seguridad de WebUI", "capabilities": "Capacidades", - "cliApps": "Apps CLI", + "cliApps": "Aplicaciones CLI", "mcp": "Servicios MCP", - "apps": "Apps", - "nativeHost": "App", - "hostSafety": "App safety" + "apps": "Aplicaciones", + "nativeHost": "Host nativo", + "hostSafety": "Seguridad de la app" }, "rows": { "theme": "Tema", @@ -104,118 +104,118 @@ "model": "Modelo", "restart": "Reiniciar nanobot", "configPath": "Ruta de configuración", - "activePreset": "Active preset", - "gateway": "Gateway", - "restartState": "Restart state", + "activePreset": "Preajuste activo", + "gateway": "Pasarela", + "restartState": "Estado de reinicio", "pendingChanges": "Cambios pendientes", - "selectedPreset": "Selected preset", - "presetModel": "Preset model", - "density": "Density", - "activityMode": "Activity detail", - "codeWrap": "Code wrapping", - "maxResults": "Max results", - "timeout": "Timeout", - "jinaReader": "Jina reader", + "selectedPreset": "Preajuste seleccionado", + "presetModel": "Modelo del preajuste", + "density": "Densidad", + "activityMode": "Detalle de actividad", + "codeWrap": "Ajuste de código", + "maxResults": "Resultados máximos", + "timeout": "Tiempo de espera", + "jinaReader": "Lector Jina", "imageGeneration": "Generación de imágenes", "imageProvider": "Proveedor de imágenes", "imageProviderStatus": "Estado del proveedor", "imageProviderBase": "Base del proveedor", "imageModel": "Modelo de imagen", - "defaultAspectRatio": "Relación predeterminada", + "defaultAspectRatio": "Proporción predeterminada", "defaultImageSize": "Tamaño predeterminado", - "maxImagesPerTurn": "Máximo de imágenes por turno", + "maxImagesPerTurn": "Máx. imágenes por turno", "imageSaveDir": "Directorio de guardado", - "botName": "Bot name", - "botIcon": "Bot icon", - "timezone": "Timezone", + "botName": "Nombre del bot", + "botIcon": "Icono del bot", + "timezone": "Zona horaria", "workspacePath": "Workspace predeterminado", - "localServiceAccess": "Local services", - "webuiDefaultAccess": "Default access", + "localServiceAccess": "Servicios locales", + "webuiDefaultAccess": "Acceso predeterminado", "currentModel": "Configuración actual", - "brandLogos": "Logotipos de marca", - "cliAppsCatalog": "Catálogo de apps CLI", - "cliAppsFilter": "Filtro de apps CLI", + "brandLogos": "Logos de marca", + "cliAppsCatalog": "Catálogo", + "cliAppsFilter": "Filtro", "engine": "Motor", "logs": "Registros", "diagnostics": "Diagnóstico", - "contextWindow": "Context window" + "contextWindow": "Ventana de contexto" }, "help": { "theme": "Cambia entre apariencia clara y oscura.", - "language": "Elige el idioma usado por la WebUI.", + "language": "Elige el idioma usado por WebUI.", "provider": "Selecciona el proveedor para nuevas solicitudes de modelo.", - "model": "Define el nombre del modelo predeterminado que usa nanobot.", - "configPath": "El archivo de configuración que usa actualmente el gateway.", - "selectedPreset": "Named presets are read-only here; edit them in config.json.", - "presetModel": "Switch to Default to edit model and provider from the WebUI.", - "density": "Stored only in this browser.", - "activityMode": "Choose how much agent activity chrome to show by default.", - "codeWrap": "Keep long code lines readable on smaller screens.", - "maxResults": "Results returned by each web_search call.", - "timeout": "Seconds before a search provider request times out.", - "jinaReader": "Use Jina Reader for web_fetch when available.", - "imageGeneration": "Expone generate_image en los chats cuando hay un proveedor de imágenes configurado disponible.", - "imageProvider": "Elige el proveedor del registro que usará generate_image.", - "imageProviderStatus": "La generación de imágenes reutiliza las credenciales de Proveedores.", + "model": "Define el nombre de modelo predeterminado de nanobot.", + "configPath": "Archivo de configuración que usa actualmente el gateway.", + "selectedPreset": "Los preajustes con nombre son de solo lectura aquí; edítalos en config.json.", + "presetModel": "Cambia a Default para editar modelo y proveedor desde WebUI.", + "density": "Solo se guarda en este navegador.", + "activityMode": "Elige cuánto detalle de actividad del agente se muestra por defecto.", + "codeWrap": "Mantiene legibles las líneas largas de código en pantallas pequeñas.", + "maxResults": "Resultados devueltos por cada llamada web_search.", + "timeout": "Segundos antes de que una solicitud de búsqueda expire.", + "jinaReader": "Usa Jina Reader para web_fetch cuando esté disponible.", + "imageGeneration": "Expone generate_image en chats cuando hay un proveedor de imagen configurado.", + "imageProvider": "Elige el proveedor registrado usado por generate_image.", + "imageProviderStatus": "La generación de imágenes reutiliza credenciales de Proveedores.", "imageModel": "Nombre del modelo enviado al proveedor de imágenes seleccionado.", - "defaultAspectRatio": "Se usa cuando el prompt no elige una relación de aspecto.", - "defaultImageSize": "Sugerencia de tamaño enviada a los proveedores que la admiten.", + "defaultAspectRatio": "Se usa cuando el prompt no elige una proporción.", + "defaultImageSize": "Pista de tamaño enviada a proveedores compatibles.", "maxImagesPerTurn": "Límite superior para una solicitud generate_image.", "botName": "Se muestra donde nanobot usa un nombre visible.", - "botIcon": "Emoji o texto corto mostrado junto al nombre del bot.", - "timezone": "Se usa para programaciones y respuestas sensibles al tiempo.", - "localServiceAccess": "Allow Full Access shell commands to reach localhost services.", - "webuiDefaultAccess": "Used by web chats without a project-specific permission.", - "securityManagedControls": "Web fetches always protect local, private, and metadata services. Core channel safety stays in config.json.", - "currentModel": "Elige la configuración de modelo que nanobot usará para las próximas respuestas.", - "selectedModelProvider": "Lo define el modelo seleccionado.", - "selectedModelValue": "Lo define el modelo seleccionado.", - "brandLogos": "Los logotipos se cargan desde los dominios de las marcas con una reserva de icono local.", - "cliAppsCatalog": "Explora CLIs de apps que nanobot puede ejecutar localmente.", + "botIcon": "Emoji o texto corto junto al nombre del bot.", + "timezone": "Se usa para horarios y respuestas con conciencia temporal.", + "localServiceAccess": "Permite que comandos shell con Full Access alcancen servicios localhost.", + "webuiDefaultAccess": "Usado por chats web sin permiso específico de proyecto.", + "securityManagedControls": "Las capturas web siempre protegen servicios locales, privados y metadata. La seguridad de canales core se gestiona en config.json.", + "currentModel": "Se usa para nuevas respuestas.", + "selectedModelProvider": "Definido por el modelo seleccionado.", + "selectedModelValue": "Definido por el modelo seleccionado.", + "brandLogos": "Muestra logos de proveedores de terceros y CLI en Ajustes.", + "cliAppsCatalog": "Instala solo adaptadores CLI de apps que nanobot puede ejecutar localmente; las apps nativas no se modifican.", "cliAppsFilter": "Busca por app, categoría o capacidad.", - "logs": "Abre la carpeta de registros del motor de escritorio.", + "logs": "Abre la carpeta de registros del motor nativo.", "diagnostics": "Exporta un pequeño informe de runtime para soporte.", - "localServiceAccessNative": "Allow Full Access shell commands to reach services on this Mac.", - "webuiDefaultAccessNative": "Used by native chats without a project-specific permission.", - "contextWindow": "Choose the default context budget for this model configuration." + "localServiceAccessNative": "Permite que comandos shell con Full Access alcancen servicios en este Mac.", + "webuiDefaultAccessNative": "Usado por chats nativos sin permiso específico de proyecto.", + "contextWindow": "Elige el presupuesto de contexto predeterminado para esta configuración de modelo." }, "values": { "light": "Claro", "dark": "Oscuro", "notAvailable": "No disponible", - "enabled": "Enabled", - "disabled": "Disabled", + "enabled": "Activado", + "disabled": "Desactivado", "restartPending": "Reinicio pendiente", "ready": "Listo", - "comfortable": "Comfortable", - "compact": "Compact", - "auto": "Auto", - "expanded": "Expanded", - "on": "On", - "off": "Off", - "defaultPermission": "Default Permission", - "fullAccess": "Full Access", - "configured": "Configured", - "notConfigured": "Not configured", + "comfortable": "Cómodo", + "compact": "Compacto", + "auto": "Automático", + "expanded": "Expandido", + "on": "Activado", + "off": "Desactivado", + "defaultPermission": "Permiso predeterminado", + "fullAccess": "Acceso completo", + "configured": "Configurado", + "notConfigured": "Sin configurar", "pending": "Pendiente", "restartingEngine": "Reiniciando" }, "status": { - "loading": "Cargando configuración...", - "loadError": "No se pudo cargar la configuración", - "unsaved": "Hay cambios sin guardar.", + "loading": "Cargando ajustes...", + "loadError": "No se pudieron cargar los ajustes", + "unsaved": "Cambios sin guardar.", "upToDate": "Actualizado.", "savedRestart": "Guardado. Reinicia nanobot para aplicar.", - "restartAfterSaving": "Guarda los cambios y reinicia cuando estés listo.", - "savedRestartApply": "Guardado. Reinicia cuando estés listo.", - "imageProviderRestart": "Cambios del proveedor de imágenes guardados. Reinicia cuando estés listo.", - "hostRestartAfterSaving": "Guarda los cambios y nanobot reiniciará su motor.", - "hostRestartPending": "Guardado. Reiniciando el motor cuando esté listo.", - "hostApiUnavailable": "Host actions are only available inside the native app.", - "logsOpened": "Opened logs folder.", - "logsOpenFailed": "Could not open logs folder.", - "diagnosticsExported": "Diagnostics exported to {{path}}.", - "diagnosticsExportFailed": "Could not export diagnostics." + "restartAfterSaving": "Guarda los cambios y reinicia cuando puedas.", + "savedRestartApply": "Guardado. Reinicia cuando puedas.", + "imageProviderRestart": "Cambios del proveedor de imagen guardados. Reinicia cuando puedas.", + "hostRestartAfterSaving": "Al guardar, nanobot reiniciará su motor.", + "hostRestartPending": "Guardado. El motor se reiniciará cuando esté listo.", + "hostApiUnavailable": "Las acciones del host solo están disponibles en la app nativa.", + "logsOpened": "Carpeta de registros abierta.", + "logsOpenFailed": "No se pudo abrir la carpeta de registros.", + "diagnosticsExported": "Diagnóstico exportado a {{path}}.", + "diagnosticsExportFailed": "No se pudo exportar el diagnóstico." }, "actions": { "save": "Guardar", @@ -224,8 +224,8 @@ "cancel": "Cancelar", "open": "Abrir", "export": "Exportar", - "opening": "Opening...", - "exporting": "Exporting..." + "opening": "Abriendo...", + "exporting": "Exportando..." }, "byok": { "description": "Usa tus propias claves de proveedor. Nanobot lee estos valores desde la configuración actual, y solo los proveedores configurados se pueden elegir en General.", @@ -250,7 +250,7 @@ "tabs": { "ariaLabel": "Tipo de credencial BYOK", "llm": "LLM", - "webSearch": "Web Search" + "webSearch": "Búsqueda web" }, "webSearch": { "provider": "Proveedor de búsqueda", @@ -270,44 +270,44 @@ } }, "overview": { - "model": "Current model", - "providers": "Providers", - "configuredCount": "{{count}} configured", - "totalProviders": "{{count}} available", - "webSearch": "Web search", + "model": "Modelo actual", + "providers": "Proveedores", + "configuredCount": "{{count}} configurados", + "totalProviders": "{{count}} disponibles", + "webSearch": "Búsqueda web", "imageGeneration": "Generación de imágenes", - "workspace": "Workspace" + "workspace": "Espacio de trabajo" }, "providers": { - "searchPlaceholder": "Search providers", - "noMatches": "No providers match this search.", + "searchPlaceholder": "Buscar proveedores", + "noMatches": "Ningún proveedor coincide con esta búsqueda.", "saveProvider": "Guardar proveedor" }, "image": { "selectProvider": "Seleccionar proveedor", - "selectAspect": "Seleccionar relación", + "selectAspect": "Seleccionar proporción", "selectSize": "Seleccionar tamaño", "configureProvider": "Configurar proveedor", "missingCredential": "Configura este proveedor antes de activar la generación de imágenes." }, "models": { "selectModel": "Seleccionar modelo", - "addConfiguration": "Añadir configuración", + "addConfiguration": "Agregar configuración", "newConfiguration": "Nueva configuración de modelo", - "newConfigurationHelp": "Guarda un proveedor y un modelo como una opción de un clic.", + "newConfigurationHelp": "Guarda un proveedor y modelo como opción de un clic.", "configurationName": "Nombre de configuración", - "configurationNameHelp": "Cambia el nombre de esta configuración de modelo guardada.", + "configurationNameHelp": "Renombra esta configuración de modelo guardada.", "configurationNamePlaceholder": "Escritura rápida", "searchModels": "Buscar o escribir ID de modelo", "useCustomModel": "Usar", "loadingModels": "Cargando modelos...", - "searchCatalog": "Busca en el catálogo del proveedor para elegir un modelo.", + "searchCatalog": "Busca el catálogo del proveedor para elegir un modelo.", "modelsAvailable": "disponibles", "noModelResults": "No hay modelos coincidentes.", "loadFailed": "Lista de modelos no disponible.", - "unsupportedModelList": "Escribe manualmente un ID de modelo.", + "unsupportedModelList": "Escribe un ID de modelo manualmente.", "providerNotConfigured": "Configura este proveedor antes de cargar modelos.", - "autoProviderCustomOnly": "El modo de proveedor automático usa ID de modelo personalizados." + "autoProviderCustomOnly": "El modo de proveedor automático usa IDs de modelo personalizados." }, "timezone": { "select": "Seleccionar zona horaria", @@ -316,7 +316,7 @@ }, "cliApps": { "allCategories": "Todas las categorías", - "availableCount": "{{count}} apps", + "availableCount": "{{count}} aplicaciones", "installedCount": "{{count}} instaladas", "summary": "{{installed}} de {{total}} CLIs instaladas", "filterAll": "Todas", @@ -402,7 +402,7 @@ "thirdPartyBrands": "Los nombres, logotipos y marcas de productos pertenecen a sus respectivos propietarios. Su uso es solo identificativo y no implica respaldo." }, "apps": { - "description": "Añade CLI de apps y servicios MCP que nanobot puede usar desde el chat.", + "description": "Agrega CLI de apps y servicios MCP que nanobot puede usar desde el chat.", "cliLabel": "CLI", "mcpLabel": "MCP", "filterAll": "Todo", @@ -416,17 +416,17 @@ "empty": "Ninguna app coincide con este filtro." }, "oauth": { - "authentication": "OAuth authentication", - "signIn": "Sign in", - "signingIn": "Signing in...", - "signInAgain": "Sign in again", - "signOut": "Sign out", - "signedInAs": "Signed in as {{account}}", - "signInHelp": "Sign in from this device; no API key is stored in config.", - "signInRequired": "Sign in required", - "signInBeforeSaving": "Sign in before saving this OAuth provider as the active model provider.", - "signedIn": "Signed in", - "notSignedIn": "Not signed in" + "authentication": "Autenticación OAuth", + "signIn": "Iniciar sesión", + "signingIn": "Iniciando sesión...", + "signInAgain": "Iniciar sesión de nuevo", + "signOut": "Cerrar sesión", + "signedInAs": "Sesión iniciada como {{account}}", + "signInHelp": "Inicia sesión desde este dispositivo; no se guarda API key en config.", + "signInRequired": "Inicio de sesión requerido", + "signInBeforeSaving": "Inicia sesión antes de guardar este proveedor OAuth como proveedor activo.", + "signedIn": "Sesión iniciada", + "notSignedIn": "Sin sesión" } }, "chat": { diff --git a/webui/src/i18n/locales/fr/common.json b/webui/src/i18n/locales/fr/common.json index ce211b254..b124c48e2 100644 --- a/webui/src/i18n/locales/fr/common.json +++ b/webui/src/i18n/locales/fr/common.json @@ -57,45 +57,45 @@ "apps": "Apps" }, "settings": { - "backToChat": "Retour à la discussion", + "backToChat": "Retour au chat", "sidebar": { - "title": "Paramètres", - "ariaLabel": "Sections des paramètres" + "title": "Réglages", + "ariaLabel": "Sections des réglages" }, "nav": { "general": "Général", "byok": "BYOK", - "overview": "Overview", - "appearance": "Appearance", - "models": "Models", - "providers": "Providers", - "image": "Image", - "browser": "Web", + "overview": "Aperçu", + "appearance": "Apparence", + "models": "Modèles", + "providers": "Fournisseurs", + "image": "Images", + "browser": "Internet", "runtime": "Système", - "advanced": "Security", + "advanced": "Sécurité", "cliApps": "Apps CLI", "mcp": "MCP", - "apps": "Apps" + "apps": "Applications" }, "sections": { - "interface": "Interface", - "ai": "IA", + "interface": "Interface utilisateur", + "ai": "AI", "system": "Système", - "status": "Status", - "localPreferences": "Local preferences", - "presets": "Presets", - "imageGeneration": "Génération d'images", + "status": "État", + "localPreferences": "Préférences locales", + "presets": "Préréglages", + "imageGeneration": "Génération d’images", "imageDefaults": "Valeurs par défaut", - "webSearch": "Web search", - "webBehavior": "Behavior", - "identity": "Identity", - "webuiSafety": "Web safety", + "webSearch": "Recherche web", + "webBehavior": "Comportement", + "identity": "Identité", + "webuiSafety": "Sécurité WebUI", "capabilities": "Capacités", - "cliApps": "Apps CLI", + "cliApps": "Applications CLI", "mcp": "Services MCP", - "apps": "Apps", - "nativeHost": "App", - "hostSafety": "App safety" + "apps": "Applications", + "nativeHost": "Hôte natif", + "hostSafety": "Sécurité de l’app" }, "rows": { "theme": "Thème", @@ -103,119 +103,119 @@ "provider": "Fournisseur", "model": "Modèle", "restart": "Redémarrer nanobot", - "configPath": "Chemin de configuration", - "activePreset": "Active preset", - "gateway": "Gateway", - "restartState": "Restart state", + "configPath": "Chemin de config", + "activePreset": "Préréglage actif", + "gateway": "Passerelle", + "restartState": "État du redémarrage", "pendingChanges": "Modifications en attente", - "selectedPreset": "Selected preset", - "presetModel": "Preset model", - "density": "Density", - "activityMode": "Activity detail", - "codeWrap": "Code wrapping", - "maxResults": "Max results", - "timeout": "Timeout", - "jinaReader": "Jina reader", - "imageGeneration": "Génération d'images", - "imageProvider": "Fournisseur d'images", + "selectedPreset": "Préréglage sélectionné", + "presetModel": "Modèle du préréglage", + "density": "Densité", + "activityMode": "Détail d’activité", + "codeWrap": "Retour à la ligne du code", + "maxResults": "Résultats max.", + "timeout": "Délai d’attente", + "jinaReader": "Lecteur Jina", + "imageGeneration": "Génération d’images", + "imageProvider": "Fournisseur d’images", "imageProviderStatus": "État du fournisseur", "imageProviderBase": "Base du fournisseur", - "imageModel": "Modèle d'image", - "defaultAspectRatio": "Format par défaut", + "imageModel": "Modèle d’image", + "defaultAspectRatio": "Ratio par défaut", "defaultImageSize": "Taille par défaut", - "maxImagesPerTurn": "Nombre max. d'images par tour", - "imageSaveDir": "Répertoire de sauvegarde", - "botName": "Bot name", - "botIcon": "Bot icon", - "timezone": "Timezone", + "maxImagesPerTurn": "Images max. par tour", + "imageSaveDir": "Dossier d’enregistrement", + "botName": "Nom du bot", + "botIcon": "Icône du bot", + "timezone": "Fuseau horaire", "workspacePath": "Espace de travail par défaut", - "localServiceAccess": "Local services", - "webuiDefaultAccess": "Default access", + "localServiceAccess": "Services locaux", + "webuiDefaultAccess": "Accès par défaut", "currentModel": "Configuration actuelle", "brandLogos": "Logos de marque", - "cliAppsCatalog": "Catalogue d'apps CLI", - "cliAppsFilter": "Filtre des apps CLI", + "cliAppsCatalog": "Catalogue", + "cliAppsFilter": "Filtre", "engine": "Moteur", "logs": "Journaux", - "diagnostics": "Diagnostics", - "contextWindow": "Context window" + "diagnostics": "Diagnostic", + "contextWindow": "Fenêtre de contexte" }, "help": { - "theme": "Basculer entre les apparences claire et sombre.", - "language": "Choisissez la langue utilisée par le WebUI.", - "provider": "Sélectionnez le fournisseur des nouvelles requêtes de modèle.", - "model": "Définissez le nom du modèle par défaut utilisé par nanobot.", + "theme": "Basculer entre l’apparence claire et sombre.", + "language": "Choisissez la langue utilisée par WebUI.", + "provider": "Sélectionnez le fournisseur à utiliser pour les nouvelles requêtes de modèle.", + "model": "Définissez le nom du modèle utilisé par défaut par nanobot.", "configPath": "Le fichier de configuration actuellement utilisé par la passerelle.", - "selectedPreset": "Named presets are read-only here; edit them in config.json.", - "presetModel": "Switch to Default to edit model and provider from the WebUI.", - "density": "Stored only in this browser.", - "activityMode": "Choose how much agent activity chrome to show by default.", - "codeWrap": "Keep long code lines readable on smaller screens.", - "maxResults": "Results returned by each web_search call.", - "timeout": "Seconds before a search provider request times out.", - "jinaReader": "Use Jina Reader for web_fetch when available.", - "imageGeneration": "Expose generate_image dans les chats lorsqu’un fournisseur d’images configuré est disponible.", - "imageProvider": "Choisissez le fournisseur du registre utilisé par generate_image.", - "imageProviderStatus": "La génération d’images réutilise les identifiants de Fournisseurs.", + "selectedPreset": "Les préréglages nommés sont en lecture seule ici ; modifiez-les dans config.json.", + "presetModel": "Passez à Default pour modifier le modèle et le fournisseur depuis WebUI.", + "density": "Enregistré seulement dans ce navigateur.", + "activityMode": "Choisissez le niveau de détail d’activité agent affiché par défaut.", + "codeWrap": "Garde les longues lignes de code lisibles sur les petits écrans.", + "maxResults": "Résultats renvoyés par chaque appel web_search.", + "timeout": "Nombre de secondes avant l’expiration d’une requête de recherche.", + "jinaReader": "Utilise Jina Reader pour web_fetch lorsque disponible.", + "imageGeneration": "Expose generate_image dans les chats lorsqu’un fournisseur d’image configuré est disponible.", + "imageProvider": "Choisissez le fournisseur inscrit utilisé par generate_image.", + "imageProviderStatus": "La génération d’images réutilise les identifiants des fournisseurs.", "imageModel": "Nom du modèle envoyé au fournisseur d’images sélectionné.", - "defaultAspectRatio": "Utilisé lorsque le prompt ne choisit pas de format.", - "defaultImageSize": "Indication de taille envoyée aux fournisseurs qui la prennent en charge.", + "defaultAspectRatio": "Utilisé lorsque le prompt ne choisit pas de ratio.", + "defaultImageSize": "Indication de taille envoyée aux fournisseurs compatibles.", "maxImagesPerTurn": "Limite supérieure pour une requête generate_image.", - "botName": "Affiché partout où nanobot utilise un nom visible.", + "botName": "Affiché là où nanobot utilise un nom visible.", "botIcon": "Emoji ou texte court affiché avec le nom du bot.", - "timezone": "Utilisé pour les planifications et les réponses sensibles à l’heure.", - "localServiceAccess": "Allow Full Access shell commands to reach localhost services.", - "webuiDefaultAccess": "Used by web chats without a project-specific permission.", - "securityManagedControls": "Web fetches always protect local, private, and metadata services. Core channel safety stays in config.json.", - "currentModel": "Choisissez la configuration de modèle que nanobot utilisera pour les prochaines réponses.", + "timezone": "Utilisé pour les horaires et les réponses tenant compte du temps.", + "localServiceAccess": "Autorise les commandes shell Full Access à atteindre les services localhost.", + "webuiDefaultAccess": "Utilisé par les chats web sans permission propre au projet.", + "securityManagedControls": "Les récupérations web protègent toujours les services locaux, privés et de métadonnées. La sécurité des canaux principaux reste gérée dans config.json.", + "currentModel": "Utilisée pour les nouvelles réponses.", "selectedModelProvider": "Défini par le modèle sélectionné.", "selectedModelValue": "Défini par le modèle sélectionné.", - "brandLogos": "Les logos sont chargés depuis les domaines des marques avec une icône locale en secours.", - "cliAppsCatalog": "Parcourez les CLIs d'apps que nanobot peut exécuter localement.", + "brandLogos": "Affiche les logos de fournisseurs tiers et CLI dans les Réglages.", + "cliAppsCatalog": "Installe uniquement les adaptateurs CLI d’apps que nanobot peut exécuter localement ; les apps natives restent inchangées.", "cliAppsFilter": "Recherchez par app, catégorie ou capacité.", - "logs": "Ouvrir le dossier des journaux du moteur natif.", - "diagnostics": "Exporter un petit rapport runtime pour le support.", - "localServiceAccessNative": "Allow Full Access shell commands to reach services on this Mac.", - "webuiDefaultAccessNative": "Used by native chats without a project-specific permission.", - "contextWindow": "Choose the default context budget for this model configuration." + "logs": "Ouvre le dossier des journaux du moteur natif.", + "diagnostics": "Exporte un petit rapport d’exécution pour le support.", + "localServiceAccessNative": "Autorise les commandes shell Full Access à atteindre les services sur ce Mac.", + "webuiDefaultAccessNative": "Utilisé par les chats natifs sans permission propre au projet.", + "contextWindow": "Choisissez le budget de contexte par défaut pour cette configuration de modèle." }, "values": { "light": "Clair", "dark": "Sombre", "notAvailable": "Indisponible", - "enabled": "Enabled", - "disabled": "Disabled", + "enabled": "Activé", + "disabled": "Désactivé", "restartPending": "Redémarrage en attente", "ready": "Prêt", - "comfortable": "Comfortable", - "compact": "Compact", - "auto": "Auto", - "expanded": "Expanded", - "on": "On", - "off": "Off", - "defaultPermission": "Default Permission", - "fullAccess": "Full Access", - "configured": "Configured", - "notConfigured": "Not configured", + "comfortable": "Confortable", + "compact": "Compacte", + "auto": "Automatique", + "expanded": "Développé", + "on": "Activé", + "off": "Désactivé", + "defaultPermission": "Autorisation par défaut", + "fullAccess": "Accès complet", + "configured": "Configuré", + "notConfigured": "Non configuré", "pending": "En attente", "restartingEngine": "Redémarrage" }, "status": { - "loading": "Chargement des paramètres...", - "loadError": "Impossible de charger les paramètres", + "loading": "Chargement des réglages...", + "loadError": "Impossible de charger les réglages", "unsaved": "Modifications non enregistrées.", "upToDate": "À jour.", "savedRestart": "Enregistré. Redémarrez nanobot pour appliquer.", - "restartAfterSaving": "Enregistrez les modifications, puis redémarrez lorsque vous êtes prêt.", - "savedRestartApply": "Enregistré. Redémarrez lorsque vous êtes prêt.", - "imageProviderRestart": "Modifications du fournisseur d’images enregistrées. Redémarrez lorsque vous êtes prêt.", - "hostRestartAfterSaving": "Enregistrez les changements et nanobot redémarrera son moteur.", - "hostRestartPending": "Enregistré. Redémarrage du moteur quand il sera prêt.", - "hostApiUnavailable": "Host actions are only available inside the native app.", - "logsOpened": "Opened logs folder.", - "logsOpenFailed": "Could not open logs folder.", - "diagnosticsExported": "Diagnostics exported to {{path}}.", - "diagnosticsExportFailed": "Could not export diagnostics." + "restartAfterSaving": "Enregistrez les changements, puis redémarrez quand vous êtes prêt.", + "savedRestartApply": "Enregistré. Redémarrez quand vous êtes prêt.", + "imageProviderRestart": "Changements du fournisseur d’image enregistrés. Redémarrez quand vous êtes prêt.", + "hostRestartAfterSaving": "En enregistrant, nanobot redémarrera son moteur.", + "hostRestartPending": "Enregistré. Le moteur redémarrera lorsqu’il sera prêt.", + "hostApiUnavailable": "Les actions de l’hôte ne sont disponibles que dans l’app native.", + "logsOpened": "Dossier des journaux ouvert.", + "logsOpenFailed": "Impossible d’ouvrir le dossier des journaux.", + "diagnosticsExported": "Diagnostic exporté vers {{path}}.", + "diagnosticsExportFailed": "Impossible d’exporter le diagnostic." }, "actions": { "save": "Enregistrer", @@ -224,8 +224,8 @@ "cancel": "Annuler", "open": "Ouvrir", "export": "Exporter", - "opening": "Opening...", - "exporting": "Exporting..." + "opening": "Ouverture...", + "exporting": "Exportation..." }, "byok": { "description": "Utilisez vos propres clés de fournisseur. Nanobot lit ces valeurs depuis la configuration actuelle, et seuls les fournisseurs configurés peuvent être sélectionnés dans Général.", @@ -250,7 +250,7 @@ "tabs": { "ariaLabel": "Type d'identifiants BYOK", "llm": "LLM", - "webSearch": "Web Search" + "webSearch": "Recherche web" }, "webSearch": { "provider": "Fournisseur de recherche", @@ -270,53 +270,53 @@ } }, "overview": { - "model": "Current model", - "providers": "Providers", - "configuredCount": "{{count}} configured", - "totalProviders": "{{count}} available", - "webSearch": "Web search", - "imageGeneration": "Génération d'images", - "workspace": "Workspace" + "model": "Modèle actuel", + "providers": "Fournisseurs", + "configuredCount": "{{count}} configurés", + "totalProviders": "{{count}} disponibles", + "webSearch": "Recherche web", + "imageGeneration": "Génération d’images", + "workspace": "Espace de travail" }, "providers": { - "searchPlaceholder": "Search providers", - "noMatches": "No providers match this search.", + "searchPlaceholder": "Rechercher des fournisseurs", + "noMatches": "Aucun fournisseur ne correspond.", "saveProvider": "Enregistrer le fournisseur" }, "image": { - "selectProvider": "Sélectionner un fournisseur", - "selectAspect": "Sélectionner un format", - "selectSize": "Sélectionner une taille", + "selectProvider": "Choisir un fournisseur", + "selectAspect": "Choisir un ratio", + "selectSize": "Choisir une taille", "configureProvider": "Configurer le fournisseur", - "missingCredential": "Configurez ce fournisseur avant d’activer la génération d’images." + "missingCredential": "Configura este proveedor antes de activar la generación de imágenes." }, "models": { - "selectModel": "Sélectionner un modèle", + "selectModel": "Choisir un modèle", "addConfiguration": "Ajouter une configuration", "newConfiguration": "Nouvelle configuration de modèle", - "newConfigurationHelp": "Enregistrez un fournisseur et un modèle comme option en un clic.", + "newConfigurationHelp": "Enregistre un fournisseur et un modèle comme option en un clic.", "configurationName": "Nom de la configuration", - "configurationNameHelp": "Renommez cette configuration de modèle enregistrée.", + "configurationNameHelp": "Renomme cette configuration de modèle enregistrée.", "configurationNamePlaceholder": "Rédaction rapide", - "searchModels": "Rechercher ou saisir l’ID du modèle", + "searchModels": "Rechercher ou saisir un ID de modèle", "useCustomModel": "Utiliser", "loadingModels": "Chargement des modèles...", - "searchCatalog": "Recherchez dans le catalogue du fournisseur pour choisir un modèle.", + "searchCatalog": "Rechercher dans le catalogue du fournisseur pour choisir un modèle.", "modelsAvailable": "disponibles", "noModelResults": "Aucun modèle correspondant.", "loadFailed": "Liste des modèles indisponible.", "unsupportedModelList": "Saisissez manuellement un ID de modèle.", "providerNotConfigured": "Configurez ce fournisseur avant de charger les modèles.", - "autoProviderCustomOnly": "Le mode fournisseur automatique utilise des ID de modèle personnalisés." + "autoProviderCustomOnly": "Le mode fournisseur automatique utilise des IDs de modèle personnalisés." }, "timezone": { - "select": "Sélectionner un fuseau horaire", + "select": "Choisir un fuseau horaire", "search": "Rechercher un fuseau horaire", "empty": "Aucun fuseau horaire correspondant." }, "cliApps": { "allCategories": "Toutes les catégories", - "availableCount": "{{count}} apps", + "availableCount": "{{count}} applications", "installedCount": "{{count}} installées", "summary": "{{installed}} CLIs installées sur {{total}}", "filterAll": "Tout", @@ -402,7 +402,7 @@ "thirdPartyBrands": "Les noms, logos et marques de produits appartiennent à leurs propriétaires respectifs. Leur utilisation sert uniquement à l'identification et n'implique aucune approbation." }, "apps": { - "description": "Ajoutez des CLI d’apps et des services MCP que nanobot peut utiliser dans le chat.", + "description": "Ajoutez des CLI d’apps et services MCP utilisables par nanobot depuis le chat.", "cliLabel": "CLI", "mcpLabel": "MCP", "filterAll": "Tout", @@ -411,22 +411,22 @@ "enabledSummary": "{{count}} activés", "caption": "{{cli}} CLI · {{mcp}} MCP", "searchPlaceholder": "Rechercher des apps", - "featured": "En vedette", + "featured": "À la une", "loading": "Chargement des apps...", - "empty": "Aucune app ne correspond à ce filtre." + "empty": "Aucune app ne correspond." }, "oauth": { - "authentication": "OAuth authentication", - "signIn": "Sign in", - "signingIn": "Signing in...", - "signInAgain": "Sign in again", - "signOut": "Sign out", - "signedInAs": "Signed in as {{account}}", - "signInHelp": "Sign in from this device; no API key is stored in config.", - "signInRequired": "Sign in required", - "signInBeforeSaving": "Sign in before saving this OAuth provider as the active model provider.", - "signedIn": "Signed in", - "notSignedIn": "Not signed in" + "authentication": "Authentification OAuth", + "signIn": "Se connecter", + "signingIn": "Connexion...", + "signInAgain": "Se reconnecter", + "signOut": "Se déconnecter", + "signedInAs": "Connecté en tant que {{account}}", + "signInHelp": "Inicia sesión desde este dispositivo; no se guarda API key en config.", + "signInRequired": "Connexion requise", + "signInBeforeSaving": "Inicia sesión antes de guardar este proveedor OAuth como proveedor activo.", + "signedIn": "Connecté", + "notSignedIn": "Non connecté" } }, "chat": { diff --git a/webui/src/i18n/locales/id/common.json b/webui/src/i18n/locales/id/common.json index 132f99436..e17795046 100644 --- a/webui/src/i18n/locales/id/common.json +++ b/webui/src/i18n/locales/id/common.json @@ -57,7 +57,7 @@ "apps": "Aplikasi" }, "settings": { - "backToChat": "Kembali ke obrolan", + "backToChat": "Kembali ke chat", "sidebar": { "title": "Pengaturan", "ariaLabel": "Bagian pengaturan" @@ -65,14 +65,14 @@ "nav": { "general": "Umum", "byok": "BYOK", - "overview": "Overview", - "appearance": "Appearance", - "models": "Models", - "providers": "Providers", - "image": "Image", - "browser": "Web", + "overview": "Ikhtisar", + "appearance": "Tampilan", + "models": "Model", + "providers": "Penyedia", + "image": "Gambar", + "browser": "Internet", "runtime": "Sistem", - "advanced": "Security", + "advanced": "Keamanan", "cliApps": "Aplikasi CLI", "mcp": "MCP", "apps": "Aplikasi" @@ -82,20 +82,20 @@ "ai": "AI", "system": "Sistem", "status": "Status", - "localPreferences": "Local preferences", - "presets": "Presets", + "localPreferences": "Preferensi lokal", + "presets": "Preset", "imageGeneration": "Pembuatan gambar", "imageDefaults": "Default", - "webSearch": "Web search", - "webBehavior": "Behavior", - "identity": "Identity", - "webuiSafety": "Web safety", - "capabilities": "Kapabilitas", - "cliApps": "App CLI", + "webSearch": "Pencarian web", + "webBehavior": "Perilaku", + "identity": "Identitas", + "webuiSafety": "Keamanan WebUI", + "capabilities": "Kemampuan", + "cliApps": "Aplikasi CLI", "mcp": "Layanan MCP", "apps": "Aplikasi", - "nativeHost": "Native host", - "hostSafety": "App safety" + "nativeHost": "Host native", + "hostSafety": "Keamanan aplikasi" }, "rows": { "theme": "Tema", @@ -104,18 +104,18 @@ "model": "Model", "restart": "Mulai ulang nanobot", "configPath": "Path konfigurasi", - "activePreset": "Active preset", - "gateway": "Gateway", - "restartState": "Restart state", + "activePreset": "Preset aktif", + "gateway": "Gerbang", + "restartState": "Status mulai ulang", "pendingChanges": "Perubahan tertunda", - "selectedPreset": "Selected preset", - "presetModel": "Preset model", - "density": "Density", - "activityMode": "Activity detail", - "codeWrap": "Code wrapping", - "maxResults": "Max results", - "timeout": "Timeout", - "jinaReader": "Jina reader", + "selectedPreset": "Preset terpilih", + "presetModel": "Model preset", + "density": "Kerapatan", + "activityMode": "Detail aktivitas", + "codeWrap": "Bungkus kode", + "maxResults": "Hasil maksimum", + "timeout": "Batas waktu", + "jinaReader": "Pembaca Jina", "imageGeneration": "Pembuatan gambar", "imageProvider": "Penyedia gambar", "imageProviderStatus": "Status penyedia", @@ -124,98 +124,98 @@ "defaultAspectRatio": "Rasio default", "defaultImageSize": "Ukuran default", "maxImagesPerTurn": "Maks. gambar per giliran", - "imageSaveDir": "Direktori penyimpanan", - "botName": "Bot name", - "botIcon": "Bot icon", - "timezone": "Timezone", + "imageSaveDir": "Direktori simpan", + "botName": "Nama bot", + "botIcon": "Ikon bot", + "timezone": "Zona waktu", "workspacePath": "Workspace default", - "localServiceAccess": "Local services", - "webuiDefaultAccess": "Default access", + "localServiceAccess": "Layanan lokal", + "webuiDefaultAccess": "Akses default", "currentModel": "Konfigurasi saat ini", "brandLogos": "Logo merek", - "cliAppsCatalog": "Katalog aplikasi CLI", - "cliAppsFilter": "Filter aplikasi CLI", - "engine": "Engine", + "cliAppsCatalog": "Katalog", + "cliAppsFilter": "Saring", + "engine": "Mesin", "logs": "Log", "diagnostics": "Diagnostik", - "contextWindow": "Context window" + "contextWindow": "Jendela konteks" }, "help": { "theme": "Beralih antara tampilan terang dan gelap.", "language": "Pilih bahasa yang digunakan WebUI.", - "provider": "Pilih penyedia untuk permintaan model baru.", - "model": "Atur nama model default yang digunakan nanobot.", - "configPath": "File konfigurasi gateway yang sedang digunakan.", - "selectedPreset": "Named presets are read-only here; edit them in config.json.", - "presetModel": "Switch to Default to edit model and provider from the WebUI.", - "density": "Stored only in this browser.", - "activityMode": "Choose how much agent activity chrome to show by default.", - "codeWrap": "Keep long code lines readable on smaller screens.", - "maxResults": "Results returned by each web_search call.", - "timeout": "Seconds before a search provider request times out.", - "jinaReader": "Use Jina Reader for web_fetch when available.", - "imageGeneration": "Tampilkan generate_image di chat saat penyedia gambar yang dikonfigurasi tersedia.", - "imageProvider": "Pilih penyedia registry yang digunakan oleh generate_image.", - "imageProviderStatus": "Pembuatan gambar menggunakan ulang kredensial penyedia dari Providers.", - "imageModel": "Nama model yang dikirim ke penyedia gambar yang dipilih.", - "defaultAspectRatio": "Digunakan saat prompt tidak memilih rasio aspek.", + "provider": "Selecciona el proveedor para nuevas solicitudes de modelo.", + "model": "Define el nombre de modelo predeterminado de nanobot.", + "configPath": "Archivo de configuración que usa actualmente el gateway.", + "selectedPreset": "Los preajustes con nombre son de solo lectura aquí; edítalos en config.json.", + "presetModel": "Beralih ke Default untuk mengedit model dan penyedia dari WebUI.", + "density": "Hanya disimpan di browser ini.", + "activityMode": "Pilih seberapa banyak detail aktivitas agen yang ditampilkan secara default.", + "codeWrap": "Menjaga baris kode panjang tetap terbaca di layar kecil.", + "maxResults": "Resultados devueltos por cada llamada web_search.", + "timeout": "Segundos antes de que una solicitud de búsqueda expire.", + "jinaReader": "Usa Jina Reader para web_fetch cuando esté disponible.", + "imageGeneration": "Expone generate_image en chats cuando hay un proveedor de imagen configurado.", + "imageProvider": "Elige el proveedor registrado usado por generate_image.", + "imageProviderStatus": "La generación de imágenes reutiliza credenciales de Proveedores.", + "imageModel": "Nombre del modelo enviado al proveedor de imágenes seleccionado.", + "defaultAspectRatio": "Se usa cuando el prompt no elige una proporción.", "defaultImageSize": "Petunjuk ukuran yang dikirim ke penyedia yang mendukungnya.", "maxImagesPerTurn": "Batas atas untuk satu permintaan generate_image.", - "botName": "Ditampilkan di tempat nanobot memakai nama tampilan.", - "botIcon": "Emoji atau teks pendek yang tampil bersama nama bot.", - "timezone": "Dipakai untuk jadwal dan balasan yang peka waktu.", - "localServiceAccess": "Allow Full Access shell commands to reach localhost services.", - "webuiDefaultAccess": "Used by web chats without a project-specific permission.", - "securityManagedControls": "Web fetches always protect local, private, and metadata services. Core channel safety stays in config.json.", - "currentModel": "Pilih konfigurasi model yang digunakan nanobot untuk balasan berikutnya.", - "selectedModelProvider": "Ditentukan oleh model yang dipilih.", - "selectedModelValue": "Ditentukan oleh model yang dipilih.", - "brandLogos": "Logo dimuat dari domain merek dengan ikon lokal sebagai cadangan.", - "cliAppsCatalog": "Jelajahi CLI aplikasi yang dapat dijalankan nanobot secara lokal.", - "cliAppsFilter": "Cari berdasarkan aplikasi, kategori, atau kemampuan.", - "logs": "Buka folder log native engine.", - "diagnostics": "Ekspor laporan runtime kecil untuk dukungan.", - "localServiceAccessNative": "Allow Full Access shell commands to reach services on this Mac.", - "webuiDefaultAccessNative": "Used by native chats without a project-specific permission.", - "contextWindow": "Choose the default context budget for this model configuration." + "botName": "Se muestra donde nanobot usa un nombre visible.", + "botIcon": "Emoji o texto corto junto al nombre del bot.", + "timezone": "Se usa para horarios y respuestas con conciencia temporal.", + "localServiceAccess": "Izinkan perintah shell Full Access menjangkau layanan localhost.", + "webuiDefaultAccess": "Digunakan oleh chat web tanpa izin khusus proyek.", + "securityManagedControls": "Las capturas web siempre protegen servicios locales, privados y metadata. La seguridad de canales core se gestiona en config.json.", + "currentModel": "Digunakan untuk balasan baru.", + "selectedModelProvider": "Definido por el modelo seleccionado.", + "selectedModelValue": "Definido por el modelo seleccionado.", + "brandLogos": "Tampilkan logo penyedia pihak ketiga dan CLI di Pengaturan.", + "cliAppsCatalog": "Instala solo adaptadores CLI de apps que nanobot puede ejecutar localmente; las apps nativas no se modifican.", + "cliAppsFilter": "Busca por app, categoría o capacidad.", + "logs": "Abre la carpeta de registros del motor nativo.", + "diagnostics": "Exporta un pequeño informe de runtime para soporte.", + "localServiceAccessNative": "Permite que comandos shell con Full Access alcancen servicios en este Mac.", + "webuiDefaultAccessNative": "Usado por chats nativos sin permiso específico de proyecto.", + "contextWindow": "Pilih anggaran konteks default untuk konfigurasi model ini." }, "values": { "light": "Terang", "dark": "Gelap", "notAvailable": "Tidak tersedia", - "enabled": "Enabled", - "disabled": "Disabled", - "restartPending": "Menunggu restart", + "enabled": "Aktif", + "disabled": "Nonaktif", + "restartPending": "Menunggu mulai ulang", "ready": "Siap", - "comfortable": "Comfortable", - "compact": "Compact", - "auto": "Auto", - "expanded": "Expanded", - "on": "On", - "off": "Off", - "defaultPermission": "Default Permission", - "fullAccess": "Full Access", - "configured": "Configured", - "notConfigured": "Not configured", + "comfortable": "Nyaman", + "compact": "Ringkas", + "auto": "Otomatis", + "expanded": "Diperluas", + "on": "Aktif", + "off": "Nonaktif", + "defaultPermission": "Izin default", + "fullAccess": "Akses penuh", + "configured": "Terkonfigurasi", + "notConfigured": "Belum dikonfigurasi", "pending": "Tertunda", "restartingEngine": "Memulai ulang" }, "status": { "loading": "Memuat pengaturan...", "loadError": "Tidak dapat memuat pengaturan", - "unsaved": "Ada perubahan yang belum disimpan.", + "unsaved": "Perubahan belum disimpan.", "upToDate": "Sudah terbaru.", - "savedRestart": "Tersimpan. Mulai ulang nanobot untuk menerapkan.", - "restartAfterSaving": "Simpan perubahan, lalu restart saat siap.", - "savedRestartApply": "Tersimpan. Restart saat siap.", - "imageProviderRestart": "Perubahan penyedia gambar tersimpan. Restart saat siap.", - "hostRestartAfterSaving": "Simpan perubahan dan nanobot akan memulai ulang engine.", - "hostRestartPending": "Tersimpan. Engine akan dimulai ulang saat siap.", - "hostApiUnavailable": "Host actions are only available inside the native app.", - "logsOpened": "Opened logs folder.", - "logsOpenFailed": "Could not open logs folder.", - "diagnosticsExported": "Diagnostics exported to {{path}}.", - "diagnosticsExportFailed": "Could not export diagnostics." + "savedRestart": "Guardado. Reinicia nanobot para aplicar.", + "restartAfterSaving": "Guarda los cambios y reinicia cuando puedas.", + "savedRestartApply": "Guardado. Reinicia cuando puedas.", + "imageProviderRestart": "Cambios del proveedor de imagen guardados. Reinicia cuando puedas.", + "hostRestartAfterSaving": "Al guardar, nanobot reiniciará su motor.", + "hostRestartPending": "Guardado. El motor se reiniciará cuando esté listo.", + "hostApiUnavailable": "Las acciones del host solo están disponibles en la app nativa.", + "logsOpened": "Carpeta de registros abierta.", + "logsOpenFailed": "No se pudo abrir la carpeta de registros.", + "diagnosticsExported": "Diagnóstico exportado a {{path}}.", + "diagnosticsExportFailed": "No se pudo exportar el diagnóstico." }, "actions": { "save": "Simpan", @@ -224,8 +224,8 @@ "cancel": "Batal", "open": "Buka", "export": "Ekspor", - "opening": "Opening...", - "exporting": "Exporting..." + "opening": "Membuka...", + "exporting": "Mengekspor..." }, "byok": { "description": "Gunakan kunci provider Anda sendiri. Nanobot membaca nilai ini dari config saat ini, dan hanya provider yang sudah dikonfigurasi yang bisa dipilih di Umum.", @@ -250,10 +250,10 @@ "tabs": { "ariaLabel": "Jenis kredensial BYOK", "llm": "LLM", - "webSearch": "Web Search" + "webSearch": "Pencarian web" }, "webSearch": { - "provider": "Search provider", + "provider": "Penyedia pencarian", "providerHelp": "Pilih backend yang digunakan alat web search.", "selectProvider": "Pilih provider", "credentials": "Kredensial", @@ -270,17 +270,17 @@ } }, "overview": { - "model": "Current model", - "providers": "Providers", - "configuredCount": "{{count}} configured", - "totalProviders": "{{count}} available", - "webSearch": "Web search", + "model": "Model saat ini", + "providers": "Penyedia", + "configuredCount": "{{count}} dikonfigurasi", + "totalProviders": "{{count}} tersedia", + "webSearch": "Pencarian web", "imageGeneration": "Pembuatan gambar", - "workspace": "Workspace" + "workspace": "Ruang kerja" }, "providers": { - "searchPlaceholder": "Search providers", - "noMatches": "No providers match this search.", + "searchPlaceholder": "Cari penyedia", + "noMatches": "Tidak ada penyedia yang cocok.", "saveProvider": "Simpan penyedia" }, "image": { @@ -288,7 +288,7 @@ "selectAspect": "Pilih rasio", "selectSize": "Pilih ukuran", "configureProvider": "Konfigurasi penyedia", - "missingCredential": "Konfigurasikan penyedia ini sebelum mengaktifkan pembuatan gambar." + "missingCredential": "Configura este proveedor antes de activar la generación de imágenes." }, "models": { "selectModel": "Pilih model", @@ -296,8 +296,8 @@ "newConfiguration": "Konfigurasi model baru", "newConfigurationHelp": "Simpan penyedia dan model sebagai opsi sekali klik.", "configurationName": "Nama konfigurasi", - "configurationNameHelp": "Ganti nama konfigurasi model yang tersimpan ini.", - "configurationNamePlaceholder": "Penulisan cepat", + "configurationNameHelp": "Ganti nama konfigurasi model tersimpan ini.", + "configurationNamePlaceholder": "Menulis cepat", "searchModels": "Cari atau ketik ID model", "useCustomModel": "Gunakan", "loadingModels": "Memuat model...", @@ -307,7 +307,7 @@ "loadFailed": "Daftar model tidak tersedia.", "unsupportedModelList": "Ketik ID model secara manual.", "providerNotConfigured": "Konfigurasikan penyedia ini sebelum memuat model.", - "autoProviderCustomOnly": "Mode penyedia otomatis menggunakan ID model khusus." + "autoProviderCustomOnly": "Mode penyedia otomatis memakai ID model kustom." }, "timezone": { "select": "Pilih zona waktu", @@ -402,31 +402,31 @@ "thirdPartyBrands": "Nama produk, logo, dan merek adalah milik pemiliknya masing-masing. Penggunaan hanya untuk identifikasi dan tidak menyiratkan dukungan." }, "apps": { - "description": "Tambahkan CLI app dan layanan MCP yang dapat digunakan nanobot dari chat.", + "description": "Tambahkan CLI aplikasi dan layanan MCP yang dapat digunakan nanobot dari chat.", "cliLabel": "CLI", "mcpLabel": "MCP", "filterAll": "Semua", - "filterCli": "App CLI", + "filterCli": "Aplikasi CLI", "filterMcp": "Layanan MCP", "enabledSummary": "{{count}} aktif", "caption": "{{cli}} CLI · {{mcp}} MCP", "searchPlaceholder": "Cari aplikasi", "featured": "Unggulan", "loading": "Memuat aplikasi...", - "empty": "Tidak ada aplikasi yang cocok dengan filter ini." + "empty": "Tidak ada aplikasi yang cocok." }, "oauth": { - "authentication": "OAuth authentication", - "signIn": "Sign in", - "signingIn": "Signing in...", - "signInAgain": "Sign in again", - "signOut": "Sign out", - "signedInAs": "Signed in as {{account}}", - "signInHelp": "Sign in from this device; no API key is stored in config.", - "signInRequired": "Sign in required", - "signInBeforeSaving": "Sign in before saving this OAuth provider as the active model provider.", - "signedIn": "Signed in", - "notSignedIn": "Not signed in" + "authentication": "Autentikasi OAuth", + "signIn": "Masuk", + "signingIn": "Masuk...", + "signInAgain": "Masuk lagi", + "signOut": "Keluar", + "signedInAs": "Masuk sebagai {{account}}", + "signInHelp": "Inicia sesión desde este dispositivo; no se guarda API key en config.", + "signInRequired": "Perlu masuk", + "signInBeforeSaving": "Inicia sesión antes de guardar este proveedor OAuth como proveedor activo.", + "signedIn": "Sudah masuk", + "notSignedIn": "Belum masuk" } }, "chat": { diff --git a/webui/src/i18n/locales/ja/common.json b/webui/src/i18n/locales/ja/common.json index d029ca7c9..9c07ece83 100644 --- a/webui/src/i18n/locales/ja/common.json +++ b/webui/src/i18n/locales/ja/common.json @@ -65,14 +65,14 @@ "nav": { "general": "一般", "byok": "BYOK", - "overview": "Overview", - "appearance": "Appearance", - "models": "Models", - "providers": "Providers", - "image": "Image", - "browser": "Web", + "overview": "概要", + "appearance": "外観", + "models": "モデル", + "providers": "プロバイダー", + "image": "画像", + "browser": "ウェブ", "runtime": "システム", - "advanced": "Security", + "advanced": "セキュリティ", "cliApps": "CLI アプリ", "mcp": "MCP", "apps": "アプリ" @@ -81,21 +81,21 @@ "interface": "インターフェース", "ai": "AI", "system": "システム", - "status": "Status", - "localPreferences": "Local preferences", - "presets": "Presets", + "status": "状態", + "localPreferences": "ローカル設定", + "presets": "プリセット", "imageGeneration": "画像生成", "imageDefaults": "既定値", - "webSearch": "Web search", - "webBehavior": "Behavior", - "identity": "Identity", - "webuiSafety": "Web safety", + "webSearch": "ウェブ検索", + "webBehavior": "動作", + "identity": "ID", + "webuiSafety": "WebUI の安全性", "capabilities": "機能", "cliApps": "CLI アプリ", "mcp": "MCP サービス", "apps": "アプリ", - "nativeHost": "App", - "hostSafety": "App safety" + "nativeHost": "ネイティブホスト", + "hostSafety": "アプリの安全性" }, "rows": { "theme": "テーマ", @@ -104,41 +104,41 @@ "model": "モデル", "restart": "nanobot を再起動", "configPath": "設定パス", - "activePreset": "Active preset", - "gateway": "Gateway", - "restartState": "Restart state", + "activePreset": "アクティブなプリセット", + "gateway": "ゲートウェイ", + "restartState": "再起動状態", "pendingChanges": "保留中の変更", - "selectedPreset": "Selected preset", - "presetModel": "Preset model", - "density": "Density", - "activityMode": "Activity detail", - "codeWrap": "Code wrapping", - "maxResults": "Max results", - "timeout": "Timeout", - "jinaReader": "Jina reader", + "selectedPreset": "選択中のプリセット", + "presetModel": "プリセットモデル", + "density": "表示密度", + "activityMode": "アクティビティ詳細", + "codeWrap": "コードの折り返し", + "maxResults": "最大結果数", + "timeout": "タイムアウト", + "jinaReader": "Jina リーダー", "imageGeneration": "画像生成", "imageProvider": "画像プロバイダー", - "imageProviderStatus": "プロバイダーの状態", - "imageProviderBase": "プロバイダーのベース URL", + "imageProviderStatus": "プロバイダー状態", + "imageProviderBase": "プロバイダー URL", "imageModel": "画像モデル", "defaultAspectRatio": "既定の比率", "defaultImageSize": "既定のサイズ", - "maxImagesPerTurn": "1 ターンあたりの最大画像数", + "maxImagesPerTurn": "1 ターンの最大画像数", "imageSaveDir": "保存先ディレクトリ", - "botName": "Bot name", - "botIcon": "Bot icon", - "timezone": "Timezone", - "workspacePath": "デフォルトワークスペース", - "localServiceAccess": "Local services", - "webuiDefaultAccess": "Default access", + "botName": "Bot 名", + "botIcon": "Bot アイコン", + "timezone": "タイムゾーン", + "workspacePath": "既定のワークスペース", + "localServiceAccess": "ローカルサービス", + "webuiDefaultAccess": "既定の権限", "currentModel": "現在の設定", "brandLogos": "ブランドロゴ", - "cliAppsCatalog": "CLI アプリカタログ", - "cliAppsFilter": "CLI アプリフィルター", + "cliAppsCatalog": "カタログ", + "cliAppsFilter": "フィルター", "engine": "エンジン", "logs": "ログ", "diagnostics": "診断", - "contextWindow": "Context window" + "contextWindow": "コンテキストウィンドウ" }, "help": { "theme": "ライト表示とダーク表示を切り替えます。", @@ -146,57 +146,57 @@ "provider": "新しいモデルリクエストに使うプロバイダーを選択します。", "model": "nanobot が既定で使用するモデル名を設定します。", "configPath": "現在ゲートウェイが使用している設定ファイルです。", - "selectedPreset": "Named presets are read-only here; edit them in config.json.", - "presetModel": "Switch to Default to edit model and provider from the WebUI.", - "density": "Stored only in this browser.", - "activityMode": "Choose how much agent activity chrome to show by default.", - "codeWrap": "Keep long code lines readable on smaller screens.", - "maxResults": "Results returned by each web_search call.", - "timeout": "Seconds before a search provider request times out.", - "jinaReader": "Use Jina Reader for web_fetch when available.", - "imageGeneration": "設定済みの画像プロバイダーが利用できる場合、チャットで generate_image を有効にします。", + "selectedPreset": "名前付きプリセットはここでは読み取り専用です。編集するには config.json を変更してください。", + "presetModel": "Default に切り替えると、WebUI からモデルとプロバイダーを編集できます。", + "density": "このブラウザーにのみ保存されます。", + "activityMode": "既定で表示する agent アクティビティの詳細量を選択します。", + "codeWrap": "小さな画面でも長いコード行を読みやすくします。", + "maxResults": "各 web_search 呼び出しで返す結果数です。", + "timeout": "検索プロバイダーのリクエストがタイムアウトするまでの秒数です。", + "jinaReader": "利用可能な場合、web_fetch に Jina Reader を使います。", + "imageGeneration": "画像プロバイダーが設定済みのとき、チャットで generate_image を有効にします。", "imageProvider": "generate_image で使用する登録済みプロバイダーを選択します。", "imageProviderStatus": "画像生成は「プロバイダー」の認証情報を再利用します。", "imageModel": "選択した画像プロバイダーへ送信するモデル名です。", - "defaultAspectRatio": "プロンプトでアスペクト比が指定されていない場合に使用します。", + "defaultAspectRatio": "プロンプトで比率が指定されていない場合に使用します。", "defaultImageSize": "対応しているプロバイダーへ送信するサイズ指定です。", "maxImagesPerTurn": "1 回の generate_image リクエストで生成できる画像数の上限です。", "botName": "nanobot が表示名を使う場所に表示されます。", "botIcon": "Bot 名の横に表示する短い emoji またはテキストです。", "timezone": "スケジュールと時刻を考慮する返信に使用します。", - "localServiceAccess": "Allow Full Access shell commands to reach localhost services.", - "webuiDefaultAccess": "Used by web chats without a project-specific permission.", - "securityManagedControls": "Web fetches always protect local, private, and metadata services. Core channel safety stays in config.json.", - "currentModel": "今後の返信で nanobot が使用するモデル設定を選択します。", + "localServiceAccess": "Full Access の shell コマンドが localhost サービスにアクセスできるようにします。", + "webuiDefaultAccess": "プロジェクト固有の権限がない Web チャットで使用します。", + "securityManagedControls": "Web 取得は常にローカル、プライベート、メタデータサービスを保護します。コアチャネルの安全性は config.json で管理されます。", + "currentModel": "新しい返信に使用します。", "selectedModelProvider": "選択したモデルによって設定されます。", "selectedModelValue": "選択したモデルによって設定されます。", - "brandLogos": "ロゴはブランドのドメインから読み込まれ、ローカルアイコンにフォールバックします。", - "cliAppsCatalog": "nanobot がローカルで実行できるアプリ CLI を探します。", + "brandLogos": "設定で第三者プロバイダーと CLI のロゴを表示します。", + "cliAppsCatalog": "nanobot がローカルで実行できるアプリ CLI アダプターだけをインストールします。ネイティブアプリは変更しません。", "cliAppsFilter": "アプリ、カテゴリ、機能で検索します。", - "logs": "Appエンジンのログフォルダを開きます。", + "logs": "ネイティブエンジンのログフォルダーを開きます。", "diagnostics": "サポート用の小さなランタイムレポートを書き出します。", - "localServiceAccessNative": "Allow Full Access shell commands to reach services on this Mac.", - "webuiDefaultAccessNative": "Used by native chats without a project-specific permission.", - "contextWindow": "Choose the default context budget for this model configuration." + "localServiceAccessNative": "Full Access の shell コマンドがこの Mac 上のサービスにアクセスできるようにします。", + "webuiDefaultAccessNative": "プロジェクト固有の権限がないネイティブチャットで使用します。", + "contextWindow": "このモデル設定で使う既定のコンテキスト予算を選択します。" }, "values": { "light": "ライト", "dark": "ダーク", "notAvailable": "利用不可", - "enabled": "Enabled", - "disabled": "Disabled", + "enabled": "有効", + "disabled": "無効", "restartPending": "再起動待ち", "ready": "準備完了", - "comfortable": "Comfortable", - "compact": "Compact", - "auto": "Auto", - "expanded": "Expanded", - "on": "On", - "off": "Off", - "defaultPermission": "Default Permission", - "fullAccess": "Full Access", - "configured": "Configured", - "notConfigured": "Not configured", + "comfortable": "標準", + "compact": "コンパクト", + "auto": "自動", + "expanded": "展開", + "on": "オン", + "off": "オフ", + "defaultPermission": "既定の権限", + "fullAccess": "完全アクセス", + "configured": "設定済み", + "notConfigured": "未設定", "pending": "保留中", "restartingEngine": "再起動中" }, @@ -211,11 +211,11 @@ "imageProviderRestart": "画像プロバイダーの変更を保存しました。準備ができたら再起動してください。", "hostRestartAfterSaving": "保存すると nanobot がエンジンを再起動します。", "hostRestartPending": "保存しました。準備ができたらエンジンを再起動します。", - "hostApiUnavailable": "Host actions are only available inside the native app.", - "logsOpened": "Opened logs folder.", - "logsOpenFailed": "Could not open logs folder.", - "diagnosticsExported": "Diagnostics exported to {{path}}.", - "diagnosticsExportFailed": "Could not export diagnostics." + "hostApiUnavailable": "ホスト操作はネイティブアプリ内でのみ利用できます。", + "logsOpened": "ログフォルダーを開きました。", + "logsOpenFailed": "ログフォルダーを開けませんでした。", + "diagnosticsExported": "診断を {{path}} に書き出しました。", + "diagnosticsExportFailed": "診断を書き出せませんでした。" }, "actions": { "save": "保存", @@ -224,8 +224,8 @@ "cancel": "キャンセル", "open": "開く", "export": "書き出す", - "opening": "Opening...", - "exporting": "Exporting..." + "opening": "開いています...", + "exporting": "書き出しています..." }, "byok": { "description": "自分の provider キーを使います。Nanobot は現在の config から値を読み込み、設定済みの provider だけを一般設定で選択できます。", @@ -250,7 +250,7 @@ "tabs": { "ariaLabel": "BYOK 認証情報タイプ", "llm": "LLM", - "webSearch": "Web Search" + "webSearch": "ウェブ検索" }, "webSearch": { "provider": "検索 provider", @@ -270,17 +270,17 @@ } }, "overview": { - "model": "Current model", - "providers": "Providers", - "configuredCount": "{{count}} configured", - "totalProviders": "{{count}} available", - "webSearch": "Web search", + "model": "現在のモデル", + "providers": "プロバイダー", + "configuredCount": "{{count}} 個設定済み", + "totalProviders": "{{count}} 個利用可能", + "webSearch": "Web 検索", "imageGeneration": "画像生成", - "workspace": "Workspace" + "workspace": "ワークスペース" }, "providers": { - "searchPlaceholder": "Search providers", - "noMatches": "No providers match this search.", + "searchPlaceholder": "プロバイダーを検索", + "noMatches": "一致するプロバイダーはありません。", "saveProvider": "プロバイダーを保存" }, "image": { @@ -297,12 +297,12 @@ "newConfigurationHelp": "プロバイダーとモデルをワンクリックの選択肢として保存します。", "configurationName": "設定名", "configurationNameHelp": "保存済みのモデル設定の名前を変更します。", - "configurationNamePlaceholder": "高速ライティング", - "searchModels": "検索またはモデル ID を入力", + "configurationNamePlaceholder": "高速執筆", + "searchModels": "モデル ID を検索または入力", "useCustomModel": "使用", - "loadingModels": "モデルを読み込み中...", - "searchCatalog": "プロバイダーのカタログを検索してモデルを選択します。", - "modelsAvailable": "件利用可能", + "loadingModels": "モデルを読み込んでいます...", + "searchCatalog": "プロバイダーのカタログからモデルを選択します。", + "modelsAvailable": "利用可能", "noModelResults": "一致するモデルはありません。", "loadFailed": "モデル一覧を利用できません。", "unsupportedModelList": "モデル ID を手動で入力してください。", @@ -402,31 +402,31 @@ "thirdPartyBrands": "製品名、ロゴ、ブランドはそれぞれの所有者に帰属します。使用は識別のみを目的とし、承認を意味するものではありません。" }, "apps": { - "description": "チャットから nanobot が使えるアプリ CLI と MCP サービスを追加します。", + "description": "nanobot がチャットで使用できる App CLI と MCP サービスを追加します。", "cliLabel": "CLI", "mcpLabel": "MCP", "filterAll": "すべて", "filterCli": "CLI アプリ", "filterMcp": "MCP サービス", "enabledSummary": "{{count}} 件有効", - "caption": "{{cli}} CLI · {{mcp}} MCP", + "caption": "CLI {{cli}} 件 · MCP {{mcp}} 件", "searchPlaceholder": "アプリを検索", - "featured": "おすすめ", + "featured": "注目", "loading": "アプリを読み込み中...", - "empty": "このフィルターに一致するアプリはありません。" + "empty": "一致するアプリはありません。" }, "oauth": { - "authentication": "OAuth authentication", - "signIn": "Sign in", - "signingIn": "Signing in...", - "signInAgain": "Sign in again", - "signOut": "Sign out", - "signedInAs": "Signed in as {{account}}", - "signInHelp": "Sign in from this device; no API key is stored in config.", - "signInRequired": "Sign in required", - "signInBeforeSaving": "Sign in before saving this OAuth provider as the active model provider.", - "signedIn": "Signed in", - "notSignedIn": "Not signed in" + "authentication": "OAuth 認証", + "signIn": "サインイン", + "signingIn": "サインイン中...", + "signInAgain": "再度サインイン", + "signOut": "サインアウト", + "signedInAs": "{{account}} としてサインイン済み", + "signInHelp": "このデバイスからサインインします。API key は config に保存されません。", + "signInRequired": "サインインが必要です", + "signInBeforeSaving": "この OAuth プロバイダーをアクティブなモデルプロバイダーとして保存する前にサインインしてください。", + "signedIn": "サインイン済み", + "notSignedIn": "未サインイン" } }, "chat": { diff --git a/webui/src/i18n/locales/ko/common.json b/webui/src/i18n/locales/ko/common.json index 5b1569a29..cddf22634 100644 --- a/webui/src/i18n/locales/ko/common.json +++ b/webui/src/i18n/locales/ko/common.json @@ -65,14 +65,14 @@ "nav": { "general": "일반", "byok": "BYOK", - "overview": "Overview", - "appearance": "Appearance", - "models": "Models", - "providers": "Providers", - "image": "Image", - "browser": "Web", + "overview": "개요", + "appearance": "외관", + "models": "모델", + "providers": "제공자", + "image": "이미지", + "browser": "웹", "runtime": "시스템", - "advanced": "Security", + "advanced": "보안", "cliApps": "CLI 앱", "mcp": "MCP", "apps": "앱" @@ -81,21 +81,21 @@ "interface": "인터페이스", "ai": "AI", "system": "시스템", - "status": "Status", - "localPreferences": "Local preferences", - "presets": "Presets", + "status": "상태", + "localPreferences": "로컬 환경설정", + "presets": "프리셋", "imageGeneration": "이미지 생성", "imageDefaults": "기본값", - "webSearch": "Web search", - "webBehavior": "Behavior", - "identity": "Identity", - "webuiSafety": "Web safety", + "webSearch": "웹 검색", + "webBehavior": "동작", + "identity": "ID", + "webuiSafety": "WebUI 보안", "capabilities": "기능", "cliApps": "CLI 앱", "mcp": "MCP 서비스", "apps": "앱", - "nativeHost": "App", - "hostSafety": "App safety" + "nativeHost": "네이티브 호스트", + "hostSafety": "앱 보안" }, "rows": { "theme": "테마", @@ -104,118 +104,118 @@ "model": "모델", "restart": "nanobot 재시작", "configPath": "설정 경로", - "activePreset": "Active preset", - "gateway": "Gateway", - "restartState": "Restart state", - "pendingChanges": "대기 중인 변경 사항", - "selectedPreset": "Selected preset", - "presetModel": "Preset model", - "density": "Density", - "activityMode": "Activity detail", - "codeWrap": "Code wrapping", - "maxResults": "Max results", - "timeout": "Timeout", - "jinaReader": "Jina reader", + "activePreset": "활성 프리셋", + "gateway": "게이트웨이", + "restartState": "재시작 상태", + "pendingChanges": "대기 중인 변경", + "selectedPreset": "선택한 프리셋", + "presetModel": "프리셋 모델", + "density": "밀도", + "activityMode": "활동 상세", + "codeWrap": "코드 줄바꿈", + "maxResults": "최대 결과 수", + "timeout": "타임아웃", + "jinaReader": "Jina 리더", "imageGeneration": "이미지 생성", "imageProvider": "이미지 제공자", "imageProviderStatus": "제공자 상태", - "imageProviderBase": "제공자 기준 주소", + "imageProviderBase": "제공자 주소", "imageModel": "이미지 모델", "defaultAspectRatio": "기본 비율", "defaultImageSize": "기본 크기", "maxImagesPerTurn": "턴당 최대 이미지 수", "imageSaveDir": "저장 디렉터리", - "botName": "Bot name", - "botIcon": "Bot icon", - "timezone": "Timezone", + "botName": "Bot 이름", + "botIcon": "Bot 아이콘", + "timezone": "시간대", "workspacePath": "기본 작업공간", - "localServiceAccess": "Local services", - "webuiDefaultAccess": "Default access", + "localServiceAccess": "로컬 서비스", + "webuiDefaultAccess": "기본 권한", "currentModel": "현재 구성", "brandLogos": "브랜드 로고", - "cliAppsCatalog": "CLI 앱 카탈로그", - "cliAppsFilter": "CLI 앱 필터", + "cliAppsCatalog": "카탈로그", + "cliAppsFilter": "필터", "engine": "엔진", "logs": "로그", "diagnostics": "진단", - "contextWindow": "Context window" + "contextWindow": "컨텍스트 창" }, "help": { "theme": "밝은 모드와 어두운 모드를 전환합니다.", "language": "WebUI에서 사용할 언어를 선택합니다.", - "provider": "새 모델 요청에 사용할 제공자를 선택합니다.", + "provider": "새 모델 요청을 처리할 제공자를 선택합니다.", "model": "nanobot이 기본으로 사용할 모델 이름을 설정합니다.", "configPath": "현재 게이트웨이가 사용하는 설정 파일입니다.", - "selectedPreset": "Named presets are read-only here; edit them in config.json.", - "presetModel": "Switch to Default to edit model and provider from the WebUI.", - "density": "Stored only in this browser.", - "activityMode": "Choose how much agent activity chrome to show by default.", - "codeWrap": "Keep long code lines readable on smaller screens.", - "maxResults": "Results returned by each web_search call.", - "timeout": "Seconds before a search provider request times out.", - "jinaReader": "Use Jina Reader for web_fetch when available.", - "imageGeneration": "구성된 이미지 제공자를 사용할 수 있을 때 채팅에서 generate_image를 노출합니다.", - "imageProvider": "generate_image가 사용할 레지스트리 제공자를 선택합니다.", - "imageProviderStatus": "이미지 생성은 Providers의 제공자 자격 증명을 재사용합니다.", + "selectedPreset": "이름 있는 프리셋은 여기서 읽기 전용입니다. config.json에서 편집하세요.", + "presetModel": "Default로 전환하면 WebUI에서 모델과 제공자를 편집할 수 있습니다.", + "density": "이 브라우저에만 저장됩니다.", + "activityMode": "기본으로 표시할 agent 활동 세부 수준을 선택합니다.", + "codeWrap": "작은 화면에서도 긴 코드 줄을 읽기 쉽게 유지합니다.", + "maxResults": "각 web_search 호출에서 반환되는 결과 수입니다.", + "timeout": "검색 제공자 요청이 타임아웃되기 전의 초입니다.", + "jinaReader": "가능할 때 web_fetch에 Jina Reader를 사용합니다.", + "imageGeneration": "구성된 이미지 제공자가 있을 때 채팅에서 generate_image를 노출합니다.", + "imageProvider": "generate_image에 사용할 등록 제공자를 선택합니다.", + "imageProviderStatus": "이미지 생성은 제공자 자격 증명을 재사용합니다.", "imageModel": "선택한 이미지 제공자에 보낼 모델 이름입니다.", - "defaultAspectRatio": "프롬프트에서 가로세로 비율을 선택하지 않았을 때 사용됩니다.", - "defaultImageSize": "지원하는 제공자에게 보내는 크기 힌트입니다.", - "maxImagesPerTurn": "한 번의 generate_image 요청에 대한 상한입니다.", - "botName": "nanobot이 표시 이름을 쓰는 곳에 표시됩니다.", + "defaultAspectRatio": "프롬프트가 비율을 선택하지 않을 때 사용됩니다.", + "defaultImageSize": "지원하는 제공자에 보낼 크기 힌트입니다.", + "maxImagesPerTurn": "한 번의 generate_image 요청에서 생성할 수 있는 이미지 상한입니다.", + "botName": "nanobot이 표시 이름을 사용하는 곳에 표시됩니다.", "botIcon": "Bot 이름 옆에 표시할 짧은 emoji 또는 텍스트입니다.", - "timezone": "예약과 시간 인식 답변에 사용됩니다.", - "localServiceAccess": "Allow Full Access shell commands to reach localhost services.", - "webuiDefaultAccess": "Used by web chats without a project-specific permission.", - "securityManagedControls": "Web fetches always protect local, private, and metadata services. Core channel safety stays in config.json.", - "currentModel": "nanobot이 새 답변에 사용할 모델 구성을 선택합니다.", - "selectedModelProvider": "선택한 모델에서 설정됩니다.", - "selectedModelValue": "선택한 모델에서 설정됩니다.", - "brandLogos": "로고는 브랜드 도메인에서 불러오며, 실패하면 로컬 아이콘을 사용합니다.", - "cliAppsCatalog": "nanobot이 로컬에서 실행할 수 있는 앱 CLI를 살펴봅니다.", + "timezone": "일정과 시간 인식 답변에 사용됩니다.", + "localServiceAccess": "Full Access shell 명령이 localhost 서비스에 접근할 수 있게 합니다.", + "webuiDefaultAccess": "프로젝트별 권한이 없는 Web 채팅에 사용됩니다.", + "securityManagedControls": "웹 가져오기는 항상 로컬, 사설, 메타데이터 서비스를 보호합니다. 핵심 채널 보안은 config.json에서 관리됩니다.", + "currentModel": "새 응답에 사용됩니다.", + "selectedModelProvider": "선택한 모델에 의해 설정됩니다.", + "selectedModelValue": "선택한 모델에 의해 설정됩니다.", + "brandLogos": "설정에서 타사 제공자와 CLI 로고를 표시합니다.", + "cliAppsCatalog": "nanobot이 로컬에서 실행할 수 있는 앱 CLI 어댑터만 설치합니다. 네이티브 앱은 변경하지 않습니다.", "cliAppsFilter": "앱, 카테고리 또는 기능으로 검색합니다.", - "logs": "App 엔진 로그 폴더를 엽니다.", + "logs": "네이티브 엔진 로그 폴더를 엽니다.", "diagnostics": "지원용 작은 런타임 보고서를 내보냅니다.", - "localServiceAccessNative": "Allow Full Access shell commands to reach services on this Mac.", - "webuiDefaultAccessNative": "Used by native chats without a project-specific permission.", - "contextWindow": "Choose the default context budget for this model configuration." + "localServiceAccessNative": "Full Access shell 명령이 이 Mac의 서비스에 접근할 수 있게 합니다.", + "webuiDefaultAccessNative": "프로젝트별 권한이 없는 네이티브 채팅에 사용됩니다.", + "contextWindow": "이 모델 구성의 기본 컨텍스트 예산을 선택합니다." }, "values": { "light": "라이트", "dark": "다크", - "notAvailable": "사용할 수 없음", - "enabled": "Enabled", - "disabled": "Disabled", - "restartPending": "재시작 대기 중", + "notAvailable": "사용 불가", + "enabled": "활성화됨", + "disabled": "비활성화됨", + "restartPending": "재시작 대기", "ready": "준비됨", - "comfortable": "Comfortable", - "compact": "Compact", - "auto": "Auto", - "expanded": "Expanded", - "on": "On", - "off": "Off", - "defaultPermission": "Default Permission", - "fullAccess": "Full Access", - "configured": "Configured", - "notConfigured": "Not configured", + "comfortable": "편안함", + "compact": "컴팩트", + "auto": "자동", + "expanded": "펼침", + "on": "켜짐", + "off": "꺼짐", + "defaultPermission": "기본 권한", + "fullAccess": "전체 접근", + "configured": "구성됨", + "notConfigured": "미구성", "pending": "대기 중", - "restartingEngine": "다시 시작 중" + "restartingEngine": "재시작 중" }, "status": { "loading": "설정을 불러오는 중...", "loadError": "설정을 불러올 수 없습니다", "unsaved": "저장되지 않은 변경 사항이 있습니다.", "upToDate": "최신 상태입니다.", - "savedRestart": "저장되었습니다. 적용하려면 nanobot을 재시작하세요.", + "savedRestart": "저장했습니다. 적용하려면 nanobot을 재시작하세요.", "restartAfterSaving": "변경 사항을 저장한 뒤 준비되면 재시작하세요.", - "savedRestartApply": "저장되었습니다. 준비되면 재시작하세요.", - "imageProviderRestart": "이미지 제공자 변경 사항이 저장되었습니다. 준비되면 재시작하세요.", - "hostRestartAfterSaving": "저장하면 nanobot이 엔진을 다시 시작합니다.", - "hostRestartPending": "저장되었습니다. 준비되면 엔진을 다시 시작합니다.", - "hostApiUnavailable": "Host actions are only available inside the native app.", - "logsOpened": "Opened logs folder.", - "logsOpenFailed": "Could not open logs folder.", - "diagnosticsExported": "Diagnostics exported to {{path}}.", - "diagnosticsExportFailed": "Could not export diagnostics." + "savedRestartApply": "저장했습니다. 준비되면 재시작하세요.", + "imageProviderRestart": "이미지 제공자 변경 사항을 저장했습니다. 준비되면 재시작하세요.", + "hostRestartAfterSaving": "저장하면 nanobot이 엔진을 재시작합니다.", + "hostRestartPending": "저장했습니다. 준비되면 엔진을 재시작합니다.", + "hostApiUnavailable": "호스트 작업은 네이티브 앱 안에서만 사용할 수 있습니다.", + "logsOpened": "로그 폴더를 열었습니다.", + "logsOpenFailed": "로그 폴더를 열 수 없습니다.", + "diagnosticsExported": "진단을 {{path}}에 내보냈습니다.", + "diagnosticsExportFailed": "진단을 내보낼 수 없습니다." }, "actions": { "save": "저장", @@ -224,8 +224,8 @@ "cancel": "취소", "open": "열기", "export": "내보내기", - "opening": "Opening...", - "exporting": "Exporting..." + "opening": "여는 중...", + "exporting": "내보내는 중..." }, "byok": { "description": "직접 provider 키를 가져옵니다. Nanobot은 현재 config에서 값을 읽고, 설정된 provider만 일반 설정에서 선택할 수 있습니다.", @@ -250,7 +250,7 @@ "tabs": { "ariaLabel": "BYOK 자격 증명 유형", "llm": "LLM", - "webSearch": "Web Search" + "webSearch": "웹 검색" }, "webSearch": { "provider": "검색 provider", @@ -270,17 +270,17 @@ } }, "overview": { - "model": "Current model", - "providers": "Providers", - "configuredCount": "{{count}} configured", - "totalProviders": "{{count}} available", - "webSearch": "Web search", + "model": "현재 모델", + "providers": "제공자", + "configuredCount": "{{count}}개 구성됨", + "totalProviders": "{{count}}개 사용 가능", + "webSearch": "웹 검색", "imageGeneration": "이미지 생성", - "workspace": "Workspace" + "workspace": "작업공간" }, "providers": { - "searchPlaceholder": "Search providers", - "noMatches": "No providers match this search.", + "searchPlaceholder": "제공자 검색", + "noMatches": "일치하는 제공자가 없습니다.", "saveProvider": "제공자 저장" }, "image": { @@ -294,15 +294,15 @@ "selectModel": "모델 선택", "addConfiguration": "구성 추가", "newConfiguration": "새 모델 구성", - "newConfigurationHelp": "제공자와 모델을 한 번에 선택할 수 있는 옵션으로 저장합니다.", + "newConfigurationHelp": "제공자와 모델을 원클릭 옵션으로 저장합니다.", "configurationName": "구성 이름", "configurationNameHelp": "저장된 모델 구성의 이름을 변경합니다.", - "configurationNamePlaceholder": "빠른 글쓰기", - "searchModels": "검색하거나 모델 ID 입력", + "configurationNamePlaceholder": "빠른 작성", + "searchModels": "모델 ID 검색 또는 입력", "useCustomModel": "사용", - "loadingModels": "모델을 불러오는 중...", - "searchCatalog": "제공자 카탈로그를 검색해 모델을 선택하세요.", - "modelsAvailable": "개 사용 가능", + "loadingModels": "모델 불러오는 중...", + "searchCatalog": "제공자 카탈로그에서 모델을 선택합니다.", + "modelsAvailable": "사용 가능", "noModelResults": "일치하는 모델이 없습니다.", "loadFailed": "모델 목록을 사용할 수 없습니다.", "unsupportedModelList": "모델 ID를 직접 입력하세요.", @@ -402,31 +402,31 @@ "thirdPartyBrands": "제품 이름, 로고 및 브랜드는 각 소유자의 자산입니다. 사용은 식별 목적일 뿐 보증이나 제휴를 의미하지 않습니다." }, "apps": { - "description": "채팅에서 nanobot이 사용할 수 있는 앱 CLI와 MCP 서비스를 추가합니다.", + "description": "nanobot이 채팅에서 사용할 수 있는 App CLI와 MCP 서비스를 추가합니다.", "cliLabel": "CLI", "mcpLabel": "MCP", "filterAll": "전체", "filterCli": "CLI 앱", "filterMcp": "MCP 서비스", "enabledSummary": "{{count}}개 활성화됨", - "caption": "{{cli}} CLI · {{mcp}} MCP", + "caption": "CLI {{cli}}개 · MCP {{mcp}}개", "searchPlaceholder": "앱 검색", "featured": "추천", - "loading": "앱 불러오는 중...", - "empty": "이 필터와 일치하는 앱이 없습니다." + "loading": "앱을 불러오는 중...", + "empty": "일치하는 앱이 없습니다." }, "oauth": { - "authentication": "OAuth authentication", - "signIn": "Sign in", - "signingIn": "Signing in...", - "signInAgain": "Sign in again", - "signOut": "Sign out", - "signedInAs": "Signed in as {{account}}", - "signInHelp": "Sign in from this device; no API key is stored in config.", - "signInRequired": "Sign in required", - "signInBeforeSaving": "Sign in before saving this OAuth provider as the active model provider.", - "signedIn": "Signed in", - "notSignedIn": "Not signed in" + "authentication": "OAuth 인증", + "signIn": "로그인", + "signingIn": "로그인 중...", + "signInAgain": "다시 로그인", + "signOut": "로그아웃", + "signedInAs": "{{account}}로 로그인됨", + "signInHelp": "이 기기에서 로그인합니다. API key는 config에 저장되지 않습니다.", + "signInRequired": "로그인이 필요합니다", + "signInBeforeSaving": "이 OAuth 제공자를 활성 모델 제공자로 저장하기 전에 로그인하세요.", + "signedIn": "로그인됨", + "notSignedIn": "로그인 안 됨" } }, "chat": { diff --git a/webui/src/i18n/locales/vi/common.json b/webui/src/i18n/locales/vi/common.json index f4892278f..a8ef89e30 100644 --- a/webui/src/i18n/locales/vi/common.json +++ b/webui/src/i18n/locales/vi/common.json @@ -57,22 +57,22 @@ "apps": "Ứng dụng" }, "settings": { - "backToChat": "Quay lại trò chuyện", + "backToChat": "Quay lại chat", "sidebar": { "title": "Cài đặt", - "ariaLabel": "Các mục cài đặt" + "ariaLabel": "Mục cài đặt" }, "nav": { "general": "Chung", "byok": "BYOK", - "overview": "Overview", - "appearance": "Appearance", - "models": "Models", - "providers": "Providers", - "image": "Image", - "browser": "Web", + "overview": "Tổng quan", + "appearance": "Giao diện", + "models": "Mô hình", + "providers": "Nhà cung cấp", + "image": "Hình ảnh", + "browser": "Trang web", "runtime": "Hệ thống", - "advanced": "Security", + "advanced": "Bảo mật", "cliApps": "Ứng dụng CLI", "mcp": "MCP", "apps": "Ứng dụng" @@ -81,122 +81,122 @@ "interface": "Giao diện", "ai": "AI", "system": "Hệ thống", - "status": "Status", - "localPreferences": "Local preferences", - "presets": "Presets", - "imageGeneration": "Tạo ảnh", + "status": "Trạng thái", + "localPreferences": "Tùy chọn cục bộ", + "presets": "Preset", + "imageGeneration": "Tạo hình ảnh", "imageDefaults": "Mặc định", - "webSearch": "Web search", - "webBehavior": "Behavior", - "identity": "Identity", - "webuiSafety": "Web safety", + "webSearch": "Tìm kiếm web", + "webBehavior": "Hành vi", + "identity": "Danh tính", + "webuiSafety": "An toàn WebUI", "capabilities": "Khả năng", "cliApps": "Ứng dụng CLI", "mcp": "Dịch vụ MCP", "apps": "Ứng dụng", - "nativeHost": "Native host", - "hostSafety": "App safety" + "nativeHost": "Host gốc", + "hostSafety": "An toàn ứng dụng" }, "rows": { - "theme": "Giao diện", + "theme": "Chủ đề", "language": "Ngôn ngữ", "provider": "Nhà cung cấp", "model": "Mô hình", "restart": "Khởi động lại nanobot", "configPath": "Đường dẫn cấu hình", - "activePreset": "Active preset", - "gateway": "Gateway", - "restartState": "Restart state", - "pendingChanges": "Thay đổi đang chờ", - "selectedPreset": "Selected preset", - "presetModel": "Preset model", - "density": "Density", - "activityMode": "Activity detail", - "codeWrap": "Code wrapping", - "maxResults": "Max results", - "timeout": "Timeout", - "jinaReader": "Jina reader", - "imageGeneration": "Tạo ảnh", - "imageProvider": "Nhà cung cấp ảnh", + "activePreset": "Preset đang dùng", + "gateway": "Cổng", + "restartState": "Trạng thái khởi động lại", + "pendingChanges": "Thay đổi chờ áp dụng", + "selectedPreset": "Preset đã chọn", + "presetModel": "Mô hình preset", + "density": "Mật độ", + "activityMode": "Chi tiết hoạt động", + "codeWrap": "Xuống dòng mã", + "maxResults": "Kết quả tối đa", + "timeout": "Thời gian chờ", + "jinaReader": "Trình đọc Jina", + "imageGeneration": "Tạo hình ảnh", + "imageProvider": "Nhà cung cấp hình ảnh", "imageProviderStatus": "Trạng thái nhà cung cấp", - "imageProviderBase": "Cơ sở nhà cung cấp", - "imageModel": "Mô hình ảnh", + "imageProviderBase": "Địa chỉ nhà cung cấp", + "imageModel": "Mô hình hình ảnh", "defaultAspectRatio": "Tỷ lệ mặc định", "defaultImageSize": "Kích thước mặc định", - "maxImagesPerTurn": "Số ảnh tối đa mỗi lượt", + "maxImagesPerTurn": "Ảnh tối đa mỗi lượt", "imageSaveDir": "Thư mục lưu", - "botName": "Bot name", - "botIcon": "Bot icon", - "timezone": "Timezone", + "botName": "Tên bot", + "botIcon": "Biểu tượng bot", + "timezone": "Múi giờ", "workspacePath": "Workspace mặc định", - "localServiceAccess": "Local services", - "webuiDefaultAccess": "Default access", + "localServiceAccess": "Dịch vụ cục bộ", + "webuiDefaultAccess": "Quyền mặc định", "currentModel": "Cấu hình hiện tại", "brandLogos": "Logo thương hiệu", - "cliAppsCatalog": "Danh mục ứng dụng CLI", - "cliAppsFilter": "Bộ lọc ứng dụng CLI", - "engine": "Engine", + "cliAppsCatalog": "Danh mục", + "cliAppsFilter": "Bộ lọc", + "engine": "Bộ máy", "logs": "Nhật ký", "diagnostics": "Chẩn đoán", - "contextWindow": "Context window" + "contextWindow": "Cửa sổ ngữ cảnh" }, "help": { "theme": "Chuyển giữa giao diện sáng và tối.", "language": "Chọn ngôn ngữ dùng trong WebUI.", - "provider": "Chọn nhà cung cấp cho các yêu cầu mô hình mới.", - "model": "Đặt tên mô hình mặc định mà nanobot sử dụng.", - "configPath": "Tệp cấu hình gateway hiện đang dùng.", - "selectedPreset": "Named presets are read-only here; edit them in config.json.", - "presetModel": "Switch to Default to edit model and provider from the WebUI.", - "density": "Stored only in this browser.", - "activityMode": "Choose how much agent activity chrome to show by default.", - "codeWrap": "Keep long code lines readable on smaller screens.", - "maxResults": "Results returned by each web_search call.", - "timeout": "Seconds before a search provider request times out.", - "jinaReader": "Use Jina Reader for web_fetch when available.", - "imageGeneration": "Hiển thị generate_image trong chat khi có nhà cung cấp ảnh đã cấu hình.", - "imageProvider": "Chọn nhà cung cấp registry được generate_image sử dụng.", - "imageProviderStatus": "Tạo ảnh dùng lại thông tin xác thực nhà cung cấp từ Providers.", - "imageModel": "Tên mô hình gửi tới nhà cung cấp ảnh đã chọn.", - "defaultAspectRatio": "Được dùng khi prompt không chọn tỷ lệ khung hình.", - "defaultImageSize": "Gợi ý kích thước gửi tới các nhà cung cấp hỗ trợ.", + "provider": "Selecciona el proveedor para nuevas solicitudes de modelo.", + "model": "Define el nombre de modelo predeterminado de nanobot.", + "configPath": "Archivo de configuración que usa actualmente el gateway.", + "selectedPreset": "Los preajustes con nombre son de solo lectura aquí; edítalos en config.json.", + "presetModel": "Chuyển sang Default để chỉnh sửa mô hình và nhà cung cấp từ WebUI.", + "density": "Chỉ lưu trong trình duyệt này.", + "activityMode": "Chọn mức chi tiết hoạt động agent hiển thị mặc định.", + "codeWrap": "Giữ các dòng mã dài dễ đọc trên màn hình nhỏ.", + "maxResults": "Resultados devueltos por cada llamada web_search.", + "timeout": "Segundos antes de que una solicitud de búsqueda expire.", + "jinaReader": "Usa Jina Reader para web_fetch cuando esté disponible.", + "imageGeneration": "Expone generate_image en chats cuando hay un proveedor de imagen configurado.", + "imageProvider": "Elige el proveedor registrado usado por generate_image.", + "imageProviderStatus": "La generación de imágenes reutiliza credenciales de Proveedores.", + "imageModel": "Nombre del modelo enviado al proveedor de imágenes seleccionado.", + "defaultAspectRatio": "Se usa cuando el prompt no elige una proporción.", + "defaultImageSize": "Gợi ý kích thước gửi tới nhà cung cấp hỗ trợ.", "maxImagesPerTurn": "Giới hạn trên cho một yêu cầu generate_image.", - "botName": "Hiển thị ở nơi nanobot dùng tên hiển thị.", - "botIcon": "Emoji hoặc văn bản ngắn hiển thị cùng tên bot.", - "timezone": "Dùng cho lịch hẹn và câu trả lời có yếu tố thời gian.", - "localServiceAccess": "Allow Full Access shell commands to reach localhost services.", - "webuiDefaultAccess": "Used by web chats without a project-specific permission.", - "securityManagedControls": "Web fetches always protect local, private, and metadata services. Core channel safety stays in config.json.", - "currentModel": "Chọn cấu hình mô hình nanobot dùng cho các câu trả lời mới.", - "selectedModelProvider": "Được đặt bởi mô hình đã chọn.", - "selectedModelValue": "Được đặt bởi mô hình đã chọn.", - "brandLogos": "Logo được tải từ tên miền thương hiệu, có biểu tượng cục bộ làm dự phòng.", - "cliAppsCatalog": "Duyệt các CLI ứng dụng mà nanobot có thể chạy cục bộ.", - "cliAppsFilter": "Tìm theo ứng dụng, danh mục hoặc khả năng.", - "logs": "Mở thư mục nhật ký native engine.", - "diagnostics": "Xuất báo cáo runtime nhỏ để hỗ trợ.", - "localServiceAccessNative": "Allow Full Access shell commands to reach services on this Mac.", - "webuiDefaultAccessNative": "Used by native chats without a project-specific permission.", - "contextWindow": "Choose the default context budget for this model configuration." + "botName": "Se muestra donde nanobot usa un nombre visible.", + "botIcon": "Emoji o texto corto junto al nombre del bot.", + "timezone": "Se usa para horarios y respuestas con conciencia temporal.", + "localServiceAccess": "Cho phép lệnh shell Full Access truy cập dịch vụ localhost.", + "webuiDefaultAccess": "Dùng cho chat web không có quyền riêng theo dự án.", + "securityManagedControls": "Las capturas web siempre protegen servicios locales, privados y metadata. La seguridad de canales core se gestiona en config.json.", + "currentModel": "Dùng cho các phản hồi mới.", + "selectedModelProvider": "Definido por el modelo seleccionado.", + "selectedModelValue": "Definido por el modelo seleccionado.", + "brandLogos": "Hiển thị logo nhà cung cấp bên thứ ba và CLI trong Cài đặt.", + "cliAppsCatalog": "Instala solo adaptadores CLI de apps que nanobot puede ejecutar localmente; las apps nativas no se modifican.", + "cliAppsFilter": "Busca por app, categoría o capacidad.", + "logs": "Abre la carpeta de registros del motor nativo.", + "diagnostics": "Exporta un pequeño informe de runtime para soporte.", + "localServiceAccessNative": "Permite que comandos shell con Full Access alcancen servicios en este Mac.", + "webuiDefaultAccessNative": "Usado por chats nativos sin permiso específico de proyecto.", + "contextWindow": "Chọn ngân sách ngữ cảnh mặc định cho cấu hình mô hình này." }, "values": { "light": "Sáng", "dark": "Tối", "notAvailable": "Không khả dụng", - "enabled": "Enabled", - "disabled": "Disabled", - "restartPending": "Đang chờ khởi động lại", + "enabled": "Đã bật", + "disabled": "Đã tắt", + "restartPending": "Chờ khởi động lại", "ready": "Sẵn sàng", - "comfortable": "Comfortable", - "compact": "Compact", - "auto": "Auto", - "expanded": "Expanded", - "on": "On", - "off": "Off", - "defaultPermission": "Default Permission", - "fullAccess": "Full Access", - "configured": "Configured", - "notConfigured": "Not configured", + "comfortable": "Thoải mái", + "compact": "Gọn", + "auto": "Tự động", + "expanded": "Mở rộng", + "on": "Bật", + "off": "Tắt", + "defaultPermission": "Quyền mặc định", + "fullAccess": "Toàn quyền", + "configured": "Đã cấu hình", + "notConfigured": "Chưa cấu hình", "pending": "Đang chờ", "restartingEngine": "Đang khởi động lại" }, @@ -205,17 +205,17 @@ "loadError": "Không thể tải cài đặt", "unsaved": "Có thay đổi chưa lưu.", "upToDate": "Đã cập nhật.", - "savedRestart": "Đã lưu. Khởi động lại nanobot để áp dụng.", - "restartAfterSaving": "Lưu thay đổi, rồi khởi động lại khi sẵn sàng.", - "savedRestartApply": "Đã lưu. Khởi động lại khi sẵn sàng.", - "imageProviderRestart": "Đã lưu thay đổi nhà cung cấp ảnh. Khởi động lại khi sẵn sàng.", - "hostRestartAfterSaving": "Lưu thay đổi và nanobot sẽ khởi động lại engine.", - "hostRestartPending": "Đã lưu. Sẽ khởi động lại engine khi sẵn sàng.", - "hostApiUnavailable": "Host actions are only available inside the native app.", - "logsOpened": "Opened logs folder.", - "logsOpenFailed": "Could not open logs folder.", - "diagnosticsExported": "Diagnostics exported to {{path}}.", - "diagnosticsExportFailed": "Could not export diagnostics." + "savedRestart": "Guardado. Reinicia nanobot para aplicar.", + "restartAfterSaving": "Guarda los cambios y reinicia cuando puedas.", + "savedRestartApply": "Guardado. Reinicia cuando puedas.", + "imageProviderRestart": "Cambios del proveedor de imagen guardados. Reinicia cuando puedas.", + "hostRestartAfterSaving": "Al guardar, nanobot reiniciará su motor.", + "hostRestartPending": "Guardado. El motor se reiniciará cuando esté listo.", + "hostApiUnavailable": "Las acciones del host solo están disponibles en la app nativa.", + "logsOpened": "Carpeta de registros abierta.", + "logsOpenFailed": "No se pudo abrir la carpeta de registros.", + "diagnosticsExported": "Diagnóstico exportado a {{path}}.", + "diagnosticsExportFailed": "No se pudo exportar el diagnóstico." }, "actions": { "save": "Lưu", @@ -224,8 +224,8 @@ "cancel": "Hủy", "open": "Mở", "export": "Xuất", - "opening": "Opening...", - "exporting": "Exporting..." + "opening": "Đang mở...", + "exporting": "Đang xuất..." }, "byok": { "description": "Dùng key provider của riêng bạn. Nanobot đọc các giá trị này từ config hiện tại, và chỉ provider đã cấu hình mới có thể chọn trong Chung.", @@ -250,10 +250,10 @@ "tabs": { "ariaLabel": "Loại thông tin xác thực BYOK", "llm": "LLM", - "webSearch": "Web Search" + "webSearch": "Tìm kiếm web" }, "webSearch": { - "provider": "Search provider", + "provider": "Nhà cung cấp tìm kiếm", "providerHelp": "Chọn backend mà công cụ web search sẽ dùng.", "selectProvider": "Chọn provider", "credentials": "Thông tin xác thực", @@ -270,17 +270,17 @@ } }, "overview": { - "model": "Current model", - "providers": "Providers", - "configuredCount": "{{count}} configured", - "totalProviders": "{{count}} available", - "webSearch": "Web search", - "imageGeneration": "Tạo ảnh", - "workspace": "Workspace" + "model": "Mô hình hiện tại", + "providers": "Nhà cung cấp", + "configuredCount": "{{count}} đã cấu hình", + "totalProviders": "{{count}} khả dụng", + "webSearch": "Tìm kiếm web", + "imageGeneration": "Tạo hình ảnh", + "workspace": "Không gian làm việc" }, "providers": { - "searchPlaceholder": "Search providers", - "noMatches": "No providers match this search.", + "searchPlaceholder": "Tìm nhà cung cấp", + "noMatches": "Không có nhà cung cấp phù hợp.", "saveProvider": "Lưu nhà cung cấp" }, "image": { @@ -288,17 +288,17 @@ "selectAspect": "Chọn tỷ lệ", "selectSize": "Chọn kích thước", "configureProvider": "Cấu hình nhà cung cấp", - "missingCredential": "Cấu hình nhà cung cấp này trước khi bật tạo ảnh." + "missingCredential": "Configura este proveedor antes de activar la generación de imágenes." }, "models": { "selectModel": "Chọn mô hình", "addConfiguration": "Thêm cấu hình", "newConfiguration": "Cấu hình mô hình mới", - "newConfigurationHelp": "Lưu nhà cung cấp và mô hình thành một lựa chọn một lần nhấp.", + "newConfigurationHelp": "Lưu nhà cung cấp và mô hình thành một lựa chọn nhanh.", "configurationName": "Tên cấu hình", "configurationNameHelp": "Đổi tên cấu hình mô hình đã lưu này.", "configurationNamePlaceholder": "Viết nhanh", - "searchModels": "Tìm hoặc nhập ID mô hình", + "searchModels": "Tìm kiếm hoặc nhập ID mô hình", "useCustomModel": "Dùng", "loadingModels": "Đang tải mô hình...", "searchCatalog": "Tìm trong danh mục nhà cung cấp để chọn mô hình.", @@ -306,7 +306,7 @@ "noModelResults": "Không có mô hình phù hợp.", "loadFailed": "Không tải được danh sách mô hình.", "unsupportedModelList": "Nhập ID mô hình thủ công.", - "providerNotConfigured": "Cấu hình nhà cung cấp này trước khi tải mô hình.", + "providerNotConfigured": "Hãy cấu hình nhà cung cấp này trước khi tải mô hình.", "autoProviderCustomOnly": "Chế độ nhà cung cấp tự động dùng ID mô hình tùy chỉnh." }, "timezone": { @@ -402,7 +402,7 @@ "thirdPartyBrands": "Tên sản phẩm, logo và thương hiệu thuộc về chủ sở hữu tương ứng. Việc sử dụng chỉ nhằm nhận diện và không ngụ ý được xác nhận." }, "apps": { - "description": "Thêm CLI ứng dụng và dịch vụ MCP để nanobot dùng trong trò chuyện.", + "description": "Thêm CLI ứng dụng và dịch vụ MCP mà nanobot có thể dùng trong chat.", "cliLabel": "CLI", "mcpLabel": "MCP", "filterAll": "Tất cả", @@ -413,20 +413,20 @@ "searchPlaceholder": "Tìm ứng dụng", "featured": "Nổi bật", "loading": "Đang tải ứng dụng...", - "empty": "Không có ứng dụng nào khớp với bộ lọc này." + "empty": "Không có ứng dụng phù hợp." }, "oauth": { - "authentication": "OAuth authentication", - "signIn": "Sign in", - "signingIn": "Signing in...", - "signInAgain": "Sign in again", - "signOut": "Sign out", - "signedInAs": "Signed in as {{account}}", - "signInHelp": "Sign in from this device; no API key is stored in config.", - "signInRequired": "Sign in required", - "signInBeforeSaving": "Sign in before saving this OAuth provider as the active model provider.", - "signedIn": "Signed in", - "notSignedIn": "Not signed in" + "authentication": "Xác thực OAuth", + "signIn": "Đăng nhập", + "signingIn": "Đang đăng nhập...", + "signInAgain": "Đăng nhập lại", + "signOut": "Đăng xuất", + "signedInAs": "Đã đăng nhập bằng {{account}}", + "signInHelp": "Inicia sesión desde este dispositivo; no se guarda API key en config.", + "signInRequired": "Cần đăng nhập", + "signInBeforeSaving": "Inicia sesión antes de guardar este proveedor OAuth como proveedor activo.", + "signedIn": "Đã đăng nhập", + "notSignedIn": "Chưa đăng nhập" } }, "chat": { diff --git a/webui/src/i18n/locales/zh-CN/common.json b/webui/src/i18n/locales/zh-CN/common.json index b16ea423f..b8efdd686 100644 --- a/webui/src/i18n/locales/zh-CN/common.json +++ b/webui/src/i18n/locales/zh-CN/common.json @@ -57,7 +57,7 @@ "apps": "应用" }, "settings": { - "backToChat": "返回对话", + "backToChat": "返回聊天", "sidebar": { "title": "设置", "ariaLabel": "设置分区" @@ -91,30 +91,30 @@ "cliApps": "CLI 应用", "mcp": "MCP 服务", "identity": "身份", - "webuiSafety": "网页端安全", + "webuiSafety": "WebUI 安全", "capabilities": "能力", "apps": "应用", - "nativeHost": "App", - "hostSafety": "App 安全" + "nativeHost": "原生宿主", + "hostSafety": "应用安全" }, "models": { "selectModel": "选择模型", "addConfiguration": "添加配置", "newConfiguration": "新建模型配置", - "newConfigurationHelp": "把服务商和模型保存为一个可直接切换的选项。", + "newConfigurationHelp": "把提供商和模型保存成一键选项。", "configurationName": "配置名称", "configurationNameHelp": "重命名这个已保存的模型配置。", "configurationNamePlaceholder": "快速写作", "searchModels": "搜索或输入模型 ID", "useCustomModel": "使用", "loadingModels": "正在加载模型...", - "searchCatalog": "搜索服务商目录来选择模型。", - "modelsAvailable": "个可用", + "searchCatalog": "搜索提供商目录来选择模型。", + "modelsAvailable": "可用", "noModelResults": "没有匹配的模型。", - "loadFailed": "模型列表暂不可用。", + "loadFailed": "模型列表不可用。", "unsupportedModelList": "手动输入模型 ID。", - "providerNotConfigured": "先配置这个服务商再加载模型。", - "autoProviderCustomOnly": "自动服务商模式使用自定义模型 ID。" + "providerNotConfigured": "加载模型前请先配置此提供商。", + "autoProviderCustomOnly": "自动提供商模式使用自定义模型 ID。" }, "rows": { "theme": "主题", @@ -126,25 +126,25 @@ "activePreset": "当前预设", "gateway": "网关", "restartState": "重启状态", - "pendingChanges": "待处理更改", + "pendingChanges": "待应用更改", "currentModel": "当前配置", "selectedPreset": "选中的预设", "presetModel": "预设模型", "density": "密度", - "activityMode": "活动细节", + "activityMode": "活动详情", "codeWrap": "代码换行", "brandLogos": "品牌 Logo", "maxResults": "最大结果数", "timeout": "超时", - "jinaReader": "Jina Reader", + "jinaReader": "Jina 阅读器", "imageGeneration": "图片生成", - "imageProvider": "图片服务商", - "imageProviderStatus": "服务商状态", - "imageProviderBase": "服务商地址", + "imageProvider": "图片提供商", + "imageProviderStatus": "提供商状态", + "imageProviderBase": "提供商地址", "imageModel": "图片模型", "defaultAspectRatio": "默认比例", "defaultImageSize": "默认尺寸", - "maxImagesPerTurn": "每轮最多图片数", + "maxImagesPerTurn": "每轮最大图片数", "imageSaveDir": "保存目录", "botName": "Bot 名称", "botIcon": "Bot 图标", @@ -162,41 +162,41 @@ "help": { "theme": "在浅色和深色外观之间切换。", "language": "选择 WebUI 使用的语言。", - "provider": "选择新模型请求使用的服务商。", + "provider": "选择处理新模型请求的提供商。", "model": "设置 nanobot 默认使用的模型名称。", "configPath": "当前网关正在使用的配置文件。", - "currentModel": "选择 nanobot 接下来回复时使用的模型配置。", - "selectedModelProvider": "由当前模型决定。", - "selectedModelValue": "由当前模型决定。", - "selectedPreset": "命名预设在这里只读;需要编辑时请改 config.json。", - "presetModel": "切回 Default 后可在 WebUI 编辑模型和服务商。", - "density": "仅保存在当前浏览器。", + "currentModel": "用于新的回复。", + "selectedModelProvider": "由选中的模型决定。", + "selectedModelValue": "由选中的模型决定。", + "selectedPreset": "命名预设在这里只读;请在 config.json 中编辑。", + "presetModel": "切回 Default 后可在 WebUI 中编辑模型和提供商。", + "density": "只保存在此浏览器中。", "activityMode": "选择默认显示多少 agent 活动细节。", - "codeWrap": "让较小屏幕上的长代码行更易读。", - "brandLogos": "在设置页显示第三方服务商和 CLI 的 Logo。", - "maxResults": "每次 web_search 返回的结果数量。", - "timeout": "搜索服务商请求超时秒数。", + "codeWrap": "让长代码行在小屏幕上也易读。", + "brandLogos": "在设置中显示第三方提供商和 CLI 图标。", + "maxResults": "每次 web_search 调用返回的结果数。", + "timeout": "搜索提供商请求超时前的秒数。", "jinaReader": "可用时为 web_fetch 使用 Jina Reader。", - "imageGeneration": "当已配置图片服务商时,在对话中开放 generate_image。", - "imageProvider": "选择 generate_image 使用的服务商。", - "imageProviderStatus": "图片生成复用服务商页里的凭证配置。", - "imageModel": "发送给所选图片服务商的模型名称。", - "defaultAspectRatio": "当提示词没有选择比例时使用。", - "defaultImageSize": "发送给支持该能力的服务商的尺寸提示。", - "maxImagesPerTurn": "单次 generate_image 请求允许的图片上限。", - "botName": "显示在 nanobot 使用名称的地方。", - "botIcon": "显示在 bot 名称旁的短 emoji 或文本。", - "timezone": "用于计划任务和需要时间感知的回复。", - "cliAppsCatalog": "只安装 nanobot 在本机调用应用时需要的 CLI 适配层,不触碰应用本体。", - "cliAppsFilter": "按应用、分类或能力搜索。", - "localServiceAccess": "允许完全访问模式下的 shell 命令访问 localhost 服务。", - "webuiDefaultAccess": "用于没有单独选择权限的网页端对话。", + "imageGeneration": "配置图片提供商后,在聊天中开放 generate_image。", + "imageProvider": "选择 generate_image 使用的注册提供商。", + "imageProviderStatus": "图片生成会复用「提供商」里的凭据。", + "imageModel": "发送给所选图片提供商的模型名称。", + "defaultAspectRatio": "当提示词没有指定比例时使用。", + "defaultImageSize": "发送给支持此选项的提供商的尺寸提示。", + "maxImagesPerTurn": "单次 generate_image 请求可生成的图片上限。", + "botName": "显示在 nanobot 使用展示名称的地方。", + "botIcon": "显示在 Bot 名称旁的短 emoji 或文字。", + "timezone": "用于日程和需要时间感知的回复。", + "cliAppsCatalog": "只安装 nanobot 可本地运行的应用 CLI 适配器;不会改动原生应用。", + "cliAppsFilter": "按应用、类别或能力搜索。", + "localServiceAccess": "允许完全访问权限下的 shell 命令访问 localhost 服务。", + "webuiDefaultAccess": "用于没有单独项目权限的 Web 聊天。", "securityManagedControls": "网页抓取始终保护本机、内网和元数据服务。核心渠道安全仍由 config.json 管理。", - "logs": "打开App引擎日志文件夹。", - "diagnostics": "导出一份用于支持排查的运行时报告。", - "localServiceAccessNative": "允许完全访问模式下的 shell 命令访问这台 Mac 上的服务。", - "webuiDefaultAccessNative": "用于没有单独选择权限的原生 App 对话。", - "contextWindow": "选择这个模型配置默认使用的上下文预算。" + "logs": "打开原生引擎日志文件夹。", + "diagnostics": "导出一份用于支持排查的小型运行报告。", + "localServiceAccessNative": "允许完全访问权限下的 shell 命令访问这台 Mac 上的服务。", + "webuiDefaultAccessNative": "用于没有单独项目权限的原生聊天。", + "contextWindow": "选择此模型配置的默认上下文预算。" }, "timezone": { "select": "选择时区", @@ -251,9 +251,9 @@ "serverUrl": "URL", "transport": "传输方式", "command": "命令", - "args": "参数 JSON", + "args": "Args JSON", "headers": "Headers JSON", - "env": "环境变量 JSON", + "env": "Env JSON", "timeout": "工具超时", "advancedOptions": "高级选项", "hideAdvanced": "收起高级", @@ -299,13 +299,13 @@ "compact": "紧凑", "auto": "自动", "expanded": "展开", - "on": "开", - "off": "关", + "on": "开启", + "off": "关闭", "defaultPermission": "默认权限", - "fullAccess": "完全访问", + "fullAccess": "完全访问权限", "configured": "已配置", "notConfigured": "未配置", - "pending": "待应用", + "pending": "等待中", "restartingEngine": "正在重启" }, "status": { @@ -314,12 +314,12 @@ "unsaved": "有未保存的更改。", "upToDate": "已是最新。", "savedRestart": "已保存。重启 nanobot 后生效。", - "restartAfterSaving": "保存后,可在合适时重启。", - "savedRestartApply": "已保存,可稍后重启。", - "imageProviderRestart": "图片服务商改动已保存,可稍后重启。", - "hostRestartAfterSaving": "保存后,nanobot 会自动重启引擎。", - "hostRestartPending": "已保存,将在合适时重启引擎。", - "hostApiUnavailable": "宿主操作只能在原生 App 内使用。", + "restartAfterSaving": "保存更改后,在合适时重启。", + "savedRestartApply": "已保存。准备好后重启。", + "imageProviderRestart": "图片提供商更改已保存。准备好后重启。", + "hostRestartAfterSaving": "保存后 nanobot 会重启引擎。", + "hostRestartPending": "已保存。准备好后会重启引擎。", + "hostApiUnavailable": "宿主操作仅在原生应用中可用。", "logsOpened": "已打开日志文件夹。", "logsOpenFailed": "无法打开日志文件夹。", "diagnosticsExported": "诊断已导出到 {{path}}。", @@ -327,13 +327,13 @@ }, "actions": { "save": "保存", - "saving": "保存中", + "saving": "正在保存", "edit": "编辑", "cancel": "取消", "open": "打开", "export": "导出", - "opening": "打开中...", - "exporting": "导出中..." + "opening": "正在打开...", + "exporting": "正在导出..." }, "byok": { "description": "自带服务商密钥。Nanobot 会从当前 config 读取这些值,只有已配置的服务商才能在通用设置里选择。", @@ -387,19 +387,19 @@ "workspace": "工作区" }, "providers": { - "searchPlaceholder": "搜索服务商", - "noMatches": "没有匹配的服务商。", - "saveProvider": "保存服务商" + "searchPlaceholder": "搜索提供商", + "noMatches": "没有匹配的提供商。", + "saveProvider": "保存提供商" }, "legal": { "thirdPartyBrands": "产品名称、Logo 和品牌归各自所有者所有;此处仅用于识别,不代表背书或合作。" }, "image": { - "selectProvider": "选择服务商", + "selectProvider": "选择提供商", "selectAspect": "选择比例", "selectSize": "选择尺寸", - "configureProvider": "配置服务商", - "missingCredential": "启用图片生成前,请先配置这个服务商。" + "configureProvider": "配置提供商", + "missingCredential": "启用图片生成前请先配置此提供商。" }, "apps": { "description": "添加 nanobot 可在聊天中使用的 App CLI 和 MCP 服务。", @@ -409,22 +409,22 @@ "filterCli": "CLI 应用", "filterMcp": "MCP 服务", "enabledSummary": "已启用 {{count}} 个", - "caption": "{{cli}} CLI · {{mcp}} MCP", + "caption": "{{cli}} 个 CLI · {{mcp}} 个 MCP", "searchPlaceholder": "搜索应用", "featured": "精选", "loading": "正在加载应用...", - "empty": "没有符合筛选条件的应用。" + "empty": "没有匹配的应用。" }, "oauth": { "authentication": "OAuth 认证", "signIn": "登录", - "signingIn": "正在登录…", + "signingIn": "登录中...", "signInAgain": "重新登录", "signOut": "退出登录", "signedInAs": "已登录为 {{account}}", - "signInHelp": "在这台设备上登录;不会把 API key 写入配置。", + "signInHelp": "从这台设备登录;不会在配置中保存 API key。", "signInRequired": "需要登录", - "signInBeforeSaving": "先登录这个 OAuth 提供商,然后再保存为当前模型提供商。", + "signInBeforeSaving": "将此 OAuth 提供商设为当前模型提供商前,请先登录。", "signedIn": "已登录", "notSignedIn": "未登录" } diff --git a/webui/src/i18n/locales/zh-TW/common.json b/webui/src/i18n/locales/zh-TW/common.json index e567363a6..810a938e1 100644 --- a/webui/src/i18n/locales/zh-TW/common.json +++ b/webui/src/i18n/locales/zh-TW/common.json @@ -57,7 +57,7 @@ "apps": "應用" }, "settings": { - "backToChat": "返回對話", + "backToChat": "返回聊天", "sidebar": { "title": "設定", "ariaLabel": "設定分區" @@ -65,14 +65,14 @@ "nav": { "general": "一般", "byok": "BYOK", - "overview": "Overview", - "appearance": "Appearance", - "models": "Models", - "providers": "Providers", - "image": "Image", - "browser": "Web", + "overview": "概覽", + "appearance": "外觀", + "models": "模型", + "providers": "提供商", + "image": "圖片", + "browser": "網頁", "runtime": "系統", - "advanced": "Security", + "advanced": "安全", "cliApps": "CLI 應用", "mcp": "MCP", "apps": "應用" @@ -81,60 +81,60 @@ "interface": "介面", "ai": "AI", "system": "系統", - "status": "Status", - "localPreferences": "Local preferences", - "presets": "Presets", + "status": "狀態", + "localPreferences": "本機偏好", + "presets": "預設", "imageGeneration": "圖片生成", "imageDefaults": "預設值", - "webSearch": "Web search", - "webBehavior": "Behavior", - "identity": "Identity", - "webuiSafety": "網頁端安全", - "capabilities": "功能", + "webSearch": "網頁搜尋", + "webBehavior": "行為", + "identity": "身分", + "webuiSafety": "WebUI 安全", + "capabilities": "能力", "cliApps": "CLI 應用", "mcp": "MCP 服務", "apps": "應用", - "nativeHost": "App", + "nativeHost": "原生宿主", "hostSafety": "App 安全" }, "rows": { "theme": "主題", "language": "語言", - "provider": "提供者", + "provider": "供應商", "model": "模型", "restart": "重新啟動 nanobot", - "configPath": "設定檔路徑", - "activePreset": "Active preset", - "gateway": "Gateway", - "restartState": "Restart state", - "pendingChanges": "待處理變更", - "selectedPreset": "Selected preset", - "presetModel": "Preset model", - "density": "Density", - "activityMode": "Activity detail", - "codeWrap": "Code wrapping", - "maxResults": "Max results", - "timeout": "Timeout", - "jinaReader": "Jina reader", + "configPath": "配置路徑", + "activePreset": "目前預設", + "gateway": "閘道", + "restartState": "重啟狀態", + "pendingChanges": "待套用更改", + "selectedPreset": "已選預設", + "presetModel": "預設模型", + "density": "密度", + "activityMode": "活動細節", + "codeWrap": "程式碼換行", + "maxResults": "最大結果數", + "timeout": "逾時", + "jinaReader": "Jina 閱讀器", "imageGeneration": "圖片生成", - "imageProvider": "圖片服務商", - "imageProviderStatus": "服務商狀態", - "imageProviderBase": "服務商位址", + "imageProvider": "圖片供應商", + "imageProviderStatus": "供應商狀態", + "imageProviderBase": "供應商位址", "imageModel": "圖片模型", "defaultAspectRatio": "預設比例", "defaultImageSize": "預設尺寸", - "maxImagesPerTurn": "每輪最多圖片數", + "maxImagesPerTurn": "每輪最大圖片數", "imageSaveDir": "儲存目錄", - "botName": "Bot name", - "botIcon": "Bot icon", - "timezone": "Timezone", + "botName": "Bot 名稱", + "botIcon": "Bot 圖示", + "timezone": "時區", "workspacePath": "預設工作區", - "localServiceAccess": "Local services", - "webuiDefaultAccess": "Default access", + "localServiceAccess": "本機服務", + "webuiDefaultAccess": "預設權限", "currentModel": "目前設定", - "brandLogos": "品牌標誌", - "cliAppsCatalog": "CLI 應用目錄", - "cliAppsFilter": "CLI 應用篩選", + "brandLogos": "品牌 Logo", + "cliAppsCatalog": "目錄", + "cliAppsFilter": "篩選", "engine": "引擎", "logs": "日誌", "diagnostics": "診斷", @@ -143,75 +143,75 @@ "help": { "theme": "在淺色與深色外觀之間切換。", "language": "選擇 WebUI 使用的語言。", - "provider": "選擇新模型請求使用的服務提供者。", + "provider": "選擇處理新模型請求的供應商。", "model": "設定 nanobot 預設使用的模型名稱。", - "configPath": "目前閘道正在使用的設定檔。", - "selectedPreset": "Named presets are read-only here; edit them in config.json.", - "presetModel": "Switch to Default to edit model and provider from the WebUI.", - "density": "Stored only in this browser.", - "activityMode": "Choose how much agent activity chrome to show by default.", - "codeWrap": "Keep long code lines readable on smaller screens.", - "maxResults": "Results returned by each web_search call.", - "timeout": "Seconds before a search provider request times out.", - "jinaReader": "Use Jina Reader for web_fetch when available.", - "imageGeneration": "當已設定圖片服務商時,在對話中開放 generate_image。", - "imageProvider": "選擇 generate_image 使用的登錄服務商。", - "imageProviderStatus": "圖片生成會重用「服務商」中的憑證。", - "imageModel": "傳送給所選圖片服務商的模型名稱。", - "defaultAspectRatio": "當提示詞未指定長寬比時使用。", - "defaultImageSize": "傳送給支援此功能的服務商的尺寸提示。", - "maxImagesPerTurn": "單次 generate_image 請求的上限。", - "botName": "顯示在 nanobot 使用名稱的地方。", - "botIcon": "顯示在 bot 名稱旁的短 emoji 或文字。", - "timezone": "用於排程與需要時間感知的回覆。", - "localServiceAccess": "允許完全存取模式下的 shell 命令存取 localhost 服務。", - "webuiDefaultAccess": "用於沒有單獨選擇權限的網頁端對話。", - "securityManagedControls": "Web fetches always protect local, private, and metadata services. Core channel safety stays in config.json.", - "currentModel": "選擇 nanobot 接下來回覆時使用的模型設定。", - "selectedModelProvider": "由目前模型決定。", - "selectedModelValue": "由目前模型決定。", - "brandLogos": "標誌會從品牌網域載入,並提供本地圖示作為備援。", - "cliAppsCatalog": "瀏覽 nanobot 可在本機執行的應用 CLI。", - "cliAppsFilter": "按應用、分類或能力搜尋。", - "logs": "開啟App引擎日誌資料夾。", - "diagnostics": "匯出一份供支援排查用的執行階段報告。", - "localServiceAccessNative": "允許完全存取模式下的 shell 命令存取這台 Mac 上的服務。", - "webuiDefaultAccessNative": "用於沒有單獨選擇權限的原生 App 對話。", - "contextWindow": "選擇這個模型設定預設使用的上下文預算。" + "configPath": "目前閘道使用中的配置檔。", + "selectedPreset": "命名預設在此為唯讀;請在 config.json 中編輯。", + "presetModel": "切回 Default 後可在 WebUI 中編輯模型與供應商。", + "density": "只儲存在此瀏覽器中。", + "activityMode": "選擇預設顯示多少 agent 活動細節。", + "codeWrap": "讓長程式碼行在小螢幕上也易讀。", + "maxResults": "每次 web_search 呼叫返回的結果數。", + "timeout": "搜尋供應商請求逾時前的秒數。", + "jinaReader": "可用時為 web_fetch 使用 Jina Reader。", + "imageGeneration": "配置圖片供應商後,在聊天中開放 generate_image。", + "imageProvider": "選擇 generate_image 使用的註冊供應商。", + "imageProviderStatus": "圖片生成會重用「供應商」中的憑證。", + "imageModel": "傳送給所選圖片供應商的模型名稱。", + "defaultAspectRatio": "提示詞未指定比例時使用。", + "defaultImageSize": "傳送給支援此選項的供應商的尺寸提示。", + "maxImagesPerTurn": "單次 generate_image 請求可生成的圖片上限。", + "botName": "顯示在 nanobot 使用顯示名稱的位置。", + "botIcon": "顯示在 Bot 名稱旁的短 emoji 或文字。", + "timezone": "用於排程和需要時間感知的回覆。", + "localServiceAccess": "允許完全訪問權限下的 shell 命令訪問 localhost 服務。", + "webuiDefaultAccess": "用於沒有單獨專案權限的 Web 聊天。", + "securityManagedControls": "網頁抓取始終保護本機、內網和 metadata 服務。核心渠道安全仍由 config.json 管理。", + "currentModel": "用於新的回覆。", + "selectedModelProvider": "由選取的模型決定。", + "selectedModelValue": "由選取的模型決定。", + "brandLogos": "在設定中顯示第三方提供商和 CLI 圖示。", + "cliAppsCatalog": "只安裝 nanobot 可在本機執行的應用 CLI 轉接器;不會改動原生應用。", + "cliAppsFilter": "按應用、類別或能力搜尋。", + "logs": "開啟原生引擎日誌資料夾。", + "diagnostics": "匯出一份用於支援排查的小型執行報告。", + "localServiceAccessNative": "允許完全訪問權限下的 shell 命令訪問這台 Mac 上的服務。", + "webuiDefaultAccessNative": "用於沒有單獨專案權限的原生聊天。", + "contextWindow": "選擇此模型配置的預設上下文預算。" }, "values": { "light": "淺色", "dark": "深色", "notAvailable": "不可用", - "enabled": "Enabled", - "disabled": "Disabled", - "restartPending": "等待重新啟動", + "enabled": "已啟用", + "disabled": "已停用", + "restartPending": "等待重啟", "ready": "就緒", - "comfortable": "Comfortable", - "compact": "Compact", - "auto": "Auto", - "expanded": "Expanded", - "on": "On", - "off": "Off", - "defaultPermission": "Default Permission", - "fullAccess": "Full Access", - "configured": "Configured", - "notConfigured": "Not configured", - "pending": "待套用", - "restartingEngine": "正在重新啟動" + "comfortable": "舒適", + "compact": "緊湊", + "auto": "自動", + "expanded": "展開", + "on": "開啟", + "off": "關閉", + "defaultPermission": "預設權限", + "fullAccess": "完全訪問權限", + "configured": "已配置", + "notConfigured": "未配置", + "pending": "等待中", + "restartingEngine": "正在重啟" }, "status": { "loading": "正在載入設定...", "loadError": "無法載入設定", - "unsaved": "有未儲存的變更。", + "unsaved": "有未儲存的更改。", "upToDate": "已是最新。", "savedRestart": "已儲存。重新啟動 nanobot 後生效。", - "restartAfterSaving": "儲存變更後,可在準備好時重新啟動。", - "savedRestartApply": "已儲存,可在準備好時重新啟動。", - "imageProviderRestart": "圖片服務商變更已儲存,可在準備好時重新啟動。", - "hostRestartAfterSaving": "儲存後,nanobot 會自動重新啟動引擎。", - "hostRestartPending": "已儲存,將在適當時重新啟動引擎。", - "hostApiUnavailable": "宿主操作只能在原生 App 內使用。", + "restartAfterSaving": "儲存更改後,在合適時重啟。", + "savedRestartApply": "已儲存。準備好後重啟。", + "imageProviderRestart": "圖片供應商更改已儲存。準備好後重啟。", + "hostRestartAfterSaving": "儲存後 nanobot 會重啟引擎。", + "hostRestartPending": "已儲存。準備好後會重啟引擎。", + "hostApiUnavailable": "宿主操作僅在原生應用中可用。", "logsOpened": "已開啟日誌資料夾。", "logsOpenFailed": "無法開啟日誌資料夾。", "diagnosticsExported": "診斷已匯出到 {{path}}。", @@ -219,13 +219,13 @@ }, "actions": { "save": "儲存", - "saving": "儲存中", + "saving": "正在儲存", "edit": "編輯", "cancel": "取消", "open": "開啟", "export": "匯出", - "opening": "開啟中...", - "exporting": "匯出中..." + "opening": "正在開啟...", + "exporting": "正在匯出..." }, "byok": { "description": "自帶 provider key。Nanobot 會從目前 config 讀取這些值,只有已設定的 provider 才能在一般設定中選擇。", @@ -250,7 +250,7 @@ "tabs": { "ariaLabel": "BYOK 憑證類型", "llm": "LLM", - "webSearch": "Web Search" + "webSearch": "網頁搜尋" }, "webSearch": { "provider": "搜尋 provider", @@ -270,44 +270,44 @@ } }, "overview": { - "model": "Current model", - "providers": "Providers", - "configuredCount": "{{count}} configured", - "totalProviders": "{{count}} available", - "webSearch": "Web search", + "model": "目前模型", + "providers": "供應商", + "configuredCount": "已配置 {{count}} 個", + "totalProviders": "共 {{count}} 個可用", + "webSearch": "網頁搜尋", "imageGeneration": "圖片生成", - "workspace": "Workspace" + "workspace": "工作區" }, "providers": { - "searchPlaceholder": "Search providers", - "noMatches": "No providers match this search.", - "saveProvider": "儲存服務商" + "searchPlaceholder": "搜尋供應商", + "noMatches": "沒有符合的供應商。", + "saveProvider": "儲存供應商" }, "image": { - "selectProvider": "選擇服務商", + "selectProvider": "選擇供應商", "selectAspect": "選擇比例", "selectSize": "選擇尺寸", - "configureProvider": "設定服務商", - "missingCredential": "啟用圖片生成前,請先設定此服務商。" + "configureProvider": "配置供應商", + "missingCredential": "啟用圖片生成前請先配置此供應商。" }, "models": { "selectModel": "選擇模型", - "addConfiguration": "新增設定", - "newConfiguration": "新增模型設定", - "newConfigurationHelp": "把服務商和模型儲存為一個可直接切換的選項。", - "configurationName": "設定名稱", + "addConfiguration": "新增配置", + "newConfiguration": "新增模型配置", + "newConfigurationHelp": "將供應商與模型儲存為一鍵選項。", + "configurationName": "配置名稱", "configurationNameHelp": "重新命名這個已儲存的模型配置。", "configurationNamePlaceholder": "快速寫作", "searchModels": "搜尋或輸入模型 ID", "useCustomModel": "使用", "loadingModels": "正在載入模型...", - "searchCatalog": "搜尋服務商目錄來選擇模型。", - "modelsAvailable": "個可用", + "searchCatalog": "搜尋供應商目錄來選擇模型。", + "modelsAvailable": "可用", "noModelResults": "沒有符合的模型。", - "loadFailed": "模型列表暫不可用。", + "loadFailed": "模型列表不可用。", "unsupportedModelList": "手動輸入模型 ID。", - "providerNotConfigured": "先設定這個服務商再載入模型。", - "autoProviderCustomOnly": "自動服務商模式使用自訂模型 ID。" + "providerNotConfigured": "載入模型前請先配置此供應商。", + "autoProviderCustomOnly": "自動供應商模式使用自訂模型 ID。" }, "timezone": { "select": "選擇時區", @@ -402,29 +402,29 @@ "thirdPartyBrands": "產品名稱、標誌與品牌均屬於其各自擁有者。使用僅為識別用途,並不代表背書。" }, "apps": { - "description": "新增 nanobot 可在聊天中使用的 App CLI 與 MCP 服務。", + "description": "新增 nanobot 可在聊天中使用的 App CLI 和 MCP 服務。", "cliLabel": "CLI", "mcpLabel": "MCP", "filterAll": "全部", "filterCli": "CLI 應用", "filterMcp": "MCP 服務", "enabledSummary": "已啟用 {{count}} 個", - "caption": "{{cli}} CLI · {{mcp}} MCP", + "caption": "{{cli}} 個 CLI · {{mcp}} 個 MCP", "searchPlaceholder": "搜尋應用", "featured": "精選", "loading": "正在載入應用...", - "empty": "沒有符合篩選條件的應用。" + "empty": "沒有符合的應用。" }, "oauth": { - "authentication": "OAuth 驗證", + "authentication": "OAuth 認證", "signIn": "登入", - "signingIn": "正在登入…", + "signingIn": "登入中...", "signInAgain": "重新登入", "signOut": "登出", "signedInAs": "已登入為 {{account}}", - "signInHelp": "在這台裝置上登入;不會把 API key 寫入設定。", + "signInHelp": "從這台裝置登入;不會在配置中儲存 API key。", "signInRequired": "需要登入", - "signInBeforeSaving": "請先登入這個 OAuth 提供商,再儲存為目前模型提供商。", + "signInBeforeSaving": "將此 OAuth 供應商設為目前模型供應商前,請先登入。", "signedIn": "已登入", "notSignedIn": "未登入" } diff --git a/webui/src/tests/i18n.test.tsx b/webui/src/tests/i18n.test.tsx index 202b9d29e..99f1f0a08 100644 --- a/webui/src/tests/i18n.test.tsx +++ b/webui/src/tests/i18n.test.tsx @@ -34,6 +34,60 @@ const SETTINGS_NAV_KEYS = [ "runtime", "advanced", ]; +const LOCALIZED_SETTINGS_COPY_KEYS = [ + "settings.backToChat", + "settings.sidebar.title", + "settings.sidebar.ariaLabel", + "settings.nav.overview", + "settings.nav.appearance", + "settings.nav.models", + "settings.nav.providers", + "settings.nav.apps", + "settings.nav.runtime", + "settings.nav.advanced", + "settings.sections.interface", + "settings.sections.localPreferences", + "settings.sections.webSearch", + "settings.sections.webBehavior", + "settings.sections.webuiSafety", + "settings.sections.capabilities", + "settings.sections.apps", + "settings.rows.theme", + "settings.rows.language", + "settings.rows.density", + "settings.rows.activityMode", + "settings.rows.codeWrap", + "settings.rows.brandLogos", + "settings.rows.currentModel", + "settings.rows.localServiceAccess", + "settings.rows.webuiDefaultAccess", + "settings.rows.contextWindow", + "settings.help.theme", + "settings.help.language", + "settings.help.density", + "settings.help.activityMode", + "settings.help.codeWrap", + "settings.help.brandLogos", + "settings.help.currentModel", + "settings.help.localServiceAccess", + "settings.help.webuiDefaultAccess", + "settings.values.light", + "settings.values.dark", + "settings.values.comfortable", + "settings.values.compact", + "settings.values.expanded", + "settings.values.enabled", + "settings.values.disabled", + "settings.values.defaultPermission", + "settings.values.fullAccess", + "settings.values.configured", + "settings.values.notConfigured", + "settings.status.loading", + "settings.status.unsaved", + "settings.status.upToDate", + "settings.actions.save", + "settings.actions.saving", +]; function isRecord(value: unknown): value is Record { return !!value && typeof value === "object" && !Array.isArray(value); } @@ -190,6 +244,20 @@ describe("webui i18n", () => { } }); + it("does not leak English settings chrome into localized locales", () => { + const english = flattenResource(resources.en.common); + + for (const [locale, resource] of Object.entries(resources)) { + if (locale === "en") continue; + const current = flattenResource(resource.common); + const leaked = LOCALIZED_SETTINGS_COPY_KEYS.filter( + (key) => current.get(key) === english.get(key), + ); + + expect({ locale, leaked }).toEqual({ locale, leaked: [] }); + } + }); + it("keeps Simplified Chinese settings overview copy localized", () => { const settings = resources["zh-CN"].common.settings; diff --git a/webui/src/tests/thread-messages.test.tsx b/webui/src/tests/thread-messages.test.tsx index 9319a0951..feed81496 100644 --- a/webui/src/tests/thread-messages.test.tsx +++ b/webui/src/tests/thread-messages.test.tsx @@ -194,7 +194,8 @@ describe("ThreadMessages", () => { render(); - expect(screen.getByLabelText(/editing foo\.txt/i)).toBeInTheDocument(); + expect(screen.getByLabelText(/edited foo\.txt/i)).toBeInTheDocument(); + expect(screen.queryByLabelText(/editing foo\.txt/i)).not.toBeInTheDocument(); }); it("folds final answer reasoning into the preceding activity timeline", () => { diff --git a/webui/src/tests/useNanobotStream.test.tsx b/webui/src/tests/useNanobotStream.test.tsx index e880bf90f..08c32b17e 100644 --- a/webui/src/tests/useNanobotStream.test.tsx +++ b/webui/src/tests/useNanobotStream.test.tsx @@ -658,7 +658,7 @@ describe("useNanobotStream", () => { }]); }); - it("keeps interrupted pre-tool text inside activity before the final answer", async () => { + it("keeps interrupted pre-tool text as assistant output before activity", async () => { const fake = fakeClient(); const { result } = renderHook(() => useNanobotStream("chat-stream-segments", EMPTY_MESSAGES), { wrapper: wrap(fake.client), @@ -692,9 +692,7 @@ describe("useNanobotStream", () => { expect(result.current.messages).toHaveLength(3); expect(result.current.messages[0]).toMatchObject({ role: "assistant", - content: "", - reasoning: "I created the files.", - isStreaming: false, + content: "I created the files.", }); expect(result.current.messages[1]).toMatchObject({ role: "tool", @@ -739,9 +737,7 @@ describe("useNanobotStream", () => { expect(result.current.messages).toHaveLength(3); expect(result.current.messages[0]).toMatchObject({ role: "assistant", - content: "", - reasoning: "I will inspect the project first.", - isStreaming: false, + content: "I will inspect the project first.", }); expect(result.current.messages[1]).toMatchObject({ role: "tool", @@ -755,6 +751,51 @@ describe("useNanobotStream", () => { }); }); + it("splits live assistant output around tool hints without moving it into reasoning", async () => { + const fake = fakeClient(); + const { result } = renderHook(() => useNanobotStream("chat-live-segments", EMPTY_MESSAGES), { + wrapper: wrap(fake.client), + }); + + act(() => { + fake.emit("chat-live-segments", { + event: "delta", + chat_id: "chat-live-segments", + text: "Lint passed; now rendering the video.", + }); + fake.emit("chat-live-segments", { + event: "message", + chat_id: "chat-live-segments", + text: 'exec({"cmd":"hyperframes render"})', + kind: "tool_hint", + }); + fake.emit("chat-live-segments", { + event: "delta", + chat_id: "chat-live-segments", + text: "Rendered successfully.", + }); + }); + + await flushStreamFrame(); + + expect(result.current.messages).toHaveLength(3); + expect(result.current.messages[0]).toMatchObject({ + role: "assistant", + content: "Lint passed; now rendering the video.", + }); + expect(result.current.messages[0].reasoning).toBeUndefined(); + expect(result.current.messages[1]).toMatchObject({ + role: "tool", + kind: "trace", + traces: ['exec({"cmd":"hyperframes render"})'], + }); + expect(result.current.messages[2]).toMatchObject({ + role: "assistant", + content: "Rendered successfully.", + }); + expect(result.current.messages[2].reasoning).toBeUndefined(); + }); + it("opens a new activity segment for reasoning after file edit activity", async () => { const fake = fakeClient(); const { result } = renderHook(() => useNanobotStream("chat-file-segments", EMPTY_MESSAGES), { @@ -967,7 +1008,7 @@ describe("useNanobotStream", () => { expect(result.current.messages[0].reasoningStreaming).toBe(false); }); - it("attaches post-hoc reasoning to the same assistant turn above the answer", () => { + it("starts a new Thought block when reasoning arrives after visible output", () => { const fake = fakeClient(); const { result } = renderHook(() => useNanobotStream("chat-r5", EMPTY_MESSAGES), { wrapper: wrap(fake.client), @@ -988,12 +1029,61 @@ describe("useNanobotStream", () => { fake.emit("chat-r5", { event: "reasoning_end", chat_id: "chat-r5" }); }); - expect(result.current.messages).toHaveLength(1); + expect(result.current.messages).toHaveLength(2); expect(result.current.messages[0].content).toBe("hi~"); - expect(result.current.messages[0].reasoning).toBe( + expect(result.current.messages[0].reasoning).toBeUndefined(); + expect(result.current.messages[1].content).toBe(""); + expect(result.current.messages[1].reasoning).toBe( "This reasoning arrived after the answer stream.", ); - expect(result.current.messages[0].reasoningStreaming).toBe(false); + expect(result.current.messages[1].reasoningStreaming).toBe(false); + }); + + it("keeps alternating reasoning and answer deltas in separate ordered blocks", async () => { + const fake = fakeClient(); + const { result } = renderHook(() => useNanobotStream("chat-r5b", EMPTY_MESSAGES), { + wrapper: wrap(fake.client), + }); + + act(() => { + fake.emit("chat-r5b", { + event: "reasoning_delta", + chat_id: "chat-r5b", + text: "Plan first.", + }); + fake.emit("chat-r5b", { + event: "delta", + chat_id: "chat-r5b", + text: "Visible progress.", + }); + fake.emit("chat-r5b", { + event: "reasoning_delta", + chat_id: "chat-r5b", + text: "Think again.", + }); + fake.emit("chat-r5b", { + event: "delta", + chat_id: "chat-r5b", + text: "Final visible text.", + }); + }); + + await flushStreamFrame(); + + expect(result.current.messages).toHaveLength(2); + expect(result.current.messages[0]).toMatchObject({ + role: "assistant", + reasoning: "Plan first.", + content: "Visible progress.", + }); + expect(result.current.messages[1]).toMatchObject({ + role: "assistant", + reasoning: "Think again.", + content: "Final visible text.", + }); + expect(result.current.messages[1].activitySegmentId).not.toBe( + result.current.messages[0].activitySegmentId, + ); }); it("does not attach a new turn's reasoning across the latest user boundary", async () => {