From a2b24c72491d00481036df9889246332e259117f Mon Sep 17 00:00:00 2001 From: khanhdeptraivaicachuong Date: Wed, 8 Jul 2026 23:28:08 +0700 Subject: [PATCH 1/4] =?UTF-8?q?th=C3=AAm=20ti=E1=BA=BFng=20vi=E1=BB=87t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/codex-plus-manager/index.html | 2 +- .../src-tauri/tauri.conf.json | 2 +- apps/codex-plus-manager/src/App.tsx | 8 +- .../src/components/ProviderPresetSelector.tsx | 6 +- apps/codex-plus-manager/src/i18n-en.ts | 5 + apps/codex-plus-manager/src/i18n-vi.ts | 783 ++++++++++++++++++ apps/codex-plus-manager/src/i18n.ts | 69 +- apps/codex-plus-manager/src/i18n.ts.bak | 76 ++ tools/build_i18n_vi.py | 42 + tools/gen-i18n-vi.mjs | 27 + tools/gen-i18n-vi.py | 74 ++ tools/i18n-export.json | 1 + tools/i18n-vi-cache.json | 1 + 13 files changed, 1059 insertions(+), 37 deletions(-) create mode 100644 apps/codex-plus-manager/src/i18n-vi.ts create mode 100644 apps/codex-plus-manager/src/i18n.ts.bak create mode 100644 tools/build_i18n_vi.py create mode 100644 tools/gen-i18n-vi.mjs create mode 100644 tools/gen-i18n-vi.py create mode 100644 tools/i18n-export.json create mode 100644 tools/i18n-vi-cache.json diff --git a/apps/codex-plus-manager/index.html b/apps/codex-plus-manager/index.html index 0bfda5710..f0bce202d 100644 --- a/apps/codex-plus-manager/index.html +++ b/apps/codex-plus-manager/index.html @@ -3,7 +3,7 @@ - Codex++ 管理工具 + Codex++ Manager
diff --git a/apps/codex-plus-manager/src-tauri/tauri.conf.json b/apps/codex-plus-manager/src-tauri/tauri.conf.json index d1540cf6e..6a13477dc 100644 --- a/apps/codex-plus-manager/src-tauri/tauri.conf.json +++ b/apps/codex-plus-manager/src-tauri/tauri.conf.json @@ -14,7 +14,7 @@ { "create": false, "label": "main", - "title": "Codex++ 管理工具", + "title": "Codex++ Manager", "width": 1180, "height": 820, "minWidth": 960, diff --git a/apps/codex-plus-manager/src/App.tsx b/apps/codex-plus-manager/src/App.tsx index 11a2c709b..55d2b9df8 100644 --- a/apps/codex-plus-manager/src/App.tsx +++ b/apps/codex-plus-manager/src/App.tsx @@ -68,7 +68,7 @@ import { serializeModelWindowRows, type ModelWindowRow, } from "./model-windows"; -import { getLanguage, t, tf, toggleLanguage } from "@/i18n"; +import { getLanguage, languageButtonTitle, t, tf, toggleLanguage } from "@/i18n"; type Status = "ok" | "failed" | "not_implemented" | "not_checked" | string; @@ -730,6 +730,10 @@ const defaultSettings: BackendSettings = { }; export function App() { + useEffect(() => { + document.title = t("Codex++ 管理工具"); + }, []); + const [theme, setTheme] = useState(() => loadInitialTheme()); const [route, setRoute] = useState(() => loadInitialRoute()); const [notice, setNotice] = useState<{ title: string; message: string; status?: Status } | null>(null); @@ -2070,7 +2074,7 @@ export function App() { ); diff --git a/apps/codex-plus-manager/src/i18n-en.ts b/apps/codex-plus-manager/src/i18n-en.ts index afcb02de2..8ccf7234a 100644 --- a/apps/codex-plus-manager/src/i18n-en.ts +++ b/apps/codex-plus-manager/src/i18n-en.ts @@ -5,6 +5,10 @@ // Plain strings: t("中文") -> EN_PLAIN["中文"]. export const EN_PLAIN: Record = { + "Codex++ 管理工具": "Codex++ Manager", + "小米 MiMo": "Xiaomi MiMo", + "百度千帆 Coding Plan": "Baidu Qianfan Coding Plan", + "火山引擎 Ark": "Volcengine Ark", "API Key 模式下扩展插件市场请求,尽量显示完整插件列表;官方/混合模式通常不需要。": "Expands plugin marketplace requests in API Key mode to show the full plugin list. Usually unnecessary in official/mixed mode.", "API Key 环境变量": "API Key environment variable", @@ -158,6 +162,7 @@ export const EN_PLAIN: Record = { "切换到此供应商时会写入 ~/.codex/auth.json": "Switching to this provider writes ~/.codex/auth.json", "切换到此供应商时会写入的预览;上下文开关变化会立即反映": "Preview of what gets written when switching to this provider; context toggles are reflected immediately", "切换到中文": "Switch to Chinese", + "切换到越南语": "Switch to Vietnamese", "切换到英文": "Switch to English", "切换到浅色": "Switch to light", "切换到深色": "Switch to dark", diff --git a/apps/codex-plus-manager/src/i18n-vi.ts b/apps/codex-plus-manager/src/i18n-vi.ts new file mode 100644 index 000000000..36005ba8a --- /dev/null +++ b/apps/codex-plus-manager/src/i18n-vi.ts @@ -0,0 +1,783 @@ +// Vietnamese UI + + +export const VI_PLAIN: Record = { + "API Key 模式下扩展插件市场请求,尽量显示完整插件列表;官方/混合模式通常不需要。": "Mở rộng các yêu cầu về thị trường plugin trong chế độ Khóa API để hiển thị danh sách plugin đầy đủ. Thường không cần thiết ở chế độ chính thức/hỗn hợp.", + "API Key 环境变量": "Biến môi trường khóa API", + "Chat Completions 转 Responses": "Hoàn thành trò chuyện để phản hồi", + "Codex 启动参数": "Đối số khởi chạy Codex", + "Codex 工具与插件": "Công cụ và plugin Codex", + "Codex 应用": "ứng dụng Codex", + "Codex 应用路径": "Đường dẫn ứng dụng Codex", + "Codex 意外停止": "Codex dừng đột ngột", + "Codex 版本": "Phiên bản Codex", + "Codex 目标": "Mục tiêu Codex", + "Codex++ 官方中转站,主打稳定接入和划算价格,支持 GPT-5.5、GPT-5.4、Claude Opus 4.8、Claude Opus 4.7、gpt-image-2 等模型与图像能力。": "Rơle Codex++ chính thức, được thiết kế để truy cập ổn định và có giá trị tốt, hỗ trợ GPT-5.5, GPT-5.4, Claude Opus 4.8, Claude Opus 4.7, gpt-image-2 và các mẫu khác cùng với khả năng hình ảnh.", + "Codex++ 导入": "Nhập Codex++", + "Codex++ 版本": "Phiên bản Codex++", + "Codex增强": "Cải tiến Codex", + "Codex增强模式": "Chế độ nâng cao Codex", + "Debug 端口": "Cổng gỡ lỗi", + "Fast 按钮": "Nút nhanh", + "GitHub Release 更新": "Cập nhật phát hành GitHub", + "GitHub Release 检查": "Kiểm tra phát hành GitHub", + "Helper 端口": "Cổng trợ giúp", + "MCP、Skills、Plugins 作为全局配置独立管理,切换任意供应商都会合并。": "MCP, Kỹ năng và Plugin được quản lý độc lập dưới dạng cấu hình chung và được hợp nhất bất cứ khi nào bạn chuyển đổi nhà cung cấp.", + "Markdown 导出": "Xuất khẩu giảm giá", + "Provider 同步目标": "Mục tiêu đồng bộ hóa nhà cung cấp", + "Stepwise 直接发送": "Gửi trực tiếp từng bước", + "基于当前对话生成下一步建议,使用独立 API 配置。": "Tạo đề xuất bước tiếp theo từ cuộc trò chuyện hiện tại bằng cách sử dụng cấu hình API riêng.", + "插件与模型": "Plugin và mô hình", + "管理插件市场、模型列表和服务档位相关增强。": "Quản lý các cải tiến cho thị trường plugin, danh sách mô hình và cấp dịch vụ.", + "对话与输入": "Cuộc trò chuyện và đầu vào", + "调整会话管理、输入行为和对话阅读体验。": "Điều chỉnh quản lý phiên, hành vi đầu vào và trải nghiệm đọc hội thoại.", + "界面与启动": "Giao diện và khởi động", + "控制语言、启动速度和 Codex 原生界面调整。": "Kiểm soát ngôn ngữ, tốc độ khởi động và điều chỉnh giao diện người dùng gốc Codex.", + "远程项目": "Dự án từ xa", + "连接 Zed Remote 和 upstream worktree 辅助能力。": "Kết nối Zed Remote và các khả năng trợ giúp của cây công việc ngược dòng.", + "最多建议数": "Đề xuất tối đa", + "最大输入字符": "Ký tự đầu vào tối đa", + "最大输出 tokens": "Mã thông báo đầu ra tối đa", + "测试连接": "Kiểm tra kết nối", + "超时毫秒": "Hết thời gian tính bằng mili giây", + "连接": "Sự liên quan", + "高级参数": "Thông số nâng cao", + "TOML 配置体": "Nội dung cấu hình TOML", + "Watcher 操作": "Hành động của người theo dõi", + "Watcher 状态": "Trạng thái người theo dõi", + "Watcher 用于保持 Codex++ 接管状态": "Người theo dõi giữ quyền kiểm soát Codex++", + "Watcher 自动接管": "Watcher tự động tiếp quản", + "Zed 远程打开": "Zed mở remote", + "Zed 远程项目": "Dự án từ xa của Zed", + "Zed 项目记录": "Lịch sử dự án Zed", + "Zed 默认打开策略": "Chiến lược mở Zed mặc định", + "Zed 默认行为": "Hành vi mặc định của Zed", + "cc-switch 导入": "nhập cc-switch", + "config.toml 预览": "xem trước config.toml", + "provider 未记录": "nhà cung cấp không được ghi lại", + "ssh:// URL 已复制。": "ssh:// URL đã được sao chép.", + "、": ", ", + "」的供应商": "”", + "上一次切换还没有完成,请稍后再试。": "Việc chuyển đổi trước đó vẫn chưa kết thúc. Vui lòng thử lại trong thời gian ngắn.", + "上下文大小": "Kích thước ngữ cảnh", + "上下文窗口": "Cửa sổ ngữ cảnh", + "上次修复结果": "Kết quả sửa chữa cuối cùng", + "上次更新结果": "Kết quả cập nhật lần cuối", + "上游协议": "Giao thức ngược dòng", + "下载并运行安装包": "Tải xuống và chạy trình cài đặt", + "不写 API 文件": "Không ghi tệp API", + "与 Codex 内插件菜单使用同一个远端广告源": "Sử dụng nguồn quảng cáo từ xa giống như menu plugin trong Codex", + "个": "", + "个会话": "(các) phiên", + "中国官方": "quan chức trung quốc", + "主页": "Trang chủ", + "主题和启动参数": "Chủ đề và khởi động các đối số", + "从 GitHub 静态清单加载": "Được tải từ bảng kê khai GitHub tĩnh", + "从 Word 等富文本粘贴到 Codex composer 时只保留纯文本,避免被识别为图片/文件附件。需重启 Codex 才生效。": "Chỉ giữ lại văn bản thuần túy khi dán văn bản có định dạng (ví dụ: từ Word) vào trình soạn thảo Codex, tránh bị coi là văn bản đính kèm hình ảnh/tệp. Yêu cầu khởi động lại Codex.", + "从上游获取": "Tìm nạp từ thượng nguồn", + "从最新 upstream 分支创建 Git worktree。": "Tạo cây công việc Git từ nhánh ngược dòng mới nhất.", + "从环境变量和 config.toml 的 /v1/models 拉取模型并补进模型列表。": "Kéo các mô hình từ các biến môi trường và /v1/models của config.toml rồi thêm chúng vào danh sách mô hình.", + "从第三方导入": "Nhập khẩu từ bên thứ ba", + "从预设模板创建": "Tạo từ mẫu đặt trước", + "会话": "Phiên họp", + "会话 ID 标识": "Huy hiệu ID phiên", + "会话删除": "Xóa phiên", + "会话删除、导出、项目移动和用户脚本等界面能力": "Các khả năng của giao diện người dùng như xóa phiên, xuất, di chuyển dự án và tập lệnh người dùng", + "会话删除、导出、项目移动和脚本能力": "Khả năng xóa phiên, xuất, di chuyển dự án và tập lệnh", + "会话总数": "Tổng số phiên", + "会话管理": "Quản lý phiên", + "会话项目移动": "Phiên di chuyển dự án", + "使用": "Sử dụng", + "使用中": "Đang sử dụng", + "例如 C:\\Program Files\\WindowsApps\\OpenAI.Codex...\\app": "ví dụ. C:\\Tệp chương trình\\WindowsApps\\OpenAI.Codex...\\app", + "例如 context7": "ví dụ. bối cảnh7", + "例如 deepseek-v4-pro": "ví dụ. deepseek-v4-pro", + "例如 gpt-5.4-mini": "e.g. gpt-5.4-mini", + "例如 主力聚合池": "e.g. Primary aggregate pool", + "供": "P", + "供应商": "Provider", + "供应商切换": "Provider switch", + "供应商切换不可用": "Provider switching unavailable", + "供应商切换中": "Switching provider", + "供应商列表": "Provider list", + "供应商测试": "Provider test", + "供应商测试模型": "Provider test model", + "供应商配置": "Provider configuration", + "供应商配置可能不正确": "Provider configuration may be incorrect", + "供应商配置已关闭": "Provider configuration disabled", + "供应商配置总开关已关闭": "The provider configuration master switch is off", + "供应商配置总开关已关闭;当前只保存配置,不写入 Codex live 文件": "The provider configuration master switch is off; changes are only saved, not written to Codex live files", + "供应商预设列表": "Provider preset list", + "保存": "Save", + "保存为默认路径": "Save as default path", + "保存到 Codex++ state,不改写 Zed settings。": "Saved to Codex++ state, does not modify Zed settings.", + "保存增强设置": "Save enhancement settings", + "保存扩展项": "Save entry", + "保存的应用路径": "Saved app path", + "保存策略": "Save strategy", + "保存自动修复设置": "Save auto-repair settings", + "保存设置": "Save settings", + "保存路径": "Saved path", + "修复入口": "Repair entrypoints", + "修复快捷方式": "Repair shortcuts", + "修复插件市场": "Repair plugin marketplace", + "健康检查": "Health check", + "先添加至少 1 个已填写 Base URL / Key 的 API 供应商,再创建聚合供应商。": "Add at least 1 API provider with a Base URL / Key before creating an aggregate provider.", + "免安装版或解包版只需要选择一次,之后静默启动会自动复用": "Portable or unpacked builds only need to be selected once; silent launch reuses it afterwards", + "入口卸载": "Entrypoint uninstall", + "入口安装": "Entrypoint install", + "入口安装、修复、Watcher 与手动启动": "Entrypoint install, repair, watcher and manual launch", + "入口管理": "Entrypoint management", + "全选当前列表": "Select all in current list", + "关于": "About", + "关于 Codex++": "About Codex++", + "关闭": "Off", + "关闭后会停用删除、导出、项目移动、插件相关和菜单位置增强。": "When off, disables deletion, export, project move, plugin-related and menu placement enhancements.", + "关闭后本工具不会在手动切换时写入 Codex 的 config.toml / auth.json;启动 Codex 时始终不会自动改这些文件。": "When off, this tool won't write Codex's config.toml / auth.json on manual switches; it never auto-modifies these files when launching Codex.", + "兼容增强": "Compatible enhancement", + "内容来自 BigPizzaV3/Ad-List,分为赞助商推荐和普通推荐。": "Content comes from BigPizzaV3/Ad-List, split into sponsor recommendations and regular recommendations.", + "内置": "Built-in", + "内置、手动和市场安装脚本;可在这里启停或删除用户脚本": "Built-in, manual and marketplace-installed scripts; enable, disable or delete user scripts here", + "内置和用户自定义脚本清单": "Built-in and user-defined script inventory", + "切换 thread 时恢复上一次浏览位置。": "Restore the last scroll position when switching threads.", + "切换中": "Switching", + "切换主题": "Toggle theme", + "切换到此供应商时会写入 ~/.codex/auth.json": "Switching to this provider writes ~/.codex/auth.json", + "切换到此供应商时会写入的预览;上下文开关变化会立即反映": "Preview of what gets written when switching to this provider; context toggles are reflected immediately", + "切换到中文": "Switch to Chinese", + "切换到英文": "Switch to English", + "切换到浅色": "Switch to light", + "切换到深色": "Switch to dark", + "切换对话保留位置": "Preserve scroll position across threads", + "点击建议后自动发送;关闭时只填入输入框。": "Automatically sends after clicking a suggestion. When off, it only fills the composer.", + "刚刚启动": "Just launched", + "删除": "Delete", + "删除会创建本地备份;如果 Codex App 正在使用该会话,建议先关闭对应会话窗口再操作。": "Deletion creates a local backup; if the Codex app is using this session, close the session window first.", + "删除会话": "Delete session", + "删除供应商": "Delete provider", + "删除已选": "Delete selected", + "删除扩展项": "Delete entry", + "删除模型": "Delete model", + "刷新": "Refresh", + "刷新会话": "Refresh sessions", + "刷新列表": "Refresh list", + "刷新市场": "Refresh marketplace", + "刷新当前页面": "Refresh current page", + "刷新推荐": "Refresh recommendations", + "刷新本地": "Refresh local", + "刷新项目": "Refresh projects", + "加入当前工作区": "Add to current workspace", + "包含版本、路径、设置和平台信息": "Includes version, paths, settings and platform info", + "协议": "Protocol", + "卸载入口": "Uninstall entrypoints", + "卸载时移除 Codex++ 托管数据": "Remove Codex++ managed data on uninstall", + "历史会话修复": "Historical session repair", + "历史会话修复失败,请查看错误提示后重试。": "Historical session repair failed. Check the error message and retry.", + "历史会话修复进度": "Historical session repair progress", + "压缩上下文大小": "Compaction context size", + "原生菜单栏位置": "Native menu bar placement", + "原生菜单汉化": "Native menu localization", + "反馈问题": "Report an issue", + "发现": "Discovered", + "发送 hi 测试": "Send a 'hi' test", + "取消": "Cancel", + "只保留非 MCP、Skills、Plugins 的跨供应商配置;工具与插件在独立页面管理。": "Keeps only cross-provider config that isn't MCP/Skills/Plugins; tools and plugins are managed on a separate page.", + "只填写表头下面的内容,例如:\ncommand = \"npx\"\nargs = [\"-y\", \"@upstash/context7-mcp\"]": "Fill in only the content under the header, for example:\ncommand = \"npx\"\nargs = [\"-y\", \"@upstash/context7-mcp\"]", + "只能勾选已填写 Base URL / Key 的 API 供应商,聚合供应商不会作为成员。": "Only API providers with a Base URL / Key can be selected; aggregate providers can't be members.", + "可更新": "Update available", + "同一对话保持一个成员,不同对话依次分配。": "Keeps one member per conversation; different conversations are assigned in turn.", + "同步 Zed settings": "Sync Zed settings", + "同步目标": "Sync target", + "名称": "Name", + "启动 Codex App 时追加到默认 CDP 参数后。留空则保持默认启动行为。": "Appended after the default CDP arguments when launching the Codex app. Leave empty to keep default launch behavior.", + "启动 Codex++": "Launch Codex++", + "启动任务": "Launch task", + "启动前自动修复历史会话": "Auto-repair historical sessions before launch", + "启动时通过本地主进程调试端口汉化 Codex 原生菜单;不修改安装包。需重启 Codex 才生效。": "Localizes the Codex native menu via the local main-process debug port at launch; doesn't modify the install package. Requires restarting Codex.", + "启用": "Enable", + "启用 Codex 图片覆盖层": "Enable Codex image overlay", + "启用 Codex增强": "Enable Codex enhancements", + "启用 Windows Computer Use Guard": "Enable Windows Computer Use Guard", + "启用供应商配置切换": "Enable provider configuration switching", + "启用此扩展项": "Enable this entry", + "启用目标功能": "Enable goals feature", + "图片": "Image", + "图片覆盖层": "Image overlay", + "在会话列表悬停显示删除按钮,并支持撤销。": "Show a delete button on hover in the session list, with undo support.", + "在会话列表显示导出按钮,导出带时间戳的 Markdown。": "Show an export button in the session list to export timestamped Markdown.", + "在 Codex 页面显示可拖动的后续建议浮层;建议由单独配置的 Stepwise API 生成。": "Shows a draggable follow-up suggestion overlay in the Codex page. Suggestions are generated by a separate Stepwise API.", + "在侧边栏会话标题前显示短 ID 和 UUIDv7 创建时间,方便定位历史会话。": "Show a short ID and UUIDv7 creation time before session titles in the sidebar to help locate past sessions.", + "基础设置": "Basic settings", + "填写中转服务 Base URL": "Enter the relay service Base URL", + "复制": "Copy", + "复制 ssh:// URL": "Copy ssh:// URL", + "复制失败": "Copy failed", + "复制报告": "Copy report", + "复用窗口": "Reuse window", + "多选": "Multi-select", + "失败": "Failed", + "失败切换": "Failover", + "失败切换会保留成员顺序,优先使用第一个可用供应商。": "Failover preserves member order, preferring the first available provider.", + "如果使用官方模式或官方混入 API 模式,通常不需要开启插件市场解锁。": "If you use official mode or official mixed-API mode, you usually don't need plugin marketplace unlock.", + "安装": "Install", + "安装 watcher": "Install watcher", + "安装入口": "Install entrypoints", + "安装包更新失败,请查看错误提示后重试。": "Installer update failed. Check the error message and retry.", + "安装包更新进度": "Installer update progress", + "安装维护": "Install & maintenance", + "完整增强": "Full enhancement", + "官方": "Official", + "官方中转站": "Official relay", + "官方混入 API Key": "Official mixed-in API Key", + "官方混合 API 不应在 auth.json 中保存 OPENAI_API_KEY。请清理此供应商的 auth.json 后再切换。": "Official mixed API should not store OPENAI_API_KEY in auth.json. Clear this provider's auth.json before switching.", + "官方登录": "Official login", + "官方登录模式": "Official login mode", + "对话居中宽度": "Centered conversation width", + "导入 Codex++ 供应商": "Import Codex++ provider", + "尚未刷新": "Not refreshed yet", + "尚未加载推荐内容": "Recommendations not loaded yet", + "尚未检查 Codex 应用路径。": "Codex app path not checked yet.", + "尚未检查 GitHub Release;更新会下载并启动安装包。": "GitHub Release not checked yet; updating downloads and launches the installer.", + "尚未生成诊断报告。": "No diagnostic report generated yet.", + "尚未运行安装包更新。": "Installer update has not run yet.", + "尚未运行历史会话修复。": "Historical session repair hasn't run yet.", + "尚未运行插件市场修复。": "Plugin marketplace repair hasn't run yet.", + "尚未检查官方远端插件缓存。": "Official remote plugin cache has not been checked yet.", + "正在检查内置官方远端插件缓存…": "Checking embedded official remote plugin cache…", + "正在释放内置远端插件快照…": "Extracting embedded remote plugin snapshot…", + "正在注册官方远端插件市场…": "Registering official remote plugin marketplace…", + "正在刷新官方远端插件缓存状态…": "Refreshing official remote plugin cache status…", + "正在处理…": "Processing…", + "工具与插件": "Tools & plugins", + "已切回官方登录;Codex增强已设为兼容增强。": "Switched back to official login; Codex enhancements set to compatible.", + "已切换到纯 API;Codex增强已设为完整增强。": "Switched to pure API; Codex enhancements set to full.", + "已切换到聚合供应商;真实对话会按所选策略轮转成员。": "Switched to aggregate provider; real conversations rotate members per the chosen strategy.", + "已刷新 Codex 应用、入口和 Watcher 状态。": "Refreshed Codex app, entrypoint and watcher status.", + "已受理": "Accepted", + "已安装": "Installed", + "已归档": "Archived", + "已打开聚合供应商详情;请先添加或完善至少 1 个普通 API 供应商的 Base URL / Key,再勾选为成员。": "Opened aggregate provider details; first add or complete the Base URL / Key of at least 1 regular API provider, then select it as a member.", + "已找到": "Found", + "已按此供应商使用官方登录,并混入 API Key;Codex增强已设为兼容增强。": "Using official login for this provider with the API Key mixed in; Codex enhancements set to compatible.", + "已按此供应商切回官方登录;Codex增强已设为兼容增强。": "Switched back to official login for this provider; Codex enhancements set to compatible.", + "已按此供应商切换到纯 API;Codex增强已设为完整增强。": "Switched to pure API for this provider; Codex enhancements set to full.", + "已检测": "Detected", + "已注册": "Registered", + "已缓存未注册": "Cached, not registered", + "已清除保存路径,后续启动会回到自动探测。": "Saved path cleared; future launches fall back to auto-detection.", + "已登录": "Logged in", + "已禁用": "Disabled", + "已选择": "Selected", + "市场状态": "Marketplace status", + "市场脚本": "Marketplace scripts", + "序列化字段": "Serialized fields", + "应用路径已保存,之后启动会自动复用。": "App path saved; future launches reuse it automatically.", + "应用路径留空时使用已保存路径;没有保存路径时使用自动探测": "When the app path is empty, the saved path is used; with no saved path, auto-detection is used", + "应用路径覆盖": "App path override", + "开启": "On", + "开启后,通过 Codex++ 启动 Codex 前自动整理一次旧对话的归属标记。": "When on, tidies up ownership tags of old conversations once before launching Codex through Codex++.", + "强制中文界面": "Force Chinese UI", + "强制启用 Codex App 内置 zh-CN 语言包,避免 Statsig/VPN 不通时回退英文。需重启 Codex 才能完整生效。": "Forces the Codex app's built-in zh-CN language pack to avoid falling back to English when Statsig/VPN is unreachable. Requires restarting Codex to fully apply.", + "当前": "Current", + "当前 provider": "current provider", + "当前不会写入 Codex config.toml / auth.json。打开供应商配置总开关后再切换。": "Codex config.toml / auth.json won't be written right now. Turn on the provider configuration master switch before switching.", + "当前为": "Currently ", + "当前为兼容增强模式,插件市场解锁不会启用;其他页面功能仍可用。": "Currently in compatible enhancement mode; plugin marketplace unlock is not enabled, but other page features still work.", + "当前会话": "Current session", + "当前使用中:打开时从 ~/.codex/auth.json 回填,保存后会作为此供应商 auth 存档": "Currently in use: backfilled from ~/.codex/auth.json when opened; saving stores it as this provider's auth archive", + "当前供应商 config.toml 里没有可提取的通用配置。": "The current provider's config.toml has no extractable common config.", + "当前供应商切换后会写入的预览;上下文开关变化会立即反映": "Preview of what the current provider writes after switching; context toggles are reflected immediately", + "当前供应商还没有完整 config.toml / API Key 存档。": "The current provider doesn't have a complete config.toml / API Key archive yet.", + "当前共有": "There are currently ", + "当前未登录官方账号,也未配置混入 API 的 Base URL / Key。": "Not logged into an official account, and no mixed-in API Base URL / Key configured.", + "当前未登录官方账号;切到官方登录模式后仍需要先在 Codex/ChatGPT 登录。": "Not logged into an official account; after switching to official login mode you still need to log in via Codex/ChatGPT first.", + "当前未登录官方账号;官方登录混入 API Key 需要先登录官方账号。": "Not logged into an official account; official login with a mixed-in API Key requires logging into an official account first.", + "当前正在使用": "Currently in use", + "当前识别": "Currently detected", + "当前还没有填写混入 API 的 Base URL / Key。": "The mixed-in API Base URL / Key hasn't been filled in yet.", + "当前进程": "Current process", + "当前配置 provider": "Currently configured provider", + "快捷方式修复": "Shortcut repair", + "快捷方式写入系统实际桌面位置,不使用写死桌面路径": "Shortcuts are written to the system's actual desktop location, not a hardcoded desktop path", + "快速启动": "Fast startup", + "总权重": "Total weight", + "成员供应商": "Member providers", + "成员数量": "Member count", + "或": " or ", + "手动": "Manual", + "手动启动": "Manual launch", + "打开": "Open", + "打开 JOJO Code": "Open JOJO Code", + "打开关于": "Open About", + "打开链接": "Open link", + "打开项目主页": "Open project homepage", + "扩展项": "entry", + "批量删除会话": "Bulk delete sessions", + "把 Codex++ 菜单插入 Codex 顶部原生菜单栏。": "Insert the Codex++ menu into Codex's top native menu bar.", + "把主对话和输入框限制到固定最大宽度,适合大屏阅读。": "Constrain the main conversation and input box to a fixed max width, good for large-screen reading.", + "把会话移动到普通对话或其他本地项目。": "Move a session to a regular conversation or another local project.", + "投稿": "Submit", + "拖动排序": "Drag to reorder", + "按对话轮转": "Per-conversation round-robin", + "按对话轮转会让同一对话尽量保持固定成员,降低上下文漂移。": "Per-conversation round-robin keeps the same conversation on a fixed member where possible, reducing context drift.", + "按成员权重分配请求,权重越高承担越多。": "Distribute requests by member weight; higher weight takes on more.", + "按成员顺序请求,失败后切到下一个供应商。": "Request members in order, switching to the next provider on failure.", + "按更新时间倒序显示": "Sorted by update time, newest first", + "按请求轮转": "Per-request round-robin", + "按请求轮转会逐请求切换成员,适合供应商能力接近的场景。": "Per-request round-robin switches members request by request, suited to providers with similar capabilities.", + "接入模式": "Access mode", + "推荐内容": "Recommendations", + "提取当前供应商配置": "Extract current provider config", + "插件": "Plugins", + "插件列表全量展示": "Show full plugin list", + "插件市场修复": "Plugin marketplace repair", + "插件市场修复失败,请查看错误提示后重试。": "Plugin marketplace repair failed. Check the error message and retry.", + "插件市场修复进度": "Plugin marketplace repair progress", + "插件市场解锁": "Plugin marketplace unlock", + "官方远端插件缓存": "Official remote plugin cache", + "官方远端插件缓存进度": "Official remote plugin cache progress", + "官方远端插件缓存修复失败,请查看错误提示后重试。": "Official remote plugin cache repair failed. Check the error message and retry.", + "使用 Codex++ 内置快照补齐远端插件,API 模式也可显示和安装 Product Design 插件。": "Use the embedded Codex++ snapshot to add remote plugins, so API mode can also show and install the Product Design plugin.", + "搜索供应商…": "Search providers…", + "数据库": "Database", + "新增": "Add ", + "新建供应商需要先保存到列表": "A new provider must be saved to the list first", + "新机器没有本地插件市场时,可从 openai/plugins 初始化到当前 CODEX_HOME。": "On a new machine without a local plugin marketplace, initialize it into the current CODEX_HOME from openai/plugins.", + "新窗口": "New window", + "日志已刷新": "Logs refreshed", + "日志已复制。": "Logs copied.", + "时间": "Time", + "释放并注册内置缓存": "Extract and register embedded cache", + "显示服务模式切换按钮;Fast 仅支持 gpt-5.4 / gpt-5.5,其他模型按 Standard 发送。": "Show the service-tier toggle button; Fast only supports gpt-5.4 / gpt-5.5, other models are sent as Standard.", + "普通推荐": "Regular recommendations", + "暂无": "None", + "暂无启动状态。": "No launch status yet.", + "暂无描述。": "No description.", + "暂无日志。": "No logs.", + "暂无普通推荐。": "No regular recommendations.", + "暂无状态文件": "No status file", + "暂无赞助商推荐。": "No sponsor recommendations.", + "暂无项目。": "No projects.", + "更多选项": "More options", + "更新": "Update", + "更新安装": "Update install", + "最新版本": "Latest version", + "最近启动": "Recent launches", + "最近打开": "Recently opened", + "最近日志": "Recent logs", + "未发现可导入供应商": "No importable providers found", + "未发现用户脚本。": "No user scripts found.", + "未命名会话": "Untitled session", + "未命名供应商": "Untitled provider", + "未命名聚合供应商": "Untitled aggregate provider", + "未填写": "Not filled in", + "未填写 URL": "No URL filled in", + "未安装": "Not installed", + "未实现": "Not implemented", + "未归档": "Not archived", + "未检查": "Not checked", + "未检测到": "Not detected", + "未检测到 Codex 应用版本。": "Codex app version not detected.", + "未发现缓存": "Cache not found", + "未发现本地缓存;点击按钮会从 Codex++ 内置快照释放并注册,无需官方账号预缓存。": "Local cache not found. Click the button to extract and register the embedded Codex++ snapshot; no official-account pre-cache is required.", + "未知": "Unknown", + "未知作者": "Unknown author", + "未知来源": "Unknown source", + "未知版本": "Unknown version", + "未记录路径": "No path recorded", + "未记录项目路径": "No project path recorded", + "未读取到本地会话,或当前 SQLite 会话库不存在。": "No local sessions read, or the current SQLite session database doesn't exist.", + "本地 Codex 增强、管理工具和安装包维护": "Local Codex enhancements, management tools and install package maintenance", + "本地会话": "Local sessions", + "本地整体": "Local overall", + "本地脚本": "Local scripts", + "权重": "Weight", + "权重轮转": "Weighted round-robin", + "权重轮转会读取每个成员的权重值,权重越高的成员获得更多请求。": "Weighted round-robin reads each member's weight; members with higher weight receive more requests.", + "查看、删除和修复 Codex 本地会话": "View, delete and repair Codex local sessions", + "检查": "Check", + "检查与修复": "Check & repair", + "检查入口、Codex 应用和 Watcher 状态": "Check entrypoint, Codex app and watcher status", + "检查配置、模型列表和一次真实请求,定位供应商不可用原因。": "Check configuration, model list and one real request to find why a provider is unavailable.", + "检查完成": "Check complete", + "检查更新": "Check for updates", + "检查问题、启动与快速修复": "Check issues, launch and quick fixes", + "诊断供应商": "Diagnose provider", + "检测": "Detect", + "检测到 OPENAI 环境变量": "OPENAI environment variables detected", + "检测到来自网页的供应商配置导入请求,确认后会写入本机 Codex++ 管理工具。": "A provider config import request from the web was detected; confirming writes it to this machine's Codex++ manager.", + "概览": "Overview", + "概览只展示关键问题,具体配置在对应页面处理": "The overview shows only key issues; specific configuration is handled on the relevant pages", + "概览已检查": "Overview checked", + "模型列表": "Model list", + "模型名称": "Model name", + "模型白名单解锁": "Model whitelist unlock", + "模式": "Mode", + "模式。": " mode.", + "正在下载安装包…": "Downloading installer…", + "正在下载插件市场快照…": "Downloading plugin marketplace snapshot…", + "正在修复…": "Repairing…", + "正在修复历史会话": "Repairing historical sessions", + "正在准备安装包下载…": "Preparing installer download…", + "正在启动安装包…": "Launching installer…", + "正在写入 Codex 配置…": "Writing Codex config…", + "正在写入修复与备份…": "Writing repairs and backups…", + "正在删除…": "Deleting…", + "正在扫描历史会话与索引…": "Scanning historical sessions and indexes…", + "正在检查会话 provider 标记…": "Checking session provider tags…", + "正在检查本地插件市场…": "Checking local plugin marketplace…", + "正在获取 GitHub Release 信息…": "Fetching GitHub Release information…", + "正在解压并校验插件文件…": "Extracting and verifying plugin files…", + "正在连接 openai/plugins…": "Connecting to openai/plugins…", + "正常": "OK", + "此上游会通过本地 127.0.0.1:57321 转成 Responses API,需要从 Codex++ 启动 Codex。": "This upstream is converted to the Responses API via local 127.0.0.1:57321, which requires launching Codex from Codex++.", + "此供应商会保留官方登录模式,并把请求混入当前 API Key;Codex增强仍使用兼容模式。": "This provider keeps official login mode and mixes the current API Key into requests; Codex enhancements stay in compatible mode.", + "此供应商会切回官方登录模式,使用 ChatGPT 官方账号,不写入 API Key。": "This provider switches back to official login mode using the official ChatGPT account, without writing an API Key.", + "此供应商会同时写入 config.toml 和 auth.json;API Key 也会注入到 provider bearer token。": "This provider writes both config.toml and auth.json; the API Key is also injected into the provider bearer token.", + "每次请求按成员顺序切换,适合均匀摊请求量。": "Switches members in order on each request, good for evenly spreading request volume.", + "每行一个参数,例如 --force_high_performance_gpu。不需要填写 open 或 --args。": "One argument per line, e.g. --force_high_performance_gpu. No need to include open or --args.", + "每行一个模型;上下文窗口可填": "One model per line; the context window can be", + "没有匹配「": "No providers matching “", + "浅色": "Light", + "测试模型": "Test model", + "消息": "Message", + "深色": "Dark", + "混入 API": "Mixed-in API", + "混入 API KEY": "Mix in API KEY", + "混入 API Key": "Mix in API Key", + "添加供应商": "Add provider", + "添加模型": "Add model", + "添加聚合供应商": "Add aggregate provider", + "清空选择": "Clear selection", + "清除保存路径": "Clear saved path", + "点击刷新会话读取本地数据库": "Click Refresh Sessions to read the local database", + "点击刷新市场加载远程脚本。": "Click Refresh Marketplace to load remote scripts.", + "版本信息、项目链接、GitHub Release 更新、日志与诊断": "Version info, project links, GitHub Release updates, logs and diagnostics", + "状态": "Status", + "独立管理 Codex 的 MCP、Skills、Plugins;切换任意供应商都会带上。": "Manage Codex's MCP, Skills and Plugins independently; they carry over whenever you switch providers.", + "独立管理 MCP、Skills、Plugins": "Independently manage MCP, Skills and Plugins", + "环境变量": "Environment variables", + "环境变量检测": "Environment variable detection", + "环境变量清理": "Environment variable cleanup", + "用户": "User", + "用户环境": "User environment", + "界面主题": "UI theme", + "留空不改写,例如 160000": "Leave empty to keep unchanged, e.g. 160000", + "留空不改写,例如 200000": "Leave empty to keep unchanged, e.g. 200000", + "留空使用默认值": "Leave empty to use the default", + "登录状态": "Login status", + "确认删除": "Confirm delete", + "确认导入": "Confirm import", + "禁用": "Disable", + "禁用此扩展项": "Disable this entry", + "移除 watcher": "Remove watcher", + "移除最近记录": "Remove from recent", + "立刻修复历史会话": "Repair historical sessions now", + "第三方": "Third party", + "策略": "Strategy", + "管理 API 供应商、协议、Key 与配置文件": "Manage API providers, protocols, keys and config files", + "管理 Codex SSH 项目并加入 Zed workspace": "Manage Codex SSH projects and add them to a Zed workspace", + "管理工具入口": "Manager entrypoint", + "管理控制台": "Management console", + "管理控制台入口": "Management console entrypoint", + "类型": "Type", + "粘贴修复": "Paste fix", + "索引": "Index", + "纯 API": "Pure API", + "纯 API 就绪:会同时写入 config.toml 和 auth.json。": "Pure API ready: writes both config.toml and auth.json.", + "纯 API 模式": "Pure API mode", + "纯 API 配置未完整写入:请检查此供应商是否有 OPENAI_API_KEY,且 config.toml 是否包含 model_provider / provider / base_url。": "Pure API config is incomplete: check whether this provider has OPENAI_API_KEY and whether config.toml includes model_provider / provider / base_url.", + "维护 Codex++ 自己的远程项目最近列表。": "Maintain Codex++'s own recent list of remote projects.", + "编辑": "Edit", + "编辑后保存列表,再切换模式时会使用新配置": "After editing, save the list; the new config is used the next time you switch modes", + "编辑扩展项": "Edit entry", + "缺失": "Missing", + "缺少 Codex++ 静默启动快捷方式时可在安装维护页修复。": "When the Codex++ silent launch shortcut is missing, repair it on the Install & Maintenance page.", + "缺少管理工具快捷方式时可在安装维护页修复。": "When the manager shortcut is missing, repair it on the Install & Maintenance page.", + "聚合": "Aggregate", + "聚合/中转": "Aggregate/relay", + "聚合供应商": "Aggregate provider", + "聚合供应商会在真实对话中轮转成员,请测试成员供应商": "Aggregate providers rotate members in real conversations; test the member providers instead", + "聚合供应商只保存成员和策略配置,成员来自已有 API 供应商;切为当前后会通过本地协议代理轮转请求。": "Aggregate providers only store member and strategy config drawn from existing API providers; once active, requests rotate through the local protocol proxy.", + "聚合供应商至少需要勾选 1 个已填写 Base URL / Key 的 API 供应商。": "An aggregate provider needs at least 1 selected API provider with a Base URL / Key filled in.", + "聚合策略": "Aggregate strategy", + "聚合配置只引用已有供应商,不复制 Key 和配置文件": "Aggregate config only references existing providers; it doesn't copy keys or config files", + "脚本市场": "Script marketplace", + "自动接管": "Auto-takeover", + "覆盖图片": "Overlay image", + "背景适配方式": "Background fit mode", + "填充": "Fill", + "适应": "Fit", + "拉伸": "Stretch", + "平铺": "Tile", + "居中": "Center", + "记录最近打开": "Record recently opened", + "设为当前": "Set as current", + "设置": "Settings", + "设置保存": "Settings saved", + "设置已加载": "Settings loaded", + "设置重置": "Settings reset", + "诊断已生成": "Diagnostics generated", + "诊断报告": "Diagnostic report", + "诊断报告已复制。": "Diagnostic report copied.", + "点击后会打开诊断弹框,按步骤检查供应商。": "Click to open the diagnostic dialog and check the provider step by step.", + "正在诊断供应商,请稍候。": "Diagnosing provider, please wait.", + "诊断已完成。": "Diagnosis complete.", + "诊断中": "Diagnosing", + "异常": "Error", + "完成": "Complete", + "配置完整性": "Configuration integrity", + "等待检查 Base URL / API Key。": "Waiting to check Base URL / API Key.", + "等待检查 /v1/models。": "Waiting to check /v1/models.", + "真实请求": "Real request", + "等待发送一次测试请求。": "Waiting to send one test request.", + "处理建议": "Recommendation", + "等待生成建议。": "Waiting to generate a recommendation.", + "正在检查配置完整性…": "Checking configuration integrity…", + "该步骤未执行。": "This step was not run.", + "请先填写或选择应用路径。": "Fill in or select an app path first.", + "请先选择要删除的会话。": "Select the sessions to delete first.", + "读取 Codex 本地 SQLite 会话库,会删除数据库记录和对应 rollout 文件": "Reads Codex's local SQLite session database; deletes database records and the corresponding rollout files", + "读取 cc-switch 供应商失败。": "Failed to read cc-switch providers.", + "读取 ~/.cc-switch/cc-switch.db": "Reading ~/.cc-switch/cc-switch.db", + "调用失败": "Call failed", + "资源": "Asset", + "赞助商推荐": "Sponsor recommendations", + "赞助商推荐与普通推荐": "Sponsor and regular recommendations", + "输入中转服务的 API Key": "Enter the relay service API Key", + "运行中": "Running", + "返回列表": "Back to list", + "这些变量可能覆盖当前供应商写入的 config.toml / auth.json;CODEX_HOME 不会被清理。": "These variables may override the config.toml / auth.json written by the current provider; CODEX_HOME won't be cleaned up.", + "进入插件页后自动连续展开“更多”,尽量一次显示完整插件列表。": "Automatically keep expanding 'More' on the plugins page to show the full plugin list at once.", + "进度": "Progress", + "远程 SSH 文件引用可直接用 Zed Remote Development 打开。": "Remote SSH file references can be opened directly with Zed Remote Development.", + "远程脚本": "Remote scripts", + "适合官方登录或官方混入 API Key;保留会话删除、导出、项目移动和用户脚本,关闭插件市场相关增强。": "Suited to official login or official mixed-in API Key; keeps session deletion, export, project move and user scripts, while disabling plugin-marketplace-related enhancements.", + "适合纯 API;启用插件市场、会话删除导出、项目移动等全部页面能力。": "Suited to pure API; enables all page capabilities including plugin marketplace, session deletion/export and project move.", + "选择 Codex 应用目录": "Select Codex app directory", + "选择 Codex.exe": "Select Codex.exe", + "选择 Codex.exe 或 Codex.app": "Select Codex.exe or Codex.app", + "选择 Codex.exe、Codex.app、app 目录或解包目录": "Select Codex.exe, Codex.app, the app directory or the unpacked directory", + "选择 png / jpg / webp / gif / bmp": "Select png / jpg / webp / gif / bmp", + "选择会话": "Select session", + "选择图片": "Select image", + "选择已有供应商作为成员,保存后写入 settings payload": "Select existing providers as members; saving writes them to the settings payload", + "选择应用目录": "Select app directory", + "选择覆盖图片": "Select overlay image", + "通用配置文件": "Common config file", + "配置": "Config", + "配置文件": "Config file", + "配置模型": "Config model", + "重启 Codex++": "Restart Codex++", + "重新安装": "Reinstall", + "重新生成": "Regenerate", + "重置背景": "Reset background", + "静默启动入口": "Silent launch entrypoint", + "项目地址": "Project URL", + "额外参数": "Extra arguments", + "高级选项,默认关闭;当前实现不主动改写 Zed settings。": "Advanced option, off by default; the current implementation doesn't actively modify Zed settings.", + "默认中转": "Default relay", + "默认关闭;开启后启动 Codex 时会自动保留官方 Computer Use 插件所需的 config.toml、bundled 插件和 notify 配置。": "Off by default; when on, launching Codex automatically preserves the config.toml, bundled plugins and notify config required by the official Computer Use plugin.", + "默认启动 Codex 时使用的模型名,请勿带后缀;上下文窗口请在下方「模型列表」中按模型单独配置。": "The model name used by default when launching Codex; don't include a suffix. Configure context windows per model in the “Model list” below.", + "默认关闭;无 VPN 时可开启,让 Statsig 初始化快速失败,减少启动时长。需重启 Codex 才生效。": "Off by default; enable it without a VPN to let Statsig initialization fail fast and reduce launch time. Requires restarting Codex.", + "默认打开策略": "Default open strategy", + "(": " (", + ")": ")", + ",可以从通用配置文件或这里新增。": "; you can add them from the common config file or here.", + ",留空表示使用 Codex 默认长度。": "; leave empty to use Codex's default length.", + ";这些条目独立于供应商保存,会写入所有供应商切换后的 config.toml。": "; these entries are saved independently of providers and written to config.toml after switching to any provider.", +}; + +export const VI_TEMPLATE: Record = { + "\n...以及另外 {0} 个会话": "\n...and {0} more session(s)", + "{0} · {1} 个成员": "{0} · {1} member(s)", + "{0} 个": "{0}", + "{0} 个 Codex++ 可识别项目,默认策略:{1}": "{0} project(s) recognized by Codex++, default strategy: {1}", + "{0} 个供应商": "{0} provider(s)", + "{0} 个供应商配置;可拖动排序,点编辑进入详情": "{0} provider configuration(s); drag to reorder, click edit for details", + "{0} 个市场脚本,已安装 {1} 个,本地整体 {2}": "{0} marketplace script(s), {1} installed, local overall {2}", + "{0} 个项目": "{0} project(s)", + "{0} 副本": "{0} copy", + "{0} 条": "{0} item(s)", + "供应商 {0}": "Provider {0}", + "供应商「{0}」缺少独立 config.toml,已停止切换,避免继续显示上一套配置文件。请先在该供应商详情里保存 config.toml。": "Provider “{0}” is missing its own config.toml, so the switch was stopped to avoid showing the previous config files. Save config.toml in this provider's details first.", + "删除会话“{0}”?此操作会删除本地数据库记录和 rollout 文件,并创建备份。": "Delete session “{0}”? This deletes the local database record and rollout file, and creates a backup.", + "删除脚本“{0}”?此操作会移除本地脚本文件。": "Delete script “{0}”? This removes the local script file.", + "删除这些环境变量?\n\n{0}\n\n删除前会写入备份。": "Delete these environment variables?\n\n{0}\n\nA backup is written before deletion.", + "删除选中的 {0} 个会话?此操作会删除本地数据库记录和 rollout 文件,并为每个会话创建备份。\n\n{1}{2}": "Delete the {0} selected session(s)? This deletes the local database records and rollout files, and creates a backup for each session.\n\n{1}{2}", + "发现 {0} 个 Codex 供应商": "Found {0} Codex provider(s)", + "发现新版本 {0}": "New version {0} available", + "官方登录已就绪:{0},会混入当前 API Key。": "Official login ready: {0}, the current API Key will be mixed in.", + "官方账号已登录:{0}。": "Official account logged in: {0}.", + "已删除 {0} 个会话。": "Deleted {0} session(s).", + "已删除 {0} 个,失败 {1} 个:{2}": "Deleted {0}, failed {1}: {2}", + "已加载 {0} 条推荐": "Loaded {0} recommendation(s)", + "已同步到 {0}:修复 {1} 个会话文件,更新 {2} 行索引{3}。": "Synced to {0}: repaired {1} session file(s), updated {2} index row(s){3}.", + "已安装 {0}": "Installed {0}", + "已缓存 {0} 个插件 / {1} 个技能。": "Cached {0} plugin(s) / {1} skill(s).", + "已运行 {0} 分钟": "Running for {0} minute(s)", + "已运行 {0} 小时 {1} 分钟": "Running for {0} hour(s) {1} minute(s)", + "市场 · {0}": "Marketplace · {0}", + "当前版本 {0}": "Current version {0}", + "打开选择器失败:{0}": "Failed to open the picker: {0}", + "正在同步到 {0}…": "Syncing to {0}…", + "清单更新时间:{0}": "Manifest updated at: {0}", + "留空使用默认:{0}": "Leave empty to use the default: {0}", + "聚合供应商 {0}": "Aggregate provider {0}", + "聚合供应商已配置为{0},包含 {1} 个成员;真实对话会走本地代理轮转。": "The aggregate provider is configured as {0} with {1} member(s); real conversations rotate through the local proxy.", + "进程状态:{0}。是否要重新启动?": "Process status: {0}. Restart?", + "选择会话 {0}": "Select session {0}", + "透明度 {0}%": "Opacity {0}%", + ",跳过 {0} 个占用文件": ", skipped {0} locked file(s)", +}; + +export const VI_BACKEND: Record = { + "后端版本已读取。": "Backend version loaded.", + "启动参数已读取。": "Launch arguments loaded.", + "概览后台任务失败。": "Overview background task failed.", + "概览已加载。": "Overview loaded.", + "启动任务已在后台开始,可稍后查看概览状态。": "Launch task started in the background. Check overview status later.", + "设置已加载。": "Settings loaded.", + "设置读取失败": "Failed to read settings", + "设置保存后重新读取失败": "Failed to reload settings after save", + "没有新的 cc-switch 供应商配置需要导入。": "No new cc-switch provider configs to import.", + "导入供应商配置后重新读取设置失败": "Failed to reload settings after importing provider config", + "待确认供应商导入已读取。": "Pending provider import loaded.", + "没有待确认的供应商导入。": "No pending provider import.", + "已取消供应商导入。": "Provider import cancelled.", + "会话 ID 不能为空。": "Session ID must not be empty.", + "推荐内容已加载。": "Recommendations loaded.", + "脚本市场已刷新。": "Script marketplace refreshed.", + "脚本 id 不能为空。": "Script ID must not be empty.", + "市场清单中未找到该脚本。": "Script not found in marketplace manifest.", + "脚本已安装。": "Script installed.", + "脚本 key 不能为空。": "Script key must not be empty.", + "脚本已启用。": "Script enabled.", + "脚本已禁用。": "Script disabled.", + "脚本启停失败": "Failed to toggle script", + "脚本已删除。": "Script deleted.", + "脚本删除失败": "Failed to delete script", + "只允许打开 http 或 https 链接。": "Only http or https links can be opened.", + "已在系统浏览器打开链接。": "Link opened in system browser.", + "安装入口": "Install entrypoints", + "卸载入口": "Uninstall entrypoints", + "修复快捷方式": "Repair shortcuts", + "修复后重新读取设置失败": "Failed to reload settings after repair", + "插件市场需要初始化或注册。": "Plugin marketplace needs initialization or registration.", + "插件市场已可用。": "Plugin marketplace is available.", + "插件市场已从 openai/plugins 初始化并注册。": "Plugin marketplace initialized and registered from openai/plugins.", + "已注册本地插件市场。": "Local plugin marketplace registered.", + "插件市场已可用,无需修复。": "Plugin marketplace is available; no repair needed.", + "官方远端插件缓存需要释放或注册。": "Official remote plugin cache needs extraction or registration.", + "官方远端插件缓存已可用。": "Official remote plugin cache is available.", + "已释放并注册内置官方远端插件缓存。": "Embedded official remote plugin cache extracted and registered.", + "已注册官方远端插件缓存。": "Official remote plugin cache registered.", + "官方远端插件缓存已可用,无需修复。": "Official remote plugin cache is available; no repair needed.", + "发现可用更新。": "Update available.", + "当前已是最新版本。": "Already on the latest version.", + "请先检查更新并选择可下载的 Release asset。": "Check for updates first and select a downloadable release asset.", + "安装包已下载并启动,请按安装向导完成更新。": "Installer downloaded and launched. Follow the setup wizard to finish updating.", + "日志已读取。": "Logs loaded.", + "诊断报告已生成。": "Diagnostics report generated.", + "设置已重置为默认值。": "Settings reset to defaults.", + "设置重置后重新读取失败": "Failed to reload settings after reset", + "图片覆盖层设置已重置。": "Image overlay settings reset.", + "图片覆盖层重置后重新读取失败": "Failed to reload settings after image overlay reset", + "已检测到 ChatGPT 登录状态。": "ChatGPT login detected.", + "未检测到 ChatGPT 登录状态,请先在 Codex/ChatGPT 中正常登录。": "ChatGPT login not detected. Please log in to Codex/ChatGPT first.", + "配置文件内容已读取。": "Config file content loaded.", + "未检测到会覆盖 Codex 供应商配置的 OPENAI 环境变量。": "No OPENAI environment variables found that would override the Codex provider config.", + "检测到可能覆盖 Codex 供应商配置的 OPENAI 环境变量。": "OPENAI environment variables detected that may override the Codex provider config.", + "环境变量已按确认项删除;重新启动 Codex 后生效。": "Environment variables deleted as confirmed. Restart Codex for changes to take effect.", + "配置文件已保存。": "Config file saved.", + "供应商切换锁已损坏,请重启管理器后再试。": "Provider switch lock is corrupted. Please restart the manager and try again.", + "供应商已切换。": "Provider switched.", + "当前供应商已不在配置列表中,已停止切换以避免覆盖用户改动。": "The current provider is no longer in the config list. Switching stopped to avoid overwriting user changes.", + "当前供应商配置已从 live 文件回填。": "Current provider config backfilled from live files.", + "工具与插件列表已读取。": "Tools & plugins list loaded.", + "通用配置已按兼容切换规则提取。": "Common config extracted using compatibility switching rules.", + "供应商配置总开关已关闭,未写入 config.toml / auth.json。": "Provider config master switch is off. Nothing written to config.toml / auth.json.", + "已按兼容切换规则切换供应商。": "Provider switched using compatibility rules.", + "未检测到 ChatGPT 登录状态": "ChatGPT login not detected", + "未检测到 ChatGPT 登录状态,已停止写入中转配置。": "ChatGPT login not detected. Relay config not written.", + "中转配置已写入,密钥未在界面明文显示。": "Relay config written. Keys are not shown in plaintext in the UI.", + "聚合供应商配置已写入,真实请求会由本地代理按策略轮转。": "Aggregate provider config written. Real requests will be rotated by the local proxy according to the strategy.", + "已清除 custom 中转 API 模式,并切换到官方 ChatGPT 登录模式。": "Custom relay API mode cleared. Switched to official ChatGPT login mode.", + "纯 API 模式已写入:config.toml 已写入 custom provider,auth.json 已切换为当前供应商。": "Pure API mode written: config.toml now has a custom provider, auth.json switched to the current provider.", + "纯 API 配置写入后未检测到完整 custom provider,请检查 config.toml 和供应商 API Key。": "After writing pure API config, no complete custom provider was detected. Please check config.toml and the provider API Key.", + "供应商导入后重新读取设置失败": "Failed to reload settings after provider import", + "诊断日志已写入。": "Diagnostics log written.", + "未命名供应商": "Unnamed provider", + "响应内容为空": "Response is empty", +}; + +export const VI_BACKEND_PATTERNS: Array<[RegExp, string]> = [ + [new RegExp("^启动静默入口失败:(.+)$"), "Failed to launch silent entrypoint: $1"], + [new RegExp("^无法启动 (.+?):(.+)$"), "Cannot launch $1: $2"], + [new RegExp("^设置已保存。(.*)$"), "Settings saved.$1"], + [new RegExp("^保存设置失败:(.+)$"), "Failed to save settings: $1"], + [new RegExp("^已读取 cc-switch Codex 供应商配置:(\\d+) 个。$"), "Read $1 cc-switch Codex provider config(s)."], + [new RegExp("^读取 cc-switch 供应商配置失败:(.+)$"), "Failed to read cc-switch provider configs: $1"], + [new RegExp("^已从 cc-switch 导入供应商配置:(\\d+) 个。$"), "Imported $1 cc-switch provider config(s)."], + [new RegExp("^保存 cc-switch 供应商配置失败:(.+)$"), "Failed to save cc-switch provider configs: $1"], + [new RegExp("^已导入供应商配置:(.+)。$"), "Imported provider config: $1."], + [new RegExp("^供应商配置已存在:(.+)。$"), "Provider config already exists: $1."], + [new RegExp("^导入供应商配置失败:(.+)$"), "Failed to import provider config: $1"], + [new RegExp("^取消供应商导入失败:(.+)$"), "Failed to cancel provider import: $1"], + [new RegExp("^已读取 (\\d+) 个本地会话。$"), "Read $1 local session(s)."], + [new RegExp("^读取部分本地会话失败:(.+)$"), "Failed to read some local sessions: $1"], + [new RegExp("^已读取 (\\d+) 个 Zed 远程项目。$"), "Read $1 Zed remote project(s)."], + [new RegExp("^读取 Zed 远程项目失败。$"), "Failed to read Zed remote projects."], + [new RegExp("^已在 Zed Remote 打开项目。$"), "Project opened in Zed Remote."], + [new RegExp("^无法在 Zed Remote 打开项目。$"), "Cannot open project in Zed Remote."], + [new RegExp("^移除 Zed 远程项目失败。$"), "Failed to remove Zed remote project."], + [new RegExp("^供应商已同步一次:(\\d+) 个会话文件,(\\d+) 行索引,跳过 (\\d+) 个占用文件。$"), "Provider synced: $1 session file(s), $2 index row(s), skipped $3 locked file(s)."], + [new RegExp("^供应商同步失败:(.+)$"), "Provider sync failed: $1"], + [new RegExp("^推荐内容加载失败:(.+)$"), "Failed to load recommendations: $1"], + [new RegExp("^脚本市场加载失败:(.+)$"), "Failed to load script marketplace: $1"], + [new RegExp("^安装脚本失败:(.+)$"), "Failed to install script: $1"], + [new RegExp("^脚本启停失败:(.+)$"), "Failed to toggle script: $1"], + [new RegExp("^脚本删除失败:(.+)$"), "Failed to delete script: $1"], + [new RegExp("^打开链接失败:(.+)$"), "Failed to open link: $1"], + [new RegExp("^插件市场修复失败:(.+)$"), "Plugin marketplace repair failed: $1"], + [new RegExp("^官方远端插件缓存修复失败:(.+)$"), "Official remote plugin cache repair failed: $1"], + [new RegExp("^检查更新失败:(.+)$"), "Failed to check for updates: $1"], + [new RegExp("^安装更新失败:(.+)$"), "Failed to install update: $1"], + [new RegExp("^安装 watcher 失败:(.+)$"), "Failed to install watcher: $1"], + [new RegExp("^移除 watcher 失败:(.+)$"), "Failed to remove watcher: $1"], + [new RegExp("^启用 watcher 失败:(.+)$"), "Failed to enable watcher: $1"], + [new RegExp("^禁用 watcher 失败:(.+)$"), "Failed to disable watcher: $1"], + [new RegExp("^读取日志失败:(.+)$"), "Failed to read logs: $1"], + [new RegExp("^重置设置失败:(.+)$"), "Failed to reset settings: $1"], + [new RegExp("^重置图片覆盖层失败:(.+)$"), "Failed to reset image overlay: $1"], + [new RegExp("^读取配置文件失败:(.+)$"), "Failed to read config file: $1"], + [new RegExp("^删除环境变量失败:(.+)$"), "Failed to delete environment variables: $1"], + [new RegExp("^保存配置文件失败:(.+)$"), "Failed to save config file: $1"], + [new RegExp("^供应商切换失败:(.+)$"), "Provider switch failed: $1"], + [new RegExp("^写入诊断日志失败:(.+)$"), "Failed to write diagnostics log: $1"], + [new RegExp("^回填当前供应商配置失败:(.+)$"), "Failed to backfill current provider config: $1"], + [new RegExp("^读取工具与插件列表失败:(.+)$"), "Failed to read tools & plugins list: $1"], + [new RegExp("^读取 live 工具与插件失败:(.+)$"), "Failed to read live tools & plugins: $1"], + [new RegExp("^保存工具与插件失败:(.+)$"), "Failed to save tools & plugins: $1"], + [new RegExp("^读取 live config.toml 失败:(.+)$"), "Failed to read live config.toml: $1"], + [new RegExp("^同步 live 工具与插件失败:(.+)$"), "Failed to sync live tools & plugins: $1"], + [new RegExp("^创建 Codex 配置目录失败:(.+)$"), "Failed to create Codex config directory: $1"], + [new RegExp("^写入 live config.toml 失败:(.+)$"), "Failed to write live config.toml: $1"], + [new RegExp("^读取同步后的 live 工具与插件失败:(.+)$"), "Failed to read live tools & plugins after sync: $1"], + [new RegExp("^删除工具与插件失败:(.+)$"), "Failed to delete tools & plugins: $1"], + [new RegExp("^提取通用配置失败:(.+)$"), "Failed to extract common config: $1"], + [new RegExp("^已向「(.+?)」用模型「(.+?)」发送 hi,HTTP (\\d+)。(.*)$"), "Sent hi to \"$1\" using model \"$2\", HTTP $3.$4"], + [new RegExp("^测试「(.+?)」失败:(.+)$"), "Test \"$1\" failed: $2"], + [new RegExp("^已从「(.+?)」获取 (\\d+) 个模型。$"), "Fetched $2 model(s) from \"$1\"."], + [new RegExp("^从「(.+?)」获取模型失败:(.+)$"), "Failed to fetch models from \"$1\": $2"], + [new RegExp("^切换完整中转配置失败:(.+)$"), "Failed to switch full relay config: $1"], + [new RegExp("^写入中转配置失败:(.+)$"), "Failed to write relay config: $1"], + [new RegExp("^写入聚合供应商配置失败:(.+)$"), "Failed to write aggregate provider config: $1"], + [new RegExp("^切换纯 API 配置失败:(.+)$"), "Failed to switch to pure API config: $1"], + [new RegExp("^写入纯 API 模式失败:(.+)$"), "Failed to write pure API mode: $1"], + [new RegExp("^清除中转配置失败:(.+)$"), "Failed to clear relay config: $1"], + [new RegExp("^未知配置文件类型:(.+)$"), "Unknown config file type: $1"], + [new RegExp("^启动系统浏览器失败:(.+)$"), "Failed to launch system browser: $1"], + [new RegExp("^诊断报告序列化失败:(.+)$"), "Failed to serialize diagnostics report: $1"], + [new RegExp("^响应:(.+)$"), "Response: $1"], +]; diff --git a/apps/codex-plus-manager/src/i18n.ts b/apps/codex-plus-manager/src/i18n.ts index b9efa2d76..0e1d70b30 100644 --- a/apps/codex-plus-manager/src/i18n.ts +++ b/apps/codex-plus-manager/src/i18n.ts @@ -2,75 +2,84 @@ // // The app is authored in Chinese. Every user-facing Chinese literal is wrapped // with `t("中文")` (plain strings) or `tf("前缀 {0}", [expr])` (interpolated -// strings) by tools/i18n-codemod.mjs. When the active language is English we -// look the source text up in the English dictionary; otherwise we return the -// original Chinese, so Chinese stays the zero-overhead default. +// strings). Dictionaries: i18n-en.ts, i18n-vi.ts. // -// Language is resolved once at module load. Many Chinese literals live in -// module-level constants (route tables, preset labels, …) that evaluate a -// single time at import, so a live in-place swap can't reach them. Switching -// language therefore persists the choice and reloads the webview, which is -// instant for a local Tauri window and guarantees every literal — module-level -// or render-level — re-evaluates under the new language. +// Language is resolved once at module load. Switching language persists and +// reloads the webview so module-level literals re-evaluate. import { EN_BACKEND, EN_BACKEND_PATTERNS, EN_PLAIN, EN_TEMPLATE } from "@/i18n-en"; +import { VI_BACKEND, VI_BACKEND_PATTERNS, VI_PLAIN, VI_TEMPLATE } from "@/i18n-vi"; -export type Language = "zh" | "en"; +export type Language = "zh" | "en" | "vi"; const STORAGE_KEY = "codex-plus-lang"; function resolveInitialLanguage(): Language { try { - return window.localStorage.getItem(STORAGE_KEY) === "en" ? "en" : "zh"; + const stored = window.localStorage.getItem(STORAGE_KEY); + if (stored === "en" || stored === "vi") return stored; } catch { - return "zh"; + // ignore } + return "zh"; } -// Resolved once per webview load. Do not mutate at runtime — use setLanguage, -// which persists and reloads so module-level literals pick up the change. const LANG: Language = resolveInitialLanguage(); export function getLanguage(): Language { return LANG; } -/** Translate a plain Chinese literal. Falls back to the source text. */ -export function t(zh: string): string { - if (LANG !== "en") return zh; - const plain = EN_PLAIN[zh] ?? EN_BACKEND[zh]; +function translatePlain(zh: string): string { + if (LANG === "zh") return zh; + const plain = LANG === "en" ? EN_PLAIN[zh] ?? EN_BACKEND[zh] : VI_PLAIN[zh] ?? VI_BACKEND[zh]; if (plain) return plain; - for (const [re, replacement] of EN_BACKEND_PATTERNS) { + const patterns = LANG === "en" ? EN_BACKEND_PATTERNS : VI_BACKEND_PATTERNS; + for (const [re, replacement] of patterns) { if (re.test(zh)) return zh.replace(re, replacement); } return zh; } -/** - * Translate an interpolated literal. `key` carries `{0}`,`{1}`… placeholders in - * the original (Chinese) order; `args` are the runtime values for each. In - * Chinese we substitute into the key itself; in English we substitute into the - * looked-up template (also falling back to the key). - */ +/** Translate a plain Chinese literal. Falls back to the source text. */ +export function t(zh: string): string { + return translatePlain(zh); +} + export function tf(key: string, args: Array): string { - const template = LANG === "en" ? EN_TEMPLATE[key] ?? key : key; + const dict = LANG === "en" ? EN_TEMPLATE : LANG === "vi" ? VI_TEMPLATE : null; + const template = dict ? dict[key] ?? key : key; return template.replace(/\{(\d+)\}/g, (match, index) => { const value = args[Number(index)]; return value === undefined || value === null ? match : String(value); }); } -/** Persist a new language and reload so every literal re-evaluates under it. */ export function setLanguage(language: Language): void { try { window.localStorage.setItem(STORAGE_KEY, language); } catch { - // Ignore storage failures; the reload below simply keeps the old value. + // ignore } window.location.reload(); } -/** Flip between Chinese and English. */ +const LANG_CYCLE: Language[] = ["zh", "vi", "en"]; + +/** Cycle UI language: 中文 → Tiếng Việt → English. */ +export function cycleLanguage(): void { + const idx = LANG_CYCLE.indexOf(LANG); + const next = LANG_CYCLE[(idx + 1) % LANG_CYCLE.length]; + setLanguage(next); +} + +/** @deprecated Use cycleLanguage() */ export function toggleLanguage(): void { - setLanguage(LANG === "en" ? "zh" : "en"); + cycleLanguage(); +} + +export function languageButtonTitle(): string { + if (LANG === "zh") return translatePlain("切换到越南语"); + if (LANG === "vi") return translatePlain("切换到英文"); + return translatePlain("切换到中文"); } diff --git a/apps/codex-plus-manager/src/i18n.ts.bak b/apps/codex-plus-manager/src/i18n.ts.bak new file mode 100644 index 000000000..b9efa2d76 --- /dev/null +++ b/apps/codex-plus-manager/src/i18n.ts.bak @@ -0,0 +1,76 @@ +// Lightweight source-text-keyed i18n for the Codex++ manager UI. +// +// The app is authored in Chinese. Every user-facing Chinese literal is wrapped +// with `t("中文")` (plain strings) or `tf("前缀 {0}", [expr])` (interpolated +// strings) by tools/i18n-codemod.mjs. When the active language is English we +// look the source text up in the English dictionary; otherwise we return the +// original Chinese, so Chinese stays the zero-overhead default. +// +// Language is resolved once at module load. Many Chinese literals live in +// module-level constants (route tables, preset labels, …) that evaluate a +// single time at import, so a live in-place swap can't reach them. Switching +// language therefore persists the choice and reloads the webview, which is +// instant for a local Tauri window and guarantees every literal — module-level +// or render-level — re-evaluates under the new language. + +import { EN_BACKEND, EN_BACKEND_PATTERNS, EN_PLAIN, EN_TEMPLATE } from "@/i18n-en"; + +export type Language = "zh" | "en"; + +const STORAGE_KEY = "codex-plus-lang"; + +function resolveInitialLanguage(): Language { + try { + return window.localStorage.getItem(STORAGE_KEY) === "en" ? "en" : "zh"; + } catch { + return "zh"; + } +} + +// Resolved once per webview load. Do not mutate at runtime — use setLanguage, +// which persists and reloads so module-level literals pick up the change. +const LANG: Language = resolveInitialLanguage(); + +export function getLanguage(): Language { + return LANG; +} + +/** Translate a plain Chinese literal. Falls back to the source text. */ +export function t(zh: string): string { + if (LANG !== "en") return zh; + const plain = EN_PLAIN[zh] ?? EN_BACKEND[zh]; + if (plain) return plain; + for (const [re, replacement] of EN_BACKEND_PATTERNS) { + if (re.test(zh)) return zh.replace(re, replacement); + } + return zh; +} + +/** + * Translate an interpolated literal. `key` carries `{0}`,`{1}`… placeholders in + * the original (Chinese) order; `args` are the runtime values for each. In + * Chinese we substitute into the key itself; in English we substitute into the + * looked-up template (also falling back to the key). + */ +export function tf(key: string, args: Array): string { + const template = LANG === "en" ? EN_TEMPLATE[key] ?? key : key; + return template.replace(/\{(\d+)\}/g, (match, index) => { + const value = args[Number(index)]; + return value === undefined || value === null ? match : String(value); + }); +} + +/** Persist a new language and reload so every literal re-evaluates under it. */ +export function setLanguage(language: Language): void { + try { + window.localStorage.setItem(STORAGE_KEY, language); + } catch { + // Ignore storage failures; the reload below simply keeps the old value. + } + window.location.reload(); +} + +/** Flip between Chinese and English. */ +export function toggleLanguage(): void { + setLanguage(LANG === "en" ? "zh" : "en"); +} diff --git a/tools/build_i18n_vi.py b/tools/build_i18n_vi.py new file mode 100644 index 000000000..912d88951 --- /dev/null +++ b/tools/build_i18n_vi.py @@ -0,0 +1,42 @@ +import json +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +DATA = json.loads((ROOT / "tools/i18n-export.json").read_text(encoding="utf-8")) +CACHE_PATH = ROOT / "tools/i18n-vi-cache.json" +cache = json.loads(CACHE_PATH.read_text(encoding="utf-8")) if CACHE_PATH.exists() else {} + +def esc(s: str) -> str: + return s.replace("\\", "\\\\").replace(""", "\"").replace(" +", " +").replace(" ", "") + +def vi(en: str) -> str: + return cache.get(en, en) + +def block(name: str, mapping: dict) -> str: + lines = [f"export const {name}: Record = {{"] + for zh, en in mapping.items(): + lines.append(f" "{esc(zh)}": "{esc(vi(en))}",") + lines.append("};") + return " +".join(lines) + +parts = [ + "// Vietnamese UI. Refresh: python tools/gen-i18n-vi.py then python tools/build_i18n_vi.py +", + block("VI_PLAIN", DATA["plain"]), + block("VI_TEMPLATE", DATA["template"]), + block("VI_BACKEND", DATA["backend"]), + "export const VI_BACKEND_PATTERNS: Array<[RegExp, string]> = [", +] +for src, en_rep in DATA["patterns"]: + parts.append(f" [new RegExp({json.dumps(src)}), {json.dumps(vi(en_rep))}],") +parts.append("]; +") +out = ROOT / "apps/codex-plus-manager/src/i18n-vi.ts" +out.write_text(" + +".join(parts), encoding="utf-8") +hit = sum(1 for en in DATA["plain"].values() if en in cache) +print(out, "cache", len(cache), "plain_vi", hit, "/", len(DATA["plain"])) diff --git a/tools/gen-i18n-vi.mjs b/tools/gen-i18n-vi.mjs new file mode 100644 index 000000000..9c1e36ca1 --- /dev/null +++ b/tools/gen-i18n-vi.mjs @@ -0,0 +1,27 @@ +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const exportPath = path.join(root, "tools/i18n-export.json"); +const cachePath = path.join(root, "tools/i18n-vi-cache.json"); +const outPath = path.join(root, "apps/codex-plus-manager/src/i18n-vi.ts"); +const data = JSON.parse(fs.readFileSync(exportPath, "utf8")); +const cache = fs.existsSync(cachePath) ? JSON.parse(fs.readFileSync(cachePath, "utf8")) : {}; +function esc(s){return s.replaceAll("\\","\\\\").replaceAll("\"","\\\"").replaceAll(" +","\n").replaceAll(" ","");} +function viFromEn(en){return cache[en]??en;} +function emitRecord(name,zhToEn){const lines=[`export const ${name}: Record = {`];for(const[zh,en]of Object.entries(zhToEn))lines.push(` "${esc(zh)}": "${esc(viFromEn(en))}",`);lines.push("};");return lines.join(" +");} +const patLines=["export const VI_BACKEND_PATTERNS: Array<[RegExp, string]> = ["]; +for(const[src,enRep]of data.patterns)patLines.push(` [new RegExp(${JSON.stringify(src)}), ${JSON.stringify(viFromEn(enRep))}],`); +patLines.push("];"); +const body=[emitRecord("VI_PLAIN",data.plain),emitRecord("VI_TEMPLATE",data.template),emitRecord("VI_BACKEND",data.backend),patLines.join(" +")].join(" + +"); +fs.writeFileSync(outPath,"// Vietnamese UI + +"+body+" +"); +const hit=Object.values(data.plain).filter(en=>cache[en]).length; +console.log("wrote",outPath,hit,Object.keys(data.plain).length); \ No newline at end of file diff --git a/tools/gen-i18n-vi.py b/tools/gen-i18n-vi.py new file mode 100644 index 000000000..a91c93169 --- /dev/null +++ b/tools/gen-i18n-vi.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +"""Generate i18n-vi.ts from tools/i18n-export.json (EN values -> VI).""" +import json, time +from pathlib import Path +from deep_translator import GoogleTranslator + +ROOT = Path(__file__).resolve().parent.parent +EXPORT = ROOT / "tools" / "i18n-export.json" +OUT = ROOT / "apps" / "codex-plus-manager" / "src" / "i18n-vi.ts" +CACHE = ROOT / "tools" / "i18n-vi-cache.json" +SLEEP = 0.35 + +def esc(s): + return s.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\n").replace("\r", "") + +def translate_map(tr, mapping, cache): + out = {} + items = list(mapping.items()) + for i, (zh_key, en_val) in enumerate(items): + if en_val in cache: + out[zh_key] = cache[en_val] + continue + try: + vi = tr.translate(en_val) + cache[en_val] = vi + out[zh_key] = vi + except Exception as e: + print("fail", i, repr(e)) + out[zh_key] = en_val + cache[en_val] = en_val + if (i + 1) % 10 == 0: + CACHE.write_text(json.dumps(cache, ensure_ascii=False), encoding="utf-8") + print(i + 1, "/", len(items)) + time.sleep(SLEEP) + return out + +def emit_record(name, mapping): + lines = ["export const %s: Record = {" % name] + for k, v in mapping.items(): + lines.append(' "%s": "%s",' % (esc(k), esc(v))) + lines.append("};") + return "\n".join(lines) + +def main(): + data = json.loads(EXPORT.read_text(encoding="utf-8")) + cache = json.loads(CACHE.read_text(encoding="utf-8")) if CACHE.exists() else {} + tr = GoogleTranslator(source="en", target="vi") + vi_plain = translate_map(tr, data["plain"], cache) + vi_template = translate_map(tr, data["template"], cache) + vi_backend = translate_map(tr, data["backend"], cache) + vi_patterns = [] + for src, en_rep in data["patterns"]: + if en_rep in cache: + vi_rep = cache[en_rep] + else: + try: + vi_rep = tr.translate(en_rep) + cache[en_rep] = vi_rep + except Exception: + vi_rep = en_rep + time.sleep(SLEEP) + vi_patterns.append((src, vi_rep)) + CACHE.write_text(json.dumps(cache, ensure_ascii=False), encoding="utf-8") + header = "// Vietnamese translations for Codex++ manager UI (tools/gen-i18n-vi.py)\n\n" + parts = [emit_record("VI_PLAIN", vi_plain), emit_record("VI_TEMPLATE", vi_template), emit_record("VI_BACKEND", vi_backend)] + pat_lines = ["export const VI_BACKEND_PATTERNS: Array<[RegExp, string]> = ["] + for src, vi_rep in vi_patterns: + pat_lines.append(" [new RegExp(%s), %s]," % (json.dumps(src), json.dumps(vi_rep))) + pat_lines.append("];") + OUT.write_text(header + "\n\n".join(parts) + "\n\n" + "\n".join(pat_lines) + "\n", encoding="utf-8") + print("wrote", OUT) + +if __name__ == "__main__": + main() diff --git a/tools/i18n-export.json b/tools/i18n-export.json new file mode 100644 index 000000000..8d958df24 --- /dev/null +++ b/tools/i18n-export.json @@ -0,0 +1 @@ +{"plain":{"API Key 模式下扩展插件市场请求,尽量显示完整插件列表;官方/混合模式通常不需要。":"Expands plugin marketplace requests in API Key mode to show the full plugin list. Usually unnecessary in official/mixed mode.","API Key 环境变量":"API Key environment variable","Chat Completions 转 Responses":"Chat Completions to Responses","Codex 启动参数":"Codex launch arguments","Codex 工具与插件":"Codex tools & plugins","Codex 应用":"Codex app","Codex 应用路径":"Codex app path","Codex 意外停止":"Codex stopped unexpectedly","Codex 版本":"Codex version","Codex 目标":"Codex goals","Codex++ 官方中转站,主打稳定接入和划算价格,支持 GPT-5.5、GPT-5.4、Claude Opus 4.8、Claude Opus 4.7、gpt-image-2 等模型与图像能力。":"The official Codex++ relay, built for stable access and good value, supporting GPT-5.5, GPT-5.4, Claude Opus 4.8, Claude Opus 4.7, gpt-image-2 and other models plus image capabilities.","Codex++ 导入":"Codex++ import","Codex++ 版本":"Codex++ version","Codex增强":"Codex enhancements","Codex增强模式":"Codex enhancement mode","Debug 端口":"Debug port","Fast 按钮":"Fast button","GitHub Release 更新":"GitHub Release update","GitHub Release 检查":"GitHub Release check","Helper 端口":"Helper port","MCP、Skills、Plugins 作为全局配置独立管理,切换任意供应商都会合并。":"MCP, Skills and Plugins are managed independently as global config and merged in whenever you switch providers.","Markdown 导出":"Markdown export","Provider 同步目标":"Provider sync target","Stepwise 直接发送":"Stepwise direct send","基于当前对话生成下一步建议,使用独立 API 配置。":"Generate next-step suggestions from the current conversation using a separate API configuration.","插件与模型":"Plugins and models","管理插件市场、模型列表和服务档位相关增强。":"Manage enhancements for the plugin marketplace, model list and service tier.","对话与输入":"Conversation and input","调整会话管理、输入行为和对话阅读体验。":"Adjust session management, input behavior and conversation reading experience.","界面与启动":"Interface and startup","控制语言、启动速度和 Codex 原生界面调整。":"Control language, startup speed and Codex native UI adjustments.","远程项目":"Remote projects","连接 Zed Remote 和 upstream worktree 辅助能力。":"Connect Zed Remote and upstream worktree helper capabilities.","最多建议数":"Max suggestions","最大输入字符":"Max input characters","最大输出 tokens":"Max output tokens","测试连接":"Test connection","超时毫秒":"Timeout milliseconds","连接":"Connection","高级参数":"Advanced parameters","TOML 配置体":"TOML config body","Watcher 操作":"Watcher action","Watcher 状态":"Watcher status","Watcher 用于保持 Codex++ 接管状态":"The watcher keeps Codex++ in control","Watcher 自动接管":"Watcher auto-takeover","Zed 远程打开":"Zed remote open","Zed 远程项目":"Zed remote projects","Zed 项目记录":"Zed project history","Zed 默认打开策略":"Default Zed open strategy","Zed 默认行为":"Zed default behavior","cc-switch 导入":"cc-switch import","config.toml 预览":"config.toml preview","provider 未记录":"provider not recorded","ssh:// URL 已复制。":"ssh:// URL copied.","、":", ","」的供应商":"”","上一次切换还没有完成,请稍后再试。":"The previous switch hasn't finished yet. Please try again shortly.","上下文大小":"Context size","上下文窗口":"Context window","上次修复结果":"Last repair result","上次更新结果":"Last update result","上游协议":"Upstream protocol","下载并运行安装包":"Download and run installer","不写 API 文件":"Does not write API files","与 Codex 内插件菜单使用同一个远端广告源":"Uses the same remote ad source as the in-Codex plugin menu","个":"","个会话":" session(s)","中国官方":"China official","主页":"Homepage","主题和启动参数":"Theme and launch arguments","从 GitHub 静态清单加载":"Loaded from the static GitHub manifest","从 Word 等富文本粘贴到 Codex composer 时只保留纯文本,避免被识别为图片/文件附件。需重启 Codex 才生效。":"Keeps only plain text when pasting rich text (e.g. from Word) into the Codex composer, avoiding it being treated as an image/file attachment. Requires restarting Codex.","从上游获取":"Fetch from upstream","从最新 upstream 分支创建 Git worktree。":"Create a Git worktree from the latest upstream branch.","从环境变量和 config.toml 的 /v1/models 拉取模型并补进模型列表。":"Pull models from environment variables and config.toml's /v1/models and add them to the model list.","从第三方导入":"Import from third party","从预设模板创建":"Create from a preset template","会话":"Session","会话 ID 标识":"Session ID badge","会话删除":"Session deletion","会话删除、导出、项目移动和用户脚本等界面能力":"UI capabilities like session deletion, export, project move and user scripts","会话删除、导出、项目移动和脚本能力":"Session deletion, export, project move and script capabilities","会话总数":"Total sessions","会话管理":"Session management","会话项目移动":"Session project move","使用":"Use","使用中":"In use","例如 C:\\Program Files\\WindowsApps\\OpenAI.Codex...\\app":"e.g. C:\\Program Files\\WindowsApps\\OpenAI.Codex...\\app","例如 context7":"e.g. context7","例如 deepseek-v4-pro":"e.g. deepseek-v4-pro","例如 gpt-5.4-mini":"e.g. gpt-5.4-mini","例如 主力聚合池":"e.g. Primary aggregate pool","供":"P","供应商":"Provider","供应商切换":"Provider switch","供应商切换不可用":"Provider switching unavailable","供应商切换中":"Switching provider","供应商列表":"Provider list","供应商测试":"Provider test","供应商测试模型":"Provider test model","供应商配置":"Provider configuration","供应商配置可能不正确":"Provider configuration may be incorrect","供应商配置已关闭":"Provider configuration disabled","供应商配置总开关已关闭":"The provider configuration master switch is off","供应商配置总开关已关闭;当前只保存配置,不写入 Codex live 文件":"The provider configuration master switch is off; changes are only saved, not written to Codex live files","供应商预设列表":"Provider preset list","保存":"Save","保存为默认路径":"Save as default path","保存到 Codex++ state,不改写 Zed settings。":"Saved to Codex++ state, does not modify Zed settings.","保存增强设置":"Save enhancement settings","保存扩展项":"Save entry","保存的应用路径":"Saved app path","保存策略":"Save strategy","保存自动修复设置":"Save auto-repair settings","保存设置":"Save settings","保存路径":"Saved path","修复入口":"Repair entrypoints","修复快捷方式":"Repair shortcuts","修复插件市场":"Repair plugin marketplace","健康检查":"Health check","先添加至少 1 个已填写 Base URL / Key 的 API 供应商,再创建聚合供应商。":"Add at least 1 API provider with a Base URL / Key before creating an aggregate provider.","免安装版或解包版只需要选择一次,之后静默启动会自动复用":"Portable or unpacked builds only need to be selected once; silent launch reuses it afterwards","入口卸载":"Entrypoint uninstall","入口安装":"Entrypoint install","入口安装、修复、Watcher 与手动启动":"Entrypoint install, repair, watcher and manual launch","入口管理":"Entrypoint management","全选当前列表":"Select all in current list","关于":"About","关于 Codex++":"About Codex++","关闭":"Off","关闭后会停用删除、导出、项目移动、插件相关和菜单位置增强。":"When off, disables deletion, export, project move, plugin-related and menu placement enhancements.","关闭后本工具不会在手动切换时写入 Codex 的 config.toml / auth.json;启动 Codex 时始终不会自动改这些文件。":"When off, this tool won't write Codex's config.toml / auth.json on manual switches; it never auto-modifies these files when launching Codex.","兼容增强":"Compatible enhancement","内容来自 BigPizzaV3/Ad-List,分为赞助商推荐和普通推荐。":"Content comes from BigPizzaV3/Ad-List, split into sponsor recommendations and regular recommendations.","内置":"Built-in","内置、手动和市场安装脚本;可在这里启停或删除用户脚本":"Built-in, manual and marketplace-installed scripts; enable, disable or delete user scripts here","内置和用户自定义脚本清单":"Built-in and user-defined script inventory","切换 thread 时恢复上一次浏览位置。":"Restore the last scroll position when switching threads.","切换中":"Switching","切换主题":"Toggle theme","切换到此供应商时会写入 ~/.codex/auth.json":"Switching to this provider writes ~/.codex/auth.json","切换到此供应商时会写入的预览;上下文开关变化会立即反映":"Preview of what gets written when switching to this provider; context toggles are reflected immediately","切换到中文":"Switch to Chinese","切换到英文":"Switch to English","切换到浅色":"Switch to light","切换到深色":"Switch to dark","切换对话保留位置":"Preserve scroll position across threads","点击建议后自动发送;关闭时只填入输入框。":"Automatically sends after clicking a suggestion. When off, it only fills the composer.","刚刚启动":"Just launched","删除":"Delete","删除会创建本地备份;如果 Codex App 正在使用该会话,建议先关闭对应会话窗口再操作。":"Deletion creates a local backup; if the Codex app is using this session, close the session window first.","删除会话":"Delete session","删除供应商":"Delete provider","删除已选":"Delete selected","删除扩展项":"Delete entry","删除模型":"Delete model","刷新":"Refresh","刷新会话":"Refresh sessions","刷新列表":"Refresh list","刷新市场":"Refresh marketplace","刷新当前页面":"Refresh current page","刷新推荐":"Refresh recommendations","刷新本地":"Refresh local","刷新项目":"Refresh projects","加入当前工作区":"Add to current workspace","包含版本、路径、设置和平台信息":"Includes version, paths, settings and platform info","协议":"Protocol","卸载入口":"Uninstall entrypoints","卸载时移除 Codex++ 托管数据":"Remove Codex++ managed data on uninstall","历史会话修复":"Historical session repair","历史会话修复失败,请查看错误提示后重试。":"Historical session repair failed. Check the error message and retry.","历史会话修复进度":"Historical session repair progress","压缩上下文大小":"Compaction context size","原生菜单栏位置":"Native menu bar placement","原生菜单汉化":"Native menu localization","反馈问题":"Report an issue","发现":"Discovered","发送 hi 测试":"Send a 'hi' test","取消":"Cancel","只保留非 MCP、Skills、Plugins 的跨供应商配置;工具与插件在独立页面管理。":"Keeps only cross-provider config that isn't MCP/Skills/Plugins; tools and plugins are managed on a separate page.","只填写表头下面的内容,例如:\ncommand = \"npx\"\nargs = [\"-y\", \"@upstash/context7-mcp\"]":"Fill in only the content under the header, for example:\ncommand = \"npx\"\nargs = [\"-y\", \"@upstash/context7-mcp\"]","只能勾选已填写 Base URL / Key 的 API 供应商,聚合供应商不会作为成员。":"Only API providers with a Base URL / Key can be selected; aggregate providers can't be members.","可更新":"Update available","同一对话保持一个成员,不同对话依次分配。":"Keeps one member per conversation; different conversations are assigned in turn.","同步 Zed settings":"Sync Zed settings","同步目标":"Sync target","名称":"Name","启动 Codex App 时追加到默认 CDP 参数后。留空则保持默认启动行为。":"Appended after the default CDP arguments when launching the Codex app. Leave empty to keep default launch behavior.","启动 Codex++":"Launch Codex++","启动任务":"Launch task","启动前自动修复历史会话":"Auto-repair historical sessions before launch","启动时通过本地主进程调试端口汉化 Codex 原生菜单;不修改安装包。需重启 Codex 才生效。":"Localizes the Codex native menu via the local main-process debug port at launch; doesn't modify the install package. Requires restarting Codex.","启用":"Enable","启用 Codex 图片覆盖层":"Enable Codex image overlay","启用 Codex增强":"Enable Codex enhancements","启用 Windows Computer Use Guard":"Enable Windows Computer Use Guard","启用供应商配置切换":"Enable provider configuration switching","启用此扩展项":"Enable this entry","启用目标功能":"Enable goals feature","图片":"Image","图片覆盖层":"Image overlay","在会话列表悬停显示删除按钮,并支持撤销。":"Show a delete button on hover in the session list, with undo support.","在会话列表显示导出按钮,导出带时间戳的 Markdown。":"Show an export button in the session list to export timestamped Markdown.","在 Codex 页面显示可拖动的后续建议浮层;建议由单独配置的 Stepwise API 生成。":"Shows a draggable follow-up suggestion overlay in the Codex page. Suggestions are generated by a separate Stepwise API.","在侧边栏会话标题前显示短 ID 和 UUIDv7 创建时间,方便定位历史会话。":"Show a short ID and UUIDv7 creation time before session titles in the sidebar to help locate past sessions.","基础设置":"Basic settings","填写中转服务 Base URL":"Enter the relay service Base URL","复制":"Copy","复制 ssh:// URL":"Copy ssh:// URL","复制失败":"Copy failed","复制报告":"Copy report","复用窗口":"Reuse window","多选":"Multi-select","失败":"Failed","失败切换":"Failover","失败切换会保留成员顺序,优先使用第一个可用供应商。":"Failover preserves member order, preferring the first available provider.","如果使用官方模式或官方混入 API 模式,通常不需要开启插件市场解锁。":"If you use official mode or official mixed-API mode, you usually don't need plugin marketplace unlock.","安装":"Install","安装 watcher":"Install watcher","安装入口":"Install entrypoints","安装包更新失败,请查看错误提示后重试。":"Installer update failed. Check the error message and retry.","安装包更新进度":"Installer update progress","安装维护":"Install & maintenance","完整增强":"Full enhancement","官方":"Official","官方中转站":"Official relay","官方混入 API Key":"Official mixed-in API Key","官方混合 API 不应在 auth.json 中保存 OPENAI_API_KEY。请清理此供应商的 auth.json 后再切换。":"Official mixed API should not store OPENAI_API_KEY in auth.json. Clear this provider's auth.json before switching.","官方登录":"Official login","官方登录模式":"Official login mode","对话居中宽度":"Centered conversation width","导入 Codex++ 供应商":"Import Codex++ provider","尚未刷新":"Not refreshed yet","尚未加载推荐内容":"Recommendations not loaded yet","尚未检查 Codex 应用路径。":"Codex app path not checked yet.","尚未检查 GitHub Release;更新会下载并启动安装包。":"GitHub Release not checked yet; updating downloads and launches the installer.","尚未生成诊断报告。":"No diagnostic report generated yet.","尚未运行安装包更新。":"Installer update has not run yet.","尚未运行历史会话修复。":"Historical session repair hasn't run yet.","尚未运行插件市场修复。":"Plugin marketplace repair hasn't run yet.","尚未检查官方远端插件缓存。":"Official remote plugin cache has not been checked yet.","正在检查内置官方远端插件缓存…":"Checking embedded official remote plugin cache…","正在释放内置远端插件快照…":"Extracting embedded remote plugin snapshot…","正在注册官方远端插件市场…":"Registering official remote plugin marketplace…","正在刷新官方远端插件缓存状态…":"Refreshing official remote plugin cache status…","正在处理…":"Processing…","工具与插件":"Tools & plugins","已切回官方登录;Codex增强已设为兼容增强。":"Switched back to official login; Codex enhancements set to compatible.","已切换到纯 API;Codex增强已设为完整增强。":"Switched to pure API; Codex enhancements set to full.","已切换到聚合供应商;真实对话会按所选策略轮转成员。":"Switched to aggregate provider; real conversations rotate members per the chosen strategy.","已刷新 Codex 应用、入口和 Watcher 状态。":"Refreshed Codex app, entrypoint and watcher status.","已受理":"Accepted","已安装":"Installed","已归档":"Archived","已打开聚合供应商详情;请先添加或完善至少 1 个普通 API 供应商的 Base URL / Key,再勾选为成员。":"Opened aggregate provider details; first add or complete the Base URL / Key of at least 1 regular API provider, then select it as a member.","已找到":"Found","已按此供应商使用官方登录,并混入 API Key;Codex增强已设为兼容增强。":"Using official login for this provider with the API Key mixed in; Codex enhancements set to compatible.","已按此供应商切回官方登录;Codex增强已设为兼容增强。":"Switched back to official login for this provider; Codex enhancements set to compatible.","已按此供应商切换到纯 API;Codex增强已设为完整增强。":"Switched to pure API for this provider; Codex enhancements set to full.","已检测":"Detected","已注册":"Registered","已缓存未注册":"Cached, not registered","已清除保存路径,后续启动会回到自动探测。":"Saved path cleared; future launches fall back to auto-detection.","已登录":"Logged in","已禁用":"Disabled","已选择":"Selected","市场状态":"Marketplace status","市场脚本":"Marketplace scripts","序列化字段":"Serialized fields","应用路径已保存,之后启动会自动复用。":"App path saved; future launches reuse it automatically.","应用路径留空时使用已保存路径;没有保存路径时使用自动探测":"When the app path is empty, the saved path is used; with no saved path, auto-detection is used","应用路径覆盖":"App path override","开启":"On","开启后,通过 Codex++ 启动 Codex 前自动整理一次旧对话的归属标记。":"When on, tidies up ownership tags of old conversations once before launching Codex through Codex++.","强制中文界面":"Force Chinese UI","强制启用 Codex App 内置 zh-CN 语言包,避免 Statsig/VPN 不通时回退英文。需重启 Codex 才能完整生效。":"Forces the Codex app's built-in zh-CN language pack to avoid falling back to English when Statsig/VPN is unreachable. Requires restarting Codex to fully apply.","当前":"Current","当前 provider":"current provider","当前不会写入 Codex config.toml / auth.json。打开供应商配置总开关后再切换。":"Codex config.toml / auth.json won't be written right now. Turn on the provider configuration master switch before switching.","当前为":"Currently ","当前为兼容增强模式,插件市场解锁不会启用;其他页面功能仍可用。":"Currently in compatible enhancement mode; plugin marketplace unlock is not enabled, but other page features still work.","当前会话":"Current session","当前使用中:打开时从 ~/.codex/auth.json 回填,保存后会作为此供应商 auth 存档":"Currently in use: backfilled from ~/.codex/auth.json when opened; saving stores it as this provider's auth archive","当前供应商 config.toml 里没有可提取的通用配置。":"The current provider's config.toml has no extractable common config.","当前供应商切换后会写入的预览;上下文开关变化会立即反映":"Preview of what the current provider writes after switching; context toggles are reflected immediately","当前供应商还没有完整 config.toml / API Key 存档。":"The current provider doesn't have a complete config.toml / API Key archive yet.","当前共有":"There are currently ","当前未登录官方账号,也未配置混入 API 的 Base URL / Key。":"Not logged into an official account, and no mixed-in API Base URL / Key configured.","当前未登录官方账号;切到官方登录模式后仍需要先在 Codex/ChatGPT 登录。":"Not logged into an official account; after switching to official login mode you still need to log in via Codex/ChatGPT first.","当前未登录官方账号;官方登录混入 API Key 需要先登录官方账号。":"Not logged into an official account; official login with a mixed-in API Key requires logging into an official account first.","当前正在使用":"Currently in use","当前识别":"Currently detected","当前还没有填写混入 API 的 Base URL / Key。":"The mixed-in API Base URL / Key hasn't been filled in yet.","当前进程":"Current process","当前配置 provider":"Currently configured provider","快捷方式修复":"Shortcut repair","快捷方式写入系统实际桌面位置,不使用写死桌面路径":"Shortcuts are written to the system's actual desktop location, not a hardcoded desktop path","快速启动":"Fast startup","总权重":"Total weight","成员供应商":"Member providers","成员数量":"Member count","或":" or ","手动":"Manual","手动启动":"Manual launch","打开":"Open","打开 JOJO Code":"Open JOJO Code","打开关于":"Open About","打开链接":"Open link","打开项目主页":"Open project homepage","扩展项":"entry","批量删除会话":"Bulk delete sessions","把 Codex++ 菜单插入 Codex 顶部原生菜单栏。":"Insert the Codex++ menu into Codex's top native menu bar.","把主对话和输入框限制到固定最大宽度,适合大屏阅读。":"Constrain the main conversation and input box to a fixed max width, good for large-screen reading.","把会话移动到普通对话或其他本地项目。":"Move a session to a regular conversation or another local project.","投稿":"Submit","拖动排序":"Drag to reorder","按对话轮转":"Per-conversation round-robin","按对话轮转会让同一对话尽量保持固定成员,降低上下文漂移。":"Per-conversation round-robin keeps the same conversation on a fixed member where possible, reducing context drift.","按成员权重分配请求,权重越高承担越多。":"Distribute requests by member weight; higher weight takes on more.","按成员顺序请求,失败后切到下一个供应商。":"Request members in order, switching to the next provider on failure.","按更新时间倒序显示":"Sorted by update time, newest first","按请求轮转":"Per-request round-robin","按请求轮转会逐请求切换成员,适合供应商能力接近的场景。":"Per-request round-robin switches members request by request, suited to providers with similar capabilities.","接入模式":"Access mode","推荐内容":"Recommendations","提取当前供应商配置":"Extract current provider config","插件":"Plugins","插件列表全量展示":"Show full plugin list","插件市场修复":"Plugin marketplace repair","插件市场修复失败,请查看错误提示后重试。":"Plugin marketplace repair failed. Check the error message and retry.","插件市场修复进度":"Plugin marketplace repair progress","插件市场解锁":"Plugin marketplace unlock","官方远端插件缓存":"Official remote plugin cache","官方远端插件缓存进度":"Official remote plugin cache progress","官方远端插件缓存修复失败,请查看错误提示后重试。":"Official remote plugin cache repair failed. Check the error message and retry.","使用 Codex++ 内置快照补齐远端插件,API 模式也可显示和安装 Product Design 插件。":"Use the embedded Codex++ snapshot to add remote plugins, so API mode can also show and install the Product Design plugin.","搜索供应商…":"Search providers…","数据库":"Database","新增":"Add ","新建供应商需要先保存到列表":"A new provider must be saved to the list first","新机器没有本地插件市场时,可从 openai/plugins 初始化到当前 CODEX_HOME。":"On a new machine without a local plugin marketplace, initialize it into the current CODEX_HOME from openai/plugins.","新窗口":"New window","日志已刷新":"Logs refreshed","日志已复制。":"Logs copied.","时间":"Time","释放并注册内置缓存":"Extract and register embedded cache","显示服务模式切换按钮;Fast 仅支持 gpt-5.4 / gpt-5.5,其他模型按 Standard 发送。":"Show the service-tier toggle button; Fast only supports gpt-5.4 / gpt-5.5, other models are sent as Standard.","普通推荐":"Regular recommendations","暂无":"None","暂无启动状态。":"No launch status yet.","暂无描述。":"No description.","暂无日志。":"No logs.","暂无普通推荐。":"No regular recommendations.","暂无状态文件":"No status file","暂无赞助商推荐。":"No sponsor recommendations.","暂无项目。":"No projects.","更多选项":"More options","更新":"Update","更新安装":"Update install","最新版本":"Latest version","最近启动":"Recent launches","最近打开":"Recently opened","最近日志":"Recent logs","未发现可导入供应商":"No importable providers found","未发现用户脚本。":"No user scripts found.","未命名会话":"Untitled session","未命名供应商":"Untitled provider","未命名聚合供应商":"Untitled aggregate provider","未填写":"Not filled in","未填写 URL":"No URL filled in","未安装":"Not installed","未实现":"Not implemented","未归档":"Not archived","未检查":"Not checked","未检测到":"Not detected","未检测到 Codex 应用版本。":"Codex app version not detected.","未发现缓存":"Cache not found","未发现本地缓存;点击按钮会从 Codex++ 内置快照释放并注册,无需官方账号预缓存。":"Local cache not found. Click the button to extract and register the embedded Codex++ snapshot; no official-account pre-cache is required.","未知":"Unknown","未知作者":"Unknown author","未知来源":"Unknown source","未知版本":"Unknown version","未记录路径":"No path recorded","未记录项目路径":"No project path recorded","未读取到本地会话,或当前 SQLite 会话库不存在。":"No local sessions read, or the current SQLite session database doesn't exist.","本地 Codex 增强、管理工具和安装包维护":"Local Codex enhancements, management tools and install package maintenance","本地会话":"Local sessions","本地整体":"Local overall","本地脚本":"Local scripts","权重":"Weight","权重轮转":"Weighted round-robin","权重轮转会读取每个成员的权重值,权重越高的成员获得更多请求。":"Weighted round-robin reads each member's weight; members with higher weight receive more requests.","查看、删除和修复 Codex 本地会话":"View, delete and repair Codex local sessions","检查":"Check","检查与修复":"Check & repair","检查入口、Codex 应用和 Watcher 状态":"Check entrypoint, Codex app and watcher status","检查配置、模型列表和一次真实请求,定位供应商不可用原因。":"Check configuration, model list and one real request to find why a provider is unavailable.","检查完成":"Check complete","检查更新":"Check for updates","检查问题、启动与快速修复":"Check issues, launch and quick fixes","诊断供应商":"Diagnose provider","检测":"Detect","检测到 OPENAI 环境变量":"OPENAI environment variables detected","检测到来自网页的供应商配置导入请求,确认后会写入本机 Codex++ 管理工具。":"A provider config import request from the web was detected; confirming writes it to this machine's Codex++ manager.","概览":"Overview","概览只展示关键问题,具体配置在对应页面处理":"The overview shows only key issues; specific configuration is handled on the relevant pages","概览已检查":"Overview checked","模型列表":"Model list","模型名称":"Model name","模型白名单解锁":"Model whitelist unlock","模式":"Mode","模式。":" mode.","正在下载安装包…":"Downloading installer…","正在下载插件市场快照…":"Downloading plugin marketplace snapshot…","正在修复…":"Repairing…","正在修复历史会话":"Repairing historical sessions","正在准备安装包下载…":"Preparing installer download…","正在启动安装包…":"Launching installer…","正在写入 Codex 配置…":"Writing Codex config…","正在写入修复与备份…":"Writing repairs and backups…","正在删除…":"Deleting…","正在扫描历史会话与索引…":"Scanning historical sessions and indexes…","正在检查会话 provider 标记…":"Checking session provider tags…","正在检查本地插件市场…":"Checking local plugin marketplace…","正在获取 GitHub Release 信息…":"Fetching GitHub Release information…","正在解压并校验插件文件…":"Extracting and verifying plugin files…","正在连接 openai/plugins…":"Connecting to openai/plugins…","正常":"OK","此上游会通过本地 127.0.0.1:57321 转成 Responses API,需要从 Codex++ 启动 Codex。":"This upstream is converted to the Responses API via local 127.0.0.1:57321, which requires launching Codex from Codex++.","此供应商会保留官方登录模式,并把请求混入当前 API Key;Codex增强仍使用兼容模式。":"This provider keeps official login mode and mixes the current API Key into requests; Codex enhancements stay in compatible mode.","此供应商会切回官方登录模式,使用 ChatGPT 官方账号,不写入 API Key。":"This provider switches back to official login mode using the official ChatGPT account, without writing an API Key.","此供应商会同时写入 config.toml 和 auth.json;API Key 也会注入到 provider bearer token。":"This provider writes both config.toml and auth.json; the API Key is also injected into the provider bearer token.","每次请求按成员顺序切换,适合均匀摊请求量。":"Switches members in order on each request, good for evenly spreading request volume.","每行一个参数,例如 --force_high_performance_gpu。不需要填写 open 或 --args。":"One argument per line, e.g. --force_high_performance_gpu. No need to include open or --args.","每行一个模型;上下文窗口可填":"One model per line; the context window can be","没有匹配「":"No providers matching “","浅色":"Light","测试模型":"Test model","消息":"Message","深色":"Dark","混入 API":"Mixed-in API","混入 API KEY":"Mix in API KEY","混入 API Key":"Mix in API Key","添加供应商":"Add provider","添加模型":"Add model","添加聚合供应商":"Add aggregate provider","清空选择":"Clear selection","清除保存路径":"Clear saved path","点击刷新会话读取本地数据库":"Click Refresh Sessions to read the local database","点击刷新市场加载远程脚本。":"Click Refresh Marketplace to load remote scripts.","版本信息、项目链接、GitHub Release 更新、日志与诊断":"Version info, project links, GitHub Release updates, logs and diagnostics","状态":"Status","独立管理 Codex 的 MCP、Skills、Plugins;切换任意供应商都会带上。":"Manage Codex's MCP, Skills and Plugins independently; they carry over whenever you switch providers.","独立管理 MCP、Skills、Plugins":"Independently manage MCP, Skills and Plugins","环境变量":"Environment variables","环境变量检测":"Environment variable detection","环境变量清理":"Environment variable cleanup","用户":"User","用户环境":"User environment","界面主题":"UI theme","留空不改写,例如 160000":"Leave empty to keep unchanged, e.g. 160000","留空不改写,例如 200000":"Leave empty to keep unchanged, e.g. 200000","留空使用默认值":"Leave empty to use the default","登录状态":"Login status","确认删除":"Confirm delete","确认导入":"Confirm import","禁用":"Disable","禁用此扩展项":"Disable this entry","移除 watcher":"Remove watcher","移除最近记录":"Remove from recent","立刻修复历史会话":"Repair historical sessions now","第三方":"Third party","策略":"Strategy","管理 API 供应商、协议、Key 与配置文件":"Manage API providers, protocols, keys and config files","管理 Codex SSH 项目并加入 Zed workspace":"Manage Codex SSH projects and add them to a Zed workspace","管理工具入口":"Manager entrypoint","管理控制台":"Management console","管理控制台入口":"Management console entrypoint","类型":"Type","粘贴修复":"Paste fix","索引":"Index","纯 API":"Pure API","纯 API 就绪:会同时写入 config.toml 和 auth.json。":"Pure API ready: writes both config.toml and auth.json.","纯 API 模式":"Pure API mode","纯 API 配置未完整写入:请检查此供应商是否有 OPENAI_API_KEY,且 config.toml 是否包含 model_provider / provider / base_url。":"Pure API config is incomplete: check whether this provider has OPENAI_API_KEY and whether config.toml includes model_provider / provider / base_url.","维护 Codex++ 自己的远程项目最近列表。":"Maintain Codex++'s own recent list of remote projects.","编辑":"Edit","编辑后保存列表,再切换模式时会使用新配置":"After editing, save the list; the new config is used the next time you switch modes","编辑扩展项":"Edit entry","缺失":"Missing","缺少 Codex++ 静默启动快捷方式时可在安装维护页修复。":"When the Codex++ silent launch shortcut is missing, repair it on the Install & Maintenance page.","缺少管理工具快捷方式时可在安装维护页修复。":"When the manager shortcut is missing, repair it on the Install & Maintenance page.","聚合":"Aggregate","聚合/中转":"Aggregate/relay","聚合供应商":"Aggregate provider","聚合供应商会在真实对话中轮转成员,请测试成员供应商":"Aggregate providers rotate members in real conversations; test the member providers instead","聚合供应商只保存成员和策略配置,成员来自已有 API 供应商;切为当前后会通过本地协议代理轮转请求。":"Aggregate providers only store member and strategy config drawn from existing API providers; once active, requests rotate through the local protocol proxy.","聚合供应商至少需要勾选 1 个已填写 Base URL / Key 的 API 供应商。":"An aggregate provider needs at least 1 selected API provider with a Base URL / Key filled in.","聚合策略":"Aggregate strategy","聚合配置只引用已有供应商,不复制 Key 和配置文件":"Aggregate config only references existing providers; it doesn't copy keys or config files","脚本市场":"Script marketplace","自动接管":"Auto-takeover","覆盖图片":"Overlay image","背景适配方式":"Background fit mode","填充":"Fill","适应":"Fit","拉伸":"Stretch","平铺":"Tile","居中":"Center","记录最近打开":"Record recently opened","设为当前":"Set as current","设置":"Settings","设置保存":"Settings saved","设置已加载":"Settings loaded","设置重置":"Settings reset","诊断已生成":"Diagnostics generated","诊断报告":"Diagnostic report","诊断报告已复制。":"Diagnostic report copied.","点击后会打开诊断弹框,按步骤检查供应商。":"Click to open the diagnostic dialog and check the provider step by step.","正在诊断供应商,请稍候。":"Diagnosing provider, please wait.","诊断已完成。":"Diagnosis complete.","诊断中":"Diagnosing","异常":"Error","完成":"Complete","配置完整性":"Configuration integrity","等待检查 Base URL / API Key。":"Waiting to check Base URL / API Key.","等待检查 /v1/models。":"Waiting to check /v1/models.","真实请求":"Real request","等待发送一次测试请求。":"Waiting to send one test request.","处理建议":"Recommendation","等待生成建议。":"Waiting to generate a recommendation.","正在检查配置完整性…":"Checking configuration integrity…","该步骤未执行。":"This step was not run.","请先填写或选择应用路径。":"Fill in or select an app path first.","请先选择要删除的会话。":"Select the sessions to delete first.","读取 Codex 本地 SQLite 会话库,会删除数据库记录和对应 rollout 文件":"Reads Codex's local SQLite session database; deletes database records and the corresponding rollout files","读取 cc-switch 供应商失败。":"Failed to read cc-switch providers.","读取 ~/.cc-switch/cc-switch.db":"Reading ~/.cc-switch/cc-switch.db","调用失败":"Call failed","资源":"Asset","赞助商推荐":"Sponsor recommendations","赞助商推荐与普通推荐":"Sponsor and regular recommendations","输入中转服务的 API Key":"Enter the relay service API Key","运行中":"Running","返回列表":"Back to list","这些变量可能覆盖当前供应商写入的 config.toml / auth.json;CODEX_HOME 不会被清理。":"These variables may override the config.toml / auth.json written by the current provider; CODEX_HOME won't be cleaned up.","进入插件页后自动连续展开“更多”,尽量一次显示完整插件列表。":"Automatically keep expanding 'More' on the plugins page to show the full plugin list at once.","进度":"Progress","远程 SSH 文件引用可直接用 Zed Remote Development 打开。":"Remote SSH file references can be opened directly with Zed Remote Development.","远程脚本":"Remote scripts","适合官方登录或官方混入 API Key;保留会话删除、导出、项目移动和用户脚本,关闭插件市场相关增强。":"Suited to official login or official mixed-in API Key; keeps session deletion, export, project move and user scripts, while disabling plugin-marketplace-related enhancements.","适合纯 API;启用插件市场、会话删除导出、项目移动等全部页面能力。":"Suited to pure API; enables all page capabilities including plugin marketplace, session deletion/export and project move.","选择 Codex 应用目录":"Select Codex app directory","选择 Codex.exe":"Select Codex.exe","选择 Codex.exe 或 Codex.app":"Select Codex.exe or Codex.app","选择 Codex.exe、Codex.app、app 目录或解包目录":"Select Codex.exe, Codex.app, the app directory or the unpacked directory","选择 png / jpg / webp / gif / bmp":"Select png / jpg / webp / gif / bmp","选择会话":"Select session","选择图片":"Select image","选择已有供应商作为成员,保存后写入 settings payload":"Select existing providers as members; saving writes them to the settings payload","选择应用目录":"Select app directory","选择覆盖图片":"Select overlay image","通用配置文件":"Common config file","配置":"Config","配置文件":"Config file","配置模型":"Config model","重启 Codex++":"Restart Codex++","重新安装":"Reinstall","重新生成":"Regenerate","重置背景":"Reset background","静默启动入口":"Silent launch entrypoint","项目地址":"Project URL","额外参数":"Extra arguments","高级选项,默认关闭;当前实现不主动改写 Zed settings。":"Advanced option, off by default; the current implementation doesn't actively modify Zed settings.","默认中转":"Default relay","默认关闭;开启后启动 Codex 时会自动保留官方 Computer Use 插件所需的 config.toml、bundled 插件和 notify 配置。":"Off by default; when on, launching Codex automatically preserves the config.toml, bundled plugins and notify config required by the official Computer Use plugin.","默认启动 Codex 时使用的模型名,请勿带后缀;上下文窗口请在下方「模型列表」中按模型单独配置。":"The model name used by default when launching Codex; don't include a suffix. Configure context windows per model in the “Model list” below.","默认关闭;无 VPN 时可开启,让 Statsig 初始化快速失败,减少启动时长。需重启 Codex 才生效。":"Off by default; enable it without a VPN to let Statsig initialization fail fast and reduce launch time. Requires restarting Codex.","默认打开策略":"Default open strategy","(":" (",")":")",",可以从通用配置文件或这里新增。":"; you can add them from the common config file or here.",",留空表示使用 Codex 默认长度。":"; leave empty to use Codex's default length.",";这些条目独立于供应商保存,会写入所有供应商切换后的 config.toml。":"; these entries are saved independently of providers and written to config.toml after switching to any provider."},"template":{"\n...以及另外 {0} 个会话":"\n...and {0} more session(s)","{0} · {1} 个成员":"{0} · {1} member(s)","{0} 个":"{0}","{0} 个 Codex++ 可识别项目,默认策略:{1}":"{0} project(s) recognized by Codex++, default strategy: {1}","{0} 个供应商":"{0} provider(s)","{0} 个供应商配置;可拖动排序,点编辑进入详情":"{0} provider configuration(s); drag to reorder, click edit for details","{0} 个市场脚本,已安装 {1} 个,本地整体 {2}":"{0} marketplace script(s), {1} installed, local overall {2}","{0} 个项目":"{0} project(s)","{0} 副本":"{0} copy","{0} 条":"{0} item(s)","供应商 {0}":"Provider {0}","供应商「{0}」缺少独立 config.toml,已停止切换,避免继续显示上一套配置文件。请先在该供应商详情里保存 config.toml。":"Provider “{0}” is missing its own config.toml, so the switch was stopped to avoid showing the previous config files. Save config.toml in this provider's details first.","删除会话“{0}”?此操作会删除本地数据库记录和 rollout 文件,并创建备份。":"Delete session “{0}”? This deletes the local database record and rollout file, and creates a backup.","删除脚本“{0}”?此操作会移除本地脚本文件。":"Delete script “{0}”? This removes the local script file.","删除这些环境变量?\n\n{0}\n\n删除前会写入备份。":"Delete these environment variables?\n\n{0}\n\nA backup is written before deletion.","删除选中的 {0} 个会话?此操作会删除本地数据库记录和 rollout 文件,并为每个会话创建备份。\n\n{1}{2}":"Delete the {0} selected session(s)? This deletes the local database records and rollout files, and creates a backup for each session.\n\n{1}{2}","发现 {0} 个 Codex 供应商":"Found {0} Codex provider(s)","发现新版本 {0}":"New version {0} available","官方登录已就绪:{0},会混入当前 API Key。":"Official login ready: {0}, the current API Key will be mixed in.","官方账号已登录:{0}。":"Official account logged in: {0}.","已删除 {0} 个会话。":"Deleted {0} session(s).","已删除 {0} 个,失败 {1} 个:{2}":"Deleted {0}, failed {1}: {2}","已加载 {0} 条推荐":"Loaded {0} recommendation(s)","已同步到 {0}:修复 {1} 个会话文件,更新 {2} 行索引{3}。":"Synced to {0}: repaired {1} session file(s), updated {2} index row(s){3}.","已安装 {0}":"Installed {0}","已缓存 {0} 个插件 / {1} 个技能。":"Cached {0} plugin(s) / {1} skill(s).","已运行 {0} 分钟":"Running for {0} minute(s)","已运行 {0} 小时 {1} 分钟":"Running for {0} hour(s) {1} minute(s)","市场 · {0}":"Marketplace · {0}","当前版本 {0}":"Current version {0}","打开选择器失败:{0}":"Failed to open the picker: {0}","正在同步到 {0}…":"Syncing to {0}…","清单更新时间:{0}":"Manifest updated at: {0}","留空使用默认:{0}":"Leave empty to use the default: {0}","聚合供应商 {0}":"Aggregate provider {0}","聚合供应商已配置为{0},包含 {1} 个成员;真实对话会走本地代理轮转。":"The aggregate provider is configured as {0} with {1} member(s); real conversations rotate through the local proxy.","进程状态:{0}。是否要重新启动?":"Process status: {0}. Restart?","选择会话 {0}":"Select session {0}","透明度 {0}%":"Opacity {0}%",",跳过 {0} 个占用文件":", skipped {0} locked file(s)"},"backend":{"后端版本已读取。":"Backend version loaded.","启动参数已读取。":"Launch arguments loaded.","概览后台任务失败。":"Overview background task failed.","概览已加载。":"Overview loaded.","启动任务已在后台开始,可稍后查看概览状态。":"Launch task started in the background. Check overview status later.","设置已加载。":"Settings loaded.","设置读取失败":"Failed to read settings","设置保存后重新读取失败":"Failed to reload settings after save","没有新的 cc-switch 供应商配置需要导入。":"No new cc-switch provider configs to import.","导入供应商配置后重新读取设置失败":"Failed to reload settings after importing provider config","待确认供应商导入已读取。":"Pending provider import loaded.","没有待确认的供应商导入。":"No pending provider import.","已取消供应商导入。":"Provider import cancelled.","会话 ID 不能为空。":"Session ID must not be empty.","推荐内容已加载。":"Recommendations loaded.","脚本市场已刷新。":"Script marketplace refreshed.","脚本 id 不能为空。":"Script ID must not be empty.","市场清单中未找到该脚本。":"Script not found in marketplace manifest.","脚本已安装。":"Script installed.","脚本 key 不能为空。":"Script key must not be empty.","脚本已启用。":"Script enabled.","脚本已禁用。":"Script disabled.","脚本启停失败":"Failed to toggle script","脚本已删除。":"Script deleted.","脚本删除失败":"Failed to delete script","只允许打开 http 或 https 链接。":"Only http or https links can be opened.","已在系统浏览器打开链接。":"Link opened in system browser.","安装入口":"Install entrypoints","卸载入口":"Uninstall entrypoints","修复快捷方式":"Repair shortcuts","修复后重新读取设置失败":"Failed to reload settings after repair","插件市场需要初始化或注册。":"Plugin marketplace needs initialization or registration.","插件市场已可用。":"Plugin marketplace is available.","插件市场已从 openai/plugins 初始化并注册。":"Plugin marketplace initialized and registered from openai/plugins.","已注册本地插件市场。":"Local plugin marketplace registered.","插件市场已可用,无需修复。":"Plugin marketplace is available; no repair needed.","官方远端插件缓存需要释放或注册。":"Official remote plugin cache needs extraction or registration.","官方远端插件缓存已可用。":"Official remote plugin cache is available.","已释放并注册内置官方远端插件缓存。":"Embedded official remote plugin cache extracted and registered.","已注册官方远端插件缓存。":"Official remote plugin cache registered.","官方远端插件缓存已可用,无需修复。":"Official remote plugin cache is available; no repair needed.","发现可用更新。":"Update available.","当前已是最新版本。":"Already on the latest version.","请先检查更新并选择可下载的 Release asset。":"Check for updates first and select a downloadable release asset.","安装包已下载并启动,请按安装向导完成更新。":"Installer downloaded and launched. Follow the setup wizard to finish updating.","日志已读取。":"Logs loaded.","诊断报告已生成。":"Diagnostics report generated.","设置已重置为默认值。":"Settings reset to defaults.","设置重置后重新读取失败":"Failed to reload settings after reset","图片覆盖层设置已重置。":"Image overlay settings reset.","图片覆盖层重置后重新读取失败":"Failed to reload settings after image overlay reset","已检测到 ChatGPT 登录状态。":"ChatGPT login detected.","未检测到 ChatGPT 登录状态,请先在 Codex/ChatGPT 中正常登录。":"ChatGPT login not detected. Please log in to Codex/ChatGPT first.","配置文件内容已读取。":"Config file content loaded.","未检测到会覆盖 Codex 供应商配置的 OPENAI 环境变量。":"No OPENAI environment variables found that would override the Codex provider config.","检测到可能覆盖 Codex 供应商配置的 OPENAI 环境变量。":"OPENAI environment variables detected that may override the Codex provider config.","环境变量已按确认项删除;重新启动 Codex 后生效。":"Environment variables deleted as confirmed. Restart Codex for changes to take effect.","配置文件已保存。":"Config file saved.","供应商切换锁已损坏,请重启管理器后再试。":"Provider switch lock is corrupted. Please restart the manager and try again.","供应商已切换。":"Provider switched.","当前供应商已不在配置列表中,已停止切换以避免覆盖用户改动。":"The current provider is no longer in the config list. Switching stopped to avoid overwriting user changes.","当前供应商配置已从 live 文件回填。":"Current provider config backfilled from live files.","工具与插件列表已读取。":"Tools & plugins list loaded.","通用配置已按兼容切换规则提取。":"Common config extracted using compatibility switching rules.","供应商配置总开关已关闭,未写入 config.toml / auth.json。":"Provider config master switch is off. Nothing written to config.toml / auth.json.","已按兼容切换规则切换供应商。":"Provider switched using compatibility rules.","未检测到 ChatGPT 登录状态":"ChatGPT login not detected","未检测到 ChatGPT 登录状态,已停止写入中转配置。":"ChatGPT login not detected. Relay config not written.","中转配置已写入,密钥未在界面明文显示。":"Relay config written. Keys are not shown in plaintext in the UI.","聚合供应商配置已写入,真实请求会由本地代理按策略轮转。":"Aggregate provider config written. Real requests will be rotated by the local proxy according to the strategy.","已清除 custom 中转 API 模式,并切换到官方 ChatGPT 登录模式。":"Custom relay API mode cleared. Switched to official ChatGPT login mode.","纯 API 模式已写入:config.toml 已写入 custom provider,auth.json 已切换为当前供应商。":"Pure API mode written: config.toml now has a custom provider, auth.json switched to the current provider.","纯 API 配置写入后未检测到完整 custom provider,请检查 config.toml 和供应商 API Key。":"After writing pure API config, no complete custom provider was detected. Please check config.toml and the provider API Key.","供应商导入后重新读取设置失败":"Failed to reload settings after provider import","诊断日志已写入。":"Diagnostics log written.","未命名供应商":"Unnamed provider","响应内容为空":"Response is empty"},"patterns":[["^启动静默入口失败:(.+)$","Failed to launch silent entrypoint: $1"],["^无法启动 (.+?):(.+)$","Cannot launch $1: $2"],["^设置已保存。(.*)$","Settings saved.$1"],["^保存设置失败:(.+)$","Failed to save settings: $1"],["^已读取 cc-switch Codex 供应商配置:(\\d+) 个。$","Read $1 cc-switch Codex provider config(s)."],["^读取 cc-switch 供应商配置失败:(.+)$","Failed to read cc-switch provider configs: $1"],["^已从 cc-switch 导入供应商配置:(\\d+) 个。$","Imported $1 cc-switch provider config(s)."],["^保存 cc-switch 供应商配置失败:(.+)$","Failed to save cc-switch provider configs: $1"],["^已导入供应商配置:(.+)。$","Imported provider config: $1."],["^供应商配置已存在:(.+)。$","Provider config already exists: $1."],["^导入供应商配置失败:(.+)$","Failed to import provider config: $1"],["^取消供应商导入失败:(.+)$","Failed to cancel provider import: $1"],["^已读取 (\\d+) 个本地会话。$","Read $1 local session(s)."],["^读取部分本地会话失败:(.+)$","Failed to read some local sessions: $1"],["^已读取 (\\d+) 个 Zed 远程项目。$","Read $1 Zed remote project(s)."],["^读取 Zed 远程项目失败。$","Failed to read Zed remote projects."],["^已在 Zed Remote 打开项目。$","Project opened in Zed Remote."],["^无法在 Zed Remote 打开项目。$","Cannot open project in Zed Remote."],["^移除 Zed 远程项目失败。$","Failed to remove Zed remote project."],["^供应商已同步一次:(\\d+) 个会话文件,(\\d+) 行索引,跳过 (\\d+) 个占用文件。$","Provider synced: $1 session file(s), $2 index row(s), skipped $3 locked file(s)."],["^供应商同步失败:(.+)$","Provider sync failed: $1"],["^推荐内容加载失败:(.+)$","Failed to load recommendations: $1"],["^脚本市场加载失败:(.+)$","Failed to load script marketplace: $1"],["^安装脚本失败:(.+)$","Failed to install script: $1"],["^脚本启停失败:(.+)$","Failed to toggle script: $1"],["^脚本删除失败:(.+)$","Failed to delete script: $1"],["^打开链接失败:(.+)$","Failed to open link: $1"],["^插件市场修复失败:(.+)$","Plugin marketplace repair failed: $1"],["^官方远端插件缓存修复失败:(.+)$","Official remote plugin cache repair failed: $1"],["^检查更新失败:(.+)$","Failed to check for updates: $1"],["^安装更新失败:(.+)$","Failed to install update: $1"],["^安装 watcher 失败:(.+)$","Failed to install watcher: $1"],["^移除 watcher 失败:(.+)$","Failed to remove watcher: $1"],["^启用 watcher 失败:(.+)$","Failed to enable watcher: $1"],["^禁用 watcher 失败:(.+)$","Failed to disable watcher: $1"],["^读取日志失败:(.+)$","Failed to read logs: $1"],["^重置设置失败:(.+)$","Failed to reset settings: $1"],["^重置图片覆盖层失败:(.+)$","Failed to reset image overlay: $1"],["^读取配置文件失败:(.+)$","Failed to read config file: $1"],["^删除环境变量失败:(.+)$","Failed to delete environment variables: $1"],["^保存配置文件失败:(.+)$","Failed to save config file: $1"],["^供应商切换失败:(.+)$","Provider switch failed: $1"],["^写入诊断日志失败:(.+)$","Failed to write diagnostics log: $1"],["^回填当前供应商配置失败:(.+)$","Failed to backfill current provider config: $1"],["^读取工具与插件列表失败:(.+)$","Failed to read tools & plugins list: $1"],["^读取 live 工具与插件失败:(.+)$","Failed to read live tools & plugins: $1"],["^保存工具与插件失败:(.+)$","Failed to save tools & plugins: $1"],["^读取 live config.toml 失败:(.+)$","Failed to read live config.toml: $1"],["^同步 live 工具与插件失败:(.+)$","Failed to sync live tools & plugins: $1"],["^创建 Codex 配置目录失败:(.+)$","Failed to create Codex config directory: $1"],["^写入 live config.toml 失败:(.+)$","Failed to write live config.toml: $1"],["^读取同步后的 live 工具与插件失败:(.+)$","Failed to read live tools & plugins after sync: $1"],["^删除工具与插件失败:(.+)$","Failed to delete tools & plugins: $1"],["^提取通用配置失败:(.+)$","Failed to extract common config: $1"],["^已向「(.+?)」用模型「(.+?)」发送 hi,HTTP (\\d+)。(.*)$","Sent hi to \"$1\" using model \"$2\", HTTP $3.$4"],["^测试「(.+?)」失败:(.+)$","Test \"$1\" failed: $2"],["^已从「(.+?)」获取 (\\d+) 个模型。$","Fetched $2 model(s) from \"$1\"."],["^从「(.+?)」获取模型失败:(.+)$","Failed to fetch models from \"$1\": $2"],["^切换完整中转配置失败:(.+)$","Failed to switch full relay config: $1"],["^写入中转配置失败:(.+)$","Failed to write relay config: $1"],["^写入聚合供应商配置失败:(.+)$","Failed to write aggregate provider config: $1"],["^切换纯 API 配置失败:(.+)$","Failed to switch to pure API config: $1"],["^写入纯 API 模式失败:(.+)$","Failed to write pure API mode: $1"],["^清除中转配置失败:(.+)$","Failed to clear relay config: $1"],["^未知配置文件类型:(.+)$","Unknown config file type: $1"],["^启动系统浏览器失败:(.+)$","Failed to launch system browser: $1"],["^诊断报告序列化失败:(.+)$","Failed to serialize diagnostics report: $1"],["^响应:(.+)$","Response: $1"]]} \ No newline at end of file diff --git a/tools/i18n-vi-cache.json b/tools/i18n-vi-cache.json new file mode 100644 index 000000000..e1ac6628b --- /dev/null +++ b/tools/i18n-vi-cache.json @@ -0,0 +1 @@ +{"Expands plugin marketplace requests in API Key mode to show the full plugin list. Usually unnecessary in official/mixed mode.": "Mở rộng các yêu cầu về thị trường plugin trong chế độ Khóa API để hiển thị danh sách plugin đầy đủ. Thường không cần thiết ở chế độ chính thức/hỗn hợp.", "API Key environment variable": "Biến môi trường khóa API", "Chat Completions to Responses": "Hoàn thành trò chuyện để phản hồi", "Codex launch arguments": "Đối số khởi chạy Codex", "Codex tools & plugins": "Công cụ và plugin Codex", "Codex app": "ứng dụng Codex", "Codex app path": "Đường dẫn ứng dụng Codex", "Codex stopped unexpectedly": "Codex dừng đột ngột", "Codex version": "Phiên bản Codex", "Codex goals": "Mục tiêu Codex", "The official Codex++ relay, built for stable access and good value, supporting GPT-5.5, GPT-5.4, Claude Opus 4.8, Claude Opus 4.7, gpt-image-2 and other models plus image capabilities.": "Rơle Codex++ chính thức, được thiết kế để truy cập ổn định và có giá trị tốt, hỗ trợ GPT-5.5, GPT-5.4, Claude Opus 4.8, Claude Opus 4.7, gpt-image-2 và các mẫu khác cùng với khả năng hình ảnh.", "Codex++ import": "Nhập Codex++", "Codex++ version": "Phiên bản Codex++", "Codex enhancements": "Cải tiến Codex", "Codex enhancement mode": "Chế độ nâng cao Codex", "Debug port": "Cổng gỡ lỗi", "Fast button": "Nút nhanh", "GitHub Release update": "Cập nhật phát hành GitHub", "GitHub Release check": "Kiểm tra phát hành GitHub", "Helper port": "Cổng trợ giúp", "MCP, Skills and Plugins are managed independently as global config and merged in whenever you switch providers.": "MCP, Kỹ năng và Plugin được quản lý độc lập dưới dạng cấu hình chung và được hợp nhất bất cứ khi nào bạn chuyển đổi nhà cung cấp.", "Markdown export": "Xuất khẩu giảm giá", "Provider sync target": "Mục tiêu đồng bộ hóa nhà cung cấp", "Stepwise direct send": "Gửi trực tiếp từng bước", "Generate next-step suggestions from the current conversation using a separate API configuration.": "Tạo đề xuất bước tiếp theo từ cuộc trò chuyện hiện tại bằng cách sử dụng cấu hình API riêng.", "Plugins and models": "Plugin và mô hình", "Manage enhancements for the plugin marketplace, model list and service tier.": "Quản lý các cải tiến cho thị trường plugin, danh sách mô hình và cấp dịch vụ.", "Conversation and input": "Cuộc trò chuyện và đầu vào", "Adjust session management, input behavior and conversation reading experience.": "Điều chỉnh quản lý phiên, hành vi đầu vào và trải nghiệm đọc hội thoại.", "Interface and startup": "Giao diện và khởi động", "Control language, startup speed and Codex native UI adjustments.": "Kiểm soát ngôn ngữ, tốc độ khởi động và điều chỉnh giao diện người dùng gốc Codex.", "Remote projects": "Dự án từ xa", "Connect Zed Remote and upstream worktree helper capabilities.": "Kết nối Zed Remote và các khả năng trợ giúp của cây công việc ngược dòng.", "Max suggestions": "Đề xuất tối đa", "Max input characters": "Ký tự đầu vào tối đa", "Max output tokens": "Mã thông báo đầu ra tối đa", "Test connection": "Kiểm tra kết nối", "Timeout milliseconds": "Hết thời gian tính bằng mili giây", "Connection": "Sự liên quan", "Advanced parameters": "Thông số nâng cao", "TOML config body": "Nội dung cấu hình TOML", "Watcher action": "Hành động của người theo dõi", "Watcher status": "Trạng thái người theo dõi", "The watcher keeps Codex++ in control": "Người theo dõi giữ quyền kiểm soát Codex++", "Watcher auto-takeover": "Watcher tự động tiếp quản", "Zed remote open": "Zed mở remote", "Zed remote projects": "Dự án từ xa của Zed", "Zed project history": "Lịch sử dự án Zed", "Default Zed open strategy": "Chiến lược mở Zed mặc định", "Zed default behavior": "Hành vi mặc định của Zed", "cc-switch import": "nhập cc-switch", "config.toml preview": "xem trước config.toml", "provider not recorded": "nhà cung cấp không được ghi lại", "ssh:// URL copied.": "ssh:// URL đã được sao chép.", ", ": null, "”": null, "The previous switch hasn't finished yet. Please try again shortly.": "Việc chuyển đổi trước đó vẫn chưa kết thúc. Vui lòng thử lại trong thời gian ngắn.", "Context size": "Kích thước ngữ cảnh", "Context window": "Cửa sổ ngữ cảnh", "Last repair result": "Kết quả sửa chữa cuối cùng", "Last update result": "Kết quả cập nhật lần cuối", "Upstream protocol": "Giao thức ngược dòng", "Download and run installer": "Tải xuống và chạy trình cài đặt", "Does not write API files": "Không ghi tệp API", "Uses the same remote ad source as the in-Codex plugin menu": "Sử dụng nguồn quảng cáo từ xa giống như menu plugin trong Codex", "": "", " session(s)": "(các) phiên", "China official": "quan chức trung quốc", "Homepage": "Trang chủ", "Theme and launch arguments": "Chủ đề và khởi động các đối số", "Loaded from the static GitHub manifest": "Được tải từ bảng kê khai GitHub tĩnh", "Keeps only plain text when pasting rich text (e.g. from Word) into the Codex composer, avoiding it being treated as an image/file attachment. Requires restarting Codex.": "Chỉ giữ lại văn bản thuần túy khi dán văn bản có định dạng (ví dụ: từ Word) vào trình soạn thảo Codex, tránh bị coi là văn bản đính kèm hình ảnh/tệp. Yêu cầu khởi động lại Codex.", "Fetch from upstream": "Tìm nạp từ thượng nguồn", "Create a Git worktree from the latest upstream branch.": "Tạo cây công việc Git từ nhánh ngược dòng mới nhất.", "Pull models from environment variables and config.toml's /v1/models and add them to the model list.": "Kéo các mô hình từ các biến môi trường và /v1/models của config.toml rồi thêm chúng vào danh sách mô hình.", "Import from third party": "Nhập khẩu từ bên thứ ba", "Create from a preset template": "Tạo từ mẫu đặt trước", "Session": "Phiên họp", "Session ID badge": "Huy hiệu ID phiên", "Session deletion": "Xóa phiên", "UI capabilities like session deletion, export, project move and user scripts": "Các khả năng của giao diện người dùng như xóa phiên, xuất, di chuyển dự án và tập lệnh người dùng", "Session deletion, export, project move and script capabilities": "Khả năng xóa phiên, xuất, di chuyển dự án và tập lệnh", "Total sessions": "Tổng số phiên", "Session management": "Quản lý phiên", "Session project move": "Phiên di chuyển dự án", "Use": "Sử dụng", "In use": "Đang sử dụng", "e.g. C:\\Program Files\\WindowsApps\\OpenAI.Codex...\\app": "ví dụ. C:\\Tệp chương trình\\WindowsApps\\OpenAI.Codex...\\app", "e.g. context7": "ví dụ. bối cảnh7", "e.g. deepseek-v4-pro": "ví dụ. deepseek-v4-pro"} \ No newline at end of file From c67b4da2a76657ffa3662c17d96c2a1307240889 Mon Sep 17 00:00:00 2001 From: khanhdeptraivaicachuong Date: Thu, 9 Jul 2026 00:18:01 +0700 Subject: [PATCH 2/4] fix: localize manager shortcuts and remaining UI text --- .../src-tauri/tests/windows_subsystem.rs | 10 +++++++++- apps/codex-plus-manager/src/model-windows.ts | 2 +- crates/codex-plus-core/src/install/windows.rs | 15 ++++++++++++++- crates/codex-plus-core/tests/cdp_bridge.rs | 14 +++++++------- scripts/installer/macos/package-dmg.sh | 6 +++--- scripts/installer/windows/CodexPlusPlus.nsi | 15 ++++++++++++--- 6 files changed, 46 insertions(+), 16 deletions(-) diff --git a/apps/codex-plus-manager/src-tauri/tests/windows_subsystem.rs b/apps/codex-plus-manager/src-tauri/tests/windows_subsystem.rs index 6ee1edf4e..fa114c379 100644 --- a/apps/codex-plus-manager/src-tauri/tests/windows_subsystem.rs +++ b/apps/codex-plus-manager/src-tauri/tests/windows_subsystem.rs @@ -110,6 +110,11 @@ fn windows_binaries_request_administrator_privileges() { assert!(windows_manifest.contains("requireAdministrator")); assert!(windows_manifest.contains("Microsoft.Windows.Common-Controls")); assert!(windows_installer.contains("RequestExecutionLevel admin")); + assert!(windows_installer.contains("CreateShortcut \"$DESKTOP\\Codex++ Manager.lnk\"")); + assert!(windows_installer.contains("CreateShortcut \"$SMPROGRAMS\\Codex++\\Codex++ Manager.lnk\"")); + assert!(windows_installer.contains("CreateShortcut \"$SMPROGRAMS\\Codex++\\Uninstall Codex++.lnk\"")); + assert!(!windows_installer.contains("CreateShortcut \"$DESKTOP\\Codex++ 管理工具.lnk\"")); + assert!(!windows_installer.contains("CreateShortcut \"$SMPROGRAMS\\Codex++\\卸载 Codex++.lnk\"")); } #[test] @@ -159,8 +164,11 @@ fn macos_packager_hides_silent_launcher_but_not_manager() { "create_app \"Codex++\" \"CodexPlusPlus\" \"$BINARY_DIR/codex-plus-plus\" \"com.bigpizzav3.codexplusplus\" \"true\"" )); assert!(script.contains( - "create_app \"Codex++ 管理工具\" \"CodexPlusPlusManager\" \"$BINARY_DIR/codex-plus-plus-manager\" \"com.bigpizzav3.codexplusplus.manager\" \"false\"" + "create_app \"Codex++ Manager\" \"CodexPlusPlusManager\" \"$BINARY_DIR/codex-plus-plus-manager\" \"com.bigpizzav3.codexplusplus.manager\" \"false\"" )); + assert!(script.contains("sign_app \"$STAGE/Codex++ Manager.app\"")); + assert!(script.contains("verify_app \"$STAGE/Codex++ Manager.app\"")); + assert!(!script.contains("create_app \"Codex++ 管理工具\"")); } #[test] diff --git a/apps/codex-plus-manager/src/model-windows.ts b/apps/codex-plus-manager/src/model-windows.ts index 3dd46a7cc..a2b82f367 100644 --- a/apps/codex-plus-manager/src/model-windows.ts +++ b/apps/codex-plus-manager/src/model-windows.ts @@ -90,7 +90,7 @@ export function buildModelWindows(modelList: string, modelWindowsText: string): if (models.length !== windows.length) { return { ok: false, - error: `模型名称有 ${models.length} 行,上下文窗口有 ${windows.length} 行,请保持行数一致。`, + error: `Danh sách model có ${models.length} dòng, cửa sổ ngữ cảnh có ${windows.length} dòng; hãy giữ số dòng khớp nhau.`, }; } return { ok: true, modelWindows: modelWindowsTextToMap(modelList, modelWindowsText) }; diff --git a/crates/codex-plus-core/src/install/windows.rs b/crates/codex-plus-core/src/install/windows.rs index 3824d007b..947d7e896 100644 --- a/crates/codex-plus-core/src/install/windows.rs +++ b/crates/codex-plus-core/src/install/windows.rs @@ -46,7 +46,7 @@ pub fn build_windows_entrypoint_plan(options: &InstallOptions) -> WindowsEntrypo .to_string_lossy() .to_string(), manager_shortcut: install_root - .join("Codex++ 管理工具.lnk") + .join("Codex++ Manager.lnk") .to_string_lossy() .to_string(), install_root: install_root.to_string_lossy().to_string(), @@ -69,6 +69,7 @@ pub fn install_shortcuts(options: &InstallOptions) -> anyhow::Result<()> { let plan = build_windows_entrypoint_plan(options); let install_root = PathBuf::from(&plan.install_root); std::fs::create_dir_all(&install_root)?; + remove_legacy_shortcuts(&install_root); create_entrypoint_shortcut( PathBuf::from(&plan.silent_shortcut), PathBuf::from(&plan.launcher_path), @@ -86,6 +87,18 @@ pub fn install_shortcuts(options: &InstallOptions) -> anyhow::Result<()> { Ok(()) } +#[cfg(windows)] +fn remove_legacy_shortcuts(install_root: &Path) { + for name in [ + "Codex++ 管理工具.lnk", + "Codex++ 绠$悊宸ュ叿.lnk", + "Uninstall Codex++.lnk", + "卸载 Codex++.lnk", + ] { + let _ = std::fs::remove_file(install_root.join(name)); + } +} + #[cfg(windows)] pub fn uninstall_shortcuts(options: &InstallOptions) -> anyhow::Result<()> { let plan = build_windows_entrypoint_plan(options); diff --git a/crates/codex-plus-core/tests/cdp_bridge.rs b/crates/codex-plus-core/tests/cdp_bridge.rs index 96d7e22ae..6cca5759d 100644 --- a/crates/codex-plus-core/tests/cdp_bridge.rs +++ b/crates/codex-plus-core/tests/cdp_bridge.rs @@ -131,14 +131,14 @@ fn injection_script_times_out_backend_bridge_calls_and_falls_back_to_helper() { fn injection_script_explains_plugin_patch_is_unneeded_in_relay_mode() { let script = assets::injection_script(57321); - assert!(script.contains("兼容增强模式下无需开启")); + assert!(script.contains("Không cần bật trong chế độ tương thích")); } #[test] fn injection_script_menu_exposes_marketplace_plugin_switch_only() { let script = assets::injection_script(57321); - assert!(script.contains("插件市场解锁")); + assert!(script.contains("Mở khóa chợ plugin")); assert!(script.contains("data-codex-plus-setting=\"pluginMarketplaceUnlock\"")); assert!(!script.contains("特殊插件强制安装")); assert!(!script.contains("data-codex-plus-setting=\"forcePluginInstall\"")); @@ -489,7 +489,7 @@ fn injection_script_exposes_conversation_view_width_control() { assert!(script.contains("conversationView: false")); assert!(script.contains("conversationView")); assert!(script.contains("conversationViewMaxWidth")); - assert!(script.contains("对话居中宽度")); + assert!(script.contains("Độ rộng hội thoại căn giữa")); assert!(script.contains("data-codex-plus-conversation-view-width")); assert!(script.contains("conversationViewWidth()")); assert!(script.contains("normalizeConversationViewWidth")); @@ -501,7 +501,7 @@ fn injection_script_exposes_sidebar_thread_id_badge_control() { assert!(script.contains("threadIdBadge: false")); assert!(script.contains("threadIdBadge: \"codexAppThreadIdBadge\"")); - assert!(script.contains("会话 ID 标识")); + assert!(script.contains("Nhãn ID phiên")); assert!(script.contains("data-codex-plus-setting=\"threadIdBadge\"")); assert!(script.contains("codex-thread-id-badge")); assert!(script.contains("data-codex-thread-id-badge-wrap=\"true\"")); @@ -651,10 +651,10 @@ fn injection_script_exposes_fast_service_tier_control() { assert!(script.contains("codexServiceTierBadgeWired")); assert!(script.contains("setAttribute(\"role\", \"button\")")); assert!(script.contains("setAttribute(\"tabindex\", \"0\")")); - assert!(script.contains("继承 config.toml")); + assert!(script.contains("Kế thừa config.toml")); assert!(script.contains("service_tier=\\\"priority\\\"")); - assert!(script.contains("Fast 仅支持")); - assert!(script.contains("当前 thread")); + assert!(script.contains("Fast chỉ hỗ trợ")); + assert!(script.contains("Thread hiện tại")); assert!(script.contains("standard")); assert!(script.contains("fast")); } diff --git a/scripts/installer/macos/package-dmg.sh b/scripts/installer/macos/package-dmg.sh index 287778ee2..2c6726547 100755 --- a/scripts/installer/macos/package-dmg.sh +++ b/scripts/installer/macos/package-dmg.sh @@ -119,13 +119,13 @@ verify_app() { prepare_icon create_app "Codex++" "CodexPlusPlus" "$BINARY_DIR/codex-plus-plus" "com.bigpizzav3.codexplusplus" "true" -create_app "Codex++ 管理工具" "CodexPlusPlusManager" "$BINARY_DIR/codex-plus-plus-manager" "com.bigpizzav3.codexplusplus.manager" "false" +create_app "Codex++ Manager" "CodexPlusPlusManager" "$BINARY_DIR/codex-plus-plus-manager" "com.bigpizzav3.codexplusplus.manager" "false" sign_app "$STAGE/Codex++.app" -sign_app "$STAGE/Codex++ 管理工具.app" +sign_app "$STAGE/Codex++ Manager.app" verify_app "$STAGE/Codex++.app" -verify_app "$STAGE/Codex++ 管理工具.app" +verify_app "$STAGE/Codex++ Manager.app" ln -s /Applications "$STAGE/Applications" diff --git a/scripts/installer/windows/CodexPlusPlus.nsi b/scripts/installer/windows/CodexPlusPlus.nsi index 59506bdd4..505f458e8 100644 --- a/scripts/installer/windows/CodexPlusPlus.nsi +++ b/scripts/installer/windows/CodexPlusPlus.nsi @@ -36,15 +36,21 @@ Section "Install" File "${ROOT}\dist\windows\app\codex-plus-plus.exe" File "${ROOT}\dist\windows\app\codex-plus-plus-manager.exe" + Delete "$DESKTOP\Codex++ Manager.lnk" + Delete "$DESKTOP\Codex++ 管理工具.lnk" Delete "$DESKTOP\Codex++ 绠$悊宸ュ叿.lnk" + Delete "$SMPROGRAMS\Codex++\Codex++ Manager.lnk" + Delete "$SMPROGRAMS\Codex++\Codex++ 管理工具.lnk" Delete "$SMPROGRAMS\Codex++\Codex++ 绠$悊宸ュ叿.lnk" + Delete "$SMPROGRAMS\Codex++\Uninstall Codex++.lnk" + Delete "$SMPROGRAMS\Codex++\卸载 Codex++.lnk" CreateShortcut "$DESKTOP\Codex++.lnk" "$INSTDIR\codex-plus-plus.exe" "" "$INSTDIR\codex-plus-plus.exe" - CreateShortcut "$DESKTOP\Codex++ 管理工具.lnk" "$INSTDIR\codex-plus-plus-manager.exe" "" "$INSTDIR\codex-plus-plus-manager.exe" + CreateShortcut "$DESKTOP\Codex++ Manager.lnk" "$INSTDIR\codex-plus-plus-manager.exe" "" "$INSTDIR\codex-plus-plus-manager.exe" CreateDirectory "$SMPROGRAMS\Codex++" CreateShortcut "$SMPROGRAMS\Codex++\Codex++.lnk" "$INSTDIR\codex-plus-plus.exe" "" "$INSTDIR\codex-plus-plus.exe" - CreateShortcut "$SMPROGRAMS\Codex++\Codex++ 管理工具.lnk" "$INSTDIR\codex-plus-plus-manager.exe" "" "$INSTDIR\codex-plus-plus-manager.exe" - CreateShortcut "$SMPROGRAMS\Codex++\卸载 Codex++.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\codex-plus-plus-manager.exe" + CreateShortcut "$SMPROGRAMS\Codex++\Codex++ Manager.lnk" "$INSTDIR\codex-plus-plus-manager.exe" "" "$INSTDIR\codex-plus-plus-manager.exe" + CreateShortcut "$SMPROGRAMS\Codex++\Uninstall Codex++.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\codex-plus-plus-manager.exe" WriteUninstaller "$INSTDIR\uninstall.exe" WriteRegStr HKCU "Software\Codex++" "InstallDir" "$INSTDIR" @@ -63,11 +69,14 @@ Section "Uninstall" Pop $0 Delete "$DESKTOP\Codex++.lnk" + Delete "$DESKTOP\Codex++ Manager.lnk" Delete "$DESKTOP\Codex++ 管理工具.lnk" Delete "$DESKTOP\Codex++ 绠$悊宸ュ叿.lnk" Delete "$SMPROGRAMS\Codex++\Codex++.lnk" + Delete "$SMPROGRAMS\Codex++\Codex++ Manager.lnk" Delete "$SMPROGRAMS\Codex++\Codex++ 管理工具.lnk" Delete "$SMPROGRAMS\Codex++\Codex++ 绠$悊宸ュ叿.lnk" + Delete "$SMPROGRAMS\Codex++\Uninstall Codex++.lnk" Delete "$SMPROGRAMS\Codex++\卸载 Codex++.lnk" RMDir "$SMPROGRAMS\Codex++" From 5b5bf84ec90b30053a9776e2a5380e4afeed01e0 Mon Sep 17 00:00:00 2001 From: khanhdeptraivaicachuong Date: Thu, 9 Jul 2026 00:31:06 +0700 Subject: [PATCH 3/4] fix: update macOS manager app checks and stabilize ad test --- .github/workflows/pr-build.yml | 2 +- .github/workflows/release-assets.yml | 2 +- assets/inject/renderer-inject.js | 291 +++++++++++++++------------ crates/codex-plus-core/tests/ads.rs | 4 +- 4 files changed, 164 insertions(+), 135 deletions(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index ce3818db7..2d6cd9e5e 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -136,7 +136,7 @@ jobs: set -euo pipefail test -L "dist/macos/stage/Applications" test "$(readlink "dist/macos/stage/Applications")" = "/Applications" - for app in "dist/macos/stage/Codex++.app" "dist/macos/stage/Codex++ 管理工具.app"; do + for app in "dist/macos/stage/Codex++.app" "dist/macos/stage/Codex++ Manager.app"; do test -f "$app/Contents/Info.plist" test -f "$app/Contents/PkgInfo" test -x "$app/Contents/MacOS/$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$app/Contents/Info.plist")" diff --git a/.github/workflows/release-assets.yml b/.github/workflows/release-assets.yml index 3fc0ad6a5..46e2afa06 100644 --- a/.github/workflows/release-assets.yml +++ b/.github/workflows/release-assets.yml @@ -133,7 +133,7 @@ jobs: set -euo pipefail test -L "dist/macos/stage/Applications" test "$(readlink "dist/macos/stage/Applications")" = "/Applications" - for app in "dist/macos/stage/Codex++.app" "dist/macos/stage/Codex++ 管理工具.app"; do + for app in "dist/macos/stage/Codex++.app" "dist/macos/stage/Codex++ Manager.app"; do test -f "$app/Contents/Info.plist" test -f "$app/Contents/PkgInfo" test -x "$app/Contents/MacOS/$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$app/Contents/Info.plist")" diff --git a/assets/inject/renderer-inject.js b/assets/inject/renderer-inject.js index 4bd8a8e5a..c0844665d 100644 --- a/assets/inject/renderer-inject.js +++ b/assets/inject/renderer-inject.js @@ -1031,7 +1031,7 @@ .codex-plus-toggle:disabled { cursor: not-allowed; opacity: .55; } .codex-plus-toggle[data-relay-unneeded="true"] { width: 72px; cursor: default; background: rgba(16,163,127,.16); color: #6ee7b7; } .codex-plus-toggle[data-relay-unneeded="true"] span { display: none; } - .codex-plus-toggle[data-relay-unneeded="true"]::after { content: "无需开启"; font-size: 12px; font-weight: 650; line-height: 1; } + .codex-plus-toggle[data-relay-unneeded="true"]::after { content: "Không cần bật"; font-size: 12px; font-weight: 650; line-height: 1; } .codex-plus-width-control { display: flex; align-items: center; justify-content: flex-end; gap: 8px; min-width: 176px; align-self: center; } .codex-plus-width-input { width: 78px; @@ -1387,7 +1387,7 @@ let codexServiceTierState = { status: "loading", serviceTier: null, - message: "正在读取…", + message: "Đang đọc…", fastTierValue: "priority", controlMode: "inherit", defaultMode: "inherit", @@ -1434,7 +1434,7 @@ if (!codexServiceTierModulePromises.has(namePart)) { const promise = Promise.resolve().then(async () => { const url = codexAppAssetUrl(namePart) || await codexAppAssetUrlFromScriptText(namePart); - if (!url) throw new Error(`未找到 Codex App asset: ${namePart}`); + if (!url) throw new Error(`Không tìm thấy Codex App asset: ${namePart}`); return await import(url); }).catch((error) => { codexServiceTierModulePromises.delete(namePart); @@ -1511,8 +1511,8 @@ } function codexServiceTierFastUnsupportedMessage(modelName = codexServiceTierCurrentModelName()) { - const modelText = modelName ? `当前模型 ${modelName} 不支持` : "当前模型未读取"; - return `Fast 仅支持 ${codexServiceTierFastModelListLabel()},${modelText}`; + const modelText = modelName ? `model hiện tại ${modelName} không hỗ trợ` : "chưa đọc model hiện tại"; + return `Fast chỉ hỗ trợ ${codexServiceTierFastModelListLabel()}, ${modelText}`; } function codexServiceTierMaybeLoadModelCatalog(force = false) { @@ -1581,9 +1581,9 @@ } function serviceTierGlobalStatusMessage(serviceTier) { - if (isFastServiceTierValue(serviceTier)) return "Fast 已开启"; - if (!serviceTier) return "默认服务模式"; - return `当前:${serviceTier}`; + if (isFastServiceTierValue(serviceTier)) return "Fast đã bật"; + if (!serviceTier) return "Chế độ dịch vụ mặc định"; + return `Hiện tại: ${serviceTier}`; } function serviceTierStatusMessage( @@ -1592,13 +1592,13 @@ effectiveMode = codexServiceTierState.effectiveMode || "standard", defaultMode = codexServiceTierState.defaultMode || "inherit" ) { - if (codexServiceTierState.status === "loading") return "正在读取…"; - if (codexServiceTierState.status === "failed") return "读取失败"; - if (controlMode === "inherit") return `继承 config.toml:${effectiveMode}`; - if (controlMode === "global-standard") return "全局 Standard"; - if (controlMode === "global-fast") return "全局 Fast"; - if (threadMode === "inherit") return `自定义:默认 ${defaultMode}`; - return `自定义:当前 thread ${threadMode}`; + if (codexServiceTierState.status === "loading") return "Đang đọc…"; + if (codexServiceTierState.status === "failed") return "Đọc thất bại"; + if (controlMode === "inherit") return `Kế thừa config.toml: ${effectiveMode}`; + if (controlMode === "global-standard") return "Standard toàn cục"; + if (controlMode === "global-fast") return "Fast toàn cục"; + if (threadMode === "inherit") return `Tùy chỉnh: mặc định ${defaultMode}`; + return `Tùy chỉnh: thread hiện tại ${threadMode}`; } function readThreadServiceTierState() { @@ -1716,7 +1716,7 @@ function setCodexServiceTierControlMode(mode) { if (codexPlusBackendStatus.status !== "ok") { - showToast("后端未连接,无法切换服务模式", null); + showToast("Backend chưa kết nối, không thể đổi chế độ dịch vụ", null); refreshCodexServiceTierControls(); return; } @@ -1742,12 +1742,12 @@ writeThreadServiceTierState(state); refreshCodexServiceTierControls(); const labels = { - inherit: "继承 config.toml", - "global-standard": "全局 Standard", - "global-fast": "全局 Fast", - custom: "自定义", + inherit: "Kế thừa config.toml", + "global-standard": "Standard toàn cục", + "global-fast": "Fast toàn cục", + custom: "Tùy chỉnh", }; - showToast(`服务模式:${labels[normalizedMode] || normalizedMode}`, null); + showToast(`Chế độ dịch vụ: ${labels[normalizedMode] || normalizedMode}`, null); } function syncCodexServiceTierEffectiveState() { @@ -1758,7 +1758,7 @@ threadMode: "inherit", effectiveServiceTier: codexServiceTierState.serviceTier || null, effectiveMode: codexServiceTierEffectiveMode(codexServiceTierState.serviceTier), - message: "未启用", + message: "Chưa bật", }; return; } @@ -1790,22 +1790,22 @@ } function codexServiceTierBadgeState() { - if (codexPlusBackendStatus.status === "checking") return { tier: "loading", label: "...", disabled: true, title: "服务模式:正在检查后端连接" }; - if (codexPlusBackendStatus.status && codexPlusBackendStatus.status !== "ok") return { tier: "failed", label: "未连接", disabled: true, title: "服务模式:后端未连接,无法切换" }; - if (codexServiceTierState.status === "loading") return { tier: "loading", label: "...", title: "服务模式:正在读取" }; - if (codexServiceTierState.status === "failed") return { tier: "failed", label: "?", title: "服务模式:读取失败" }; + if (codexPlusBackendStatus.status === "checking") return { tier: "loading", label: "...", disabled: true, title: "Chế độ dịch vụ: đang kiểm tra kết nối backend" }; + if (codexPlusBackendStatus.status && codexPlusBackendStatus.status !== "ok") return { tier: "failed", label: "Chưa kết nối", disabled: true, title: "Chế độ dịch vụ: backend chưa kết nối, không thể đổi" }; + if (codexServiceTierState.status === "loading") return { tier: "loading", label: "...", title: "Chế độ dịch vụ: đang đọc" }; + if (codexServiceTierState.status === "failed") return { tier: "failed", label: "?", title: "Chế độ dịch vụ: đọc thất bại" }; const fastAvailability = codexServiceTierFastAvailability(); const effectiveMode = codexServiceTierState.effectiveMode || "standard"; const scope = codexServiceTierState.controlMode === "custom" && codexServiceTierState.threadMode !== "inherit" - ? `当前 thread:${codexServiceTierState.threadMode}` + ? `Thread hiện tại:${codexServiceTierState.threadMode}` : serviceTierStatusMessage(codexServiceTierState.controlMode, codexServiceTierState.threadMode, effectiveMode, codexServiceTierState.defaultMode); const title = [ - `服务模式:${scope}`, - "Standard:使用标准处理;不在请求上设置 priority。", - `Fast:仅支持 ${codexServiceTierFastModelListLabel()};对支持模型使用 service_tier=\"priority\",官方说明其延迟更低且更一致,但会按更高价格计费;rate limit 与 Standard 共享,流量快速上涨时可能回落到 Standard。`, + `Chế độ dịch vụ: ${scope}`, + "Standard: dùng xử lý tiêu chuẩn; không đặt priority trong request.", + `Fast: chỉ hỗ trợ ${codexServiceTierFastModelListLabel()}; model hỗ trợ sẽ dùng service_tier=\"priority\", độ trễ thấp và ổn định hơn nhưng giá cao hơn; rate limit dùng chung với Standard và có thể rơi về Standard khi lưu lượng tăng nhanh.`, ].join("\n"); if (effectiveMode === "fast" && !fastAvailability.supported) { - return { tier: "unsupported", label: "不支持", title: `${title}\n${codexServiceTierFastUnsupportedMessage(fastAvailability.modelName)};当前请求会按 Standard 发送。` }; + return { tier: "unsupported", label: "Không hỗ trợ", title: `${title}\n${codexServiceTierFastUnsupportedMessage(fastAvailability.modelName)};request hiện tại sẽ gửi theo Standard。` }; } if (effectiveMode === "fast") return { tier: "fast", label: "fast", title }; return { tier: "standard", label: "standard", title }; @@ -1840,8 +1840,8 @@ document.querySelectorAll("[data-codex-service-tier-status]").forEach((node) => { node.dataset.status = fastUnsupportedActive ? "unsupported" : (featureEnabled && backendConnected ? (codexServiceTierState.status || "loading") : (backendChecking ? "loading" : "failed")); node.textContent = featureEnabled - ? (backendConnected ? (codexServiceTierState.message || "未读取") : (backendChecking ? "正在检查后端…" : "未连接")) - : "未启用"; + ? (backendConnected ? (codexServiceTierState.message || "Chưa đọc") : (backendChecking ? "Đang kiểm tra backend…" : "Chưa kết nối")) + : "Chưa bật"; }); document.querySelectorAll("[data-codex-service-tier-inherit]").forEach((button) => { button.disabled = !featureEnabled || !backendConnected || codexServiceTierState.status === "loading"; @@ -1863,7 +1863,7 @@ document.querySelectorAll("[data-codex-service-tier-thread-inherit]").forEach((button) => { button.disabled = !featureEnabled || !backendConnected || codexServiceTierState.status === "loading"; button.dataset.active = String(codexServiceTierState.controlMode === "custom" && codexServiceTierState.threadMode === "inherit"); - button.title = `当前 thread 不单独覆盖,继承自定义默认 ${codexServiceTierState.defaultMode || "inherit"}`; + button.title = `Thread hiện tại không ghi đè riêng, kế thừa mặc định tùy chỉnh ${codexServiceTierState.defaultMode || "inherit"}`; }); document.querySelectorAll("[data-codex-service-tier-thread-standard]").forEach((button) => { button.disabled = !featureEnabled || !backendConnected || codexServiceTierState.status === "loading"; @@ -1879,11 +1879,11 @@ async function loadCodexServiceTierState() { if (!codexPlusSettings().serviceTierControls) { - codexServiceTierState = { ...codexServiceTierState, status: "idle", message: "未启用" }; + codexServiceTierState = { ...codexServiceTierState, status: "idle", message: "Chưa bật" }; refreshCodexServiceTierControls(); return; } - codexServiceTierState = { ...codexServiceTierState, status: "loading", message: "正在读取…" }; + codexServiceTierState = { ...codexServiceTierState, status: "loading", message: "Đang đọc…" }; refreshCodexServiceTierControls(); try { const serviceTier = await getCodexServiceTierSetting(); @@ -1897,7 +1897,7 @@ codexServiceTierState = { ...codexServiceTierState, status: "failed", - message: "读取失败", + message: "Đọc thất bại", }; sendCodexPlusDiagnostic("service_tier_read_failed", { errorName: error?.name || "", @@ -1910,7 +1910,7 @@ function setCodexThreadServiceTierMode(mode) { if (codexPlusBackendStatus.status !== "ok") { - showToast("后端未连接,无法切换服务模式", null); + showToast("Backend chưa kết nối, không thể đổi chế độ dịch vụ", null); refreshCodexServiceTierControls(); return; } @@ -1927,13 +1927,13 @@ const threadId = validThreadScrollSessionKey(currentSessionRef().session_id); setCodexThreadServiceTierOverride(threadId, normalizedMode); refreshCodexServiceTierControls(); - const target = threadId ? "当前 thread" : "新 thread 草稿"; - showToast(`${target}服务模式:${normalizedMode === "inherit" ? "继承" : normalizedMode}`, null); + const target = threadId ? "Thread hiện tại" : "Bản nháp thread mới"; + showToast(`${target} - chế độ dịch vụ: ${normalizedMode === "inherit" ? "Kế thừa" : normalizedMode}`, null); } function toggleCodexServiceTierFromBadge() { if (codexPlusBackendStatus.status !== "ok") { - showToast("后端未连接,无法切换服务模式", null); + showToast("Backend chưa kết nối, không thể đổi chế độ dịch vụ", null); refreshCodexServiceTierControls(); return; } @@ -2163,7 +2163,7 @@ } let codexPlusUserScripts = { enabled: true, builtin_dir: "", user_dir: "", scripts: [] }; - let codexPlusBackendStatus = { status: "checking", message: "正在检查后端…" }; + let codexPlusBackendStatus = { status: "checking", message: "Đang kiểm tra backend…" }; let codexPlusBackendCheckSeq = 0; function setCodexPlusTriggerLabel(trigger) { @@ -2189,6 +2189,35 @@ return indicator; } + function codexPlusViMessage(message) { + if (!message) return message; + const text = String(message); + const exact = { + "后端已连接": "Backend đã kết nối", + "未连接": "Chưa kết nối", + "正在检查后端…": "Đang kiểm tra backend…", + "正在检查后端": "Đang kiểm tra backend", + "后端检查超时": "Kiểm tra backend quá thời gian", + "未启用": "Chưa bật", + "未读取": "Chưa đọc", + "读取失败": "Đọc thất bại", + "管理工具已打开": "Đã mở công cụ quản lý", + "打开管理工具失败": "Mở công cụ quản lý thất bại", + }; + return exact[text] || text + .replaceAll("后端已连接", "Backend đã kết nối") + .replaceAll("后端未连接,无法切换服务模式", "Backend chưa kết nối, không thể đổi chế độ dịch vụ") + .replaceAll("未连接", "Chưa kết nối") + .replaceAll("正在检查后端", "Đang kiểm tra backend") + .replaceAll("服务模式", "Chế độ dịch vụ") + .replaceAll("读取失败", "Đọc thất bại") + .replaceAll("正在读取", "Đang đọc") + .replaceAll("未启用", "Chưa bật") + .replaceAll("继承", "Kế thừa") + .replaceAll("当前 thread", "Thread hiện tại") + .replaceAll("自定义", "Tùy chỉnh"); + } + function renderBackendStatus() { const status = codexPlusBackendStatus.status || "failed"; if (codexPlusBackendStatus.version) { @@ -2201,11 +2230,11 @@ const label = document.querySelector("[data-codex-backend-status]"); if (label) { label.dataset.status = status; - label.textContent = codexPlusBackendStatus.message || (status === "ok" ? "后端已连接" : "未连接"); + label.textContent = codexPlusViMessage(codexPlusBackendStatus.message) || (status === "ok" ? "Backend đã kết nối" : "Chưa kết nối"); } document.querySelectorAll("[data-codex-backend-indicator]").forEach((indicator) => { indicator.dataset.status = status; - indicator.title = status === "ok" ? "后端已连接" : status === "checking" ? "正在检查后端" : "未连接"; + indicator.title = status === "ok" ? "Backend đã kết nối" : status === "checking" ? "Đang kiểm tra backend" : "Chưa kết nối"; }); refreshCodexServiceTierControls(); } @@ -2213,7 +2242,7 @@ function withBackendTimeout(request) { return Promise.race([ request, - new Promise((resolve) => setTimeout(() => resolve({ status: "failed", message: "后端检查超时", timeout: true }), 2000)), + new Promise((resolve) => setTimeout(() => resolve({ status: "failed", message: "Kiểm tra backend quá thời gian", timeout: true }), 2000)), ]); } @@ -2235,9 +2264,9 @@ async function openManagerFromCodex() { const result = await postJson("/manager/open", {}); if (result.status === "ok") { - showToast("管理工具已打开", null); + showToast("Đã mở công cụ quản lý", null); } else { - showToast(result.message || "打开管理工具失败", null); + showToast(codexPlusViMessage(result.message) || "Mở công cụ quản lý thất bại", null); } } @@ -2248,25 +2277,25 @@ } function userScriptStatusLabel(status) { - return { loaded: "已加载", failed: "失败", disabled: "已禁用", not_loaded: "未加载", loading: "加载中" }[status] || status || "未知"; + return { loaded: "Đã tải", failed: "Lỗi", disabled: "Đã tắt", not_loaded: "Chưa tải", loading: "Đang tải" }[status] || status || "Không rõ"; } function renderUserScripts() { const enabledToggle = document.querySelector("[data-codex-user-scripts-enabled]"); if (enabledToggle) enabledToggle.dataset.enabled = String(!!codexPlusUserScripts.enabled); const dirs = document.querySelector("[data-codex-user-script-dirs]"); - if (dirs) dirs.textContent = `内置:${codexPlusUserScripts.builtin_dir || "未找到"} 用户:${codexPlusUserScripts.user_dir || "未找到"}`; + if (dirs) dirs.textContent = `Tích hợp: ${codexPlusUserScripts.builtin_dir || "Không tìm thấy"} Người dùng: ${codexPlusUserScripts.user_dir || "Không tìm thấy"}`; const list = document.querySelector("[data-codex-user-script-list]"); if (!list) return; if (!codexPlusUserScripts.scripts?.length) { - list.textContent = "未发现用户脚本。"; + list.textContent = "Không tìm thấy script người dùng."; return; } list.innerHTML = codexPlusUserScripts.scripts.map((script) => `
${escapeHtml(script.name || script.key)}
-
${script.source === "builtin" ? "内置" : "用户"} · ${userScriptStatusLabel(script.status)}
+
${script.source === "builtin" ? "Tích hợp" : "Người dùng"} · ${userScriptStatusLabel(script.status)}
${script.error ? `
${escapeHtml(script.error)}
` : ""}
@@ -2320,23 +2349,23 @@
${ad.highlights.map((item) => `${escapeHtml(item)}`).join("")}
- 访问 ${escapeHtml(new URL(ad.url).hostname)} + Truy cập ${escapeHtml(new URL(ad.url).hostname)}
`).join(""); } function renderCodexPlusAds() { - if (!codexPlusAdsLoaded) return `
推荐内容加载中…
`; - if (!codexPlusAds.length) return `
暂无推荐内容。
`; + if (!codexPlusAdsLoaded) return `
推荐内容Đang tải…
`; + if (!codexPlusAds.length) return `
Chưa có nội dung đề xuất.
`; return `
-

赞助商推荐

-
${renderCodexPlusAdGroup("sponsor", "暂无赞助商推荐。")}
+

Đề xuất tài trợ

+
${renderCodexPlusAdGroup("sponsor", "暂无Đề xuất tài trợ。")}
-

普通推荐

-
${renderCodexPlusAdGroup("normal", "暂无普通推荐。")}
+

Đề xuất thường

+
${renderCodexPlusAdGroup("normal", "暂无Đề xuất thường。")}
`; } @@ -2407,89 +2436,89 @@