* Polish workspace panel behavior and app dialogs * Replace remaining emoji UI glyphs with Lucide icons * Redesign composer footer around model and context controls Move the model selector into the composer footer, replace the linear context pill with a compact circular badge plus tooltip, and remove the redundant topbar model pill. Design credit and inspiration: Theo / T3 Code. Reference implementation: https://github.com/pingdotgg/t3code/ * Remove obsolete activity bar Drop the old activity bar, keep turn-scoped state in the composer footer, and route remaining non-chat status messages through toasts. This leaves live tool cards and the message timeline as the primary progress UI, with the composer owning stop/cancel and brief turn status. * Move workspace and model switching into composer footer * Move profile switching into composer footer * Refactor Hermes control center UI * Redesign control center settings modal layout Widen the modal to 860px, simplify the tab list to icon+label rows, stretch the tab column's divider to full height, lock the panel to a fixed height so switching tabs no longer resizes the outer shell, and always open on the Conversation tab. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Put session item actions in a dropdown * Use Hermes mark in sidebar control button * Reset control center section on close * Drop session-item left border indicator Remove the left-border accent used for active, CLI, and project rows — each state already has a dedicated cue (gold fill, cli badge, project dot), so the border was redundant. Fully round the row, add 2px bottom spacing between rows, and strip the matching JS/CSS overrides. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Increase session search input vertical padding Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Normalise odd pixel values across UI Snap padding, gap, and border-radius values to the 2/4/6/8/10/12 grid across composer chips, sidebar panels, cron list, settings, approval buttons, dropdowns, and inline message edit — eliminating the 7/9/11px drift that was making sibling elements feel subtly misaligned. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add missing #btnMobileFiles button and .mobile-files-btn CSS (for mobile QA suite) The mobile layout regression suite (test_mobile_layout.py) requires: - #btnMobileFiles onclick=toggleMobileFiles() in topbar chips - .mobile-files-btn CSS rules for responsive show/hide at 640/900px breakpoints Also adds max-width guard to .profile-dropdown to prevent clipping at narrow viewports. * Improve composer footer mobile responsiveness and UX - Collapse composer chips to icon-only at <=400px viewports - Add model chip icon (CPU) so it remains tappable when labels are hidden - Show send button always (disabled state when empty, hidden during streaming) - Show context usage indicator on session load, not just after streaming - Add cancel status fallback timeout to prevent stale "Cancelling..." text - Update tests to match new send button and busy state behavior * Fix duplicate files button and broken workspace close on mobile Remove redundant #btnMobileFiles button that duplicated #btnWorkspacePanelToggle in the mobile topbar. Fix workspace panel close button calling undefined closeMobileFiles() — now calls closeWorkspacePanel(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix model chip icon vertical alignment in composer footer Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix workspace toggle button hidden on desktop by conflicting CSS class Remove mobile-files-btn class from #btnWorkspacePanelToggle — its display:none!important rule was overriding workspace-toggle-btn visibility on non-mobile viewports. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix session actions dots button inaccessible on mobile sidebar Always show the session actions trigger on mobile (no hover state on touch devices) and restore right padding so text truncates with ellipsis before the dots icon. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix composer footer manage links not opening sidebar panel The "Manage profiles" and "Manage workspaces" links in the composer footer dropdowns called switchPanel() which only changes the active panel content but doesn't open the sidebar. Replaced with mobileSwitchPanel() which also opens the sidebar so the panel is actually visible. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Widen icon-only composer chips breakpoint from 400px to 768px Move the icon-only chip styling up into the existing max-width:768px media query so chips collapse to icon-only on tablets too, preventing composer footer overflow on mid-size screens. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix composer-left vertical scrollbar by setting overflow-y:hidden When overflow-x is set to auto, the CSS spec implicitly changes overflow-y from visible to auto, allowing a vertical scrollbar to appear from slight chip padding/border overflow. Explicitly set overflow-y:hidden to prevent this. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve rebase conflicts and fix control center test assertions - Resolved 4 conflicts during rebase onto master (workspace.js, boot.js, index.html, test_sprint34.py) - Fixed test_sprint34.py: _controlSection -> _settingsSection, cc-tab -> settings-tabs (matching actual implementation) - Fixed quoting syntax error in test assertion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update version badge in System tab to v0.49.4 * docs: update README and CHANGELOG for v0.50.0 UI refresh, bump version badge --------- Co-authored-by: Aron Prins <pwf.aron@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
1201 lines
64 KiB
JavaScript
1201 lines
64 KiB
JavaScript
// ── 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_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_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_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_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_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_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',
|
||
dialog_confirm_title: '确认操作',
|
||
dialog_prompt_title: '输入内容',
|
||
dialog_confirm_btn: '确认',
|
||
discard: '放弃',
|
||
clear: '清空',
|
||
create: '创建',
|
||
remove: '移除',
|
||
project_name_prompt: '项目名称:',
|
||
// missing keys from English
|
||
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\u7801',
|
||
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\u53f0\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\u4f1a\u8a71\u6dfb\u52a0\u5230\u4f1a\u8a71\u6e05\u55ae\u3002\u9ede\u64ca\u4e00\u500b CLI \u4f1a\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\u4f1a\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\u7801\u3002\u5047\u5982\u5df2\u8a2d\u7f6e\uff0c\u6bcf\u6b21\u52a0\u8f09\u90fd\u9700\u8981\u767b\u5165\u3002',
|
||
settings_label_sound: '\u901a\u77e5\u8072\u97f3',
|
||
},
|
||
|
||
// 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: '\u59c4\u59b9\u5141\u8a31',
|
||
approval_btn_always_title: '\u59c4\u59b9\u5141\u8a31\u6b64\u547d\u4ee4\u6a21\u5f0f',
|
||
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();
|