fix(i18n): complete Simplified Chinese core and plugin translations (#1542)

* fix(i18n): complete Chinese onboarding and navigation labels

* fix(i18n): translate remaining Chinese core and plugin interfaces

* fix(i18n): translate host editor tab overflow controls

* fix(i18n): use consistent Chinese fleet terminology

* fix(i18n): localize hardcoded controls and plugin views

* fix(i18n): translate built-in homepage widget catalog

* test(homepage): follow translated timezone placeholder

* fix(i18n): translate plugin-provided homepage widgets

* fix(i18n): localize feature settings section titles
This commit is contained in:
ZacharyZcR authored and GitHub committed 2026-10-03 21:37:00 +08:00
1 parent 13f5736d8f
commit 0214a81e5d
78 files changed
+2423 -1833

No files matched your search

+3
View File
@@ -54,5 +54,8 @@
"request": "Request certificate now", "request": "Request certificate now",
"requesting": "Requesting certificate...", "requesting": "Requesting certificate...",
"issued": "Certificate issued and installed" "issued": "Certificate issued and installed"
},
"plugin": {
"name": "ACME Certificates"
} }
} }
+25 -22
View File
@@ -1,14 +1,14 @@
{ {
"permissions": { "permissions": {
"manage": { "manage": {
"title": "Manage ACME certificates", "title": "管理 ACME 证书",
"description": "See the certificate status and request a new certificate now." "description": "查看证书状态并立即申请新证书。"
} }
}, },
"settings": { "settings": {
"autoRenew": { "autoRenew": {
"label": "Renew automatically", "label": "自动续期",
"description": "Check twice a day and get a new certificate when there is none, it does not cover the domain, or it expires within 30 days." "description": "每天检查两次。在没有证书、证书未覆盖域名或将在 30 天内过期时申请新证书。"
}, },
"domain": { "domain": {
"label": "域名", "label": "域名",
@@ -17,42 +17,45 @@
}, },
"email": { "email": {
"label": "邮箱", "label": "邮箱",
"description": "Contact email for the ACME account and expiry notices.", "description": "用于 ACME 账户和到期通知的联系邮箱。",
"placeholder": "admin@example.com" "placeholder": "admin@example.com"
}, },
"provider": { "provider": {
"label": "证书颁发机构", "label": "证书颁发机构",
"description": "Where to get the certificate. Use staging to test without hitting Let's Encrypt rate limits.", "description": "证书申请来源。测试时使用测试环境,以免触发 Let's Encrypt 频率限制。",
"letsencrypt": "Let's Encrypt", "letsencrypt": "Let's Encrypt",
"letsencryptStaging": "Let's Encrypt (staging)", "letsencryptStaging": "Let's Encrypt(测试环境)",
"custom": "Custom ACME directory" "custom": "自定义 ACME 目录"
}, },
"directoryUrl": { "directoryUrl": {
"label": "ACME directory URL", "label": "ACME 目录 URL",
"description": "Only for a custom certificate authority. Must be an https URL." "description": "仅用于自定义证书颁发机构,必须为 HTTPS URL。"
}, },
"challengeType": { "challengeType": {
"label": "Challenge type", "label": "验证类型",
"description": "How to prove you own the domain.", "description": "用于证明域名所有权的方式。",
"http": "HTTP - needs port 80 reachable from the internet", "http": "HTTP:需要互联网能够访问端口 80",
"cloudflare": "DNS (Cloudflare) - needs a Cloudflare API token" "cloudflare": "DNS(Cloudflare):需要 Cloudflare API 令牌"
}, },
"cloudflareToken": { "cloudflareToken": {
"label": "Cloudflare API token", "label": "Cloudflare API 令牌",
"description": "具有您域名的 Zone:DNS:Edit 权限的限定作用域令牌。" "description": "具有您域名的 Zone:DNS:Edit 权限的限定作用域令牌。"
} }
}, },
"status": { "status": {
"title": "Certificate status", "title": "证书状态",
"none": "无证书", "none": "无证书",
"names": "Covers {{names}}", "names": "覆盖域名:{{names}}",
"issuer": "Issued by {{issuer}}", "issuer": "签发者:{{issuer}}",
"expires": "过期于 {{date}}", "expires": "过期于 {{date}}",
"lastIssued": "上次签发于 {{date}}", "lastIssued": "上次签发于 {{date}}",
"lastError": "Last attempt failed: {{error}}", "lastError": "上次尝试失败:{{error}}",
"missing": "Set the domain, email and the fields your challenge type needs, then save, before requesting a certificate.", "missing": "请先设置域名、邮箱及所选验证方式需要的字段,保存后再申请证书。",
"request": "Request certificate now", "request": "立即申请证书",
"requesting": "正在请求证书...", "requesting": "正在请求证书...",
"issued": "Certificate issued and installed" "issued": "证书已签发并安装"
},
"plugin": {
"name": "ACME 证书"
} }
} }
+3
View File
@@ -115,5 +115,8 @@
}, },
"hosts": { "hosts": {
"docsLink": "View docs" "docsLink": "View docs"
},
"plugin": {
"name": "AI Assistant"
} }
} }
+54 -51
View File
@@ -1,29 +1,29 @@
{ {
"permissions": { "permissions": {
"use": { "use": {
"title": "Use the assistant", "title": "使用助手",
"description": "Open the assistant and ask it things." "description": "打开助手并向其提问。"
}, },
"manage_providers": { "manage_providers": {
"title": "Manage providers", "title": "管理服务提供商",
"description": "Add, edit and remove the AI providers and their API keys." "description": "添加、编辑和移除 AI 服务提供商及其 API 密钥。"
}, },
"apply_proposals": { "apply_proposals": {
"title": "Apply proposals", "title": "应用建议",
"description": "Let the assistant carry out the changes it suggests." "description": "允许助手执行其建议的更改。"
}, },
"services.use": { "services.use": {
"title": "Use AI from other features", "title": "在其他功能中使用 AI",
"description": "Other features may call the assistant on your behalf." "description": "其他功能可以代表你调用助手。"
}, },
"secrets.share": { "secrets.share": {
"title": "Share the API key", "title": "共享 API 密钥",
"description": "Other features may use the stored AI API key without seeing it." "description": "其他功能可以使用已保存的 AI API 密钥,但无法查看其内容。"
} }
}, },
"settings": { "settings": {
"assistantUnavailable": "The AI assistant is turned off on this server.", "assistantUnavailable": "此服务器已关闭 AI 助手。",
"providersNeedOptIn": "Turn on the AI assistant above to add providers.", "providersNeedOptIn": "请先在上方启用 AI 助手,再添加服务提供商。",
"admin": { "admin": {
"globallyEnabled": { "globallyEnabled": {
"label": "AI 助手", "label": "AI 助手",
@@ -31,74 +31,74 @@
}, },
"privateEndpoints": { "privateEndpoints": {
"label": "允许的私有 AI 主机", "label": "允许的私有 AI 主机",
"description": "Hosts on your private network that users may point a provider at, such as a self-hosted Ollama. Put one host on each line." "description": "允许用户将服务提供商指向的内网主机,例如自托管的 Ollama。每行填写一台主机。"
} }
}, },
"host": { "host": {
"enableAiAssistant": { "enableAiAssistant": {
"label": "Enable AI Assistant", "label": "启用 AI 助手",
"description": "Allow the AI assistant to help in this host's terminal. Off by default, and still needs AI enabled for your account." "description": "允许 AI 助手在此主机的终端中提供帮助。默认关闭,且你的账户也必须启用 AI。"
} }
} }
}, },
"ai": { "ai": {
"title": "Assistant", "title": "助手",
"send": "Send", "send": "发送",
"stop": "停止", "stop": "停止",
"save": "保存", "save": "保存",
"cancel": "取消", "cancel": "取消",
"running": "运行中", "running": "运行中",
"approve": "Approve", "approve": "批准",
"reject": "Reject", "reject": "拒绝",
"inputPlaceholder": "Ask about your servers, or describe a change to propose", "inputPlaceholder": "询问服务器相关问题,或描述你想进行的更改",
"selectProvider": "Select a provider", "selectProvider": "选择服务提供商",
"providerSettings": "Provider settings", "providerSettings": "服务提供商设置",
"addProvider": "Add provider", "addProvider": "添加服务提供商",
"editProvider": "编辑提供商", "editProvider": "编辑提供商",
"removeProvider": "Remove provider", "removeProvider": "移除服务提供商",
"providerType": "提供商", "providerType": "提供商",
"providerLabel": "名称", "providerLabel": "名称",
"providerLabelPlaceholder": "例如:本地 Ollama", "providerLabelPlaceholder": "例如:本地 Ollama",
"baseUrl": "Server address", "baseUrl": "服务器地址",
"apiKey": "API 密钥", "apiKey": "API 密钥",
"defaultModel": "Default model", "defaultModel": "默认模型",
"defaultModelPlaceholder": "For example: llama3.1 or claude-opus-5", "defaultModelPlaceholder": "例如 llama3.1 或 claude-opus-5",
"labelRequired": "给此提供商起个名字", "labelRequired": "给此提供商起个名字",
"providerSaveFailed": "Could not save the provider", "providerSaveFailed": "无法保存服务提供商",
"providerUpdated": "Provider updated", "providerUpdated": "服务提供商已更新",
"providerDeleteFailed": "Could not remove the provider", "providerDeleteFailed": "无法移除服务提供商",
"privateEndpointHint": "Addresses on your private network need an administrator to allow the host first.", "privateEndpointHint": "使用内网地址前,需要管理员先允许该主机。",
"providerOllama": "Ollama", "providerOllama": "Ollama",
"providerAnthropic": "Anthropic", "providerAnthropic": "Anthropic",
"providerOpenai": "OpenAI", "providerOpenai": "OpenAI",
"providerGemini": "Google Gemini", "providerGemini": "Google Gemini",
"providerOpenaiCompatible": "OpenAI compatible", "providerOpenaiCompatible": "兼容 OpenAI",
"toolArguments": "输入", "toolArguments": "输入",
"toolResult": "Result", "toolResult": "结果",
"statusApplied": "已应用", "statusApplied": "已应用",
"statusRejected": "Rejected", "statusRejected": "已拒绝",
"proposalApplied": "Change applied", "proposalApplied": "更改已应用",
"proposalApplyFailed": "Could not apply the change", "proposalApplyFailed": "无法应用更改",
"proposalRejectFailed": "Could not reject the change", "proposalRejectFailed": "无法拒绝更改",
"enableTitle": "启用 AI 助手", "enableTitle": "启用 AI 助手",
"enableDescription": "Let the assistant read your setup and suggest changes for you to approve.", "enableDescription": "允许助手读取你的配置并提出更改建议,由你批准后执行。",
"readOnlyCommands": "Allow read-only diagnostic commands", "readOnlyCommands": "允许只读诊断命令",
"readOnlyCommandsDescription": "The assistant may run a short list of safe commands like df and uptime without asking each time. Anything that changes a server is always proposed first.", "readOnlyCommandsDescription": "助手可以执行 df、uptime 等少量安全命令,无需每次询问。任何会修改服务器的操作都会先提交建议。",
"modelDetectFailed": "无法联系到提供商,所以这里提供的是通用列表。您可以输入任何模型名称。", "modelDetectFailed": "无法联系到提供商,所以这里提供的是通用列表。您可以输入任何模型名称。",
"modelCustom": "请输入模型名称", "modelCustom": "请输入模型名称",
"modelRefresh": "Refresh list", "modelRefresh": "刷新列表",
"modelPlaceholder": "选择或输入模型", "modelPlaceholder": "选择或输入模型",
"attachHint": "Type @ to reference a host, snippet or automation. Changes are proposed for you to approve.", "attachHint": "输入 @ 引用主机、代码片段或自动化。更改会先提交给你批准。",
"runCommandTitle": "Run command", "runCommandTitle": "执行命令",
"assistant": "AI Assistant", "assistant": "AI 助手",
"terminalPanelEmpty": "Ask the assistant about this host. Anything that changes state needs your approval first.", "terminalPanelEmpty": "向助手询问此主机的相关问题。任何会改变状态的操作都需要先获得你的批准。",
"terminalPanelPlaceholder": "Ask about this host...", "terminalPanelPlaceholder": "询问此主机的相关问题……",
"noProvidersConfigured": "No AI provider is configured yet. Add one in the AI assistant tab.", "noProvidersConfigured": "尚未配置 AI 服务提供商,请在 AI 助手标签页中添加。",
"runsInOpenTerminal": "Runs in your open terminal session.", "runsInOpenTerminal": "在你已打开的终端会话中执行。",
"runInTerminal": "运行", "runInTerminal": "运行",
"terminalContextPrefill": "Here is my terminal output:\n\n{{output}}\n\n", "terminalContextPrefill": "以下是我的终端输出:\n\n{{output}}\n\n",
"secrets": { "secrets": {
"shareApiKey": "Let other features use your AI provider API key for their own requests. The key is never copied, and you can revoke this at any time." "shareApiKey": "允许其他功能使用你的 AI 服务提供商 API 密钥发起请求。密钥不会被复制,你可以随时撤销授权。"
} }
}, },
"nav": { "nav": {
@@ -106,7 +106,7 @@
}, },
"onboarding": { "onboarding": {
"aiTitle": "AI 助手", "aiTitle": "AI 助手",
"aiIntro": "Termix can include an optional AI assistant. You can change this later in your settings.", "aiIntro": "Termix 提供可选的 AI 助手,你可以稍后在设置中更改。",
"aiSkipTitle": "禁用", "aiSkipTitle": "禁用",
"aiSkipDesc": "Termix 正常工作,AI 助手将保持隐藏状态。", "aiSkipDesc": "Termix 正常工作,AI 助手将保持隐藏状态。",
"aiEnableTitle": "启用", "aiEnableTitle": "启用",
@@ -115,5 +115,8 @@
}, },
"hosts": { "hosts": {
"docsLink": "查看文档" "docsLink": "查看文档"
},
"plugin": {
"name": "AI 助手"
} }
} }
+3
View File
@@ -168,5 +168,8 @@
"showRead": "Show read alerts", "showRead": "Show read alerts",
"allClear": "All clear", "allClear": "All clear",
"loading": "Loading..." "loading": "Loading..."
},
"plugin": {
"name": "Alerts"
} }
} }
+86 -83
View File
@@ -4,13 +4,13 @@
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Use alerts", "title": "使用告警",
"description": "Get alerts in the inbox and send them to your own channels." "description": "在收件箱中接收告警,并发送到你自己的渠道。"
} }
}, },
"onboarding": { "onboarding": {
"feature": "告警", "feature": "告警",
"featureDesc": "Every alert in one inbox, sent on to Discord, ntfy, email or a webhook if you want." "featureDesc": "在统一收件箱中查看所有告警,也可将它们发送到 Discord、ntfy、电子邮箱或 Webhook。"
}, },
"settings": { "settings": {
"group": { "group": {
@@ -19,97 +19,97 @@
}, },
"admin": { "admin": {
"announcements": { "announcements": {
"label": "Termix announcements", "label": "Termix 公告",
"description": "Show news from the Termix team, like security notices and releases, in everyone's inbox." "description": "在所有用户的收件箱中显示 Termix 团队发布的安全通知、版本更新等消息。"
}, },
"retentionDays": { "retentionDays": {
"label": "Keep alerts for (days)", "label": "告警保留天数",
"description": "Older alerts are removed from every inbox." "description": "超过保留时间的告警将从所有收件箱中移除。"
}, },
"smtpHost": { "smtpHost": {
"label": "SMTP server", "label": "SMTP 服务器",
"description": "Needed for email channels. Leave empty to turn email off.", "description": "邮件渠道需要此项,留空则关闭邮件发送。",
"placeholder": "smtp.example.com" "placeholder": "smtp.example.com"
}, },
"smtpPort": { "smtpPort": {
"label": "SMTP port", "label": "SMTP 端口",
"description": "Usually 587 with STARTTLS or 465 with TLS." "description": "通常使用 587(STARTTLS)或 465(TLS)。"
}, },
"smtpSecure": { "smtpSecure": {
"label": "Use TLS from the start", "label": "连接时直接使用 TLS",
"description": "Turn on for port 465. Leave off for 587, which upgrades to TLS on its own." "description": "使用端口 465 时开启。使用端口 587 时关闭,该端口会自动升级为 TLS。"
}, },
"smtpUser": { "smtpUser": {
"label": "SMTP username", "label": "SMTP 用户名",
"description": "Leave empty if the server does not need a login." "description": "服务器无需登录时留空。"
}, },
"smtpPassword": { "smtpPassword": {
"label": "SMTP password", "label": "SMTP 密码",
"description": "Stored encrypted." "description": "加密保存。"
}, },
"smtpFrom": { "smtpFrom": {
"label": "From address", "label": "发件人地址",
"description": "The sender alerts come from.", "description": "告警邮件的发件人。",
"placeholder": "Termix <alerts@example.com>" "placeholder": "Termix <alerts@example.com>"
} }
}, },
"user": { "user": {
"popups": { "popups": {
"label": "Alert popups", "label": "告警弹窗",
"description": "Which new alerts pop up while you use Termix. They always land in the inbox.", "description": "选择使用 Termix 时哪些新告警会弹出提示。所有告警都会保留在收件箱中。",
"all": "All alerts", "all": "所有告警",
"warning": "Warnings and critical", "warning": "警告和严重告警",
"critical": "Critical only", "critical": "仅严重告警",
"off": "全不选" "off": "全不选"
} }
} }
}, },
"sections": { "sections": {
"inbox": "Inbox", "inbox": "收件箱",
"channels": "通道", "channels": "通道",
"rules": "Delivery rules" "rules": "投递规则"
}, },
"severity": { "severity": {
"info": "信息", "info": "信息",
"success": "成功", "success": "成功",
"warning": "警告", "warning": "警告",
"critical": "Critical" "critical": "严重"
}, },
"inbox": { "inbox": {
"sourceTermix": "Termix", "sourceTermix": "Termix",
"openLink": "打开", "openLink": "打开",
"view": "查看", "view": "查看",
"unread": "Unread", "unread": "未读",
"deliveryFailed_one": "{{count}} channel failed", "deliveryFailed_one": "{{count}} 个渠道发送失败",
"deliveryFailed_other": "{{count}} channels failed", "deliveryFailed_other": "{{count}} 个渠道发送失败",
"markRead": "Mark as read", "markRead": "标记为已读",
"markUnread": "Mark as unread", "markUnread": "标记为未读",
"markAllRead": "Mark all as read", "markAllRead": "全部标记为已读",
"delete": "删除", "delete": "删除",
"clearRead": "Delete read alerts", "clearRead": "删除已读告警",
"cleared_one": "Deleted {{count}} alert", "cleared_one": "已删除 {{count}} 条告警",
"cleared_other": "Deleted {{count}} alerts", "cleared_other": "已删除 {{count}} 条告警",
"filterAll": "全部", "filterAll": "全部",
"filterUnread": "Unread", "filterUnread": "未读",
"anySeverity": "Any severity", "anySeverity": "任意严重程度",
"anySource": "Any source", "anySource": "任意来源",
"openAll": "Open in a tab", "openAll": "在标签页中打开",
"empty": "No alerts yet.", "empty": "暂无告警。",
"emptyUnread": "You are all caught up.", "emptyUnread": "没有未读消息。",
"loadMore": "Load more" "loadMore": "加载更多"
}, },
"channels": { "channels": {
"intro": "Channels are where alerts can go outside Termix. Delivery rules pick which alerts use them.", "intro": "渠道用于将告警发送到 Termix 之外,投递规则决定哪些告警使用这些渠道。",
"add": "Add channel", "add": "添加渠道",
"edit": "Edit channel", "edit": "编辑渠道",
"test": "Send a test", "test": "发送测试消息",
"testSent": "Test alert sent", "testSent": "测试告警已发送",
"testFailed": "Test failed: {{error}}", "testFailed": "测试失败:{{error}}",
"deleteConfirm": "Delete the channel \"{{name}}\"?", "deleteConfirm": "删除渠道“{{name}}”?",
"deleted": "Channel deleted", "deleted": "渠道已删除",
"empty": "No channels yet.", "empty": "暂无渠道。",
"enabled": "已启用", "enabled": "已启用",
"needsSave": "This channel needs to be opened and saved again before it can send.", "needsSave": "此渠道需要重新打开并保存后才能发送。",
"name": "名称", "name": "名称",
"nameRequired": "名称为必填项", "nameRequired": "名称为必填项",
"type": "类型", "type": "类型",
@@ -119,41 +119,41 @@
"discord": "Discord", "discord": "Discord",
"email": "邮箱" "email": "邮箱"
}, },
"emailUnavailable": "Email needs an SMTP server, which an admin sets up in the Alerts settings.", "emailUnavailable": "邮件需要 SMTP 服务器,由管理员在告警设置中配置。",
"webhookUrl": "URL", "webhookUrl": "URL",
"webhookHint": "Gets a JSON body for every alert.", "webhookHint": "每条告警都会发送包含 JSON 数据的请求体。",
"method": "Method", "method": "请求方法",
"headers": "Headers", "headers": "请求头",
"headersHint": "One per line, like Name: value.", "headersHint": "每行一项,例如 Name: value。",
"ntfyServer": "服务器 URL", "ntfyServer": "服务器 URL",
"ntfyTopic": "主题", "ntfyTopic": "主题",
"ntfyToken": "Access token (optional)", "ntfyToken": "访问令牌(可选)",
"discordUrl": "Discord webhook URL", "discordUrl": "Discord Webhook 地址",
"discordHint": "From the channel's settings in Discord, under Integrations.", "discordHint": "可在 Discord 频道设置的“集成”中获取。",
"discordUsername": "Display name (optional)", "discordUsername": "显示名称(可选)",
"discordAvatar": "Avatar URL (optional)", "discordAvatar": "头像 URL(可选)",
"emailTo": "Send to", "emailTo": "发送至",
"emailHint": "Separate more than one address with commas.", "emailHint": "多个地址之间用逗号分隔。",
"privateNetwork": "Allow a private network address", "privateNetwork": "允许内网地址",
"privateNetworkHint": "Only works for hosts an admin allows in the private endpoint list." "privateNetworkHint": "仅适用于管理员在私有端点列表中允许的主机。"
}, },
"rules": { "rules": {
"intro": "A delivery rule sends matching alerts to your channels. Alerts always stay in the inbox too.", "intro": "投递规则将匹配的告警发送到你的渠道,告警也会始终保留在收件箱中。",
"add": "Add rule", "add": "添加规则",
"edit": "Edit rule", "edit": "编辑规则",
"empty": "No rules yet, so alerts only show up in the inbox.", "empty": "暂无规则,告警仅显示在收件箱中。",
"needChannel": "Add a channel first.", "needChannel": "请先添加渠道。",
"enabled": "已启用", "enabled": "已启用",
"name": "名称", "name": "名称",
"nameRequired": "名称为必填项", "nameRequired": "名称为必填项",
"match": "Alerts to send", "match": "要发送的告警",
"matchHint": "* for everything, automations.* for everything from automations, or one exact kind.", "matchHint": "使用 * 匹配全部,automations.* 匹配所有自动化告警,或填写一个精确的类型。",
"minSeverity": "At least", "minSeverity": "最低严重程度",
"channels": "Send to", "channels": "发送至",
"channelRequired": "Pick at least one channel", "channelRequired": "请至少选择一个渠道",
"deleteConfirm": "Delete the rule \"{{name}}\"?", "deleteConfirm": "删除规则“{{name}}”?",
"everything": "Everything", "everything": "全部",
"summary": "{{match}}, {{severity}} or higher, to {{channels}}" "summary": "{{match}},{{severity}} 及以上,发送至 {{channels}}"
}, },
"actions": { "actions": {
"cancel": "取消", "cancel": "取消",
@@ -163,10 +163,13 @@
}, },
"widget": { "widget": {
"name": "告警信息流", "name": "告警信息流",
"description": "Your latest alerts, with a quick way to mark them read", "description": "显示最新告警,并可快速标记为已读",
"maxItems": "Max items", "maxItems": "最大条目数",
"showRead": "Show read alerts", "showRead": "显示已读告警",
"allClear": "一切正常", "allClear": "一切正常",
"loading": "加载中..." "loading": "加载中..."
},
"plugin": {
"name": "告警"
} }
} }
+14
View File
@@ -207,5 +207,19 @@
"remove": "Remove schedule", "remove": "Remove schedule",
"loadFailed": "Could not refresh maintenance status. Changes are disabled until it can be loaded.", "loadFailed": "Could not refresh maintenance status. Changes are disabled until it can be loaded.",
"loading": "Loading maintenance status…" "loading": "Loading maintenance status…"
},
"ui": {
"sidebar": {
"automations": {
"options": {
"connect": "connect",
"disconnect": "disconnect",
"offline": "offline",
"online": "online",
"failing": "failing",
"recovered": "recovered"
}
}
}
} }
} }
+129 -115
View File
@@ -1,110 +1,110 @@
{ {
"permissions": { "permissions": {
"view": { "view": {
"title": "View automations", "title": "查看自动化",
"description": "See the automations and their history." "description": "查看自动化及其执行历史。"
}, },
"create": { "create": {
"title": "Create automations", "title": "创建自动化",
"description": "Add new automations." "description": "添加新的自动化。"
}, },
"edit": { "edit": {
"title": "Edit automations", "title": "编辑自动化",
"description": "Change existing automations." "description": "修改现有自动化。"
}, },
"delete": { "delete": {
"title": "Delete automations", "title": "删除自动化",
"description": "Remove automations." "description": "移除自动化。"
}, },
"run": { "run": {
"title": "Run automations", "title": "运行自动化",
"description": "Trigger an automation by hand." "description": "手动触发自动化。"
} }
}, },
"newUi": { "newUi": {
"sidebar": { "sidebar": {
"automations": { "automations": {
"title": "Automations", "title": "自动化",
"tabAutomations": "Automations", "tabAutomations": "自动化",
"tabRuns": "运行记录", "tabRuns": "运行记录",
"tabChannels": "通道", "tabChannels": "通道",
"createAutomation": "New Automation", "createAutomation": "新建自动化",
"backToAutomations": "Back to Automations", "backToAutomations": "返回自动化",
"createTitle": "New Automation", "createTitle": "新建自动化",
"editTitle": "Edit Automation", "editTitle": "编辑自动化",
"empty": "No automations yet. Create one to react to a metric, a schedule, or an event.", "empty": "暂无自动化。创建自动化以响应指标、计划或事件。",
"emptyRuns": "暂无运行记录。", "emptyRuns": "暂无运行记录。",
"channelsHint": "Channels are set up in Alerts", "channelsHint": "渠道在“告警”中设置",
"noChannelsHint": "The alert always goes to your inbox. Add channels in Alerts to also send it somewhere else.", "noChannelsHint": "告警始终会发送到收件箱。在“告警”中添加渠道后,也可发送到其他位置。",
"nameLabel": "名称", "nameLabel": "名称",
"namePlaceholder": "e.g., Clean up /data when it fills", "namePlaceholder": "例如:/data 空间不足时自动清理",
"nameRequired": "名称为必填项", "nameRequired": "名称为必填项",
"descriptionLabel": "描述", "descriptionLabel": "描述",
"descriptionPlaceholder": "可选描述", "descriptionPlaceholder": "可选描述",
"enabledLabel": "已启用", "enabledLabel": "已启用",
"concurrencyLabel": "If it is already running", "concurrencyLabel": "已有任务运行时",
"concurrencyHint": "What to do when this is triggered again before the last run has finished.", "concurrencyHint": "上次运行尚未结束时再次触发的处理方式。",
"concurrency": { "concurrency": {
"skip": "Skip the new run", "skip": "跳过本次运行",
"queue": "稍等会再运行", "queue": "稍等会再运行",
"allow": "Run both at once" "allow": "同时运行两个任务"
}, },
"save": "保存", "save": "保存",
"created": "自动化已创建", "created": "自动化已创建",
"updated": "自动化已保存", "updated": "自动化已保存",
"deleted": "自动化已删除", "deleted": "自动化已删除",
"deleteConfirm": "Delete this automation? Its run history is deleted too.", "deleteConfirm": "删除此自动化?其运行历史也会一并删除。",
"runNow": "Run now", "runNow": "立即运行",
"testRun": "Test run", "testRun": "测试运行",
"testRunHint": "Runs every step but does not touch anything outside Termix.", "testRunHint": "执行每个步骤,但不会修改 Termix 之外的任何内容。",
"runStarted": "Run started", "runStarted": "已开始运行",
"runFinished": "Run finished: {{status}}", "runFinished": "运行结束:{{status}}",
"trigger": "触发器", "trigger": "触发器",
"steps": "Steps", "steps": "步骤",
"addStep": "Add step", "addStep": "添加步骤",
"removeStep": "Remove step", "removeStep": "移除步骤",
"moveUp": "Move up", "moveUp": "上移",
"moveDown": "Move down", "moveDown": "下移",
"noSteps": "No steps yet. Add one to say what should happen.", "noSteps": "暂无步骤。添加步骤以定义要执行的操作。",
"webhookTokenTitle": "Webhook URL", "webhookTokenTitle": "Webhook 地址",
"webhookTokenDescription": "This is shown once. Save it now, it cannot be retrieved later.", "webhookTokenDescription": "此内容仅显示一次,请立即保存,之后无法再次获取。",
"copy": "复制", "copy": "复制",
"copied": "已复制", "copied": "已复制",
"viewJson": "JSON", "viewJson": "JSON",
"viewBuilder": "构建器", "viewBuilder": "构建器",
"viewGraph": "Graph", "viewGraph": "流程图",
"lastRun": "上次运行", "lastRun": "上次运行",
"never": "从未", "never": "从未",
"duration": "{{seconds}}s", "duration": "{{seconds}} 秒",
"stepOutput": "输出", "stepOutput": "输出",
"stepError": "错误", "stepError": "错误",
"dryRunBadge": "Test run", "dryRunBadge": "测试运行",
"hostSelector": { "hostSelector": {
"trigger": "Triggering host", "trigger": "触发事件的主机",
"host": "Specific host", "host": "指定主机",
"fleet": "Fleet", "fleet": "主机组",
"all": "所有主机" "all": "所有主机"
}, },
"triggerKinds": { "triggerKinds": {
"metric_threshold": "Metric threshold", "metric_threshold": "指标阈值",
"host_status": "主机上线或离线", "host_status": "主机上线或离线",
"health_check": "Health check changes", "health_check": "健康检查状态变化",
"schedule": "On a schedule", "schedule": "按计划运行",
"docker_event": "Container event", "docker_event": "容器事件",
"internal_event": "Termix 事件", "internal_event": "Termix 事件",
"webhook": "Incoming webhook" "webhook": "接收 Webhook"
}, },
"stepTypes": { "stepTypes": {
"notify": "Send an alert", "notify": "发送告警",
"http": "Call a URL", "http": "请求 URL",
"run_snippet": "Run a snippet", "run_snippet": "执行代码片段",
"run_command": "Run a command", "run_command": "执行命令",
"docker": "Control a container", "docker": "控制容器",
"tunnel": "Control a tunnel", "tunnel": "控制隧道",
"wol": "Wake a host", "wol": "唤醒主机",
"wait": "Wait", "wait": "等待",
"set_var": "Set a variable", "set_var": "设置变量",
"if": "If / otherwise", "if": "如果 / 否则",
"run_automation": "运行另一个自动化程序", "run_automation": "运行另一个自动化程序",
"stop": "停止" "stop": "停止"
}, },
@@ -112,52 +112,52 @@
"running": "运行中", "running": "运行中",
"success": "成功", "success": "成功",
"failed": "失败", "failed": "失败",
"timeout": "Timed out", "timeout": "已超时",
"skipped": "Skipped", "skipped": "已跳过",
"cancelled": "已取消" "cancelled": "已取消"
}, },
"fields": { "fields": {
"host": "主机", "host": "主机",
"metric": "指标", "metric": "指标",
"mount": "挂载点", "mount": "挂载点",
"operator": "Is", "operator": "等于",
"value": "值", "value": "值",
"forSeconds": "Sustained for (seconds)", "forSeconds": "持续时间(秒)",
"cooldown": "冷却时间(分钟)", "cooldown": "冷却时间(分钟)",
"cron": "Cron 表达式", "cron": "Cron 表达式",
"intervalSeconds": "Every (seconds)", "intervalSeconds": "间隔(秒)",
"timezone": "Time zone", "timezone": "时区",
"timezoneServer": "Server time", "timezoneServer": "服务器时间",
"timezoneHint": "Cron times are read in this zone. Does not apply to interval schedules.", "timezoneHint": "Cron 时间按此时区解释,不适用于按间隔运行的计划。",
"container": "Container", "container": "容器",
"command": "命令", "command": "命令",
"url": "URL", "url": "URL",
"method": "Method", "method": "请求方法",
"body": "请求体", "body": "请求体",
"channels": "Also send to", "channels": "同时发送至",
"title": "标题", "title": "标题",
"message": "消息", "message": "消息",
"snippet": "Snippet", "snippet": "代码片段",
"seconds": "Seconds", "seconds": "秒",
"conditionLeftPlaceholder": "Value to check", "conditionLeftPlaceholder": "待检查的值",
"conditionRightPlaceholder": "Compare against", "conditionRightPlaceholder": "比较目标值",
"variableName": "变量名", "variableName": "变量名",
"variableValue": "值", "variableValue": "值",
"elevated": "Run with sudo", "elevated": "使用 sudo 执行",
"onError": "If this fails", "onError": "失败时",
"allowPrivateNetwork": "Allow local network addresses", "allowPrivateNetwork": "允许局域网地址",
"allowPrivateNetworkHint": "Needed for a self-hosted service on your LAN. Off by default to prevent requests to internal addresses." "allowPrivateNetworkHint": "访问局域网内的自托管服务时需要开启。默认关闭,以防止请求内部地址。"
}, },
"onError": { "onError": {
"stop": "停止运行", "stop": "停止运行",
"continue": "Carry on" "continue": "继续执行"
}, },
"needsPlugin": "Needs {{plugin}}", "needsPlugin": "需要 {{plugin}}",
"needsPluginHint": "This automation is skipped until the feature it needs is available again.", "needsPluginHint": "在所需功能恢复可用之前,此自动化将被跳过。",
"offBadge": "关闭", "offBadge": "关闭",
"docsLink": "查看文档", "docsLink": "查看文档",
"ago": { "ago": {
"seconds": "{{count}}s ago", "seconds": "{{count}} 秒前",
"minutes": "{{count}} 分钟前", "minutes": "{{count}} 分钟前",
"hours": "{{count}} 小时前", "hours": "{{count}} 小时前",
"days": "{{count}} 天前" "days": "{{count}} 天前"
@@ -166,46 +166,60 @@
} }
}, },
"nav": { "nav": {
"automations": "Automations" "automations": "自动化"
}, },
"onboarding": { "onboarding": {
"feature_automations": "Automations", "feature_automations": "自动化",
"feature_automations_desc": "触发特定事件时自动执行任务,并接收通知。" "feature_automations_desc": "触发特定事件时自动执行任务,并接收通知。"
}, },
"maintenance": { "maintenance": {
"title": "维护中", "title": "维护中",
"description": "Pause host status, health, metric, container and tunnel-disconnection automations while work is in progress. Monitoring, manual runs and scheduled automations continue.", "description": "维护期间暂停由主机状态、健康检查、指标、容器和隧道断开事件触发的自动化。监控、手动执行和定时自动化继续运行。",
"denied": "You do not have permission to view maintenance.", "denied": "你没有查看维护信息的权限。",
"sharedReadOnly": "This host was shared with you. Only its owner can change maintenance.", "sharedReadOnly": "此主机由他人共享给你,只有所有者才能修改维护设置。",
"failed": "Maintenance could not be updated.", "failed": "无法更新维护设置。",
"active": "Maintenance active", "active": "维护中",
"estimated": "Estimated completion", "estimated": "预计完成时间",
"noAutoEnd": "The estimate does not end maintenance. End it explicitly when the work is complete.", "noAutoEnd": "预计时间不会自动结束维护,请在工作完成后手动结束。",
"endConfirm": "End maintenance and resume event-triggered automations for this host?", "endConfirm": "结束维护并恢复此主机的事件触发自动化?",
"confirmEnd": "Confirm end", "confirmEnd": "确认结束",
"cancel": "取消", "cancel": "取消",
"end": "End maintenance", "end": "结束维护",
"action": "操作", "action": "操作",
"startNow": "Start now", "startNow": "立即开始",
"schedule": "Schedule for later", "schedule": "安排稍后执行",
"reason": "Reason", "reason": "原因",
"startUtc": "First start (UTC)", "startUtc": "首次开始时间(UTC)",
"repeat": "Repeat", "repeat": "重复",
"once": "Once", "once": "仅一次",
"weekly": "Weekly", "weekly": "每周",
"monthly": "Monthly", "monthly": "每月",
"utcHelp": "Schedules use UTC throughout the year. Monthly schedules skip months without the selected day. Planned windows cannot overlap; overrunning maintenance continues across later windows.", "utcHelp": "计划全年使用 UTC。每月计划会跳过没有指定日期的月份。计划窗口不能重叠;超时未结束的维护会持续到后续窗口。",
"duration": "Estimated duration (minutes)", "duration": "预计时长(分钟)",
"grace": "Grace period (minutes)", "grace": "宽限期(分钟)",
"notify": "Notify once if still offline after the estimate plus grace period", "notify": "预计完成时间加宽限期后仍离线时,发送一次通知",
"addSchedule": "Add schedule", "addSchedule": "添加计划",
"schedules": "Schedules", "schedules": "计划",
"noSchedules": "No maintenance schedules.", "noSchedules": "暂无维护计划。",
"completed": "Already started", "completed": "已开始",
"minutes_one": "{{count}} minute", "minutes_one": "{{count}} 分钟",
"minutes_other": "{{count}} minutes", "minutes_other": "{{count}} 分钟",
"remove": "Remove schedule", "remove": "移除计划",
"loadFailed": "Could not refresh maintenance status. Changes are disabled until it can be loaded.", "loadFailed": "无法刷新维护状态,在成功加载之前无法更改设置。",
"loading": "Loading maintenance status…" "loading": "正在加载维护状态……"
},
"ui": {
"sidebar": {
"automations": {
"options": {
"connect": "连接",
"disconnect": "断开连接",
"offline": "离线",
"online": "在线",
"failing": "故障",
"recovered": "已恢复"
}
}
}
} }
} }
@@ -368,8 +368,12 @@ export function StepBlock({
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="connect">connect</SelectItem> <SelectItem value="connect">
<SelectItem value="disconnect">disconnect</SelectItem> {t("ui.sidebar.automations.options.connect")}
</SelectItem>
<SelectItem value="disconnect">
{t("ui.sidebar.automations.options.disconnect")}
</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<Input <Input
@@ -250,13 +250,21 @@ export function TriggerCard({
<SelectContent> <SelectContent>
{trigger.kind === "host_status" ? ( {trigger.kind === "host_status" ? (
<> <>
<SelectItem value="offline">offline</SelectItem> <SelectItem value="offline">
<SelectItem value="online">online</SelectItem> {t("ui.sidebar.automations.options.offline")}
</SelectItem>
<SelectItem value="online">
{t("ui.sidebar.automations.options.online")}
</SelectItem>
</> </>
) : ( ) : (
<> <>
<SelectItem value="failing">failing</SelectItem> <SelectItem value="failing">
<SelectItem value="recovered">recovered</SelectItem> {t("ui.sidebar.automations.options.failing")}
</SelectItem>
<SelectItem value="recovered">
{t("ui.sidebar.automations.options.recovered")}
</SelectItem>
</> </>
)} )}
</SelectContent> </SelectContent>
+8 -8
View File
@@ -24,8 +24,8 @@
"stop": "停止", "stop": "停止",
"status": "状态", "status": "状态",
"actions": "操作", "actions": "操作",
"cardView": "Card view", "cardView": "卡片视图",
"listView": "List view", "listView": "列表视图",
"confirmRemoveContainer": "确定要移除容器 '{{name}}' 吗?此操作无法撤销。", "confirmRemoveContainer": "确定要移除容器 '{{name}}' 吗?此操作无法撤销。",
"runningContainerWarning": "警告:此容器正在运行。移除操作将先停止容器。", "runningContainerWarning": "警告:此容器正在运行。移除操作将先停止容器。",
"loadingContainers": "正在加载容器...", "loadingContainers": "正在加载容器...",
@@ -70,7 +70,7 @@
"blockIo": "块 I/O", "blockIo": "块 I/O",
"read": "读取", "read": "读取",
"write": "写入", "write": "写入",
"pids": "PIDs", "pids": "进程 ID",
"containerInformation": "容器信息", "containerInformation": "容器信息",
"name": "名称", "name": "名称",
"id": "ID", "id": "ID",
@@ -78,7 +78,7 @@
"containerMustBeRunning": "访问控制台需要容器处于运行状态", "containerMustBeRunning": "访问控制台需要容器处于运行状态",
"verificationCodePrompt": "输入验证码", "verificationCodePrompt": "输入验证码",
"totpVerificationFailed": "TOTP 验证失败,请重试。", "totpVerificationFailed": "TOTP 验证失败,请重试。",
"browserSignInFailed": "Sign-in failed. Please try again.", "browserSignInFailed": "登录失败,请重试。",
"connectedTo": "已连接到 {{containerName}}", "connectedTo": "已连接到 {{containerName}}",
"disconnected": "已断开连接", "disconnected": "已断开连接",
"consoleError": "控制台错误", "consoleError": "控制台错误",
@@ -118,15 +118,15 @@
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Use Docker", "title": "使用 Docker",
"description": "Open the Docker manager and container consoles on hosts with Docker enabled." "description": "在启用了 Docker 的主机上打开 Docker 管理器和容器控制台。"
} }
}, },
"settings": { "settings": {
"host": { "host": {
"enable": { "enable": {
"label": "启用 Docker", "label": "启用 Docker",
"description": "Manage containers on this host." "description": "管理此主机上的容器。"
}, },
"containerRuntime": { "containerRuntime": {
"label": "容器运行时", "label": "容器运行时",
@@ -137,7 +137,7 @@
} }
}, },
"errors": { "errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type." "remoteServerRequired": "需要远程服务器。请在设置中连接远程服务器以使用此连接类型。"
}, },
"hosts": { "hosts": {
"docsLink": "查看文档", "docsLink": "查看文档",
+15 -3
View File
@@ -323,7 +323,7 @@
"sideBySide": "Side by Side", "sideBySide": "Side by Side",
"inline": "Inline", "inline": "Inline",
"fileComparison": "File Comparison: {{file1}} vs {{file2}}", "fileComparison": "File Comparison: {{file1}} vs {{file2}}",
"fileTooLarge": "File too large: {{error}}", "fileTooLarge": "File is too large (> 10MB) and cannot be opened as text for security reasons.",
"sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})", "sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})",
"loadFileFailed": "Failed to load file: {{error}}", "loadFileFailed": "Failed to load file: {{error}}",
"connecting": "Connecting...", "connecting": "Connecting...",
@@ -365,7 +365,7 @@
"comfortableLayout": "Use comfortable layout", "comfortableLayout": "Use comfortable layout",
"cannotLoadPdf": "Cannot load PDF", "cannotLoadPdf": "Cannot load PDF",
"pdfLoadError": "There was an error loading this PDF file.", "pdfLoadError": "There was an error loading this PDF file.",
"loadingPdf": "Loading PDF...", "loadingPdf": "Loading PDF viewer...",
"loadingPage": "Loading page...", "loadingPage": "Loading page...",
"cancel": "Cancel", "cancel": "Cancel",
"copiedItems_one": "Copied {{count}} item", "copiedItems_one": "Copied {{count}} item",
@@ -383,7 +383,16 @@
"videoUnsupported": "Your browser cannot play this video format.", "videoUnsupported": "Your browser cannot play this video format.",
"downloadToPlay": "Download to play it elsewhere", "downloadToPlay": "Download to play it elsewhere",
"cannotPreviewType": "Cannot preview this file type", "cannotPreviewType": "Cannot preview this file type",
"cannotPreviewTypeHint": "This file type has no preview. Download it to open it in another app." "cannotPreviewTypeHint": "This file type has no preview. Download it to open it in another app.",
"loadingFile": "Loading file...",
"loadingImage": "Loading image...",
"loadingEditor": "Loading editor...",
"loadingPreview": "Loading preview...",
"loadingAudio": "Loading audio player...",
"emptyPreview": "Nothing to preview yet...",
"revert": "Revert",
"openAsText": "Open as Text",
"confirmOpenAsText": "Do you want to continue opening this file as text? This may slow down your browser."
}, },
"sftpTransfer": { "sftpTransfer": {
"title": "SFTP", "title": "SFTP",
@@ -539,5 +548,8 @@
"metricsHopSourceRead": "Source → server: {{throughput}}", "metricsHopSourceRead": "Source → server: {{throughput}}",
"metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}", "metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}",
"metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}" "metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}"
},
"plugin": {
"name": "File Manager"
} }
} }
+135 -123
View File
@@ -6,15 +6,15 @@
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Use file manager", "title": "使用文件管理器",
"description": "Browse, edit and transfer files over SFTP." "description": "通过 SFTP 浏览、编辑和传输文件。"
} }
}, },
"settings": { "settings": {
"host": { "host": {
"enable": { "enable": {
"label": "启用文件管理器", "label": "启用文件管理器",
"description": "Browse and edit files on this host over SFTP." "description": "通过 SFTP 浏览和编辑此主机上的文件。"
}, },
"defaultPath": { "defaultPath": {
"label": "默认路径" "label": "默认路径"
@@ -25,12 +25,12 @@
}, },
"user": { "user": {
"transferConcurrency": { "transferConcurrency": {
"label": "Simultaneous File Transfers", "label": "同时传输文件数",
"description": "How many files the side by side local/remote manager uploads or downloads at the same time. Lower this if your server limits SFTP channels." "description": "本地与远程分栏文件管理器同时上传或下载的文件数。如果服务器限制 SFTP 通道数,请调低此值。"
}, },
"confirmBeforeTrash": { "confirmBeforeTrash": {
"label": "Confirm before moving files to trash", "label": "移入回收站前确认",
"description": "Ask before moving remote files to trash. Permanent deletion still requires confirmation." "description": "将远程文件移入回收站前询问。永久删除仍需确认。"
} }
} }
}, },
@@ -72,14 +72,14 @@
"rename": "重命名", "rename": "重命名",
"uploading": "上传中...", "uploading": "上传中...",
"uploadingFile": "正在上传 {{name}}...", "uploadingFile": "正在上传 {{name}}...",
"uploadingChunk": "Uploading {{name}}: {{percent}}% (part {{chunk}} of {{total}})", "uploadingChunk": "正在上传 {{name}}:{{percent}}%(第 {{chunk}} 部分,共 {{total}} 部分)",
"fileName": "文件名", "fileName": "文件名",
"folderName": "文件夹名称", "folderName": "文件夹名称",
"fileUploadedSuccessfully": "文件 \"{{name}}\" 上传成功", "fileUploadedSuccessfully": "文件 \"{{name}}\" 上传成功",
"failedToUploadFile": "上传文件失败", "failedToUploadFile": "上传文件失败",
"uploadingFolderFiles": "Uploading {{count}} file(s)...", "uploadingFolderFiles": "正在上传 {{count}} 个文件……",
"uploadedFolderFiles": "Uploaded {{count}} file(s) successfully", "uploadedFolderFiles": "成功上传 {{count}} 个文件",
"uploadedFolderPartial": "Uploaded {{uploaded}} file(s), {{failed}} failed", "uploadedFolderPartial": "已上传 {{uploaded}} 个文件,{{failed}} 个失败",
"fileDownloadedSuccessfully": "文件 \"{{name}}\" 下载成功", "fileDownloadedSuccessfully": "文件 \"{{name}}\" 下载成功",
"failedToDownloadFile": "下载文件失败", "failedToDownloadFile": "下载文件失败",
"fileCreatedSuccessfully": "文件 \"{{name}}\" 创建成功", "fileCreatedSuccessfully": "文件 \"{{name}}\" 创建成功",
@@ -95,58 +95,58 @@
"sudoOperationFailed": "Sudo 操作失败", "sudoOperationFailed": "Sudo 操作失败",
"sudoAuthFailed": "Sudo 认证失败", "sudoAuthFailed": "Sudo 认证失败",
"dragFilesToUpload": "将文件拖放到此处以上传", "dragFilesToUpload": "将文件拖放到此处以上传",
"dropToUploadHere": "Drop to upload here", "dropToUploadHere": "拖放到此处上传",
"dropToDownloadHere": "Drop to download here", "dropToDownloadHere": "拖放到此处下载",
"localFiles": "本地", "localFiles": "本地",
"showLocalFiles": "Show local files (split view)", "showLocalFiles": "显示本地文件(分栏视图)",
"hideLocalFiles": "Hide local files", "hideLocalFiles": "隐藏本地文件",
"localHome": "Home folder", "localHome": "主目录",
"localPathPlaceholder": "Local folder path", "localPathPlaceholder": "本地文件夹路径",
"localShowHidden": "Show hidden files", "localShowHidden": "显示隐藏文件",
"localHideHidden": "Hide hidden files", "localHideHidden": "隐藏隐藏文件",
"localRevealInFileManager": "Reveal in system file manager", "localRevealInFileManager": "在系统文件管理器中显示",
"localCannotOpenFolder": "Cannot open folder", "localCannotOpenFolder": "无法打开文件夹",
"localItemCount": "{{count}} 个项目", "localItemCount": "{{count}} 个项目",
"localSelectedCount": "已选择 {{count}} 项", "localSelectedCount": "已选择 {{count}} 项",
"back": "返回", "back": "返回",
"forward": "Forward", "forward": "前进",
"up": "Up", "up": "上一级",
"kind": "Kind", "kind": "类型",
"localUploadingProgress": "Uploading {{current}} of {{total}}", "localUploadingProgress": "正在上传第 {{current}} 个,共 {{total}} 个",
"localDownloadingProgress": "Downloading {{current}} of {{total}}", "localDownloadingProgress": "正在下载第 {{current}} 个,共 {{total}} 个",
"localTransferInFlight": "{{count}} at once", "localTransferInFlight": "同时 {{count}} 个",
"localPreparingDownload": "Preparing download...", "localPreparingDownload": "正在准备下载……",
"localTransferCancelled": "传输已取消", "localTransferCancelled": "传输已取消",
"localNothingToTransfer": "Nothing to transfer", "localNothingToTransfer": "没有需要传输的内容",
"localUploadComplete": "Uploaded {{count}} file(s)", "localUploadComplete": "已上传 {{count}} 个文件",
"localUploadFailed": "Upload failed", "localUploadFailed": "上传失败",
"localUploadPartial": "Uploaded {{done}} file(s), {{failed}} failed", "localUploadPartial": "已上传 {{done}} 个文件,{{failed}} 个失败",
"localDownloadComplete": "Downloaded {{count}} file(s)", "localDownloadComplete": "已下载 {{count}} 个文件",
"localDownloadFailed": "下载失败", "localDownloadFailed": "下载失败",
"localDownloadPartial": "Downloaded {{done}} file(s), {{failed}} failed", "localDownloadPartial": "已下载 {{done}} 个文件,{{failed}} 个失败",
"localReplaceExistingPrompt": "{{count}} item(s) already exist in this folder. Replace them?", "localReplaceExistingPrompt": "此文件夹中已有 {{count}} 个同名项目,是否替换?",
"localReplace": "替换", "localReplace": "替换",
"localSkipExisting": "Skip", "localSkipExisting": "跳过",
"localSkippedExisting": "Skipped {{count}} existing item(s)", "localSkippedExisting": "已跳过 {{count}} 个现有项目",
"localUnsafeNamesSkipped": "Skipped {{count}} item(s) whose names cannot be used on this computer", "localUnsafeNamesSkipped": "已跳过 {{count}} 个名称无法在此计算机上使用的项目",
"localOpen": "打开", "localOpen": "打开",
"localOpenFolder": "Open folder", "localOpenFolder": "打开文件夹",
"localUploadToRemote": "Upload to server", "localUploadToRemote": "上传到服务器",
"localUploadToRemoteMany": "Upload {{count}} items to server", "localUploadToRemoteMany": "将 {{count}} 个项目上传到服务器",
"localMoveToTrash": "Move to Trash", "localMoveToTrash": "移入回收站",
"localMoveToTrashMany": "Move {{count}} items to Trash", "localMoveToTrashMany": "将 {{count}} 个项目移入回收站",
"localTrashConfirmSingle": "Move \"{{name}}\" to the Trash?", "localTrashConfirmSingle": "将“{{name}}”移入回收站?",
"localTrashConfirmFolder": "Move the folder \"{{name}}\" and all its contents to the Trash?", "localTrashConfirmFolder": "将文件夹“{{name}}”及其中所有内容移入回收站?",
"localTrashConfirmMany": "Move {{count}} items to the Trash?", "localTrashConfirmMany": "将 {{count}} 个项目移入回收站?",
"localTrashed": "Moved {{count}} item(s) to the Trash", "localTrashed": "已将 {{count}} 个项目移入回收站",
"localTrashFailed": "Could not move to the Trash", "localTrashFailed": "无法移入回收站",
"localRenameFailed": "Rename failed", "localRenameFailed": "重命名失败",
"localCreateFailed": "Could not create item", "localCreateFailed": "无法创建项目",
"goToParentFolder": "Go to parent folder", "goToParentFolder": "转到父文件夹",
"resizeSidebar": "Drag to resize the sidebar (double-click to reset)", "resizeSidebar": "拖动调整侧边栏宽度(双击重置)",
"resizeLocalPane": "Drag to resize the local pane (double-click to reset)", "resizeLocalPane": "拖动调整本地窗格大小(双击重置)",
"columns": "列", "columns": "列",
"columnsHint": "Right-click to choose columns", "columnsHint": "右键点击选择显示的列",
"emptyFolder": "此文件夹为空", "emptyFolder": "此文件夹为空",
"searchFiles": "搜索文件...", "searchFiles": "搜索文件...",
"upload": "上传", "upload": "上传",
@@ -203,25 +203,25 @@
"confirmDeleteFolder": "确定要永久删除文件夹“{{name}}”及其所有内容吗?", "confirmDeleteFolder": "确定要永久删除文件夹“{{name}}”及其所有内容吗?",
"permanentDeleteWarning": "此操作无法撤销。项目将从服务器永久删除。", "permanentDeleteWarning": "此操作无法撤销。项目将从服务器永久删除。",
"moveToTrashWarning": "该项目将被移至 Termix 回收站,可在保留期到期前恢复。", "moveToTrashWarning": "该项目将被移至 Termix 回收站,可在保留期到期前恢复。",
"itemsMovedToTrash": "{{count}} item(s) moved to Trash", "itemsMovedToTrash": "已将 {{count}} 个项目移入回收站",
"trash": "回收站", "trash": "回收站",
"trashDescription": "Deleted items are retained for {{days}} day(s) on this host.", "trashDescription": "删除的项目将在此主机上保留 {{days}} 天。",
"trashLoadFailed": "Failed to load Trash", "trashLoadFailed": "加载回收站失败",
"trashEmpty": "回收站是空的", "trashEmpty": "回收站是空的",
"trashRestore": "恢复", "trashRestore": "恢复",
"trashRestored": "Restored {{name}}", "trashRestored": "已恢复 {{name}}",
"trashRestoreFailed": "Failed to restore item. The original path may already exist.", "trashRestoreFailed": "恢复项目失败,原路径可能已存在。",
"trashDeletePermanently": "Delete permanently", "trashDeletePermanently": "永久删除",
"trashDeleteConfirm": "Permanently delete {{name}}? This cannot be undone.", "trashDeleteConfirm": "永久删除 {{name}}?此操作无法撤销。",
"trashPermanentlyDeleted": "Permanently deleted {{name}}", "trashPermanentlyDeleted": "已永久删除 {{name}}",
"trashDeleteFailed": "Failed to permanently delete item", "trashDeleteFailed": "永久删除项目失败",
"trashEmptyAction": "清空回收站", "trashEmptyAction": "清空回收站",
"trashEmptyConfirm": "Permanently delete every item in Trash? This cannot be undone.", "trashEmptyConfirm": "永久删除回收站中的所有项目?此操作无法撤销。",
"trashEmptied": "回收站已清空", "trashEmptied": "回收站已清空",
"trashRetention": "自动清理(N 天后)", "trashRetention": "自动清理(N 天后)",
"trashRetentionSaved": "回收站保留设置已更新", "trashRetentionSaved": "回收站保留设置已更新",
"trashRetentionFailed": "更新回收站保留设置失败", "trashRetentionFailed": "更新回收站保留设置失败",
"trashUnavailableConfirm": "This item cannot be moved to Trash, usually because it is on another filesystem or requires elevated permissions. Delete it permanently instead?", "trashUnavailableConfirm": "此项目无法移入回收站,通常是因为它位于其他文件系统中或需要更高权限。是否改为永久删除?",
"recent": "最近", "recent": "最近",
"pinned": "已固定", "pinned": "已固定",
"folderShortcuts": "文件夹快捷方式", "folderShortcuts": "文件夹快捷方式",
@@ -303,7 +303,7 @@
"operationCompleted": "{{operation}}了{{count}}个项目", "operationCompleted": "{{operation}}了{{count}}个项目",
"downloadFileSuccess": "文件 {{name}} 下载成功", "downloadFileSuccess": "文件 {{name}} 下载成功",
"downloadFileFailed": "下载失败", "downloadFileFailed": "下载失败",
"downloadingFile": "Downloading {{name}}…", "downloadingFile": "正在下载 {{name}}……",
"downloadProgressBytes": "{{transferred}} / {{total}}", "downloadProgressBytes": "{{transferred}} / {{total}}",
"moveTo": "移动到 {{name}}", "moveTo": "移动到 {{name}}",
"diffCompareWith": "与 {{name}} 对比差异", "diffCompareWith": "与 {{name}} 对比差异",
@@ -323,13 +323,13 @@
"sideBySide": "并排", "sideBySide": "并排",
"inline": "内联", "inline": "内联",
"fileComparison": "文件比较: {{file1}} 对比 {{file2}}", "fileComparison": "文件比较: {{file1}} 对比 {{file2}}",
"fileTooLarge": "文件过大: {{error}}", "fileTooLarge": "文件过大(超过 10 MB),出于安全考虑无法作为文本打开。",
"sshConnectionFailed": "SSH 连接失败。请检查您到 {{name}} ({{ip}}:{{port}}) 的连接", "sshConnectionFailed": "SSH 连接失败。请检查您到 {{name}} ({{ip}}:{{port}}) 的连接",
"loadFileFailed": "加载文件失败: {{error}}", "loadFileFailed": "加载文件失败: {{error}}",
"connecting": "连接中...", "connecting": "连接中...",
"connectedSuccessfully": "连接成功", "connectedSuccessfully": "连接成功",
"totpVerificationFailed": "TOTP 验证失败", "totpVerificationFailed": "TOTP 验证失败",
"browserSignInFailed": "Sign-in failed. Please try again.", "browserSignInFailed": "登录失败,请重试。",
"authenticationFailed": "认证失败", "authenticationFailed": "认证失败",
"incorrectPassphrase": "密码错误。请重试。", "incorrectPassphrase": "密码错误。请重试。",
"verificationCodePrompt": "验证码:", "verificationCodePrompt": "验证码:",
@@ -361,73 +361,82 @@
"used": "已用", "used": "已用",
"of": "/", "of": "/",
"toggleSidebar": "切换侧边栏", "toggleSidebar": "切换侧边栏",
"compactLayout": "Use compact layout", "compactLayout": "使用紧凑布局",
"comfortableLayout": "Use comfortable layout", "comfortableLayout": "使用宽松布局",
"cannotLoadPdf": "无法加载 PDF", "cannotLoadPdf": "无法加载 PDF",
"pdfLoadError": "加载此 PDF 文件时出错。", "pdfLoadError": "加载此 PDF 文件时出错。",
"loadingPdf": "正在加载 PDF...", "loadingPdf": "正在加载 PDF 查看器……",
"loadingPage": "正在加载页面...", "loadingPage": "正在加载页面...",
"cancel": "取消", "cancel": "取消",
"copiedItems_one": "Copied {{count}} item", "copiedItems_one": "已复制 {{count}} 个项目",
"copiedItems_other": "Copied {{count}} items", "copiedItems_other": "已复制 {{count}} 个项目",
"deleteCopiedFileFailed": "Could not delete the copy of {{name}}: {{error}}", "deleteCopiedFileFailed": "无法删除 {{name}} 的副本:{{error}}",
"dragMovedItems_one": "Moved {{count}} item to {{target}}", "dragMovedItems_one": "已将 {{count}} 个项目移至 {{target}}",
"dragMovedItems_other": "Moved {{count}} items to {{target}}", "dragMovedItems_other": "已将 {{count}} 个项目移至 {{target}}",
"moveBackFileFailed": "Could not move {{name}} back: {{error}}", "moveBackFileFailed": "无法将 {{name}} 移回原处:{{error}}",
"terminal": "Terminal: {{host}} ({{path}})", "terminal": "终端:{{host}}({{path}})",
"dragToDesktop": "Dragging {{name}} to the desktop", "dragToDesktop": "正在将 {{name}} 拖到桌面",
"dragFilesToDesktop": "Dragging {{count}} files to the desktop", "dragFilesToDesktop": "正在将 {{count}} 个文件拖到桌面",
"folderDragUnsupported": "Dragging folders to the desktop is not supported yet", "folderDragUnsupported": "暂不支持将文件夹拖到桌面",
"saveToDesktopFailed": "Save failed: {{error}}", "saveToDesktopFailed": "保存失败:{{error}}",
"dragFailedDetail": "Drag failed: {{error}}", "dragFailedDetail": "拖放失败:{{error}}",
"videoUnsupported": "Your browser cannot play this video format.", "videoUnsupported": "你的浏览器无法播放此视频格式。",
"downloadToPlay": "Download to play it elsewhere", "downloadToPlay": "下载后使用其他程序播放",
"cannotPreviewType": "Cannot preview this file type", "cannotPreviewType": "无法预览此文件类型",
"cannotPreviewTypeHint": "This file type has no preview. Download it to open it in another app." "cannotPreviewTypeHint": "此文件类型不支持预览,请下载后使用其他应用打开。",
"loadingFile": "正在加载文件……",
"loadingImage": "正在加载图片……",
"loadingEditor": "正在加载编辑器……",
"loadingPreview": "正在加载预览……",
"loadingAudio": "正在加载音频播放器……",
"emptyPreview": "暂无可预览内容……",
"revert": "还原",
"openAsText": "作为文本打开",
"confirmOpenAsText": "是否继续将此文件作为文本打开?这可能导致浏览器运行变慢。"
}, },
"sftpTransfer": { "sftpTransfer": {
"title": "SFTP", "title": "SFTP",
"description": "Transfer files between two SSH hosts.", "description": "在两台 SSH 主机之间传输文件。",
"sourceServer": "Source Server", "sourceServer": "源服务器",
"destinationServer": "Destination Server", "destinationServer": "目标服务器",
"selectHost": "Select host", "selectHost": "选择主机",
"noHostSelected": "未选择主机", "noHostSelected": "未选择主机",
"move": "移动", "move": "移动",
"copy": "复制", "copy": "复制",
"upload": "上传", "upload": "上传",
"itemsSelected": "{{count}} item selected", "itemsSelected": "已选择 {{count}} 个项目",
"itemsSelected_other": "{{count}} items selected", "itemsSelected_other": "已选择 {{count}} 个项目",
"ready": "就绪", "ready": "就绪",
"loadingHosts": "正在加载主机...", "loadingHosts": "正在加载主机...",
"name": "名称", "name": "名称",
"modified": "修改时间", "modified": "修改时间",
"size": "大小", "size": "大小",
"noFiles": "No files", "noFiles": "没有文件",
"open": "打开", "open": "打开",
"up": "Up", "up": "上一级",
"refresh": "刷新", "refresh": "刷新",
"copyToTarget": "Copy to Target Directory", "copyToTarget": "复制到目标目录",
"rename": "重命名", "rename": "重命名",
"delete": "删除", "delete": "删除",
"editPermissions": "Edit Permissions", "editPermissions": "编辑权限",
"createNewFolder": "Create New Folder", "createNewFolder": "新建文件夹",
"deleteSelectedItems": "Delete Selected Items", "deleteSelectedItems": "删除所选项目",
"deleteConfirm": "Delete {{count}} selected item?", "deleteConfirm": "删除选中的 {{count}} 个项目?",
"deleteConfirm_other": "Delete {{count}} selected items?", "deleteConfirm_other": "删除选中的 {{count}} 个项目?",
"cancel": "取消", "cancel": "取消",
"save": "保存", "save": "保存",
"folderCreated": "Folder created", "folderCreated": "文件夹已创建",
"itemRenamed": "Item renamed", "itemRenamed": "项目已重命名",
"actionFailed": "操作失败", "actionFailed": "操作失败",
"itemsDeleted": "{{count}} item deleted", "itemsDeleted": "已删除 {{count}} 个项目",
"itemsDeleted_other": "{{count}} items deleted", "itemsDeleted_other": "已删除 {{count}} 个项目",
"deleteFailed": "Delete failed", "deleteFailed": "删除失败",
"permissionsUpdated": "Permissions updated", "permissionsUpdated": "权限已更新",
"noRemoteSessionConnected": "No remote session connected", "noRemoteSessionConnected": "尚未连接远程会话",
"failedToListFiles": "文件列表失败", "failedToListFiles": "文件列表失败",
"failedToRefresh": "Failed to refresh", "failedToRefresh": "刷新失败",
"destinationInsideSource": "Destination cannot be inside the selected source path.", "destinationInsideSource": "目标路径不能位于所选源路径内。",
"sameHostNoOp": "Source and destination are the same folder." "sameHostNoOp": "源路径与目标路径为同一文件夹。"
}, },
"hosts": { "hosts": {
"fileManager": "文件管理器", "fileManager": "文件管理器",
@@ -438,7 +447,7 @@
"loadingHost": "正在加载主机..." "loadingHost": "正在加载主机..."
}, },
"onboarding": { "onboarding": {
"feature_files": "File manager", "feature_files": "文件管理器",
"feature_files_desc": "无需离开应用,即可通过 SFTP 浏览、上传、下载及编辑文件。" "feature_files_desc": "无需离开应用,即可通过 SFTP 浏览、上传、下载及编辑文件。"
}, },
"terminal": { "terminal": {
@@ -464,7 +473,7 @@
"destinationPath": "目标路径", "destinationPath": "目标路径",
"destMustBeDirectory": "对于多项目传输,目标必须是一个目录", "destMustBeDirectory": "对于多项目传输,目标必须是一个目录",
"goUp": "向上", "goUp": "向上",
"hostAuthRequired": "Authentication required. Open File Manager on this host first", "hostAuthRequired": "需要身份验证,请先在此主机上打开文件管理器",
"hostConnecting": "连接中…", "hostConnecting": "连接中…",
"hostConnectionFailed": "连接失败", "hostConnectionFailed": "连接失败",
"hostDisconnected": "未连接", "hostDisconnected": "未连接",
@@ -485,12 +494,12 @@
"methodReason": { "methodReason": {
"user_item_sftp": "您选择了逐文件 SFTP。", "user_item_sftp": "您选择了逐文件 SFTP。",
"user_tar": "您选择了 Tar 归档。", "user_tar": "您选择了 Tar 归档。",
"tar_unavailable": "Tar is not available on one or both hosts, so per-file SFTP will be used instead.", "tar_unavailable": "一台或两台主机未安装 tar,将改为逐文件使用 SFTP 传输。",
"windows_host": "A Windows host is involved, so tar is not used.", "windows_host": "传输涉及 Windows 主机,因此不使用 tar。",
"auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data. Tar bundles them into one transfer.", "auto_multi_large": "自动选择:多个文件中包含可压缩的大文件({{largestSize}}),使用 tar 打包后统一传输。",
"auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set, so per-file SFTP.", "auto_single_large_in_archive": "自动选择:文件集中包含一个大文件({{largestSize}}),采用逐文件 SFTP 传输。",
"auto_many_incompressible": "Auto: mostly incompressible data, so per-file SFTP.", "auto_many_incompressible": "自动选择:数据大多不可压缩,采用逐文件 SFTP 传输。",
"auto_many_files": "Auto: many files ({{fileCount}}), tar cuts the per-file overhead.", "auto_many_files": "自动选择:文件较多({{fileCount}} 个),使用 tar 减少逐文件传输开销。",
"auto_default": "自动:此集合使用逐文件 SFTP。" "auto_default": "自动:此集合使用逐文件 SFTP。"
}, },
"methodTar": "Tar 归档", "methodTar": "Tar 归档",
@@ -498,11 +507,11 @@
"metricsCompress": "源端压缩:{{duration}}", "metricsCompress": "源端压缩:{{duration}}",
"metricsExtract": "目标端解压:{{duration}}", "metricsExtract": "目标端解压:{{duration}}",
"metricsPrepare": "准备目标:{{duration}}", "metricsPrepare": "准备目标:{{duration}}",
"metricsRouteBenchmark": "Route probe: direct {{direct}}, relay {{relay}}", "metricsRouteBenchmark": "路径探测:直连 {{direct}},中转 {{relay}}",
"metricsSourceDelete": "从源端删除:{{duration}}", "metricsSourceDelete": "从源端删除:{{duration}}",
"metricsTotal": "总计:{{duration}}", "metricsTotal": "总计:{{duration}}",
"metricsTransfer": "端到端:{{throughput}} ({{duration}})", "metricsTransfer": "端到端:{{throughput}} ({{duration}})",
"metricsVerify": "SHA-256 verified: {{duration}}", "metricsVerify": "SHA-256 校验通过:{{duration}}",
"moveFolderToHost": "移动文件夹到主机…", "moveFolderToHost": "移动文件夹到主机…",
"moveItemsToHost": "移动 {{count}} 个项目到主机…", "moveItemsToHost": "移动 {{count}} 个项目到主机…",
"moveToHost": "移动到主机…", "moveToHost": "移动到主机…",
@@ -511,7 +520,7 @@
"parallelSegmentsHint": "大文件会被拆分为 256 MB 的块。多个通道使用独立连接(如同同时进行多个传输),以获得更高的总吞吐量。", "parallelSegmentsHint": "大文件会被拆分为 256 MB 的块。多个通道使用独立连接(如同同时进行多个传输),以获得更高的总吞吐量。",
"parallelSegmentsLabel": "并行传输通道", "parallelSegmentsLabel": "并行传输通道",
"parallelSegmentsOption": "{{count}} 个通道", "parallelSegmentsOption": "{{count}} 个通道",
"progressBenchmarking": "Selecting the fastest transfer route…", "progressBenchmarking": "正在选择最快的传输路径……",
"progressBytes": "{{transferred}} / {{total}}", "progressBytes": "{{transferred}} / {{total}}",
"progressCancel": "取消", "progressCancel": "取消",
"progressCancelling": "正在取消…", "progressCancelling": "正在取消…",
@@ -523,7 +532,7 @@
"progressTotalSpeed": "{{speed}} 总计({{lanes}} 通道)", "progressTotalSpeed": "{{speed}} 总计({{lanes}} 通道)",
"progressTransferring": "正在传输数据…", "progressTransferring": "正在传输数据…",
"progressTransferringItems": "正在传输文件({{current}}/{{total}})…", "progressTransferringItems": "正在传输文件({{current}}/{{total}})…",
"progressVerifying": "Verifying file integrity…", "progressVerifying": "正在校验文件完整性……",
"recentDestinations": "最近目标", "recentDestinations": "最近目标",
"resumedHint": "已重新连接到在另一个窗口中启动的进行中的传输。", "resumedHint": "已重新连接到在另一个窗口中启动的进行中的传输。",
"retryTransfer": "重试", "retryTransfer": "重试",
@@ -539,5 +548,8 @@
"metricsHopSourceRead": "源 → 服务器:{{throughput}}", "metricsHopSourceRead": "源 → 服务器:{{throughput}}",
"metricsHopDestSftpWrite": "服务器 → 目标 (SFTP):{{throughput}}", "metricsHopDestSftpWrite": "服务器 → 目标 (SFTP):{{throughput}}",
"metricsHopDestLocalWrite": "服务器 → 目标 (本地):{{throughput}}" "metricsHopDestLocalWrite": "服务器 → 目标 (本地):{{throughput}}"
},
"plugin": {
"name": "文件管理器"
} }
} }
@@ -350,7 +350,9 @@ export function FileViewer({
<div className="h-full flex items-center justify-center"> <div className="h-full flex items-center justify-center">
<div className="text-center"> <div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-4"></div> <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-4"></div>
<p className="text-sm text-muted-foreground">Loading file...</p> <p className="text-sm text-muted-foreground">
{t("fileManager.loadingFile")}
</p>
</div> </div>
</div> </div>
); );
@@ -446,7 +448,7 @@ export function FileViewer({
className="flex items-center gap-2" className="flex items-center gap-2"
> >
<RotateCcw className="w-4 h-4" /> <RotateCcw className="w-4 h-4" />
Revert {t("fileManager.revert")}
</Button> </Button>
<Button <Button
variant="default" variant="default"
@@ -456,7 +458,7 @@ export function FileViewer({
className="flex items-center gap-2" className="flex items-center gap-2"
> >
<Save className="w-4 h-4" /> <Save className="w-4 h-4" />
Save {t("common.save")}
</Button> </Button>
</> </>
)} )}
@@ -647,14 +649,12 @@ export function FileViewer({
{isTooLarge ? ( {isTooLarge ? (
<div className="bg-destructive/10 border border-destructive/30 rounded p-3 mb-4"> <div className="bg-destructive/10 border border-destructive/30 rounded p-3 mb-4">
<p className="text-sm text-destructive font-medium"> <p className="text-sm text-destructive font-medium">
File is too large (&gt; 10MB) and cannot be opened as {t("fileManager.fileTooLarge")}
text for security reasons.
</p> </p>
</div> </div>
) : ( ) : (
<p className="text-sm text-muted-foreground mb-4"> <p className="text-sm text-muted-foreground mb-4">
Do you want to continue opening this file as text? This {t("fileManager.confirmOpenAsText")}
may slow down your browser.
</p> </p>
)} )}
</div> </div>
@@ -669,7 +669,7 @@ export function FileViewer({
className="flex items-center gap-2" className="flex items-center gap-2"
> >
<FileText className="w-4 h-4" /> <FileText className="w-4 h-4" />
Open as Text {t("fileManager.openAsText")}
</Button> </Button>
)} )}
<Button <Button
@@ -686,7 +686,7 @@ export function FileViewer({
size="sm" size="sm"
onClick={handleCancelOpenAsText} onClick={handleCancelOpenAsText}
> >
Cancel {t("common.cancel")}
</Button> </Button>
</div> </div>
</div> </div>
@@ -694,7 +694,9 @@ export function FileViewer({
)} )}
{fileTypeInfo.type === "image" && !showLargeFileWarning && ( {fileTypeInfo.type === "image" && !showLargeFileWarning && (
<Suspense fallback={<PreviewFallback label="Loading image..." />}> <Suspense
fallback={<PreviewFallback label={t("fileManager.loadingImage")} />}
>
<ImagePreview <ImagePreview
content={content} content={content}
fileName={file.name} fileName={file.name}
@@ -708,7 +710,9 @@ export function FileViewer({
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
{isEditable ? ( {isEditable ? (
<Suspense <Suspense
fallback={<PreviewFallback label="Loading editor..." />} fallback={
<PreviewFallback label={t("fileManager.loadingEditor")} />
}
> >
<CodeEditor <CodeEditor
ref={editorRef} ref={editorRef}
@@ -857,12 +861,16 @@ export function FileViewer({
<div className="p-4"> <div className="p-4">
<Suspense <Suspense
fallback={ fallback={
<PreviewFallback label="Loading preview..." /> <PreviewFallback
label={t("fileManager.loadingPreview")}
/>
} }
> >
<MarkdownRenderer <MarkdownRenderer
compact compact
content={editedContent || "Nothing to preview yet..."} content={
editedContent || t("fileManager.emptyPreview")
}
/> />
</Suspense> </Suspense>
</div> </div>
@@ -872,7 +880,11 @@ export function FileViewer({
<div className="flex-1 overflow-auto thin-scrollbar p-6"> <div className="flex-1 overflow-auto thin-scrollbar p-6">
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">
<Suspense <Suspense
fallback={<PreviewFallback label="Loading preview..." />} fallback={
<PreviewFallback
label={t("fileManager.loadingPreview")}
/>
}
> >
<MarkdownRenderer content={editedContent} /> <MarkdownRenderer content={editedContent} />
</Suspense> </Suspense>
@@ -885,7 +897,7 @@ export function FileViewer({
{fileTypeInfo.type === "pdf" && !showLargeFileWarning && ( {fileTypeInfo.type === "pdf" && !showLargeFileWarning && (
<Suspense <Suspense
fallback={<PreviewFallback label="Loading PDF viewer..." />} fallback={<PreviewFallback label={t("fileManager.loadingPdf")} />}
> >
<PdfPreview <PdfPreview
content={content} content={content}
@@ -897,7 +909,7 @@ export function FileViewer({
{fileTypeInfo.type === "audio" && !showLargeFileWarning && ( {fileTypeInfo.type === "audio" && !showLargeFileWarning && (
<Suspense <Suspense
fallback={<PreviewFallback label="Loading audio player..." />} fallback={<PreviewFallback label={t("fileManager.loadingAudio")} />}
> >
<AudioPreview <AudioPreview
file={file} file={file}
@@ -940,7 +952,7 @@ export function FileViewer({
<span>{file.path}</span> <span>{file.path}</span>
{hasChanges && ( {hasChanges && (
<span className="text-orange-600 font-medium"> <span className="text-orange-600 font-medium">
● Unsaved changes ● {t("common.unsavedChanges")}
</span> </span>
)} )}
</div> </div>
@@ -103,7 +103,9 @@ export function ImagePreview({
<div className="absolute inset-0 flex items-center justify-center bg-background/80"> <div className="absolute inset-0 flex items-center justify-center bg-background/80">
<div className="text-center"> <div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-2"></div> <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-2"></div>
<p className="text-sm text-muted-foreground">Loading image...</p> <p className="text-sm text-muted-foreground">
{t("fileManager.loadingImage")}
</p>
</div> </div>
</div> </div>
)} )}
+57 -57
View File
@@ -1,77 +1,77 @@
{ {
"permissions": { "permissions": {
"view": { "view": {
"title": "View fleets", "title": "查看主机组",
"description": "See fleets, their members and inventory." "description": "查看主机组、成员及资产信息。"
}, },
"manage": { "manage": {
"title": "Manage fleets", "title": "管理主机组",
"description": "Create, edit and delete fleets, change membership, and share fleets with other users or roles." "description": "创建、编辑和删除主机组,调整成员,并与其他用户或角色共享主机组。"
}, },
"execute": { "execute": {
"title": "Run fleet actions", "title": "执行主机组操作",
"description": "Run commands and package actions across a fleet, and transfer files to and from its member hosts." "description": "在主机组内批量执行命令、软件包操作,以及与成员主机传输文件。"
} }
}, },
"newUi": { "newUi": {
"sidebar": { "sidebar": {
"fleets": { "fleets": {
"title": "Fleets", "title": "主机组",
"createFleet": "Create Fleet", "createFleet": "创建主机组",
"createFleetTitle": "Create Fleet", "createFleetTitle": "创建主机组",
"editFleetTitle": "Edit Fleet", "editFleetTitle": "编辑主机组",
"createFleetDescription": "舰队是一个命名的主机组,你可以将其作为一个整体来执行操作。", "createFleetDescription": "主机组是一组已命名的主机,可作为整体执行操作。",
"nameLabel": "名称", "nameLabel": "名称",
"namePlaceholder": "例如:Web 服务器", "namePlaceholder": "例如:Web 服务器",
"nameRequired": "需要填写舰队名称。", "nameRequired": "需要填写主机组名称。",
"descriptionLabel": "描述", "descriptionLabel": "描述",
"descriptionPlaceholder": "可选描述", "descriptionPlaceholder": "可选描述",
"colorLabel": "颜色", "colorLabel": "颜色",
"tagRulesLabel": "Tag rules", "tagRulesLabel": "标签规则",
"tagRulesPlaceholder": "prod-web, edge", "tagRulesPlaceholder": "prod-web, edge",
"tagRulesHint": "Hosts with any of these tags are automatically included, in addition to hosts added manually.", "tagRulesHint": "除手动添加的主机外,具有任一指定标签的主机也会自动加入。",
"fleetCreated": "舰队已创建", "fleetCreated": "主机组已创建",
"fleetUpdated": "舰队已更新", "fleetUpdated": "主机组已更新",
"fleetDeleted": "Fleet deleted", "fleetDeleted": "主机组已删除",
"saveFailed": "保存舰队失败", "saveFailed": "保存主机组失败",
"deleteFailed": "Failed to delete fleet", "deleteFailed": "删除主机组失败",
"loadFailed": "Failed to load fleets", "loadFailed": "加载主机组失败",
"deleteFleetTitle": "Delete Fleet", "deleteFleetTitle": "删除主机组",
"deleteFleetDescription": "Are you sure you want to delete \"{{name}}\"? This does not delete the member hosts themselves.", "deleteFleetDescription": "确定删除“{{name}}”?此操作不会删除成员主机本身。",
"searchFleets": "Search fleets...", "searchFleets": "搜索主机组……",
"noFleets": "No fleets yet. Create one to get started.", "noFleets": "暂无主机组,请创建一个以开始使用。",
"memberCount": "{{count}} 台主机", "memberCount": "{{count}} 台主机",
"memberCount_other": "{{count}} 台主机", "memberCount_other": "{{count}} 台主机",
"manageMembers": "Manage Members", "manageMembers": "管理成员",
"manageMembersTitle": "管理舰队成员", "manageMembersTitle": "管理主机组成员",
"manageMembersDescription": "Toggle hosts in or out of this fleet's static membership. Hosts matched by tag rules are always included.", "manageMembersDescription": "将主机加入或移出此主机组的固定成员列表。匹配标签规则的主机始终包含在内。",
"searchHosts": "搜索主机...", "searchHosts": "搜索主机...",
"noHostsFound": "未找到主机", "noHostsFound": "未找到主机",
"noMembers": "This fleet has no members yet. Add hosts or set a tag rule.", "noMembers": "此主机组暂无成员,请添加主机或设置标签规则。",
"membersLoadFailed": "Failed to load fleet members", "membersLoadFailed": "加载主机组成员失败",
"memberUpdateFailed": "Failed to update fleet membership", "memberUpdateFailed": "更新主机组成员失败",
"tabRun": "Run Command", "tabRun": "执行命令",
"tabTransfer": "Transfer Files", "tabTransfer": "传输文件",
"tabInventory": "清单", "tabInventory": "清单",
"openInventoryTab": "Open in full tab", "openInventoryTab": "在完整标签页中打开",
"tabPackages": "软件包", "tabPackages": "软件包",
"runOnFleet": "在舰队上运行", "runOnFleet": "在主机组上运行",
"commandPlaceholder": "uptime && df -h", "commandPlaceholder": "uptime && df -h",
"commandRequired": "A command is required", "commandRequired": "必须填写命令",
"commandInputsLabel": "Command inputs", "commandInputsLabel": "命令输入",
"commandSucceeded": "Command succeeded on all hosts", "commandSucceeded": "命令已在所有主机上执行成功",
"commandPartialFailure": "{{total}} 台主机中有 {{failed}} 台失败", "commandPartialFailure": "{{total}} 台主机中有 {{failed}} 台失败",
"commandFailed": "Failed to run command", "commandFailed": "执行命令失败",
"resultSuccess": "成功", "resultSuccess": "成功",
"resultFailed": "失败", "resultFailed": "失败",
"push": "推送", "push": "推送",
"pull": "拉取", "pull": "拉取",
"remotePathLabel": "Remote path", "remotePathLabel": "远程路径",
"remotePathRequired": "A remote path is required", "remotePathRequired": "必须填写远程路径",
"fileLabel": "文件", "fileLabel": "文件",
"fileRequired": "需要选择文件", "fileRequired": "需要选择文件",
"pushHint": "选定的文件将被写入舰队中每台主机上的指定路径。", "pushHint": "选定的文件将被写入主机组中每台主机上的指定路径。",
"pullHint": "The file at this path is read from every host and downloaded as a single zip, one folder per host.", "pullHint": "从每台主机读取此路径下的文件,按主机分文件夹打包为一个 ZIP 文件下载。",
"transferComplete": "传输完成", "transferComplete": "传输完成",
"transferFailed": "传输失败", "transferFailed": "传输失败",
"refreshInventory": "刷新清单", "refreshInventory": "刷新清单",
@@ -85,27 +85,27 @@
"inventoryKernel": "内核", "inventoryKernel": "内核",
"inventoryArch": "架构", "inventoryArch": "架构",
"inventoryUptime": "运行时间", "inventoryUptime": "运行时间",
"inventoryPackageManager": "Package manager", "inventoryPackageManager": "软件包管理器",
"inventoryCollectedAt": "Collected", "inventoryCollectedAt": "已收集",
"packageActionLabel": "操作", "packageActionLabel": "操作",
"packageInstall": "Install", "packageInstall": "安装",
"packageRemove": "移除", "packageRemove": "移除",
"packageUpgradeAll": "升级所有软件包", "packageUpgradeAll": "升级所有软件包",
"packageNameLabel": "Package name", "packageNameLabel": "软件包名称",
"packageNameRequired": "A package name is required", "packageNameRequired": "必须填写软件包名称",
"packageHint": "Requires manage-level access per host and elevates using the host's stored sudo password.", "packageHint": "需要对每台主机具有管理级别权限,并使用主机保存的 sudo 密码提升权限。",
"shareFleet": "Share Fleet", "shareFleet": "共享主机组",
"shareFleetTitle": "Share \"{{name}}\"", "shareFleetTitle": "共享“{{name}}”",
"shareFleetDescription": "Shares this fleet's current member hosts with the selected users or roles. Hosts added to the fleet later are not shared automatically - share again to include them.", "shareFleetDescription": "将此主机组当前的成员主机共享给所选用户或角色。后续加入的主机不会自动共享,需要再次执行共享操作。",
"shareSelectionRequired": "Select at least one user or role", "shareSelectionRequired": "请至少选择一个用户或角色",
"fleetSharedSuccessfully": "Shared {{shared}} of {{total}} host(s) in this fleet", "fleetSharedSuccessfully": "已共享此主机组中的 {{shared}}/{{total}} 台主机",
"failedToShareFleet": "Failed to share fleet", "failedToShareFleet": "共享主机组失败",
"noMembersToShare": "This fleet has no member hosts to share yet." "noMembersToShare": "此主机组尚无可共享的成员主机。"
} }
} }
}, },
"nav": { "nav": {
"fleets": "Fleets" "fleets": "主机组"
}, },
"hosts": { "hosts": {
"docsLink": "查看文档", "docsLink": "查看文档",
@@ -131,7 +131,7 @@
}, },
"edit": { "edit": {
"label": "编辑", "label": "编辑",
"description": "View, plus modify non-authentication host settings. The owner's SSH authentication stays private and owner-only." "description": "允许查看和修改与身份验证无关的主机设置。所有者的 SSH 身份验证信息保持私密,仅供所有者使用。"
}, },
"manage": { "manage": {
"label": "管理", "label": "管理",
+136 -1
View File
@@ -169,7 +169,139 @@
"noServices": "No services set up", "noServices": "No services set up",
"configureLocation": "Set a location in the widget settings", "configureLocation": "Set a location in the widget settings",
"bannerTextPlaceholder": "Banner text", "bannerTextPlaceholder": "Banner text",
"serviceNamePlaceholder": "My service" "serviceNamePlaceholder": "My service",
"emptyNote": "Empty note...",
"weatherLoadFailed": "Could not load weather for \"{{location}}\"",
"feelsLike": "Feels like {{temperature}}",
"timezonePlaceholder": "America/New_York (leave blank for local)",
"notesPlaceholder": "Write your notes here...",
"locationPlaceholder": "New York, London, Tokyo...",
"widgets": {
"bookmark_list": {
"name": "Bookmarks",
"description": "A list of quick links"
},
"calendar": {
"name": "Calendar",
"description": "A month calendar with today highlighted"
},
"clock": {
"name": "Clock",
"description": "A live clock with configurable timezone"
},
"countdown": {
"name": "Countdown",
"description": "Counts down to a target date and time"
},
"custom_api": {
"name": "Custom API",
"description": "Fetch and display data from any JSON API"
},
"dashboard_links": {
"name": "Dashboard Links",
"description": "Shows your saved dashboard service links"
},
"folder": {
"name": "Folder",
"description": "A background card for grouping widgets visually"
},
"host_grid": {
"name": "Host Grid",
"description": "Live status overview grid for all your SSH hosts"
},
"host_status": {
"name": "Host Status",
"description": "Shows live status and metrics for an SSH host"
},
"iframe_embed": {
"name": "iFrame Embed",
"description": "Embed any URL in an iframe"
},
"image_widget": {
"name": "Image",
"description": "Display any image from a URL"
},
"link_tree": {
"name": "Link Tree",
"description": "Grouped links organized into labeled sections"
},
"markdown_notes": {
"name": "Markdown Notes",
"description": "Notes with optional Markdown rendering and inline editing"
},
"notes": {
"name": "Notes",
"description": "A text notes widget"
},
"ping_status": {
"name": "Ping Status",
"description": "Check if URLs or services are reachable"
},
"quick_connect": {
"name": "Quick Connect",
"description": "Launch terminal, files, docker, tunnels, and more for your hosts"
},
"recent_activity": {
"name": "Recent Activity",
"description": "Shows recent terminal, Docker, file, and tunnel activity"
},
"rss_feed": {
"name": "RSS Feed",
"description": "Display items from an RSS or Atom feed"
},
"search_bar": {
"name": "Search Bar",
"description": "Quick search with Google, DuckDuckGo, Bing, or a custom engine"
},
"search_links": {
"name": "Search Shortcuts",
"description": "Quick buttons to search different engines"
},
"service_grid": {
"name": "Service Grid",
"description": "A grid of service tiles with icons and links"
},
"service_link": {
"name": "Service Link",
"description": "A clickable tile linking to a service URL"
},
"ssh_terminal": {
"name": "SSH Terminal",
"description": "An inline SSH terminal connected to a configured host"
},
"system_overview": {
"name": "System Overview",
"description": "Termix version, database health, and uptime at a glance"
},
"termix_uptime": {
"name": "Termix Uptime",
"description": "Shows how long the Termix server has been running"
},
"text_banner": {
"name": "Text Banner",
"description": "A styled text label for sections and headings"
},
"weather": {
"name": "Weather",
"description": "Live weather for any location"
},
"docker_widget": {
"name": "Docker Manager",
"description": "Embedded Docker container manager for a configured host"
},
"file_manager_widget": {
"name": "File Manager",
"description": "Embedded SFTP file manager for a configured host"
},
"tunnel_widget": {
"name": "Tunnel Manager",
"description": "Embedded SSH tunnel manager for a configured host"
},
"metrics_chart": {
"name": "Metrics Chart",
"description": "Historical CPU, memory, disk, or network chart for a host"
}
}
}, },
"permissions": { "permissions": {
"use": { "use": {
@@ -186,5 +318,8 @@
"label": "Private certificate authority (PEM)", "label": "Private certificate authority (PEM)",
"description": "Optional trusted CA bundle for allowlisted hosts. Certificate verification remains enabled. Leave empty to use the system trust store." "description": "Optional trusted CA bundle for allowlisted hosts. Certificate verification remains enabled. Leave empty to use the system trust store."
} }
},
"plugin": {
"name": "Homepage"
} }
} }
+154 -19
View File
@@ -18,7 +18,7 @@
"homepage": { "homepage": {
"title": "主页", "title": "主页",
"addWidget": "添加小部件", "addWidget": "添加小部件",
"noWidgetsInCategory": "No widgets in this category", "noWidgetsInCategory": "此分类没有组件",
"editWidget": "编辑小部件", "editWidget": "编辑小部件",
"zoomIn": "放大", "zoomIn": "放大",
"zoomOut": "缩小", "zoomOut": "缩小",
@@ -39,7 +39,7 @@
"description": "描述", "description": "描述",
"color": "颜色", "color": "颜色",
"timezone": "时区", "timezone": "时区",
"invalidTimezone": "Invalid timezone. Use an IANA name such as America/New_York", "invalidTimezone": "时区无效,请使用 America/New_York 等 IANA 时区名称",
"showSeconds": "显示秒", "showSeconds": "显示秒",
"format12h": "12 小时制", "format12h": "12 小时制",
"content": "内容", "content": "内容",
@@ -60,9 +60,9 @@
"maxItems": "最大条目数", "maxItems": "最大条目数",
"showDescription": "显示描述", "showDescription": "显示描述",
"widgetRssName": "RSS 订阅源", "widgetRssName": "RSS 订阅源",
"rssNoFeedUrl": "Configure a feed URL in widget settings", "rssNoFeedUrl": "请在组件设置中配置订阅源 URL",
"rssLoadError": "Could not load feed", "rssLoadError": "无法加载订阅源",
"rssNoItems": "No items", "rssNoItems": "暂无项目",
"noHostSelected": "未选择主机", "noHostSelected": "未选择主机",
"displayedMetrics": "显示指标", "displayedMetrics": "显示指标",
"metricCpu": "CPU", "metricCpu": "CPU",
@@ -162,29 +162,164 @@
"selectHosts": "选择主机", "selectHosts": "选择主机",
"listLayout": "列表", "listLayout": "列表",
"gridLayout": "网格", "gridLayout": "网格",
"configureUrl": "Set a URL in the widget settings", "configureUrl": "请在组件设置中填写 URL",
"embeddedContent": "Embedded content", "embeddedContent": "嵌入内容",
"noBookmarks": "No bookmarks", "noBookmarks": "暂无书签",
"noSections": "No sections set up", "noSections": "尚未设置分区",
"noServices": "No services set up", "noServices": "尚未设置服务",
"configureLocation": "Set a location in the widget settings", "configureLocation": "请在组件设置中选择位置",
"bannerTextPlaceholder": "Banner text", "bannerTextPlaceholder": "横幅文字",
"serviceNamePlaceholder": "My service" "serviceNamePlaceholder": "我的服务",
"emptyNote": "暂无笔记内容……",
"weatherLoadFailed": "无法加载“{{location}}”的天气",
"feelsLike": "体感温度 {{temperature}}",
"timezonePlaceholder": "Asia/Shanghai(留空使用本地时区)",
"notesPlaceholder": "在此填写笔记……",
"locationPlaceholder": "北京、上海、东京……",
"widgets": {
"bookmark_list": {
"name": "书签",
"description": "显示快捷链接列表"
},
"calendar": {
"name": "日历",
"description": "显示月历并突出显示今天"
},
"clock": {
"name": "时钟",
"description": "显示实时时钟,可配置时区"
},
"countdown": {
"name": "倒计时",
"description": "距离目标日期和时间的倒计时"
},
"custom_api": {
"name": "自定义 API",
"description": "获取并展示 JSON API 数据"
},
"dashboard_links": {
"name": "仪表盘链接",
"description": "显示已保存的仪表盘服务链接"
},
"folder": {
"name": "文件夹",
"description": "用于对组件进行视觉分组的背景卡片"
},
"host_grid": {
"name": "主机网格",
"description": "以网格展示所有 SSH 主机的实时状态"
},
"host_status": {
"name": "主机状态",
"description": "显示 SSH 主机的实时状态和指标"
},
"iframe_embed": {
"name": "嵌入网页",
"description": "通过 iframe 嵌入指定 URL"
},
"image_widget": {
"name": "图片",
"description": "显示指定 URL 的图片"
},
"link_tree": {
"name": "链接树",
"description": "按分组展示链接与服务入口"
},
"markdown_notes": {
"name": "Markdown 笔记",
"description": "支持 Markdown 渲染和行内编辑的笔记"
},
"notes": {
"name": "笔记",
"description": "显示文本笔记"
},
"ping_status": {
"name": "连通性状态",
"description": "检查 URL 或服务是否可访问"
},
"quick_connect": {
"name": "快速连接",
"description": "为主机打开终端、文件、Docker、隧道等功能"
},
"recent_activity": {
"name": "最近活动",
"description": "显示最近的终端、Docker、文件和隧道活动"
},
"rss_feed": {
"name": "RSS 订阅源",
"description": "显示 RSS 或 Atom 订阅源的内容"
},
"search_bar": {
"name": "搜索栏",
"description": "快速使用 Google、DuckDuckGo、Bing 或自定义搜索引擎"
},
"search_links": {
"name": "搜索快捷方式",
"description": "快速使用不同搜索引擎"
},
"service_grid": {
"name": "服务网格",
"description": "以包含图标和链接的网格展示服务"
},
"service_link": {
"name": "服务链接",
"description": "点击卡片即可打开服务 URL"
},
"ssh_terminal": {
"name": "SSH 终端",
"description": "连接到指定主机的内嵌 SSH 终端"
},
"system_overview": {
"name": "系统概览",
"description": "显示 Termix 系统状态概览"
},
"termix_uptime": {
"name": "Termix 运行时间",
"description": "显示 Termix 服务器已运行的时长"
},
"text_banner": {
"name": "文字横幅",
"description": "用于分区和标题的样式化文字标签"
},
"weather": {
"name": "天气",
"description": "显示指定地点的实时天气"
},
"docker_widget": {
"name": "Docker 管理器",
"description": "用于管理指定主机 Docker 容器的内嵌管理器"
},
"file_manager_widget": {
"name": "文件管理器",
"description": "用于管理指定主机文件的内嵌 SFTP 管理器"
},
"tunnel_widget": {
"name": "隧道管理器",
"description": "用于管理指定主机 SSH 隧道的内嵌管理器"
},
"metrics_chart": {
"name": "指标图表",
"description": "显示主机 CPU、内存、磁盘或网络的历史指标图表"
}
}
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Use the homepage", "title": "使用首页",
"description": "View and edit the homepage canvas and dashboard service links." "description": "查看和编辑首页画布及仪表盘服务链接。"
} }
}, },
"settings": { "settings": {
"privateEndpoints": { "privateEndpoints": {
"label": "Allowed private hosts", "label": "允许访问的内网主机",
"description": "Exact private or loopback hostnames/IPs for Ping Status and Custom API widgets. Separate entries with commas or newlines; omit schemes, ports and paths. Empty blocks private destinations." "description": "供连通性状态和自定义 API 组件使用的精确内网或回环主机名/IP。用逗号或换行分隔,不包含协议、端口和路径。留空会阻止访问内网目标。"
}, },
"privateCertificateAuthority": { "privateCertificateAuthority": {
"label": "Private certificate authority (PEM)", "label": "私有证书颁发机构(PEM)",
"description": "Optional trusted CA bundle for allowlisted hosts. Certificate verification remains enabled. Leave empty to use the system trust store." "description": "可选的受信任 CA 证书包,用于允许列表中的主机。证书验证保持开启,留空则使用系统信任库。"
} }
},
"plugin": {
"name": "首页"
} }
} }
@@ -140,14 +140,18 @@ export function AddWidgetMenu({ state, onAdd, onClose }: AddWidgetMenuProps) {
{type.labelComponent ? ( {type.labelComponent ? (
<type.labelComponent part="name" /> <type.labelComponent part="name" />
) : ( ) : (
type.name t(`homepage.widgets.${type.id}.name`, {
defaultValue: type.name,
})
)} )}
</span> </span>
<span className="text-[10px] text-muted-foreground truncate"> <span className="text-[10px] text-muted-foreground truncate">
{type.labelComponent ? ( {type.labelComponent ? (
<type.labelComponent part="description" /> <type.labelComponent part="description" />
) : ( ) : (
type.description t(`homepage.widgets.${type.id}.description`, {
defaultValue: type.description,
})
)} )}
</span> </span>
</div> </div>
@@ -23,7 +23,7 @@ export function ClockEditForm({
onChange={(e) => onChange={(e) =>
onChange({ ...config, timezone: e.target.value || undefined }) onChange({ ...config, timezone: e.target.value || undefined })
} }
placeholder="America/New_York (leave blank for local)" placeholder={t("homepage.timezonePlaceholder")}
aria-invalid={invalidTimezone} aria-invalid={invalidTimezone}
className={ className={
invalidTimezone ? "h-8 text-sm border-destructive" : "h-8 text-sm" invalidTimezone ? "h-8 text-sm border-destructive" : "h-8 text-sm"
@@ -16,7 +16,7 @@ export function NotesEditForm({
<Textarea <Textarea
value={config.content} value={config.content}
onChange={(e) => onChange({ ...config, content: e.target.value })} onChange={(e) => onChange({ ...config, content: e.target.value })}
placeholder="Write your notes here..." placeholder={t("homepage.notesPlaceholder")}
className="text-sm min-h-[120px] resize-none rounded-none" className="text-sm min-h-[120px] resize-none rounded-none"
/> />
</div> </div>
@@ -17,7 +17,7 @@ export function WeatherEditForm({
<Input <Input
value={config.location} value={config.location}
onChange={(e) => onChange({ ...config, location: e.target.value })} onChange={(e) => onChange({ ...config, location: e.target.value })}
placeholder="New York, London, Tokyo..." placeholder={t("homepage.locationPlaceholder")}
className="h-8 text-sm" className="h-8 text-sm"
/> />
</div> </div>
@@ -1,3 +1,4 @@
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { StickyNote } from "lucide-react"; import { StickyNote } from "lucide-react";
import { registerWidget } from "./WidgetRegistry"; import { registerWidget } from "./WidgetRegistry";
import { import {
@@ -8,6 +9,7 @@ import {
import { WidgetTitle } from "@termix/plugin-sdk/ui"; import { WidgetTitle } from "@termix/plugin-sdk/ui";
function NotesWidget({ widget, config }: WidgetComponentProps<NotesConfig>) { function NotesWidget({ widget, config }: WidgetComponentProps<NotesConfig>) {
const { t } = useTranslation();
const { content, backgroundColor } = config; const { content, backgroundColor } = config;
return ( return (
@@ -19,7 +21,9 @@ function NotesWidget({ widget, config }: WidgetComponentProps<NotesConfig>) {
<div className="flex-1 overflow-auto p-3"> <div className="flex-1 overflow-auto p-3">
<pre className="text-xs text-foreground whitespace-pre-wrap break-words font-sans leading-relaxed select-text w-full"> <pre className="text-xs text-foreground whitespace-pre-wrap break-words font-sans leading-relaxed select-text w-full">
{content || ( {content || (
<span className="text-muted-foreground italic">Empty note...</span> <span className="text-muted-foreground italic">
{t("homepage.emptyNote")}
</span>
)} )}
</pre> </pre>
</div> </div>
@@ -100,7 +100,7 @@ function WeatherWidget({
if (error) { if (error) {
return ( return (
<div className="flex items-center justify-center w-full h-full text-xs text-destructive/70 p-3 text-center"> <div className="flex items-center justify-center w-full h-full text-xs text-destructive/70 p-3 text-center">
Could not load weather for "{location}" {t("homepage.weatherLoadFailed", { location })}
</div> </div>
); );
} }
@@ -108,7 +108,7 @@ function WeatherWidget({
if (!data) { if (!data) {
return ( return (
<div className="flex items-center justify-center w-full h-full text-xs text-muted-foreground/50"> <div className="flex items-center justify-center w-full h-full text-xs text-muted-foreground/50">
Loading... {t("common.loading")}
</div> </div>
); );
} }
@@ -138,7 +138,7 @@ function WeatherWidget({
{desc} {desc}
</span> </span>
<span className="text-[10px] text-muted-foreground/70"> <span className="text-[10px] text-muted-foreground/70">
Feels like {displayFeels} {t("homepage.feelsLike", { temperature: displayFeels })}
</span> </span>
</div> </div>
</div> </div>
@@ -45,7 +45,9 @@ function renderForm(config: Partial<ClockConfig> = {}) {
); );
return { return {
onChange, onChange,
input: screen.getByPlaceholderText(/America\/New_York/) as HTMLInputElement, input: screen.getByPlaceholderText(
"homepage.timezonePlaceholder",
) as HTMLInputElement,
}; };
} }
@@ -36,7 +36,9 @@ function open(timezone?: string) {
return { return {
onSave, onSave,
onClose, onClose,
input: screen.getByPlaceholderText(/America\/New_York/) as HTMLInputElement, input: screen.getByPlaceholderText(
"homepage.timezonePlaceholder",
) as HTMLInputElement,
save: screen save: screen
.getByText("homepage.save") .getByText("homepage.save")
.closest("button") as HTMLButtonElement, .closest("button") as HTMLButtonElement,
+3
View File
@@ -308,5 +308,8 @@
"label": "Monitored paths" "label": "Monitored paths"
} }
} }
},
"plugin": {
"name": "Host Metrics"
} }
} }
@@ -13,27 +13,27 @@
"free": "空闲", "free": "空闲",
"connecting": "连接中...", "connecting": "连接中...",
"connectionFailed": "连接服务器失败", "connectionFailed": "连接服务器失败",
"naCpus": "N/A CPU", "naCpus": "CPU 数据不可用",
"cpuCores_one": "{{count}} 核心", "cpuCores_one": "{{count}} 核心",
"cpuCores_other": "{{count}} 核心", "cpuCores_other": "{{count}} 核心",
"cpuUsage": "CPU 使用率", "cpuUsage": "CPU 使用率",
"memoryUsage": "内存使用率", "memoryUsage": "内存使用率",
"diskUsage": "磁盘使用率", "diskUsage": "磁盘使用率",
"selectFilesystem": "Select filesystem", "selectFilesystem": "选择文件系统",
"temperature": "温度", "temperature": "温度",
"highestTemperature": "最高温度", "highestTemperature": "最高温度",
"primaryTemperatureSensor": "Primary sensor", "primaryTemperatureSensor": "主传感器",
"gpu": { "gpu": {
"title": "GPU", "title": "GPU",
"noDevice": "No NVIDIA GPU detected. Install the NVIDIA driver (it provides nvidia-smi) to see GPU metrics.", "noDevice": "未检测到 NVIDIA GPU。请安装 NVIDIA 驱动(包含 nvidia-smi)以查看 GPU 指标。",
"driver": "Driver {{version}}", "driver": "驱动版本 {{version}}",
"utilization": "Utilization", "utilization": "使用率",
"memory": "VRAM", "memory": "显存",
"temperature": "Temp", "temperature": "温度",
"power": "Power", "power": "功率",
"fan": "Fan", "fan": "风扇",
"processes": "GPU processes", "processes": "GPU 进程",
"noProcesses": "No processes are using the GPU" "noProcesses": "没有进程正在使用 GPU"
}, },
"failedToFetchHostConfig": "获取主机配置失败", "failedToFetchHostConfig": "获取主机配置失败",
"serverOffline": "服务器离线", "serverOffline": "服务器离线",
@@ -143,7 +143,7 @@
"sslIssued": "证书已签发", "sslIssued": "证书已签发",
"sslDomainsPlaceholder": "example.com, www.example.com", "sslDomainsPlaceholder": "example.com, www.example.com",
"sslHttpStandalone": "HTTP (独立模式)", "sslHttpStandalone": "HTTP (独立模式)",
"sslHttpWebroot": "HTTP (webroot)", "sslHttpWebroot": "HTTP(网站根目录)",
"sslDns": "DNS", "sslDns": "DNS",
"sslDnsProvider": "DNS 提供商 (如 cloudflare)", "sslDnsProvider": "DNS 提供商 (如 cloudflare)",
"sslIssueHint": "DNS 提供商凭据必须已在主机上配置。", "sslIssueHint": "DNS 提供商凭据必须已在主机上配置。",
@@ -178,7 +178,7 @@
"wgLastHandshake": "上次握手", "wgLastHandshake": "上次握手",
"wgHandshakeNever": "从未", "wgHandshakeNever": "从未",
"wgTransfer": "传输", "wgTransfer": "传输",
"wgNoPeers": "No peers" "wgNoPeers": "没有对等节点"
} }
}, },
"metricsHistory": { "metricsHistory": {
@@ -186,8 +186,8 @@
"historySuffix": "历史", "historySuffix": "历史",
"cpuMemoryDisk": "CPU / 内存 / 磁盘", "cpuMemoryDisk": "CPU / 内存 / 磁盘",
"network": "网络", "network": "网络",
"download": "RX", "download": "接收",
"upload": "TX", "upload": "发送",
"noData": "此时间范围内没有可用的历史数据。", "noData": "此时间范围内没有可用的历史数据。",
"custom": "自定义", "custom": "自定义",
"to": "至", "to": "至",
@@ -204,14 +204,14 @@
"metricsIntervalS": "指标间隔(秒)", "metricsIntervalS": "指标间隔(秒)",
"metricsIntervalDesc2": "指标快照之间的秒数", "metricsIntervalDesc2": "指标快照之间的秒数",
"excludedMountsLabel": "排除的挂载点", "excludedMountsLabel": "排除的挂载点",
"monitoredMountsLabel": "Custom monitored paths", "monitoredMountsLabel": "自定义监控路径",
"monitoredMountsDesc": "Add paths that are not exposed as separate mounts, including bind-mounted Home Assistant paths such as /config.", "monitoredMountsDesc": "添加未作为独立挂载点显示的路径,包括 /config 等通过绑定挂载的 Home Assistant 路径。",
"monitoredMountPathPlaceholder": "Path (/config, /mnt/data)", "monitoredMountPathPlaceholder": "路径(/config、/mnt/data)",
"monitoredMountLabelPlaceholder": "标签(可选)", "monitoredMountLabelPlaceholder": "标签(可选)",
"addMonitoredMount": "Add path", "addMonitoredMount": "添加路径",
"excludedMountsDesc": "Hide specific mount points or filesystem types (e.g. nfs, cifs) from disk usage and alerts for this host.", "excludedMountsDesc": "在此主机的磁盘使用情况和告警中隐藏指定挂载点或文件系统类型(如 nfs、cifs)。",
"excludedMountsPlaceholder": "Mount path (/mnt/nas) or filesystem type (nfs, cifs)", "excludedMountsPlaceholder": "挂载路径(/mnt/nas)或文件系统类型(nfs、cifs)",
"excludedMountsEmpty": "No mounts excluded. All detected filesystems are shown.", "excludedMountsEmpty": "未排除任何挂载点,将显示所有检测到的文件系统。",
"addExcludedMount": "添加", "addExcludedMount": "添加",
"visibleWidgets": "可见小部件", "visibleWidgets": "可见小部件",
"widgetsMovedToHostMetrics": "卡片现在可在“主机指标”选项卡中直接添加、排列和调整大小。打开此主机的“主机指标”,使用“自定义”选择要显示的卡片。", "widgetsMovedToHostMetrics": "卡片现在可在“主机指标”选项卡中直接添加、排列和调整大小。打开此主机的“主机指标”,使用“自定义”选择要显示的卡片。",
@@ -250,7 +250,7 @@
"selectHost": "选择主机..." "selectHost": "选择主机..."
}, },
"terminalStatus": { "terminalStatus": {
"unavailable": "Live stats unavailable" "unavailable": "实时统计不可用"
}, },
"dashboard": { "dashboard": {
"cpu": "CPU", "cpu": "CPU",
@@ -264,49 +264,52 @@
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Use Host Metrics", "title": "使用主机指标",
"description": "View host metrics and use the host managers (services, packages, firewall and more) on hosts you can reach." "description": "查看主机指标,并在你有权访问的主机上使用服务、软件包、防火墙等管理工具。"
} }
}, },
"settings": { "settings": {
"admin": { "admin": {
"metricsInterval": { "metricsInterval": {
"label": "Metrics interval (seconds)", "label": "指标采样间隔(秒)",
"description": "How often a host being viewed is sampled, unless the host sets its own interval." "description": "正在查看的主机的采样频率,除非该主机另行设置了间隔。"
}, },
"historyRetentionDays": { "historyRetentionDays": {
"label": "History retention (days)", "label": "历史保留天数",
"description": "How long metrics samples are kept for the history charts." "description": "用于历史图表的指标采样数据保留时长。"
}, },
"enabledForNewHosts": { "enabledForNewHosts": {
"label": "Metrics on for new hosts", "label": "新主机默认启用指标",
"description": "Whether new hosts start with metrics collection turned on." "description": "是否为新主机默认启用指标采集。"
} }
}, },
"user": { "user": {
"temperatureUnit": { "temperatureUnit": {
"label": "Temperature unit", "label": "温度单位",
"description": "How temperatures are shown in Host Metrics.", "description": "主机指标中的温度显示方式。",
"celsius": "Celsius", "celsius": "摄氏度",
"fahrenheit": "Fahrenheit" "fahrenheit": "华氏度"
} }
}, },
"host": { "host": {
"metricsEnabled": { "metricsEnabled": {
"label": "Collect metrics" "label": "采集指标"
}, },
"metricsInterval": { "metricsInterval": {
"label": "Metrics interval (seconds)" "label": "指标采样间隔(秒)"
}, },
"enabledWidgets": { "enabledWidgets": {
"label": "Enabled widgets" "label": "启用的组件"
}, },
"excludedMounts": { "excludedMounts": {
"label": "Excluded mounts" "label": "排除的挂载点"
}, },
"monitoredMounts": { "monitoredMounts": {
"label": "Monitored paths" "label": "监控路径"
} }
} }
},
"plugin": {
"name": "主机指标"
} }
} }
+3
View File
@@ -46,5 +46,8 @@
"groupSearchBase": "Group Search Base", "groupSearchBase": "Group Search Base",
"adminGroup": "Admin Group", "adminGroup": "Admin Group",
"allowedUsers": "Allowed Users" "allowedUsers": "Allowed Users"
},
"plugin": {
"name": "LDAP"
} }
} }
+16 -13
View File
@@ -1,5 +1,5 @@
{ {
"loginWithLdap": "Sign in with LDAP", "loginWithLdap": "使用 LDAP 登录",
"loginWithProvider": "使用{{name}}登录", "loginWithProvider": "使用{{name}}登录",
"loading": "加载中...", "loading": "加载中...",
"requiredField": "此字段为必填项", "requiredField": "此字段为必填项",
@@ -8,28 +8,28 @@
"signIn": "登录", "signIn": "登录",
"permissions": { "permissions": {
"manage": { "manage": {
"title": "Manage LDAP directories", "title": "管理 LDAP 目录",
"description": "Add, change and remove the LDAP directories people can sign in with." "description": "添加、修改和移除可供用户登录的 LDAP 目录。"
} }
}, },
"providers": { "providers": {
"docsLink": "查看文档", "docsLink": "查看文档",
"none": "No LDAP directories configured.", "none": "尚未配置 LDAP 目录。",
"add": "Add Directory", "add": "添加目录",
"edit": "Edit Directory", "edit": "编辑目录",
"delete": "Delete Directory", "delete": "删除目录",
"deleteConfirm": "Delete this directory? Users who sign in with it will not be able to log in.", "deleteConfirm": "删除此目录?依赖该目录登录的用户将无法登录。",
"name": "显示名称", "name": "显示名称",
"namePlaceholder": "Company directory", "namePlaceholder": "公司目录",
"nameRequired": "A display name is required", "nameRequired": "必须填写显示名称",
"enabled": "已启用", "enabled": "已启用",
"save": "Save Directory", "save": "保存目录",
"saving": "保存中...", "saving": "保存中...",
"saved": "已保存", "saved": "已保存",
"saveFailed": "保存失败", "saveFailed": "保存失败",
"deleted": "已删除", "deleted": "已删除",
"deleteFailed": "删除失败", "deleteFailed": "删除失败",
"loadFailed": "Failed to load LDAP directories", "loadFailed": "加载 LDAP 目录失败",
"cancel": "取消" "cancel": "取消"
}, },
"fields": { "fields": {
@@ -38,7 +38,7 @@
"useTls": "使用 TLS (LDAPS)", "useTls": "使用 TLS (LDAPS)",
"bindDn": "绑定 DN", "bindDn": "绑定 DN",
"bindPassword": "绑定密码", "bindPassword": "绑定密码",
"secretKeep": "A password is saved. Leave this empty to keep it.", "secretKeep": "已保存密码,留空可保留现有密码。",
"userSearchBase": "用户搜索基准", "userSearchBase": "用户搜索基准",
"userSearchFilter": "用户搜索过滤器", "userSearchFilter": "用户搜索过滤器",
"usernameAttr": "用户名属性", "usernameAttr": "用户名属性",
@@ -46,5 +46,8 @@
"groupSearchBase": "群组搜索基准", "groupSearchBase": "群组搜索基准",
"adminGroup": "管理员组", "adminGroup": "管理员组",
"allowedUsers": "允许的用户" "allowedUsers": "允许的用户"
},
"plugin": {
"name": "LDAP"
} }
} }
@@ -54,8 +54,8 @@
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Use network graph", "title": "使用网络拓扑图",
"description": "View and edit the network topology graph." "description": "查看和编辑网络拓扑图。"
} }
}, },
"hosts": { "hosts": {
+3
View File
@@ -28,5 +28,8 @@
}, },
"hosts": { "hosts": {
"filterAuthOpkssh": "OPKSSH" "filterAuthOpkssh": "OPKSSH"
},
"plugin": {
"name": "OPKSSH"
} }
} }
+7 -4
View File
@@ -18,15 +18,18 @@
}, },
"settings": { "settings": {
"redirectUri": { "redirectUri": {
"label": "Redirect URI", "label": "重定向 URI",
"description": "Register this URL as an allowed redirect URI with every identity provider in your OPKSSH config." "description": "在 OPKSSH 配置中的每个身份提供商处,将此 URL 登记为允许的重定向 URI。"
}, },
"legacyCallback": { "legacyCallback": {
"label": "Use the old redirect URI", "label": "使用旧版重定向 URI",
"description": "Keep sending /host/opkssh-callback, the redirect URI used before 2.9. Turn this off once your identity providers allow the new one." "description": "继续发送 2.9 之前使用的重定向 URI /host/opkssh-callback。当身份提供商允许新 URI 后,可关闭此选项。"
} }
}, },
"hosts": { "hosts": {
"filterAuthOpkssh": "OPKSSH" "filterAuthOpkssh": "OPKSSH"
},
"plugin": {
"name": "OPKSSH"
} }
} }
+17 -17
View File
@@ -1,9 +1,9 @@
{ {
"nav": { "nav": {
"proxmoxStats": "Proxmox Stats" "proxmoxStats": "Proxmox 统计"
}, },
"proxmoxStats": { "proxmoxStats": {
"tabTitle": "Proxmox Stats", "tabTitle": "Proxmox 统计",
"cpuUsage": "CPU 使用率", "cpuUsage": "CPU 使用率",
"memoryUsage": "内存使用量", "memoryUsage": "内存使用量",
"diskUsage": "磁盘使用率", "diskUsage": "磁盘使用率",
@@ -12,14 +12,14 @@
"systemInfo": "系统信息", "systemInfo": "系统信息",
"guestsSummary": "客户主机", "guestsSummary": "客户主机",
"storagePools": "存储池", "storagePools": "存储池",
"clusterHealth": "Cluster Health", "clusterHealth": "集群健康状态",
"cpuCores_one": "{{count}} 核", "cpuCores_one": "{{count}} 核",
"cpuCores_other": "{{count}} 核", "cpuCores_other": "{{count}} 核",
"used": "已用", "used": "已用",
"total": "总计", "total": "总计",
"hostname": "主机名", "hostname": "主机名",
"kernel": "内核", "kernel": "内核",
"pveVersion": "PVE Version", "pveVersion": "PVE 版本",
"name": "名称", "name": "名称",
"status": "状态", "status": "状态",
"type": "类型", "type": "类型",
@@ -28,35 +28,35 @@
"disk": "磁盘", "disk": "磁盘",
"running": "运行中", "running": "运行中",
"stopped": "已停止", "stopped": "已停止",
"guestCounts": "{{running}}/{{total}} running", "guestCounts": "{{running}}/{{total}} 正在运行",
"inactive": "未激活", "inactive": "未激活",
"notClustered": "Standalone node (not part of a cluster)", "notClustered": "独立节点(不属于集群)",
"quorate": "达到法定节点数量", "quorate": "达到法定节点数量",
"notQuorate": "未达到法定节点数量", "notQuorate": "未达到法定节点数量",
"nodeOnline": "在线", "nodeOnline": "在线",
"nodeOffline": "离线", "nodeOffline": "离线",
"noHostSelected": "未选择主机", "noHostSelected": "未选择主机",
"pveshNotFound": "pvesh not found - is this a Proxmox node?", "pveshNotFound": "未找到 pvesh,此主机是否为 Proxmox 节点?",
"nodeDetectionFailed": "Unable to determine a valid Proxmox node name", "nodeDetectionFailed": "无法确定有效的 Proxmox 节点名称",
"connecting": "连接中...", "connecting": "连接中...",
"connectionFailed": "Failed to connect to Proxmox node", "connectionFailed": "连接 Proxmox 节点失败",
"refresh": "刷新", "refresh": "刷新",
"searchGuests": "搜索客户主机...", "searchGuests": "搜索客户主机...",
"filterAll": "全部", "filterAll": "全部",
"filterVm": "VM", "filterVm": "虚拟机",
"filterLxc": "LXC", "filterLxc": "LXC",
"noGuests": "No VMs or LXC containers found on this node.", "noGuests": "此节点上未找到虚拟机或 LXC 容器。",
"noGuestsMatch": "没有符合当前筛选条件的客户主机。" "noGuestsMatch": "没有符合当前筛选条件的客户主机。"
}, },
"hosts": { "hosts": {
"tabProxmox": "Proxmox", "tabProxmox": "Proxmox",
"enableProxmoxStats": "Enable Proxmox Stats", "enableProxmoxStats": "启用 Proxmox 统计",
"enableProxmoxStatsDesc": "显示此主机的 Proxmox 统计信息标签页,其中包含通过 SSH 收集的实时节点、客户机、存储和集群统计信息。", "enableProxmoxStatsDesc": "显示此主机的 Proxmox 统计信息标签页,其中包含通过 SSH 收集的实时节点、客户机、存储和集群统计信息。",
"proxmoxStatsPollInterval": "Poll interval (seconds)", "proxmoxStatsPollInterval": "轮询间隔(秒)",
"proxmoxStatsPollIntervalDesc": "Minimum 15 seconds. How often to refresh Proxmox node stats.", "proxmoxStatsPollIntervalDesc": "Proxmox 节点统计的刷新频率,最少 15 秒。",
"proxmoxStatsNodeOverride": "覆盖节点名称", "proxmoxStatsNodeOverride": "覆盖节点名称",
"proxmoxStatsNodeOverrideDesc": "覆盖自动检测的 Proxmox 节点名称(默认情况下通过主机名检测)。", "proxmoxStatsNodeOverrideDesc": "覆盖自动检测的 Proxmox 节点名称(默认情况下通过主机名检测)。",
"proxmoxStatsNodeOverridePlaceholder": "Auto-detected from hostname", "proxmoxStatsNodeOverridePlaceholder": "根据主机名自动检测",
"authTypeStepca": "Step CA", "authTypeStepca": "Step CA",
"proxmoxIntegration": "Proxmox 集成", "proxmoxIntegration": "Proxmox 集成",
"enableProxmox": "启用 Proxmox", "enableProxmox": "启用 Proxmox",
@@ -110,8 +110,8 @@
"hostNotFound": "未找到主机", "hostNotFound": "未找到主机",
"loadingHost": "正在加载主机...", "loadingHost": "正在加载主机...",
"none": "无", "none": "无",
"proxmoxGuestsSelected_one": "{{count}} guest, {{selected}} selected", "proxmoxGuestsSelected_one": "{{count}} 个客户机,已选 {{selected}} 个",
"proxmoxGuestsSelected_other": "{{count}} guests, {{selected}} selected", "proxmoxGuestsSelected_other": "{{count}} 个客户机,已选 {{selected}} 个",
"proxmoxImportButton_one": "导入 {{count}} 个主机", "proxmoxImportButton_one": "导入 {{count}} 个主机",
"proxmoxImportButton_other": "导入 {{count}} 个主机" "proxmoxImportButton_other": "导入 {{count}} 个主机"
}, },
+25 -1
View File
@@ -212,7 +212,28 @@
"backspaceKey": "Backspace Key", "backspaceKey": "Backspace Key",
"colorDepth": "Color Depth", "colorDepth": "Color Depth",
"resizeMethod": "Resize Method", "resizeMethod": "Resize Method",
"driveNamePlaceholder": "Termix Drive" "driveNamePlaceholder": "Termix Drive",
"options": {
"any": "Any",
"auto": "Auto",
"nlaExtended": "NLA Extended",
"color8": "8-bit",
"color16": "16-bit",
"color24": "24-bit",
"color32": "32-bit",
"displayUpdate": "Display Update",
"reconnect": "Reconnect",
"preserve": "Preserve",
"unix": "Unix (LF)",
"windows": "Windows (CRLF)",
"local": "Local",
"remote": "Remote",
"failsafe": "failsafe",
"blackOnWhite": "Black on White",
"whiteOnBlack": "White on Black",
"grayOnBlack": "Gray on Black",
"greenOnBlack": "Green on Black"
}
}, },
"remoteDesktop": "Remote Desktop", "remoteDesktop": "Remote Desktop",
"tabRdp": "RDP", "tabRdp": "RDP",
@@ -346,5 +367,8 @@
}, },
"errors": { "errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type." "remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
},
"plugin": {
"name": "Remote Desktop"
} }
} }
@@ -1,11 +1,11 @@
{ {
"remoteDesktop": { "remoteDesktop": {
"connecting": "正在连接到 {{type}} 会话...", "connecting": "正在连接到 {{type}} 会话...",
"checkingGuacd": "Checking remote desktop service...", "checkingGuacd": "正在检查远程桌面服务……",
"requestingToken": "Requesting {{type}} connection token...", "requestingToken": "正在请求 {{type}} 连接令牌……",
"openingSession": "Opening {{type}} session...", "openingSession": "正在打开 {{type}} 会话……",
"negotiating": "Negotiating {{type}} handshake...", "negotiating": "正在协商 {{type}} 握手……",
"sessionReady": "{{type}} session ready", "sessionReady": "{{type}} 会话已就绪",
"connectionError": "连接错误", "connectionError": "连接错误",
"connectionFailed": "连接失败", "connectionFailed": "连接失败",
"failedToConnect": "获取连接令牌失败", "failedToConnect": "获取连接令牌失败",
@@ -14,36 +14,36 @@
"reconnect": "重新连接", "reconnect": "重新连接",
"retry": "重试", "retry": "重试",
"guacdUnavailable": "远程桌面服务 (guacd) 不可用。请确保 guacd 正在运行并可访问,且在管理设置中正确配置。", "guacdUnavailable": "远程桌面服务 (guacd) 不可用。请确保 guacd 正在运行并可访问,且在管理设置中正确配置。",
"credentialPromptTitle": "Enter RDP Credentials", "credentialPromptTitle": "输入 RDP 凭据",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.", "credentialPromptDescription": "此主机配置为连接时询问凭据。凭据仅用于当前会话,不会保存。",
"connect": "连接", "connect": "连接",
"ctrlAltDel": "Ctrl+Alt+Del", "ctrlAltDel": "Ctrl+Alt+Del",
"files": { "files": {
"title": "Drive Files", "title": "驱动器文件",
"parent": "Parent directory", "parent": "父目录",
"refresh": "刷新", "refresh": "刷新",
"empty": "此文件夹为空", "empty": "此文件夹为空",
"listFailed": "Failed to read this folder", "listFailed": "读取此文件夹失败",
"download": "下载", "download": "下载",
"downloadFailed": "Failed to download {{name}}", "downloadFailed": "下载 {{name}} 失败",
"upload": "Upload files", "upload": "上传文件",
"uploading": "正在上传 {{name}}...", "uploading": "正在上传 {{name}}...",
"uploaded": "Uploaded {{name}}", "uploaded": "已上传 {{name}}",
"uploadFailed": "Failed to upload {{name}}", "uploadFailed": "上传 {{name}} 失败",
"uploadDisabled": "Uploads are disabled for this connection", "uploadDisabled": "此连接已禁用上传",
"driveUnavailable": "Enable RDP drive redirection before uploading files", "driveUnavailable": "上传文件前请先启用 RDP 驱动器重定向",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.", "driveNotWritable": "guacd 无法写入驱动器文件夹,请检查 GUACD_DRIVE_PATH(或主机的“驱动器路径”)及其权限。",
"dropToUpload": "Drop files to upload" "dropToUpload": "拖放文件以上传"
}, },
"toolbar": { "toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del", "ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (锁定屏幕)", "winL": "Win+L (锁定屏幕)",
"winKey": "Windows 键", "winKey": "Windows 键",
"superL": "Super+L (Lock Screen)", "superL": "Super+L(锁屏)",
"superKey": "Super Key", "superKey": "Super 键",
"super": "Super", "super": "Super",
"cmdL": "Cmd+L (Lock Screen)", "cmdL": "Cmd+L(锁屏)",
"cmdKey": "Command Key", "cmdKey": "Command 键",
"cmd": "Cmd", "cmd": "Cmd",
"ctrl": "Ctrl", "ctrl": "Ctrl",
"alt": "Alt", "alt": "Alt",
@@ -51,37 +51,37 @@
"win": "Win", "win": "Win",
"stickyActive": "{{key}}(已锁定 - 点击释放)", "stickyActive": "{{key}}(已锁定 - 点击释放)",
"stickyInactive": "{{key}}(点击锁定)", "stickyInactive": "{{key}}(点击锁定)",
"esc": "Escape", "esc": "Esc 键",
"tab": "Tab", "tab": "Tab 键",
"home": "Home", "home": "Home 键",
"end": "End", "end": "End 键",
"pageUp": "Page Up", "pageUp": "向上翻页",
"pageDown": "Page Down", "pageDown": "向下翻页",
"arrowUp": "Arrow Up", "arrowUp": "向上方向键",
"arrowDown": "Arrow Down", "arrowDown": "向下方向键",
"arrowLeft": "Arrow Left", "arrowLeft": "向左方向键",
"arrowRight": "Arrow Right", "arrowRight": "向右方向键",
"fnToggle": "功能键", "fnToggle": "功能键",
"reconnect": "重新连接会话", "reconnect": "重新连接会话",
"collapse": "折叠工具栏", "collapse": "折叠工具栏",
"expand": "展开工具栏", "expand": "展开工具栏",
"dragHandle": "拖动以重新定位", "dragHandle": "拖动以重新定位",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.", "hide": "仅在此会话中隐藏。如需永久禁用,请打开“主机设置 → 常规 → 连接工具栏”。",
"zoomIn": "Zoom in", "zoomIn": "放大",
"zoomOut": "Zoom out", "zoomOut": "缩小",
"resetZoom": "Reset zoom", "resetZoom": "重置缩放",
"fitToScreen": "Fit to screen", "fitToScreen": "适应屏幕",
"switchToTrackpad": "切换到触控板模式(拖动移动光标,轻点进行点击)", "switchToTrackpad": "切换到触控板模式(拖动移动光标,轻点进行点击)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)" "switchToTouch": "切换到触摸模式(直接点按目标位置)"
}, },
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings." "disabled": "远程桌面已关闭,管理员可在设置中启用。"
}, },
"hosts": { "hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop", "nativeRdpOpened": "已在 Windows 远程桌面中打开",
"nativeRdpFailed": "Failed to open Windows Remote Desktop", "nativeRdpFailed": "打开 Windows 远程桌面失败",
"openNativeRdp": "Open in Windows Remote Desktop", "openNativeRdp": "在 Windows 远程桌面中打开",
"guac": { "guac": {
"toolbar": "Toolbar", "toolbar": "工具栏",
"connection": "连接", "connection": "连接",
"authentication": "认证", "authentication": "认证",
"storedCredential": "已存储凭据", "storedCredential": "已存储凭据",
@@ -89,7 +89,7 @@
"authTypeDirect": "直接", "authTypeDirect": "直接",
"authTypeCredential": "凭据", "authTypeCredential": "凭据",
"authTypeNone": "无", "authTypeNone": "无",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.", "authTypeNoneDesc": "未保存凭据。每次连接时都会询问用户名和密码,且不会保存。",
"selectCredential": "选择凭据...", "selectCredential": "选择凭据...",
"connectionSettings": "连接设置", "connectionSettings": "连接设置",
"displaySettings": "显示设置", "displaySettings": "显示设置",
@@ -107,7 +107,7 @@
"rdpPort": "RDP 端口", "rdpPort": "RDP 端口",
"username": "用户名", "username": "用户名",
"password": "密码", "password": "密码",
"passwordSaved": "Password saved, type to change", "passwordSaved": "密码已保存,输入以修改",
"domain": "域", "domain": "域",
"securityMode": "安全模式", "securityMode": "安全模式",
"width": "宽度", "width": "宽度",
@@ -212,7 +212,28 @@
"backspaceKey": "退格键", "backspaceKey": "退格键",
"colorDepth": "颜色深度", "colorDepth": "颜色深度",
"resizeMethod": "调整大小方法", "resizeMethod": "调整大小方法",
"driveNamePlaceholder": "Termix Drive" "driveNamePlaceholder": "Termix 驱动器",
"options": {
"any": "任意",
"auto": "自动",
"nlaExtended": "扩展 NLA",
"color8": "8 位",
"color16": "16 位",
"color24": "24 位",
"color32": "32 位",
"displayUpdate": "显示更新",
"reconnect": "重新连接",
"preserve": "保留原样",
"unix": "Unix(LF 换行)",
"windows": "Windows(CRLF 换行)",
"local": "本地",
"remote": "远程",
"failsafe": "兼容模式",
"blackOnWhite": "白底黑字",
"whiteOnBlack": "黑底白字",
"grayOnBlack": "黑底灰字",
"greenOnBlack": "黑底绿字"
}
}, },
"remoteDesktop": "远程桌面", "remoteDesktop": "远程桌面",
"tabRdp": "RDP", "tabRdp": "RDP",
@@ -221,28 +242,28 @@
"virtualNetwork": "虚拟网络", "virtualNetwork": "虚拟网络",
"unencryptedShell": "未加密 Shell", "unencryptedShell": "未加密 Shell",
"remoteDesktopNotes": "笔记", "remoteDesktopNotes": "笔记",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.", "userDefaultsNote": "未修改的设置将使用主机默认值。",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.", "connectionOriginNote": "在桌面应用中,RDP、VNC 和 Telnet 默认通过远程服务器连接。若主机设置为“此设备”,则需要本机可访问的 guacd。可在远程桌面设置中配置,或为每台主机单独设置 guacd 代理。",
"docsLink": "查看文档", "docsLink": "查看文档",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy." "connectionOriginGuacamoleNote": "RDP、VNC 和 Telnet 默认通过远程服务器连接。选择“此设备”时,需要本机可访问的 guacd。可在 guacd URL 设置中配置,或为每台主机单独设置 guacd 代理。"
}, },
"onboarding": { "onboarding": {
"feature_desktop": "Remote desktop", "feature_desktop": "远程桌面",
"feature_desktop_desc": "像 SSH 一样,在标签页中直接打开 RDP、VNC 和 Telnet 会话。" "feature_desktop_desc": "像 SSH 一样,在标签页中直接打开 RDP、VNC 和 Telnet 会话。"
}, },
"settings": { "settings": {
"user": { "user": {
"colorDepth": { "colorDepth": {
"label": "颜色深度", "label": "颜色深度",
"description": "Used for RDP hosts that leave it on their default." "description": "用于使用默认值的 RDP 主机。"
}, },
"resizeMethod": { "resizeMethod": {
"label": "调整大小方法", "label": "调整大小方法",
"description": "How the remote screen follows the window size." "description": "远程屏幕随窗口大小变化的方式。"
}, },
"resizeDisplayUpdate": "Display Update", "resizeDisplayUpdate": "显示更新方式",
"resizeReconnect": "重新连接", "resizeReconnect": "重新连接",
"inherit": "Inherit", "inherit": "继承",
"on": "开启", "on": "开启",
"off": "关闭", "off": "关闭",
"forceLossless": { "forceLossless": {
@@ -281,19 +302,19 @@
"label": "禁用粘贴", "label": "禁用粘贴",
"description": "禁止粘贴文本到远程会话" "description": "禁止粘贴文本到远程会话"
}, },
"group": "Remote Desktop defaults", "group": "远程桌面默认值",
"colorDepth16": "16-bit", "colorDepth16": "16 位",
"colorDepth24": "24-bit", "colorDepth24": "24 位",
"colorDepth32": "32-bit" "colorDepth32": "32 位"
}, },
"admin": { "admin": {
"enabled": { "enabled": {
"label": "Enable Remote Desktop", "label": "启用远程桌面",
"description": "Allow RDP, VNC and Telnet connections through guacd." "description": "允许通过 guacd 建立 RDP、VNC 和 Telnet 连接。"
}, },
"guacdUrl": { "guacdUrl": {
"label": "guacd URL", "label": "guacd 地址",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.", "description": "guacd 监听地址,格式为 host:port。GUACD_URL、GUACD_HOST 和 GUACD_PORT 环境变量会覆盖此设置。",
"placeholder": "guacd:4822" "placeholder": "guacd:4822"
} }
}, },
@@ -308,43 +329,46 @@
"label": "Telnet" "label": "Telnet"
}, },
"rdpPort": { "rdpPort": {
"label": "RDP port" "label": "RDP 端口"
}, },
"vncPort": { "vncPort": {
"label": "VNC port" "label": "VNC 端口"
}, },
"telnetPort": { "telnetPort": {
"label": "Telnet port" "label": "Telnet 端口"
}, },
"rdpSecurity": { "rdpSecurity": {
"label": "RDP security mode" "label": "RDP 安全模式"
}, },
"rdpIgnoreCert": { "rdpIgnoreCert": {
"label": "Ignore certificate errors" "label": "忽略证书错误"
}, },
"guacamoleConfig": { "guacamoleConfig": {
"label": "guacd settings" "label": "guacd 设置"
}, },
"enableToolbar": { "enableToolbar": {
"label": "Remote desktop toolbar", "label": "远程桌面工具栏",
"description": "Show the toolbar in RDP, VNC and Telnet sessions." "description": "在 RDP、VNC 和 Telnet 会话中显示工具栏。"
}, },
"displayGroup": "Display", "displayGroup": "显示",
"displayDefaultsTab": "远程桌面" "displayDefaultsTab": "远程桌面"
} }
}, },
"palette": { "palette": {
"connectRdp": "Connect with RDP", "connectRdp": "通过 RDP 连接",
"connectVnc": "Connect with VNC", "connectVnc": "通过 VNC 连接",
"connectTelnet": "Connect with Telnet" "connectTelnet": "通过 Telnet 连接"
}, },
"permissions": { "permissions": {
"sessions": { "sessions": {
"title": "Share remote desktop sessions", "title": "共享远程桌面会话",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others." "description": "允许通过会话共享和协作房间向他人展示此用户的 RDP、VNC 和 Telnet 会话。"
} }
}, },
"errors": { "errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type." "remoteServerRequired": "需要远程服务器。请在设置中连接远程服务器以使用此连接类型。"
},
"plugin": {
"name": "远程桌面"
} }
} }
@@ -248,9 +248,11 @@ export function HostEditorRdpTab({
value={form.security ?? "any"} value={form.security ?? "any"}
onChange={(e) => setField("security", e.target.value)} onChange={(e) => setField("security", e.target.value)}
> >
<option value="any">Any</option> <option value="any">{t("hosts.guac.options.any")}</option>
<option value="nla">NLA</option> <option value="nla">NLA</option>
<option value="nla-ext">NLA Extended</option> <option value="nla-ext">
{t("hosts.guac.options.nlaExtended")}
</option>
<option value="tls">TLS</option> <option value="tls">TLS</option>
<option value="vmconnect">VMConnect</option> <option value="vmconnect">VMConnect</option>
<option value="rdp">RDP</option> <option value="rdp">RDP</option>
@@ -299,11 +301,11 @@ export function HostEditorRdpTab({
value={form.guacamoleConfig["color-depth"] ?? "auto"} value={form.guacamoleConfig["color-depth"] ?? "auto"}
onChange={(e) => setGuacField("color-depth", e.target.value)} onChange={(e) => setGuacField("color-depth", e.target.value)}
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option value="8">8-bit</option> <option value="8">{t("hosts.guac.options.color8")}</option>
<option value="16">16-bit</option> <option value="16">{t("hosts.guac.options.color16")}</option>
<option value="24">24-bit</option> <option value="24">{t("hosts.guac.options.color24")}</option>
<option value="32">32-bit</option> <option value="32">{t("hosts.guac.options.color32")}</option>
</Select2> </Select2>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
@@ -353,9 +355,13 @@ export function HostEditorRdpTab({
value={form.guacamoleConfig["resize-method"] ?? "auto"} value={form.guacamoleConfig["resize-method"] ?? "auto"}
onChange={(e) => setGuacField("resize-method", e.target.value)} onChange={(e) => setGuacField("resize-method", e.target.value)}
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option value="display-update">Display Update</option> <option value="display-update">
<option value="reconnect">Reconnect</option> {t("hosts.guac.options.displayUpdate")}
</option>
<option value="reconnect">
{t("hosts.guac.options.reconnect")}
</option>
</Select2> </Select2>
</div> </div>
<SettingRow <SettingRow
@@ -621,7 +627,7 @@ export function HostEditorRdpTab({
value={form.guacamoleConfig["server-layout"] ?? "auto"} value={form.guacamoleConfig["server-layout"] ?? "auto"}
onChange={(e) => setGuacField("server-layout", e.target.value)} onChange={(e) => setGuacField("server-layout", e.target.value)}
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option>en-us-qwerty</option> <option>en-us-qwerty</option>
<option>en-gb-qwerty</option> <option>en-gb-qwerty</option>
<option>de-de-qwertz</option> <option>de-de-qwertz</option>
@@ -633,7 +639,7 @@ export function HostEditorRdpTab({
<option>ja-jp-qwerty</option> <option>ja-jp-qwerty</option>
<option>pt-br-qwerty</option> <option>pt-br-qwerty</option>
<option>es-es-qwerty</option> <option>es-es-qwerty</option>
<option>failsafe</option> <option>{t("hosts.guac.options.failsafe")}</option>
</Select2> </Select2>
</div> </div>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
@@ -771,10 +777,12 @@ export function HostEditorRdpTab({
setGuacField("normalize-clipboard", e.target.value) setGuacField("normalize-clipboard", e.target.value)
} }
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option value="preserve">Preserve</option> <option value="preserve">
<option value="unix">Unix (LF)</option> {t("hosts.guac.options.preserve")}
<option value="windows">Windows (CRLF)</option> </option>
<option value="unix">{t("hosts.guac.options.unix")}</option>
<option value="windows">{t("hosts.guac.options.windows")}</option>
</Select2> </Select2>
</div> </div>
<SettingRow <SettingRow
@@ -1101,11 +1109,11 @@ export function HostEditorVncTab({
value={form.guacamoleConfig["color-depth"] ?? "auto"} value={form.guacamoleConfig["color-depth"] ?? "auto"}
onChange={(e) => setGuacField("color-depth", e.target.value)} onChange={(e) => setGuacField("color-depth", e.target.value)}
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option value="8">8-bit</option> <option value="8">{t("hosts.guac.options.color8")}</option>
<option value="16">16-bit</option> <option value="16">{t("hosts.guac.options.color16")}</option>
<option value="24">24-bit</option> <option value="24">{t("hosts.guac.options.color24")}</option>
<option value="32">32-bit</option> <option value="32">{t("hosts.guac.options.color32")}</option>
</Select2> </Select2>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
@@ -1141,9 +1149,13 @@ export function HostEditorVncTab({
value={form.guacamoleConfig["resize-method"] ?? "auto"} value={form.guacamoleConfig["resize-method"] ?? "auto"}
onChange={(e) => setGuacField("resize-method", e.target.value)} onChange={(e) => setGuacField("resize-method", e.target.value)}
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option value="display-update">Display Update</option> <option value="display-update">
<option value="reconnect">Reconnect</option> {t("hosts.guac.options.displayUpdate")}
</option>
<option value="reconnect">
{t("hosts.guac.options.reconnect")}
</option>
</Select2> </Select2>
</div> </div>
<SettingRow <SettingRow
@@ -1189,9 +1201,9 @@ export function HostEditorVncTab({
value={form.guacamoleConfig["cursor"] ?? "auto"} value={form.guacamoleConfig["cursor"] ?? "auto"}
onChange={(e) => setGuacField("cursor", e.target.value)} onChange={(e) => setGuacField("cursor", e.target.value)}
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option value="local">Local</option> <option value="local">{t("hosts.guac.options.local")}</option>
<option value="remote">Remote</option> <option value="remote">{t("hosts.guac.options.remote")}</option>
</Select2> </Select2>
</div> </div>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
@@ -1203,7 +1215,7 @@ export function HostEditorVncTab({
value={form.guacamoleConfig["server-layout"] ?? "auto"} value={form.guacamoleConfig["server-layout"] ?? "auto"}
onChange={(e) => setGuacField("server-layout", e.target.value)} onChange={(e) => setGuacField("server-layout", e.target.value)}
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option>en-us-qwerty</option> <option>en-us-qwerty</option>
<option>en-gb-qwerty</option> <option>en-gb-qwerty</option>
<option>de-de-qwertz</option> <option>de-de-qwertz</option>
@@ -1215,7 +1227,7 @@ export function HostEditorVncTab({
<option>ja-jp-qwerty</option> <option>ja-jp-qwerty</option>
<option>pt-br-qwerty</option> <option>pt-br-qwerty</option>
<option>es-es-qwerty</option> <option>es-es-qwerty</option>
<option>failsafe</option> <option>{t("hosts.guac.options.failsafe")}</option>
</Select2> </Select2>
</div> </div>
<SettingRow <SettingRow
@@ -1255,10 +1267,12 @@ export function HostEditorVncTab({
setGuacField("normalize-clipboard", e.target.value) setGuacField("normalize-clipboard", e.target.value)
} }
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option value="preserve">Preserve</option> <option value="preserve">
<option value="unix">Unix (LF)</option> {t("hosts.guac.options.preserve")}
<option value="windows">Windows (CRLF)</option> </option>
<option value="unix">{t("hosts.guac.options.unix")}</option>
<option value="windows">{t("hosts.guac.options.windows")}</option>
</Select2> </Select2>
</div> </div>
<SettingRow <SettingRow
@@ -1614,7 +1628,7 @@ export function HostEditorTelnetTab({
value={form.guacamoleConfig["terminal-type"] ?? "auto"} value={form.guacamoleConfig["terminal-type"] ?? "auto"}
onChange={(e) => setGuacField("terminal-type", e.target.value)} onChange={(e) => setGuacField("terminal-type", e.target.value)}
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option value="xterm">xterm</option> <option value="xterm">xterm</option>
<option value="xterm-256color">xterm-256color</option> <option value="xterm-256color">xterm-256color</option>
<option value="vt100">VT100</option> <option value="vt100">VT100</option>
@@ -1652,11 +1666,19 @@ export function HostEditorTelnetTab({
value={form.guacamoleConfig["color-scheme"] ?? "auto"} value={form.guacamoleConfig["color-scheme"] ?? "auto"}
onChange={(e) => setGuacField("color-scheme", e.target.value)} onChange={(e) => setGuacField("color-scheme", e.target.value)}
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option value="black-white">Black on White</option> <option value="black-white">
<option value="white-black">White on Black</option> {t("hosts.guac.options.blackOnWhite")}
<option value="gray-black">Gray on Black</option> </option>
<option value="green-black">Green on Black</option> <option value="white-black">
{t("hosts.guac.options.whiteOnBlack")}
</option>
<option value="gray-black">
{t("hosts.guac.options.grayOnBlack")}
</option>
<option value="green-black">
{t("hosts.guac.options.greenOnBlack")}
</option>
</Select2> </Select2>
</div> </div>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
@@ -1668,7 +1690,7 @@ export function HostEditorTelnetTab({
value={form.guacamoleConfig["backspace"] ?? "auto"} value={form.guacamoleConfig["backspace"] ?? "auto"}
onChange={(e) => setGuacField("backspace", e.target.value)} onChange={(e) => setGuacField("backspace", e.target.value)}
> >
<option value="auto">Auto</option> <option value="auto">{t("hosts.guac.options.auto")}</option>
<option value="127">DEL (127)</option> <option value="127">DEL (127)</option>
<option value="8">BS (8)</option> <option value="8">BS (8)</option>
</Select2> </Select2>
+3
View File
@@ -23,5 +23,8 @@
"description": "Private or loopback hosts a secret source may reach, one per line. A self-hosted 1Password Connect server usually needs its own address listed here." "description": "Private or loopback hosts a secret source may reach, one per line. A self-hosted 1Password Connect server usually needs its own address listed here."
} }
} }
},
"plugin": {
"name": "Secret Sources"
} }
} }
@@ -1,27 +1,30 @@
{ {
"hint": "You can paste a 1Password reference (op://vault/item/field) instead of the secret. It is fetched from your secret source when connecting.", "hint": "可粘贴 1Password 引用(op://vault/item/field)来代替机密值,连接时会从机密来源读取。",
"manage": "Manage secret sources", "manage": "管理机密来源",
"title": "Secret sources", "title": "机密来源",
"description": "External password managers Termix reads secrets from at connect time. Currently 1Password Connect (self-hosted). The access token is stored encrypted with your data key.", "description": "Termix 在连接时从外部密码管理器读取机密。目前支持自托管的 1Password Connect,访问令牌使用你的数据密钥加密保存。",
"new": "New source", "new": "新建来源",
"nameLabel": "Friendly name", "nameLabel": "显示名称",
"urlLabel": "Connect server URL", "urlLabel": "Connect 服务器 URL",
"tokenLabel": "Connect access token", "tokenLabel": "Connect 访问令牌",
"tokenKeep": "Leave empty to keep the current token", "tokenKeep": "留空以保留当前令牌",
"sharedLabel": "Share with all users (admins only, resolves while you are signed in)", "sharedLabel": "与所有用户共享(仅管理员可设置,仅在你登录期间解析)",
"shared": "已共享", "shared": "已共享",
"test": "测试", "test": "测试",
"testOk": "Connected, {{count}} vault(s) visible", "testOk": "已连接,可见 {{count}} 个保管库",
"testFailed": "Connection test failed", "testFailed": "连接测试失败",
"required": "Name, server URL and token are required", "required": "名称、服务器 URL 和令牌为必填项",
"saved": "Secret source saved", "saved": "机密来源已保存",
"deleted": "Secret source deleted", "deleted": "机密来源已删除",
"settings": { "settings": {
"admin": { "admin": {
"privateEndpoints": { "privateEndpoints": {
"label": "Private endpoint allowlist", "label": "私有端点允许列表",
"description": "Private or loopback hosts a secret source may reach, one per line. A self-hosted 1Password Connect server usually needs its own address listed here." "description": "机密来源允许访问的内网或回环主机,每行一个。自托管的 1Password Connect 服务器通常需要在此列出其地址。"
} }
} }
},
"plugin": {
"name": "密钥来源"
} }
} }
@@ -56,5 +56,8 @@
"speed": "Playback speed", "speed": "Playback speed",
"play": "Play recording", "play": "Play recording",
"pause": "Pause recording" "pause": "Pause recording"
},
"plugin": {
"name": "Session Recording"
} }
} }
@@ -8,26 +8,26 @@
}, },
"permissions": { "permissions": {
"view": { "view": {
"title": "View session recordings", "title": "查看会话录像",
"description": "See and play back your own session recordings." "description": "查看和回放你自己的会话录像。"
} }
}, },
"settings": { "settings": {
"admin": { "admin": {
"retentionDays": { "retentionDays": {
"label": "Retention (days)", "label": "保留天数",
"description": "How long a session recording is kept before it is deleted automatically." "description": "会话录像在自动删除前的保留时长。"
} }
}, },
"host": { "host": {
"enable": { "enable": {
"label": "Enable session recording", "label": "启用会话录制",
"description": "Record terminal output for this host so you can play it back or download it later." "description": "记录此主机的终端输出,以便稍后回放或下载。"
} }
} }
}, },
"terminalStatus": { "terminalStatus": {
"recording": "Recording" "recording": "录制中"
}, },
"sessionLogs": { "sessionLogs": {
"title": "会话日志", "title": "会话日志",
@@ -36,7 +36,7 @@
"duration": "持续时间", "duration": "持续时间",
"viewLog": "查看日志", "viewLog": "查看日志",
"downloadLog": "下载", "downloadLog": "下载",
"downloadAsText": "Download as text", "downloadAsText": "下载为文本",
"deleteLog": "删除", "deleteLog": "删除",
"confirmDelete": "删除此会话日志?", "confirmDelete": "删除此会话日志?",
"confirmDeleteDesc": "此操作无法撤消。", "confirmDeleteDesc": "此操作无法撤消。",
@@ -47,14 +47,17 @@
"filterByHost": "按主机筛选...", "filterByHost": "按主机筛选...",
"back": "返回", "back": "返回",
"empty": "(空)", "empty": "(空)",
"noResults": "No results for \"{{query}}\"", "noResults": "没有与“{{query}}”匹配的结果",
"hostFallback": "Host {{id}}", "hostFallback": "主机 {{id}}",
"sessionFallback": "Session {{id}}" "sessionFallback": "会话 {{id}}"
}, },
"player": { "player": {
"timeline": "Recording timeline", "timeline": "录像时间轴",
"speed": "Playback speed", "speed": "播放速度",
"play": "Play recording", "play": "播放录像",
"pause": "Pause recording" "pause": "暂停播放"
},
"plugin": {
"name": "会话录制"
} }
} }
+3
View File
@@ -182,5 +182,8 @@
"sharedBy": "Shared by {{username}}", "sharedBy": "Shared by {{username}}",
"join": "Join", "join": "Join",
"sharedSessionLabel": "{{hostName}} (shared)" "sharedSessionLabel": "{{hostName}} (shared)"
},
"plugin": {
"name": "Session Sharing"
} }
} }
@@ -1,41 +1,41 @@
{ {
"sessionSharing": { "sessionSharing": {
"guestView": { "guestView": {
"loading": "Connecting to shared session...", "loading": "正在连接共享会话……",
"linkInvalid": "此共享链接无效、已过期或已被撤销。", "linkInvalid": "此共享链接无效、已过期或已被撤销。",
"rateLimited": "Too many attempts, please try again shortly", "rateLimited": "尝试次数过多,请稍后重试",
"sessionEnded": "此会话已结束", "sessionEnded": "此会话已结束",
"readOnlyBadge": "View only", "readOnlyBadge": "仅查看",
"ownerLabel": "Host owner" "ownerLabel": "主机所有者"
}, },
"modalTitle": "共享会话", "modalTitle": "共享会话",
"shareButton": "共享", "shareButton": "共享",
"notReadyToShare": "Session is not ready to share yet", "notReadyToShare": "会话尚未就绪,暂时无法共享",
"modeTab": { "modeTab": {
"link": "链接", "link": "链接",
"user": "用户" "user": "用户"
}, },
"linkModeDescription": "Anyone with this link can join, no account required.", "linkModeDescription": "任何拥有此链接的人均可加入,无需账户。",
"userModeDescription": "将主机共享给已拥有此主机访问权限的特定用户。如果他们还没有访问权限,请先将主机共享给他们,或者使用链接代替。共享后,会话将显示在他们的“连接”标签页中。", "userModeDescription": "将主机共享给已拥有此主机访问权限的特定用户。如果他们还没有访问权限,请先将主机共享给他们,或者使用链接代替。共享后,会话将显示在他们的“连接”标签页中。",
"permissionLevel": { "permissionLevel": {
"label": "权限级别", "label": "权限级别",
"readOnly": "只读", "readOnly": "只读",
"readOnlyDescription": "Can watch the session live but cannot type or interact.", "readOnlyDescription": "可以实时观看会话,但无法输入或交互。",
"readWrite": "Read-write", "readWrite": "可读写",
"readWriteDescription": "Can type and interact with the session just like the owner." "readWriteDescription": "可以像所有者一样输入并与会话交互。"
}, },
"expiryLabel": "Link expiry", "expiryLabel": "链接有效期",
"createLinkButton": "Create link", "createLinkButton": "创建链接",
"createShareButton": "与用户共享", "createShareButton": "与用户共享",
"searchUsersPlaceholder": "搜索用户......", "searchUsersPlaceholder": "搜索用户......",
"noUsersFound": "No users found", "noUsersFound": "未找到用户",
"linkCreated": "已创建共享链接", "linkCreated": "已创建共享链接",
"linkNeedsServer": "Share links only work for sessions on a Termix server. Connect this host through your linked server to share it with a link.", "linkNeedsServer": "共享链接仅适用于 Termix 服务器上的会话。请通过已关联的服务器连接此主机,再通过链接共享。",
"linkCopied": "Link copied to clipboard", "linkCopied": "链接已复制到剪贴板",
"copyLink": "Copy link", "copyLink": "复制链接",
"shareCreated": "会话已共享。它将出现在对方的“连接”标签页中。", "shareCreated": "会话已共享。它将出现在对方的“连接”标签页中。",
"shareFailed": "Failed to create share", "shareFailed": "创建共享失败",
"userLacksHostAccess": "That user does not have access to this host yet. Share the host with them first, or use a link instead.", "userLacksHostAccess": "该用户尚无此主机的访问权限,请先将主机共享给对方,或改用链接。",
"activeShares": "有效共享", "activeShares": "有效共享",
"noActiveShares": "此会话暂无活跃共享", "noActiveShares": "此会话暂无活跃共享",
"revoke": "吊销", "revoke": "吊销",
@@ -49,120 +49,120 @@
"linkShareBadge": "链接", "linkShareBadge": "链接",
"userShareBadge": "用户:{{username}}", "userShareBadge": "用户:{{username}}",
"loadSharesFailed": "无法加载共享", "loadSharesFailed": "无法加载共享",
"remoteDisplayUnavailable": "Remote desktop is not available right now.", "remoteDisplayUnavailable": "远程桌面当前不可用。",
"sharedWithMe": "Sessions shared with me", "sharedWithMe": "共享给我的会话",
"customHoursPlaceholder": "过期前的小时数" "customHoursPlaceholder": "过期前的小时数"
}, },
"collab": { "collab": {
"createRoom": "New room", "createRoom": "新建房间",
"roomName": "Room name", "roomName": "房间名称",
"persistentRoom": "Persistent room", "persistentRoom": "持久房间",
"persistentRoomHint": "Persistent rooms stay listed after the meeting ends and can be reused.", "persistentRoomHint": "持久房间在协作结束后仍保留在列表中,可再次使用。",
"noRooms": "No rooms yet. Create one to start a meeting.", "noRooms": "暂无房间,创建房间即可开始协作。",
"openRoom": "打开", "openRoom": "打开",
"endRoom": "End meeting", "endRoom": "结束协作",
"leaveRoom": "Leave", "leaveRoom": "离开",
"deleteMember": "删除", "deleteMember": "删除",
"removeMember": "移除{{name}}", "removeMember": "移除{{name}}",
"removeMemberTitle": "Remove this member?", "removeMemberTitle": "移除此成员?",
"removeMemberDescription": "{{name}} will lose access to this room immediately. Any active SSH viewer will be disconnected.", "removeMemberDescription": "{{name}} 将立即失去此房间的访问权限,所有正在使用的 SSH 观看连接都会断开。",
"invite": "Invite", "invite": "邀请",
"inviteTitle": "Invite members", "inviteTitle": "邀请成员",
"members": "Members", "members": "成员",
"membersPanel": "Room members and control requests", "membersPanel": "房间成员与控制请求",
"membersWithCount": "Members ({{count}})", "membersWithCount": "成员({{count}})",
"guestsWithCount": "Guests ({{count}})", "guestsWithCount": "访客({{count}})",
"removeGuests": "Remove guests", "removeGuests": "移除访客",
"anonymousGuest": "Guest", "anonymousGuest": "访客",
"online": "在线", "online": "在线",
"offline": "离线", "offline": "离线",
"you": "(you)", "you": "(你)",
"hostBadge": "主机", "hostBadge": "主机",
"presenterBadge": "Presenting", "presenterBadge": "正在演示",
"present": "Present", "present": "演示",
"presentTitle": "Take the stage", "presentTitle": "开始演示",
"presentHost": "主机", "presentHost": "主机",
"searchHosts": "搜索主机", "searchHosts": "搜索主机",
"noHostsFound": "No available hosts found.", "noHostsFound": "未找到可用主机。",
"presentProtocol": "协议", "presentProtocol": "协议",
"stopPresenting": "Stop presenting", "stopPresenting": "停止演示",
"takeOver": "Take over", "takeOver": "接管演示",
"takeOverConfirmTitle": "Replace the current presentation?", "takeOverConfirmTitle": "替换当前演示?",
"takeOverDescription": "{{name}} is presenting now. Taking over will disconnect everyone from that stage.", "takeOverDescription": "{{name}} 正在演示。接管后,所有人都会断开与当前演示的连接。",
"emptyStage": "Nobody is presenting. Take the stage to share a session.", "emptyStage": "当前无人演示,开始演示即可共享会话。",
"stageLoading": "Connecting to the stage...", "stageLoading": "正在连接演示……",
"stageEnded": "The presentation ended", "stageEnded": "演示已结束",
"roomEnded": "This meeting has ended", "roomEnded": "此协作会话已结束",
"presenterLabel": "{{name}} is presenting", "presenterLabel": "{{name}} 正在演示",
"guestLink": "Guest link", "guestLink": "访客链接",
"guestLinkOn": "Guest link is on. Anyone with the link can watch the stage.", "guestLinkOn": "访客链接已启用,任何拥有链接的人都可观看演示。",
"guestLinkOff": "Guest link is off.", "guestLinkOff": "访客链接已关闭。",
"copyLink": "Copy link", "copyLink": "复制链接",
"rotateLink": "Rotate link", "rotateLink": "更换链接",
"disableLink": "Disable link", "disableLink": "禁用链接",
"rotateLinkConfirmTitle": "Rotate the guest link?", "rotateLinkConfirmTitle": "更换访客链接?",
"rotateLinkDescription": "The current link will stop working immediately. Connected SSH guests will be disconnected.", "rotateLinkDescription": "当前链接将立即失效,已连接的 SSH 访客会被断开。",
"disableLinkConfirmTitle": "Disable the guest link?", "disableLinkConfirmTitle": "禁用访客链接?",
"disableLinkDescription": "Anonymous SSH viewers will be disconnected immediately and the current link will stop working.", "disableLinkDescription": "匿名 SSH 观看者将立即断开连接,当前链接将失效。",
"linkCopied": "Link copied", "linkCopied": "链接已复制",
"roles": "角色", "roles": "角色",
"users": "用户", "users": "用户",
"searchUsers": "Search users", "searchUsers": "搜索用户",
"endConfirmTitle": "End this meeting?", "endConfirmTitle": "结束此协作会话?",
"endDescription": "{{name}} will end for everyone and cannot be reopened.", "endDescription": "{{name}} 将对所有人结束,且无法重新打开。",
"endPersistentDescription": "The active presentation in {{name}} will stop. The room remains available for reuse.", "endPersistentDescription": "{{name}} 中正在进行的演示将停止,房间会保留以便再次使用。",
"deleteRoom": "Delete room", "deleteRoom": "删除房间",
"deleteConfirmTitle": "Delete this room?", "deleteConfirmTitle": "删除此房间?",
"deleteDescription": "{{name}} will be permanently deleted for everyone and cannot be undone.", "deleteDescription": "{{name}} 将对所有人永久删除,且无法撤销。",
"invitedTo": "You were invited to \"{{name}}\"", "invitedTo": "你被邀请加入“{{name}}”",
"guest": { "guest": {
"title": "Meeting", "title": "协作会话",
"waiting": "Waiting for the presenter...", "waiting": "正在等待演示者……",
"linkInvalid": "This guest link is invalid or the meeting has ended" "linkInvalid": "此访客链接无效,或协作会话已结束"
}, },
"reopenFromPanel": "This meeting tab expired. Reopen the room from the Meetings panel.", "reopenFromPanel": "此协作标签页已过期,请从“协作会话”面板重新打开房间。",
"retry": "重试", "retry": "重试",
"requestControl": "Request control", "requestControl": "请求控制",
"cancelControlRequest": "Cancel request", "cancelControlRequest": "取消请求",
"controlQueue": "Control requests", "controlQueue": "控制请求",
"noControlRequests": "No pending requests.", "noControlRequests": "没有待处理请求。",
"grantControlTo": "Give control to {{name}}", "grantControlTo": "将控制权交给 {{name}}",
"dismissControlRequest": "Dismiss {{name}}'s control request", "dismissControlRequest": "忽略 {{name}} 的控制请求",
"releaseControl": "Release control", "releaseControl": "释放控制权",
"grantControl": "Give control", "grantControl": "授予控制权",
"revokeControl": "Take back control", "revokeControl": "收回控制权",
"controlRequestedBy": "{{name}} asked for control", "controlRequestedBy": "{{name}} 请求控制权",
"grant": "Grant", "grant": "授予",
"controlBadge": "In control", "controlBadge": "正在控制",
"youArePresenting": "You are presenting in another tab. Stop presenting to hand the stage over.", "youArePresenting": "你正在另一个标签页中演示,请停止演示后再交接。",
"created": "Room created", "created": "房间已创建",
"invited": "Members invited", "invited": "已邀请成员",
"roomTab": "Meeting", "roomTab": "协作会话",
"serverChanged": "The meeting server changed. Reopen this meeting from the Meetings panel.", "serverChanged": "协作服务器已更改,请从“协作会话”面板重新打开此会话。",
"hostNotSynced": "Sync this host to the meeting server before presenting.", "hostNotSynced": "请先将此主机同步到协作服务器,再开始演示。",
"linkRequiresServer": "Link this desktop to a Termix server and create the meeting there to share a web link." "linkRequiresServer": "将此桌面客户端关联到 Termix 服务器,并在服务器上创建协作会话,才能共享网页链接。"
}, },
"nav": { "nav": {
"collab": "Meetings" "collab": "协作会话"
}, },
"settings": { "settings": {
"admin": { "admin": {
"globallyEnabled": { "globallyEnabled": {
"label": "Allow Session Sharing", "label": "允许会话共享",
"description": "Allow live terminal, RDP, VNC, and Telnet sessions to be shared instance-wide. Overrides every per-host sharing toggle when disabled." "description": "在整个实例中允许共享实时终端、RDP、VNC 和 Telnet 会话。关闭后会覆盖所有主机的共享开关。"
} }
}, },
"host": { "host": {
"allowSessionSharing": { "allowSessionSharing": {
"label": "Allow Session Sharing", "label": "允许会话共享",
"description": "允许通过链接或其他方式共享此主机上的实时会话。" "description": "允许通过链接或其他方式共享此主机上的实时会话。"
} }
} }
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Share sessions and join rooms", "title": "共享会话并加入房间",
"description": "Share live sessions by link or with other users, and create and join collaboration rooms." "description": "通过链接或直接向其他用户共享实时会话,以及创建和加入协作房间。"
} }
}, },
"hosts": { "hosts": {
@@ -180,7 +180,10 @@
"connections": { "connections": {
"sectionSharedWithMe": "与我共享", "sectionSharedWithMe": "与我共享",
"sharedBy": "由 {{username}} 共享", "sharedBy": "由 {{username}} 共享",
"join": "Join", "join": "加入",
"sharedSessionLabel": "{{hostName}}(共享)" "sharedSessionLabel": "{{hostName}}(共享)"
},
"plugin": {
"name": "会话共享"
} }
} }
+4 -1
View File
@@ -231,5 +231,8 @@
"targetTerminals": "Run in", "targetTerminals": "Run in",
"targetTerminalsHint": "optional, else the active tab", "targetTerminalsHint": "optional, else the active tab",
"selectAll": "All", "selectAll": "All",
"selectNone": "None" "selectNone": "None",
"plugin": {
"name": "Snippets"
}
} }
+93 -90
View File
@@ -8,50 +8,50 @@
}, },
"permissions": { "permissions": {
"view": { "view": {
"title": "View snippets", "title": "查看代码片段",
"description": "See your own snippets, notes and shared snippets." "description": "查看你自己的代码片段、笔记及共享的代码片段。"
}, },
"create": { "create": {
"title": "Create snippets", "title": "创建代码片段",
"description": "Create new snippets, notes and folders." "description": "创建新的代码片段、笔记和文件夹。"
}, },
"edit": { "edit": {
"title": "Edit snippets", "title": "编辑代码片段",
"description": "Rename or change existing snippets and folders." "description": "重命名或修改现有代码片段和文件夹。"
}, },
"delete": { "delete": {
"title": "Delete snippets", "title": "删除代码片段",
"description": "Delete snippets and folders." "description": "删除代码片段和文件夹。"
}, },
"share": { "share": {
"title": "Share snippets", "title": "共享代码片段",
"description": "Share snippets and folders with other users or roles." "description": "与其他用户或角色共享代码片段和文件夹。"
} }
}, },
"settings": { "settings": {
"foldersCollapsed": { "foldersCollapsed": {
"label": "默认折叠文件夹", "label": "默认折叠文件夹",
"description": "Start each snippet folder collapsed when you open the Snippets panel." "description": "打开代码片段面板时,默认折叠所有代码片段文件夹。"
}, },
"showCommands": { "showCommands": {
"label": "Show commands", "label": "显示命令",
"description": "Show the first line of each snippet's command under its name." "description": "在代码片段名称下方显示其命令的第一行。"
}, },
"confirmExecution": { "confirmExecution": {
"label": "Confirm before running", "label": "执行前确认",
"description": "Ask before a command snippet runs in a terminal." "description": "在终端中执行命令类代码片段前询问。"
} }
}, },
"cancel": "取消", "cancel": "取消",
"loading": "加载中...", "loading": "加载中...",
"searchPlaceholder": "搜索代码片段", "searchPlaceholder": "搜索代码片段",
"newFolder": "新建文件夹", "newFolder": "新建文件夹",
"newSnippet": "New snippet", "newSnippet": "新建代码片段",
"noFolder": "无文件夹", "noFolder": "无文件夹",
"noSnippetsInFolder": "此文件夹中没有代码片段", "noSnippetsInFolder": "此文件夹中没有代码片段",
"noTerminalTabsOpen": "No terminal tab is open", "noTerminalTabsOpen": "没有打开的终端标签页",
"run": "运行", "run": "运行",
"pasteToTerminal": "Paste to terminal", "pasteToTerminal": "粘贴到终端",
"copySuccess": "已复制到剪贴板", "copySuccess": "已复制到剪贴板",
"importExport": "导入 / 导出", "importExport": "导入 / 导出",
"importFailed": "导入代码片段失败", "importFailed": "导入代码片段失败",
@@ -62,37 +62,37 @@
"folderLabel": "文件夹", "folderLabel": "文件夹",
"typeLabel": "类型", "typeLabel": "类型",
"typeCommand": "命令", "typeCommand": "命令",
"typeNote": "Note", "typeNote": "笔记",
"commandLabel": "命令", "commandLabel": "命令",
"commandPlaceholder": "Command to run, e.g. systemctl status $HOST", "commandPlaceholder": "要执行的命令,例如 systemctl status $HOST",
"noteLabel": "Note", "noteLabel": "笔记",
"notePlaceholder": "Text to paste into the terminal", "notePlaceholder": "要粘贴到终端的文本",
"createSnippetTitle": "Create snippet", "createSnippetTitle": "创建代码片段",
"createSnippetButton": "Create snippet", "createSnippetButton": "创建代码片段",
"createSuccess": "Snippet created", "createSuccess": "代码片段已创建",
"createFailed": "创建代码片段失败", "createFailed": "创建代码片段失败",
"editSnippetTitle": "Edit snippet", "editSnippetTitle": "编辑代码片段",
"saveSnippetButton": "Save snippet", "saveSnippetButton": "保存代码片段",
"updateSuccess": "Snippet updated", "updateSuccess": "代码片段已更新",
"updateFailed": "更新代码片段失败", "updateFailed": "更新代码片段失败",
"deleteFailed": "删除代码片段失败", "deleteFailed": "删除代码片段失败",
"createFolderTitle": "创建文件夹", "createFolderTitle": "创建文件夹",
"createFolderButton": "创建文件夹", "createFolderButton": "创建文件夹",
"folderCreateSuccess": "Folder created", "folderCreateSuccess": "文件夹已创建",
"folderCreateFailed": "创建文件夹失败", "folderCreateFailed": "创建文件夹失败",
"editFolderTitle": "编辑文件夹", "editFolderTitle": "编辑文件夹",
"saveFolderButton": "保存文件夹", "saveFolderButton": "保存文件夹",
"folderEditSuccess": "Folder updated", "folderEditSuccess": "文件夹已更新",
"folderEditFailed": "更新文件夹失败", "folderEditFailed": "更新文件夹失败",
"folderNameLabel": "文件夹名称", "folderNameLabel": "文件夹名称",
"folderNamePlaceholder": "文件夹名称", "folderNamePlaceholder": "文件夹名称",
"folderIconLabel": "图标", "folderIconLabel": "图标",
"folderDeleteSuccess": "Folder deleted", "folderDeleteSuccess": "文件夹已删除",
"folderDeleteFailed": "删除文件夹失败", "folderDeleteFailed": "删除文件夹失败",
"confirmRunMessage": "运行“{{name}}”?", "confirmRunMessage": "运行“{{name}}”?",
"confirmRunButton": "运行", "confirmRunButton": "运行",
"runSuccess": "Ran \"{{name}}\" on {{count}} terminal(s)", "runSuccess": "已在 {{count}} 个终端中执行“{{name}}”",
"pasteSuccess": "Pasted \"{{name}}\" into {{count}} terminal(s)", "pasteSuccess": "已将“{{name}}”粘贴到 {{count}} 个终端",
"admin": { "admin": {
"tabTitle": "代码片段", "tabTitle": "代码片段",
"count": "{{count}} 个代码片段", "count": "{{count}} 个代码片段",
@@ -110,101 +110,101 @@
"saving": "保存中...", "saving": "保存中...",
"save": "保存" "save": "保存"
}, },
"backToSnippets": "Back to snippets", "backToSnippets": "返回代码片段",
"settingsTitle": "Snippet settings", "settingsTitle": "代码片段设置",
"settingsDisplayTitle": "Display", "settingsDisplayTitle": "显示",
"settingsBehaviorTitle": "交互", "settingsBehaviorTitle": "交互",
"typeCommandHint": "Runs in the active terminal.", "typeCommandHint": "在当前终端中执行。",
"typeNoteHint": "Pastes into the active terminal without running.", "typeNoteHint": "粘贴到当前终端,但不执行。",
"variablesHint": "变量:$HOST、$USER、$PORT、$NAME、${INPUT_1:Label}", "variablesHint": "变量:$HOST、$USER、$PORT、$NAME、${INPUT_1:Label}",
"doubleClickToRun": "Double click to run", "doubleClickToRun": "双击执行",
"sharedBadge": "已共享", "sharedBadge": "已共享",
"copyToClipboard": "Copy to clipboard", "copyToClipboard": "复制到剪贴板",
"copyFailed": "复制到剪贴板失败", "copyFailed": "复制到剪贴板失败",
"moreOptions": "More options", "moreOptions": "更多选项",
"moreActions": "更多操作", "moreActions": "更多操作",
"moveToFolder": "Move to folder", "moveToFolder": "移至文件夹",
"deleteSnippet": "Delete snippet", "deleteSnippet": "删除代码片段",
"deleteSnippetConfirm": "Delete snippet \"{{name}}\"?", "deleteSnippetConfirm": "删除代码片段“{{name}}”?",
"deleteSuccess": "代码片段已删除", "deleteSuccess": "代码片段已删除",
"delete": "删除", "delete": "删除",
"folderActions": "Folder actions", "folderActions": "文件夹操作",
"addSnippetHere": "New snippet here", "addSnippetHere": "在此新建代码片段",
"deleteFolder": "删除文件夹", "deleteFolder": "删除文件夹",
"deleteFolderConfirm": "Delete folder \"{{name}}\"? Its snippets move out of the folder.", "deleteFolderConfirm": "删除文件夹“{{name}}”?其中的代码片段将移出文件夹。",
"createFolderDescription": "Group related snippets together.", "createFolderDescription": "将相关代码片段归入同一组。",
"editFolderDescription": "Change this folder's name, color or icon.", "editFolderDescription": "更改此文件夹的名称、颜色或图标。",
"folderColorLabel": "颜色", "folderColorLabel": "颜色",
"loadFailed": "Failed to load snippets", "loadFailed": "加载代码片段失败",
"importSkipExisting": "导入(跳过现有)", "importSkipExisting": "导入(跳过现有)",
"importOverwrite": "Import (overwrite existing)", "importOverwrite": "导入(覆盖已有内容)",
"importInvalidFile": "That file is not a valid snippets export", "importInvalidFile": "该文件不是有效的代码片段导出文件",
"importSuccess": "Imported {{imported}}, updated {{updated}}, skipped {{skipped}}", "importSuccess": "已导入 {{imported}} 项,更新 {{updated}} 项,跳过 {{skipped}} 项",
"exportSnippets": "Export snippets", "exportSnippets": "导出代码片段",
"exportSuccess": "Snippets exported", "exportSuccess": "代码片段已导出",
"exportFailed": "导出代码片段失败", "exportFailed": "导出代码片段失败",
"expandAll": "展开所有文件夹", "expandAll": "展开所有文件夹",
"collapseAll": "收起所有文件夹", "collapseAll": "收起所有文件夹",
"docsLink": "文档", "docsLink": "文档",
"clearSearch": "Clear search", "clearSearch": "清除搜索",
"noSearchResults": "No snippets match your search", "noSearchResults": "没有符合搜索条件的代码片段",
"emptyTitle": "目前还没有代码片段", "emptyTitle": "目前还没有代码片段",
"emptyDescription": "Save commands you run often, then run them in a terminal with one click.", "emptyDescription": "保存常用命令,即可在终端中一键执行。",
"shareSnippet": "Share snippet", "shareSnippet": "共享代码片段",
"shareFolder": "Share folder", "shareFolder": "共享文件夹",
"shareExpiry": "过期时间", "shareExpiry": "过期时间",
"shareExpiryOptions": { "shareExpiryOptions": {
"h0": "从未", "h0": "从未",
"h1": "In 1 hour", "h1": "1 小时后",
"h24": "In 1 day", "h24": "1 天后",
"h168": "In 7 days", "h168": "7 天后",
"h720": "In 30 days" "h720": "30 天后"
}, },
"shareButton": "共享", "shareButton": "共享",
"shareSuccess": "Shared with {{count}}", "shareSuccess": "已共享给 {{count}} 个对象",
"shareFailed": "共享失败", "shareFailed": "共享失败",
"shareLoadFailed": "Failed to load sharing details", "shareLoadFailed": "加载共享详情失败",
"currentAccess": "当前访问权限", "currentAccess": "当前访问权限",
"notSharedYet": "Not shared with anyone yet", "notSharedYet": "尚未共享给任何人",
"revokeAccess": "吊销", "revokeAccess": "吊销",
"revokeSuccess": "访问权限已撤销", "revokeSuccess": "访问权限已撤销",
"revokeFailed": "撤销访问权限失败", "revokeFailed": "撤销访问权限失败",
"close": "关闭", "close": "关闭",
"targetHostsLabel": "Target hosts", "targetHostsLabel": "目标主机",
"targetHostsHint": "Pick hosts to run this command on directly, without a terminal open. Leave empty to run it in the active terminal.", "targetHostsHint": "选择直接执行此命令的主机,无需打开终端。留空则在当前终端中执行。",
"clearTargetHosts": "清除", "clearTargetHosts": "清除",
"noHostsAvailable": "暂无可用主机", "noHostsAvailable": "暂无可用主机",
"searchHosts": "搜索主机", "searchHosts": "搜索主机",
"runOnTargets": "Run on target hosts", "runOnTargets": "在目标主机上执行",
"executionResultTitle": "Results for \"{{name}}\"", "executionResultTitle": "“{{name}}”的执行结果",
"executionResultDescription": "Output from each target host.", "executionResultDescription": "各目标主机的输出。",
"executionSuccess": "成功", "executionSuccess": "成功",
"executionFailed": "失败", "executionFailed": "失败",
"directRunSuccess": "已在 {{count}} 台主机上运行 \"{{name}}\"", "directRunSuccess": "已在 {{count}} 台主机上运行 \"{{name}}\"",
"directRunPartialFail": "\"{{name}}\" failed on some hosts", "directRunPartialFail": "“{{name}}”在部分主机上执行失败",
"reorderFailed": "重新排序代码片段失败", "reorderFailed": "重新排序代码片段失败",
"dropToRemoveFromFolder": "Drop here to move out of the folder", "dropToRemoveFromFolder": "拖放到此处以移出文件夹",
"shareSnippetTitle": "Share \"{{name}}\"", "shareSnippetTitle": "共享“{{name}}”",
"shareFolderTitle": "Share folder \"{{name}}\"", "shareFolderTitle": "共享文件夹“{{name}}”",
"shareWith": "共享给", "shareWith": "共享给",
"usersTab": "用户", "usersTab": "用户",
"rolesTab": "角色", "rolesTab": "角色",
"shareSearchPlaceholder": "搜索用户或角色", "shareSearchPlaceholder": "搜索用户或角色",
"shareNoMatches": "No matches", "shareNoMatches": "没有匹配项",
"shareWithCount": "Share with {{count}}", "shareWithCount": "共享给 {{count}} 个对象",
"shareFolderHint": "Shares every snippet in this folder. Snippets added later are not shared automatically.", "shareFolderHint": "共享此文件夹中的所有代码片段,后续添加的代码片段不会自动共享。",
"shareFolderSummary": "Shared {{count}} snippet(s) in this folder", "shareFolderSummary": "已共享此文件夹中的 {{count}} 个代码片段",
"grantedBy": "授权者", "grantedBy": "授权者",
"expired": "已过期", "expired": "已过期",
"variablesDialogTitle": "运行“{{name}}”", "variablesDialogTitle": "运行“{{name}}”",
"variablesDialogDescription": "Fill in the values this snippet needs before running it.", "variablesDialogDescription": "执行前请填写此代码片段需要的值。",
"variablesPreviewLabel": "预览", "variablesPreviewLabel": "预览",
"variablesConfirmButton": "运行", "variablesConfirmButton": "运行",
"keybindings": { "keybindings": {
"runSnippet": "Run existing snippet", "runSnippet": "执行已有代码片段",
"snippetLabel": "Snippet", "snippetLabel": "代码片段",
"selectSnippet": "选择代码片段", "selectSnippet": "选择代码片段",
"snippetMissing": "snippet not found", "snippetMissing": "未找到代码片段",
"snippetRequired": "选择代码片段" "snippetRequired": "选择代码片段"
}, },
"paletteHintRun": "在活动终端标签页中运行", "paletteHintRun": "在活动终端标签页中运行",
@@ -217,9 +217,9 @@
"selectSnippet": "选择代码片段……", "selectSnippet": "选择代码片段……",
"addQuickAction": "添加操作", "addQuickAction": "添加操作",
"tabTitle": "代码片段", "tabTitle": "代码片段",
"startupDescription": "Runs in the terminal each time it connects to this host. Variables like $HOST are filled in; a snippet that asks for inputs is skipped.", "startupDescription": "每次连接此主机时在终端中执行。$HOST 等变量会自动填入;需要输入参数的代码片段将被跳过。",
"none": "全不选", "none": "全不选",
"removeQuickAction": "Remove quick action" "removeQuickAction": "移除快捷操作"
}, },
"quickActions": { "quickActions": {
"running": "正在执行 {{name}}...", "running": "正在执行 {{name}}...",
@@ -227,9 +227,12 @@
"failed": "{{name}} 执行失败", "failed": "{{name}} 执行失败",
"error": "执行 {{name}} 失败" "error": "执行 {{name}} 失败"
}, },
"paletteHintPaste": "Paste in the active terminal tab", "paletteHintPaste": "粘贴到当前终端标签页",
"targetTerminals": "Run in", "targetTerminals": "执行位置",
"targetTerminalsHint": "optional, else the active tab", "targetTerminalsHint": "可选,留空则使用当前标签页",
"selectAll": "全部", "selectAll": "全部",
"selectNone": "全不选" "selectNone": "全不选",
"plugin": {
"name": "代码片段"
}
} }
+3
View File
@@ -701,5 +701,8 @@
}, },
"errors": { "errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type." "remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
},
"plugin": {
"name": "SSH Terminal"
} }
} }
+249 -246
View File
@@ -1,18 +1,18 @@
{ {
"mobileKeyboard": { "mobileKeyboard": {
"addQuickKey": "添加", "addQuickKey": "添加",
"arrowDown": "Arrow Down", "arrowDown": "向下方向键",
"arrowLeft": "Arrow Left", "arrowLeft": "向左方向键",
"arrowRight": "Arrow Right", "arrowRight": "向右方向键",
"arrowUp": "Arrow Up", "arrowUp": "向上方向键",
"backTab": "⇥", "backTab": "⇥",
"ctrl": "Ctrl", "ctrl": "Ctrl",
"delete": "Del", "delete": "Del",
"done": "完成", "done": "完成",
"editQuickKeys": "编辑快速键", "editQuickKeys": "编辑快速键",
"end": "End", "end": "End 键",
"esc": "Esc", "esc": "Esc",
"home": "Home", "home": "Home 键",
"pageDown": "PgDn", "pageDown": "PgDn",
"pageUp": "PgUp", "pageUp": "PgUp",
"paste": "粘贴", "paste": "粘贴",
@@ -22,12 +22,12 @@
"removeQuickKey": "删除", "removeQuickKey": "删除",
"resetDefaults": "重置为默认值", "resetDefaults": "重置为默认值",
"shift": "Shift", "shift": "Shift",
"tab": "Tab" "tab": "Tab 键"
}, },
"terminal": { "terminal": {
"autoTmuxEnableFailed": "Failed to enable Auto-Tmux", "autoTmuxEnableFailed": "启用自动 tmux 失败",
"autoTmuxEnabled": "Auto-Tmux enabled for {{host}}. It applies from the next connection.", "autoTmuxEnabled": "已为 {{host}} 启用自动 tmux,将从下次连接开始生效。",
"clipboardHttpWarning": "Couldn't read the clipboard. Some browsers restrict clipboard access outside HTTPS - try Ctrl+V instead, or serve Termix over HTTPS.", "clipboardHttpWarning": "无法读取剪贴板。部分浏览器会限制非 HTTPS 页面的剪贴板访问,请尝试 Ctrl+V,或通过 HTTPS 访问 Termix。",
"closeTab": "关闭", "closeTab": "关闭",
"connecting": "正在连接…", "connecting": "正在连接…",
"connectionLost": "连接丢失", "connectionLost": "连接丢失",
@@ -44,18 +44,18 @@
"reconnect": "重新连接", "reconnect": "重新连接",
"reconnected": "重新连接成功", "reconnected": "重新连接成功",
"reconnecting": "正在重新连接…({{attempt}}/{{max}})", "reconnecting": "正在重新连接…({{attempt}}/{{max}})",
"autoReconnecting": "Connection lost, reconnecting...", "autoReconnecting": "连接已断开,正在重新连接……",
"searchCaseSensitive": "区分大小写", "searchCaseSensitive": "区分大小写",
"searchClose": "关闭(Esc)", "searchClose": "关闭(Esc)",
"searchNext": "下一个匹配(Enter)", "searchNext": "下一个匹配(Enter)",
"searchNoResults": "No results", "searchNoResults": "没有结果",
"searchPlaceholder": "搜索", "searchPlaceholder": "搜索",
"searchPrevious": "上一个匹配(Shift+Enter)", "searchPrevious": "上一个匹配(Shift+Enter)",
"searchRegex": "Use Regular Expression", "searchRegex": "使用正则表达式",
"searchResultCount": "{{index}} of {{count}}", "searchResultCount": "第 {{index}} 项,共 {{count}} 项",
"searchWholeWord": "全字匹配", "searchWholeWord": "全字匹配",
"sessionExpiredNotice": "Your previous session on {{host}} expired after {{minutes}} minutes disconnected; the shell and anything running in it are gone. Enable Auto-Tmux on this host to keep sessions alive.", "sessionExpiredNotice": "你在 {{host}} 上的上一个会话已在断开 {{minutes}} 分钟后过期,Shell 及其中运行的所有程序均已结束。为此主机启用自动 tmux 可保持会话。",
"sessionExpiredNoticeNoMinutes": "Your previous session on {{host}} expired while disconnected; the shell and anything running in it are gone. Enable Auto-Tmux on this host to keep sessions alive.", "sessionExpiredNoticeNoMinutes": "你在 {{host}} 上的上一个会话已在断开期间过期,Shell 及其中运行的所有程序均已结束。为此主机启用自动 tmux 可保持会话。",
"sessionTakenOver": "会话已在另一个标签页中打开。正在重新连接...", "sessionTakenOver": "会话已在另一个标签页中打开。正在重新连接...",
"tmuxAttached": "已连接客户端", "tmuxAttached": "已连接客户端",
"tmuxAttachedCount": "{{count}} 个已连接", "tmuxAttachedCount": "{{count}} 个已连接",
@@ -81,35 +81,35 @@
"clipboardWriteFailed": "复制到剪贴板失败。请确保页面通过 HTTPS 或 localhost 提供服务。", "clipboardWriteFailed": "复制到剪贴板失败。请确保页面通过 HTTPS 或 localhost 提供服务。",
"connected": "已连接", "connected": "已连接",
"totpCodeLabel": "验证码", "totpCodeLabel": "验证码",
"duplicateTab": "Duplicate tab", "duplicateTab": "复制标签页",
"imageChooseFile": "Choose an image file", "imageChooseFile": "选择图片文件",
"imageUploaded": "Image uploaded: {{path}}", "imageUploaded": "图片已上传:{{path}}",
"imageClipboardUnavailable": "This browser cannot read images from the clipboard", "imageClipboardUnavailable": "此浏览器无法从剪贴板读取图片",
"imageClipboardEmpty": "No image found in the clipboard", "imageClipboardEmpty": "剪贴板中未找到图片",
"localShell": "Local terminal shell", "localShell": "本地终端 Shell",
"autocompleteHintComplete": "Tab or Enter to complete", "autocompleteHintComplete": "按 Tab 或 Enter 补全",
"autocompleteHintNavigate": "↑↓ to navigate", "autocompleteHintNavigate": "使用 ↑↓ 选择",
"autocompleteHintClose": "Esc to close" "autocompleteHintClose": "按 Esc 关闭"
}, },
"terminalToolbar": { "terminalToolbar": {
"detachTmux": "Detach tmux", "detachTmux": "分离 tmux",
"detachTmuxDescription": "Detach tmux session", "detachTmuxDescription": "分离 tmux 会话",
"dismissError": "Dismiss image error", "dismissError": "关闭图片错误提示",
"hideToolbar": "Hide toolbar", "hideToolbar": "隐藏工具栏",
"image": "Image actions (images only)", "image": "图片操作(仅支持图片)",
"imageActionFailed": "Image action failed", "imageActionFailed": "图片操作失败",
"layout": "Toolbar display mode", "layout": "工具栏显示模式",
"layoutExpanded": "Expanded with metrics", "layoutExpanded": "展开并显示指标",
"layoutIcon": "Icon only", "layoutIcon": "仅图标",
"layoutLabeled": "Icons and labels", "layoutLabeled": "图标和标签",
"moveToolbar": "Move toolbar", "moveToolbar": "移动工具栏",
"moveToolbarHint": "Drag to reposition the toolbar", "moveToolbarHint": "拖动以调整工具栏位置",
"paste": "Paste image", "paste": "粘贴图片",
"pasteImage": "Paste image from clipboard (images only)", "pasteImage": "从剪贴板粘贴图片(仅支持图片)",
"retry": "重试", "retry": "重试",
"showToolbar": "展开工具栏", "showToolbar": "展开工具栏",
"upload": "Upload image", "upload": "上传图片",
"uploadImage": "Upload an image to the host", "uploadImage": "将图片上传到主机",
"uploadingImage": "上传中..." "uploadingImage": "上传中..."
}, },
"history": { "history": {
@@ -119,153 +119,153 @@
"clearAll": "全部清除", "clearAll": "全部清除",
"noHistoryEntries": "无历史记录", "noHistoryEntries": "无历史记录",
"trackingDisabled": "历史跟踪已禁用", "trackingDisabled": "历史跟踪已禁用",
"trackingDisabledHint": "Turn on Command History in this host's SSH Terminal tab.", "trackingDisabledHint": "请在此主机的 SSH 终端标签页中启用命令历史。",
"commandCount_one": "{{count}} command", "commandCount_one": "{{count}} 条命令",
"commandCount_other": "{{count}} commands" "commandCount_other": "{{count}} 条命令"
}, },
"touchInput": { "touchInput": {
"enabled": "Enable Touch Input", "enabled": "启用触摸输入",
"enabledDesc": "Allow one-finger touch scrolling in terminals", "enabledDesc": "允许在终端中使用单指触摸滚动",
"momentumEnabled": "Enable Momentum", "momentumEnabled": "启用惯性滚动",
"momentumEnabledDesc": "Continue scrolling briefly after a swipe", "momentumEnabledDesc": "滑动结束后继续短暂滚动",
"advanced": "高级", "advanced": "高级",
"resetDefaults": "重置为默认值", "resetDefaults": "重置为默认值",
"dragThreshold": "拖动阈值", "dragThreshold": "拖动阈值",
"maxWheelDelta": "最大滚轮增量", "maxWheelDelta": "最大滚轮增量",
"momentumSampleWindow": "Momentum sample window", "momentumSampleWindow": "惯性采样窗口",
"releaseGracePeriod": "释放宽限期", "releaseGracePeriod": "释放宽限期",
"minimumVelocity": "最小速度", "minimumVelocity": "最小速度",
"maximumVelocity": "Maximum velocity", "maximumVelocity": "最大速度",
"maximumDuration": "Maximum duration", "maximumDuration": "最长持续时间",
"maximumTravel": "Maximum travel", "maximumTravel": "最大移动距离",
"decayTime": "Decay time", "decayTime": "衰减时间",
"pixelsPerTick": "Pixels per tick", "pixelsPerTick": "每次滚动的像素数",
"maximumFrameInterval": "Maximum frame interval", "maximumFrameInterval": "最大帧间隔",
"maximumTicksPerFrame": "每帧最大刻度数" "maximumTicksPerFrame": "每帧最大刻度数"
}, },
"imageStorage": { "imageStorage": {
"instanceId": "Terminal session instance ID", "instanceId": "终端会话实例 ID",
"instanceIdDesc": "用于测试可见性的已连接终端标签页的实例 ID。", "instanceIdDesc": "用于测试可见性的已连接终端标签页的实例 ID。",
"selectedModeUnavailable": "Unavailable", "selectedModeUnavailable": "不可用",
"test": "测试", "test": "测试",
"testing": "Testing...", "testing": "正在测试……",
"testFailed": "Failed to test image storage", "testFailed": "测试图片存储失败",
"testConnected": "Terminal session is connected.", "testConnected": "终端会话已连接。",
"testNotConnected": "No connected terminal session with this instance ID.", "testNotConnected": "没有使用此实例 ID 的已连接终端会话。",
"testSelectedMode": "上传将使用:{{mode}}", "testSelectedMode": "上传将使用:{{mode}}",
"testLocalVisible": "The local directory is visible from the connected host.", "testLocalVisible": "已连接的主机可以访问此本地目录。",
"testLocalNotVisible": "The local directory is NOT visible from the connected host." "testLocalNotVisible": "已连接的主机无法访问此本地目录。"
}, },
"settings": { "settings": {
"admin": { "admin": {
"group": { "group": {
"touch": "Touch Input", "touch": "触摸输入",
"images": "Image Storage", "images": "图片存储",
"sessions": "会话", "sessions": "会话",
"newHosts": "New Hosts" "newHosts": "新主机"
}, },
"sessionTimeoutMinutes": { "sessionTimeoutMinutes": {
"label": "Terminal Session Persistence", "label": "终端会话保持",
"description": "How long a disconnected terminal is kept alive on the server before it is closed. Min 1, max 1440 minutes. For sessions that must outlive this, enable Auto-Tmux on the host." "description": "断开的终端会话在服务器上保留多久后关闭。最少 1 分钟,最多 1440 分钟。需要更长时间保持的会话,请为主机启用自动 tmux。"
}, },
"commandHistoryEnabled": { "commandHistoryEnabled": {
"label": "命令历史", "label": "命令历史",
"description": "允许记录命令历史。禁用后,无论各主机设置如何,均不保存历史记录。" "description": "允许记录命令历史。禁用后,无论各主机设置如何,均不保存历史记录。"
}, },
"commandHistoryForNewHosts": { "commandHistoryForNewHosts": {
"label": "Command history for new hosts", "label": "新主机默认启用命令历史",
"description": "Whether new hosts start with command history on." "description": "是否为新主机默认启用命令历史。"
}, },
"imageStorageMode": { "imageStorageMode": {
"label": "Storage Mode", "label": "存储模式",
"description": "粘贴和上传的终端图片的写入位置。自动模式根据连接自动选择,本地和远程(SFTP)为固定模式。", "description": "粘贴和上传的终端图片的写入位置。自动模式根据连接自动选择,本地和远程(SFTP)为固定模式。",
"auto": "自动", "auto": "自动",
"local": "本地", "local": "本地",
"remoteSftp": "Remote (SFTP)" "remoteSftp": "远程(SFTP)"
}, },
"imageLocalDir": { "imageLocalDir": {
"label": "Local directory", "label": "本地目录",
"description": "Absolute path on the Termix backend where local mode writes images. Never shown after saving. Empty uses TERMIX_IMAGE_DIR." "description": "本地模式保存图片时使用的 Termix 后端绝对路径。保存后不会显示,留空则使用 TERMIX_IMAGE_DIR。"
}, },
"imageHostPath": { "imageHostPath": {
"label": "Host path", "label": "主机路径",
"description": "The host-side view of the local directory that terminal agents receive." "description": "传递给终端 Agent 的本地目录在目标主机上的对应路径。"
}, },
"imageTtlMs": { "imageTtlMs": {
"label": "TTL (ms)", "label": "保留时长(毫秒)",
"description": "How long an uploaded image is kept, in milliseconds. Empty uses TERMIX_IMAGE_TTL_MS or one hour." "description": "上传图片的保留时长,单位为毫秒。留空则使用 TERMIX_IMAGE_TTL_MS,未设置时默认为一小时。"
}, },
"imageMaxCount": { "imageMaxCount": {
"label": "Max image count", "label": "最多保留图片数",
"description": "Most images kept at once. Empty uses TERMIX_MAX_IMAGE_COUNT or 100." "description": "同时保留的最大图片数量。留空则使用 TERMIX_MAX_IMAGE_COUNT,未设置时默认为 100。"
}, },
"imageMaxBytes": { "imageMaxBytes": {
"label": "Max storage bytes", "label": "最大存储字节数",
"description": "Most bytes of images kept at once. Empty uses TERMIX_MAX_IMAGE_STORAGE_BYTES or 5 GiB." "description": "同时保留图片的最大总字节数。留空则使用 TERMIX_MAX_IMAGE_STORAGE_BYTES,未设置时默认为 5 GiB。"
}, },
"sessionPersistence": { "sessionPersistence": {
"label": "Keep sessions after disconnect", "label": "断开连接后保留会话",
"description": "Keep a disconnected terminal alive on the server for the time above, so reopening its tab picks up where it left off." "description": "在上述时长内保持服务器上的终端会话,重新打开标签页即可继续之前的操作。"
}, },
"newHostAutoTmux": { "newHostAutoTmux": {
"label": "Auto-Tmux", "label": "自动 tmux",
"description": "Run new hosts' terminals inside tmux so sessions survive disconnects (needs tmux on the host)" "description": "在 tmux 中运行新主机的终端,以便断开后保留会话(主机需要安装 tmux)"
}, },
"newHostTheme": { "newHostTheme": {
"label": "Theme for new hosts", "label": "新主机默认主题",
"description": "The terminal theme a new host starts with." "description": "新主机默认使用的终端主题。"
}, },
"newHostFontFamily": { "newHostFontFamily": {
"label": "Font for new hosts", "label": "新主机默认字体",
"description": "The terminal font a new host starts with." "description": "新主机默认使用的终端字体。"
}, },
"newHostFontSize": { "newHostFontSize": {
"label": "Font size for new hosts", "label": "新主机默认字号",
"description": "The terminal font size a new host starts with, in pixels." "description": "新主机默认使用的终端字号,单位为像素。"
}, },
"newHostCursorStyle": { "newHostCursorStyle": {
"label": "Cursor style for new hosts" "label": "新主机默认光标样式"
}, },
"newHostCursorBlink": { "newHostCursorBlink": {
"label": "Blinking cursor for new hosts" "label": "新主机默认闪烁光标"
} }
}, },
"host": { "host": {
"enableTerminalToolbar": { "enableTerminalToolbar": {
"label": "Enable Terminal Toolbar", "label": "启用终端工具栏",
"description": "在当前终端上显示该主机的工具和实时信息的 Dock栏" "description": "在当前终端上显示该主机的工具和实时信息的 Dock栏"
}, },
"terminalToolbarPosition": { "terminalToolbarPosition": {
"label": "Toolbar Position", "label": "工具栏位置",
"description": "Where the toolbar sits in the terminal. You can still drag it from there.", "description": "工具栏在终端中的初始位置,之后仍可拖动调整。",
"bottom": "Bottom center", "bottom": "底部居中",
"bottomLeft": "Bottom left", "bottomLeft": "左下角",
"bottomRight": "Bottom right", "bottomRight": "右下角",
"top": "Top center", "top": "顶部居中",
"topLeft": "Top left", "topLeft": "左上角",
"topRight": "Top right" "topRight": "右上角"
}, },
"terminalToolbarStartState": { "terminalToolbarStartState": {
"label": "Toolbar Start State", "label": "工具栏初始状态",
"description": "Whether the toolbar opens expanded or minimized when the terminal connects", "description": "终端连接时,工具栏以展开还是最小化状态显示",
"expanded": "Expanded", "expanded": "展开",
"collapsed": "Minimized" "collapsed": "最小化"
}, },
"terminalToolbarDisplay": { "terminalToolbarDisplay": {
"label": "Toolbar Display Mode", "label": "工具栏显示模式",
"description": "How toolbar buttons are shown. Last used keeps whatever you picked in the toolbar.", "description": "工具栏按钮的显示方式。“上次使用”会保留你在工具栏中选择的模式。",
"remember": "Last used", "remember": "上次使用",
"icon": "Icon only", "icon": "仅图标",
"labeled": "Icons and labels", "labeled": "图标和标签",
"expanded": "Expanded with stats" "expanded": "展开并显示统计"
}, },
"terminalToolbarShowStatus": { "terminalToolbarShowStatus": {
"label": "Show Live Stats", "label": "显示实时统计",
"description": "Show live host stats above the toolbar buttons in expanded mode" "description": "在展开模式下,在工具栏按钮上方显示主机实时统计"
}, },
"terminalToolbarFade": { "terminalToolbarFade": {
"label": "Fade When Idle", "label": "空闲时淡出",
"description": "Fade the toolbar out until you hover over it" "description": "工具栏淡出隐藏,悬停时显示"
}, },
"enableCommandHistory": { "enableCommandHistory": {
"label": "命令历史", "label": "命令历史",
@@ -273,134 +273,134 @@
}, },
"enableTerminal": { "enableTerminal": {
"label": "启用终端", "label": "启用终端",
"description": "Offer an SSH terminal for this host." "description": "为此主机提供 SSH 终端。"
}, },
"inheritAppearance": { "inheritAppearance": {
"label": "Use user defaults" "label": "使用用户默认值"
}, },
"theme": { "theme": {
"label": "主题" "label": "主题"
}, },
"cursorBlink": { "cursorBlink": {
"label": "Blinking cursor" "label": "闪烁光标"
}, },
"cursorStyle": { "cursorStyle": {
"label": "Cursor style" "label": "光标样式"
}, },
"fontSize": { "fontSize": {
"label": "Font size" "label": "字号"
}, },
"fontFamily": { "fontFamily": {
"label": "Font" "label": "字体"
}, },
"scrollback": { "scrollback": {
"label": "Scrollback" "label": "回滚缓冲区"
}, },
"letterSpacing": { "letterSpacing": {
"label": "Letter spacing" "label": "字符间距"
}, },
"lineHeight": { "lineHeight": {
"label": "Line height" "label": "行高"
}, },
"bellStyle": { "bellStyle": {
"label": "Bell" "label": "响铃"
}, },
"minimumContrastRatio": { "minimumContrastRatio": {
"label": "Minimum contrast ratio" "label": "最小对比度"
}, },
"backgroundImage": { "backgroundImage": {
"label": "Background image" "label": "背景图片"
}, },
"backgroundImageOpacity": { "backgroundImageOpacity": {
"label": "Background image opacity" "label": "背景图片不透明度"
}, },
"customThemeColors": { "customThemeColors": {
"label": "Custom theme colors" "label": "自定义主题颜色"
}, },
"rightClickSelectsWord": { "rightClickSelectsWord": {
"label": "Right click selects word" "label": "右键选择单词"
}, },
"macOptionIsMeta": { "macOptionIsMeta": {
"label": "Option is Meta" "label": "将 Option 用作 Meta"
}, },
"fastScrollModifier": { "fastScrollModifier": {
"label": "Fast scroll key" "label": "快速滚动按键"
}, },
"fastScrollSensitivity": { "fastScrollSensitivity": {
"label": "Fast scroll speed" "label": "快速滚动速度"
}, },
"backspaceMode": { "backspaceMode": {
"label": "Backspace" "label": "退格键"
}, },
"autoMosh": { "autoMosh": {
"label": "Auto mosh" "label": "自动 mosh"
}, },
"moshCommand": { "moshCommand": {
"label": "Mosh command" "label": "Mosh 命令"
}, },
"autoTmux": { "autoTmux": {
"label": "Auto tmux" "label": "自动 tmux"
}, },
"useSSHTitle": { "useSSHTitle": {
"label": "Use the SSH title" "label": "使用 SSH 标题"
}, },
"syntaxHighlighting": { "syntaxHighlighting": {
"label": "Syntax highlighting" "label": "语法高亮"
}, },
"syntaxHighlightingOptions": { "syntaxHighlightingOptions": {
"label": "Syntax highlighting categories" "label": "语法高亮类别"
}, },
"linkClickBehavior": { "linkClickBehavior": {
"label": "Link click" "label": "链接点击方式"
}, },
"localEcho": { "localEcho": {
"label": "Local echo" "label": "本地回显"
}, },
"passwordPromptAutoFill": { "passwordPromptAutoFill": {
"label": "Offer to fill password prompts" "label": "提示自动填写密码"
}, },
"sudoPasswordAutoFill": { "sudoPasswordAutoFill": {
"label": "Fill sudo prompts" "label": "自动填写 sudo 提示"
}, },
"autoReconnect": { "autoReconnect": {
"label": "Reconnect automatically" "label": "自动重连"
} }
}, },
"user": { "user": {
"commandAutocomplete": { "commandAutocomplete": {
"label": "命令自动补全", "label": "命令自动补全",
"description": "Show autocomplete while typing if you press tab" "description": "输入时按 Tab 显示自动补全"
}, },
"localEcho": { "localEcho": {
"label": "Local Echo", "label": "本地回显",
"description": "Reduce perceived input latency using safe local keystroke prediction" "description": "通过安全的本地按键预测减少感知输入延迟"
}, },
"linkClickBehavior": { "linkClickBehavior": {
"label": "终端链接点击", "label": "终端链接点击",
"description": "点击终端内链接时的默认行为" "description": "点击终端内链接时的默认行为"
}, },
"customThemes": { "customThemes": {
"label": "Saved themes" "label": "已保存的主题"
} }
} }
}, },
"palette": { "palette": {
"localTerminal": "Local Terminal" "localTerminal": "本地终端"
}, },
"permissions": { "permissions": {
"sessions": { "sessions": {
"title": "Use live terminal sessions", "title": "使用实时终端会话",
"description": "Lets other features, such as session sharing and recording, reach your live terminal sessions." "description": "允许会话共享、录制等其他功能访问你的实时终端会话。"
}, },
"history": { "history": {
"title": "Read command history", "title": "读取命令历史",
"description": "Lets other features, such as the AI assistant, read your command history." "description": "允许 AI 助手等其他功能读取你的命令历史。"
} }
}, },
"hosts": { "hosts": {
"tabTerminal": "终端", "tabTerminal": "终端",
"addHost": "添加主机", "addHost": "添加主机",
"applyGlobalThemeTooltip": "Apply this theme", "applyGlobalThemeTooltip": "应用此主题",
"backgroundImageDesc": "终端背景图片的可选 URL", "backgroundImageDesc": "终端背景图片的可选 URL",
"backgroundImageLabel": "背景图片 URL", "backgroundImageLabel": "背景图片 URL",
"backgroundImageOpacityLabel": "背景图片不透明度", "backgroundImageOpacityLabel": "背景图片不透明度",
@@ -413,7 +413,7 @@
"bellStyleBoth": "两者皆有", "bellStyleBoth": "两者皆有",
"bellStyleLabel": "响铃样式", "bellStyleLabel": "响铃样式",
"bellStyleNone": "全不选", "bellStyleNone": "全不选",
"bellStyleSound": "Sound", "bellStyleSound": "声音",
"bellStyleVisual": "视觉", "bellStyleVisual": "视觉",
"colorTheme": "颜色主题", "colorTheme": "颜色主题",
"cursorBlink": "光标闪烁", "cursorBlink": "光标闪烁",
@@ -448,12 +448,12 @@
"customThemeSelection": "选中色", "customThemeSelection": "选中色",
"customThemeWhite": "白色", "customThemeWhite": "白色",
"customThemeYellow": "黄色", "customThemeYellow": "黄色",
"deleteGlobalThemeTooltip": "Delete this theme", "deleteGlobalThemeTooltip": "删除此主题",
"docsLink": "查看文档", "docsLink": "查看文档",
"enableAutoMosh": "启用自动 Mosh", "enableAutoMosh": "启用自动 Mosh",
"enableAutoMoshDesc": "若可用,优先使用 Mosh 而非 SSH", "enableAutoMoshDesc": "若可用,优先使用 Mosh 而非 SSH",
"enableAutoTmux": "启用自动 Tmux", "enableAutoTmux": "启用自动 Tmux",
"enableAutoTmuxDesc": "Keep your shell and anything running in it alive across disconnects and browser restarts by running inside tmux (tmux must be installed on the host)", "enableAutoTmuxDesc": "在 tmux 中运行 Shell,使其及其中运行的程序在断开连接和浏览器重启后继续保留(主机必须安装 tmux)",
"fastScrollModifier": "快速滚动修饰键", "fastScrollModifier": "快速滚动修饰键",
"fastScrollModifierAlt": "Alt", "fastScrollModifierAlt": "Alt",
"fastScrollModifierCtrl": "Ctrl", "fastScrollModifierCtrl": "Ctrl",
@@ -462,9 +462,9 @@
"fastScrollSensitivity": "快速滚动灵敏度", "fastScrollSensitivity": "快速滚动灵敏度",
"fastScrollSensitivityLabel": "快速滚动灵敏度", "fastScrollSensitivityLabel": "快速滚动灵敏度",
"fontFamily": "字体族", "fontFamily": "字体族",
"fontFamilyCustomHint": "Pick a bundled font, or choose Custom and type the exact name of a font already installed on your system (e.g. MesloLGS NF, Noto Sans Mono). Termix cannot install fonts for you, install it on your OS first.", "fontFamilyCustomHint": "选择内置字体,或选择“自定义”并输入系统中已安装字体的准确名称(例如 MesloLGS NF、Noto Sans Mono)。Termix 无法替你安装字体,请先在操作系统中安装。",
"fontFamilyCustomOption": "自定义...", "fontFamilyCustomOption": "自定义...",
"fontFamilyCustomPlaceholder": "Exact font name, e.g. MesloLGS NF", "fontFamilyCustomPlaceholder": "准确的字体名称,例如 MesloLGS NF",
"fontFamilyLabel": "字体", "fontFamilyLabel": "字体",
"fontSize": "字体大小", "fontSize": "字体大小",
"fontSizeLabel": "字体大小", "fontSizeLabel": "字体大小",
@@ -479,28 +479,28 @@
"linkClickBehaviorLabel": "链接点击行为", "linkClickBehaviorLabel": "链接点击行为",
"loadingHost": "正在加载主机...", "loadingHost": "正在加载主机...",
"localEchoAuto": "自动", "localEchoAuto": "自动",
"localEchoDesc": "Predict safe printable keystrokes locally on slow connections. Password prompts and control input are never echoed.", "localEchoDesc": "在连接较慢时,于本地预测安全的可打印按键。密码提示和控制输入不会被回显。",
"localEchoLabel": "Local Echo", "localEchoLabel": "本地回显",
"localEchoOff": "关闭", "localEchoOff": "关闭",
"localEchoOn": "Always on", "localEchoOn": "始终开启",
"macOptionIsMetaLabel": "选项键发送 Meta", "macOptionIsMetaLabel": "选项键发送 Meta",
"macOptionIsMetaShortDesc": "禁用后,可在 macOS 上使用 Option/Alt 键输入特殊字符(例如 @ 或 |),而不是发送 Meta。", "macOptionIsMetaShortDesc": "禁用后,可在 macOS 上使用 Option/Alt 键输入特殊字符(例如 @ 或 |),而不是发送 Meta。",
"moshCommand": "MOSH 命令", "moshCommand": "MOSH 命令",
"moshCommandLabel": "Mosh 命令", "moshCommandLabel": "Mosh 命令",
"noSavedThemes": "No saved themes yet", "noSavedThemes": "尚未保存任何主题",
"none": "无", "none": "无",
"passwordPromptAutoFillDesc": "Offer to fill in the saved password when the terminal asks for one. Turn off to never auto-fill any password on this host.", "passwordPromptAutoFillDesc": "终端请求密码时,提示填写已保存的密码。关闭后,此主机不会自动填写任何密码。",
"passwordPromptAutoFillLabel": "Offer Password Auto-fill", "passwordPromptAutoFillLabel": "提示密码自动填充",
"autoReconnectLabel": "Auto Reconnect", "autoReconnectLabel": "自动重连",
"autoReconnectDesc": "Reconnect on its own when the SSH connection drops, like after a network blip or sleep. Keeps the scrollback.", "autoReconnectDesc": "SSH 因网络波动或休眠等原因断开时自动重连,并保留回滚缓冲区。",
"rightClickSelectsWord": "右键单击选择单词", "rightClickSelectsWord": "右键单击选择单词",
"rightClickSelectsWordLabel": "右键选择单词", "rightClickSelectsWordLabel": "右键选择单词",
"rightClickSelectsWordShortDesc": "右键点击时选中光标下的单词", "rightClickSelectsWordShortDesc": "右键点击时选中光标下的单词",
"saveAsGlobalTheme": "Save as Global Theme", "saveAsGlobalTheme": "保存为全局主题",
"saveGlobalThemeError": "Failed to save theme", "saveGlobalThemeError": "保存主题失败",
"saveGlobalThemeNamePrompt": "Enter a name for this theme", "saveGlobalThemeNamePrompt": "为此主题输入名称",
"saveGlobalThemeSuccess": "Theme saved", "saveGlobalThemeSuccess": "主题已保存",
"savedThemesLabel": "Saved Themes", "savedThemesLabel": "已保存的主题",
"scrollbackBufferLabel": "回滚缓冲区", "scrollbackBufferLabel": "回滚缓冲区",
"scrollbackMaxLines": "保留在历史记录中的最大行数", "scrollbackMaxLines": "保留在历史记录中的最大行数",
"sudoPasswordAutoFill": "Sudo 密码自动填充", "sudoPasswordAutoFill": "Sudo 密码自动填充",
@@ -534,7 +534,7 @@
"success": "成功" "success": "成功"
}, },
"proxmoxResultSkippedNoIp": "{{count}} 已跳过(未找到 IP )", "proxmoxResultSkippedNoIp": "{{count}} 已跳过(未找到 IP )",
"proxmoxStatsUrlCopied": "Proxmox Stats URL copied to clipboard", "proxmoxStatsUrlCopied": "Proxmox 统计 URL 已复制到剪贴板",
"rdpUrlCopied": "RDP URL 已复制", "rdpUrlCopied": "RDP URL 已复制",
"vncUrlCopied": "VNC URL 已复制", "vncUrlCopied": "VNC URL 已复制",
"filterAuthStepca": "Step CA", "filterAuthStepca": "Step CA",
@@ -560,14 +560,14 @@
}, },
"nav": { "nav": {
"history": "历史记录", "history": "历史记录",
"localTerminal": "Local Terminal", "localTerminal": "本地终端",
"sshTools": "SSH 工具", "sshTools": "SSH 工具",
"terminal": "终端", "terminal": "终端",
"macros": "Macros" "macros": "宏"
}, },
"sshTools": { "sshTools": {
"broadcastInputPlaceholder": "在此输入以广播按键...", "broadcastInputPlaceholder": "在此输入以广播按键...",
"copyOnSelect": "Copy selected text automatically", "copyOnSelect": "自动复制选中文本",
"enableRightClickCopyPaste": "启用右键复制/粘贴", "enableRightClickCopyPaste": "启用右键复制/粘贴",
"fillPassword": "填充密码", "fillPassword": "填充密码",
"fillPasswordMissing": "{{count}} 个所选终端无保存的密码", "fillPasswordMissing": "{{count}} 个所选终端无保存的密码",
@@ -583,123 +583,126 @@
"stopRecording": "停止录制" "stopRecording": "停止录制"
}, },
"keybindings": { "keybindings": {
"copy": "Copy selection", "copy": "复制选中内容",
"paste": "粘贴", "paste": "粘贴",
"sendControlCode": "Send Ctrl+letter signal", "sendControlCode": "发送 Ctrl+字母 控制信号",
"sendText": "发送文本", "sendText": "发送文本",
"controlCodeLabel": "字母", "controlCodeLabel": "字母",
"textLabel": "Text to send", "textLabel": "要发送的文本",
"appendEnterLabel": "Press Enter after sending", "appendEnterLabel": "发送后按 Enter",
"clipboardPermissionNote": "May prompt for clipboard permission on first use in some browsers.", "clipboardPermissionNote": "部分浏览器在首次使用时可能要求剪贴板权限。",
"textRequired": "Enter the text to send", "textRequired": "输入要发送的文本",
"controlCodeRequired": "Enter a single letter for the control code", "controlCodeRequired": "输入单个字母以指定控制码",
"builtIn": { "builtIn": {
"copyCtrlC": "Copy selection (Ctrl+C, only when text is selected)", "copyCtrlC": "复制选中内容(Ctrl+C,仅在选中文本时)",
"copyCtrlShiftC": "Copy selection (Ctrl+Shift+C)", "copyCtrlShiftC": "复制选中内容(Ctrl+Shift+C)",
"copyCmdC": "Copy selection (Cmd+C)", "copyCmdC": "复制选中内容(Cmd+C)",
"pasteCtrlShiftV": "Paste from clipboard (Ctrl+Shift+V)", "pasteCtrlShiftV": "从剪贴板粘贴(Ctrl+Shift+V)",
"ctrlW": "Send Ctrl+W to shell (blocked from closing browser tab)", "ctrlW": "向 Shell 发送 Ctrl+W(阻止关闭浏览器标签页)",
"ctrlT": "Send Ctrl+T to shell (blocked from opening browser tab)", "ctrlT": "向 Shell 发送 Ctrl+T(阻止打开浏览器标签页)",
"ctrlN": "Send Ctrl+N to shell (blocked from opening browser window)", "ctrlN": "向 Shell 发送 Ctrl+N(阻止打开浏览器窗口)",
"ctrlQ": "Send Ctrl+Q to shell (blocked from quitting browser)" "ctrlQ": "向 Shell 发送 Ctrl+Q(阻止退出浏览器)"
} }
}, },
"macros": { "macros": {
"title": "Macros", "title": "宏",
"description": "Run a saved series of commands in a terminal, and react to what the server sends back.", "description": "在终端中执行已保存的一组命令,并根据服务器返回的内容作出响应。",
"target": "目标", "target": "目标",
"noTerminal": "No terminal open", "noTerminal": "没有打开的终端",
"empty": "No macros yet", "empty": "暂无宏",
"emptyHint": "Create a macro to automate a terminal flow, like logging in, restarting a service, or answering prompts.", "emptyHint": "创建宏以自动执行终端流程,例如登录、重启服务或回答提示。",
"create": "New macro", "create": "新建宏",
"back": "Back to macros", "back": "返回宏",
"name": "名称", "name": "名称",
"namePlaceholder": "重启 Web 服务器", "namePlaceholder": "重启 Web 服务器",
"nameRequired": "Give the macro a name before saving", "nameRequired": "保存前请为宏命名",
"descriptionLabel": "描述", "descriptionLabel": "描述",
"descriptionPlaceholder": "What this macro does (optional)", "descriptionPlaceholder": "此宏的用途(可选)",
"unsaved": "未保存的更改", "unsaved": "未保存的更改",
"save": "保存", "save": "保存",
"saved": "Macro saved", "saved": "宏已保存",
"saveFailed": "Could not save the macro", "saveFailed": "无法保存宏",
"delete": "删除", "delete": "删除",
"deleteTitle": "Delete this macro?", "deleteTitle": "删除此宏?",
"deleteBody": "\"{{name}}\" will be permanently deleted. This cannot be undone.", "deleteBody": "“{{name}}”将被永久删除,此操作无法撤销。",
"deleted": "Macro deleted", "deleted": "宏已删除",
"cancel": "取消", "cancel": "取消",
"run": "运行", "run": "运行",
"stop": "停止", "stop": "停止",
"running": "运行中...", "running": "运行中...",
"completed": "Macro finished", "completed": "宏已完成",
"failed": "Macro failed", "failed": "宏执行失败",
"loadFailed": "Could not load macros", "loadFailed": "无法加载宏",
"needTerminal": "Open and connect a terminal first", "needTerminal": "请先打开并连接终端",
"noSteps": "This macro has no steps yet. Add one below.", "noSteps": "此宏尚无步骤,请在下方添加。",
"addStep": "Add step", "addStep": "添加步骤",
"stepNumber": "Step {{number}}", "stepNumber": "步骤 {{number}}",
"removeStep": "Remove step", "removeStep": "移除步骤",
"confirmRunTitle": "Run this macro?", "confirmRunTitle": "执行此宏?",
"confirmRunBody": "\"{{name}}\" will send commands to {{host}}.", "confirmRunBody": "“{{name}}”将向 {{host}} 发送命令。",
"confirmRunAction": "Run macro", "confirmRunAction": "执行宏",
"stepTypes": { "stepTypes": {
"send": { "send": {
"label": "Send command", "label": "发送命令",
"help": "Types text into the terminal, the same as if you typed it." "help": "向终端输入文本,效果与你手动输入相同。"
}, },
"wait": { "wait": {
"label": "Wait for output", "label": "等待输出",
"help": "Pauses until the server prints the text you name, like a password prompt." "help": "暂停,直到服务器输出指定文本,例如密码提示。"
}, },
"delay": { "delay": {
"label": "Pause", "label": "暂停",
"help": "Waits a fixed amount of time before the next step." "help": "在下一步之前等待固定时长。"
}, },
"if": { "if": {
"label": "If output contains", "label": "如果输出包含",
"help": "Checks everything the server has printed so far, then runs one of two branches." "help": "检查服务器截至目前的全部输出,然后执行两个分支中的一个。"
}, },
"repeat": { "repeat": {
"label": "Repeat", "label": "重复",
"help": "Runs the steps inside it a set number of times." "help": "按指定次数重复执行其中的步骤。"
} }
}, },
"send": { "send": {
"text": "Command or text", "text": "命令或文本",
"textPlaceholder": "systemctl restart nginx", "textPlaceholder": "systemctl restart nginx",
"pressEnter": "Press Enter after sending", "pressEnter": "发送后按 Enter",
"pressEnterHelp": "Turn this off to type text without submitting it." "pressEnterHelp": "关闭此选项可仅输入文本而不提交。"
}, },
"wait": { "wait": {
"pattern": "Wait until the output contains", "pattern": "等待输出包含",
"patternPlaceholder": "密码:", "patternPlaceholder": "密码:",
"timeout": "Give up after", "timeout": "超时放弃时间",
"onTimeout": "If it never appears", "onTimeout": "始终未出现时",
"stop": "Stop the macro", "stop": "停止宏",
"continue": "无论如何都继续" "continue": "无论如何都继续"
}, },
"delay": { "delay": {
"duration": "Wait for" "duration": "等待时长"
}, },
"ifStep": { "ifStep": {
"pattern": "If the output contains", "pattern": "如果输出包含",
"patternPlaceholder": "Active: active (running)", "patternPlaceholder": "Active: active (running)",
"then": "Then do this", "then": "则执行",
"else": "Otherwise do this", "else": "否则执行",
"emptyBranch": "No steps" "emptyBranch": "没有步骤"
}, },
"repeat": { "repeat": {
"count": "Number of times", "count": "执行次数",
"times": "次" "times": "次"
}, },
"match": { "match": {
"useRegex": "Match with a regular expression", "useRegex": "使用正则表达式匹配",
"useRegexHelp": "关闭此选项以完全按原文匹配文本。", "useRegexHelp": "关闭此选项以完全按原文匹配文本。",
"invalidRegex": "Not a valid regular expression, it will be matched as plain text" "invalidRegex": "正则表达式无效,将按普通文本匹配"
}, },
"seconds": "秒", "seconds": "秒",
"secondsShort": "{{count}}s" "secondsShort": "{{count}} 秒"
}, },
"errors": { "errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type." "remoteServerRequired": "需要远程服务器。请在设置中连接远程服务器以使用此连接类型。"
},
"plugin": {
"name": "SSH 终端"
} }
} }
+3
View File
@@ -61,5 +61,8 @@
"label": "Sign in with SSO automatically", "label": "Sign in with SSO automatically",
"description": "Send visitors straight to the first SSO provider instead of showing the login form. The OIDC_SILENT_LOGIN_DEFAULT environment variable overrides this." "description": "Send visitors straight to the first SSO provider instead of showing the login form. The OIDC_SILENT_LOGIN_DEFAULT environment variable overrides this."
} }
},
"plugin": {
"name": "Single sign-on"
} }
} }
+17 -14
View File
@@ -1,11 +1,11 @@
{ {
"loginWithSso": "Sign in with SSO", "loginWithSso": "使用 SSO 登录",
"loginWithProvider": "使用{{name}}登录", "loginWithProvider": "使用{{name}}登录",
"loading": "加载中...", "loading": "加载中...",
"permissions": { "permissions": {
"manage": { "manage": {
"title": "Manage SSO providers", "title": "管理 SSO 提供商",
"description": "Add, change and remove the SSO providers people can sign in with." "description": "添加、修改和移除可供用户登录的 SSO 提供商。"
} }
}, },
"providers": { "providers": {
@@ -16,8 +16,8 @@
"delete": "删除提供商", "delete": "删除提供商",
"deleteConfirm": "删除此提供商?关联用户将无法登录。", "deleteConfirm": "删除此提供商?关联用户将无法登录。",
"name": "显示名称", "name": "显示名称",
"namePlaceholder": "My SSO provider", "namePlaceholder": "我的 SSO 提供商",
"nameRequired": "A display name is required", "nameRequired": "必须填写显示名称",
"type": "提供商类型", "type": "提供商类型",
"enabled": "已启用", "enabled": "已启用",
"save": "保存提供商", "save": "保存提供商",
@@ -26,20 +26,20 @@
"saveFailed": "保存失败", "saveFailed": "保存失败",
"deleted": "已删除", "deleted": "已删除",
"deleteFailed": "删除失败", "deleteFailed": "删除失败",
"loadFailed": "Failed to load SSO providers", "loadFailed": "加载 SSO 提供商失败",
"cancel": "取消", "cancel": "取消",
"copy": "复制", "copy": "复制",
"copied": "已复制", "copied": "已复制",
"redirectUri": "Redirect URI", "redirectUri": "重定向 URI",
"redirectUriDesc": "Register this URL as the redirect or callback URL in your identity provider.", "redirectUriDesc": "在身份提供商处将此 URL 登记为重定向或回调 URL。",
"legacyCallback": "Keep the old redirect URI", "legacyCallback": "保留旧版重定向 URI",
"legacyCallbackDesc": "This provider was set up before Termix 2.9 and still uses the old redirect URI, which keeps working. To move to the new one, add {{uri}} in your identity provider first, then turn this off.", "legacyCallbackDesc": "此提供商在 Termix 2.9 之前配置,仍使用可正常工作的旧重定向 URI。如需迁移,请先在身份提供商处添加 {{uri}},再关闭此选项。",
"authorizationUrl": "Authorization URL: {{url}}" "authorizationUrl": "授权 URL:{{url}}"
}, },
"fields": { "fields": {
"clientId": "客户端 ID", "clientId": "客户端 ID",
"clientSecret": "客户端密钥", "clientSecret": "客户端密钥",
"secretKeep": "A secret is saved. Leave this empty to keep it.", "secretKeep": "已保存机密值,留空可保留现有值。",
"issuerUrl": "颁发者 URL", "issuerUrl": "颁发者 URL",
"authUrl": "授权 URL", "authUrl": "授权 URL",
"tokenUrl": "令牌 URL", "tokenUrl": "令牌 URL",
@@ -58,8 +58,11 @@
}, },
"settings": { "settings": {
"silentLoginDefault": { "silentLoginDefault": {
"label": "Sign in with SSO automatically", "label": "自动使用 SSO 登录",
"description": "Send visitors straight to the first SSO provider instead of showing the login form. The OIDC_SILENT_LOGIN_DEFAULT environment variable overrides this." "description": "将访客直接跳转到第一个 SSO 提供商,而不显示登录表单。OIDC_SILENT_LOGIN_DEFAULT 环境变量会覆盖此设置。"
} }
},
"plugin": {
"name": "单点登录"
} }
} }
+3
View File
@@ -44,5 +44,8 @@
"label": "Use the old redirect URI", "label": "Use the old redirect URI",
"description": "Keep sending /host/step-ca-callback, the redirect URI used before 2.9. Turn this off once your identity provider allows the new one." "description": "Keep sending /host/step-ca-callback, the redirect URI used before 2.9. Turn this off once your identity provider allows the new one."
} }
},
"plugin": {
"name": "Step CA"
} }
} }
+18 -15
View File
@@ -4,12 +4,12 @@
}, },
"editor": { "editor": {
"label": "Step CA", "label": "Step CA",
"description": "Sign in through your identity provider and let your Step CA issue a short-lived SSH certificate for this host. The CA URL, root fingerprint and OIDC provisioner are set by an administrator in the Step CA section of Admin Settings.", "description": "通过身份提供商登录,由 Step CA 为此主机签发短期 SSH 证书。CA URL、根证书指纹和 OIDC 签发配置由管理员在管理设置的 Step CA 部分配置。",
"docs": "查看文档" "docs": "查看文档"
}, },
"dialog": { "dialog": {
"title": "Step CA Authentication Required", "title": "需要 Step CA 身份验证",
"description": "Complete authentication in your browser to continue. The certificate stays valid for as long as your CA allows.", "description": "请在浏览器中完成验证以继续。证书有效期由你的 CA 决定。",
"openBrowser": "打开浏览器进行认证", "openBrowser": "打开浏览器进行认证",
"waiting": "等待浏览器认证...", "waiting": "等待浏览器认证...",
"authenticating": "正在处理认证..." "authenticating": "正在处理认证..."
@@ -20,29 +20,32 @@
}, },
"settings": { "settings": {
"caUrl": { "caUrl": {
"label": "CA URL", "label": "CA 地址",
"description": "The https URL of your smallstep CA. Leave the CA URL, fingerprint and provisioner empty to turn Step CA off.", "description": "你的 smallstep CA 的 HTTPS URL。将 CA URL、指纹和签发配置留空可关闭 Step CA。",
"placeholder": "https://ca.internal:9000" "placeholder": "https://ca.internal:9000"
}, },
"fingerprint": { "fingerprint": {
"label": "Root fingerprint (SHA-256)", "label": "根证书指纹(SHA-256)",
"description": "The root certificate fingerprint, as shown by step ca bootstrap." "description": "根证书指纹,可通过 step ca bootstrap 查看。"
}, },
"provisioner": { "provisioner": {
"label": "OIDC provisioner name", "label": "OIDC 签发配置名称",
"description": "The name of the CA's OIDC provisioner. Then choose \"Step CA\" as a host's authentication type." "description": "CA 的 OIDC 签发配置名称。之后可将主机的身份验证类型设为“Step CA”。"
}, },
"privateEndpoints": { "privateEndpoints": {
"label": "Allowed private Step CA hosts", "label": "允许的私有 Step CA 主机",
"description": "Private hosts the Step CA certificate flow may contact: the CA itself and, if internal, your identity provider. Separate them with commas or new lines." "description": "Step CA 证书流程可以访问的内网主机:CA 本身,以及部署在内网的身份提供商。使用逗号或换行分隔。"
}, },
"redirectUri": { "redirectUri": {
"label": "Redirect URI", "label": "重定向 URI",
"description": "Register this URL as an allowed redirect URI with the identity provider behind the OIDC provisioner." "description": "在 OIDC 签发配置使用的身份提供商处,将此 URL 登记为允许的重定向 URI。"
}, },
"legacyCallback": { "legacyCallback": {
"label": "Use the old redirect URI", "label": "使用旧版重定向 URI",
"description": "Keep sending /host/step-ca-callback, the redirect URI used before 2.9. Turn this off once your identity provider allows the new one." "description": "继续发送 2.9 之前使用的重定向 URI /host/step-ca-callback。当身份提供商允许新 URI 后,可关闭此选项。"
} }
},
"plugin": {
"name": "Step CA"
} }
} }
+3
View File
@@ -82,5 +82,8 @@
"usernameLabel": "Username" "usernameLabel": "Username"
} }
} }
},
"plugin": {
"name": "Tailscale"
} }
} }
+27 -24
View File
@@ -1,66 +1,66 @@
{ {
"permissions": { "permissions": {
"devices.view": { "devices.view": {
"title": "View tailnet devices", "title": "查看 tailnet 设备",
"description": "See the devices on the connected tailnet." "description": "查看已连接 tailnet 中的设备。"
} }
}, },
"settings": { "settings": {
"group": { "group": {
"api": "Tailscale API", "api": "Tailscale 接口",
"devices": "Tailnet" "devices": "tailnet 网络"
}, },
"apiKey": { "apiKey": {
"label": "API 密钥", "label": "API 密钥",
"description": "Used to list the devices on your tailnet. Works with a Tailscale or Headscale key.", "description": "用于列出 tailnet 中的设备,支持 Tailscale 或 Headscale 密钥。",
"placeholder": "tskey-api-... / hskey-api-..." "placeholder": "tskey-api-... / hskey-api-..."
}, },
"apiBaseUrl": { "apiBaseUrl": {
"label": "API base URL", "label": "API 基础 URL",
"description": "Leave empty for Tailscale. Set this to point at a Headscale instance.", "description": "使用 Tailscale 时留空;使用 Headscale 时填写其实例地址。",
"placeholder": "https://api.tailscale.com/api/v2" "placeholder": "https://api.tailscale.com/api/v2"
}, },
"devices": { "devices": {
"label": "Device list", "label": "设备列表",
"checking": "Checking the tailnet...", "checking": "正在检查 tailnet……",
"noKey": "No API key set yet.", "noKey": "尚未设置 API 密钥。",
"error": "Could not reach the tailnet. Check the API key.", "error": "无法连接 tailnet,请检查 API 密钥。",
"count": "{{count}} device reachable", "count": "{{count}} 台设备可访问",
"count_other": "{{count}} devices reachable", "count_other": "{{count}} 台设备可访问",
"refresh": "Check" "refresh": "检查"
} }
}, },
"nav": { "nav": {
"tailscale": "Tailscale" "tailscale": "Tailscale"
}, },
"hosts": { "hosts": {
"tailscaleUsernameHint": "This must be a Unix user your Tailscale identity is granted in the tailnet's SSH ACL, not necessarily root.", "tailscaleUsernameHint": "此处必须使用 tailnet 的 SSH ACL 授予你的 Tailscale 身份的 Unix 用户,不一定是 root。",
"tailscaleDeviceSelect": "选择 Tailscale 设备", "tailscaleDeviceSelect": "选择 Tailscale 设备",
"tailscaleDeviceSelectPlaceholder": "选择设备...", "tailscaleDeviceSelectPlaceholder": "选择设备...",
"tailscaleNoApiKey": "未配置 Tailscale API 密钥。请在管理设置中添加一个以启用设备发现。", "tailscaleNoApiKey": "未配置 Tailscale API 密钥。请在管理设置中添加一个以启用设备发现。",
"tailscaleDocsLink": "查看文档", "tailscaleDocsLink": "查看文档",
"tailscaleLoadingDevices": "正在加载设备...", "tailscaleLoadingDevices": "正在加载设备...",
"tailscaleDeviceLoadFailed": "Failed to load Tailscale devices. Check the API URL and network connection.", "tailscaleDeviceLoadFailed": "加载 Tailscale 设备失败,请检查 API URL 和网络连接。",
"tailscaleNoDevices": "在您的 tailnet 中未找到设备。", "tailscaleNoDevices": "在您的 tailnet 中未找到设备。",
"tailscaleDeviceAutoFill": "选择设备将自动填充主机 IP 地址。", "tailscaleDeviceAutoFill": "选择设备将自动填充主机 IP 地址。",
"tailscaleAddHost": "Add host", "tailscaleAddHost": "添加主机",
"tailscaleCopyIp": "Copy Tailscale IP", "tailscaleCopyIp": "复制 Tailscale IP",
"tailscaleIpCopied": "IP copied", "tailscaleIpCopied": "IP 已复制",
"tailscaleIpCopyFailed": "Could not copy the IP", "tailscaleIpCopyFailed": "无法复制 IP",
"filterAuthTailscale": "Tailscale" "filterAuthTailscale": "Tailscale"
}, },
"terminal": { "terminal": {
"tailscaleCheckRequired": "Tailscale Authentication Required", "tailscaleCheckRequired": "需要 Tailscale 身份验证",
"tailscaleCheckDescription": "Tailscale SSH requires an additional check. Authenticate in your browser to continue.", "tailscaleCheckDescription": "Tailscale SSH 需要额外检查,请在浏览器中完成验证以继续。",
"tailscaleCheckOpenBrowser": "打开浏览器进行认证", "tailscaleCheckOpenBrowser": "打开浏览器进行认证",
"tailscaleCheckWaiting": "Waiting for Tailscale authentication...", "tailscaleCheckWaiting": "正在等待 Tailscale 身份验证……",
"tailscaleCheckTimeout": "Tailscale 身份验证超时,请重试。" "tailscaleCheckTimeout": "Tailscale 身份验证超时,请重试。"
}, },
"manager": { "manager": {
"title": "Tailscale", "title": "Tailscale",
"refresh": "刷新", "refresh": "刷新",
"retry": "重试", "retry": "重试",
"sudoHint": "Set a sudo password for this host to allow elevated actions.", "sudoHint": "为此主机设置 sudo 密码,以允许需要提升权限的操作。",
"noData": "暂无数据", "noData": "暂无数据",
"filter": "筛选...", "filter": "筛选...",
"tsNotInstalled": "此主机未安装 Tailscale", "tsNotInstalled": "此主机未安装 Tailscale",
@@ -82,5 +82,8 @@
"usernameLabel": "用户名" "usernameLabel": "用户名"
} }
} }
},
"plugin": {
"name": "Tailscale"
} }
} }
+3
View File
@@ -54,5 +54,8 @@
"previewDescription": "This is exactly what would be sent right now.", "previewDescription": "This is exactly what would be sent right now.",
"close": "Close", "close": "Close",
"loadFailed": "Could not load the usage statistics status" "loadFailed": "Could not load the usage statistics status"
},
"plugin": {
"name": "Usage Statistics"
} }
} }
+37 -34
View File
@@ -1,58 +1,61 @@
{ {
"permissions": { "permissions": {
"manage": { "manage": {
"title": "Manage usage statistics", "title": "管理使用统计",
"description": "See what usage statistics are sent, send them now and reset the instance ID." "description": "查看将发送的使用统计、立即发送统计以及重置实例 ID。"
} }
}, },
"settings": { "settings": {
"enabled": { "enabled": {
"label": "Share anonymous usage statistics", "label": "共享匿名使用统计",
"description": "Sends a small anonymous summary once a day to help improve Termix. It never includes usernames, hostnames, IP addresses or credentials." "description": "每天发送一份简短的匿名汇总,以帮助改进 Termix。不会包含用户名、主机名、IP 地址或凭据。"
}, },
"includePlatform": { "includePlatform": {
"label": "Include platform info", "label": "包含平台信息",
"description": "The operating system, CPU architecture, Node.js version, database type and how Termix is installed (Docker, desktop app or server)." "description": "操作系统、CPU 架构、Node.js 版本、数据库类型和 Termix 安装方式(Docker、桌面应用或服务器)。"
}, },
"includeFeatureUsage": { "includeFeatureUsage": {
"label": "Include feature usage", "label": "包含功能使用情况",
"description": "How many times each kind of tab was opened and how many SSH logins happened since the last report. Users can turn off their own part in their profile." "description": "自上次报告以来各类标签页的打开次数和 SSH 登录次数。用户可在个人资料中关闭自己的统计。"
}, },
"includePlugins": { "includePlugins": {
"label": "Include installed features", "label": "包含已安装功能",
"description": "Which built-in features are installed and running." "description": "已安装并运行的内置功能。"
}, },
"instanceId": { "instanceId": {
"label": "Instance ID" "label": "实例 ID"
}, },
"shareFeatureUsage": { "shareFeatureUsage": {
"label": "Include my feature usage", "label": "包含我的功能使用情况",
"description": "Counts which kinds of tabs you open in the anonymous usage statistics. Nothing about you or your hosts is sent." "description": "在匿名使用统计中记录你打开的标签页类型,不会发送有关你或你的主机的信息。"
} }
}, },
"status": { "status": {
"title": "状态", "title": "状态",
"on": "Sharing usage statistics once a day", "on": "每天共享一次使用统计",
"off": "Not sharing usage statistics", "off": "不共享使用统计",
"lockedOn": "Turned on by the ENABLE_TELEMETRY environment variable. Change it there.", "lockedOn": "已通过 ENABLE_TELEMETRY 环境变量开启,请修改该变量以更改设置。",
"lockedOff": "Turned off by the ENABLE_TELEMETRY environment variable. Change it there.", "lockedOff": "已通过 ENABLE_TELEMETRY 环境变量关闭,请修改该变量以更改设置。",
"instanceId": "Instance ID: {{id}}", "instanceId": "实例 ID:{{id}}",
"noInstanceId": "No instance ID yet", "noInstanceId": "尚无实例 ID",
"lastSent": "Last sent {{date}}", "lastSent": "上次发送时间:{{date}}",
"neverSent": "Not sent yet", "neverSent": "尚未发送",
"nextSend": "Next report due {{date}}", "nextSend": "下次报告时间:{{date}}",
"lastError": "Last attempt failed: {{error}}", "lastError": "上次尝试失败:{{error}}",
"preview": "Preview report", "preview": "预览报告",
"sendNow": "Send now", "sendNow": "立即发送",
"sending": "Sending...", "sending": "发送中……",
"sent": "Usage statistics sent", "sent": "使用统计已发送",
"notSent": "Usage statistics are turned off, nothing was sent", "notSent": "使用统计已关闭,未发送任何内容",
"resetId": "Reset instance ID", "resetId": "重置实例 ID",
"resetIdConfirm": "Reset the instance ID? Future reports will look like a new install.", "resetIdConfirm": "重置实例 ID?后续报告将被视为来自新的安装实例。",
"resetIdDone": "Instance ID reset", "resetIdDone": "实例 ID 已重置",
"previewTitle": "Next report", "previewTitle": "下次报告",
"previewDescription": "This is exactly what would be sent right now.", "previewDescription": "这是现在发送时将包含的完整内容。",
"close": "关闭", "close": "关闭",
"loadFailed": "Could not load the usage statistics status" "loadFailed": "无法加载使用统计状态"
},
"plugin": {
"name": "使用统计"
} }
} }
@@ -23,7 +23,7 @@
"provisionLabel": "配置服务器", "provisionLabel": "配置服务器",
"publishTitle": "发布公钥", "publishTitle": "发布公钥",
"generate": "生成", "generate": "生成",
"generateTooltip": "Generate an Ed25519 key pair. Publishes the public key and downloads the private key to your device.", "generateTooltip": "生成 Ed25519 密钥对,发布公钥并将私钥下载到你的设备。",
"saveToVault": "保存到凭据", "saveToVault": "保存到凭据",
"keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host", "keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host",
"labelPlaceholder": "标签(可选)", "labelPlaceholder": "标签(可选)",
@@ -32,7 +32,7 @@
"keyPublished": "密钥已发布", "keyPublished": "密钥已发布",
"addKeyFailed": "添加密钥失败", "addKeyFailed": "添加密钥失败",
"generatedSaved": "密钥对已生成并保存到您的凭据库。私钥也已下载。", "generatedSaved": "密钥对已生成并保存到您的凭据库。私钥也已下载。",
"generatedOnly": "Key pair generated. Private key downloaded (shown only once).", "generatedOnly": "密钥对已生成,私钥已下载(仅显示一次)。",
"generateFailed": "密钥生成失败", "generateFailed": "密钥生成失败",
"imported": "密钥已从凭据导入", "imported": "密钥已从凭据导入",
"importFailed": "导入密钥失败", "importFailed": "导入密钥失败",
@@ -56,7 +56,7 @@
"caTrustLabel": "在服务器上信任(以 root 身份运行)", "caTrustLabel": "在服务器上信任(以 root 身份运行)",
"caRotate": "轮换", "caRotate": "轮换",
"caRotateConfirm": "轮换 CA?其已签名的所有证书将停止被接受,必须重新签发。", "caRotateConfirm": "轮换 CA?其已签名的所有证书将停止被接受,必须重新签发。",
"caRotated": "CA rotated. Previous certificates revoked.", "caRotated": "CA 已轮换,先前签发的证书已撤销。",
"caRotateFailed": "轮换 CA 失败", "caRotateFailed": "轮换 CA 失败",
"caDelete": "移除 CA", "caDelete": "移除 CA",
"caDeleteConfirm": "移除证书颁发机构?", "caDeleteConfirm": "移除证书颁发机构?",
@@ -68,8 +68,8 @@
"certIssued": "证书已签发并下载", "certIssued": "证书已签发并下载",
"certIssueFailed": "签发证书失败", "certIssueFailed": "签发证书失败",
"docsLink": "查看文档", "docsLink": "查看文档",
"principalsLabel": "SSH principals", "principalsLabel": "SSH 身份主体",
"principalsPlaceholder": "SSH principal(s), e.g. root" "principalsPlaceholder": "SSH 身份主体,例如 root"
}, },
"nav": { "nav": {
"termixId": "ID" "termixId": "ID"
@@ -79,8 +79,8 @@
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Use Termix ID", "title": "使用 Termix ID",
"description": "Claim a public handle, publish SSH keys and run a certificate authority." "description": "注册公开用户名、发布 SSH 密钥并运行证书颁发机构。"
} }
} }
} }
@@ -14,19 +14,19 @@
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Use Tmux Monitor", "title": "使用 tmux 监控",
"description": "Browse and control tmux sessions, windows and panes on hosts with the monitor enabled." "description": "在启用了监控的主机上浏览和控制 tmux 会话、窗口和窗格。"
} }
}, },
"settings": { "settings": {
"host": { "host": {
"enable": { "enable": {
"label": "启用 Tmux 监控", "label": "启用 Tmux 监控",
"description": "Watch and attach to tmux sessions running on this host." "description": "监控并连接此主机上运行的 tmux 会话。"
}, },
"mouseEnabled": { "mouseEnabled": {
"label": "Enable tmux mouse support", "label": "启用 tmux 鼠标支持",
"description": "Apply mouse support to the tmux session when attaching or creating a session. Other sessions and global tmux settings are unchanged." "description": "连接或创建 tmux 会话时为该会话启用鼠标支持,不更改其他会话和 tmux 全局设置。"
} }
} }
}, },
@@ -73,7 +73,7 @@
"sessionRenameFailed": "重命名会话失败", "sessionRenameFailed": "重命名会话失败",
"editTagsTitle": "编辑 \"{{name}}\" 的标签", "editTagsTitle": "编辑 \"{{name}}\" 的标签",
"killPane": "终止窗格", "killPane": "终止窗格",
"resizeTree": "Drag to resize, double-click to reset", "resizeTree": "拖动调整大小,双击重置",
"reattach": "重新附加(修复显示错乱)", "reattach": "重新附加(修复显示错乱)",
"killWindow": "终止窗口", "killWindow": "终止窗口",
"killWindowTitle": "终止会话 \"{{session}}\" 的窗口 {{index}}?", "killWindowTitle": "终止会话 \"{{session}}\" 的窗口 {{index}}?",
@@ -93,25 +93,25 @@
"sessionKillFailed": "终止会话失败", "sessionKillFailed": "终止会话失败",
"hostUnreachable": "无法连接到主机。请检查主机是否在线且可访问。", "hostUnreachable": "无法连接到主机。请检查主机是否在线且可访问。",
"noServer": "此主机上未运行 tmux 服务器。", "noServer": "此主机上未运行 tmux 服务器。",
"searchTruncated": "Partial results. Search covers the last {{lines}} lines of each pane and at most {{panes}} panes.", "searchTruncated": "结果不完整。搜索仅覆盖每个窗格最近的 {{lines}} 行,最多搜索 {{panes}} 个窗格。",
"closeSearchResults": "关闭搜索结果", "closeSearchResults": "关闭搜索结果",
"retry": "重试", "retry": "重试",
"noHosts": "没有可用的 SSH 主机", "noHosts": "没有可用的 SSH 主机",
"noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager, Tmux Monitor tab) to monitor its tmux sessions.", "noHostsHint": "在 SSH 主机的主机管理器中,打开“tmux 监控”标签页并启用监控选项,即可监控其 tmux 会话。",
"tmuxInstallHint": "使用以下命令在主机上安装:", "tmuxInstallHint": "使用以下命令在主机上安装:",
"attachTooltip": "打开到 {{host}} 的终端", "attachTooltip": "打开到 {{host}} 的终端",
"attachTooltipPane": "Open a terminal to {{host}}, tmux session {{session}}", "attachTooltipPane": "打开连接到 {{host}} 的终端,使用 tmux 会话 {{session}}",
"timeJustNow": "刚刚", "timeJustNow": "刚刚",
"timeMinutes": "{{count}} 分钟前", "timeMinutes": "{{count}} 分钟前",
"timeHours": "{{count}} 小时前", "timeHours": "{{count}} 小时前",
"timeDays": "{{count}} 天前", "timeDays": "{{count}} 天前",
"allHosts": "所有主机", "allHosts": "所有主机",
"singleHost": "Single host", "singleHost": "单台主机",
"hostScope": "Monitor scope", "hostScope": "监控范围",
"filterHosts": "Filter hosts by name or address", "filterHosts": "按名称或地址筛选主机",
"selectHost": "Select host", "selectHost": "选择主机",
"toggleHost": "Expand or collapse {{host}}", "toggleHost": "展开或折叠 {{host}}",
"noMatchingHosts": "No matching hosts", "noMatchingHosts": "没有匹配的主机",
"toggleSession": "Expand or collapse {{session}}" "toggleSession": "展开或折叠 {{session}}"
} }
} }
+12 -12
View File
@@ -1,10 +1,10 @@
{ {
"title": "Authenticator App", "title": "身份验证器应用",
"factor": "Authenticator code", "factor": "身份验证器验证码",
"challenge": { "challenge": {
"enterCode": "输入验证码", "enterCode": "输入验证码",
"verify": "验证", "verify": "验证",
"backupCodeHint": "You can also enter one of your backup codes." "backupCodeHint": "你也可以输入备用验证码。"
}, },
"enrollment": { "enrollment": {
"title": "TOTP 身份验证器", "title": "TOTP 身份验证器",
@@ -13,19 +13,19 @@
"off": "添加额外的登录安全", "off": "添加额外的登录安全",
"enable": "启用", "enable": "启用",
"disable": "禁用", "disable": "禁用",
"addDevice": "Add device", "addDevice": "添加设备",
"addTitle": "Add authenticator device", "addTitle": "添加身份验证器设备",
"addDescription": "Confirm with a code from your authenticator or a backup code to enroll another device with the same key.", "addDescription": "使用身份验证器验证码或备用验证码确认,以便使用同一密钥添加另一台设备。",
"addScanInstructions": "Scan this QR code with the additional authenticator. Existing authenticators will continue to work.", "addScanInstructions": "使用新增身份验证器扫描此二维码,已有身份验证器仍可继续使用。",
"addSuccess": "Additional authenticator enrolled", "addSuccess": "已添加新的身份验证器",
"addFailed": "Failed to add authenticator", "addFailed": "添加身份验证器失败",
"codeRequired": "Enter a TOTP code or backup code", "codeRequired": "输入 TOTP 验证码或备用验证码",
"codePlaceholder": "TOTP code or backup code", "codePlaceholder": "TOTP 验证码或备用验证码",
"setupTitle": "设置 TOTP", "setupTitle": "设置 TOTP",
"setupFailed": "启动 TOTP 设置失败", "setupFailed": "启动 TOTP 设置失败",
"qrCode": "二维码", "qrCode": "二维码",
"secretCopied": "密钥已复制到剪贴板", "secretCopied": "密钥已复制到剪贴板",
"instructions": "Scan the QR code or enter the secret in your authenticator app, then enter the 6-digit code", "instructions": "使用身份验证器应用扫描二维码或输入密钥,然后输入 6 位验证码",
"continueVerify": "继续验证", "continueVerify": "继续验证",
"verifyTitle": "验证", "verifyTitle": "验证",
"verify": "验证", "verify": "验证",
+24 -24
View File
@@ -33,7 +33,7 @@
"loadingHost": "正在加载主机..." "loadingHost": "正在加载主机..."
}, },
"nav": { "nav": {
"portForwarding": "Forwarding", "portForwarding": "端口转发",
"tunnels": "隧道" "tunnels": "隧道"
}, },
"placeholders": { "placeholders": {
@@ -42,8 +42,8 @@
"localListenerHost": "127.0.0.1 (本地监听)", "localListenerHost": "127.0.0.1 (本地监听)",
"localTargetHost": "127.0.0.1 (此计算机上的目标)", "localTargetHost": "127.0.0.1 (此计算机上的目标)",
"maxRetries": "3", "maxRetries": "3",
"remoteBindHost": "127.0.0.1 (listen on endpoint)", "remoteBindHost": "127.0.0.1(在端点上监听)",
"remoteTargetHost": "127.0.0.1 (target from endpoint)", "remoteTargetHost": "127.0.0.1(从端点访问的目标地址)",
"retryInterval": "10", "retryInterval": "10",
"socksListenerHost": "127.0.0.1(SOCKS 监听器)" "socksListenerHost": "127.0.0.1(SOCKS 监听器)"
}, },
@@ -63,8 +63,8 @@
"c2sPresetToLoad": "要加载的预设", "c2sPresetToLoad": "要加载的预设",
"c2sTunnelPresets": "客户端隧道预设", "c2sTunnelPresets": "客户端隧道预设",
"c2sTunnelPresetsUnavailable": "客户端隧道预设仅在桌面客户端中可用。", "c2sTunnelPresetsUnavailable": "客户端隧道预设仅在桌面客户端中可用。",
"c2sPresetRenameError": "Failed to rename the preset", "c2sPresetRenameError": "重命名预设失败",
"c2sPresetDeleteError": "Failed to delete the preset" "c2sPresetDeleteError": "删除预设失败"
}, },
"tunnels": { "tunnels": {
"attempt": "尝试 {{current}}/{{max}}", "attempt": "尝试 {{current}}/{{max}}",
@@ -85,8 +85,8 @@
"createFirstTunnelMessage": "您尚未创建任何 SSH 隧道。在主机管理器中配置隧道连接以开始使用。", "createFirstTunnelMessage": "您尚未创建任何 SSH 隧道。在主机管理器中配置隧道连接以开始使用。",
"currentHostPort": "当前主机端口", "currentHostPort": "当前主机端口",
"destination": "目标", "destination": "目标",
"destinationAddress": "Destination Address", "destinationAddress": "目标地址",
"destinationPort": "Destination Port", "destinationPort": "目标端口",
"disconnected": "已断开", "disconnected": "已断开",
"duplicateAutoStartBind": "只有一个自启动客户端隧道可以使用 {{bind}}。", "duplicateAutoStartBind": "只有一个自启动客户端隧道可以使用 {{bind}}。",
"endpointPort": "端点端口", "endpointPort": "端点端口",
@@ -101,18 +101,18 @@
"forwardDescriptionServerLocal": "当前主机 {{sourcePort}} → 端点 {{endpointPort}}。", "forwardDescriptionServerLocal": "当前主机 {{sourcePort}} → 端点 {{endpointPort}}。",
"forwardDescriptionServerRemote": "端点 {{endpointPort}} → 当前主机 {{sourcePort}}。", "forwardDescriptionServerRemote": "端点 {{endpointPort}} → 当前主机 {{sourcePort}}。",
"host": "主机", "host": "主机",
"intermediateHost": "Intermediate Host", "intermediateHost": "中间主机",
"invalidBindIp": "本地 IP 必须是有效的 IPv4 地址。", "invalidBindIp": "本地 IP 必须是有效的 IPv4 地址。",
"invalidEndpointPort": "端点端口必须在 1 到 65535 之间。", "invalidEndpointPort": "端点端口必须在 1 到 65535 之间。",
"invalidLocalPort": "本地端口必须在 1 到 65535 之间。", "invalidLocalPort": "本地端口必须在 1 到 65535 之间。",
"invalidLocalTargetIp": "本地目标 IP 必须是有效的 IPv4 地址。", "invalidLocalTargetIp": "本地目标 IP 必须是有效的 IPv4 地址。",
"invalidLocalTargetPort": "本地目标端口必须在 1 到 65535 之间。", "invalidLocalTargetPort": "本地目标端口必须在 1 到 65535 之间。",
"invalidRemoteAddress": "Remote address is required.", "invalidRemoteAddress": "必须填写远程地址。",
"invalidRemotePort": "远程端口必须在 1 到 65535 之间。", "invalidRemotePort": "远程端口必须在 1 到 65535 之间。",
"lastError": "最后错误", "lastError": "最后错误",
"lastStarted": "最后启动", "lastStarted": "最后启动",
"lastTested": "最后测试", "lastTested": "最后测试",
"localAddress": "Local Address", "localAddress": "本地地址",
"localPort": "本地端口", "localPort": "本地端口",
"localSaveError": "保存本地客户端隧道失败", "localSaveError": "保存本地客户端隧道失败",
"localSaved": "客户端隧道已保存", "localSaved": "客户端隧道已保存",
@@ -124,14 +124,14 @@
"noHostSelected": "未选择主机", "noHostSelected": "未选择主机",
"noSshTunnels": "无 SSH 隧道", "noSshTunnels": "无 SSH 隧道",
"port": "端口", "port": "端口",
"remoteBindAddress": "Remote Bind Address", "remoteBindAddress": "远程绑定地址",
"remoteHost": "远程主机", "remoteHost": "远程主机",
"remotePort": "远程端口", "remotePort": "远程端口",
"retryInterval": "重试间隔(秒)", "retryInterval": "重试间隔(秒)",
"route": "路由:", "route": "路由:",
"summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS 通过 {{endpoint}}", "summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS 通过 {{endpoint}}",
"summaryClientLocal": "{{localHost}}:{{localPort}} → {{remoteHost}}:{{remotePort}} via {{endpoint}}", "summaryClientLocal": "{{localHost}}:{{localPort}} → {{remoteHost}}:{{remotePort}},经由 {{endpoint}}",
"summaryClientRemote": "{{remoteHost}}:{{remotePort}} on {{endpoint}} → {{localHost}}:{{localPort}}", "summaryClientRemote": "{{endpoint}} 上的 {{remoteHost}}:{{remotePort}} → {{localHost}}:{{localPort}}",
"test": "测试", "test": "测试",
"tunnelName": "隧道名称", "tunnelName": "隧道名称",
"tunnelTestFailed": "隧道测试失败", "tunnelTestFailed": "隧道测试失败",
@@ -141,30 +141,30 @@
"typeLocal": "本地 (-L)", "typeLocal": "本地 (-L)",
"typeRemote": "远程 (-R)", "typeRemote": "远程 (-R)",
"working": "处理中...", "working": "处理中...",
"activeCount": "{{connected}}/{{total}} active", "activeCount": "{{connected}}/{{total}} 个处于活动状态",
"listensOn": "Bound to localhost:{{port}}", "listensOn": "绑定到 localhost:{{port}}",
"socksProxy": "SOCKS5 proxy", "socksProxy": "SOCKS5 代理",
"waiting": "等待", "waiting": "等待",
"remoteSessionExpired": "Remote Sync session expired. Reconnect Remote Sync and try again.", "remoteSessionExpired": "远程同步会话已过期,请重新连接远程同步后再试。",
"activeTunnels": "活跃隧道", "activeTunnels": "活跃隧道",
"start": "启动", "start": "启动",
"stop": "停止", "stop": "停止",
"authTitle": "Tunnel authentication", "authTitle": "隧道身份验证",
"authRetry": "The previous code was not accepted. Enter a new code.", "authRetry": "上一个验证码未通过,请输入新的验证码。",
"authAnswer": "Authentication response", "authAnswer": "身份验证响应",
"authContinue": "继续" "authContinue": "继续"
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Use tunnels", "title": "使用隧道",
"description": "Start, stop and watch SSH tunnels, and save client tunnel presets." "description": "启动、停止和监控 SSH 隧道,并保存客户端隧道预设。"
} }
}, },
"settings": { "settings": {
"host": { "host": {
"enable": { "enable": {
"label": "Enable tunnels", "label": "启用隧道",
"description": "Set up SSH tunnels through this host." "description": "通过此主机建立 SSH 隧道。"
} }
} }
}, },
+3
View File
@@ -65,5 +65,8 @@
"label": "Vault signer profile" "label": "Vault signer profile"
} }
} }
},
"plugin": {
"name": "HashiCorp Vault"
} }
} }
+12 -9
View File
@@ -43,27 +43,30 @@
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Use Vault SSH signing", "title": "使用 Vault SSH 签名",
"description": "Create Vault signer profiles and connect to hosts that use Vault." "description": "创建 Vault 签名配置,并连接使用 Vault 的主机。"
}, },
"share": { "share": {
"title": "Share Vault profiles", "title": "共享 Vault 配置",
"description": "Mark a Vault signer profile as shared so every user can pick it." "description": "将 Vault 签名配置标记为共享,以供所有用户选择。"
} }
}, },
"settings": { "settings": {
"redirectUri": { "redirectUri": {
"label": "Redirect URI", "label": "重定向 URI",
"description": "Add this URL to allowed_redirect_uris in each Vault OIDC role that Termix signs in with." "description": "在 Termix 登录所用的每个 Vault OIDC 角色中,将此 URL 添加到 allowed_redirect_uris。"
}, },
"legacyCallback": { "legacyCallback": {
"label": "Use the old redirect URI", "label": "使用旧版重定向 URI",
"description": "Keep sending /vault/oidc/callback, the redirect URI used before 2.9. Turn this off once your Vault roles allow the new one." "description": "继续发送 2.9 之前使用的重定向 URI /vault/oidc/callback。当 Vault 角色允许新 URI 后,可关闭此选项。"
}, },
"host": { "host": {
"profileId": { "profileId": {
"label": "Vault signer profile" "label": "Vault 签名配置"
} }
} }
},
"plugin": {
"name": "HashiCorp Vault"
} }
} }
@@ -1,25 +1,25 @@
{ {
"hostAction": { "hostAction": {
"title": "Wake", "title": "唤醒",
"sent": "Wake-on-LAN packet sent", "sent": "网络唤醒数据包已发送",
"failed": "Failed to send Wake-on-LAN packet" "failed": "发送网络唤醒数据包失败"
}, },
"permissions": { "permissions": {
"send": { "send": {
"title": "Send Wake-on-LAN packets", "title": "发送网络唤醒数据包",
"description": "Wake a host by sending a magic packet to its MAC address." "description": "向主机的 MAC 地址发送魔术包以唤醒主机。"
} }
}, },
"settings": { "settings": {
"host": { "host": {
"macAddress": { "macAddress": {
"label": "MAC address", "label": "MAC 地址",
"description": "The host's network MAC address, used to send Wake-on-LAN packets.", "description": "主机的网络 MAC 地址,用于发送网络唤醒数据包。",
"placeholder": "AA:BB:CC:DD:EE:FF" "placeholder": "AA:BB:CC:DD:EE:FF"
}, },
"broadcastAddress": { "broadcastAddress": {
"label": "Broadcast address", "label": "广播地址",
"description": "The subnet broadcast address to send the packet to. Leave blank for 255.255.255.255.", "description": "发送数据包所用的子网广播地址,留空则使用 255.255.255.255。",
"placeholder": "192.168.1.255" "placeholder": "192.168.1.255"
} }
} }
@@ -1,55 +1,55 @@
{ {
"webEndpoint": { "webEndpoint": {
"noHostSelected": "未选择主机", "noHostSelected": "未选择主机",
"notFound": "This endpoint no longer exists.", "notFound": "此端点已不存在。",
"tunnelUnreachableFromBrowser": "This endpoint tunnels to 127.0.0.1 on the machine running Termix, which your browser cannot reach. Set the endpoint's Bind Host to an address that machine answers on, such as 0.0.0.0, and give it a fixed Local Port.", "tunnelUnreachableFromBrowser": "此端点的隧道绑定到运行 Termix 的机器上的 127.0.0.1,浏览器无法访问。请将端点的“绑定主机”设为该机器可接受连接的地址(如 0.0.0.0),并指定固定的本地端口。",
"tunnelSharesSessionCookie": "Opening this tunnel would send your Termix session to the tunnelled service, because your browser would reach it at the same hostname Termix is served from. Cookies ignore the port, so the two cannot be told apart. Use the Termix desktop app, or open Termix at localhost.", "tunnelSharesSessionCookie": "打开此隧道会将你的 Termix 会话发送给隧道内的服务,因为浏览器会使用与 Termix 相同的主机名访问它。Cookie 不区分端口,因此无法区分两者。请使用 Termix 桌面应用,或通过 localhost 打开 Termix。",
"directSharesSessionCookie": "Opening this endpoint would send your Termix session to it, because your browser reaches it at the same site Termix is served from. Cookies ignore the port, so a different port is not a different identity. Use the Termix desktop app, or serve this web UI from a different hostname.", "directSharesSessionCookie": "打开此端点会将你的 Termix 会话发送给它,因为浏览器通过与 Termix 相同的站点访问它。Cookie 不区分端口,更换端口无法隔离身份。请使用 Termix 桌面应用,或通过不同主机名提供此网页界面。",
"retry": "重试", "retry": "重试",
"reload": "重新加载", "reload": "重新加载",
"copyUrl": "Copy URL", "copyUrl": "复制 URL",
"urlCopied": "URL copied", "urlCopied": "URL 已复制",
"copyFailed": "Failed to copy URL", "copyFailed": "复制 URL 失败",
"openExternally": "在外部打开", "openExternally": "在外部打开",
"isolationUnavailable": "This browser cannot isolate web endpoints from your Termix session. Use a browser with credentialless iframe support.", "isolationUnavailable": "此浏览器无法将网页端点与你的 Termix 会话隔离,请使用支持 credentialless iframe 的浏览器。",
"isolationNotice": "Embedded pages use an isolated session. For pages requiring storage or popup sign-in, use an isolated window in the desktop app.", "isolationNotice": "嵌入页面使用隔离会话。对于需要存储或弹窗登录的页面,请在桌面应用中使用隔离窗口。",
"openIsolatedWindow": "Open isolated window" "openIsolatedWindow": "打开隔离窗口"
}, },
"hosts": { "hosts": {
"tabWebUi": "Web UI", "tabWebUi": "网页界面",
"webUiEndpointLabel": "Endpoint {{number}}", "webUiEndpointLabel": "端点 {{number}}",
"webUiIntegration": "Web UI", "webUiIntegration": "网页界面",
"webUiEndpointsSection": "Endpoints", "webUiEndpointsSection": "端点",
"webUiNoEndpoints": "No web endpoints yet. Use Add endpoint to create one.", "webUiNoEndpoints": "暂无网页端点,请点击“添加端点”创建。",
"webUiEndpointsDisabled": "Turn on Web UI above to add endpoints.", "webUiEndpointsDisabled": "请先在上方启用网页界面,再添加端点。",
"webUiRequirementsText": "A direct endpoint is opened by your browser, so its port must be reachable from here. An SSH tunnel is opened by Termix over this host's SSH connection instead, so the port only has to be reachable from the host.", "webUiRequirementsText": "直接访问的端点由浏览器打开,因此其端口必须可从此处访问。SSH 隧道由 Termix 通过此主机的 SSH 连接建立,因此端口只需能从该主机访问。",
"enableWebUi": "Enable web endpoints", "enableWebUi": "启用网页端点",
"enableWebUiDesc": "Open this host's web interfaces from the sidebar.", "enableWebUiDesc": "从侧边栏打开此主机的网页界面。",
"webUiAddEndpoint": "Add endpoint", "webUiAddEndpoint": "添加端点",
"webUiAddress": "地址", "webUiAddress": "地址",
"webUiOpening": "Opening", "webUiOpening": "正在打开",
"webUiBindHostDesc": "Where the backend binds the forward. Leave blank for 127.0.0.1, which only the machine running Termix can reach.", "webUiBindHostDesc": "后端绑定端口转发的地址。留空则使用 127.0.0.1,仅运行 Termix 的机器可以访问。",
"webUiBindHostExposed": "This address is reachable beyond the Termix machine, so anyone who can reach the port gets this web UI with no login.", "webUiBindHostExposed": "此地址可从 Termix 所在机器之外访问,因此任何能访问该端口的人都可以无需登录访问此网页界面。",
"webUiLocalPort": "本地端口", "webUiLocalPort": "本地端口",
"webUiLocalPortAuto": "自动", "webUiLocalPortAuto": "自动",
"webUiLocalPortDesc": "Which port the forward listens on. Leave blank to let the system pick, or set one when the port has to be known in advance, such as a container that publishes it.", "webUiLocalPortDesc": "端口转发监听的端口。留空由系统选择;需要预先确定端口时(例如容器需要发布该端口),请指定固定端口。",
"webUiNewEndpointLabel": "New endpoint", "webUiNewEndpointLabel": "新建端点",
"webUiLabel": "标签", "webUiLabel": "标签",
"webUiScheme": "Scheme", "webUiScheme": "协议",
"webUiPort": "端口", "webUiPort": "端口",
"webUiPath": "路径", "webUiPath": "路径",
"webUiAccess": "Access", "webUiAccess": "访问方式",
"webUiAccessDirect": "直接", "webUiAccessDirect": "直接",
"webUiAccessTunnel": "SSH tunnel", "webUiAccessTunnel": "SSH 隧道",
"webUiAccessTunnelUnavailable": "An SSH tunnel needs SSH enabled on this host and a connection origin of this device.", "webUiAccessTunnelUnavailable": "SSH 隧道要求此主机启用 SSH,并将连接发起位置设为此设备。",
"webUiRender": "Open in", "webUiRender": "打开方式",
"webUiRenderExternal": "Isolated desktop window", "webUiRenderExternal": "隔离的桌面窗口",
"webUiRenderEmbedded": "Termix tab", "webUiRenderEmbedded": "Termix 标签页",
"webUiRenderEmbeddedDesc": "Embedding is best effort: some web interfaces refuse to load in a frame, and a login that relies on same-site cookies may loop. The external browser always works.", "webUiRenderEmbeddedDesc": "嵌入显示无法保证兼容:某些网页界面拒绝在框架中加载,依赖同站点 Cookie 的登录也可能循环。可改用外部浏览器。",
"webUiIgnoreCert": "允许无效证书", "webUiIgnoreCert": "允许无效证书",
"webUiIgnoreCertDesc": "Direct endpoints only. Loopback is already exempt, so this has no effect on tunnel endpoints.", "webUiIgnoreCertDesc": "仅用于直接访问的端点。回环地址已被豁免,因此不会影响隧道端点。",
"webUiRemoveEndpoint": "Remove endpoint", "webUiRemoveEndpoint": "移除端点",
"webUiOpenFailed": "Failed to open the web endpoint", "webUiOpenFailed": "打开网页端点失败",
"bindHost": "绑定主机" "bindHost": "绑定主机"
} }
} }
@@ -4,7 +4,7 @@
"loginFailed": "通行密钥登录失败", "loginFailed": "通行密钥登录失败",
"description": "使用 WebAuthn/FIDO2 凭据进行无密码登录", "description": "使用 WebAuthn/FIDO2 凭据进行无密码登录",
"namePlaceholder": "通行密钥名称", "namePlaceholder": "通行密钥名称",
"defaultName": "Passkey", "defaultName": "通行密钥",
"uv": { "uv": {
"preferred": "首选", "preferred": "首选",
"required": "必需", "required": "必需",
@@ -14,9 +14,9 @@
"none": "未注册通行密钥。", "none": "未注册通行密钥。",
"added": "通行密钥已添加", "added": "通行密钥已添加",
"addFailed": "添加通行密钥失败", "addFailed": "添加通行密钥失败",
"delete": "Delete passkey", "delete": "删除通行密钥",
"deleted": "通行密钥已删除", "deleted": "通行密钥已删除",
"deleteFailed": "删除通行密钥失败", "deleteFailed": "删除通行密钥失败",
"unknownDevice": "未知", "unknownDevice": "未知",
"synced": "synced" "synced": "已同步"
} }
@@ -2,48 +2,48 @@
"newUi": { "newUi": {
"sidebar": { "sidebar": {
"workspaces": { "workspaces": {
"title": "Workspaces", "title": "工作区",
"saveCurrent": "Save Current", "saveCurrent": "保存当前布局",
"saveCurrentTitle": "Save Current Workspace", "saveCurrentTitle": "保存当前工作区",
"saveCurrentDescription": "Save every open tab, split layout, and pane arrangement as a new named workspace.", "saveCurrentDescription": "将所有打开的标签页、分屏布局和窗格排列保存为新的命名工作区。",
"nameLabel": "名称", "nameLabel": "名称",
"namePlaceholder": "e.g., Prod Debugging", "namePlaceholder": "例如:生产环境调试",
"nameRequired": "Workspace name is required", "nameRequired": "必须填写工作区名称",
"colorLabel": "颜色", "colorLabel": "颜色",
"workspaceCreated": "Workspace saved", "workspaceCreated": "工作区已保存",
"workspaceUpdated": "Workspace updated", "workspaceUpdated": "工作区已更新",
"workspaceDeleted": "Workspace deleted", "workspaceDeleted": "工作区已删除",
"workspaceDuplicated": "Workspace duplicated", "workspaceDuplicated": "工作区已复制",
"saveFailed": "Failed to save workspace", "saveFailed": "保存工作区失败",
"updateFailed": "Failed to update workspace", "updateFailed": "更新工作区失败",
"deleteFailed": "Failed to delete workspace", "deleteFailed": "删除工作区失败",
"duplicateFailed": "Failed to duplicate workspace", "duplicateFailed": "复制工作区失败",
"loadFailed": "Failed to load workspaces", "loadFailed": "加载工作区失败",
"applyFailed": "Failed to apply workspace", "applyFailed": "应用工作区失败",
"deleteWorkspaceTitle": "Delete Workspace", "deleteWorkspaceTitle": "删除工作区",
"deleteWorkspaceDescription": "Are you sure you want to delete \"{{name}}\"? This cannot be undone.", "deleteWorkspaceDescription": "确定删除“{{name}}”?此操作无法撤销。",
"noWorkspaces": "No saved workspaces yet. Save your current tab arrangement to get started.", "noWorkspaces": "尚无已保存的工作区,保存当前标签页布局即可开始使用。",
"tabCount": "{{count}} 个标签页", "tabCount": "{{count}} 个标签页",
"tabCount_other": "{{count}} 个标签页", "tabCount_other": "{{count}} 个标签页",
"apply": "应用", "apply": "应用",
"applyConfirmTitle": "Apply Workspace", "applyConfirmTitle": "应用工作区",
"applyConfirmDescription": "Applying \"{{name}}\" will close your current tabs and replace them with this workspace's saved arrangement. Unsaved changes to your current layout will be lost.", "applyConfirmDescription": "应用“{{name}}”会关闭当前标签页,并替换为此工作区保存的布局。当前布局中未保存的更改将丢失。",
"applyConfirmButton": "Apply Workspace", "applyConfirmButton": "应用工作区",
"updateWithCurrent": "Update with Current", "updateWithCurrent": "用当前布局更新",
"updateWithCurrentConfirm": "用您当前的标签页排列覆盖“{{name}}”?", "updateWithCurrentConfirm": "用您当前的标签页排列覆盖“{{name}}”?",
"rename": "重命名", "rename": "重命名",
"duplicate": "Duplicate", "duplicate": "创建副本",
"duplicateNameSuffix": "{{name}}(副本)", "duplicateNameSuffix": "{{name}}(副本)",
"setDefault": "Set as Default", "setDefault": "设为默认",
"unsetDefault": "Remove as Default", "unsetDefault": "取消默认",
"defaultBadge": "Default", "defaultBadge": "默认",
"lastSession": "上次会话", "lastSession": "上次会话",
"lastSessionDescription": "Automatically updated with your most recent tab arrangement. Apply it to recover what you had open.", "lastSessionDescription": "自动更新为最近使用的标签页布局。应用此工作区可恢复之前打开的内容。",
"lastUsed": "Last used {{time}}", "lastUsed": "上次使用:{{time}}",
"neverUsed": "从未应用过", "neverUsed": "从未应用过",
"tabsSkipped": "Workspace applied, but {{count}} tab(s) were skipped because their host no longer exists: {{names}}", "tabsSkipped": "工作区已应用,但有 {{count}} 个标签页因对应主机已不存在而被跳过:{{names}}",
"timeAgo": { "timeAgo": {
"seconds": "{{count}}s ago", "seconds": "{{count}} 秒前",
"minutes": "{{count}} 分钟前", "minutes": "{{count}} 分钟前",
"hours": "{{count}} 小时前", "hours": "{{count}} 小时前",
"days": "{{count}} 天前" "days": "{{count}} 天前"
@@ -52,16 +52,16 @@
} }
}, },
"nav": { "nav": {
"workspaces": "Workspaces" "workspaces": "工作区"
}, },
"onboarding": { "onboarding": {
"workflow_workspaces": "Workspaces", "workflow_workspaces": "工作区",
"workflow_workspaces_desc": "保存当前打开的标签页组合,方便之后一键恢复整个布局。" "workflow_workspaces_desc": "保存当前打开的标签页组合,方便之后一键恢复整个布局。"
}, },
"permissions": { "permissions": {
"use": { "use": {
"title": "Use workspaces", "title": "使用工作区",
"description": "Save, apply and manage saved tab layouts." "description": "保存、应用和管理标签页布局。"
} }
}, },
"hosts": { "hosts": {
+7 -2
View File
@@ -1,3 +1,4 @@
import { useTranslation } from "react-i18next";
import * as React from "react"; import * as React from "react";
import { Dialog as DialogPrimitive } from "radix-ui"; import { Dialog as DialogPrimitive } from "radix-ui";
@@ -53,6 +54,8 @@ function DialogContent({
}: React.ComponentProps<typeof DialogPrimitive.Content> & { }: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean; showCloseButton?: boolean;
}) { }) {
const { t } = useTranslation();
return ( return (
<DialogPortal> <DialogPortal>
<DialogOverlay /> <DialogOverlay />
@@ -73,7 +76,7 @@ function DialogContent({
size="icon-sm" size="icon-sm"
> >
<XIcon /> <XIcon />
<span className="sr-only">Close</span> <span className="sr-only">{t("common.close")}</span>
</Button> </Button>
</DialogPrimitive.Close> </DialogPrimitive.Close>
)} )}
@@ -100,6 +103,8 @@ function DialogFooter({
}: React.ComponentProps<"div"> & { }: React.ComponentProps<"div"> & {
showCloseButton?: boolean; showCloseButton?: boolean;
}) { }) {
const { t } = useTranslation();
return ( return (
<div <div
data-slot="dialog-footer" data-slot="dialog-footer"
@@ -112,7 +117,7 @@ function DialogFooter({
{children} {children}
{showCloseButton && ( {showCloseButton && (
<DialogPrimitive.Close asChild> <DialogPrimitive.Close asChild>
<Button variant="outline">Close</Button> <Button variant="outline">{t("common.close")}</Button>
</DialogPrimitive.Close> </DialogPrimitive.Close>
)} )}
</div> </div>
+1 -1
View File
@@ -188,7 +188,7 @@ function Select2({
<input <input
value={query} value={query}
onChange={(event) => setQuery(event.target.value)} onChange={(event) => setQuery(event.target.value)}
placeholder="Search..." placeholder={t("common.search")}
className="h-9 w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground" className="h-9 w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground"
autoFocus autoFocus
/> />
+4 -1
View File
@@ -1,3 +1,4 @@
import { useTranslation } from "react-i18next";
import * as React from "react"; import * as React from "react";
import { Dialog as SheetPrimitive } from "radix-ui"; import { Dialog as SheetPrimitive } from "radix-ui";
@@ -53,6 +54,8 @@ function SheetContent({
side?: "top" | "right" | "bottom" | "left"; side?: "top" | "right" | "bottom" | "left";
showCloseButton?: boolean; showCloseButton?: boolean;
}) { }) {
const { t } = useTranslation();
return ( return (
<SheetPortal> <SheetPortal>
<SheetOverlay /> <SheetOverlay />
@@ -74,7 +77,7 @@ function SheetContent({
size="icon-sm" size="icon-sm"
> >
<XIcon /> <XIcon />
<span className="sr-only">Close</span> <span className="sr-only">{t("common.close")}</span>
</Button> </Button>
</SheetPrimitive.Close> </SheetPrimitive.Close>
)} )}
+11 -5
View File
@@ -1,6 +1,7 @@
/* eslint-disable react-refresh/only-export-components */ /* eslint-disable react-refresh/only-export-components */
"use client"; "use client";
import { useTranslation } from "react-i18next";
import * as React from "react"; import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority"; import { cva, type VariantProps } from "class-variance-authority";
import { Slot } from "radix-ui"; import { Slot } from "radix-ui";
@@ -159,6 +160,7 @@ function Sidebar({
collapsible?: "offcanvas" | "icon" | "none"; collapsible?: "offcanvas" | "icon" | "none";
}) { }) {
const { isMobile, state, openMobile, setOpenMobile } = useSidebar(); const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
const { t } = useTranslation();
if (collapsible === "none") { if (collapsible === "none") {
return ( return (
@@ -192,8 +194,10 @@ function Sidebar({
side={side} side={side}
> >
<SheetHeader className="sr-only"> <SheetHeader className="sr-only">
<SheetTitle>Sidebar</SheetTitle> <SheetTitle>{t("common.sidebar")}</SheetTitle>
<SheetDescription>Displays the mobile sidebar.</SheetDescription> <SheetDescription>
{t("common.mobileSidebarDescription")}
</SheetDescription>
</SheetHeader> </SheetHeader>
<div className="flex h-full w-full flex-col">{children}</div> <div className="flex h-full w-full flex-col">{children}</div>
</SheetContent> </SheetContent>
@@ -253,6 +257,7 @@ function SidebarTrigger({
...props ...props
}: React.ComponentProps<typeof Button>) { }: React.ComponentProps<typeof Button>) {
const { toggleSidebar } = useSidebar(); const { toggleSidebar } = useSidebar();
const { t } = useTranslation();
return ( return (
<Button <Button
@@ -268,22 +273,23 @@ function SidebarTrigger({
{...props} {...props}
> >
<PanelLeftIcon /> <PanelLeftIcon />
<span className="sr-only">Toggle Sidebar</span> <span className="sr-only">{t("common.toggleSidebar")}</span>
</Button> </Button>
); );
} }
function SidebarRail({ className, ...props }: React.ComponentProps<"button">) { function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
const { toggleSidebar } = useSidebar(); const { toggleSidebar } = useSidebar();
const { t } = useTranslation();
return ( return (
<button <button
data-sidebar="rail" data-sidebar="rail"
data-slot="sidebar-rail" data-slot="sidebar-rail"
aria-label="Toggle Sidebar" aria-label={t("common.toggleSidebar")}
tabIndex={-1} tabIndex={-1}
onClick={toggleSidebar} onClick={toggleSidebar}
title="Toggle Sidebar" title={t("common.toggleSidebar")}
className={cn( className={cn(
"absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2", "absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2",
"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize", "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
+12 -2
View File
@@ -189,7 +189,11 @@
"remove": "Remove", "remove": "Remove",
"add": "Add", "add": "Add",
"update": "Update", "update": "Update",
"noResults": "No results found" "noResults": "No results found",
"sidebar": "Sidebar",
"mobileSidebarDescription": "Displays the mobile sidebar.",
"toggleSidebar": "Toggle Sidebar",
"search": "Search..."
}, },
"onboarding": { "onboarding": {
"stepCounter": "Step {{current}} of {{total}}", "stepCounter": "Step {{current}} of {{total}}",
@@ -1122,7 +1126,13 @@
"language": "Language", "language": "Language",
"keyPassword": "key password", "keyPassword": "key password",
"pastePrivateKey": "Paste your private key here...", "pastePrivateKey": "Paste your private key here...",
"enterPassword": "Enter your password" "enterPassword": "Enter your password",
"apiKeyName": "e.g., CI Pipeline",
"credentialName": "e.g. Production SSH Key",
"optionalDetails": "Optional details...",
"sshUsername": "e.g. root or deploy",
"hostAddress": "10.0.0.1 or example.com",
"hostName": "e.g. Web Server Production"
}, },
"dashboard": { "dashboard": {
"title": "Dashboard", "title": "Dashboard",
File diff suppressed because it is too large. Load diff
+9 -2
View File
@@ -77,9 +77,12 @@ export function FeatureSettingsSection({
open: boolean; open: boolean;
onToggle: () => void; onToggle: () => void;
}) { }) {
const { t } = useTranslation();
return ( return (
<AccordionSection <AccordionSection
label={plugin.name} label={t(pluginKey(plugin.id, "plugin.name"), {
defaultValue: plugin.name,
})}
icon={<PluginIcon name={plugin.icon} className="size-3.5" />} icon={<PluginIcon name={plugin.icon} className="size-3.5" />}
open={open} open={open}
onToggle={onToggle} onToggle={onToggle}
@@ -206,7 +209,11 @@ function FeatureSettingsForm({
<div className="flex flex-col gap-2 pt-2"> <div className="flex flex-col gap-2 pt-2">
{!running && ( {!running && (
<div className="border border-yellow-500/40 bg-yellow-500/10 px-3 py-2 text-xs text-yellow-500"> <div className="border border-yellow-500/40 bg-yellow-500/10 px-3 py-2 text-xs text-yellow-500">
{t("settings.featureUnavailable", { name: plugin.name })} {t("settings.featureUnavailable", {
name: t(pluginKey(plugin.id, "plugin.name"), {
defaultValue: plugin.name,
}),
})}
</div> </div>
)} )}
+3 -1
View File
@@ -646,7 +646,9 @@ export function CommandPalette({
className="flex items-center gap-3 px-3 py-2 rounded-none hover:bg-accent-brand/10 cursor-pointer" className="flex items-center gap-3 px-3 py-2 rounded-none hover:bg-accent-brand/10 cursor-pointer"
> >
<LifeBuoy className="size-4 text-muted-foreground" /> <LifeBuoy className="size-4 text-muted-foreground" />
<span className="text-sm font-medium">Support</span> <span className="text-sm font-medium">
{t("dashboard.support")}
</span>
</CommandItem> </CommandItem>
</div> </div>
</CommandGroup> </CommandGroup>
+1 -1
View File
@@ -150,7 +150,7 @@ export function AdminApiKeysSection({
<span className="text-accent-brand">*</span> <span className="text-accent-brand">*</span>
</label> </label>
<Input <Input
placeholder="e.g., CI Pipeline" placeholder={t("placeholders.apiKeyName")}
value={newKeyName} value={newKeyName}
onChange={(e) => setNewKeyName(e.target.value)} onChange={(e) => setNewKeyName(e.target.value)}
className="text-xs" className="text-xs"
+3 -3
View File
@@ -183,7 +183,7 @@ export function CredentialEditorView({
{t("hosts.friendlyNameLabel")} {t("hosts.friendlyNameLabel")}
</label> </label>
<Input <Input
placeholder="e.g. Production SSH Key" placeholder={t("placeholders.credentialName")}
value={credForm.name} value={credForm.name}
onChange={(e) => setCredField("name", e.target.value)} onChange={(e) => setCredField("name", e.target.value)}
/> />
@@ -203,7 +203,7 @@ export function CredentialEditorView({
{t("hosts.descriptionLabel")} {t("hosts.descriptionLabel")}
</label> </label>
<Input <Input
placeholder="Optional details..." placeholder={t("placeholders.optionalDetails")}
value={credForm.description} value={credForm.description}
onChange={(e) => setCredField("description", e.target.value)} onChange={(e) => setCredField("description", e.target.value)}
/> />
@@ -278,7 +278,7 @@ export function CredentialEditorView({
{t("hosts.username")} {t("hosts.username")}
</label> </label>
<Input <Input
placeholder="e.g. root or deploy" placeholder={t("placeholders.sshUsername")}
value={credForm.username} value={credForm.username}
onChange={(e) => setCredField("username", e.target.value)} onChange={(e) => setCredField("username", e.target.value)}
/> />
+1 -1
View File
@@ -1444,7 +1444,7 @@ export function HostEditor({
{t("hosts.friendlyNameLabel")} {t("hosts.friendlyNameLabel")}
</label> </label>
<Input <Input
placeholder="e.g. Production SSH Key" placeholder={t("placeholders.credentialName")}
value={quickCredentialName} value={quickCredentialName}
onChange={(e) => setQuickCredentialName(e.target.value)} onChange={(e) => setQuickCredentialName(e.target.value)}
/> />
+2 -2
View File
@@ -208,7 +208,7 @@ export function HostEditorGeneralTab({
{t("hosts.addressIp")} {t("hosts.addressIp")}
</label> </label>
<Input <Input
placeholder="10.0.0.1 or example.com" placeholder={t("placeholders.hostAddress")}
value={form.ip} value={form.ip}
onChange={(e) => setField("ip", e.target.value)} onChange={(e) => setField("ip", e.target.value)}
/> />
@@ -220,7 +220,7 @@ export function HostEditorGeneralTab({
{t("hosts.friendlyName")} {t("hosts.friendlyName")}
</label> </label>
<Input <Input
placeholder="e.g. Web Server Production" placeholder={t("placeholders.hostName")}
value={form.name} value={form.name}
onChange={(e) => setField("name", e.target.value)} onChange={(e) => setField("name", e.target.value)}
/> />
@@ -23,8 +23,15 @@ vi.mock("@/api/plugins-api", async (importOriginal) => ({
})); }));
vi.mock("react-i18next", () => { vi.mock("react-i18next", () => {
const t = (key: string, options?: { name?: string }) => const t = (
options?.name ? `${key}:${options.name}` : key; key: string,
options?: { name?: string; defaultValue?: string },
) =>
key === "ai:plugin.name"
? "AI 助手"
: options?.name
? `${key}:${options.name}`
: (options?.defaultValue ?? key);
return { useTranslation: () => ({ t }) }; return { useTranslation: () => ({ t }) };
}); });
@@ -112,10 +119,22 @@ describe("FeatureSettingsSection", () => {
/>, />,
); );
expect(screen.getByText("AI Assistant")).toBeTruthy(); expect(screen.getByText("AI 助手")).toBeTruthy();
expect(api.getPluginUserSettings).not.toHaveBeenCalled(); expect(api.getPluginUserSettings).not.toHaveBeenCalled();
}); });
it("falls back to the manifest name for plugins without a translated title", () => {
render(
<FeatureSettingsSection
plugin={plugin({ id: "third-party", name: "Custom Feature" })}
scope="admin"
open={false}
onToggle={vi.fn()}
/>,
);
expect(screen.getByText("Custom Feature")).toBeTruthy();
});
it("loads and saves the scope's values once open", async () => { it("loads and saves the scope's values once open", async () => {
api.getPluginUserSettings.mockResolvedValue({ enabled: false }); api.getPluginUserSettings.mockResolvedValue({ enabled: false });
api.updatePluginUserSettings.mockResolvedValue({ enabled: true }); api.updatePluginUserSettings.mockResolvedValue({ enabled: true });
@@ -179,7 +198,7 @@ describe("FeatureSettingsSection", () => {
); );
expect( expect(
await screen.findByText("settings.featureUnavailable:AI Assistant"), await screen.findByText("settings.featureUnavailable:AI 助手"),
).toBeTruthy(); ).toBeTruthy();
}); });
}); });