export default { // Login login: { title: 'Hermes Studio', description: 'Enter your username and password to continue.', 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', defaultCredentialsHint: 'Default username: admin. Default password: 123456.', credentialsRequired: 'Please enter username and password', invalidCredentials: 'Invalid username or password', tooManyAttempts: 'Too many failed attempts, please try again later', lockResetHint: 'If this is your server, clear the login lock with:', defaultLoginResetHint: 'To reset the default admin password, run:', desktopLockResetHint: 'Too many failed login attempts. Open the system tray menu and choose Reset Login to reset the password.', sessionExpired: 'Login expired. Please sign in again.', accessDenied: 'You do not have permission to access this resource.', 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: 'Manage the username and password used to sign in.', removeConfirm: 'Password login is required for user accounts.', passwordLoginNotConfigured: 'Password login is not configured', passwordLoginConfigured: 'Current account: {username}', defaultCredentialTitle: 'Change the default account credentials', defaultCredentialMessage: 'This account is still using the default username or password. To prevent unauthorized access, update the username and password as soon as possible.', defaultCredentialAction: 'Update now', defaultCredentialLater: 'Remind me later', }, users: { title: 'Account Management', description: 'Create users, assign roles, and control which profiles regular admins can access.', create: 'Create User', edit: 'Edit User', username: 'Username', role: 'Role', statusLabel: 'Status', profiles: 'Accessible Profiles', profilesPlaceholder: 'Select accessible profiles', allProfiles: 'All profiles', noProfiles: 'No profiles assigned', lastLogin: 'Last Login', newPasswordOptional: 'New Password (leave blank to keep)', loadFailed: 'Failed to load users', deleteConfirm: 'Delete this user?', enable: 'Enable', disable: 'Disable', roles: { superAdmin: 'Super Admin', admin: 'Admin', }, status: { active: 'Active', disabled: 'Disabled', }, }, // Common common: { loading: 'Loading...', cancel: 'Cancel', delete: 'Delete', edit: 'Edit', save: 'Save', retry: 'Retry', reset: 'Reset', 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', }, // MCP Management mcp: { title: 'MCP Servers', loadFailed: 'Failed to load MCP servers', reloadAll: 'Reload All', refresh: 'Refresh', total: 'Total', connected: 'Connected', disconnected: 'Disconnected', tools: 'tools', tool: 'Tools', searchPlaceholder: 'Search servers...', addServer: '+ Add Server', zeroTools: '0 tools', loading: 'Loading...', empty: 'No MCP servers configured', reloaded: 'Reloaded {server}', reloadedAll: 'All MCP servers reloaded', reloadFailed: 'Reload failed', serverAdded: 'Server "{name}" added', addFailed: 'Failed to add server', serverUpdated: 'Server "{name}" updated', updateFailed: 'Failed to update server', saveFailed: 'Save failed', serverRemoved: 'Removed "{name}"', enabled: "Enabled {name}", disabled: "Disabled {name}", connectedStatus: 'Connected', disconnectedStatus: 'Disconnected', disabledStatus: 'Disabled', toolList: 'Tool List', count: ' ', more: 'more', removeFailed: 'Failed to remove server', testOk: 'Test OK — {count} tools available', testEmpty: 'Test returned no tools', testFailed: 'Test failed', edit: 'Edit', test: 'Test', reload: 'Reload', remove: 'Remove', confirmRemove: 'Remove server "{name}"?', cancel: 'Cancel', add: 'Add', save: 'Save', addTitle: 'Add MCP Server', editTitle: 'Edit MCP Server', invalidJson: 'Invalid JSON format', invalidYaml: 'Invalid YAML format', invalidConfig: 'Invalid configuration', invalidServerConfig: 'Invalid server configuration', missingCommandOrUrl: 'Must have command or url', manageTools: 'Manage Tools', toolsVisibilityTitle: 'Tools Visibility Management', fetchTools: 'Fetch Tools List', fetchToolsFailed: 'Failed to fetch tools list', toolsMode: 'Mode:', toolsModeAll: 'All', toolsModeInclude: 'Include', toolsModeExclude: 'Exclude', toolsListHeader: 'Tool Name', toolsEmpty: 'No tools available, please fetch tools list first', toolsSummaryAll: '{count} tools total, all enabled', toolsSummaryInclude: '{total} tools total, {count} selected', toolsSummaryExclude: '{total} tools total, {count} excluded', toolsVisibilitySaved: 'Tools visibility saved', toolsSelectAll: 'Select all', toolsClearSelection: 'Clear selection', toolsExcludeAll: 'Exclude all', toolsClearExcluded: 'Clear excluded', }, // Sidebar sidebar: { chat: 'Chat', backToChat: 'Back', search: 'Search', apiRelay: 'API Relay', history: 'History', jobs: 'Jobs', kanban: 'Kanban', workflow: 'Workflow', models: 'Models', profiles: 'Profiles', skills: 'Skills', plugins: 'Plugins', mcp: 'MCP', petdex: 'Pets', memory: 'Memory', logs: 'Logs', usage: 'Usage', performance: 'Performance', journey: 'Journey', skillsUsage: 'Skills Usage', channels: 'Channels', gateways: 'Gateways', terminal: 'Terminal', singleChat: 'Chat', globalAgent: 'Global Agent', groupChat: 'Group Chat', files: 'Files', devices: 'Devices', groupConversation: 'Conversation', groupConversationShort: 'Conv', groupPlatform: 'Platform', groupAgent: 'Agent', groupAgentShort: 'Agent', groupSystem: 'System', groupSystemShort: 'Sys', groupMonitoring: 'Monitoring', groupMonitoringShort: 'Mon', groupTools: 'Tools', groupToolsShort: "Tools", versionPreview: "Version Preview", settings: 'Settings', connected: 'Connected', disconnected: 'Disconnected', collapse: 'Collapse menu', expand: 'Expand menu', updateTip: 'Run "hermes-ui update" in terminal to update', updateVersion: 'Upgrade to v{version}', reloadClientVersion: 'Reload for v{version}', updating: 'Updating...', updateSuccess: 'Update successful. Please refresh the page shortly. If it does not start after a while, start it manually.', 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', versionManagement: 'Version Management', updateAvailableLabel: '(New version)', dockerUpdateTitle: 'Docker Update', dockerUpdateGuide: 'In Docker environment, please pull the latest image and recreate the container to upgrade:', dockerUpdateNote: 'Your data will persist after the container is recreated.', }, runtimeVersions: { title: 'Version Management', runtimeTitle: 'Hermes Runtime', webUiTitle: 'Web UI', platform: 'Platform', currentWebUi: 'Current Web UI', activeVersion: 'Active version', active: 'Active', installed: 'Installed', download: 'Download', downloadGithub: 'GitHub Download', downloadCf: 'Cloudflare Download', github: 'GitHub', cf: 'Cloudflare', useVersion: 'Use on next restart', deleteVersion: 'Delete', deleteRuntimeConfirm: 'Delete Hermes Runtime {version}?', deleteWebUiConfirm: 'Delete Web UI {version}?', deleteRuntimeSuccess: 'Runtime deleted', deleteWebUiSuccess: 'Web UI deleted', refresh: 'Refresh', noVersions: 'No versions available', remoteLoadFailed: 'Failed to load remote versions', downloadTasks: 'Download tasks', downloadStarted: 'Download started', activateSuccess: 'Version selection saved. Restart Hermes Studio to use it.', jobStatus: { queued: 'Queued', running: 'Running', completed: 'Completed', failed: 'Failed', }, jobStageStatus: { queued: 'Queued', resolve: 'Resolving', download: 'Downloading', verify: 'Verifying', extract: 'Extracting', install: 'Installing', completed: 'Completed', failed: 'Failed', }, jobStage: { queued: 'Queued', download: 'Downloading', resolveRuntime: 'Resolving runtime package', verifyRuntime: 'Verifying runtime archive', extractRuntime: 'Extracting runtime archive', installRuntime: 'Installing runtime', resolveWebUi: 'Resolving Web UI package', verifyWebUi: 'Verifying Web UI archive', extractWebUi: 'Extracting Web UI archive', installWebUi: 'Installing Web UI', completed: 'Completed', failed: 'Failed', }, }, mcuDevices: { title: 'Little Box', subtitle: 'View device link status', name: 'Name', nameOptional: 'Name is optional', deviceCode: 'Device code', deviceCodePlaceholder: 'Enter device code', deviceCodeRequired: 'Enter device code', channel: 'Channel', official: 'Official', unofficial: 'Unofficial', createdAt: 'Added at', lanStatus: 'LAN', remoteStatus: 'Remote', connected: 'Connected', disconnected: 'Disconnected', remoteConnect: 'Remote connect', remoteConnected: 'Remote connected', remoteConnectFailed: 'Failed to connect remote', remoteDisconnect: 'Disconnect', remoteDisconnected: 'Remote disconnected', remoteDisconnectFailed: 'Failed to disconnect remote', actions: 'Actions', add: 'Add', addTitle: 'Add MCU device', added: 'MCU device added', addFailed: 'Failed to add MCU device', purchase: 'Buy', edit: 'Edit', editNameTitle: 'Edit name', nameUpdated: 'Name updated', nameUpdateFailed: 'Failed to update name', delete: 'Delete', deleteTitle: 'Delete MCU device', deleteConfirm: 'Delete "{name}"?', deleted: 'MCU device deleted', deleteFailed: 'Failed to delete MCU device', loadFailed: 'Failed to load MCU devices', close: 'Close', }, devices: { title: 'Devices', count: '{count} devices', lastScanned: 'Last scanned: {time}', refresh: 'Refresh', empty: 'No devices found', computer: 'Computer', endpointLabel: 'Endpoint', statusLabel: 'Status', online: 'Online', offline: 'Offline', address: 'Address', os: 'OS', agentVersion: 'Agent', webUiVersion: 'Web UI', responseMs: 'Latency', lastSeen: 'Last seen', open: 'Open', never: 'Never', unknown: 'Unknown', loadFailed: 'Failed to load devices', scanFailed: 'Failed to scan devices', updateFailed: 'Failed to update device', duplicateRequest: 'A pending pairing request already exists', manualPairing: 'Pair by URL', copyPairingLink: 'Copy pairing link', manualUrlPlaceholder: 'https://device.example.com', manualUrlRequired: 'Enter a device URL', manualRequestSent: 'Pairing request sent', manualRequestFailed: 'Failed to request remote pairing', pairingLinkCopied: 'Pairing link copied', pairingLinkCopyFailed: 'Failed to copy pairing link', pairingCodeTitle: 'Enter pairing code', pairingCodePlaceholder: 'Pairing code', pairingCodeRequired: 'Enter the pairing code', submitPairingRequest: 'Submit request', processed: 'Processed', unprocessed: 'Unprocessed', deleteHistory: 'Delete record', deleteHistoryConfirm: 'Delete this request record and revoke this device connection access?', requests: 'Requests', requestsWithCount: 'Requests ({count})', noRequests: 'No requests', requestPairing: 'Request pairing', paired: 'Paired', approve: 'Approve', reject: 'Reject', block: 'Block', unblock: 'Unblock', inboundStatus: { none: 'Allowed to request', pending: 'Pending', approved: 'Approved', rejected: 'Rejected', blocked: 'Blocked', }, outboundStatus: { none: 'Unpaired', pending: 'Requested', approved: 'Paired', rejected: 'Rejected', blocked: 'Blocked', }, endpoint: { web: 'Web', desktop: 'Desktop', custom: 'Custom', }, }, performance: { title: 'Performance', subtitle: 'Inspect system resources, bridge broker, workers, and active sessions', refresh: 'Refresh', autoRefreshOn: 'Auto refresh', autoRefreshOff: 'Manual refresh', loadFailed: 'Failed to load performance metrics', systemCpu: 'System CPU', systemMemory: 'System Memory', activeSessions: 'Active Sessions', runningSessions: 'Running {count}', workers: 'Workers', totalWorkerMemory: 'Worker memory', processes: 'Processes', uptime: 'Uptime', running: 'Running', stopped: 'Stopped', workerMemory: 'Worker Memory', lastUpdated: 'Updated', profile: 'Profile', memory: 'Memory', sessions: 'Sessions', runningActiveSessions: 'Running / Active', lastUsed: 'Last Used', status: 'Status', noWorkers: 'No workers', sessionsByProfile: 'Sessions by Profile', noActiveSessions: 'No active sessions', }, journey: { title: 'Learning Journey', play: 'Play journey', pause: 'Pause journey', refresh: 'Refresh', loadFailed: 'Failed to load learning journey', nodes: 'Nodes', edges: 'Edges', skills: 'Skills', memories: 'Memories', clusters: 'Clusters', profile: 'Profile', source: 'Source', learnedSkills: 'Learned skills', memorySkillEdges: 'Memory links', allKinds: 'All kinds', allCategories: 'All categories', searchPlaceholder: 'Search nodes', noClusters: 'No clusters', noNodes: 'No journey nodes', noCategory: 'Uncategorized', category: 'Category', useCount: 'Use count', createdBy: 'Created by', timestamp: 'Time', pinned: 'Pinned', }, // 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: 256k (Hermes default), 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', emptyStateAgent: 'Start a conversation with {agent}', viewOlderInHistory: 'View earlier messages in History', scrollToBottom: 'Scroll to bottom', forkedFrom: 'Forked from', previousLastMessage: 'Previous last message', cliEmptyState: 'Start a CLI chat session', outlineTitle: 'Conversation Outline', outlineEmpty: 'No conversation content', outlineUserQuestion: 'User question', inputPlaceholder: 'Type a message... (Enter to send, Shift+Enter for new line)', inputHeightResizeHint: 'Drag to resize the chat input. Double-click to reset.', slashCommandArgs: { message: '', title: '', text: '<text>', }, slashCommands: { usage: 'Calculate current session usage', status: 'Show session status and queue', abort: 'Stop the active bridge run', queue: 'Queue a message behind the active run', learn: 'Learn a reusable skill from described sources', plan: 'Write a markdown implementation plan', moa: 'Run one prompt through the default model ensemble', goal: 'Set a standing goal that continues across turns', goalStatus: 'Show the active goal status', goalPause: 'Pause the active goal loop', goalResume: 'Resume the paused goal loop', goalDone: 'Complete and clear the active goal', goalClear: 'Clear the active goal', subgoal: 'Add a criterion to the active goal', clear: 'Clear the current display', clearHistory: 'Delete this session’s stored message history', title: 'Rename this session', compress: 'Run context compression while idle', fork: 'Fork this idle session into a new linked conversation', steer: 'Send steering text to the active bridge run', destroy: 'Release the bridge agent for this session', reloadMcp: 'Reload MCP servers', reloadSkills: 'Reload skill commands', }, attachFiles: 'Attach files', reasoningEffort: { tooltip: 'Reasoning effort', defaultLabel: 'Default', options: { default: 'Default (config.yaml)', none: 'None', minimal: 'Minimal', low: 'Low', medium: 'Medium', high: 'High', xhigh: 'Extra high', }, }, autoPlaySpeech: 'Auto-play voice', voiceInput: { startCapture: 'Start voice capture', stopCaptureAndInsert: 'Stop voice capture and place transcript in the input', cancelCapture: 'Cancel voice dialogue', statusLabel: 'Status: {status}', transcriptLabel: 'Transcript: {text}', errorLabel: 'Error: {error}', recentEvents: 'Recent events', status: { idle: 'Idle', capturing: 'Capturing', transcribing: 'Transcribing', sending: 'Adding to input', error: 'Error', }, browserSpeechUnsupported: 'Browser speech recognition is not supported in this browser.', browserSpeechFailed: 'Browser speech recognition failed.', browserSpeechFailedWithReason: 'Browser speech recognition failed: {error}.', microphoneUnsupported: 'Microphone capture is not supported in this browser.', microphoneRecordingFailed: 'Microphone recording failed.', }, showToolCalls: 'Show tool calls', hideToolCalls: 'Hide tool calls', messageQueue: 'Message queue', removeQueuedMessage: 'Remove queued message', stop: 'Stop', start: 'Start', stopGateway: 'Stop Gateway', send: 'Send', contextUsed: 'Context used:', sessions: 'Sessions', webUiSessions: 'Sessions', allProfiles: 'All profiles', profileMissingModelsTip: 'Profile "{profile}" has no available provider or model for this session', 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 for the current profile, grouped by source.', noSessions: 'No sessions', searchTitle: 'Search Sessions', searchSubtitle: 'Search by title or message content', searchScope: 'Search scope: Web UI local session database only. Read-only Hermes history sessions are not included.', 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', agent: 'Agent', approvalKicker: 'Terminal permission', approvalTitle: 'Review command before running', approvalAgree: 'Agree', approvalAllowOnce: 'Allow once', approvalAllowSession: 'Allow session', approvalAlways: 'Always', approvalDeny: 'Deny', clarifyKicker: 'Agent needs clarification', clarifyTitle: 'The agent has a question for you', clarifyPlaceholder: 'Type your answer...', clarifySubmit: 'Reply', clarifyDismiss: 'Dismiss', newCliChat: 'New CLI', 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', importToWebUi: 'Import to Web UI', importSessionSuccess: 'Session imported to Web UI', importSessionAlreadyExists: 'Session already exists in Web UI', importSessionFailed: 'Failed to import session', rename: 'Rename', archiveSession: 'Archive', unarchiveSession: 'Unarchive', 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', copySessionLink: 'Copy Session Link', openSessionInNewTab: 'Open in new tab', sessionLinkCopied: 'Session link copied', 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', sessionArchived: 'Session archived', archiveSessionFailed: 'Failed to archive session', sessionUnarchived: 'Session unarchived', unarchiveSessionFailed: 'Failed to unarchive session', renameSession: 'Rename Session', sessionNotFound: 'Session not found', enterNewTitle: 'Enter new title', workspace: 'Workspace', workspaceDefault: 'Default', workspaceDefaultTooltip: 'This workspace is set as default and will be auto-filled for new chats', workspacePin: 'Set as default workspace', workspaceUnpin: 'Unset default workspace', workspaceRecent: 'Recent', defaultWorkspace: 'Default Workspace', more: 'More', setWorkspace: 'Set Workspace', setWorkspaceTitle: 'Set Session Workspace', workspacePlaceholder: 'Enter project path, e.g. /home/user/project', folderPickerEmpty: '(Empty)', folderPickerNoFolders: 'No workspace folders', folderPickerSelected: 'Selected:', workspaceSet: 'Workspace set', workspaceSetFailed: 'Failed to set workspace', setModel: 'Set Model', setModelTitle: 'Set Session Model', modelSet: 'Model set', modelSetFailed: 'Failed to set model', other: 'Other', runFailed: 'Run failed', error: 'Error', tool: 'Tool', arguments: 'Arguments', result: 'Result', workspaceChanges: 'Workspace changes', changedFiles: '{files} files changed', diffUnavailable: 'Diff unavailable', binaryFileDiffUnavailable: 'Binary file diff unavailable', truncated: '... (truncated)', unchangedLines: '{count} unchanged lines', 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', }, workflow: { title: 'Workflow', profile: 'Profile', namePlaceholder: 'Workflow name', workspace: { title: 'Select Workspace', select: 'Select workspace', clear: 'Clear workspace', }, actions: { newWorkflow: 'New Workflow', addNode: 'Add Node', createWorkflowFirst: 'Please create a workflow first', reset: 'Reset', startExecution: 'Start Execution', executionPending: 'Workflow execution is not connected yet', executionStarted: 'Workflow execution started', executionCompleted: 'Workflow execution completed', executionFailed: 'Workflow execution failed', rerunDownstreamKeepNode: 'Keep this node and run downstream', rerunDownstreamClearNode: 'Clear this node and run downstream', rerunDownstreamStarted: 'Downstream rerun started', rerunFromNodeStarted: 'Rerun from this node started', rerunFailed: 'Workflow rerun failed', deleteNode: 'Delete Node', deleteEdge: 'Delete Connection', }, batch: { toggle: 'Batch selection', selectAll: 'Select all', confirmDelete: 'Delete {count} selected workflows?', deleteSuccess: 'Deleted {count} workflows', deletePartial: '{failed} workflows failed to delete', deleteFailed: 'Batch delete failed', }, validation: { nodesRequired: 'Add at least one node', nodeNameRequired: 'Node {node} needs a name', providerRequired: 'Node {node} needs a provider', modelRequired: 'Node {node} needs a model', apiModeRequired: 'Node {node} needs an API mode', inputRequired: 'Node {node} needs input', invalidEdge: 'A connection references a missing node', invalidConnectionDirection: 'Connections must go from the right output to the left input', orphanNode: 'Node {node} is not connected to the workflow', disconnectedFlow: 'The workflow cannot be split into multiple disconnected flows', cycle: 'The workflow contains a cycle. Adjust the connections before saving.', }, node: { title: 'Node name', agent: 'Agent', model: 'Model', apiMode: 'API Mode', input: 'Input', approvalRequired: 'Approval gate', promptPlaceholder: 'Describe what this agent should do...', skillsPlaceholder: 'Skills, press Enter to add', uploadImages: 'Upload images', }, status: { idle: 'Idle', queued: 'Queued', running: 'Running', pending_approval: 'Pending approval', completed: 'Completed', failed: 'Failed', approval_rejected: 'Approval rejected', canceled: 'Canceled', }, runs: { title: 'Run Records', refresh: 'Refresh', empty: 'No run records', startNodes: '{count} start nodes', show: 'Show run records', hide: 'Hide run records', nodeSessionTitle: 'Node Session - {node}', noNodeSession: 'This node has no session record yet', loadNodeSessionFailed: 'Failed to load node session', stop: 'Stop run', stopRequested: 'Stop requested', stopFailed: 'Failed to stop run', delete: 'Delete record', deleteSuccess: 'Run record deleted', }, inspector: { selected: 'Selected Node', none: 'No node selected', }, stats: { nodes: 'Nodes', edges: 'Links', }, agents: { planner: 'Planner', researcher: 'Researcher', builder: 'Builder', reviewer: 'Reviewer', }, models: { default: 'Default Model', fast: 'Fast Model', reasoning: 'Reasoning Model', }, initialNodes: { node1: 'Node 1', node2: 'Node 2', node3: 'Node 3', plan: 'Plan', execute: 'Execute', review: 'Review', }, initialPrompts: { node1: 'Enter this node input.', node2: 'Enter this node input.', node3: 'Enter this node input.', plan: 'Break the request into actionable steps.', execute: 'Run the implementation steps and produce artifacts.', review: 'Review output quality and identify follow-up work.', }, newNodeTitle: 'Node {count}', }, // 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', scheduled: 'Scheduled', ready: 'Ready', running: 'Running', blocked: 'Blocked', review: 'Review', 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...', tenant: 'Tenant', tenantPlaceholder: 'Tenant (optional)', workspace: 'Workspace', workspaceScratch: 'Scratch workspace', workspaceDir: 'Use directory', workspaceWorktree: 'Use worktree', workspacePath: 'Workspace path', workspacePathPlaceholder: 'Absolute path (optional)', branch: 'Branch', branchPlaceholder: 'Worktree branch', skills: 'Skills', skillsPlaceholder: 'Select skills...', triage: 'Create in triage', goalMode: 'Goal mode', maxRuntime: 'Max runtime', maxRuntimePlaceholder: 'e.g. 30m, 2h', maxRetries: 'Max retries', maxRetriesPlaceholder: 'Number of retries', goalMaxTurns: 'Goal max turns', goalMaxTurnsPlaceholder: 'Maximum turns', }, 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', noLog: 'No log found for this task.', addComment: 'Add Comment', commentPlaceholder: 'Write a task comment...', operations: 'Operations', }, action: { title: 'Actions', complete: 'Complete', completeSummary: 'Completion summary (optional)', block: 'Block', blockReason: 'Reason for blocking', unblock: 'Unblock', assign: 'Assign', assignTo: 'Assign to...', dispatch: 'Dispatch', recoveryReason: 'Recovery reason (optional)', reclaim: 'Reclaim', reassign: 'Reassign', specify: 'Specify', loadLog: 'Load Log', loadDiagnostics: 'Load Diagnostics', }, message: { taskCreated: 'Task created', taskCompleted: 'Task completed', taskBlocked: 'Task blocked', taskUnblocked: 'Task unblocked', taskAssigned: 'Task assigned', dispatchNudged: 'Dispatcher nudged', commentAdded: 'Comment added', taskReclaimed: 'Task reclaimed', taskReassigned: 'Task reassigned', taskSpecified: 'Task specified', 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.', sortBy: 'Sort', sortByName: 'Name Sort', sortByTime: 'Time Sort', sortByNameHint: 'Click to sort by name', sortByTimeHint: 'Click to sort by time', sortAsc: 'Ascending', sortDesc: 'Descending', 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', skills: 'Skills', skillsPlaceholder: 'Select skills...', provider: 'Provider', model: 'Model', defaultProvider: 'Default provider', defaultModel: 'Default model', 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', provider: 'Provider', 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', targetFilter: 'Runtime', targets: { hermes: 'Hermes', claude: 'Claude', codex: 'Codex', }, 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', edit: 'Edit', saving: 'Saving...', editorLabel: 'Skill editor', saveSuccess: 'Skill saved', saveFailed: 'Failed to save skill', toggleFailed: 'Failed to toggle skill', source: { builtin: 'Builtin', hub: 'Hub', local: 'Local', external: 'External', }, delete: 'Delete', deleteConfirm: 'Delete skill "{name}"? This cannot be undone.', deleteSuccess: 'Skill deleted', deleteFailed: 'Failed to delete skill', import: 'Import', importTitle: 'Import skill', importModeZip: 'Zip', importModeFolder: 'Folder', importHintZip: 'Upload a .zip archive containing a directory with SKILL.md', importHintFolder: 'Pick a local skill folder (must contain SKILL.md)', importTargetCategory: 'Target category (optional)', importTargetCategoryPlaceholder: 'Leave empty to drop under misc', importSelectFile: 'Choose .zip file', importSelectFolder: 'Choose folder', importInvalidFile: 'Only .zip archives are supported', importFileCount: 'files', importNoSelection: 'Please choose a file or folder first', importSuccess: 'Skill imported', importFailed: 'Failed to import skill', path: { local: 'Local', external: 'External', }, externalDirs: { manage: 'External dirs', title: 'Manage external skill dirs', hint: 'Manages config.skills.external_dirs. Supports ~ and $VAR; missing paths are greyed out.', add: 'Add external dir', placeholder: '/path/to/skills or ~/my-skills', empty: 'No external dirs', missing: 'Path not found', notDir: 'Not a directory', saveSuccess: 'External dirs saved', saveFailed: 'Save failed', loadFailed: 'Failed to load external dirs', removeRow: 'Remove', }, pathCopied: 'Path copied', pathCopyFailed: 'Failed to copy path', reloadHint: 'Active sessions: run /reload-skills; new sessions pick it up automatically.', writeApprovalButton: 'Pending {count}', writeApprovalTitle: 'Pending write approvals', writeApprovalDescription: 'Approve or reject staged Hermes Agent memory and skill writes.', writeApprovalRefresh: 'Refresh', writeApprovalEmpty: 'No pending writes.', writeApprovalUnsupported: 'This Hermes Agent version does not support write approvals. Upgrade Hermes Agent to use this feature.', writeApprovalMemory: 'Memory', writeApprovalSkills: 'Skills', writeApprovalViewDiff: 'Review', writeApprovalHideDiff: 'Hide', writeApprovalNoDiff: 'No diff available.', writeApprovalLoadFailed: 'Failed to load pending writes', writeApprovalDiffFailed: 'Failed to load write details', writeApprovalActionFailed: 'Failed to resolve pending write', writeApprovalApproved: 'Pending write approved', writeApprovalRejected: 'Pending write rejected', writeApprovalApprove: 'Approve', writeApprovalReject: 'Reject', writeApprovalPendingMemoryWrite: 'Pending memory write', writeApprovalCurrentFile: 'Current {file}', writeApprovalProposedFile: 'Proposed {file}', writeApprovalDiffSection: 'Diff', writeApprovalPatchOldStringMissing: 'The patch old_string was not found in the current {file}, so approving it will not change the file.', writeApprovalRequestedOldString: 'Requested old_string', writeApprovalCurrentReadFailed: 'The current {file} could not be read as UTF-8 text.', writeApprovalDeleteSkill: 'This approval deletes skill {skill}.', writeApprovalRemoveFile: 'This approval removes {file} from skill {skill}.', }, // Plugins plugins: { title: 'Plugins', refresh: 'Refresh', notice: 'Inventory of discoverable Hermes plugin manifests. Discovery metadata is read without loading plugin code. Standalone user plugins can be enabled or disabled here; changes take effect in new Hermes sessions.', loadFailed: 'Failed to load plugins', updateFailed: 'Failed to update plugin', enableSuccess: 'Enabled {name}', disableSuccess: 'Disabled {name}', 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', manage: 'Manage', }, capabilities: { tools: '{count} tools', hooks: '{count} hooks', env: '{count} env', }, metadata: { agentRoot: 'Agent root', python: 'Python', scanCwd: 'Scan cwd', projectPlugins: 'Project plugins', }, }, petdex: { title: 'Petdex Pets', subtitle: 'Browse the public petdex catalog used by Hermes desktop pets.', refresh: 'Refresh', adopt: 'Adopt', active: 'Active', adopted: 'Adopted {name}', adoptFailed: 'Failed to adopt pet', loadFailed: 'Failed to load petdex catalog', searchPlaceholder: 'Search by name, slug, author, or kind', kindFilter: 'Kind', spritesheet: 'Spritesheet', submittedBy: 'By {name}', empty: 'No pets match the current filters.', showMore: 'Show {count} more', summary: { total: 'Catalog total', visible: 'Matching', kinds: 'Kinds', generatedAt: 'Generated at', }, }, // 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...', noResults: 'No results', noModels: 'No models', addProvider: 'Add Provider', refreshModelCache: 'Refresh model cache', refreshModelCacheLoading: 'Refreshing model cache...', refreshModelCacheSuccess: 'Model cache refreshed', refreshModelCacheFailed: 'Failed to refresh model cache', 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', providerCredentialsCleared: 'Provider credentials cleared', clearProviderCredentials: 'Clear credentials', clearCredentialsConfirm: 'Clear stored credentials for "{name}"? This built-in provider will stay listed and can be used again after credentials are configured.', disableProvider: 'Disable provider', disableProviderConfirm: 'Disable built-in provider "{name}"? The provider will stay built in and can be enabled again later.', providerDisabled: 'Provider disabled', defaultProviderUpdated: 'Default provider updated', defaultProviderUpdateFailed: 'Failed to update default provider', defaultModelUpdated: 'Default model updated', defaultModelUpdateFailed: 'Failed to update default model', 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.', xaiLoginTitle: 'xAI Grok OAuth Login', xaiWaiting: 'Complete authorization in the opened xAI page. This window will close automatically once approved.', xaiOpenLink: 'Open xAI authorization page', xaiCopyLink: 'Copy authorization link', xaiApproved: 'Sign-in succeeded!', xaiExpired: 'The authorization link has expired. Please retry.', anthropicLoginTitle: 'Claude OAuth Login', anthropicWaiting: 'Complete authorization on the Claude page, then paste the returned code here.', anthropicOpenLink: 'Open Claude authorization page', anthropicCopyLink: 'Copy authorization link', anthropicCodePlaceholder: 'Paste the authorization code...', anthropicSubmitCode: 'Submit code', anthropicApproved: 'Sign-in succeeded!', anthropicExpired: 'Authorization expired. Please retry.', geminiLoginTitle: 'Google Gemini OAuth Login', geminiWaiting: 'Complete authorization on the opened Google page. This window will close automatically once approved.', geminiOpenLink: 'Open Google authorization page', geminiCopyLink: 'Copy authorization link', geminiApproved: 'Sign-in succeeded!', geminiExpired: 'The authorization link has expired. Please retry.', 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', refresh: 'Refresh model list (reload config.yaml)', 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', clearVisibleModels: 'Clear selection', generalTitle: 'General Models', auxiliaryTitle: 'Auxiliary Models', auxiliarySubtitle: 'Side-task model overrides for compression, vision, approvals, MCP, and background maintenance.', combinationTitle: 'Model Ensembles', combinationSubtitle: 'Configure MoA presets: reference models provide perspectives first, then one aggregator model handles the final response and tool calls.', combinationPreset: 'Preset', combinationName: 'Name', combinationAddModel: 'Add model ensemble', combinationEditModel: 'Edit model ensemble', combinationNewPreset: 'New preset', combinationPresetPlaceholder: 'e.g. review', combinationAddPreset: 'Add preset', combinationSetDefault: 'Set default', combinationEnabled: 'Enable references', combinationReferenceModels: 'Reference models', combinationReferenceModel: 'Reference model', combinationAddReference: 'Add reference model', combinationAggregator: 'Aggregator model', combinationReferenceTemperature: 'Reference temperature', combinationAggregatorTemperature: 'Aggregator temperature', combinationMaxTokens: 'Max tokens', combinationNotSet: 'Not set', combinationSaved: 'Model ensemble saved', combinationLoadFailed: 'Failed to load model ensemble', combinationSaveFailed: 'Failed to save model ensemble', combinationPresetExists: 'Preset already exists', combinationNameRequired: 'Enter a model ensemble name', combinationNameInvalid: 'Name can only contain letters, numbers, underscores, hyphens, and dots', combinationReferenceRequired: 'Add at least one reference model', combinationAggregatorRequired: 'Select an aggregator model', auxiliaryRefresh: 'Refresh', auxiliaryTask: 'Task', auxiliaryTimeout: 'Timeout', auxiliaryDownloadTimeout: 'Download timeout', auxiliaryActions: 'Actions', auxiliaryDefault: 'Default', auxiliaryCustomEndpoint: 'Custom endpoint', auxiliaryProviderAuto: 'Auto', auxiliaryProviderMain: 'Main model', auxiliaryProviderPlaceholder: 'auto, main, or provider key', auxiliaryDownloadShort: 'download', auxiliaryExtraBody: 'Extra body JSON', auxiliaryClear: 'Clear', auxiliarySaved: 'Auxiliary models saved', auxiliaryLoadFailed: 'Failed to load auxiliary models', auxiliarySaveFailed: 'Failed to save auxiliary models', auxiliaryInvalidExtraBody: 'Extra body must be a JSON object', auxiliaryTaskCompression: 'Compression', auxiliaryTaskVision: 'Vision', auxiliaryTaskWebExtract: 'Web extract', auxiliaryTaskSkillsHub: 'Skills hub', auxiliaryTaskApproval: 'Approval', auxiliaryTaskMcp: 'MCP', auxiliaryTaskTitleGeneration: 'Title generation', auxiliaryTaskTriageSpecifier: 'Triage specifier', auxiliaryTaskKanbanDecomposer: 'Kanban decomposer', auxiliaryTaskProfileDescriber: 'Profile describer', auxiliaryTaskCurator: 'Curator', auxiliaryTaskSessionSearch: 'Session search', auxiliaryTaskFlushMemories: 'Flush memories', currentDefault: 'Current default', defaultShort: 'Default', setDefaultProvider: 'Set default provider', setDefault: 'Set default', builtIn: 'Built-in', customType: 'Custom', provider: 'Provider', contextLength: 'Context Length', contextLengthPlaceholder: 'e.g. 256000 (optional)', apiMode: 'API Mode', 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', editConfig: 'Edit Config', switchTo: 'Switch Hermes Profile', switchConfirm: 'This will run `hermes profile use {name}` and change the active Hermes CLI profile. Continue?', switchSuccess: 'Hermes active profile switched to "{name}"', switchFailed: 'Failed to switch Hermes 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.', avatar: { title: 'Custom Avatar', customize: 'Avatar', upload: 'Upload Image', random: 'Randomize', reset: 'Use Default', hint: 'PNG, JPEG, or WebP. Max 1MB.', invalidType: 'Please choose a PNG, JPEG, or WebP image', tooLarge: 'Avatar image must be 1MB or smaller', saveSuccess: 'Avatar saved', saveFailed: 'Failed to save avatar', resetSuccess: 'Default avatar restored', resetFailed: 'Failed to restore default avatar', }, runtime: { activeProfile: 'Active: {name}', bridgeWorker: 'Bridge worker', gateway: 'Gateway', active: 'Active', activeTag: 'Active', idle: 'Idle', checking: 'Checking', running: 'Running', stopped: 'Stopped', restartGateway: 'Restart Gateway', restartProfile: 'Restart Profile', switchProfile: 'Switch Frontend Profile', gatewayRestarted: 'Gateway restarted: {name}', gatewayRestartFailed: 'Failed to restart gateway', profileRestarted: 'Profile restarted: {name}', profileRestartFailed: 'Failed to restart profile', }, }, // 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: 'Current Account', users: 'Account Management', agent: 'Agent', proxy: 'Proxy', memory: 'Memory', compression: 'Compression', 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', notifyOnComplete: 'Completion Notification', notifyOnCompleteHint: 'Show a system notification when AI finishes', notifyOnCompleteMacHint: 'On macOS, allow browser notifications in System Settings.', notifyOnCompleteDenied: 'Notification permission was denied', notifyOnCompleteInsecure: 'Browser notifications require HTTPS or localhost', notifyOnCompleteUnsupported: 'This browser does not support notifications', notifyOnCompleteTest: 'Notifications are enabled.', notifyOnCompleteTestButton: 'Test', notifyOnCompleteTesting: 'Sending test notification...', notifyOnCompleteTestSent: 'Test notification sent', notifyOnCompleteTestFailed: 'Failed to send test notification', 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', chatInputHeight: 'Chat input height', chatInputHeightHint: 'Default desktop chat input height in pixels. Mobile keeps auto height.' }, 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', }, gatewayAutoStart: { title: 'Gateway Auto-Start', description: 'Control which local Hermes profile gateways the Web UI starts during startup. This is stored in the Web UI config, not a profile config.yaml.', enabled: 'Auto-start gateways', enabledHint: 'When disabled, startup will not automatically start any profile gateway.', management: 'Unified gateway', managementHint: 'Requires Hermes Agent 0.17.0 or later. When enabled, Web UI writes multiplex_profiles so the default gateway serves every profile; other profiles cannot configure separate Feishu, Weixin, QQBot, or similar messaging entrypoints. Turning it off removes that config.', managementAuto: 'Auto', managementPerProfile: 'One gateway per profile', managementUnified: 'Unified default gateway', mode: 'Profile policy', modeHint: 'Start all discovered profiles or only the explicit include list.', modeAll: 'All discovered profiles', modeInclude: 'Only included profiles', include: 'Included profiles', includeHint: 'Comma-separated profile names. In include mode, an empty list starts none.', exclude: 'Excluded profiles', excludeHint: 'Comma-separated profile names to skip after the include/all policy is applied.', profileListPlaceholder: 'default, reviewer', }, proxy: { title: 'Gateway Proxy', description: 'Set proxy environment variables for the selected profile gateway. Changes restart the gateway so messaging platforms can use the new network settings.', httpsProxy: 'HTTPS_PROXY', httpsProxyHint: 'Proxy for HTTPS requests. Usually the main value to set.', httpProxy: 'HTTP_PROXY', httpProxyHint: 'Proxy for plain HTTP requests.', allProxy: 'ALL_PROXY', allProxyHint: 'Fallback proxy used when a protocol-specific proxy is not set.', noProxy: 'NO_PROXY', noProxyHint: 'Comma-separated hosts or domains that should bypass the proxy.', save: 'Save Proxy Settings', }, 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', }, compression: { enabled: 'Enable Compression', enabledHint: 'Automatically compress long chat history before it exceeds the model context', threshold: 'Compression Threshold', thresholdHint: 'Start compression when estimated tokens exceed this context ratio', targetRatio: 'Target Ratio', targetRatioHint: 'Target history size after compression as a context ratio', protectLastN: 'Protect Recent Messages', protectLastNHint: 'Keep this many latest messages uncompressed', protectFirstN: 'Protect First Messages', protectFirstNHint: 'Keep this many earliest messages uncompressed', }, session: { mode: 'Reset Mode', modeHint: 'Trigger condition for session reset', modeBoth: 'Idle + Scheduled', modeIdle: 'Idle Only', modeDaily: 'Scheduled Only', modeNone: 'Never (Manual 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', memoryWriteApproval: 'Review memory writes', memoryWriteApprovalHint: 'Stage Hermes Agent memory writes for approval before they are saved.', skillsWriteApproval: 'Review skill writes', skillsWriteApprovalHint: 'Stage Hermes Agent skill changes for approval before they are saved.', writeApprovalTitle: 'Pending write approvals', writeApprovalDescription: 'Approve or reject staged Hermes Agent memory and skill writes.', writeApprovalRefresh: 'Refresh', writeApprovalEmpty: 'No pending writes.', writeApprovalMemory: 'Memory', writeApprovalSkills: 'Skills', writeApprovalViewDiff: 'Review', writeApprovalHideDiff: 'Hide', writeApprovalNoDiff: 'No diff available.', writeApprovalLoadFailed: 'Failed to load pending writes', writeApprovalDiffFailed: 'Failed to load write details', writeApprovalActionFailed: 'Failed to resolve pending write', writeApprovalApproved: 'Pending write approved', writeApprovalRejected: 'Pending write rejected', writeApprovalApprove: 'Approve', writeApprovalReject: 'Reject', writeApprovalPendingMemoryWrite: 'Pending memory write', writeApprovalCurrentFile: 'Current {file}', writeApprovalProposedFile: 'Proposed {file}', writeApprovalDiffSection: 'Diff', writeApprovalPatchOldStringMissing: 'The patch old_string was not found in the current {file}, so approving it will not change the file.', writeApprovalRequestedOldString: 'Requested old_string', writeApprovalCurrentReadFailed: 'The current {file} could not be read as UTF-8 text.', writeApprovalDeleteSkill: 'This approval deletes skill {skill}.', writeApprovalRemoveFile: 'This approval removes {file} from skill {skill}.', }, 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', type: { password: 'Password', token: 'API token', pairing: 'Device pairing', }, }, userAvatar: { title: 'User Avatar', upload: 'Upload Image', random: 'Generate Random', reset: 'Reset to Default', hint: 'Supports PNG/JPEG/WebP up to 1MB, auto-compressed to under 500KB', invalidType: 'Please choose a PNG, JPEG, or WebP image', tooLarge: 'Avatar image must be under 1MB', saveSuccess: 'Avatar saved', saveFailed: 'Failed to save avatar', resetSuccess: 'Avatar reset to default', resetFailed: 'Failed to reset avatar', }, voice: { activeTtsApi: 'Active TTS API', activeSttApi: 'Active STT API', ttsProvidersTitle: 'TTS providers', ttsProvidersDescription: 'Manage text-to-speech integrations used to play assistant responses and voice previews.', sttProvidersTitle: 'STT providers', sttProvidersDescription: 'Manage speech-to-text integrations used by the microphone and voice input flow.', noneSelected: 'None selected', keyStored: 'Stored', keyMissing: 'Missing', customApi: 'API', localBuiltin: 'Local built-in', ttsCardHint: 'Speech synthesis provider', sttCardHint: 'Speech transcription provider', setActive: 'Set active', testAction: 'Test', connect: 'Connect', presetEdgeTtsLabel: 'Edge TTS', presetEdgeTtsDescription: 'Free high-quality cloud voices from Microsoft Edge.', presetOpenaiTtsLabel: 'OpenAI TTS', presetMimoTtsLabel: 'MiMo TTS', presetDoubaoTtsLabel: 'Volcengine Doubao TTS', presetCustomTtsLabel: 'Custom TTS', presetBrowserSttLabel: 'Browser STT', presetBrowserSttDescription: 'Native browser speech recognition API.', presetOpenaiWhisperLabel: 'OpenAI Whisper', presetGroqSttLabel: 'Groq STT', presetDoubaoSttLabel: 'Volcengine Doubao STT', presetCustomSttLabel: 'Custom STT', apiCompatibility: 'API compatibility', openaiCompatible: 'OpenAI-compatible', manualCustomEndpoint: 'Manual custom endpoint', connectionStep: 'Connection', discoveryManualHint: 'Manual custom endpoint selected. Model discovery is not available; enter the model name manually.', discoveryCredentialHint: 'Enter Base URL and API key, then connect to fetch available models.', discoveryNoAutoProbeHint: 'No requests are sent while typing. Use Connect when the credentials are ready.', apiKeyConnectPlaceholder: 'Paste API key to connect; stored keys are never shown', connectFetchModels: 'Connect & fetch models', modelRecommendedSuffix: 'recommended', modelConnectManualHint: 'Connect to auto-fill, or enter manually.', modelRequiredConnectManual: 'Model is required. Connect to auto-fill, or enter manually.', apiKeyRequired: 'API key is required.', apiKeyRequiredForDiscovery: 'API key is required to fetch models.', baseUrlRequired: 'Base URL is required.', baseUrlHttpsRequired: 'Please use http:// or https://.', baseUrlInvalid: 'Enter a valid Base URL, including https://.', discoveryFailedManualFallback: 'Could not fetch models. You can still enter the model name manually.', completeRequiredProviderFields: 'Complete the required provider fields before saving.', fetchingModels: 'Fetching models…', recommendedModel: 'Recommended: {model}.', useRecommendedModel: 'Use this.', modelsDiscovered: '{count} models discovered. Recommended model is selected when the field is empty.', remove: 'Remove', testSuccess: 'Test completed', testTextRequired: 'Enter text before testing.', keyMissingForTest: 'Add an API key before testing this provider.', testing: 'Testing…', transcribing: 'Transcribing…', browserSttTestHint: 'Browser STT is tested from the chat microphone.', sttEmptyAudio: 'No audio was captured. Try again.', sttTestSuccess: 'Transcription completed', sttRecordingHint: 'Recording… click Stop to transcribe.', keepStoredKeyPlaceholder: 'Leave blank to keep the stored key', apiKeyPlaceholder: 'Enter API key', lastTest: 'Last test', notTested: '—', recording: 'Recording', testFailedShort: 'Failed', testFailedSummary: 'Test failed. Check provider settings and try again.', showDetails: 'Show details', hideDetails: 'Hide details', moreActions: 'More', moreActionsFor: 'More actions for {provider}', addTtsApi: 'Add TTS API', addSttApi: 'Add STT API', active: 'Active', builtin: 'Built-in', connected: 'Connected', disconnected: 'Disconnected', source: 'Source', model: 'Model', voice: 'Voice', doubaoVoiceHint: 'Select a Seed TTS 2.0 voice; the resource field is updated automatically.', apiKey: 'API key', confirmDeleteSecret: 'Delete the stored API key for this voice API?', ttsTitle: 'Text-to-speech', ttsSaved: 'TTS settings saved on the server', ttsSaveFailed: 'Failed to save TTS settings', ttsTestSaved: 'Audition succeeded; TTS settings saved on the server', ttsTestSaving: 'Saving TTS settings before audition…', ttsMissingBaseUrl: 'Base URL is required before testing. Enter a provider endpoint, then test again.', ttsMissingApiKey: 'API key is required before testing. Paste the key, or save a stored server key first.', ttsCleared: 'Stored TTS key cleared', ttsClearFailed: 'Failed to clear stored TTS key', storedBaseUrlsTitle: 'Stored Base URLs', storedBaseUrlsHint: 'Saved voice provider endpoints. Field-level inputs only clear the current value; delete saved endpoints here.', storedBaseUrlsEmpty: 'No custom voice Base URLs are saved on the server.', storedBaseUrlDelete: 'Delete URL', storedApiKeysTitle: 'Stored API keys', storedApiKeysHint: 'Masked list of server-stored voice API keys. Only provider/source is shown; raw keys are never displayed.', storedApiKeysEmpty: 'No voice API keys are stored on the server.', storedKeyKindTts: 'TTS', storedKeyKindStt: 'STT', storedApiKeyMasked: '[stored]', storedApiKeyDelete: 'Delete key', baseUrlPresetClear: 'Clear URL', baseUrlPresetCleared: 'Base URL cleared from the field.', baseUrlPresetDelete: 'Delete URL', baseUrlPresetDeleted: 'Base URL removed from saved list.', baseUrlPresetDeleteFailed: 'Failed to remove saved Base URL.', ttsPendingSecret: 'New key will be saved', ttsStoredSecret: 'Server has stored key: {value}', ttsClearKey: 'Clear stored key', sttTitle: 'Speech-to-text', sttProvider: 'STT Provider', sttProviderHint: 'Choose how voice input is transcribed', sttProviderBrowser: 'Browser', sttProviderOpenai: 'OpenAI-compatible', sttProviderCustom: 'Custom', sttBrowserHint: 'Use built-in browser speech recognition when available.', sttApiKey: 'API Key', sttApiKeyHint: 'Stored server-side for STT providers that require authentication.', sttStoredSecret: 'Stored server key: {value}', sttPendingSecret: 'Stored server key: will be updated on save', sttClearKey: 'Clear stored key', sttModel: 'Model', sttModelHint: 'Speech-to-text model to use.', sttLanguage: 'Language', sttLanguageHint: 'Optional language hint such as en or zh.', sttPrompt: 'Prompt', sttPromptHint: 'Optional transcription prompt.', sttAudioTranscode: 'Audio transcoding', sttAudioTranscodeNone: 'Send original audio', sttAudioTranscodeFfmpeg: 'Convert to WAV with ffmpeg', sttBaseUrl: 'Base URL', sttBaseUrlHint: 'Custom OpenAI-compatible speech-to-text endpoint.', sttSave: 'Save STT settings', sttSaved: 'STT settings saved on server.', sttSaveFailed: 'Failed to save STT settings.', sttCleared: 'Stored STT secret cleared from server.', sttClearFailed: 'Failed to clear stored STT secret.', sttActiveSaved: 'Active STT provider saved on server.', sttSaveActive: 'Save active STT provider', sttEndpointHint: 'Final transcription endpoint: {endpoint}', sttTestTitle: 'Test speech-to-text', sttTestButton: 'Test STT API', sttTestStopButton: 'Stop and transcribe', sttTestBackendOnly: 'STT API test is available for OpenAI-compatible and Custom providers.', sttTestRecording: 'Recording test sample… click Stop and transcribe when done.', sttTestTranscribing: 'Transcribing test sample…', sttTestEmpty: 'No audio was captured for the STT test.', sttTestSucceeded: 'STT test succeeded.', sttTestFailed: 'STT test failed: {error} — {hint}', sttFailureHintMissingConfig: 'Check that the provider is saved with a base URL/model and an API key before testing.', sttFailureHintAuth: 'Check the API key. If a key was already stored, paste the current key again and save before testing.', sttFailureHintForbidden: 'The key reached the provider but does not have permission for this STT model or account. Check provider access/billing.', sttFailureHintEndpointModel: 'Check the Base URL and model. For Groq, use {endpoint} with whisper-large-v3-turbo or another Groq STT model.', sttFailureHintModel: 'Check that the selected STT model exists and is enabled for this account.', sttFailureHintQuota: 'The provider reports quota, billing, or rate-limit trouble. Check account quota/billing or retry later.', sttFailureHintNetworkCustom: 'The server could not reach the custom STT endpoint. Check that {endpoint} is reachable from the Web UI server and is OpenAI-compatible.', sttFailureHintNetwork: 'The Web UI server could not reach the STT provider. Check network/proxy/firewall and retry.', sttFailureHintAudio: 'The recorded audio was empty or rejected. Try a short clear sample, allow microphone access, or retry in Chrome.', sttFailureHintGenericCustom: 'Check API key, Base URL, model, and the final endpoint {endpoint}.', sttFailureHintGeneric: 'Check API key, model, and provider account access, then retry.', sttTestRecordFailed: 'Could not start microphone recording: {error}', sttTestTranscript: 'Transcript: {text}', 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}', // MiMo TTS providerMimo: 'MiMo TTS', mimoHint: 'Xiaomi MiMo TTS — supports preset voices, voice design, and voice clone modes', mimoApiKey: 'API Key', mimoApiKeyHint: 'Get your key at platform.xiaomimimo.com', mimoApiKeyPlaceholder: 'MiMo API Key', mimoAuthMode: 'Authentication Mode', mimoAuthModeHint: 'Header format expected by the selected MiMo endpoint', mimoAuthModeBearer: 'Bearer token (Authorization)', mimoAuthModeApiKey: 'API key header (api-key)', mimoAuthModeBoth: 'Both headers', mimoBaseUrl: 'Base URL', mimoBaseUrlHint: 'MiMo API endpoint URL', mimoModel: 'Model', mimoModelHint: 'Select speech synthesis model', mimoModelPreset: 'Preset Voices', mimoModelVoiceDesign: 'Voice Design', mimoModelVoiceClone: 'Voice Clone', mimoVoice: 'Voice', mimoVoiceHint: 'Select a preset voice', mimoVoiceDesignPrompt: 'Voice Description', mimoVoiceDesignPromptHint: 'Describe the voice characteristics you want', mimoVoiceDesignPromptPlaceholder: 'e.g., A warm young female voice, slightly slow, with a magnetic tone', mimoCloneAudio: 'Upload Audio', mimoCloneAudioHint: 'Upload an audio sample for voice cloning (mp3/wav, max 10MB)', mimoCloneAudioUpload: 'Choose File', mimoCloneAudioClear: 'Clear', mimoStylePrompt: 'Style Prompt', mimoStylePromptHint: 'Optional — describe the speaking style in natural language', mimoStylePromptPlaceholder: 'e.g., Bright and bouncy tone, fast pace', }, }, githubPreview: { title: "Version Preview", description: "Clone a selected GitHub tag into the Web UI preview workspace, install dependencies, and run it with the development ports.", refresh: "Refresh", selectTag: "Select a tag", prepare: "Prepare Code", install: "Install Dependencies", start: "Start Preview", stop: "Stop", note: "Preview code is stored under the Web UI data home. Production remains on port 8648; preview development runs on frontend 8651 and backend 8650.", path: "Preview Path", webuiHome: "Preview Data Home", currentTag: "Current Tag", repoReady: "Repository Ready", dependencies: "Dependencies Installed", running: "Running", notRunning: "Not running", open: "Open Preview", log: "Action Log Path", logOutput: "Log Output", actionLog: "Action Log", devLog: "Dev Server Log", yes: "Yes", no: "No", actionFailed: "Action failed", nodeEnvironmentMissing: "Node/npm was not detected. Please install Node.js and try again.", prepareSuccess: "Preview code is ready", installSuccess: "Dependencies installed", startSuccess: "Preview completed", stopSuccess: "Preview stopped", }, // 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', proxyUrl: 'Proxy URL', proxyUrlHint: 'Optional platform proxy. Supports http://, https://, and socks5://', accessToken: 'Access Token', accessTokenHint: 'Matrix access token', homeserver: 'Homeserver URL', homeserverHint: 'Matrix homeserver URL', matrixUserId: 'Matrix User ID', matrixUserIdHint: 'Required with password login; optional with access token', matrixPassword: 'Matrix Password', matrixPasswordHint: 'Alternative to an access token', appId: 'App ID', appIdHint: 'Feishu App ID', appSecret: 'App Secret', appSecretHint: 'Feishu App Secret', encryptKey: 'Encrypt Key', encryptKeyHint: 'Feishu Encrypt Key', verificationToken: 'Verification Token', verificationTokenHint: 'Feishu Verification Token', clientId: 'Client ID', clientIdHint: 'DingTalk Client ID', clientSecret: 'Client Secret', clientSecretHint: 'DingTalk Client Secret', cardTemplateId: 'AI Card Template ID', cardTemplateIdHint: 'DingTalk AI Card template ID; leave empty to disable AI Cards', allowedUsers: 'Allowed Users', allowedUsersHint: 'Whitelist user IDs or OpenIDs (comma-separated)', allowAllUsers: 'Allow All Users', allowAllUsersHint: 'Allow messages from any user; keep off to use the allowlist', 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', selectRoomFirst: 'Please select a room first', agentAlreadyInRoom: 'Agent already in this room', agentAddFailedCount: '{count} agent(s) were not added: {details}', noAgents: 'No agents in this room', members: 'members', roomCreated: 'Room created', roomDeleted: 'Room deleted', roomCloned: 'Room cloned', cloneRoom: 'Clone room', copyRoomLink: 'Copy Room Link', deleteRoomConfirm: 'Delete this room?', clearContext: 'Clear context', clearContextConfirm: 'Clear this room context? Messages and compression snapshots will be removed, but agents and members stay.', contextCleared: 'Context cleared', you: 'You', joined: 'Joined room', joinFailed: 'Failed to join room', inputPlaceholder: 'Type a message... (Enter to send)', messageDisplayLimit: 'Group chat displays up to 600 messages', 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', agentBreakdown: 'Agent Breakdown', agents: { hermes: 'Hermes Agent', unknown: 'Unknown' }, dailyTrend: 'Daily Usage', date: 'Date', tokens: 'Tokens', cache: 'Cache', cacheRead: 'Cache Read', cacheWrite: 'Cache Write', sessions: 'Sessions', cost: 'Cost', noData: 'No usage data', }, skillsUsage: { title: 'Skills Usage', subtitle: 'Track skill loads and edits from Hermes sessions', refresh: 'Refresh', periodSelector: 'Skill usage period', periodLabel: '{days}d', summary: 'Summary', totalActions: 'Actions', loads: 'Loads', edits: 'Edits', distinctSkills: 'Skills', topSkills: 'Top Skills', dailyTrend: 'Daily Trend', periodSummary: 'Last {days} days', skill: 'Skill', share: 'Share', lastUsed: 'Last Used', noData: 'No skill usage data', loadFailed: 'Failed to load skill usage', otherSkills: 'Other skills', }, // 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', contentDisplay: 'Content display', download: 'Download', downloadFile: 'Download file', }, // Changelog changelog: { new_0_6_29_9: 'The project is now hermes-ui: the Electron desktop app, marketing site, and ESP32 firmware are removed, leaving only the web client and server', new_0_6_29_10: 'UI branding is unified under hermes-ui, and locales are consolidated to Simplified Chinese and English', }, }