diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..c83812b --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,18 @@ +cff-version: 1.2.0 +message: "If Agent Switch supports your work, please cite the software using these metadata." +title: "Agent Switch" +type: software +authors: + - name: "JNHFlow21" +repository-code: "https://github.com/JNHFlow21/agent-switch" +url: "https://github.com/JNHFlow21/agent-switch" +license: MIT +version: 0.2.0 +date-released: 2026-07-26 +keywords: + - MCP + - macOS + - AI agents + - secret management + - Codex + - Claude Code diff --git a/README.md b/README.md index a9816cb..804acd9 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,15 @@

- Agent Switch dashboard showing health, configuration drift, MCP routes, and credential availability + GitHub stars + GitHub forks + GitHub commit activity + Release downloads + README visit counter +

+ +

+ Agent Switch dashboard showing health, configuration drift, MCP routes, and credential availability

@@ -40,6 +48,21 @@ credential store, and deterministic per-agent projections. > and [release checksums](https://github.com/JNHFlow21/agent-switch/releases/tag/v0.2.0) > before installing. + +## Project activity + +| Public signal | Live or latest owner-visible value | +| --- | ---: | +| Stars / forks / commits / release downloads | Live badges above | +| README visits | Public counter above; may include bots and repeat visits | +| Unique repository visitors | **0** in the rolling 14-day GitHub Traffic window | +| Unique Git cloners | **11** (**12** total clones) in the rolling 14-day window | + +Traffic snapshot: 2026-08-10. GitHub exposes clone and unique-visitor analytics only to maintainers, so those values are a dated, transparent snapshot rather than a token-backed public badge. + +The native app follows the macOS language preference and ships complete +**English** and **Simplified Chinese** interface catalogs. + ## Quick start ### Prerequisites diff --git a/README.zh-CN.md b/README.zh-CN.md index e4a97b4..94a4794 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -19,7 +19,15 @@

- Agent Switch 仪表板,展示健康状态、配置漂移、MCP 路由和密钥可用状态 + GitHub stars + GitHub forks + GitHub commit activity + Release downloads + README visit counter +

+ +

+ Agent Switch 仪表板,展示健康状态、配置漂移、MCP 路由和密钥可用状态

@@ -38,6 +46,20 @@ Agent Switch 用一个本地 MCP 注册表、一个私密凭据存储和可重 > quarantine。安装前请先检查[公开源码](https://github.com/JNHFlow21/agent-switch) > 和[发行校验和](https://github.com/JNHFlow21/agent-switch/releases/tag/v0.2.0)。 + +## 项目数据 + +| 公开指标 | 实时数据或最近一次维护者可见数据 | +| --- | ---: | +| Star / Fork / Commit / 发行下载量 | 见上方实时徽章 | +| README 访问量 | 见上方公开计数器;可能包含机器人和重复访问 | +| 仓库独立访客 | GitHub Traffic 最近 14 天滚动窗口内为 **0** | +| 独立克隆者 | 最近 14 天滚动窗口内为 **11**(共 **12** 次克隆) | + +数据快照日期:2026-08-10。GitHub 只向仓库维护者提供克隆与独立访客数据,因此这里采用注明日期的透明快照,而不是需要私密 Token 的公开徽章。 + +原生应用会跟随 macOS 的语言偏好,并完整提供**英文**与**简体中文**界面。 + ## 快速开始 ### 前置条件 diff --git a/docs/assets/agent-switch-dashboard-en.png b/docs/assets/agent-switch-dashboard-en.png new file mode 100644 index 0000000..f4cbbb2 Binary files /dev/null and b/docs/assets/agent-switch-dashboard-en.png differ diff --git a/docs/assets/agent-switch-dashboard.jpg b/docs/assets/agent-switch-dashboard.jpg deleted file mode 100644 index 1c95dd2..0000000 Binary files a/docs/assets/agent-switch-dashboard.jpg and /dev/null differ diff --git a/llms.txt b/llms.txt index 3870709..c49a301 100644 --- a/llms.txt +++ b/llms.txt @@ -5,6 +5,7 @@ Canonical repository: https://github.com/JNHFlow21/agent-switch License: MIT Platform: macOS 14+ for the native app; Python 3.11+ for the CLI +Interface languages: English and Simplified Chinese ## Primary purpose @@ -21,6 +22,7 @@ Agent Switch prevents configuration drift across local AI coding agents. It cent - Diagnostics list secret names only. - Downloaded Skills are dormant until explicitly activated for a project or the global profile. - Skill source updates never activate Skills. +- The native interface is localized in English and Simplified Chinese and follows the macOS language preference. - `doctor` previews and validates; `reconcile` writes atomically and creates backups. ## Supported integrations diff --git a/macos-app/AgentSwitch/AgentSwitch.xcodeproj/project.pbxproj b/macos-app/AgentSwitch/AgentSwitch.xcodeproj/project.pbxproj index 50ac0a5..4d281d5 100644 --- a/macos-app/AgentSwitch/AgentSwitch.xcodeproj/project.pbxproj +++ b/macos-app/AgentSwitch/AgentSwitch.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ A1000001000000000000000F /* AgentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000001000000000000000F; }; A10000010000000000000010 /* CLIsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000010000000000000010; }; A10000010000000000000011 /* SkillsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000010000000000000011; }; + A10000010000000000000012 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = A20000010000000000000012; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -44,6 +45,7 @@ A2000001000000000000000F /* AgentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentsView.swift; sourceTree = ""; }; A20000010000000000000010 /* CLIsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLIsView.swift; sourceTree = ""; }; A20000010000000000000011 /* SkillsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkillsView.swift; sourceTree = ""; }; + A20000010000000000000012 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; A30000010000000000000001 /* Agent Switch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Agent Switch.app"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -179,6 +181,7 @@ isa = PBXGroup; children = ( A2000001000000000000000D /* L10n.swift */, + A20000010000000000000012 /* Localizable.xcstrings */, A2000001000000000000000E /* Assets.xcassets */, ); path = Resources; @@ -243,6 +246,7 @@ hasScannedForEncodings = 0; knownRegions = ( en, + "zh-Hans", Base, ); mainGroup = A40000010000000000000001; @@ -261,6 +265,7 @@ buildActionMask = 2147483647; files = ( A1000001000000000000000E /* Assets.xcassets in Resources */, + A10000010000000000000012 /* Localizable.xcstrings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macos-app/AgentSwitch/AgentSwitch/AgentSwitchApp.swift b/macos-app/AgentSwitch/AgentSwitch/AgentSwitchApp.swift index b3585f3..9ef2c93 100644 --- a/macos-app/AgentSwitch/AgentSwitch/AgentSwitchApp.swift +++ b/macos-app/AgentSwitch/AgentSwitch/AgentSwitchApp.swift @@ -80,9 +80,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate { let appMenuItem = NSMenuItem() let appMenu = NSMenu(title: L10n.appName) - appMenu.addItem(NSMenuItem(title: "About \(L10n.appName)", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: "")) + appMenu.addItem(NSMenuItem(title: L10n.aboutAgentSwitch, action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: "")) appMenu.addItem(NSMenuItem.separator()) - appMenu.addItem(NSMenuItem(title: "Quit \(L10n.appName)", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")) + appMenu.addItem(NSMenuItem(title: L10n.quitAgentSwitch, action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")) appMenuItem.submenu = appMenu mainMenu.addItem(appMenuItem) @@ -93,9 +93,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate { mainMenu.addItem(actionsMenuItem) let windowMenuItem = NSMenuItem() - let windowMenu = NSMenu(title: "Window") - windowMenu.addItem(menuItem(title: "Show \(L10n.appName)", action: #selector(showMainWindowFromMenu), key: "0")) - windowMenu.addItem(NSMenuItem(title: "Minimize", action: #selector(NSWindow.miniaturize(_:)), keyEquivalent: "m")) + let windowMenu = NSMenu(title: L10n.window) + windowMenu.addItem(menuItem(title: L10n.showAgentSwitch, action: #selector(showMainWindowFromMenu), key: "0")) + windowMenu.addItem(NSMenuItem(title: L10n.minimize, action: #selector(NSWindow.miniaturize(_:)), keyEquivalent: "m")) windowMenuItem.submenu = windowMenu mainMenu.addItem(windowMenuItem) NSApp.windowsMenu = windowMenu diff --git a/macos-app/AgentSwitch/AgentSwitch/Features/Dashboard/DashboardView.swift b/macos-app/AgentSwitch/AgentSwitch/Features/Dashboard/DashboardView.swift index 4954d13..2c97409 100644 --- a/macos-app/AgentSwitch/AgentSwitch/Features/Dashboard/DashboardView.swift +++ b/macos-app/AgentSwitch/AgentSwitch/Features/Dashboard/DashboardView.swift @@ -55,7 +55,7 @@ struct DashboardView: View { DSMetricCard( label: L10n.runtimeState, value: report.map { $0.blocked ? L10n.blocked : L10n.ready } ?? "—", - note: report.map { $0.driftCount == 0 ? L10n.noDriftDetected : L10n.reviewPlannedChanges } ?? "加载中..." + note: report.map { $0.driftCount == 0 ? L10n.noDriftDetected : L10n.reviewPlannedChanges } ?? L10n.loadingTools ) DSMetricCard( label: L10n.driftChanges, diff --git a/macos-app/AgentSwitch/AgentSwitch/Features/Skills/SkillsView.swift b/macos-app/AgentSwitch/AgentSwitch/Features/Skills/SkillsView.swift index afc51a1..3ad553a 100644 --- a/macos-app/AgentSwitch/AgentSwitch/Features/Skills/SkillsView.swift +++ b/macos-app/AgentSwitch/AgentSwitch/Features/Skills/SkillsView.swift @@ -154,7 +154,7 @@ private enum SkillFilter: String, CaseIterable, Identifiable { var id: String { rawValue } var label: String { switch self { - case .all: return "全部" + case .all: return L10n.allSkills case .dormant: return L10n.dormant case .project: return L10n.projectActive case .global: return L10n.globalActive diff --git a/macos-app/AgentSwitch/AgentSwitch/Resources/L10n.swift b/macos-app/AgentSwitch/AgentSwitch/Resources/L10n.swift index df18410..119a30e 100644 --- a/macos-app/AgentSwitch/AgentSwitch/Resources/L10n.swift +++ b/macos-app/AgentSwitch/AgentSwitch/Resources/L10n.swift @@ -10,186 +10,200 @@ enum AppVersion { } enum L10n { + private static func localized(_ key: String.LocalizationValue) -> String { +#if SWIFT_PACKAGE + String(localized: key, bundle: .module) +#else + String(localized: key, bundle: .main) +#endif + } + // MARK: - Sidebar - static let appName = String(localized: "Agent Switch", comment: "App name") - static let dashboard = String(localized: "仪表板", comment: "Sidebar: Dashboard") - static let agents = String(localized: "Agent 托管", comment: "Sidebar: Agents") - static let clis = String(localized: "CLI 管理", comment: "Sidebar: CLI") - static let skills = String(localized: "Skill 仓库", comment: "Sidebar: Skills") - static let mcpTools = String(localized: "MCP 工具", comment: "Sidebar: MCP Tools") - static let secrets = String(localized: "密钥管理", comment: "Sidebar: Secrets") - static let settings = String(localized: "设置", comment: "Sidebar: Settings") - static let healthy = String(localized: "运行正常", comment: "Status: healthy") - static let needsAttention = String(localized: "需要关注", comment: "Status: needs attention") - static let checking = String(localized: "检查中", comment: "Status: checking") + static let appName = localized("appName") + static let dashboard = localized("dashboard") + static let agents = localized("agents") + static let clis = localized("clis") + static let skills = localized("skills") + static let mcpTools = localized("mcpTools") + static let secrets = localized("secrets") + static let settings = localized("settings") + static let healthy = localized("healthy") + static let needsAttention = localized("needsAttention") + static let checking = localized("checking") // MARK: - Dashboard - static let lastRefreshed = String(localized: "上次刷新:", comment: "Dashboard header") - static let blocked = String(localized: "已阻塞", comment: "State: blocked") - static let ready = String(localized: "就绪", comment: "State: ready") - static let drift = String(localized: "漂移", comment: "Drift suffix") - static let runtimeState = String(localized: "运行状态", comment: "Metric: runtime state") - static let driftChanges = String(localized: "漂移变更", comment: "Metric: drift changes") - static let findingsLabel = String(localized: "检查结果", comment: "Metric: findings") - static let secretsLabel = String(localized: "密钥状态", comment: "Metric: secrets") - static let noDriftDetected = String(localized: "无漂移", comment: "Metric note") - static let reviewPlannedChanges = String(localized: "请检查计划变更", comment: "Metric note") - static let allTargetsInSync = String(localized: "所有目标已同步", comment: "Metric note") - static let runReconcileToFix = String(localized: "运行修复以解决", comment: "Metric note") - static let noIssuesFound = String(localized: "无问题", comment: "Metric note") - static let errors = String(localized: "个错误", comment: "Error count suffix") - static let allPresent = String(localized: "全部就位", comment: "Metric note") - static let missing = String(localized: "缺失", comment: "Missing suffix") - static let loadFailed = String(localized: "加载失败", comment: "Dashboard error") + static let lastRefreshed = localized("lastRefreshed") + static let blocked = localized("blocked") + static let ready = localized("ready") + static let drift = localized("drift") + static let runtimeState = localized("runtimeState") + static let driftChanges = localized("driftChanges") + static let findingsLabel = localized("findingsLabel") + static let secretsLabel = localized("secretsLabel") + static let noDriftDetected = localized("noDriftDetected") + static let reviewPlannedChanges = localized("reviewPlannedChanges") + static let allTargetsInSync = localized("allTargetsInSync") + static let runReconcileToFix = localized("runReconcileToFix") + static let noIssuesFound = localized("noIssuesFound") + static let errors = localized("errors") + static let allPresent = localized("allPresent") + static let missing = localized("missing") + static let loadFailed = localized("loadFailed") // MARK: - Routes - static let accessRoutes = String(localized: "访问路由", comment: "Section: Access Routes") - static let searchDefault = String(localized: "搜索默认", comment: "Route: search default") - static let xReader = String(localized: "X 阅读器", comment: "Route: X reader") - static let xFallback = String(localized: "X 备用", comment: "Route: X fallback") + static let accessRoutes = localized("accessRoutes") + static let searchDefault = localized("searchDefault") + static let xReader = localized("xReader") + static let xFallback = localized("xFallback") // MARK: - Findings - static let findings = String(localized: "检查发现", comment: "Section: Findings") - static let noFindingsHealthy = String(localized: "无异常发现,所有托管资源运行正常。", comment: "Empty findings") - static let plannedChanges = String(localized: "计划变更", comment: "Section: Planned Changes") - static let noPlannedChanges = String(localized: "无计划变更,修复同步当前为空操作。", comment: "Empty changes") + static let findings = localized("findings") + static let noFindingsHealthy = localized("noFindingsHealthy") + static let plannedChanges = localized("plannedChanges") + static let noPlannedChanges = localized("noPlannedChanges") // MARK: - Tools - static let managedMCPTools = String(localized: "托管 MCP 工具", comment: "Page: Tools") - static let loadingTools = String(localized: "加载工具中…", comment: "Loading state") - static let targets = String(localized: "目标应用", comment: "Tool row: targets") - static let secretsSection = String(localized: "所需密钥", comment: "Tool row: secrets") - static let command = String(localized: "命令", comment: "Tool row: command") - static let readyState = String(localized: "就绪", comment: "Tool badge") - static let missingSecrets = String(localized: "缺少密钥", comment: "Tool badge") - static let driftState = String(localized: "漂移", comment: "Tool badge: drift") - static let addMCP = String(localized: "添加 MCP", comment: "Add MCP") - static let editMCP = String(localized: "编辑 MCP", comment: "Edit MCP") - static let removeMCP = String(localized: "删除 MCP", comment: "Remove MCP") - static let removeMCPConfirmation = String(localized: "确认从统一注册表删除:", comment: "Remove MCP confirmation") - static let enableMCP = String(localized: "启用 MCP", comment: "Enable MCP") - static let disableMCP = String(localized: "停用 MCP", comment: "Disable MCP") - static let disabledMCP = String(localized: "已停用", comment: "Disabled MCP") - static let importMCPs = String(localized: "导入已有 MCP", comment: "Import MCPs") - static let importAndAdoptMCPs = String(localized: "备份并统一接管", comment: "Import and adopt MCPs") - static let importMCPConfirmation = String(localized: "Agent Switch 会扫描已安装 Agent 的 MCP,迁移内嵌密钥,备份原配置,再用统一注册表生成托管项。", comment: "Import MCP confirmation") - static let discoveredMCPs = String(localized: "发现 MCP", comment: "Import preview discovered count") - static let supportedMCPs = String(localized: "可接管 MCP", comment: "Import preview supported count") - static let skippedMCPs = String(localized: "保留未接管", comment: "Import preview skipped MCPs") - static let secretNamesOnly = String(localized: "密钥名称(不含值)", comment: "Import preview secret names") - static let none = String(localized: "无", comment: "Empty value") - static let saveMCP = String(localized: "保存 MCP", comment: "Save MCP") - static let mcpID = String(localized: "MCP ID,例如 filesystem", comment: "MCP id") - static let mcpName = String(localized: "显示名称", comment: "MCP name") - static let mcpCommand = String(localized: "启动命令", comment: "MCP command") - static let mcpDescription = String(localized: "说明(可选)", comment: "MCP description") - static let mcpArguments = String(localized: "参数(每行一个)", comment: "MCP arguments") - static let mcpRequiredSecrets = String(localized: "所需密钥名称(每行一个)", comment: "MCP secrets") - static let mcpEnvironment = String(localized: "普通环境变量(每行 NAME=VALUE;密钥不要填这里)", comment: "MCP environment") - static let mcpEditorNote = String(localized: "密钥值只在密钥页面保存;每个 MCP 运行时只能获得这里声明的密钥。", comment: "MCP editor note") + static let managedMCPTools = localized("managedMCPTools") + static let loadingTools = localized("loadingTools") + static let targets = localized("targets") + static let secretsSection = localized("secretsSection") + static let command = localized("command") + static let readyState = localized("readyState") + static let missingSecrets = localized("missingSecrets") + static let driftState = localized("driftState") + static let addMCP = localized("addMCP") + static let editMCP = localized("editMCP") + static let removeMCP = localized("removeMCP") + static let removeMCPConfirmation = localized("removeMCPConfirmation") + static let enableMCP = localized("enableMCP") + static let disableMCP = localized("disableMCP") + static let disabledMCP = localized("disabledMCP") + static let importMCPs = localized("importMCPs") + static let importAndAdoptMCPs = localized("importAndAdoptMCPs") + static let importMCPConfirmation = localized("importMCPConfirmation") + static let discoveredMCPs = localized("discoveredMCPs") + static let supportedMCPs = localized("supportedMCPs") + static let skippedMCPs = localized("skippedMCPs") + static let secretNamesOnly = localized("secretNamesOnly") + static let none = localized("none") + static let saveMCP = localized("saveMCP") + static let mcpID = localized("mcpID") + static let mcpName = localized("mcpName") + static let mcpCommand = localized("mcpCommand") + static let mcpDescription = localized("mcpDescription") + static let mcpArguments = localized("mcpArguments") + static let mcpRequiredSecrets = localized("mcpRequiredSecrets") + static let mcpEnvironment = localized("mcpEnvironment") + static let mcpEditorNote = localized("mcpEditorNote") // MARK: - Secrets - static let secretsInventory = String(localized: "密钥清单", comment: "Page: Secrets") - static let secretManagementSubtitle = String(localized: "安全地添加、查看和维护本机密钥", comment: "Page: Secrets subtitle") - static let secretFile = String(localized: "密钥文件", comment: "Metric: secret file") - static let found = String(localized: "已找到", comment: "Secret file found") - static let missingFile = String(localized: "文件缺失", comment: "Secret file missing") - static let present = String(localized: "已配置", comment: "Metric: present") - static let ofRequired = String(localized: "共需", comment: "of N required prefix") - static let requiredSuffix = String(localized: "项", comment: "required suffix") - static let allSecretsConfigured = String(localized: "所有密钥已配置", comment: "Metric note") - static let requiredSecrets = String(localized: "所需密钥列表", comment: "Section: Required Secrets") - static let presentBadge = String(localized: "已配置", comment: "Badge: present") - static let missingBadge = String(localized: "缺失", comment: "Badge: missing") - static let secretFileLocation = String(localized: "密钥文件位置", comment: "Section: Secret File Location") - static let revealInFinder = String(localized: "在访达中显示", comment: "Button: Reveal in Finder") - static let secretsRuntimeNote = String(localized: "密钥由 wrapper 脚本在运行时加载,不会嵌入应用配置文件中。", comment: "Note") - static let noSecretInfo = String(localized: "无密钥信息", comment: "Empty state title") - static let runDoctorForSecrets = String(localized: "运行 Doctor 以检查密钥状态", comment: "Empty state subtitle") - static let stored = String(localized: "已保存", comment: "Secret stored count") - static let storedSecrets = String(localized: "已保存密钥", comment: "Metric: stored secrets") - static let storedSecretsNote = String(localized: "包括暂未被 MCP 引用的密钥", comment: "Stored secrets note") - static let requiredSecretsNote = String(localized: "当前 MCP 配置声明需要", comment: "Required secrets note") - static let allSecrets = String(localized: "全部密钥", comment: "Section: all secrets") - static let noStoredSecrets = String(localized: "尚未保存任何密钥。", comment: "Empty secret list") - static let addSecret = String(localized: "添加密钥", comment: "Add secret") - static let updateSecret = String(localized: "更新密钥", comment: "Update secret") - static let deleteSecret = String(localized: "删除密钥", comment: "Delete secret") - static let deleteSecretConfirmation = String(localized: "确认删除本机密钥:", comment: "Delete secret confirmation") - static let revealSecret = String(localized: "查看密钥", comment: "Reveal secret") - static let hideSecret = String(localized: "隐藏密钥", comment: "Hide secret") - static let copySecret = String(localized: "复制密钥", comment: "Copy secret") - static let configureSecret = String(localized: "配置密钥", comment: "Configure missing secret") - static let notConfigured = String(localized: "尚未配置", comment: "Secret missing") - static let requiredBadge = String(localized: "必需", comment: "Required badge") - static let secretEditorNote = String(localized: "密钥值通过安全管道写入,不会进入命令参数或日志。", comment: "Secret editor note") - static let usedByMCP = String(localized: "授权给", comment: "Secret consumers") - static let secretName = String(localized: "密钥名称,例如 FIRECRAWL_API_KEY", comment: "Secret name field") - static let secretNameLabel = String(localized: "密钥名称", comment: "Existing secret name label") - static let secretValue = String(localized: "密钥值", comment: "Secret value field") - static let cancel = String(localized: "取消", comment: "Cancel") + static let secretsInventory = localized("secretsInventory") + static let secretManagementSubtitle = localized("secretManagementSubtitle") + static let secretFile = localized("secretFile") + static let found = localized("found") + static let missingFile = localized("missingFile") + static let present = localized("present") + static let ofRequired = localized("ofRequired") + static let requiredSuffix = localized("requiredSuffix") + static let allSecretsConfigured = localized("allSecretsConfigured") + static let requiredSecrets = localized("requiredSecrets") + static let presentBadge = localized("presentBadge") + static let missingBadge = localized("missingBadge") + static let secretFileLocation = localized("secretFileLocation") + static let revealInFinder = localized("revealInFinder") + static let secretsRuntimeNote = localized("secretsRuntimeNote") + static let noSecretInfo = localized("noSecretInfo") + static let runDoctorForSecrets = localized("runDoctorForSecrets") + static let stored = localized("stored") + static let storedSecrets = localized("storedSecrets") + static let storedSecretsNote = localized("storedSecretsNote") + static let requiredSecretsNote = localized("requiredSecretsNote") + static let allSecrets = localized("allSecrets") + static let noStoredSecrets = localized("noStoredSecrets") + static let addSecret = localized("addSecret") + static let updateSecret = localized("updateSecret") + static let deleteSecret = localized("deleteSecret") + static let deleteSecretConfirmation = localized("deleteSecretConfirmation") + static let revealSecret = localized("revealSecret") + static let hideSecret = localized("hideSecret") + static let copySecret = localized("copySecret") + static let configureSecret = localized("configureSecret") + static let notConfigured = localized("notConfigured") + static let requiredBadge = localized("requiredBadge") + static let secretEditorNote = localized("secretEditorNote") + static let usedByMCP = localized("usedByMCP") + static let secretName = localized("secretName") + static let secretNameLabel = localized("secretNameLabel") + static let secretValue = localized("secretValue") + static let cancel = localized("cancel") // MARK: - CLI management - static let cliManagement = String(localized: "CLI 管理", comment: "Page: CLI management") - static let cliManagementSubtitle = String(localized: "查看本机 Agent 与工具链的安装路径和版本", comment: "CLI page subtitle") - static let installed = String(localized: "已安装", comment: "Installed status") - static let notInstalled = String(localized: "未安装", comment: "Not installed status") - static let packageManager = String(localized: "安装方式", comment: "CLI package manager") - static let installedCLIs = String(localized: "已安装 CLI", comment: "Installed CLI metric") - static let cliInventory = String(localized: "CLI 清单", comment: "CLI list") - static let cliUpdateNote = String(localized: "当前先统一盘点版本与路径;自动更新将按各 CLI 的安装方式执行。", comment: "CLI update note") + static let cliManagement = localized("cliManagement") + static let cliManagementSubtitle = localized("cliManagementSubtitle") + static let installed = localized("installed") + static let notInstalled = localized("notInstalled") + static let packageManager = localized("packageManager") + static let installedCLIs = localized("installedCLIs") + static let cliInventory = localized("cliInventory") + static let cliUpdateNote = localized("cliUpdateNote") // MARK: - Skill management - static let skillWarehouse = String(localized: "Skill 仓库", comment: "Page: Skill warehouse") - static let skillWarehouseSubtitle = String(localized: "下载只入库;明确加载后才进入项目或全局", comment: "Skill page subtitle") - static let skillSources = String(localized: "来源", comment: "Skill sources") - static let skillProfiles = String(localized: "项目配置", comment: "Skill profiles") - static let skillInventory = String(localized: "Skill 清单", comment: "Skill list") - static let dormant = String(localized: "沉默", comment: "Dormant skill") - static let projectActive = String(localized: "项目", comment: "Project active skill") - static let globalActive = String(localized: "全局", comment: "Global active skill") - static let missingSkill = String(localized: "缺失", comment: "Missing skill") - static let searchSkills = String(localized: "搜索 Skill、来源或项目", comment: "Search skills") - static let updateGitSources = String(localized: "更新 Git 来源", comment: "Update git skill sources") - static let updateGitSourcesNote = String(localized: "只更新 Skill Hub 中的 Git 仓库和版本锁,不会自动启用任何 Skill。", comment: "Skill update note") - static let noMatchingSkills = String(localized: "没有匹配的 Skill", comment: "No matching skills") + static let skillWarehouse = localized("skillWarehouse") + static let skillWarehouseSubtitle = localized("skillWarehouseSubtitle") + static let skillSources = localized("skillSources") + static let skillProfiles = localized("skillProfiles") + static let skillInventory = localized("skillInventory") + static let allSkills = localized("allSkills") + static let dormant = localized("dormant") + static let projectActive = localized("projectActive") + static let globalActive = localized("globalActive") + static let missingSkill = localized("missingSkill") + static let searchSkills = localized("searchSkills") + static let updateGitSources = localized("updateGitSources") + static let updateGitSourcesNote = localized("updateGitSourcesNote") + static let noMatchingSkills = localized("noMatchingSkills") // MARK: - File actions - static let openFile = String(localized: "直接打开", comment: "Open file action") - static let syncAndCheck = String(localized: "同步并检查", comment: "Single toolbar synchronization action") + static let openFile = localized("openFile") + static let syncAndCheck = localized("syncAndCheck") // MARK: - Agent management - static let agentManagement = String(localized: "Agent 托管", comment: "Page: agent management") - static let agentManagementSubtitle = String(localized: "一套规则,同步到所有受支持的 Agent", comment: "Agent page subtitle") - static let managed = String(localized: "已托管", comment: "Managed status") - static let detected = String(localized: "已发现", comment: "Detected status") - static let notDetected = String(localized: "未发现", comment: "Not detected status") - static let notManaged = String(localized: "未托管", comment: "Not managed status") - static let synchronized = String(localized: "已同步", comment: "Synchronized status") - static let needsSync = String(localized: "需要同步", comment: "Needs sync status") - static let manageDetectedAgents = String(localized: "托管并同步所有支持的 Agent", comment: "Manage supported agents") - static let manageDetectedAgentsNote = String(localized: "只写入 Agent Switch 管理区域;其他配置保持不变并自动备份。", comment: "Manage agents note") - static let howManagementWorks = String(localized: "规则如何生效", comment: "How management works heading") - static let howManagementWorksBody = String(localized: "Agent Switch 维护一份统一规则,再分别写入 Codex 的 AGENTS.md 入口、Claude Code 的 CLAUDE.md 管理区和 Hermes 的 SOUL.md 管理区。", comment: "How management works body") - static let welcomeHeading = String(localized: "让 Agent Switch 接管这套环境", comment: "Onboarding heading") - static let welcomeBody = String(localized: "已发现以下 Agent。开始托管后,Agent Switch 将同步统一指令和 MCP,并保留、备份现有配置。", comment: "Onboarding body") - static let startManagement = String(localized: "开始托管", comment: "Start management") - static let notNow = String(localized: "暂不处理", comment: "Not now") + static let agentManagement = localized("agentManagement") + static let agentManagementSubtitle = localized("agentManagementSubtitle") + static let managed = localized("managed") + static let detected = localized("detected") + static let notDetected = localized("notDetected") + static let notManaged = localized("notManaged") + static let synchronized = localized("synchronized") + static let needsSync = localized("needsSync") + static let manageDetectedAgents = localized("manageDetectedAgents") + static let manageDetectedAgentsNote = localized("manageDetectedAgentsNote") + static let howManagementWorks = localized("howManagementWorks") + static let howManagementWorksBody = localized("howManagementWorksBody") + static let welcomeHeading = localized("welcomeHeading") + static let welcomeBody = localized("welcomeBody") + static let startManagement = localized("startManagement") + static let notNow = localized("notNow") // MARK: - Settings - static let general = String(localized: "通用", comment: "Settings: General") - static let autoRefreshOnLaunch = String(localized: "启动时自动刷新", comment: "Settings toggle") - static let includeCCSwitch = String(localized: "Doctor 检查时包含 CC Switch 数据库", comment: "Settings toggle") - static let runtimePaths = String(localized: "运行时路径", comment: "Settings: Runtime Paths") - static let config = String(localized: "配置文件", comment: "Settings label") - static let secretsPath = String(localized: "密钥文件", comment: "Settings label") - static let wrappers = String(localized: "Wrapper 脚本", comment: "Settings label") - static let backups = String(localized: "备份目录", comment: "Settings label") - static let targetConfigs = String(localized: "目标配置文件", comment: "Settings: Target Configs") - static let about = String(localized: "关于", comment: "Settings: About") - static let version = String(localized: "版本", comment: "Settings label") - static let repository = String(localized: "仓库地址", comment: "Settings label") + static let general = localized("general") + static let autoRefreshOnLaunch = localized("autoRefreshOnLaunch") + static let includeCCSwitch = localized("includeCCSwitch") + static let runtimePaths = localized("runtimePaths") + static let config = localized("config") + static let secretsPath = localized("secretsPath") + static let wrappers = localized("wrappers") + static let backups = localized("backups") + static let targetConfigs = localized("targetConfigs") + static let about = localized("about") + static let version = localized("version") + static let repository = localized("repository") // MARK: - Actions (menu) - static let actions = String(localized: "操作", comment: "Menu: Actions") + static let actions = localized("actions") + static let aboutAgentSwitch = localized("aboutAgentSwitch") + static let quitAgentSwitch = localized("quitAgentSwitch") + static let window = localized("window") + static let showAgentSwitch = localized("showAgentSwitch") + static let minimize = localized("minimize") } diff --git a/macos-app/AgentSwitch/AgentSwitch/Resources/Localizable.xcstrings b/macos-app/AgentSwitch/AgentSwitch/Resources/Localizable.xcstrings new file mode 100644 index 0000000..5b71721 --- /dev/null +++ b/macos-app/AgentSwitch/AgentSwitch/Resources/Localizable.xcstrings @@ -0,0 +1,2811 @@ +{ + "sourceLanguage": "en", + "strings": { + "appName": { + "comment": "App name", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Agent Switch" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Agent Switch" + } + } + } + }, + "dashboard": { + "comment": "Sidebar: Dashboard", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Dashboard" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "仪表板" + } + } + } + }, + "agents": { + "comment": "Sidebar: Agents", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Agents" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Agent 托管" + } + } + } + }, + "clis": { + "comment": "Sidebar: CLI", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "CLI Inventory" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "CLI 管理" + } + } + } + }, + "skills": { + "comment": "Sidebar: Skills", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Skill Library" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Skill 仓库" + } + } + } + }, + "mcpTools": { + "comment": "Sidebar: MCP Tools", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "MCP Tools" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "MCP 工具" + } + } + } + }, + "secrets": { + "comment": "Sidebar: Secrets", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Secrets" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥管理" + } + } + } + }, + "settings": { + "comment": "Sidebar: Settings", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Settings" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "设置" + } + } + } + }, + "healthy": { + "comment": "Status: healthy", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Healthy" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "运行正常" + } + } + } + }, + "needsAttention": { + "comment": "Status: needs attention", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Needs attention" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "需要关注" + } + } + } + }, + "checking": { + "comment": "Status: checking", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Checking" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "检查中" + } + } + } + }, + "lastRefreshed": { + "comment": "Dashboard header", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Last refreshed: " + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "上次刷新:" + } + } + } + }, + "blocked": { + "comment": "State: blocked", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Blocked" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已阻塞" + } + } + } + }, + "ready": { + "comment": "State: ready", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Ready" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "就绪" + } + } + } + }, + "drift": { + "comment": "Drift suffix", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "drift" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "漂移" + } + } + } + }, + "runtimeState": { + "comment": "Metric: runtime state", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Runtime status" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "运行状态" + } + } + } + }, + "driftChanges": { + "comment": "Metric: drift changes", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Drift changes" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "漂移变更" + } + } + } + }, + "findingsLabel": { + "comment": "Metric: findings", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Findings" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "检查结果" + } + } + } + }, + "secretsLabel": { + "comment": "Metric: secrets", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Secret status" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥状态" + } + } + } + }, + "noDriftDetected": { + "comment": "Metric note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "No drift" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "无漂移" + } + } + } + }, + "reviewPlannedChanges": { + "comment": "Metric note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Review planned changes" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "请检查计划变更" + } + } + } + }, + "allTargetsInSync": { + "comment": "Metric note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "All targets in sync" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "所有目标已同步" + } + } + } + }, + "runReconcileToFix": { + "comment": "Metric note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Run reconcile to fix" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "运行修复以解决" + } + } + } + }, + "noIssuesFound": { + "comment": "Metric note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "No issues" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "无问题" + } + } + } + }, + "errors": { + "comment": "Error count suffix", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "errors" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "个错误" + } + } + } + }, + "allPresent": { + "comment": "Metric note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "All present" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "全部就位" + } + } + } + }, + "missing": { + "comment": "Missing suffix", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "missing" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "缺失" + } + } + } + }, + "loadFailed": { + "comment": "Dashboard error", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Load failed" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "加载失败" + } + } + } + }, + "accessRoutes": { + "comment": "Section: Access Routes", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Access routes" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "访问路由" + } + } + } + }, + "searchDefault": { + "comment": "Route: search default", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Default search" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "搜索默认" + } + } + } + }, + "xReader": { + "comment": "Route: X reader", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "X reader" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "X 阅读器" + } + } + } + }, + "xFallback": { + "comment": "Route: X fallback", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "X fallback" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "X 备用" + } + } + } + }, + "findings": { + "comment": "Section: Findings", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Findings" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "检查发现" + } + } + } + }, + "noFindingsHealthy": { + "comment": "Empty findings", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "No findings. All managed resources are healthy." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "无异常发现,所有托管资源运行正常。" + } + } + } + }, + "plannedChanges": { + "comment": "Section: Planned Changes", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Planned changes" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "计划变更" + } + } + } + }, + "noPlannedChanges": { + "comment": "Empty changes", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "No planned changes. Reconcile is currently a no-op." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "无计划变更,修复同步当前为空操作。" + } + } + } + }, + "managedMCPTools": { + "comment": "Page: Tools", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Managed MCP tools" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "托管 MCP 工具" + } + } + } + }, + "loadingTools": { + "comment": "Loading state", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Loading tools…" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "加载工具中…" + } + } + } + }, + "targets": { + "comment": "Tool row: targets", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Target apps" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "目标应用" + } + } + } + }, + "secretsSection": { + "comment": "Tool row: secrets", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Required secrets" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "所需密钥" + } + } + } + }, + "command": { + "comment": "Tool row: command", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Command" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "命令" + } + } + } + }, + "readyState": { + "comment": "Tool badge", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Ready" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "就绪" + } + } + } + }, + "missingSecrets": { + "comment": "Tool badge", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Missing secrets" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "缺少密钥" + } + } + } + }, + "driftState": { + "comment": "Tool badge: drift", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Drift" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "漂移" + } + } + } + }, + "addMCP": { + "comment": "Add MCP", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Add MCP" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "添加 MCP" + } + } + } + }, + "editMCP": { + "comment": "Edit MCP", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Edit MCP" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "编辑 MCP" + } + } + } + }, + "removeMCP": { + "comment": "Remove MCP", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Remove MCP" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "删除 MCP" + } + } + } + }, + "removeMCPConfirmation": { + "comment": "Remove MCP confirmation", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Remove from the central registry:" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "确认从统一注册表删除:" + } + } + } + }, + "enableMCP": { + "comment": "Enable MCP", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Enable MCP" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "启用 MCP" + } + } + } + }, + "disableMCP": { + "comment": "Disable MCP", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Disable MCP" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "停用 MCP" + } + } + } + }, + "disabledMCP": { + "comment": "Disabled MCP", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Disabled" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已停用" + } + } + } + }, + "importMCPs": { + "comment": "Import MCPs", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Import existing MCPs" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "导入已有 MCP" + } + } + } + }, + "importAndAdoptMCPs": { + "comment": "Import and adopt MCPs", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Back up and adopt" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "备份并统一接管" + } + } + } + }, + "importMCPConfirmation": { + "comment": "Import MCP confirmation", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Agent Switch will scan installed agents for MCPs, migrate embedded secrets, back up native configs, and generate managed entries from the central registry." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Agent Switch 会扫描已安装 Agent 的 MCP,迁移内嵌密钥,备份原配置,再用统一注册表生成托管项。" + } + } + } + }, + "discoveredMCPs": { + "comment": "Import preview discovered count", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Discovered MCPs" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "发现 MCP" + } + } + } + }, + "supportedMCPs": { + "comment": "Import preview supported count", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Supported MCPs" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "可接管 MCP" + } + } + } + }, + "skippedMCPs": { + "comment": "Import preview skipped MCPs", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Skipped and preserved" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "保留未接管" + } + } + } + }, + "secretNamesOnly": { + "comment": "Import preview secret names", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Secret names (values excluded)" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥名称(不含值)" + } + } + } + }, + "none": { + "comment": "Empty value", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "None" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "无" + } + } + } + }, + "saveMCP": { + "comment": "Save MCP", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Save MCP" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "保存 MCP" + } + } + } + }, + "mcpID": { + "comment": "MCP id", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "MCP ID, for example filesystem" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "MCP ID,例如 filesystem" + } + } + } + }, + "mcpName": { + "comment": "MCP name", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Display name" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "显示名称" + } + } + } + }, + "mcpCommand": { + "comment": "MCP command", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Launch command" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "启动命令" + } + } + } + }, + "mcpDescription": { + "comment": "MCP description", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Description (optional)" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "说明(可选)" + } + } + } + }, + "mcpArguments": { + "comment": "MCP arguments", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Arguments (one per line)" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "参数(每行一个)" + } + } + } + }, + "mcpRequiredSecrets": { + "comment": "MCP secrets", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Required secret names (one per line)" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "所需密钥名称(每行一个)" + } + } + } + }, + "mcpEnvironment": { + "comment": "MCP environment", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Regular environment variables (one NAME=VALUE per line; never put secrets here)" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "普通环境变量(每行 NAME=VALUE;密钥不要填这里)" + } + } + } + }, + "mcpEditorNote": { + "comment": "MCP editor note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Store secret values only on the Secrets page. Each MCP receives only the secret names declared here." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥值只在密钥页面保存;每个 MCP 运行时只能获得这里声明的密钥。" + } + } + } + }, + "secretsInventory": { + "comment": "Page: Secrets", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Secret inventory" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥清单" + } + } + } + }, + "secretManagementSubtitle": { + "comment": "Page: Secrets subtitle", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Safely add, inspect, and maintain local credentials" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "安全地添加、查看和维护本机密钥" + } + } + } + }, + "secretFile": { + "comment": "Metric: secret file", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Secret file" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥文件" + } + } + } + }, + "found": { + "comment": "Secret file found", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Found" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已找到" + } + } + } + }, + "missingFile": { + "comment": "Secret file missing", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "File missing" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "文件缺失" + } + } + } + }, + "present": { + "comment": "Metric: present", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Configured" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已配置" + } + } + } + }, + "ofRequired": { + "comment": "of N required prefix", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "of" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "共需" + } + } + } + }, + "requiredSuffix": { + "comment": "required suffix", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "required" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "项" + } + } + } + }, + "allSecretsConfigured": { + "comment": "Metric note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "All secrets configured" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "所有密钥已配置" + } + } + } + }, + "requiredSecrets": { + "comment": "Section: Required Secrets", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Required secrets" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "所需密钥列表" + } + } + } + }, + "presentBadge": { + "comment": "Badge: present", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Configured" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已配置" + } + } + } + }, + "missingBadge": { + "comment": "Badge: missing", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Missing" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "缺失" + } + } + } + }, + "secretFileLocation": { + "comment": "Section: Secret File Location", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Secret file location" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥文件位置" + } + } + } + }, + "revealInFinder": { + "comment": "Button: Reveal in Finder", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Reveal in Finder" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "在访达中显示" + } + } + } + }, + "secretsRuntimeNote": { + "comment": "Note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Wrapper scripts load secrets at runtime; values are never embedded in app configuration files." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥由 wrapper 脚本在运行时加载,不会嵌入应用配置文件中。" + } + } + } + }, + "noSecretInfo": { + "comment": "Empty state title", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "No secret information" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "无密钥信息" + } + } + } + }, + "runDoctorForSecrets": { + "comment": "Empty state subtitle", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Run Doctor to inspect secret status" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "运行 Doctor 以检查密钥状态" + } + } + } + }, + "stored": { + "comment": "Secret stored count", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "stored" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已保存" + } + } + } + }, + "storedSecrets": { + "comment": "Metric: stored secrets", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Stored secrets" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已保存密钥" + } + } + } + }, + "storedSecretsNote": { + "comment": "Stored secrets note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Includes secrets not currently referenced by an MCP" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "包括暂未被 MCP 引用的密钥" + } + } + } + }, + "requiredSecretsNote": { + "comment": "Required secrets note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Declared by the current MCP configuration" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "当前 MCP 配置声明需要" + } + } + } + }, + "allSecrets": { + "comment": "Section: all secrets", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "All secrets" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "全部密钥" + } + } + } + }, + "noStoredSecrets": { + "comment": "Empty secret list", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "No secrets have been stored." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "尚未保存任何密钥。" + } + } + } + }, + "addSecret": { + "comment": "Add secret", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Add secret" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "添加密钥" + } + } + } + }, + "updateSecret": { + "comment": "Update secret", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Update secret" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "更新密钥" + } + } + } + }, + "deleteSecret": { + "comment": "Delete secret", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Delete secret" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "删除密钥" + } + } + } + }, + "deleteSecretConfirmation": { + "comment": "Delete secret confirmation", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Delete local secret:" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "确认删除本机密钥:" + } + } + } + }, + "revealSecret": { + "comment": "Reveal secret", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Reveal secret" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "查看密钥" + } + } + } + }, + "hideSecret": { + "comment": "Hide secret", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Hide secret" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "隐藏密钥" + } + } + } + }, + "copySecret": { + "comment": "Copy secret", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Copy secret" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "复制密钥" + } + } + } + }, + "configureSecret": { + "comment": "Configure missing secret", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Configure secret" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "配置密钥" + } + } + } + }, + "notConfigured": { + "comment": "Secret missing", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Not configured" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "尚未配置" + } + } + } + }, + "requiredBadge": { + "comment": "Required badge", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Required" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "必需" + } + } + } + }, + "secretEditorNote": { + "comment": "Secret editor note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Secret values are written through a protected pipe and never enter command arguments or logs." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥值通过安全管道写入,不会进入命令参数或日志。" + } + } + } + }, + "usedByMCP": { + "comment": "Secret consumers", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Granted to" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "授权给" + } + } + } + }, + "secretName": { + "comment": "Secret name field", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Secret name, for example FIRECRAWL_API_KEY" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥名称,例如 FIRECRAWL_API_KEY" + } + } + } + }, + "secretNameLabel": { + "comment": "Existing secret name label", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Secret name" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥名称" + } + } + } + }, + "secretValue": { + "comment": "Secret value field", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Secret value" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥值" + } + } + } + }, + "cancel": { + "comment": "Cancel", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Cancel" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "取消" + } + } + } + }, + "cliManagement": { + "comment": "Page: CLI management", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "CLI inventory" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "CLI 管理" + } + } + } + }, + "cliManagementSubtitle": { + "comment": "CLI page subtitle", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Inspect installed agents and toolchain paths and versions" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "查看本机 Agent 与工具链的安装路径和版本" + } + } + } + }, + "installed": { + "comment": "Installed status", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Installed" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已安装" + } + } + } + }, + "notInstalled": { + "comment": "Not installed status", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Not installed" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "未安装" + } + } + } + }, + "packageManager": { + "comment": "CLI package manager", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Installed via" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "安装方式" + } + } + } + }, + "installedCLIs": { + "comment": "Installed CLI metric", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Installed CLIs" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已安装 CLI" + } + } + } + }, + "cliInventory": { + "comment": "CLI list", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "CLI inventory" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "CLI 清单" + } + } + } + }, + "cliUpdateNote": { + "comment": "CLI update note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Agent Switch currently inventories versions and paths. Future updates will follow each CLI installation method." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "当前先统一盘点版本与路径;自动更新将按各 CLI 的安装方式执行。" + } + } + } + }, + "skillWarehouse": { + "comment": "Page: Skill warehouse", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Skill library" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Skill 仓库" + } + } + } + }, + "skillWarehouseSubtitle": { + "comment": "Skill page subtitle", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Downloads stay dormant until explicitly enabled for a project or globally" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "下载只入库;明确加载后才进入项目或全局" + } + } + } + }, + "skillSources": { + "comment": "Skill sources", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Sources" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "来源" + } + } + } + }, + "skillProfiles": { + "comment": "Skill profiles", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Project profiles" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "项目配置" + } + } + } + }, + "skillInventory": { + "comment": "Skill list", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Skill inventory" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Skill 清单" + } + } + } + }, + "dormant": { + "comment": "Dormant skill", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Dormant" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "沉默" + } + } + } + }, + "projectActive": { + "comment": "Project active skill", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Project" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "项目" + } + } + } + }, + "globalActive": { + "comment": "Global active skill", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Global" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "全局" + } + } + } + }, + "missingSkill": { + "comment": "Missing skill", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Missing" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "缺失" + } + } + } + }, + "searchSkills": { + "comment": "Search skills", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Search skills, sources, or projects" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "搜索 Skill、来源或项目" + } + } + } + }, + "updateGitSources": { + "comment": "Update git skill sources", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Update Git sources" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "更新 Git 来源" + } + } + } + }, + "updateGitSourcesNote": { + "comment": "Skill update note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Updates Git checkouts and version locks in Skill Hub without enabling any Skill." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "只更新 Skill Hub 中的 Git 仓库和版本锁,不会自动启用任何 Skill。" + } + } + } + }, + "noMatchingSkills": { + "comment": "No matching skills", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "No matching Skills" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "没有匹配的 Skill" + } + } + } + }, + "openFile": { + "comment": "Open file action", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Open directly" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "直接打开" + } + } + } + }, + "syncAndCheck": { + "comment": "Single toolbar synchronization action", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Sync and check" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "同步并检查" + } + } + } + }, + "agentManagement": { + "comment": "Page: agent management", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Agent management" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Agent 托管" + } + } + } + }, + "agentManagementSubtitle": { + "comment": "Agent page subtitle", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "One policy, synchronized across every supported agent" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "一套规则,同步到所有受支持的 Agent" + } + } + } + }, + "managed": { + "comment": "Managed status", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "managed" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已托管" + } + } + } + }, + "detected": { + "comment": "Detected status", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "detected" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已发现" + } + } + } + }, + "notDetected": { + "comment": "Not detected status", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Not detected" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "未发现" + } + } + } + }, + "notManaged": { + "comment": "Not managed status", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Not managed" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "未托管" + } + } + } + }, + "synchronized": { + "comment": "Synchronized status", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "In sync" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已同步" + } + } + } + }, + "needsSync": { + "comment": "Needs sync status", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Needs sync" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "需要同步" + } + } + } + }, + "manageDetectedAgents": { + "comment": "Manage supported agents", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Manage and sync all supported agents" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "托管并同步所有支持的 Agent" + } + } + } + }, + "manageDetectedAgentsNote": { + "comment": "Manage agents note", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Writes only to Agent Switch managed sections. Other settings are preserved and backed up." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "只写入 Agent Switch 管理区域;其他配置保持不变并自动备份。" + } + } + } + }, + "howManagementWorks": { + "comment": "How management works heading", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "How management works" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "规则如何生效" + } + } + } + }, + "howManagementWorksBody": { + "comment": "How management works body", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Agent Switch maintains one shared policy and projects it into Codex AGENTS.md, Claude Code CLAUDE.md managed blocks, and Hermes SOUL.md managed blocks." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Agent Switch 维护一份统一规则,再分别写入 Codex 的 AGENTS.md 入口、Claude Code 的 CLAUDE.md 管理区和 Hermes 的 SOUL.md 管理区。" + } + } + } + }, + "welcomeHeading": { + "comment": "Onboarding heading", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Let Agent Switch manage this environment" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "让 Agent Switch 接管这套环境" + } + } + } + }, + "welcomeBody": { + "comment": "Onboarding body", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "The following agents were detected. Agent Switch will synchronize shared instructions and MCPs while preserving and backing up existing configuration." + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "已发现以下 Agent。开始托管后,Agent Switch 将同步统一指令和 MCP,并保留、备份现有配置。" + } + } + } + }, + "startManagement": { + "comment": "Start management", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Start management" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "开始托管" + } + } + } + }, + "notNow": { + "comment": "Not now", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Not now" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "暂不处理" + } + } + } + }, + "general": { + "comment": "Settings: General", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "General" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "通用" + } + } + } + }, + "autoRefreshOnLaunch": { + "comment": "Settings toggle", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Refresh automatically at launch" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "启动时自动刷新" + } + } + } + }, + "includeCCSwitch": { + "comment": "Settings toggle", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Include the CC Switch database in Doctor checks" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Doctor 检查时包含 CC Switch 数据库" + } + } + } + }, + "runtimePaths": { + "comment": "Settings: Runtime Paths", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Runtime paths" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "运行时路径" + } + } + } + }, + "config": { + "comment": "Settings label", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Configuration" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "配置文件" + } + } + } + }, + "secretsPath": { + "comment": "Settings label", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Secret file" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "密钥文件" + } + } + } + }, + "wrappers": { + "comment": "Settings label", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Wrapper scripts" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "Wrapper 脚本" + } + } + } + }, + "backups": { + "comment": "Settings label", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Backup directory" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "备份目录" + } + } + } + }, + "targetConfigs": { + "comment": "Settings: Target Configs", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Target configuration files" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "目标配置文件" + } + } + } + }, + "about": { + "comment": "Settings: About", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "About" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "关于" + } + } + } + }, + "version": { + "comment": "Settings label", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Version" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "版本" + } + } + } + }, + "repository": { + "comment": "Settings label", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Repository" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "仓库地址" + } + } + } + }, + "actions": { + "comment": "Menu: Actions", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Actions" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "操作" + } + } + } + }, + "aboutAgentSwitch": { + "comment": "App menu: About Agent Switch", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "About Agent Switch" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "关于 Agent Switch" + } + } + } + }, + "quitAgentSwitch": { + "comment": "App menu: Quit Agent Switch", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Quit Agent Switch" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "退出 Agent Switch" + } + } + } + }, + "window": { + "comment": "Window menu title", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Window" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "窗口" + } + } + } + }, + "showAgentSwitch": { + "comment": "Window menu: Show Agent Switch", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Show Agent Switch" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "显示 Agent Switch" + } + } + } + }, + "minimize": { + "comment": "Window menu: Minimize", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Minimize" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "最小化" + } + } + } + }, + "allSkills": { + "comment": "Skill filter: All", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "All" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "全部" + } + } + } + } + }, + "version": "1.0" +} diff --git a/macos-app/AgentSwitch/Package.swift b/macos-app/AgentSwitch/Package.swift index 46c0567..23a91cb 100644 --- a/macos-app/AgentSwitch/Package.swift +++ b/macos-app/AgentSwitch/Package.swift @@ -8,7 +8,8 @@ let package = Package( .executableTarget( name: "AgentSwitch", path: "AgentSwitch", - exclude: ["Resources/Assets.xcassets"] + exclude: ["Resources/Assets.xcassets"], + resources: [.process("Resources/Localizable.xcstrings")] ), ] ) diff --git a/tests/test_localization_catalog.py b/tests/test_localization_catalog.py new file mode 100644 index 0000000..1a3131b --- /dev/null +++ b/tests/test_localization_catalog.py @@ -0,0 +1,43 @@ +import json +import re +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +APP = ROOT / "macos-app" / "AgentSwitch" +L10N = APP / "AgentSwitch" / "Resources" / "L10n.swift" +CATALOG = APP / "AgentSwitch" / "Resources" / "Localizable.xcstrings" + + +class LocalizationCatalogTests(unittest.TestCase): + def test_every_declared_key_has_complete_english_and_chinese_text(self) -> None: + declared = set(re.findall(r'localized\("([^"]+)"\)', L10N.read_text())) + strings = json.loads(CATALOG.read_text())["strings"] + + self.assertEqual(declared, set(strings), "L10n.swift and the string catalog must stay in sync") + self.assertGreaterEqual(len(strings), 165) + + for key, entry in strings.items(): + localizations = entry.get("localizations", {}) + for language in ("en", "zh-Hans"): + value = localizations.get(language, {}).get("stringUnit", {}).get("value", "") + self.assertTrue(value.strip(), f"{key} is missing a {language} translation") + + def test_build_systems_package_the_string_catalog(self) -> None: + package = (APP / "Package.swift").read_text() + project = (APP / "AgentSwitch.xcodeproj" / "project.pbxproj").read_text() + + self.assertIn('Resources/Localizable.xcstrings', package) + self.assertIn('Localizable.xcstrings', project) + self.assertIn('zh-Hans', project) + + def test_readmes_use_the_sanitized_english_dashboard(self) -> None: + image = "docs/assets/agent-switch-dashboard-en.png" + self.assertTrue((ROOT / image).is_file()) + for readme in ("README.md", "README.zh-CN.md"): + self.assertIn(image, (ROOT / readme).read_text()) + + +if __name__ == "__main__": + unittest.main()