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