mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 19:20:16 +00:00
7d1aa2e261
* feat: add manual 'Check for Updates' button in System settings (#785) Add a 'Check now' button next to the version badge in the System settings section, allowing users to manually trigger an update check at any time without waiting for the automatic periodic check. Changes: - index.html: add button with spinner and status text inline with version badge - panels.js: add checkUpdatesNow() calling /api/updates/check?force=1 with immediate feedback (checking... / up to date / X updates available) - style.css: style the button block and spinner - i18n.js: add 5 new keys (settings_check_now, settings_checking, settings_up_to_date, settings_updates_available, settings_updates_disabled) in all 6 locales (en, ru, es, de, zh, zh-Hant) * fix: sanitize error message in checkUpdatesNow to avoid exposing paths Review feedback: strip filesystem paths from error messages and cap length to prevent internal details leaking into the UI. * fix: fully sanitize error in update check — never expose raw e.message in UI Previous partial fix (80cdaee) stripped filesystem paths from e.message but still displayed the JS exception message to users. Per reviewer feedback and project convention (NEVER expose raw e.message in UI), replace with: - A generic user-facing i18n key (settings_update_check_failed) as default - Fallback to API response body error if available (structured, not raw) - Full error logged via console.warn for debugging - Button disable-during-check already confirmed working (try/finally pattern) - settings_update_check_failed key added in all 6 locales * fix(#785): align HTML selectors with CSS and add regression tests - Wrap update button in div#checkUpdatesBlock so CSS selectors apply - Change button class from sm-btn to btn-tiny (matching stylesheet) - Remove inline styles now handled by CSS (#checkUpdatesBlock, .btn-tiny) - Move spinner sizing to CSS class .spinner-xs - Add 4 static tests in test_update_banner_fixes.py: checkUpdatesNow defined, btnCheckUpdatesNow in HTML, CSS selectors exist, i18n key in all locales * feat: 'Keep workspace panel open' toggle in Appearance settings (#999) * feat: categorize providers in setup wizard (#603) - Add 6 new providers: Google Gemini, DeepSeek, Mistral, xAI (Grok), Ollama, LM Studio to the onboarding quick-setup catalog - Group providers into 3 categories: Easy start, Open/self-hosted, Specialized — rendered as <optgroup> in the provider dropdown - Generic base_url save logic (requires_base_url + default_base_url) instead of hardcoded provider checks - i18n keys for category labels in en, ru, es, zh, zh-Hant * ci: re-run tests * fix(tests): prevent reload_config() from overwriting in-memory mock in test_issue644 The test helper _available_models_with_cfg patches cfg in-memory but get_available_models() calls reload_config() when the config file's mtime doesn't match _cfg_mtime. On CI, config.yaml exists so mtime > 0 and _cfg_mtime starts at 0.0, triggering a reload that overwrites the test's mock with on-disk content. Fix: freeze _cfg_mtime to the current config file mtime inside the helper, so reload_config() is not triggered during the test. * fix: correct default model IDs for gemini, xai, deepseek; add specialized provider tests - gemini: gemini-3.1-pro-preview → gemini-2.5-pro-preview - x-ai: grok-4.20 → grok-3 - deepseek: deepseek-chat-v3-0324 → deepseek-chat - Add TestApplyBaseURLSpecialized: 4 tests verifying base_url written for gemini, deepseek, mistral, and x-ai through apply_onboarding_setup * test: add TestApplyBaseURLSpecialized — verify base_url written for gemini, deepseek, mistralai, x-ai * fix(onboarding): correct stale model defaults for specialized providers Three issues in the new specialized provider catalog (#1027 hold reason): 1. gemini default_model was `gemini-2.5-pro-preview` — agent's catalog has the 3.1 family. Updated to `gemini-3.1-pro-preview`. 2. x-ai default_model was `grok-3` — agent's catalog has `grok-4.20`. Updated. 3. gemini `models` list was sourcing from `_PROVIDER_MODELS.get("gemini")` which returns []. The catalog in api/config.py is keyed under "google" (even though the agent's alias map normalizes google -> gemini). Switched to `_PROVIDER_MODELS.get("google")` so the wizard surfaces the actual 5-model list. Also forward-compatible lookup for x-ai (xai or x-ai key). Without these fixes, users picking gemini or x-ai in the wizard would see no model dropdown and the default_model written to config.yaml would 404 on first chat. deepseek default_model bumped from `deepseek-chat` to `deepseek-chat-v3-0324` to match the test fixture's expectation and the agent catalog's pinned version. Added two regression tests: - test_gemini_model_list_is_populated: pins the catalog-key correctness - test_specialized_default_models_match_catalog: pins the version prefixes (3.x for gemini, 4.x for grok) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: inline HTML preview in workspace panel (#779) Render .html/.htm files as live previews in a sandboxed iframe instead of showing raw source code. Adds an 'Open in browser' button to open the file in a new tab. Changes: - workspace.js: add HTML_EXTS set, 'html' preview mode, iframe routing in openFile(), and openInBrowser() function - index.html: add sandboxed iframe element and 'Open in browser' button in preview toolbar (visible only for HTML files) - i18n.js: add 'open_in_browser' key in all 6 locales The iframe uses sandbox='allow-scripts' for security. Download button remains available alongside the new preview. * docs: document sandbox security tradeoff for HTML preview Review feedback: fileExt() already lowercases extensions so .HTML/.HTM work. Added code comment explaining the deliberate sandbox=allow-scripts choice: scripts are needed for most HTML documents but the iframe is still origin- isolated and cannot access parent cookies/data. * fix: pass ?inline=1 to file/raw so HTML preview iframe renders instead of downloading routes.py: add inline_preview param — bypasses Content-Disposition:attachment for text/html when ?inline=1 is set, serving the file inline for the sandboxed iframe. workspace.js: add &inline=1 to the iframe src URL. test: add 5 static regression tests for the inline HTML preview. * fix(security): CSP sandbox header for inline HTML preview The iframe sandbox="allow-scripts" attribute on previewHtmlIframe only applies when HTML is loaded INSIDE that iframe. A user tricked into opening /api/file/raw?path=evil.html&inline=1 directly in a top-level tab (e.g. via a chat link) would render the HTML in the WebUI's origin without any sandbox, giving the page full access to cookies and localStorage. Server-side Content-Security-Policy: sandbox allow-scripts mirrors the iframe sandbox exactly: scripts run, but the document is treated as a unique opaque origin (no allow-same-origin) and cannot read WebUI cookies, localStorage, or postMessage to the parent regardless of how the URL is accessed. Added test_inline_html_response_sets_csp_sandbox to pin the header. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: v0.50.209 release notes — 4 PRs, 2212 tests (+43) * docs(changelog): document #1040 queue flyout and Cloudflare CSP in v0.50.209 The stage commited2bd18listed v0.50.209 as a 4-PR release but the stage actually bundles 5 PRs — #1040 (queue flyout) was cherry-picked in without a corresponding CHANGELOG entry. Without this fix, the queue feature ships silently and the bundled Cloudflare CSP relaxation in api/helpers.py is also undocumented. Adds two entries: - Added: queue flyout (#1040) under v0.50.209 - Changed: CSP allowlist for Cloudflare Access deployments Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: bergeouss <bergeouss@users.noreply.github.com> Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com> Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3400 lines
195 KiB
JavaScript
3400 lines
195 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',
|
||
clarify_heading: 'Clarification needed',
|
||
clarify_hint: 'Pick a choice, or type your own answer below.',
|
||
clarify_other: 'Other',
|
||
clarify_send: 'Send',
|
||
clarify_input_placeholder: 'Type your response…',
|
||
clarify_responding: 'Responding\u2026',
|
||
untitled: 'Untitled',
|
||
n_messages: (n) => `${n} messages`,
|
||
queued_label: 'Sends after response',
|
||
queued_count: (n) => n === 1 ? '1 queued' : `${n} queued`,
|
||
queued_cancel: 'Cancel queued message',
|
||
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',
|
||
model_not_found_label: 'Model not found',
|
||
model_custom_label: 'Custom model ID',
|
||
model_custom_placeholder: 'e.g. openai/gpt-5.4',
|
||
model_search_placeholder: 'Search models…',
|
||
model_search_no_results: 'No models found',
|
||
// commands.js
|
||
cmd_clear: 'Clear conversation messages',
|
||
cmd_compress: 'Manually compress conversation context (usage: /compress [focus topic])',
|
||
cmd_compact_alias: 'Legacy alias for /compress',
|
||
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 appearance (theme: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard)',
|
||
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',
|
||
command_label: 'Command',
|
||
context_compaction_label: 'Context compaction',
|
||
reference_only_label: 'Reference only',
|
||
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...',
|
||
compress_running_label: 'Compressing',
|
||
compress_complete_label: 'Compression complete',
|
||
compress_failed_label: 'Compression failed',
|
||
focus_label: 'Focus',
|
||
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',
|
||
|
||
slash_skill_badge:'Skill',
|
||
slash_skill_desc:'Invoke this skill',
|
||
cmd_stop:'Stop the current response',
|
||
cmd_title:'Get or set the session title',
|
||
cmd_retry:'Resend the last message',
|
||
cmd_undo:'Remove the last exchange',
|
||
cmd_btw:'Ask a side question (ephemeral)',
|
||
cmd_btw_usage:'/btw <question> — ask a side question using session context',
|
||
cmd_background:'Run a prompt in background',
|
||
cmd_background_usage:'/background <prompt> — run in parallel without blocking',
|
||
btw_asking:'Asking side question...',
|
||
btw_label:'Side question — not in history',
|
||
btw_done:'Side question answered',
|
||
btw_no_answer:'No answer received.',
|
||
btw_failed:'Side question failed: ',
|
||
bg_running:'Running in background...',
|
||
bg_complete:'Background task complete',
|
||
bg_label:'Background result:',
|
||
bg_no_answer:'(no answer)',
|
||
bg_failed:'Background task failed: ',
|
||
undo_exchange:'Undo last exchange',
|
||
cmd_status:'Show session info',
|
||
cmd_voice:'Toggle microphone input',
|
||
stream_stopped:'Response stopped.',
|
||
no_active_task:'No active task to stop.',
|
||
cancel_unavailable:'Cancel not available.',
|
||
retry_failed:'Retry failed: ',
|
||
undo_failed:'Undo failed: ',
|
||
undid_n_messages:'Removed',
|
||
undid_messages_suffix:'message(s).',
|
||
status_heading:'Session Status',
|
||
status_session_id:'Session ID',
|
||
status_title:'Title',
|
||
status_model:'Model',
|
||
status_workspace:'Workspace',
|
||
status_personality:'Personality',
|
||
status_messages:'Messages',
|
||
status_agent_running:'Agent running',
|
||
status_yes:'Yes',
|
||
status_no:'No',
|
||
status_load_failed:'Failed to load status: ',
|
||
title_current:'Current title',
|
||
title_change_hint:'Use `/title <new name>` to rename.',
|
||
title_set:'Title set to',
|
||
cmd_webui_only_session:'This command is not available for CLI-imported sessions.',
|
||
cmd_voice_use_mic:'Click the mic button in the composer.',
|
||
usage_heading:'Token Usage',
|
||
usage_default_model:'default',
|
||
usage_unknown:'unknown',
|
||
usage_input_tokens:'Input tokens',
|
||
usage_output_tokens:'Output tokens',
|
||
usage_total:'Total tokens',
|
||
usage_estimated_cost:'Estimated cost',
|
||
usage_settings_tip:'Note: cost estimates are approximate.',
|
||
usage_load_failed:'Failed to load usage: ',
|
||
usage_personality_none:'none',
|
||
untitled:'Untitled',
|
||
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',
|
||
workspace_empty_no_path: 'No workspace selected. Set a workspace in Settings \u2192 Workspace to browse files.',
|
||
workspace_empty_dir: 'This workspace is empty.',
|
||
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: ',
|
||
workspace_auto_create_folder: 'Create folder if it doesn\'t exist',
|
||
folder_add_as_space_btn: 'Add as Space',
|
||
folder_add_as_space_msg: 'Add this folder as a new space in your workspace list?',
|
||
folder_add_as_space_title: 'Add as Space?',
|
||
remove_title: 'Remove',
|
||
empty_dir: '(empty)',
|
||
upload_failed: 'Upload failed: ',
|
||
all_uploads_failed: (n) => `All ${n} upload(s) failed`,
|
||
session_pin: 'Pin conversation',
|
||
session_unpin: 'Unpin conversation',
|
||
session_pin_desc: 'Keep this conversation at the top',
|
||
session_unpin_desc: 'Remove from pinned',
|
||
session_pin_failed: 'Pin failed: ',
|
||
session_move_project: 'Move to project',
|
||
session_move_project_desc_has: 'Change the project for this conversation',
|
||
session_move_project_desc_none: 'Assign a project to this conversation',
|
||
session_archive: 'Archive conversation',
|
||
session_restore: 'Restore conversation',
|
||
session_archive_desc: 'Hide this conversation until archived is shown',
|
||
session_restore_desc: 'Bring this conversation back into the main list',
|
||
session_archived: 'Session archived',
|
||
session_restored: 'Session restored',
|
||
session_archive_failed: 'Archive failed: ',
|
||
session_duplicate: 'Duplicate conversation',
|
||
session_duplicate_desc: 'Create a copy with the same workspace and model',
|
||
session_duplicated: 'Session duplicated',
|
||
session_duplicate_failed: 'Duplicate failed: ',
|
||
session_delete: 'Delete conversation',
|
||
session_delete_desc: 'Permanently remove this conversation',
|
||
// settings panel
|
||
settings_heading_title: 'Control Center',
|
||
settings_heading_subtitle: 'Preferences, conversation tools, and system controls.',
|
||
settings_section_conversation_title: 'Conversation',
|
||
settings_section_appearance_title: 'Appearance',
|
||
settings_section_appearance_meta: 'Theme, accent colors, and visual style.',
|
||
settings_section_preferences_title: 'Preferences',
|
||
settings_section_preferences_meta: 'Defaults and UI behavior for Hermes Web UI.',
|
||
settings_section_system_title: 'System',
|
||
settings_section_system_meta: 'Instance version and access controls.',
|
||
settings_check_now: 'Check now',
|
||
settings_checking: 'Checking\u2026',
|
||
settings_up_to_date: 'Up to date \u2713',
|
||
settings_updates_available: '{count} update(s) available',
|
||
settings_updates_disabled: 'Update checks disabled',
|
||
settings_update_check_failed: 'Update check failed',
|
||
settings_label_workspace_panel_open: 'Keep workspace panel open by default',
|
||
settings_desc_workspace_panel_open: 'When enabled, the workspace / file browser panel opens automatically with each new session. You can still close it manually at any time.',
|
||
open_in_browser: 'Open in browser',
|
||
settings_dropdown_conversation: 'Conversation',
|
||
settings_dropdown_appearance: 'Appearance',
|
||
settings_dropdown_preferences: 'Preferences',
|
||
settings_dropdown_providers: 'Providers',
|
||
settings_dropdown_system: 'System',
|
||
settings_tab_conversation: 'Conversation',
|
||
settings_tab_appearance: 'Appearance',
|
||
settings_tab_preferences: 'Preferences',
|
||
settings_tab_system: 'System',
|
||
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_skin: 'Skin',
|
||
settings_label_font_size: 'Font size',
|
||
font_size_small: 'Small',
|
||
font_size_default: 'Default',
|
||
font_size_large: 'Large',
|
||
settings_label_language: 'Language',
|
||
settings_label_token_usage: 'Show token usage',
|
||
settings_label_sidebar_density: 'Sidebar density',
|
||
cmd_reasoning: 'Toggle thinking visibility (show/hide), set effort level, or check current status',
|
||
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 protection enabled and this browser stays signed in',
|
||
settings_saved_pw_updated: 'Settings saved — password updated',
|
||
// 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',
|
||
tab_settings: 'Settings',
|
||
new_conversation: 'New conversation',
|
||
filter_conversations: 'Filter conversations...',
|
||
session_time_unknown: 'Unknown',
|
||
session_time_just_now: 'just now',
|
||
session_time_minutes_ago: (n) => `${n} minute${n === 1 ? '' : 's'} ago`,
|
||
session_time_hours_ago: (n) => `${n} hour${n === 1 ? '' : 's'} ago`,
|
||
session_time_days_ago: (n) => `${n} day${n === 1 ? '' : 's'} ago`,
|
||
session_time_last_week: 'last week',
|
||
session_time_bucket_today: 'Today',
|
||
session_time_bucket_yesterday: 'Yesterday',
|
||
session_time_bucket_this_week: 'This week',
|
||
session_time_bucket_last_week: 'Last week',
|
||
session_time_bucket_older: 'Older',
|
||
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.',
|
||
session_meta_messages: (n) => `${n} msg${n === 1 ? '' : 's'}`,
|
||
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 app is in the background.',
|
||
settings_desc_token_usage: 'Displays input/output token count below each assistant reply. Also toggled with /usage.',
|
||
settings_sidebar_density_compact: 'Compact',
|
||
settings_sidebar_density_detailed: 'Detailed',
|
||
settings_desc_sidebar_density: 'Controls how much metadata the session list shows in the left sidebar.',
|
||
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',
|
||
// Providers panel
|
||
providers_tab_title: 'Providers',
|
||
providers_section_title: 'Providers',
|
||
providers_section_meta: 'Manage API keys for AI providers. Changes take effect immediately.',
|
||
providers_status_configured: 'API key configured',
|
||
providers_status_not_configured: 'No API key',
|
||
providers_status_oauth: 'OAuth',
|
||
providers_status_api_key: 'API key',
|
||
providers_status_not_configured_label: 'Not configured',
|
||
providers_oauth_hint: 'Authenticated via OAuth. No API key needed.',
|
||
providers_save: 'Save',
|
||
providers_remove: 'Remove',
|
||
providers_saving: 'Saving…',
|
||
providers_removing: 'Removing…',
|
||
providers_enter_key: 'Please enter an API key',
|
||
providers_empty: 'No configurable providers found.',
|
||
providers_key_updated: 'API key saved',
|
||
providers_key_removed: 'API key removed',
|
||
providers_key_placeholder_new: 'sk-...',
|
||
providers_key_placeholder_replace: 'Enter new key to replace…',
|
||
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_skip: 'Skip setup',
|
||
onboarding_skipped: 'Setup skipped — using existing config.',
|
||
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',
|
||
provider_category_easy_start: 'Easy start',
|
||
provider_category_self_hosted: 'Open / self-hosted',
|
||
provider_category_specialized: 'Specialized',
|
||
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_will_replace: 'Will be replaced',
|
||
onboarding_password_keep_existing: 'Keep current password',
|
||
onboarding_password_remains_disabled: 'Will remain disabled',
|
||
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',
|
||
// panel/runtime i18n
|
||
error_prefix: 'Error: ',
|
||
not_available: 'N/A',
|
||
never: 'never',
|
||
add: 'Add',
|
||
add_failed: 'Add failed: ',
|
||
remove_failed: 'Remove failed: ',
|
||
switch_failed: 'Switch failed: ',
|
||
name_required: 'Name is required',
|
||
content_required: 'Content is required',
|
||
view: 'View',
|
||
dismiss: 'Dismiss',
|
||
disable: 'Disable',
|
||
cron_no_jobs: 'No scheduled jobs found.',
|
||
cron_status_off: 'off',
|
||
cron_status_paused: 'paused',
|
||
cron_status_error: 'error',
|
||
cron_status_active: 'active',
|
||
cron_status_running: 'running\u2026',
|
||
cron_next: 'Next',
|
||
cron_last: 'Last',
|
||
cron_run_now: 'Run now',
|
||
cron_pause: 'Pause',
|
||
cron_resume: 'Resume',
|
||
cron_job_name_placeholder: 'Job name',
|
||
cron_schedule_placeholder: 'Schedule',
|
||
cron_prompt_placeholder: 'Prompt',
|
||
cron_last_output: 'Last output',
|
||
cron_all_runs: 'All runs',
|
||
cron_hide_runs: 'Hide runs',
|
||
cron_no_runs_yet: '(no runs yet)',
|
||
cron_schedule_required_example: 'Schedule is required (e.g. "0 9 * * *" or "every 1h")',
|
||
cron_schedule_required: 'Schedule is required',
|
||
cron_prompt_required: 'Prompt is required',
|
||
cron_job_created: 'Job created',
|
||
cron_job_triggered: 'Job triggered',
|
||
cron_job_paused: 'Job paused',
|
||
cron_job_resumed: 'Job resumed',
|
||
cron_job_updated: 'Job updated',
|
||
cron_delete_confirm_title: 'Delete cron job',
|
||
cron_delete_confirm_message: 'This cannot be undone.',
|
||
cron_job_deleted: 'Job deleted',
|
||
cron_completion_status: (name, status) => `Cron "${name}" ${status}`,
|
||
status_failed: 'failed',
|
||
status_completed: 'completed',
|
||
todos_no_active: 'No active task list in this session.',
|
||
clear_conversation_title: 'Clear conversation',
|
||
clear_conversation_message: 'Clear all messages? This cannot be undone.',
|
||
clear_failed: 'Clear failed: ',
|
||
skills_no_match: 'No skills match.',
|
||
linked_files: 'Linked Files',
|
||
skill_load_failed: 'Could not load skill: ',
|
||
skill_file_load_failed: 'Could not load file: ',
|
||
skill_name_required: 'Skill name is required',
|
||
skill_updated: 'Skill updated',
|
||
skill_created: 'Skill created',
|
||
skill_deleted: 'Skill deleted',
|
||
skill_delete_confirm: 'Delete skill "{0}"?',
|
||
skills_empty_title: 'Select a skill',
|
||
skills_empty_sub: 'Pick a skill from the sidebar to view its contents, or create a new one.',
|
||
skills_edit: 'Edit',
|
||
skills_delete: 'Delete',
|
||
skills_back_to: 'Back to {0}',
|
||
tasks_empty_title: 'Select a scheduled job',
|
||
tasks_empty_sub: 'Pick a job from the sidebar to view its details and runs, or create a new one.',
|
||
workspaces_empty_title: 'Select a space',
|
||
workspaces_empty_sub: 'Pick a space from the sidebar to view its files and settings, or add a new one.',
|
||
profiles_empty_title: 'Select a profile',
|
||
profiles_empty_sub: 'Pick an agent profile from the sidebar to view and edit its settings, or create a new one.',
|
||
memory_notes_label: 'memory (notes)',
|
||
memory_saved: 'Memory saved',
|
||
my_notes: 'My Notes',
|
||
user_profile: 'User Profile',
|
||
no_notes_yet: 'No notes yet.',
|
||
no_profile_yet: 'No profile yet.',
|
||
workspace_choose_path: 'Choose workspace path',
|
||
workspace_choose_path_meta: 'Add a validated path and switch this conversation',
|
||
workspace_manage: 'Manage workspaces',
|
||
workspace_manage_meta: 'Open the Spaces panel',
|
||
workspace_use_title: 'Use in current session',
|
||
workspace_use: 'Use',
|
||
workspace_add_path_placeholder: 'Add workspace path (e.g. /home/user/my-project)',
|
||
workspace_paths_validated_hint: 'Paths are validated as existing directories before saving.',
|
||
workspace_added: 'Workspace added',
|
||
workspace_renamed: 'Workspace renamed',
|
||
workspace_remove_confirm_title: 'Remove workspace',
|
||
workspace_remove_confirm_message: (path) => `Remove "${path}"?`,
|
||
workspace_removed: 'Workspace removed',
|
||
workspace_switch_prompt_title: 'Switch workspace',
|
||
workspace_switch_prompt_message: 'Enter an absolute workspace path to add and switch this conversation to.',
|
||
workspace_switch_prompt_confirm: 'Switch',
|
||
workspace_switch_prompt_placeholder: '/Users/you/project',
|
||
workspace_not_added: 'Workspace was not added',
|
||
workspace_already_saved: 'Workspace already saved — choose it from the list',
|
||
workspace_busy_switch: 'Cannot switch workspace while agent is running',
|
||
discard_file_edits_title: 'Discard file edits?',
|
||
discard_file_edits_message: 'Switching workspaces will discard unsaved file edits in the preview.',
|
||
workspace_switched_to: (name) => `Switched to ${name}`,
|
||
profiles_no_profiles: 'No profiles found.',
|
||
profile_api_keys_configured: 'API keys configured',
|
||
profile_gateway_running: 'Gateway running',
|
||
profile_gateway_stopped: 'Gateway stopped',
|
||
profile_active: 'ACTIVE',
|
||
profile_no_configuration: 'No configuration',
|
||
profile_skill_count: (count) => `${count} skill${count === 1 ? '' : 's'}`,
|
||
profile_use: 'Use',
|
||
profile_switch_title: 'Switch to this profile',
|
||
profile_delete_title: 'Delete this profile',
|
||
profile_default_label: '(default)',
|
||
profile_name_placeholder: 'Profile name (lowercase, a-z 0-9 hyphens)',
|
||
profile_clone_label: 'Clone config from active profile',
|
||
profile_base_url_placeholder: 'Base URL (optional, e.g. http://localhost:11434)',
|
||
profile_api_key_placeholder: 'API key (optional)',
|
||
manage_profiles: 'Manage profiles',
|
||
profiles_load_failed: 'Failed to load profiles',
|
||
profiles_busy_switch: 'Cannot switch profiles while agent is running',
|
||
profile_switched_new_conversation: (name) => `Switched to profile: ${name} — new conversation started`,
|
||
profile_switched: (name) => `Switched to profile: ${name}`,
|
||
profile_name_rule: 'Lowercase letters, numbers, hyphens, underscores only',
|
||
profile_base_url_rule: 'Base URL must start with http:// or https://',
|
||
profile_created: (name) => `Profile created: ${name}`,
|
||
profile_delete_confirm_title: (name) => `Delete profile "${name}"?`,
|
||
profile_delete_confirm_message: 'All sessions, config, skills, and memory for this profile will be permanently deleted. This cannot be undone.',
|
||
profile_deleted: (name) => `Profile deleted: ${name}`,
|
||
active_conversation_none: 'No active conversation selected.',
|
||
active_conversation_meta: (title, count) => `${title} · ${count} message${count === 1 ? '' : 's'}`,
|
||
settings_unsaved_changes: 'You have unsaved changes.',
|
||
sign_out_failed: 'Sign out failed: ',
|
||
disable_auth_confirm_title: 'Disable password protection',
|
||
disable_auth_confirm_message: 'Anyone will be able to access this instance.',
|
||
auth_disabled: 'Auth disabled — password protection removed',
|
||
disable_auth_failed: 'Failed to disable auth: ',
|
||
bg_error_single: (title) => `"${title}" has encountered an error`,
|
||
bg_error_multi: (count) => `${count} sessions have encountered an error`,
|
||
// skill form
|
||
skill_name: 'Name',
|
||
skill_category: 'Category',
|
||
skill_category_placeholder: 'Optional, e.g. devops',
|
||
skill_content: 'SKILL.md content',
|
||
skill_content_placeholder: 'YAML frontmatter + markdown body',
|
||
skill_rename_not_supported: 'Renaming a skill is not supported. Create a new skill and delete the old one to rename.',
|
||
skill_metadata: 'Metadata',
|
||
// cron form
|
||
cron_name_label: 'Name',
|
||
cron_name_placeholder: 'Optional',
|
||
cron_schedule_label: 'Schedule',
|
||
cron_schedule_hint: "Cron expression or shorthand like 'every 1h'.",
|
||
cron_prompt_label: 'Prompt',
|
||
cron_deliver_label: 'Deliver output to',
|
||
cron_deliver_local: 'Local (save output only)',
|
||
cron_skills_label: 'Skills',
|
||
cron_skills_placeholder: 'Add skills (optional)…',
|
||
cron_skills_edit_hint: 'Skill list is not editable after creation.',
|
||
// workspace form
|
||
workspace_name_label: 'Name',
|
||
workspace_name_placeholder: 'Optional friendly name',
|
||
workspace_path_label: 'Path',
|
||
workspace_path_required: 'Path is required',
|
||
workspace_path_readonly: 'Path cannot be changed. Rename only.',
|
||
workspace_new_title: 'New space',
|
||
// profile form
|
||
profile_name_label: 'Name',
|
||
profile_base_url_label: 'Base URL',
|
||
profile_api_key_label: 'API key',
|
||
},
|
||
|
||
ru: {
|
||
_lang: 'ru',
|
||
_label: 'Русский',
|
||
_speech: 'ru-RU',
|
||
cancelling: 'Отменяю…',
|
||
cancel_failed: 'Не удалось отменить: ',
|
||
mic_denied: 'Доступ к микрофону запрещён. Проверьте разрешения браузера.',
|
||
mic_no_speech: 'Речь не распознана. Попробуйте ещё раз.',
|
||
mic_network: 'Распознавание речи недоступно.',
|
||
mic_error: 'Ошибка ввода речи: ',
|
||
session_imported: 'Сеанс импортирован',
|
||
import_failed: 'Не удалось импортировать: ',
|
||
import_invalid_json: 'Неверный JSON',
|
||
image_pasted: 'Изображение вставлено: ',
|
||
edit_message: 'Редактировать сообщение',
|
||
regenerate: 'Сгенерировать ответ заново',
|
||
copy: 'Копировать',
|
||
copied: 'Скопировано!',
|
||
you: 'Вы',
|
||
thinking: 'Думаю',
|
||
expand_all: 'Развернуть всё',
|
||
collapse_all: 'Свернуть всё',
|
||
edit_failed: 'Не удалось отредактировать: ',
|
||
regen_failed: 'Не удалось сгенерировать заново: ',
|
||
reconnect_active: 'Ответ всё ещё генерируется. Обновить, когда будет готово?',
|
||
reconnect_finished: 'Когда вы уходили, ответ ещё генерировался. Сообщения могли обновиться.',
|
||
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: 'Без названия',
|
||
n_messages: (n) => `${n} сообщений`,
|
||
queued_label: 'Отправить после ответа',
|
||
queued_count: (n) => n === 1 ? '1 в очереди' : `${n} в очереди`,
|
||
queued_cancel: 'Отменить сообщение',
|
||
model_unavailable: ' (недоступна)',
|
||
model_unavailable_title: 'Эта модель больше не входит в ваш текущий список провайдеров',
|
||
provider_mismatch_warning: (m, p) =>
|
||
`"${m}" может не работать с вашим настроенным провайдером (${p}). Всё равно отправить или запустите \`hermes model\` в терминале, чтобы переключиться.`,
|
||
provider_mismatch_label: 'Несовпадение провайдера',
|
||
model_not_found_label: 'Модель не найдена',
|
||
model_custom_label: 'Пользовательский ID модели',
|
||
model_custom_placeholder: 'например, openai/gpt-5.4',
|
||
cmd_help: 'Показать доступные команды',
|
||
cmd_clear: 'Очистить сообщения беседы',
|
||
cmd_compact: 'Сжать контекст беседы',
|
||
cmd_model: 'Переключить модель (например, /model gpt-4o)',
|
||
cmd_workspace: 'Переключить рабочее пространство по названию',
|
||
cmd_new: 'Начать новую сессию чата',
|
||
cmd_usage: 'Показать или скрыть использование токенов',
|
||
cmd_theme: 'Переключить тему (dark/light/slate/solarized/monokai/nord/oled)',
|
||
cmd_personality: 'Переключить личность агента',
|
||
cmd_skills: 'Показать доступные навыки Hermes',
|
||
available_commands: 'Доступные команды:',
|
||
type_slash: 'Введите /, чтобы увидеть команды',
|
||
conversation_cleared: 'Беседа очищена',
|
||
model_usage: 'Использование: /model <name>',
|
||
no_model_match: 'Нет модели, соответствующей "',
|
||
switched_to: 'Переключено на ',
|
||
workspace_usage: 'Использование: /workspace <name>',
|
||
no_workspace_match: 'Нет рабочего пространства, соответствующего "',
|
||
switched_workspace: 'Переключено на рабочее пространство: ',
|
||
workspace_switch_failed: 'Не удалось переключить рабочее пространство: ',
|
||
new_session: 'Новая сессия создана',
|
||
compressing: 'Запрашиваю сжатие контекста...',
|
||
token_usage_on: 'Отображение токенов включено',
|
||
token_usage_off: 'Отображение токенов выключено',
|
||
theme_usage: 'Использование: /theme ',
|
||
theme_set: 'Тема: ',
|
||
no_active_session: 'Нет активной сессии',
|
||
|
||
no_personalities: 'Личности не найдены (добавьте их в ~/.hermes/personalities/)',
|
||
clarify_heading: 'Требуется уточнение',
|
||
clarify_hint: 'Выберите вариант или введите свой ответ ниже.',
|
||
clarify_input_placeholder: 'Введите ответ…',
|
||
clarify_other: 'Другое',
|
||
clarify_responding: 'Отвечаю…',
|
||
clarify_send: 'Отправить',
|
||
cmd_compact_alias: 'Устаревший псевдоним для /compress',
|
||
cmd_compress: 'Сжать контекст беседы (использование: /compress [тема])',
|
||
command_label: 'Команда',
|
||
compress_complete_label: 'Сжатие завершено',
|
||
compress_failed_label: 'Ошибка сжатия',
|
||
compress_running_label: 'Сжатие…',
|
||
context_compaction_label: 'Сжатие контекста',
|
||
focus_label: 'Фокус',
|
||
model_search_no_results: 'Модели не найдены',
|
||
model_search_placeholder: 'Поиск моделей…',
|
||
reference_only_label: 'Только справка',
|
||
settings_label_skin: 'Скин',
|
||
settings_label_font_size: 'Размер шрифта',
|
||
font_size_small: 'Маленький',
|
||
font_size_default: 'Стандарт',
|
||
font_size_large: 'Большой',
|
||
workspace_empty_dir: 'Это рабочее пространство пусто.',
|
||
workspace_empty_no_path: 'Рабочее пространство не выбрано. Настройте его в Настройки → Рабочее пространство.',
|
||
available_personalities: 'Доступные личности:',
|
||
personality_switch_hint: '\n\nИспользуйте `/personality <name>` для переключения или `/personality none` для сброса.',
|
||
personalities_load_failed: 'Не удалось загрузить личности',
|
||
personality_cleared: 'Личность очищена',
|
||
personality_set: 'Личность: ',
|
||
failed_colon: 'Не удалось: ',
|
||
no_workspace: 'Нет рабочего пространства',
|
||
dialog_confirm_title: 'Подтвердить действие',
|
||
dialog_prompt_title: 'Введите значение',
|
||
dialog_confirm_btn: 'Подтвердить',
|
||
unsaved_confirm: 'У вас есть несохранённые изменения в предпросмотре. Отменить и перейти дальше?',
|
||
discard: 'Отменить',
|
||
save: 'Сохранить',
|
||
edit: 'Редактировать',
|
||
clear: 'Очистить',
|
||
create: 'Создать',
|
||
remove: 'Удалить',
|
||
save_title: 'Сохранить изменения',
|
||
edit_title: 'Редактировать этот файл',
|
||
saved: 'Сохранено',
|
||
save_failed: 'Не удалось сохранить: ',
|
||
image_load_failed: 'Не удалось загрузить изображение',
|
||
file_open_failed: 'Не удалось открыть файл',
|
||
downloading: (name) => `Скачиваю ${name}…`,
|
||
double_click_rename: 'Дважды щёлкните, чтобы переименовать',
|
||
renamed_to: 'Переименовано в ',
|
||
rename_failed: 'Не удалось переименовать: ',
|
||
delete_title: 'Удалить',
|
||
delete_confirm: (name) => `Удалить ${name}?`,
|
||
deleted: 'Удалено ',
|
||
delete_failed: 'Не удалось удалить: ',
|
||
new_file_prompt: 'Имя нового файла (например, notes.md):',
|
||
project_name_prompt: 'Имя проекта:',
|
||
created: 'Создано ',
|
||
create_failed: 'Не удалось создать: ',
|
||
new_folder_prompt: 'Имя новой папки:',
|
||
folder_created: 'Папка создана ',
|
||
folder_create_failed: 'Не удалось создать папку: ',
|
||
workspace_auto_create_folder: 'Создать папку, если она не существует',
|
||
folder_add_as_space_btn: 'Добавить',
|
||
folder_add_as_space_msg: 'Добавить эту папку как новое пространство?',
|
||
folder_add_as_space_title: 'Добавить как пространство?',
|
||
remove_title: 'Удаление',
|
||
empty_dir: '(пусто)',
|
||
upload_failed: 'Не удалось загрузить: ',
|
||
all_uploads_failed: (n) => `Не удалось загрузить все ${n} файлов`,
|
||
settings_title: 'Настройки',
|
||
settings_save_btn: 'Сохранить настройки',
|
||
settings_label_model: 'Модель по умолчанию',
|
||
settings_label_send_key: 'Клавиша отправки',
|
||
settings_label_theme: 'Тема',
|
||
settings_label_language: 'Язык',
|
||
settings_label_token_usage: 'Показывать использование токенов',
|
||
settings_label_sidebar_density: 'Плотность боковой панели',
|
||
cmd_reasoning: 'Toggle thinking visibility (show/hide), set effort level, or check current status',
|
||
settings_label_cli_sessions: 'Показывать сеансы агента',
|
||
settings_label_sync_insights: 'Синхронизировать с Insights',
|
||
settings_label_check_updates: 'Проверять обновления',
|
||
settings_label_bot_name: 'Имя помощника',
|
||
settings_label_password: 'Пароль доступа',
|
||
settings_saved: 'Настройки сохранены',
|
||
settings_save_failed: 'Не удалось сохранить: ',
|
||
settings_load_failed: 'Не удалось загрузить настройки: ',
|
||
settings_saved_pw: 'Настройки сохранены (пароль задан — теперь требуется вход)',
|
||
settings_saved_pw_updated: 'Настройки сохранены (пароль обновлён)',
|
||
login_title: 'Вход',
|
||
login_subtitle: 'Введите пароль, чтобы продолжить',
|
||
login_placeholder: 'Пароль',
|
||
login_btn: 'Войти',
|
||
login_invalid_pw: 'Неверный пароль',
|
||
login_conn_failed: 'Не удалось подключиться',
|
||
tab_chat: 'Чат',
|
||
tab_tasks: 'Задачи',
|
||
tab_skills: 'Навыки',
|
||
tab_memory: 'Память',
|
||
tab_workspaces: 'Рабочие пространства',
|
||
tab_profiles: 'Профили',
|
||
tab_todos: 'Список дел',
|
||
tab_settings: 'Настройки',
|
||
new_conversation: 'Новая беседа',
|
||
filter_conversations: 'Фильтр бесед...',
|
||
session_time_unknown: 'Неизвестно',
|
||
session_time_just_now: 'только что',
|
||
session_time_minutes_ago: (n) => {
|
||
const mod10 = n % 10;
|
||
const mod100 = n % 100;
|
||
const word = mod10 === 1 && mod100 !== 11
|
||
? 'минута'
|
||
: (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)
|
||
? 'минуты'
|
||
: 'минут');
|
||
return `${n} ${word} назад`;
|
||
},
|
||
session_time_hours_ago: (n) => {
|
||
const mod10 = n % 10;
|
||
const mod100 = n % 100;
|
||
const word = mod10 === 1 && mod100 !== 11
|
||
? 'час'
|
||
: (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)
|
||
? 'часа'
|
||
: 'часов');
|
||
return `${n} ${word} назад`;
|
||
},
|
||
session_time_days_ago: (n) => {
|
||
const mod10 = n % 10;
|
||
const mod100 = n % 100;
|
||
const word = mod10 === 1 && mod100 !== 11
|
||
? 'день'
|
||
: (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)
|
||
? 'дня'
|
||
: 'дней');
|
||
return `${n} ${word} назад`;
|
||
},
|
||
session_time_last_week: 'на прошлой неделе',
|
||
session_time_bucket_today: 'Сегодня',
|
||
session_time_bucket_yesterday: 'Вчера',
|
||
session_time_bucket_this_week: 'На этой неделе',
|
||
session_time_bucket_last_week: 'На прошлой неделе',
|
||
session_time_bucket_older: 'Ранее',
|
||
scheduled_jobs: 'Запланированные задания',
|
||
new_job: 'Новое задание',
|
||
loading: 'Загрузка...',
|
||
search_skills: 'Поиск навыков...',
|
||
new_skill: 'Новый навык',
|
||
personal_memory: 'Личная память',
|
||
current_task_list: 'Текущий список задач',
|
||
workspace_desc: 'Добавляйте рабочие пространства и переключайтесь между ними в своих сеансах.',
|
||
session_meta_messages: (n) => `${n} сообщ.`,
|
||
new_profile: 'Новый профиль',
|
||
transcript: 'Транскрипт',
|
||
download_transcript: 'Скачать как Markdown',
|
||
import: 'Импорт',
|
||
settings_label_sound: 'Звук уведомления',
|
||
settings_desc_sound: 'Проигрывать звук, когда помощник завершает ответ.',
|
||
settings_label_notifications: 'Уведомления браузера',
|
||
settings_desc_notifications: 'Показывать системное уведомление, когда ответ готов, а вкладка находится в фоне.',
|
||
settings_desc_token_usage: 'Показывает количество входных и выходных токенов под каждым ответом помощника. Также переключается через /usage.',
|
||
settings_sidebar_density_compact: 'Компактно',
|
||
settings_sidebar_density_detailed: 'Подробно',
|
||
settings_desc_sidebar_density: 'Управляет тем, сколько метаданных показывается в списке сеансов на левой панели.',
|
||
settings_desc_cli_sessions: 'Объединяет сеансы из Hermes CLI (state.db) в список сеансов. Нажмите на CLI-сеанс, чтобы импортировать его и продолжить разговор.',
|
||
settings_desc_sync_insights: 'Синхронизирует использование токенов WebUI в state.db, чтобы Hermes /insights включал данные браузерных сеансов. Выключено по умолчанию.',
|
||
settings_desc_check_updates: 'Показывает баннер, когда доступны более новые версии WebUI или Agent. Периодически выполняет git fetch в фоне.',
|
||
settings_desc_bot_name: 'Отображаемое имя помощника во всём интерфейсе. По умолчанию Hermes.',
|
||
settings_desc_password: 'Введите новый пароль, чтобы задать или изменить его. Оставьте пустым, чтобы сохранить текущую настройку.',
|
||
password_placeholder: 'Введите новый пароль…',
|
||
disable_auth: 'Отключить авторизацию',
|
||
sign_out: 'Выйти',
|
||
// Providers panel (English fallback — native translations welcome in follow-up PRs)
|
||
providers_tab_title: 'Providers',
|
||
providers_section_title: 'Providers',
|
||
providers_section_meta: 'Manage API keys for AI providers. Changes take effect immediately.',
|
||
providers_status_configured: 'API key configured',
|
||
providers_status_not_configured: 'No API key',
|
||
providers_status_oauth: 'OAuth',
|
||
providers_status_api_key: 'API key',
|
||
providers_status_not_configured_label: 'Not configured',
|
||
providers_oauth_hint: 'Authenticated via OAuth. No API key needed.',
|
||
providers_save: 'Save',
|
||
providers_remove: 'Remove',
|
||
providers_saving: 'Saving…',
|
||
providers_removing: 'Removing…',
|
||
providers_enter_key: 'Please enter an API key',
|
||
providers_empty: 'No configurable providers found.',
|
||
providers_key_updated: 'API key saved',
|
||
providers_key_removed: 'API key removed',
|
||
providers_key_placeholder_new: 'sk-...',
|
||
providers_key_placeholder_replace: 'Enter new key to replace…',
|
||
cancel: 'Отмена',
|
||
create_job: 'Создать задание',
|
||
save_skill: 'Сохранить навык',
|
||
editing: 'Редактирование',
|
||
empty_title: 'Чем я могу помочь?',
|
||
empty_subtitle: 'Спрашивайте что угодно, запускайте команды, изучайте файлы или управляйте запланированными задачами.',
|
||
suggest_files: 'Какие файлы есть в этом рабочем пространстве?',
|
||
suggest_schedule: 'Что у меня сегодня в расписании?',
|
||
suggest_plan: 'Помоги спланировать небольшой проект.',
|
||
onboarding_badge: 'ПЕРВЫЙ ЗАПУСК',
|
||
onboarding_title: 'Добро пожаловать в Hermes Web UI',
|
||
onboarding_lead: 'Краткая пошаговая настройка проверит Hermes, сохранит рабочую конфигурацию провайдера, выберет рабочее пространство и модель и при желании защитит приложение паролем.',
|
||
onboarding_back: 'Назад',
|
||
onboarding_continue: 'Продолжить',
|
||
onboarding_skip: 'Пропустить настройку',
|
||
onboarding_skipped: 'Настройка пропущена — используется существующая конфигурация.',
|
||
onboarding_open: 'Открыть Hermes',
|
||
onboarding_step_system_title: 'Проверка системы',
|
||
onboarding_step_system_desc: 'Проверить Hermes Agent и видимость конфигурации.',
|
||
onboarding_step_setup_title: 'Настройка провайдера',
|
||
onboarding_step_setup_desc: 'Сохранить минимальную рабочую конфигурацию провайдера Hermes.',
|
||
onboarding_step_workspace_title: 'Рабочее пространство и модель',
|
||
onboarding_step_workspace_desc: 'Выбрать значения по умолчанию для новых сеансов и чатов.',
|
||
onboarding_step_password_title: 'Необязательный пароль',
|
||
onboarding_step_password_desc: 'Защитить Web UI перед тем, как делиться им.',
|
||
onboarding_step_finish_title: 'Готово',
|
||
onboarding_step_finish_desc: 'Проверьте настройки и войдите в приложение.',
|
||
onboarding_notice_system_ready: 'Hermes Agent, похоже, доступен из Web UI.',
|
||
onboarding_notice_system_unavailable: 'Hermes Agent ещё не полностью доступен. Bootstrap может установить его, но для настройки провайдера всё ещё может понадобиться терминал.',
|
||
onboarding_check_agent: 'Hermes Agent',
|
||
onboarding_check_agent_ready: 'Обнаружен и доступен для импорта',
|
||
onboarding_check_agent_missing: 'Отсутствует или доступен только частично',
|
||
onboarding_check_password: 'Пароль',
|
||
onboarding_check_password_enabled: 'Уже включён',
|
||
onboarding_check_password_disabled: 'Пока не включён',
|
||
onboarding_check_provider: 'Конфигурация провайдера',
|
||
onboarding_check_provider_ready: 'Готова к чату',
|
||
onboarding_check_provider_partial: 'Сохранена, но не завершена',
|
||
onboarding_check_provider_pending: 'Требует проверки',
|
||
onboarding_config_file: 'Файл конфигурации:',
|
||
onboarding_env_file: 'Файл .env:',
|
||
onboarding_unknown: 'Неизвестно',
|
||
onboarding_current_provider: 'Текущая конфигурация:',
|
||
onboarding_missing_imports: 'Отсутствующие импорты:',
|
||
onboarding_notice_setup_required: 'Выберите здесь простой путь настройки провайдера. Продвинутые OAuth-сценарии пока остаются в Hermes CLI.',
|
||
onboarding_notice_setup_already_ready: 'Уже обнаружена рабочая конфигурация провайдера Hermes. Вы можете оставить её или заменить здесь.',
|
||
onboarding_oauth_provider_ready_title: 'Провайдер уже авторизован',
|
||
onboarding_oauth_provider_ready_body: 'Этот экземпляр настроен на использование OAuth-провайдера (<strong>{provider}</strong>), настроенного через Hermes CLI. API-ключ здесь не нужен — нажмите «Продолжить», чтобы завершить настройку.',
|
||
onboarding_oauth_provider_not_ready_title: 'OAuth-провайдер ещё не авторизован',
|
||
onboarding_oauth_provider_not_ready_body: 'Этот экземпляр настроен на использование <strong>{provider}</strong>, который работает через OAuth, а не через API-ключ. Запустите <code>hermes auth</code> или <code>hermes model</code> в терминале, чтобы пройти авторизацию, затем обновите Web UI.',
|
||
onboarding_oauth_switch_hint: 'Или выберите ниже другой провайдер, чтобы перейти на настройку с ключом API:',
|
||
onboarding_notice_workspace: 'Эти значения используют те же API настроек, что и обычное приложение.',
|
||
onboarding_workspace_label: 'Рабочее пространство',
|
||
onboarding_workspace_or_path: 'Или укажите путь к рабочему пространству',
|
||
onboarding_workspace_placeholder: '/home/you/workspace',
|
||
onboarding_provider_label: 'Режим настройки',
|
||
onboarding_quick_setup_badge: 'Быстрая настройка',
|
||
provider_category_easy_start: 'Быстрый старт',
|
||
provider_category_self_hosted: 'Локальные / Open source',
|
||
provider_category_specialized: 'Специализированные',
|
||
onboarding_api_key_label: 'Ключ API',
|
||
onboarding_api_key_placeholder: 'Оставьте пустым, чтобы сохранить уже сохранённый ключ',
|
||
onboarding_api_key_help_prefix: 'Сохраняется как секрет в вашем файле `.env` Hermes с помощью',
|
||
onboarding_base_url_label: 'Базовый URL',
|
||
onboarding_base_url_placeholder: 'https://your-endpoint.example/v1',
|
||
onboarding_base_url_help: 'Используйте это для OpenAI-compatible маршрутизаторов, self-hosted серверов, LiteLLM, Ollama, LM Studio, vLLM и похожих endpoint-ов.',
|
||
onboarding_model_label: 'Модель по умолчанию',
|
||
onboarding_workspace_help: 'Выберите модель, которую Hermes должен использовать для новых чатов после завершения настройки.',
|
||
onboarding_custom_model_placeholder: 'имя_вашей_модели',
|
||
onboarding_custom_model_help: 'Для собственных endpoint-ов укажите точный ID модели, который ожидает ваш сервер.',
|
||
onboarding_notice_password_enabled: 'Пароль уже настроен. Вводите новый только если хотите заменить текущий.',
|
||
onboarding_notice_password_recommended: 'Необязательно, но рекомендуется, если вы собираетесь открывать UI не только на localhost.',
|
||
onboarding_password_label: 'Пароль (необязательно)',
|
||
onboarding_password_placeholder: 'Оставьте пустым, чтобы пропустить',
|
||
onboarding_password_help: 'Пароли сохраняются через существующий API настроек и хэшируются на сервере.',
|
||
onboarding_notice_finish: 'Позже вы сможете снова открыть настройки и изменить любое из этих значений.',
|
||
onboarding_not_set: 'Не задано',
|
||
onboarding_password_will_enable: 'Будет включён',
|
||
onboarding_password_will_replace: 'Будет заменён текущий пароль',
|
||
onboarding_password_keep_existing: 'Оставить текущий пароль',
|
||
onboarding_password_remains_disabled: 'Останется отключённым',
|
||
onboarding_password_skipped: 'Пропустить пока',
|
||
onboarding_finish_help: 'После завершения в настройках сохранится <code>onboarding_completed</code>, и вы попадёте в обычное приложение.',
|
||
onboarding_error_choose_workspace: 'Выберите рабочее пространство перед продолжением.',
|
||
onboarding_error_choose_model: 'Выберите модель перед продолжением.',
|
||
onboarding_error_provider_required: 'Выберите режим настройки перед продолжением.',
|
||
onboarding_error_base_url_required: 'Для собственных endpoint-ов требуется базовый URL.',
|
||
onboarding_error_workspace_required: 'Рабочее пространство обязательно.',
|
||
onboarding_error_model_required: 'Модель обязательна.',
|
||
onboarding_complete: 'Первичная настройка завершена',
|
||
error_prefix: 'Ошибка: ',
|
||
not_available: 'н/д',
|
||
never: 'никогда',
|
||
add: 'Добавить',
|
||
add_failed: 'Не удалось добавить: ',
|
||
remove_failed: 'Не удалось удалить: ',
|
||
switch_failed: 'Не удалось переключить: ',
|
||
name_required: 'Требуется имя',
|
||
content_required: 'Требуется содержимое',
|
||
view: 'Просмотр',
|
||
dismiss: 'Скрыть',
|
||
disable: 'Отключить',
|
||
cron_no_jobs: 'Запланированные задания не найдены.',
|
||
cron_status_off: 'неактивно',
|
||
cron_status_paused: 'на паузе',
|
||
cron_status_error: 'ошибка',
|
||
cron_status_active: 'активно',
|
||
cron_status_running: 'выполняется\u2026',
|
||
cron_next: 'Следующий',
|
||
cron_last: 'Последний',
|
||
cron_run_now: 'Запустить сейчас',
|
||
cron_pause: 'Пауза',
|
||
cron_resume: 'Возобновить',
|
||
cron_job_name_placeholder: 'Имя задания',
|
||
cron_schedule_placeholder: 'Расписание',
|
||
cron_prompt_placeholder: 'Промпт',
|
||
cron_last_output: 'Последний вывод',
|
||
cron_all_runs: 'Все запуски',
|
||
cron_hide_runs: 'Скрыть запуски',
|
||
cron_no_runs_yet: '(пока запусков нет)',
|
||
cron_schedule_required_example: 'Требуется расписание (например, "0 9 * * *" или "every 1h")',
|
||
cron_schedule_required: 'Требуется расписание',
|
||
cron_prompt_required: 'Требуется промпт',
|
||
cron_job_created: 'Задание создано',
|
||
cron_job_triggered: 'Задание запущено',
|
||
cron_job_paused: 'Задание поставлено на паузу',
|
||
cron_job_resumed: 'Задание возобновлено',
|
||
cron_job_updated: 'Задание обновлено',
|
||
cron_delete_confirm_title: 'Удалить cron-задание',
|
||
cron_delete_confirm_message: 'Это действие нельзя отменить.',
|
||
cron_job_deleted: 'Задание удалено',
|
||
cron_completion_status: (name, status) => `Cron-задание «${name}» — ${status}`,
|
||
status_failed: 'неудачно',
|
||
status_completed: 'завершено',
|
||
todos_no_active: 'В этой сессии нет активного списка задач.',
|
||
clear_conversation_title: 'Очистить беседу',
|
||
clear_conversation_message: 'Очистить все сообщения? Это действие нельзя отменить.',
|
||
clear_failed: 'Не удалось очистить: ',
|
||
skills_no_match: 'Подходящих навыков не найдено.',
|
||
linked_files: 'Связанные файлы',
|
||
skill_load_failed: 'Не удалось загрузить навык: ',
|
||
skill_file_load_failed: 'Не удалось загрузить файл: ',
|
||
skill_name_required: 'Требуется имя навыка',
|
||
skill_updated: 'Навык обновлён',
|
||
skill_created: 'Навык создан',
|
||
memory_notes_label: 'память (заметки)',
|
||
memory_saved: 'Память сохранена',
|
||
my_notes: 'Мои заметки',
|
||
user_profile: 'Пользовательский профиль',
|
||
no_notes_yet: 'Пока нет заметок.',
|
||
no_profile_yet: 'Пока нет профиля.',
|
||
workspace_choose_path: 'Выберите путь к рабочему пространству',
|
||
workspace_choose_path_meta: 'Добавьте проверенный путь и переключите эту беседу',
|
||
workspace_manage: 'Управление рабочими пространствами',
|
||
workspace_manage_meta: 'Открыть панель Spaces',
|
||
workspace_use_title: 'Использовать в текущем сеансе',
|
||
workspace_use: 'Использовать',
|
||
workspace_add_path_placeholder: 'Добавьте путь к рабочему пространству (например, /Users/you/project)',
|
||
workspace_paths_validated_hint: 'Перед сохранением пути проверяются на существование.',
|
||
workspace_added: 'Рабочее пространство добавлено',
|
||
workspace_renamed: 'Рабочее пространство переименовано',
|
||
workspace_remove_confirm_title: 'Удалить рабочее пространство',
|
||
workspace_remove_confirm_message: (path) => `Удалить «${path}»?`,
|
||
workspace_removed: 'Рабочее пространство удалено',
|
||
workspace_switch_prompt_title: 'Переключить рабочее пространство',
|
||
workspace_switch_prompt_message: 'Введите абсолютный путь к рабочему пространству, чтобы добавить его и переключить эту беседу.',
|
||
workspace_switch_prompt_confirm: 'Переключить',
|
||
workspace_switch_prompt_placeholder: '/Users/you/project',
|
||
workspace_not_added: 'Рабочее пространство не добавлено',
|
||
workspace_already_saved: 'Рабочее пространство уже сохранено — выберите его из списка',
|
||
workspace_busy_switch: 'Нельзя переключать рабочее пространство, пока агент работает',
|
||
discard_file_edits_title: 'Отменить изменения файлов?',
|
||
discard_file_edits_message: 'При переключении рабочих пространств несохранённые изменения в предпросмотре будут потеряны.',
|
||
workspace_switched_to: (name) => `Переключено на ${name}`,
|
||
profiles_no_profiles: 'Профили не найдены.',
|
||
profile_api_keys_configured: 'API-ключи настроены',
|
||
profile_gateway_running: 'Gateway запущен',
|
||
profile_gateway_stopped: 'Gateway остановлен',
|
||
profile_active: 'АКТИВЕН',
|
||
profile_no_configuration: 'Нет конфигурации',
|
||
profile_skill_count: (count) => {
|
||
const mod10 = count % 10;
|
||
const mod100 = count % 100;
|
||
const word = mod10 === 1 && mod100 !== 11
|
||
? 'навык'
|
||
: (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)
|
||
? 'навыка'
|
||
: 'навыков');
|
||
return `${count} ${word}`;
|
||
},
|
||
profile_use: 'Использовать',
|
||
profile_switch_title: 'Переключиться на этот профиль',
|
||
profile_delete_title: 'Удалить этот профиль',
|
||
profile_default_label: '(по умолчанию)',
|
||
profile_name_placeholder: 'Название профиля (строчные буквы, a-z, 0-9, дефисы)',
|
||
profile_clone_label: 'Скопировать конфигурацию из активного профиля',
|
||
profile_base_url_placeholder: 'Базовый URL (необязательно, например http://localhost:11434)',
|
||
profile_api_key_placeholder: 'API-ключ (необязательно)',
|
||
manage_profiles: 'Управление профилями',
|
||
profiles_load_failed: 'Не удалось загрузить профили',
|
||
profiles_busy_switch: 'Нельзя переключать профили, пока агент работает',
|
||
profile_switched_new_conversation: (name) => `Переключено на профиль: ${name} — начата новая беседа`,
|
||
profile_switched: (name) => `Переключено на профиль: ${name}`,
|
||
profile_name_rule: 'Только строчные буквы, цифры, дефисы и подчёркивания',
|
||
profile_base_url_rule: 'Базовый URL должен начинаться с http:// или https://',
|
||
profile_created: (name) => `Профиль создан: ${name}`,
|
||
profile_delete_confirm_title: (name) => `Удалить профиль «${name}»?`,
|
||
profile_delete_confirm_message: 'Все сеансы, конфигурация, навыки и память этого профиля будут удалены безвозвратно. Это действие невозможно отменить.',
|
||
profile_deleted: (name) => `Профиль удалён: ${name}`,
|
||
active_conversation_none: 'Активная беседа не выбрана.',
|
||
active_conversation_meta: (title, count) => {
|
||
const mod10 = count % 10;
|
||
const mod100 = count % 100;
|
||
const word = mod10 === 1 && mod100 !== 11
|
||
? 'сообщение'
|
||
: (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)
|
||
? 'сообщения'
|
||
: 'сообщений');
|
||
return `${title} · ${count} ${word}`;
|
||
},
|
||
settings_unsaved_changes: 'У вас есть несохранённые изменения.',
|
||
sign_out_failed: 'Не удалось выйти: ',
|
||
disable_auth_confirm_title: 'Отключить защиту паролем',
|
||
disable_auth_confirm_message: 'Любой сможет получить доступ к этому экземпляру.',
|
||
auth_disabled: 'Авторизация отключена — защита паролем снята',
|
||
disable_auth_failed: 'Не удалось отключить авторизацию: ',
|
||
bg_error_single: (title) => `В "${title}" возникла ошибка`,
|
||
bg_error_multi: (count) => `${count} сеансов столкнулись с ошибкой`,
|
||
skill_deleted: 'Навык удалён',
|
||
skill_delete_confirm: 'Удалить навык "{0}"?',
|
||
skills_empty_title: 'Выберите навык',
|
||
skills_empty_sub: 'Выберите навык на боковой панели, чтобы просмотреть его содержимое, или создайте новый.',
|
||
skills_edit: 'Редактировать',
|
||
skills_delete: 'Удалить',
|
||
skills_back_to: 'Назад к {0}',
|
||
tasks_empty_title: 'Выберите запланированное задание',
|
||
tasks_empty_sub: 'Выберите задание на боковой панели, чтобы просмотреть его детали и запуски, или создайте новое.',
|
||
workspaces_empty_title: 'Выберите пространство',
|
||
workspaces_empty_sub: 'Выберите пространство на боковой панели, чтобы просмотреть его файлы и настройки, или добавьте новое.',
|
||
profiles_empty_title: 'Выберите профиль',
|
||
profiles_empty_sub: 'Выберите профиль агента на боковой панели, чтобы просмотреть и изменить его настройки, или создайте новый.',
|
||
// skill form
|
||
skill_name: 'Имя',
|
||
skill_category: 'Категория',
|
||
skill_category_placeholder: 'Необязательно, например devops',
|
||
skill_content: 'Содержимое SKILL.md',
|
||
skill_content_placeholder: 'YAML-заголовок + тело markdown',
|
||
skill_rename_not_supported: 'Переименование навыка не поддерживается. Создайте новый навык и удалите старый.',
|
||
skill_metadata: 'Метаданные',
|
||
// cron form
|
||
cron_name_label: 'Имя',
|
||
cron_name_placeholder: 'Необязательно',
|
||
cron_schedule_label: 'Расписание',
|
||
cron_schedule_hint: "Cron-выражение или сокращение, например 'every 1h'.",
|
||
cron_prompt_label: 'Запрос',
|
||
cron_deliver_label: 'Доставлять вывод',
|
||
cron_deliver_local: 'Локально (только сохранение)',
|
||
cron_skills_label: 'Навыки',
|
||
cron_skills_placeholder: 'Добавить навыки (необязательно)…',
|
||
cron_skills_edit_hint: 'Список навыков нельзя изменить после создания.',
|
||
// workspace form
|
||
workspace_name_label: 'Имя',
|
||
workspace_name_placeholder: 'Необязательное понятное имя',
|
||
workspace_path_label: 'Путь',
|
||
workspace_path_required: 'Путь обязателен',
|
||
workspace_path_readonly: 'Путь нельзя изменить. Только переименование.',
|
||
workspace_new_title: 'Новое пространство',
|
||
// profile form
|
||
profile_name_label: 'Имя',
|
||
profile_base_url_label: 'Базовый URL',
|
||
profile_api_key_label: 'API-ключ',
|
||
|
||
// Session management and settings keys (en fallback — pending translation)
|
||
session_archive: 'Archive conversation',
|
||
session_archive_desc: 'Hide this conversation until archived is shown',
|
||
session_archive_failed: 'Archive failed: ',
|
||
session_archived: 'Session archived',
|
||
session_delete: 'Delete conversation',
|
||
session_delete_desc: 'Permanently remove this conversation',
|
||
session_duplicate: 'Duplicate conversation',
|
||
session_duplicate_desc: 'Create a copy with the same workspace and model',
|
||
session_duplicate_failed: 'Duplicate failed: ',
|
||
session_duplicated: 'Session duplicated',
|
||
session_move_project: 'Move to project',
|
||
session_move_project_desc_has: 'Change the project for this conversation',
|
||
session_move_project_desc_none: 'Assign a project to this conversation',
|
||
session_pin: 'Pin conversation',
|
||
session_pin_desc: 'Keep this conversation at the top',
|
||
session_pin_failed: 'Pin failed: ',
|
||
session_restore: 'Restore conversation',
|
||
session_restore_desc: 'Bring this conversation back into the main list',
|
||
session_restored: 'Session restored',
|
||
session_unpin: 'Unpin conversation',
|
||
session_unpin_desc: 'Remove from pinned',
|
||
settings_dropdown_appearance: 'Appearance',
|
||
settings_dropdown_conversation: 'Conversation',
|
||
settings_dropdown_preferences: 'Preferences',
|
||
settings_dropdown_providers: 'Providers',
|
||
settings_dropdown_system: 'System',
|
||
settings_heading_subtitle: 'Preferences, conversation tools, and system controls.',
|
||
settings_heading_title: 'Control Center',
|
||
settings_section_appearance_meta: 'Theme, accent colors, and visual style.',
|
||
settings_section_appearance_title: 'Appearance',
|
||
settings_section_conversation_title: 'Conversation',
|
||
settings_section_preferences_meta: 'Defaults and UI behavior for Hermes Web UI.',
|
||
settings_section_preferences_title: 'Preferences',
|
||
settings_section_system_meta: 'Instance version and access controls.',
|
||
settings_check_now: 'Проверить',
|
||
settings_checking: 'Проверка\u2026',
|
||
settings_up_to_date: 'Актуально \u2713',
|
||
settings_updates_available: 'Доступно обновлений: {count}',
|
||
settings_updates_disabled: 'Проверка обновлений отключена',
|
||
settings_update_check_failed: 'Ошибка проверки обновлений',
|
||
settings_label_workspace_panel_open: 'Открывать панель рабочей области по умолчанию',
|
||
settings_desc_workspace_panel_open: 'При включении панель файлов будет открываться автоматически в каждой новой сессии.',
|
||
open_in_browser: 'Открыть в браузере',
|
||
settings_section_system_title: 'System',
|
||
settings_tab_appearance: 'Appearance',
|
||
settings_tab_conversation: 'Conversation',
|
||
settings_tab_preferences: 'Preferences',
|
||
settings_tab_system: 'System',
|
||
},
|
||
|
||
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…',
|
||
clarify_heading: 'Se necesita aclaración',
|
||
clarify_hint: 'Elige una opción o escribe tu propia respuesta abajo.',
|
||
clarify_other: 'Otra',
|
||
clarify_send: 'Enviar',
|
||
clarify_input_placeholder: 'Escribe tu respuesta…',
|
||
clarify_responding: 'Respondiendo…',
|
||
untitled: 'Sin título',
|
||
n_messages: (n) => `${n} mensajes`,
|
||
queued_label: 'Enviar después de la respuesta',
|
||
queued_count: (n) => n === 1 ? '1 en cola' : `${n} en cola`,
|
||
queued_cancel: 'Cancelar mensaje en cola',
|
||
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',
|
||
model_not_found_label: 'Modelo no encontrado',
|
||
model_custom_label: 'ID de modelo personalizado',
|
||
model_custom_placeholder: 'p. ej. openai/gpt-5.4',
|
||
model_search_placeholder: 'Buscar modelos…',
|
||
model_search_no_results: 'No se encontraron modelos',
|
||
// commands.js
|
||
cmd_help: 'Listar los comandos disponibles',
|
||
cmd_clear: 'Borrar los mensajes de la conversación',
|
||
cmd_compress: 'Comprimir manualmente el contexto de la conversación (uso: /compress [tema])',
|
||
cmd_compact_alias: 'Alias antiguo de /compress',
|
||
cmd_compact: 'Comprimir 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 apariencia (tema: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard)',
|
||
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',
|
||
command_label: 'Comando',
|
||
context_compaction_label: 'Compacción de contexto',
|
||
reference_only_label: 'Solo referencia',
|
||
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...',
|
||
compress_running_label: 'Comprimiendo',
|
||
compress_complete_label: 'Compresión completa',
|
||
compress_failed_label: 'La compresión falló',
|
||
focus_label: 'Tema',
|
||
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_empty_no_path: 'No hay espacio de trabajo seleccionado. Configure un espacio de trabajo en Ajustes \u2192 Workspace para explorar archivos.',
|
||
workspace_empty_dir: 'Este espacio de trabajo está vacío.',
|
||
// 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: ',
|
||
workspace_auto_create_folder: 'Crear carpeta si no existe',
|
||
folder_add_as_space_btn: 'Añadir como espacio',
|
||
folder_add_as_space_msg: '¿Añadir esta carpeta como un nuevo espacio?',
|
||
folder_add_as_space_title: '¿Añadir como espacio?',
|
||
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_skin: 'Piel',
|
||
settings_label_font_size: 'Tamaño de fuente',
|
||
font_size_small: 'Pequeño',
|
||
font_size_default: 'Por defecto',
|
||
font_size_large: 'Grande',
|
||
settings_label_language: 'Idioma',
|
||
settings_label_token_usage: 'Mostrar uso de tokens',
|
||
settings_label_sidebar_density: 'Densidad de la barra lateral',
|
||
cmd_reasoning: 'Toggle thinking visibility (show/hide), set effort level, or check current status',
|
||
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 — la contraseña queda activada y este navegador sigue autenticado',
|
||
settings_saved_pw_updated: 'Configuración guardada — contraseña actualizada',
|
||
// 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',
|
||
tab_settings: 'Ajustes',
|
||
new_conversation: 'Nueva conversación',
|
||
filter_conversations: 'Filtrar conversaciones...',
|
||
session_time_unknown: 'Desconocido',
|
||
session_time_just_now: 'justo ahora',
|
||
session_time_minutes_ago: (n) => `hace ${n} minuto${n === 1 ? '' : 's'}`,
|
||
session_time_hours_ago: (n) => `hace ${n} hora${n === 1 ? '' : 's'}`,
|
||
session_time_days_ago: (n) => `hace ${n} día${n === 1 ? '' : 's'}`,
|
||
session_time_last_week: 'la semana pasada',
|
||
session_time_bucket_today: 'Hoy',
|
||
session_time_bucket_yesterday: 'Ayer',
|
||
session_time_bucket_this_week: 'Esta semana',
|
||
session_time_bucket_last_week: 'La semana pasada',
|
||
session_time_bucket_older: 'Más antiguo',
|
||
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.',
|
||
session_meta_messages: (n) => `${n} mens.`,
|
||
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_sidebar_density_compact: 'Compacta',
|
||
settings_sidebar_density_detailed: 'Detallada',
|
||
settings_desc_sidebar_density: 'Controla cuántos metadatos muestra la lista de sesiones en la barra lateral izquierda.',
|
||
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',
|
||
// Providers panel (English fallback — native translations welcome in follow-up PRs)
|
||
providers_tab_title: 'Providers',
|
||
providers_section_title: 'Providers',
|
||
providers_section_meta: 'Manage API keys for AI providers. Changes take effect immediately.',
|
||
providers_status_configured: 'API key configured',
|
||
providers_status_not_configured: 'No API key',
|
||
providers_status_oauth: 'OAuth',
|
||
providers_status_api_key: 'API key',
|
||
providers_status_not_configured_label: 'Not configured',
|
||
providers_oauth_hint: 'Authenticated via OAuth. No API key needed.',
|
||
providers_save: 'Save',
|
||
providers_remove: 'Remove',
|
||
providers_saving: 'Saving…',
|
||
providers_removing: 'Removing…',
|
||
providers_enter_key: 'Please enter an API key',
|
||
providers_empty: 'No configurable providers found.',
|
||
providers_key_updated: 'API key saved',
|
||
providers_key_removed: 'API key removed',
|
||
providers_key_placeholder_new: 'sk-...',
|
||
providers_key_placeholder_replace: 'Enter new key to replace…',
|
||
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_skip: 'Omitir configuración',
|
||
onboarding_skipped: 'Configuración omitida — se usa la configuración existente.',
|
||
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',
|
||
provider_category_easy_start: 'Inicio rápido',
|
||
provider_category_self_hosted: 'Local / Open source',
|
||
provider_category_specialized: 'Especializados',
|
||
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_will_replace: 'Se reemplazará',
|
||
onboarding_password_keep_existing: 'Mantener la contraseña actual',
|
||
onboarding_password_remains_disabled: 'Seguirá desactivada',
|
||
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',
|
||
// panel/runtime i18n
|
||
error_prefix: 'Error: ',
|
||
not_available: 'N/A',
|
||
never: 'never',
|
||
add: 'Add',
|
||
add_failed: 'Add failed: ',
|
||
remove_failed: 'Remove failed: ',
|
||
switch_failed: 'Switch failed: ',
|
||
name_required: 'Name is required',
|
||
content_required: 'Content is required',
|
||
view: 'View',
|
||
dismiss: 'Dismiss',
|
||
disable: 'Disable',
|
||
cron_no_jobs: 'No scheduled jobs found.',
|
||
cron_status_off: 'off',
|
||
cron_status_paused: 'paused',
|
||
cron_status_error: 'error',
|
||
cron_status_active: 'active',
|
||
cron_status_running: 'running\u2026',
|
||
cron_next: 'Next',
|
||
cron_last: 'Last',
|
||
cron_run_now: 'Run now',
|
||
cron_pause: 'Pause',
|
||
cron_resume: 'Resume',
|
||
cron_job_name_placeholder: 'Job name',
|
||
cron_schedule_placeholder: 'Schedule',
|
||
cron_prompt_placeholder: 'Prompt',
|
||
cron_last_output: 'Last output',
|
||
cron_all_runs: 'All runs',
|
||
cron_hide_runs: 'Hide runs',
|
||
cron_no_runs_yet: '(no runs yet)',
|
||
cron_schedule_required_example: 'Schedule is required (e.g. "0 9 * * *" or "every 1h")',
|
||
cron_schedule_required: 'Schedule is required',
|
||
cron_prompt_required: 'Prompt is required',
|
||
cron_job_created: 'Job created',
|
||
cron_job_triggered: 'Job triggered',
|
||
cron_job_paused: 'Job paused',
|
||
cron_job_resumed: 'Job resumed',
|
||
cron_job_updated: 'Job updated',
|
||
cron_delete_confirm_title: 'Delete cron job',
|
||
cron_delete_confirm_message: 'This cannot be undone.',
|
||
cron_job_deleted: 'Job deleted',
|
||
cron_completion_status: (name, status) => `Cron "${name}" ${status}`,
|
||
status_failed: 'failed',
|
||
status_completed: 'completed',
|
||
todos_no_active: 'No active task list in this session.',
|
||
clear_conversation_title: 'Clear conversation',
|
||
clear_conversation_message: 'Clear all messages? This cannot be undone.',
|
||
clear_failed: 'Clear failed: ',
|
||
skills_no_match: 'No skills match.',
|
||
linked_files: 'Linked Files',
|
||
skill_load_failed: 'Could not load skill: ',
|
||
skill_file_load_failed: 'Could not load file: ',
|
||
skill_name_required: 'Skill name is required',
|
||
skill_updated: 'Skill updated',
|
||
skill_created: 'Skill created',
|
||
skill_deleted: 'Skill deleted',
|
||
skill_delete_confirm: 'Delete skill "{0}"?',
|
||
skills_empty_title: 'Select a skill',
|
||
skills_empty_sub: 'Pick a skill from the sidebar to view its contents, or create a new one.',
|
||
skills_edit: 'Edit',
|
||
skills_delete: 'Delete',
|
||
skills_back_to: 'Back to {0}',
|
||
tasks_empty_title: 'Select a scheduled job',
|
||
tasks_empty_sub: 'Pick a job from the sidebar to view its details and runs, or create a new one.',
|
||
workspaces_empty_title: 'Select a space',
|
||
workspaces_empty_sub: 'Pick a space from the sidebar to view its files and settings, or add a new one.',
|
||
profiles_empty_title: 'Select a profile',
|
||
profiles_empty_sub: 'Pick an agent profile from the sidebar to view and edit its settings, or create a new one.',
|
||
memory_notes_label: 'memory (notes)',
|
||
memory_saved: 'Memory saved',
|
||
my_notes: 'My Notes',
|
||
user_profile: 'User Profile',
|
||
no_notes_yet: 'No notes yet.',
|
||
no_profile_yet: 'No profile yet.',
|
||
workspace_choose_path: 'Choose workspace path',
|
||
workspace_choose_path_meta: 'Add a validated path and switch this conversation',
|
||
workspace_manage: 'Manage workspaces',
|
||
workspace_manage_meta: 'Open the Spaces panel',
|
||
workspace_use_title: 'Use in current session',
|
||
workspace_use: 'Use',
|
||
workspace_add_path_placeholder: 'Add workspace path (e.g. /home/user/my-project)',
|
||
workspace_paths_validated_hint: 'Paths are validated as existing directories before saving.',
|
||
workspace_added: 'Workspace added',
|
||
workspace_renamed: 'Espacio renombrado',
|
||
workspace_remove_confirm_title: 'Remove workspace',
|
||
workspace_remove_confirm_message: (path) => `Remove "${path}"?`,
|
||
workspace_removed: 'Workspace removed',
|
||
workspace_switch_prompt_title: 'Switch workspace',
|
||
workspace_switch_prompt_message: 'Enter an absolute workspace path to add and switch this conversation to.',
|
||
workspace_switch_prompt_confirm: 'Switch',
|
||
workspace_switch_prompt_placeholder: '/Users/you/project',
|
||
workspace_not_added: 'Workspace was not added',
|
||
workspace_already_saved: 'Workspace already saved — choose it from the list',
|
||
workspace_busy_switch: 'Cannot switch workspace while agent is running',
|
||
discard_file_edits_title: 'Discard file edits?',
|
||
discard_file_edits_message: 'Switching workspaces will discard unsaved file edits in the preview.',
|
||
workspace_switched_to: (name) => `Switched to ${name}`,
|
||
profiles_no_profiles: 'No profiles found.',
|
||
profile_api_keys_configured: 'API keys configured',
|
||
profile_gateway_running: 'Gateway running',
|
||
profile_gateway_stopped: 'Gateway stopped',
|
||
profile_active: 'ACTIVE',
|
||
profile_no_configuration: 'No configuration',
|
||
profile_skill_count: (count) => `${count} habilidad${count === 1 ? '' : 'es'}`,
|
||
profile_use: 'Use',
|
||
profile_switch_title: 'Switch to this profile',
|
||
profile_delete_title: 'Eliminar este perfil',
|
||
profile_default_label: '(predeterminado)',
|
||
profile_name_placeholder: 'Nombre del perfil (minúsculas, a-z, 0-9, guiones)',
|
||
profile_clone_label: 'Clonar configuración del perfil activo',
|
||
profile_base_url_placeholder: 'URL base (opcional, p. ej. http://localhost:11434)',
|
||
profile_api_key_placeholder: 'Clave API (opcional)',
|
||
manage_profiles: 'Manage profiles',
|
||
profiles_load_failed: 'Failed to load profiles',
|
||
profiles_busy_switch: 'Cannot switch profiles while agent is running',
|
||
profile_switched_new_conversation: (name) => `Switched to profile: ${name} — new conversation started`,
|
||
profile_switched: (name) => `Switched to profile: ${name}`,
|
||
profile_name_rule: 'Lowercase letters, numbers, hyphens, underscores only',
|
||
profile_base_url_rule: 'Base URL must start with http:// or https://',
|
||
profile_created: (name) => `Profile created: ${name}`,
|
||
profile_delete_confirm_title: (name) => `Delete profile "${name}"?`,
|
||
profile_delete_confirm_message: 'Todas las sesiones, configuración, habilidades y memoria de este perfil se eliminarán de forma permanente. Esta acción no se puede deshacer.',
|
||
profile_deleted: (name) => `Profile deleted: ${name}`,
|
||
active_conversation_none: 'No active conversation selected.',
|
||
active_conversation_meta: (title, count) => `${title} · ${count} message${count === 1 ? '' : 's'}`,
|
||
settings_unsaved_changes: 'You have unsaved changes.',
|
||
sign_out_failed: 'Sign out failed: ',
|
||
disable_auth_confirm_title: 'Disable password protection',
|
||
disable_auth_confirm_message: 'Anyone will be able to access this instance.',
|
||
auth_disabled: 'Auth disabled — password protection removed',
|
||
disable_auth_failed: 'Failed to disable auth: ',
|
||
bg_error_single: (title) => `"${title}" has encountered an error`,
|
||
bg_error_multi: (count) => `${count} sessions have encountered an error`,
|
||
// skill form
|
||
skill_name: 'Nombre',
|
||
skill_category: 'Categoría',
|
||
skill_category_placeholder: 'Opcional, p. ej. devops',
|
||
skill_content: 'Contenido de SKILL.md',
|
||
skill_content_placeholder: 'Frontmatter YAML + cuerpo markdown',
|
||
skill_rename_not_supported: 'No se admite renombrar una habilidad. Crea una nueva y elimina la anterior.',
|
||
skill_metadata: 'Metadatos',
|
||
// cron form
|
||
cron_name_label: 'Nombre',
|
||
cron_name_placeholder: 'Opcional',
|
||
cron_schedule_label: 'Programación',
|
||
cron_schedule_hint: "Expresión cron o abreviatura como 'every 1h'.",
|
||
cron_prompt_label: 'Prompt',
|
||
cron_deliver_label: 'Entregar salida a',
|
||
cron_deliver_local: 'Local (solo guardar salida)',
|
||
cron_skills_label: 'Habilidades',
|
||
cron_skills_placeholder: 'Añadir habilidades (opcional)…',
|
||
cron_skills_edit_hint: 'La lista de habilidades no es editable después de crear.',
|
||
// workspace form
|
||
workspace_name_label: 'Nombre',
|
||
workspace_name_placeholder: 'Nombre opcional',
|
||
workspace_path_label: 'Ruta',
|
||
workspace_path_required: 'La ruta es obligatoria',
|
||
workspace_path_readonly: 'La ruta no se puede cambiar. Solo renombrar.',
|
||
workspace_new_title: 'Nuevo espacio',
|
||
// profile form
|
||
profile_name_label: 'Nombre',
|
||
profile_base_url_label: 'URL base',
|
||
profile_api_key_label: 'Clave API',
|
||
|
||
cmd_status: 'Show session info',
|
||
// Session management and settings keys (en fallback — pending translation)
|
||
session_archive: 'Archive conversation',
|
||
session_archive_desc: 'Hide this conversation until archived is shown',
|
||
session_archive_failed: 'Archive failed: ',
|
||
session_archived: 'Session archived',
|
||
session_delete: 'Delete conversation',
|
||
session_delete_desc: 'Permanently remove this conversation',
|
||
session_duplicate: 'Duplicate conversation',
|
||
session_duplicate_desc: 'Create a copy with the same workspace and model',
|
||
session_duplicate_failed: 'Duplicate failed: ',
|
||
session_duplicated: 'Session duplicated',
|
||
session_move_project: 'Move to project',
|
||
session_move_project_desc_has: 'Change the project for this conversation',
|
||
session_move_project_desc_none: 'Assign a project to this conversation',
|
||
session_pin: 'Pin conversation',
|
||
session_pin_desc: 'Keep this conversation at the top',
|
||
session_pin_failed: 'Pin failed: ',
|
||
session_restore: 'Restore conversation',
|
||
session_restore_desc: 'Bring this conversation back into the main list',
|
||
session_restored: 'Session restored',
|
||
session_unpin: 'Unpin conversation',
|
||
session_unpin_desc: 'Remove from pinned',
|
||
settings_dropdown_appearance: 'Appearance',
|
||
settings_dropdown_conversation: 'Conversation',
|
||
settings_dropdown_preferences: 'Preferences',
|
||
settings_dropdown_providers: 'Providers',
|
||
settings_dropdown_system: 'System',
|
||
settings_heading_subtitle: 'Preferences, conversation tools, and system controls.',
|
||
settings_heading_title: 'Control Center',
|
||
settings_section_appearance_meta: 'Theme, accent colors, and visual style.',
|
||
settings_section_appearance_title: 'Appearance',
|
||
settings_section_conversation_title: 'Conversation',
|
||
settings_section_preferences_meta: 'Defaults and UI behavior for Hermes Web UI.',
|
||
settings_section_preferences_title: 'Preferences',
|
||
settings_section_system_meta: 'Instance version and access controls.',
|
||
settings_check_now: 'Comprobar ahora',
|
||
settings_checking: 'Comprobando\u2026',
|
||
settings_up_to_date: 'Actualizado \u2713',
|
||
settings_updates_available: '{count} actualización(es) disponible(s)',
|
||
settings_updates_disabled: 'Comprobación de actualizaciones desactivada',
|
||
settings_update_check_failed: 'Error al comprobar actualizaciones',
|
||
settings_label_workspace_panel_open: 'Mantener panel de espacio abierto',
|
||
settings_desc_workspace_panel_open: 'Al activar, el panel de archivos se abre automáticamente en cada nueva sesión. Aún puedes cerrarlo manualmente.',
|
||
open_in_browser: 'Abrir en el navegador',
|
||
settings_section_system_title: 'System',
|
||
settings_tab_appearance: 'Appearance',
|
||
settings_tab_conversation: 'Conversation',
|
||
settings_tab_preferences: 'Preferences',
|
||
settings_tab_system: 'System',
|
||
},
|
||
|
||
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',
|
||
clarify_heading: 'Klärung erforderlich',
|
||
clarify_hint: 'Wähle eine Option oder schreibe deine eigene Antwort unten.',
|
||
clarify_other: 'Andere',
|
||
clarify_send: 'Senden',
|
||
clarify_input_placeholder: 'Gib deine Antwort ein…',
|
||
clarify_responding: 'Antwortet\u2026',
|
||
untitled: 'Unbenannt',
|
||
n_messages: (n) => `${n} Nachrichten`,
|
||
queued_label: 'Wird nach Antwort gesendet',
|
||
queued_count: (n) => n === 1 ? '1 in Warteschlange' : `${n} in Warteschlange`,
|
||
queued_cancel: 'Nachricht abbrechen',
|
||
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',
|
||
model_not_found_label: 'Modell nicht gefunden',
|
||
// commands.js
|
||
cmd_help: 'Verfügbare Befehle auflisten',
|
||
cmd_clear: 'Konversationsverlauf löschen',
|
||
cmd_compress: 'Kontext manuell komprimieren (Nutzung: /compress [Thema])',
|
||
cmd_compact_alias: 'Alte Alias für /compress',
|
||
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: 'Darstellung wechseln (Theme: system/dark/light, Skin: default/ares/mono/slate/poseidon/sisyphus/charizard)',
|
||
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',
|
||
command_label: 'Befehl',
|
||
context_compaction_label: 'Kontextkomprimierung',
|
||
reference_only_label: 'Nur Referenz',
|
||
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...',
|
||
compress_running_label: 'Komprimierung',
|
||
compress_complete_label: 'Komprimierung abgeschlossen',
|
||
compress_failed_label: 'Komprimierung fehlgeschlagen',
|
||
focus_label: 'Thema',
|
||
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',
|
||
workspace_empty_no_path: 'Kein Workspace ausgewählt. Wähle einen Workspace unter Einstellungen \u2192 Workspace, um Dateien zu durchsuchen.',
|
||
workspace_empty_dir: 'Dieser Workspace ist leer.',
|
||
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: ',
|
||
workspace_auto_create_folder: 'Ordner erstellen, falls nicht vorhanden',
|
||
folder_add_as_space_btn: 'Als Bereich hinzufügen',
|
||
folder_add_as_space_msg: 'Diesen Ordner als neuen Bereich zur Liste hinzufügen?',
|
||
folder_add_as_space_title: 'Als Bereich hinzufügen?',
|
||
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_skin: 'Skin',
|
||
settings_label_font_size: 'Font size',
|
||
font_size_small: 'Small',
|
||
font_size_default: 'Default',
|
||
font_size_large: 'Large',
|
||
settings_label_language: 'Sprache',
|
||
settings_label_token_usage: 'Token-Verbrauch anzeigen',
|
||
settings_label_sidebar_density: 'Seitenleistendichte',
|
||
cmd_reasoning: 'Toggle thinking visibility (show/hide), set effort level, or check current status',
|
||
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 — Passwortschutz aktiviert und dieser Browser bleibt angemeldet',
|
||
settings_saved_pw_updated: 'Einstellungen gespeichert — Passwort aktualisiert',
|
||
// 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',
|
||
tab_settings: 'Einstellungen',
|
||
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.',
|
||
session_meta_messages: (n) => `${n} Nachr.`,
|
||
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_sidebar_density_compact: 'Kompakt',
|
||
settings_sidebar_density_detailed: 'Detailliert',
|
||
settings_desc_sidebar_density: 'Steuert, wie viele Metadaten die Sitzungsliste in der linken Seitenleiste anzeigt.',
|
||
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',
|
||
// Providers panel (English fallback — native translations welcome in follow-up PRs)
|
||
providers_tab_title: 'Providers',
|
||
providers_section_title: 'Providers',
|
||
providers_section_meta: 'Manage API keys for AI providers. Changes take effect immediately.',
|
||
providers_status_configured: 'API key configured',
|
||
providers_status_not_configured: 'No API key',
|
||
providers_status_oauth: 'OAuth',
|
||
providers_status_api_key: 'API key',
|
||
providers_status_not_configured_label: 'Not configured',
|
||
providers_oauth_hint: 'Authenticated via OAuth. No API key needed.',
|
||
providers_save: 'Save',
|
||
providers_remove: 'Remove',
|
||
providers_saving: 'Saving…',
|
||
providers_removing: 'Removing…',
|
||
providers_enter_key: 'Please enter an API key',
|
||
providers_empty: 'No configurable providers found.',
|
||
providers_key_updated: 'API key saved',
|
||
providers_key_removed: 'API key removed',
|
||
providers_key_placeholder_new: 'sk-...',
|
||
providers_key_placeholder_replace: 'Enter new key to replace…',
|
||
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.',
|
||
onboarding_password_will_enable: 'Wird aktiviert',
|
||
onboarding_password_will_replace: 'Wird ersetzt',
|
||
onboarding_password_keep_existing: 'Aktuelles Passwort beibehalten',
|
||
onboarding_password_remains_disabled: 'Bleibt deaktiviert',
|
||
profile_delete_confirm_message: 'Alle Sitzungen, Konfigurationen, Fähigkeiten und Erinnerungen dieses Profils werden dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.',
|
||
// skill form
|
||
skill_name: 'Name',
|
||
skill_category: 'Kategorie',
|
||
skill_category_placeholder: 'Optional, z. B. devops',
|
||
skill_content: 'SKILL.md-Inhalt',
|
||
skill_content_placeholder: 'YAML-Frontmatter + Markdown-Text',
|
||
skill_rename_not_supported: 'Umbenennen einer Fähigkeit wird nicht unterstützt. Erstelle eine neue und lösche die alte.',
|
||
skill_metadata: 'Metadaten',
|
||
// cron form
|
||
cron_name_label: 'Name',
|
||
cron_name_placeholder: 'Optional',
|
||
cron_schedule_label: 'Zeitplan',
|
||
cron_schedule_hint: "Cron-Ausdruck oder Kurzform wie 'every 1h'.",
|
||
cron_prompt_label: 'Prompt',
|
||
cron_deliver_label: 'Ausgabe senden an',
|
||
cron_deliver_local: 'Lokal (nur speichern)',
|
||
cron_skills_label: 'Fähigkeiten',
|
||
cron_skills_placeholder: 'Fähigkeiten hinzufügen (optional)…',
|
||
cron_skills_edit_hint: 'Die Fähigkeitenliste kann nach der Erstellung nicht bearbeitet werden.',
|
||
// workspace form
|
||
workspace_name_label: 'Name',
|
||
workspace_name_placeholder: 'Optionaler Anzeigename',
|
||
workspace_path_label: 'Pfad',
|
||
workspace_path_required: 'Pfad ist erforderlich',
|
||
workspace_path_readonly: 'Pfad kann nicht geändert werden. Nur umbenennen.',
|
||
workspace_new_title: 'Neuer Bereich',
|
||
// profile form
|
||
profile_name_label: 'Name',
|
||
profile_base_url_label: 'Basis-URL',
|
||
profile_api_key_label: 'API-Schlüssel',
|
||
|
||
// Session management and settings keys (en fallback — pending translation)
|
||
session_archive: 'Archive conversation',
|
||
session_archive_desc: 'Hide this conversation until archived is shown',
|
||
session_archive_failed: 'Archive failed: ',
|
||
session_archived: 'Session archived',
|
||
session_delete: 'Delete conversation',
|
||
session_delete_desc: 'Permanently remove this conversation',
|
||
session_duplicate: 'Duplicate conversation',
|
||
session_duplicate_desc: 'Create a copy with the same workspace and model',
|
||
session_duplicate_failed: 'Duplicate failed: ',
|
||
session_duplicated: 'Session duplicated',
|
||
session_move_project: 'Move to project',
|
||
session_move_project_desc_has: 'Change the project for this conversation',
|
||
session_move_project_desc_none: 'Assign a project to this conversation',
|
||
session_pin: 'Pin conversation',
|
||
session_pin_desc: 'Keep this conversation at the top',
|
||
session_pin_failed: 'Pin failed: ',
|
||
session_restore: 'Restore conversation',
|
||
session_restore_desc: 'Bring this conversation back into the main list',
|
||
session_restored: 'Session restored',
|
||
session_unpin: 'Unpin conversation',
|
||
session_unpin_desc: 'Remove from pinned',
|
||
settings_dropdown_appearance: 'Appearance',
|
||
settings_dropdown_conversation: 'Conversation',
|
||
settings_dropdown_preferences: 'Preferences',
|
||
settings_dropdown_providers: 'Providers',
|
||
settings_dropdown_system: 'System',
|
||
settings_heading_subtitle: 'Preferences, conversation tools, and system controls.',
|
||
settings_heading_title: 'Control Center',
|
||
settings_section_appearance_meta: 'Theme, accent colors, and visual style.',
|
||
settings_section_appearance_title: 'Appearance',
|
||
settings_section_conversation_title: 'Conversation',
|
||
settings_section_preferences_meta: 'Defaults and UI behavior for Hermes Web UI.',
|
||
settings_section_preferences_title: 'Preferences',
|
||
settings_section_system_meta: 'Instance version and access controls.',
|
||
settings_check_now: 'Jetzt prüfen',
|
||
settings_checking: 'Prüfung\u2026',
|
||
settings_up_to_date: 'Aktuell \u2713',
|
||
settings_updates_available: '{count} Update(s) verfügbar',
|
||
settings_updates_disabled: 'Update-Prüfung deaktiviert',
|
||
settings_update_check_failed: 'Update-Prüfung fehlgeschlagen',
|
||
settings_label_workspace_panel_open: 'Arbeitsbereich-Panel standardmäßig öffnen',
|
||
settings_desc_workspace_panel_open: 'Wenn aktiviert, wird der Datei-Browser bei jeder neuen Sitzung automatisch geöffnet. Er kann jederzeit manuell geschlossen werden.',
|
||
open_in_browser: 'Im Browser öffnen',
|
||
settings_section_system_title: 'System',
|
||
settings_tab_appearance: 'Appearance',
|
||
settings_tab_conversation: 'Conversation',
|
||
settings_tab_preferences: 'Preferences',
|
||
settings_tab_system: 'System',
|
||
},
|
||
|
||
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: '处理中…',
|
||
clarify_heading: '需要澄清',
|
||
clarify_hint: '请选择一个选项,或在下方输入你自己的回答。',
|
||
clarify_other: '其他',
|
||
clarify_send: '发送',
|
||
clarify_input_placeholder: '请输入你的回答…',
|
||
clarify_responding: '处理中…',
|
||
untitled: '\u672a\u547d\u540d',
|
||
n_messages: (n) => `${n} \u6761\u6d88\u606f`,
|
||
queued_label: '响应后发送',
|
||
queued_count: (n) => n === 1 ? '1 条排队' : `${n} 条排队`,
|
||
queued_cancel: '取消排队消息',
|
||
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',
|
||
model_not_found_label: '\u672a\u627e\u5230\u6a21\u578b',
|
||
model_custom_label: '\u81ea\u5b9a\u4e49\u6a21\u578b ID',
|
||
model_custom_placeholder: '\u4f8b\u5982 openai/gpt-5.4',
|
||
model_search_placeholder: '\u641c\u7d22\u6a21\u578b\u2026',
|
||
model_search_no_results: '\u672a\u627e\u5230\u6a21\u578b',
|
||
// commands.js
|
||
cmd_help: '\u67e5\u770b\u53ef\u7528\u547d\u4ee4',
|
||
cmd_clear: '\u6e05\u7a7a\u5f53\u524d\u5bf9\u8bdd\u6d88\u606f',
|
||
cmd_compress: '\u624b\u52a8\u538b\u7f29\u5bf9\u8bdd\u4e0a\u4e0b\u6587\uff08\u7528\u6cd5\uff1a/compress [\u4e3b\u9898]\uff09',
|
||
cmd_compact_alias: '\u65e7\u522b\u540d\uff1a/compress',
|
||
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\u5916\u89c2\uff08\u4e3b\u9898\uff1asystem/dark/light\uff0c\u76ae\u80a4\uff1adefault/ares/mono/slate/poseidon/sisyphus/charizard\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',
|
||
command_label: '\u547d\u4ee4',
|
||
context_compaction_label: '\u4e0a\u4e0b\u6587\u538b\u7f29',
|
||
reference_only_label: '\u4ec5\u4f9b\u53c2\u8003',
|
||
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...',
|
||
compress_running_label: '\u538b\u7f29\u4e2d',
|
||
compress_complete_label: '\u538b\u7f29\u5b8c\u6210',
|
||
compress_failed_label: '\u538b\u7f29\u5931\u8d25',
|
||
focus_label: '\u4e3b\u9898',
|
||
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',
|
||
|
||
workspace_empty_no_path: '未选择工作区。请在 设置 → 工作区 中设置工作区以浏览文件。',
|
||
workspace_empty_dir: '此工作区为空。',
|
||
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',
|
||
workspace_auto_create_folder: '\u5982\u679c\u6587\u4ef6\u5939\u4e0d\u5b58\u5728\u5219\u521b\u5efa',
|
||
folder_add_as_space_btn: '\u6dfb\u52a0\u4e3a\u5de5\u4f5c\u533a',
|
||
folder_add_as_space_msg: '\u662f\u5426\u5c06\u6b64\u6587\u4ef6\u5939\u6dfb\u52a0\u4e3a\u65b0\u7684\u5de5\u4f5c\u533a\uff1f',
|
||
folder_add_as_space_title: '\u6dfb\u52a0\u4e3a\u5de5\u4f5c\u533a\uff1f',
|
||
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_skin: '\u76ae\u80a4',
|
||
settings_label_font_size: '\u5b57\u4f53\u5927\u5c0f',
|
||
font_size_small: '\u5c0f',
|
||
font_size_default: '\u9ed8\u8ba4',
|
||
font_size_large: '\u5927',
|
||
settings_label_language: '\u8bed\u8a00',
|
||
settings_label_token_usage: '\u663e\u793a token \u7528\u91cf',
|
||
settings_label_sidebar_density: '侧边栏密度',
|
||
cmd_reasoning: 'Toggle thinking visibility (show/hide), set effort level, or check current status',
|
||
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\uff0c\u5df2\u542f\u7528\u5bc6\u7801\u4fdd\u62a4\uff0c\u5f53\u524d\u6d4f\u89c8\u5668\u4f1a\u4fdd\u6301\u767b\u5f55',
|
||
settings_saved_pw_updated: '\u8bbe\u7f6e\u5df2\u4fdd\u5b58\uff0c\u5bc6\u7801\u5df2\u66f4\u65b0',
|
||
// 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: '工作区',
|
||
tab_profiles: '配置',
|
||
tab_settings: '设置',
|
||
new_conversation: '新建对话',
|
||
filter_conversations: '筛选对话…',
|
||
session_time_unknown: '未知',
|
||
session_time_just_now: '刚刚',
|
||
session_time_minutes_ago: (n) => `${n} 分钟前`,
|
||
session_time_hours_ago: (n) => `${n} 小时前`,
|
||
session_time_days_ago: (n) => `${n} 天前`,
|
||
session_time_last_week: '上周',
|
||
session_time_bucket_today: '今天',
|
||
session_time_bucket_yesterday: '昨天',
|
||
session_time_bucket_this_week: '本周',
|
||
session_time_bucket_last_week: '上周',
|
||
session_time_bucket_older: '更早',
|
||
scheduled_jobs: '定时任务',
|
||
new_job: '新任务',
|
||
search_skills: '搜索技能…',
|
||
new_skill: '新技能',
|
||
save_skill: '保存技能',
|
||
personal_memory: '个人记忆',
|
||
current_task_list: '当前任务列表',
|
||
workspace_desc: '为你的会话添加并切换工作区。',
|
||
session_meta_messages: (n) => `${n} 条消息`,
|
||
new_profile: '新配置',
|
||
transcript: '记录',
|
||
download_transcript: '下载为 Markdown',
|
||
import: '导入',
|
||
editing: '编辑中',
|
||
empty_title: '有什么可以帮您?',
|
||
empty_subtitle: '随时提问、运行命令、浏览文件或管理定时任务。',
|
||
cancel: '取消',
|
||
loading: '加载中…',
|
||
create_job: '创建任务',
|
||
suggest_plan: '帮我规划一个小项目。',
|
||
suggest_schedule: '今天有什么安排?',
|
||
suggest_files: '这个工作区有哪些文件?',
|
||
sign_out: '退出登录',
|
||
// Providers panel (English fallback — native translations welcome in follow-up PRs)
|
||
providers_tab_title: 'Providers',
|
||
providers_section_title: 'Providers',
|
||
providers_section_meta: 'Manage API keys for AI providers. Changes take effect immediately.',
|
||
providers_status_configured: 'API key configured',
|
||
providers_status_not_configured: 'No API key',
|
||
providers_status_oauth: 'OAuth',
|
||
providers_status_api_key: 'API key',
|
||
providers_status_not_configured_label: 'Not configured',
|
||
providers_oauth_hint: 'Authenticated via OAuth. No API key needed.',
|
||
providers_save: 'Save',
|
||
providers_remove: 'Remove',
|
||
providers_saving: 'Saving…',
|
||
providers_removing: 'Removing…',
|
||
providers_enter_key: 'Please enter an API key',
|
||
providers_empty: 'No configurable providers found.',
|
||
providers_key_updated: 'API key saved',
|
||
providers_key_removed: 'API key removed',
|
||
providers_key_placeholder_new: 'sk-...',
|
||
providers_key_placeholder_replace: 'Enter new key to replace…',
|
||
password_placeholder: '输入新密码…',
|
||
disable_auth: '停用认证',
|
||
settings_label_sound: '通知声音',
|
||
settings_label_notifications: '浏览器通知',
|
||
settings_desc_sound: '助手完成回复时播放提示音。',
|
||
settings_desc_notifications: '当标签页在后台时,回复完成后显示系统通知。',
|
||
settings_desc_token_usage: '在助手每次回复下方显示输入/输出 token 数量。也可以用 /usage 切换。',
|
||
settings_sidebar_density_compact: '紧凑',
|
||
settings_sidebar_density_detailed: '详细',
|
||
settings_desc_sidebar_density: '控制左侧会话列表展示多少元信息。',
|
||
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: '输入新密码以设置或更改。留空保持当前设置。',
|
||
// onboarding
|
||
onboarding_badge: '首次运行',
|
||
onboarding_title: '欢迎使用 Hermes Web UI',
|
||
onboarding_lead: '快速引导将验证 Hermes、保存真实的提供商配置、选择工作区和模型,并可选设置密码保护应用。',
|
||
onboarding_back: '返回',
|
||
onboarding_continue: '继续',
|
||
onboarding_skip: '跳过设置',
|
||
onboarding_skipped: '设置已跳过 — 使用现有配置。',
|
||
onboarding_open: '打开 Hermes',
|
||
onboarding_step_system_title: '系统检查',
|
||
onboarding_step_system_desc: '验证 Hermes Agent 与配置可见性。',
|
||
onboarding_step_setup_title: '提供商设置',
|
||
onboarding_step_setup_desc: '保存最小可用的 Hermes 提供商配置。',
|
||
onboarding_step_workspace_title: '工作区 + 模型',
|
||
onboarding_step_workspace_desc: '为新会话和聊天选择默认值。',
|
||
onboarding_step_password_title: '可选密码',
|
||
onboarding_step_password_desc: '在分享前为 Web UI 添加保护。',
|
||
onboarding_step_finish_title: '完成',
|
||
onboarding_step_finish_desc: '确认信息并进入应用。',
|
||
onboarding_notice_system_ready: 'Hermes Agent 看起来可从 Web UI 访问。',
|
||
onboarding_notice_system_unavailable: 'Hermes Agent 尚未完全可用。Bootstrap 可以安装它,但提供商设置可能仍需要终端。',
|
||
onboarding_check_agent: 'Hermes Agent',
|
||
onboarding_check_agent_ready: '已检测且可导入',
|
||
onboarding_check_agent_missing: '缺失或仅部分可导入',
|
||
onboarding_check_password: '密码',
|
||
onboarding_check_password_enabled: '已启用',
|
||
onboarding_check_password_disabled: '尚未启用',
|
||
onboarding_check_provider: '提供商配置',
|
||
onboarding_check_provider_ready: '可开始聊天',
|
||
onboarding_check_provider_partial: '已保存但不完整',
|
||
onboarding_check_provider_pending: '需要验证',
|
||
onboarding_config_file: '配置文件:',
|
||
onboarding_env_file: '.env 文件:',
|
||
onboarding_unknown: '未知',
|
||
onboarding_current_provider: '当前配置:',
|
||
onboarding_missing_imports: '缺失导入:',
|
||
onboarding_notice_setup_required: '请先在此选择一个简单的提供商路径。高级 OAuth 流程暂时仍建议在 Hermes CLI 中完成。',
|
||
onboarding_notice_setup_already_ready: '已检测到可用的 Hermes 提供商配置。你可以保留它,或在这里替换。',
|
||
onboarding_oauth_provider_ready_title: '提供商已完成认证',
|
||
onboarding_oauth_provider_ready_body: '此实例已配置为使用通过 Hermes CLI 设置的 OAuth 提供商(<strong>{provider}</strong>)。这里不需要 API key,点击继续即可完成设置。',
|
||
onboarding_oauth_provider_not_ready_title: 'OAuth 提供商尚未认证',
|
||
onboarding_oauth_provider_not_ready_body: '此实例已配置为使用 <strong>{provider}</strong>,该提供商使用 OAuth 而非 API key。请在终端运行 <code>hermes auth</code> 或 <code>hermes model</code> 完成认证,然后重新加载 Web UI。',
|
||
onboarding_oauth_switch_hint: '或者在下方选择其他提供商,切换到 API key 配置:',
|
||
onboarding_notice_workspace: '这些值复用与正式应用相同的设置 API。',
|
||
onboarding_workspace_label: '工作区',
|
||
onboarding_workspace_or_path: '或输入工作区路径',
|
||
onboarding_workspace_placeholder: '/home/you/workspace',
|
||
onboarding_provider_label: '设置模式',
|
||
onboarding_quick_setup_badge: '快速设置',
|
||
provider_category_easy_start: '快速开始',
|
||
provider_category_self_hosted: '本地 / 开源',
|
||
provider_category_specialized: '专业服务',
|
||
onboarding_api_key_label: 'API key',
|
||
onboarding_api_key_placeholder: '留空可保留已保存的 key',
|
||
onboarding_api_key_help_prefix: '会作为密钥保存到 Hermes .env 文件中,变量名为',
|
||
onboarding_base_url_label: 'Base URL',
|
||
onboarding_base_url_placeholder: 'https://your-endpoint.example/v1',
|
||
onboarding_base_url_help: '用于 OpenAI 兼容路由、自托管服务、LiteLLM、Ollama、LM Studio、vLLM 或类似端点。',
|
||
onboarding_model_label: '默认模型',
|
||
onboarding_workspace_help: '选择设置完成后 Hermes 在新聊天中使用的模型。',
|
||
onboarding_custom_model_placeholder: 'your-model-name',
|
||
onboarding_custom_model_help: '对于自定义端点,请填写服务端要求的精确模型 ID。',
|
||
onboarding_notice_password_enabled: '已配置密码。仅在你想替换时输入新密码。',
|
||
onboarding_notice_password_recommended: '可选,但如果你会把 UI 暴露到 localhost 之外,建议设置。',
|
||
onboarding_password_label: '密码(可选)',
|
||
onboarding_password_placeholder: '留空则跳过',
|
||
onboarding_password_help: '密码通过现有设置 API 保存,并在服务端进行哈希处理。',
|
||
onboarding_notice_finish: '你之后仍可在设置中修改这些选项。',
|
||
onboarding_not_set: '未设置',
|
||
onboarding_password_will_enable: '将启用',
|
||
onboarding_password_will_replace: '将被替换',
|
||
onboarding_password_keep_existing: '保留当前密码',
|
||
onboarding_password_remains_disabled: '将保持禁用',
|
||
onboarding_password_skipped: '暂时跳过',
|
||
onboarding_finish_help: '完成后会在设置中写入 <code>onboarding_completed</code>,并进入常规应用界面。',
|
||
onboarding_error_choose_workspace: '继续前请先选择工作区。',
|
||
onboarding_error_choose_model: '继续前请先选择模型。',
|
||
onboarding_error_provider_required: '继续前请先选择设置模式。',
|
||
onboarding_error_base_url_required: '自定义端点必须填写 Base URL。',
|
||
onboarding_error_workspace_required: '必须填写工作区。',
|
||
onboarding_error_model_required: '必须填写模型。',
|
||
onboarding_complete: '引导完成',
|
||
// panel/runtime i18n
|
||
error_prefix: '错误:',
|
||
not_available: '无',
|
||
never: '从未',
|
||
add: '添加',
|
||
add_failed: '添加失败:',
|
||
remove_failed: '移除失败:',
|
||
switch_failed: '切换失败:',
|
||
name_required: '名称不能为空',
|
||
content_required: '内容不能为空',
|
||
view: '查看',
|
||
dismiss: '忽略',
|
||
disable: '停用',
|
||
cron_no_jobs: '未找到定时任务。',
|
||
cron_status_off: '关闭',
|
||
cron_status_paused: '暂停',
|
||
cron_status_error: '错误',
|
||
cron_status_active: '运行中',
|
||
cron_status_running: '执行中\u2026',
|
||
cron_next: '下次',
|
||
cron_last: '上次',
|
||
cron_run_now: '立即运行',
|
||
cron_pause: '暂停',
|
||
cron_resume: '恢复',
|
||
cron_job_name_placeholder: '任务名称',
|
||
cron_schedule_placeholder: '调度表达式',
|
||
cron_prompt_placeholder: '提示词',
|
||
cron_last_output: '最近输出',
|
||
cron_all_runs: '全部运行记录',
|
||
cron_hide_runs: '隐藏记录',
|
||
cron_no_runs_yet: '(暂无运行记录)',
|
||
cron_schedule_required_example: '必须填写调度(例如 "0 9 * * *" 或 "every 1h")',
|
||
cron_schedule_required: '必须填写调度',
|
||
cron_prompt_required: '必须填写提示词',
|
||
cron_job_created: '任务已创建',
|
||
cron_job_triggered: '任务已触发',
|
||
cron_job_paused: '任务已暂停',
|
||
cron_job_resumed: '任务已恢复',
|
||
cron_job_updated: '任务已更新',
|
||
cron_delete_confirm_title: '删除定时任务',
|
||
cron_delete_confirm_message: '此操作无法撤销。',
|
||
cron_job_deleted: '任务已删除',
|
||
cron_completion_status: (name, status) => `定时任务“${name}”${status}`,
|
||
status_failed: '失败',
|
||
status_completed: '完成',
|
||
todos_no_active: '此会话暂无活动任务列表。',
|
||
clear_conversation_title: '清空对话',
|
||
clear_conversation_message: '要清空所有消息吗?此操作无法撤销。',
|
||
clear_failed: '清空失败:',
|
||
skills_no_match: '没有匹配的技能。',
|
||
linked_files: '关联文件',
|
||
skill_load_failed: '加载技能失败:',
|
||
skill_file_load_failed: '加载文件失败:',
|
||
skill_name_required: '技能名称不能为空',
|
||
skill_updated: '技能已更新',
|
||
skill_created: '技能已创建',
|
||
memory_notes_label: '记忆(备注)',
|
||
memory_saved: '记忆已保存',
|
||
my_notes: '我的备注',
|
||
user_profile: '用户画像',
|
||
no_notes_yet: '暂无备注。',
|
||
no_profile_yet: '暂无用户画像。',
|
||
workspace_choose_path: '选择工作区路径',
|
||
workspace_choose_path_meta: '添加已校验路径并切换当前会话',
|
||
workspace_manage: '管理工作区',
|
||
workspace_manage_meta: '打开 Spaces 面板',
|
||
workspace_use_title: '用于当前会话',
|
||
workspace_use: '使用',
|
||
workspace_add_path_placeholder: '添加工作区路径(例如 /home/user/my-project)',
|
||
workspace_paths_validated_hint: '保存前会校验路径是否为已存在目录。',
|
||
workspace_added: '工作区已添加',
|
||
workspace_renamed: '工作区已重命名',
|
||
workspace_remove_confirm_title: '移除工作区',
|
||
workspace_remove_confirm_message: (path) => `要移除"${path}"吗?`,
|
||
workspace_removed: '工作区已移除',
|
||
workspace_switch_prompt_title: '切换工作区',
|
||
workspace_switch_prompt_message: '输入绝对路径以添加并切换当前会话的工作区。',
|
||
workspace_switch_prompt_confirm: '切换',
|
||
workspace_switch_prompt_placeholder: '/Users/you/project',
|
||
workspace_not_added: '工作区未添加成功',
|
||
workspace_already_saved: '工作区已存在,请在列表中选择',
|
||
workspace_busy_switch: 'Agent 运行中,无法切换工作区',
|
||
discard_file_edits_title: '放弃文件编辑?',
|
||
discard_file_edits_message: '切换工作区将丢弃预览区未保存的文件修改。',
|
||
workspace_switched_to: (name) => `已切换到 ${name}`,
|
||
profiles_no_profiles: '未找到配置档。',
|
||
profile_api_keys_configured: '已配置 API 密钥',
|
||
profile_gateway_running: '网关运行中',
|
||
profile_gateway_stopped: '网关已停止',
|
||
profile_active: '当前',
|
||
profile_no_configuration: '无配置',
|
||
profile_skill_count: (count) => `${count} 个技能`,
|
||
profile_use: '使用',
|
||
profile_switch_title: '切换到此配置档',
|
||
profile_delete_title: '删除此配置档',
|
||
profile_default_label: '(默认)',
|
||
profile_name_placeholder: '配置档名称(小写字母、a-z、0-9、连字符)',
|
||
profile_clone_label: '复制当前配置档的配置',
|
||
profile_base_url_placeholder: 'Base URL(可选,例如 http://localhost:11434)',
|
||
profile_api_key_placeholder: 'API 密钥(可选)',
|
||
manage_profiles: '管理配置档',
|
||
profiles_load_failed: '加载配置档失败',
|
||
profiles_busy_switch: 'Agent 运行中,无法切换配置档',
|
||
profile_switched_new_conversation: (name) => `已切换到配置档:${name},并新建对话`,
|
||
profile_switched: (name) => `已切换到配置档:${name}`,
|
||
profile_name_rule: '仅允许小写字母、数字、连字符和下划线',
|
||
profile_base_url_rule: 'Base URL 必须以 http:// 或 https:// 开头',
|
||
profile_created: (name) => `配置档已创建:${name}`,
|
||
profile_delete_confirm_title: (name) => `删除配置档“${name}”?`,
|
||
profile_delete_confirm_message: '该配置档的所有会话、配置、技能和记忆将被永久删除。此操作无法撤销。',
|
||
profile_deleted: (name) => `配置档已删除:${name}`,
|
||
active_conversation_none: '当前未选择活动会话。',
|
||
active_conversation_meta: (title, count) => `${title} · ${count} 条消息`,
|
||
settings_unsaved_changes: '你有未保存的更改。',
|
||
sign_out_failed: '退出登录失败:',
|
||
disable_auth_confirm_title: '停用密码保护',
|
||
disable_auth_confirm_message: '任何人都可以访问此实例。',
|
||
auth_disabled: '认证已停用,密码保护已移除',
|
||
disable_auth_failed: '停用认证失败:',
|
||
bg_error_single: (title) => `“${title}”出现错误`,
|
||
bg_error_multi: (count) => `${count} 个会话出现错误`,
|
||
skill_deleted: '技能已删除',
|
||
skill_delete_confirm: '删除技能 "{0}"?',
|
||
skills_empty_title: '选择一个技能',
|
||
skills_empty_sub: '从侧边栏中选择一个技能以查看其内容,或创建一个新技能。',
|
||
skills_edit: '编辑',
|
||
skills_delete: '删除',
|
||
skills_back_to: '返回 {0}',
|
||
tasks_empty_title: '选择一个计划任务',
|
||
tasks_empty_sub: '从侧边栏中选择一个任务以查看其详情和运行记录,或创建一个新任务。',
|
||
workspaces_empty_title: '选择一个空间',
|
||
workspaces_empty_sub: '从侧边栏中选择一个空间以查看其文件和设置,或添加一个新空间。',
|
||
profiles_empty_title: '选择一个配置文件',
|
||
profiles_empty_sub: '从侧边栏中选择一个代理配置文件以查看和编辑其设置,或创建一个新配置文件。',
|
||
// skill form
|
||
skill_name: '名称',
|
||
skill_category: '分类',
|
||
skill_category_placeholder: '可选,例如 devops',
|
||
skill_content: 'SKILL.md 内容',
|
||
skill_content_placeholder: 'YAML frontmatter + markdown 正文',
|
||
skill_rename_not_supported: '不支持重命名技能。请创建新技能并删除旧技能。',
|
||
skill_metadata: '元数据',
|
||
// cron form
|
||
cron_name_label: '名称',
|
||
cron_name_placeholder: '可选',
|
||
cron_schedule_label: '计划',
|
||
cron_schedule_hint: "Cron 表达式或简写,例如 'every 1h'。",
|
||
cron_prompt_label: '提示词',
|
||
cron_deliver_label: '输出位置',
|
||
cron_deliver_local: '本地(仅保存输出)',
|
||
cron_skills_label: '技能',
|
||
cron_skills_placeholder: '添加技能(可选)…',
|
||
cron_skills_edit_hint: '创建后无法再编辑技能列表。',
|
||
// workspace form
|
||
workspace_name_label: '名称',
|
||
workspace_name_placeholder: '可选的友好名称',
|
||
workspace_path_label: '路径',
|
||
workspace_path_required: '路径是必填项',
|
||
workspace_path_readonly: '路径不可更改。只能重命名。',
|
||
workspace_new_title: '新空间',
|
||
// profile form
|
||
profile_name_label: '名称',
|
||
profile_base_url_label: '基础 URL',
|
||
profile_api_key_label: 'API 密钥',
|
||
|
||
// Session management and settings keys (en fallback — pending translation)
|
||
session_archive: 'Archive conversation',
|
||
session_archive_desc: 'Hide this conversation until archived is shown',
|
||
session_archive_failed: 'Archive failed: ',
|
||
session_archived: 'Session archived',
|
||
session_delete: 'Delete conversation',
|
||
session_delete_desc: 'Permanently remove this conversation',
|
||
session_duplicate: 'Duplicate conversation',
|
||
session_duplicate_desc: 'Create a copy with the same workspace and model',
|
||
session_duplicate_failed: 'Duplicate failed: ',
|
||
session_duplicated: 'Session duplicated',
|
||
session_move_project: 'Move to project',
|
||
session_move_project_desc_has: 'Change the project for this conversation',
|
||
session_move_project_desc_none: 'Assign a project to this conversation',
|
||
session_pin: 'Pin conversation',
|
||
session_pin_desc: 'Keep this conversation at the top',
|
||
session_pin_failed: 'Pin failed: ',
|
||
session_restore: 'Restore conversation',
|
||
session_restore_desc: 'Bring this conversation back into the main list',
|
||
session_restored: 'Session restored',
|
||
session_unpin: 'Unpin conversation',
|
||
session_unpin_desc: 'Remove from pinned',
|
||
settings_dropdown_appearance: 'Appearance',
|
||
settings_dropdown_conversation: 'Conversation',
|
||
settings_dropdown_preferences: 'Preferences',
|
||
settings_dropdown_providers: 'Providers',
|
||
settings_dropdown_system: 'System',
|
||
settings_heading_subtitle: 'Preferences, conversation tools, and system controls.',
|
||
settings_heading_title: 'Control Center',
|
||
settings_section_appearance_meta: 'Theme, accent colors, and visual style.',
|
||
settings_section_appearance_title: 'Appearance',
|
||
settings_section_conversation_title: 'Conversation',
|
||
settings_section_preferences_meta: 'Defaults and UI behavior for Hermes Web UI.',
|
||
settings_section_preferences_title: 'Preferences',
|
||
settings_section_system_meta: 'Instance version and access controls.',
|
||
settings_check_now: '立即检查',
|
||
settings_checking: '检查中\u2026',
|
||
settings_up_to_date: '已是最新 \u2713',
|
||
settings_updates_available: '有 {count} 个更新可用',
|
||
settings_updates_disabled: '更新检查已禁用',
|
||
settings_update_check_failed: '更新检查失败',
|
||
settings_label_workspace_panel_open: '默认保持工作区面板打开',
|
||
settings_desc_workspace_panel_open: '启用后,工作区/文件浏览器面板会在每次新会话时自动打开。您仍可随时手动关闭。',
|
||
open_in_browser: '在浏览器中打开',
|
||
settings_section_system_title: 'System',
|
||
settings_tab_appearance: 'Appearance',
|
||
settings_tab_conversation: 'Conversation',
|
||
settings_tab_preferences: 'Preferences',
|
||
settings_tab_system: 'System',
|
||
},
|
||
|
||
// 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\u5be9\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\u7d55',
|
||
approval_btn_deny_title: '\u62d2\u7edd — \u4e0d\u57f7\u884c\u6b64\u547d\u4ee4',
|
||
approval_responding: '\u8655\u7406\u4e2d\u2026',
|
||
clarify_heading: '\u9700\u8981\u91d0\u6e05',
|
||
clarify_hint: '\u8acb\u9078\u64c7\u4e00\u500b\u9078\u9805\uff0c\u6216\u5728\u4e0b\u65b9\u8f38\u5165\u4f60\u81ea\u5df1\u7684\u56de\u7b54\u3002',
|
||
clarify_other: '\u5176\u4ed6',
|
||
clarify_send: '\u9001\u51fa',
|
||
clarify_input_placeholder: '\u8f38\u5165\u4f60\u7684\u56de\u7b54\u2026',
|
||
clarify_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',
|
||
model_not_found_label: '\u672a\u627e\u5230\u6a21\u578b',
|
||
// commands.js
|
||
cmd_help: '\u67e5\u770b\u53ef\u7528\u547d\u4ee4',
|
||
cmd_clear: '\u6e05\u7a7a\u7576\u524d\u5c0d\u8a71\u8a0a\u606f',
|
||
cmd_compress: '\u624b\u52d5\u58d3\u7e2e\u5c0d\u8a71\u4e0a\u4e0b\u6587\uff08\u7528\u6cd5\uff1a/compress [\u4e3b\u984c]\uff09',
|
||
cmd_compact_alias: '\u820a\u5225\u540d\uff1a/compress',
|
||
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\u5916\u89c0\uff08\u4e3b\u984c\uff1asystem/dark/light\uff0c\u76ae\u819a\uff1adefault/ares/mono/slate/poseidon/sisyphus/charizard\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',
|
||
command_label: '\u547d\u4ee4',
|
||
context_compaction_label: '\u4e0a\u4e0b\u6587\u58d3\u7e2e',
|
||
reference_only_label: '\u50c5\u4f9b\u53c3\u8003',
|
||
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...',
|
||
compress_running_label: '\u58d3\u7e2e\u4e2d',
|
||
compress_complete_label: '\u58d3\u7e2e\u5b8c\u6210',
|
||
compress_failed_label: '\u58d3\u7e2e\u5931\u6557',
|
||
focus_label: '\u4e3b\u984c',
|
||
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',
|
||
|
||
workspace_empty_no_path: '未選擇工作區。請在 設定 → 工作區 中設定工作區以瀏覽檔案。',
|
||
workspace_empty_dir: '此工作區為空。',
|
||
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',
|
||
workspace_auto_create_folder: '\u8cc7\u6599\u593e\u4e0d\u5b58\u5728\u6642\u5247\u5efa\u7acb',
|
||
folder_add_as_space_btn: '\u65b0\u589e\u70ba\u5de5\u4f5c\u5340',
|
||
folder_add_as_space_msg: '\u662f\u5426\u5c07\u6b64\u8cc7\u6599\u593e\u65b0\u589e\u70ba\u5de5\u4f5c\u5340\uff1f',
|
||
folder_add_as_space_title: '\u65b0\u589e\u70ba\u5de5\u4f5c\u5340\uff1f',
|
||
remove_title: '\u79fb\u9664',
|
||
empty_dir: '(空)',
|
||
upload_failed: '上傳失敗:',
|
||
all_uploads_failed: (n) => `${n} 個檔案全部上傳失敗`,
|
||
session_pin: '釘選對話',
|
||
session_unpin: '取消釘選',
|
||
session_pin_desc: '將此對話置頂',
|
||
session_unpin_desc: '從置頂移除',
|
||
session_pin_failed: '釘選失敗:',
|
||
session_move_project: '移至專案',
|
||
session_move_project_desc_has: '變更此對話的專案',
|
||
session_move_project_desc_none: '為此對話指定專案',
|
||
session_archive: '封存對話',
|
||
session_restore: '還原對話',
|
||
session_archive_desc: '隱藏此對話,直到開啟顯示封存',
|
||
session_restore_desc: '將此對話移回主清單',
|
||
session_archived: '對話已封存',
|
||
session_restored: '對話已還原',
|
||
session_archive_failed: '封存失敗:',
|
||
session_duplicate: '複製對話',
|
||
session_duplicate_desc: '建立一個相同工作區與模型的副本',
|
||
session_duplicated: '對話已複製',
|
||
session_duplicate_failed: '複製失敗:',
|
||
session_delete: '刪除對話',
|
||
session_delete_desc: '永久移除這個對話',
|
||
// settings panel
|
||
settings_heading_title: '控制中心',
|
||
settings_heading_subtitle: '偏好設定、對話工具與系統控制。',
|
||
settings_section_conversation_title: '對話',
|
||
settings_section_appearance_title: '外觀',
|
||
settings_section_appearance_meta: '主題、強調色與視覺風格。',
|
||
settings_section_preferences_title: '偏好設定',
|
||
settings_section_preferences_meta: 'Hermes Web UI 的預設值與介面行為。',
|
||
settings_section_system_title: '系統',
|
||
settings_section_system_meta: '實例版本與存取控制。',
|
||
settings_check_now: '立即檢查',
|
||
settings_checking: '檢查中\u2026',
|
||
settings_up_to_date: '已是最新 \u2713',
|
||
settings_updates_available: '有 {count} 個更新可用',
|
||
settings_updates_disabled: '更新檢查已禁用',
|
||
settings_update_check_failed: '更新檢查失敗',
|
||
settings_label_workspace_panel_open: '預設保持工作區面板開啓',
|
||
settings_desc_workspace_panel_open: '啟用後,工作區/檔案瀏覽器面板會在每次新會話時自動開啓。您仍可隨時手動關閉。',
|
||
open_in_browser: '在瀏覽器中開啓',
|
||
settings_dropdown_conversation: '對話',
|
||
settings_dropdown_appearance: '外觀',
|
||
settings_dropdown_preferences: '偏好設定',
|
||
settings_dropdown_providers: '供應商',
|
||
settings_dropdown_system: '系統',
|
||
settings_tab_conversation: '對話',
|
||
settings_tab_appearance: '外觀',
|
||
settings_tab_preferences: '偏好設定',
|
||
settings_tab_system: '系統',
|
||
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_skin: '佈景',
|
||
settings_label_font_size: '\u5b57\u9ad4\u5927\u5c0f',
|
||
font_size_small: '\u5c0f',
|
||
font_size_default: '\u9810\u8a2d',
|
||
font_size_large: '\u5927',
|
||
settings_label_language: '\u8a9e\u8a00',
|
||
settings_label_token_usage: '\u986f\u793a token \u7528\u91cf',
|
||
settings_label_sidebar_density: '側邊欄密度',
|
||
cmd_reasoning: '切換思考區塊可見性(顯示/隱藏)或設定努力等級',
|
||
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\u554f\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\uff0c\u5bc6\u78bc\u4fdd\u8b77\u5df2\u555f\u7528\uff0c\u7576\u524d\u700f\u89bd\u5668\u6703\u4fdd\u6301\u767b\u5165',
|
||
settings_saved_pw_updated: '\u8a2d\u5b9a\u5df2\u5132\u5b58\uff0c\u5bc6\u78bc\u5df2\u66f4\u65b0',
|
||
// 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: '待辦',
|
||
tab_workspaces: '\u5de5\u4f5c\u5340',
|
||
new_conversation: '新對話',
|
||
filter_conversations: '篩選對話',
|
||
scheduled_jobs: '排程任務',
|
||
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',
|
||
session_meta_messages: (n) => `${n} 則訊息`,
|
||
new_profile: '\u65b0\u914d\u7f6e\u6a94',
|
||
transcript: '\u8a18\u9304',
|
||
download_transcript: '\u4e0b\u8f09\u8a18\u9304',
|
||
import: '\u5c0e\u5165',
|
||
editing: '\u7de8\u8f2f\u4e2d',
|
||
empty_title: '有什麼可以幫忙?',
|
||
empty_subtitle: '點擊上方按鈕開始對話',
|
||
cancel: '\u53d6\u6d88',
|
||
loading: '\u52a0\u8f09\u4e2d',
|
||
create_job: '\u5efa\u7acb\u4efb\u52d9',
|
||
suggest_plan: '幫我規劃一個小專案',
|
||
suggest_schedule: '今天的時程如何?',
|
||
suggest_files: '這個工作區有哪些檔案?',
|
||
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: '助手完成回答時播放聲音。',
|
||
settings_desc_notifications: '當分頁在後台時,有回答完成時會顯示系統通知。',
|
||
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_sidebar_density_compact: '精簡',
|
||
settings_sidebar_density_detailed: '詳細',
|
||
settings_desc_sidebar_density: '控制左側對話清單要顯示多少額外資訊。',
|
||
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 中的顯示名稱。預設未更改。',
|
||
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',
|
||
onboarding_password_will_enable: '\u5c07\u6703\u555f\u7528',
|
||
onboarding_password_will_replace: '\u5c07\u6703\u53d6\u4ee3',
|
||
onboarding_password_keep_existing: '\u4fdd\u7559\u76ee\u524d\u5bc6\u78bc',
|
||
onboarding_password_remains_disabled: '\u6703\u7e7c\u7e8c\u4fdd\u6301\u95dc\u9589',
|
||
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',
|
||
tab_settings: '\u8a2d\u5b9a',
|
||
profile_delete_confirm_message: '\u6b64\u914d\u7f6e\u6a94\u7684\u6240\u6709\u6703\u8a71\u3001\u8a2d\u5b9a\u3001\u6280\u80fd\u548c\u8a18\u61b6\u5c07\u88ab\u6c38\u4e45\u522a\u9664\u3002\u6b64\u64cd\u4f5c\u7121\u6cd5\u64a4\u92b7\u3002',
|
||
workspace_renamed: '\u5de5\u4f5c\u5340\u5df2\u91cd\u65b0\u547d\u540d',
|
||
cron_name_placeholder: '\u9078\u586b',
|
||
active_conversation_meta: (n) => `${n} 則訊息`,
|
||
all_uploads_failed: (n) => `${n} 個上傳失敗`,
|
||
cron_job_name_placeholder: '\u4efb\u52d9\u540d\u7a31',
|
||
cron_prompt_placeholder: '\u63d0\u793a\u8a5e',
|
||
cron_schedule_placeholder: '\u6392\u7a0b',
|
||
delete_confirm: (name) => `刪除「${name}」?`,
|
||
downloading: (filename) => `正在下載 ${filename}…`,
|
||
n_messages: (n) => `${n} 則訊息`,
|
||
onboarding_api_key_help_prefix: '\u900f\u904e\u4ee5\u4e0b\u65b9\u5f0f\u5132\u5b58\u70ba Hermes .env \u6a94\u6848\u4e2d\u7684\u6a5f\u5bc6',
|
||
onboarding_api_key_label: 'API \u91d1\u9470',
|
||
onboarding_api_key_placeholder: '\u7559\u7a7a\u4ee5\u4fdd\u7559\u5df2\u5132\u5b58\u7684\u91d1\u9470',
|
||
onboarding_back: '\u4e0a\u4e00\u6b65',
|
||
onboarding_badge: '\u9996\u6b21\u57f7\u884c',
|
||
onboarding_base_url_help: '\u7528\u65bc OpenAI \u76f8\u5bb9\u8def\u7531\u5668\u3001\u81ea\u67b6\u4f3a\u670d\u5668\u3001LiteLLM\u3001Ollama\u3001LM Studio\u3001vLLM \u7b49\u7aef\u9ede\u3002',
|
||
onboarding_base_url_label: '\u57fa\u790e URL',
|
||
onboarding_base_url_placeholder: 'https://your-endpoint.example/v1',
|
||
onboarding_check_agent: 'Hermes Agent',
|
||
onboarding_check_agent_missing: '\u907a\u5931\u6216\u50c5\u90e8\u5206\u53ef\u7528',
|
||
onboarding_check_agent_ready: '\u5df2\u5075\u6e2c\u5230\u4e26\u53ef\u4f9b\u532f\u5165',
|
||
onboarding_check_password: '\u5bc6\u78bc',
|
||
onboarding_check_password_disabled: '\u5c1a\u672a\u555f\u7528',
|
||
onboarding_check_password_enabled: '\u5df2\u555f\u7528',
|
||
onboarding_check_provider: '\u63d0\u4f9b\u8005\u914d\u7f6e',
|
||
onboarding_check_provider_partial: '\u5df2\u5132\u5b58\u4f46\u672a\u5b8c\u6210',
|
||
onboarding_check_provider_pending: '\u9700\u8981\u9a57\u8b49',
|
||
onboarding_check_provider_ready: '\u53ef\u958b\u59cb\u804a\u5929',
|
||
onboarding_complete: '\u521d\u59cb\u8a2d\u5b9a\u5b8c\u6210',
|
||
onboarding_config_file: '\u8a2d\u5b9a\u6a94\uff1a',
|
||
onboarding_continue: '\u7e7c\u7e8c',
|
||
onboarding_current_provider: '\u76ee\u524d\u914d\u7f6e\uff1a',
|
||
onboarding_custom_model_help: '\u5c0d\u65bc\u81ea\u8a02\u7aef\u9ede\uff0c\u8acb\u6307\u5b9a\u60a8\u7684\u4f3a\u670d\u5668\u9810\u671f\u7684\u6a21\u578b ID\u3002',
|
||
onboarding_custom_model_placeholder: 'your_model_name',
|
||
onboarding_env_file: '.env \u6a94\u6848\uff1a',
|
||
onboarding_error_base_url_required: '\u81ea\u8a02\u7aef\u9ede\u9700\u8981\u57fa\u790e URL\u3002',
|
||
onboarding_error_choose_model: '\u8acb\u5148\u9078\u64c7\u6a21\u578b\u518d\u7e7c\u7e8c\u3002',
|
||
onboarding_error_choose_workspace: '\u8acb\u5148\u9078\u64c7\u5de5\u4f5c\u5340\u518d\u7e7c\u7e8c\u3002',
|
||
onboarding_error_model_required: '\u9700\u8981\u6a21\u578b\u3002',
|
||
onboarding_error_provider_required: '\u8acb\u5148\u9078\u64c7\u8a2d\u5b9a\u6a21\u5f0f\u518d\u7e7c\u7e8c\u3002',
|
||
onboarding_error_workspace_required: '\u9700\u8981\u5de5\u4f5c\u5340\u3002',
|
||
onboarding_finish_help: '\u5b8c\u6210\u5f8c\uff0c<code>onboarding_completed</code> \u5c07\u5132\u5b58\u5728\u8a2d\u5b9a\u4e2d\uff0c\u60a8\u5c07\u9032\u5165\u6b63\u5e38\u61c9\u7528\u7a0b\u5f0f\u3002',
|
||
onboarding_lead: '\u5feb\u901f\u9010\u6b65\u8a2d\u5b9a\uff1a\u6aa2\u67e5 Hermes\u3001\u5132\u5b58\u63d0\u4f9b\u8005\u914d\u7f6e\u3001\u9078\u64c7\u5de5\u4f5c\u5340\u8207\u6a21\u578b\uff0c\u4e26\u53ef\u9078\u64c7\u52a0\u5165\u5bc6\u78bc\u4fdd\u8b77\u3002',
|
||
onboarding_missing_imports: '\u7f3a\u5c11\u7684\u532f\u5165\uff1a',
|
||
onboarding_model_label: '\u9810\u8a2d\u6a21\u578b',
|
||
onboarding_not_set: '\u672a\u8a2d\u5b9a',
|
||
onboarding_notice_finish: '\u60a8\u4e4b\u5f8c\u53ef\u4ee5\u96a8\u6642\u56de\u5230\u8a2d\u5b9a\u9801\u9762\u8b8a\u66f4\u9019\u4e9b\u503c\u3002',
|
||
onboarding_notice_password_enabled: '\u5bc6\u78bc\u5df2\u8a2d\u5b9a\u3002\u50c5\u5728\u9700\u8981\u53d6\u4ee3\u6642\u8f38\u5165\u65b0\u5bc6\u78bc\u3002',
|
||
onboarding_notice_password_recommended: '\u9078\u586b\uff0c\u4f46\u5efa\u8b70\u5728\u975e localhost \u74b0\u5883\u4f7f\u7528\u3002',
|
||
onboarding_notice_setup_already_ready: '\u5df2\u5075\u6e2c\u5230\u53ef\u7528\u7684 Hermes \u63d0\u4f9b\u8005\u914d\u7f6e\u3002\u60a8\u53ef\u4ee5\u4fdd\u7559\u6216\u5728\u6b64\u53d6\u4ee3\u3002',
|
||
onboarding_notice_setup_required: '\u5728\u6b64\u9078\u64c7\u5feb\u901f\u63d0\u4f9b\u8005\u8a2d\u5b9a\u8def\u5f91\u3002\u9032\u968e OAuth \u8a2d\u5b9a\u4ecd\u8acb\u4f7f\u7528 Hermes CLI\u3002',
|
||
onboarding_notice_system_ready: 'Hermes Agent \u53ef\u5f9e Web UI \u5b58\u53d6\u3002',
|
||
onboarding_notice_system_unavailable: 'Hermes Agent \u5c1a\u672a\u5b8c\u5168\u53ef\u7528\u3002\u5f15\u5c0e\u7a0b\u5f0f\u53ef\u80fd\u5b89\u88dd\u5b83\uff0c\u4f46\u63d0\u4f9b\u8005\u8a2d\u5b9a\u53ef\u80fd\u4ecd\u9700\u7d42\u7aef\u6a5f\u3002',
|
||
onboarding_notice_workspace: '\u9019\u4e9b\u503c\u4f7f\u7528\u8207\u4e00\u822c\u61c9\u7528\u7a0b\u5f0f\u76f8\u540c\u7684\u8a2d\u5b9a API\u3002',
|
||
onboarding_oauth_provider_not_ready_body: '\u6b64\u5be6\u4f8b\u8a2d\u5b9a\u4f7f\u7528 <strong>{provider}</strong>\uff0c\u5b83\u4f7f\u7528 OAuth \u800c\u975e API \u91d1\u9470\u3002\u8acb\u5728\u7d42\u7aef\u6a5f\u4e2d\u57f7\u884c <code>hermes auth</code> \u6216 <code>hermes model</code> \u9032\u884c\u6388\u6b0a\uff0c\u7136\u5f8c\u91cd\u65b0\u6574\u7406 Web UI\u3002',
|
||
onboarding_oauth_provider_not_ready_title: 'OAuth \u63d0\u4f9b\u8005\u5c1a\u672a\u6388\u6b0a',
|
||
onboarding_oauth_provider_ready_body: '\u6b64\u5be6\u4f8b\u8a2d\u5b9a\u4f7f\u7528\u900f\u904e Hermes CLI \u914d\u7f6e\u7684 OAuth \u63d0\u4f9b\u8005\uff08<strong>{provider}</strong>\uff09\u3002\u7121\u9700 API \u91d1\u9470 \u2014 \u6309\u7e7c\u7e8c\u5b8c\u6210\u8a2d\u5b9a\u3002',
|
||
onboarding_oauth_provider_ready_title: '\u63d0\u4f9b\u8005\u5df2\u6388\u6b0a',
|
||
onboarding_oauth_switch_hint: '\u6216\u9078\u64c7\u4e0b\u65b9\u5176\u4ed6\u63d0\u4f9b\u8005\u4ee5\u5207\u63db\u5230 API \u91d1\u9470\u8a2d\u5b9a\uff1a',
|
||
onboarding_open: '\u958b\u555f Hermes',
|
||
onboarding_password_help: '\u5bc6\u78bc\u900f\u904e\u73fe\u6709\u8a2d\u5b9a API \u5132\u5b58\uff0c\u4e26\u5728\u4f3a\u670d\u5668\u7aef\u9032\u884c\u96dc\u6e4a\u8655\u7406\u3002',
|
||
onboarding_password_label: '\u5bc6\u78bc\uff08\u9078\u586b\uff09',
|
||
onboarding_password_placeholder: '\u7559\u7a7a\u4ee5\u8df3\u904e',
|
||
onboarding_password_remains_disabled: '\u5c07\u4fdd\u6301\u505c\u7528',
|
||
onboarding_password_skipped: '\u66ab\u6642\u8df3\u904e',
|
||
onboarding_password_will_enable: '\u5c07\u555f\u7528',
|
||
onboarding_password_will_replace: '\u5c07\u53d6\u4ee3',
|
||
onboarding_provider_label: '\u8a2d\u5b9a\u6a21\u5f0f',
|
||
onboarding_quick_setup_badge: '\u5feb\u901f\u8a2d\u5b9a',
|
||
provider_category_easy_start: '\u5feb\u901f\u958b\u59cb',
|
||
provider_category_self_hosted: '\u672c\u5730 / \u958b\u6e90',
|
||
provider_category_specialized: '\u5c08\u696d\u670d\u52d9',
|
||
onboarding_skip: '\u8df3\u904e\u8a2d\u5b9a',
|
||
onboarding_skipped: '\u5df2\u8df3\u904e\u8a2d\u5b9a \u2014 \u4f7f\u7528\u73fe\u6709\u914d\u7f6e\u3002',
|
||
onboarding_step_finish_desc: '\u6aa2\u8996\u8a2d\u5b9a\u4e26\u9032\u5165\u61c9\u7528\u7a0b\u5f0f\u3002',
|
||
onboarding_step_finish_title: '\u5b8c\u6210',
|
||
onboarding_step_password_desc: '\u5206\u4eab\u524d\u4fdd\u8b77 Web UI\u3002',
|
||
onboarding_step_password_title: '\u9078\u586b\u5bc6\u78bc',
|
||
onboarding_step_setup_desc: '\u5132\u5b58\u6700\u5c0f\u53ef\u7528\u7684 Hermes \u63d0\u4f9b\u8005\u914d\u7f6e\u3002',
|
||
onboarding_step_setup_title: '\u63d0\u4f9b\u8005\u8a2d\u5b9a',
|
||
onboarding_step_system_desc: '\u9a57\u8b49 Hermes Agent \u8207\u914d\u7f6e\u53ef\u898b\u6027\u3002',
|
||
onboarding_step_system_title: '\u7cfb\u7d71\u6aa2\u67e5',
|
||
onboarding_step_workspace_desc: '\u70ba\u65b0\u6703\u8a71\u9078\u64c7\u9810\u8a2d\u503c\u3002',
|
||
onboarding_step_workspace_title: '\u5de5\u4f5c\u5340\u8207\u6a21\u578b',
|
||
onboarding_title: '\u6b61\u8fce\u4f7f\u7528 Hermes Web UI',
|
||
onboarding_unknown: '\u672a\u77e5',
|
||
onboarding_workspace_help: '\u9078\u64c7 Hermes \u5728\u8a2d\u5b9a\u5b8c\u6210\u5f8c\u7528\u65bc\u65b0\u804a\u5929\u5ba4\u7684\u6a21\u578b\u3002',
|
||
onboarding_workspace_label: '\u5de5\u4f5c\u5340',
|
||
onboarding_workspace_or_path: '\u6216\u8f38\u5165\u5de5\u4f5c\u5340\u8def\u5f91',
|
||
onboarding_workspace_placeholder: '/home/you/workspace',
|
||
project_name_prompt: '\u5c08\u6848\u540d\u7a31\uff1a',
|
||
provider_mismatch_warning: (provider) => `提供者不符:會話使用 ${provider}`,
|
||
session_meta_messages: (n) => `${n} 則訊息`,
|
||
settings_label_model: '\u9810\u8a2d\u6a21\u578b',
|
||
skill_created: '\u6280\u80fd\u5df2\u5efa\u7acb',
|
||
skill_file_load_failed: '\u8f09\u5165\u6a94\u6848\u5931\u6557\uff1a',
|
||
skill_load_failed: '\u8f09\u5165\u6280\u80fd\u5931\u6557\uff1a',
|
||
skill_name_required: '\u9700\u8981\u6280\u80fd\u540d\u7a31',
|
||
skill_updated: '\u6280\u80fd\u5df2\u66f4\u65b0',
|
||
skills_no_match: '\u627e\u4e0d\u5230\u7b26\u5408\u7684\u6280\u80fd\u3002',
|
||
slash_skill_badge: '\u6280\u80fd',
|
||
slash_skill_desc: '\u547c\u53eb\u6b64\u6280\u80fd',
|
||
title_change_hint: '\u4f7f\u7528 /title <\u65b0\u540d\u7a31> \u91cd\u65b0\u547d\u540d\u3002',
|
||
title_current: '\u76ee\u524d\u6a19\u984c',
|
||
title_set: '\u6a19\u984c\u5df2\u8a2d\u70ba',
|
||
todos_no_active: '\u6b64\u6703\u8a71\u4e2d\u7121\u6d3b\u8e8d\u4efb\u52d9\u6e05\u55ae\u3002',
|
||
upload_failed: '\u4e0a\u50b3\u5931\u6557\uff1a',
|
||
active_conversation_none: '\u672a\u9078\u53d6\u6d3b\u8e8d\u6703\u8a71\u3002',
|
||
add: '\u65b0\u589e',
|
||
add_failed: '\u65b0\u589e\u5931\u6557\uff1a',
|
||
auth_disabled: '\u9a57\u8b49\u5df2\u505c\u7528 \u2014 \u5bc6\u78bc\u4fdd\u8b77\u5df2\u95dc\u9589',
|
||
cancel_unavailable: '\u7121\u6cd5\u53d6\u6d88\u3002',
|
||
clear_conversation_message: '\u6e05\u9664\u6240\u6709\u8a0a\u606f\uff1f\u6b64\u64cd\u4f5c\u7121\u6cd5\u5fa9\u539f\u3002',
|
||
clear_conversation_title: '\u6e05\u9664\u5c0d\u8a71',
|
||
clear_failed: '\u6e05\u9664\u5931\u6557\uff1a',
|
||
content_required: '\u9700\u8981\u5167\u5bb9',
|
||
create_failed: '\u5efa\u7acb\u5931\u6557\uff1a',
|
||
delete_failed: '\u522a\u9664\u5931\u6557\uff1a',
|
||
disable: '\u505c\u7528',
|
||
disable_auth: '\u505c\u7528\u9a57\u8b49',
|
||
disable_auth_confirm_message: '\u4efb\u4f55\u4eba\u90fd\u53ef\u4ee5\u5b58\u53d6\u6b64\u5be6\u4f8b\u3002',
|
||
disable_auth_confirm_title: '\u505c\u7528\u5bc6\u78bc\u4fdd\u8b77',
|
||
disable_auth_failed: '\u505c\u7528\u9a57\u8b49\u5931\u6557\uff1a',
|
||
discard_file_edits_message: '\u5207\u63db\u5de5\u4f5c\u5340\u5c07\u653e\u68c4\u672a\u5132\u5b58\u7684\u9810\u89bd\u8b8a\u66f4\u3002',
|
||
discard_file_edits_title: '\u653e\u68c4\u6a94\u6848\u7de8\u8f2f\uff1f',
|
||
dismiss: '\u95dc\u9589',
|
||
edit: '\u7de8\u8f2f',
|
||
error_prefix: '\u932f\u8aa4\uff1a',
|
||
linked_files: '\u95dc\u806f\u6a94\u6848',
|
||
manage_profiles: '\u7ba1\u7406\u8a2d\u5b9a\u6a94',
|
||
memory_notes_label: '\u8a18\u61b6\uff08\u5099\u8a3b\uff09',
|
||
model_custom_label: '\u81ea\u8a02\u6a21\u578b ID',
|
||
model_custom_placeholder: '\u4f8b\u5982 openai/gpt-5.4',
|
||
model_search_no_results: '\u627e\u4e0d\u5230\u6a21\u578b',
|
||
model_search_placeholder: '\u641c\u5c0b\u6a21\u578b\u2026',
|
||
my_notes: '\u6211\u7684\u5099\u8a3b',
|
||
name_required: '\u9700\u8981\u540d\u7a31',
|
||
never: '\u5f9e\u4e0d',
|
||
no_active_session: '\u7121\u6d3b\u8e8d\u6703\u8a71',
|
||
no_active_task: '\u7121\u57f7\u884c\u4e2d\u7684\u4efb\u52d9\u53ef\u505c\u6b62\u3002',
|
||
no_notes_yet: '\u5c1a\u7121\u5099\u8a3b\u3002',
|
||
no_profile_yet: '\u5c1a\u7121\u8a2d\u5b9a\u6a94\u3002',
|
||
not_available: '\u7121',
|
||
profile_active: '\u555f\u7528\u4e2d',
|
||
profile_api_key_placeholder: 'API \u91d1\u9470\uff08\u9078\u586b\uff09',
|
||
profile_api_keys_configured: 'API \u91d1\u9470\u5df2\u8a2d\u5b9a',
|
||
profile_base_url_placeholder: '\u57fa\u672c URL\uff08\u9078\u586b\uff0c\u4f8b\u5982 http://localhost:11434\uff09',
|
||
profile_base_url_rule: '\u57fa\u672c URL \u5fc5\u9808\u4ee5 http:// \u6216 https:// \u958b\u982d',
|
||
profile_clone_label: '\u5f9e\u555f\u7528\u4e2d\u7684\u8a2d\u5b9a\u6a94\u8907\u88fd\u914d\u7f6e',
|
||
profile_created: (name) => `設定檔 ${name} 已建立`,
|
||
profile_default_label: '\uff08\u9810\u8a2d\uff09',
|
||
profile_delete_confirm_title: (name) => `刪除設定檔「${name}」?`,
|
||
profile_deleted: (name) => `設定檔 ${name} 已刪除`,
|
||
profile_gateway_running: 'Gateway \u57f7\u884c\u4e2d',
|
||
profile_gateway_stopped: 'Gateway \u5df2\u505c\u6b62',
|
||
profile_name_placeholder: '\u8a2d\u5b9a\u6a94\u540d\u7a31\uff08\u5c0f\u5beb\u5b57\u6bcd\u3001\u6578\u5b57\u3001\u9023\u5b57\u865f\uff09',
|
||
profile_name_rule: '\u50c5\u9650\u5c0f\u5beb\u5b57\u6bcd\u3001\u6578\u5b57\u3001\u9023\u5b57\u865f\u548c\u5e95\u7dda',
|
||
profile_no_configuration: '\u7121\u914d\u7f6e',
|
||
profile_skill_count: (n) => `${n} 個技能`,
|
||
profile_switch_title: '\u5207\u63db\u5230\u6b64\u8a2d\u5b9a\u6a94',
|
||
profile_switched: (name) => `已切換到 ${name}`,
|
||
profile_switched_new_conversation: (name) => `已切換到 ${name}(新會話)`,
|
||
profile_use: '\u4f7f\u7528',
|
||
profiles_busy_switch: 'Agent \u57f7\u884c\u4e2d\u7121\u6cd5\u5207\u63db\u8a2d\u5b9a\u6a94',
|
||
profiles_load_failed: '\u8f09\u5165\u8a2d\u5b9a\u6a94\u5931\u6557',
|
||
profiles_no_profiles: '\u627e\u4e0d\u5230\u8a2d\u5b9a\u6a94\u3002',
|
||
remove: '\u79fb\u9664',
|
||
remove_failed: '\u79fb\u9664\u5931\u6557\uff1a',
|
||
rename_failed: '\u91cd\u65b0\u547d\u540d\u5931\u6557\uff1a',
|
||
retry_failed: '\u91cd\u8a66\u5931\u6557\uff1a',
|
||
save: '\u5132\u5b58',
|
||
save_failed: '\u5132\u5b58\u5931\u6557\uff1a',
|
||
session_time_bucket_last_week: '\u4e0a\u9031',
|
||
session_time_bucket_older: '\u66f4\u65e9',
|
||
session_time_bucket_this_week: '\u672c\u9031',
|
||
session_time_bucket_today: '\u4eca\u5929',
|
||
session_time_bucket_yesterday: '\u6628\u5929',
|
||
session_time_days_ago: (d) => `${d} 天前`,
|
||
session_time_hours_ago: (h) => `${h} 小時前`,
|
||
session_time_just_now: '\u525b\u525b',
|
||
session_time_last_week: '\u4e0a\u9031',
|
||
session_time_minutes_ago: (m) => `${m} 分鐘前`,
|
||
session_time_unknown: '\u672a\u77e5',
|
||
settings_unsaved_changes: '\u60a8\u6709\u672a\u5132\u5b58\u7684\u8b8a\u66f4\u3002',
|
||
sign_out_failed: '\u767b\u51fa\u5931\u6557\uff1a',
|
||
stream_stopped: '\u56de\u61c9\u5df2\u505c\u6b62\u3002',
|
||
switch_failed: '\u5207\u63db\u5931\u6557\uff1a',
|
||
undid_messages_suffix: '\u5247\u8a0a\u606f\u3002',
|
||
undid_n_messages: '\u5df2\u79fb\u9664',
|
||
undo_exchange: '\u5fa9\u539f\u6700\u5f8c\u4e00\u8f2a',
|
||
undo_failed: '\u5fa9\u539f\u5931\u6557\uff1a',
|
||
user_profile: '\u4f7f\u7528\u8005\u8a2d\u5b9a\u6a94',
|
||
view: '\u6aa2\u8996',
|
||
workspace_add_path_placeholder: '\u65b0\u589e\u5de5\u4f5c\u5340\u8def\u5f91\uff08\u4f8b\u5982 /Users/you/project\uff09',
|
||
workspace_added: '\u5de5\u4f5c\u5340\u5df2\u65b0\u589e',
|
||
workspace_already_saved: '\u5de5\u4f5c\u5340\u5df2\u5132\u5b58 \u2014 \u5f9e\u6e05\u55ae\u4e2d\u9078\u53d6',
|
||
workspace_busy_switch: 'Agent \u57f7\u884c\u4e2d\u7121\u6cd5\u5207\u63db\u5de5\u4f5c\u5340',
|
||
workspace_choose_path: '\u9078\u64c7\u5de5\u4f5c\u5340\u8def\u5f91',
|
||
workspace_choose_path_meta: '\u65b0\u589e\u9a57\u8b49\u904e\u7684\u8def\u5f91\u4e26\u5207\u63db\u6b64\u6703\u8a71',
|
||
workspace_manage: '\u7ba1\u7406\u5de5\u4f5c\u5340',
|
||
workspace_manage_meta: '\u958b\u555f Spaces \u9762\u677f',
|
||
workspace_not_added: '\u5de5\u4f5c\u5340\u672a\u65b0\u589e',
|
||
workspace_paths_validated_hint: '\u8def\u5f91\u6703\u5148\u9a57\u8b49\u662f\u5426\u5b58\u5728\u518d\u5132\u5b58\u3002',
|
||
workspace_remove_confirm_message: (name) => `移除工作區「${name}」?`,
|
||
workspace_remove_confirm_title: '\u79fb\u9664\u5de5\u4f5c\u5340',
|
||
workspace_removed: '\u5de5\u4f5c\u5340\u5df2\u79fb\u9664',
|
||
workspace_switch_prompt_confirm: '\u5207\u63db',
|
||
workspace_switch_prompt_message: '\u8f38\u5165\u5de5\u4f5c\u5340\u7684\u7d55\u5c0d\u8def\u5f91\u4ee5\u65b0\u589e\u4e26\u5207\u63db\u6b64\u6703\u8a71\u3002',
|
||
workspace_switch_prompt_placeholder: '/Users/you/project',
|
||
workspace_switch_prompt_title: '\u5207\u63db\u5de5\u4f5c\u5340',
|
||
workspace_switched_to: (name) => `已切換到 ${name}`,
|
||
workspace_use: '\u4f7f\u7528',
|
||
workspace_use_title: '\u5728\u7576\u524d\u6703\u8a71\u4e2d\u4f7f\u7528',
|
||
bg_complete: '\u80cc\u666f\u4efb\u52d9\u5b8c\u6210',
|
||
bg_error_multi: (n) => `${n} 個背景任務失敗`,
|
||
cmd_status: '顯示會話資訊',
|
||
memory_saved: '記憶已儲存',
|
||
profile_delete_title: '刪除此設定檔',
|
||
bg_error_single: (n) => `${n} 個背景任務失敗`,
|
||
bg_failed: '\u80cc\u666f\u4efb\u52d9\u5931\u6557\uff1a',
|
||
bg_label: '\u80cc\u666f\u7d50\u679c\uff1a',
|
||
bg_no_answer: '\uff08\u7121\u56de\u61c9\uff09',
|
||
bg_running: '\u80cc\u666f\u57f7\u884c\u4e2d\u2026',
|
||
btw_asking: '\u6b63\u5728\u8a62\u554f\u9644\u5e36\u554f\u984c\u2026',
|
||
btw_done: '\u9644\u5e36\u554f\u984c\u5df2\u56de\u7b54',
|
||
btw_failed: '\u9644\u5e36\u554f\u984c\u5931\u6557\uff1a',
|
||
btw_label: '\u9644\u5e36\u554f\u984c \u2014 \u4e0d\u5728\u6b77\u53f2\u8a18\u9304\u4e2d',
|
||
btw_no_answer: '\u672a\u6536\u5230\u56de\u61c9\u3002',
|
||
cmd_background: '\u5728\u80cc\u666f\u57f7\u884c\u63d0\u793a\u8a5e',
|
||
cmd_background_usage: '/background <\u63d0\u793a\u8a5e> \u2014 \u5e73\u884c\u57f7\u884c\u4e0d\u963b\u585e',
|
||
cmd_btw: '\u63d0\u51fa\u9644\u5e36\u554f\u984c\uff08\u66ab\u6642\u6027\uff09',
|
||
cmd_btw_usage: '/btw <\u554f\u984c> \u2014 \u4f7f\u7528\u7576\u524d\u6703\u8a71\u80cc\u666f\u63d0\u554f',
|
||
cmd_compact: '\u58d3\u7e2e\u5c0d\u8a71\u4e0a\u4e0b\u6587',
|
||
cmd_retry: '\u91cd\u65b0\u767c\u9001\u6700\u5f8c\u4e00\u689d\u8a0a\u606f',
|
||
cmd_stop: '\u505c\u6b62\u7576\u524d\u56de\u61c9',
|
||
cmd_title: '\u53d6\u5f97\u6216\u8a2d\u5b9a\u6703\u8a71\u6a19\u984c',
|
||
cmd_undo: '\u79fb\u9664\u6700\u5f8c\u4e00\u8f2a\u5c0d\u8a71',
|
||
cmd_voice: '\u5207\u63db\u9ea5\u514b\u98a8\u8f38\u5165',
|
||
cmd_voice_use_mic: '\u9ede\u64ca\u7de8\u8f2f\u5668\u4e2d\u7684\u9ea5\u514b\u98a8\u6309\u9215\u3002',
|
||
cmd_webui_only_session: '\u6b64\u6307\u4ee4\u4e0d\u9069\u7528\u65bc CLI \u532f\u5165\u7684\u6703\u8a71\u3002',
|
||
cron_all_runs: '\u6240\u6709\u57f7\u884c',
|
||
cron_completion_status: (n) => `${n} 次完成`,
|
||
cron_delete_confirm_message: '\u6b64\u64cd\u4f5c\u7121\u6cd5\u5fa9\u539f\u3002',
|
||
cron_delete_confirm_title: '\u522a\u9664\u6392\u7a0b\u4efb\u52d9',
|
||
cron_hide_runs: '\u96b1\u85cf\u57f7\u884c\u8a18\u9304',
|
||
cron_job_created: '\u6392\u7a0b\u4efb\u52d9\u5df2\u5efa\u7acb',
|
||
cron_job_deleted: '\u6392\u7a0b\u4efb\u52d9\u5df2\u522a\u9664',
|
||
cron_job_name_placeholder: '\u4efb\u52d9\u540d\u7a31',
|
||
cron_job_paused: '\u6392\u7a0b\u4efb\u52d9\u5df2\u66ab\u505c',
|
||
cron_job_resumed: '\u6392\u7a0b\u4efb\u52d9\u5df2\u6062\u5fa9',
|
||
cron_job_triggered: '\u6392\u7a0b\u4efb\u52d9\u5df2\u89f8\u767c',
|
||
cron_job_updated: '\u6392\u7a0b\u4efb\u52d9\u5df2\u66f4\u65b0',
|
||
cron_last: '\u4e0a\u6b21',
|
||
cron_last_output: '\u6700\u5f8c\u8f38\u51fa',
|
||
cron_next: '\u4e0b\u6b21',
|
||
cron_no_jobs: '\u627e\u4e0d\u5230\u6392\u7a0b\u4efb\u52d9\u3002',
|
||
cron_no_runs_yet: '\uff08\u5c1a\u7121\u57f7\u884c\u8a18\u9304\uff09',
|
||
cron_pause: '\u66ab\u505c',
|
||
cron_prompt_placeholder: '\u63d0\u793a\u8a5e',
|
||
cron_prompt_required: '\u9700\u8981\u63d0\u793a\u8a5e',
|
||
cron_resume: '\u6062\u5fa9',
|
||
cron_run_now: '\u7acb\u5373\u57f7\u884c',
|
||
cron_schedule_placeholder: '\u6392\u7a0b',
|
||
cron_schedule_required: '\u9700\u8981\u6392\u7a0b',
|
||
cron_schedule_required_example: '\u9700\u8981\u6392\u7a0b\uff08\u4f8b\u5982 "0 9 * * *" \u6216 "every 1h"\uff09',
|
||
cron_status_active: '\u6d3b\u8e8d\u4e2d',
|
||
cron_status_running: '\u57f7\u884c\u4e2d\u2026',
|
||
cron_status_error: '\u932f\u8aa4',
|
||
cron_status_off: '\u672a\u555f\u7528',
|
||
cron_status_paused: '\u5df2\u66ab\u505c',
|
||
providers_empty: '\u627e\u4e0d\u5230\u53ef\u8a2d\u5b9a\u7684\u63d0\u4f9b\u8005\u3002',
|
||
providers_enter_key: '\u8acb\u8f38\u5165 API \u91d1\u9470',
|
||
providers_key_placeholder_new: 'sk-...',
|
||
providers_key_placeholder_replace: '\u8f38\u5165\u65b0\u91d1\u9470\u4ee5\u53d6\u4ee3\u2026',
|
||
providers_key_removed: 'API \u91d1\u9470\u5df2\u79fb\u9664',
|
||
providers_key_updated: 'API \u91d1\u9470\u5df2\u5132\u5b58',
|
||
providers_oauth_hint: '\u5df2\u900f\u904e OAuth \u9a57\u8b49\u3002\u7121\u9700 API \u91d1\u9470\u3002',
|
||
providers_remove: '\u79fb\u9664',
|
||
providers_removing: '\u79fb\u9664\u4e2d\u2026',
|
||
providers_save: '\u5132\u5b58',
|
||
providers_saving: '\u5132\u5b58\u4e2d\u2026',
|
||
providers_section_meta: '管理 AI 提供者的 API 金鑰。變更會立即生效。',
|
||
providers_section_title: '供應商',
|
||
providers_status_api_key: 'API 金鑰',
|
||
providers_status_configured: 'API \u91d1\u9470\u5df2\u8a2d\u5b9a',
|
||
providers_status_not_configured: '\u7121 API \u91d1\u9470',
|
||
providers_status_not_configured_label: '\u672a\u8a2d\u5b9a',
|
||
providers_status_oauth: 'OAuth',
|
||
providers_tab_title: '供應商',
|
||
status_agent_running: 'Agent 執行中',
|
||
status_completed: '\u5df2\u5b8c\u6210',
|
||
status_failed: '\u5931\u6557',
|
||
status_heading: '\u6703\u8a71\u72c0\u614b',
|
||
status_load_failed: '\u8f09\u5165\u72c0\u614b\u5931\u6557\uff1a',
|
||
status_messages: '\u8a0a\u606f\u6578',
|
||
status_model: '\u6a21\u578b',
|
||
status_no: '\u5426',
|
||
status_personality: '\u4eba\u8a2d',
|
||
status_session_id: '\u6703\u8a71 ID',
|
||
status_title: '\u6a19\u984c',
|
||
status_workspace: '\u5de5\u4f5c\u5340',
|
||
status_yes: '\u662f',
|
||
usage_default_model: '\u9810\u8a2d',
|
||
usage_estimated_cost: '\u9810\u4f30\u8cbb\u7528',
|
||
usage_heading: 'Token \u7528\u91cf',
|
||
usage_input_tokens: '\u8f38\u5165 Token',
|
||
usage_load_failed: '\u8f09\u5165\u7528\u91cf\u5931\u6557\uff1a',
|
||
usage_output_tokens: '\u8f38\u51fa Token',
|
||
usage_personality_none: '\u7121',
|
||
usage_settings_tip: '\u6ce8\u610f\uff1a\u8cbb\u7528\u70ba\u9810\u4f30\u503c\u3002',
|
||
usage_total: '\u7e3d Token \u6578',
|
||
usage_unknown: '\u672a\u77e5',
|
||
},
|
||
};
|
||
|
||
// Active locale — defaults to English; overridden by loadLocale() at boot.
|
||
let _locale = LOCALES.en;
|
||
|
||
/**
|
||
* Resolve an incoming locale tag to a known LOCALES key.
|
||
* Supports exact keys, case-insensitive matches, and a few common aliases
|
||
* (e.g. zh-CN -> zh, zh-TW -> zh-Hant). Returns null when unresolved.
|
||
* @param {string} lang
|
||
* @returns {string|null}
|
||
*/
|
||
function resolveLocale(lang) {
|
||
if (typeof lang !== 'string') return null;
|
||
const raw = lang.trim();
|
||
if (!raw) return null;
|
||
if (LOCALES[raw]) return raw;
|
||
|
||
const lower = raw.toLowerCase().replace(/_/g, '-');
|
||
|
||
// Case-insensitive direct match first.
|
||
const direct = Object.keys(LOCALES).find((k) => k.toLowerCase() === lower);
|
||
if (direct) return direct;
|
||
|
||
// Common Chinese variants.
|
||
if (lower === 'zh' || lower.startsWith('zh-cn') || lower.startsWith('zh-sg') || lower.startsWith('zh-hans')) {
|
||
return LOCALES.zh ? 'zh' : null;
|
||
}
|
||
if (lower.startsWith('zh-tw') || lower.startsWith('zh-hk') || lower.startsWith('zh-mo') || lower.startsWith('zh-hant')) {
|
||
return LOCALES['zh-Hant'] ? 'zh-Hant' : null;
|
||
}
|
||
|
||
// Fallback to base language subtag (e.g. en-US -> en).
|
||
const base = lower.split('-')[0];
|
||
const baseMatch = Object.keys(LOCALES).find((k) => k.toLowerCase() === base);
|
||
return baseMatch || null;
|
||
}
|
||
|
||
/**
|
||
* Resolve locale with precedence:
|
||
* 1) primary (typically server setting)
|
||
* 2) fallback (typically localStorage)
|
||
* 3) English
|
||
* @param {string} primary
|
||
* @param {string} fallback
|
||
* @returns {string}
|
||
*/
|
||
function resolvePreferredLocale(primary, fallback) {
|
||
return resolveLocale(primary) || resolveLocale(fallback) || '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 = resolveLocale(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() {
|
||
setLocale(resolvePreferredLocale(null, localStorage.getItem('hermes-lang')));
|
||
}
|
||
|
||
/**
|
||
* 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;
|
||
});
|
||
if (typeof syncAppTitlebar === 'function') syncAppTitlebar();
|
||
}
|
||
|
||
// Apply saved locale immediately so there's no flash of English on reload.
|
||
loadLocale();
|