// ── i18n: locale bundles and t() helper ──────────────────────────────────────
// To add a new language: add an entry to LOCALES below with all keys translated.
// The language code must match a valid BCP 47 tag (used for speech recognition).
// Keys missing in a non-English locale fall back to English automatically.

const LOCALES = {
  en: {
    _lang: 'en',
    _label: 'English',
    _speech: 'en-US',
    // boot.js
    cancelling: 'Cancelling\u2026',
    cancel_failed: 'Cancel failed: ',
    mic_denied: 'Microphone access denied. Check browser permissions.',
    mic_no_speech: 'No speech detected. Try again.',
    mic_network: 'Speech recognition unavailable.',
    mic_error: 'Voice input error: ',
    session_imported: 'Session imported',
    import_failed: 'Import failed: ',
    import_invalid_json: 'Invalid JSON',
    image_pasted: 'Image pasted: ',
    // messages.js
    edit_message: 'Edit message',
    regenerate: 'Regenerate response',
    copy: 'Copy',
    copied: 'Copied!',
    you: 'You',
    thinking: 'Thinking',
    expand_all: 'Expand all',
    collapse_all: 'Collapse all',
    edit_failed: 'Edit failed: ',
    regen_failed: 'Regenerate failed: ',
    reconnect_active: 'A response is still being generated. Reload when ready?',
    reconnect_finished: 'A response was in progress when you last left. Messages may have updated.',
    // approval card
    approval_heading: 'Approval required',
    approval_desc_prefix: 'Dangerous command detected',
    approval_btn_once: 'Allow once',
    approval_btn_once_title: 'Allow this one command (Enter)',
    approval_btn_session: 'Allow session',
    approval_btn_session_title: 'Allow for this conversation session',
    approval_btn_always: 'Always allow',
    approval_btn_always_title: 'Always allow this command pattern',
    approval_btn_deny: 'Deny',
    approval_btn_deny_title: 'Deny — do not run this command',
    approval_responding: 'Responding\u2026',
    untitled: 'Untitled',
    n_messages: (n) => `${n} messages`,
    model_unavailable: ' (unavailable)',
    model_unavailable_title: 'This model is no longer in your current provider list',
    provider_mismatch_warning: (m,p)=>`"${m}" may not work with your configured provider (${p}). Send anyway, or run \`hermes model\` in your terminal to switch.`,
    provider_mismatch_label: 'Provider mismatch',
    // commands.js
    cmd_help: 'List available commands',
    cmd_clear: 'Clear conversation messages',
    cmd_compact: 'Compress conversation context',
    cmd_model: 'Switch model (e.g. /model gpt-4o)',
    cmd_workspace: 'Switch workspace by name',
    cmd_new: 'Start a new chat session',
    cmd_usage: 'Toggle token usage display on/off',
    cmd_theme: 'Switch theme (dark/light/slate/solarized/monokai/nord/oled)',
    cmd_personality: 'Switch agent personality',
    cmd_skills: 'List available Hermes skills',
    available_commands: 'Available commands:',
    type_slash: 'Type / to see commands',
    conversation_cleared: 'Conversation cleared',
    model_usage: 'Usage: /model <name>',
    no_model_match: 'No model matching "',
    switched_to: 'Switched to ',
    workspace_usage: 'Usage: /workspace <name>',
    no_workspace_match: 'No workspace matching "',
    switched_workspace: 'Switched to workspace: ',
    workspace_switch_failed: 'Workspace switch failed: ',
    new_session: 'New session created',
    compressing: 'Requesting context compression...',
    token_usage_on: 'Token usage on',
    token_usage_off: 'Token usage off',
    theme_usage: 'Usage: /theme ',
    theme_set: 'Theme: ',
    no_active_session: 'No active session',
    no_personalities: 'No personalities found (add them to ~/.hermes/personalities/)',
    available_personalities: 'Available personalities:',
    personality_switch_hint: '\n\nUse `/personality <name>` to switch, or `/personality none` to clear.',
    personalities_load_failed: 'Failed to load personalities',
    personality_cleared: 'Personality cleared',
    personality_set: 'Personality: ',
    failed_colon: 'Failed: ',
    // ui.js
    no_workspace: 'No workspace',
    dialog_confirm_title: 'Confirm action',
    dialog_prompt_title: 'Enter a value',
    dialog_confirm_btn: 'Confirm',
    // workspace.js
    unsaved_confirm: 'You have unsaved changes in the preview. Discard and navigate?',
    discard: 'Discard',
    save: 'Save',
    edit: 'Edit',
    clear: 'Clear',
    create: 'Create',
    remove: 'Remove',
    save_title: 'Save changes',
    edit_title: 'Edit this file',
    saved: 'Saved',
    save_failed: 'Save failed: ',
    image_load_failed: 'Could not load image',
    file_open_failed: 'Could not open file',
    downloading: (name) => `Downloading ${name}\u2026`,
    double_click_rename: 'Double-click to rename',
    renamed_to: 'Renamed to ',
    rename_failed: 'Rename failed: ',
    delete_title: 'Delete',
    delete_confirm: (name) => `Delete ${name}?`,
    deleted: 'Deleted ',
    delete_failed: 'Delete failed: ',
    new_file_prompt: 'New file name (e.g. notes.md):',
    project_name_prompt: 'Project name:',
    created: 'Created ',
    create_failed: 'Create failed: ',
    new_folder_prompt: 'New folder name:',
    folder_created: 'Created folder ',
    folder_create_failed: 'Create folder failed: ',
    remove_title: 'Remove',
    empty_dir: '(empty)',
    upload_failed: 'Upload failed: ',
    all_uploads_failed: (n) => `All ${n} upload(s) failed`,
    // settings panel
    settings_title: 'Settings',
    settings_save_btn: 'Save Settings',
    settings_label_model: 'Default Model',
    settings_label_send_key: 'Send Key',
    settings_label_theme: 'Theme',
    settings_label_language: 'Language',
    settings_label_token_usage: 'Show token usage',
    settings_label_bubble_layout: 'Chat bubble layout',
    settings_label_cli_sessions: 'Show agent sessions',
    settings_label_sync_insights: 'Sync to insights',
    settings_label_check_updates: 'Check for updates',
    settings_label_bot_name: 'Assistant Name',
    settings_label_password: 'Access Password',
    settings_saved: 'Settings saved',
    settings_save_failed: 'Save failed: ',
    settings_load_failed: 'Failed to load settings: ',
    settings_saved_pw: 'Settings saved (password set \u2014 login now required)',
    // login page (used server-side via /api/i18n/login endpoint)
    login_title: 'Sign in',
    login_subtitle: 'Enter your password to continue',
    login_placeholder: 'Password',
    login_btn: 'Sign in',
    login_invalid_pw: 'Invalid password',
    login_conn_failed: 'Connection failed',
    dialog_confirm_title: 'Confirm action',
    dialog_prompt_title: 'Enter a value',
    dialog_confirm_btn: 'Confirm',
    discard: 'Discard',
    clear: 'Clear',
    create: 'Create',
    remove: 'Remove',
    project_name_prompt: 'Project name:',
    // Sidebar & Tabs
    tab_chat: 'Chat',
    tab_tasks: 'Tasks',
    tab_skills: 'Skills',
    tab_memory: 'Memory',
    tab_workspaces: 'Spaces',
    tab_profiles: 'Profiles',
    tab_todos: 'Todos',
    new_conversation: 'New conversation',
    filter_conversations: 'Filter conversations...',
    scheduled_jobs: 'Scheduled jobs',
    new_job: 'New job',
    loading: 'Loading...',
    search_skills: 'Search skills...',
    new_skill: 'New skill',
    personal_memory: 'Personal memory',
    current_task_list: 'Current task list',
    workspace_desc: 'Add and switch workspaces for your sessions.',
    new_profile: 'New profile',
    transcript: 'Transcript',
    download_transcript: 'Download as Markdown',
    import: 'Import',
    // Settings detail
    settings_label_sound: 'Notification sound',
    settings_desc_sound: 'Play a sound when the assistant finishes a response.',
    settings_label_notifications: 'Browser notifications',
    settings_desc_notifications: 'Show a system notification when a response completes while the tab is in the background.',
    settings_desc_token_usage: 'Displays input/output token count below each assistant reply. Also toggled with /usage.',
    settings_desc_bubble_layout: 'Right-align user messages and left-align assistant replies. Off by default to keep code blocks and tool output full-width.',
    settings_desc_cli_sessions: 'Merges sessions from the Hermes CLI (state.db) into the session list. Click a CLI session to import it and continue the conversation.',
    settings_desc_sync_insights: 'Mirrors WebUI token usage to state.db so hermes /insights includes browser session data. Off by default.',
    settings_desc_check_updates: 'Show a banner when newer versions of the WebUI or Agent are available. Runs a background git fetch periodically.',
    settings_desc_bot_name: 'Display name for the assistant throughout the UI. Defaults to Hermes.',
    settings_desc_password: 'Enter a new password to set or change it. Leave blank to keep current setting.',
    password_placeholder: 'Enter new password…',
    disable_auth: 'Disable Auth',
    sign_out: 'Sign Out',
    cancel: 'Cancel',
    create_job: 'Create job',
    save_skill: 'Save skill',
    editing: 'Editing',
    // Empty state
    empty_title: 'What can I help with?',
    empty_subtitle: 'Ask anything, run commands, explore files, or manage your scheduled tasks.',
    suggest_files: 'What files are in this workspace?',
    suggest_schedule: "What's on my schedule today?",
    suggest_plan: 'Help me plan a small project.',
    // onboarding
    onboarding_badge: 'FIRST RUN',
    onboarding_title: 'Welcome to Hermes Web UI',
    onboarding_lead: 'A quick guided setup will verify Hermes, save a real provider configuration, choose a workspace and model, and optionally protect the app with a password.',
    onboarding_back: 'Back',
    onboarding_continue: 'Continue',
    onboarding_open: 'Open Hermes',
    onboarding_step_system_title: 'System check',
    onboarding_step_system_desc: 'Verify Hermes Agent and config visibility.',
    onboarding_step_setup_title: 'Provider setup',
    onboarding_step_setup_desc: 'Save the minimum Hermes provider config.',
    onboarding_step_workspace_title: 'Workspace + model',
    onboarding_step_workspace_desc: 'Pick defaults for new sessions and chat.',
    onboarding_step_password_title: 'Optional password',
    onboarding_step_password_desc: 'Protect the Web UI before sharing it.',
    onboarding_step_finish_title: 'Finish',
    onboarding_step_finish_desc: 'Review and enter the app.',
    onboarding_notice_system_ready: 'Hermes Agent looks reachable from the Web UI.',
    onboarding_notice_system_unavailable: 'Hermes Agent is not fully available yet. Bootstrap can install it, but provider setup may still require a terminal.',
    onboarding_check_agent: 'Hermes Agent',
    onboarding_check_agent_ready: 'Detected and importable',
    onboarding_check_agent_missing: 'Missing or partially importable',
    onboarding_check_password: 'Password',
    onboarding_check_password_enabled: 'Already enabled',
    onboarding_check_password_disabled: 'Not enabled yet',
    onboarding_check_provider: 'Provider config',
    onboarding_check_provider_ready: 'Ready to chat',
    onboarding_check_provider_partial: 'Saved but incomplete',
    onboarding_check_provider_pending: 'Needs verification',
    onboarding_config_file: 'Config file:',
    onboarding_env_file: '.env file:',
    onboarding_unknown: 'Unknown',
    onboarding_current_provider: 'Current setup:',
    onboarding_missing_imports: 'Missing imports:',
    onboarding_notice_setup_required: 'Choose a simple provider path here. Advanced OAuth flows still belong in the Hermes CLI for now.',
    onboarding_notice_setup_already_ready: 'A working Hermes provider setup is already detected. You can keep it or replace it here.',
    onboarding_oauth_provider_ready_title: 'Provider already authenticated',
    onboarding_oauth_provider_ready_body: 'This instance is configured to use an OAuth provider (<strong>{provider}</strong>) that was set up via the Hermes CLI. No API key is needed here — click Continue to finish setup.',
    onboarding_oauth_provider_not_ready_title: 'OAuth provider not yet authenticated',
    onboarding_oauth_provider_not_ready_body: 'This instance is configured to use <strong>{provider}</strong>, which uses OAuth rather than an API key. Run <code>hermes auth</code> or <code>hermes model</code> in a terminal to authenticate, then reload the Web UI.',
    onboarding_oauth_switch_hint: 'Or choose a different provider below to switch to an API-key setup:',
    onboarding_notice_workspace: 'These values reuse the same settings APIs as the normal app.',
    onboarding_workspace_label: 'Workspace',
    onboarding_workspace_or_path: 'Or enter a workspace path',
    onboarding_workspace_placeholder: '/home/you/workspace',
    onboarding_provider_label: 'Setup mode',
    onboarding_quick_setup_badge: 'quick setup',
    onboarding_api_key_label: 'API key',
    onboarding_api_key_placeholder: 'Leave blank to keep an existing saved key',
    onboarding_api_key_help_prefix: 'Saved as a secret in your Hermes .env file using',
    onboarding_base_url_label: 'Base URL',
    onboarding_base_url_placeholder: 'https://your-endpoint.example/v1',
    onboarding_base_url_help: 'Use this for OpenAI-compatible routers, self-hosted servers, LiteLLM, Ollama, LM Studio, vLLM, or similar endpoints.',
    onboarding_model_label: 'Default model',
    onboarding_workspace_help: 'Pick the model Hermes should use for new chats after setup completes.',
    onboarding_custom_model_placeholder: 'your-model-name',
    onboarding_custom_model_help: 'For custom endpoints, enter the exact model ID your server expects.',
    onboarding_notice_password_enabled: 'A password is already configured. Enter a new one only if you want to replace it.',
    onboarding_notice_password_recommended: 'Optional but recommended if you will expose the UI beyond localhost.',
    onboarding_password_label: 'Password (optional)',
    onboarding_password_placeholder: 'Leave blank to skip',
    onboarding_password_help: 'Passwords are stored through the existing settings API and hashed server-side.',
    onboarding_notice_finish: 'You can reopen Settings later to change any of this.',
    onboarding_not_set: 'Not set',
    onboarding_password_will_enable: 'Will be enabled',
    onboarding_password_skipped: 'Skipped for now',
    onboarding_finish_help: 'Finishing stores <code>onboarding_completed</code> in settings and drops you into the normal app.',
    onboarding_error_choose_workspace: 'Choose a workspace before continuing.',
    onboarding_error_choose_model: 'Choose a model before continuing.',
    onboarding_error_provider_required: 'Choose a setup mode before continuing.',
    onboarding_error_base_url_required: 'Base URL is required for custom endpoints.',
    onboarding_error_workspace_required: 'Workspace is required.',
    onboarding_error_model_required: 'Model is required.',
    onboarding_complete: 'Onboarding complete',
  },

  es: {
    _lang: 'es',
    _label: 'Español',
    _speech: 'es-ES',
    // boot.js
    cancelling: 'Cancelando…',
    cancel_failed: 'Error al cancelar: ',
    mic_denied: 'Acceso al micrófono denegado. Revisa los permisos del navegador.',
    mic_no_speech: 'No se detectó voz. Inténtalo de nuevo.',
    mic_network: 'El reconocimiento de voz no está disponible.',
    mic_error: 'Error de entrada por voz: ',
    session_imported: 'Sesión importada',
    import_failed: 'Error al importar: ',
    import_invalid_json: 'JSON inválido',
    image_pasted: 'Imagen pegada: ',
    // messages.js
    edit_message: 'Editar mensaje',
    regenerate: 'Regenerar respuesta',
    copy: 'Copiar',
    copied: '¡Copiado!',
    you: 'Tú',
    thinking: 'Pensando',
    expand_all: 'Expandir todo',
    collapse_all: 'Contraer todo',
    edit_failed: 'Error al editar: ',
    regen_failed: 'Error al regenerar: ',
    reconnect_active: 'Todavía se está generando una respuesta. ¿Recargar cuando termine?',
    reconnect_finished: 'Había una respuesta en curso cuando te fuiste. Puede que los mensajes se hayan actualizado.',
    // approval card
    approval_heading: 'Se requiere aprobación',
    approval_desc_prefix: 'Se detectó un comando peligroso',
    approval_btn_once: 'Permitir una vez',
    approval_btn_once_title: 'Permitir solo este comando (Enter)',
    approval_btn_session: 'Permitir en la sesión',
    approval_btn_session_title: 'Permitir durante esta sesión de conversación',
    approval_btn_always: 'Permitir siempre',
    approval_btn_always_title: 'Permitir siempre este patrón de comando',
    approval_btn_deny: 'Denegar',
    approval_btn_deny_title: 'Denegar — no ejecutar este comando',
    approval_responding: 'Respondiendo…',
    untitled: 'Sin título',
    n_messages: (n) => `${n} mensajes`,
    model_unavailable: ' (no disponible)',
    model_unavailable_title: 'Este modelo ya no está en tu lista actual de proveedores',
    provider_mismatch_warning: (m,p)=>`"${m}" puede no funcionar con tu proveedor configurado (${p}). Envía de todas formas, o ejecuta \`hermes model\` en la terminal para cambiar.`,
    provider_mismatch_label: 'Proveedor incompatible',
    // commands.js
    cmd_help: 'Listar los comandos disponibles',
    cmd_clear: 'Borrar los mensajes de la conversación',
    cmd_compact: 'Comprimir el contexto de la conversación',
    cmd_model: 'Cambiar de modelo (p. ej. /model gpt-4o)',
    cmd_workspace: 'Cambiar de espacio de trabajo por nombre',
    cmd_new: 'Iniciar una nueva sesión de chat',
    cmd_usage: 'Activar o desactivar el uso de tokens',
    cmd_theme: 'Cambiar tema (dark/light/slate/solarized/monokai/nord/oled)',
    cmd_personality: 'Cambiar la personalidad del agente',
    cmd_skills: 'Listar las skills de Hermes disponibles',
    available_commands: 'Comandos disponibles:',
    type_slash: 'Escribe / para ver los comandos',
    conversation_cleared: 'Conversación borrada',
    model_usage: 'Uso: /model <name>',
    no_model_match: 'No hay ningún modelo que coincida con "',
    switched_to: 'Se cambió a ',
    workspace_usage: 'Uso: /workspace <name>',
    no_workspace_match: 'No hay ningún espacio de trabajo que coincida con "',
    switched_workspace: 'Se cambió al espacio de trabajo: ',
    workspace_switch_failed: 'Error al cambiar de espacio de trabajo: ',
    new_session: 'Nueva sesión creada',
    compressing: 'Solicitando compresión del contexto...',
    token_usage_on: 'Uso de tokens activado',
    token_usage_off: 'Uso de tokens desactivado',
    theme_usage: 'Uso: /theme ',
    theme_set: 'Tema: ',
    no_active_session: 'No hay ninguna sesión activa',
    no_personalities: 'No se encontraron personalidades (añádelas a ~/.hermes/personalities/)',
    available_personalities: 'Personalidades disponibles:',
    personality_switch_hint: '\n\nUsa `/personality <name>` para cambiar, o `/personality none` para limpiar.',
    personalities_load_failed: 'No se pudieron cargar las personalidades',
    personality_cleared: 'Personalidad borrada',
    personality_set: 'Personalidad: ',
    failed_colon: 'Error: ',
    // ui.js
    no_workspace: 'Sin espacio de trabajo',
    // workspace.js
    unsaved_confirm: 'Tienes cambios sin guardar en la vista previa. ¿Descartar y navegar?',
    save: 'Guardar',
    edit: 'Editar',
    save_title: 'Guardar cambios',
    edit_title: 'Editar este archivo',
    saved: 'Guardado',
    save_failed: 'Error al guardar: ',
    image_load_failed: 'No se pudo cargar la imagen',
    file_open_failed: 'No se pudo abrir el archivo',
    downloading: (name) => `Descargando ${name}…`,
    double_click_rename: 'Haz doble clic para renombrar',
    renamed_to: 'Renombrado a ',
    rename_failed: 'Error al renombrar: ',
    delete_title: 'Eliminar',
    delete_confirm: (name) => `¿Eliminar ${name}?`,
    deleted: 'Eliminado ',
    delete_failed: 'Error al eliminar: ',
    new_file_prompt: 'Nombre del archivo nuevo (p. ej. notes.md):',
    created: 'Creado ',
    create_failed: 'Error al crear: ',
    new_folder_prompt: 'Nombre de la carpeta nueva:',
    folder_created: 'Carpeta creada ',
    folder_create_failed: 'Error al crear la carpeta: ',
    remove_title: 'Quitar',
    empty_dir: '(vacío)',
    upload_failed: 'Error al subir: ',
    all_uploads_failed: (n) => `Fallaron las ${n} subida(s)`,
    // settings panel
    settings_title: 'Configuración',
    settings_save_btn: 'Guardar configuración',
    settings_label_model: 'Modelo predeterminado',
    settings_label_send_key: 'Tecla de envío',
    settings_label_theme: 'Tema',
    settings_label_language: 'Idioma',
    settings_label_token_usage: 'Mostrar uso de tokens',
    settings_label_bubble_layout: 'Disposición en burbujas',
    settings_label_cli_sessions: 'Mostrar sesiones de CLI',
    settings_label_sync_insights: 'Sincronizar con insights',
    settings_label_check_updates: 'Buscar actualizaciones',
    settings_label_bot_name: 'Nombre del asistente',
    settings_label_password: 'Contraseña de acceso',
    settings_saved: 'Configuración guardada',
    settings_save_failed: 'Error al guardar: ',
    settings_load_failed: 'Error al cargar la configuración: ',
    settings_saved_pw: 'Configuración guardada (contraseña establecida — ahora se requiere iniciar sesión)',
    // login page (used server-side via /api/i18n/login endpoint)
    login_title: 'Iniciar sesión',
    login_subtitle: 'Introduce tu contraseña para continuar',
    login_placeholder: 'Contraseña',
    login_btn: 'Entrar',
    login_invalid_pw: 'Contraseña inválida',
    login_conn_failed: 'Error de conexión',
    dialog_confirm_title: 'Confirmar acción',
    dialog_prompt_title: 'Introduce un valor',
    dialog_confirm_btn: 'Confirmar',
    discard: 'Descartar',
    clear: 'Borrar',
    create: 'Crear',
    remove: 'Quitar',
    project_name_prompt: 'Nombre del proyecto:',
    // Sidebar & Tabs
    tab_chat: 'Chat',
    tab_tasks: 'Tareas',
    tab_skills: 'Habilidades',
    tab_memory: 'Memoria',
    tab_workspaces: 'Espacios',
    tab_profiles: 'Perfiles',
    tab_todos: 'Todos',
    new_conversation: 'Nueva conversación',
    filter_conversations: 'Filtrar conversaciones...',
    scheduled_jobs: 'Tareas programadas',
    new_job: 'Nueva tarea',
    loading: 'Cargando...',
    search_skills: 'Buscar skills...',
    new_skill: 'Nueva skill',
    personal_memory: 'Memoria personal',
    current_task_list: 'Lista de tareas actual',
    workspace_desc: 'Añade y cambia espacios de trabajo para tus sesiones.',
    new_profile: 'Nuevo perfil',
    transcript: 'Transcripción',
    download_transcript: 'Descargar como Markdown',
    import: 'Importar',
    // Settings detail
    settings_label_sound: 'Sonido de notificación',
    settings_desc_sound: 'Reproduce un sonido cuando el asistente termina una respuesta.',
    settings_label_notifications: 'Notificaciones del navegador',
    settings_desc_notifications: 'Muestra una notificación del sistema cuando una respuesta termina mientras la pestaña está en segundo plano.',
    settings_desc_token_usage: 'Muestra el conteo de tokens de entrada/salida debajo de cada respuesta del asistente. También se puede alternar con /usage.',
    settings_desc_bubble_layout: 'Alinea los mensajes del usuario a la derecha y las respuestas del asistente a la izquierda. Desactivado por defecto para mantener los bloques de código y la salida de herramientas a ancho completo.',
    settings_desc_cli_sessions: 'Fusiona las sesiones del CLI de Hermes (state.db) en la lista de sesiones. Haz clic en una sesión de CLI para importarla y continuar la conversación.',
    settings_desc_sync_insights: 'Refleja el uso de tokens de la WebUI en state.db para que hermes /insights incluya datos de sesiones del navegador. Desactivado por defecto.',
    settings_desc_check_updates: 'Muestra un banner cuando haya versiones más nuevas de la WebUI o del Agent. Ejecuta periódicamente un git fetch en segundo plano.',
    settings_desc_bot_name: 'Nombre visible del asistente en toda la UI. Por defecto es Hermes.',
    settings_desc_password: 'Introduce una nueva contraseña para establecerla o cambiarla. Déjalo en blanco para mantener la configuración actual.',
    password_placeholder: 'Introduce una contraseña nueva…',
    disable_auth: 'Desactivar autenticación',
    sign_out: 'Cerrar sesión',
    cancel: 'Cancelar',
    create_job: 'Crear tarea',
    save_skill: 'Guardar skill',
    editing: 'Editando',
    // Empty state
    empty_title: '¿En qué puedo ayudarte?',
    empty_subtitle: 'Pregunta lo que quieras, ejecuta comandos, explora archivos o gestiona tus tareas programadas.',
    suggest_files: '¿Qué archivos hay en este espacio de trabajo?',
    suggest_schedule: '¿Qué tengo hoy en mi agenda?',
    suggest_plan: 'Ayúdame a planificar un proyecto pequeño.',
    // onboarding
    onboarding_badge: 'PRIMER USO',
    onboarding_title: 'Bienvenido a Hermes Web UI',
    onboarding_lead: 'Una guía rápida verificará Hermes, guardará una configuración real del proveedor, elegirá un espacio de trabajo y un modelo, y opcionalmente protegerá la app con una contraseña.',
    onboarding_back: 'Atrás',
    onboarding_continue: 'Continuar',
    onboarding_open: 'Abrir Hermes',
    onboarding_step_system_title: 'Comprobación del sistema',
    onboarding_step_system_desc: 'Verifica Hermes Agent y la visibilidad de la configuración.',
    onboarding_step_setup_title: 'Configuración del proveedor',
    onboarding_step_setup_desc: 'Guarda la configuración mínima real de Hermes.',
    onboarding_step_workspace_title: 'Espacio de trabajo + modelo',
    onboarding_step_workspace_desc: 'Elige los valores predeterminados para nuevas sesiones y chats.',
    onboarding_step_password_title: 'Contraseña opcional',
    onboarding_step_password_desc: 'Protege la Web UI antes de compartirla.',
    onboarding_step_finish_title: 'Finalizar',
    onboarding_step_finish_desc: 'Revisa todo y entra en la app.',
    onboarding_notice_system_ready: 'Parece que Hermes Agent está accesible desde la Web UI.',
    onboarding_notice_system_unavailable: 'Hermes Agent todavía no está totalmente disponible. Bootstrap puede instalarlo, pero la configuración del proveedor quizá aún requiera una terminal.',
    onboarding_check_agent: 'Hermes Agent',
    onboarding_check_agent_ready: 'Detectado e importable',
    onboarding_check_agent_missing: 'Falta o solo es parcialmente importable',
    onboarding_check_password: 'Contraseña',
    onboarding_check_password_enabled: 'Ya está activada',
    onboarding_check_password_disabled: 'Todavía no está activada',
    onboarding_check_provider: 'Configuración del proveedor',
    onboarding_check_provider_ready: 'Listo para chatear',
    onboarding_check_provider_partial: 'Guardado pero incompleto',
    onboarding_check_provider_pending: 'Necesita verificación',
    onboarding_config_file: 'Archivo de configuración:',
    onboarding_env_file: 'Archivo .env:',
    onboarding_unknown: 'Desconocido',
    onboarding_current_provider: 'Configuración actual:',
    onboarding_missing_imports: 'Importaciones faltantes:',
    onboarding_notice_setup_required: 'Elige aquí una ruta simple de proveedor. Los flujos OAuth avanzados siguen siendo del CLI de Hermes por ahora.',
    onboarding_notice_setup_already_ready: 'Ya se detectó una configuración funcional del proveedor de Hermes. Puedes conservarla o reemplazarla aquí.',
    onboarding_oauth_provider_ready_title: 'Proveedor ya autenticado',
    onboarding_oauth_provider_ready_body: 'Esta instancia está configurada para usar un proveedor OAuth (<strong>{provider}</strong>) configurado mediante la CLI de Hermes. No se necesita clave API aquí — haz clic en Continuar para finalizar la configuración.',
    onboarding_oauth_provider_not_ready_title: 'Proveedor OAuth no autenticado aún',
    onboarding_oauth_provider_not_ready_body: 'Esta instancia está configurada para usar <strong>{provider}</strong>, que utiliza OAuth en lugar de una clave API. Ejecuta <code>hermes auth</code> o <code>hermes model</code> en una terminal para autenticarte y recarga la interfaz web.',
    onboarding_oauth_switch_hint: 'O elige un proveedor diferente a continuación para cambiar a la configuración con clave API:',
    onboarding_notice_workspace: 'Estos valores reutilizan las mismas APIs de configuración que la app normal.',
    onboarding_workspace_label: 'Espacio de trabajo',
    onboarding_workspace_or_path: 'O introduce la ruta de un espacio de trabajo',
    onboarding_workspace_placeholder: '/home/you/workspace',
    onboarding_provider_label: 'Modo de configuración',
    onboarding_quick_setup_badge: 'configuración rápida',
    onboarding_api_key_label: 'API key',
    onboarding_api_key_placeholder: 'Déjala en blanco para conservar una key ya guardada',
    onboarding_api_key_help_prefix: 'Se guarda como secreto en tu archivo .env de Hermes usando',
    onboarding_base_url_label: 'Base URL',
    onboarding_base_url_placeholder: 'https://tu-endpoint.example/v1',
    onboarding_base_url_help: 'Úsalo para routers OpenAI-compatible, servidores autoalojados, LiteLLM, Ollama, LM Studio, vLLM o endpoints parecidos.',
    onboarding_model_label: 'Modelo predeterminado',
    onboarding_workspace_help: 'Elige el modelo que Hermes debe usar para nuevos chats cuando termine la configuración.',
    onboarding_custom_model_placeholder: 'tu-modelo',
    onboarding_custom_model_help: 'Para endpoints personalizados, introduce el identificador exacto del modelo que espera tu servidor.',
    onboarding_notice_password_enabled: 'Ya hay una contraseña configurada. Introduce una nueva solo si quieres reemplazarla.',
    onboarding_notice_password_recommended: 'Es opcional, pero recomendable si vas a exponer la UI más allá de localhost.',
    onboarding_password_label: 'Contraseña (opcional)',
    onboarding_password_placeholder: 'Déjala en blanco para omitirla',
    onboarding_password_help: 'Las contraseñas se guardan mediante la API de configuración existente y se hashean en el servidor.',
    onboarding_notice_finish: 'Puedes volver a abrir Configuración más tarde para cambiar cualquiera de estos valores.',
    onboarding_not_set: 'Sin definir',
    onboarding_password_will_enable: 'Se activará',
    onboarding_password_skipped: 'Se omitirá por ahora',
    onboarding_finish_help: 'Al finalizar se guarda <code>onboarding_completed</code> en la configuración y entras en la app normal.',
    onboarding_error_choose_workspace: 'Elige un espacio de trabajo antes de continuar.',
    onboarding_error_choose_model: 'Elige un modelo antes de continuar.',
    onboarding_error_provider_required: 'Elige un modo de configuración antes de continuar.',
    onboarding_error_base_url_required: 'La base URL es obligatoria para endpoints personalizados.',
    onboarding_error_workspace_required: 'El espacio de trabajo es obligatorio.',
    onboarding_error_model_required: 'El modelo es obligatorio.',
    onboarding_complete: 'Onboarding completado',
  },

  de: {
    _lang: 'de',
    _label: 'Deutsch',
    _speech: 'de-DE',
    // boot.js
    cancelling: 'Wird abgebrochen\u2026',
    cancel_failed: 'Abbrechen fehlgeschlagen: ',
    mic_denied: 'Mikrofonzugriff verweigert. Überprüfen Sie die Browserberechtigungen.',
    mic_no_speech: 'Keine Sprache erkannt. Versuchen Sie es erneut.',
    mic_network: 'Spracherkennung nicht verfügbar.',
    mic_error: 'Spracheingabefehler: ',
    session_imported: 'Sitzung importiert',
    import_failed: 'Import fehlgeschlagen: ',
    import_invalid_json: 'Ungültiges JSON',
    image_pasted: 'Bild eingefügt: ',
    // messages.js
    edit_message: 'Nachricht bearbeiten',
    regenerate: 'Antwort regenerieren',
    copy: 'Kopieren',
    copied: 'Kopiert!',
    you: 'Du',
    thinking: 'Nachdenken',
    expand_all: 'Alle ausklappen',
    collapse_all: 'Alle einklappen',
    edit_failed: 'Bearbeiten fehlgeschlagen: ',
    regen_failed: 'Regeneration fehlgeschlagen: ',
    reconnect_active: 'Eine Antwort wird noch generiert. Neu laden, wenn bereit?',
    reconnect_finished: 'Eine Antwort war in Arbeit, als Sie zuletzt gegangen sind. Nachrichten könnten aktualisiert worden sein.',
    // approval card
    approval_heading: 'Genehmigung erforderlich',
    approval_desc_prefix: 'Gefährlicher Befehl erkannt',
    approval_btn_once: 'Einmal zulassen',
    approval_btn_once_title: 'Diesen einen Befehl zulassen (Enter)',
    approval_btn_session: 'Sitzung zulassen',
    approval_btn_session_title: 'Für diese Konversationssitzung zulassen',
    approval_btn_always: 'Immer zulassen',
    approval_btn_always_title: 'Dieses Befehlsmuster immer zulassen',
    approval_btn_deny: 'Ablehnen',
    approval_btn_deny_title: 'Ablehnen \u2014 diesen Befehl nicht ausführen',
    approval_responding: 'Antwortet\u2026',
    untitled: 'Unbenannt',
    n_messages: (n) => `${n} Nachrichten`,
    model_unavailable: ' (nicht verfügbar)',
    model_unavailable_title: 'Dieses Modell ist nicht mehr in Ihrer aktuellen Provider-Liste',
    provider_mismatch_warning: (m,p)=>`"${m}" funktioniert möglicherweise nicht mit Ihrem konfigurierten Provider (${p}). Trotzdem senden, oder \`hermes model\` im Terminal ausführen.`,
    provider_mismatch_label: 'Provider-Konflikt',
    // commands.js
    cmd_help: 'Verfügbare Befehle auflisten',
    cmd_clear: 'Konversationsverlauf löschen',
    cmd_compact: 'Kontext komprimieren',
    cmd_model: 'Modell wechseln (z.B. /model gpt-4o)',
    cmd_workspace: 'Workspace nach Namen wechseln',
    cmd_new: 'Neue Chat-Sitzung starten',
    cmd_usage: 'Token-Verbrauchsanzeige umschalten',
    cmd_theme: 'Theme wechseln (dark/light/slate/solarized/monokai/nord/oled)',
    cmd_personality: 'Agenten-Persönlichkeit wechseln',
    cmd_skills: 'Verfügbare Hermes-Skills auflisten',
    available_commands: 'Verfügbare Befehle:',
    type_slash: 'Tippe / für Befehle',
    conversation_cleared: 'Konversation gelöscht',
    model_usage: 'Nutzung: /model <name>',
    no_model_match: 'Kein Modell gefunden für "',
    switched_to: 'Gewechselt zu ',
    workspace_usage: 'Nutzung: /workspace <name>',
    no_workspace_match: 'Kein Workspace gefunden für "',
    switched_workspace: 'Gewechselt zu Workspace: ',
    workspace_switch_failed: 'Workspace-Wechsel fehlgeschlagen: ',
    new_session: 'Neue Sitzung erstellt',
    compressing: 'Kontext-Komprimierung wird angefordert...',
    token_usage_on: 'Token-Verbrauch an',
    token_usage_off: 'Token-Verbrauch aus',
    theme_usage: 'Nutzung: /theme ',
    theme_set: 'Theme: ',
    no_active_session: 'Keine aktive Sitzung',
    no_personalities: 'Keine Persönlichkeiten gefunden (füge sie in ~/.hermes/personalities/ hinzu)',
    available_personalities: 'Verfügbare Persönlichkeiten:',
    personality_switch_hint: '\n\nNutze `/personality <name>` zum Wechseln, oder `/personality none` zum Löschen.',
    personalities_load_failed: 'Fehler beim Laden der Persönlichkeiten',
    personality_cleared: 'Persönlichkeit gelöscht',
    personality_set: 'Persönlichkeit: ',
    failed_colon: 'Fehlgeschlagen: ',
    // ui.js
    no_workspace: 'Kein Workspace',
    dialog_confirm_title: 'Aktion bestätigen',
    dialog_prompt_title: 'Wert eingeben',
    dialog_confirm_btn: 'Bestätigen',
    // workspace.js
    unsaved_confirm: 'Sie haben ungespeicherte Änderungen in der Vorschau. Verwerfen und fortfahren?',
    discard: 'Verwerfen',
    save: 'Speichern',
    edit: 'Bearbeiten',
    clear: 'Leeren',
    create: 'Erstellen',
    remove: 'Entfernen',
    save_title: 'Änderungen speichern',
    edit_title: 'Diese Datei bearbeiten',
    saved: 'Gespeichert',
    save_failed: 'Speichern fehlgeschlagen: ',
    image_load_failed: 'Bild konnte nicht geladen werden',
    file_open_failed: 'Datei konnte nicht geöffnet werden',
    downloading: (name) => `Lade ${name} herunter\u2026`,
    double_click_rename: 'Doppelklick zum Umbenennen',
    renamed_to: 'Umbenannt in ',
    rename_failed: 'Umbenennen fehlgeschlagen: ',
    delete_title: 'Löschen',
    delete_confirm: (name) => `${name} löschen?`,
    deleted: 'Gelöscht ',
    delete_failed: 'Löschen fehlgeschlagen: ',
    new_file_prompt: 'Neuer Dateiname (z.B. notes.md):',
    project_name_prompt: 'Projektname:',
    created: 'Erstellt ',
    create_failed: 'Erstellen fehlgeschlagen: ',
    new_folder_prompt: 'Neuer Ordnername:',
    folder_created: 'Ordner erstellt ',
    folder_create_failed: 'Ordner erstellen fehlgeschlagen: ',
    remove_title: 'Entfernen',
    empty_dir: '(leer)',
    upload_failed: 'Upload fehlgeschlagen: ',
    all_uploads_failed: (n) => `Alle ${n} Upload(s) fehlgeschlagen`,
    // settings panel
    settings_title: 'Einstellungen',
    settings_save_btn: 'Einstellungen speichern',
    settings_label_model: 'Standard-Modell',
    settings_label_send_key: 'Sende-Taste',
    settings_label_theme: 'Theme',
    settings_label_language: 'Sprache',
    settings_label_token_usage: 'Token-Verbrauch anzeigen',
    settings_label_cli_sessions: 'Agent-Sitzungen anzeigen',
    settings_label_sync_insights: 'Mit Insights synchronisieren',
    settings_label_check_updates: 'Nach Updates suchen',
    settings_label_bot_name: 'Assistenten-Name',
    settings_label_password: 'Zugangspasswort',
    settings_saved: 'Einstellungen gespeichert',
    settings_save_failed: 'Speichern fehlgeschlagen: ',
    settings_load_failed: 'Laden der Einstellungen fehlgeschlagen: ',
    settings_saved_pw: 'Einstellungen gespeichert (Passwort gesetzt \u2014 Login jetzt erforderlich)',
    // login page
    login_title: 'Anmelden',
    login_subtitle: 'Geben Sie Ihr Passwort ein, um fortzufahren',
    login_placeholder: 'Passwort',
    login_btn: 'Anmelden',
    login_invalid_pw: 'Ungültiges Passwort',
    login_conn_failed: 'Verbindung fehlgeschlagen',
    dialog_confirm_title: 'Aktion bestätigen',
    dialog_prompt_title: 'Wert eingeben',
    dialog_confirm_btn: 'Bestätigen',
    discard: 'Verwerfen',
    clear: 'Leeren',
    create: 'Erstellen',
    remove: 'Entfernen',
    project_name_prompt: 'Projektname:',
    // Sidebar & Tabs
    tab_chat: 'Chat',
    tab_tasks: 'Aufgaben',
    tab_skills: 'Skills',
    tab_memory: 'Gedächtnis',
    tab_workspaces: 'Spaces',
    tab_profiles: 'Profile',
    tab_todos: 'Todos',
    new_conversation: 'Neuer Chat',
    filter_conversations: 'Chats filtern...',
    scheduled_jobs: 'Geplante Aufgaben',
    new_job: 'Neuer Job',
    loading: 'Lädt...',
    search_skills: 'Skills suchen...',
    new_skill: 'Neuer Skill',
    personal_memory: 'Persönliches Gedächtnis',
    current_task_list: 'Aktuelle Aufgabenliste',
    workspace_desc: 'Workspaces hinzufügen und wechseln.',
    new_profile: 'Neues Profil',
    transcript: 'Protokoll',
    download_transcript: 'Als Markdown herunterladen',
    import: 'Importieren',
    // Settings detail
    settings_label_sound: 'Benachrichtigungston',
    settings_desc_sound: 'Spielt einen Ton ab, wenn der Assistent eine Antwort beendet.',
    settings_label_notifications: 'Browser-Benachrichtigungen',
    settings_desc_notifications: 'Zeigt eine Systembenachrichtigung an, wenn eine Antwort fertiggestellt wird, während der Tab im Hintergrund ist.',
    settings_desc_token_usage: 'Zeigt die Anzahl der Input/Output-Token unter jeder Antwort des Assistenten an. Auch umschaltbar mit /usage.',
    settings_desc_cli_sessions: 'Fügt Sitzungen aus der Hermes CLI (state.db) in die Sitzungsliste ein. Klicken Sie auf eine CLI-Sitzung, um sie zu importieren und das Gespräch fortzusetzen.',
    settings_desc_sync_insights: 'Spiegelt den WebUI-Token-Verbrauch in die state.db, sodass hermes /insights Browser-Sitzungsdaten enthält. Standardmäßig aus.',
    settings_desc_check_updates: 'Zeigt ein Banner an, wenn neuere Versionen der WebUI oder des Agenten verfügbar sind. Führt regelmäßig einen Git-Fetch im Hintergrund aus.',
    settings_desc_bot_name: 'Anzeigename für den Assistenten in der UI. Standardmäßig Hermes.',
    settings_desc_password: 'Geben Sie ein neues Passwort ein, um es zu setzen oder zu ändern. Leer lassen, um die aktuelle Einstellung beizubehalten.',
    password_placeholder: 'Neues Passwort eingeben…',
    disable_auth: 'Authentifizierung deaktivieren',
    sign_out: 'Abmelden',
    cancel: 'Abbrechen',
    create_job: 'Job erstellen',
    save_skill: 'Skill speichern',
    editing: 'Bearbeitung',
    // Empty state
    empty_title: 'Wie kann ich helfen?',
    empty_subtitle: 'Frage mich alles, führe Befehle aus, erkunde Dateien oder verwalte deine Aufgaben.',
    suggest_files: 'Welche Dateien sind in diesem Workspace?',
    suggest_schedule: 'Was steht heute auf meinem Plan?',
    suggest_plan: 'Hilf mir, ein kleines Projekt zu planen.',
  },

  zh: {
    _lang: 'zh',
    _label: '\u7b80\u4f53\u4e2d\u6587',
    _speech: 'zh-CN',
    // boot.js
    cancelling: '\u6b63\u5728\u53d6\u6d88...',
    cancel_failed: '\u53d6\u6d88\u5931\u8d25\uff1a',
    mic_denied: '\u9ea6\u514b\u98ce\u8bbf\u95ee\u88ab\u62d2\u7edd\uff0c\u8bf7\u68c0\u67e5\u6d4f\u89c8\u5668\u6743\u9650\u3002',
    mic_no_speech: '\u6ca1\u6709\u68c0\u6d4b\u5230\u8bed\u97f3\uff0c\u8bf7\u518d\u8bd5\u4e00\u6b21\u3002',
    mic_network: '\u8bed\u97f3\u8bc6\u522b\u5f53\u524d\u4e0d\u53ef\u7528\u3002',
    mic_error: '\u8bed\u97f3\u8f93\u5165\u51fa\u9519\uff1a',
    session_imported: '\u4f1a\u8bdd\u5df2\u5bfc\u5165',
    import_failed: '\u5bfc\u5165\u5931\u8d25\uff1a',
    import_invalid_json: 'JSON \u65e0\u6548',
    image_pasted: '\u5df2\u7c98\u8d34\u56fe\u7247\uff1a',
    // messages.js
    edit_message: '\u7f16\u8f91\u6d88\u606f',
    regenerate: '\u91cd\u65b0\u751f\u6210\u56de\u590d',
    copy: '\u590d\u5236',
    copied: '\u5df2\u590d\u5236',
    you: '\u4f60',
    thinking: '\u601d\u8003\u8fc7\u7a0b',
    expand_all: '\u5168\u90e8\u5c55\u5f00',
    collapse_all: '\u5168\u90e8\u6298\u53e0',
    edit_failed: '\u7f16\u8f91\u5931\u8d25\uff1a',
    regen_failed: '\u91cd\u65b0\u751f\u6210\u5931\u8d25\uff1a',
    reconnect_active: '\u56de\u590d\u4ecd\u5728\u751f\u6210\u4e2d\uff0c\u51c6\u5907\u597d\u540e\u8981\u91cd\u65b0\u52a0\u8f7d\u5417\uff1f',
    reconnect_finished: '\u4f60\u79bb\u5f00\u65f6\u6709\u56de\u590d\u6b63\u5728\u751f\u6210\uff0c\u6d88\u606f\u5185\u5bb9\u53ef\u80fd\u5df2\u7ecf\u66f4\u65b0\u3002',
    // approval card
    approval_heading: '需要审批',
    approval_desc_prefix: '检测到危险命令',
    approval_btn_once: '允许一次',
    approval_btn_once_title: '允许执行此命令一次（Enter）',
    approval_btn_session: '本次允许',
    approval_btn_session_title: '本次会话期间允许',
    approval_btn_always: '始终允许',
    approval_btn_always_title: '始终允许此命令模式',
    approval_btn_deny: '拒绝',
    approval_btn_deny_title: '拒绝 — 不执行此命令',
    approval_responding: '处理中…',
    untitled: '\u672a\u547d\u540d',
    n_messages: (n) => `${n} \u6761\u6d88\u606f`,
    model_unavailable: '\uff08\u4e0d\u53ef\u7528\uff09',
    model_unavailable_title: '\u8fd9\u4e2a\u6a21\u578b\u5df2\u7ecf\u4e0d\u5728\u5f53\u524d provider \u5217\u8868\u4e2d',
    provider_mismatch_warning: (m,p)=>`\"${m}\" \u53ef\u80fd\u65e0\u6cd5\u5728\u5f53\u524d\u914d\u7f6e\u7684\u63d0\u4f9b\u5546 (${p}) \u4e0b\u5de5\u4f5c\u3002\u76f4\u63a5\u53d1\u9001\uff0c\u6216\u5728\u7ec8\u7aef\u8fd0\u884c \`hermes model\` \u5207\u6362\u3002`,
    provider_mismatch_label: '\u63d0\u4f9b\u5546\u4e0d\u5339\u914d',
    // commands.js
    cmd_help: '\u67e5\u770b\u53ef\u7528\u547d\u4ee4',
    cmd_clear: '\u6e05\u7a7a\u5f53\u524d\u5bf9\u8bdd\u6d88\u606f',
    cmd_compact: '\u538b\u7f29\u5bf9\u8bdd\u4e0a\u4e0b\u6587',
    cmd_model: '\u5207\u6362\u6a21\u578b\uff08\u4f8b\u5982 /model gpt-4o\uff09',
    cmd_workspace: '\u6309\u540d\u79f0\u5207\u6362\u5de5\u4f5c\u533a',
    cmd_new: '\u65b0\u5efa\u804a\u5929\u4f1a\u8bdd',
    cmd_usage: '\u5207\u6362 token \u7528\u91cf\u663e\u793a',
    cmd_theme: '\u5207\u6362\u4e3b\u9898\uff08dark/light/slate/solarized/monokai/nord/oled\uff09',
    cmd_personality: '\u5207\u6362 Agent \u4eba\u8bbe',
    cmd_skills: '\u5217\u51fa\u53ef\u7528\u7684 Hermes \u6280\u80fd',
    available_commands: '\u53ef\u7528\u547d\u4ee4\uff1a',
    type_slash: '\u8f93\u5165 / \u53ef\u67e5\u770b\u547d\u4ee4',
    conversation_cleared: '\u5bf9\u8bdd\u5df2\u6e05\u7a7a',
    model_usage: '\u7528\u6cd5\uff1a/model <name>',
    no_model_match: '\u6ca1\u6709\u5339\u914d\u201c',
    switched_to: '\u5df2\u5207\u6362\u5230 ',
    workspace_usage: '\u7528\u6cd5\uff1a/workspace <name>',
    no_workspace_match: '\u6ca1\u6709\u5339\u914d\u201c',
    switched_workspace: '\u5df2\u5207\u6362\u5de5\u4f5c\u533a\uff1a',
    workspace_switch_failed: '\u5de5\u4f5c\u533a\u5207\u6362\u5931\u8d25\uff1a',
    new_session: '\u5df2\u65b0\u5efa\u4f1a\u8bdd',
    compressing: '\u6b63\u5728\u8bf7\u6c42\u538b\u7f29\u4e0a\u4e0b\u6587...',
    token_usage_on: 'Token \u7528\u91cf\u663e\u793a\u5df2\u5f00\u542f',
    token_usage_off: 'Token \u7528\u91cf\u663e\u793a\u5df2\u5173\u95ed',
    theme_usage: '\u7528\u6cd5\uff1a/theme ',
    theme_set: '\u4e3b\u9898\uff1a',
    no_active_session: '\u5f53\u524d\u6ca1\u6709\u6d3b\u52a8\u4f1a\u8bdd',
    no_personalities: '\u6ca1\u6709\u627e\u5230\u4eba\u8bbe\uff08\u53ef\u6dfb\u52a0\u5230 ~/.hermes/personalities/\uff09',
    available_personalities: '\u53ef\u7528\u4eba\u8bbe\uff1a',
    personality_switch_hint: '\n\n\u4f7f\u7528 `/personality <name>` \u5207\u6362\uff0c\u6216\u7528 `/personality none` \u6e05\u7a7a\u3002',
    personalities_load_failed: '\u52a0\u8f7d\u4eba\u8bbe\u5931\u8d25',
    personality_cleared: '\u4eba\u8bbe\u5df2\u6e05\u7a7a',
    personality_set: '\u5f53\u524d\u4eba\u8bbe\uff1a',
    failed_colon: '\u5931\u8d25\uff1a',
    // ui.js
    no_workspace: '\u672a\u9009\u62e9\u5de5\u4f5c\u533a',
    dialog_confirm_title: '\u786e\u8ba4\u64cd\u4f5c',
    dialog_prompt_title: '\u8f93\u5165\u5185\u5bb9',
    dialog_confirm_btn: '\u786e\u8ba4',
    // workspace.js
    unsaved_confirm: '\u9884\u89c8\u533a\u6709\u672a\u4fdd\u5b58\u4fee\u6539\uff0c\u8981\u653e\u5f03\u66f4\u6539\u5e76\u7ee7\u7eed\u8df3\u8f6c\u5417\uff1f',
    discard: '\u653e\u5f03',
    save: '\u4fdd\u5b58',
    edit: '\u7f16\u8f91',
    clear: '\u6e05\u7a7a',
    create: '\u521b\u5efa',
    remove: '\u79fb\u9664',
    save_title: '\u4fdd\u5b58\u4fee\u6539',
    edit_title: '\u7f16\u8f91\u6b64\u6587\u4ef6',
    saved: '\u5df2\u4fdd\u5b58',
    save_failed: '\u4fdd\u5b58\u5931\u8d25\uff1a',
    image_load_failed: '\u56fe\u7247\u52a0\u8f7d\u5931\u8d25',
    file_open_failed: '\u65e0\u6cd5\u6253\u5f00\u6587\u4ef6',
    downloading: (name) => `\u6b63\u5728\u4e0b\u8f7d ${name}...`,
    double_click_rename: '\u53cc\u51fb\u91cd\u547d\u540d',
    renamed_to: '\u5df2\u91cd\u547d\u540d\u4e3a ',
    rename_failed: '\u91cd\u547d\u540d\u5931\u8d25\uff1a',
    delete_title: '\u5220\u9664',
    delete_confirm: (name) => `\u8981\u5220\u9664 ${name} \u5417\uff1f`,
    deleted: '\u5df2\u5220\u9664 ',
    delete_failed: '\u5220\u9664\u5931\u8d25\uff1a',
    new_file_prompt: '\u65b0\u6587\u4ef6\u540d\uff08\u4f8b\u5982 notes.md\uff09\uff1a',
    project_name_prompt: '\u9879\u76ee\u540d\u79f0\uff1a',
    created: '\u5df2\u521b\u5efa ',
    create_failed: '\u521b\u5efa\u5931\u8d25\uff1a',
    new_folder_prompt: '\u65b0\u6587\u4ef6\u5939\u540d\u79f0\uff1a',
    folder_created: '\u5df2\u521b\u5efa\u6587\u4ef6\u5939 ',
    folder_create_failed: '\u521b\u5efa\u6587\u4ef6\u5939\u5931\u8d25\uff1a',
    remove_title: '\u79fb\u9664',
    empty_dir: '(\u7a7a)',
    upload_failed: '\u4e0a\u4f20\u5931\u8d25\uff1a',
    all_uploads_failed: (n) => `${n} \u4e2a\u6587\u4ef6\u5168\u90e8\u4e0a\u4f20\u5931\u8d25`,
    // settings panel
    settings_title: '\u8bbe\u7f6e',
    settings_save_btn: '\u4fdd\u5b58\u8bbe\u7f6e',
    settings_label_model: '\u9ed8\u8ba4\u6a21\u578b',
    settings_label_send_key: '\u53d1\u9001\u5feb\u6377\u952e',
    settings_label_theme: '\u4e3b\u9898',
    settings_label_language: '\u8bed\u8a00',
    settings_label_token_usage: '\u663e\u793a token \u7528\u91cf',
    settings_label_cli_sessions: '\u663e\u793a CLI \u4f1a\u8bdd',
    settings_label_sync_insights: '\u540c\u6b65\u5230 insights',
    settings_label_check_updates: '\u68c0\u67e5\u66f4\u65b0',
    settings_label_bot_name: '\u52a9\u624b\u540d\u79f0',
    settings_label_password: '\u8bbf\u95ee\u5bc6\u7801',
    settings_saved: '\u8bbe\u7f6e\u5df2\u4fdd\u5b58',
    settings_save_failed: '\u4fdd\u5b58\u5931\u8d25\uff1a',
    settings_load_failed: '\u8bbe\u7f6e\u52a0\u8f7d\u5931\u8d25\uff1a',
    settings_saved_pw: '\u8bbe\u7f6e\u5df2\u4fdd\u5b58\uff08\u5bc6\u7801\u5df2\u8bbe\u7f6e\u2014\u73b0\u5728\u9700\u8981\u767b\u5f55\uff09',
    // login page
    login_title: '\u767b\u5f55',
    login_subtitle: '\u8f93\u5165\u5bc6\u7801\u7ee7\u7eed\u4f7f\u7528',
    login_placeholder: '\u5bc6\u7801',
    login_btn: '\u767b\u5f55',
    login_invalid_pw: '\u5bc6\u7801\u9519\u8bef',
    login_conn_failed: '\u8fde\u63a5\u5931\u8d25',
    // sidebar & navigation
    tab_chat: '聊天',
    tab_memory: '记忆',
    tab_skills: '技能',
    tab_tasks: '任务',
    tab_todos: '待办',
    tab_workspaces: '工作区',
    new_conversation: '新建对话',
    filter_conversations: '筛选对话…',
    scheduled_jobs: '定时任务',
    new_job: '新任务',
    search_skills: '搜索技能…',
    new_skill: '新技能',
    save_skill: '保存技能',
    personal_memory: '个人记忆',
    current_task_list: '当前任务列表',
    new_profile: '新配置',
    transcript: '记录',
    download_transcript: '下载为 Markdown',
    import: '导入',
    editing: '编辑中',
    empty_title: '有什么可以帮您？',
    empty_subtitle: '随时提问、运行命令、浏览文件或管理定时任务。',
    cancel: '取消',
    loading: '加载中…',
    create_job: '创建任务',
    suggest_plan: '帮我规划一个小项目。',
    suggest_schedule: '今天有什么安排？',
    suggest_files: '这个工作区有哪些文件？',
    sign_out: '退出登录',
    password_placeholder: '输入新密码…',
    disable_auth: '停用认证',
    settings_label_sound: '通知声音',
    settings_label_notifications: '浏览器通知',
    settings_desc_sound: '助手完成回复时播放提示音。',
    settings_desc_notifications: '当标签页在后台时，回复完成后显示系统通知。',
    settings_desc_token_usage: '在助手每次回复下方显示输入/输出 token 数量。也可以用 /usage 切换。',
    settings_desc_cli_sessions: '将 Hermes CLI（state.db）中的会话合并到会话列表。点击某个 CLI 会话可导入并继续对话。',
    settings_desc_sync_insights: '将 WebUI token 使用情况同步到 state.db，使 hermes /insights 包含浏览器会话数据。默认关闭。',
    settings_desc_check_updates: '当有更新的 WebUI 或助手版本时显示横幅。会在后台定期执行 git fetch。',
    settings_desc_bot_name: '助手在 UI 中的显示名称。默认为 Hermes。',
    settings_desc_password: '输入新密码以设置或更改。留空保持当前设置。',
  },

  // Traditional Chinese (zh-Hant)
  'zh-Hant': {
    _lang: 'zh-Hant',
    _label: '\u7e41\u9ad4\u4e2d\u6587',
    _speech: 'zh-TW',
    // boot.js
    cancelling: '\u6b63\u5728\u53d6\u6d88...',
    cancel_failed: '\u53d6\u6d88\u5931\u6557\uff1a',
    mic_denied: '\u9ea6\u514b\u98a8\u8a2a\u554f\u88ab\u62d2\u7d75\uff0c\u8acb\u6aa2\u67e5\u700f\u89bd\u5668\u6b0a\u9650\u3002',
    mic_no_speech: '\u6c92\u6709\u6aa2\u6e2c\u5230\u8a71\u97f3\uff0c\u8acb\u518d\u5617\u4e00\u6b21\u3002',
    mic_network: '\u8a71\u97f3\u8b58\u5225\u76ee\u524d\u4e0d\u53ef\u7528\u3002',
    mic_error: '\u8a71\u97f3\u8f38\u5165\u51fa\u932f\uff1a',
    session_imported: '\u6703\u8a71\u5df2\u5c0e\u5165',
    import_failed: '\u5c0e\u5165\u5931\u6557\uff1a',
    import_invalid_json: 'JSON \u7121\u6548',
    image_pasted: '\u5df2\u7c98\u8cbc\u5716\u7247\uff1a',
    // messages.js
    edit_message: '\u7de8\u8f2f\u8a0a\u606f',
    regenerate: '\u91cd\u65b0\u751f\u6210\u56de\u8986',
    copy: '\u8907\u88fd',
    copied: '\u5df2\u8907\u88fd',
    you: '\u4f60',
    thinking: '\u601d\u8003\u904e\u7a0b',
    expand_all: '\u5168\u90e8\u5c55\u958b',
    collapse_all: '\u5168\u90e8\u6298\u758a',
    edit_failed: '\u7de8\u8f2f\u5931\u6557\uff1a',
    regen_failed: '\u91cd\u65b0\u751f\u6210\u5931\u6557\uff1a',
    reconnect_active: '\u56de\u8986\u4ecd\u5728\u751f\u6210\u4e2d\uff0c\u6e96\u5099\u597d\u5f8c\u8981\u91cd\u65b0\u52a0\u8f09\u55ce\uff1f',
    reconnect_finished: '\u4f60\u96e2\u958b\u6642\u6709\u56de\u8986\u6b63\u5728\u751f\u6210\uff0c\u8a0a\u606f\u5167\u5bb9\u53ef\u80fd\u5df2\u7d93\u66f4\u65b0\u3002',
    // approval card
    approval_heading: '\u9700\u8981\u5ba1\u6838',
    approval_desc_prefix: '\u6aa2\u6e2c\u5230\u5371\u96aa\u547d\u4ee4',
    approval_btn_once: '\u5141\u8a31\u4e00\u6b21',
    approval_btn_once_title: '\u5141\u8a31\u57f7\u884c\u6b64\u547d\u4ee4\u4e00\u6b21\uff08Enter\uff09',
    approval_btn_session: '\u672c\u6b21\u5141\u8a31',
    approval_btn_session_title: '\u672c\u6b21\u6703\u8a71\u671f\u9593\u5141\u8a31',
    approval_btn_always: '始終允許',
    approval_btn_always_title: '始終允許此命令模式',
    approval_btn_deny: '\u62d2\u7edd',
    approval_btn_deny_title: '\u62d2\u7edd — \u4e0d\u57f7\u884c\u6b64\u547d\u4ee4',
    approval_responding: '\u8655\u7406\u4e2d\u2026',
    untitled: '\u672a\u547d\u540d',
    n_messages: (n) => `${n} \u689d\u8a0a\u606f`,
    model_unavailable: '\uff08\u4e0d\u53ef\u7528\uff09',
    model_unavailable_title: '\u6b64\u6a21\u578b\u5df2\u7d93\u4e0d\u5728\u7576\u524d provider \u5217\u8868\u4e2d',
    provider_mismatch_warning: (m,p)=>`\"${m}\" \u53ef\u80fd\u7121\u6cd5\u5728\u7576\u524d\u914d\u7f6e\u7684\u63d0\u4f9b\u8005 (${p}) \u4e0b\u904b\u4f5c\u3002\u5c1a\u9001\uff0c\u6216\u5728\u7d42\u7aef\u57f7\u884c \`hermes model\` \u5207\u63db\u3002`,
    provider_mismatch_label: '\u63d0\u4f9b\u8005\u4e0d\u76f8\u7b26',
    // commands.js
    cmd_help: '\u67e5\u770b\u53ef\u7528\u547d\u4ee4',
    cmd_clear: '\u6e05\u7a7a\u7576\u524d\u5c0d\u8a71\u8a0a\u606f',
    cmd_compact: '\u58d3\u7e2e\u5c0d\u8a71\u4e0a\u4e0b\u6587',
    cmd_model: '\u5207\u63db\u6a21\u578b\uff08\u4f8b\u5982 /model gpt-4o\uff09',
    cmd_workspace: '\u6309\u540d\u7a31\u5207\u63db\u5de5\u4f5c\u5340',
    cmd_new: '\u65b0\u5efa\u804a\u5929\u6703\u8a71',
    cmd_usage: '\u5207\u63db token \u7528\u91cf\u986f\u793a',
    cmd_theme: '\u5207\u63db\u4e3b\u984c\uff08dark/light/slate/solarized/monokai/nord/oled\uff09',
    cmd_personality: '\u5207\u63db Agent \u4eba\u8a2d',
    cmd_skills: '\u5217\u51fa\u53ef\u7528\u7684 Hermes \u6280\u80fd',
    available_commands: '\u53ef\u7528\u547d\u4ee4\uff1a',
    type_slash: '\u8f38\u5165 / \u53ef\u67e5\u770b\u547d\u4ee4',
    conversation_cleared: '\u5c0d\u8a71\u5df2\u6e05\u7a7a',
    model_usage: '\u7528\u6cd5\uff1a/model <name>',
    no_model_match: '\u6c92\u6709\u5339\u914d\u201c',
    switched_to: '\u5df2\u5207\u63db\u5230 ',
    workspace_usage: '\u7528\u6cd5\uff1a/workspace <name>',
    no_workspace_match: '\u6c92\u6709\u5339\u914d\u201c',
    switched_workspace: '\u5df2\u5207\u63db\u5de5\u4f5c\u5340\uff1a',
    workspace_switch_failed: '\u5de5\u4f5c\u5340\u5207\u63db\u5931\u6557\uff1a',
    new_session: '\u5df2\u65b0\u5efa\u6703\u8a71',
    compressing: '\u6b63\u5728\u8981\u6c42\u58d3\u7e2e\u4e0a\u4e0b\u6587...',
    token_usage_on: 'Token \u7528\u91cf\u986f\u793a\u5df2\u958b\u555f',
    token_usage_off: 'Token \u7528\u91cf\u986f\u793a\u5df2\u95dc\u9589',
    theme_usage: '\u7528\u6cd5\uff1a/theme ',
    theme_set: '\u4e3b\u984c\uff1a',
    no_active_session: '\u7576\u524d\u6c92\u6709\u6d3b\u52d5\u6703\u8a71',
    no_personalities: '\u6c92\u6709\u627e\u5230\u4eba\u8a2d\uff08\u53ef\u6dfb\u52a0\u5230 ~/.hermes/personalities/\uff09',
    available_personalities: '\u53ef\u7528\u4eba\u8a2d\uff1a',
    personality_switch_hint: '\n\n\u4f7f\u7528 `/personality <name>` \u5207\u63db\uff0c\u6216\u7528 `/personality none` \u6e05\u7a7a\u3002',
    personalities_load_failed: '\u52a0\u8f7d\u4eba\u8a2d\u5931\u6557',
    personality_cleared: '\u4eba\u8a2d\u5df2\u6e05\u7a7a',
    personality_set: '\u7576\u524d\u4eba\u8a2d\uff1a',
    failed_colon: '\u5931\u6557\uff1a',
    // ui.js
    no_workspace: '\u672a\u9078\u64c7\u5de5\u4f5c\u5340',
    // workspace.js
    unsaved_confirm: '\u9810\u89bd\u5340\u6709\u672a\u5132\u5b58\u4fee\u6539\uff0c\u8981\u653e\u68c4\u66f4\u6539\u5e76\u7e7c\u7e8c\u8df3\u8ee2\u55ce\uff1f',
    save: '\u5132\u5b58',
    edit: '\u7de8\u8f2f',
    save_title: '\u5132\u5b58\u4fee\u6539',
    edit_title: '\u7de8\u8f2f\u6b64\u6587\u4ef6',
    saved: '\u5df2\u5132\u5b58',
    save_failed: '\u5132\u5b58\u5931\u6557\uff1a',
    image_load_failed: '\u5716\u7247\u52a0\u8f09\u5931\u6557',
    file_open_failed: '\u7121\u6cd5\u6253\u958b\u6587\u4ef6',
    downloading: (name) => `\u6b63\u5728\u4e0b\u8f09 ${name}...`,
    double_click_rename: '\u96d9\u64ca\u91cd\u547d\u540d',
    renamed_to: '\u5df2\u91cd\u547d\u540d\u70ba ',
    rename_failed: '\u91cd\u547d\u540d\u5931\u6557\uff1a',
    delete_title: '\u522a\u9664',
    delete_confirm: (name) => `\u8981\u522a\u9664 ${name} \u55ce\uff1f`,
    deleted: '\u5df2\u522a\u9664 ',
    delete_failed: '\u522a\u9664\u5931\u6557\uff1a',
    new_file_prompt: '\u65b0\u6587\u4ef6\u540d\uff08\u4f8b\u5982 notes.md\uff09\uff1a',
    created: '\u5df2\u5275\u5efa ',
    create_failed: '\u5275\u5efa\u5931\u6557\uff1a',
    new_folder_prompt: '\u65b0\u6587\u4ef6\u593e\u540d\u7a31\uff1a',
    folder_created: '\u5df2\u5275\u5efa\u6587\u4ef6\u593e ',
    folder_create_failed: '\u5275\u5efa\u6587\u4ef6\u593e\u5931\u6557\uff1a',
    remove_title: '\u79fb\u9664',
    empty_dir: '(\u7a7a)',
    upload_failed: '\u4e0a\u50b3\u5931\u6557\uff1a',
    all_uploads_failed: (n) => `${n} \u500b\u6587\u4ef6\u5168\u90e8\u4e0a\u50b3\u5931\u6557`,
    // settings panel
    settings_title: '\u8a2d\u5b9a',
    settings_save_btn: '\u5132\u5b58\u8a2d\u5b9a',
    settings_label_model: '\u9ed8\u8a8d\u6a21\u578b',
    settings_label_send_key: '\u767c\u9001\u5feb\u6377\u9375',
    settings_label_theme: '\u4e3b\u984c',
    settings_label_language: '\u8a9d\u8a00',
    settings_label_token_usage: '\u986f\u793a token \u7528\u91cf',
    settings_label_cli_sessions: '\u986f\u793a CLI \u6703\u8a71',
    settings_label_sync_insights: '\u540c\u6b65\u5230 insights',
    settings_label_check_updates: '\u6aa2\u67e5\u66f4\u65b0',
    settings_label_bot_name: '\u52a9\u624b\u540d\u7a31',
    settings_label_password: '\u8a2a\u8aad\u5bc6\u78bc',
    settings_saved: '\u8a2d\u5b9a\u5df2\u5132\u5b58',
    settings_save_failed: '\u5132\u5b58\u5931\u6557\uff1a',
    settings_load_failed: '\u8a2d\u5b9a\u52a0\u8f09\u5931\u6557\uff1a',
    settings_saved_pw: '\u8a2d\u5b9a\u5df2\u5132\u5b58\uff08\u5bc6\u78bc\u5df2\u8a2d\u5b9a\u2014\u73fe\u5728\u9700\u8981\u767b\u5f55\uff09',
    // login page
    login_title: '\u767b\u5f55',
    login_subtitle: '\u8f38\u5165\u5bc6\u78bc\u7e7c\u7e8c\u4f7f\u7528',
    login_placeholder: '\u5bc6\u78bc',
    login_btn: '\u767b\u5f55',
    login_invalid_pw: '\u5bc6\u78bc\u932f\u8aa4',
    login_conn_failed: '\u9023\u63a5\u5931\u6557',
    // missing keys from English
    dialog_confirm_title: '確認操作',
    dialog_prompt_title: '輸入內容',
    dialog_confirm_btn: '確認',
    discard: '放棄',
    clear: '清空',
    create: '建立',
    remove: '移除',
    project_name_prompt: '專案名稱：',
    tab_chat: '\u804a\u5929',
    tab_memory: '\u8a18\u61b6',
    tab_skills: '\u6280\u80fd',
    tab_tasks: '\u4efb\u52d9',
    tab_todos: '\u5f85\u8e29',
    tab_workspaces: '\u5de5\u4f5c\u5340',
    new_conversation: '\u65b0\u5b58\u5c0d\u8a71',
    filter_conversations: '\u7b5c\u9078\u5b58\u5c0d\u8a71',
    scheduled_jobs: '\u5b58\u5287\u4efb\u52d9',
    new_job: '\u65b0\u4efb\u52d9',
    search_skills: '\u641c\u5c0b\u6280\u80fd',
    new_skill: '\u65b0\u6280\u80fd',
    save_skill: '\u5132\u5b58\u6280\u80fd',
    personal_memory: '\u500b\u4eba\u8a18\u61b6',
    current_task_list: '\u76ee\u524d\u4efb\u52d9\u6e05\u55ae',
    new_profile: '\u65b0\u914d\u7f6e\u6a94',
    transcript: '\u8a18\u9304',
    download_transcript: '\u4e0b\u8f09\u8a18\u9304',
    import: '\u5c0e\u5165',
    editing: '\u7de8\u8f2f\u4e2d',
    empty_title: '\u7a7a\u767c\u5b58\u7a7a\u9593',
    empty_subtitle: '\u9ede\u64ca\u4e0a\u65b9\u6309\u9215\u958b\u59cb\u5c0d\u8a71',
    cancel: '\u53d6\u6d88',
    loading: '\u52a0\u8f09\u4e2d',
    create_job: '\u5efa\u7acb\u4efb\u52d9',
    suggest_plan: '\u5efa\u8b70\u8a08\u5287',
    suggest_schedule: '\u5efa\u8b70\u6642\u7a0b',
    suggest_files: '\u5efa\u8b70\u6a94\u6848',
    sign_out: '\u767b\u51fa',
    password_placeholder: '\u5bc6\u78bc',
    disable_auth: '\u505c\u7528\u9a57\u8b49',
    settings_label_sound: '\u901a\u77e5\u8072\u97f3',
    settings_label_notifications: '\u700f\u89bd\u901a\u77e5',
    settings_desc_sound: '\u52a9\u624b\u5b8c\u6210\u56de\u7b54\u6642\u64a9\u653e\u8072\u97f3\u3002',
    settings_desc_notifications: '\u7576\u5206\u9801\u5728\u5f8c\u81ea\u6642\uff0c\u6709\u56de\u7b54\u5b8c\u6210\u6e05\u55ae\u6703\u986f\u793a\u7cfb\u7d71\u901a\u77e5\u3002',
    settings_desc_token_usage: '\u5728\u52a9\u624b\u6bcf\u6b21\u56de\u7b54\u4e0b\u65b9\u986f\u793a Input/Output token \u6578\u91cf\u3002\u4e5f\u53ef\u4ee5\u7528 /usage \u5207\u63db\u3002',
    settings_desc_cli_sessions: '\u5c07 Hermes CLI (\u7684 state.db) \u4e2d\u7684\u6703\u8a71\u6dfb\u52a0\u5230\u6703\u8a71\u6e05\u55ae\u3002\u9ede\u64ca\u4e00\u500b CLI \u6703\u8a71\u5c07\u5c0e\u5165\u5b83\u7a0b\u5f0f\u4e26\u7e7c\u7e8c\u5b58\u5c0d\u8a71\u3002',
    settings_desc_sync_insights: '\u5c07 WebUI token \u4f7f\u7528\u60c5\u6cc1\u540c\u6b65\u5230 state.db\uff0c\u8a93 hermes /insights \u5305\u542b\u700f\u89bd\u5668\u6703\u8a71\u6578\u64da\u3002\u9810\u8a2d\u70b8\u555f\u7528\u3002',
    settings_desc_check_updates: '\u7576\u6709\u66f4\u65b0\u7684 WebUI \u6216\u52a9\u624b\u7248\u672c\u6642\u986f\u793a\u6a19\u8a18\u3002\u5c07\u5728\u5f8c\u81ea\u6b63\u5e38\u57f7\u884c Git-Fetch\u3002',
    settings_desc_bot_name: '\u52a9\u624b\u5728 UI \u4e2d\u7684\u986f\u793a\u540d\u7a31\u3002\u9810\u8a2d\u70b8\u7528\u6539\u3002',
    settings_desc_password: '\u8a2d\u5b9a WebUI \u767b\u5165\u5bc6\u78bc\u3002\u5047\u5982\u5df2\u8a2d\u7f6e\uff0c\u6bcf\u6b21\u52a0\u8f09\u90fd\u9700\u8981\u767b\u5165\u3002',
    settings_label_sound: '\u901a\u77e5\u8072\u97f3',
    // boot.js
    cancelling: '\u6b63\u5728\u53d6\u6d88...',
    cancel_failed: '\u53d6\u6d88\u5931\u6557\uff1a',
    mic_denied: '\u9ea6\u514b\u98a8\u8a2a\u554f\u88ab\u62d2\u7d75\uff0c\u8acb\u6aa2\u67e5\u700f\u89bd\u5668\u6b0a\u9650\u3002',
    mic_no_speech: '\u6c92\u6709\u6aa2\u6e2c\u5230\u8a71\u97f3\uff0c\u8acb\u518d\u5617\u4e00\u6b21\u3002',
    mic_network: '\u8a71\u97f3\u8b58\u5225\u76ee\u524d\u4e0d\u53ef\u7528\u3002',
    mic_error: '\u8a71\u97f3\u8f38\u5165\u51fa\u932f\uff1a',
    session_imported: '\u6703\u8a71\u5df2\u5c0e\u5165',
    import_failed: '\u5c0e\u5165\u5931\u6557\uff1a',
    import_invalid_json: 'JSON \u7121\u6548',
    image_pasted: '\u5df2\u7c98\u8cbc\u5716\u7247\uff1a',
    // messages.js
    edit_message: '\u7de8\u8f2f\u8a0a\u606f',
    regenerate: '\u91cd\u65b0\u751f\u6210\u56de\u8986',
    copy: '\u8907\u88fd',
    copied: '\u5df2\u8907\u88fd',
    // ui.js
    workspace_desc: '\u8acb\u9078\u64c7\u5de5\u4f5c\u5340\uff0c\u6216\u8f09\u5165\u65b0\u540d\u7a31\u5beb\u4e00\u500b',
    tab_profiles: '\u914d\u7f6e',
  },
};

