/** * dsh-ssh surface copy: zh is the key source, en mirrors every key. */ export const zh = { 'entry.label': 'SSH', 'entry.tooltip': '远程 SSH 运维面板', 'panel.title': '远程 SSH', 'panel.backToConversation': '返回会话', 'tab.hosts': '主机', 'tab.terminal': '终端', 'tab.transfer': '传输', 'tab.tunnels': '隧道', 'tab.cluster': '集群', // hosts 'hosts.empty': '尚未配置主机。点击「新增主机」添加,或从 ~/.ssh/config 导入。', 'hosts.add': '新增主机', 'hosts.import': '导入 ~/.ssh/config', 'hosts.search': '搜索别名 / 描述 / 标签…', 'hosts.groupBy.label': '分组方式', 'hosts.groupBy.none': '不分组', 'hosts.groupBy.environment': '按环境分组', 'hosts.groupBy.tags': '按标签分组', 'hosts.group.ungrouped': '未设置环境', 'hosts.group.noTags': '无标签', 'hosts.group.count': '{count} 台', 'hosts.group.testAll': '测试全部', 'hosts.col.alias': '别名', 'hosts.col.host': '主机', 'hosts.col.user': '用户', 'hosts.col.auth': '认证', 'hosts.col.environment': '环境', 'hosts.col.tags': '标签', 'hosts.col.description': '备注', 'hosts.col.actions': '操作', 'hosts.test': '测试', 'hosts.edit': '编辑', 'hosts.delete': '删除', 'hosts.deleteConfirm': '确定删除主机「{alias}」?将同时停止其隧道。', 'hosts.imported': '解析 {parsed} 个配置块,新增 {added} 台主机,跳过 {skipped} 个。', 'hosts.proxyBadge': '代理', 'import.reason.wildcard': '通配符 pattern,需手动配置', 'import.reason.existing': '别名已存在', 'import.reason.match': 'Match 条件块,不支持', 'import.reason.invalid': '字段不合法,无法导入', 'import.more': '其余 {count} 个未显示', 'hosts.testing': '连接测试中…', 'hosts.testOk': '连接成功({latency} ms)', 'hosts.testFail': '连接失败:{error}', 'hosts.connected': '连接', 'hosts.notConnected': '未连接', // host form 'form.title.create': '新增主机', 'form.title.edit': '编辑主机 {alias}', 'form.alias': '别名', 'form.aliasHint': '小写字母、数字与单连字符,操作时使用', 'form.host': '主机地址', 'form.port': '端口', 'form.user': '用户名', 'form.auth': '认证方式', 'form.auth.key': '密钥', 'form.auth.password': '密码', 'form.auth.agent': 'SSH Agent', 'form.agentPath': 'Agent 路径', 'form.agentPathHint': '留空自动检测(SSH_AUTH_SOCK / Windows Pageant);也可填 socket 路径或 pageant', 'form.authKeepHint': '编辑时密钥/密码/Agent 留空将保留原认证方式与已存凭据;如需更换认证方式请填写新凭据。', 'form.keyPath': '私钥路径', 'form.keyPathHint': '如 ~/.ssh/id_ed25519', 'form.passphrase': '密钥口令', 'form.password': '密码', 'form.passwordHint': '明文保存在 $DSH_HOME/dsh-ssh.json(本机私有文件)', 'form.passwordRequired': '密码认证需要填写密码。', 'form.proxyJump': '跳板机别名', 'form.proxyJumpHint': '逗号分隔的本地主机别名,或 [user@]host[:port] 地址,按顺序连接', 'form.proxyCommand': 'ProxyCommand', 'form.proxyCommandHint': 'OpenSSH ProxyCommand:%h 主机、%p 端口、%r 用户、%n 别名;留空或填 none 表示不使用。与跳板机别名互斥。', 'form.description': '备注', 'form.environment': '环境', 'form.environmentHint': '如 development / production', 'form.tags': '标签', 'form.tagsHint': '逗号分隔', 'form.location': '位置', 'form.save': '保存', 'form.cancel': '取消', 'form.required': '请填写必填字段:别名、主机地址、用户名。', 'form.portInvalid': '端口必须是 1-65535 之间的整数。', // terminal 'terminal.selectHost': '选择主机', 'terminal.connect': '连接', 'terminal.disconnect': '断开', 'terminal.placeholder': '选择主机后点击「连接」打开远程终端。', 'terminal.connecting': '正在连接…', 'terminal.ready': '终端已连接({alias})', 'terminal.exited': '终端已退出({alias})', 'terminal.error': '终端错误:{error}', 'terminal.auth.title': '二次身份验证 (2FA / 交互式认证)', 'terminal.auth.submit': '提交验证码', 'terminal.auth.cancel': '取消', 'terminal.auth.placeholder': '请输入验证码或口令', 'terminal.auth.hint': '服务器请求进行交互式二次验证(输入仅本次生效,不落盘):', // transfer 'transfer.selectHost': '选择主机', 'transfer.remotePath': '远程路径', 'transfer.remotePathHint': '如 /tmp/ 或 /home/user/app.tar.gz', 'transfer.upload': '上传', 'transfer.uploading': '正在上传 {file}…', 'transfer.download': '下载', 'transfer.downloading': '正在下载…', 'transfer.done': '完成:{bytes} 字节', 'transfer.failed': '传输失败:{error}', 'transfer.browseRemote': '浏览远程目录', 'transfer.refresh': '刷新', 'transfer.name': '名称', 'transfer.upLevel': '上级目录', 'transfer.size': '大小', 'transfer.modified': '修改时间', 'transfer.bytes': '{value} B', 'transfer.kib': '{value} KB', 'transfer.mib': '{value} MB', 'transfer.gib': '{value} GB', 'transfer.percent': '{value}%', 'transfer.speed': '{value}/s', 'transfer.dir': '目录', 'transfer.file': '文件', // tunnels 'tunnel.empty': '没有活跃的隧道。', 'tunnel.start': '新建隧道', 'tunnel.alias': '主机别名', 'tunnel.remotePort': '远程端口', 'tunnel.remoteHost': '远程主机', 'tunnel.remoteHostHint': '默认 127.0.0.1(服务器自身)', 'tunnel.localPort': '本地端口', 'tunnel.localPortHint': '留空自动分配', 'tunnel.stop': '停止', 'tunnel.stopAll': '全部停止', 'tunnel.stopAllConfirm': '确定停止全部隧道吗?', 'tunnel.row': '{alias}: 127.0.0.1:{localPort} -> {remoteHost}:{remotePort}', 'tunnel.started': '隧道已建立:127.0.0.1:{localPort}', 'tunnel.failed': '隧道启动失败:{error}', // cluster 'cluster.command': '命令', 'cluster.run': '执行', 'cluster.confirm': '确定在匹配的主机上执行该命令吗?该操作会真实运行在远程服务器上。', 'cluster.aliases': '主机别名(逗号分隔,与环境、标签至少填一项)', 'cluster.environment': '环境过滤', 'cluster.tags': '标签过滤(逗号分隔)', 'cluster.results': '执行结果', 'cluster.empty': '输入命令后点击「执行」。', 'cluster.noMatch': '没有匹配的主机。', 'cluster.ok': '成功', 'cluster.fail': '失败', 'cluster.timeout': '超时', 'cluster.col.alias': '主机', 'cluster.col.status': '状态', 'cluster.col.exit': '退出码', 'cluster.col.stdout': '标准输出', 'cluster.col.stderr': '标准错误', 'cluster.col.error': '错误', 'cluster.col.duration': '耗时', // common 'common.loading': '加载中…', 'common.error': '出错:{error}', 'common.refresh': '刷新', 'common.confirm': '确定', 'error.disabled': 'SSH 插件在宿主端未启用。请前往「设置 → Web 插件 → SSH」打开 enabled 开关。', } as const export const en: Record = { 'entry.label': 'SSH', 'entry.tooltip': 'Remote SSH operations panel', 'panel.title': 'Remote SSH', 'panel.backToConversation': 'Back to chat', 'tab.hosts': 'Hosts', 'tab.terminal': 'Terminal', 'tab.transfer': 'Transfer', 'tab.tunnels': 'Tunnels', 'tab.cluster': 'Cluster', 'hosts.empty': 'No hosts configured. Click "Add host" or import from ~/.ssh/config.', 'hosts.add': 'Add host', 'hosts.import': 'Import ~/.ssh/config', 'hosts.search': 'Search alias / description / tags…', 'hosts.groupBy.label': 'Group by', 'hosts.groupBy.none': 'No grouping', 'hosts.groupBy.environment': 'Group by environment', 'hosts.groupBy.tags': 'Group by tags', 'hosts.group.ungrouped': 'No environment', 'hosts.group.noTags': 'No tags', 'hosts.group.count': '{count} hosts', 'hosts.group.testAll': 'Test all', 'hosts.col.alias': 'Alias', 'hosts.col.host': 'Host', 'hosts.col.user': 'User', 'hosts.col.auth': 'Auth', 'hosts.col.environment': 'Environment', 'hosts.col.tags': 'Tags', 'hosts.col.description': 'Description', 'hosts.col.actions': 'Actions', 'hosts.test': 'Test', 'hosts.edit': 'Edit', 'hosts.delete': 'Delete', 'hosts.deleteConfirm': 'Delete host "{alias}"? Its tunnels will be stopped.', 'hosts.imported': 'Parsed {parsed} blocks, added {added} hosts, skipped {skipped}.', 'hosts.proxyBadge': 'proxy', 'import.reason.wildcard': 'wildcard pattern — configure it by hand', 'import.reason.existing': 'alias already configured', 'import.reason.match': 'Match block — not supported', 'import.reason.invalid': 'invalid fields — not imported', 'import.more': '{count} more not shown', 'hosts.testing': 'Testing connection…', 'hosts.testOk': 'Connected ({latency} ms)', 'hosts.testFail': 'Connection failed: {error}', 'hosts.connected': 'Connect', 'hosts.notConnected': 'Not connected', 'form.title.create': 'Add host', 'form.title.edit': 'Edit host {alias}', 'form.alias': 'Alias', 'form.aliasHint': 'Lowercase letters, digits and single hyphens; used in every operation', 'form.host': 'Host', 'form.port': 'Port', 'form.user': 'User', 'form.auth': 'Authentication', 'form.auth.key': 'Key', 'form.auth.password': 'Password', 'form.auth.agent': 'SSH Agent', 'form.agentPath': 'Agent path', 'form.agentPathHint': 'Leave empty to auto-detect (SSH_AUTH_SOCK / Pageant on Windows), or set a socket path or "pageant"', 'form.authKeepHint': 'While editing, empty secret fields keep the stored authentication and credentials; enter new values to switch methods.', 'form.keyPath': 'Private key path', 'form.keyPathHint': 'e.g. ~/.ssh/id_ed25519', 'form.passphrase': 'Key passphrase', 'form.password': 'Password', 'form.passwordHint': 'Stored in plaintext in $DSH_HOME/dsh-ssh.json (private local file)', 'form.passwordRequired': 'Password auth requires a password.', 'form.proxyJump': 'Jump host aliases', 'form.proxyJumpHint': 'Comma-separated local aliases or [user@]host[:port] addresses, chained in order', 'form.proxyCommand': 'ProxyCommand', 'form.proxyCommandHint': 'OpenSSH ProxyCommand: %h host, %p port, %r user, %n alias; empty or none disables it. Mutually exclusive with the jump chain.', 'form.description': 'Description', 'form.environment': 'Environment', 'form.environmentHint': 'e.g. development / production', 'form.tags': 'Tags', 'form.tagsHint': 'Comma-separated', 'form.location': 'Location', 'form.save': 'Save', 'form.cancel': 'Cancel', 'form.required': 'Required fields missing: alias, host, user.', 'form.portInvalid': 'Port must be an integer between 1 and 65535.', 'terminal.selectHost': 'Select host', 'terminal.connect': 'Connect', 'terminal.disconnect': 'Disconnect', 'terminal.placeholder': 'Select a host and click "Connect" to open the remote terminal.', 'terminal.connecting': 'Connecting…', 'terminal.ready': 'Terminal connected ({alias})', 'terminal.exited': 'Terminal exited ({alias})', 'terminal.error': 'Terminal error: {error}', 'terminal.auth.title': 'Two-Factor Authentication (2FA)', 'terminal.auth.submit': 'Submit Code', 'terminal.auth.cancel': 'Cancel', 'terminal.auth.placeholder': 'Enter code or passphrase', 'terminal.auth.hint': 'The server requested interactive 2FA (session only, never saved):', 'transfer.selectHost': 'Select host', 'transfer.remotePath': 'Remote path', 'transfer.remotePathHint': 'e.g. /tmp/ or /home/user/app.tar.gz', 'transfer.upload': 'Upload', 'transfer.uploading': 'Uploading {file}…', 'transfer.download': 'Download', 'transfer.downloading': 'Downloading…', 'transfer.done': 'Done: {bytes} bytes', 'transfer.failed': 'Transfer failed: {error}', 'transfer.browseRemote': 'Browse remote', 'transfer.refresh': 'Refresh', 'transfer.name': 'Name', 'transfer.upLevel': 'Parent directory', 'transfer.size': 'Size', 'transfer.modified': 'Modified', 'transfer.bytes': '{value} B', 'transfer.kib': '{value} KB', 'transfer.mib': '{value} MB', 'transfer.gib': '{value} GB', 'transfer.percent': '{value}%', 'transfer.speed': '{value}/s', 'transfer.dir': 'Directory', 'transfer.file': 'File', 'tunnel.empty': 'No active tunnels.', 'tunnel.start': 'New tunnel', 'tunnel.alias': 'Host alias', 'tunnel.remotePort': 'Remote port', 'tunnel.remoteHost': 'Remote host', 'tunnel.remoteHostHint': 'Default 127.0.0.1 (the server itself)', 'tunnel.localPort': 'Local port', 'tunnel.localPortHint': 'Leave empty for auto-assign', 'tunnel.stop': 'Stop', 'tunnel.stopAll': 'Stop all', 'tunnel.stopAllConfirm': 'Stop all tunnels?', 'tunnel.row': '{alias}: 127.0.0.1:{localPort} -> {remoteHost}:{remotePort}', 'tunnel.started': 'Tunnel up: 127.0.0.1:{localPort}', 'tunnel.failed': 'Tunnel failed: {error}', 'cluster.command': 'Command', 'cluster.run': 'Run', 'cluster.confirm': 'Run this command on the matching hosts? It executes for real on the remote servers.', 'cluster.aliases': 'Host aliases (comma-separated; require at least one filter)', 'cluster.environment': 'Environment filter', 'cluster.tags': 'Tag filter (comma-separated)', 'cluster.results': 'Results', 'cluster.empty': 'Type a command and click "Run".', 'cluster.noMatch': 'No hosts matched.', 'cluster.ok': 'OK', 'cluster.fail': 'Failed', 'cluster.timeout': 'Timed out', 'cluster.col.alias': 'Host', 'cluster.col.status': 'Status', 'cluster.col.exit': 'Exit', 'cluster.col.stdout': 'Stdout', 'cluster.col.stderr': 'Stderr', 'cluster.col.error': 'Error', 'cluster.col.duration': 'Duration', 'common.loading': 'Loading…', 'common.error': 'Error: {error}', 'common.refresh': 'Refresh', 'common.confirm': 'Confirm', 'error.disabled': 'The SSH plugin is disabled on the host. Please enable it in Settings → Web Plugins → SSH.', } /** Locale key union. */ export type SshKey = keyof typeof zh /** Tiny interpolation: {name} -> value. */ export function t(dictionary: Record, key: string, values?: Record): string { let text = dictionary[key] ?? key if (values !== undefined) { for (const [name, value] of Object.entries(values)) { text = text.replaceAll(`{${name}}`, String(value)) } } return text }