export default { // Login login: { title: 'Hermes Web UI', description: 'Enter your access token to continue. Find it in the server startup logs.', placeholder: 'Access token', submit: 'Login', tokenRequired: 'Please enter your access token', invalidToken: 'Invalid token', connectionFailed: 'Cannot connect to server', passwordLogin: 'Password', tokenLogin: 'Token', usernamePlaceholder: 'Username', passwordPlaceholder: 'Password', credentialsRequired: 'Please enter username and password', invalidCredentials: 'Invalid username or password', tooManyAttempts: 'Too many failed attempts, please try again later', passwordMismatch: 'Passwords do not match', passwordTooShort: 'Password must be at least 6 characters', setupSuccess: 'Password login configured successfully', passwordChanged: 'Password changed successfully', passwordRemoved: 'Password login removed', setupPassword: 'Set Up Password Login', changePassword: 'Change Password', changeUsername: 'Change Username', removePasswordLogin: 'Remove', username: 'Username', currentPassword: 'Current Password', newPassword: 'New Password', confirmPassword: 'Confirm Password', newUsername: 'New Username', usernameChanged: 'Username changed successfully', usernameTooShort: 'Username must be at least 2 characters', setupDescription: 'Set up a username and password for convenient login. The access token will continue to work as a backup.', removeConfirm: 'Are you sure you want to remove password login? You will need to use the access token to log in.', passwordLoginNotConfigured: 'Password login is not configured', passwordLoginConfigured: 'Password login enabled ({username})', }, // Common common: { loading: 'Loading...', cancel: 'Cancel', delete: 'Delete', edit: 'Edit', save: 'Save', retry: 'Retry', saved: 'Saved', update: 'Update', create: 'Create', saveFailed: 'Save failed', deleteFailed: 'Delete failed', ok: 'OK', copied: 'Copied', copy: 'Copy', noData: 'No data', expired: 'Expired', fetch: 'Fetch', add: 'Add', enable: 'Enable', disable: 'Disable', configured: 'Configured', notConfigured: 'Not configured', confirm: 'Confirm', expand: 'Expand', collapse: 'Collapse', start: 'Start', stop: 'Stop', }, // Sidebar sidebar: { chat: 'Chat', search: 'Search', apiRelay: 'API Relay', history: 'History', jobs: 'Jobs', kanban: 'Kanban', models: 'Models', profiles: 'Profiles', skills: 'Skills', plugins: 'Plugins', memory: 'Memory', logs: 'Logs', usage: 'Usage', channels: 'Channels', gateways: 'Gateways', terminal: 'Terminal', groupChat: 'Group Chat', files: 'Files', groupConversation: 'Conversation', groupPlatform: 'Platform', groupAgent: 'Agent', groupSystem: 'System', groupMonitoring: 'Monitoring', groupTools: 'Tools', settings: 'Settings', connected: 'Connected', disconnected: 'Disconnected', collapse: 'Collapse menu', expand: 'Expand menu', updateTip: 'Run "hermes-web-ui update" in terminal to update', updateVersion: 'Upgrade to v{version}', reloadClientVersion: 'Reload for v{version}', updating: 'Updating...', updateSuccess: 'Update complete, please restart the server', updateFailed: 'Update failed', logout: 'Sign Out', nodeVersionWarning: 'Detected Node.js v{version}. Please upgrade to version 23 or later.', changelog: 'Changelog', noChangelog: 'No changelog available', }, // Drawer drawer: { terminal: 'Terminal', files: 'Workspace', }, // Chat chat: { contextRemaining: 'remaining', contextClickToEdit: 'Click to edit context length', contextEditTitle: 'Edit Context Length', contextEditDesc: 'Set context length limit for current model (in tokens)', contextEditPlaceholder: 'Enter context length', contextEditHint: 'Common values: 200k (Claude), 128k (GPT-4), 32k (GPT-3.5)', contextEditSave: 'Save', contextEditCancel: 'Cancel', contextEditInvalid: 'Please enter a valid context length', contextEditSuccess: 'Context length updated', contextEditFailed: 'Update failed', emptyState: 'Start a conversation with Hermes Agent', inputPlaceholder: 'Type a message... (Enter to send, Shift+Enter for new line)', attachFiles: 'Attach files', autoPlaySpeech: 'Auto-play voice', messageQueue: 'Message queue', removeQueuedMessage: 'Remove queued message', stop: 'Stop', start: 'Start', stopGateway: 'Stop Gateway', send: 'Send', contextUsed: 'Context used:', sessions: 'Sessions', webUiSessions: 'Sessions', sessionScopeHint: 'Chat shows Web UI/API Server sessions only. CLI, Telegram, Discord, Cron, and other channel sessions are read-only in History.', openHistory: 'Open History', hermesHistory: 'Hermes History', historyScopeHint: 'Read-only Hermes history sessions grouped by source.', noSessions: 'No sessions', searchTitle: 'Search Sessions', searchSubtitle: 'Search by title or message content', searchHint: 'Cmd/Ctrl+K', searchPlaceholder: 'Search sessions...', searchEmpty: 'Recent sessions', searchRecent: 'Recent session', searchNoResults: 'No sessions match your search', searchNoSnippet: 'No snippet available', searchEnterHint: 'Enter to open · Esc to close', searchFailed: 'Failed to search sessions', newChat: 'New Chat', deleteSession: 'Delete this session?', sessionDeleted: 'Session deleted', toggleBatchMode: 'Batch selection', selectAll: 'Select all', confirmBatchDelete: 'Delete {count} selected sessions?', batchDeleteSuccess: 'Deleted {count} sessions', batchDeletePartial: '{failed} sessions failed to delete', batchDeleteFailed: 'Batch delete failed', rename: 'Rename', pin: 'Pin', unpin: 'Unpin', pinned: 'Pinned', chatMode: 'Chat', liveMode: 'Live', liveSessions: 'Live Sessions', recentBadge: 'Recent', linkedSessions: '{count} linked', noVisibleMessages: 'No human-visible messages.', monitorRoleUser: 'User', monitorRoleAssistant: 'Assistant', copySessionId: 'Copy Session ID', export: 'Export', exportFull: 'Full Export (JSON)', exportCompressed: 'Compressed Export (TXT)', exportCompressing: 'Compressing context, please wait...', exportSuccess: 'Session exported', exportFailed: 'Export failed', renamed: 'Renamed', renameFailed: 'Rename failed', renameSession: 'Rename Session', sessionNotFound: 'Session not found', enterNewTitle: 'Enter new title', workspace: 'Workspace', setWorkspace: 'Set Workspace', setWorkspaceTitle: 'Set Session Workspace', workspacePlaceholder: 'Enter project path, e.g. /home/user/project', workspaceSet: 'Workspace set', workspaceSetFailed: 'Failed to set workspace', other: 'Other', runFailed: 'Run failed', error: 'Error', tool: 'Tool', arguments: 'Arguments', result: 'Result', truncated: '... (truncated)', executionDuration: 'Execution time', thinkingLabel: 'Thinking', thinkingInProgress: 'Thinking…', thinkingShow: 'Show thinking', thinkingHide: 'Hide thinking', thinkingDuration: 'Observed {duration}', thinkingChars: '{count} chars', copyBubble: 'Copy message', copiedBubble: 'Message copied', copyFailed: 'Copy failed', playSpeech: 'Play voice', pauseSpeech: 'Pause', resumeSpeech: 'Resume', stopSpeech: 'Stop', speechNotSupported: 'Voice playback not supported in this browser', }, // Kanban kanban: { title: 'Kanban Board', createTask: 'New Task', noTasks: 'No tasks', allStatuses: 'All Statuses', allAssignees: 'All Assignees', board: { create: 'New Board', archive: 'Archive Board', slugPlaceholder: 'Board slug, e.g. project-a', namePlaceholder: 'Display name (optional)', slugRequired: 'Board slug is required', created: 'Board created', archived: 'Board archived', archiveConfirm: 'Archive the current board?', }, columns: { triage: 'Triage', todo: 'To Do', ready: 'Ready', running: 'Running', blocked: 'Blocked', done: 'Done', archived: 'Archived', }, form: { title: 'Title', titlePlaceholder: 'Task title', titleRequired: 'Title is required', body: 'Description', bodyPlaceholder: 'Task description (optional)', assignee: 'Assignee', selectAssignee: 'Select assignee...', priority: 'Priority', selectPriority: 'Select priority...', }, card: { assigneeTooltip: 'Assignee', priority: { low: 'Low', medium: 'Medium', high: 'High', }, timeAgo: { justNow: 'just now', minutes: '{count}m ago', hours: '{count}h ago', days: '{count}d ago', }, }, detail: { status: 'Status', assignee: 'Assignee', priority: 'Priority', tenant: 'Tenant', createdAt: 'Created', startedAt: 'Started', completedAt: 'Completed', comments: 'Comments', events: 'Events', runs: 'Runs', result: 'Result', sessions: 'Related Sessions', sessionMessages: 'Session Messages', noSessions: 'No related sessions found.', artifacts: 'Artifacts', sources: 'Sources', highlights: 'Highlights', }, action: { title: 'Actions', complete: 'Complete', completeSummary: 'Completion summary (optional)', block: 'Block', blockReason: 'Reason for blocking', unblock: 'Unblock', assign: 'Assign', assignTo: 'Assign to...', }, message: { taskCreated: 'Task created', taskCompleted: 'Task completed', taskBlocked: 'Task blocked', taskUnblocked: 'Task unblocked', taskAssigned: 'Task assigned', loadFailed: 'Failed to load task', }, stats: { total: 'Total', tasks: 'Tasks', }, }, // Jobs jobs: { title: 'Scheduled Jobs', createJob: 'Create Job', editJob: 'Edit Job', noJobs: 'No scheduled jobs yet. Create one to get started.', name: 'Name', namePlaceholder: 'Job name', schedule: 'Schedule (Cron Expression)', schedulePlaceholder: 'e.g. 0 9 * * *', quickPresets: 'Quick Presets', selectPreset: 'Select a preset...', presetEveryMinute: 'Every minute', presetEvery5Min: 'Every 5 minutes', presetEveryHour: 'Every hour', presetEveryDay: 'Every day at 00:00', presetEveryDay9: 'Every day at 09:00', presetEveryMonday: 'Every Monday at 09:00', presetEveryMonth: 'Every month 1st at 09:00', prompt: 'Prompt', promptPlaceholder: 'The prompt to execute', deliverTarget: 'Deliver Target', origin: 'Origin', local: 'Local', repeatCount: 'Repeat Count (optional)', modelPlaceholder: 'Default model', repeatPlaceholder: 'Leave empty for infinite', jobCreated: 'Job created', jobUpdated: 'Job updated', nameRequired: 'Name is required', scheduleRequired: 'Schedule is required', loadFailed: 'Failed to load job', jobPaused: 'Job paused', jobResumed: 'Job resumed', jobTriggered: 'Job triggered', modelUpdated: 'Model updated', jobDeleted: 'Job deleted', status: { running: 'Running', paused: 'Paused', disabled: 'Disabled', scheduled: 'Scheduled', }, info: { model: 'Model', schedule: 'Schedule', lastRun: 'Last Run', nextRun: 'Next Run', deliver: 'Deliver', repeat: 'Repeat', }, action: { pause: 'Pause', pauseJob: 'Pause job', resume: 'Resume', resumeJob: 'Resume job', runNow: 'Run Now', triggerImmediately: 'Trigger immediately', }, runHistory: { title: 'Run History', runs: 'runs', noRuns: 'No run history found.', }, }, // Skills skills: { title: 'Skills', searchPlaceholder: 'Search skills...', noMatch: 'No skills match your search', noSkills: 'No skills found', backTo: 'Back to', attachedFiles: 'Attached Files', loadFailed: 'Failed to load skill', fileLoadFailed: 'Failed to load file', modified: 'Modified', archived: 'Archived', pinned: 'Pinned', pin: 'Pin skill', unpin: 'Unpin skill', pinFailed: 'Failed to change pin status', toggleFailed: 'Failed to toggle skill', source: { builtin: 'Builtin', hub: 'Hub', local: 'Local', }, }, // Plugins plugins: { title: 'Plugins', refresh: 'Refresh', notice: 'Read-only inventory of discoverable Hermes plugin manifests. Discovery metadata is read without loading plugin code. Management actions stay in CLI for v1; changes take effect in new Hermes sessions.', loadFailed: 'Failed to load plugins', commandCopied: 'Command copied', searchPlaceholder: 'Search key, name, description, path...', source: 'Source', kind: 'Kind', statusTitle: 'Status', configStatus: 'config: {status}', notAvailable: 'n/a', copyCommand: 'Copy command', managedElsewhere: 'managed elsewhere', noMatch: 'No plugins match the current filters', enabled: 'enabled', disabled: 'disabled', summary: { total: 'Total', active: 'Enabled / auto', inactive: 'Inactive', disabled: 'Disabled', providerManaged: 'Provider-managed', }, status: { enabled: 'Enabled', 'auto-active': 'Auto-active', inactive: 'Inactive', disabled: 'Disabled', 'provider-managed': 'Provider-managed', }, statusLabel: { enabled: 'Enabled by config', 'auto-active': 'Auto-active', inactive: 'Inactive', disabled: 'Disabled', 'provider-managed': 'Provider-managed', }, configStatuses: { enabled: 'enabled', disabled: 'disabled', 'not-enabled': 'not enabled', auto: 'auto', 'provider-managed': 'provider-managed', }, table: { plugin: 'Plugin', status: 'Status', source: 'Source', kind: 'Kind', capabilities: 'Capabilities', path: 'Path / entrypoint', cli: 'CLI', }, capabilities: { tools: '{count} tools', hooks: '{count} hooks', env: '{count} env', }, metadata: { agentRoot: 'Agent root', python: 'Python', scanCwd: 'Scan cwd', projectPlugins: 'Project plugins', }, }, // Memory memory: { title: 'Memory', refresh: 'Refresh', loadFailed: 'Failed to load memory', myNotes: 'My Notes', noNotes: 'No notes yet.', notesPlaceholder: 'Write your notes...', userProfile: 'User Profile', noProfile: 'No profile yet.', profilePlaceholder: 'Write your profile...', soul: 'Soul', noSoul: 'No soul configuration yet.', soulPlaceholder: 'Write soul configuration...', }, // Models models: { title: 'Models', searchPlaceholder: 'Search models...', addProvider: 'Add Provider', providerType: 'Provider Type', preset: 'Preset', custom: 'Custom', selectProvider: 'Select Provider', chooseProvider: 'Choose a provider...', getApiKey: 'Get API Key', name: 'Name', autoGeneratedName: 'Auto-generated from Base URL', baseUrl: 'Base URL', region: 'Region', regionIntl: 'International', regionCn: 'Mainland China', baseUrlPlaceholder: 'e.g. https://api.example.com/v1', apiKey: 'API Key', apiKeyPlaceholder: 'sk-...', defaultModel: 'Default Model', selectOrInput: 'Select or type a model name...', selectModel: 'Select a model...', providerAdded: 'Provider added', providerDeleted: 'Provider deleted', deleteProvider: 'Delete Provider', deleteConfirm: 'Are you sure you want to delete "{name}"?', codexLoginTitle: 'OpenAI Codex Login', codexWaiting: 'Enter this code at the authorization page to complete login:', codexCopyCode: 'Code copied', codexOpenLink: 'Open authorization page', codexApproved: 'Login successful', codexExpired: 'Authorization expired. Please try again.', nousLoginTitle: 'Nous Portal Login', nousWaiting: 'Enter this code at the authorization page to complete login:', nousCopyCode: 'Code copied', nousOpenLink: 'Open authorization page', nousApproved: 'Login successful', nousDenied: 'Authorization was denied. Please try again.', nousExpired: 'Authorization expired. Please try again.', copilotLoginTitle: 'GitHub Copilot Login', copilotWaiting: 'Open GitHub and enter the device code below to authorize. The window will close automatically once approved.', copilotCopyCode: 'Code copied', copilotOpenLink: 'Open GitHub authorization page', copilotApproved: 'Sign-in succeeded!', copilotDenied: 'Authorization denied.', copilotExpired: 'The authorization link has expired. Please retry.', copilotAddDetectedTitle: 'GitHub Copilot detected', copilotAddDetected: 'A GitHub Copilot OAuth token was detected on this machine. Click Add to enable Copilot in Hermes.', copilotAddSourceEnv: 'Source: ~/.hermes/.env (COPILOT_GITHUB_TOKEN)', copilotAddSourceGhCli: 'Source: gh CLI (gh auth token)', copilotAddSourceAppsJson: 'Source: VS Code Copilot extension (apps.json)', copilotDeleteHintEnv: 'This will clear COPILOT_GITHUB_TOKEN in ~/.hermes/.env. Other tools are not affected.', copilotDeleteHintGhCli: 'Copilot will be hidden from Hermes. Your gh CLI login is not affected — `gh auth status` will still show you signed in.', copilotDeleteHintAppsJson: 'Copilot will be hidden from Hermes. Your VS Code Copilot extension login is not affected.', customBadge: 'CUSTOM', previewBadge: 'PREVIEW', disabledBadge: 'UNAVAILABLE', disabledTooltip: "This model is currently unavailable for your account.", customModelPlaceholder: 'Unlisted model ID', customModelHint: 'For provider-supported models not returned by the API; not a display rename. Press Enter to load.', removeCustomModel: 'Remove this unlisted model', noProviders: 'No providers found. Add a custom provider to get started.', models: 'Models', count: 'models', more: 'more', aliasEdit: 'Rename', aliasTitle: 'Model display name', aliasTitleFor: 'Display name for {model}', aliasPlaceholder: 'Leave empty to use original model ID', aliasHint: 'Display-only alias. Hermes still receives the canonical model ID.', aliasCanonical: 'Original ID: {model}', aliasUseOriginal: 'Use original ID', aliasManage: 'Display names', aliasManageFor: 'Display names for {provider}', aliasSaveFailed: 'Failed to save display name', manageVisibleModels: 'Manage visible models', manageVisibleModelsFor: 'Manage visible models for {name}', visibilityHint: 'Only affects the Web UI model picker and Models page. Hermes CLI provider/model config is not rewritten; calls still use canonical model IDs.', visibilitySelectOne: 'Keep at least one visible model', visibilitySaved: 'Visible models saved', visibilitySaveFailed: 'Failed to save visible models', showAllModels: 'Show all models', builtIn: 'Built-in', customType: 'Custom', provider: 'Provider', contextLength: 'Context Length', contextLengthPlaceholder: 'e.g. 200000 (optional)', local: 'Local ({host})', selectProviderRequired: 'Please select a provider', baseUrlRequired: 'Base URL is required', apiKeyRequired: 'API Key is required', modelRequired: 'Default Model is required', enterBaseUrl: 'Please enter Base URL first', unexpectedFormat: 'Unexpected response format', foundModels: 'Found {count} models', fetchFailed: 'Failed to fetch models', }, // Profiles gateways: { title: 'Gateways', running: 'Running', stopped: 'Stopped', started: 'Started', startFailed: 'Failed to start gateway', stopFailed: 'Failed to stop gateway', }, profiles: { title: 'Profiles', create: 'Create Profile', import: 'Import', export: 'Export', rename: 'Rename', delete: 'Delete', switchTo: 'Switch to', switchConfirm: 'Switching to profile "{name}" will restart the gateway. Continue?', switchSuccess: 'Switched to profile "{name}"', switchFailed: 'Failed to switch profile. Gateway may need manual restart.', createSuccess: 'Profile "{name}" created', createFailed: 'Failed to create profile', renameSuccess: 'Profile renamed', renameFailed: 'Failed to rename profile', deleteConfirm: 'Are you sure you want to delete profile "{name}"?', deleteSuccess: 'Profile deleted', deleteFailed: 'Failed to delete profile', exportSuccess: 'Profile exported', exportFailed: 'Failed to export profile', importSuccess: 'Profile imported', importFailed: 'Failed to import profile', importSelectFile: 'Select archive file', importInvalidFile: 'Please select a valid archive (.tar.gz, .tgz, .gz, .zip)', name: 'Profile Name', namePlaceholder: 'Lowercase letters, numbers, hyphens only', nameValidation: 'Profile name can only contain lowercase letters, numbers, underscores, and hyphens', newName: 'New Name', newNamePlaceholder: 'Lowercase letters, numbers, hyphens', cloneFromCurrent: 'Clone from current profile', cloneCleanupNotice: 'Cloning automatically skips exclusive platform credentials (Weixin / Telegram / Slack, etc.) to avoid conflicts with the source profile', cloneStrippedCredentials: 'Stripped {count} exclusive credential(s): {list}', cloneDisabledPlatforms: 'Disabled {count} platform(s): {list}', cloneStrippedConfigCredentials: 'Stripped {count} embedded credential(s) from config.yaml: {list}', archivePath: 'Archive Path', archivePathPlaceholder: 'Server path to archive file', importName: 'Profile Name (optional)', importNamePlaceholder: 'Leave empty to use archive name', active: 'Active', model: 'Model', gateway: 'Gateway', alias: 'Alias', provider: 'Provider', path: 'Path', skills: 'Skills', hasEnv: 'Has .env', hasSoulMd: 'Has soul.md', noProfiles: 'No profiles found. Create one to get started.', }, // Logs logs: { title: 'Logs', all: 'All', searchPlaceholder: 'Search...', refresh: 'Refresh', noEntries: 'No log entries', }, // Settings settings: { title: 'Settings', saved: 'Saved', saveFailed: 'Save failed', tabs: { display: 'Display', account: 'Account', agent: 'Agent', memory: 'Memory', session: 'Session', privacy: 'Privacy', apiServer: 'API Server', models: 'Models', voice: 'Voice', }, models: { apiKey: 'API Key', apiKeyPlaceholder: 'Enter API key', save: 'Save', saved: 'Saved', saveFailed: 'Save failed', noProviders: 'No providers configured', }, display: { streaming: 'Stream Responses', streamingHint: 'Show AI replies in real-time', compact: 'Compact Mode', compactHint: 'Reduce message spacing', showReasoning: 'Show Reasoning', showReasoningHint: 'Show model thinking process', showCost: 'Show Cost', showCostHint: 'Show token usage in replies', inlineDiffs: 'Inline Diffs', inlineDiffsHint: 'Show code changes inline', bellOnComplete: 'Completion Sound', bellOnCompleteHint: 'Play sound when AI finishes', busyInputMode: 'Busy Input Mode', busyInputModeHint: 'Allow input while AI is processing', theme: 'Theme', themeHint: 'Choose light, dark, or follow system preference', themeLight: 'Light', themeDark: 'Dark', themeSystem: 'System', }, agent: { maxTurns: 'Max Turns', maxTurnsHint: 'Maximum interaction rounds per conversation', gatewayTimeout: 'Gateway Timeout', gatewayTimeoutHint: 'Request timeout in seconds', restartDrainTimeout: 'Restart Drain Timeout', restartDrainTimeoutHint: 'Drain timeout before restart in seconds', toolEnforcement: 'Tool Enforcement', toolEnforcementHint: 'Control tool call execution mode', auto: 'Auto', always: 'Always', never: 'Never', }, memory: { enabled: 'Enable Memory', enabledHint: 'Allow AI to remember conversation context', userProfile: 'User Profile', userProfileHint: 'Allow AI to remember user preferences', charLimit: 'Memory Char Limit', charLimitHint: 'Max characters for MEMORY.md', userCharLimit: 'User Profile Char Limit', userCharLimitHint: 'Max characters for USER.md', }, session: { mode: 'Reset Mode', modeHint: 'Trigger condition for session reset', modeBoth: 'Idle + Scheduled', modeIdle: 'Idle Only', modeHourly: 'Scheduled Only', idleMinutes: 'Idle Timeout', idleMinutesHint: 'Wait time before auto-reset (minutes)', atHour: 'Scheduled Reset Time', humanOnly: 'Show human sessions only', humanOnlyHint: 'Hide sub-agent/session monitor noise by default', liveMonitorHumanOnly: 'Live monitor: show human sessions only', liveMonitorHumanOnlyHint: 'Hide sub-agent/session monitor noise in the Live monitor by default', atHourHint: 'Reset session at this hour daily', requireAuth: 'Session Authorization', requireAuthHint: 'Require authorization for session operations', }, privacy: { redactPii: 'Redact PII', redactPiiHint: 'Auto-detect and hide sensitive info (passwords, keys, etc.)', }, apiServer: { enable: 'Enable', enableHint: 'Enable API server', host: 'Host', hostHint: 'Listen address', port: 'Port', portHint: 'Listen port', key: 'Key', keyHint: 'API access key', cors: 'CORS Origins', corsHint: 'Allowed cross-origin sources', }, lockedIps: { title: 'Locked IPs', count: '{count} locked', empty: 'No locked IPs', unlock: 'Unlock', unlockAll: 'Unlock All', unlockAllConfirm: 'Unlock all locked IPs?', unlocked: 'IP unlocked', allUnlocked: '{count} IPs unlocked', }, voice: { ttsProvider: 'TTS Provider', ttsProviderHint: 'Choose the text-to-speech engine for message playback', providerWebSpeech: 'WebSpeech API (Browser)', providerOpenai: 'OpenAI TTS', providerCustom: 'Custom Endpoint (OpenAI-compatible)', providerEdge: 'Edge TTS (Free, no API Key)', // WebSpeech webspeechVoice: 'Voice', webspeechVoiceHint: 'Select a voice from your browser or OS', webspeechVoicePlaceholder: 'Auto (default voice)', // OpenAI openaiKey: 'API Key', openaiKeyHint: 'Your OpenAI API key with TTS access', openaiUrl: 'API Base URL', openaiUrlHint: 'e.g. https://api.openai.com/v1/audio/speech', openaiModel: 'Model', openaiModelHint: 'tts-1 (faster) / tts-1-hd (higher quality)', openaiVoice: 'Voice', openaiVoiceHint: 'Voice to use for synthesis', // Custom endpoint customHint: 'Use any OpenAI-compatible TTS API — works with GPT-SoVITS, CosyVoice, etc.', customUrl: 'API URL', customUrlHint: 'Base URL of your TTS service', customUrlPlaceholder: 'The address configured in the local adapter, e.g. http://127.0.0.1:9880', customApiKey: 'API Key (Optional)', customApiKeyHint: 'Some custom endpoints require authentication', customApiKeyPlaceholder: 'Leave blank if not needed', // Edge TTS edgeHint: 'Powered by Microsoft Edge TTS (node-edge-tts).', edgeUrl: 'Adapter URL', edgeUrlHint: 'Address of your Edge TTS adapter, e.g. http://127.0.0.1:9882', edgeUrlPlaceholder: 'http://127.0.0.1:9882', edgeVoice: 'Voice', edgeVoiceHint: 'Select a voice for speech synthesis', edgeRate: 'Speed', edgeRateHint: 'Adjust speech speed (0.5x ~ 2.0x)', edgePitch: 'Pitch', edgePitchHint: 'Adjust speech pitch (-20 ~ +20 Hz)', // Test testTitle: 'Test Voice', testText: 'Test Text', testTextPlaceholder: 'Enter text to test...', testTextDefault: 'Hello, this is a voice test.', testButton: 'Test', testButtonPlaying: 'Playing...', testFailed: 'Test failed: {error}', }, }, // Platform channel settings platform: { requireMention: "Require {'@'}Mention", requireMentionGroup: "Require {'@'}mention in groups to respond", requireMentionChannel: "Require {'@'}mention in channels to respond", requireMentionRoom: "Require {'@'}mention in rooms to respond", reactions: 'Reactions', reactionsHint: 'React to messages with emoji', freeResponseChats: 'Free Response Chats', freeResponseChatsHint: "Chat IDs that respond without {'@'}mention (comma-separated)", freeResponseChannels: 'Free Response Channels', freeResponseChannelsHint: "Channel IDs that respond without {'@'}mention (comma-separated)", freeResponseRooms: 'Free Response Rooms', freeResponseRoomsHint: "Room IDs that respond without {'@'}mention (comma-separated)", mentionPatterns: 'Custom Mention Patterns', mentionPatternsHint: 'Additional trigger patterns', autoThread: 'Auto Thread', autoThreadHint: "Auto-create reply threads after {'@'}mention", autoThreadHintRoom: 'Auto-create reply threads in rooms', dmMentionThreads: 'DM Mention Threads', dmMentionThreadsHint: 'Use thread replies for mentions in DMs', allowBots: 'Allow Bot Messages', allowBotsHint: 'Respond to messages from other bots', allowedChannels: 'Allowed Channels', allowedChannelsHint: 'Whitelist channel IDs (comma-separated)', ignoredChannels: 'Ignored Channels', ignoredChannelsHint: 'Channels where bot never responds (comma-separated)', noThreadChannels: 'No-Thread Channels', noThreadChannelsHint: 'Channels where bot responds without threads (comma-separated)', exclusiveTokenWarning: 'This platform uses exclusive token locking. Each profile must use a different identity token to avoid conflicts with other profiles.', botToken: 'Bot Token', botTokenHint: 'Bot token from developer portal', accessToken: 'Access Token', accessTokenHint: 'Matrix access token', homeserver: 'Homeserver URL', homeserverHint: 'Matrix homeserver URL', appId: 'App ID', appIdHint: 'Feishu App ID', appSecret: 'App Secret', appSecretHint: 'Feishu App Secret', clientId: 'Client ID', clientIdHint: 'DingTalk Client ID', clientSecret: 'Client Secret', clientSecretHint: 'DingTalk Client Secret', botId: 'Bot ID', botIdHint: 'WeCom Bot ID', wecomSecretHint: 'WeCom Bot Secret', waEnabled: 'Enable WhatsApp', waEnabledHint: 'Enable WhatsApp via QR code pairing', weixinToken: 'Weixin Token', weixinTokenHint: 'From weixin CLI QR login (hermes weixin)', accountId: 'Account ID', accountIdHint: 'Weixin account ID', qrLogin: 'QR Login', qrRelogin: 'Re-login', qrFetching: 'Fetching QR code...', qrScanHint: 'Scan with WeChat to login', qrScanedHint: 'Scaned, please confirm on phone...', // QQ qqAppId: 'App ID', qqAppIdHint: 'QQ Open Platform Bot App ID', qqAppSecret: 'App Secret', qqAppSecretHint: 'QQ Open Platform Bot App Secret', qqMarkdown: 'Markdown Support', qqMarkdownHint: 'Enable Markdown formatted messages (some clients may not support)', qqSandbox: 'Sandbox Mode', qqSandboxHint: 'Enable sandbox environment (for testing)', qqQrScanHint: 'Scan the QR code with QQ, or open the link on your phone to complete binding', }, // Language language: { label: 'Language', zh: '中文', en: 'English', }, // Terminal terminal: { sessions: 'Sessions', newTab: 'New Terminal', closeSession: 'Close this session?', sessionExited: 'Exited', processExited: 'Process exited with code {code}', noSessions: 'No terminal sessions', connectionFailed: 'Terminal service connection failed', connectionClosed: 'Terminal connection closed', connectionError: 'Terminal connection error', }, // Group Chat groupChat: { title: 'Group Chat', createRoom: 'Create Room', joinByCode: 'Join by Code', roomName: 'Room Name', roomNamePlaceholder: 'Enter room name', inviteCode: 'Invite Code', autoGenerate: 'Auto-generate', noRooms: 'No rooms yet', selectOrCreate: 'Select or create a room to start chatting', agents: 'Agents', addAgent: 'Add Agent', selectProfile: 'Select a profile', agentAdded: 'Agent added', agentAlreadyInRoom: 'Agent already in this room', noAgents: 'No agents in this room', members: 'members', roomCreated: 'Room created', roomDeleted: 'Room deleted', deleteRoomConfirm: 'Delete this room?', you: 'You', joined: 'Joined room', joinFailed: 'Failed to join room', inputPlaceholder: 'Type a message... (Enter to send)', enterCode: 'Enter invite code', yourName: 'Your Name', yourNamePlaceholder: 'Enter your display name', yourDescription: 'Description (optional)', yourDescriptionPlaceholder: 'Tell others who you are...', agentName: 'Agent Name', agentNamePlaceholder: 'Custom name (leave empty to use profile name)', agentDesc: 'Agent Description', agentDescPlaceholder: 'Describe what this agent does...', agentReplying: 'is replying...', agentCompressing: 'is compressing context...', compressionSettings: 'Compression Settings', triggerTokens: 'Trigger Tokens', triggerTokensDesc: 'Token threshold to trigger context compression', maxHistoryTokens: 'Max History Tokens', maxHistoryTokensDesc: 'Maximum tokens for compressed context sent to LLM', tailMessageCount: 'Tail Message Count', tailMessageCountDesc: 'Number of recent messages to keep verbatim after compression', compressionConfig: 'Compression Config', compressionSaved: 'Compression config saved', compressNow: 'Compress Now', compressingInProgress: 'Compression in progress, please wait', }, // Usage usage: { title: 'Usage Statistics', refresh: 'Refresh', totalTokens: 'Total Tokens', inputTokens: 'Input', outputTokens: 'Output', totalSessions: 'Total Sessions', avgPerDay: '~{n}/day avg', estimatedCost: 'Est. Cost', cacheHitRate: 'Cache Hit Rate', modelBreakdown: 'Model Breakdown', dailyTrend: 'Daily Usage', date: 'Date', tokens: 'Tokens', cache: 'Cache', cacheRead: 'Cache Read', cacheWrite: 'Cache Write', sessions: 'Sessions', cost: 'Cost', noData: 'No usage data', }, // Files files: { title: 'Files', fileTree: 'File Tree', tree: 'Directory Tree', list: 'File List', breadcrumbRoot: 'Home', newFile: 'New File', newFolder: 'New Folder', upload: 'Upload', refresh: 'Refresh', open: 'Open', edit: 'Edit', preview: 'Preview', download: 'Download', copyPath: 'Copy Path', rename: 'Rename', delete: 'Delete', name: 'Name', size: 'Size', modified: 'Modified', actions: 'Actions', emptyDir: 'Empty directory', loading: 'Loading...', confirmDelete: 'Are you sure you want to delete "{name}"?', confirmDeleteDir: 'Are you sure you want to delete directory "{name}" and all its contents?', deleteFailed: 'Delete failed', deleted: 'Deleted', renameTo: 'Rename to', newFileName: 'File name', newFolderName: 'Folder name', created: 'Created', createFailed: 'Create failed', renamed: 'Renamed', renameFailed: 'Rename failed', uploadSuccess: 'Uploaded {count} file(s)', uploadFailed: 'Upload failed', saveFailed: 'Save failed', saved: 'Saved', unsavedChanges: 'You have unsaved changes. Discard?', pathCopied: 'Path copied', fileTooLarge: 'File too large (max 10MB)', permissionDenied: 'Cannot modify protected file', notFound: 'File or directory not found', backendError: 'File operation failed', dragDropHint: 'Drag files here to upload', closeEditor: 'Close Editor', closePreview: 'Close', saveFile: 'Save', }, // Download download: { downloading: 'Downloading...', downloadFailed: 'Download failed', fileNotFound: 'File not found or deleted', fileTooLarge: 'File too large (exceeds limit)', backendError: 'File read failed, remote environment may be unavailable', backendTimeout: 'File read timed out', unsupportedBackend: 'Current terminal backend does not support file download', invalidPath: 'Invalid file path', download: 'Download', downloadFile: 'Download file', }, // Changelog changelog: { new_0_5_14_1: 'Add session export with full and compressed modes (JSON or plain text)', new_0_5_14_2: 'Fix terminal panel connecting on mount causing PTY resource exhaustion, switch to lazy connect', new_0_5_14_3: 'Fix WSL2 health check failure caused by IPv6 dual-stack binding', new_0_5_14_4: 'Fix SQLite connection not released on shutdown causing database lock on restart', new_0_5_14_5: 'Update FUN-Codex/FUN-Claude model lists, add builtin badge and API relay link', new_0_5_15_1: 'Add Kanban board for visual task and session management', new_0_5_15_2: 'Add official landing page and documentation website', new_0_5_15_3: 'Fix group-chat agent client to use dynamic port instead of hardcoded 8648', new_0_5_15_4: 'Add node-edge-tts voice module', new_0_5_15_5: 'Fix WSL default listen host binding', new_0_5_15_6: 'Add website homepage metadata for SEO', new_0_5_15_7: 'Add IP-based login brute-force protection', new_0_5_15_8: 'Fix double-wrapping of download URLs in MarkdownRenderer', new_0_5_15_9: 'Fix Hermes markdown media rendering and sync retry', new_0_5_15_10: 'Refactor to remove upstream env dependency', new_0_5_15_11: 'If the Kanban feature is not available, please upgrade hermes-agent', new_0_5_16_1: 'Migrate chat streaming from /v1/runs to /v1/responses API for lower latency', new_0_5_16_2: 'Persist real API usage (tokens, cache, reasoning) to usage table', new_0_5_16_3: 'Add QQ group QR code to website navigation bar', new_0_5_16_4: 'Remove unused codex_reasoning_items field from message schema', new_0_5_17_1: 'Full Windows compatibility: path handling, process management, terminal, log parsing', new_0_5_17_2: 'Refactor Gateway process management with cross-platform start/stop/health-check', new_0_5_17_3: 'Fix plugin discovery on Termux by parsing hermes shebang to locate Python', new_0_5_17_4: 'Tolerate duplicate YAML keys in config parsing', new_0_5_17_5: 'Improve auth lock window and dev shutdown flow', new_0_5_17_6: 'Add Chinese (ZCOOL KuaiLe), Japanese (Zen Maru Gothic), Korean (Gaegu) handwritten fonts for Comic theme', new_0_5_17_7: 'Add Comic/doodle theme style', new_0_5_17_8: 'Change license to BSL-1.1', new_0_5_17_9: 'Add read-only Hermes plugins page', new_0_5_17_10: 'Convert image uploads to base64 multimodal format', new_0_5_17_11: 'Fix Kanban board selection and isolation', new_0_5_17_12: 'Add voice playback settings with 4-provider TTS support', new_0_5_17_13: 'Lower context compression message threshold from 200 to 150', new_0_5_17_14: 'Fix Web UI self-update restart logic', new_0_5_17_15: 'Fix opencode-zen and opencode-go shared env var causing config coupling', new_0_5_17_16: 'Add Traditional Chinese (zh-TW) language support', new_0_5_17_17: 'Support managing visible models in Web UI', new_0_5_17_18: 'Kanban: complete task action chain (comments, logs, assign, dispatch) with capability boundaries', new_0_5_17_19: 'Fix provider auth entries not cleared on delete', new_0_5_17_20: 'Fix Codex credential-pool auth recognition', new_0_5_17_21: 'Add Edge TTS rate/pitch sliders to voice settings', new_0_5_17_22: 'Config YAML duplicate keys no longer crash parsing', new_0_5_17_23: 'Gateway port ownership now based on PID file, preventing cross-profile port hijacking', new_0_5_17_24: 'History page now shows Cron session records', new_0_5_17_25: 'Fix language switch and theme icons squeezed in collapsed sidebar', new_0_5_13_1: 'Add message queue for sequential run processing to prevent concurrent request conflicts', new_0_5_13_2: 'Support two-level skills directory structure with misc category for flat skills', new_0_5_13_3: 'Filter out ephemeral sessions during startup sync to avoid importing internal sessions', new_0_5_13_4: 'Add Termux/proot environment compatibility for mobile and embedded deployments', new_0_5_13_5: 'Handle chat run abort lifecycle: proper cleanup and state reset on user cancellation', new_0_5_13_6: 'Remove streaming indicator from history message list for cleaner display', new_0_5_13_7: 'Fix context resolution for custom providers with improved model matching', new_0_5_13_8: 'Fix IPv6 listen default to ensure correct address binding', new_0_5_13_9: 'Improve chat message display and context compression logic', new_0_5_13_10: 'Fix chat completion sound toggle not taking effect and cron silent run history empty issue', new_0_5_12_1: 'Add batch session deletion: checkbox multi-select, select all, and batch delete for improved session management', new_0_5_12_2: 'Add visual context length editor: click token count to open edit modal with custom limit support', new_0_5_12_3: 'Fix group chat mention keyboard selection: replace NDropdown with custom dropdown supporting keyboard navigation and scroll following', new_0_5_12_4: 'Add chat completion sound: auto-play toggle for better user experience', new_0_5_12_5: 'Improve chat message display: filter empty assistant messages and better history/compression logic', new_0_5_12_6: 'Improve update mechanism: use npm prefix -g for dynamic path resolution supporting Homebrew and non-standard Node.js installations', new_0_5_10_1: 'Automate Docker image builds on GitHub release with versioned tags (e.g., :v0.5.10)', new_0_5_10_2: 'Add session authorization mode configuration: approvals.mode (off/manual)', new_0_5_10_3: 'Add automatic OpenAPI documentation generation (94 endpoints across 24 tags)', new_0_5_10_4: 'Enhance media rendering: support images, videos, and files in Markdown', new_0_5_10_5: 'Optimize group chat prompts and fix media handling', new_0_5_6_1: 'Add voice playback feature with Web Speech API: manual button, auto-play toggle, rainbow border animation, and mobile optimization', new_0_5_6_2: 'Add robust LLM JSON parser with tolerance for Python format and extract text from streaming events', new_0_5_6_3: 'Add Skills enhancements: usage stats, source filtering, archived skills, provenance, and pin toggle', new_0_5_6_4: 'Expand daily usage stats with detailed token breakdown including cache read/write separation', new_0_5_6_5: 'Clarify session history scope with improved descriptions in chat and history views', new_0_5_6_6: 'Redesigned attachment handling using Anthropic-style ContentBlock array format with type discriminated unions (text, image, file)', new_0_5_6_7: 'Added frontend file download functionality supporting both ContentBlock and Markdown formats with authentication', new_0_5_6_8: 'Fixed multi-process conflict causing SQLite database resets by eliminating redundant nodemon instances', new_0_5_9_1: 'Unify profile management across the application with consistent API and state management', new_0_5_9_2: 'Add GitHub issue and pull request templates for better contribution workflow', new_0_5_8_1: 'Add drawer panel with mobile sidebar support and customizable rainbow button', new_0_5_8_2: 'Fix profile switching state sync issue with immediate UI update and backend verification', new_0_5_8_3: 'Filter special characters and emoji in speech playback for better text-to-speech', new_0_5_8_4: 'Add missing i18n key and unify session data source to prioritize database', new_0_5_8_5: 'Optimize Vite build configuration for faster Docker builds with esbuild and chunk splitting', new_0_5_7_1: 'Optimize context compression to support rich content (images, files) with improved tool message handling', new_0_5_7_2: 'Improve session sync with batch inserts and transaction protection for data consistency', new_0_5_7_3: 'Fix usage.updated event reception to ensure accurate token tracking across runs', new_0_5_5_1: '🎉 Happy Labor Day! No work this Labor Day, please bear with us if there are any issues', new_0_5_5_2: 'Add History page for browsing Hermes session history', new_0_5_5_3: 'History page manages session state independently without interfering with active chat', new_0_5_5_4: 'History page auto-loads and selects first CLI session by default', new_0_5_5_5: 'Add HistoryMessageList component with session prop injection support', new_0_5_5_6: 'Filter empty content messages and tool messages without toolName for better display', new_0_5_5_7: 'Remove localStorage session cache, all session data now fetched directly from backend', new_0_5_5_8: 'Optimize profile switching logic by removing废弃 cache cleanup calls', new_0_5_4_1: 'Fix concurrent chat sessions event cross-talk with WebSocket event routing refactoring', new_0_5_4_2: 'Fix cron job edit payloads with partial PATCH to support long prompt name-only edits', new_0_5_4_3: 'Fix web terminal Hermes CLI availability after Docker deployment', new_0_5_4_4: 'Add workspace dialog i18n translations for title and improve session persistence', new_0_5_4_5: 'Support code block copy feedback with user notifications', new_0_5_4_6: 'Align usage analytics with Hermes state DB schema', new_0_4_7_1: 'Real-time streaming display of thinking/reasoning blocks', new_0_4_7_2: 'Skip prepare script during Docker build', new_0_4_7_3: 'Group chat mobile UX improvements and UI polish', new_0_4_7_4: 'Clamp context remaining tokens to 0 instead of negative', new_0_4_7_5: 'Add Alibaba Coding Plan builtin provider with .env base_url override', new_0_4_7_6: 'Skip remote profiles on startup to prevent hang', new_0_4_7_7: 'Detect and surface silently swallowed run errors', new_0_4_7_8: 'Provider-aware context length lookup', new_0_4_7_9: 'Reset config.model on switch and resolve CLI custom provider', new_0_4_7_10: 'Clear base_url_env from .env when deleting builtin provider', new_0_4_7_11: 'Align group chat room sidebar background with session list', new_0_4_4_1: 'Add file browser with multi-backend support (local/Docker/SSH/Singularity)', new_0_4_4_2: 'Add file download from chat message attachments', new_0_4_4_3: 'Add live badge on active chat sessions', new_0_4_4_4: 'Add StepFun and Nous Portal provider support', new_0_4_4_5: 'Fix special character search queries causing 500 error', new_0_4_5_1: 'Add group chat with multi-agent rooms, @mention routing, and typing status recovery', new_0_4_5_2: 'Rewrite model-context config to use YAML with context_length setting', new_0_4_5_3: 'Add gpt-5.5 to OpenAI Codex model list', new_0_4_5_4: 'Replace jobs proxy with local controller and optimize model loading', new_0_4_5_5: 'Add i18n support for custom model feature in ModelSelector', new_0_4_5_6: 'Fix sidebar i18n missing key warnings', new_0_4_5_7: 'Clear all localStorage on logout', new_0_4_5_8: 'Add periodic log rotation to prevent unbounded log growth', new_0_4_2_1: 'Add token usage tracking, context display, and dynamic context length', new_0_4_2_2: 'Add session search modal', new_0_4_2_3: 'Restore group chat system with Socket.IO and SQLite persistence', new_0_4_2_4: 'Add pinned sessions and live monitor in Chat page', new_0_4_2_5: 'Fix builtin provider detection and model matching', }, }