// Active locale — defaults to English; overridden by loadLocale() at boot.
let _locale = LOCALES.en;

/**
 * Translate a key. Falls back to English if the key is missing in the active locale.
 * Supports function values (for interpolated strings): call t('key', arg).
 * @param {string} key
 * @param {...*} args - forwarded to function-valued translations
 * @returns {string}
 */
function t(key, ...args) {
  const val = _locale[key] ?? LOCALES.en[key];
  if (val === undefined) return key;  // final fallback: return key itself
  return typeof val === 'function' ? val(...args) : val;
}

/**
 * Switch locale by language code (e.g. 'en', 'zh').
 * Persists to localStorage and updates the <html lang> attribute.
 * @param {string} lang
 */
function setLocale(lang) {
  const resolved = LOCALES[lang] ? lang : 'en';
  _locale = LOCALES[resolved];
  localStorage.setItem('hermes-lang', resolved);
  document.documentElement.lang = _locale._speech || resolved;
}

/**
 * Load locale from localStorage (called once at boot, before DOMContentLoaded).
 * Server-persisted preference is applied later in loadSettingsPanel().
 */
function loadLocale() {
  const saved = localStorage.getItem('hermes-lang');
  setLocale(saved && LOCALES[saved] ? saved : 'en');
}

/**
 * Re-stamp all [data-i18n] elements in the DOM with the current locale.
 * Safe to call at any time — missing keys fall back to English.
 * Call after setLocale() to make static HTML text update without a reload.
 */
function applyLocaleToDOM() {
  document.querySelectorAll('[data-i18n]').forEach(el => {
    const key = el.getAttribute('data-i18n');
    const val = t(key);
    if (val && val !== key) el.textContent = val;
  });
  document.querySelectorAll('[data-i18n-title]').forEach(el => {
    const key = el.getAttribute('data-i18n-title');
    const val = t(key);
    if (val && val !== key) el.title = val;
  });
  document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
    const key = el.getAttribute('data-i18n-placeholder');
    const val = t(key);
    if (val && val !== key) el.placeholder = val;
  });
}

// Apply saved locale immediately so there's no flash of English on reload.
loadLocale();
