diff --git a/.agents/skills/provider-adaptation/SKILL.md b/.agents/skills/provider-adaptation/SKILL.md new file mode 100644 index 00000000..cd4d7a39 --- /dev/null +++ b/.agents/skills/provider-adaptation/SKILL.md @@ -0,0 +1,304 @@ +--- +name: provider-adaptation +description: Agent2API(workbuddy)仓库接入新提供商(provider)与验收适配完整度的规范 —— README「支持情况一览」六列的口径、必须登记的代码与前端落点、能力探测脚本的用法、静默失效排查、六份 README 同步。凡是涉及新增一家反代 Agent / 客户端 provider、审核这类 PR、判断某家「做完了没有」、改 providers/ 下的适配器或能力位(supports_*)、给签到清单加一家、或补 README 支持矩阵时,都先按本 Skill 执行 —— 即使对方没提「provider」「适配」这类字眼。 +--- + +# Agent2API 提供商接入规范 + +覆盖两件事:**接一家新 provider 要改哪些地方**(第 2–5 节),以及**怎么判断它接完了没有**(第 1、6、7 节)。排查见第 8 节。 + +## 0. 一条红线:README 的支持矩阵就是验收口径 + +`README.md` 的「支持情况一览」有六列,每一格都是**用户可见的承诺**: + +| 列 | 含义 | +|---|---| +| LLM 请求 | 能转发对话(含流式) | +| Token 自动续期 | 凭证过期能自己换新 | +| 模型列表 | 有清单,且支持在线拉取 | +| 余额查询 | 能读余额 / 积分 | +| 签到 | 有每日签到(或等价动作),且接进了自动签到框架 | +| 领取类 | 有可领取的活动(新手礼 / 每日任务 / 限时套餐) | + +**打 ✓ 的每一格,必须有一个落地物和一个可执行的验收动作**(见第 6 节)。**打 ✗ 或 — 的每一格,必须能说清「上游确实没有这个能力」是怎么排除的**,不能是「还没做」。 + +这条红线的由来:本仓库历史上多次出现「只做了对话,模型获取 / 余额 / 签到没做」的半成品,而表格和代码之间没有任何自动校验 —— 表格只能靠人自觉。因此: + +- **✗ 不等于没做,等于确认不存在。** 结论要能复述排查过程(打过哪些接口、返回什么)。 +- **默认值不是结论。** `ProviderAdapter` 的 `supports_*()` 默认全是 `false`,那是「尚未接入」的语义,不是「这家不支持」。 +- **不要为了让表格好看而打 ✓。** 一个必然报错的按钮比没有按钮更糟(见 `auto_checkin.rs` 的 `CHECKIN_PROVIDERS` 注释)。 + +## 1. 先做的三件事(动手写代码之前) + +### 1.1 摸清上游的真实形态 + +拿到这家客户端的通信证据(抓包 / 安装包逆向 / 官方 CLI 源码 / 参考实现),至少要回答: + +- 推理是 OpenAI 兼容,还是自有协议?(决定走 `build_chat_request` 还是 `forward_conversation`) +- 响应是 SSE、NDJSON,还是别的信封?(决定要不要新开 `UpstreamResponse` 变体) +- 鉴权头怎么拼?凭证从哪来、会不会过期、有没有续期接口? +- 有没有模型目录接口、余额接口、签到接口?**每一条都要有实际请求证据。** +- 有没有地区 / 额度池的区分?(决定是不是要拆成两个 provider) + +### 1.2 判断要不要拆成两家 provider + +**地区 / 额度池是 provider 身份,不是账号上的字段。** 这条在本仓库被反复确认过,走过这条路的:WorkBuddy、AutoClaw、Qoder、Accio、ZCode、MonkeyCode、ModelScope(两地),Cline(两池)。 + +把地区做成账号字段的后果每次都一样: + +1. 模型目录只有一份缓存,两个地区互相覆盖; +2. 模型规则只有一个 `(provider, id)` 命名空间,同名模型无法分别点名; +3. 转发候选不含地区,请求可能落到另一地区的账号上,而「模型不存在」的 400 是 `Fatal`、不换账号,直接失败。 + +判断依据是**账号体系是否分开**:两套账号、两份目录、令牌不通用 → 拆。只是同一套协议换域名、账号通用 → 不必拆(但仍需确认)。 + +拆了之后:`ProviderKind` 加两个变体,`PROVIDERS` 加两条(相邻排列),实现是**一套**、按 `Region` 参数化,两个静态实例由 `adapter_for` 分派。地区互查放在 `region.rs`(`kind` / `provider_id` / `from_provider_id`),**别处不要再写 `"xxx-intl"` 这类字面量**。 + +### 1.3 确认「没有某接口」的排除过程 + +CatPaw 与 AutoClaw 都曾把「上游没有目录接口」写进注释,后来两次都被证伪:CatPaw 的 `tenant/scene/env` 是 POST body 不是 query;AutoClaw 的目录在同 host 的 `/proxy/` 一级而不是对话用的 `/proxy/autoclaw`。 + +所以:**「这家没有某接口」的结论要能说清是怎么排除的**,否则它只是一个还没找到的接口。把这个过程写进对应模块的模块头,别只写结论。 + +## 2. 后端登记点(漏一个就是静默失效) + +按顺序改,每一条都写清「漏了会怎样」。 + +### 2.1 身份与注册表 + +| 文件 | 改什么 | 漏了会怎样 | +|---|---|---| +| `core/providers/mod.rs` | `ProviderKind` 加变体(**加在末尾**) | 编译不过 | +| 同上 | `PROVIDERS` 加 `ProviderMeta { id, label }` | 编译不过 / 界面无此项 | +| 同上 | `kind_from_id` 加分支 | 编译期不报,运行期「未知 provider」,账号静默失配 | +| 同上 | `kind_id` 加分支 | 编译报错(穷举 match) | +| `core/providers/adapter.rs` | `adapter_for` 加分支 | 编译报错(穷举 match) | +| 同上 | `implemented_kinds()` 加 kind | **不报错**,但后台目录刷新永不调度 —— 症状是「一直是内置兜底、手动刷新也没用」 | + +`ProviderMeta.id` 是落盘契约(`accounts.json` 里的 `provider` 字段),**存量 id 不能改名**。新家取新 id;只有国际版这类新增身份才用新串。 + +### 2.2 账号层 + +| 文件 | 改什么 | 漏了会怎样 | +|---|---|---| +| `api/accounts.rs` | `add_account` 加 `ProviderKind` 分派分支 | 这家根本加不了号 | +| 同上 | `refresh_provider_account` 分派(如果支持手动刷新凭证) | 界面上「刷新」按钮点了没反应或报未知 provider | +| `core/account_store/_accounts.rs` | 新建:账号记录的读写、公开形态转换、id 生成 | 账号无法落库 / 界面上看不到 | +| `core/account_store/mod.rs` | 加 `is__family(provider_id)` | 两地 / 两池的家判定失效 | +| `core/account_store/store_view.rs` | `public_account` 加分支(去掉凭证本体) | **凭证会随公开形态泄漏到前端** | + +公开形态**绝不能含 accessToken / refreshToken**,只留 `tokenTail` 与「有没有令牌」。这条是安全边界。 + +### 2.3 目录与转发 + +| 文件 | 改什么 | 漏了会怎样 | +|---|---|---| +| `core/providers//models.rs` | `list_models`(静态兜底 + 远程清单) | `/v1/models` 无此项 | +| `core/providers/catalog.rs` | `refresh_meta` 加分支(穷举 match) | 编译报错 | +| `core/providers/catalog_cache.rs` | `SCOPE_*` 常量 + 加进 `ALL_SCOPES` | **不报错**,清单进程重启后丢失 | +| `core/providers//adapter.rs` | `supports_model_refresh` + `refresh_models` | 界面「刷新模型」按钮无效 | + +`ALL_SCOPES` 漏了不会报错 —— 缓存能写进内存,但重启后读不回来。这是最容易漏的一处。 + +### 2.4 能力位 + +`ProviderAdapter` 的每个能力位都有明确的消费方,填错会直接影响界面行为: + +| 能力位 | 消费方 | 填错的后果 | +|---|---|---| +| `supports_chat` | `account_store::pick_current`(全局队首) | 无转发能力的家排进队首,顶栏显示成「当前登录态」 | +| `supports_refresh` | `credential_maintenance` | 声称支持但刷不动 → 每轮白跑 + 灌失败日志 | +| `supports_model_refresh` | `catalog_refresh` | 声称支持却拿不到新清单,用户以为坏了 | +| `supports_usage` | `usage_query` / 前端积分按钮 | 给了入口但必然失败 | +| `supports_web_login` | `api::session` 登录路由 | 标 true 但 `build_login_url` 返回 None → 能用的功能变 400 | + +口径统一为:**「上游到底有没有那个接口」,不是「我们想不想实现」**。判不出来时返回 `false`(宁可漏刷,也不要凭猜测打上游)。 + +### 2.5 签到与领取 + +| 文件 | 改什么 | 漏了会怎样 | +|---|---|---| +| `core/auto_checkin.rs` | `CHECKIN_PROVIDERS` 加 id(**数组长度 `[&str; 16]` 也要 +1**) | 签到中心无这家、定时任务不跑 | +| 同上 | `PROVIDERS_VERSION` +1,`PROVIDERS_ADDED_IN` 加条目(**数组长度 `[(&str, i64); 6]` 也要 +1**) | **不报错**,存量用户的签到清单里不会自动出现这家 | +| `core/billing/checkin.rs` | `checkin_for` 的 `match provider_id` 加分支 | 批量签到对这家算 `skipped` | +| `core/providers//checkin.rs` | 新建:claim 实现 | — | +| 领取类 | 各家自己的入口(`zcode::claim` / `codearts::welfare` / `chatglm::tasks` / `workbuddy::growth`) | 无统一位置,最容易被整块漏掉 | + +**`PROVIDERS_ADDED_IN` 是最隐蔽的一处。** 只往 `CHECKIN_PROVIDERS` 加 id 而不升版本号,新家不会进入存量用户的清单;而每次读都补齐又会让用户取消不掉勾选(读完就补回来)。版本号就是那个「只补一次」的记账。 + +加一家之前**先确认签到链路真的存在**。上游没有独立签到接口但有等价动作(每日首次登录、调一次信息接口就触发)也算,但要在模块头写清依据。完全没活动就不进清单 —— 一个点了必然报错的复选框比没有更糟。 + +### 2.6 错误分类(最容易做错的一档) + +`classify_error` 只能归到四档,**多出来的分类在编排层没有对应分支,会静默落进 `Fatal`**: + +| 档 | 动作 | 判错的后果 | +|---|---|---| +| `QuotaLimited` | 标记账号对该模型冷却 + 换账号 | 限额被当 Fatal → 账号永不轮换,每次都撞同一面墙 | +| `TokenExpired` | 刷新凭证后**同一账号**重试一次 | 凭证过期被当 Fatal → 用户看到 401 | +| `ContentBlocked` | **不罚账号**,换中性提示词重试一次 | 审核误报被当限额 → 白扔另一个账号的额度 | +| `Fatal` | 原样透传给客户端 | — | + +几条实践要点: + +- 有些上游把「额度耗尽」和「速率窗口」都用 429 表达,只能靠**文案**区分,而处置完全不同(前者冷却到次日零点,后者是分钟级窗口)。判据要写清来源。 +- 有些上游把业务失败包装成 **HTTP 200**(正文一句固定话术,或流内 `{"type":"error"}`)。这种要靠 `is_error_response` / `stream_refusal_finishes` / `stream_refusal_markers` 拦下,否则会被当成一次成功答复。 +- `stream_refusal_finishes` 判的是**内容策略**拒绝(不罚账号、换家);`stream_refusal_markers` 判的是**本账号额度**问题(必须罚账号)。两条别混。 +- 会话式转发(`is_stateful`)的错误默认走 `Fatal`。要参与限额记账必须覆写 `classify_conversation_error`,否则福利池耗尽后每个请求都从头撞一遍全部账号。 + +## 3. 前端登记点 + +| 文件 | 改什么 | 漏了会怎样 | +|---|---|---| +| `ui-islands/src/islands/add-account-configs.ts` | `ProviderConfig`:表单字段、登录方式(`webLogin` / `smsLogin` / `oauthLogin` / 访客) | 添加账号弹窗里没有这家的表单 | +| `ui-islands/src/islands/add-provider-pick.tsx` | 卡片列表登记 | 选不到这家 | +| `ui-islands/src/islands/add-provider-blocks.tsx` | 特殊块(如 WorkBuddy 的版本 + 打开方式) | 仅特殊形态需要 | +| `ui-islands/src/islands/accounts-domain.ts` | 账号行的能力标记(`usage` / `creditDetail` / `edition` / `identifier` / `expiry` / `longLived`) | 账号卡片上的徽章、余额格、有效期显示不对 | +| `ui-islands/src/islands/checkin-page.tsx` | `PROVIDER_DESC` 加链路说明 | 签到中心该家分组没有说明文字 | +| `ui/assets/providers/.png` | 图标 | 卡片没有图标 | + +`ui/providers.js` **不用改** —— 它从 `/api/session` 的 `accounts.providers`(后端注册表摘要)自动派生。 + +改完前端要重新构建岛:`npm run ui:islands`(产物 `ui/islands/ui.js` 要提交)。 + +## 4. i18n 与文档 + +### 4.1 i18n + +中文是源语言,`i18n/i18n-src/` 下只有五份译文(`en` / `ja` / `ko` / `pt-BR` / `zh-Hant`)。新增的中文文案会被 `scan.mjs` 扫出来,缺译文就是空值。 + +```bash +cd desktop-tauri/i18n +npm run scan # 扫出中文键,补齐到 i18n-src/* +npm run check # 检查是否有空值(CI 会拦) +npm run build # 生成 ui/i18n/*.js +``` + +历史教训:上游 v3.0.7 加了六条中文文案但没跑扫描,`scan.mjs --check` 时被扫出空值。**改完前端一定要跑 `check`。** + +### 4.2 文档 + +`README.md` 的「支持情况一览」**六份一一对应**,加一家要六份一起改: + +- `README.md`(简体中文,源) +- `docs/README.en.md` +- `docs/README.zh-Hant.md` +- `docs/README.ja.md` +- `docs/README.ko.md` +- `docs/README.pt-BR.md` + +另外注意「内置 N 家提供商」这个数字(「核心特性」一节,**六份 README 都有**,各自语言的措辞不同)。它数的是**表格里的内置行数**(不含「自定义提供商」那一行),不是 `PROVIDERS` 的条目数: + +- 表格把两地 / 两池合并成一行(如 `AutoClaw(国内版 / 国际版)`、`Cline(Free / Pass)`),而 `PROVIDERS` 里是两个条目; +- 所以加一家两地 provider 时,`PROVIDERS` +2、表格只 +1 行、这个数字也只 +1。 + +当前状态:表格 22 行内置 + 1 行自定义 = 23 行,`PROVIDERS` 29 条。改之前先数一遍,别按 `PROVIDERS` 的条数算。 + +安装 / 部署相关的内容只在 `docs/install*.md` 里维护,不要往 README 塞。 + +## 5. 施工顺序 + +按功能纵向闭环,不要先把后端全写完再做前端: + +1. **身份**:`ProviderKind` + `PROVIDERS` + `kind_from_id` / `kind_id` + `adapter_for` + `implemented_kinds` +2. **账号**:`_accounts.rs` + `add_account` 分派 + 公开形态 + 前端表单 → 能加号、能在列表里看到 +3. **目录**:`list_models` + `catalog_cache` scope + `refresh_models` → `/v1/models` 有内容、刷新按钮有效 +4. **转发**:`build_chat_request` 或 `forward_conversation` + `classify_error` → 能对话、限额能轮换 +5. **余额**:`supports_usage` + `query_usage` → 账号行能看到余额 +6. **签到 / 领取**:`CHECKIN_PROVIDERS` + 版本号 + `checkin_for` 分派 +7. **前端收尾**:`accounts-domain` 标记、签到中心说明、图标 +8. **i18n + 六份 README** + +每一步做完都能独立验证,不要攒到最后一起测。 + +## 6. 完成度验收清单 + +每一格都要能跑出结果,不是「我实现了」。 + +### 6.1 用探测脚本验(推荐) + +```bash +node scripts/probe-provider.mjs \ + --base http://127.0.0.1:3065 \ + --key sk-dev- \ + --provider \ + --model <模型名> +``` + +前置:在面板「网关 Key」页建一把**只允许目标 provider** 的 Key。不建的话,同名模型在多家里都有映射时会被路由到别家,结论无法归因(脚本启动时会先校验并报错退出)。 + +脚本由两个文件组成:`scripts/probe-provider.mjs`(CLI 外壳:参数、HTTP、前置校验、输出)与 `scripts/probe-cases.mjs`(九个用例的请求构造与判定)。加新用例改后者,`CASE_ORDER` 决定执行顺序。 + +用例与能力位的对应: + +| 用例 | 验的是什么 | 对应 README 列 / 能力位 | +|---|---|---| +| `stream` | 流式 SSE 能正常逐帧下发 | LLM 请求 | +| `tool_call` | `tools` 声明被上游接受,返回 `tool_calls` | LLM 请求(工具调用) | +| `parallel_tools` | 一次返回多个 `tool_calls` | LLM 请求(并行工具) | +| `tool_roundtrip` | 工具结果回灌后能继续生成 | LLM 请求(多轮循环) | +| `image` | 图片输入被接受 | LLM 请求(多模态) | +| `reasoning` | 思考字段能透出 | LLM 请求(思考) | +| `json_schema` | 结构化输出可用 | LLM 请求(结构化输出) | +| `usage` | 终帧带非零 usage | 报表 / 用量统计 | +| `context_window` | 目录声明的上下文窗口(读 `/v1/models`,不发上游) | 模型列表 | + +判定口径: + +- **必需项失败 = 这家的「LLM 请求」不能算接完。** `tool_call` 即使被目录声明为不支持,也记 **FAIL** 而不是 SKIP —— 记 SKIP 会让汇总显示「必需项全部通过」,而实际上 agent 客户端根本用不了这家。 +- 可选项目录声明不支持时(`image` / `reasoning`)记 **SKIP** 而不是 FAIL,否则每家都会刷一堆假失败。`--force` 可强测(用于「怀疑目录写错了」)。 +- `tool_call` 允许重试一次(模型偶尔就是不调工具),失败后换更硬的提示再发一次。 +- `parallel_tools` 只返回 1 个 call 时记 **INFO** —— 模型可能打算分两轮返回,那是行为差异不是适配缺陷。 +- `context_window` 记 **INFO/PASS** —— 它读的是目录声明,不是上游行为,和其余用例性质不同。 + +退出码:`0` = 没有必需项失败;`1` = 有必需项失败;`2` = 用法或前置条件错误(连不上网关、模型不在清单里、模型不由该家承载)。 + +### 6.2 逐列验收 + +| README 列 | 验收动作 | +|---|---| +| LLM 请求 ✓ | 探测 `stream` + `tool_call` + `tool_roundtrip` 全 PASS;用真实客户端(Claude Code / Codex 等)跑一轮多轮对话不报错 | +| Token 自动续期 ✓ | 账号页能看到有效期;等一个临期窗口(或手动改凭证时间)后跑 `credential_maintenance`,日志里有刷新成功记录;401 时能自动恢复而不是报错给客户端 | +| 模型列表 ✓ | `/v1/models` 有该家的条目;模型管理页点「获取模型」后清单变化;**重启进程后清单还在**(验 `ALL_SCOPES` 有没有漏) | +| 余额查询 ✓ | 账号行显示余额;点「积分明细」能展开;读不到的数值留空显示,不是 0 | +| 签到 ✓ | 签到中心出现该家分组且有说明文字;手动点一次成功;次日定时任务跑完台账有记录;**存量配置升级后这家在默认清单里**(验 `PROVIDERS_VERSION`) | +| 领取类 ✓ | 对应按钮能点且返回可读结果;没有活动时返回中性说明而不是红色失败 | +| 某列 ✗ | 能在代码注释里找到「怎么排除的」记录(打过哪些接口、返回什么) | + +### 6.3 静默失效自查 + +这些**不会报错**,只能靠主动验: + +- [ ] 重启进程后模型清单还在(`ALL_SCOPES`) +- [ ] 后台目录刷新真的会调度到这家(`implemented_kinds`) +- [ ] 存量用户升级后签到清单里有这家(`PROVIDERS_VERSION` + `PROVIDERS_ADDED_IN`) +- [ ] 公开形态里没有 token 本体(`store_view::public_account`) +- [ ] 限额错误真的触发了账号轮换,而不是当 Fatal 透传(看请求日志的 attempts 与冷却标记) +- [ ] 前端中文文案全部有译文(`npm run check`) + +## 7. 调试设施(都是现成的,别重造) + +| 要看的 | 怎么看 | +|---|---| +| **发往上游的最终 body**(已按家改写、已脱敏) | 面板设置页开「调试模式」→ 跑一次请求 → 请求日志详情页「原始报文」标签。数据在 `debug_traffic` 表,保留最近 500 条 | +| 上游原始响应(含 SSE 全文) | 同上,同一个标签 | +| 客户端发来的请求体 | 调试模式开启后同样可见(`request_raw` 表) | +| 单次转发的选路、重试链、usage | 模型管理页的「测试」按钮(`POST /api/models/test`),走真实转发链路、钉住 provider + account、结果失败也返回 2xx | +| 帧级翻译问题(Anthropic / Gemini / NDJSON / Chatglm 快照) | 调试报文里的上游原始 SSE,对照 `core/upstream/translate` 的输出 | + +**出站报文不需要新做工具** —— `TrafficCapture::reset_request` 抓的就是「即将发送前」那一份,四家路径全覆盖。 + +## 8. 已知坑 + +- **「上游没有这个接口」要先怀疑找法。** CatPaw 的目录参数在 POST body 里(看着像 query),AutoClaw 的目录在另一个路径前缀下(顺着 `chat/completions` 找永远找不到)。两次结论都错。 +- **429 不一定都是限额。** 有的家把额度耗尽、速率窗口、未验证邮箱都用 429/403 表达,处置完全不同(次日零点 vs 分钟级 vs 永久)。只看状态码会把三种混成一档。 +- **HTTP 200 可能是失败。** 正文一句固定话术、或流内 `{"type":"error"}`、或 `finish_reason: content_filter`,都要单独识别。 +- **`supports_*` 默认 false 不是「不支持」。** 它是「尚未接入」。判据必须是上游有没有那个接口。 +- **两个地区拆家时,`PROVIDERS` 里要相邻排列。** 中间隔着别家会让「找国际版」变成一次扫描。 +- **`implemented_kinds()` 与 `PROVIDERS` 是两个口径。** 前者是「适配器接线了没有」,后者是「注册了没有」。占位实现不该进前者。 +- **签到清单的默认全选是个陷阱。** 加一家进 `CHECKIN_PROVIDERS` 会让存量用户第二天零点就多发一轮请求。想默认开启就升版本号;不想默认开启就别进 `PROVIDERS_ADDED_IN`。 +- **改完前端要重新构建岛并提交产物。** `ui/islands/ui.js` 是检入仓库的,只改 tsx 不构建等于没改。 + +## 9. 参考实现 + +`git show 1576330` 是 ModelScope 接入的完整提交,48 个文件,正好覆盖第 2–4 节的每一类落点。拿不准该改哪些文件时,对着这个提交的 `--stat` 过一遍。 diff --git a/.agents/skills/release-flow/SKILL.md b/.agents/skills/release-flow/SKILL.md new file mode 100644 index 00000000..b5d0ed9f --- /dev/null +++ b/.agents/skills/release-flow/SKILL.md @@ -0,0 +1,218 @@ +--- +name: release-flow +description: Agent2API(workbuddy)仓库的协作与发版规范 —— 分支落脚点、合并 PR 与回复 Issue 的署名、版本号递增、正式版与 beta 预发布流程、发版验收与排错。凡是涉及切分支、给 PR 选 base、合并 PR、回 Issue/PR、改版本号、写更新日志、打 tag、挂 GitHub Release、发 beta 版、发版后验收,或排查发版失败时,都先按本 Skill 执行 —— 即使对方没提「发版」「分支」这类字眼。 +--- + +# Agent2API 协作与发版规范 + +覆盖两类工作:**日常协作**(分支落脚点、PR 署名、Issue 落款,第 1–3 节)与**发版**(版本号、更新日志、tag、Release、验收,第 4–7 节),排错见第 8 节。 + +## 0. 两条人工红线(必须停下等确认) + +下列两件事**必须由人先审过、明确点头**,代理才能继续;可以准备草稿、可以列依据,不能自己拍板往下走: + +1. **发版**:进入发版流程(改版本号、建发布提交、推 main / beta、打 tag、挂 Release)之前,先把版本号与「这次要发什么」讲清楚,等用户确认。**未经确认不得推任何分支或 tag** —— tag 一旦推出去就会触发构建、产出公开的安装包与镜像,收不回来。 +2. **更新日志**:写好的日志(发布提交的提交信息,同时也是 GitHub Release 正文)必须先给用户逐条过目。用户要求改措辞、增删条目、调整分类前缀时,按反馈改完要**再确认一次**,不能因为「改得不多」就直接提交。 + +一次发版里两条同时适用:既要有版本号的确认,也要有日志的确认;用户说「继续」之后才动手。 + +## 1. 分支落脚点:日常只碰 beta,发版才动 main + +| 分支 | 角色 | 什么时候动它 | +|---|---|---| +| `beta` | 集成分支(默认落脚点) | 日常修改、合并 PR、解冲突 —— 一律先到这里 | +| `main` | 发版分支 | 只在构建发版时:切 main、把 beta 合进来、打 tag | + +- **合并 PR 时 base 选 `beta`**,不要选 `main`。合进 `main` 等于把还没发布的改动提前塞进发版分支;之后还得反过来把 main 合回 beta,平白多一次合并提交、多一次冲突机会。已经把 PR 开成 base=main 的,先在 PR 页面把 base 改成 `beta` 再合。 +- **本地改动也提交到 `beta`**(写代码、改文档、解冲突都算),推 `origin/beta`;合并结果的核对、构建自检都在 beta 上做完。 +- **只有正式发版才切 `main`**:`git switch main` → 把 beta 合进来 → 写发布提交、推 main、打 tag;tag 必须落在 `main` 上。发完版 beta 再把 main 合回来,两边保持一致。 +- **不要自作主张选落脚分支**:用户没说要发版,就不要往 `main` 提交 / 推送,也不要把 PR 的 base 改成 `main`。 +- beta 版(预发布)是唯一例外:不切 `main`,tag 直接落在 `beta` 分支自己身上(见第 6 节)。 + +## 2. 合并 PR:保留贡献者署名 + +合并贡献者的 PR 时,提交作者(author)必须还是贡献者本人 —— 这直接决定对方能不能进仓库首页 Contributors 名单、贡献图里有没有这一笔。署名是贡献者应得的东西,不该因为合并方式丢掉。 + +GitHub 的归属规则只有一条:**提交头里的 author 邮箱出现在某个账号已验证的邮箱列表里,这次提交就算那个账号的**;否则记成匿名条目,不进 Contributors。另有两条口径:统计只认默认分支(本仓库 `main`),合并提交与空提交不计入。 + +- **默认用 GitHub 网页上的合并按钮**(`Merge pull request` / `Squash and merge` / `Rebase and merge`):三种都会保留原作者为 author;squash 时 PR 里的多位作者会一并进 co-author 名单。 +- **禁止「本地摘取重提」**:把对方的改动拷过来自己提交(本地 `git merge --squash` 后自己 commit、重抄一遍改动、或改了 author 再提交)都不行 —— 这样 author 是合并者,贡献者的署名在 GitHub 侧完全不可见。本仓库历史上出现过这种情况,别再来一次。 +- **确实要在本地处理时**(有冲突要解、要顺带调整),必须把署名带上:单个原作者用 `git commit --author="原作者 <原作者邮箱>"`;一个提交里有多个人的工作,在提交信息末尾加 trailer `Co-authored-by: 名字 <邮箱>`。 +- **摘除 AI 的 co-author trailer**:贡献者提交信息末尾的 `Co-Authored-By: Claude Code <…>` 这类 AI trailer,合并时一律删除 —— GitHub 会把它渲染成共同作者,进了 `main` 就出现在 Contributors 名单里,而 AI 不是贡献者。只删 trailer 行本身,author 与其余提交信息逐字保留;正文里对 AI 产品的**技术性提及**(「支持 Claude Code 客户端」「Claude 兼容端点」之类)与此无关,不动。摘除动的是已合入分支的历史,趁合并后、发版前尽早做(`git filter-branch --msg-filter` 摘行后 force-push),拖到发版后就得重写 `main`。 +- **邮箱必须能对上账号**:用对方 GitHub 账号里**已验证**的地址;对方想保密就用 GitHub 给的 `ID+用户名@users.noreply.github.com`(在对方账号的 Emails 设置里查)。内网地址、`user@机器名` 这类邮箱永远关联不上账号,提交会被记成匿名。 +- 合并后顺手核对一次:`git log --format='%an <%ae>' -1 <合并提交>`,或在 PR 页面看 commit 旁的头像是否指向原作者。 + +## 3. 回复 Issue / PR:标明 AI 来源 + +**由 AI 代理发出(含代笔)的每一条 Issue / PR 回复,末尾都要单独一行写明来源。** 收件人有权知道自己在跟谁说话 —— 与第 2 节的署名是同一条道理:**谁说的写谁**。 + +固定落款,逐字照用(别改写成「机器人」「助手」这类含糊说法): + +> 🤖 本条回复由 AI Agent 生成 + +- **位置**:整条回复的最后一行,单独成段(前后各留一个空行);不要塞进正文段落、列表项、引用块或代码块里 —— 引用别人的话时,落款落在自己的话之后。 +- **英文场合**(外部贡献者的 PR / Issue)在中文那行下面再补一行 `Generated by an AI agent.`,说的是同一件事,两边都不省。 +- **代理自己新建的 Issue / PR**,正文末尾同样加。 +- **代笔也算**:人在对话里让代理转达(哪怕逐字)一段话,落款照加 —— 它描述的是「这条回复是谁发出去的」,不是「谁构思的」。 +- 只约束 Issue / PR **回复**(评论、review 意见、关闭 / 合并说明)。提交信息不追加这一行,署名口径见第 2 节。 + +## 4. 版本号 + +- 版本号形如 `X.Y.Z`(如 `2.7.2`),日常小版本「递增 0.01」= 末位 +1(如 `2.7.8` → `2.7.9`)。 +- **递增幅度以用户要求为准**:默认用上面的「递增 0.01」;用户另有要求时(如「这次递增 0.1」)一律听用户的,按用户指定的幅度算出新版本号,不要自作主张套用默认幅度。 +- **版本号要同步改 6 处**(缺一处会导致安装包与显示版本对不上): + 1. `package.json`(根) + 2. `desktop-tauri/package.json` + 3. `desktop-tauri/src-tauri/Cargo.toml` + 4. `desktop-tauri/src-tauri/tauri.conf.json` + 5. `desktop-tauri/src-tauri/server/Cargo.toml` + 6. `desktop-tauri/mobile/package.json` —— 手机端安装包的版本号都从这里读:Android 的 `versionName` / `versionCode` 由 `android/variables.gradle` 读它(`versionCode` 由 semver 折算,只增不减,见该文件注释);iOS 的 `MARKETING_VERSION` / `CURRENT_PROJECT_VERSION` 由 `build.yml` 的 ios job 读它并传给 xcodebuild。不改它两端的包都还是旧版本号,产物名 `Agent2API-<版本>.apk` / `.ipa` 也取自这里 +- **改完第 6 处,必须顺手把 `desktop-tauri/mobile/package-lock.json` 根部的 `version` 也改成同一个值**(文件顶层的 `"version"` 与 `packages[""].version` 两处)。`npm ci` 对这两处不一致是**静默容忍**的(实测不会报错,也不会自动改),漏改 CI 照绿,但仓库里会留下版本号不一致的 lock 文件,下次谁跑 `npm install` 又会把它改回来,平白多出一个无关 diff。桌面端那几处没有这个问题(`desktop-tauri/package-lock.json` 的版本本来就落后,不随发版改)。 +- 应用内「关于」与更新检查读的是 `env!("CARGO_PKG_VERSION")`(第 5 处),改完跑一次 `cargo check` 让 `Cargo.lock` 跟着刷新。 +- 手机端界面目前**不显示**包自身版本号(只显示所连网关的版本);要核对版本,看系统「应用信息」页,或直接看 Release 附件名。 +- **顺手过一遍安装文档**:安装说明已经从 README 搬进 `docs/install.md`(README 只留速查表 + 一个链接),另有 5 份译文 `docs/install.{en,zh-Hant,ja,ko,pt-BR}.md`,六份内容一一对应。凡是这次改动碰到了**安装 / 升级 / 卸载步骤、产物体名、默认端口、环境变量、安装脚本参数**,六份都要同步改;README 速查表受影响时也一起改。 + +## 5. 正式版发版流程 + +按顺序执行;第 0 节的两条红线贯穿全程。 + +```bash +# 1. 收干净工作区(tag 里的代码就是发出去的代码) +git status --short # 必须为空;有改动就先提交或清理 +cargo check # 或前端有改动时做一次构建自检 + +# 2. 切到 main 并把 beta 合进来 +git switch main && git pull +git merge beta + +# 3. 六处版本号一起改(默认末位 +1,幅度听用户);改完刷新 Cargo.lock +cargo check + +# 4. 写发布提交:提交信息 = 更新日志 +git commit -am "…更新日志…" + +# 5. 推 main、打 tag、推 tag(一条 v* tag 触发全部构建) +git push origin main +git tag vX.Y.Z +git push origin vX.Y.Z + +# 6. 两个工作流跑完后挂 GitHub Release +gh run list --limit 4 # 手动跑 gh 前先设代理,见第 8 节 +bash scripts/release.sh vX.Y.Z +``` + +**发布提交的提交信息 = 更新日志**,因为发版收尾脚本创建 / 更新 Release 时,正文取「tag 所指提交的提交信息」(`git log -1 --format=%B`)。 + +- 格式沿用历史版本的编号清单,每行一条,带分类前缀:`新增:` / `优化:` / `修复:` / `更新:` / `移除:`,最后一条固定 `版本:X.Y.Z → X.Y.Z+1`; +- 内容取「上个 tag 以来的全部提交」综合整理(`git log vX.Y.Z..HEAD --oneline`),纯 CI / 临时的调试提交归并成一条流程性描述即可; +- 本次没有代码改动时用空提交承载:`git commit --allow-empty -F 更新日志.txt`; +- 更新日志草稿与历史发布说明统一写在 `docs/release-notes/<版本号>.md`(如 `docs/release-notes/2.9.2.md`),不要在项目根目录新建 `.release-notes-*.md` —— `docs/release-notes/` 已在忽略列表里(只忽略这个目录,`docs/` 下其它文档正常入库),新增文件不必再加忽略规则。 + +推 `v*` tag 会**同时触发两个工作流**(`.github/workflows/`): + +| 工作流 | 产出 | 说明 | +|---|---|---| +| `build.yml`(build) | 桌面端三平台安装包 + 两个服务端包 + Android APK + iOS IPA | 七个 job:`macos`(dmg)/ `windows`(NSIS exe)/ `linux-desktop`(deb + AppImage)/ `linux-server`(容器内更新用的 tar.gz)/ `windows-server`(无 GUI 的 zip)/ `android`(APK)/ `ios`(IPA,跑在 `macos-26`)。macOS / iOS 与 Linux 桌面端的产物**只能在各自平台上构建**,无法从 Windows 交叉编译;所有产物只挂 artifact,GitHub Release 由本地脚本挂载 | +| `docker.yml`(docker) | Docker Hub `aimodcc/agent2api:<版本>` + `:latest`(amd64 / arm64 双架构) | 手动 `workflow_dispatch` 触发时只出 `:dev` 测试 tag,不碰正式 tag;beta tag(版本号带 `-`)只出 `<版本>`,**不移动 `:latest`** | + +跟踪进度: + +```bash +gh run list --limit 4 +gh run watch --exit-status +``` + +**发版收尾**:GitHub Release 不由 CI 发布(Release 本来就不会自动创建),由本地脚本一步挂载: + +```bash +bash scripts/release.sh vX.Y.Z # 自动找该 tag 的成功 build run +bash scripts/release.sh vX.Y.Z # 或显式指定 run +``` + +脚本做三件事:下载 build 的安装包 artifact 到 `dist/` → 按 tag 提交信息(= 更新日志)创建 / 更新 GitHub Release 并挂附件 → 打印验收提示。幂等可重跑(`--clobber` 覆盖附件)。 + +## 6. beta 版(预发布) + +应用里有「设置 → 软件更新 → 更新设置 → **加入 beta 版**」开关:**打开的人才收到 beta 更新,没打开的人只看正式发布。** + +beta 版不是另一条流水线,而是第 5 节流程的一个变体 —— 唯一结构差别是「**从 `beta` 分支打带预发布后缀的 tag**」,提交、tag、Release、验收逐字照旧。第 0 节的两条红线同样适用。 + +```bash +# 1. 在 beta 分支上动手(beta 版不碰 main) +git switch beta && git pull +git status --short # 必须为空 + +# 2. 六处版本号一起改成「与 tag 逐字一致」的形态: +# tag 打 v3.1.0-beta.1 → 六处都写 3.1.0-beta.1(清单见第 4 节) +cargo check # 顺带刷新 Cargo.lock + +# 3. 提交并推 beta 分支(提交信息 = 这次的 beta 更新日志,格式同第 5 节) +git commit -am "…beta 版日志…" +git push origin beta + +# 4. 打 tag 并推:tag 落在 beta 分支的提交上 +git tag v3.1.0-beta.1 +git push origin v3.1.0-beta.1 + +# 5. 两个工作流跑完后挂 Release(脚本按 tag 里的 `-` 自动标 pre-release) +gh run list --limit 4 +bash scripts/release.sh v3.1.0-beta.1 +``` + +验收就四条:Release 显示 **Prerelease**、exe / dmg / deb / AppImage / APK 附件齐全、Docker Hub 上 `:latest` 的 Pushed 时间**没有**跟着变、APK 能装能连(完整清单见第 7 节)。 + +**三处不能错**,每处错了都有明确症状: + +1. **六处版本号漏了后缀**(写成 `3.1.0`):比较是按 semver 做的,运行版本 `3.1.0` 对线上 tag `3.1.0-beta.1` 会判成「当前比线上新」,**beta 用户永远收不到更新**。六个文件都接受带后缀的版本号(Cargo / npm / tauri.conf.json 都认),照 tag 抄即可。APK 的 `versionCode` 由 semver 前三段折算、不带后缀(见 `android/variables.gradle`),beta 与同号正式版取同一个值,不构成冲突。 +2. **Release 漏标 pre-release**:标记就是开关的分界线 —— 正式通道走 GitHub 的 `/releases/latest`,它排除预发布,只有开了开关的人从 `/releases` 列表里看到 beta。**漏标 = beta 被所有正式版用户当成正式更新收到。** `scripts/release.sh` 已按「tag 里带 `-`」自动标记,重跑收尾时也会把标记纠正回来,走脚本就不用操心。 +3. **tag 打在 `main` 上**:beta 是集成分支,beta 版要的是它的快照。打在 `main` 上要么不含 beta 分支的新改动(白测一场),要么等于把还没发布的改动提前推进 `main`。 + +docker 的 `:latest` 不会被 beta tag 移动(`.github/workflows/docker.yml`),镜像仍出 `3.1.0-beta.1` 那个版本 tag,想尝鲜的按版本号拉。 + +后续常见三件事: + +- **再发一个 beta**:核心号不变、递增序号(`3.1.0-beta.2`,六处一起改,上面那套命令照走)。预发布序号能分出先后(`beta.2` > `beta.1`),已经在 beta 上的人照样收到更新 —— 所以 beta 之间也要保持「tag 与版本号逐字一致」。 +- **转正**(发 `3.1.0` 正式版):按第 5 节在 `main` 上走,六处版本号去掉后缀。**beta 用户不会自动接回正式版**:开关开着的人只看预发布版(`pick_listed_release` 按「当前跑的是不是 beta 版」筛),转正的 `3.1.0` 对他们不算更新 —— 要留在 beta 频道的人等下一个 beta(`3.2.0-beta.1` 一出现就收得到);想回正式版由用户自己关开关,关掉后只在**版本号高于他手上那个 beta** 的正式版发布时才收到更新(`3.1.0` > `3.1.0-beta.1`,但更早的 `3.0.9` 不会把他降级)。 +- **「打开了却没有反应」**:仓库里一个预发布 Release 都没有时,打开这个开关也只会看到正式版 —— 第一次 beta 发出去之后才有效果。 + +## 7. 验收清单 + +- [ ] GitHub Release:`gh release view vX.Y.Z`(手动跑 gh 前先设代理,见第 8 节)—— 正文日志齐全,四个桌面端附件都在:Windows `Agent2API_<版本>_x64-setup.exe`、macOS `Agent2API_<版本>_universal.dmg`、Linux `Agent2API_<版本>_amd64.deb` 与 `Agent2API_<版本>_amd64.AppImage`(另有两个 `agent2api-server-linux-*.tar.gz` + `.sha256`,那是容器内更新包,见第 5 节); +- [ ] Linux 桌面端:装 deb(或 `chmod +x` 后跑 AppImage)能启动,且面板「设置 → 软件更新」能检查到版本、给出对应包型的安装入口(deb → 系统安装器,AppImage → 换文件); +- [ ] 手机端 APK:Release 里有 `Agent2API-<版本>.apk` 与 `.sha256`;装到 Android 上能启动、添加网关并登录,系统「应用信息」里的版本号与 tag 一致(APK 的 `versionName` 取自 `desktop-tauri/mobile/package.json`,见第 4 节第 6 处); +- [ ] 手机端 iOS:Release 里有 `Agent2API-<版本>.ipa` 与 `.sha256`(未签名包需用 AltStore / SideStore 重签;配了 iOS 签名 secret 的仓库出的是已签名包)。CI 的 `ios` job 在 `macos-26` 上跑,日志里能读到 `xcodebuild -version` 与版本号解析结果; +- [ ] Docker Hub:`aimodcc/agent2api` 的 Tags 页出现 `<版本>` 与 `latest`,Pushed 时间一致; +- [ ] 安装包「关于」页版本号与 tag 一致; +- [ ] 安装文档对得上实际产物:`docs/install.md` 与 5 份译文的产物名、端口、命令、环境变量跟这次 Release 一致(README 里那几张速查表同理); +- [ ] beta 版多一条:Release 显示 **Prerelease**,且 Docker Hub 上 `latest` 没有跟着动。 + +## 8. 已知坑与排查 + +- **GHCR 新包默认私有**:若以后镜像改推 GHCR,首次推送后需到包设置手动改 Public(当前推的是 Docker Hub,无此问题)。 +- **gh 不读 git 的代理配置,跑 gh 前要先设代理环境变量**:`gh` 是 Go 程序,不读 `~/.gitconfig` 里的 `http(s).proxy`,也不读 Windows 系统代理(WinINET),只认 `HTTPS_PROXY` / `HTTP_PROXY` 环境变量。本机 GitHub 直连时通时不通,手动执行 gh 命令前先设: + + ```bash + export HTTPS_PROXY=$(git config --get-urlmatch http.proxy https://github.com || git config --get https.proxy) + ``` + + 只设 `HTTPS_PROXY` 就够(gh 的请求全是 HTTPS,实测不读 `HTTP_PROXY`)。`scripts/release.sh` 已内置这一步(从 git 配置读取后透传给 gh),用脚本发版无需手动设。代理没开时 gh 会立刻报 `proxyconnect tcp: ... connection refused` 而不回退直连 —— 与 `git push` 的表现一致。 + +- **APK 签名走四个 secret,缺一个 `android` job 直接红**:`ANDROID_KEYSTORE_BASE64` / `ANDROID_KEYSTORE_PASSWORD` / `ANDROID_KEY_ALIAS` / `ANDROID_KEY_PASSWORD`。密钥本体(`*.jks`)与 `keystore.properties` 都在 `.gitignore` 里,只在打包机与仓库 secret 各存一份。**2026-10-19 已配好**(仓库 `Agent2API/agent2api`,来源是打包机 `desktop-tauri/mobile/android/` 下的 `agent2api-release.jks` 与 `keystore.properties`,keyAlias 为 `agent2api`);换机器或密钥轮换时按下述方式重配。 +- **重配 / 补配 secret 的操作**(在打包机上执行,值从本地文件读,不要手抄): + + ```bash + cd desktop-tauri/mobile/android + # base64 必须是单行(-w0),带换行会解出损坏的 jks + base64 -w0 agent2api-release.jks > /tmp/ks.b64 + gh secret set ANDROID_KEYSTORE_BASE64 --repo Agent2API/agent2api < /tmp/ks.b64 + # 其余三个直接取 keystore.properties 的值,去掉键名与换行 + sed -n 's/^storePassword=//p' keystore.properties | tr -d '\n' | gh secret set ANDROID_KEYSTORE_PASSWORD --repo Agent2API/agent2api + sed -n 's/^keyAlias=//p' keystore.properties | tr -d '\n' | gh secret set ANDROID_KEY_ALIAS --repo Agent2API/agent2api + sed -n 's/^keyPassword=//p' keystore.properties | tr -d '\n' | gh secret set ANDROID_KEY_PASSWORD --repo Agent2API/agent2api + rm -f /tmp/ks.b64 + gh secret list --repo Agent2API/agent2api # 只显示名字与更新时间,值读不回来(GitHub 设计如此) + ``` + + 校验密钥本身是否可用:`keytool -list -keystore agent2api-release.jks -storepass `,应看到别名 `agent2api` 的 `PrivateKeyEntry`。**换密钥的后果**是已安装用户无法覆盖升级(签名不一致),不是重装一遍就好 —— 原密钥丢了等于断了 APK 的升级路径。 +- **APK 构建的两处环境差异**(`build.yml` 的 android job 已处理,改这块时别删):`android/gradle.properties` 里的 `org.gradle.java.home` 是本机 Windows 绝对路径,runner 上要删掉回落 `JAVA_HOME`;`gradle-wrapper.properties` 的 `distributionUrl` 指向腾讯云镜像,runner 在海外要换回官方源。 +- **APK 的产物名由 `mobile/package.json` 的 version 决定**:`Agent2API-<版本>.apk`,`release.sh` 原样挂上 Release。安装文档六份里写的也是这个名字,改命名规则要连文档一起改。 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..01edc506 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# 钩子脚本必须以 LF 检出。 +# +# Windows 上 core.autocrlf=true 会把文本文件检出成 CRLF,而 CRLF 的 +# `#!/bin/sh` 会让 Git Bash 报 "bad interpreter",钩子直接失效(还不会 +# 报错提示,只是静默不跑)。只约束 .githooks/ 下的文件,仓库其余部分 +# 维持现状,不做全仓库行尾规范化。 +.githooks/** text eol=lf diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100644 index 00000000..0417089c --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,89 @@ +#!/bin/sh +# +# pre-commit:本次提交若动了前端源码,就自动重建入库产物并一起提交。 +# +# ── 为什么需要它 ────────────────────────────────────────────── +# 本仓库把前端构建产物**入库**(ui/islands/ui.js、ui/islands/ui.css、 +# ui/i18n/**、ui/m/**、ui/sw.js),Tauri 打包(tauri.conf.json 的 +# frontendDist 指向 ui/)与 Docker 镜像(Dockerfile COPY ui/)都直接取 +# 这些字节 —— 仓库里那几份就是用户拿到的界面,没有任何构建兜底。 +# +# 于是「改了源码忘了重建」会一路进分支,而 review 看不出 1 MB 压缩产物 +# 里少了什么。CI 的 frontend-artifacts 闸能在推送后拦住,但那时代码已经 +# 进了远端;这道钩子把拦截提前到本地提交前 —— 忘了也交不上去。 +# +# ── 行为 ────────────────────────────────────────────────────── +# 检测暂存区里是否含前端源码改动,有则重建对应产物并 git add: +# ui-islands / ui-kit → desktop-tauri/ui/islands/{ui.js,ui.css} +# mobile → desktop-tauri/ui/m/** 与 desktop-tauri/ui/sw.js +# i18n → desktop-tauri/ui/i18n/** +# 任何一步构建失败都会中止提交(产物没更新,提交出去就与源码不同源)。 +# +# ── 逃生门 ──────────────────────────────────────────────────── +# 刻意想分开提交(例如先提源码再提产物)时: +# git commit --no-verify +# 关掉钩子(本机生效): +# git config --unset core.hooksPath + +set -e + +# 暂存区名单落到临时文件:管道里的 while 跑在子 shell 里,变量传不出来 +staged="$(mktemp)" +trap 'rm -f "$staged"' EXIT +git diff --cached --name-only --diff-filter=ACMR > "$staged" + +need_islands=0 +need_mobile=0 +need_i18n=0 + +while IFS= read -r f; do + [ -n "$f" ] || continue + case "$f" in + # ui-kit 以源码方式被两端引用:岛走 vite alias,手机端引它的样式 + desktop-tauri/ui-kit/* | desktop-tauri/ui-islands/*) + need_islands=1 + need_mobile=1 + ;; + desktop-tauri/i18n/*) + need_i18n=1 + ;; + # 手机端原生工程不参与 Web 产物,改了不用重建 + desktop-tauri/mobile/android/* | desktop-tauri/mobile/ios/* | desktop-tauri/mobile/android-www/*) + ;; + desktop-tauri/mobile/*) + need_mobile=1 + ;; + esac +done < "$staged" + +if [ "$need_islands" = 0 ] && [ "$need_mobile" = 0 ] && [ "$need_i18n" = 0 ]; then + exit 0 +fi + +if ! command -v npm > /dev/null 2>&1; then + echo "pre-commit: 检测到前端源码改动,但找不到 npm,无法重建产物。" >&2 + echo " 装好 Node 后重试,或用 git commit --no-verify 跳过(不推荐)。" >&2 + exit 1 +fi + +echo "pre-commit: 检测到前端源码改动,重建入库产物……" + +if [ "$need_islands" = 1 ]; then + npm --prefix desktop-tauri/ui-islands run build +fi +if [ "$need_mobile" = 1 ]; then + npm --prefix desktop-tauri/mobile run build +fi +if [ "$need_i18n" = 1 ]; then + npm --prefix desktop-tauri/i18n run build +fi + +# 只 add 产物所在目录:ui/ 根下的 app.js、css/ 等是手写文件,不归钩子管 +[ "$need_islands" = 1 ] && git add desktop-tauri/ui/islands +[ "$need_mobile" = 1 ] && git add desktop-tauri/ui/m desktop-tauri/ui/sw.js +[ "$need_i18n" = 1 ] && git add desktop-tauri/ui/i18n + +# 产物更新后可能在暂存区留下差异(也可能没有 —— 那说明本来就同源), +# 两种情况都放行;真正的把关交给构建是否成功 +echo "pre-commit: 产物已重建并加入本次提交" +exit 0 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index f7fb0c82..c6a639ae 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -7,4 +7,4 @@ # 注意:链接固定指向默认分支(main),文档合并到 main 之后按钮才可用。 # 将来接入其它平台(爱发电 / Buy Me a Coffee / GitHub Sponsors 等)时, # 在这里追加对应的键(如 buy_me_a_coffee)或往 custom 数组里再加链接即可(最多 4 条)。 -custom: ["https://github.com/aimod-cc/agent2api/blob/main/docs/SPONSOR.md"] +custom: ["https://github.com/Agent2API/agent2api/blob/main/docs/SPONSOR.md"] diff --git a/.github/secret_scanning.yml b/.github/secret_scanning.yml new file mode 100644 index 00000000..f026d588 --- /dev/null +++ b/.github/secret_scanning.yml @@ -0,0 +1,21 @@ +# secret scanning / push protection 路径排除清单(GitHub 官方机制: +# 「Excluding folders and files from secret scanning」,键为 paths-ignore)。 +# +# 下列文件含有**第三方官方客户端自带**的公开凭证 —— 不是本仓库的机密, +# 也不能改写(改了对应提供商的登录链路就断了),因此按整文件排除, +# 避免历史告警反复出现、push protection 拦截触碰这些值的推送: +# +# 1. providers/antigravity/endpoints.rs —— Google 官方 Antigravity 客户端 +# 随安装包分发的公开 OAuth client_id / client_secret(全网开源实现原样 +# 公开;本仓文件头注释也写明「公开客户端凭证,不是用户机密」)。 +# 用于 Antigravity 提供商的 Google 账号网页登录(oauth.rs)。 +# 2. providers/orcaterm/login.rs —— 腾讯 OrcaTerm 官方自己的微信开放平台 +# AppID(其登录页 302 跳转 URL 里明文可见的公开标识符;微信的机密是 +# AppSecret,本仓没有)。该值在文件里仅出现在注释中。 +# +# 注意:此配置按仓库默认分支(main)读取,改动合入 main 后才生效; +# 命中的已有告警会被自动关闭(理由:ignored by configuration)。 +# 不要往本文件写入任何密钥明文 —— 本文件自身不在排除清单里,写了反而会触发告警。 +paths-ignore: + - "desktop-tauri/src-tauri/server/src/server/core/providers/antigravity/endpoints.rs" + - "desktop-tauri/src-tauri/server/src/server/core/providers/orcaterm/login.rs" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4fcef6f7..6057aabc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,15 +4,35 @@ name: build # # ── 为什么需要 CI 构建 ───────────────────────────────────────── # Tauri 的 macOS 产物(.app / .dmg)**只能在 macOS 上生成**,无法从 Windows -# 交叉编译。因此 Windows 上开发、又要出 Mac 包时,只能借助 CI 的 macOS runner。 +# 交叉编译;Linux 产物(deb / AppImage)同理 —— 它们链接 webkit2gtk / gtk3, +# 同样无法从 Windows 交叉。因此这两个平台的桌面端都交给 CI 的 runner 出。 # 本仓库是公开仓库,标准 GitHub-hosted runner(含 macOS)对公开仓库免费且 # 不计分钟数 —— macOS runner 是私有仓库里最贵的一档,公开仓库没有这个问题。 # +# ── 六个 job 的分工(名字相近,别看混)──────────────────────── +# macos → macOS 桌面端 dmg → artifact: macos-universal +# windows → Windows 桌面端 NSIS exe → artifact: windows-nsis +# linux-desktop → Linux 桌面端 deb+AppImage → artifact: linux-desktop +# linux-server → Linux **服务端**包(无 GUI)→ artifact: linux-server-{amd64,arm64} +# windows-server → Windows **服务端**包(无 GUI)→ artifact: windows-server-amd64 +# android → Android APK(Capacitor) → artifact: android-apk +# ios → iOS IPA(Capacitor) → artifact: ios-ipa +# +# 前三者是「桌面端安装包」(给用户装在自己机器上),中间两者是**服务器形态**: +# 无图形界面、命令行安装、注册成系统服务(Linux systemd / Windows SCM)。 +# linux-server 的产物同时还是容器内一键更新的载荷。 +# +# android / ios 是**手机端**:Capacitor 把 `desktop-tauri/mobile` 的界面打进 +# 安装包,与 PWA 形态共用同一份 UI 代码。它的 Web 形态(`ui/m/**`)不需要在 +# 这里构建 —— 那份产物**入库**,由 frontend-artifacts.yml 校验可复现,桌面端 / +# 服务端 / Docker 的 `ui/` 静态目录直接带上它,发版流程一行都不用改。这里只补 +# 「需要签名密钥、只能在打包机上出」的那一半。 +# # ── 触发方式 ────────────────────────────────────────────────── # · 手动 workflow_dispatch(在 Actions 页面选分支触发): -# 三个平台一起构建,产物作为 workflow artifact 下载,不发布 Release +# 各平台一起构建,产物作为 workflow artifact 下载,不发布 Release # · 推送 v* tag:与 docker.yml 同时触发,一次打 tag 安装包与镜像全出; -# Windows/macOS 包与 Linux 服务端包只挂 workflow artifact —— GitHub +# 桌面端安装包与 Linux 服务端包只挂 workflow artifact —— GitHub # Release 由发版收尾脚本在本地挂载(scripts/release.sh):Release 本就 # 不自动建,安装包只挂 artifact 让构建与发布解耦,发布那步重跑脚本即可 # @@ -40,12 +60,23 @@ name: build # (甚至因为找不到 lock 文件而直接报错)。 # # ── 签名 ────────────────────────────────────────────────────── -# 默认**不签名**(ad-hoc)。macOS 上未签名的应用 Gatekeeper 会拦下, +# 桌面端默认**不签名**(ad-hoc)。macOS 上未签名的应用 Gatekeeper 会拦下, # 用户需要在「系统设置 → 隐私与安全性」里手动放行一次。要消除这一步需要 # Apple Developer 账号($99/年)做 Developer ID 签名 + 公证,届时在仓库里配 # APPLE_CERTIFICATE / APPLE_CERTIFICATE_PASSWORD / APPLE_SIGNING_IDENTITY / # APPLE_ID / APPLE_PASSWORD / APPLE_TEAM_ID 这几个 secret 即可, # tauri-action 与 tauri CLI 会自动完成签名与公证。 +# +# APK 则**必须签名**(Android 不接受未签名的 release 包),密钥与密码不入库 +# (见 .gitignore),由下面 android job 从四个 secret 还原: +# ANDROID_KEYSTORE_BASE64 agent2api-release.jks 的 base64(单行) +# ANDROID_KEYSTORE_PASSWORD keystore.properties 的 storePassword +# ANDROID_KEY_ALIAS keyAlias(当前是 agent2api) +# ANDROID_KEY_PASSWORD keyPassword +# ⚠️ 这四样换不得:换了密钥签名就不一致,已装的用户无法覆盖升级。原密钥 +# 除了打包机之外要另存一份,secret 只是它的第二份副本。 +# 生成 base64:base64 -w0 agent2api-release.jks(Windows 用 +# [Convert]::ToBase64String([IO.File]::ReadAllBytes('agent2api-release.jks'))) on: workflow_dispatch: @@ -141,6 +172,86 @@ jobs: path: target/release/bundle/nsis/*.exe if-no-files-found: error + # ── Linux 桌面端(deb + AppImage)──────────────────────────── + # Tauri 的 Linux 产物同样**只能在 Linux 上生成**(链接 webkit2gtk / gtk3, + # 无法从 Windows 交叉编译),所以和 macOS 一样交给 CI。 + # + # ── 与下面 linux-server job 的区别(两者都在 Linux 上构建,别混)── + # · 本 job:**桌面端**安装包(deb / AppImage),带 Tauri 壳与 GUI 界面, + # 产物名以 `Agent2API_<版本>_` 开头,用户在桌面上装它; + # · linux-server job:**服务端**包(容器内一键更新用的 tar.gz), + # 无 GUI,产物名以 `agent2api-server-linux-` 开头。 + # 两者服务于完全不同的形态,Release 上并存(见 scripts/release.sh)。 + # + # ── 平台 conf 用 tauri.linux.conf.json(与 macOS 同款做法)────── + # 基础 tauri.conf.json 的 targets 是 ["nsis"](Windows 专用),Linux 构建 + # 必须用 `--config` 覆盖成 deb + appimage,同时补上 Linux 需要的 PNG 图标 + # 与 deb 的运行时依赖声明。理由与 macOS 那份一致:三个平台的 bundle 配置 + # 差别太大(目标格式、图标格式、依赖声明各不同),挤在一个文件里会让 + # 每个平台都要读一整套无关字段。 + linux-desktop: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - name: 安装 Node + uses: actions/setup-node@v7 + with: + node-version: '22' + # 依赖在 desktop-tauri/ 下(根 package.json 没有依赖),必须显式指路 + cache: npm + cache-dependency-path: desktop-tauri/package-lock.json + + - name: 安装 Rust + uses: dtolnay/rust-toolchain@stable + + - name: 缓存 Rust 依赖 + uses: Swatinem/rust-cache@v2 + with: + workspaces: desktop-tauri/src-tauri -> ../../target + + # ── 系统依赖:Tauri 在 Linux 上的硬要求 ────────────────────── + # webkit2gtk-4.1 是 WebView 本体(4.1 = libsoup3 一代,Tauri 2 用这个; + # 4.0 是 Tauri 1 的,装了也没用);gtk3 是窗口层; + # libayatana-appindicator3 是**托盘**(本应用的关窗后唯一入口,缺它 + # 托盘建不起来,用户关窗就再也叫不回界面);librsvg2 供图标渲染 + # (tauri bundler 把 SVG 图标转 PNG 时用);patchelf 是 bundler 给 + # AppImage 改 RPATH 时调的。缺任何一个都会在 `tauri build` 阶段报错, + # 而不是编译期 —— 所以这一步必须在上传产物之前。 + - name: 安装 Linux 构建依赖 + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libwebkit2gtk-4.1-dev \ + libgtk-3-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + patchelf \ + build-essential + # 打印实际版本:排查「构建机换代导致行为变化」时这是第一条线索 + pkg-config --modversion webkit2gtk-4.1 gtk+-3.0 ayatana-appindicator3-0.1 + + - name: 安装桌面端依赖 + run: npm --prefix desktop-tauri ci + + # 不走 tauri-action:它的默认脚本是 `npm run tauri build`,而本项目的 + # package.json 里那条脚本叫 `build`(同 macOS job 的说明)。 + - name: 构建 Linux 桌面端安装包 + run: npm --prefix desktop-tauri run build -- --config src-tauri/tauri.linux.conf.json + + - name: 上传产物 + uses: actions/upload-artifact@v7 + with: + name: linux-desktop + # deb 与 AppImage 两个目录各出一份产物: + # target/release/bundle/deb/*.deb + # target/release/bundle/appimage/*.AppImage + # 同一目录下还有 .sig / 中间产物,只用通配精确取这两种。 + path: | + target/release/bundle/deb/*.deb + target/release/bundle/appimage/*.AppImage + if-no-files-found: error + # ── Linux 服务端包(容器内一键更新的 Release 资产)────────── # 面板在容器里「一键更新」时下载的就是这里的产物:包内是可执行文件 + # ui/ 目录,由服务端自己替换后再重启(见 core/update/selfupdate.rs)。 @@ -178,13 +289,28 @@ jobs: - name: 打包并生成校验和 run: | name="agent2api-server-linux-${{ matrix.arch }}.tar.gz" - mkdir -p dist - # 包内布局是硬约定:根下 agent2api-server 与 ui/(解包方按名取) - tar -C out/bundle -czf "dist/$name" agent2api-server ui + mkdir -p dist stage + # 先把要进包的东西归拢到一个暂存目录,再一次压成 tar.gz。 + # 不能「先 tar 再 -r 追加」:gzip 流无法原地追加,tar 会直接 + # 报 "Cannot update compressed archives"。 + rm -rf stage/pkg + mkdir -p stage/pkg + cp -r out/bundle/agent2api-server out/bundle/ui stage/pkg/ + cp packaging/linux/install.sh \ + packaging/linux/agent2api.service \ + packaging/linux/agent2api.env.example stage/pkg/ + # 包内布局:根下 agent2api-server 与 ui/ 是容器内更新链的**硬约定** + # (selfupdate::extract_bundle 按名取这两个条目);其余条目它读完即丢, + # 所以 install.sh / 单元 / env 样例跟它们同放不会影响一键更新。 + # 同一个包于是既能被容器更新链消费,也能解包当裸机安装包用。 + tar -C stage/pkg -czf "dist/$name" agent2api-server ui \ + install.sh agent2api.service agent2api.env.example # 校验文件与包同名 + .sha256:面板按这个名字找它,找不到就拒绝安装 ( cd dist && sha256sum "$name" > "$name.sha256" ) cat "dist/$name.sha256" ls -lh dist + echo "── 包内条目 ──" + tar -tzf "dist/$name" | head -20 - name: 上传产物 uses: actions/upload-artifact@v7 @@ -192,3 +318,446 @@ jobs: name: linux-server-${{ matrix.arch }} path: dist/* if-no-files-found: error + + # ── Windows 服务端包(无 GUI,注册成 Windows 服务)──────────── + # 产物是 zip:agent2api-server.exe + ui\ + install-service.ps1 + # + start-windows.bat(下游新增,见打包步骤里的说明)。 + # 用户在服务器上解压后跑 install-service.ps1,脚本用 sc.exe 把它注册成 + # 开机自启的 Windows 服务(见 packaging/windows/install-service.ps1); + # 只想在本机试用的,双击 start-windows.bat 前台跑,关窗口即停。 + # + # 为什么不复用 windows job 的产物:那个 job 构建的是 Tauri 桌面端 + # (带窗口、托盘、WebView2),装的是 NSIS 安装包。这里的产物无 GUI, + # 跑的是 `cargo build -p agent2api-server`(不链接 tauri 全家桶), + # 两者用途完全不同 —— 一个给桌面用户,一个给服务器。 + # + # 为什么不走 Dockerfile:Windows 容器镜像不能跑 Linux 构建,且 + # windows-latest runner 原生就能编,没必要多一层。 + windows-server: + runs-on: windows-latest + steps: + - uses: actions/checkout@v7 + + - name: 安装 Rust + uses: dtolnay/rust-toolchain@stable + + - name: 缓存 Rust 依赖 + uses: Swatinem/rust-cache@v2 + with: + workspaces: desktop-tauri/src-tauri -> ../../target + + # 只编 server crate(`-p`),不碰桌面壳:少了 tauri / wry / webview2 + # 那一整棵依赖树,构建快且产物里没有任何 GUI 依赖。 + # + # ⚠️ 必须在 `desktop-tauri/src-tauri` 下跑,不能用「仓库根 + --manifest-path」: + # 产物目录由 `src-tauri/.cargo/config.toml` 的 `target-dir = "../../target"` + # 决定,而 cargo **只从 CWD 往上找配置文件** —— 从仓库根跑,这个配置根本 + # 不会被读到,产物落到 `desktop-tauri/src-tauri/target/`,下面的打包步骤 + # 按 `target/release/...` 去找就会「找不到文件」而失败(2026-10-17 实测, + # v3.0.9-beta.1 的 windows-server 作业就是这么挂的)。 + - name: 构建服务端 + working-directory: desktop-tauri/src-tauri + run: cargo build --release -p agent2api-server --bin agent2api-server + + - name: 打包 + shell: pwsh + run: | + $name = "agent2api-server-windows-amd64" + $stage = "stage/$name" + New-Item -ItemType Directory -Force -Path $stage | Out-Null + + # 布局是 install-service.ps1 的硬约定:脚本与 exe、ui\ 同级 + Copy-Item "target/release/agent2api-server.exe" "$stage/" + Copy-Item -Recurse "desktop-tauri/ui" "$stage/ui" + Copy-Item "packaging/windows/install-service.ps1" "$stage/" + + # ── 双击启动器(下游新增,上游包只有 install-service.ps1)────── + # install-service.ps1 要管理员权限、把网关注册成系统服务,适合服务器; + # 「本地 web」形态(自己电脑上解压后想立刻用浏览器打开面板)用不上它, + # 也不该为了试一下就先注册一个开机自启的服务。这里再放一个前台启动器: + # 双击即跑、自动开浏览器、关窗口即停,不碰 SCM、不写 ProgramData。 + # + # 在校验里现生成而不入库:.bat 必须是 CRLF + 本机代码页,放仓库里 + # 迟早被编辑器的 LF / UTF-8 改坏(cmd 读出乱码甚至把 echo 行当命令)。 + # 数组逐行拼(不用 here-string):YAML 块缩进会原样进 here-string, + # 每行前面多出的空格虽被 cmd 容忍,但会让 `if (` 与 `echo.` 变得可疑。 + $launcher = @( + '@echo off' + 'setlocal' + 'cd /d "%~dp0"' + 'if not exist "agent2api-server.exe" (' + ' echo [error] agent2api-server.exe not found next to this file.' + ' pause' + ' exit /b 1' + ')' + 'if not exist "ui\index.html" (' + ' echo [error] ui\index.html not found. Keep the ui folder beside the exe.' + ' pause' + ' exit /b 1' + ')' + 'echo.' + 'echo Agent2API local gateway' + 'echo -----------------------' + 'echo The panel opens in your browser (default http://127.0.0.1:3065/).' + 'echo Keep this window open; close it or press Ctrl+C to stop.' + 'echo.' + 'agent2api-server.exe' + 'echo.' + 'echo Server stopped.' + 'pause' + ) + $launcher | Out-File -FilePath "$stage/start-windows.bat" -Encoding ascii + + New-Item -ItemType Directory -Force -Path dist | Out-Null + Compress-Archive -Path "$stage/*" -DestinationPath "dist/$name.zip" -Force + + # 校验和:与 Linux 包同格式(sha256 十六进制 + 两个空格 + 文件名), + # 用手动升级的用户可以照 README 里的命令核对 + $hash = (Get-FileHash "dist/$name.zip" -Algorithm SHA256).Hash.ToLower() + "$hash $name.zip" | Out-File -Encoding ascii "dist/$name.zip.sha256" + + Get-ChildItem dist | Format-Table Name, Length + + - name: 上传产物 + uses: actions/upload-artifact@v7 + with: + name: windows-server-amd64 + path: dist/* + if-no-files-found: error + + # ── Android APK(Capacitor)────────────────────────────────── + # 手机端的**安装包**形态。Web 形态(PWA)不在这里构建:`ui/m/**` 入库、 + # 由 frontend-artifacts.yml 校验可复现,桌面端 / 服务端 / Docker 的 `ui/` + # 静态目录直接带上它,一行都不用改。这里补的是「必须签名、只能在打包机 + # 上出」的那一半。 + # + # ── 为什么用 ubuntu-latest 而不是 macOS / Windows ──────────── + # Android 构建与宿主平台无关(AGP 出的是同一个 APK),而 GitHub 的 + # ubuntu runner 自带 Android SDK(ANDROID_HOME 已设,含 compileSdk 36 + # 与 build-tools 36.0.0 —— 与 android/variables.gradle 对得上), + # 不需要额外下载 SDK。macOS runner 对公开仓库虽然免费,但启动与排队 + # 都更慢,没必要。 + # + # ── 为什么必须先装 ui-kit ──────────────────────────────────── + # 手机端以**源码**方式接入组件库(vite alias 指到 ../ui-kit/src),而 + # ui-kit/styles/globals.css 里的 `@import "tailwindcss"` / `tw-animate-css` + # / `shadcn` 要从**它自己**的 node_modules 解析。只装 mobile 不装 ui-kit + # 时样式解析不到,构建出的 mobile.css 会缺一大块 —— 与 + # frontend-artifacts.yml 里同一条注意事项。 + android: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - name: 安装 Node + uses: actions/setup-node@v7 + with: + node-version: '22' + cache: npm + # 两个 lock 文件都要列:mobile 的构建依赖 ui-kit 的源码与样式 + cache-dependency-path: | + desktop-tauri/ui-kit/package-lock.json + desktop-tauri/mobile/package-lock.json + + # Capacitor 8 生成的 capacitor.build.gradle 要求 Java 21 + # (sourceCompatibility / targetCompatibility = VERSION_21)。 + - name: 安装 JDK 21 + uses: actions/setup-java@v6 + with: + distribution: temurin + java-version: '21' + + # 缓存 Gradle 分发包与依赖(~/.gradle)。首次构建要下 wrapper 与 + # 一整棵 AGP / androidx 依赖树,命中后只剩编译。 + - name: 设置 Gradle 缓存 + uses: gradle/actions/setup-gradle@v6 + + - name: 安装依赖 + run: | + npm --prefix desktop-tauri/ui-kit ci + npm --prefix desktop-tauri/mobile ci + + # ── 签名密钥:从 secret 还原 ───────────────────────────── + # 密钥与密码不入库(见 .gitignore),这里用四个 secret 现场拼出 + # android/agent2api-release.jks 与 keystore.properties。 + # + # 缺 secret 时**明确失败**,不产未签名包:Android 不接受未签名的 + # release APK,而「静默跳过」的结果是 Release 少一个安装包、用户 + # 装不上 —— 让流水线红在配置缺失上,比让发版悄悄缺件好排查。 + # + # ⚠️ keystore.properties 的 storeFile 是 `../agent2api-release.jks`, + # 相对于 android/app/ 解析 → 密钥要放在 android/ 目录下。 + - name: 还原 APK 签名密钥 + env: + ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + run: | + missing="" + [ -n "$ANDROID_KEYSTORE_BASE64" ] || missing="$missing ANDROID_KEYSTORE_BASE64" + [ -n "$ANDROID_KEYSTORE_PASSWORD" ] || missing="$missing ANDROID_KEYSTORE_PASSWORD" + [ -n "$ANDROID_KEY_ALIAS" ] || missing="$missing ANDROID_KEY_ALIAS" + [ -n "$ANDROID_KEY_PASSWORD" ] || missing="$missing ANDROID_KEY_PASSWORD" + if [ -n "$missing" ]; then + echo "::error::缺少 secret:$missing" + echo "APK 必须签名才能安装,流水线不产未签名包。到仓库" + echo "Settings → Secrets and variables → Actions 补齐这四个 secret," + echo "生成方式见 .github/workflows/build.yml 顶部「签名」一节。" + exit 1 + fi + + cd desktop-tauri/mobile/android + printf '%s' "$ANDROID_KEYSTORE_BASE64" | base64 -d > agent2api-release.jks + # 用 printf 而不是 heredoc:YAML 块标量会给每行加缩进, + # heredoc 的结束符必须顶格,缩进后会一直读到文件尾 + printf 'storeFile=../agent2api-release.jks\nstorePassword=%s\nkeyAlias=%s\nkeyPassword=%s\n' \ + "$ANDROID_KEYSTORE_PASSWORD" "$ANDROID_KEY_ALIAS" "$ANDROID_KEY_PASSWORD" \ + > keystore.properties + ls -l agent2api-release.jks + + - name: 构建 APK + run: | + cd desktop-tauri/mobile/android + + # gradle.properties 里的 org.gradle.java.home 是本机 Windows 路径 + # (C:/Program Files/Eclipse Adoptium/jdk-21…),Linux runner 上 + # 不存在,Gradle 会直接报「无效的源发行版」或找不到 JVM。删掉这行, + # 让它回落到 setup-java 设好的 JAVA_HOME。 + sed -i '/^org\.gradle\.java\.home=/d' gradle.properties + + # wrapper 默认走腾讯云镜像(本机在国内用它加速);runner 在海外, + # 该镜像慢且不稳。CI 改官方源,并用 -bin 而不是 -all + # (-all 含源码与文档,约 200 MB,构建用不到)。 + sed -i 's|^distributionUrl=.*|distributionUrl=https\\://services.gradle.org/distributions/gradle-8.14.3-bin.zip|' \ + gradle/wrapper/gradle-wrapper.properties + grep '^distributionUrl=' gradle/wrapper/gradle-wrapper.properties + + # 入库的 gradlew 是 100644(Windows 上生成,没有执行位), + # Linux 上要补一下才能 ./gradlew + chmod +x gradlew + + # 先出 Web 产物再同步进原生工程: + # tsc --noEmit → vite build --mode apk(产物落 android-www/) + # → cap sync android(拷进 app/src/main/assets/public) + # + # ⚠️ 只回一级:当前位置是 desktop-tauri/mobile/android,上一级 mobile + # 才是 build:apk 所在的 package。曾写成 `cd ../..`,退到了 desktop-tauri + # —— 那个 package.json 没有 build:apk 脚本,CI 报 + # `npm error Missing script: "build:apk"`(tag v3.2.0-beta.1 首跑即此)。 + cd .. + npm run build:apk + cd android + ./gradlew assembleRelease --no-daemon + + - name: 打包并生成校验和 + run: | + # 版本号取 mobile/package.json(与 gradle 的 versionName 同源), + # 产物名与历史 Release 资产保持一致:Agent2API-<版本>.apk + # 用 node 读而不是 jq:node 是这套流程本来就有的依赖, + # ubuntu runner 虽然带 jq,但少一个工具就少一处环境假设 + version="$(node -p "require('./desktop-tauri/mobile/package.json').version")" + src="desktop-tauri/mobile/android/app/build/outputs/apk/release/app-release.apk" + [ -f "$src" ] || { echo "::error::没有产出 $src"; exit 1; } + + mkdir -p dist + cp "$src" "dist/Agent2API-$version.apk" + # 与 linux-server / windows-server 包同格式(sha256 + 两个空格 + 文件名) + ( cd dist && sha256sum "Agent2API-$version.apk" > "Agent2API-$version.apk.sha256" ) + + ls -lh dist + cat "dist/Agent2API-$version.apk.sha256" + + - name: 上传产物 + uses: actions/upload-artifact@v7 + with: + name: android-apk + path: dist/* + if-no-files-found: error + + # ── iOS IPA(Capacitor)─────────────────────────────────────── + # 手机端的 iOS 安装包。与 android job 共用同一份 Web 产物 + # (`npm run build:native` → `android-www/` → `cap sync ios`)。 + # + # ── 为什么必须 macos-26 ────────────────────────────────────── + # Capacitor 8 要求 Xcode 26.0+(见官方支持策略),而 Xcode 只能在 macOS 上 + # 跑。`macos-26` 镜像默认带 Xcode 26.x;`macos-latest` 目前也已迁到 26, + # 但显式写死版本号,避免 GitHub 换默认镜像时构建突然失败。 + # + # ── 签名策略:默认出**未签名** IPA ──────────────────────────── + # 这里不碰 App Store 分发(那需要 Apple Developer Program、证书、描述文件 + # 与 App Store Connect 凭据,都得用户自己配)。默认产出的是**未签名 .ipa**: + # 可以用 AltStore / SideStore / Sideloadly 拿自己的 Apple ID 重签安装。 + # + # 要出已签名包时,在仓库 Secrets 里配齐下面四个,job 会自动改走签名分支: + # IOS_CERTIFICATE_BASE64 .p12 分发证书的 base64 + # IOS_CERTIFICATE_PASSWORD .p12 的密码 + # IOS_PROVISIONING_PROFILE .mobileprovision 的 base64 + # IOS_TEAM_ID Apple Developer Team ID + # 生成 base64:base64 -i cert.p12 | tr -d '\n' + # 缺任何一个都退回未签名包,并在日志里明确说明,不静默产出半成品。 + # + # ── 版本号从哪来 ──────────────────────────────────────────── + # 与 APK 同源:`desktop-tauri/mobile/package.json` 的 version。 + # xcodebuild 用 MARKETING_VERSION / CURRENT_PROJECT_VERSION 覆盖 pbxproj 里的 + # 1.0 / 1,保证与 tag 逐字一致(beta 后缀也能带上)。CFBundleVersion 只允许 + # 数字与点,所以预发布后缀只进 MARKETING_VERSION,build number 用折算整数。 + ios: + runs-on: macos-26 + steps: + - uses: actions/checkout@v7 + + - name: 安装 Node + uses: actions/setup-node@v7 + with: + node-version: '22' + cache: npm + # 与 android job 同因:mobile 的构建依赖 ui-kit 的源码与样式 + cache-dependency-path: | + desktop-tauri/ui-kit/package-lock.json + desktop-tauri/mobile/package-lock.json + + - name: 确认 Xcode 版本 + run: | + xcodebuild -version + # Capacitor 8 要求 26.0+;低于这个版本直接失败,不产出装不上的包 + major="$(xcodebuild -version | head -1 | sed -E 's/Xcode ([0-9]+).*/\1/')" + if [ "$major" -lt 26 ]; then + echo "::error::需要 Xcode 26.0+(Capacitor 8 的要求),当前是 $major" + exit 1 + fi + + - name: 安装依赖 + run: | + npm --prefix desktop-tauri/ui-kit ci + npm --prefix desktop-tauri/mobile ci + + # 先出 Web 产物再同步进原生工程: + # tsc --noEmit → vite build --mode apk(产物落 android-www/) + # → cap sync ios(拷进 ios/App/App/public,并重写 CapApp-SPM/Package.swift) + - name: 构建 Web 产物并同步到 iOS 工程 + run: cd desktop-tauri/mobile && npm run build:ios + + - name: 解析版本号 + id: version + run: | + cd desktop-tauri/mobile + version="$(node -p "require('./package.json').version")" + # 3.1.0-beta.1 → MARKETING_VERSION 保留完整后缀;build number 用三段折算 + build="$(node -p " + const v = require('./package.json').version.split('-')[0].split('.'); + String(Number(v[0]) * 10000 + Number(v[1]) * 100 + Number(v[2] || 0)); + ")" + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "build=$build" >> "$GITHUB_OUTPUT" + echo "版本: $version / build $build" + + # ── 可选签名:四个 secret 齐了才走 ────────────────────── + - name: 还原 iOS 签名材料 + id: signing + env: + IOS_CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE_BASE64 }} + IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }} + IOS_PROVISIONING_PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE }} + IOS_TEAM_ID: ${{ secrets.IOS_TEAM_ID }} + run: | + if [ -z "$IOS_CERTIFICATE_BASE64" ] || [ -z "$IOS_CERTIFICATE_PASSWORD" ] \ + || [ -z "$IOS_PROVISIONING_PROFILE" ] || [ -z "$IOS_TEAM_ID" ]; then + echo "::notice::未配置 iOS 签名 secret,产出未签名 IPA(可用 AltStore / SideStore 重签安装)" + echo "signed=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # runner 是干净的,导入的证书要有个 keychain 落点 + KEYCHAIN="$RUNNER_TEMP/signing.keychain-db" + security create-keychain -p "" "$KEYCHAIN" + security set-keychain-settings -lut 21600 "$KEYCHAIN" + security unlock-keychain -p "" "$KEYCHAIN" + + echo "$IOS_CERTIFICATE_BASE64" | base64 -d > "$RUNNER_TEMP/cert.p12" + security import "$RUNNER_TEMP/cert.p12" -k "$KEYCHAIN" \ + -P "$IOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple: -k "" "$KEYCHAIN" >/dev/null + security list-keychains -d user -s "$KEYCHAIN" $(security list-keychains -d user | tr -d '"') + + mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" + echo "$IOS_PROVISIONING_PROFILE" | base64 -d \ + > "$HOME/Library/MobileDevice/Provisioning Profiles/profile.mobileprovision" + + echo "signed=true" >> "$GITHUB_OUTPUT" + + - name: 构建未签名 Archive + run: | + cd desktop-tauri/mobile/ios/App + + # CODE_SIGNING_ALLOWED=NO 让 xcodebuild 不去找证书 —— 签名在下一步 + # 单独处理,这样「有没有配 secret」不会影响这一步能否跑通。 + xcodebuild \ + -project App.xcodeproj \ + -scheme App \ + -configuration Release \ + -sdk iphoneos \ + -destination 'generic/platform=iOS' \ + -archivePath "$RUNNER_TEMP/App.xcarchive" \ + MARKETING_VERSION="${{ steps.version.outputs.version }}" \ + CURRENT_PROJECT_VERSION="${{ steps.version.outputs.build }}" \ + CODE_SIGNING_ALLOWED=NO \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGN_IDENTITY="" \ + archive + + app="$RUNNER_TEMP/App.xcarchive/Products/Applications/App.app" + [ -d "$app" ] || { echo "::error::Archive 里没有 App.app"; exit 1; } + + - name: 打包 IPA(签名 / 未签名两条路) + run: | + version="${{ steps.version.outputs.version }}" + app="$RUNNER_TEMP/App.xcarchive/Products/Applications/App.app" + mkdir -p dist + + if [ "${{ steps.signing.outputs.signed }}" = "true" ]; then + # 已签名:走官方导出流程(会重签并嵌入描述文件) + cat > "$RUNNER_TEMP/ExportOptions.plist" < + + + + method + app-store-connect + teamID + ${{ secrets.IOS_TEAM_ID }} + signingStyle + manual + uploadSymbols + + + + PLIST + + xcodebuild -exportArchive \ + -archivePath "$RUNNER_TEMP/App.xcarchive" \ + -exportOptionsPlist "$RUNNER_TEMP/ExportOptions.plist" \ + -exportPath "$RUNNER_TEMP/export" + + src="$(find "$RUNNER_TEMP/export" -maxdepth 1 -name '*.ipa' | head -1)" + [ -n "$src" ] || { echo "::error::导出目录里没有 .ipa"; exit 1; } + cp "$src" "dist/Agent2API-$version.ipa" + echo "→ 已导出签名 IPA" + else + # 未签名:手工拼 Payload 结构(AltStore / SideStore 能直接重签这个形态) + mkdir -p "$RUNNER_TEMP/Payload" + cp -R "$app" "$RUNNER_TEMP/Payload/" + ( cd "$RUNNER_TEMP" && zip -qry "$GITHUB_WORKSPACE/dist/Agent2API-$version.ipa" Payload ) + echo "→ 已打包未签名 IPA" + fi + + ( cd dist && shasum -a 256 "Agent2API-$version.ipa" > "Agent2API-$version.ipa.sha256" ) + ls -lh dist + cat "dist/Agent2API-$version.ipa.sha256" + + - name: 上传产物 + uses: actions/upload-artifact@v7 + with: + name: ios-ipa + path: dist/* + if-no-files-found: error diff --git a/.github/workflows/frontend-artifacts.yml b/.github/workflows/frontend-artifacts.yml index 60fdc243..4f96d63c 100644 --- a/.github/workflows/frontend-artifacts.yml +++ b/.github/workflows/frontend-artifacts.yml @@ -3,10 +3,11 @@ name: frontend-artifacts # 校验「入库的前端构建产物」与源码同源。 # # ── 为什么需要单独一道闸 ────────────────────────────────────── -# 本仓库把构建产物**入库**(`ui/islands/ui.js`、`ui/islands/ui.css`、`ui/i18n/**`), -# 而 Tauri 打包的正是 `ui/` 这个静态目录(见 tauri.conf.json 的 frontendDist)—— -# 也就是说上线的字节就是仓库里那几份。于是「只改源码不重新构建」与「手改产物」 -# 都能一路合进 beta 并被打进安装包,而常规 review 看不出 1.1 MB 压缩文件里少了什么。 +# 本仓库把构建产物**入库**(`ui/islands/ui.js`、`ui/islands/ui.css`、`ui/i18n/**`、 +# `ui/m/**`),而 Tauri 打包的正是 `ui/` 这个静态目录(见 tauri.conf.json 的 +# frontendDist)—— 也就是说上线的字节就是仓库里那几份。于是「只改源码不重新构建」 +# 与「手改产物」都能一路合进 beta 并被打进安装包,而常规 review 看不出 1.1 MB +# 压缩文件里少了什么。 # # 2026-10 合并 #228 / #235 时踩到过这个面:两个 PR 各自重建过产物,在 `ui.js` 上 # 必然撞车,而**取任何一边都会静默回退另一边的 UI 改动**(那一次是靠人工核对 @@ -50,6 +51,7 @@ jobs: cache-dependency-path: | desktop-tauri/ui-kit/package-lock.json desktop-tauri/ui-islands/package-lock.json + desktop-tauri/mobile/package-lock.json desktop-tauri/i18n/package-lock.json # ui-kit 也必须装,不是可选项:岛构建以**源码**方式接入它(vite alias 指到 @@ -61,12 +63,15 @@ jobs: run: | npm --prefix desktop-tauri/ui-kit ci npm --prefix desktop-tauri/ui-islands ci + npm --prefix desktop-tauri/mobile ci npm --prefix desktop-tauri/i18n ci - name: 重建产物 run: | # 岛产物:build 脚本是 `tsc --noEmit && vite build`,顺带做类型检查 npm --prefix desktop-tauri/ui-islands run build + # 手机端产物:同样带类型检查,产物落 ui/m/ + npm --prefix desktop-tauri/mobile run build # 多语言词典产物 npm --prefix desktop-tauri/i18n run build @@ -89,6 +94,7 @@ jobs: echo echo "本地修复方式:" echo " npm --prefix desktop-tauri/ui-islands run build" + echo " npm --prefix desktop-tauri/mobile run build" echo " node desktop-tauri/i18n/build.mjs" - echo "(不要手动编辑 ui/islands/ui.js 或 ui/i18n/** —— 它们是产物,重建即可)" + echo "(不要手动编辑 ui/islands/ui.js、ui/m/** 或 ui/i18n/** —— 它们是产物,重建即可)" exit 1 diff --git a/.gitignore b/.gitignore index f988fbbe..ed5a29ef 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ desktop-tauri/src-tauri/gen/schemas/ .zcode/ .verify/ prototype/ +.codeartsdoer/ # ZCode 的忽略配置文件本身(本机工具配置,不入库) .zcodeignore @@ -24,6 +25,20 @@ desktop-tauri/web logs/ desktop-tauri/window-shot.png +# 逆向取证工作区(181MB / 2325 个文件,且含**真实凭证**) +# marvis-cred.json 里是 mv_ 令牌 + openid,marvis-aksk.json 是 AKSK, +# apiKeys.bak.json 是 API Key,还有多份 token-dump / headers 抓包。 +# 本仓库是公开的 ⇒ 一律不入库。要留证据请脱敏后写进 docs/ 或 providers/<家>/ 的模块头。 +_recon/ + +# JVM / JIT 崩溃转储(cargo test OOM 时留下的大文件,与仓库内容无关) +hs_err_pid*.log +replay_pid*.log + +# curl 调试残留(-D 响应头 / -c cookie jar),含会话令牌,不入库 +desktop-tauri/src-tauri/h1.txt +desktop-tauri/src-tauri/c1.txt + # 发布说明草稿只在本机留存,不入库;docs/ 下的其它文档正常入库 /docs/release-notes/ @@ -33,3 +48,40 @@ dist/ # Docker 部署:密钥与运行数据不入库 .env data/ + +# ── 手机 APK(Capacitor)────────────────────────────────────── +# android/ 是 `npx cap add android` 生成的原生工程,需要入库: +# 图标、Manifest、网络安全策略都是手改过的,重新生成会丢。 +# 但下面这些是构建机本地状态,不该进仓库: +desktop-tauri/mobile/android/.gradle/ +desktop-tauri/mobile/android/build/ +desktop-tauri/mobile/android/app/build/ +desktop-tauri/mobile/android/local.properties +desktop-tauri/mobile/android/.idea/ +desktop-tauri/mobile/android/capacitor-cordova-android-plugins/build/ +# 构建中间产物(android-www 由 npm run build:apk 生成,再同步进 android/) +desktop-tauri/mobile/android-www/ + +# APK 签名密钥与配套密码:**不入库**。 +# 拿着密钥 + 密码就能签一个同包名的假 APK 覆盖安装;本仓库是公开的, +# 所以两者只留在打包机上。新机器打 release 包时按 +# desktop-tauri/mobile/android/keystore.properties.example 建一份 +# keystore.properties,再把密钥放回 android/ 目录。 +*.jks +*.keystore +desktop-tauri/mobile/android/keystore.properties + +# 本地测试跑出来的运行数据(数据库、加密密钥、日志) +.agenttest2/ + +# ── iOS(Capacitor)──────────────────────────────────────────── +# ios/ 是 `npx cap add ios` 生成的原生工程,需要入库: +# Info.plist(ATS / 本地网络用途)、AppIcon、Splash 都是手改过的, +# 重新生成会丢。 +# 构建机本地状态与生成物不入库(ios/.gitignore 已覆盖大部分,这里补两条 +# 全局的,避免换机器时把 Xcode 的派生数据带进来): +desktop-tauri/mobile/ios/App/build/ +desktop-tauri/mobile/ios/DerivedData/ + +# 本地打包产物(.workbuddy 外的手工验包,不入库) +/dist-local/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..1e629288 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,18 @@ +# AGENTS.md + +> **涉及分支、发版、版本号的操作,一律按 Skill `release-flow` 执行:** +> **`.agents/skills/release-flow/SKILL.md`** —— 动手前先读它,细则一律以该文件为准,本文件只作入口指路。 +> +> 覆盖范围:切分支 / 给 PR 选 base / 合并 PR / 回复 Issue、PR / 改版本号 / 写更新日志 / +> 打 tag / 挂 GitHub Release / 发 beta 预发布版 / 发版验收 / 排查发版失败。 + +## 两条红线(先问人,不得自行推进) + +1. **发版**:改版本号、推 `main` / `beta`、打 tag、挂 Release 之前,先把版本号与「这次要发什么」讲清楚,等用户确认;**未经确认不得推任何分支或 tag** —— tag 一旦推出去就会触发构建、产出公开的安装包与镜像,收不回来。 +2. **更新日志**:写好的日志(= 发布提交信息 = Release 正文)必须先给用户逐条过目;按反馈改完**再确认一次**才能提交。 + +其余细则(分支落脚点、署名与 AI 落款、六处版本号、正式版 / beta 完整流程、验收清单、已知坑与排查)见上述 Skill。 + +## 文档落点 + +安装 / 部署 / 升级 / 排错说明只在 `docs/install.md`(另有 5 份译文 `docs/install.{en,zh-Hant,ja,ko,pt-BR}.md`,六份一一对应)里维护;各语言 README 只留「快速开始」速查表 + 一个指向安装文档的链接,**不要再把安装细节写回 README**。产物名、端口、环境变量、安装脚本参数有改动时,六份一起改。 diff --git a/LICENSE b/LICENSE index d55c3678..b6ef4e4a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 aimod-cc (https://github.com/aimod-cc) +Copyright (c) 2026 Agent2API (https://github.com/Agent2API) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index a06edb0d..d3ba3623 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,12 @@ [简体中文](./README.md) | [English](./docs/README.en.md) | [繁體中文](./docs/README.zh-Hant.md) | [日本語](./docs/README.ja.md) | [한국어](./docs/README.ko.md) | [Português (BR)](./docs/README.pt-BR.md) [![license](https://img.shields.io/badge/license-MIT%20%2B%20附加声明-green)](./LICENSE) -[![release](https://img.shields.io/github/v/release/aimod-cc/agent2api?include_prereleases)](https://github.com/aimod-cc/agent2api/releases/latest) +[![release](https://img.shields.io/github/v/release/Agent2API/agent2api?include_prereleases)](https://github.com/Agent2API/agent2api/releases/latest) [![docker pulls](https://img.shields.io/docker/pulls/aimodcc/agent2api)](https://hub.docker.com/r/aimodcc/agent2api) -[![stars](https://img.shields.io/github/stars/aimod-cc/agent2api?style=flat&color=yellow)](https://github.com/aimod-cc/agent2api/stargazers) -[![platform](https://img.shields.io/badge/platform-Windows%20%7C%20Docker-blue)](https://github.com/aimod-cc/agent2api/releases/latest) +[![stars](https://img.shields.io/github/stars/Agent2API/agent2api?style=flat&color=yellow)](https://github.com/Agent2API/agent2api/stargazers) +[![platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux%20%7C%20Docker-blue)](https://github.com/Agent2API/agent2api/releases/latest) -[✨ 核心特性](#-核心特性) • [💬 社区与支持](#-社区与支持) • [🤝 支持的提供商](#-支持的提供商) • [🚀 快速开始](#-快速开始) • [🐳 Docker 部署](#-docker-部署) • [📸 界面预览](#-界面预览) • [🔧 开发与构建](#-开发与构建) • [📜 使用声明](#-使用声明) +[✨ 核心特性](#-核心特性) • [💬 社区与支持](#-社区与支持) • [🤝 支持的提供商](#-支持的提供商) • [🚀 快速开始](#-快速开始) • [📦 安装与部署](./docs/install.md) • [📸 界面预览](#-界面预览) • [🔧 开发与构建](#-开发与构建) • [📜 使用声明](#-使用声明) @@ -23,7 +23,7 @@ Agent2API 把多家 AI 桌面客户端里已登录的账号接到一起,共用 ## ✨ 核心特性 - 🔌 **OpenAI 兼容** —— 客户端填个 `base_url` 就能用,Chat / Responses / Messages 三种协议都支持 -- 🏪 **内置 17 家提供商** —— WorkBuddy、小浣熊、CatPaw、Qoder、Trae 等开箱即用,也支持自定义提供商 +- 🏪 **内置 24 家提供商** —— WorkBuddy、小浣熊、CatPaw、Qoder、Trae 等开箱即用,也支持自定义提供商 - 🔁 **凭证自动续期** —— Token 到期自动换新,临期凭证可批量刷新,模型目录也不会过期 - 🪂 **多账号轮转** —— 按优先级排队,429 自动换账号,限额冷却期内不空等 - 🛡️ **出网代理** —— 每个账号可配独立出口,出站指纹按官方客户端伪装 @@ -31,7 +31,7 @@ Agent2API 把多家 AI 桌面客户端里已登录的账号接到一起,共用 - 📊 **请求报表** —— 请求量、成功率、Token 消耗一览,还有缓存命中率与 365 天热力图 - ⏰ **定时任务** —— 自动签到、凭证维护、模型目录刷新,开关与间隔在一页里管理 - 💰 **余额与用量** —— 账号行内直接看余额与用量,支持跨账号并发查询 -- 🖥️ **两种部署** —— 桌面端托盘常驻、免装 Node;Docker 版面板与网关同端口,界面六语 +- 🖥️ **三种形态** —— 桌面端(Windows / macOS / Linux,托盘常驻)、服务端(Windows / Linux,无界面,注册成系统服务)、Docker(面板与网关同端口,amd64 / arm64);都不需要装 Node,界面六语 ## ❤️ 赞助商 @@ -48,7 +48,9 @@ Agent2API 把多家 AI 桌面客户端里已登录的账号接到一起,共用 ## 💬 社区与支持 -- **QQ 群**:Agent2API 交流群,群号 `1127656296` —— 在 QQ 里搜索群号即可加入,使用答疑与问题反馈都在这里。 +- **QQ 群**:Agent2API 交流群 —— **群 1(`1127656296`)即将满员,请优先加群 2(`1130894674`)**;在 QQ 里搜索群号即可加入。 +- **Telegram 群**: +- **Discord 群**: - **💖 赞赏**:如果 Agent2API 帮到了你,欢迎[请作者喝杯咖啡](docs/SPONSOR.md);赞赏完全自愿,不解锁任何功能。 - **🤝 赞助 / 商务合作**:想赞助本项目,或想让你的产品出现在这里?发邮件到 [541675139@qq.com](mailto:541675139@qq.com)。 @@ -73,11 +75,21 @@ Agent2API 把多家 AI 桌面客户端里已登录的账号接到一起,共用 | KukuAI(百度文库) | ✓ | ✗ | ✓ | ✓ | ✓ | — | | MonkeyCode(长亭科技,国内版 / 国际版) | ✓ | ✗ | ✓ | — | — | — | | Command Code | ✓ | ✗ | ✓ | — | — | — | -| Antigravity(Google,Gemini) | ✓ | ✓ | ✓ | — | — | — | +| Antigravity(Google,Gemini / Claude / GPT-OSS) | ✓ | ✓ | ✓ | — | — | — | | OfficeAce(华为云果办 / OfficeClaw) | ✓ | ✓ | ✓ | ✓ | ✓ | — | +| OrcaTerm(腾讯云遨驰终端) | ✓ | ✓ | ✓ | ✓ | — | — | +| 智谱清言 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| ModelScope(魔搭,国内版 / 国际版) | ✓ | ✗ | ✓ | ✓ | ✓ | — | +| AtomCode(AtomGit / GitCode) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| LobsterAI(网易有道「有道龙虾」) | ✓ | ✓ | ✓ | ✓ | ✓ | — | +| 马维斯 Marvis(腾讯) | ✓ | ✗ | ✗ | ✓ | ✗ | — | +| MiniMax Code(国内版 / 国际版) | ✓ | ✓ | ✓ | ✓ | ✓ | — | +| Coze 扣子(字节跳动) | ✓ | ✗ | ✓ | ✓ | ✓ | ✓ | | 自定义提供商 | ✓ | — | ✓ | — | — | — | > 注:模型列表的 ✓ 表示支持在线自动拉取,部分平台另有内置静态表兜底;ZCode 仅有内置静态表。Token 续期列 ✗ 表示该平台没有可用的续期接口(账号在有效期内不受影响)。Qoder 签到仅中国版可用。 +> +> **智谱清言**是本项目**唯一支持「访客模式」**的一家:不需要登录,点一下就能换到一份可直接对话的凭证(免费额度由上游按设备发放)。代价是访客账号没有积分体系 —— 上表里的「余额查询 / 签到 / 领取类」,以及**任务中心的新手任务与每日任务**,都只对**登录账号**有效(粘贴令牌或导入客户端登录态),访客会被如实跳过。 `/v1/chat/completions`、`/v1/responses`、`/v1/messages`(含 `count_tokens`)与 `/v1/models` 对所有平台一视同仁;模型映射、全局优先级队列、429 降级、出网代理、出站指纹脱敏与请求报表同样全平台通用。 @@ -87,85 +99,38 @@ Agent2API 把多家 AI 桌面客户端里已登录的账号接到一起,共用 ## 🚀 快速开始 -从 [Releases](https://github.com/aimod-cc/agent2api/releases/latest) 下载安装包(NSIS,简体中文,默认装到 `C:\Program Files\Agent2API`,安装时需要管理员授权),安装后启动即可,**无需安装 Node 或任何其它运行时**。 - -1. 首次启动即在应用进程内启动本机网关(端口 3065)并打开主窗口;若检测到旧版本的数据目录或数据文件,会弹窗提示迁移,按指引操作即可。 -2. 点「账号」页的「添加账号」,选提供商后按弹窗提示完成登录或填写凭证即可(方式以弹窗为准:网页登录 / 手机验证码 / 粘贴凭证 / 导入本机登录态)。 -3. 把 OpenAI 客户端的 `base_url` 填成 `http://127.0.0.1:3065/v1`,`api_key` 随便填(例如 `sk-local`,未启用鉴权时服务端不校验)。 +三种形态,按你的环境选一个;**完整的安装、升级、卸载与排错见 [安装与部署](docs/install.md)**。 -关闭窗口默认最小化到托盘,网关继续在后台转发;彻底退出请在托盘图标右键选「退出」。 +| 形态 | 产物 | 界面 | +| --- | --- | --- | +| **桌面端**(Windows / macOS / Linux) | `Agent2API_<版本>_x64-setup.exe`、`_universal.dmg`、`_amd64.deb`、`_amd64.AppImage` | 应用窗口 | +| **服务端**(Windows Server / Linux,无 GUI) | `agent2api-server-windows-amd64.zip`、`agent2api-server-linux-.tar.gz` | 浏览器面板 | +| **Docker** | `aimodcc/agent2api:latest`(amd64 / arm64) | 浏览器面板 | -### 验证 +**桌面端**:装完启动 → 「账号」页添加账号 → 客户端 `base_url` 填 `http://127.0.0.1:3065/v1`(`api_key` 随便填,例如 `sk-local`)。无需安装 Node 或任何其它运行时,网关跑在应用进程内,托盘常驻。 -网关起来后,用 curl 确认联通性(`model` 填 `GET /v1/models` 里任一实际存在的名字): +**服务端**(无图形界面,注册成系统服务、开机自启): ```bash -curl http://127.0.0.1:3065/health -curl http://127.0.0.1:3065/v1/models - -curl http://127.0.0.1:3065/v1/chat/completions \ - -H 'Content-Type: application/json' \ - -d '{"model":"deepseek-v4.1-flash","messages":[{"role":"user","content":"你好"}],"stream":true}' +# Linux +tar -xzf agent2api-server-linux-amd64.tar.gz && cd agent2api-server-linux-amd64 && sudo bash install.sh ``` -客户端接入示例(Python SDK): - -```python -from openai import OpenAI - -client = OpenAI(base_url="http://127.0.0.1:3065/v1", api_key="sk-local") -resp = client.chat.completions.create( - model="deepseek-v4.1-flash", # 名单里有哪家就是哪家,见 GET /v1/models - messages=[{"role": "user", "content": "你好"}], -) -print(resp.choices[0].message.content) +```powershell +# Windows Server(管理员 PowerShell) +Expand-Archive agent2api-server-windows-amd64.zip -DestinationPath C:\agent2api-pkg +cd C:\agent2api-pkg; .\install-service.ps1 ``` -**浏览器页面**(自建 Web UI 等)用 `fetch` 直连会被跨源预检拒绝——网关默认不应答 CORS,预检落到 API Key 校验上得 401。两种解法:① 设置页「安全 → 网关跨域访问」打开(来源 `*`;此时任何网页都能借本机网关打上游,建议同时配「网关 Key」);② 页面与网关同源——本地静态服务托管页面并把 `/v1` 反代到 `127.0.0.1:3065`。 - -### 局域网访问 - -默认只监听 `127.0.0.1`。开启「设置 → 通用 → 局域网访问」后改听所有网卡,局域网设备把 API 地址指向本机 IP 即可共用(界面会给出完整地址)。出于安全,开启前必须注册面板管理员:管理接口要求管理员会话或网关 Key,无可用 Key 时转发接口同样拒绝服务(开启流程会自动补一把「默认」Key);也可选择同时开放网页面板(默认关闭)。改动重启生效。 - -## 🐳 Docker 部署 +**Docker**: ```bash -docker run -d --name agent2api --restart unless-stopped \ - -p 3065:3065 -v ./data:/data \ - aimodcc/agent2api:latest +docker run -d --name agent2api --restart unless-stopped -p 3065:3065 -v ./data:/data aimodcc/agent2api:latest ``` -浏览器打开 `http://<主机>:3065`,首次进入会引导**注册管理员账号**;登录后在「网关 Key」页建一把 API Key 给客户端用 —— `http://<主机>:3065/v1` 即 OpenAI 兼容端点(未建 Key 前拒绝转发)。状态(SQLite 库 / 配置 / 日志)都在 `./data` 一个卷里。 +三种形态装完后都打开面板(`http://127.0.0.1:3065/`,服务端与 Docker 换成主机 IP)注册管理员账号,再在「网关 Key」页建一把 Key。连通性自测:`curl http://127.0.0.1:3065/health` -compose 用户(amd64 / arm64 都有镜像): - -```yaml -services: - agent2api: - image: aimodcc/agent2api:latest - container_name: agent2api - restart: unless-stopped - ports: - - "3065:3065" - volumes: - - ./data:/data -``` - -环境变量(都可选): - -| 变量 | 说明 | -| --- | --- | -| `AGENT2API_ADMIN_USER` + `AGENT2API_ADMIN_PASSWORD` | 预置管理员账号密码(密码明文填,启动时自动转哈希);不填走面板注册 | -| `AGENT2API_PANEL_PORT` | 面板分端口:设后面板(界面 + `/api/*`)单独监听该端口,公网只映射主端口即可把管理面留在内网(面板端口绑回环,写 `127.0.0.1:3066:3066`) | -| `AGENT2API_HOST` / `AGENT2API_PROXY_PORT` | 监听地址(默认 `0.0.0.0`)/ 端口(默认 `3065`) | -| `AGENT2API_ALLOW_NO_KEY` | 置 `1` 关闭 fail-closed(未配 Key 也放行 `/v1`,仅限纯内网) | -| `AGENT2API_CAPTCHA_ENABLED` | 登录页人机验证:默认 `1` 开启,`0` 关闭 | -| `AGENT2API_SELF_UPDATE` | 容器内的更新入口:默认 `1` 开启(面板可「下载并更新」);置 `0` 关闭,只走手动升级 | -| `WORKBUDDY_UPDATE_REPO` + `WORKBUDDY_UPDATE_API_BASE` | 进阶:把「检查更新 / 下载更新」换成自建源。前者换仓库(默认跟上游 `aimod-cc/agent2api`,fork 后可改),后者换 API 根(默认 `https://api.github.com`,需返回与 GitHub 同形的 release JSON)。配了自建源后,下载白名单只额外放行**该源自己的域名**(按源的 scheme:配 `http://` 就允许明文下载,仅建议本地演练/内网镜像用)| - -从源码构建:克隆本仓库后 `docker compose up -d --build`(镜像里只有网关与面板,不含 Rust 工具链)。 - -**网页端功能差异**(源于「没有本机桌面客户端」):网页登录(WorkBuddy / Qoder / Cline)、手机验证码、粘贴凭证完全可用;需要本机回调的网页登录(AutoClaw / CatPaw / Accio / CodeArts / Trae)与「导入本机登录态」不可用,请改用粘贴凭证。 +**手机端**:浏览器打开 `http://<网关IP>:3065/m` 即可(可「添加到主屏幕」变成全屏 PWA);想要独立图标就装 Release 里的 `Agent2API-<版本>.apk`(Android)或 `Agent2API-<版本>.ipa`(iOS,未签名,需自行重签)。细节见[安装与部署 → 手机端](docs/install.md#手机端)。 ## 📸 界面预览 @@ -201,6 +166,7 @@ services: - Rust >= 1.77 与 Tauri 2 工具链(编译桌面端本体;Windows 上还需 WebView2 运行时) - Node.js >= 18.17(只用于 `npm run tauri:*` 与前端构建脚本,桌面端运行时不依赖 Node) +- 目标平台:Windows / macOS / Linux 桌面端、Windows / Linux 服务端(无 GUI)、Docker 服务端 ### 常用脚本 @@ -212,13 +178,15 @@ npm run tauri:build # 构建桌面端安装包 npm run build:icon # 生成图标源图(改图标设计后执行,再跑 tauri icon) ``` -打包产物为 `target/release/bundle/nsis/Agent2API_<版本>_x64-setup.exe`(当前约 3.0 MB;`src-tauri/.cargo/config.toml` 把 cargo 的 `target-dir` 指到了项目根的 `target/`)。 +打包产物为 `target/release/bundle/nsis/Agent2API_<版本>_x64-setup.exe`(3.0 版约 8.6 MB;`src-tauri/.cargo/config.toml` 把 cargo 的 `target-dir` 指到了项目根的 `target/`)。 + +macOS 的 `.dmg` **只能在 macOS 上构建**(`npm --prefix desktop-tauri run build -- --target universal-apple-darwin`),无法从 Windows 交叉编译,所以正式产物由 CI 的 `build` 工作流产(`macos-latest` + 双目标合并);Docker 镜像与容器内更新用的服务端包由同一工作流的 `linux-server` job 出(复用 Dockerfile 的编译路径,包内可执行文件与镜像同源同工具链)。 ## 📜 使用声明 ### 仅供学习与交流 -本项目是一个用于学习 HTTP 反向代理、SSE 流式透传、多上游协议适配与桌面端打包(Tauri)等技术主题的实践项目,**仅供个人学习与研究使用**。它不是官方产品,与腾讯公司及 WorkBuddy / CodeBuddy、美团及 CatPaw、商汤及小浣熊、智谱及 AutoClaw / autoglm、阿里巴巴及 Qoder / Accio、华为云及 CodeArts / OfficeAce、字节跳动及 Trae、科大讯飞及 Loomy、百度文库及 KukuAI、长亭科技及 MonkeyCode、Command Code、Google 及 Antigravity 均无任何关联,未获得其授权、认可或赞助。 +本项目是一个用于学习 HTTP 反向代理、SSE 流式透传、多上游协议适配与桌面端打包(Tauri)等技术主题的实践项目,**仅供个人学习与研究使用**。它不是官方产品,与腾讯公司及 WorkBuddy / CodeBuddy、美团及 CatPaw、商汤及小浣熊、智谱及 AutoClaw / autoglm、阿里巴巴及 Qoder / Accio、华为云及 CodeArts / OfficeAce、字节跳动及 Trae、科大讯飞及 Loomy、百度文库及 KukuAI、长亭科技及 MonkeyCode、Command Code、Google 及 Antigravity、阿里巴巴及 ModelScope(魔搭)、GitCode / AtomGit 及 AtomCode、网易有道及 LobsterAI(有道龙虾)、腾讯及马维斯 Marvis、MiniMax 及 MiniMax Code 均无任何关联,未获得其授权、认可或赞助。 ### 关于反向代理行为 @@ -244,17 +212,17 @@ npm run build:icon # 生成图标源图(改图标设计后执行,再 ## ⭐ Star History - + - - - Star History Chart + + + Star History Chart
-**[下载最新版](https://github.com/aimod-cc/agent2api/releases/latest)** • **[问题反馈](https://github.com/aimod-cc/agent2api/issues)** +**[下载最新版](https://github.com/Agent2API/agent2api/releases/latest)** • **[问题反馈](https://github.com/Agent2API/agent2api/issues)** 如果这个项目对你有帮助,欢迎点一个 ⭐️ Star! diff --git a/agent.md b/agent.md deleted file mode 100644 index 13ce1ad0..00000000 --- a/agent.md +++ /dev/null @@ -1,211 +0,0 @@ -# AGENT.md - -> 本文档面向维护者与 AI 代理:Agent2API(workbuddy)桌面端 + Docker 镜像的**标准发版流程**。 -> 所有发布动作都由 `.github/workflows/` 下的两个工作流自动完成,人工只负责「提交、打 tag、挂 GitHub Release、验收」。 -> beta 版(预发布)是同一套流程的变体,完整步骤见第 4 节末尾。 - -## 0. 两条人工红线(AI 代理必须停下等确认) - -这两件事**必须由人先审过、明确点头**,代理才能继续执行。代理可以准备草稿、可以列依据,但不能自己拍板往下走: - -1. **发版**:进入发版流程(改版本号、建发布提交、推 main / beta、打 tag、挂 Release)之前,先把版本号与「这次要发什么」讲清楚,等用户确认。**未经确认不得推任何分支或 tag** —— tag 一旦推出去就会触发两个工作流、产出公开的安装包与镜像,收不回来。 -2. **提交日志**:写好的更新日志(发布提交的提交信息,同时也是 GitHub Release 正文)必须先给用户逐条过目,等明确确认后才能提交。用户可以要求改措辞、增删条目、调整分类前缀,代理按反馈改完要**再确认一次**,不能因为「改得不多」就直接提交。 - -两条红线同时适用:一次发版里既要有版本号的确认,也要有日志的确认;用户说「继续」之后才动手。 - -## 1. 分支约定:改动一律先落 beta,发版才动 main - -本仓库有两条长期分支,**日常动作只碰 `beta`,`main` 只在发版时前进**: - -| 分支 | 角色 | 什么时候动它 | -|---|---|---| -| `beta` | 集成分支(默认落脚点) | 日常修改、合并 PR、解冲突 —— 一律先到这里 | -| `main` | 发版分支 | 只在构建发版时:切到 main、把 beta 的改动合进来、打 tag | - -- **合并 PR 时 base 选 `beta`**,不要选 `main`。PR 直接合进 `main`,等于把还没发布的改动提前塞进发版分支;之后还得反过来把 `main` 合回 `beta`,平白多一次合并提交、多一次冲突机会。已经把 PR 开成 base=main 的,先在 PR 页面上把 base 改成 `beta` 再合。 -- **本地改动也提交到 `beta`**(写代码、改文档、解冲突都算),推 `origin/beta`;合并结果的核对、构建自检都在 beta 上做完。 -- **只有正式发版才切到 `main`**:`git switch main` → 把 beta 合进来 → 按第 6、7 节写发布提交、推 main、打 tag;tag 必须落在 `main` 上。发完版后 beta 再把 main 合回来,两边保持一致。(beta 版是这条规则唯一的例外:不切 `main`,tag 落在 `beta` 分支自己身上 —— 见第 4 节末尾。) -- **代理不要自作主张选落脚分支**:用户没说要发版,就不要往 `main` 提交 / 推送,也不要把 PR 的 base 改成 `main`。 - -## 2. 合并 PR:保留原作者署名 - -**合并贡献者的 PR 时,提交作者(author)必须还是贡献者本人。** 这直接决定对方能不能进仓库首页的 Contributors 名单、贡献图里有没有这一笔 —— 署名是贡献者应得的东西,不该因为合并方式丢掉。 - -GitHub 的归属规则只有一条:**提交头里的 author 邮箱,出现在某个账号已验证的邮箱列表里,这次提交就算那个账号的**;否则记成匿名条目,不进 Contributors。补充两条口径:统计只认默认分支(本仓库 `main`),合并提交与空提交不计入。 - -- **用 GitHub 网页上的合并按钮**(`Merge pull request` / `Squash and merge` / `Rebase and merge`):三种都会保留原作者为 commit 的 author;squash 时 PR 里的多位作者会一并进 co-author 名单。这是默认做法。 -- **禁止「本地摘取重提」**:把对方的改动拷过来自己提交(本地 `git merge --squash` 后自己 commit、重抄一遍改动、或改了 author 再提交)都不行 —— 这样 author 是合并者,贡献者的署名在 GitHub 侧完全不可见。本仓库历史上出现过这种情况,别再来一次。 -- **确实要在本地处理时**(有冲突要解、要顺带调整),必须把署名带上:单个原作者用 `git commit --author="原作者 <原作者邮箱>"`;一个提交里有多个人的工作,在提交信息末尾加 trailer `Co-authored-by: 名字 <邮箱>`(GitHub 会把 co-author 一并计入贡献者)。 -- **AI 不计入贡献者署名(特例)**:贡献者提交信息末尾的 `Co-Authored-By: Claude Code <…>` 这类 AI trailer,合并时一律摘除 —— GitHub 会把它渲染成共同作者,进了 `main` 就出现在 Contributors 名单里,而 AI 不是贡献者。摘除只删 trailer 行本身,author 与其余提交信息逐字保留;正文里对 AI 产品的**技术性提及**(「支持 Claude Code 客户端」「Claude 兼容端点」之类)与此无关,不动。摘除动的是已合入分支的历史,趁合并后、发版前尽早做(`git filter-branch --msg-filter` 摘行后 force-push),拖到发版后就得重写 `main`。 -- **邮箱必须能对上账号**:用对方 GitHub 账号里**已验证**的地址;对方想保密就用 GitHub 给的 `ID+用户名@users.noreply.github.com`(在对方账号的 Emails 设置里查)。内网地址、`user@机器名` 这类邮箱永远关联不上账号,提交会被记成匿名。 - -合并后顺手核对一次:`git log --format='%an <%ae>' -1 <合并提交>`,或在 PR 页面看 commit 旁的头像是否指向原作者。 - -## 3. 回复 Issue / PR:标明 AI 来源 - -**由 AI 代理发出(含代笔)的每一条 Issue / PR 回复,末尾都要单独一行写明来源。** 收件人有权知道自己在跟谁说话 —— 这与第 2 节的署名是同一条道理:**谁说的写谁**,看得见落款才谈得上信任与追责。 - -固定落款,逐字照用(别改写成「机器人」「助手」这类含糊说法): - -> 🤖 本条回复由 AI Agent 生成 - -- **位置**:整条回复的最后一行,单独成段(前后各留一个空行);不要塞进正文段落、列表项、引用块或代码块里 —— 引用别人的话时,落款落在自己的话之后。 -- **英文场合**(外部贡献者的 PR / Issue)在中文那行下面再补一行 `Generated by an AI agent.`:说的是同一件事,两边都不省。 -- **代理自己新建的 Issue / PR**,正文末尾同样加。 -- **代笔也算**:人在对话里让代理转达(哪怕逐字)一段话,落款照加 —— 它描述的是「这条回复是谁发出去的」,不是「谁构思的」。 -- 只约束 Issue / PR **回复**(评论、review 意见、关闭 / 合并说明)。提交信息不追加这一行,署名口径见第 2 节。 - -## 4. 版本号约定 - -- 版本号形如 `X.Y.Z`(如 `2.7.2`),日常小版本「递增 0.01」= 末位 +1(如 `2.7.8` → `2.7.9`)。 -- **递增幅度以用户要求为准**:默认用上面的「递增 0.01」;用户另有要求时(如「这次递增 0.1」)一律听用户的,按用户指定的幅度算出新版本号,不要自作主张套用默认幅度。 -- **版本号要同步改 5 处**(缺一处会导致安装包与显示版本对不上): - 1. `package.json`(根) - 2. `desktop-tauri/package.json` - 3. `desktop-tauri/src-tauri/Cargo.toml` - 4. `desktop-tauri/src-tauri/tauri.conf.json` - 5. `desktop-tauri/src-tauri/server/Cargo.toml` -- 应用内「关于」与更新检查读的是 `env!("CARGO_PKG_VERSION")`(第 5 处),改完跑一次 `cargo check` 让 `Cargo.lock` 跟着刷新。 - -### beta 版(预发布):怎么发 - -应用里有「设置 → 软件更新 → 更新设置 → **加入 beta 版**」开关:**打开的人才收到 beta -更新,没打开的人只看正式发布。** beta 版不是另一条流水线,而是上面这套流程的一个变体 —— -唯一的结构差别是「**从 `beta` 分支打带预发布后缀的 tag**」,提交、tag、Release、验收逐字照旧。 - -第 0 节的两条红线同样适用:版本号与更新日志先给用户过目、确认了再动手。 - -```bash -# 1. 在 beta 分支上动手(beta 版不碰 main) -git switch beta && git pull -git status --short # 必须为空,有改动先提交或清理 - -# 2. 五处版本号一起改成「与 tag 逐字一致」的形态: -# tag 打 v3.1.0-beta.1 → 五处都写 3.1.0-beta.1(清单见上面) -cargo check # 顺带刷新 Cargo.lock - -# 3. 提交并推 beta 分支(提交信息 = 这次的 beta 更新日志,格式同第 6 节) -git commit -am "…beta 版日志…" -git push origin beta - -# 4. 打 tag 并推:tag 落在 **beta 分支** 的提交上(只有正式版才要求落 main) -git tag v3.1.0-beta.1 -git push origin v3.1.0-beta.1 - -# 5. 两个工作流跑完后挂 Release(脚本按 tag 里的 `-` 自动标 pre-release) -gh run list --limit 4 # 手动跑 gh 前先设代理,见第 10 节 -bash scripts/release.sh v3.1.0-beta.1 -``` - -验收就三条:Release 显示 **Prerelease**、exe / dmg 附件齐全、Docker Hub 上 `:latest` 的 -Pushed 时间**没有**跟着变(完整清单见第 9 节)。 - -**三处不能错**,每处错了都有明确症状: - -1. **五处版本号漏了后缀**(写成 `3.1.0`):比较是按 semver 做的,运行版本 `3.1.0` 对线上 - tag `3.1.0-beta.1` 会判成「当前比线上新」,**beta 用户永远收不到更新**。五个文件都接受 - 带后缀的版本号(Cargo / npm / tauri.conf.json 都认),照 tag 抄即可。 -2. **Release 漏标 pre-release**:标记就是开关的分界线 —— 正式通道走 GitHub 的 - `/releases/latest`,它排除预发布,只有开了开关的人从 `/releases` 列表里看到 beta。 - **漏标 = beta 被所有正式版用户当成正式更新收到。** `scripts/release.sh` 已按「tag 里带 - `-`」自动标记,重跑收尾时也会把标记纠正回来,所以这一条只要走脚本就不用操心。 -3. **tag 打在 `main` 上**:beta 是集成分支,beta 版要的是它的快照。打在 `main` 上要么不含 - beta 分支的新改动(白测一场),要么等于把还没发布的改动提前推进 `main` —— 后者正是第 1 - 节明确不做的事。 - -docker 的 `:latest` 不会被 beta tag 移动(`.github/workflows/docker.yml`),镜像仍出 -`3.1.0-beta.1` 那个版本 tag,想尝鲜的按版本号拉。 - -后续常见三件事: - -- **再发一个 beta**:核心号不变、递增序号(`3.1.0-beta.2`,五处一起改,上面那套命令照走)。 - 预发布序号能分出先后(`beta.2` > `beta.1`),已经在 beta 上的人照样收到更新 —— 所以 beta - 之间也要保持「tag 与版本号逐字一致」。 -- **转正**(发 `3.1.0` 正式版):按第 6、7 节在 `main` 上走,五处版本号去掉后缀。 - **beta 用户不会自动接回正式版**:开关开着的人只看预发布版(`pick_listed_release` - 按「当前跑的是不是 beta 版」筛),转正的 `3.1.0` 对他们不算更新 —— 要留在 beta 频道的人 - 等下一个 beta(`3.2.0-beta.1` 一出现就收得到);想回正式版由用户自己关开关,关掉后 - 只在**版本号高于他手上那个 beta** 的正式版发布时才收到更新(`3.1.0` > `3.1.0-beta.1`, - 但更早的 `3.0.9` 不会把他降级)。 -- **「打开了却没有反应」**:仓库里一个预发布 Release 都没有时,打开这个开关也只会看到 - 正式版 —— 第一次 beta 发出去之后才有效果(跑正式版的人打开开关时仍是「预发布一并 - 参与、取最高」,只是没有 beta 可拿)。 - -## 5. 提交:保证工作区干净 - -发版前必须把工作区收干净,tag 里的代码就是发出去的代码: - -```bash -git status --short # 必须为空;有改动就先提交或清理 -cargo check # 或前端有改动时做一次构建自检 -``` - -## 6. 发布提交:更新日志写进提交信息 - -**发布提交的提交信息 = 更新日志**。原因:发版收尾脚本创建 / 更新 GitHub Release 时,正文取「tag 所指提交的提交信息」(`git log -1 --format=%B`)。 - -- 格式沿用历史版本的编号清单,每行一条,带分类前缀:`新增:` / `优化:` / `修复:` / `更新:` / `移除:`,最后一条固定 `版本:X.Y.Z → X.Y.Z+1`; -- 内容取「上个 tag 以来的全部提交」综合整理(`git log vX.Y.Z..HEAD --oneline`),纯 CI/临时的调试提交归并成一条流程性描述即可; -- 本次没有代码改动时用空提交承载:`git commit --allow-empty -F 更新日志.txt`(历史发版两种做法都有先例)。 -- 更新日志草稿与历史发布说明统一写在 `docs/release-notes/<版本号>.md`(如 `docs/release-notes/2.9.2.md`),不要在项目根目录新建 `.release-notes-*.md` —— `docs/release-notes/` 已在忽略列表里(只忽略这个目录,`docs/` 下的其它文档正常入库),新增文件不必再单独加忽略规则。 - -## 7. 打 tag 并推送:一条 tag 触发全部构建 - -```bash -git push origin main -git tag vX.Y.Z -git push origin vX.Y.Z -``` - -(beta 版同法,只是 tag 带预发布后缀、落在 `beta` 分支的提交上:`git tag vX.Y.Z-beta.N` -→ `git push origin vX.Y.Z-beta.N`,见第 4 节末尾。) - -推 `v*` tag 会**同时触发两个工作流**(`.github/workflows/`): - -| 工作流 | 产出 | 说明 | -|---|---|---| -| `build.yml`(build) | Windows NSIS 安装包 + macOS universal dmg | `macos` / `windows` 两个 job 构建并上传 artifact(macOS 包**只能在 CI 构建**,无法从 Windows 交叉编译);安装包只挂 artifact,GitHub Release 由本地脚本挂载(见第 8 节) | -| `docker.yml`(docker) | Docker Hub `aimodcc/agent2api:<版本>` + `:latest`(amd64 / arm64 双架构) | 手动 `workflow_dispatch` 触发时只出 `:dev` 测试 tag,不碰正式 tag;beta tag(版本号带 `-`)只出 `<版本>`,**不移动 `:latest`** | - -跟踪进度(手动跑 gh 前要先设代理,见第 10 节): - -```bash -gh run list --limit 4 # 确认工作流都已触发 -gh run watch --exit-status -``` - -## 8. 发版收尾:挂 GitHub Release(本地脚本一条命令) - -GitHub Release **不由 CI 发布**(Release 本来就不会自动创建),由本地脚本 -一步挂载: - -```bash -bash scripts/release.sh vX.Y.Z # 自动找该 tag 的成功 build run -bash scripts/release.sh vX.Y.Z # 或显式指定 run -``` - -脚本做三件事:下载 build 的两个安装包 artifact 到 dist/ → 按 tag 提交信息 -(= 更新日志,见第 6 节)创建 / 更新 GitHub Release 并挂附件 → 打印验收 -提示。幂等可重跑(`--clobber` 覆盖附件)。 - -**beta tag(`v3.1.0-beta.1`)不用改任何参数**:脚本按「tag 里带 `-`」自动把 -Release 标成 pre-release(重跑时也会把标记纠正回来),验收提示里会写明这一点 —— -标记是应用内「加入 beta 版」开关的分界线(完整步骤见第 4 节末尾)。 - -## 9. 验收清单(三处核对) - -- [ ] GitHub Release:`gh release view vX.Y.Z`(手动跑 gh 前先设代理,见第 10 节)—— 正文日志齐全,exe / dmg 两个附件都在; -- [ ] Docker Hub:`aimodcc/agent2api` 的 Tags 页出现 `<版本>` 与 `latest`,Pushed 时间一致; -- [ ] 安装包「关于」页版本号与 tag 一致; -- [ ] 只有 beta 版多一条:Release 显示 **Prerelease**,且 Docker Hub 上 `latest` 没有跟着动(见第 4 节末尾的 beta 版流程)。 - -## 10. 已知坑与排查 - -- **GHCR 新包默认私有**:若以后镜像改推 GHCR,首次推送后需到包设置手动改 Public(当前推的是 Docker Hub,无此问题)。 -- **gh 不读 git 的代理配置,跑 gh 前要先设代理环境变量**:`gh` 是 Go 程序,不读 `~/.gitconfig` 里的 `http(s).proxy`,也不读 Windows 系统代理(WinINET),只认 `HTTPS_PROXY` / `HTTP_PROXY` 环境变量。本机 GitHub 直连时通时不通,手动执行 gh 命令前先设: - - ```bash - export HTTPS_PROXY=$(git config --get-urlmatch http.proxy https://github.com || git config --get https.proxy) - ``` - - 只设 `HTTPS_PROXY` 就够(gh 的请求全是 HTTPS,实测不读 `HTTP_PROXY`)。第 8 节的 `scripts/release.sh` 已内置这一步(从 git 配置读取后透传给 gh),用脚本发版无需手动设。代理没开时 gh 会立刻报 `proxyconnect tcp: ... connection refused` 而不回退直连 —— 与 `git push` 的表现一致。 diff --git a/build/make-icon.mjs b/build/make-icon.mjs index b942693e..03530baa 100644 --- a/build/make-icon.mjs +++ b/build/make-icon.mjs @@ -124,50 +124,91 @@ function shapeAlpha(nx, ny) { // ─── 光栅化(3×3 超采样)──────────────────────────────────── const SS = 3; -const pixels = Buffer.alloc(SIZE * SIZE * 4); /** 线性插值:目前只用于把纯白箭头按覆盖率混到纯色底上 */ function lerp(a, b, t) { return a + (b - a) * t; } -for (let y = 0; y < SIZE; y++) { - for (let x = 0; x < SIZE; x++) { - let rectHits = 0; - let arrowHits = 0; - for (let sy = 0; sy < SS; sy++) { - for (let sx = 0; sx < SS; sx++) { - const px = (x + (sx + 0.5) / SS) / SIZE - 0.5; - const py = (y + (sy + 0.5) / SS) / SIZE - 0.5; - const { rect, arrow } = shapeAlpha(px, py); - if (rect <= 0) rectHits++; - if (arrow <= 0) arrowHits++; +/** + * 把一块画布光栅化成 RGBA 像素。 + * + * `shape` 收归一化坐标([-0.5, 0.5]),返回 `{ rect, arrow }` 两个 SDF 值。 + * 分成「底」与「箭头」两层是为了让两者各自带抗锯齿覆盖率 —— 箭头压在底上, + * 边缘不能直接取整。 + */ +function rasterize(size, shape) { + const pixels = Buffer.alloc(size * size * 4); + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + let rectHits = 0; + let arrowHits = 0; + for (let sy = 0; sy < SS; sy++) { + for (let sx = 0; sx < SS; sx++) { + const px = (x + (sx + 0.5) / SS) / size - 0.5; + const py = (y + (sy + 0.5) / SS) / size - 0.5; + const { rect, arrow } = shape(px, py); + if (rect <= 0) rectHits++; + if (arrow <= 0) arrowHits++; + } } + const total = SS * SS; + const rectA = rectHits / total; + const arrowA = arrowHits / total; + if (rectA === 0) continue; + + // 底色改为纯苹果蓝 #007AFF(与 iOS/macOS 系统蓝同族的观感),不再做对角渐变, + // 这样图标在浅色/深色壁纸和 Dock 里都保持同一个可辨识的品牌蓝。 + let r = 0x00; + let g = 0x7a; + let b = 0xff; + // 箭头为纯白,按覆盖率与底色混合 + if (arrowA > 0) { + r = lerp(r, 255, arrowA); + g = lerp(g, 255, arrowA); + b = lerp(b, 255, arrowA); + } + const i = (y * size + x) * 4; + pixels[i] = Math.round(r); + pixels[i + 1] = Math.round(g); + pixels[i + 2] = Math.round(b); + pixels[i + 3] = Math.round(rectA * 255); } - const total = SS * SS; - const rectA = rectHits / total; - const arrowA = arrowHits / total; - if (rectA === 0) continue; - - // 底色改为纯苹果蓝 #007AFF(与 iOS/macOS 系统蓝同族的观感),不再做对角渐变, - // 这样图标在浅色/深色壁纸和 Dock 里都保持同一个可辨识的品牌蓝。 - let r = 0x00; - let g = 0x7a; - let b = 0xff; - // 箭头为纯白,按覆盖率与底色混合 - if (arrowA > 0) { - r = lerp(r, 255, arrowA); - g = lerp(g, 255, arrowA); - b = lerp(b, 255, arrowA); - } - const i = (y * SIZE + x) * 4; - pixels[i] = Math.round(r); - pixels[i + 1] = Math.round(g); - pixels[i + 2] = Math.round(b); - pixels[i + 3] = Math.round(rectA * 255); } + return pixels; } +const pixels = rasterize(SIZE, shapeAlpha); + mkdirSync(dirname(OUT), { recursive: true }); writeFileSync(OUT, encodePng(SIZE, SIZE, pixels)); console.log(`[icon] 已生成 ${OUT}`); + +// ─── PWA maskable 图标 ────────────────────────────────────── +// +// 「maskable」是 Android 自适应图标用的那一档:系统会**按自己的形状裁剪** +// (圆形、方形、水滴…),并可能把图形放大到 1.33 倍。规范要求关键内容落在 +// 中心 80% 直径的圆内,否则会被裁掉。 +// +// 所以这一版与 source.png 有两处必须不同,不能直接复用同一张图: +// ① **满铺、不透明、无圆角** —— 带透明圆角的图被裁成圆形后,四角会露出 +// 底色(黑边),且规范明确要求 maskable 图标不能有透明像素; +// ② **箭头缩到 60%** —— 留出被裁剪的余量,放大 1.33 倍后仍完整。 +// +// 输出直接落到 `ui/assets/`(手机端静态资源目录),不进 tauri icons —— +// 那一套是桌面端安装包用的,与 PWA 无关。 +const MASKABLE_OUT = join(HERE, '..', 'desktop-tauri', 'ui', 'assets', 'app-icon-maskable-512.png'); +const MASKABLE_SIZE = 512; +/** 箭头缩放:0.6 时箭头横向跨度约 0.34,落在 80% 安全圆内绰绰有余 */ +const MASKABLE_SCALE = 0.6; + +const maskablePixels = rasterize(MASKABLE_SIZE, (nx, ny) => { + // 底:满铺整块画布(SDF 恒 <0),于是没有透明像素、也没有圆角 + const rect = -1; + // 箭头:坐标放大 = 图形缩小,居中不变 + const { arrow } = shapeAlpha(nx / MASKABLE_SCALE, ny / MASKABLE_SCALE); + return { rect, arrow }; +}); + +writeFileSync(MASKABLE_OUT, encodePng(MASKABLE_SIZE, MASKABLE_SIZE, maskablePixels)); +console.log(`[icon] 已生成 ${MASKABLE_OUT}`); diff --git a/desktop-tauri/i18n/i18n-src/en/accounts.json b/desktop-tauri/i18n/i18n-src/en/accounts.json index 62687c52..ddf2af92 100644 --- a/desktop-tauri/i18n/i18n-src/en/accounts.json +++ b/desktop-tauri/i18n/i18n-src/en/accounts.json @@ -7,7 +7,9 @@ "0 = 不限": "0 = unlimited", "0 = 按本家默认 {default}": "0 = provider default {default}", "30 秒 ~ 24 小时;手动查询会顺延下一轮": "30 seconds to 24 hours; a manual query postpones the next round", + "7 天内到期": "Expires within 7 days", "API Key": "API Key", + "Accio 国内版": "Accio China edition", "Base URL": "Base URL", "Clash Verge 里还没有配置混合监听器或节点端口": "No mixed listener or node port is configured in Clash Verge", "Clash 出口:": "Clash exit: ", @@ -15,12 +17,18 @@ "NewAPI 用户 ID": "NewAPI user ID", "OpenAI 兼容": "OpenAI-compatible", "OpenAI 兼容填到 /v1;Anthropic 填根地址": "For OpenAI-compatible use /v1; for Anthropic use the root URL", + "Qoder 中国版": "Qoder China edition", + "Qoder 国际版": "Qoder International edition", "Token 单位": "Token unit", "Token 时": "tokens", "Token 消耗": "Token usage", "Token 限额": "Token limit", "Token 限额 · 已禁用": "Token limit · Disabled", "Token 限额 · 已跳过": "Token limit · Skipped", + "WorkBuddy 国内版": "WorkBuddy China edition", + "WorkBuddy 国际版": "WorkBuddy International edition", + "ZCode 国内版": "ZCode China edition", + "ZCode 国际版": "ZCode International edition", "ZCode 套餐明细 — {name}": "ZCode plans — {name}", "bigmodel_token_production 的值": "Value of bigmodel_token_production", "{action}{who}": "{action} {who}", @@ -32,13 +40,24 @@ "{label}({count})": "{label} ({count})", "{message}(去该账号的「设置」里填上查询凭证即可)": "{message} (fill in the query credential in this account's Settings)", "{month}月{day}日 {clock}": "{month}/{day} {clock}", + "{name} · 分组设置": "{name} · Group settings", "{name} 移到 {group} 组内第 {rank} 位": "Moved {name} to position {rank} within the {group} group", "{name} 移到第 {rank} 顺位": "Moved {name} to global position {rank}", + "{name}的方向": "Direction of {name}", "{name}({parts})": "{name} ({parts})", "{name}({time} 起生效)": "{name} (takes effect at {time})", + "{n} 个已计入": "{n} counted", + "{n} 个查询失败": "{n} failed to query", "{n} 个模型 ▾": "{n} models ▾", + "{n} 个模型限流中 · 到恢复时间自动解除": "{n} models rate-limited · clears automatically at the reset time", "{n} 个模型限流中 · 记录来自上游 429 / 限额码,到恢复时间自动解除": "{n} models rate-limited · recorded from upstream 429 / quota codes; clears automatically at the reset time", "{n} 个请求正在使用该账号(含还在下发内容的流式请求)": "{n} requests are using this account (including streaming requests still sending content)", + "{n} 个读数只读到一半(读数可信但不完整)": "{n} only partially read (readable but incomplete)", + "{n} 个读数没有绝对数(只给比例或无额度),未计入": "{n} have no absolute figure (percentage only or no quota), not counted", + "{n} 个账号 · {e} 启用 · {l} 限流": "{n} accounts · {e} enabled · {l} rate limited", + "{n} 个账号 · 启用 {e}": "{n} accounts · {e} enabled", + "{n} 个还没查到(未查询或正在查询)": "{n} not read yet (not queried or in progress)", + "{n} 个还没查询": "{n} not queried yet", "{n} 分钟": "{n} min", "{n} 分钟后": "in {n} min", "{n} 天后": "in {n} days", @@ -47,6 +66,9 @@ "{n} 秒": "{n} s", "{period}窗口": "{period} window", "{plan}({time} 领取)": "{plan} (claimed {time})", + "{reason}。该账号的登录态已被上游注销,自动刷新不会再成功,请重新登录该账号": "{reason}. The upstream has revoked this account's session; automatic refresh will never succeed again — sign in to this account again.", + "{reason}之后组顺位才可拖动(别家的组整组不可见,落点没法定位)": "", + "{reason}之后顺序才可拖动(模型作用域是全局队列的投影,这里没有可写的顺序)": "", "{scope} {used} / {quota} Token,{countdown}重置": "{scope} {used} / {quota} tokens, {countdown} until reset", "{text}过期": "Expires {text}", "{text}过期({full})": "Expires {text} ({full})", @@ -79,7 +101,9 @@ "✅ 顺序已调整": "", " 出口 IP {ip}": " Exit IP {ip}", "、": ", ", - "、补官方会话头、请求体补两个占位工具(上游免费档的三道校验)。": ", adds official session headers, and adds two placeholder tools to the request body (the three checks of the upstream free tier).", + "、补官方会话头、请求体补占位工具(上游免费档的过门校验)。": ", adds official session headers and a placeholder tool in the request body (the upstream free tier's shape checks).", + "「{name}」已在「网络代理」页禁用,请先启用它或换一条": "\"{name}\" is disabled on the \"Network proxy\" page. Enable it first or pick another one.", + "「{name}」开着排序规则:先关掉规则,才能手动调整组内顺序": "“{name}” has sorting rules on: turn them off before adjusting the order inside the group", "「{name}」本身": "\"{name}\" itself", "「已保存的代理」": "\"Saved proxy\"", "「网络代理」页还没有出口 —— 去那里新增,或在「订阅管理」里点「同步 Clash Verge」把 Clash 的出口导进来": "The Network proxy page has no exits yet — add one there, or click \"Sync Clash Verge\" under \"Manage subscriptions\" to import Clash's exits", @@ -91,6 +115,8 @@ "上游的 API Key。留空 = 不修改(改凭证不会换账号 id);要清掉已配的凭证用右边的「清除」": "The upstream API Key. Leave blank = no change (editing a credential does not change the account id); to remove a configured credential use \"Clear\" on the right", "上游的余额读数里没有这个套餐的额度桶,剩余量读不到;余额列显示的是套餐发放的权益额度,不是剩余量": "The upstream balance reading has no quota bucket for this plan, so the remaining amount is unavailable; the balance column shows the plan's granted entitlement, not the remaining amount", "上游返回 {status}": "Upstream returned {status}", + "上面所有规则都相同时,保持你拖出来的顺序": "When every rule above ties, keep the order you dragged", + "不到 1 分钟": "under a minute", "不可用:{error}": "Unavailable: {error}", "不限": "Unlimited", "与队列里的上一个账号交换优先级(可能是另一家的账号)": "Swap priority with the previous account in the queue (may be an account from another provider)", @@ -98,11 +124,16 @@ "个": "total", "个 · 共": "of", "个人": "Personal", + "主凭证没有固定到期时刻,到期由网关自动续期或本来就不会过期": "The primary credential has no fixed expiry — the gateway renews it automatically, or it does not expire at all", "主机": "Host", "仅将优先级调整到全局第一位,不改变启用状态": "Only moves priority to first in the global queue; does not change the enabled state", + "仅按手动顺序": "Manual order only", "仅账号管理": "Account management only", + "今天 24 点": "", "今天 {clock}": "today {clock}", "今天 {time}": "today {time}", + "今天剩余:{name}": "", + "今天剩余:{name} · 这一份还剩 {value}(每天 24 点回满)": "", "今天还没有领过:上面「可领取的套餐」里逐份点「领取」即可": "Nothing claimed today yet: just click \"Claim\" on each plan under \"Claimable plans\" above.", "今天(北京时间 {day})已由官方确认到账 {n} 项;领到的是套餐赠送积分,不增加福利模型的 token 池;按自然日重置,明天可再领": "The official side confirmed {n} credits today (Beijing time {day}); they are plan gift points and do not add to the welfare model token pool; resets per calendar day, so you can claim again tomorrow", "今天(北京时间 {day})已试过 {n} 次但官方尚未确认到账,": "Tried {n} times today (Beijing time {day}) but the official side has not confirmed the credit yet. ", @@ -137,18 +168,22 @@ "余额不足": "Low balance", "余额不足 · 已跳过": "Low balance · Skipped", "余额低于": "Balance below", + "余额低于阈值时的提示": "Warn when balance drops below a threshold", "余额低于阈值,转发时会跳过该账号(余额回升自动恢复)": "Balance is below the threshold; forwarding skips this account (recovers automatically when the balance rises)", "余额低于限制器的阈值,转发时会跳过该账号(余额回升自动恢复)": "Balance is below the limiter threshold; forwarding skips this account (recovers automatically when the balance rises)", "余额列读数的自动刷新;也是「限制器」余额规则的数据来源。": "Auto-refreshes the balance column reading; also the data source for the Limiter's balance rules.", "余额响应为空": "Empty balance response", + "余额已更新": "Balance updated", "余额查询凭证": "Balance query credential", "余额查询失败:{message}": "Balance query failed: {message}", "余额查询方案:{label}": "Balance query scheme: {label}", "余额沿用现有口径;Token 按重置方式内的累计消耗判定(切换类型会清空阈值)": "Balance keeps its existing basis; Token is judged by cumulative usage within the reset window (switching types clears the threshold)", + "余额详情": "Balance details", "使用套餐": "Plan in use", "保存失败:{error}": "Save failed: {error}", "保存规则": "Save rule", "修改代理": "Change proxy", + "倍率 {rate}": "", "免费版": "Free", "全局一条队列:数值越小越先用,不分提供商": "One global queue: lower numbers are tried first, regardless of provider", "全局唯一:所有提供商的账号都不能重号,数值越小越先用": "Globally unique: no two accounts across all providers may share a number; lower numbers are tried first", @@ -165,18 +200,30 @@ "关闭则不参与转发(余额查询照常,见「查询设置」)": "When off, the account takes no part in forwarding (balance queries still run — see \"Query settings\")", "内消耗 ≥": "usage ≥", "再领一次": "Claim again", + "冷却进度 {percent}%": "Cooldown progress {percent}%", + "凭证": "", "凭证与套餐": "Credential & plan", + "凭证已刷新": "", + "凭证已被上游注销": "", "凭证已过期,转发时会先刷新": "Credential expired; it is refreshed before forwarding", "出口": "Exit", "出网代理": "Outbound proxy", + "分组设置": "Group settings", "分钟": "minutes", + "切回全局账号表": "", + "删除中…": "", "删除提供商": "Delete provider", "删除账号": "Delete account", "删除这条账号记录(不会影响客户端自己的登录态;之后可再点「导入桌面端登录态」加回来)": "Delete this account record (it does not affect the client's own login state; you can add it back later with \"Import desktop login state\")", "删除选中的账号": "Delete selected accounts", "到恢复时间后自动解除,无需手动操作": "Clears automatically at the reset time; no manual action needed", "到期 {time}": "Expires {time}", + "到期 近→远": "Expiry near→far", + "到期 远→近": "Expiry far→near", + "到期时刻 {time}": "", "刷新 Token": "Refresh token", + "刷新凭证": "", + "刷新队列": "", "剩 {left} / {total}{unit}": "{left} / {total}{unit} left", "剩 {n} 分钟": "{n} min left", "剩 {remaining}": "{remaining} left", @@ -186,6 +233,8 @@ "协议": "Protocol", "卡片": "", "又不勾": "without checking it", + "取消排序": "", + "取消排序,回到默认次序(不影响已写回的队列顺序)": "", "取消选择": "Deselect", "另 {n} 个分组全是禁用账号": "", "另有 {n} 个已勾选账号被当前筛选隐藏,仍会参与操作": "{n} selected accounts are hidden by the current filter but still take part", @@ -196,9 +245,13 @@ "可用合计": "Total available", "可留空:新版请求头只认令牌;旧版会把用户 ID 放进 New-Api-User 头": "Optional: newer builds only use the token; older ones put the user ID in the New-Api-User header", "可领取的套餐": "Claimable plans", + "各厂商组内排序:分组与组序不变;组内先后已写回调度队列": "", "同提供商内暂无其他账号占用优先级": "No other accounts in this provider use a priority", "同提供商已占用:{names}": "Taken in this provider: {names}", "名称": "Name", + "后续加入": "Coming later", + "否": "", + "含 {n} 个不限额度账号,合计记为 ∞": "includes {n} unlimited account(s); total shown as ∞", "启用状态": "Enabled status", "启用该规则": "Enable this rule", "启用该账号": "Enable this account", @@ -208,6 +261,7 @@ "周期单位": "Period unit", "商汤日日新控制台的登录密码": "SenseNova console login password", "商汤日日新控制台的登录账号": "SenseNova console login account", + "固定兜底,不受开关影响": "Fixed fallback, not affected by the toggles", "固定周期": "Fixed period", "在 NewAPI 站点「个人设置」里生成的访问令牌": "Access token generated under \"Personal settings\" on the NewAPI site", "在列表里拖:拖分组带调整整组顺位,拖行首把手调整组内先后(顺序由拖拽表达,不再填数字)": "Drag in the list: drag a group band to reorder whole groups, drag a row handle to reorder within the group (order is expressed by dragging — no more typing numbers)", @@ -216,6 +270,8 @@ "基本": "Basic", "备注名 / 启用 / 代理": "Label / enable / proxy", "备注名 {name}": "Label {name}", + "备注名不能为空": "Label cannot be empty", + "备注名已更新": "Label updated", "失败 {count} 个:": "{count} failed:", "套餐 {name}": "Plan {name}", "套餐明细弹窗未加载,请重启应用后重试": "The plan details dialog is not loaded; please restart the app and try again", @@ -225,10 +281,12 @@ "套餐池(Start Plan)— 走 zcode.z.ai 端点,消耗 ZCode 套餐池的额度(新用户体验额度 / 活动领取)": "Plan pool (Start Plan) — uses the zcode.z.ai endpoints and the ZCode plan-pool quota (new-user trial quota / event grants)", "套餐积分的重置日期": "Reset date for plan points", "它会改写请求体": "It rewrites the request body", + "完整": "", "官方限时活动": "Official limited-time event", "对话 token": "Chat tokens", "将作用于以下账号": "Applies to the following accounts", "小时": "hours", + "小浣熊": "Raccoon", "尚未查询该账号的余额": "This account's balance has not been queried yet", "展开全部": "Expand all", "展开全部分组": "Expand all groups", @@ -236,16 +294,25 @@ "展开这一组": "Expand this group", "已保存的代理": "Saved proxy", "已停用": "Off", + "已删除账号": "", + "已启用 {name}": "Enabled {name}", "已启用,点击禁用(不参与转发)": "Enabled; click to disable (excluded from forwarding)", "已在全局队列第一位": "Already first in the global queue", + "已按「{label}」{dir}排序": "", + "已按「{label}」{dir}排序并保存": "", + "已是首选": "Already first", "已更新 {ok}/{total} 个账号,{failed} 个失败": "Updated {ok}/{total} accounts, {failed} failed", + "已查询": "Queried", "已用 {used}": "{used} used", "已用 {used} / 共 {total}": "Used {used} / {total}", "已用完 / 已过期": "Used up / Expired", "已禁用": "Disabled", + "已禁用 {name}": "Disabled {name}", "已禁用,点击启用": "Disabled; click to enable", + "已筛选 {n} 项": "{n} shown", "已被「{name}」占用": "Taken by \"{name}\"", "已触发 · 账号已禁用": "Triggered · Account disabled", + "已设为首选": "Set as first", "已设置": "Configured", "已跳过{current}": "Skipped{current}", "已跳过{usage}{countdown}": "Skipped{usage}{countdown}", @@ -257,10 +324,10 @@ "已限流": "Rate-limited", "已领到、还没到生效时间的套餐(生效后余额列会按它显示):\n{lines}": "Claimed plans that have not taken effect yet (the balance column shows them once they do):\n{lines}", "并发上限": "Concurrency limit", - "并发上限 0~{max};有上游硬顶的家(如 CodeArts)以它的硬顶为准;达到上限的账号会被跳过、请求转给其他账号": "Concurrency limit 0–{max}; for providers with an upstream hard cap (e.g. CodeArts) that cap wins; accounts at the limit are skipped and requests go to other accounts", + "并发上限 0~{max};CodeArts 等有默认值的家填 0 即按本家默认;达到上限的账号会被跳过、请求转给其他账号": "Concurrency limit 0–{max}; for providers with a default (e.g. CodeArts), 0 means that default; accounts at the limit are skipped and requests go to other accounts", "并发上限 0~{max};达到上限的账号会被跳过、请求转给其他账号": "Concurrency limit 0–{max}; accounts at the limit are skipped and requests go to other accounts", "并发上限 0~{max};达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "Concurrency limit 0–{max}; accounts at the limit are skipped and requests go to other accounts (the \"Concurrency limit\" entry from the list's ⋯ menu is now here)", - "并发上限 受上游硬顶约束,达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "The concurrency limit is capped by the upstream; accounts at the limit are skipped and requests go to other accounts (the \"Concurrency limit\" entry from the list's ⋯ menu is now here)", + "并发上限 填 0 即按本家默认 {default};填 1 或 2 就按填的算,更大的数会被本家上限截断(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "0 uses the provider default ({default}); 1 or 2 are honoured as entered, larger values are capped by the provider limit (the \"Concurrency limit\" entry from the list's ⋯ menu is now here)", "并发上限必须是 0~{max} 的整数": "Concurrency limit must be an integer from 0 to {max}", "并发上限:{value}": "Concurrency limit: {value}", "开启后,网关按 OpenCode 官方 CLI 的形状发请求:没填 Key 的账号用匿名凭证": "When on, the gateway sends requests in the shape of the official OpenCode CLI: for accounts with no Key it uses anonymous credentials", @@ -268,10 +335,16 @@ "当前代理不可用:{error}(转发时会回退直连)": "Current proxy is unavailable: {error} (falls back to direct when forwarding)", "当前余额 {n};与余额列同一数字口径,严格小于才触发(等于阈值仍可用)": "Current balance {n}; same reading as the balance column — the rule triggers only when strictly below it (equal to the threshold is still usable)", "当前可用 ∞(不参与余额不足判定)": "Currently ∞ available (excluded from the low-balance check)", + "当前已经是默认规则": "This is already the default rule set", + "当前排序规则:{rules}。点击调整": "Current sorting: {rules}. Click to adjust", "当前没有任何模型处于限流中": "No models are currently rate-limited", "当前没有可领取的套餐(活动期内每天发一份,明天可再领)": "No claimable plans right now (one is issued each day during the event; you can claim again tomorrow)", "当前没有限流中的模型。": "No models are currently rate-limited.", + "当前筛选下 {n} 个账号的可用余额之和(随筛选、搜索变化,不受分页影响)": "", + "当前筛选下没有账号": "No accounts match the current filters", "当前筛选条件下没有账号": "No accounts match the current filter", + "当前规则:{rules}。自动排序生效时,组内拖动把手会锁定;关掉全部规则即回到手动顺序。": "Current rules: {rules}. While auto-sort is active the drag handle inside the group is locked; turn every rule off to go back to manual order.", + "当前视图不能调整组内顺序:{reason} 之后再拖动": "", "当前视图不能调整组顺位:{reason} 之后再拖动": "Group order cannot be changed in this view: clear {reason} first, then drag", "当前:{label}(未经过代理池 —— 可在「网络代理」页把出口同步进池后来这里改选)": "Current: {label} (not via the proxy pool — sync the exit into the pool on the Network proxy page to select it here)", "当前:{label}(每条请求换一个出口);「自定义代理…」打开完整设置": "Current: {label} (a different exit per request); \"Custom proxy…\" opens the full settings", @@ -284,13 +357,20 @@ "必填;留空 = 不修改,清除用右边的按钮": "Required; leave empty to keep the current one, use the button on the right to clear", "总剩余 {total} · 套餐 {plan} · 奖励 {bonus}": "Total left {total} · plan {plan} · bonus {bonus}", "总量 {value}": "Total {value}", + "恢复到「积分到期 → 添加时间」这套默认规则": "Restore the default rules: credit expiry → added time", "恢复时间未知": "Reset time unknown", "我的套餐": "My plans", + "所属厂商:{label}": "", "所有分组里都没有启用的账号(禁用不参与转发)": "", + "所有提供商都有这个字段": "Every provider has this field", + "所有积分包里**最先作废**的那一份什么时候到期;逐份明细见「余额」格里的积分明细": "", + "所有积分包里最先作废的那一份什么时候到期;没有一次性包时退到每日重置额度、显示今天还剩多久;逐份明细见「余额」格里的积分明细": "", "手动添加": "Manual entry", "打开批量操作弹窗,并预选这一组的账号(改代理等动作在弹窗里挑参数)": "Opens the bulk-operations dialog with this group's accounts preselected (pick parameters like the proxy inside the dialog)", + "打开筛选": "", "执行中…": "Running…", "执行({action})": "Run ({action})", + "扫码 / 粘贴令牌 / 导入登录态需在桌面端完成": "", "批量动作": "Batch action", "批量操作 · 已选 {count} 个账号": "Batch operation · {count} accounts selected", "批量操作失败:{error}": "Batch operation failed: {error}", @@ -303,20 +383,31 @@ "拖动调整列宽(双击还原)": "Drag to resize the column (double-click to reset)", "拖动调整整组顺序": "Drag to reorder whole groups", "拖动调整组内顺序": "Drag to reorder within the group", + "按优先级从上到下比较,勾选的项才参与": "Compared top to bottom; only checked rules take part", "按住拖动,调整这一家的组顺位(整组一起动)": "Hold and drag to change this provider's group position (the whole group moves together)", "按住拖动,调整这个账号在本组内的顺序": "Hold and drag to change this account's order within its group", "按提供商筛选账号": "Filter accounts by provider", "按模型": "Per model", "按规则限制该账号参与转发:没有规则(或全部停用)= 不限制;跳过档自动恢复,禁用档需手动启用。": "Limit this account's participation in forwarding with rules: no rules (or all off) = no limit; skip rules recover automatically, disable rules need manual re-enabling.", + "按账号最早到期、且还有剩余的积分包比较": "Compares each account by its earliest credit pack that still has balance and has not expired", + "排序": "Sorting", + "排序粒度": "", "提供商显示名,1~{max} 个字符": "Provider display name, 1–{max} characters", "提供商:{name}": "Provider: {name}", + "提高优先级": "Raise priority", "搜索": "Search", "搜索账号": "Search accounts", + "搜索账号 / 邮箱 / 备注": "Search account / email / note", "操作失败:{message}": "Operation failed: {message}", "收起": "Collapse", + "改动即时生效": "Changes apply immediately", "改协议 / Base URL 会改变它们的转发方式,正在进行的请求可能失败;模型清单与映射在「模型管理」页。": "Changing the protocol / Base URL changes how they forward, and in-flight requests may fail; the model list and mapping are on the Model management page.", "数值越小越先用;": "Lower numbers are tried first; ", "数值越小越先用;{hint}": "Lower numbers are tried first; {hint}", + "整表": "", + "整表排序:厂商分组已散开,行里带厂商标记。此顺序仅用于查看,不写回调度队列 —— 写回会打破「同一家连续」的队列规则": "", + "整表排序:打散厂商分组,全表一起来(行里带厂商标记;仅用于查看,不写回队列)": "", + "新账号落到组首 / 组尾": "New accounts go to the top / bottom", "旗舰": "Pro", "无上限 · 已用 {used}": "Unlimited · {used} used", "无上限按日计(上游到达上限后仍继续计数,超额如实显示)": "Unlimited, counted daily (counting continues after the upstream limit is reached, and the overage is shown as-is)", @@ -329,24 +420,39 @@ "明天 {clock}": "tomorrow {clock}", "明天 {time}": "tomorrow {time}", "明细": "Details", + "是": "", "昵称 {name}": "Nickname {name}", "显示账号名": "Show name", "暂无可查询余额的账号": "No accounts with a queryable balance", + "暂无数据:先查询一次余额,之后会自动按到期排序": "No data yet: query the balance once and expiry sorting takes over automatically", "暂无账号,请点击右上角「添加账号」": "No accounts yet — click \"Add account\" in the top right", "更多操作": "More actions", + "更多设置": "More settings", "更新于 {time}": "Updated {time}", + "最先作废的那一份还剩多少(不是面额);按这一份的剩余量排序,缺失值恒排最后": "", + "最先作废的那一份还剩多少(不是面额:这一列回答「马上要作废掉多少」)": "", + "最快到期优先": "Soonest expiry first", + "最新添加优先": "Newest first", "最早 {soonest} 恢复": "Earliest reset {soonest}", "最早恢复": "Earliest reset", + "最早添加优先": "Oldest first", + "最晚到期优先": "Latest expiry first", + "最近到期": "", + "最近到期积分": "", + "最近到期:{name}": "", + "最近到期:{name} · 这一份还剩 {value}(不是面额)": "", + "最近更新": "", "有效期": "Expiry", "有的账号过了 0 点额度就回来 —— 选「自然日」": "Some accounts regain quota after midnight — choose \"Calendar day\"", "未使用": "Unused", - "未分配端口": "No port assigned", "未命名账号": "Unnamed account", "未查询": "Not queried", "未检测到 Clash Verge": "Clash Verge not detected", "未检测到 Clash Verge 配置": "No Clash Verge config detected", + "未被使用": "Not in use", "未触发{current}": "Not triggered{current}", "未触发{usage}{countdown}": "Not triggered{usage}{countdown}", + "未设备注名": "No label", "未返回余额数据": "No balance data returned", "未返回可识别的余额数据": "No recognizable balance data returned", "未配置": "Not configured", @@ -356,9 +462,12 @@ "本周期已用": "Used this period", "本地台账里今天还领过这些,但上面的读数里还没有:{plans}(读数刷新可能有延迟)": "Also claimed today according to the local record, but not in the readings above yet: {plans} (the reading may take a while to refresh)", "本地领取于 {time}": "Claimed locally at {time}", + "本组 {n} 个账号的可用余额之和,按整组统计(不受筛选、搜索影响)": "Sum of available balance across all {n} accounts in this group, counted over the whole group (unaffected by filters or search)", + "本组代理": "Proxy for this group", "本组操作": "Group actions", "权益 {value}": "Grant {value}", "权益 {value} {unit}": "Grant {value} {unit}", + "条件即时生效,关掉面板就能看到结果。": "Filters apply instantly — close the panel to see the results.", "来源 {source}": "Source {source}", "查看": "View", "查看可领取的套餐与名下已有的套餐(每天一期,领取需过一次人机验证)": "View the claimable plans and the plans you already have (one issue per day; claiming requires a human verification)", @@ -374,12 +483,14 @@ "查询间隔": "Query interval", "查询间隔必须是 {min} 秒 ~ 24 小时": "The query interval must be {min} seconds to 24 hours", "桌面端实时登录态(凭证每次从客户端登录态文件读取)": "Live desktop login state (the credential is read from the client login-state file each time)", + "模型作用域下分组带随队列走,不折叠": "", "正在加载…": "Loading…", "正在探测可领取的套餐…": "Checking for claimable plans…", "正在查询": "Querying", "正在读取 Clash Verge 配置…": "Reading Clash Verge config…", "正在读取…": "Loading…", "正在读取代理列表…": "Loading proxy list…", + "正在读取调度队列…": "", "正在连接上游…": "Connecting to upstream…", "正常": "Normal", "此刻正在使用这个账号的请求数(含还在下发内容的流式请求);为 0 时不显示": "The number of requests currently using this account (including streaming requests still sending content); hidden when 0", @@ -388,10 +499,13 @@ "每日消耗 ≥": "Daily usage ≥", "每日消耗 ≥ {count} Token(每天 0 点重置)": "Daily usage ≥ {count} tokens (resets at midnight)", "每日重置": "Resets daily", + "每日重置({time} 回满,不是作废)": "", "每日重置(额度按周期回满,不是作废)": "Resets daily (the quota refills each cycle; it is not voided)", "每条请求换一个出口(按顺序轮流),上游看到的 IP 因此是变化的;出口的地址与端口由「网络代理」页管理": "Each request moves to the next exit in turn, so the upstream sees a changing IP; exit addresses and ports are managed on the Network proxy page", + "没有匹配「{kw}」的账号": "No accounts match \"{kw}\"", "没有可全选的账号": "No accounts to select", "没有可用出口": "No exits available", + "没有将到期的积分(没有一次性额度包,或这一家不给逐份读数)": "", "没有选中的账号": "No accounts selected", "活动套餐": "Start Plan", "活动套餐的推理端点要求每条请求带一个阿里云验证码令牌;令牌由本应用在后台静默铸造,界面关闭时无法铸造": "The Start Plan inference endpoint requires an Alibaba Cloud captcha token on every request; the app mints tokens silently in the background and cannot mint while the UI is closed", @@ -400,7 +514,10 @@ "活动期内每天发一份新套餐;同一份当天重复领取会提示「已领取过」,次日可再领。领取要走一次人机验证(滑块)。": "During the event a new plan is issued every day; claiming the same one again the same day returns \"already claimed\", and you can claim again the next day. Claiming goes through a human verification (slider).", "测试中…": "Testing…", "测试出口": "Test exit", + "添加 新→旧": "Added new→old", + "添加 旧→新": "Added old→new", "添加规则": "Add rule", + "添加账号请用桌面端或网页版面板": "", "添加限制": "Add limit", "清掉本机的限流标记,立刻重新尝试该模型(上游若仍在限流会再次被标记)": "Clear the local rate-limit flag and retry this model immediately (it will be flagged again if the upstream is still rate-limiting)", "清掉该账号全部模型的限流标记": "Clear the rate-limit flags of every model on this account", @@ -413,7 +530,9 @@ "清除已配置的余额查询凭证": "Clear the configured balance query credential", "清除标记": "Clear flag", "清除筛选": "Clear filters", + "点一下按这一列排序(升序 → 降序 → 恢复默认次序)": "", "点击{action}各模型的限流明细": "Click to {action} the per-model rate-limit details", + "点击查看各模型的限流明细": "Click to view per-model rate-limit details", "状态": "Status", "状态 {status}": "Status {status}", "状态未知": "Unknown", @@ -429,7 +548,10 @@ "留空 = 不修改,清除用右边的按钮": "Leave empty to keep the current one; clear with the button on the right", "登录 / 导入一个新账号": "Sign in / import a new account", "直连": "Direct", + "确定删除账号「{name}」?这个操作不可恢复。": "", "确定删除选中的 {count} 个账号?此操作不可恢复,账号的登录态会一并移除。": "Delete the selected {count} accounts? This cannot be undone, and their login state will be removed as well.", + "确定清除全部模型的限流标记?": "Clear rate-limit flags for all models?", + "确定清除模型「{model}」的限流标记?": "Clear the rate-limit flag for model \"{model}\"?", "禁用": "Disable", "禁用该账号(需手动重新启用)": "Disable the account (manual re-enabling required)", "禁用这一组里的全部账号(含被筛选隐藏的)": "Disable every account in this group (including ones hidden by filters)", @@ -438,6 +560,7 @@ "福利网关未读到:{error}": "Failed to read the welfare gateway: {error}", "秒": "seconds", "积分": "Points", + "积分到期时间": "Credit expiry", "积分包": "Point pack", "积分明细": "Points details", "积分查询已复用转发用的登录凭证,这里通常留空即可。只有旧版本填过、或从旧代理导入过凭证时才有值": "Points queries reuse the login credential used for forwarding, so this is usually left blank. It only has a value if an older version filled it in or credentials were imported from the old proxy", @@ -446,11 +569,17 @@ "窗口内已用 {used} / {limit} Token,转发时会跳过该账号(重置后自动恢复)": "{used} / {limit} tokens used in the window; forwarding skips this account (recovers automatically after the reset)", "窗口对齐自然时间(整点 / 整 N 分钟一轮回),重置即清零恢复;按账号在 requests 表聚合": "The window aligns to wall-clock time (rolls over on the hour / every N minutes); a reset clears it and restores the account; aggregated per account in the requests table", "端口": "Port", + "端口准备中": "Port pending", "第 {rank} 顺位": "Position {rank}", + "策略 {strategy} · 会话粘性 {sticky}": "", + "筛选": "Filter", "筛选 / 搜索 / 分页生效时组顺位不可拖动(别家的组整组不可见,落点没法定位)": "Group order cannot be dragged while a filter / search / paging is active (other groups are hidden entirely, so the drop target cannot be located)", "级联删除名下全部账号,不可恢复": "Cascade-deletes all its accounts; cannot be undone", "组内": "", "组内先后:按住左边的把手上下拖动即可调整;整组的先后拖分组带的把手": "Within the group: hold the handle on the left and drag up / down to reorder; for the order of whole groups, drag the group band's handle", + "组内并发上限": "Group concurrency limit", + "组内手动顺序": "Manual order within the group", + "组内排序:只在每一家内部重排,分组与组序不变(组内先后写回队列)": "", "组内第 {rank} 位": "Position {rank} in group", "组顺位已锁定": "Group order locked", "组顺位:越小越先用(拖动分组带调整)": "Group position: smaller goes first (drag the group band to adjust)", @@ -459,6 +588,7 @@ "编码套餐(Coding Plan)— 走开放平台端点,用自己订阅的额度": "Coding Plan — uses the open-platform endpoints and your own subscription quota", "编辑": "Edit", "编辑限制": "Edit limit", + "缺失": "", "网络": "Network", "自动查询余额": "Auto-query balance", "自动查询余额(按下面的间隔自动刷新余额列的读数)": "Auto-query balance (refresh the balance column reading at the interval below)", @@ -467,11 +597,14 @@ "自定义:": "Custom: ", "自然日(每天 0 点)": "Calendar day (00:00 daily)", "表格": "", + "被跳过:这一轮不会用到它,所以没有位次": "", "视图": "", "解析失败": "Resolve failed", "触发动作": "Action", "触发后": "Then", "触发条件": "Condition", + "计入 {counted}/{total}": "counted {counted}/{total}", + "订阅已移除": "Subscription removed", "订阅统计未读到:{error}": "Failed to read subscription stats: {error}", "记录里没有过期时间": "No expiry time in the record", "设为首选": "Set as first", @@ -481,6 +614,8 @@ "该上游无需鉴权": "This upstream needs no auth", "该上游无需鉴权(转发与拉取模型都不发送鉴权头)": "This upstream needs no auth (no auth header is sent when forwarding or fetching models)", "该提供商没有余额查询": "This provider has no balance query", + "该提供商没有余额查询,因此读不到积分包": "", + "该提供商没有余额查询,因此读不到积分包的到期时刻": "", "该提供商的推理转发尚未接入,账号不参与转发": "Inference forwarding for this provider is not wired up yet; the account takes no part in forwarding", "该账号出网走的代理(Clash 出口 / 自定义 / 直连);点击可修改": "The proxy this account uses for outbound traffic (Clash exit / custom / direct); click to change", "该账号当前限流中的模型;点徽章看明细": "Models this account is currently rate-limited on; click the badge for details", @@ -488,6 +623,7 @@ "该账号没有套餐登录态(jwt),活动套餐会失败:请重新登录该账号": "This account has no plan login state (JWT); Start Plan will fail — sign in to this account again", "该账号没有福利模型额度(福利按活动下发,不是每个账号都有)": "This account has no welfare model quota (welfare is issued through events and not every account has it)", "该账号没有积分类读数(额度只有福利 token 池)": "This account has no points-class reading (its quota is only the welfare token pool)", + "该账号的登录态已被上游注销,自动刷新不会再成功,请重新登录该账号": "", "该账号的额度只有福利 token 池(见余额格那两行),订阅统计没有积分类计量表": "This account's quota is only the welfare token pool (see the two lines in the balance cell); the subscription stats have no points-class meter", "请先勾选要操作的账号": "Select the accounts to act on first", "请先选择 Clash Verge 出口": "Select a Clash Verge exit first", @@ -502,21 +638,27 @@ "读取中…": "Loading…", "读取失败:{error}": "Failed to load: {error}", "读取自 {dir};端口由 Clash Verge 管理,这里实时同步": "Read from {dir}; the port is managed by Clash Verge and syncs live here", + "读取调度队列失败:{error}": "", "读数来自自动余额查询;与余额列同一数字口径,严格小于才触发(等于阈值仍可用)": "The reading comes from the auto balance query; same reading as the balance column — the rule triggers only when strictly below it (equal to the threshold is still usable)", "读数里没有套餐清单(免费账号、或套餐的上游状态还没刷新;可点右上角「查询余额」重读)": "The reading contains no plan list (free account, or the upstream plan state is not refreshed yet; click \"Check balance\" at the top right to re-read)", "读数里没有额度桶,这里按套餐权益里的已用 / 总量显示(上游给的数)": "The reading has no quota bucket; this shows the used / total from the plan entitlement (the upstream figures)", + "调度队列:{n} 个可用 · 跳过 {m} 个": "", + "调整到全局队列第一位": "Move to the front of the global queue", "调整顺序失败:{message}": "Failed to reorder: {message}", "账号": "Accounts", + "账号 ID": "", "账号不存在,请刷新后重试": "Account not found; refresh and try again", "账号不存在,请刷新后重试。": "Account not found; refresh and try again.", "账号名 / 邮箱 / 备注": "Account name / email / note", "账号已保存,但提供商未更新:{error}": "Account saved, but the provider was not updated: {error}", "账号显示名称": "Account display name", + "账号添加时间": "Account added time", "账号表": "Accounts table", "账号视图": "", "账号设置": "Account settings", "账号设置 · {name}": "Account settings · {name}", "账号设置分组": "Account settings sections", + "账号详情": "", "账号邮箱": "Account email", "跳过该账号(余额回升自动恢复)": "Skip this account (recovers automatically when the balance rises)", "跳过该账号(自动恢复)": "Skip this account (recovers automatically)", @@ -529,13 +671,24 @@ "轮询的代理": "Proxies to rotate", "轮询至少要选择一条代理(或改选「全部」)": "Select at least one proxy to rotate (or switch back to \"All\")", "轮询(换 IP)": "Rotate (change IP)", + "还有 {d} 天": "{d}d left", + "还有 {h} 小时": "{h}h left", + "还有 {h} 小时 {m} 分": "{h}h {m}m left", + "还有 {n} 分钟": "{n} min left", "还没有代理(去「网络代理」页添加)": "No proxies yet (add one on the Network proxy page)", "还没有余额读数(已自动查询一次,稍候;也可以点右上角「查询余额」重读)": "No balance reading yet (one query has been started automatically, please wait; you can also click \"Check balance\" at the top right)", "还没有保存的代理": "No saved proxies yet", + "还没有账号,去桌面端添加": "No accounts yet. Add one on desktop.", "还没有限制规则:账号照常参与转发,余额与 Token 消耗都不设上限。": "No limit rules yet: the account forwards as usual, with no cap on balance or Token usage.", "这一份今天已经领过了(重复领取上游会回「已领取过」)": "This one was already claimed today (claiming it again returns \"already claimed\" from upstream)", + "这一份还剩多少:{name} · 上游没给剩余量": "", + "这一家在这个模型上的倍率(与模型管理页同一份数据)": "", + "这一家查不到积分到期时间": "This provider has no credit expiry data", "这一栏改的是": "This section changes ", "这一段按提供商出现:有什么配什么。": "This section appears per provider: configure whatever it offers.", + "这一组开着排序规则:关掉规则后才能手动拖": "Auto-sort is on for this group: turn the rules off to drag manually", + "这个模型的队列里没有账号": "", + "这个模型队列里第 {rank} 位": "", "这个账号的余额走窗口限额通道(只配了编码套餐 API Key):只有每 N 小时的剩余比例,读不到套餐清单": "This account reads its balance through the window-limit channel (only a Coding Plan API Key is configured): it only has the remaining percentage of each N-hour window, not the plan list", "这些分组里一个启用的账号都没有(禁用不参与转发),默认不显示;点一下切到「禁用」查看,也就能重新启用它们": "", "这会{what}所有启用中的账号,转发将不可用。确定继续?": "This will {what} all enabled accounts, and forwarding will be unavailable. Continue?", @@ -547,32 +700,46 @@ "连接数": "Connections", "选择": "Select", "选择要轮询的代理(可多选)": "Select proxies to rotate (multiple)", + "逐份明细见「余额」格的积分明细": "", "逐请求轮换出口(换 IP):OpenCode Zen 免费档按 IP 限流,单个 IP 的额度用完会对它一律 429": "Rotates the exit per request (changing the IP): the OpenCode Zen free tier limits by IP — once an IP runs out of quota, requests from it all get 429", + "邮箱": "", "邮箱 {email}": "Email {email}", + "重新算一次这个模型的调度队列(限流冷却 / 并发释放后队列会变)": "", "重新读取": "Reload", "重新读取该账号的余额与套餐(走余额查询那条链)": "Re-read this account's balance and plans (goes through the balance query chain)", + "重置": "Reset", "重置 {date}": "Resets {date}", "重置也不恢复,需手动启用": "A reset will not restore it; enable it manually", "重置周期必须是 30 分钟 ~ 24 小时": "The reset period must be 30 minutes to 24 hours", "重置方式": "Reset mode", + "长期有效": "Long-lived", "间隔单位": "Interval unit", "阈值": "Threshold", + "降低优先级": "Lower priority", "限制器": "Limiter", "限制器:{window}已用 {used} / {threshold} Token": "Limiter: {window} — {used} / {threshold} tokens used", "限制类型": "Limit type", "限制阈值必须是大于 0 的数字": "The limit threshold must be a number greater than 0", "限流": "Rate limit", "限流中": "Rate-limited", + "限流已清除": "Rate limit cleared", + "限流明细": "Rate-limit details", + "限流模型": "", "限额": "Limit", "限额状态": "Limit status", "隐藏账号名(名字显示为星号)": "Hide name (shown as asterisks)", + "需要重新登录": "Sign-in required", + "需重登": "Relogin", "顺序": "Order", + "顺序已锁定": "", "顺序未保存:{message}": "Order not saved: {message}", "领取 {time}": "Claimed {time}", "领套餐": "Claim plan", "额度": "Quota", "额度窗口读数来自开放平台监控通道(套餐 JWT 那条通道没有额度桶):读的是每 N 小时 / 每周窗口的剩余比例": "The quota-window reading comes from the open-platform monitoring channel (the Coding Plan JWT channel has no quota bucket): it reads the remaining percentage of each N-hour / weekly window", "验证码令牌:{counts}": "Captcha tokens: {counts}", + "默认不排序:组内顺序保持你拖出来的样子。需要自动排序时,勾选上面的规则即可。": "No sorting by default: the order inside the group stays exactly as you dragged it. Tick a rule above when you want automatic sorting.", + "默认展开 / 收起": "Default expanded / collapsed", "({n} 个未配置查询凭证)": " ({n} without query credentials)", "({plans})": " ({plans})", "(不可用)": " (unavailable)", @@ -587,13 +754,5 @@ ",只对 OpenCode Zen 这类上游有意义,别的家请关掉。": ", so it only matters for upstreams like OpenCode Zen — turn it off for other providers.", ",已达到上限": ", limit reached", ":到「网络代理」页的「订阅管理」里点「同步 Clash Verge」把出口导进池后即可选 —— 那样能集中测试、也能被多个账号共用。": ": on the Network proxy page, open \"Manage subscriptions\" and click \"Sync Clash Verge\" to import exits into the pool and then select them — that centralizes testing and lets several accounts share them.", - ";": "; ", - "WorkBuddy 国内版": "WorkBuddy China edition", - "WorkBuddy 国际版": "WorkBuddy International edition", - "小浣熊": "Raccoon", - "Qoder 中国版": "Qoder China edition", - "Qoder 国际版": "Qoder International edition", - "Accio 国内版": "Accio China edition", - "ZCode 国内版": "ZCode China edition", - "ZCode 国际版": "ZCode International edition" + ";": "; " } diff --git a/desktop-tauri/i18n/i18n-src/en/add-account.json b/desktop-tauri/i18n/i18n-src/en/add-account.json index d4c9e114..624150d3 100644 --- a/desktop-tauri/i18n/i18n-src/en/add-account.json +++ b/desktop-tauri/i18n/i18n-src/en/add-account.json @@ -10,6 +10,7 @@ "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "The Anthropic-compatible endpoint is https://api.deepseek.com/anthropic (fill it in when you switch the protocol to anthropic)", "Anthropic 协议填根地址,不要带 /v1": "For the Anthropic protocol, use the root URL without /v1", "Antigravity": "Antigravity", + "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw China edition", "AutoClaw 国际版": "AutoClaw International edition", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS is required; paste the **full Cookie** (a Cookie editor can export it in one click) — the **PTOKEN must be kept**, as the gateway uses it to exchange for a KukuAI business session token from Baidu; without it, model refresh reports \"Not signed in\". A full Cookie header, a JSON array exported by a Cookie editor, or a {BDUSS, STOKEN} object are all supported. KukuAI has no refresh mechanism, so once the login state expires, just paste it again.", @@ -18,19 +19,35 @@ "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Get the Cline Pass API Key at app.cline.bot (subscription-based); the endpoint is OpenAI-compatible, the model catalog follows the Cline console, and model names are added under Model management with the plain name (e.g. glm-5.2 / kimi-k2.7-code / deepseek-v4-pro); the upstream validates the client shape, so the required client request headers are preset on this card", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT (three dot-separated segments)", "Command Code": "Command Code", + "Coze 扣子": "", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT (three dot-separated parts; you can also paste a whole Bearer line or the data.bin content)", + "JWT(三段点分;也可直接粘贴 Bearer 整行)": "JWT (three dot-separated parts; pasting a whole Bearer line works too)", + "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT (three dot-separated parts; a whole chatglm_token=… line works too)", + "LobsterAI": "LobsterAI", + "MiniMax Code": "MiniMax Code", + "MiniMax Code 国内版": "MiniMax Code (China)", + "MiniMax Code 国际版": "MiniMax Code (International)", + "ModelScope 国内版": "ModelScope (China)", + "ModelScope 国际版": "ModelScope (International)", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode International edition", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM managed endpoint; apply for the Key at integrate.api.nvidia.com", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken is Accio's login credential (a long opaque token, not a JWT); refreshToken is optional and enables automatic renewal on expiry. Enter the credential of the {label} account — {siteNote} (the easiest way to get it: just use \"Web login\" above; no need to hunt for the token manually.)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken is Trae's Cloud-IDE-JWT (three dot-separated segments), and refreshToken enables automatic renewal on expiry (this provider rotates refreshToken on every use: once exchanged, the old one is void, so do not refresh the same account from two programs at once).
When pasting manually, fill in machineId / deviceId as well: upstream ties them to the device public key uploaded at sign-in to identify the device, and swapping in a new pair from nowhere triggers the 2xxxx family of device-binding rejections.
Without these three, the proper route is \"Web login\" above; for container / remote deployments, paste the callback URL from the address bar as prompted.", + "mmoat_… 或客户端 auth.json 里的 accessToken 值": "", + "mmoat_…(国际站 account.minimax.io 签发)": "", + "mmort_…(可选;有它才能自动续期)": "", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "The value of monkeycode_ai_session (you can also paste the whole Cookie)", + "ms- 开头的 API 令牌": "API token starting with ms-", + "openclaw.json 里 gateway.auth.token 的值": "", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken is optional and enables automatic renewal on expiry; both can be obtained from the Raccoon client login-state file.", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken is optional and enables automatic renewal; both can be obtained from the client login-state file. The same Cline account works in both pools; here it is grouped under {label}.", + "sessionid_ss=…; sid_guard=…; ttwid=…(或 Cookie 编辑器导出的 JSON)": "", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "token supports the enc: prefix (decrypted automatically on Windows). Without a refreshToken, automatic renewal is not possible.", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "token is the login-state Cookie; CatPaw has no refresh mechanism, so sign in again in the client after it expires.", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "API Key starting with user_ (you can also paste the whole Authorization: Bearer … line)", + "{\"access_token\":\"mv_…\",\"openid\":\"oAN1…\",\"guid\":\"a79a…\"}(整段粘贴)": "", "{label} 网页登录的打开方式": "How to open {label} web login", "{label} 账号地区": "{label} account region", "{label} 账号的添加方式": "How to add a {label} account", @@ -39,6 +56,7 @@ "{n} 个账号": "{n} accounts", "{n} 个账号,点击添加": "{n} accounts, click to add", "{seconds}s 后重发": "Resend in {seconds}s", + "⚠️ {label}账号已添加,但探活未通过:{message}": "", "✅ {label}账号已添加": "✅ {label} account added", "✅ {label}账号已添加{name}": "✅ {label} account added{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ Imported {n} providers from cc-switch", @@ -58,23 +76,30 @@ "不是有效的凭证文件:需要包含 accessToken 与 uid": "Not a valid credential file: accessToken and uid are required", "不是自定义提供商": "Not a custom provider", "不需要 Key:转发与拉取模型都不发送鉴权头": "No Key needed: neither forwarding nor model fetching sends auth headers", + "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "No sign-in needed: click the button below and the gateway will obtain a guest credential from Zhipu Qingyan directly and add it to the account list (you can fill in a note name first). The guest credential can be used for chat right away; the free quota is issued by the upstream per device.

Note: a guest account has no points system — its member points, daily sign-in points and activity claim endpoints all require sign-in, so those capabilities are skipped for guests (no readings are shown on the accounts page or in the check-in center). To get points, use \"Fill in credentials\" to paste a signed-in account token, or \"Import desktop sign-in state\".", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "Both editions' accounts can be saved at once, each with its own model catalog (the China and International editions are two providers that can be enabled and mapped separately).", "中国版": "China edition", "今天没有需要领的活动(都已到账)": "No campaigns to claim today (all already credited)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
": "Tried {attempts} times today (Beijing time {day}); {left} attempts left{next}
", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "Grab three items from another machine (or from the OfficeAce desktop app's models.json) and paste them here: the model gateway base URL (baseUrl, shaped like https://modelgw-…/v2) plus modelAppKey / modelAppSecret. These three are enough to forward requests (the gateway credential never expires); the control-plane temporary credentials (accessKeyId and so on) are optional — leaving them blank disables balance queries and auto-renewal but does not affect forwarding.", + "从本机 MiniMax Code 客户端读取国际版登录态(只读,不会改动客户端)": "", + "从本机 MiniMax Code 客户端读取登录态(只读,不会改动客户端)": "", "从本机导入桌面端登录态": "Import desktop login state from this machine", + "从本机马维斯客户端读取登录凭证(需要客户端已登录;会弹一次管理员授权)": "", + "令牌当前不可用": "", "任务镜像 id": "Task image id", "余额查询": "Balance query", "使用 Google 账号登录": "Sign in with Google", "使用 Zai 账号登录": "Sign in with Zai", "使用个人访问令牌(PAT)": "Use a personal access token (PAT)", + "先用管理员身份运行 _recon/marvis-cred-extract.py(从已登录的马维斯客户端内存里提取),然后把输出的 JSON 整段粘贴进来。需要三项:accessToken(mv_ 开头)、openid、guid。令牌没有续期接口,过期后重新提取一次即可(会更新同一条账号记录)。": "", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "Free models need no Key (\"This upstream needs no authentication\" is checked by default, and the gateway uses anonymous credentials automatically); for paid models, enter your Zen API Key. The free tier only accepts the official client form, so \"Emulate the official OpenCode client\" is on by default", "内嵌窗口": "Embedded window", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "Opens in the embedded window; closing it cancels the wait (if a Google account is rejected, switch to the system browser)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "Opens in the embedded window; added to the list when done; closing it cancels the wait", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "Opens in an embedded window; the account is added automatically once you have scanned — closing the window cancels the wait", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "The embedded window uses a fresh environment, so multiple accounts do not affect each other; closing it cancels the wait", + "内嵌窗口(必须)": "", "内嵌窗口(推荐)": "Embedded window (recommended)", "准备验证…": "Preparing verification…", "凭据 JSON": "Credential JSON", @@ -91,29 +116,38 @@ "取消失败:{message}": "Cancel failed: {message}", "取消等待": "Cancel sign-in", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "Only the Embedded window can restore third-party options: we cannot modify the sign-in page in the system browser", + "只有在「导入本机登录态」不可用时才需要:以管理员身份运行 _recon/marvis-cred-extract.py,把生成的 JSON 整段粘贴进来。需要三项:accessToken(mv_ 开头)、openid、guid。令牌没有续期接口,过期后重新导入一次即可(会更新同一条账号记录)。": "", "可选": "Optional", "可选(cloudaicompanionProject):留空由网关自动发现": "Optional (cloudaicompanionProject); if blank, the gateway discovers it automatically", + "可选,仅当令牌里读不出用户 id 时需要": "Optional; needed only when the user id cannot be read from the token", "可选,展示用": "Optional; for display", "可选,没有则无法自动续期": "Optional; without it, automatic renewal is not possible", + "可选,用于生成账号标识": "Optional; used to build the account identifier", "可选,留空使用凭据里的 user_name": "Optional; defaults to the user_name in the credential", "可选,留空使用账号姓名、邮箱或令牌指纹": "Optional; defaults to the account name, email, or token fingerprint", "可选,留空使用账号昵称或邮箱": "Optional; defaults to the account nickname or email", "可选,留空则用 key 尾号": "Optional; defaults to the key suffix", "可选,留空则用 userId": "Optional; defaults to userId", + "可选,留空则用上游账号昵称": "Optional; defaults to the upstream account nickname", "可选,留空则用上游返回的账号名或账号 id": "Optional; defaults to the account name or account id returned by upstream", + "可选,留空则用令牌尾号": "", "可选,留空则用凭证里的账号名": "Optional; defaults to the account name in the credential", "可选,留空则用文件里的昵称": "Optional; leave empty to use the nickname from the file", "可选,留空则用登录名或 uid": "Optional; defaults to the login name or uid", "可选,留空则用脱敏手机号": "Optional; defaults to the masked phone number", "可选,留空则用脱敏手机号或 userId": "Optional; defaults to the masked phone number or userId", "可选,留空则用账号 uk": "Optional; defaults to the account uk", + "可选,留空则用账号昵称": "Optional; defaults to the account nickname", "可选,留空则用账号昵称或 userId": "Optional; leave blank to use the account nickname or userId", "可选,留空则用邮箱或令牌尾号": "Optional; defaults to the email or token suffix", + "可选,留空用「AtomCode」": "Optional; defaults to “AtomCode”", "可选,留空用「OfficeAce 果办」": "Optional; defaults to “OfficeAce”", "可选,留空自动生成": "Optional; generated automatically if blank", "可选,留空自动用昵称": "Optional; defaults to the nickname", + "可选,给这个访客账号起个备注名": "Optional; a note name for this guest account", "可选,续期时带上": "Optional; sent when renewing", "可选,讯飞侧 userid": "Optional; the userid on the iFlytek side", + "可选,默认 9434": "", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "Optional; required to start a conversation. If blank, the gateway discovers it automatically from this account's existing tasks; if it cannot find one, fill it in manually later.", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "Optional; the account email, used for display and to recognize the identity (the same account is recognized even after switching to a new token)", "可选;UUID 形态,与凭据同生共死": "Optional; UUID form, lives and dies with the credential", @@ -124,6 +158,7 @@ "可领取": "Claimable", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "Keyed and no-auth accounts can coexist in one provider, each routed by its own rules; multiple no-auth accounts are allowed too (each with its own egress proxy)", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "Switch the protocol to anthropic on the same address to use its Claude-compatible endpoint. Forwarding requires an OpenCode Go API Key (subscription-based; fetching the model catalog needs no Key, but requests get a 401 \"Missing API key\" from upstream)", + "后端没有返回授权地址(登录发起失败)": "", "响应为空": "Empty response", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "SenseNova's OpenAI-compatible endpoint; balance query uses the console account and password (see \"Balance query\" below)", "国内版": "China edition", @@ -141,11 +176,16 @@ "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "The International edition's inference site is api.z.ai, a different site from the China edition's; plans are separate as well.", "国际版(WorkBuddy AI)": "International edition (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "International site Coding plan; the OpenAI-compatible address is https://api.minimax.io/v1", + "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "Paste the two values from the AtomCode page on AtomGit: accessToken and refreshToken. The refresh token is strongly recommended — this provider's token is not a JWT (it expires in about 7 days and carries no readable expiry), so an account without it cannot renew itself and must be re-authorised once it expires.", "在 cc-switch 里找到": "Found", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "Complete the official sign-in in the embedded window; the account is added to the list automatically on success.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "Generate a PAT in the Qoder account settings → Integrations for {label} (do not use a Google / GitHub token). Accounts and credentials do not carry over between the two regions.", - "在打开的官方 Web 控制台里用微信扫码登录(窗口会自动弹出登录面板;若没有,点左下角头像 → 立即登录)。登录成功后自动加入账号列表。": "Scan the WeChat QR code in the official web console that opens (the sign-in panel pops up automatically; if it does not, click the avatar at the bottom left and choose \"Sign in\"). The account is added automatically once you are signed in.", + "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "Scan the WeChat QR code on the official authorization page that opens (the QR code appears right away). The account is added to the list automatically after a successful scan.", + "在打开的官方登录页里完成登录,成功后自动加入账号列表。": "Complete the sign-in on the official page that opens; the account is added to the list automatically on success.", + "在打开的登录页里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "Sign in to the KukuAI main site with your Baidu account on the login page that opens (SMS code or QR scan both work); the account is added to the list automatically on success. The gateway exchanges it with Baidu for a KukuAI business session token (requires the KukuAI client installed on this machine).", + "在打开的登录页里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "Sign in with your Google account on the login page that opens (Antigravity's authorization page); the account is added to the list automatically once authorization completes.", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "In the window that opens, sign in to the KukuAI main site with your Baidu account (SMS code or QR scan both work); the account is added to the list automatically on success. The gateway exchanges it with Baidu for a KukuAI business session token (requires the KukuAI client installed on this machine).", + "在打开的窗口里登录 Coze(抖音账号 / 微信扫码 / 手机号均可),登录成功后自动加入账号列表。请用内嵌窗口:系统浏览器里网关读不到登录窗口的 Cookie。": "", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "Sign in with your Google account in the window that opens (Antigravity's authorization page); the account is added to the list automatically once authorization completes.", "在浏览器中打开登录页": "Open the sign-in page in your browser", "地区": "Region", @@ -165,6 +205,8 @@ "导入所选": "Import selected", "导入所选({n})": "Import selected ({n})", "导入桌面端登录态": "Import desktop login state", + "将在新标签页中打开授权页;完成后自动加入账号列表。": "The authorization page opens in a new tab; the account is added to the list automatically when done.", + "将在新标签页中打开授权页;完成后自动加入账号列表。若页面跳回本机地址后账号没有出现,按提示把地址栏里的完整回调地址粘贴回来即可。": "The authorization page opens in a new tab; the account is added to the list automatically when done. If the page jumps back to a local address and the account does not appear, paste the full callback URL from the address bar when prompted.", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "The embedded window will open; the account is added to the list automatically once authorization completes. Closing the window cancels the wait. The authorization page may end with a message that it cannot open the zcode:// link — that is normal; the result has already been recorded server-side", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "The embedded window will open; the account is added to the list automatically once authorization completes. On a remote panel you can paste the callback URL. Closing the window cancels the wait. The link is valid for 5 minutes; if it times out or is not clicked, it is voided (you can start again)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "The embedded window will open; the account is added to the list automatically after sign-in. Closing the window cancels the wait", @@ -173,6 +215,7 @@ "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "The authorization page will open in your system default browser (reusing the Huawei Cloud account already signed in there); the gateway polls the cloud for the authorization code, so it does not matter which machine the browser runs on. The account is added to the list automatically once done", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "The authorization page will open in your system default browser (reusing the Huawei Cloud account already signed in there); when the browser and the gateway are not on the same machine, paste the final callback URL back into the panel as described above", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "The sign-in page will open in your system default browser (reusing the Accio account already signed in there); the account is added to the list automatically after sign-in, and closing the popup cancels the wait", + "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "The sign-in page will open in your system default browser (reusing the LobsterAI account already signed in there); the account is added to the list automatically after sign-in, and closing the popup cancels the wait", "小米 MiMo Token Plan 套餐(新加坡站)": "Xiaomi MiMo Token Plan (Singapore site)", "已到账": "Credited", "已取消登录等待": "Pending sign-in canceled", @@ -187,24 +230,32 @@ "已点发送,等待短信…": "Sent; waiting for the SMS…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "Sign-in page opened in your system default browser; complete sign-in in the browser…", "已领取,等官方确认": "Claimed, awaiting official confirmation", + "开始登录(获取设备码)": "", + "当前壳不支持直接打开浏览器,请手动访问:{url}": "", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "This app version does not support AutoClaw web login; update the app", "当前没有可领取的套餐(活动尚未开始)": "No plans are available to claim right now (the event has not started yet)", "当前环境不支持领取(桥接方法缺失)": "Claiming is not supported in this environment (bridge method missing)", "必填,CatPaw 账号标识": "Required; CatPaw account identifier", "恢复 Google / GitHub 入口": "Restore Google / GitHub options", + "手工粘贴凭证": "", "手机号": "Phone number", "手机验证码登录": "SMS code login", + "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "Open the official LobsterAI sign-in page (lobsterai.youdao.com) and sign in with your LobsterAI account: once finished, the official page returns to this machine and the account is added automatically — the callback lands on the gateway's own port, so the browser must be on the same machine as the gateway; the auth code is only handed to the gateway locally and no plaintext token is shown in the UI.", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Open the official authorization page for Trae SOLO (trae.cn) and sign in with your Trae account: once authorized, the official page redirects back to a temporary port on this machine; a same-machine deployment receives it automatically, while Docker / remote panels should copy the callback URL from the address bar as prompted.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Open the official sign-in page for Accio {label} ({site}) and sign in with your Accio account: on success the official page redirects back to this machine; a same-machine setup completes automatically, while Docker / remote panels just paste the callback URL as prompted (the authorization code is passed to the gateway only on this machine, and no plaintext token is shown in the UI).", "打开 Accio {label}登录页": "Open the Accio {label} sign-in page", "打开 Antigravity 网页登录": "Open Antigravity web login", + "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "Open the AtomGit authorization page and sign in: once finished, the gateway automatically polls the broker for the token and adds the account (the callback is terminated by the broker and never returns to this machine, so no callback URL needs to be filled in).", "打开 CatPaw 网页登录": "Open CatPaw web login", "打开 Cline 授权页": "Open Cline authorization page", "打开 CodeArts 授权页": "Open CodeArts authorization page", + "打开 LobsterAI 登录页": "Open the LobsterAI sign-in page", "打开 Qoder 网页登录": "Open Qoder web login", "打开 Trae 授权页": "Open Trae authorization page", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "Open the official authorization page for ZCode {label} and sign in with your account. The authorization result is recorded by the ZCode server; the gateway fetches the credential in the background and adds the account to the list.", "打开 ZCode {label}授权页": "Open the ZCode {label} authorization page", + "打开千问办公的官方授权页完成设备码授权:登录完成后网关自动拿到登录态并加入账号列表(不需要手工回填任何回调地址)。": "Open QwenWork's official authorization page and complete the device-code authorization: once signed in, the gateway picks up the sign-in state automatically and adds the account to the list (no callback URL to fill in by hand).", + "打开千问办公网页登录": "Open QwenWork web login", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Open the official Huawei Cloud CodeArts authorization page to sign in: once done, the official page brings the browser back to the gateway's own /oauth/callback, and the gateway exchanges the one-time authorization code for temporary credentials and adds the account to the list.
When the gateway runs on another machine, the web UI prompts you to paste the final callback URL from the address bar back into the panel.", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Open the Huawei Cloud authorization page and sign in: once finished, the gateway automatically polls the cloud for the authorization code and adds the account to the list (no OfficeAce desktop app required, and no need to paste a callback URL back manually).", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "Open the official authorization page and complete the device-code authorization; you will be signing in to a {label} ({site}) account.", @@ -238,23 +289,31 @@ "模型网关基址": "Model gateway base URL", "正在打开百度登录页…": "Opening the Baidu sign-in page…", "正在扫描本机 cc-switch…": "Scanning cc-switch on this machine…", + "正在换发访客凭证…": "Requesting a guest credential…", + "正在添加…": "Adding…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "Waiting for {name} sign-in to finish; you can start a new sign-in only after it completes or is canceled", "每周": "Weekly", "每日": "Daily", "每月": "Monthly", "没有则无法自动续期": "Without it, automatic renewal is not possible", "没有匹配「{keyword}」的提供商": "No providers match \"{keyword}\"", + "添加 QClaw 账号": "", "添加 {name} 账号": "Add {name} account", "添加中…": "Adding…", "添加到 ": "Add to ", + "添加千问办公账号": "Add QwenWork account", "添加失败:{reason}": "Add failed: {reason}", "添加小浣熊账号": "Add Raccoon account", "添加方式": "Add method", + "添加访客账号": "Add a guest account", "添加账号": "Add account", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Volcano Engine Coding plan; the general inference endpoint is https://ark.cn-beijing.volces.com/api/v3", "点击添加账号": "Click to add an account", "点开即预填,填 Key 接入": "Opens prefilled; enter your Key to connect", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "After clicking the button, first pass a slider verification (the vendor's risk control), then the sign-in page opens and the account is added automatically once you sign in.
This is the only official sign-in method for the International edition; if you are already signed in to the client, \"Import desktop login state\" is faster.", + "点按钮后网关会申请一个设备码并在窗口里打开授权页;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
授权在浏览器里完成,网关不接触密码;跨机部署(Docker / 远程面板)同样可用。": "", + "点按钮后网关会申请一个设备码,并把授权页打开;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
国际版与国内版是两套账号,请用国际站(agent.minimax.io)的账号登录。": "", + "点按钮后网关会申请一个设备码,并把授权页打开;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
授权在浏览器里完成,网关不接触密码;跨机部署(Docker / 远程面板)同样可用。": "", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "Sign in with the phone number bound to your AutoClaw account: click \"Get code\", then enter the SMS code below and sign in. The code is submitted from this machine directly to the official API; no token is shown in the UI.", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "Sign in with your CatPaw account (Meituan Passport); the account is added to the list automatically on success.", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Sign in with the phone number bound to your Loomy account: click \"Get code\" and enter it. This is the easiest way in for this provider.", @@ -270,38 +329,55 @@ "登录已结束": "Sign-in has ended", "登录并添加": "Sign in and add", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "Login-state file path: ~/.box-agent/config/auth.json (Windows: C:\\Users\\\\.box-agent\\config\\auth.json)", + "登录态由壳侧从窗口的 Cookie 存储读出;完成后自动加入列表": "", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "Delete custom provider \"{name}\"? This will also delete {count} accounts under it; deletion cannot be undone.", "等待 Accio {label}登录完成…": "Waiting for Accio {label} sign-in…", + "等待 AtomCode 登录完成…": "Waiting for AtomCode sign-in…", "等待 CatPaw 登录完成…": "Waiting for CatPaw sign-in…", "等待 Cline 授权确认…": "Waiting for Cline authorization confirmation…", "等待 CodeArts 登录完成…": "Waiting for CodeArts sign-in…", + "等待 Coze 登录完成…": "", "等待 Google 授权完成…": "Waiting for Google authorization…", "等待 KukuAI 登录完成…": "Waiting for KukuAI sign-in…", + "等待 LobsterAI 登录完成…": "Waiting for LobsterAI sign-in…", "等待 OfficeAce 登录完成…": "Waiting for OfficeAce sign-in…", "等待 OrcaTerm 登录完成…": "Waiting for OrcaTerm sign-in…", "等待 Qoder 授权完成…": "Waiting for Qoder authorization…", "等待 Trae 授权完成…": "Waiting for Trae authorization…", "等待 ZCode {label}授权完成…": "Waiting for ZCode {label} authorization…", + "等待千问办公授权完成…": "Waiting for QwenWork authorization…", "等待小浣熊登录完成…": "Waiting for Raccoon sign-in…", + "等待授权超时,请重新发起": "", + "等待浏览器里确认授权…": "", "等待登录完成…": "Waiting for sign-in…", "等待网页登录…": "Waiting for web login…", "粘贴 API Key": "Paste API Key", + "粘贴 API 令牌": "Paste API token", "粘贴 Accio 的 accessToken": "Paste the Accio accessToken", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Paste Antigravity's Google refresh token (starts with 1//; pasting the whole line containing refresh_token= also works).
This is a manual entry besides web login (for example, exported from the official IDE you are already signed in to). The refresh token does not expire, and the gateway renews the access token automatically when it expires; if the token is lost, you can only go through Google authorization again, so keep it safe.", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Paste the Command Code API Key (starts with user_; get it from the official CLI's ~/.commandcode/auth.json or the web studio). Pasting the whole line / with quotes / with the Bearer prefix all work; the gateway strips it down to the bare key itself. The key does not expire and there is no renewal API, so if it stops working just get a new one. When adding, the gateway runs a lightweight liveness check; a failure does not affect adding.", "粘贴 Cookie": "Paste Cookie", "粘贴 Google refresh token": "Paste Google refresh token", + "粘贴 ModelScope 的 API 令牌(ms- 开头,在 {site} 的「访问令牌」页获取)。
{siteNote}
这枚令牌两用:网关既用它调推理,也用它读账号身份、魔粒余额与每日签到。令牌长期有效、没有续期接口,失效后重新获取再粘一次即可。": "Paste a ModelScope API token (starts with ms-; create one on the \"Access tokens\" page of {site}).
{siteNote}
The same token serves two purposes: the gateway uses it both to call inference and to read the account identity, the credits balance and the daily check-in. The token is long-lived and there is no renewal API — if it stops working, just create a new one and paste it again.", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "Paste OrcaTerm's OAuth accessToken (a JWT with three dot-separated parts; available from the desktop client's data.bin or the web version's ot_session cookie). Pasting a whole line containing Bearer / ot_session= is fine too — the gateway strips it down to the bare token. After adding, the gateway renews it automatically (the upstream has a renewal API), so you do not need to follow the 2-hour expiry by hand.", + "粘贴 QClaw 本机网关的令牌(~/.qclaw/openclaw.json 里 gateway.auth.token 的值)。一般用「从本机导入」就够了;这里适合读不到配置文件时手动兜底 —— 端口留空则用默认的 9434。粘贴后会先连一次网关做校验,通过才落账号。": "", "粘贴 Qoder 个人访问令牌(pt-…)": "Paste the Qoder personal access token (pt-…)", "粘贴 accessToken": "Paste accessToken", "粘贴 access_token(一长串 JWT)": "Paste access_token (a long JWT)", "粘贴 session cookie": "Paste session cookie", "粘贴 token / refreshToken": "Paste token / refreshToken", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "Paste a token starting with workos:… (the prefix is added automatically if missing)", + "粘贴令牌对": "Paste token pair", + "粘贴凭证三件套": "", + "粘贴千问办公的登录令牌(JWT,三段点分)。一般用「从本机导入」就够了;这里适合换机器迁移、或本机解密失败时手动兜底 —— 此时还需要一并填写 userId(令牌里解不出时会提示)。粘贴后会先向上游校验一次,校验通过才落账号。": "Paste QwenWork's login token (a JWT with three dot-separated parts). Usually \"Import from this machine\" is enough; this manual route is for migrating to another machine, or as a fallback when local decryption fails — in that case also fill in userId (you will be prompted when the user id cannot be read from the token). After pasting, it is verified against upstream once, and the account is only saved if that passes.", + "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "Paste a Zhipu Qingyan access token (a JWT with three dot-separated parts). How to get it: open chatglm.cn in a browser, sign in, then copy the value of the chatglm_token cookie from Application → Cookies in the developer tools (pasting the whole chatglm_token=…; line works too). It is verified against the upstream once after pasting, and the account is only saved if that passes; signed-in accounts get points and sign-in capability, and the gateway renews it automatically afterwards (the upstream has a renewal API).", + "粘贴登录令牌": "Paste login token", "粘贴登录凭据": "Paste login credentials", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "What you paste is the login session (which is also the model gateway token). Loomy has no renewal API; the session is valid for about 14 days, so just sign in again after it expires.", + "粘贴网关令牌": "", "粘贴网关凭据": "Paste gateway credentials", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
{siteNote}
MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "Paste the value of the web session cookie monkeycode_ai_session (copying the whole Cookie in the browser and pasting it directly also works; the gateway extracts the value itself).
{siteNote}
MonkeyCode has no renewal API: the session is valid for about 30 days (a hard limit); once it expires, sign in to the web page again and paste it one more time.", + "粘贴访问令牌": "Paste an access token", "系统浏览器": "System browser", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "The system browser can reuse an existing login state (recommended for the International edition); the embedded window is cleaner.", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "Opens in the system browser (reuses the signed-in Cline account); added to the list when done", @@ -310,16 +386,25 @@ "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "Opens in the system browser (reuses the signed-in Meituan account); added to the list when done", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "Opens in the system browser (reuses a signed-in account); added to the list when done", "系统默认浏览器": "System default browser", + "续期令牌": "Refresh token", "编码套餐 API Key": "Coding Plan API Key", "网关 Secret": "Gateway secret", + "网关会以管理员身份读取马维斯客户端的内存来取凭证 —— 客户端的令牌不落明文(登录库是密文、密钥容器校验调用方签名),只在进程内存里,而客户端进程又设了访问限制,普通权限读不到。所以这一步会弹一次 UAC,点「是」即可,之后无需再操作。请确保客户端处于登录状态。": "", "网页登录": "Web login", "网页登录的打开方式": "How to open web login", "网页登录(Zai / Google)": "Web login (Zai / Google)", + "网页登录(浏览器授权)": "", "自定义": "Custom", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "Self-hosted aggregation panel: fill in your own site address. Balance query calls {base}/v1/usage with this account's API key; no extra setup", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "Self-hosted aggregation panel: fill in your own site address; balance query additionally needs a NewAPI personal access token (see \"Balance query\" below)", "获取风控配置失败,将直接尝试领取:{message}": "Failed to get the risk-control config; trying to claim directly: {message}", "获取验证码": "Get code", + "设备码": "", + "设备码 5 分钟内有效;在授权页确认后这里会自动完成": "", + "设备码 5 分钟内有效;窗口里显示「等待你在浏览器里确认」时去授权页点确认即可": "", + "访客模式": "Guest mode", + "访客模式(免登录)": "Guest mode (no sign-in)", + "访问令牌": "Access token", "该上游无需鉴权(不发送鉴权头)": "This upstream needs no authentication (no auth headers are sent)", "该提供商": "This provider", "该提供商账号添加功能即将上线": "Account adding for this provider is coming soon", @@ -332,11 +417,18 @@ "请填写 {label}": "Enter {label}", "请填写名称": "Enter a name", "请完成验证…": "Complete the verification…", + "请粘贴 www.coze.cn 的完整 Cookie(浏览器开发者工具 → Network → 任取一条 www.coze.cn 请求的 Cookie 头,或 Cookie 编辑器一键导出)。整包带上最稳:任务中心与机器人列表只认 Cookie,少了几项会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {name: value} 对象。Coze 没有刷新机制,登录态过期后重新登录或重新粘贴即可。": "", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "Reads and decrypts %APPDATA%/AutoClaw/auth.json; Windows only", + "读取 %USERPROFILE%/.minimax/auth 下客户端已保存的令牌,只读不写 —— 客户端用一对必须相等的版本号判断「是否在别处登录过」,写一次就会让它要求你重新扫码。注意:导入是一次快照,之后网关与客户端各自续期,两边的刷新令牌会互相轮换,建议只在一处使用。": "", + "读取 %USERPROFILE%/.minimax/auth/prod/en 下客户端已保存的令牌,只读不写。客户端按「环境 / 地区」分目录存两套登录态,国际版在 en 目录里;没在国际站登录过时这里读不到,请改用上面的网页登录。": "", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "Reads ~/.cline/data/settings/providers.json; requires being signed in to the Cline client", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "Reads ~/.meituan-catpaw/auth.json; requires being signed in to the CatPaw client", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "Reads the local KukuAI client login state (%APPDATA%\\baidugenflowpro\\Network\\Cookies); requires being signed in to the KukuAI client", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "Read the local OrcaTerm client sign-in state (%APPDATA%\\com.orcaterm-desktop.app\\data.bin); the OrcaTerm client must be signed in", + "读取本机 QClaw 客户端网关配置(%USERPROFILE%\\.qclaw\\openclaw.json),需 QClaw 正在运行": "", + "读取本机千问办公客户端登录态(%APPDATA%\\QwenWorkCN\\auth-v2.dat),需已在客户端登录过": "Reads the local QwenWork client sign-in state (%APPDATA%\\QwenWorkCN\\auth-v2.dat); requires having signed in to the client", + "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "Read the local Zhipu Qingyan client sign-in state (%APPDATA%\\chatglm\\Network\\Cookies); the client must be signed in", + "读本机 QClaw 客户端拉起的 OpenClaw 网关配置(端口 + 令牌)。与别家不同,本家需要 QClaw 正在运行 —— 网关是客户端拉起的,端口和令牌都以它在运行为前提;后端会真连一次做校验。": "", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "Reads the account currently logged in to the official client (Tencent WorkBuddy / WorkBuddy AI) on this machine: the newest session file in its credential directory is located automatically (the official client must be logged in on this machine). The gateway renews the token after import — no need to swap tokens manually with the 2-hour expiry.", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "Reads the local client's current login state (plaintext Cookie, no decryption needed). The Cookies file is exclusively locked while the client runs, so close the KukuAI client before importing.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state).", @@ -345,22 +437,29 @@ "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state). Users who sign in to the client with Zai / Google should use this one.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state). This one is grouped under {label}.", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "Reads the OAuth token the local client is currently signed in with (plain-text JSON, no decryption needed). After importing, the gateway renews it automatically — no need to follow the client's 2-hour expiry by hand.", + "读本机客户端当前的登录态。它是 Chromium os_crypt 加密的(DPAPI 主密钥 + AES-256-GCM),因此仅 Windows 可用。DPAPI 按当前 Windows 用户解密,不需要客户端正在运行 —— 只要在本机登录过一次即可(换机器时请在目标机器上重新登录一次客户端)。": "Reads the current sign-in state of the local client. It is Chromium os_crypt-encrypted (DPAPI master key + AES-256-GCM), so it is Windows-only. DPAPI decrypts as the current Windows user and does not require the client to be running — just sign in once on this machine (when moving machines, sign in to the client once on the target machine).", + "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "Read the current sign-in state of the local client. The client's cookies are DPAPI-encrypted (Chromium os_crypt), so this is Windows-only; the file may be unreadable while the client is running — quit the client and retry (or use \"Fill in credentials\" to paste a token instead).", "账号信息": "Account info", "账号信息不完整,请刷新后重试": "Account info is incomplete; refresh and try again", "账号已添加,但请留意:{warning}": "Account added, but note: {warning}", "账号版本": "Account edition", "账号类型": "Account type", "账号:{name}
": "Account: {name}
", + "贴国际站的访问令牌(mmoat_ 开头)。国内站的令牌拿到这里会被上游拒(401)—— 两地账号体系不同,不能互换。": "", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Routes through Inference Providers; create the Key at hf.co/settings/tokens", "还没有账号": "No accounts yet", "这一家当前不支持网页登录,请改用填写凭证": "This provider does not support web login right now; use Enter credentials instead", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "This account has no campaigns that can be claimed automatically right now (only the daily login gift points are claimed automatically)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "This quota goes through the Start Plan channel: if forwarding reports that the Coding Plan has expired, ", + "这里填国内站(modelscope.cn)的账号:请粘贴国内站的令牌。国际站(modelscope.ai)是另一家 provider、另一套账号,两边的令牌不通用。": "This is for the China site (modelscope.cn) account: paste the China site's token. The international site (modelscope.ai) is another provider with its own account; tokens are not interchangeable between the two.", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "This is for the China site (monkeycode-ai.com) account: paste the China site's session. The international site (monkeycode-ai.net) is another provider with its own account; credentials are not interchangeable between the two.", + "这里填国际站(modelscope.ai)的账号:请粘贴国际站的令牌。国内站(modelscope.cn)是另一家 provider、另一套账号,两边的令牌不通用。": "This is for the international site (modelscope.ai) account: paste the international site's token. The China site (modelscope.cn) is another provider with its own account; tokens are not interchangeable between the two.", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "This is for the international site (monkeycode-ai.net) account: paste the international site's session. The China site (monkeycode-ai.com) is another provider with its own account; credentials are not interchangeable between the two.", + "适合已在别处登录、或客户端数据目录被改过的场景。只需要 accessToken(mmoat_ 开头);JWT 里能读出身份与到期,无需再填别的。只给访问令牌时约 1 小时有效、无法自动续期 —— 想要自动续期请用上面的网页登录。": "", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Select the CatPaw client's login-state file auth.json (Windows: C:\\Users\\\\.meituan-catpaw\\auth.json; macOS / Linux: ~/.meituan-catpaw/auth.json). After import the credentials live on the gateway and can be forwarded independently — no longer dependent on this machine's client.", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Select the Cline client's login-state file providers.json (Windows: C:\\Users\\\\.cline\\data\\settings\\providers.json; macOS / Linux: ~/.cline/data/settings/providers.json). After import the credentials live on the gateway and can be forwarded independently — no longer dependent on this machine's client; this record goes into {label}.", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "Select the KukuAI client's Cookies file (%APPDATA%\\baidugenflowpro\\Network\\Cookies): it is a **binary** SQLite database (no extension, not JSON). The import reads a copy of the file you selected and does not compete with the running client for the lock, so **you do not need to close the client first**. After import the credentials live on the gateway and can be forwarded independently.", + "选择一份存着 www.coze.cn Cookie 的文本文件(整段 Cookie 头或 Cookie 编辑器导出的 JSON 都可以)。导入后凭证直接落在网关、可独立转发,不依赖本机客户端(Coze 客户端运行时占着 Cookie 库,读不了)。": "", "选择凭证文件导入": "Import from credential file", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "Select the official client's session credential file (workbuddy-desktop.info / workbuddy-desktop-ai.info) or an account JSON exported by wb2api (nested or flat).", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Select the Raccoon client's login-state file auth.json (Windows: C:\\Users\\\\.box-agent\\config\\auth.json; macOS / Linux: ~/.box-agent/config/auth.json). After import the credentials live on the gateway and can be forwarded independently — no longer dependent on this machine's client.", @@ -379,6 +478,7 @@ "领取失败:{message}": "Claim failed: {message}", "领取并让官方确认": "Claim and await confirmation", "风控验证配置不完整,请稍后重试": "Risk-control verification config is incomplete; try again later", + "马维斯 Marvis": "", "验证码": "Verification code", "验证码已发送。收到后填入下方并点「登录并添加」": "Code sent. Enter it below and click \"Sign in and add\"", "验证码已发送,请查看手机短信": "Code sent; check your SMS", diff --git a/desktop-tauri/i18n/i18n-src/en/agent.json b/desktop-tauri/i18n/i18n-src/en/agent.json new file mode 100644 index 00000000..01a47ad0 --- /dev/null +++ b/desktop-tauri/i18n/i18n-src/en/agent.json @@ -0,0 +1,102 @@ +{ + "· 走网关": "· via gateway", + "。这里的开关与还原作用在网关所在的机器上。": "", + "一键把本机 AI 客户端接到本地网关:改写它自己的配置文件,保留其它设置": "Point local AI clients at the local gateway: rewrite their config files and leave every other setting alone", + "上游模型 id,例如 raccoon-coder-pro": "Upstream model id, e.g. raccoon-coder-pro", + "下面是这个客户端文件里现有的配置(只读)。点「从客户端导入」把它收进我的配置,之后就能编辑和切换。": "These are the profiles already in this client's config file (read-only). Click \"Import from client\" to copy them into my profiles — then they can be edited and switched.", + "个 · 已安装": " · installed", + "中转": "Relay", + "从客户端导入": "Import from client", + "会用最近一次备份覆盖它的配置文件。这一步不会改动你在本页存的配置列表。": "Overwrites its config file with the most recent backup. The profile list on this page is untouched.", + "低": "Low", + "保存修改": "Save changes", + "共": "Total", + "写入中…": "Writing…", + "写入后需要重开会话才生效": "Restart the session after writing for it to take effect", + "写入失败:{reason}": "Write failed: {reason}", + "写入客户端": "Write to client", + "写入预览": "Write preview", + "单配置": "Single profile", + "单配置文件": "", + "只改 enabled 与默认项,其余条目原样保留": "Only the enabled and default flags change; every other entry is kept as-is", + "只改属于我们的那几个键,其它设置原样保留": "Only the keys we own change; every other setting is kept as-is", + "可同时启用多条;切换只改 enabled 与默认项": "Several entries can be enabled at once; switching only changes the enabled and default flags", + "同一时刻只有一套生效;写入 = 把这一套整段写进客户端文件": "Only one profile is active at a time; writing copies that profile into the client config file", + "启用 {name}": "Enable {name}", + "备注": "Note", + "多套配置": "Multiple profiles", + "多配置 · 已启用 {n}": "Multi-profile · {n} enabled", + "官方直连": "Official direct", + "客户端列表加载失败:{reason}": "Failed to load the client list: {reason}", + "客户端清单": "Clients", + "客户端自己的设置优先;这里写的是写入时带进去的初值": "The client's own settings win; this is only the initial value written in", + "已保存": "Saved", + "已写入 {name}": "Written to {name}", + "已写入 {name};这个客户端只在启动时读配置,重开一个会话才生效": "Written to {name}; this client reads its config at startup, so reopen a session for it to take effect", + "已安装": "Installed", + "已按备份还原 {name}": "Restored {name} from backup", + "已收进我的配置:{n} 条": "Imported into my profiles: {n}", + "已还原「{name}」": "", + "当前环境不支持保存(桥接方法缺失)": "Saving is unavailable here (bridge method missing)", + "当前环境不支持写入(桥接方法缺失)": "Writing is unavailable here (bridge method missing)", + "当前环境不支持客户端接管(桥接方法缺失)": "Client takeover is unavailable here (bridge method missing)", + "当前环境不支持导入(桥接方法缺失)": "Importing is unavailable here (bridge method missing)", + "当前环境不支持还原(桥接方法缺失)": "Restoring is unavailable here (bridge method missing)", + "手机上只能查看与还原接管。改写配置内容请回桌面端": "", + "按备份还原": "Restore from backup", + "按备份还原 {name}?": "Restore {name} from backup?", + "接管会把客户端的 base_url 指向本网关,并在写入前自动备份原文件。": "", + "搜索客户端": "Search clients", + "搜索客户端 / 配置文件…": "Search clients or config files…", + "改写网关所在机器上的客户端配置": "", + "改动": "Changed", + "新增": "Added", + "新建配置": "New profile", + "新配置": "New profile", + "无配置": "No profile", + "暂无预览": "No preview yet", + "未安装": "Not installed", + "未接管": "", + "未检测到": "Not detected", + "未检测到安装": "No installation detected", + "未走网关": "Not via gateway", + "本机": "This device", + "本机服务": "Local service", + "本机没有找到 {name}": "{name} was not found on this machine", + "本机网关": "Local gateway", + "来自客户端文件,只读": "From the client config, read-only", + "模型 id": "Model id", + "正在生成预览…": "Generating preview…", + "正在读取本机客户端…": "Reading local clients…", + "每个客户端可以有": "Each client can have", + "没有匹配的客户端": "No matching clients", + "没有可导入的新条目(可能还没配过,或已经在我的配置里)": "No new entries to import (not configured yet, or already in my profiles)", + "没有新的条目可收(都已在我的配置里)": "No new entries to import (all are already in my profiles)", + "没有检测到可接管的客户端": "", + "确定按最近一次备份还原「{name}」?": "", + "第三方中转": "Third-party relay", + "编辑:{name}": "Edit: {name}", + "行号由后端逐行比对给出": "Line numbers come from the backend line-by-line comparison", + "装好后回到这一页点「重新检测」,它会出现在上面的列表里。": "Once installed, come back to this page and click \"Recheck\" — it will show up in the list above.", + "设为默认": "Set as default", + "读取客户端配置失败:{reason}": "Failed to read the client config: {reason}", + "走网关": "Via gateway", + "还原中…": "", + "还原失败:{reason}": "Restore failed: {reason}", + "还没有配置": "No profiles yet", + "还没有配置。点「新建配置」建一套指向本机网关的,再写入客户端。": "No profiles yet. Click \"New profile\" to create one pointing at the local gateway, then write it to the client.", + "还没有预览。选一套配置后这里会显示写入客户端之后的样子。": "No preview yet. Pick a profile and this shows what the client config will look like after writing.", + "这个客户端会自己重读配置,写入后开新会话即生效。": "This client reloads its config on its own; open a new session after writing.", + "这个客户端只在启动时读配置:写入后重开一个会话才生效。": "This client only reads its config at startup: reopen a session after writing.", + "这个客户端的配置里没有读出地址(可能还没配过,或形状不认识)": "No address could be read from this client's config (it may not be configured yet, or the shape is unrecognized)", + "这个客户端自己支持多套配置并存": "This client supports several profiles side by side", + "这个客户端自己管理多套配置,可同时启用多条;切换只改 enabled 与默认项,不动它的模型清单。": "This client manages several profiles itself and can enable more than one; switching only changes the enabled and default flags, never its model list.", + "这套配置是干什么用的(只显示在本页)": "What this profile is for (shown on this page only)", + "选一个客户端开始": "Pick a client to start", + "配置": "Profile", + "配置来源": "Profile source", + "需重开会话生效": "Restart the session to apply", + "高": "High", + "(手机读不到本机文件)": "", + "(本机网关 / 官方直连 / 第三方中转 / 本机服务),随时切换。单配置客户端切换 = 整段覆写并备份原文件;多配置客户端(如 ZCode)只改 enabled 与默认项,它自己的条目一律不动。": "(local gateway / official direct / third-party relay / local service) and switch between them at any time. For single-profile clients, switching rewrites the whole profile and backs up the original file; for multi-profile clients such as ZCode it only changes the enabled and default flags and never touches their own entries." +} diff --git a/desktop-tauri/i18n/i18n-src/en/assistant.json b/desktop-tauri/i18n/i18n-src/en/assistant.json index bb9e31da..e9c8a068 100644 --- a/desktop-tauri/i18n/i18n-src/en/assistant.json +++ b/desktop-tauri/i18n/i18n-src/en/assistant.json @@ -1,32 +1,147 @@ { "CodeArts 的账号余额还剩多少?": "How much credit is left on the CodeArts account?", + "Enter 发送 · Shift+Enter 换行": "", + "section 只能是 models / mappings / both": "section must be models / mappings / both", + "{n} / {total}": "", + "{n} 个账号正在限流:": "", + "{n} 家": "", + "{n} 条改动": "", + "{n} 条映射": "{n} mappings", + "✓ 完成": "", + "✗ 失败": "", + "「{name}」超过 {n} MB,换一张小一点的。": "", + "一次最多 {n} 张图片。": "", + "一次最多 {n} 张图片,多出的没有添加。": "", + "上面四个快捷查询直接读网关数据,不经过模型": "", + "不指定,交给映射绑定": "", + "今天用了 {tokens} Token,共 {requests} 次请求。": "", + "今天用了多少 Token?": "How many tokens did I use today?", + "会话响应不完整": "Incomplete session response", + "会话里的选择优先于这里的默认值": "", + "余额是缓存读数,点账号详情里的「刷新凭证」可触发实时查询。": "", + "作为 system 消息注入": "", + "保存思考等级失败:{message}": "Failed to save reasoning level: {message}", "修改模型能力": "Edit model capabilities", + "共 {n} 个模型条目。": "", + "共 {n} 个模型条目。映射与改名请在桌面端「模型管理」里做。": "", + "共 {n} 个账号,前 {m} 个的余额读数:": "", + "删除会话失败:{message}": "Failed to delete session: {message}", + "删除这条对话": "Delete this conversation", + "助手设置": "", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "Assistant settings could not be read, so the chat model can't be saved yet — see the hint below the input box.", + "助手设置没读出来,暂时存不了对话模型。": "", + "助手设置没读出来,暂时存不了思考等级 —— 看输入框下方的提示。": "Assistant settings could not be loaded, so the reasoning level cannot be saved right now — see the hint below the input box.", + "助手设置没读出来,暂时存不了思考等级。": "", + "助手设置没读出来,现在还不能发消息 —— 点右上角「助手设置」看看。": "", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "Assistant settings could not be read, so messages can't be sent yet — see the hint below the input box.", + "勾选即注入": "", + "历史对话": "Conversation history", + "原先在「设置 → AI 助手」的配置都搬到这里了": "", + "发消息或做任务…(Enter 发送,Shift+Enter 换行,可粘贴图片)": "", "发起模型测试": "Run a model test", + "变更前确认": "", + "只支持图片文件(PNG / JPG / WebP / GIF)。": "", + "只读 · 不会改动配置": "", + "只跳过确认,不扩大工具权限;每次执行仍会记录在对话里。": "", + "各账号余额还剩多少?": "How much balance is left on each account?", "启用 / 禁用模型": "Enable / disable models", "启用 / 禁用账号": "Enable / disable accounts", + "哪些模型在多家重复了?": "Which models are duplicated across providers?", + "图片 {n}": "", + "完全访问": "", + "完全访问已开启:改动类工具会直接执行,不再弹出确认卡。": "", + "完整对话在桌面端「AI 助手」页": "", "对指定模型发一次真实请求,回报连通性与耗时。": "Sends one real request to the given model and reports reachability and latency.", + "对话由网关转发到上游模型,用量计入请求日志": "Chat is relayed to the upstream model by the gateway; usage is recorded in the request log", + "已停止生成": "Generation stopped", + "已全部勾选": "", + "已切换到变更前确认": "", + "已切换到完全访问": "", + "已启用的模型": "", + "已调用": "Called", "开关模型的对外可用性,不改其它属性。": "Toggles a model's outward availability without touching other attributes.", + "当前一个工具都没勾选,助手只能纯聊天。点右上角「助手设置」勾上需要的工具。": "", + "当前模型没有声明图片能力,图可能发不上去。": "", + "当前没有同名模型分布在多家。": "", + "当前:已关闭(助手收不到任何系统提示词,工具定义照发)": "", + "当前:已开启 · 使用内置默认提示词": "", + "快捷查询走本地网关,不消耗额度": "", + "思考等级已切换": "", + "思考:跟随模型": "Reasoning: follow model", + "总请求": "", + "成功率": "", + "我可以直接查网关里的真实数据:余额、模型重复、今日用量、限流账号。": "", + "打开会话失败:{message}": "Failed to open session: {message}", + "执行中": "", + "找到 {n} 组同名模型:": "", "把重复的模型加上家名前缀区分开": "Prefix duplicated models with their provider name to tell them apart", + "推理等级": "", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "Edits capability flags such as context window, max output tokens, and tool calls.", + "改动先列清单": "", + "改动直接执行": "", + "改动类 · 默认需要确认": "", + "改动类工具先列清单,等你点「执行」": "", + "改动类工具直接执行,不再询问": "", + "改回变更前确认": "", "改某一家的整组顺位(先试哪一家)。": "Reorders a provider's whole group (which provider is tried first).", "改账号在全局队列里的名次,决定主备与切换顺序。": "Changes an account's rank in the global queue, deciding primary/backup and failover order.", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "Adds/removes mapping names, with bulk prefixing and merging a shared name across providers.", + "新建对话时使用": "", + "映射": "", + "有什么可以帮你?可以问配置、查余额、让我改模型设置。": "How can I help? Ask about configuration, check balances, or have me change model settings.", + "未命名对话": "Untitled conversation", + "未知的工具:{name}": "Unknown tool: {name}", + "未选择": "", + "权限模式": "", + "查询账号": "", "查询账号余额": "Query account balance", + "模型不支持时会自动忽略": "", + "模型没有返回内容": "The model returned no content", + "正在思考…": "Thinking…", + "正在生成,点右侧按钮可停止": "Generating — tap the button on the right to stop", "没有可用的模型": "No models available", + "没有已启用的模型,请先在「模型管理」里启用": "No enabled models. Enable some under Model management first.", "测一下当前最快的模型通不通": "Check whether the currently fastest model is reachable", "深度推理": "Deep reasoning", + "添加图片": "", + "添加图片(也可粘贴或拖入)": "", + "清空全部": "Clear all", + "清空历史失败:{message}": "Failed to clear history: {message}", + "现在有哪些账号被限流?": "Which accounts are rate limited right now?", + "现在没有账号被限流。": "", "看看哪些模型在多家重复了": "Show which models are duplicated across providers", "禁用会把账号移出转发队列,线路可能因此断供。": "Disabling removes the account from the forwarding queue; the route may run dry as a result.", + "自定义提供商模块未就绪(请刷新界面后重试)": "Custom provider module is not ready (refresh the UI and try again)", + "自由对话请用桌面端…": "", "获取模型列表": "List models", + "要改配置的话,桌面端的助手支持完整对话与工具调用。": "", "设置模型映射 ID": "Set model mapping ID", + "读取历史会话失败:{message}": "Failed to load conversation history: {message}", + "读取图片失败": "", + "读取图片失败:{message}": "", + "读取模型清单": "", + "读取模型清单失败": "Failed to load the model list", + "读取账号列表": "", "读各提供商的模型清单、映射名与启用状态。": "Reads each provider's model list, mapping names, and enabled state.", "读各账号的额度 / 剩余用量。": "Reads each account's quota and remaining usage.", "调整提供商顺位": "Reorder providers", "调整账号优先级": "Adjust account priority", + "走本地网关 · 消耗所选账号额度": "", + "跟随模型": "", + "跟随模型(不指定)": "Follow model (unspecified)", + "跟随网关设置": "Use gateway setting", + "还没有历史对话": "No conversation history yet", + "还没有账号。": "", + "还没有选择对话模型:在输入框右下角选一个,或点右上角「助手设置」。": "", + "还没有配置默认模型": "No default model configured yet", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "This gateway build doesn't have the AI assistant API yet (HTTP 404): the backend is older. Rebuild or restart the client and try again. {message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "This turn has reached the {n}-round-trip limit, so I stopped for your input (try rephrasing, or split it into a few steps).", + "这个查询还没有实现。": "", + "这条会话没能存进历史:{message}": "This conversation could not be saved to history: {message}", + "这里只改本次会话用的模型,不会改网关里保存的默认值。": "This only changes the model for this session; the default saved in the gateway is untouched.", "连接网关失败({base}):{message}": "Failed to reach the gateway ({base}): {message}", - "配置响应不完整": "Incomplete config response" + "选择对话模型": "Choose chat model", + "配置响应不完整": "Incomplete config response", + "问点什么…": "Ask something…", + "默认行为": "" } diff --git a/desktop-tauri/i18n/i18n-src/en/checkin.json b/desktop-tauri/i18n/i18n-src/en/checkin.json index 1e527334..35e59870 100644 --- a/desktop-tauri/i18n/i18n-src/en/checkin.json +++ b/desktop-tauri/i18n/i18n-src/en/checkin.json @@ -1,12 +1,15 @@ { " · ": " · ", + "QClaw": "", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "SOLO checkin_credits claims · reset at midnight (calendar day)", "UMID 组件安装失败:{error}": "UMID component installation failed: {error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy CN · Query the task tree, then light up / claim in one click · includes the school-season event and cat travel · click a row for details", "WorkBuddy 国际版保活模型,按顺序回退": "WorkBuddy International keep-alive models, tried in order", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "Free models for WorkBuddy International daily activity · one global setting, unrelated to schedules", "{date}(每天 {time})": "{date} (daily at {time})", + "{done}/{total} 已签": "{done}/{total} checked in", "{label} ×{count}({reason})": "{label} ×{count} ({reason})", + "{n} 积分": "{n} points", "{n} 项可完成": "{n} to complete", "{n} 项待领": "{n} to claim", "{total} 账号 · {done} 已签": "{done} of {total} checked in", @@ -22,10 +25,15 @@ "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "The \"keep-alive model chain\" is the free models used to keep WorkBuddy International accounts active during daily activity runs — tried in order, stops at the first success; clearing the list and saving restores the default chain (current default: {defaults}). Same-day dedup + idempotent claiming: repeat runs only return \"already claimed\" and never add points twice.", "「免费领积分」的每日任务 · 逐个领取": "\"Free points\" daily tasks · claimed one by one", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "One-time rewards · queried and claimed automatically after check-in; once claimed they are recorded locally and no longer queried upstream · click a row to expand the task list", + "一次性福利已全部结清": "All one-time rewards settled", + "一次性福利已结清": "One-time rewards settled", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "Claims all of the day’s rewards in one action (daily check-in + newcomer gift) · idempotency guaranteed by the upstream", "一键安装组件": "Install component", "一键完成": "Complete all", "一键领取({n})": "Claim all ({n})", + "七天一轮共 4000 积分(400/400/400/1000/400/400/1000) · 每份 30 天后到期 · 当天重复领是幂等的": "", + "上一轮({time}):{n} 个动作,+{credit} 积分": "Last run ({time}): {n} actions, +{credit} credits", + "上一轮({time}):{n} 项,+{credit} 积分": "", "上次执行:{line}": "Last run: {line}", "上次签到时间": "Last check-in", "上次领取 {time} · 领取需通过滑块验证码": "Last claimed {time} · claim requires passing a slider CAPTCHA", @@ -33,16 +41,27 @@ "下次 {date} · 点此管理条目": "Next {date} · click to manage schedules", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "Not in daily check-in: {list}. CodeArts one-time rewards are under \"Starter rewards\" below; its and ZCode's daily/plan benefits are claimed under \"Activity rewards\".", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "Not in daily check-in: {list}. CodeArts one-time rewards are under \"Starter rewards\" below, and its daily welfare is right above under \"Daily check-in\"; ZCode plan claims are under \"Activity rewards\".", + "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」;QClaw 已停运、积分随迁移到 WorkBuddy 发放,见下方「只读余额(已停运)」。": "Not in daily check-in: {list}. CodeArts one-time rewards are under \"Starter rewards\" below, and its daily welfare is right above under \"Daily check-in\"; ZCode plan claims are under \"Activity rewards\"; QClaw is discontinued and its credits are issued through the WorkBuddy migration, see \"Read-only balance (discontinued)\" below.", + "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」;QClaw 的积分随登录自动发放,见「自动发放」。": "", + "不参与签到": "Not in check-in", + "不可领": "Unavailable", "与国内版同一套任务接口 · 站点不同": "Same task API as the China edition · different site", + "与国内版同一套链路 · 站点不同、令牌不通用": "Same flow as the China edition · different site, tokens not interchangeable", + "个账号不参与": "accounts excluded", + "今天 · 北京时间自然日": "Today · Beijing calendar day", + "今天已执行:{list}": "Ran today: {list}", "今天已领 {n} 份": "Claimed {n} today", "今天未领": "Not claimed today", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "Claimed today (Beijing time {day}) · {n} items confirmed received · can claim again tomorrow", "今日已执行": "Ran today", "今日已派出": "Sent out today", + "今日已签": "Checked in today", "今日已领取": "Claimed today", "今日签到": "Today", "今日签到进度": "Today's check-in progress", "从清单里移除": "Remove from the list", + "余额查询中…": "", + "余额查询失败:{error}": "", "保活": "Keep-alive", "保活 {n}": "{n} kept alive", "保活+领取": "Keep-alive + claim", @@ -55,6 +74,8 @@ "全部结果": "All outcomes", "全部账号均可签到": "All accounts can check in", "全部领取": "All claimed", + "关着:不自动巡检(仍可手动巡检)": "Off: no automatic inspection (manual inspection still works)", + "关着:不自动执行(仍可手动执行)": "", "关着:所有条目都不执行": "Off: no schedule runs", "删除失败:{error}": "Delete failed: {error}", "删除定时条目": "Delete schedule", @@ -62,22 +83,27 @@ "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "At the scheduled time, auto check-in runs for the available accounts of the selected providers (WorkBuddy China uses the daily check-in API; International uses daily activity tasks — detect the campaign, claim the daily-activity reward, then keep the account alive with free models; Raccoon uses the desktop daily points flow; AutoClaw uses the official client's daily check-in task; Qoder China uses campaign claims, and accounts with no campaign assigned get a neutral notice; Qoder International needs a device risk-control identity first (generated by the UMID component of the local Qoder client / qodercli) to see the \"Daily 100 Credits\" campaign, and a missing component yields a clear explanation instead of an error; Trae claims SOLO's daily check-in points (reset at midnight by calendar day), and when the upstream has no campaign for the account or the credential has no device ID the result is \"not claimed + the specific reason\" instead of an error; Loomy awards first-login points once a day per account; KukuAI claims the \"Free points\" daily tasks; CodeArts claims that day's campaign welfare (claim → confirm → read back to verify; automatic runs are limited to 6 write attempts per day with a 10-minute gap); OfficeAce claims all of that day's rewards in one action (daily check-in + newcomer gift)). If the machine was off at the scheduled time, the missed run is made up automatically after it starts — nothing is skipped just because the machine was off. Every provider's check-in API is idempotent, so running it again never claims twice.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "At the scheduled time, auto check-in runs for the available accounts of the selected providers (WorkBuddy China uses the daily check-in API; International uses daily activity tasks — detect the campaign, claim the daily-activity reward, then keep the account alive with free models; Raccoon uses the desktop daily points flow; AutoClaw uses the official client's daily check-in task; Qoder China uses campaign claims, and accounts with no campaign assigned get a neutral notice; Qoder International needs a device risk-control identity first (generated by the UMID component of the local Qoder client / qodercli) to see the \"Daily 100 Credits\" campaign, and a missing component yields a clear explanation instead of an error; Trae claims SOLO's daily check-in points (reset at midnight by calendar day), and when the upstream has no campaign for the account or the credential has no device ID the result is \"not claimed + the specific reason\" instead of an error; Loomy awards first-login points once a day per account; KukuAI claims the \"Free points\" daily tasks; OfficeAce claims all of that day's rewards in one action (daily check-in + newcomer gift)). If the machine was off at the scheduled time, the missed run is made up automatically after it starts — nothing is skipped just because the machine was off. Every provider's check-in API is idempotent, so running it again never claims twice.", "到点时逐个签到,已勾 {n} / {total} 家": "Checked {n} of {total} — each is checked in at the scheduled time", + "北京时间 · 逗号分隔 · 最多 6 条 · 到点后最多晚 30 秒执行;错过时点开机后会补跑一轮。": "Beijing time · comma-separated · up to 6 entries · runs at most 30 seconds late; a missed slot is caught up once after boot.", "北京时间 · 错过时点开机后会自动补签": "Beijing time · missed runs are made up automatically after startup", "去领取": "Claim now", "另有 {n} 个账号不参与每日签到": "{n} more accounts are not part of daily check-in", + "只读余额(已停运)": "Read-only balance (discontinued)", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "Claim rewards of completed tasks only (idempotent; repeated clicks will not double-credit)", "可完成": "Can complete", "可派出": "Can send out", "可选 · 缺省显示时刻": "Optional · defaults to the time", + "可领": "Available", "可领奖": "Can claim", "后端未返回定时签到数据": "Backend returned no schedule data", "后端未返回签到中心数据": "The backend did not return check-in center data", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "International check-in requires the UMID component to generate a device risk-control identity (not detected on this machine).", + "在桌面端设置": "Configure on desktop", "在途": "Traveling", "填入缺省链": "Fill in default chain", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "Pick a time and the providers, then click Save; the name is optional (the time is shown when empty)", "失败 {n}": "{n} failed", "套餐明细": "Plan details", + "套餐领取": "Plan claim", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Install the Qoder client or qodercli to enable it (no need to restart the gateway).", "安装中…": "Installing…", "官方客户端的每日签到任务": "Official client's daily check-in task", @@ -86,30 +112,49 @@ "尚未查询": "Not queried yet", "尚未查询任务状态": "Task status not queried yet", "巡检一次": "Run one round", + "巡检中…": "Inspecting…", "已保存:{label}": "Saved: {label}", "已保存:保活模型链": "Saved: Keep-alive model chain", "已保存:定时条目": "Saved: schedule", + "已保存:成长任务排程": "", + "已保存:猫猫旅行排程": "Saved: cat travel schedule", + "已停运": "Discontinued", + "已全部领取": "All claimed", "已删除定时条目": "Schedule deleted", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "Activity reported (streak lit up), but the campaign is not active (nothing to claim this time)", + "已开始批量签到": "Bulk check-in started", + "已得 {earned} / {total} 积分": "{earned} / {total} points earned", "已抽 {n} 次": "Drew {n} times", + "已提交「{label}」的签到": "Check-in submitted for {label}", "已有结果 {checked} / 共 {total} 个账号": "Results for {checked} / {total} accounts", "已签": "Checked in", + "已领": "", "已领 {claimed}/{total} · 累计 {earned} 积分": "Claimed {claimed}/{total} · {earned} points earned", "已领养": "Adopted", "已领取": "Claimed", + "已领取「{name}」": "Claimed {name}", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "Claim \"Daily 100 Credits\" with a device risk-control identity · requires the UMID component", + "开关": "Toggles", "开学季执行失败:{error}": "School-season run failed: {error}", "开学季执行完成:{message}": "School-season run completed: {message}", "开学季活动": "School-season event", "开着:启用的条目按各自时刻执行": "On: enabled schedules run at their own times", + "开着:按下面的时刻自动巡检": "On: inspects automatically at the times below", + "开着:按下面的时刻自动执行": "", + "当前没有可领取的套餐": "No plans available right now", "待签": "Not checked in", + "待领取": "To claim", "待领新手福利": "Starter rewards to claim", "总开关": "Master switch", "总开关已关闭 · 在「定时签到」页打开": "Master switch is off · enable it on the \"Scheduled check-in\" tab", "成功 {n}": "{n} succeeded", "成长任务": "Growth tasks", + "成长任务失败:{error}": "", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "Growth tasks finished ({done} succeeded, {failed} failed): credits +{credit}, energy +{energy}", + "成长任务定时排程": "", "成长任务执行失败:{error}": "Growth task run failed: {error}", + "成长任务:{done} 项完成、{failed} 项失败,+{credit} 积分 +{energy} 能量": "", + "成长任务:{done} 项完成,+{credit} 积分 +{energy} 能量": "", "执行失败:{error}": "Run failed: {error}", "执行并抽奖": "Run & draw", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "Run school-season tasks and spend all draw chances (share / chat / expert events)", @@ -125,14 +170,21 @@ "搜账号 / 收益 / 错误 / 触发原因": "Search account / gain / error / trigger reason", "收起新建": "Close new entry", "新建定时条目": "New scheduled entry", + "新手任务": "Starter tasks", "新手福利": "Starter rewards", + "新手福利与活动": "Starter rewards & campaigns", + "新手福利是一次性任务:领完就没了,重复点不会重复加分。": "Starter rewards are one-time tasks: once claimed they are gone, and repeated taps do not add points again.", "新手福利有 {n} 项领取失败,可重试": "{n} starter reward(s) failed to claim — you can retry", "无": "None", + "无独立签到接口 · 每天替账号打一次登录信息(即当日魔粒)": "No dedicated check-in API · hits the login-info endpoint once a day per account (which grants the daily credits)", "无独立签到接口 · 每天替账号打一次首次登录积分": "No dedicated check-in API · awards first-login points once a day per account", "无账号": "No accounts", + "时刻": "Times", "暂不可领": "Not claimable yet", + "暂不可领(未满足条件)": "Not claimable yet (requirements not met)", "暂不支持": "Not supported yet", "暂无启用的条目 · 点此添加": "No enabled schedules · click to add one", + "最近到期 {date}": "", "有未保存的改动": "Unsaved changes", "未勾选提供商": "No provider selected", "未开启": "Off", @@ -141,30 +193,45 @@ "未领取": "Not claimed", "本地台账里今天还没领过;活动期内每天发一份新套餐": "No claim recorded locally for today yet; a new plan is issued every day during the event", "本地台账里今天(北京时间)领过的套餐:{plans}": "Plans claimed today (Beijing time) according to the local record: {plans}", + "本站已停止发放积分,余额在原有效期内仍可用": "This site no longer grants credits; existing balances remain usable until they expire", "条启用": "enabled", "查询于 {time}": "Queried at {time}", "查询任务": "Query tasks", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "Query the task tree, then light up / claim in one click (includes school-season and cat travel)", "查询失败:{error}": "Query failed: {error}", + "查询无结果": "", "桌面端每日积分链路": "Desktop daily points flow", "正在读取定时签到数据…": "Loading schedule data…", "正在读取签到数据…": "Loading check-in data…", "每天 {time}": "Daily at {time}", "每天 {time} · {scope}": "Daily {time} · {scope}", + "每天定点巡检 · 一趟推进一步 · 与签到排程互相独立": "Runs once per slot · one step per run · independent of the check-in schedule", + "每天定点执行成长任务 · 与签到 / 旅行互相独立 · 默认关": "", + "每天按这里的时刻自动巡检一遍 WorkBuddy 国内版账号:没有猫就领养、在家就派出、到站就领奖。一趟只推进一步,所以建议留两个时刻(早上派出、晚上领奖)。企业版与国际版账号自动跳过。": "Automatically inspects every WorkBuddy China account at these times: adopts a cat if there is none, sends it out when idle, claims the reward when it arrives. Each run advances one step, so two slots are recommended (send out in the morning, claim in the evening). Enterprise and international accounts are skipped automatically.", + "每天按这里的时刻自动执行 WorkBuddy 国内版账号的成长任务:逐个点亮、完成后领奖。夜猫子任务只有 23:00-08:00(北京时间)能点亮,所以缺省时刻是 23:30。默认关闭 —— 它会上报事件并领积分,请确认要自动执行再打开。企业版与国际版账号自动跳过,不可伪造的任务(如真实捐款)也跳过。": "", "每天时刻": "Daily time", + "每天自动执行": "Runs automatically every day", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "One row per account per check-in run · outcomes and gains as recorded by the backend", + "每日活动签到(+100 积分,已领则中性提示)": "Daily activity check-in (+100 credits; a neutral note if already claimed)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "Daily activity tasks · a web-app conversation lights up the daily activity, only claims add points · the two can run separately", + "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "Daily sign-in points · claims member activities along the way · guest accounts have no points system (skipped)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "Daily welfare · claim → confirm → read back to verify · resets on the Beijing calendar day", "每日签到": "Daily check-in", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "Each schedule has its own time and scope · same-day dedup per entry · missed runs are made up after startup", "每账号一条明细 · 点此查看「签到记录」": "One row per account · open \"Check-in records\"", + "没有可签到的账号": "No accounts available for check-in", "没有可领套餐的账号(ZCode 未添加)。": "No accounts with claimable plans (ZCode not added).", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "No accounts with claimable rewards (CodeArts / ZCode not added).", + "没有已停运但仍可查余额的账号(目前有 QClaw 一家)。": "No accounts from discontinued providers with a readable balance (QClaw is the only one so far).", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "No accounts support growth tasks (WorkBuddy CN required).", "没有支持新手福利的账号": "No accounts support starter rewards", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "No accounts support starter rewards — currently Loomy, Raccoon and CodeArts.", "没有符合筛选条件的签到记录": "No check-in records match the current filters", + "没有随登录自动发放积分的账号(目前有 QClaw 一家)。": "", "没有需要执行的任务": "Nothing to run", + "活动奖励(30 天 / 7 天有效期)+ 下载客户端奖励 · 两条链都幂等": "", + "活动尚未开始(上游接口未部署)": "Campaign has not started (upstream endpoint not deployed)", + "活动按自然日发新套餐,每天可领一次": "The campaign issues a new plan per calendar day; one claim per day", "活动福利": "Event rewards", "活动(campaign)领取 · 每天 10:00 刷新": "Campaign claims · refreshed daily at 10:00", "活跃上报与保活均未成功,且活动未开启": "Both the activity report and keep-alive failed, and the campaign is not active", @@ -183,17 +250,26 @@ "清空签到记录": "Clear check-in records", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "Click \"Plan details\" to see which plans you got and when they take effect; you can claim again every day during the event", "点击行展开账号明细 · 单账号签到不受范围限制": "Click a row to expand account details · single-account check-in is not limited by scope", + "点进去查看任务并领取": "Tap to view the tasks and claim", "独立链路 · 只提供手动入口 · 不进自动签到": "Separate flow · manual entry only · not part of auto check-in", "猫猫旅行": "Cat travel", "猫猫旅行失败:{error}": "Cat travel failed: {error}", "猫猫旅行失败:{message}": "Cat travel failed: {message}", + "猫猫旅行定时排程": "Cat travel schedule", "猫猫旅行:{message}": "Cat travel: {message}", + "猫猫旅行:{n} 个动作,+{credit} 积分": "Cat travel: {n} actions, +{credit} credits", + "猫猫旅行:{n} 个动作,+{credit} 积分,{failed} 个失败": "Cat travel: {n} actions, +{credit} credits, {failed} failed", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "Keep the account active with free models and a web-app conversation · no rewards claimed", "确定删除「{name}」?删除后到点不再执行。": "Delete \"{name}\"? It will no longer run at its scheduled time.", "确定清空全部签到记录?清空后无法恢复。": "Clear ALL check-in records? This cannot be undone.", "确定清空当前筛选出的签到记录?清空后无法恢复。": "Clear the filtered check-in records? This cannot be undone.", + "福利与套餐的逐项领取在桌面端签到中心完成": "", + "积分随登录到账 · 无需签到 · 只读:余额与到期时间由上游直接给出": "", + "积分随登录自动发放,无需签到": "", "移除 {name}": "Remove {name}", "立即全部签到": "Check in all now", + "立即巡检全部": "Inspect all now", + "立即执行全部": "", "签到": "Check in", "签到中…": "Checking in…", "签到中心分区": "Check-in center sections", @@ -210,6 +286,7 @@ "结算于 {time}": "Settled at {time}", "能量 {n}": "Energy {n}", "腾讯每日签到接口 · 仅国内版": "Tencent daily check-in API · China edition only", + "自动发放": "", "至少勾一家才能保存(不勾等于这条条目不签到)": "Select at least one provider to save (an empty scope means this entry checks in nothing)", "读取定时签到数据失败:{error}": "Failed to load schedule data: {error}", "读取签到数据失败:{error}": "Failed to load check-in data: {error}", @@ -219,6 +296,9 @@ "还没执行过": "Never run yet", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "No schedules yet — click \"Add schedule\" to create one, or check in manually on the \"Check-in center\" tab.", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "No check-in records yet — details appear here after a manual check-in or a scheduled run.", + "还没有记录": "No records yet", + "这个账号没有新手任务": "This account has no starter tasks", + "这些提供商已停止发放积分 · 余额在原有效期内仍可用 · 只读:余额与到期时间由上游直接给出": "These providers no longer grant credits · existing balances remain usable until they expire · read-only: balance and expiry come straight from upstream", "连登 {n} 天": "{n}-day streak", "部分": "Partial", "重签": "Check in again", @@ -231,8 +311,11 @@ "领取": "Claim", "领取中": "Claiming", "领取中…": "Claiming…", + "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "Claim the free CodingPlan (on demand: when the plan is active the quota is only read back)", "领取失败": "Claim failed", "领取失败:{error}": "Claim failed: {error}", + "领取每日积分 · 幂等(当天已领回 0,不报错)": "", + "领取通常需要过一次滑块验证;活动套餐多为当天稍晚生效,领取后可在桌面端「账号」里看套餐明细。": "Claiming usually requires passing a slider verification. Campaign plans often take effect later the same day; after claiming, see plan details under Accounts on desktop.", "(共 {n} 个账号)": " ({n} accounts total)", "(能量 / 连登读数本次未取到)": "(Energy / streak readings unavailable this time)", ",日活保活 {n} 个": ", {n} kept alive" diff --git a/desktop-tauri/i18n/i18n-src/en/common.json b/desktop-tauri/i18n/i18n-src/en/common.json index f133f32d..1a32537e 100644 --- a/desktop-tauri/i18n/i18n-src/en/common.json +++ b/desktop-tauri/i18n/i18n-src/en/common.json @@ -1,7 +1,11 @@ { "无可用账号:{reason}": "No accounts available: {reason}", "AI 助手": "AI Assistant", + "Agent": "", + "Agent 接管": "", "Agent2API · 多提供商本地网关": "Agent2API · Multi-provider local gateway", + "QQ 群号": "QQ group number", + "Webhook 地址(https://…)": "", "{key} 需要 true / false / null": "{key} must be true / false / null", "{key} 需要数字": "{key} must be a number", "{n} 个启用": "{n} enabled", @@ -12,39 +16,76 @@ "{n} 个账号启用": "{n} accounts enabled", "{n} 条失败请求未读,点开「请求日志」查看": "{n} unread failed requests — open \"Request logs\" to view", "{n} 条错误日志未读,点开「日志」查看": "{n} unread error logs — open \"Logs\" to view", + "{n} 项 · {shown} 显示": "{n} items · {shown} shown", + "{size} · 下载后可直接安装,无需跳转浏览器": "{size} · Downloads and installs in place — no browser redirect", + "{size} · 点击下载后可直接安装,无需跳转浏览器": "{size} · Tap download to install right away — no browser redirect", + "{total} 个菜单 · 显示 {shown} · 隐藏 {hidden} · {groups} 个分组": "{total} menus · {shown} shown · {hidden} hidden · {groups} groups", "· 不会改动任何配置": "· makes no config changes", "· 每次执行前列改动清单,等你确认": "· lists the changes before each run and waits for your confirmation", "—": "—", "★ 下一单走它": "", "✅ Token 已刷新": "✅ Token refreshed", "✅ 已将账号优先级调整到全局第一位": "✅ Account moved to first in the global queue", + "✅ 测试消息已发出,请查看对应群": "", + "✅ 通知设置已保存": "", "「{name}」匹配到多个账号:{list},请用完整账号名重试": "\"{name}\" matches multiple accounts: {list}. Retry with the full account name.", "「工具全不勾选 → 不注入系统提示词」:想当纯聊天用时,不必再去关上面的开关。": "\"No tools selected → no system prompt\": if you want plain chat, you don't need to turn the switch above off.", + "「设置」是固定项,含它的分组不能整组隐藏;可以单独隐藏组内其它菜单": "\"Settings\" is pinned, so its group cannot be hidden as a whole; you can still hide its other menus individually", + "一个都不勾 = 全部订阅(配好渠道就能收到东西)": "", + "下拉刷新": "Pull to refresh", "不限模型": "", + "与电脑端一致:每 {n} 分钟": "Matches the desktop app: every {n} min", + "主导航": "Main navigation", "代价": "Cost", "代理:池": "", + "会恢复默认的分组、顺序与显隐设置。你自建的分组会被删除(组内菜单不会丢)。": "This restores the default groups, order and visibility. Groups you created are removed (their menus are kept).", "会话内": "In-session", + "会话已过期。重新登录后会自动回到手机端。": "Your session expired. Sign in again and you will return to the mobile app automatically.", "会话粘性": "Session stickiness", "低于限制器的阈值,转发时跳过该账号": "", "余额不足或 Token 周期用量到顶,被限制器拦下,自动恢复后回来": "", "余额优先": "", "作为 system 消息注入给助手": "Injected to the assistant as a system message", + "例如:家里、公司": "e.g. Home, Office", + "侧边栏布局模块未加载(ui/sidebar-layout.js 应在 app.js 之前引入)。": "The sidebar layout module did not load (ui/sidebar-layout.js must be included before app.js).", + "侧边栏没有显示任何菜单。到「设置 → 显示 → 侧边栏菜单」里打开需要的项。": "No menus are shown in the sidebar. Turn on the ones you need under Settings → Display → Sidebar menus.", + "侧边栏菜单": "Sidebar menus", "保存对话模型失败:{message}": "Failed to save the chat model: {message}", + "保存并继续": "Save and continue", "候选队列": "", "停止": "Stop", + "先添加一台网关": "Add a gateway first", "全不选": "Deselect all", "全局队列位次(转发按策略在此序上再排)": "", + "全局(账号表)": "", + "共 {n} 条记录": "{n} records", + "共 {total} 个账号,已读到 {queried} 个": "", "关": "Off", + "关掉时不发任何消息(配置保留)。默认关 —— 没配好就发是打扰。": "", "冷却": "", + "出事了主动推给你(桌面端常驻托盘,不必自己盯着看)": "", + "分组名": "Group name", + "分组名,例如「常用」": "Group name, e.g. \"Favorites\"", "列表 = 模型管理页里所有已启用的模型。助手走本地网关发起对话,消耗所选账号的额度。": "The list is every enabled model from the Model management page. The assistant chats through the local gateway and spends the selected account's quota.", + "创建并进入": "Create & enter", + "创建管理员账号": "Create admin account", "删除 {n} 条": "Delete {n} entries", + "删除分组": "Delete group", + "删除分组「{name}」?组内 {n} 个菜单会移到「{target}」,不会丢。": "Delete group \"{name}\"? Its {n} menus will move to \"{target}\" — nothing is lost.", + "删除空分组「{name}」?": "Delete the empty group \"{name}\"?", + "删除这个分组(组内菜单移到第一组)": "Delete this group (its menus move to the first group)", "加载助手设置失败:{message}": "Failed to load assistant settings: {message}", "加载失败:{error}": "Load failed: {error}", + "加载更早的记录": "Load older records", "助手": "Assistant", "助手自己用哪个模型生成回复": "Which model the assistant uses to generate replies", "助手设置读取失败,工具可能已过时。": "Failed to read assistant settings; the tool list may be out of date.", "助手走本地网关,消耗所选账号的额度": "The assistant goes through the local gateway and spends the selected account's quota", "勾选即注入该工具;不勾选 = 助手看不到它": "Checked tools are injected; unchecked ones are invisible to the assistant", + "升序": "", + "单签": "Single", + "去下载": "Get it", + "去登录": "Sign in", "参数不完整(需要 account / enabled)": "Incomplete parameters (account / enabled required)", "参数不完整(需要 account / position)": "Incomplete parameters (account / position required)", "参数不完整(需要 provider / id / alias)": "Incomplete parameters (provider / id / alias required)", @@ -52,21 +93,34 @@ "参数不完整(需要 provider / id / enabled)": "Incomplete parameters (provider / id / enabled required)", "参数不完整(需要 provider / position)": "Incomplete parameters (provider / position required)", "发现新版本 {latest}(当前 {current}),点开「设置 - 软件更新」可查看更新日志并下载": "New version {latest} available (current: {current}) — open \"Settings - Software update\" to see the changelog and download", + "发现新版本 {version}": "New version {version} available", "发送": "Send", + "发送测试": "", "只改映射名,不动上游模型 ID;映射立即生效(新对话即可用)。": "Only the mapping name changes; the upstream model ID stays. Mappings take effect immediately (usable in a new chat).", "只注入工具定义": "Only tool definitions are injected", "只读": "Read-only", "只读工具": "Read-only tools", "可用": "usable", + "含固定项「设置」,不可整组隐藏(可以单独隐藏组内其它菜单)": "Contains the pinned \"Settings\" menu, so the group cannot be hidden as a whole (you can still hide its other menus individually)", "启用后重新参与转发": "", + "启用通知": "", "四种组合的实际效果": "What each combination actually does", "回复由 {model} 生成 · 走本地网关": "Reply generated by {model} · via the local gateway", + "固定": "Pinned", "在全局队列里上移一位": "", "在全局队列里下移一位": "", + "地址已遮住 token(保存时不动它);要更换请整体重新粘贴": "", + "备注名(可留空)": "", + "备注名(可选)": "Label (optional)", "复制失败,请手动选择复制": "Copy failed, please select and copy manually", "复制订阅链接": "Copy subscription link", "失败 · {status}": "Failed · {status}", "失败退避": "Failure backoff", + "奖励 {bonus}": "Bonus {bonus}", + "套餐 {plan}": "Plan {plan}", + "安装": "Install", + "安装包已就绪,点「安装」即可完成更新": "Package ready — tap \"Install\" to finish updating", + "定时": "Scheduled", "定时任务": "Scheduled tasks", "实际注入": "Actually injected", "对话模型": "Chat model", @@ -80,118 +134,203 @@ "将把账号「{account}」移到第 {position} 顺位:": "About to move account \"{account}\" to position {position}:", "将改动 {n} 条映射({parts}):": "About to change {n} mappings ({parts}):", "尚未登录": "Not signed in yet", - "已保存": "Saved", + "展开作用域栏": "", + "展开左栏,看 {label} 下的 {n} 个模型": "", + "展开来自{label}的 {n} 个模型": "", + "已下载,待安装": "Downloaded, ready to install", "已修改 {n} 个模型": "Changed {n} models", "已修改 {n} 个账号": "Changed {n} accounts", "已修改 {n} 项能力": "Changed {n} capabilities", "已停止这次回答。": "This reply was stopped.", "已全部取消勾选": "All tools deselected", + "已关闭自动检查": "Automatic checks off", + "已删除分组「{name}」": "Deleted group \"{name}\"", "已勾选 {n} / {total}": "{n} / {total} selected", "已勾选「{label}」": "\"{label}\" selected", "已勾选工具": "Tools selected", "已取消「{label}」": "\"{label}\" deselected", + "已启动安装器": "Installer launched", + "已复制{label}": "", "已复制:{text}": "Copied: {text}", + "已完成 {percent}%": "", + "已开启自动检查": "Automatic checks on", "已恢复推荐勾选": "Recommended selection restored", + "已恢复默认分组:概览 / 转发链路 / 诊断 / 系统": "Restored the default groups: Overview / Forwarding / Diagnostics / System", "已改动 {n} 条映射": "Changed {n} mappings", + "已新建分组「{name}」,点组头的 + 把菜单移进来": "Created group \"{name}\" — use + on its header to move menus in", + "已是最新版本": "You are on the latest version", "已注入 {n} / {total} 个工具 · 系统提示词:{state}": "{n} / {total} tools injected · System prompt: {state}", + "已添加 {n} 台": "", + "已添加的服务器": "Added servers", "已登录": "Signed in", + "已离线 · 显示的是缓存内容": "Offline — showing cached content", "已移到第 {n} 顺位": "Moved to position {n}", "已试过": "", "并发上限 {n}": "", "并发已满,等一个在途回落再参与": "", "并发满": "", "开": "On", + "当前": "Current", "当前一个工具都没勾选,助手只能纯聊天。去「设置 → AI 助手 → 工具」里勾上需要的工具。": "No tools are selected, so the assistant can only chat. Enable the tools you need under Settings → AI Assistant → Tools.", + "当前版本 {version}": "Current version {version}", + "当前环境不支持读取调度队列": "", "当前:已关闭(助手不收到任何系统提示词,工具定义照发)": "Current: off (the assistant receives no system prompt; tool definitions are still sent)", "当前:已开启 · 使用内置默认提示词(还没自定义)": "Current: on · using the built-in default prompt (not customized yet)", "当前:已开启 · 自定义内容 {n} 字": "Current: on · custom content, {n} characters", "影响转发": "Affects routing", "待确认": "Pending confirmation", + "总剩余 {total}": "Total left {total}", "恢复上游原值": "Restore upstream values", "恢复推荐": "Restore recommended", + "恢复默认侧边栏": "Restore default sidebar", + "成功领取": "Claimed", "我会在动手前把改动列给你确认。": "I'll list the changes for you to confirm before making them.", + "所有菜单都已在这一组": "Every menu is already in this group", + "手机端打开": "", "打开账号设置": "", "执行": "Run", + "把菜单移入 {name}": "Move a menu into {name}", + "把菜单移入「{name}」": "Move a menu into \"{name}\"", "报表": "Reports", + "拖动分组 {name}": "Drag group {name}", + "拖动左侧 ⠿ 调整顺序(菜单可以跨组拖);拨开关控制显隐;点分组名可重命名;组头的 + 把别的菜单移进来;眼睛按钮隐藏整组。改动即时生效并记住,左侧侧边栏就是结果。键盘也能排序:把手聚焦后按 Alt+↑ / ↓。": "Drag the ⠿ handle to reorder (menus can move across groups); use the switches to show or hide; click a group name to rename it; the + on a group header moves another menu into it; the eye button hides a whole group. Changes apply instantly and are remembered — the sidebar on the left is the result. Keyboard: focus a handle and press Alt+↑ / ↓.", + "拖动菜单 {name}": "Drag menu {name}", + "拖动调整分组顺序(Alt+↑ / ↓ 也可)": "Drag to reorder groups (Alt+↑ / ↓ also works)", "拖动调整队列顺序": "", + "拖动调整顺序,可跨组(Alt+↑ / ↓ 也可)": "Drag to reorder, across groups too (Alt+↑ / ↓ also works)", + "按作用域选择:全局账号表,或某一个模型的调度视图": "", + "按厂商": "", "按模型展开队列": "", "按请求的模型展开 —— 与转发选路同一份排序,下一个请求会走 ★ 那位": "", + "按配置顺序逐个发送;一个失败不影响其它": "", "排第 1 的接走所有请求,直到它不能答(限流 / 故障 / 额度尽)才轮到下一位 —— 就是「从上往下」的现状行为。": "Position 1 takes every request until it can't answer (rate-limited / failing / out of quota) before the next one steps in — the same top-down behavior as today.", + "接口文档": "", + "控制主侧边栏显示哪些菜单、按什么顺序、怎么分组。隐藏只影响导航显示,不会撤销页面访问权限;被隐藏的页面仍可通过地址直接打开。": "Choose which menus the sidebar shows, in what order, and how they are grouped. Hiding only affects navigation — it does not revoke access, and hidden pages can still be opened by URL.", "提示词": "Prompt", "提示词开关": "Prompt switch", + "搜索模型或厂商": "", + "搜索模型或厂商…": "", + "收起作用域栏": "", + "收起来自{label}的 {n} 个模型": "", "改动立即生效;禁用后指向它的请求会落到下一个候选。": "Changes take effect immediately; once disabled, requests for it fall through to the next candidate.", "改动类工具": "Write tools", "改成": "Change to", "文档": "Docs", "新": "New", - "新增": "Add", + "新分组名": "New group name", "新增 {n} 条": "Add {n} entries", "新对话": "New chat", + "新建分组": "New group", "无凭证": "", "日志": "Logs", "映射名": "Mapping name", + "显示 {name}": "Show {name}", + "显示分组 {name}": "Show group {name}", + "显示这一组": "Show this group", "智能": "AI", + "更多": "More", + "最先作废的一份到期日": "", "最大化": "Maximize", "最小化": "Minimize", + "最早 {day} 到期": "", "最早 {time} 恢复": "", "有": "Yes", + "有新版本,重启后生效": "New version ready — restart to apply", + "服务器": "Servers", "未同步": "Not synced", "未就绪": "Not ready", "未登录": "Not signed in", "未知工具「{name}」:它在设置里可能刚被取消勾选,或这一版助手还不认识它。": "Unknown tool \"{name}\": it may have just been deselected in settings, or this version of the assistant doesn't recognize it yet.", + "未选择服务器": "No server selected", "本周期消耗达到限制器上限,转发时跳过": "", "本地网关监听状态": "Local gateway listening status", + "松开刷新": "Release to refresh", "查余额": "", "查看参数": "View arguments", "查看结果": "View result", "查询该账号的余额": "", "桌面桥不可用(本页需要在 Agent2API 客户端里打开)": "Desktop bridge unavailable (this page must be opened inside the Agent2API client)", + "模型清单为空:先去模型管理页点「获取模型」": "", "模型管理": "Model management", "次熔断": "", + "正在下载…": "Downloading…", + "正在完成人机验证…": "Completing human verification…", + "正在检查…": "Checking…", "正在识别二维码…": "Recognizing QR code…", + "正在连接…": "Connecting…", + "每台服务器的登录状态各自独立保存,切换后不用重新登录。地址改掉后需要重新登录。": "Each server keeps its own sign-in, so switching back does not require signing in again. Changing a server's address requires signing in again.", "每条请求轮换到下一个可用账号;新会话交给最近最闲的那位。适合想把手里的号雨露均沾。": "", "每轮": "Per turn", "没有已启用的模型": "", + "没有带余额概念的账号。添加账号后,这里会显示各账号的额度余量与到期。": "", "没有找到提供商「{provider}」的账号": "No accounts found for provider \"{provider}\"", "没有找到账号「{name}」": "No account found named \"{name}\"", "没给账号名": "No account name given", "注入系统提示词": "Inject system prompt", + "活动": "Campaign", "浅色": "Light", + "测试未全部成功,详见下方": "", "消耗该账号额度": "Spends this account's quota", "消耗额度": "Spends quota", "深色": "Dark", + "添加": "Add", + "添加服务器": "Add server", + "添加渠道": "", "清空 = 回到内置默认(与「恢复默认」等价)。": "Clearing reverts to the built-in default (same as \"Restore Defaults\").", + "渠道类型": "", "点「编辑」弹窗改内容;开关关掉就整段不发。": "Click \"Edit\" to change the content in a dialog; turning the switch off sends none of it.", + "点击重命名": "Click to rename", "熔断": "Circuit breaker", "熔断时长(秒)": "", + "版本 {version} · 从 GitHub 直接下载安装包": "Version {version} · downloads the package straight from GitHub", + "版本 {version} · 从网关下载安装包": "Version {version} · downloads the package from the gateway", + "版本 {version} · 更新包需从 Release 页面获取": "Version {version} · get the update from the Release page", "用一句话让我查或改本机的模型转发配置。": "Ask me in one sentence to inspect or change this machine's model routing config.", + "用量与额度": "", "用量占比最低的先用;没有读数的账号按队列位次排在有读数的后面。适合「谁空谁上」。": "", + "电脑端已关闭「软件版本检查」,此处只做手动检查": "Version checks are off on the desktop app, so this page only checks manually", + "登录面板": "Sign in", "监听 127.0.0.1": "Listening on 127.0.0.1", "直接说要改什么 / 查什么…(Enter 发送,Shift+Enter 换行)": "Just say what to change or check… (Enter to send, Shift+Enter for a new line)", + "看这个模型的调度队列:{tip}": "", "确定删除账号「{name}」?{note}": "Delete account \"{name}\"? {note}", "确认后立即生效": "Takes effect right after confirmation", "禁用会把账号移出转发队列;若这条线路没有别的可用账号,请求会直接失败。": "Disabling removes the account from the forwarding queue; if this route has no other usable account, requests will fail outright.", "禁用后不参与转发,余额仍可查": "", + "移入「{name}」": "Move into \"{name}\"", + "稍后": "Later", + "空分组 · 点组头的 + 把菜单移进来": "Empty group · use + on the header to move menus in", + "等待窗口": "Waiting for window", "等这一轮跑完…": "Waiting for this turn to finish…", "签到中心": "Check-in center", "系统提示词 + 工具定义": "System prompt + tool definitions", "系统提示词已保存": "System prompt saved", "系统提示词已关闭": "System prompt turned off", "系统提示词已开启": "System prompt turned on", + "编辑服务器": "Edit server", "编辑系统提示词": "Edit system prompt", "网关 Key": "Gateway key", "网关运行中": "Gateway running", "网络代理": "Network proxy", "能力位决定下游看到的声明(能不能贴图、能塞多少上下文),不改上游的真实行为。": "Capability flags decide what downstream sees declared (image input, context size) without changing upstream behavior.", "自动": "Auto", + "至少要保留一个分组": "At least one group must remain", + "补签": "Catch-up", + "订阅哪些事": "", "订阅里的节点会自动同步进下方代理池,账号即可引用。": "", "订阅里的节点会被内核起成本地监听器,镜像到下方代理池(账号按 id 引用,与本页其它出口同一用法)。": "", "订阅链接必须是 http:// 或 https:// 开头": "The subscription link must start with http:// or https://", "设置": "Settings", + "设置不可隐藏(隐藏了只能清缓存找回来)": "\"Settings\" cannot be hidden (you would have to clear storage to get it back)", "诊断": "Diagnostics", + "请允许安装未知来源应用后重试": "Allow installing apps from unknown sources, then try again", + "请在 Release 页面下载后通过 TestFlight 或 App Store 安装": "Download from the Release page, then install via TestFlight or the App Store", + "请填一个分组名": "Please enter a group name", "请填写订阅名称": "Please enter a subscription name", "请求失败:{message}": "Request failed: {message}", "请求日志": "Request logs", + "读不到完整模型清单:{reason}": "", + "读取通知设置失败:{reason}": "", "账号不在当前队列里": "The account is not in the current queue", "账号已删除": "Account deleted", "账号已在全局队列第一位": "Account is already first in the global queue", @@ -200,29 +339,54 @@ "转发失败的账号休息一会儿再回来(连续失败翻倍,上限 1 小时)": "", "转发能力": "Forwarding", "轮流均摊": "", + "输入这台网关的管理员账号与密码。": "Enter this gateway's administrator account and password.", + "运行日志": "", "运行正常 · {url}": "Running normally · {url}", "运行状态": "Status", + "返回": "", "还有额度时被 429 的账号排到队尾,轮一圈才回来 —— 负载摊开而不是每次回到第 1 个": "", + "还没有可用模型(添加账号后可见)": "", + "还没有渠道。点右上角「添加渠道」,把群机器人的 Webhook 地址填进来。": "", + "还没有签到记录": "No check-in records yet", "还没有订阅:粘贴订阅链接,或从图片识别二维码导入。": "", "还没有选择对话模型:先在下方选一个(或去「设置 → AI 助手」里选)。": "No chat model selected yet: pick one below (or under Settings → AI Assistant).", + "还没查到余额。到「账号」页点一次「查询余额」,或等自动查询跑完。": "", + "这一版没有提供 Android 安装包": "This release does not include an Android package", + "这一项已隐藏": "This item is hidden", + "这一项正在显示": "This item is visible", "这个模型下没有可用账号": "", + "这个面板没有管理员,用一把已启用的 API Key 访问(在桌面端「网关 Key」页创建)。": "This panel has no admin account. Access it with an enabled API key (create one under Gateway keys on desktop).", "这会真实发起上游请求并消耗额度": "This sends a real upstream request and spends quota", + "这台网关没有管理员,用一把已启用的 API Key 访问(在桌面端「网关 Key」页创建)。": "This gateway has no administrator. Use an enabled API key instead (create one on the desktop app's \"Gateway Key\" page).", + "这台网关还没有管理员。设置一个账号密码,之后用它登录。": "This gateway has no administrator yet. Set an account and password, then use them to sign in.", + "这家没有返回可用明细(可能额度都在上游侧,或全部已用尽)": "", "这是真实的上游请求,与生产走同一条链路(会记账,报表排除)。": "This is a real upstream request over the same path as production (it is billed but excluded from reports).", "这段文本作为 system 消息排在对话最前面。工具的定义与当前配置概览由网关自动附加,不用写在这里。": "This text goes at the very front of the conversation as a system message. Tool definitions and the current config overview are appended automatically by the gateway, so don't write them here.", "连接 {n}": "", + "连接并保存": "Connect & save", "连续失败到阈值后熔断一段时间;到点放行试一次,连续成功才算恢复": "", "连续失败阈值": "", "连通 · {ms}ms": "Reachable · {ms}ms", "选择对话模型…": "", "选择模型…": "Choose a model…", + "通知与告警": "", + "通知渠道": "", "都不注入": "Nothing is injected", "都不注入(助手退化为纯聊天)": "Nothing is injected (the assistant falls back to plain chat)", + "重启更新": "Restart & update", "队列是一次性整表提交(后端按新顺序重编优先级),移动会连带调整其它组的相对次序。": "The queue is submitted as a whole table at once (the backend renumbers priorities in the new order), so a move also shifts the relative order of other groups.", "队列是一次性整表提交;位置越靠前,路由时越先被选中。": "The queue is submitted as a whole table at once; the earlier the position, the sooner it is picked when routing.", + "降序": "", "限流 · {n} 个模型": "", "限流垫底": "Rate-limit demotion", + "隐藏 {name}": "Hide {name}", + "隐藏分组 {name}": "Hide group {name}", + "隐藏这一组": "Hide this group", + "需要网关 API Key": "Gateway API key required", "顺序优先": "Strict order", + "首页": "Home", "高危": "High risk", + "({n} 个整组隐藏)": " ({n} groups hidden)", "(不会影响客户端登录态)": "(does not affect the client's sign-in state)", "(仅显示前 20 条,共 {n} 条)": "(showing the first 20 of {n})", "(未指定)": "(not specified)", diff --git a/desktop-tauri/i18n/i18n-src/en/docs.json b/desktop-tauri/i18n/i18n-src/en/docs.json index 076186d2..c0e34260 100644 --- a/desktop-tauri/i18n/i18n-src/en/docs.json +++ b/desktop-tauri/i18n/i18n-src/en/docs.json @@ -1,18 +1,28 @@ { " 或 ": " or ", "Base URL 填进客户端的「API 地址 / Base URL」栏;对话协议按客户端支持的类型三选一(Chat Completions / Responses / Anthropic Messages),三者共用同一套模型与账号池,可随时切换;鉴权用「网关 Key」页里任一启用 Key。": "Put the Base URL into your client's \"API address / Base URL\" field; choose one of the three chat protocols based on what your client supports (Chat Completions / Responses / Anthropic Messages); all three share the same models and account pool, so you can switch at any time; authenticate with any enabled Key from the Gateway Key page.", + "Claude Code / Anthropic SDK": "", "Claude Code / Anthropic SDK(token 计数:/v1/messages/count_tokens)": "Claude Code / Anthropic SDK (token counting: /v1/messages/count_tokens)", + "Codex、新版 SDK": "", "OpenAI 新协议(Codex、新版 SDK)": "Newer OpenAI protocol (Codex, newer SDKs)", + "curl 命令": "", "。一把启用的 Key 都没有时不鉴权(默认网关只监听 127.0.0.1;在设置页开启「局域网访问」后监听所有网卡,鉴权与安全闸门随之启用)。": ". When no Key is enabled, auth is off (by default the Gateway listens only on 127.0.0.1; after you enable \"LAN access\" in Settings it listens on all network interfaces, and auth and security gates turn on with it).", + "。用「网关 Key」页里任一启用的 Key;一把都没有时不鉴权。": "", + "不受鉴权限制,配 Key 前就能拉": "", "启用": "enabled", "填进客户端的「API 地址 / Base URL」": "Paste into your client's \"API address / Base URL\" field", + "复制命令": "", "复制地址": "Copy address", "多数 OpenAI 兼容客户端的默认协议": "The default protocol for most OpenAI-compatible clients", + "多数兼容客户端的默认协议": "", "客户端自动拉取可用模型;不受鉴权限制,配 Key 之前就能拉": "Clients fetch available models automatically; no auth required, so it works even before a Key is configured", "对话协议(按客户端支持的类型选一行)": "Chat protocol (pick the row your client supports)", + "快速验证": "", + "手机端只读文档。完整的使用说明与部署指引在仓库的 docs/ 目录。": "", "接口地址": "API address", "模型列表": "List models", "模型清单": "Model list", "的 Key:请求带 ": " Key from the Gateway Key page: requests carry ", + "请求带 ": "", "鉴权用「网关 Key」页里任一": "Authenticate with any " } diff --git a/desktop-tauri/i18n/i18n-src/en/keys.json b/desktop-tauri/i18n/i18n-src/en/keys.json index ef63921b..e6ed2153 100644 --- a/desktop-tauri/i18n/i18n-src/en/keys.json +++ b/desktop-tauri/i18n/i18n-src/en/keys.json @@ -7,11 +7,13 @@ "✅ Key 已创建,记得复制给客户端": "✅ Key created — remember to copy it to your client", "✅ 可用范围已保存": "✅ Allowed scope saved", "不限制": "No restrictions", + "不限制提供商与模型": "", "与": " and ", "例如 Cursor / 公司电脑": "e.g. Cursor / Work laptop", "保存失败:{reason}": "Save failed: {reason}", "保存范围": "Save scope", "创建": "Create", + "创建于 {time}": "", "创建时间": "Created", "删除网关 Key": "Delete gateway key", "可用提供商": "Available providers", @@ -22,14 +24,17 @@ "可用范围 · {name}": "Allowed scope · {name}", "启用「{name}」": "Enable \"{name}\"", "复制 Key": "Copy key", + "客户端请求带上 ": "", "客户端请求需带 ": "Client requests must include ", + "已删除": "", "已启用鉴权 · {n} 把 Key 生效": "Auth enabled · {n} keys active", "当前不限制:这把 Key 可以用全部提供商与全部对外模型": "No restrictions: this key can use all providers and all public models", "提供商:{names}": "Providers: {names}", "搜索模型…": "Search models…", "操作失败:{reason}": "Operation failed: {reason}", "新建 API Key": "New API Key", - "显示": "Show", + "新建 Key 与设置可用范围(提供商 / 模型白名单)在桌面端完成。": "", + "显示": "Shown", "未启用鉴权": "Auth not enabled", "未命名": "Untitled", "模型:{names}": "Models: {names}", @@ -38,9 +43,11 @@ "留空自动生成;手填至少 8 个字符": "Leave blank to auto-generate; enter at least 8 characters manually", "留空表示不限制;同时设置时请求需同时满足两个条件(模型在白名单内且路由到允许的提供商)。 「可用模型」的候选跟着上面勾选的提供商走:没勾提供商时它是空的(还没有约束范围), 勾了几家就列出这几家能收的全部对外名。": "Leave blank for no restriction; when both are set, requests must satisfy both conditions (the model is on the allowlist and routing resolves to an allowed provider). \"Available models\" candidates follow the providers checked above: with none checked it is empty (no scope constrained yet), and with a few checked it lists all the public names those providers accept.", "确定删除 Key「{name}」?使用它的客户端会立刻无法访问。": "Delete key \"{name}\"? Clients using it will lose access immediately.", + "确定删除「{name}」?用这把 Key 的客户端会立即失效。": "", "网关 Key 表": "Gateway key table", "请先在上面选择可用提供商": "Select available providers above first", "读取 Key 列表失败:{reason}": "Failed to load key list: {reason}", + "还没有 Key。去桌面端「网关 Key」页新建一把。": "", "还没有 Key,当前不鉴权": "No keys yet — auth is currently off", "这几家当前没有可用模型(账号未登录或清单为空)": "These providers currently have no available models (accounts not signed in or catalog empty)", "这把 Key 不限制提供商与模型(可用全部上游与全部对外模型)": "This key is not restricted by provider or model (all upstreams and all public models are available)", @@ -49,5 +56,7 @@ "(模型候选需先选提供商;不选则沿用当前这几项,保存后仍按模型白名单生效)": "(model candidates require selecting providers first; if none are selected the current entries are kept, and the model allowlist still applies after saving)", "(行内「可用范围」):留空 = 不限制,两个都设时 按交集生效 —— 被限制的模型对这把 Key 表现为「不存在」(拉 /v1/models 也看不到它), 提供它的家不在可用列表里时请求同样被拒。": " (the inline \"Allowed scope\"): leave blank = no restriction; when both are set, the intersection applies — a restricted model simply \"does not exist\" for this key (it will not appear when fetching /v1/models either), and requests are also rejected when the provider offering it is not in the allowed list.", "+ 新建 Key": "+ New key", - "; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制": "; changes take effect immediately, and this app itself automatically uses the first enabled key. Each key can restrict " + "; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制": "; changes take effect immediately, and this app itself automatically uses the first enabled key. Each key can restrict ", + ";一把启用的 Key 都没有时不鉴权。": "", + ";当前必须带 Key。": "" } diff --git a/desktop-tauri/i18n/i18n-src/en/logs.json b/desktop-tauri/i18n/i18n-src/en/logs.json index d3459e6b..8721b5b5 100644 --- a/desktop-tauri/i18n/i18n-src/en/logs.json +++ b/desktop-tauri/i18n/i18n-src/en/logs.json @@ -10,9 +10,13 @@ "信息": "Info", "全部分类": "All categories", "全部级别": "All levels", + "复制失败": "", + "复制当前筛选结果": "", "导出": "Export", + "已复制 {n} 条日志": "", "按分类筛选": "Filter by category", "按最低级别筛选": "Filter by minimum level", + "搜索关键字": "", "搜索消息关键词": "Search message keywords", "搜索消息关键词…": "Search message keywords…", "操作失败:{error}": "Operation failed: {error}", @@ -20,6 +24,7 @@ "暂无日志可导出": "No logs to export", "模型 {model}": "Model {model}", "正在加载日志…": "Loading logs…", + "没有符合条件的日志": "", "没有符合筛选条件的日志": "No logs match the filters", "清空中…": "Clearing…", "清空运行日志": "Clear runtime logs", @@ -27,6 +32,7 @@ "确定清空当前筛选出的 {count} 条日志?清空后无法恢复。": "Clear the {count} filtered log entries? This cannot be undone.", "系统事件": "System events", "警告": "Warning", + "读取日志失败": "", "调试": "Debug", "运行日志已清空": "Runtime logs cleared", "错误": "Error" diff --git a/desktop-tauri/i18n/i18n-src/en/models.json b/desktop-tauri/i18n/i18n-src/en/models.json index 82601c46..a319c50f 100644 --- a/desktop-tauri/i18n/i18n-src/en/models.json +++ b/desktop-tauri/i18n/i18n-src/en/models.json @@ -36,6 +36,7 @@ "{name}({id})": "{name} ({id})", "{name}(不在该家当前清单里)": "{name} (not in this provider's current catalog)", "{n} 个": "{n}", + "{n} 个映射": "", "{n} 个模型": "{n} models", "{total} 家 · 成功 {done}": "{total} providers · {done} succeeded", "{total} 家 · 成功 {done} · 失败 {failed}": "{total} providers · {done} succeeded · {failed} failed", @@ -67,8 +68,10 @@ "上游没有给出可读的错误说明": "Upstream returned no readable error description", "上游没有返回任何模型": "Upstream returned no models", "上游目录尚未拉到,用的是内置静态清单;点「刷新模型清单」可重试": "The upstream catalog hasn't been fetched yet, so the built-in static catalog is in use; click “Fetch models” to retry", + "上游能列出这个模型,但官方客户端不展示它;本仓已默认关闭它的默认绑定(点这一行的开关可以手动打开)": "Upstream lists this model, but the official client does not show it; the gateway has disabled its default binding (use the row switch to enable it manually)", "下游客户端": "downstream clients", "下游请求 ": "Downstream request ", + "不展示": "Not shown", "不影响网关的转发与路由": "Doesn't affect gateway forwarding or routing", "不支持": "Not supported", "不覆盖": "Do not override", @@ -115,6 +118,7 @@ "刷新全部家(逐家打上游)": "Refresh every provider (each hits upstream once)", "刷新完成,但没有得到任何结果": "Refresh finished, but no results were returned", "刷新模型管理页里各提供商的远程模型目录,逐家结果列在弹窗里": "Refresh the remote model catalogs of the providers on the Model Management page; per-provider results are listed in the dialog", + "刷新清单": "", "加载中…": "Loading…", "勾上它,导入后即进入这家的清单": "Check it to add it to this provider's catalog on import", "勾几个就并行测几次;默认选队列里最靠前的一个。": "Each account you check is tested in parallel; the first one in the queue is selected by default.", @@ -126,6 +130,8 @@ "只刷新这一家的模型清单": "Refresh only this provider's model catalog", "只属于这一家": "exclusive to this provider", "可删除 {n} 个(手动登记 / 自定义家);": "{n} can be deleted (manually registered / custom provider); ", + "可见": "Visible", + "可见性": "Visibility", "可配思考档位": "Configurable reasoning levels", "同一条错误出现在所有账号上": "the same error appears on all accounts", "后端桥不可用": "Backend bridge unavailable", @@ -140,6 +146,7 @@ "失败": "Failed", "失败 {n} 个 · 首个失败:{error}": "{n} failed · first failure: {error}", "完成": "Done", + "官方客户端会展示这个模型": "The official client lists this model", "对下游声明的能力(工具调用 / 图片识别 / 视频识别 / 支持思考)—— 点击修改": "Capabilities declared to downstream (tool calling / image recognition / video recognition / reasoning support) — click to edit", "对外名可自由命名,允许与上游模型 ID 同名(同名时该上游的原生路由优先,映射作兜底); 支持字母、数字与 ": "The public name can be anything and may match an upstream model ID (when identical, that upstream's native route takes precedence and the mapping acts as a fallback); supported characters are letters, digits and ", "对外映射名(下游请求时用)": "Public mapping name (used in downstream requests)", @@ -183,12 +190,14 @@ "批量操作": "Batch actions", "批量操作 · 已选 {n} 个模型": "Batch actions · {n} models selected", "按倍率排序": "Sort by multiplier", + "按可见性筛选": "Filter by visibility", "按所选网关对话协议组装请求并校验响应转换(Chat Completions / Responses / Anthropic Messages)。": "Build the request and verify response translation using the selected gateway protocol (Chat Completions / Responses / Anthropic Messages).", "按提供商选择": "Select by provider", "按状态筛选": "Filter by status", "提供商": "Provider", "搜索模型": "Search models", "搜索模型 ID / 名称 / 映射名…": "Search model ID / name / mapping name…", + "搜索模型 ID / 提供商": "", "搜索模型 ID…": "Search model ID…", "搜索账号…": "Search accounts…", "操作": "Actions", @@ -197,6 +206,7 @@ "收起 ▴": "Collapse ▴", "数字带虚线底 = 由 models.dev 补齐;右上角小点 = 你手动覆盖过;— = 未声明。点任意一格可改。": "Number with a dotted underline = filled in by models.dev; small dot at the corner = you overrode it; — = not declared. Click any cell to edit.", "新建一个自定义提供商(同时创建它的第一个账号)": "Create a custom provider (and its first account)", + "映射、思考等级、上下文窗口与工具调用能力在桌面端「模型管理」里改;手机端只做启停。": "", "映射「{alias} → {target}」不存在": "Mapping “{alias} → {target}” doesn't exist", "映射上未绑定思考等级,「跟随映射」等于这次不注入": "No reasoning level is bound to this mapping, so “Follow mapping” means nothing is injected this time", "映射上绑定的思考等级是 {level},「跟随映射」按它注入": "The reasoning level bound to this mapping is {level}; “Follow mapping” injects it", @@ -227,6 +237,7 @@ "模型数": "Models", "模型映射": "Model mapping", "模型来源": "Model source", + "模型清单已刷新": "", "模型管理表": "Models table", "模型能力": "Model capabilities", "正在刷新…": "Refreshing…", @@ -235,6 +246,7 @@ "每条映射 = 对外名 → 上游模型;原始 ID 与别名有独立开关,默认绑定永远存在(没有映射时是合成的那条)。": "Each mapping = public name → upstream model; the original ID and each alias have their own toggle, and the default binding always exists (it's the synthesized one when there are no mappings).", "没有匹配「{keyword}」的模型": "No models match “{keyword}”", "没有匹配当前筛选的模型": "No models match the current filter", + "没有匹配的模型": "", "没有选中任何模型": "No models selected", "没有选中的模型": "No models selected", "流式请求": "Streaming request", @@ -324,8 +336,11 @@ "还没有可刷新的提供商:先在账号页添加一个账号": "No providers to refresh yet: add an account on the Accounts page first", "还没有自定义提供商": "No custom providers yet", "这一行已不在当前清单里(模型被移除、或目录刷新后上游不再提供它)。关闭后刷新列表再试。": "This row is no longer in the current catalog (the model was removed, or upstream no longer offers it after a catalog refresh). Close this dialog, refresh the list, and try again.", + "这个模型在官方客户端里是否会被展列;不展示的那些默认已关闭(含默认绑定)": "Whether the official client lists this model; entries that are not shown are disabled by default (including the default binding)", "这个模型已经在这家的清单里,不重复导入": "This model is already in this provider's catalog; it won't be imported again", "这几项是给": "These fields are capability declarations for ", + "这家提供商没有可见性判据(目前只有 Antigravity / Qoder / 小浣熊 / CodeArts 有)": "This provider has no visibility rule (currently only Antigravity / Qoder / Raccoon / CodeArts have one)", + "这家有可见性判据,但这一条上游没有表态 —— 按可见处理": "This provider has a visibility rule, but upstream did not state it for this entry — treated as visible", "这家没有可用账号(要在账号页启用一个、且凭证完整)": "This provider has no usable accounts (enable one on the Accounts page with complete credentials)", "这家用固定模型清单,无可刷新": "This provider uses a fixed model catalog; nothing to refresh", "这家的模型清单是全局的,不跟账号走": "This provider's model catalog is global and not tied to accounts", diff --git a/desktop-tauri/i18n/i18n-src/en/proxies.json b/desktop-tauri/i18n/i18n-src/en/proxies.json index 28560e1d..24a5a453 100644 --- a/desktop-tauri/i18n/i18n-src/en/proxies.json +++ b/desktop-tauri/i18n/i18n-src/en/proxies.json @@ -3,11 +3,14 @@ " 提供 AI 账号专用的原生静态住宅 IP(Static Residential ISP):IP 来自真实家庭宽带运营商,Scamalytics 低欺诈评分,每个 IP 独享,不限流量,$3.90 起。": " offers native static residential ISP proxies for AI accounts (Static Residential ISP): IPs come from real home broadband operators with low Scamalytics fraud scores, each IP is dedicated to you, with unlimited traffic, from $3.90.", " 那一类由「同步 Clash Verge」 镜像进来(进入本页时会自动同步一次):名称、端口、启用都跟随 Clash, 在 Clash 里改或删,这里跟着变 —— 所以它们只读,改请到 Clash Verge 里改。": " entries are mirrored in by \"Sync Clash Verge\" (auto-synced once when you enter this page): name, port, and enabled state all follow Clash — edit or delete them there and they change here too, so these entries are read-only; make your changes in Clash Verge.", "Clash Verge": "Clash Verge", + "Clash 同步失败:{msg}": "", + "Clash 镜像": "", "YAML 文件": "YAML file", "{names} 等 {n} 条": "{names} and {n} in total", "{n} 个": "{n}", "{n} 个出口": "{n} endpoints", "{n} 个账号在用": "{n} accounts using", + "{n} 个账号引用": "", "{n} 条订阅": "{n} subscriptions", "{percent}%({received} / {total} MB)": "{percent}% ({received} / {total} MB)", "✅ {ip}": "✅ {ip}", @@ -31,7 +34,7 @@ "也可以把二维码截图直接粘贴到弹窗里(Ctrl+V)": "You can also paste a QR code screenshot directly into this dialog (Ctrl+V)", "二维码里没有订阅链接": "No subscription URL found in the QR code", "从图片识别二维码": "Scan QR code from image", - "代理内核未安装(在「订阅管理」里下载内核后,节点会自动分配监听端口)": "Proxy kernel is not installed (download it under \"Manage subscriptions\" and nodes get listening ports automatically)", + "代理内核未安装(在「订阅管理」里下载内核后,被账号引用的节点会自动开监听端口)": "Proxy kernel is not installed (after downloading it in Subscription Management, referenced nodes automatically get a listening port)", "代理列表加载失败:{reason}": "Failed to load proxy list: {reason}", "代理池": "Proxy pool", "代理池表": "Proxy pool table", @@ -69,6 +72,8 @@ "内核没有在超时内停下来": "the kernel did not stop within the timeout", "内核目录没能删掉(可能仍被进程占用),稍后重试一次": "The kernel folder could not be removed — a process may still hold it. Try again in a moment.", "内核运行中": "Kernel running", + "出口 IP {ip}": "", + "出口可用": "", "出口测试网址": "Connectivity test URL", "删除代理": "Delete proxy", "删除内核": "Delete kernel", @@ -83,6 +88,7 @@ "只读:在 Clash Verge 里改": "Read-only: edit it in Clash Verge", "只读:在「订阅管理」里改": "Read-only: edit it under \"Manage subscriptions\"", "同步 Clash Verge": "Sync Clash Verge", + "同步 Clash Verge 出口": "", "同步中…": "Syncing…", "同步失败:{reason}": "Sync failed: {reason}", "名字与端口在 Clash Verge 里改;启用可在本页关闭": "Edit the name and port in Clash Verge; the enable switch can be turned off on this page", @@ -96,6 +102,7 @@ "启用(禁用的订阅不参与内核同步,它的节点会从代理池移除)": "Enabled (disabled subscriptions are not synced by the kernel, and their nodes are removed from the proxy pool)", "图片读取失败": "Failed to read the image", "地址": "Address", + "地址不完整": "Incomplete address", "复制订阅链接": "Copy subscription URL", "失败 {n} 个({reason})": "{n} failed ({reason})", "存放位置": "Folder", @@ -109,13 +116,16 @@ "已删除 {n} 个代理": "Deleted {n} proxies", "已删除「{name}」": "Deleted \"{name}\"", "已删除内核(订阅与账号引用保留,重新下载后会重新分配端口)": "Kernel deleted (subscriptions and account references are kept; ports are reassigned after you download it again)", + "已删除内核(订阅与账号引用保留;有文件仍被占用,将在下次启动时清理)": "Kernel deleted (subscriptions and account references are kept; some files are still in use and will be cleaned up on the next launch)", "已刷新「{name}」,正在同步节点…": "Refreshed \"{name}\"; syncing nodes…", + "已同步 {n} 项变更": "", "已启用「{name}」": "Enabled \"{name}\"", "已启用「{name}」,正在补建监听端口(内核重建中)": "Enabled \"{name}\"; rebuilding its listening port (kernel is restarting)", "已在本页禁用(打开开关即恢复)": "Disabled on this page (turn the switch on to restore it)", "已新增「{name}」": "Added \"{name}\"", "已新增订阅「{name}」,内核正在同步节点…": "Added subscription \"{name}\"; the kernel is syncing nodes…", "已是最新,没有需要同步的变更": "Already up to date — no changes to sync", + "已有账号引用它,但内核还没把监听端口补建出来 —— 稍等片刻,或重新启用这条节点": "An account already references it, but the kernel has not created the listening port yet — wait a moment, or re-enable this node", "已禁用": "Disabled", "已禁用「{name}」": "Disabled \"{name}\"", "已禁用「{name}」,监听端口正在关闭(内核重建中)": "Disabled \"{name}\"; its listening port is closing (kernel is restarting)", @@ -132,6 +142,7 @@ "当前环境不支持订阅(桥接方法缺失)": "Subscriptions are not supported in this environment (bridge method missing)", "当前环境不支持选择文件夹,直接填写绝对路径即可": "This environment cannot open a folder picker - just type the absolute path", "当前线路不可用:{error};重新选一条或切回直连": "The current route is unavailable: {error}. Pick another one or switch back to direct", + "所属订阅已删除,这条节点不会再分配端口 —— 可以删除它": "Its subscription was deleted, so this node will never get a port — you can delete it", "手动填写的地址与端口": "Manually entered address and port", "打开文件夹选择框失败:{reason}": "Failed to open the folder picker: {reason}", "批量删除": "Delete selected", @@ -148,6 +159,7 @@ "搜索节点": "Search endpoints", "文件读取失败": "Failed to read the file", "新增代理": "New proxy", + "新增出口、编辑地址与订阅管理在桌面端完成;手机端做启停与连通性测试。": "", "新增订阅": "New subscription", "显示 {n} / 共 {total} 个出口": "Showing {n} / {total} endpoints", "更新中…": "Updating…", @@ -162,6 +174,7 @@ "本地文件不会自动更新:要更新节点,重新选择文件后保存;不重选则内容保持不变。": "Local files do not update automatically: to update the nodes, pick the file again and save; without re-picking, the content stays unchanged.", "本地文件订阅无需更新(要换内容请编辑后重新选择文件)": "Local-file subscriptions need no update (to change the content, edit it and pick the file again)", "本地文件订阅的内容在导入时固定:节点会由内核起成本地监听器并镜像进代理池,节点数稍等几秒由轮询刷新。": "A local-file subscription's content is fixed at import time: the kernel starts the nodes as local listeners and mirrors them into the proxy pool; the node count refreshes by polling in a few seconds.", + "本机没有读到 Clash Verge 配置": "", "本页可编辑、可删除": "Editable and deletable on this page", "来自 Clash Verge 同步(名称、端口跟随 Clash),请到 Clash Verge 中修改;启用可在本页关闭": "Synced from Clash Verge (name and port follow Clash); edit them in Clash Verge. The enable switch can be turned off on this page.", "正在下载内核": "Downloading kernel", @@ -180,7 +193,7 @@ "测试网址要以 http:// 或 https:// 开头": "The test URL must start with http:// or https://", "确定删除「{name}」?{used}": "Delete \"{name}\"? {used}", "确定删除「{name}」?它在代理池里的节点会一并移除,引用这些节点的账号会回退直连。": "Delete \"{name}\"? Its nodes in the proxy pool will be removed too, and accounts referencing them will fall back to direct connections.", - "确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会变成「未分配端口」,引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。": "Delete the downloaded kernel{version}? The kernel files and the port mapping will be removed, subscription nodes become \"no port assigned\", and accounts referencing them fall back to a direct connection for now. The subscriptions themselves are kept, and ports are reassigned after you download the kernel again.", + "确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会失去监听端口(地址列显示「端口准备中」),引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。": "Delete the downloaded kernel{version}? The kernel files and the port mapping will be removed, subscription nodes lose their listening ports (the address column shows \"Port pending\"), and accounts referencing them fall back to a direct connection for now. The subscriptions themselves are kept, and ports are reassigned after you download the kernel again.", "确定删除选中的 {n} 个代理?": "Delete the {n} selected proxies?", "禁用后本端不再用它(引用它的账号回退直连),并会关掉它的本地监听端口": "Once disabled this machine stops using it (accounts referencing it fall back to direct), and its local listening port is closed", "禁用后本端不再用它(引用它的账号回退直连);端口在 Clash Verge 侧,不受影响": "Once disabled this machine stops using it (accounts referencing it fall back to direct); the port stays as it is on the Clash Verge side", @@ -200,6 +213,7 @@ "订阅来源": "Subscription source", "订阅管理": "Manage subscriptions", "订阅管理({n})": "Manage subscriptions ({n})", + "订阅节点": "", "订阅节点会在本机各占一个监听端口,从该范围里挑空闲的分配;节点多的大订阅把默认范围({base}-{end})用尽时会报「没有可用的节点端口」,这时把范围调大再重启内核。已分配出去的端口不会因改范围而失效。": "Each subscription node takes one local listener port, allocated from free ports in this range; when a large subscription exhausts the default range ({base}-{end}) the app reports \"no free node ports\" — enlarge the range and restart the kernel. Ports already allocated stay valid when the range changes.", "订阅里的节点会自动同步进下方代理池,账号即可引用。": "Nodes from subscriptions are synced into the proxy pool below, ready for accounts to use.", "订阅里的节点会自动同步进代理池,账号即可引用。": "Nodes from the subscription are synced into the proxy pool automatically, ready for accounts to use.", @@ -216,10 +230,11 @@ "跳过 {n} 个(同步只读条目)": "skipped {n} (synced read-only entries)", "还没有代理。点右上角「+ 新增代理」手填一个,或在「订阅管理」里导入订阅节点、同步 Clash Verge 的出口。": "No proxies yet. Click \"+ New proxy\" in the top right to fill one in manually, or import subscription nodes / sync Clash Verge endpoints under \"Manage subscriptions\".", "还没有代理。点右上角「+ 新增代理」手填一个,或点「同步 Clash Verge」把 Clash 的出口导进来。": "No proxies yet. Click \"+ New proxy\" in the top right to fill one in manually, or click \"Sync Clash Verge\" to import Clash endpoints.", - "还没有监听端口(还没有账号引用它 —— 在账号里选它并保存后,内核会自动开端口)": "No listening port yet (no account references it — pick it in an account and save, the kernel opens the port automatically)", + "还没有出口。在桌面端「网络代理」页新增,或点上面同步 Clash。": "", "还没有订阅。点右上角「+ 新增订阅」粘贴订阅链接,或从图片识别二维码导入。": "No subscriptions yet. Click \"+ New subscription\" in the top right to paste a subscription URL, or import one by scanning a QR code from an image.", "还没有订阅:粘贴订阅链接、导入本地 YAML 文件,或从图片识别二维码。": "No subscriptions yet: paste a subscription URL, import a local YAML file, or scan a QR code from an image.", "还没有订阅:粘贴订阅链接,或从图片识别二维码导入。": "No subscriptions yet: paste a subscription URL, or scan a QR code from an image.", + "还没有账号使用它(端口只在被账号引用时才分配 —— 在账号里选它并保存后,内核会自动开端口)": "No account is using it yet (a port is only assigned once an account references the node — select it in an account and save, and the kernel opens the port automatically)", "这条在 Clash Verge 里是停用的,本页启用不生效 —— 到 Clash Verge 里打开它": "This listener is disabled in Clash Verge, so enabling it here has no effect — turn it on in Clash Verge", "连接成功": "Connected", "连通性": "Connectivity", @@ -231,6 +246,7 @@ "部分测试结果没能记下:{reason}": "Some test results could not be saved: {reason}", "重启内核": "Restart kernel", "重新选择文件…": "Choose a different file…", + "镜像当前出口集合,名称与端口跟随 Clash": "", "需要干净的出口?": "Need a clean exit?", "首次购买使用优惠码 AGENT2API25 享 75 折。": "Use code AGENT2API25 for 25% off your first purchase.", "+ 新增代理": "+ New proxy", diff --git a/desktop-tauri/i18n/i18n-src/en/report.json b/desktop-tauri/i18n/i18n-src/en/report.json index c5c3708b..2bda8365 100644 --- a/desktop-tauri/i18n/i18n-src/en/report.json +++ b/desktop-tauri/i18n/i18n-src/en/report.json @@ -1,6 +1,7 @@ { "30 天": "30 days", "7 天": "7 days", + "Key": "", "Token": "Token", "Top 提供商": "Top providers", "Top 模型": "Top models", @@ -12,6 +13,11 @@ "{m}月{d}日 周{wd}": "{m}/{d} {wd}", "{name} · {tokens} tokens · {n} 次请求 · {pct}": "{name} · {tokens} tokens · {n} requests · {pct}", "{name}:{tokens} tokens · 占 {pct}%": "{name}: {tokens} tokens · {pct}% of total", + "{n} 个账号启用 · {m} 个限流中": "", + "{n} 个账号正在限流,转发会自动跳过它们": "", + "{n} 天": "", + "{n} 次请求": "", + "{start} ~ {end}": "", "{tokens} tokens · 占 {pct}": "{tokens} tokens · {pct} of total", "…还有 {n} 个模型": "…and {n} more models", "一": "Mo", @@ -19,14 +25,21 @@ "二": "Tu", "五": "Fr", "今天": "Today", + "今天都签完了": "", + "今日签到 {done} / {total}": "", + "今日签到 {done}/{total}": "", + "今日请求": "", "六": "Sa", "区间共 {n} 天": "{n} days in range", + "区间内累计": "", + "可用账号": "", "右轴": "Right axis", "含今天在内往前数": "Counting back from today", "命中 {hit} / 输入 {input}": "Hits {hit} / input {input}", "命中率 = 缓存读取 Token / 输入 Token(promptTokens)。窗口越短越贴近「此刻」:上游把缓存读取与输入分开上报时,该比值合法地可能超过 100%,此处不夹取值、如实展示。": "Hit rate = cache-read tokens / input tokens (promptTokens). The shorter the window, the closer it is to \"right now\": when an upstream reports cache reads separately from input, this ratio can legitimately exceed 100%; it is shown as-is, without clamping.", "命中率 {rate}(命中 {hit} / 输入 {input})": "Hit rate {rate} ({hit} hits / {input} input)", "四": "Th", + "失败 {n} 次": "", "左轴": "Left axis", "开始日期": "Start date", "当前连续天数": "Current streak", @@ -46,32 +59,49 @@ "所选范围内还没有请求记录": "No request records in the selected range", "所选范围内还没有账号用量": "No account usage in the selected range", "报表时间范围": "Report time range", + "报表视角": "", + "按天 Token 用量": "", "按天 Token 趋势": "Daily token trend", "按本地整点,无请求的整点不标注": "By local hour; hours with no requests are not labeled", "提供商用": "Provider usage", "提供商用占比": "Provider usage share", + "提供商用量": "", "提供商:{text}": "Provider: {text}", "方格为本地自然日,无请求显示为空槽": "Each cell is a local calendar day; days with no requests appear as empty slots", "无请求的整点按 0% 计,但不写读数": "Hours with no requests count as 0%, but are not labeled", "日": "Su", + "时间范围。「今天」是本地自然日今天 0 点到现在;「昨天」是昨天一整天(0 点到 24 点);「近 7 天 / 近 30 天」含今天在内往前数,即今天与之前的 6 / 29 天,不是「不含今天的 7 / 30 天」。": "Time range. \"Today\" is the local calendar day from 00:00 until now; \"Yesterday\" is the whole of yesterday (00:00–24:00); \"Last 7 days\" / \"Last 30 days\" count backwards from today inclusive, i.e. today plus the previous 6 / 29 days — not 7 / 30 days excluding today.", + "昨天": "Yesterday", "暂无热力图数据": "No heatmap data", "暂无缓存趋势数据": "No cache trend data", "暂无趋势数据": "No trend data", + "最近一次请求使用的模型": "", + "最近请求": "", + "最近请求 {n} 条": "", "未知模型": "Unknown model", + "未配置上游": "", "本月": "This month", "模型用量": "Model usage", "模型用量占比": "Model usage share", "每天的总 Token 用量(输入 + 输出)。柱顶标出当天的读数;区间很长、柱子挤到放不下时只标最高的那几天,其余悬停柱子即可读到。": "Total token usage for each day (input + output). Each bar is labeled with that day's reading; when the range is long and bars are too crowded for labels, only the highest days are labeled—hover over any bar to read the rest.", + "活跃": "", "活跃天数": "Active days", "活跃热力图": "Activity heatmap", + "管理": "", + "累计请求 {n} 条": "{n} requests in total", "结束日期": "End date", "结束日期(含当天)": "End date (inclusive)", + "统计区间加载中": "", "统计概览": "Statistics overview", "缓存命中率": "Cache hit rate", "自定义区间": "Custom range", + "请求量": "", "读取报表失败:{error}": "Failed to load report: {error}", + "账号分布": "", + "账号用量": "", "起始日期(含当天)": "Start date (inclusive)", "输入 + 输出": "Input + output", + "运行中": "", "近 1 小时": "Last 1 hour", "近 10 分钟": "Last 10 minutes", "近 24 小时": "Last 24 hours", @@ -80,5 +110,9 @@ "近 30 天": "Last 30 days", "近 365 天活跃热力图": "Last 365 days: activity heatmap", "近 365 天,按天 Token 用量着色(档位按本窗口峰值自动定)": "Last 365 days, colored by daily token usage (levels are set automatically from this window's peak)", - "近 7 天": "Last 7 days" + "近 7 天": "Last 7 days", + "还有 {n} 个待签": "", + "还没有添加账号": "", + "还没有请求记录": "", + "连续天数": "" } diff --git a/desktop-tauri/i18n/i18n-src/en/requests.json b/desktop-tauri/i18n/i18n-src/en/requests.json index a7382b2b..19260642 100644 --- a/desktop-tauri/i18n/i18n-src/en/requests.json +++ b/desktop-tauri/i18n/i18n-src/en/requests.json @@ -41,6 +41,7 @@ "切换路径:": "Switch path:", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "List data has been refreshed; please try again (close and reopen this detail)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "Deletes the log records themselves. With filters active, only matching entries are deleted and daily aggregate reports are unaffected; with no filters, everything is cleared and reports are reset. This cannot be undone.", + "加载更早的请求": "", "压缩中…": "Vacuuming…", "压缩失败:{message}": "Vacuum failed: {message}", "压缩已在后台开始,完成后会自动提示": "Vacuum started in the background; you will be notified when it completes", @@ -49,6 +50,7 @@ "压缩数据库": "Vacuum database", "压缩正在后台执行,完成后自动恢复": "Vacuum is running in the background; it resumes automatically when it completes", "原始报文": "Raw payload", + "发给上游的模型": "", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "{missing} more attempts have no details recorded (only the earliest {kept} are kept)", "另有 {n} 个词命中": "{n} more words hit", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "Only shows requests currently being forwarded (the status column shows the phase: Connecting / Awaiting response / Streaming / Retrying; the duration column shows elapsed time)", @@ -62,14 +64,18 @@ "响应头": "Response headers", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "Reclaims the disk space used by deleted data (checkpoint + VACUUM, runs in the background)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "Raw payloads are only saved for requests made after \"Debug mode\" is enabled in Settings → General", + "在途 {n}": "", "失败请求不记录用量": "Usage is not recorded for failed requests", "失败({status}):{error}": "Failed ({status}): {error}", "失败:{error}": "Failed: {error}", "实际发给上游 {bytes} 字节": "{bytes} bytes actually sent upstream", "客户端": "Client", + "客户端请求的模型": "", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "Will delete {count} logs · {raw} of them still carry raw payloads · database size {db}", "尝试 {n} ·": "Attempt {n} ·", "尝试明细": "Attempt details", + "尝试次数": "", + "尝试链": "", "工具结果": "Tool result", "工具结果 · {id}": "Tool result · {id}", "工具结果图片": "Tool result image", @@ -77,6 +83,7 @@ "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "Abort accepted: the upstream connection is closed; the list will refresh to \"Request manually aborted\" shortly", "已抹掉 {count} 条报文原文": "Raw payloads wiped for {count} logs", "已用时(请求仍在转发中)": "Elapsed time (request is still being forwarded)", + "当前筛选下没有记录": "", "成功": "Success", "成功({status})": "Success ({status})", "执行清理": "Run clear", @@ -88,6 +95,7 @@ "按模型筛选": "Filter by model", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "Filter by model (only lists models seen in the logs; matches the exact model name)", "按请求结果筛选": "Filter by request result", + "换号链:{chain}": "", "排队中": "Queued", "提供商 / 账号": "Provider / Account", "提供商: {provider}": "Provider: {provider}", @@ -98,12 +106,15 @@ "无报文原文(未开启「调试模式」,或已被清理)": "No raw payload (debug mode was off, or it has been cleared)", "无结果记录": "No result recorded", "时间": "Time", + "暂无请求日志": "", "暂无请求日志,网关还没有转发过请求": "No request logs yet; the Gateway hasn't forwarded any requests", "最终承载:{picked}": "Finally served by: {picked}", + "有重试": "", "本轮尝试失败,正在退避等待或切换到下一个账号": "This attempt failed; backing off or switching to the next account", "来源客户端:{client}": "Source client: {client}", "查看该请求的上游原始报文": "View the raw upstream payload for this request", "模型": "Model", + "模型映射:{from} → {to}": "", "模型测试": "Model test", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "Requests started by the \"Test\" button in the action column of the Model Management page: the same forwarding path as real requests, but not counted in reports", "正在加载请求日志…": "Loading request logs…", @@ -130,6 +141,7 @@ "缓存读取: - / 命中率: -": "Cache reads: - / Hit rate: -", "缓存读取: {cache} / 命中率: {rate}": "Cache reads: {cache} / Hit rate: {rate}", "耗时": "Duration", + "脱敏命中:{words}": "", "被跳过:": "Skipped: ", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent present but not a known client type: {ua}", "详情": "Details", @@ -148,7 +160,9 @@ "路由回放": "Routing replay", "转发到上游的模型:{model}": "Model forwarded to the upstream: {model}", "轮次": "Round", + "输入 Token": "", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "Input {input} · Output {output} · Total {total} · Cache reads {cache}", + "输出 Token": "", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "This detail row is a model test (manually started): it takes the same forwarding path as real requests but is not counted in reports", "这条明细没有记录提供商(旧数据或请求未走到转发)": "No provider is recorded for this detail row (old data, or the request never reached forwarding)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "No source client recorded for this entry (legacy data, model test, or request without User-Agent)", @@ -160,11 +174,13 @@ "进行中…": "In progress…", "连接中": "Connecting", "重试 {n}": "Retry {n}", + "重试 {n} 次": "", "重试中": "Retrying", "重试次数": "Retries", "预览对话": "Conversation preview", "预览读取失败": "Failed to load preview", "预览读取失败:{error}": "Failed to load preview: {error}", + "首响": "", "首响 {time}": "TTFB {time}", "首响:上游首帧到达的耗时": "TTFB: time to first byte from the upstream", "首响:上游首帧到达的耗时(请求仍在转发中)": "TTFB: time to first byte from the upstream (request is still being forwarded)", diff --git a/desktop-tauri/i18n/i18n-src/en/settings.json b/desktop-tauri/i18n/i18n-src/en/settings.json index dde21add..b518a054 100644 --- a/desktop-tauri/i18n/i18n-src/en/settings.json +++ b/desktop-tauri/i18n/i18n-src/en/settings.json @@ -2,30 +2,58 @@ " 到 {file}": " to {file}", " 正文已改:{edited}(其余段用官方原文)。": " Text edited: {edited} (the remaining blocks use the official text).", "(无 id)": "(no id)", + "/v1 拒绝无 Key 请求": "/v1 rejects keyless requests", "0 B": "0 B", "100%(默认)": "100% (default)", "<本机IP>": "", + "Access Key ID": "Access Key ID", + "Agent2API 手机端": "Agent2API Mobile", + "Android 应用(APK)": "Android app (APK)", "Cline 上游按请求头判定「调用方是不是 Cline 自家产品」:X-CLIENT-TYPE: cline-sdk 是硬门槛(缺了它免费池模型一律 403),版本号与平台标识让请求更贴近官方客户端的形态。这里列出的是网关默认会发的头:改某一行的值 = 覆盖默认值(也可以新增默认清单之外的自定义头);把值清空保存 = 这个头不发送;在界面删掉被改过的行 = 回落默认值。改完点「保存」,下一个 Cline 请求立即生效。不懂某一行的含义就不要动它 —— 伪装头的价值在「与官方客户端逐字一致」。": "The Cline upstream decides from the request headers whether the caller is one of Cline's own products: X-CLIENT-TYPE: cline-sdk is a hard requirement (without it, free-pool models always return 403), and the version number and platform identifier make the request look closer to the official client. Listed here are the headers the gateway sends by default: editing a row's value overrides the default (you can also add custom headers beyond the default list); saving an empty value means this header is not sent; deleting an edited row in the UI reverts it to the default. Click \"Save\" when done and the next Cline request picks it up immediately. Do not touch a row you do not understand — the value of spoofed headers lies in being word-for-word identical to the official client.", "Cline 伪装头": "Cline Spoofed Headers", "Cline 伪装头已刷新": "Cline spoofed headers refreshed", + "Docker 容器": "Docker container", + "Endpoint": "Endpoint", + "Endpoint 填 https://<账户ID>.r2.cloudflarestorage.com,Region 固定填 auto。": "Set Endpoint to https://.r2.cloudflarestorage.com and Region to auto.", + "Endpoint 填 https://cos.ap-guangzhou.myqcloud.com,Region 填 ap-guangzhou(按实际地域改)。": "Set Endpoint to https://cos.ap-guangzhou.myqcloud.com and Region to ap-guangzhou (change to your actual region).", + "Endpoint 填 https://obs.cn-north-4.myhuaweicloud.com,Region 填 cn-north-4(按实际地域改)。": "Set Endpoint to https://obs.cn-north-4.myhuaweicloud.com and Region to cn-north-4 (change to your actual region).", + "Endpoint 填 https://oss-cn-hangzhou.aliyuncs.com,Region 填 cn-hangzhou(按实际地域改)。": "Set Endpoint to https://oss-cn-hangzhou.aliyuncs.com and Region to cn-hangzhou (change to your actual region).", + "Endpoint 形如 http://192.168.1.10:9000(自建通常用 http);Region 一般填 us-east-1。": "Endpoint looks like http://192.168.1.10:9000 (self-hosted usually uses http); Region is typically us-east-1.", + "Endpoint 留空即走 AWS 官方地址;Region 必须与桶所在区域一致。": "Leave Endpoint empty to use the official AWS address; Region must match the bucket's region.", + "Linux 安装包(deb)": "Linux package (deb)", + "Nextcloud": "Nextcloud", "OpenAI 新版 API 用 developer 角色取代 system 承载指令,新版 OpenAI SDK 与部分客户端发的对话里会出现 role 为 developer 的消息;不少 OpenAI 兼容上游(尤其是自定义提供商的透传路径)不认识这个角色,收到就报 400。开启本项后,网关在每次转发前把 developer 角色的消息改写为 system —— 两类角色在 OpenAI 语义上等价(OpenAI 自己的迁移指南也是这么映射的),改写不影响对话内容与语义;对本来就只认 system 的各家内置上游同样无害。默认开启;关掉后 developer 消息会原样发往上游,不认识它的上游会报「角色不存在」一类的错误。": "The newer OpenAI API carries instructions in a developer role instead of system, so conversations sent by current OpenAI SDKs and some clients contain messages with role \"developer\". Many OpenAI-compatible upstreams (especially the passthrough path of custom providers) do not recognize that role and reject the whole request with 400. When enabled, the gateway rewrites developer-role messages to system before every forward — the two roles are equivalent in OpenAI semantics (OpenAI's own migration guide maps them the same way), so the rewrite changes neither content nor meaning, and it is harmless to the built-in upstreams that only accept system anyway. Enabled by default; when off, developer messages go upstream as-is and upstreams that do not know the role return \"unknown role\"-style errors.", + "QQ 群 · Telegram · Discord": "QQ groups · Telegram · Discord", + "S3 兼容存储": "S3-compatible storage", + "Safari 没有安装按钮:点底部的「分享」,选「添加到主屏幕」,再点「添加」。": "Safari has no install button: tap Share at the bottom, choose \"Add to Home Screen\", then tap \"Add\".", + "Secret Access Key": "Secret Access Key", "Token 读数量级": "Token Readout Scale", "UTF-8 文本文件路径。留空用内置默认;只在「替换 / 追加」下读取。": "", "UTF-8 文本文件路径,留空用内置默认提示词(通用编码助手提示词,不含任何会被上游拦截的模板句)。只在「替换 / 追加」模式下生效,透传模式不读它。": "Path to a UTF-8 text file. Leave empty to use the built-in default prompt (a general coding-assistant prompt with no template sentences the upstream would block). Only applies in Replace / Append mode; passthrough mode does not read it.", + "WebView / 浏览器内核": "WebView / browser engine", + "Windows 安装包(NSIS)": "Windows installer (NSIS)", + "Windows 服务": "Windows service", "ZCode 套餐池(Start Plan)通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。": "Every request on the ZCode plan-pool (Start Plan) channel consumes a freshly minted CAPTCHA token (one-shot, expires in 2 minutes); when stock falls below this value the UI mints more up to it. Default 3. A larger value absorbs bursts, but unused tokens expire after 2 minutes and burn the upstream risk-control quota. Allowed 1-10.", "ZCode 活动套餐通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。": "", "ZCode「套餐池(Start Plan)」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。": "Every inference request on the ZCode \"Plan pool (Start Plan)\" channel must carry a freshly minted Aliyun invisible-CAPTCHA token (one-shot, expires in about 2 minutes): tokens are minted silently by the mint guard in the UI and pushed into the gateway pool, from which forwarding takes one per request. The three values here govern the restocking rhythm: ① \"Stock target\" = the guard mints more only when stock falls below it, up to it (default 3); ② \"Polling interval\" = how often the guard checks stock, default 2500 ms (only affects reaction speed — no extra minting while stock is sufficient); ③ \"Per-round mint cap\" = the most tokens minted in one round upon a shortage, default 1 (consecutive mints are fixed 500 ms apart). When requests are dense and the request log keeps showing 503 \"needs a CAPTCHA token\", raise the target and per-round cap and poll faster so the pool keeps up — the trade-off is that the upstream (Aliyun) rate-limits minting: the more and faster you mint, the likelier you trip the limit, and unused tokens expire within 2 minutes anyway, effectively burning quota — so adjust in small steps. Saving takes effect on the next restock round, no restart needed.", "ZCode「活动套餐」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。": "", + "beta 版": "", + "iOS 应用": "iOS app", + "models.dev 元数据": "models.dev metadata", "models.dev 元数据已更新({count} 条)": "models.dev metadata updated ({count} entries)", "models.dev 是一个开源的模型资料库(社区维护),这里用它补齐「上游没告诉我们」的模型能力:上下文窗口、最大输出、工具调用、图片、视频、是否支持思考,以及这个模型认识哪些思考档位。优先级是:你在「模型能力」里手工改的值 > 上游清单自己声明的值 > models.dev 的值 —— 它只填上游没声明的空位,从不推翻上游已声明的。网关随包内嵌了一份快照,所以离线、首次启动、访问不通时也有数据;开启时会按「模型目录刷新」任务的间隔联网更新(同一轮里一并刷新,也可在这里立即刷新)。关闭只停联网刷新,已经拿到的数据继续用。": "models.dev is an open-source, community-maintained model database. It fills in what the upstream never told us: context window, max output, tool calling, images, video, reasoning support, and which reasoning levels the model actually accepts. Priority: values you edited by hand under \"Model capabilities\" > what the upstream catalog declares > models.dev — it only fills gaps the upstream left, and never overrides a declared value. A snapshot ships inside the app, so offline, first-run and blocked-network cases still have data; while enabled it refreshes online on the \"Model catalog refresh\" schedule (in the same round, or on demand here). Turning it off only stops the online refresh; existing data stays in use.", "models.dev 模型元数据": "models.dev model metadata", + "systemd 服务": "systemd service", "{count} 条": "{count} entries", "{count} 条 · 来自{origin}{at} · {mode}": "{count} entries · from {origin}{at} · {mode}", + "{d} 天 {h} 小时": "{d}d {h}h", + "{h} 小时 {m} 分钟": "{h}h {m}m", "{label} 提示词文件": "{label} prompt file", "{label} 的提示词模式": "{label} prompt mode", "{label}({message})": "{label} ({message})", "{lines} 行、{chars} 字符(按当前草稿计)": "{lines} lines, {chars} characters (based on the current draft)", "{mode}生效:上游收到的 system 来自{source}{lines}。": "{mode} in effect: the system prompt sent upstream comes from {source}{lines}.", + "{m} 分钟": "{m}m", "{noun}不能为空(可填 {min}–{max})": "{noun} cannot be empty (allowed range: {min}–{max})", "{noun}必须在 {min}–{max} 之间(当前填的是 {text})": "{noun} must be between {min} and {max} (currently entered: {text})", "{noun}必须是整数": "{noun} must be an integer", @@ -34,22 +62,33 @@ "{n} KB": "{n} KB", "{n} MB": "{n} MB", "{n} 个头将随每个 Cline 请求发送": "{n} headers will be sent with every Cline request", + "{n} 台可选 · 点此切换": "{n} servers · tap to switch", + "{n} 项 · {shown} 显示": "{n} items · {shown} shown", + "{total} 个菜单 · 显示 {shown} · 隐藏 {hidden} · {groups} 个分组": "{total} menus · {shown} shown · {hidden} hidden · {groups} groups", + "…": "", "⚠️ 密钥警告": "⚠️ Key warning", + "⚠️ 已关闭「{label}」的机器人校验": "⚠️ Bot check disabled for \"{label}\"", "⚠️ 机器人校验已关闭": "⚠️ Captcha disabled", "✅ Cline 伪装头已保存": "✅ Cline spoofed headers saved", "✅ models.dev 模型元数据已开启": "✅ models.dev model metadata enabled", + "✅ 云同步设置已保存": "✅ Cloud sync settings saved", "✅ 出站指纹脱敏已开启": "✅ Fingerprint masking enabled", + "✅ 密码已修改,其他设备的登录已失效": "✅ Password changed. Sign-ins on other devices have been revoked", "✅ 导入完成:{summary}": "✅ Import complete: {summary}", + "✅ 已上传到云端({size})": "✅ Uploaded to the cloud ({size})", "✅ 已为「{label}」装上网关自带提示词": "✅ Gateway-provided prompt installed for \"{label}\"", + "✅ 已从云端恢复{note}": "✅ Restored from the cloud{note}", "✅ 已保存:{label}": "✅ Saved: {label}", "✅ 已保存:{label} {value}": "✅ Saved: {label} {value}", "✅ 已保存:指定错误码直接换号": "✅ Saved: Switch Accounts on Specified Error Codes", "✅ 已保留 {days} 天": "✅ Kept for {days} days", "✅ 已保留 {days} 天,超出部分已清理": "✅ Kept for {days} days; older data cleared", "✅ 已备份 {n} 张表({size})到 {file}": "✅ Backed up {n} tables ({size}) to {file}", + "✅ 已导入账号密钥,请继续下载": "✅ Account key imported; continue with the download", "✅ 已导出 {count} 个账号{note}{file}": "✅ Exported {count} accounts{note}{file}", + "✅ 已开启「{label}」的机器人校验": "✅ Bot check enabled for \"{label}\"", "✅ 已恢复 {n} 张表{note}": "✅ Restored {n} tables{note}", - "✅ 已改用 k / M 缩写": "✅ Now using k / M abbreviations", + "✅ 已改用 k / M / B 缩写": "✅ Now using k / M / B abbreviations", "✅ 已改用本地量级词(万 / 亿式)": "✅ Now using local magnitude words (万 / 亿-style)", "✅ 已自动创建网关 Key「默认」(可在「网关 Key」页查看)": "✅ Automatically created the gateway key \"Default\" (view it on the \"Gateway Key\" page)", "✅ 已解除内容拦截降级": "✅ Content-block fallback cleared", @@ -59,7 +98,9 @@ "✅ 调试模式已关闭": "✅ Debug mode disabled", "✅ 调试模式已开启": "✅ Debug mode enabled", "✅ 路由设置已保存": "✅ Routing settings saved", + "✅ 连接成功:{path}": "✅ Connected: {path}", "、{n} 个自定义提供商": ", {n} custom providers", + "。事件日志、请求明细、调试报文与面板登录态保持不变。替换前会自动把当前业务数据备份到配置目录的 backups/ 下。": ". Event logs, request details, debug payloads and panel login state stay untouched. The current business data is backed up to backups/ under the config directory before replacing.", "「{label}」将从 {previous} 天改为 {days} 天。": "\"{label}\" will change from {previous} days to {days} days.", "「{label}」改用内置默认提示词": "\"{label}\" now uses the built-in default prompt", "「{label}」的提示词文件已更新": "\"{label}\" prompt file updated", @@ -69,29 +110,58 @@ "「{label}」的网关自带提示词正文已更新": "\"{label}\" gateway-provided prompt text updated", "「模型管理」里每个能力的来源标记(上游 / models.dev / 已覆盖)读的就是这份数据;映射弹窗的思考档位下拉也按这里的数据只列该模型真实认识的档位。": "The per-capability source marks in \"Model management\" (upstream / models.dev / overridden) come from this data, and the reasoning-level dropdown in the mapping dialog lists only the levels the model actually accepts, based on it.", "「用哪份提示词」本来是按上游分别决定的事,所以每家可以有自己的模式、提示词文件与正文:不在这里列出的提供商一律沿用上面那份默认设置,「跟随默认」把这一家取消、回到默认。带「网关自带」那一行的家(目前是 ZCode 两家)默认就列在这里 —— 那一段是网关自己装上去的文本,不来自客户端也不来自提示词文件,默认开启、可以关掉,也可以改它的正文(改过的那一段以你的文本为准);关掉后还能不能通过上游校验,取决于上游当下的口径,行内说明了实测依据。「模式 / 提示词文件 / 正文」管的是客户端自己的 system 怎么处理,与那个开关是两件事。": "Which prompt to use is decided per upstream, so each provider can have its own mode, prompt file, and text: providers not listed here all use the default settings above, and \"Follow Default\" removes a provider's override and returns it to the default. Providers with a \"Gateway-Provided Prompt\" row (currently the two ZCode providers) are listed here by default — that text is installed by the gateway itself, comes from neither the client nor a prompt file, is on by default, can be turned off, and its text can be edited (edited blocks use your text); whether it still passes upstream validation after being turned off depends on the upstream's current criteria, and the row's hint documents what was measured. \"Mode / Prompt File / Prompt Text\" controls how the client's own system prompt is handled — a separate matter from that switch.", + "「设置」是固定项,含它的分组不能整组隐藏;可以单独隐藏组内其它菜单": "\"Settings\" is pinned, so its group cannot be hidden as a whole; you can still hide its other menus individually", "「请求日志」页的逐条请求记录,默认 99999 天(近似全量保留),可填 1–99999 天。": "Per-request records on the \"Request Logs\" page. Defaults to 99999 days (about 274 years — kept in full for practical purposes); range 1–99999 days.", "一段内置装配": "a built-in assembly", "三档数据各自独立计时,超出保留天数的部分会被删除:事件日志是登录、账号切换、429 切换这类系统事件;请求日志是网关每次转发到上游的逐条记录;按天聚合供报表页的热力图与按天趋势使用。请求日志与按天聚合默认 99999 天(约 274 年,效果上等于全量保留),事件日志默认 30 天。把某一档改小(例如 30 天改成 7 天)保存后会立即删除超出的历史数据,此操作不可恢复;改大或保持不变不会删除任何数据。三档的可填范围均为 1–99999 天。": "The three data types are timed independently; anything beyond its retention period is deleted: event logs are system events such as sign-ins, account switches, and 429 switches; request logs are the per-request records of everything the gateway forwards upstream; the daily aggregates feed the heatmap and daily trends on the Reports page. Request logs and daily aggregates default to 99999 days (about 274 years — kept in full for practical purposes), and event logs default to 30 days. Lowering a value (for example, 30 days to 7 days) and saving deletes the excess historical data immediately, and this cannot be undone; raising it or leaving it unchanged deletes nothing. All three accept 1–99999 days.", "三段各自成块,所以分开编辑": "Three separate blocks, edited separately", "三段必须各自成块(上游按结构校验,并成一段会被回 405),所以这里分开编辑。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。": "", + "上传": "Upload", + "上传中…": "Uploading…", + "上传内容含加密后的账号凭证与网关配置": "The upload contains encrypted account credentials and gateway configuration", + "上传到云端": "Upload to cloud", + "上传失败:{message}": "Upload failed: {message}", + "上次同步:{time}({action})": "Last sync: {time} ({action})", "上次成功刷新的结果(存在本机数据库里)": "Result of the last successful refresh (stored in the local database)", + "上游 CLI 版本": "Upstream CLI version", + "上游客户端版本": "Upstream client version", "上游按": "The upstream checks ", "上游按「结构」校验身份:三段必须各自成块,所以这里分开编辑三段(不能合成一段)。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。第三段发出时前面会自带一个空行(官方形状)。": "The upstream verifies identity by \"structure\": the three blocks must each be a separate block, so they are edited separately here (they cannot be merged into one). Runtime values in the dynamic block are written as placeholders and substituted only when a request is sent: {cwd} working directory, {platform} platform, {shell} shell, {os_version} OS version, {git} whether this is a git repository, {provider} provider identifier, {model} model name. Clearing a block = that block reverts to the official text. A blank line is automatically prepended to the third block when it is sent (the official shape).", + "上游未配置": "Upstream not configured", "上游模型繁忙时会先排队(回一句「建议 N 秒后再来」,Qoder 免费模型就是这样):网关等一会儿再发同一请求,最多等几次。默认 2 次,可填 0–10;填 0 表示不等待,排队直接返回 503 并说明「不是登录态或额度问题」。": "Busy upstream models queue first (replying \"suggest waiting N seconds\" — this is how Qoder's free models behave): the gateway waits a while and resends the same request, up to a set number of waits. Default 2; accepts 0–10. 0 means no waiting: a queued response returns 503 directly and states that this is not a sign-in or quota problem.", "上游用「逐字精确匹配」的方式审核请求体(不是语义审核):客户端注入的固定模板句、计费头字段名、以及某些裸错误码出现在报文里就会整单拦截,返回 400。开启本项后,网关在每次转发前改写这些指纹 —— 表头键值整段删除,承载语义的模板句只换一个词(如 official CLI for Claude → official CLI tool for Claude),对话内容与语义都不受影响。规则集是内置的,不需要也无法维护词表。关掉本项后客户端模板会原样发往上游,可能重新出现模板句被误拦的报错。": "The upstream reviews request bodies by exact word-for-word matching (not semantic review): fixed template sentences injected by the client, billing header field names, and certain bare error codes in the payload cause the entire request to be blocked with 400. With this option on, the gateway rewrites those fingerprints before every forward — header key/value pairs are deleted entirely, and template sentences that carry meaning have a single word swapped (e.g. official CLI for Claude → official CLI tool for Claude); the conversation content and meaning are unaffected. The rule set is built in — there is no word list to maintain, and none can be. With this option off, client templates go upstream as-is and template-sentence false blocks may reappear.", "上游请求四个阶段各自的等待上限(秒,1~3600):①「连接中超时」= 建立 TCP/TLS 连接或代理隧道的最大等待,默认 30 秒;②「等待响应超时」= 请求发出后等上游响应头的最大时间,默认 300 秒 —— 网关请求上游恒带 stream,响应头在 SSE 建立时就到达,与模型思考多久无关;③「流式响应空闲超时」= 流式响应相邻两块数据之间允许的最大空闲,收到新数据即重新计时,默认 300 秒 —— 上游长时间不吐数据即判定连接僵死并断开;④「非流式响应超时」= 读完整份非流式响应体的总预算(一次性计时、不重置),默认 300 秒。四项都是「等待上限」,只要数据还在来,正常的流式回答就一直往下传。保存后对下一个请求立即生效,不用重启。": "The upstream wait limit for each of the four request stages (seconds, 1–3600): (1) \"Connect Timeout\" = the maximum wait to establish the TCP/TLS connection or proxy tunnel, default 30 seconds; (2) \"Response Headers Timeout\" = the maximum time to wait for the upstream's response headers after the request is sent, default 300 seconds — gateway requests always use stream, and the response headers arrive as soon as the SSE stream is established, regardless of how long the model thinks; (3) \"Stream Idle Timeout\" = the maximum idle gap allowed between adjacent chunks of a streaming response, the timer restarting on each new chunk, default 300 seconds — if the upstream sends no data for a long time, the connection is judged dead and disconnected; (4) \"Non-Streaming Timeout\" = the total budget for reading a complete non-streaming response body (a single timer that is not reset), default 300 seconds. All four are wait limits: as long as data keeps arriving, a normal streaming answer keeps flowing. Changes take effect on the next request without a restart.", + "下载失败:{message}": "Download failed: {message}", + "不一致": "Mismatch", "不做会话识别,每条请求都重新按策略选路。": "No session detection: every request is routed by strategy from scratch.", "不可用": "Unavailable", "不在这里列出的提供商都用上面的全局配置;「跟随默认」取消这一家的单独配置。带「网关自带」的家列在这里:那段文本是网关自己装上去的(可关、可改正文),与「模式」是两件事。": "", + "与网关同源的面板;完整管理功能在桌面端": "A panel served by the same gateway; full management lives on desktop", + "两张开关分别管桌面端(应用内窗口,也包含开启局域网访问后从其他设备访问的那份面板)与网页端(Docker / 服务端自托管的浏览器面板)。默认都开启;桌面端只在本机使用、嫌验证耗时可以关掉,网页端暴露在网络上,建议保持开启。": "The two switches cover the desktop side (the in-app window, including the panel reached from other devices when LAN access is on) and the web side (the browser panel hosted by Docker / server). Both default to on. Turn the desktop one off if you only use it locally and find the check slow; keep the web one on, since that page is exposed to the network.", + "两次输入的新密码不一致": "The new passwords do not match", "两次重试之间的等待时间,两项共用,可填 0–300 秒。": "Wait time between two retries, shared by both settings. Range: 0–300 seconds.", "为了安全,开启前需要先注册一个": "For security, you need to register a ", "主进程未返回启动设置,请更新桌面端后重试": "The main process did not return startup settings. Update the desktop app and try again.", "主进程桥不可用": "Main process bridge unavailable", - "事件日志": "Event Log", + "主题": "Theme", + "事件日志": "Event logs", "事件日志保留天数": "Event Log Retention Days", + "云同步": "Cloud sync", + "人机验证": "Human verification", + "人机验证失败": "Human verification failed", + "人机验证已完成": "Human verification complete", + "人机验证还在计算,请稍候": "Human verification is still running, please wait", "仅用现有数据、不联网刷新": "Using existing data only, no online refresh", + "从云端恢复": "Restore from cloud", + "从桌面图标启动,全屏、无地址栏。想卸载:长按图标删除,或清除浏览器站点数据。": "Launch it from the home screen icon: full screen, no address bar. To uninstall, long-press the icon and remove it, or clear the browser's site data.", + "会恢复默认的分组、顺序与显隐设置。你自建的分组会被删除(组内菜单不会丢)。": "This restores the default groups, order and visibility. Groups you created are removed (their menus are kept).", + "位置": "Location", + "位置与端口": "Location and ports", "使用中文单位(亿 / 万)": "Use Chinese units (yi / wan)", "使用本地量级词(万 / 亿式)": "Use local magnitude words (万 / 亿-style)", + "侧边栏布局模块未加载(ui/sidebar-layout.js 应在 app.js 之前引入)。": "The sidebar layout module did not load (ui/sidebar-layout.js must be included before app.js).", + "侧边栏没有显示任何菜单。到「设置 → 显示 → 侧边栏菜单」里打开需要的项。": "No menus are shown in the sidebar. Turn on the ones you need under Settings → Display → Sidebar menus.", + "侧边栏菜单": "Sidebar menus", "保存上游原始报文(请求头、请求体、响应头、响应体)": "Save raw upstream payloads (request headers, request body, response headers, response body)", "保存后以这份为准(优先于上面的文件);清空回到文件 / 内置默认。": "", "保存后以这里的文本为准": "The text here takes precedence once saved", @@ -104,16 +174,21 @@ "保存后立即生效,不用重启;超时按上游失败处理(计入请求日志的「错误」列,并按「请求重试」的设置决定是否重试)。": "Takes effect immediately after saving, no restart needed; a timeout is treated as an upstream failure (counted in the \"Error\" column of the request logs, and retried according to the \"Request Retries\" settings).", "保存后立即生效:改小保留天数会立刻删除超出的历史数据,且不可恢复。": "Takes effect immediately after saving: lowering the retention days deletes the excess historical data at once, and this cannot be undone.", "保存并重启": "Save & Restart", + "保活换行丢弃": "Strip keepalive newlines", "保留天数已刷新": "Retention days refreshed", + "修改密码": "Change password", + "修改密码需要输入当前密码;修改成功后,其他设备 / 浏览器上的登录会立即失效,当前这条会话保留。": "Changing the password requires the current one. After success, sign-ins on other devices and browsers are revoked immediately; this session stays active.", "值": "Value", "值与默认值相同的行不占覆盖(存的是「改过的键」,不是全量配置);值留空保存表示这个头不发送,删除改过的行表示回落默认值。改完立即生效,不用重启。": "Rows whose value equals the default do not count as overrides (only changed keys are stored, not the whole configuration); saving an empty value means this header is not sent, and deleting an edited row reverts it to the default. Changes take effect immediately, no restart needed.", "值(留空 = 不发送)": "Value (empty = not sent)", "允许局域网内的设备访问网关": "Allow devices on the LAN to access the gateway", + "允许无 Key 裸跑": "Allow running without keys", "允许浏览器里的页面跨来源调用网关(/v1/*)": "Allow pages in a browser to call the gateway cross-origin (/v1/*)", "全局配置": "Global Configuration", "全部已配置": "All configured", "全部数据(账号、事件日志、请求记录、调试报文、设置)保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "", "全部数据(账号、事件日志、请求记录、调试报文、设置)统一保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "All data (accounts, event logs, request records, debug payloads, and settings) is kept in a single SQLite database, agent2api.db, in the configuration directory. To back up, just copy this file; to change its location, set the AGENT2API_PROXY_HOME environment variable and restart the app.", + "关闭后手动上传 / 下载也会被拒绝。": "When off, manual upload/download is rejected too.", "关闭后网关回到只监听 127.0.0.1,局域网内的设备将无法继续访问;已配置的网关 Key 与账号都不受影响。": "Once disabled, the gateway goes back to listening only on 127.0.0.1 and devices on the LAN can no longer reach it; configured gateway keys and accounts are unaffected.", "关闭后,管理界面不再从局域网提供,只有本机的桌面程序可以管理;已开启的 API 转发不受影响。": "Once disabled, the admin UI is no longer exposed over the LAN — only the desktop app on this machine can manage it; API forwarding that is already enabled is unaffected.", "关闭局域网访问": "Disable LAN Access", @@ -124,16 +199,30 @@ "其余沿用上面的全局配置": "", "内容拦截降级": "Content-block fallback", "内置默认提示词": "the built-in default prompt", + "再输入一次新密码": "", "凭据类请求头已脱敏。": "Credential request headers have been redacted.", + "凭证只保存在这台手机上,按服务器分别存放": "Credentials are stored only on this phone, separately for each server", + "出问题时把这一页截图,或点「复制全部信息」把文本贴进反馈,一般就能定位到大致原因。": "When something goes wrong, screenshot this page or use \"Copy all info\" and paste the text into your report — that is usually enough to narrow down the cause.", + "分享图标在 Safari 底部工具栏中间": "The Share icon sits in the middle of Safari's bottom toolbar", + "分组名": "Group name", + "分组名,例如「常用」": "Group name, e.g. \"Favorites\"", "切换账号重试次数": "Account-Switch Retries", "刚刚从 models.dev 拉到的最新数据": "Fresh data just fetched from models.dev", "删除 {code}": "Delete {code}", + "删除分组": "Delete group", + "删除分组「{name}」?组内 {n} 个菜单会移到「{target}」,不会丢。": "Delete group \"{name}\"? Its {n} menus will move to \"{target}\" — nothing is lost.", "删除状态码 {code}": "Delete status code {code}", + "删除空分组「{name}」?": "Delete the empty group \"{name}\"?", + "删除这个分组(组内菜单移到第一组)": "Delete this group (its menus move to the first group)", "刷新": "Refresh", "刷新失败:{message}": "Refresh failed: {message}", + "前台运行": "Foreground process", "剥离上游审核黑名单指纹(改写请求体里的模板句与表头)": "Strip upstream review blacklist fingerprints (rewrite template sentences and headers in request bodies)", "功能建议": "Feature Request", "包含日志与调试报文": "Include logs and debug payloads", + "包含:{items}": "Includes: {items}", + "区域(Region)": "Region", + "华为云 OBS": "Huawei Cloud OBS", "单轮铸造上限": "Per-round mint cap", "占用大小": "Size on Disk", "去反馈": "Give Feedback", @@ -144,25 +233,48 @@ "变更网页管理面板": "Change Web Admin Panel", "只列单独配置过的家,其余沿用上面的全局配置": "Only providers configured individually are listed; the rest use the global configuration above", "只对「排队制」的上游生效(目前是 Qoder 的免费模型):模型繁忙时上游不报错,只回一句「建议 N 秒后再来」(业务码 10605),网关按建议时长等一会儿再发同一请求,等满次数仍排不上才把「排队中」作为错误返回(HTTP 503,文案会说明这不是登录态或额度问题)。等待发生在首个字节之前,吃的是「等待响应超时」那份预算 —— 两项设置一起决定一次请求最多卡多久;排队不会标记账号限额、也不会换账号(换谁都一样在排队)。保存后对下一个请求立即生效,不用重启。": "Applies only to queueing upstreams (currently Qoder's free models): when a model is busy the upstream raises no error, it just replies \"suggest waiting N seconds\" (business code 10605); the gateway waits for the suggested duration and resends the same request, and only when the wait count is exhausted is \"queueing\" returned as an error (HTTP 503, with text explaining that this is not a sign-in or quota problem). The wait happens before the first byte and comes out of the \"Response Headers Timeout\" budget — together the two settings determine how long one request can stall at most; queueing does not mark the account as rate-limited and does not switch accounts (everyone is queueing). Takes effect on the next request after saving, no restart needed.", + "只清掉这个浏览器存的 Key,不影响网关": "Only clears the key stored in this browser; the gateway is untouched", + "可回滚到上一版本": "Can roll back one version", + "合计只累加当前筛选后可见的账号,筛选、搜索一改数字就跟着变。": "Totals only add up accounts visible under the current filter; changing the filter or search changes the number.", + "合计按整组统计(默认):不受筛选、搜索影响,被筛掉与已禁用的账号也计入。": "Totals cover the whole group (default): filtering and search do not change them, and filtered-out or disabled accounts still count.", "同一会话在 5 分钟内尽量跟住同一个账号;停了 5 分钟再发就算新会话。多数场景用它 —— 多轮对话少换号,也不至于把账号焊死。": "The same session tries to stay on one account for 5 minutes; pausing longer than 5 minutes starts a new session. The choice for most cases — fewer account switches in multi-turn chats, without pinning an account.", "同一会话在 6 小时内跟住同一个账号;适合一坐几个小时的长会话,上游的上下文缓存能吃到更多。": "The same session stays on one account for up to 6 hours; suited to marathon sessions — the upstream context cache gets reused much more.", "同一账号重试次数": "Same-Account Retries", "同时开放网页管理面板": "Also expose the web admin panel", + "同步密码": "Sync password", "名单最多 {max} 个状态码": "At most {max} status codes", + "后端没有返回版本信息": "The backend returned no version info", + "含固定项「设置」,不可整组隐藏(可以单独隐藏组内其它菜单)": "Contains the pinned \"Settings\" menu, so the group cannot be hidden as a whole (you can still hide its other menus individually)", "启动与托盘": "Startup & Tray", + "启动时间": "Started at", + "启用云同步": "Enable cloud sync", "命中这些上游状态码的失败不在同一账号重发,直接换下一个账号继续试(换号占用「切换账号重试次数」,换满仍失败才把错误原样返回给客户端)。输入 100–599 的状态码后回车添加,点 × 或退格删除;默认 402(积分不足)。清空全部表示任何错误都照常重试。": "Failures matching these upstream status codes are not retried on the same account — the gateway switches to the next account and keeps trying (an account switch consumes an \"Account-Switch Retries\" slot; only when those are exhausted is the error returned to the client as-is). Type a status code from 100–599 and press Enter to add it; click × or press Backspace to delete one. Default: 402 (insufficient points). Clearing the list means any error is retried as usual.", + "固定": "Pinned", "在同一个账号上原地重发几次(不含首发;整条请求共用一份,用完后换来的账号只发一次就继续换)。可填 0–10,0 表示失败立即换号。": "How many times to resend in place on the same account (not counting the first send; the whole request shares one pool, and once it is used up, any account switched to gets only one send before switching continues). Accepts 0–10; 0 means switch accounts immediately on failure.", "在这里直接编辑提示词正文:保存后以这份文本为准(优先于上面的提示词文件),清空则回到文件 / 内置默认。改一个字不必再去编辑器里开文件。": "Edit the prompt text right here: after saving, this text takes precedence (over the prompt file above), and clearing it falls back to the file / built-in default. No need to open a file in an editor just to change one word.", + "在途请求": "In-flight requests", + "地址格式不对。填「IP 或域名:端口」,可带 http:// 或 https://,例如 192.168.1.23:3065": "Invalid address. Enter \"IP or domain:port\", optionally with http:// or https:// — e.g. 192.168.1.23:3065", + "坚果云": "Jianguoyun (Nutstore)", + "坚果云需使用「第三方应用密码」(账户信息 → 安全选项里生成),地址必须是 /dav/ 下的目录。": "Jianguoyun requires an app-specific password (generate it under Account info → Security), and the URL must point to a directory under /dav/.", + "填「IP 或域名:端口」。协议可写 http:// 或 https://,不写默认 http;http 不写端口默认 3065,https 默认 443。地址里的 /m、/v1 会自动去掉。": "Enter \"IP or domain:port\". The scheme may be http:// or https:// (http is assumed if omitted). Without a port, http defaults to 3065 and https to 443. Any /m or /v1 in the address is stripped automatically.", "备份中…": "Backing up…", "备份整库": "Back up database", "备份文件内含全部账号凭证、网关 API Key 与面板管理员凭据,可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "The backup file contains every account credential, the gateway API keys, and the panel admin credentials — enough to log in directly. Keep it safe; do not share it or upload it anywhere public.", + "复制全部信息": "Copy all info", + "复制给客户端": "Copy for your client", + "外观": "Appearance", + "外观 · 接入 · 关于": "Appearance · Access · About", "天": "days", "天数": "Days", "失败 {n} 个": "failed: {n}", "失败 {n} 个:{detail}": "Failed: {n}; {detail}", "失败后最多再换几个账号试(填 N = 最多换 N 个,首发那个不算)。换到同一家名下另一个账号、或换到另一家,都各算一次;换满仍失败就返回错误。可填 0–10,0 表示失败立即报错。": "How many more accounts to try on failure (N = switch to at most N accounts; the account that made the first attempt does not count). Switching to another account under the same provider and switching to a different provider each count as one; once the limit is reached and the request still fails, an error is returned. Accepts 0–10; 0 means report the error immediately on failure.", + "存储桶(Bucket)": "Bucket", "存储概况已刷新": "Storage overview refreshed", "安全": "Security", + "安装到主屏幕": "Add to Home Screen", + "安装到桌面": "Install as app", + "安装包形态": "Installer kind", "官方原文只有一句:声明自己是 ZCode。": "The official text is a single sentence: a declaration that it is ZCode.", "客户端 system 原样出站。": "The client's system prompt goes out as-is.", "客户端 system 原样出站(只靠指纹脱敏改写模板句)。": "The client's system prompt goes out as-is (only fingerprint masking rewrites template sentences).", @@ -170,19 +282,27 @@ "客户端(Claude Code / Codex 等 CLI)会往 system 里塞几十句固定模板,上游逐字审核,命中就整单拦截(HTTP 400)。「模式」决定网关怎么处理这份 system:透传 = 原样不动(默认);替换 = 删掉客户端的 system / developer,换成网关提示词;追加 = 保留客户端内容,只在最前面插一条网关提示词。网关提示词来自「提示词文件」或界面里编辑的正文(正文优先,清空回到文件 / 内置默认)。撞上拦截时会自动改用一段最小中性提示词,次日 00:00 自动恢复,也可以随时解除。": "", "家之间仍按优先级,只有同一家内部轮流用各账号 —— 想优先用某几家、又不愿把某一家的某个账号焊死时用它。": "Providers still follow priority; only accounts within the same provider take turns. For preferring a few providers without pinning any single account.", "家内轮询": "Per-provider rotation", + "容器内更新": "In-container update", "密码至少 8 位": "Password must be at least 8 characters", + "导入前会把本机的 account.key 改名备份(account.key.bak-时间戳),不会直接删除。": "The local account.key is renamed to a backup (account.key.bak-) before importing; it is never deleted.", "导入完成:{summary}": "Import complete: {summary}", + "导入密钥失败:{message}": "Key import failed: {message}", + "导入密钥并继续": "Import key and continue", "导入账号": "Import Accounts", "导出中…": "Exporting…", "导出会把全部账号与自定义提供商定义写入一个 JSON 文件,可以拷到另一台机器上导入后继续使用。导入采用合并策略:同提供商下按业务身份(UID / userId / apiKey 等)去重 —— 已存在的账号只更新凭证,保留本机原有的优先级顺序;新账号追加到转发顺序末尾,不会抢占当前正在使用的账号;自定义提供商定义按 id 合并,本机缺失时自动补建。": "Export writes all accounts and custom provider definitions to a JSON file that can be copied to another machine and imported for continued use. Import merges: within the same provider, accounts are deduplicated by business identity (UID / userId / apiKey, etc.) — existing accounts only get their credentials updated and keep this machine's existing priority order; new accounts are appended to the end of the forwarding order and never displace the account currently in use; custom provider definitions are merged by id and created automatically when missing locally.", "导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义": "The export file contains credentials such as accessToken / refreshToken / apiKey along with custom provider definitions", "导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义,可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "The export file contains credentials such as accessToken / refreshToken / apiKey along with custom provider definitions, and can be used to sign in directly. Keep it safe; do not share it or upload it to a public location.", "导出账号": "Export Accounts", + "尚未注册管理员:管理接口不需要登录。开启「允许局域网访问」时会引导注册,也可以从登录页注册。": "No administrator registered: the management API does not require sign-in. Enabling \"Allow LAN access\" walks you through registration; you can also register from the login page.", "局域网开放后,管理接口要求登录。请设置管理员账号与密码,注册完成会直接继续开启流程。": "Once LAN access is on, the admin API requires signing in. Set an admin account and password; registration finishes and the enablement flow continues automatically.", "局域网访问": "LAN Access", + "展开或收起设置分类": "", "工具用法与环境说明那一大段。": "The long block covering tool usage and environment notes.", "已为「{label}」单独配置提示词": "Prompt configured individually for \"{label}\"", + "已作为独立应用运行": "Standalone app", "已保存 {count} / {limit} 条报文(超出后丢弃最旧的)。": "{count} / {limit} payloads saved (oldest discarded when the limit is exceeded).", + "已保存,留空沿用": "Saved; leave blank to keep", "已关闭": "Disabled", "已关闭 models.dev 联网刷新": "Online refresh for models.dev turned off", "已关闭。{note}": "Disabled. {note}", @@ -190,26 +310,43 @@ "已关闭出站指纹脱敏": "Fingerprint masking disabled", "已关闭开发者角色兼容": "Developer role compatibility disabled", "已关闭网关跨域访问": "Gateway CORS disabled", + "已删除分组「{name}」": "Deleted group \"{name}\"", "已取消「{label}」的单独配置": "Individual configuration removed for \"{label}\"", + "已取消安装": "Installation cancelled", "已取消导入": "Import canceled", "已取消导出": "Export canceled", + "已取消恢复": "Restore canceled", + "已启用登录": "Sign-in enabled", + "已复制全部版本信息": "Copied all version info", "已应用": "Applied", "已开启": "Enabled", "已开启:其他设备把 API 地址指向 {url}{panel}。": "Enabled: on other devices, point the API address to {url}{panel}.", + "已恢复默认分组:概览 / 转发链路 / 诊断 / 系统": "Restored the default groups: Overview / Forwarding / Diagnostics / System", + "已指定出口": "Pinned egress", "已接管": "Managed", "已改回内置默认提示词": "Reverted to the built-in default prompt", + "已新建分组「{name}」,点组头的 + 把菜单移进来": "Created group \"{name}\" — use + on its header to move menus in", "已清掉「{label}」界面编辑的正文": "Cleared the UI-edited text for \"{label}\"", "已清掉界面编辑的提示词(回落文件 / 内置默认)": "Cleared the UI-edited prompt (falls back to the file / built-in default)", + "已清除本机保存的 API Key": "Removed the API key stored in this browser", "已生效": "In Effect", "已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。": "Switched automatically to a minimal neutral prompt (an upstream content block — most likely a system-prompt fingerprint false positive); lifts automatically at {until}.", "已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。": "Automatically switched to a minimal neutral prompt (upstream content blocking was hit, most likely a false positive on system fingerprints); it clears automatically at {until}. During this period this mode's own prompt is not sent; once you have fixed the prompt you can clear it immediately.", "已自动改用最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。": "", + "已运行": "Uptime", + "已退出登录": "Signed out", + "已隐藏": "Hidden", "巴西葡萄牙语": "Brazilian Portuguese", "希望接入新的提供商或模型": "A new provider or model you would like supported", + "库中 schema": "Schema in database", "库主文件的大小(不含运行期间的 WAL 临时文件,退出时已自动并回)。": "Size of the database's main file (excluding WAL temp files created while running, which are merged back in automatically on exit).", "库存目标": "Stock target", + "应用版本:{version}": "App version: {version}", + "应用状态": "App status", + "应用窗口": "App window", "建立上游 TCP/TLS 连接或代理隧道的最大等待时间(秒),默认 30 秒,范围 1~3600。": "Maximum time to establish the upstream TCP/TLS connection or proxy tunnel (seconds). Default 30 seconds, range 1–3600.", - "开发者角色兼容": "Developer Role Compatibility", + "开发构建": "Debug build", + "开发者角色兼容": "Developer role compat", "开发者角色兼容设置已刷新": "Developer role compatibility setting refreshed", "开启后新发生的请求才会被记录(已经过去的请求补不回来)。报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。": "Only requests that happen after turning this on are recorded (past ones can't be backfilled). Payloads live in the local database and only the most recent 500 are kept (the oldest are dropped beyond that); the count shows under \"Data → Data storage\" on the left as \"Debug payloads\".", "开启后新发生的请求才会被记录(已经过去的请求补不回来),请求日志的「预览对话」也随之开始保存原始正文。上游报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。": "Only requests that occur after this is enabled are recorded (past requests cannot be recovered), and the \"Conversation preview\" in request logs also starts saving raw bodies. Upstream payloads are stored in the local database, keeping only the most recent 500 (the oldest are discarded when the limit is exceeded); the count is shown under \"Data → Data Storage\" → \"Debug Payloads\".", @@ -218,111 +355,186 @@ "开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 活动套餐」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。与「开机自动启动」同时开启时,开机后也不建界面(只留托盘与后台进程),点托盘才打开 —— 效果与关窗后一致。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。": "", "开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 活动套餐」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。": "Once enabled, closing the window does not merely hide it — the UI window is destroyed entirely: WebView2's six resident processes (about 200MB of memory) exit with it, while gateway forwarding, scheduled check-ins, and balance queries keep running in the background process, unaffected; to get the UI back, click the tray icon (a cold WebView start takes about 1 second). Note: while the UI is destroyed, the verification-code tokens for the \"ZCode activity plan\" channel are minted by the UI and cannot be replenished — requests going through that channel land in the request logs as 503 (with the reason stated), and reopening the UI restores them automatically. This switch only matters when \"Minimize to tray when the window is closed\" is on; minimizing the window is unaffected.", "开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。": "Once enabled, the sign-in page completes verification in the browser in the background (imperceptible to a real person), while every scripted attempt must first solve a challenge — brute-forcing and mass registration become significantly more expensive. This only affects panel sign-in / registration, not the API keys used by API clients.", + "开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。桌面端与网页端各自独立:桌面端主要服务本机与局域网,网页端(Docker / 服务端)直接暴露在网络上的登录页风险更高,建议保持开启。": "When on, the login page solves the challenge in the background (invisible to humans) while every scripted attempt must compute one first — brute force and account squatting become far more expensive. It only affects panel sign-in / registration, not API keys used by clients. Desktop and web are independent: the desktop page mostly serves this machine and the LAN, while the web page (Docker / server) is exposed to the network and benefits more from staying on.", "开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。": "When on, the gateway stores every forward's **request to the upstream** (headers + body) and the **upstream response** (status code + headers + body) locally in full, for the \"Details\" view on the request logs page. Credential fields in request headers — Authorization, Cookie, API Key, and the like — are always replaced with [redacted] and never hit the disk in plain text; request and response bodies are stored as-is (they may contain your conversations). Only the most recent 500 payloads are kept; the oldest are dropped beyond that. This is a temporary troubleshooting switch — keep it off when you don't need it.", "开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看;请求日志详情的「预览对话」也依赖本开关 —— 它保存的是客户端发来的请求体与网关下发的响应体(两侧各截断到 128 KiB),不开这个开关时预览对话没有数据可看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。上游报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。": "Once enabled, the gateway saves every forward's **request to the upstream** (request headers + request body) and **the upstream's response** (status code + response headers + response body) in full locally, for viewing under \"Details\" on the request logs page; the \"Conversation preview\" in request details also depends on this switch — it saves the client's request body and the gateway's response body (each truncated to 128 KiB), and with the switch off there is no data to preview. Credential fields in request headers such as Authorization, Cookie, and API Key are always replaced with [redacted] and never written to disk in plain text; request and response bodies are saved as-is (they may contain your conversation content). Only the most recent 500 upstream payloads are kept; older ones are discarded. This is a temporary switch for troubleshooting — turn it off when you no longer need it.", "开启局域网访问": "Enable LAN Access", "开放后,局域网内其他设备的浏览器打开本机 IP 即可进入管理面板(需管理员账号登录)。": "Once enabled, other devices on the LAN can open this machine's IP in a browser to reach the admin panel (signing in with an admin account is required).", "开机自动启动": "Launch at startup", + "开(最多 {n} 轮)": "On (up to {n} rounds)", "当前三段都是官方原文。": "All three blocks are currently the official text.", "当前固定为浅色模式,不随系统变化。": "Currently fixed to Light mode; it does not follow the system.", "当前固定为深色模式,不随系统变化。": "Currently fixed to Dark mode; it does not follow the system.", + "当前密码": "Current password", "当前已改过:{edited}。": "Edited so far: {edited}.", + "当前形态": "Running as", "当前按 100% 显示(默认比例)。": "Currently displayed at 100% (the default).", "当前按 k / M 缩写显示(如 1.20M / 8.4k)。": "Currently shown with k / M abbreviations (e.g. 1.20M / 8.4k).", "当前按 {n}% 显示。": "Currently displayed at {n}%.", "当前按「万 / 亿」式本地量级词显示(如 1.2亿 / 8400万)。": "Currently shown with 万 / 亿-style local magnitude words (e.g. 1.2亿 / 8400万).", "当前是透传模式:存下来也不会发出,切成「替换 / 追加」之后才生效。": "Currently in Passthrough mode: saving it will not send it; it takes effect only after switching to \"Replace / Append\".", - "当前显示为「1.20M / 8.4k」这类英文缩写。": "Currently shown as \"1.20M / 8.4k\"-style abbreviations.", + "当前显示为「1.20M / 8.4k / 1.2B」这类英文缩写。": "Currently shown with k / M / B abbreviations (e.g. 1.20M / 8.4k / 1.2B).", "当前显示为「1.2亿 / 8400万」这类中文量级。": "Currently shown as \"1.2亿 / 8400万\"-style Chinese magnitudes.", "当前浏览器以管理员身份登录着本面板。「退出登录」会撤销这台设备的登录会话(30 天内的自动续期一并失效),需要重新输入账号密码;其他已登录的设备不受影响。": "This browser is currently signed in to the panel as an administrator. \"Sign Out\" revokes this device's session (automatic renewals within 30 days are voided too) and the account and password must be entered again; other signed-in devices are unaffected.", + "当前环境不支持修改机器人校验设置": "", "当前环境不支持局域网访问设置": "This environment does not support LAN access settings", + "当前环境不支持整库备份": "Full-database backup is not available in this environment", "当前环境不支持整库备份(仅桌面端可用)": "Full-database backup is not available here (desktop only)", + "当前环境不支持整库恢复": "Full-database restore is not available in this environment", "当前环境不支持整库恢复(仅桌面端可用)": "Full-database restore is not available here (desktop only)", + "当前环境不支持读取版本信息(桥接方法缺失)": "This environment cannot read version info (bridge method missing)", + "当前环境不支持退出面板登录": "", "当前生效的正文来自{source}{file}。保存后这一份会盖过提示词文件与内置默认{note}。": "The active text comes from {source}{file}. Once saved, this copy overrides the prompt file and the built-in default{note}.", + "当前界面": "Current interface", + "当前管理员:{name}": "Signed in as: {name}", "当前跟随操作系统的深浅色设置,系统切换时界面会自动跟上。": "Currently following the operating system's light / dark setting; the UI follows along automatically when the system switches.", + "待迁移旧数据": "Legacy data pending migration", + "待配置": "Not configured", + "忘记密码时无法在界面里找回:需要停掉程序后,从数据库删除管理员记录(kv 表的 panelAdmin 键),重启后按首次使用重新注册。具体命令与各形态的库文件路径见 docs 目录下的「重置管理员」文档。": "If you forget the password it cannot be recovered in the UI: stop the program, delete the administrator record from the database (the panelAdmin key in the kv table), then restart and register again as on first use. Commands and the database path for each deployment form are in the \"Reset administrator\" document under docs.", "恢复中…": "Restoring…", "恢复失败:{message}": "Restore failed: {message}", "恢复官方原文": "Restore Official Text", "恢复整库": "Restore database", "恢复整库备份": "Restore full backup", "恢复默认": "Restore Defaults", + "恢复默认侧边栏": "Restore default sidebar", "想回到原来那份,把这里清空再保存即可(不会删掉你配的文件路径)。": "To go back to the previous one, clear this and save (the file path you configured is not deleted).", "想要的新功能或改进想法": "A new feature or improvement you would like to see", "所有未单独配置的提供商都用这一份": "Used by every provider that is not configured individually", + "所有菜单都已在这一组": "Every menu is already in this group", + "手机端只做查看与高频操作;添加账号、编辑凭证、改写客户端配置仍需桌面端。": "", + "托管网页界面": "Serves web UI", + "把 Base URL 复制给客户端": "", + "把地址里的 your-server 与 USERNAME 换成你自己的;目录不存在时会自动创建。": "Replace your-server and USERNAME in the URL with your own; the directory is created automatically if missing.", "把整份数据库(账号、网关配置、事件日志、请求统计、调试报文与桌面设置)导出成一个 SQL 文件,恢复时整体替换本机全部数据 —— 与上面「账号导入 / 导出」的逐条合并是两种语义:恢复会连配置、日志与当前启用状态一起换成备份那台机器的状态。导出默认包含日志与调试报文(文件可能很大),不需要时可以取消勾选只备业务数据。恢复前会自动把当前数据备份到配置目录的 backups/ 下,恢复后需要重新登录管理面板。": "Exports the entire database (accounts, gateway config, event logs, request stats, debug payloads, desktop settings) into one SQL file; restoring wholesale-replaces all local data. This is a different semantic from the \"Account import / export\" above, which merges item by item: a restore swaps config, logs, and enabled states to whatever the backup machine had. The export includes logs and debug payloads by default (the file can be large); uncheck to back up business data only. Before a restore, the current data is automatically backed up to backups/ under the config directory, and you will need to log in to the panel again afterwards.", + "把菜单移入 {name}": "Move a menu into {name}", + "把菜单移入「{name}」": "Move a menu into \"{name}\"", + "把账号、网关配置、自定义提供商、Agent 配置、代理池 / 订阅、签到与余额记录同步到 WebDAV 或 S3 兼容存储,换台机器后一键取回。事件日志、请求明细、调试报文与面板登录态不在同步范围内(它们是本机的运行痕迹)。上传的账号凭证仍是加密形态,解密用的账号密钥由「同步密码」单独加密后一并上传 —— 同步密码不会保存到云端,换机下载时需要重新输入。": "Sync accounts, gateway configuration, custom providers, Agent profiles, proxy pool / subscriptions, and check-in and balance records to WebDAV or S3-compatible storage, then pull them back on another machine in one click. Event logs, request details, debug payloads and panel login state are out of scope (they are this machine's runtime traces). Uploaded account credentials stay encrypted; the account key that decrypts them is itself encrypted with the sync password and uploaded alongside — the sync password is never stored in the cloud, so you must re-enter it when restoring on another machine.", + "把路径里的主目录显示成 ~,截图分享时不暴露用户名": "Shows the home directory in paths as ~ so screenshots do not reveal your username", + "把路径里的主目录(如 C:\\Users\\你的用户名)显示成 ~,截图分享时不暴露用户名。只影响本页显示与复制,不改任何文件。": "Shows the home directory in paths (e.g. C:\\Users\\your-name) as ~ so screenshots do not reveal your username. Only affects this page and its copied text; no files are changed.", "报表天数": "Report Days", "报表页的热力图与按天趋势,默认 99999 天(近似全量保留),可填 1–99999 天。": "The heatmap and daily trends on the Reports page. Defaults to 99999 days (about 274 years — kept in full for practical purposes); range 1–99999 days.", + "拖动分组 {name}": "Drag group {name}", + "拖动左侧 ⠿ 调整顺序(菜单可以跨组拖);拨开关控制显隐;点分组名可重命名;组头的 + 把别的菜单移进来;眼睛按钮隐藏整组。改动即时生效并记住,左侧侧边栏就是结果。键盘也能排序:把手聚焦后按 Alt+↑ / ↓。": "Drag the ⠿ handle to reorder (menus can move across groups); use the switches to show or hide; click a group name to rename it; the + on a group header moves another menu into it; the eye button hides a whole group. Changes apply instantly and are remembered — the sidebar on the left is the result. Keyboard: focus a handle and press Alt+↑ / ↓.", + "拖动菜单 {name}": "Drag menu {name}", + "拖动调整分组顺序(Alt+↑ / ↓ 也可)": "Drag to reorder groups (Alt+↑ / ↓ also works)", + "拖动调整顺序,可跨组(Alt+↑ / ↓ 也可)": "Drag to reorder, across groups too (Alt+↑ / ↓ also works)", "指定错误码直接换号": "Switch Accounts on Specified Error Codes", - "指纹脱敏": "Fingerprint Masking", + "指纹脱敏": "Fingerprint sanitizing", "指纹脱敏设置已刷新": "Fingerprint masking settings refreshed", "按天聚合保留天数": "Daily aggregate retention (days)", "按提供商配置": "Per-Provider Configuration", + "换一台服务器": "Switch server", "排队等待": "Queueing", "排队等待次数": "Queue Wait Count", "排队等待秒数": "Queue Wait Seconds", "排队等待设置已刷新": "Queueing settings refreshed", + "接入": "Access", + "控制主侧边栏显示哪些菜单、按什么顺序、怎么分组。隐藏只影响导航显示,不会撤销页面访问权限;被隐藏的页面仍可通过地址直接打开。": "Choose which menus the sidebar shows, in what order, and how they are grouped. Hiding only affects navigation — it does not revoke access, and hidden pages can still be opened by URL.", "控制报表与请求日志里 Token 读数的写法:开启后按「万 / 亿」式的本地量级词显示(简体为万 / 亿,繁体为萬 / 億,日文为万 / 億,韩文为만 / 억),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。英文 / 葡语的本地量级词就是 k / M,这项对它们没有区别。只影响显示口径,不改变任何统计与存储的数值。": "Controls how Token counts are written in reports and request logs: when on, values are shown with 万 / 亿-style local magnitude words (万 / 亿 in Simplified Chinese, 萬 / 億 in Traditional Chinese, 万 / 億 in Japanese, 만 / 억 in Korean); when off, k / M abbreviations are used (matching how upstream docs and API fields write them). For English / Portuguese the local magnitude words are k / M, so this makes no difference to them. This only affects how values are displayed; no statistics or stored numbers change.", - "控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。": "Controls how token counts read in reports and request logs: on shows Chinese magnitudes (1.2亿 / 8400万), off uses k / M abbreviations (consistent with upstream docs and API fields). This only changes the display; no statistics or stored numbers are affected.", + "控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M / B 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。": "Controls how token counts read in reports and request logs: on shows Chinese magnitudes (1.2亿 / 8400万), off uses k / M / B abbreviations (consistent with upstream docs and API fields). This only changes the display; no statistics or stored numbers are affected.", "控制界面的深浅色,与左侧边栏底部的三个主题按钮是同一个设置(改哪一处,另一处立刻跟上)。「跟随系统」会随操作系统当前的浅色 / 深色自动切换,并在系统主题变化时即时跟上;选「浅色」或「深色」则把界面固定在该模式,不再随系统变化。窗口标题栏的深浅色会一并同步,不会出现深色界面配一条浅色标题栏的情况。": "Controls the UI's light / dark colors; it is the same setting as the three theme buttons at the bottom of the left sidebar (change either one and the other follows immediately). \"System\" switches automatically with the operating system's current light / dark setting and follows changes as they happen; choosing \"Light\" or \"Dark\" pins the UI to that mode regardless of the system. The window title bar's light / dark color stays in sync too, so you never end up with a dark UI under a light title bar.", "提建议": "Make a Suggestion", "提示词文件": "Prompt File", "提示词文件已更新": "Prompt file updated", "提示词正文": "Prompt Text", "提示词正文已更新": "Prompt text updated", + "操作系统": "Operating system", + "改动仍保存在本机,修复网络或凭据后下一次改动会自动重试,也可以手动点「上传到云端」。": "Your changes are still saved locally. After fixing the network or credentials, the next change retries automatically, or click \"Upload to cloud\" manually.", "改完立即生效,不用重启(下一个请求就用新模式)。降级是自动的临时状态,解除后本模式自己的提示词立刻恢复。": "Takes effect immediately after the change, no restart needed (the next request uses the new mode). The fallback is an automatic temporary state; once cleared, this mode's own prompt is restored immediately.", "放大或缩小整个界面(文字与控件一起变),效果与浏览器 Ctrl +/- 相同:共 11 档,立即生效并记住。": "Zoom the whole interface in or out (text and controls scale together), equivalent to Ctrl +/- in a browser: 11 steps, applied immediately and remembered.", "数据": "Data", "数据保留": "Data Retention", "数据存储": "Data Storage", + "数据库 schema": "Database schema", "数据库不可用": "Database unavailable", - "数据库文件": "Database File", + "数据库文件": "Database file", + "数据目录": "Data directory", "数据统一保存在配置目录下的 agent2api.db;如需更换位置,请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "Data is kept in agent2api.db in the configuration directory; to change the location, set the AGENT2API_PROXY_HOME environment variable and restart the app.", "整体替换": "wholesale-replaced", "整库备份 / 恢复": "Full-database backup / restore", + "新分组名": "New group name", "新增 {added} 个、更新 {updated} 个{suffix}{note}": "{added} added, {updated} updated{suffix}{note}", + "新密码": "New password", + "新密码不能与当前密码相同": "New password must differ from the current one", + "新密码至少 8 位": "Password must be at least 8 characters", + "新建分组": "New group", + "日志文件": "Log file", "日语": "Japanese", + "显示 {name}": "Show {name}", + "显示分组 {name}": "Show group {name}", "显示模式": "Theme", + "显示这一组": "Show this group", "更新": "Update", + "更新出网线路": "Update egress route", + "更新通道": "Update channel", "替换": "Replace", "替换 = 客户端 system / developer 消息全部删掉,换成网关的提示词;追加 = 保留客户端内容,只在开头 system 块之后多插一条。两者都只改发给上游的副本,客户端看到的响应不变。": "Replace = delete all client system / developer messages and substitute the gateway's prompt; Append = keep the client's content and just insert one extra prompt after the leading system blocks. Both only change the copy sent upstream; the response the client sees is unchanged.", "替换为网关提示词": "Replace with gateway prompt", "替换前会自动把当前数据备份到配置目录的 backups/ 文件夹。": "Before the replace, the current data is automatically backed up to the backups/ folder under the config directory.", + "替换并恢复": "Replace and restore", "有未保存的修改": "There are unsaved changes", + "有未保存的改动:保存后才能同步(测试连接不受影响)。": "Unsaved changes: save first to sync (testing the connection is unaffected).", + "服务商": "Provider", + "服务器地址": "Server URL", "期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。": "While this mode is on, its own prompt is not sent; lift it as soon as your prompt is fixed.", "未单独配置:模式、提示词文件与正文都跟随上面的全局配置(当前「{mode}」)。": "Not configured individually: mode, prompt file, and text all follow the global configuration above (currently \"{mode}\").", + "未启用": "Disabled", "未开启轻量模式:关窗仅隐藏窗口,界面进程常驻内存,随时打开不等待": "Lightweight mode off: closing the window only hides it, the UI process stays in memory, and it opens instantly", "未开启,developer 角色消息会原样发往上游,不认识该角色的上游会报错(400)。": "Off: developer-role messages are sent upstream as-is; upstreams that do not know the role return an error (400).", "未开启,客户端 system 模板会原样发往上游,可能被内容审核误拦(400)。": "Off: client system templates go upstream as-is and may be falsely blocked by content review (400).", "未开启,浏览器跨域无法直连网关。": "Off: browsers cannot reach the gateway cross-origin.", "未开启,转发时不保存任何原始报文。": "Off: no raw payloads are saved while forwarding.", "未开启:网关只监听 127.0.0.1,仅本机可以访问。": "Off: the gateway listens only on 127.0.0.1 and is reachable only from this machine.", + "未注册": "Not registered", + "未注册闸门": "Unregistered gate", "未知原因": "Unknown reason", "未知来源": "Unknown source", + "未知设备": "Unknown device", "未知账号": "Unknown account", "未能读取 Cline 伪装头设置,请稍后重试": "Could not read the Cline spoofed header settings. Please try again later.", "未能读取 models.dev 元数据状态,请稍后重试": "Could not read the models.dev metadata state. Please try again later.", + "未能读取云同步设置,请稍后重试。": "Could not read cloud sync settings. Please try again later.", "未能读取开发者角色兼容设置,请稍后重试": "Could not read the developer role compatibility setting, please try again later", "未能读取指纹脱敏设置,请稍后重试": "Could not read the fingerprint masking settings. Please try again later.", "未能读取系统提示词设置,请稍后重试": "Could not read the system prompt settings. Please try again later.", "未能读取网关跨域访问设置,请稍后重试": "Could not read the gateway CORS settings. Please try again later.", "未能读取调试模式设置,请稍后重试": "Could not read the debug mode settings. Please try again later.", + "未能读取账户信息,请稍后重试": "Could not read account information, please try again later", + "本地账号、配置、签到与余额记录变化后自动上传(合并 1 秒内的连续改动,最长 10 秒传一次;内容没变时不发请求)。": "Automatically upload after local account, configuration, check-in and balance record changes (changes within 1 second are merged, at most one upload every 10 seconds; no request is sent when nothing changed).", + "本机 APK 版本": "Local APK version", + "本机应用版本": "Local app version", + "本机界面版本": "Local UI version", + "本机的业务数据(账号、配置、Agent 配置、代理池 / 订阅、签到与余额记录)将上传并": "This machine's business data (accounts, configuration, Agent profiles, proxy pool / subscriptions, check-in and balance records) will be uploaded and", "本机的全部数据(账号、网关配置、日志统计与桌面设置)将被所选备份文件": "All data on this machine (accounts, gateway config, log stats, and desktop settings) will be ", + "本机的账号、网关配置、Agent 配置、代理池 / 订阅、签到与余额记录将被远端快照": "This machine's accounts, gateway configuration, Agent profiles, proxy pool / subscriptions, and check-in and balance records will be replaced by the remote snapshot", "本次无需刷新": "No refresh needed this time", "机器人校验": "Captcha", + "来源设备:{device}": "Source device: {device}", + "来自 {device},生成于 {time}": "From {device}, created at {time}", + "架构": "Architecture", "查询管理员状态失败:{message}": "Failed to check admin status: {message}", "校验身份:这三段必须各自成块排在最前(实测:三段并成一段会被回 405 / 3012)。因此这里按段编辑,不能合成一段。改过的段以你的文本为准,没改的段继续用官方原文;清空某一段 = 那一段恢复官方原文。": "for identity: the three blocks must each be a separate block placed at the very top (measured: merging the three into one returns 405 / 3012). So they are edited per block here; they cannot be merged into one. Edited blocks use your text; unedited blocks keep the official text; clearing a block reverts that block to the official text.", + "桌面端": "Desktop app", + "桌面端:登录 / 注册需要 ALTCHA 人机验证": "Desktop: sign-in / registration requires ALTCHA proof-of-work", + "桌面端:登录 / 注册需要 ALTCHA 人机验证(当前正在使用)": "Desktop: sign-in / registration requires ALTCHA proof-of-work (in use now)", "桌面运行时不可用,暂时无法保存": "Desktop runtime unavailable; cannot save right now", + "档案": "Profile", "检测中…": "Checking…", + "概览": "Overview", "模式": "Mode", "模式改为「{mode}」": "Mode changed to \"{mode}\"", "次": "times", "次日 00:00": "00:00 the next day", "正在保存上游原始报文:": "Saving raw upstream payloads:", + "正在修改…": "Changing…", "正在剥离出站请求里的审核指纹:表头键值整段删除,模板句最小改写。": "Stripping review fingerprints from outbound requests: header key/value pairs are deleted entirely, and template sentences get minimal rewrites.", + "正在安装…": "Installing…", "正在应答跨源请求:浏览器跨域请求可直连 /v1/*。": "Answering cross-origin requests: browsers can reach /v1/* directly cross-origin.", "正在归一角色:developer 角色消息转发前改写为 system。": "Normalizing roles: developer-role messages are rewritten to system before forwarding.", "正在注册…": "Registering…", "正在联网更新": "Updating online", + "正式版": "Stable", "每条请求轮换到下一个可用账号;新会话交给当前最闲的那位。适合想把手里的号摊开用、不让某一个一直扛。": "Rotates to the next usable account on every request; a new session goes to whoever is least busy. For spreading usage across accounts instead of letting one carry everything.", "每条请求都重新按策略选路 —— 行为与「关闭」一致,仅保留会话识别的记录。": "Every request is routed by strategy from scratch — same behavior as \"Off\", only the session records are kept.", "每次等待的时长,默认 0 = 跟随上游建议(实测 9~30 秒,上游没给建议时用 15 秒)。可填 0–120 秒强制一个固定时长。": "Duration of each wait. Default 0 = follow the upstream's suggestion (measured 9–30 seconds; 15 seconds when the upstream gives none). Accepts 0–120 seconds to force a fixed duration.", @@ -335,15 +547,32 @@ "注意:这一家的「网关自带」开关现在是关的,改过的正文要等开关打开才生效。": "Note: this provider's \"Gateway-Provided Prompt\" switch is currently off; edited text takes effect only after the switch is turned on.", "流式响应相邻数据之间允许的最大空闲时间(秒),收到新数据后重新计时,默认 300 秒,范围 1~3600。": "Maximum idle time allowed between adjacent chunks of a streaming response (seconds); the timer restarts on each new chunk. Default 300 seconds, range 1–3600.", "流式响应空闲超时": "Stream Idle Timeout", + "测试连接": "Test connection", + "浏览器 / PWA": "Browser / PWA", + "浏览器只在 https 或 localhost 下提供离线缓存;局域网 http 访问不影响正常使用": "Browsers only offer offline caching over https or localhost. Accessing over plain http on the LAN still works normally.", + "浏览器标签页": "Browser tab", + "浏览器面板": "Browser panel", "添加提供商": "Add Provider", "添加提供商…": "Add provider…", "添加自定义头": "Add Custom Header", "添加要单独配置的提供商": "Add a provider to configure individually", + "清除本机凭证": "Clear local credentials", "清除这里的正文": "Clear Text Here", "点下面的入口会用系统默认浏览器打开 GitHub 的对应表单(需要 GitHub 账号,模板已预设好,填完直接提交即可)。": "The entries below open the matching GitHub form in your system's default browser (a GitHub account is required; the template is preset, so just fill it in and submit).", + "点击重命名": "Click to rename", + "点此添加或切换服务器": "Tap to add or switch servers", + "点此添加服务器": "Tap to add a server", + "版本": "Version", + "版本信息": "Version info", + "版本信息 · 所连网关环境": "Version info · connected gateway", "状态码 {code} 已在名单里": "Status code {code} is already in the list", "状态码必须是 {min}–{max} 的整数(收到: {text})": "Status code must be an integer from {min} to {max} (received: {text})", + "环境": "Environment", + "生成时间:{time}": "Created at: {time}", "用 models.dev 补齐上游没声明的模型能力与思考档位(联网刷新,内嵌快照始终生效)": "Fill in model capabilities and reasoning levels the upstream never declares, using models.dev (refreshed online; the bundled snapshot always applies)", + "用于加密上云的账号密钥。请自己记牢 —— 它不保存到云端,换机下载时必须输入同一个密码,否则账号数据无法解密。": "Used to encrypt the account key that goes to the cloud. Remember it — it is not stored in the cloud, and you must enter the same password when restoring on another machine, otherwise the account data cannot be decrypted.", + "用浏览器菜单里的「添加到主屏幕」把这一页装成 App:全屏打开、没有地址栏,和原生应用一样。": "Use \"Add to Home Screen\" in the browser menu to install this page as an app: full screen, no address bar, just like a native app.", + "界面": "Interface", "界面缩放": "Interface Zoom", "界面缩放比例": "Interface Zoom Level", "界面语言": "Interface Language", @@ -351,14 +580,28 @@ "界面语言只保存在本机(localStorage)。切换后整页立即刷新,不影响账号、密钥与转发。": "The interface language is stored only on this machine (localStorage). Switching reloads the whole page immediately and does not affect accounts, keys, or forwarding.", "界面里编辑的正文": "the text edited in the UI", "留空 = 用内置默认提示词": "Leave empty = use the built-in default prompt", + "留空使用 AWS 官方地址": "Leave blank to use the official AWS address", "登录 / 注册需要通过 ALTCHA 人机验证(工作量证明)": "Require ALTCHA human verification (proof of work) for sign-in / registration", "登录 / 账号切换 / 429 切换等系统事件,可填 1–99999 天。": "System events such as sign-ins, account switches, and 429 switches. Range: 1–99999 days.", + "登录 Agent2API": "Sign in to Agent2API", + "登录人机验证": "Login CAPTCHA", + "监听地址": "Listen address", + "目标路径:{path}": "Target path: {path}", "确定继续?": " Continue?", + "确认上传": "Confirm upload", + "确认新密码": "Confirm new password", "确认缩短保留天数": "Confirm Shorter Retention", + "社区与支持": "Community & support", + "离线缓存未启用": "Offline cache is off", + "积分合计只统计当前筛选出来的账号": "Only sum accounts visible under the current filter", + "移入「{name}」": "Move into \"{name}\"", + "程序路径": "Executable path", "空 = 不要这一份,回落提示词文件 / 内置默认提示词。": "Empty = do not keep this copy; falls back to the prompt file / built-in default prompt.", + "空分组 · 点组头的 + 把菜单移进来": "Empty group · use + on the header to move menus in", "立即解除": "Clear Now", "第一段": "Block 1", "第一段(身份句)": "Block 1 (identity sentence)", + "第三方应用密码": "App-specific password", "第三段": "Block 3", "第三段(动态段)": "Block 3 (dynamic block)", "第二段": "Block 2", @@ -367,8 +610,12 @@ "等待期间请求日志的状态列会显示「排队中」阶段,详情弹窗的「内部重试」里能逐次看到等待时长;等待用尽后返回 HTTP 503(不是 401/429:既不会刷新凭证,也不会把账号标成限额)。": "While waiting, the status column in the request logs shows a \"Queueing\" phase, and the \"Internal Retries\" section of the detail dialog lists each wait duration; when the waits run out, HTTP 503 is returned (not 401/429: credentials are not refreshed and the account is not marked as rate-limited).", "等比放大或缩小整个界面(文字、控件、间距一起变),效果与浏览器按 Ctrl +/- 相同:80%–130%、5% 一档。窗口本身不缩放,变的是页面内容的显示比例,设置立即生效并记住,下次启动直接按这个比例打开。放得越大可视范围越小,窗口较窄或表格较宽时不建议调得太大。": "Zoom the whole interface proportionally (text, controls, and spacing scale together), equivalent to Ctrl +/- in a browser: 80%–130% in 5% steps. The window itself does not resize — what changes is the page content's display scale; the setting applies immediately and is remembered, so the next launch opens at this scale. The larger the scale, the less you can see at once; with a narrow window or wide tables, do not set it too high.", "简体中文": "Simplified Chinese", + "管理员已注册": "Admin registered", + "管理员账号": "Administrator account", + "系统": "", "系统提示词模式": "System Prompt Mode", "系统提示词设置已刷新": "System prompt settings refreshed", + "累计请求": "Total requests", "繁体中文": "Traditional Chinese", "约 {n} 字符": "approx. {n} characters", "结构": "structure ", @@ -379,19 +626,39 @@ "编辑网关自带提示词 · {label}": "Edit Gateway-Provided Prompt · {label}", "缩放比例": "Zoom Level", "网关": "Gateway", + "网关 Key 数量": "Gateway key count", + "网关状态": "", "网关自带": "Gateway-Provided Prompt", "网关跨域访问已开启": "Gateway CORS enabled", "网关跨域访问设置已刷新": "Gateway CORS settings refreshed", "网关跨域访问(CORS)": "Gateway CORS", "网关面(/v1/*)默认不应答浏览器跨源请求:页面直连会被预检拦下,只报「无法连接 API」。注意来源是 * —— 未配「网关 Key」时任何网页都能借本机网关打上游,故默认关闭;只给自己用的话,让页面与网关同源(本地反代 /v1)更稳妥。面板接口(/api/*)不受影响。": "The gateway endpoints (/v1/*) do not answer browser cross-origin requests by default: a page connecting directly is stopped by the preflight and only reports \"cannot connect to the API\". Note that the allowed origin is * — with no gateway key configured, any web page could use this machine's gateway to hit upstreams, which is why the default is off; if you only use it yourself, serving the page from the same origin as the gateway (a local reverse proxy for /v1) is safer. The panel API (/api/*) is unaffected.", + "网页端": "Web", "网页端的界面缩放由浏览器自己控制(Ctrl + / Ctrl -,或浏览器菜单里的缩放),此项不可调。": "In the web build, interface zoom is controlled by the browser itself (Ctrl + / Ctrl -, or the zoom entry in the browser menu); this setting is not adjustable.", + "网页端:登录 / 注册需要 ALTCHA 人机验证": "Web: sign-in / registration requires ALTCHA proof-of-work", + "网页端:登录 / 注册需要 ALTCHA 人机验证(当前正在使用)": "Web: sign-in / registration requires ALTCHA proof-of-work (in use now)", + "群晖 NAS": "Synology NAS", + "群晖需在「控制面板 → 应用程序门户 → WebDAV」里启用服务,端口默认 5005(HTTP)/ 5006(HTTPS)。": "On Synology, enable the service under Control Panel → Application Portal → WebDAV; the default ports are 5005 (HTTP) / 5006 (HTTPS).", + "腾讯云 COS": "Tencent Cloud COS", + "自动同步": "Auto sync", + "自动同步上次失败": "Last auto sync failed", + "自动续写": "Auto continuation", "自定义提供商 {id}": "Custom provider {id}", "自愈保护": "Self-healing guards", + "至少 8 位,换机下载时需要": "At least 8 characters; needed when restoring on another machine", + "至少要保留一个分组": "At least one group must remain", "英语": "English", "装上{size}的官方身份提示词": "Install the official identity prompt ({size})", + "装成 App 后从桌面图标直接进入:全屏打开、没有地址栏。": "Installed as an app it opens straight from the home screen icon: full screen, no address bar.", + "覆盖": "overwrite", "解除失败:{message}": "Failed to clear the fallback: {message}", + "计数": "Counts", "计量单位": "Number notation", + "设置不可隐藏(隐藏了只能清缓存找回来)": "\"Settings\" cannot be hidden (you would have to clear storage to get it back)", + "详细日志": "Verbose logs", "语言设置": "Language Settings", + "请先保存设置再同步": "Save the settings before syncing", + "请填一个分组名": "Please enter a group name", "请填写管理员账号(64 字符以内)": "Enter an admin account (up to 64 characters)", "请求发出后等待上游响应头的最大时间(秒),默认 300 秒,范围 1~3600。": "Maximum time to wait for the upstream's response headers after the request is sent (seconds). Default 300 seconds, range 1–3600.", "请求提供商 / 模型支持": "Provider / Model Support Request", @@ -400,17 +667,29 @@ "请求超时": "Request Timeouts", "请求转发到上游失败时(服务器瞬时错误、连接失败,以及 WorkBuddy 的 11-128 敏感词拦截),网关会等一段间隔后再发一次。次数分两档,管的是两件事:①「同一账号重试次数」在同一账号上原地重发几次 —— 一般是提示词被上游拦截、或链路瞬时抖动,等一会儿重发往往就好了;②「切换账号重试次数」这份请求最多再换几个账号试 —— 首发的那个账号不算,每换一个扣一次,不分是不是同一家(换到同一家的下一个账号也算一次),换满还失败就把错误返回给客户端。两项都设为 0 表示失败不重试、直接报错。注意账号级限额(429)不占这里的换号次数 —— 那类失败走「冷却该账号并换下一个账号」的降级,与这里的重试是两条路。另有「指定错误码直接换号」名单(默认 402):命中名单的上游状态码不在同一账号重发,直接按队列换下一个账号继续试(换号也占「切换账号重试次数」),换满仍失败才把错误返回给客户端。": "When forwarding a request to an upstream fails (transient server errors, connection failures, and WorkBuddy's 11-128 sensitive-word blocking), the gateway waits for an interval and sends it once more. The counts come in two tiers covering two different things: (1) \"Same-Account Retries\" resends in place on the same account a few times — usually the prompt was blocked by the upstream or the link flickered, and resending after a wait tends to succeed; (2) \"Account-Switch Retries\" is how many more accounts this request may try — the account that sent it first does not count, each switch costs one, regardless of whether it is the same provider (switching to the next account of the same provider also counts), and when the switches are exhausted and it still fails, the error is returned to the client. Setting both to 0 means no retries on failure — error out immediately. Note that account-level throttling (429) does not consume these account switches — that kind of failure goes through the \"cool down the account and move to the next one\" fallback, a separate path from the retries here. There is also the \"Switch Accounts on Specified Error Codes\" list (default 402): upstream status codes on the list are not retried on the same account; the gateway switches to the next account in the queue and keeps trying (the switch also consumes an \"Account-Switch Retries\" slot), and only when those are exhausted is the error returned to the client.", "请求重试": "Request Retries", + "请输入同步密码": "Enter the sync password", + "请输入当前密码": "Please enter your current password", + "读取云同步设置失败:{message}": "Failed to read cloud sync settings: {message}", + "读取失败:{message}": "Failed to read: {message}", "读取完整非流式响应体允许的最大时间(秒),默认 300 秒,范围 1~3600。": "Maximum time allowed to read a complete non-streaming response body (seconds). Default 300 seconds, range 1–3600.", + "读取网关信息失败:{message}": "Failed to read gateway info: {message}", + "读取远端…": "Reading remote…", + "读取远端信息失败:{message}": "Failed to read remote info: {message}", "调试报文": "Debug Payloads", - "调试模式": "Debug Mode", + "调试模式": "Debug mode", "调试模式设置已刷新": "Debug mode settings refreshed", "负载均衡": "Load balancing", + "账号列表": "Account list", "账号导入 / 导出": "Account Import / Export", "账号路由": "Account routing", + "账号页表格的两项显示偏好,只存在本机(localStorage),不影响账号数据与转发。「积分合计只统计当前筛选出来的账号」:账号页每一家的分组带右侧有一枚「可用合计」,默认按整组求和 —— 不受筛选、搜索影响,被筛掉的账号也计入(余额是资产,禁用也只表示不参与转发,额度仍在账上)。打开这一项后改为只累加当前可见的行:例如筛出 1 个 100 积分的账号,合计就是 100,而不是同组两个账号之和 1,000。分页不参与这个口径 —— 翻页看的是同一份结果的不同屏,合计不随页码变化。「隐藏账号名」与表头「账号」旁边那颗眼睛是同一个开关:打开后账号列的名字整体显示成星号,适合截图分享时遮挡账号。": "Two display preferences for the accounts table. They are stored only on this machine (localStorage) and never affect account data or forwarding. \"Only sum accounts visible under the current filter\": each provider band on the accounts page shows a \"Total available\" badge, and by default it sums the whole group — filtering and search do not change it, and accounts filtered out still count (balance is an asset; disabling an account only keeps it out of forwarding, the credit stays). Turn this on to add up only the rows currently visible: filter down to one account with 100 credits and the total reads 100, not 1,000 (the sum of two accounts in that group). Paging does not take part in this rule — pages are just different screens of the same result, so the total does not change as you flip pages. \"Hide account names\" is the same switch as the eye next to the \"Account\" column header: when on, names in the account column are shown as asterisks, handy for screenshots.", + "账户": "Account", "超出的历史数据会被立即删除,且不可恢复。": "Excess historical data will be deleted immediately and cannot be recovered.", "超时": "Timeout", "超时设置已刷新": "Timeout settings refreshed", + "跟随系统 · 浅色 · 深色": "System · Light · Dark", "跟随默认": "Follow Default", + "跨域访问": "CORS", "路由": "Routing", "路由设置已刷新": "Routing settings refreshed", "跳过 {n} 个": "skipped: {n}", @@ -419,40 +698,79 @@ "转发一个请求时按什么顺序挑账号:①「路由策略」决定排队规则 —— 顺序优先是排第 1 的接走所有请求、直到它不能答(限流 / 故障 / 额度尽)才轮到下一位;轮流均摊是每条请求换下一位,雨露均沾;余额优先是用量占比最低的先用,「谁空谁上」。②「会话粘性」让同一会话(相同的模型 + 相同的开头消息)尽量跟住同一个账号 —— 多轮对话的上下文缓存能复用、少换号少浪费;自动档粘 5 分钟、会话内档粘 6 小时、每轮与关闭都不粘。账号的排队位次(账号表的第一列)在所有策略下都是地基,策略在它之上再排。此外路由还有三层自愈保护默认生效,不需要配置:转发失败的账号退避休息一会儿(15 分钟起步翻倍、封顶 1 小时),被 429 的账号垫底排到队尾一圈,余额不足 / Token 周期用量到顶的账号由限制器拦下 —— 恢复后自动回到队列。": "", "转发前将 developer 角色消息转换为 system,兼容不支持 developer 角色的 OpenAI 兼容上游": "Convert developer-role messages to system before forwarding, for OpenAI-compatible upstreams that do not support the developer role", "转发失败的账号休息一会儿再回来:15 分钟起步,连续失败翻倍,封顶 1 小时;成功一次立即恢复。目的是「让这次失败先凉一会儿」,免得一条条地反复撞同一个坏账号。": "An account that fails to forward rests for a while before coming back: starting at 15 minutes, doubling per consecutive failure, capped at 1 hour; one success restores it immediately. The point is to let a failure cool off instead of hammering the same bad account request after request.", + "转发链路": "Forwarding", + "转发链路 · 诊断 · 系统": "", "轮询间隔": "Poll interval", + "软件版本": "Software version", "轻量模式": "Lightweight mode", "轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 套餐池(Start Plan)通道不可用,相关请求会在请求日志里注明原因": "Lightweight mode is on: closing the window destroys the UI process (freeing about 200 MB of memory) while the gateway continues in the background; while the UI is destroyed, the ZCode plan-pool (Start Plan) channel is unavailable and affected requests will note the reason in the request log", "轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 活动套餐通道不可用,相关请求会在请求日志里注明原因": "Lightweight mode on: closing the window destroys the UI process (freeing about 200MB of memory) while the gateway keeps running in the background; while the UI is destroyed, the ZCode activity-plan channel is unavailable, and affected requests are noted in the request logs with the reason", "轻量模式(关窗释放界面内存)": "Lightweight mode (releases UI memory when the window is closed)", + "输入同步密码": "Enter sync password", + "输入当前登录用的密码": "", "输入状态码,回车添加": "Enter a status code and press Enter", + "运行形态": "Deployment", "还原": "Revert", "还有额度时被 429 的账号排到队尾,30 分钟后自动回到队列(成功转发一次也会立即回来)。它标记的是「疑似共享池被别人抢」,不是「这个账号本身有问题」。⚠️ 注意它与路由策略的配合:在「家内轮询」下,队尾意味着这一家整体被往后排 —— 如果某个提供商的账号被批量限流,它们会集体沉到队尾,那个家可能长时间轮不到。这时可以关掉本项,让 429 过的账号只受冷却约束(冷却到点自动回来)。": "An account with quota left that gets 429'd goes to the back of the queue and returns automatically after 30 minutes (one successful forward also brings it right back). This marks \"the shared pool is probably being drained by someone else\", not \"this account is broken\". ⚠️ Mind the interplay with the routing strategy: under \"Per-provider rotation\", the back of the queue means the whole provider gets pushed back — if one provider's accounts are rate-limited en masse, they all sink to the back and that provider may not be reached for a long time. Turn this off in that case, so 429'd accounts are only bound by cooldown (they come back automatically when it expires).", + "还没有同步过。": "Not synced yet.", + "还没有添加服务器。点右上角「添加」,填入网关所在机器的 IP 或域名与端口。": "No servers yet. Tap \"Add\" at the top right and enter the IP or domain and port of the machine running the gateway.", "还没有装载任何数据。": "No data loaded yet.", + "还没有读到版本信息": "Version info has not been loaded yet", "这一份就是「替换 / 追加」模式下网关要发的 system 正文。": "This is the system text the gateway sends in Replace / Append mode.", + "这一项已隐藏": "This item is hidden", + "这一项正在显示": "This item is visible", + "这台网关未启用机器人校验": "This gateway does not require bot verification", + "这台网关没有管理员,用一把已启用的 API Key 访问。": "This gateway has no admin account. Access it with an enabled API key.", + "进程 PID": "Process PID", + "远程根目录": "Remote root directory", + "远端同一路径下的旧快照。事件日志、请求明细与调试报文不会上传。": "the old snapshot at the same remote path. Event logs, request details and debug payloads are not uploaded.", + "远端已有一份快照,将被覆盖": "A snapshot already exists on the remote and will be overwritten", + "远端快照使用了另一把账号密钥,但没有携带密钥文件,无法在本机解密。请在原来那台机器上重新上传一次。": "The remote snapshot uses a different account key but does not include the key file, so it cannot be decrypted here. Re-upload once from the original machine.", + "远端快照使用的账号密钥与本机不同。请输入上传那台机器设置的同步密码,用它解出密钥装到本机后再继续下载。": "The remote snapshot uses a different account key than this machine. Enter the sync password set on the uploading machine to decrypt the key and install it here, then continue the download.", + "远端数据与当前版本不兼容": "Remote data is incompatible with this version", + "远端该路径下还没有快照,本次是首次上传。": "No snapshot exists at that remote path yet; this is the first upload.", + "远端路径:{path}": "Remote path: {path}", + "远端还没有可下载的同步数据": "No sync data available to download on the remote", + "连不上这台网关。确认地址与端口正确、网关已启动,且已开启「局域网访问」;用 https 时还要确认证书有效": "Cannot reach this gateway. Check the address and port, that the gateway is running, and that \"LAN access\" is enabled; for https also check that the certificate is valid.", "连接中超时": "Connect Timeout", + "连接失败:{message}": "Connection failed: {message}", "连续失败到阈值后把这个账号整个打开(默认关)。与失败退避的分工:退避管「单次失败先凉一会儿」,惩罚轻、自动恢复;熔断管「这个账号现在整体不对劲,别再一条条地试」,要连续成功若干次才关。打开期间该账号被跳过,除非全场只剩它 —— 那时仍放行试一次。": "Trips this account off entirely after consecutive failures reach a threshold (off by default). Division of labor with failure backoff: backoff means \"let a single failure cool off\" — light penalty, automatic recovery; the circuit breaker means \"this account is misbehaving as a whole, stop trying it one request at a time\" and takes several consecutive successes to close. While open, the account is skipped — unless it is the only one left, in which case it still gets one try.", "追加": "Append", "追加网关提示词": "Append gateway prompt", "退出失败:{message}": "Sign-out failed: {message}", "退出登录": "Sign Out", + "退出这台服务器的登录,不影响其他服务器": "Sign out of this server only; others are unaffected", "选择备份文件并恢复": "Choose a backup file and restore", "选路规则": "Selection rules", "透传": "Passthrough", "透传 = 不动客户端 system;替换 = 删掉客户端的 system / developer,换成网关提示词;追加 = 保留客户端内容,在最前面多插一条。": "", "透传客户端 system": "Passthrough client system", "透传客户端 system(默认)": "Passthrough client system (default)", + "通常是网络问题,点右侧重试": "Usually a network issue — tap Retry on the right", "通用": "General", "遇到 Bug、报错或异常行为": "Bugs, errors, or unexpected behavior", "配置目录下的 agent2api.db(账号、日志、请求记录、调试报文与设置都在里面)。": "agent2api.db in the configuration directory (it holds accounts, logs, request records, debug payloads, and settings).", + "重启后生效,不影响当前操作": "Takes effect after a restart; nothing you're doing is interrupted", + "重置会清空管理员账号与密码,面板将回到「未注册」状态。": "Reset clears the administrator account and password, returning the panel to the \"not registered\" state.", + "重置管理员": "Reset administrator", "重试设置已刷新": "Retry settings refreshed", "重试间隔": "Retry Interval", "铸造守卫每隔多久看一次库存(毫秒)。只影响发现缺货的反应速度 —— 库存够用时轮询不会铸造任何令牌。默认 2500 毫秒,可填 1000–30000。": "How often the minting guard checks the stock (milliseconds). It only affects how quickly a shortage is noticed — with enough stock, the poll mints nothing. Default 2500 ms; 1000–30000 accepted.", "问题反馈": "Bug Report", + "阿里云 OSS": "Alibaba Cloud OSS", + "限流中账号": "Rate-limited accounts", "随包内嵌的快照(发版时的那份)": "Snapshot bundled with the app (the one from the last release)", + "隐藏 {name}": "Hide {name}", + "隐藏分组 {name}": "Hide group {name}", + "隐藏用户名": "Hide username", + "隐藏账号名(显示成星号)": "Hide account names (show asterisks)", + "隐藏这一组": "Hide this group", "非流式响应超时": "Non-Streaming Timeout", - "面板登录": "Panel Login", + "面板登录": "Panel login", + "面板端口": "Panel port", "面板管理员账号": "panel admin account", "韩语": "Korean", + "预设": "Preset", "验证码令牌池": "Captcha token pool", "验证码令牌池设置已刷新": "Captcha token pool settings refreshed", "默认值通常够用,出问题时才需要调": "The defaults are usually fine; only tune these when something goes wrong", @@ -461,6 +779,7 @@ "默认开启。只改 role 字段本身,消息内容与顺序都不动;本项只改发给上游的副本,客户端看到的响应内容不变。": "Enabled by default. Only the role field changes — message content and order stay untouched; this only rewrites the copy sent upstream, and what the client sees never changes.", "默认开启。命中明细会记进请求日志的「敏」标签(悬停可看命中了哪几条规则)。本项只改发给上游的副本,客户端看到的响应内容不变。": "On by default. Hits are recorded in the request log's \"sensitive\" tag (hover to see which rules matched). This only changes the copy sent upstream; the response content the client sees is unchanged.", "默认网关只监听 127.0.0.1,只有本机能访问。开启后网关改听所有网卡,同一局域网内的设备可以把 API 地址指向本机 IP 一起使用。出于安全考虑,开启前必须先注册一个面板管理员:管理接口从此要求管理员会话或网关 Key,模型额度不会对局域网裸奔;一把启用的网关 Key 都没有时,转发接口也会拒绝服务,直到创建第一把。改动需要重启应用生效。「同时开放网页管理面板」把管理界面也出给局域网(其他设备的浏览器打开本机 IP 即可管理),默认关闭 —— 桌面端的面板仍只由本程序自己出。": "By default the gateway listens only on 127.0.0.1 and only this machine can reach it. Once enabled, the gateway listens on all network interfaces, and devices on the same LAN can point the API address at this machine's IP to share it. For security, you must first register a panel administrator: the admin API then requires an admin session or a gateway key, so model quotas are not left wide open to the LAN; and when no gateway key is enabled, the forwarding API refuses service too, until the first one is created. The change requires an app restart to take effect. \"Also serve the web admin panel\" exposes the admin UI to the LAN as well (other devices can open this machine's IP in a browser to manage it); it is off by default — the desktop build still serves the panel only from this app itself.", + "({n} 个整组隐藏)": " ({n} groups hidden)", "({n} 行)": " ({n} lines)", "({n} 项被覆盖)": " ({n} overridden)", "(全部为默认值)": " (all defaults)", @@ -473,6 +792,7 @@ "(未生效)": "(not in effect)", ",{items}": ", {items}", ",{time}": ", {time}", + ",以及代理池 / 订阅里可能存在的代理凭据。请使用只有你能访问的私有存储;账号密钥由同步密码单独加密,泄露存储本身不等于泄露账号。": ", plus any proxy credentials that may exist in the proxy pool / subscriptions. Use private storage only you can access; the account key is encrypted separately with the sync password, so leaking the storage alone does not expose your accounts.", ",包括恢复后需要重新登录管理面板。": " with the selected backup file, including the admin re-login after restore.", ",可直接用于登录。请妥善保管,不要外传或上传到公共位置。": ", and it can be used to sign in directly. Keep it safe; do not share it or upload it to a public location.", ",文件里后续的改动在清空这里之前不再生效": "; later changes to the file will not take effect until this is cleared", diff --git a/desktop-tauri/i18n/i18n-src/en/tasks.json b/desktop-tauri/i18n/i18n-src/en/tasks.json index 37de7df0..e4ff5be6 100644 --- a/desktop-tauri/i18n/i18n-src/en/tasks.json +++ b/desktop-tauri/i18n/i18n-src/en/tasks.json @@ -7,6 +7,7 @@ "{n} 秒后": "in {n} seconds", "✅ {task}:{summary}": "✅ {task}: {summary}", "✅ 已更新「{label}」": "✅ Updated \"{label}\"", + "「{name}」已执行": "", "上次执行 {time}": "Last run {time}", "上次执行 {time}({result})": "Last run {time} ({result})", "下次执行 {time}({relative})": "Next run {time} ({relative})", @@ -17,21 +18,30 @@ "停留在「请求日志」页时按此间隔重新拉取请求日志;页面不可见时不请求。": "While the Request logs page is open, request logs are fetched again at this interval; no requests are made while the page is not visible.", "冷却中,{relative}重试": "Cooling down, retrying {relative}", "凭证自动维护": "Credential auto-maintenance", + "分": "", "即将执行": "About to run", + "可填 {min}–{max} {unit}": "", "可填 {min}–{max} {unit}(默认 {def} {unit})": "Allowed: {min}–{max} {unit} (default {def} {unit})", + "后台任务": "", "定时任务已刷新": "Scheduled tasks refreshed", "定期向 GitHub 查询最新发布版本,默认每 20 分钟一次。检查结果、排期和限流冷却跨重启保留;手动检查也会合并重复请求并遵守冷却。开关与间隔在「软件更新」面板的「更新设置」弹窗里配置(定时任务页不再渲染这一条)。": "Periodically queries GitHub for the latest release version, once every 20 minutes by default. Check results, scheduling, and the rate-limit cooldown persist across restarts; manual checks also merge duplicate requests and respect the cooldown. The toggle and interval are configured in the \"Update settings\" dialog of the \"Software update\" panel (this row is no longer rendered on the Scheduled tasks page).", "定期拉取最新模型清单,重启沿用排期与缓存。客户端读取模型列表的后台刷新也遵守本任务开关、间隔和失败冷却;手动获取模型可提前刷新。": "Periodically fetches the latest model list; the schedule and cache carry over across restarts. The background refresh when clients read the model list also respects this task's toggle, interval, and failure cooldown; a manual model fetch can refresh ahead of schedule.", "定期拉取最新模型清单,重启沿用排期与缓存。客户端读取模型列表的后台刷新也遵守本任务开关、间隔和失败冷却;手动获取模型可提前刷新。同一轮顺带刷新 models.dev 模型元数据(能力位与思考档位,可在设置页单独关闭)。": "Periodically fetches the latest model list; the schedule and cache carry over across restarts. The background refresh when clients read the model list also respects this task's toggle, interval, and failure cooldown; a manual model fetch can refresh ahead of schedule. The same round also refreshes the models.dev model metadata (capability flags and reasoning tiers, which can be turned off separately on the Settings page).", "定期遍历账号,只刷新已过期或临期的凭证。重启后沿用上次排期,失败账号在冷却后重试;转发需要的按需续期不受此开关影响。": "Periodically iterates over accounts and refreshes only expired or soon-to-expire credentials. The previous schedule carries over after a restart, and failed accounts are retried after a cooldown; on-demand renewals needed for forwarding are not affected by this toggle.", + "已{action}「{name}」": "", "已执行": "Done", + "已更新间隔": "", + "开启": "", "执行失败:{reason}": "Run failed: {reason}", "报表自动刷新": "Reports auto-refresh", "改动立即生效,不需要重启程序": "Changes take effect immediately; no restart needed", + "改间隔": "", "日志页自动刷新": "Logs auto-refresh", "模型目录刷新": "Model catalog refresh", "正在加载定时任务…": "Loading scheduled tasks…", + "每 {n} {unit}": "", "没有可显示的定时任务。后端未返回任务清单,请确认网关正在运行。": "No scheduled tasks to show. The backend did not return a task list — make sure the gateway is running.", + "没有后台任务": "", "状态每 20 秒自动同步": "Status syncs automatically every 20 seconds", "由页面按间隔自动刷新": "Auto-refreshed by the page on its interval", "立即执行": "Run now", @@ -40,5 +50,8 @@ "还没有执行记录": "No runs recorded yet", "间隔不能为空(可填 {min}–{max} {unit})": "Interval cannot be empty (allowed: {min}–{max} {unit})", "间隔必须在 {min}–{max} {unit} 之间(当前填的是 {value})": "Interval must be between {min} and {max} {unit} (currently {value})", - "间隔必须是整数": "Interval must be an integer" + "间隔必须是 {min}–{max} 的整数": "", + "间隔必须是整数": "Interval must be an integer", + "页面刷新由浏览器执行,只在页面打开时生效": "", + "页面自动刷新": "" } diff --git a/desktop-tauri/i18n/i18n-src/en/update.json b/desktop-tauri/i18n/i18n-src/en/update.json index 2ed59c5c..e934b0f7 100644 --- a/desktop-tauri/i18n/i18n-src/en/update.json +++ b/desktop-tauri/i18n/i18n-src/en/update.json @@ -1,9 +1,14 @@ { " · 检查于 {time}": " · checked at {time}", " 已发布(当前 {current}),更新日志如下:": " has been released (current: {current}). Release notes below:", + "Discord 群": "Discord server", "GitHub Token 未填写": "GitHub Token not set", "GitHub 令牌": "GitHub Token", - "QQ 群二维码": "QQ group QR code", + "QQ 群": "QQ group", + "QQ 群 / Telegram / Discord 都在这里": "QQ groups, Telegram and Discord are all here", + "QQ 群 1": "QQ group 1", + "QQ 群 2": "QQ group 2", + "Telegram 群": "Telegram group", "{version} 已就位,服务正在重启,页面会在恢复后自动刷新…({seconds}s)": "{version} is in place. The service is restarting; this page will refresh automatically once it is back… ({seconds}s)", "{version}(预发布)": "{version} (Pre-release)", "✅ GitHub 令牌已保存(加密存储,界面不再显示)": "✅ GitHub Token saved (stored encrypted; it will no longer be shown in the UI)", @@ -30,8 +35,10 @@ "出网线路已切换,但写入磁盘失败(重启后会恢复原线路)": "Outbound route switched, but writing to disk failed (the old route will be restored after a restart)", "分钟({min} ~ {max})": "minutes ({min}–{max})", "到点自动向 GitHub 查询新版本,查到就走「检测到更新」弹窗;关闭后「检查更新」按钮仍可手动触发": "Checks GitHub for new versions on schedule and opens the \"Update available\" dialog when one is found; dismissing it still leaves manual checks available via the Check for updates button", - "加入 QQ 群": "Join the QQ group", "加入 beta 版": "Join the beta channel", + "加入交流群": "Join the community", + "即将满员": "Almost full", + "即将满员,请优先加群 2": "Almost full — please join group 2", "去更新": "Update now", "发现新版本 {latest}(当前 {current})。{at}": "New version {latest} is available (current: {current}).{at}", "发现新版本 {latest}(当前 {current}),但该版本没有提供容器内更新包;请在宿主机执行 docker compose pull && docker compose up -d 升级。{at}": "New version {latest} found (current: {current}), but this release provides no in-container update package. Upgrade on the host with docker compose pull && docker compose up -d.{at}", @@ -45,14 +52,22 @@ "回滚失败": "Rollback failed", "回滚失败:{error}": "Rollback failed: {error}", "在 GitHub 查看完整说明": "View full release notes on GitHub", + "在 QQ 里搜索群号即可加入。": "Search the group number in QQ to join.", + "在浏览器中打开": "Open in browser", + "在浏览器中打开 GitHub 发布页": "Open the GitHub release page in your browser", "在浏览器中打开项目仓库,点 Star 支持作者": "Open the project repository in your browser and Star it to support the author", "在默认浏览器中打开 GitHub 的令牌创建页(备注已预设,无需勾选任何权限,直接点 Generate token)": "Open GitHub's Token creation page in your default browser (the note is prefilled, no scopes need to be selected—just click Generate token)", "填写后检查限额 60 → 5000 次/小时(创建令牌无需勾选任何权限)": "Fill it in to raise the check rate limit from 60 to 5000 requests/hour (no scopes needed to create the Token)", + "复制群号 {group}": "Copy group number {group}", "安装中": "Installing", "安装包": "installer", "安装包已下载完成,正在启动安装程序(需要管理员权限,会弹出 UAC 确认框)…": "Installer downloaded. Launching the installer (admin rights required; a UAC prompt will appear)…", + "安装包已下载完成,正在启动新的 AppImage…": "Installer downloaded; launching the new AppImage…", + "安装包已下载完成,正在打开系统安装器…": "Installer downloaded; opening the system installer…", "安装包已下载完成,正在挂载磁盘映像…": "Installer downloaded. Mounting the disk image…", + "安装包已就绪:{name}。点击「安装并重启」后会启动新的 AppImage;把它移动到你的应用目录并替换旧文件即完成升级(本程序会先退出以释放文件占用)。": "Installer ready: {name}. \"Install and restart\" launches the new AppImage; move it to your applications folder and replace the old file to finish the upgrade (this app quits first to release the file).", "安装包已就绪:{name}。点击「安装并重启」后需要管理员权限,会弹出 UAC 确认框。": "Installer ready: {name}. Clicking \"Install and restart\" requires admin rights and will show a UAC prompt.", + "安装包已就绪:{name}。点击「打开安装包」后会交给系统安装器,按提示输入密码即可完成安装(本程序无需退出)。": "Installer ready: {name}. \"Open installer\" hands it to the system installer — enter your password when prompted (this app does not need to quit).", "安装包已就绪:{name}。点击「打开安装包」后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装。": "Installer ready: {name}. Clicking \"Open installer\" mounts the disk image; drag the app into Applications to finish installing.", "安装包已挂载,请在弹出的窗口里把应用拖进「应用程序」完成安装。安装完成后重新打开本程序即可。": "The disk image is mounted. Drag the app into Applications in the window that appeared to finish installing, then reopen this app.", "安装失败": "Installation failed", @@ -64,6 +79,7 @@ "已取消": "Canceled", "已填写": "Saved", "已填写(粘贴新令牌可覆盖)": "Saved (paste a new Token to overwrite)", + "已打开系统安装器,请在它弹出的窗口里完成安装。安装完成后重新打开本程序即可。": "The system installer is open — finish the installation in its window, then reopen this app.", "已是最新": "Up to date", "已替换为 {version},正在重启服务…": "Replaced with {version}; restarting the service…", "已还原上一版本,正在重启服务…": "Previous version restored; restarting the service…", @@ -89,6 +105,7 @@ "更新包从 GitHub 发布页下载,下载完成后可直接启动安装程序并自动重启本程序。安装包会先校验来源域名与文件完整性。检查更新与下载安装包的出网线路、GitHub 令牌都在「更新设置」里:线路默认直连(直连失败时自动借 Clash Verge 的混合端口重试一次),选定指定出口后只走该出口;填写 GitHub 令牌可把检查限额从 60 次/小时提高到 5000 次/小时。": "Update packages are downloaded from the GitHub releases page; once downloaded, the installer can be started directly and this app restarts automatically. The installer verifies the source domain and file integrity first. The outbound route and GitHub Token for update checks and installer downloads are both in Update settings: the route defaults to a direct connection (if that fails, it automatically retries once through Clash Verge's mixed port), and once a specific proxy is selected only that route is used; filling in a GitHub Token raises the check rate limit from 60 to 5000 requests/hour.", "更新包已就绪:{name}。点「安装并重启」后,面板会校验并替换容器内的程序与界面文件,然后重启服务(页面会自动刷新)。": "Update package ready: {name}. After you click \"Install and restart\", the panel verifies and replaces the program and UI files inside the container, then restarts the service (the page refreshes automatically).", "更新已应用到容器内,重启服务后生效。{at}": "The update has been applied inside the container and takes effect after the service restarts.{at}", + "更新已应用到容器内,重启服务后生效。{at}(本次检查失败:{error})": "The update has been applied inside the container and takes effect after the service restarts.{at} (this check failed: {error})", "更新日志": "Release notes", "更新设置": "Update settings", "替换完成,正在重启服务…": "Replacement complete; restarting the service…", @@ -104,10 +121,13 @@ "检查更新": "Check for updates", "检查更新与下载安装包走哪条线路(当前:{current});选择即保存。选项来自「网络代理」页;直连失败时会自动借 Clash 混合端口重试一次,选定指定代理后只走它": "Which route update checks and installer downloads use (current: {current}); your choice is saved immediately. Options come from the Network proxy page; if a direct connection fails, it automatically retries once through Clash's mixed port, and once a specific proxy is selected only that route is used", "检查更新失败:{error}": "Update check failed: {error}", + "检查更新失败:{error}(以下版本与更新日志来自上次成功检查,检查于 {time})": "Update check failed: {error} (the versions and release notes below are from the last successful check at {time})", "检查间隔必须是 {min} ~ {max} 分钟": "The check interval must be {min}–{max} minutes", "检查间隔(分钟)": "Check interval (minutes)", "检测到更新": "Update available", "正在下载 {name}:{percent}%({received} / {total} MB)": "Downloading {name}: {percent}% ({received} / {total} MB)", + "正在下载安装包…(下载完成后会交给系统安装器,按提示输入密码即可完成安装)": "Downloading the installer… (once done it is handed to the system installer; enter your password when prompted).", + "正在下载安装包…(下载完成后会启动新的 AppImage;把它替换掉旧文件即完成升级)": "Downloading the installer… (once done the new AppImage launches; move it over the old file to finish the upgrade).", "正在下载安装包…(下载完成后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装)": "Downloading installer… (when it finishes, the disk image will be mounted; drag the app into Applications to complete the installation)", "正在下载安装包…(下载完成后启动安装程序需要管理员权限,会弹出 UAC 确认框)": "Downloading installer… (when it finishes, starting the installer requires admin rights and will show a UAC prompt)", "正在下载服务端更新包…(下载完成后由面板完成替换,重启容器内的服务后生效)": "Downloading the server update package… (the panel replaces the files once the download finishes; the update takes effect after the service inside the container restarts)", @@ -120,8 +140,9 @@ "点击「检查更新」后,这里会显示最新版本的更新说明。": "After you click \"Check for updates\", the release notes for the latest version will appear here.", "点击「检查更新」查询 GitHub 上的最新发布版本。": "Click \"Check for updates\" to query GitHub for the latest release.", "点击「检查更新」查询最新发布版本。": "Click \"Check for updates\" to query the latest release.", + "点击在浏览器中打开": "Click to open in your browser", + "点击复制群号": "Click to copy the group number", "环境变量": "Environment variable", - "用手机 QQ 扫上面的二维码,或在 QQ 里搜索群号加入。": "Scan the QR code above with mobile QQ, or search for the group number in QQ to join.", "立即重启": "Restart now", "粘贴 GitHub 令牌(ghp_… / github_pat_…)": "Paste GitHub Token (ghp_… / github_pat_…)", "群号": "Group number", @@ -135,6 +156,7 @@ "这一版没有提供容器内更新包": "This release provides no in-container update package", "这个版本没有填写发布说明。": "No release notes were provided for this version.", "通道已切换,但写入磁盘失败(重启后会回到原设置)": "Channel switched, but writing to disk failed (the old setting will be restored after a restart)", + "遇到问题或有使用心得,欢迎到群里交流。": "Questions and tips are welcome in any of these groups.", "重启中": "Restarting", "重新粘贴保存一次即可自愈": "Paste and save it again to recover", "重试下载": "Retry download", diff --git a/desktop-tauri/i18n/i18n-src/ja/accounts.json b/desktop-tauri/i18n/i18n-src/ja/accounts.json index 91790948..2c1d50d6 100644 --- a/desktop-tauri/i18n/i18n-src/ja/accounts.json +++ b/desktop-tauri/i18n/i18n-src/ja/accounts.json @@ -7,7 +7,9 @@ "0 = 不限": "0 = 無制限", "0 = 按本家默认 {default}": "0 = 本家のデフォルト {default}", "30 秒 ~ 24 小时;手动查询会顺延下一轮": "30 秒 ~ 24 時間;手動クエリは次の周期に順延", + "7 天内到期": "7 日以内に期限切れ", "API Key": "API Key", + "Accio 国内版": "Accio 中国版", "Base URL": "Base URL", "Clash Verge 里还没有配置混合监听器或节点端口": "Clash Verge に混合リスナーまたはノードポートがまだ設定されていません", "Clash 出口:": "Clash 出口:", @@ -15,12 +17,18 @@ "NewAPI 用户 ID": "NewAPI ユーザー ID", "OpenAI 兼容": "OpenAI 互換", "OpenAI 兼容填到 /v1;Anthropic 填根地址": "OpenAI 互換は /v1 まで、Anthropic はルートアドレスを入力", + "Qoder 中国版": "Qoder 中国版", + "Qoder 国际版": "Qoder 国際版", "Token 单位": "Token 単位", "Token 时": "Token 以上になったとき", "Token 消耗": "Token 消費", "Token 限额": "Token 上限", "Token 限额 · 已禁用": "Token 上限 · 無効", "Token 限额 · 已跳过": "Token 上限 · スキップ", + "WorkBuddy 国内版": "WorkBuddy 中国版", + "WorkBuddy 国际版": "WorkBuddy 国際版", + "ZCode 国内版": "ZCode 中国版", + "ZCode 国际版": "ZCode 国際版", "ZCode 套餐明细 — {name}": "ZCode プラン明細 — {name}", "bigmodel_token_production 的值": "bigmodel_token_production の値", "{action}{who}": "{action}{who}", @@ -32,13 +40,24 @@ "{label}({count})": "{label}({count})", "{message}(去该账号的「设置」里填上查询凭证即可)": "{message}(そのアカウントの「設定」でクエリ用認証情報を入力すれば解決します)", "{month}月{day}日 {clock}": "{month}月{day}日 {clock}", + "{name} · 分组设置": "{name} · グループ設定", "{name} 移到 {group} 组内第 {rank} 位": "{name} を {group} グループ内の {rank} 番へ移動しました", "{name} 移到第 {rank} 顺位": "{name} を全体 {rank} 番へ移動しました", + "{name}的方向": "{name}の方向", "{name}({parts})": "{name}({parts})", "{name}({time} 起生效)": "{name}({time} から有効)", + "{n} 个已计入": "{n} 件を合計", + "{n} 个查询失败": "{n} 件の照会に失敗", "{n} 个模型 ▾": "{n} 個のモデル ▾", + "{n} 个模型限流中 · 到恢复时间自动解除": "", "{n} 个模型限流中 · 记录来自上游 429 / 限额码,到恢复时间自动解除": "{n} 個のモデルがレート制限中 · 記録はアップストリームの 429 / クォータコードによるもので、リセット時刻に自動解除", "{n} 个请求正在使用该账号(含还在下发内容的流式请求)": "{n} 件のリクエストがこのアカウントを使用中(コンテンツ配信中のストリーミングリクエストを含む)", + "{n} 个读数只读到一半(读数可信但不完整)": "{n} 件は一部のみ取得(値は信頼できるが不完全)", + "{n} 个读数没有绝对数(只给比例或无额度),未计入": "{n} 件は絶対値なし(割合のみ、または枠なし)のため合計に含めず", + "{n} 个账号 · {e} 启用 · {l} 限流": "{n} 件 · 有効 {e} · レート制限 {l}", + "{n} 个账号 · 启用 {e}": "{n} 件 · 有効 {e}", + "{n} 个还没查到(未查询或正在查询)": "{n} 件は未取得(未照会または照会中)", + "{n} 个还没查询": "{n} 件は未照会", "{n} 分钟": "{n} 分", "{n} 分钟后": "{n} 分後", "{n} 天后": "{n} 日後", @@ -47,6 +66,9 @@ "{n} 秒": "{n} 秒", "{period}窗口": "{period}ウィンドウ", "{plan}({time} 领取)": "{plan}({time} 受け取り)", + "{reason}。该账号的登录态已被上游注销,自动刷新不会再成功,请重新登录该账号": "{reason}。このアカウントのログイン状態はアップストリーム側で取り消されました。自動更新は今後成功しないため、このアカウントで再度ログインしてください。", + "{reason}之后组顺位才可拖动(别家的组整组不可见,落点没法定位)": "", + "{reason}之后顺序才可拖动(模型作用域是全局队列的投影,这里没有可写的顺序)": "", "{scope} {used} / {quota} Token,{countdown}重置": "{scope} {used} / {quota} Token、{countdown}にリセット", "{text}过期": "{text}に期限切れ", "{text}过期({full})": "{text}に期限切れ({full})", @@ -79,7 +101,9 @@ "✅ 顺序已调整": "", " 出口 IP {ip}": " 出口 IP {ip}", "、": "、", - "、补官方会话头、请求体补两个占位工具(上游免费档的三道校验)。": "、公式セッションヘッダーを補完し、リクエストボディに 2 つのプレースホルダーツールを補完します(アップストリーム無料枠の 3 つの検証)。", + "、补官方会话头、请求体补占位工具(上游免费档的过门校验)。": "、公式セッションヘッダーを補完し、リクエストボディにプレースホルダーツールを補完します(アップストリーム無料枠の形状検証)。", + "「{name}」已在「网络代理」页禁用,请先启用它或换一条": "「{name}」は「ネットワークプロキシ」ページで無効になっています。先に有効にするか、別のものを選んでください。", + "「{name}」开着排序规则:先关掉规则,才能手动调整组内顺序": "「{name}」は並べ替えルールが有効です。グループ内の順序を手動で調整する前にルールをオフにしてください", "「{name}」本身": "「{name}」自体", "「已保存的代理」": "「保存済みのプロキシ」", "「网络代理」页还没有出口 —— 去那里新增,或在「订阅管理」里点「同步 Clash Verge」把 Clash 的出口导进来": "「ネットワークプロキシ」ページにまだ出口がありません —— そこで追加するか、「サブスク管理」で「Clash Verge を同期」をクリックして Clash の出口をインポートしてください", @@ -91,6 +115,8 @@ "上游的 API Key。留空 = 不修改(改凭证不会换账号 id);要清掉已配的凭证用右边的「清除」": "アップストリームの API Key。空欄 = 変更しない(認証情報を変更してもアカウント id は変わりません);設定済みの認証情報を消去するには右の「消去」を使います", "上游的余额读数里没有这个套餐的额度桶,剩余量读不到;余额列显示的是套餐发放的权益额度,不是剩余量": "上流の残高読み取り値にはこのプランの枠(バケット)がなく、残量は取得できません。残高列に表示されるのはプランの付与権益であり、残量ではありません", "上游返回 {status}": "アップストリームが {status} を返却", + "上面所有规则都相同时,保持你拖出来的顺序": "上のすべてのルールが同じ場合、ドラッグした順序を維持します", + "不到 1 分钟": "1 分未満", "不可用:{error}": "使用不可:{error}", "不限": "無制限", "与队列里的上一个账号交换优先级(可能是另一家的账号)": "キューの 1 つ前のアカウントと優先度を交換(別プロバイダーのアカウントの場合もあります)", @@ -98,11 +124,16 @@ "个": "個", "个 · 共": "個 · 合計", "个人": "個人", + "主凭证没有固定到期时刻,到期由网关自动续期或本来就不会过期": "主となる資格情報に固定の有効期限はありません(期限切れはゲートウェイが自動更新します。そもそも期限がない場合もあります)", "主机": "ホスト", "仅将优先级调整到全局第一位,不改变启用状态": "優先度をグローバルキューの先頭に調整するだけで、有効状態は変更しません", + "仅按手动顺序": "手動順序のみ", "仅账号管理": "アカウント管理のみ", + "今天 24 点": "", "今天 {clock}": "今日 {clock}", "今天 {time}": "今日 {time}", + "今天剩余:{name}": "", + "今天剩余:{name} · 这一份还剩 {value}(每天 24 点回满)": "", "今天还没有领过:上面「可领取的套餐」里逐份点「领取」即可": "本日はまだ受け取っていません。上の「受け取れるプラン」で各プランの「受け取る」を押してください。", "今天(北京时间 {day})已由官方确认到账 {n} 项;领到的是套餐赠送积分,不增加福利模型的 token 池;按自然日重置,明天可再领": "今日(北京時間 {day})は {n} 件の到着が公式に確認済み;受け取ったのはプラン特典積分で、特典モデルの token プールは増えません;自然日でリセットされ、明日また受け取れます", "今天(北京时间 {day})已试过 {n} 次但官方尚未确认到账,": "今日(北京時間 {day})は {n} 回試しましたが公式の到着確認はまだです、", @@ -137,18 +168,22 @@ "余额不足": "残高不足", "余额不足 · 已跳过": "残高不足 · スキップ", "余额低于": "残高が", + "余额低于阈值时的提示": "残高がしきい値を下回ったときに通知", "余额低于阈值,转发时会跳过该账号(余额回升自动恢复)": "残高がしきい値を下回ると、転送時にこのアカウントをスキップします(残高が戻れば自動復帰)", "余额低于限制器的阈值,转发时会跳过该账号(余额回升自动恢复)": "残高がリミッターのしきい値を下回ると、転送時にこのアカウントをスキップします(残高が戻れば自動復帰)", "余额列读数的自动刷新;也是「限制器」余额规则的数据来源。": "残高列の読み取り値の自動更新;「リミッター」の残高ルールのデータソースでもあります。", "余额响应为空": "残高レスポンスが空", + "余额已更新": "残高を更新しました", "余额查询凭证": "残高クエリ用認証情報", "余额查询失败:{message}": "残高クエリ失敗:{message}", "余额查询方案:{label}": "残高照会の方式:{label}", "余额沿用现有口径;Token 按重置方式内的累计消耗判定(切换类型会清空阈值)": "残高は既存の基準を踏襲;Token はリセット方式内の累計消費で判定(タイプを切り替えるとしきい値はクリアされます)", + "余额详情": "残高の詳細", "使用套餐": "利用プラン", "保存失败:{error}": "保存失敗:{error}", "保存规则": "ルールを保存", "修改代理": "プロキシを変更", + "倍率 {rate}": "", "免费版": "無料版", "全局一条队列:数值越小越先用,不分提供商": "グローバルで 1 本のキュー:数値が小さいほど優先、プロバイダーは問いません", "全局唯一:所有提供商的账号都不能重号,数值越小越先用": "グローバルで一意:すべてのプロバイダーのアカウントで重複不可、数値が小さいほど優先", @@ -165,18 +200,30 @@ "关闭则不参与转发(余额查询照常,见「查询设置」)": "オフにすると転送に参加しません(残高クエリは通常どおり、詳細は「クエリ設定」)", "内消耗 ≥": "内の消費が", "再领一次": "もう一度受け取る", + "冷却进度 {percent}%": "", + "凭证": "", "凭证与套餐": "認証情報とプラン", + "凭证已刷新": "", + "凭证已被上游注销": "", "凭证已过期,转发时会先刷新": "認証情報が期限切れです。転送時に先に更新されます", "出口": "出口", "出网代理": "送信プロキシ", + "分组设置": "グループ設定", "分钟": "分", + "切回全局账号表": "", + "删除中…": "", "删除提供商": "プロバイダーを削除", "删除账号": "アカウントを削除", "删除这条账号记录(不会影响客户端自己的登录态;之后可再点「导入桌面端登录态」加回来)": "このアカウント記録を削除(クライアント自身のログイン状態には影響しません;後で「デスクトップ版のログイン状態をインポート」をクリックすれば戻せます)", "删除选中的账号": "選択したアカウントを削除", "到恢复时间后自动解除,无需手动操作": "リセット時刻になると自動解除、手動操作は不要", "到期 {time}": "期限 {time}", + "到期 近→远": "期限 近→遠", + "到期 远→近": "期限 遠→近", + "到期时刻 {time}": "", "刷新 Token": "Token を更新", + "刷新凭证": "", + "刷新队列": "", "剩 {left} / {total}{unit}": "残り {left} / {total}{unit}", "剩 {n} 分钟": "残り {n} 分", "剩 {remaining}": "残り {remaining}", @@ -186,6 +233,8 @@ "协议": "プロトコル", "卡片": "", "又不勾": "チェックしないと", + "取消排序": "", + "取消排序,回到默认次序(不影响已写回的队列顺序)": "", "取消选择": "選択を解除", "另 {n} 个分组全是禁用账号": "", "另有 {n} 个已勾选账号被当前筛选隐藏,仍会参与操作": "他に {n} 個のチェック済みアカウントが現在の絞り込みで非表示です。操作の対象には含まれます", @@ -196,9 +245,13 @@ "可用合计": "利用可能合計", "可留空:新版请求头只认令牌;旧版会把用户 ID 放进 New-Api-User 头": "空欄可:新しい版はトークンのみで、旧版はユーザー ID を New-Api-User ヘッダーに入れます", "可领取的套餐": "受け取れるプラン", + "各厂商组内排序:分组与组序不变;组内先后已写回调度队列": "", "同提供商内暂无其他账号占用优先级": "同一プロバイダー内で優先度を使用している他のアカウントはありません", "同提供商已占用:{names}": "同一プロバイダーで使用中:{names}", "名称": "名称", + "后续加入": "今後追加予定", + "否": "", + "含 {n} 个不限额度账号,合计记为 ∞": "無制限のアカウントを {n} 件含むため、合計は ∞ と表示", "启用状态": "有効状態", "启用该规则": "このルールを有効化", "启用该账号": "このアカウントを有効化", @@ -208,6 +261,7 @@ "周期单位": "周期単位", "商汤日日新控制台的登录密码": "SenseNova コンソールのログインパスワード", "商汤日日新控制台的登录账号": "SenseNova コンソールのログインアカウント", + "固定兜底,不受开关影响": "固定のフォールバック(スイッチの影響を受けません)", "固定周期": "固定周期", "在 NewAPI 站点「个人设置」里生成的访问令牌": "NewAPI サイトの「個人設定」で発行したアクセストークン", "在列表里拖:拖分组带调整整组顺位,拖行首把手调整组内先后(顺序由拖拽表达,不再填数字)": "リスト内でドラッグ:グループバーをドラッグでグループ全体の順位、行頭のハンドルをドラッグでグループ内の順序を調整します(順序はドラッグで表現、数値入力は廃止)", @@ -216,6 +270,8 @@ "基本": "基本", "备注名 / 启用 / 代理": "ラベル名 / 有効 / プロキシ", "备注名 {name}": "ラベル名 {name}", + "备注名不能为空": "ラベル名を空にすることはできません", + "备注名已更新": "ラベル名を更新しました", "失败 {count} 个:": "失敗 {count} 件:", "套餐 {name}": "プラン {name}", "套餐明细弹窗未加载,请重启应用后重试": "プラン明細ダイアログが読み込まれていません。アプリを再起動してからお試しください", @@ -225,10 +281,12 @@ "套餐池(Start Plan)— 走 zcode.z.ai 端点,消耗 ZCode 套餐池的额度(新用户体验额度 / 活动领取)": "プランプール(Start Plan)— zcode.z.ai のエンドポイントを使用し、ZCode プランプールのクォータ(新規ユーザーの体験クォータ / イベント受け取り分)を消費", "套餐积分的重置日期": "プラン積分のリセット日", "它会改写请求体": "リクエストボディを書き換えます", + "完整": "", "官方限时活动": "公式期間限定キャンペーン", "对话 token": "対話 token", "将作用于以下账号": "以下のアカウントに適用されます", "小时": "時間", + "小浣熊": "小浣熊", "尚未查询该账号的余额": "このアカウントの残高はまだクエリしていません", "展开全部": "すべて展開", "展开全部分组": "全グループを展開", @@ -236,16 +294,25 @@ "展开这一组": "このグループを展開", "已保存的代理": "保存済みのプロキシ", "已停用": "停止中", + "已删除账号": "", + "已启用 {name}": "「{name}」を有効にしました", "已启用,点击禁用(不参与转发)": "有効中、クリックで無効化(転送に参加しません)", "已在全局队列第一位": "すでにグローバルキューの先頭です", + "已按「{label}」{dir}排序": "", + "已按「{label}」{dir}排序并保存": "", + "已是首选": "すでに最優先です", "已更新 {ok}/{total} 个账号,{failed} 个失败": "{ok}/{total} アカウントを更新、{failed} 件失敗", + "已查询": "取得済み", "已用 {used}": "使用済み {used}", "已用 {used} / 共 {total}": "使用済み {used} / 全体 {total}", "已用完 / 已过期": "使い切り / 期限切れ", "已禁用": "無効", + "已禁用 {name}": "「{name}」を無効にしました", "已禁用,点击启用": "無効中、クリックで有効化", + "已筛选 {n} 项": "{n} 件を表示中", "已被「{name}」占用": "「{name}」が使用中", "已触发 · 账号已禁用": "発動済み · アカウント無効", + "已设为首选": "最優先に設定しました", "已设置": "設定済み", "已跳过{current}": "スキップ{current}", "已跳过{usage}{countdown}": "スキップ{usage}{countdown}", @@ -257,10 +324,10 @@ "已限流": "レート制限中", "已领到、还没到生效时间的套餐(生效后余额列会按它显示):\n{lines}": "受け取り済みで、まだ有効になっていないプラン(有効になると残高列に表示されます):\n{lines}", "并发上限": "同時接続上限", - "并发上限 0~{max};有上游硬顶的家(如 CodeArts)以它的硬顶为准;达到上限的账号会被跳过、请求转给其他账号": "同時接続上限 0~{max};上流のハードキャップがあるプロバイダー(CodeArts など)はそのキャップが優先されます;上限に達したアカウントはスキップされ、リクエストは他のアカウントに回されます", + "并发上限 0~{max};CodeArts 等有默认值的家填 0 即按本家默认;达到上限的账号会被跳过、请求转给其他账号": "同時接続上限 0~{max};デフォルト値のあるプロバイダー(CodeArts など)は 0 を指定するとそのデフォルトになります;上限に達したアカウントはスキップされ、リクエストは他のアカウントに回されます", "并发上限 0~{max};达到上限的账号会被跳过、请求转给其他账号": "同時接続上限 0~{max};上限に達したアカウントはスキップされ、リクエストは他のアカウントに回されます", "并发上限 0~{max};达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "同時接続上限 0~{max};上限に達したアカウントはスキップされ、リクエストは他のアカウントに回されます(旧リストの ⋯ メニューの「同時接続上限」はここに統合されました)", - "并发上限 受上游硬顶约束,达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "同時接続上限はアップストリームのハードキャップに制約されます。上限に達したアカウントはスキップされ、リクエストは他のアカウントに回されます(旧リストの ⋯ メニューの「同時接続上限」はここに統合されました)", + "并发上限 填 0 即按本家默认 {default};填 1 或 2 就按填的算,更大的数会被本家上限截断(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "0 を指定するとこのプロバイダーのデフォルト({default})になります;1 や 2 はそのまま適用され、それより大きい値はプロバイダー側の上限で切り詰められます(旧リストの ⋯ メニューの「同時接続上限」はここに統合されました)", "并发上限必须是 0~{max} 的整数": "同時接続上限は 0~{max} の整数でなければなりません", "并发上限:{value}": "同時接続上限:{value}", "开启后,网关按 OpenCode 官方 CLI 的形状发请求:没填 Key 的账号用匿名凭证": "オンにすると、ゲートウェイは OpenCode 公式 CLI の形式でリクエストを送信します:Key を入力していないアカウントの匿名認証情報は", @@ -268,10 +335,16 @@ "当前代理不可用:{error}(转发时会回退直连)": "現在のプロキシは使用不可:{error}(転送時は直接接続にフォールバック)", "当前余额 {n};与余额列同一数字口径,严格小于才触发(等于阈值仍可用)": "現在の残高 {n};残高列と同じ数値基準で、厳密に下回った場合のみ発動(しきい値と同じなら利用可能)", "当前可用 ∞(不参与余额不足判定)": "現在利用可能 ∞(残高不足の判定対象外)", + "当前已经是默认规则": "すでに既定のルールです", + "当前排序规则:{rules}。点击调整": "現在の並べ替えルール:{rules}。クリックして調整", "当前没有任何模型处于限流中": "現在レート制限中のモデルはありません", "当前没有可领取的套餐(活动期内每天发一份,明天可再领)": "現在受け取れるプランはありません(キャンペーン期間中は毎日 1 件配布されます。明日また受け取れます)", "当前没有限流中的模型。": "現在レート制限中のモデルはありません。", + "当前筛选下 {n} 个账号的可用余额之和(随筛选、搜索变化,不受分页影响)": "", + "当前筛选下没有账号": "現在の条件に一致するアカウントはありません", "当前筛选条件下没有账号": "現在の絞り込み条件に該当するアカウントはありません", + "当前规则:{rules}。自动排序生效时,组内拖动把手会锁定;关掉全部规则即回到手动顺序。": "現在のルール:{rules}。自動並べ替えが有効な間はグループ内のドラッグハンドルがロックされます。すべてのルールをオフにすると手動順序に戻ります。", + "当前视图不能调整组内顺序:{reason} 之后再拖动": "", "当前视图不能调整组顺位:{reason} 之后再拖动": "この表示ではグループ順位を変更できません:{reason} にしてからドラッグしてください", "当前:{label}(未经过代理池 —— 可在「网络代理」页把出口同步进池后来这里改选)": "現在:{label}(プロキシプールを経由していません —— 「ネットワークプロキシ」ページで出口をプールに同期してからここで選び直せます)", "当前:{label}(每条请求换一个出口);「自定义代理…」打开完整设置": "現在:{label}(リクエストごとに出口が変わります);「カスタムプロキシ…」で詳細設定を開きます", @@ -284,13 +357,20 @@ "必填;留空 = 不修改,清除用右边的按钮": "必須。空欄なら現状を維持、クリアは右のボタンで", "总剩余 {total} · 套餐 {plan} · 奖励 {bonus}": "残り合計 {total} · プラン {plan} · ボーナス {bonus}", "总量 {value}": "総量 {value}", + "恢复到「积分到期 → 添加时间」这套默认规则": "既定のルール「クレジット有効期限 → 追加日時」に戻す", "恢复时间未知": "リセット時刻不明", "我的套餐": "自分のプラン", + "所属厂商:{label}": "", "所有分组里都没有启用的账号(禁用不参与转发)": "", + "所有提供商都有这个字段": "すべてのプロバイダーにこの項目があります", + "所有积分包里**最先作废**的那一份什么时候到期;逐份明细见「余额」格里的积分明细": "", + "所有积分包里最先作废的那一份什么时候到期;没有一次性包时退到每日重置额度、显示今天还剩多久;逐份明细见「余额」格里的积分明细": "", "手动添加": "手動追加", "打开批量操作弹窗,并预选这一组的账号(改代理等动作在弹窗里挑参数)": "一括操作ダイアログを開き、このグループのアカウントを事前選択します(プロキシ変更などのパラメータはダイアログ内で選択)", + "打开筛选": "", "执行中…": "実行中…", "执行({action})": "実行({action})", + "扫码 / 粘贴令牌 / 导入登录态需在桌面端完成": "", "批量动作": "一括操作", "批量操作 · 已选 {count} 个账号": "一括操作 · {count} アカウントを選択", "批量操作失败:{error}": "一括操作失敗:{error}", @@ -303,20 +383,31 @@ "拖动调整列宽(双击还原)": "ドラッグで列幅を調整(ダブルクリックでリセット)", "拖动调整整组顺序": "ドラッグでグループ全体の順序を調整", "拖动调整组内顺序": "ドラッグでグループ内の順序を調整", + "按优先级从上到下比较,勾选的项才参与": "上から順に優先度で比較し、チェックした項目だけが使われます", "按住拖动,调整这一家的组顺位(整组一起动)": "ドラッグでこのプロバイダーのグループ順位を調整(グループごと動きます)", "按住拖动,调整这个账号在本组内的顺序": "ドラッグでこのアカウントのグループ内の順序を調整", "按提供商筛选账号": "プロバイダーでアカウントを絞り込み", "按模型": "モデル別", "按规则限制该账号参与转发:没有规则(或全部停用)= 不限制;跳过档自动恢复,禁用档需手动启用。": "ルールに従ってこのアカウントの転送参加を制限:ルールがない(またはすべて停止)= 制限なし;スキップ段階は自動復帰、無効段階は手動での有効化が必要です。", + "按账号最早到期、且还有剩余的积分包比较": "残高が残り、まだ有効なクレジットパックのうち最も早い期限で比較します", + "排序": "並べ替え", + "排序粒度": "", "提供商显示名,1~{max} 个字符": "プロバイダーの表示名、1~{max} 文字", "提供商:{name}": "プロバイダー:{name}", + "提高优先级": "優先度を上げる", "搜索": "検索", "搜索账号": "アカウントを検索", + "搜索账号 / 邮箱 / 备注": "アカウント / メール / メモを検索", "操作失败:{message}": "操作失敗:{message}", "收起": "折りたたむ", + "改动即时生效": "変更はすぐに反映されます", "改协议 / Base URL 会改变它们的转发方式,正在进行的请求可能失败;模型清单与映射在「模型管理」页。": "プロトコル / Base URL を変更すると転送方式が変わり、進行中のリクエストが失敗する可能性があります;モデル一覧とマッピングは「モデル管理」ページにあります。", "数值越小越先用;": "数値が小さいほど優先;", "数值越小越先用;{hint}": "数値が小さいほど優先;{hint}", + "整表": "", + "整表排序:厂商分组已散开,行里带厂商标记。此顺序仅用于查看,不写回调度队列 —— 写回会打破「同一家连续」的队列规则": "", + "整表排序:打散厂商分组,全表一起来(行里带厂商标记;仅用于查看,不写回队列)": "", + "新账号落到组首 / 组尾": "新しいアカウントを先頭 / 末尾に追加", "旗舰": "フラッグシップ", "无上限 · 已用 {used}": "上限なし · 使用済み {used}", "无上限按日计(上游到达上限后仍继续计数,超额如实显示)": "上限なしは日単位で集計(アップストリームが上限に達した後もカウントを継続し、超過分はそのまま表示)", @@ -329,24 +420,39 @@ "明天 {clock}": "明日 {clock}", "明天 {time}": "明日 {time}", "明细": "明細", + "是": "", "昵称 {name}": "ニックネーム {name}", "显示账号名": "アカウント名を表示", "暂无可查询余额的账号": "残高をクエリできるアカウントがありません", + "暂无数据:先查询一次余额,之后会自动按到期排序": "データがありません:一度残高を照会すると、以降は有効期限で自動的に並びます", "暂无账号,请点击右上角「添加账号」": "アカウントがありません。右上の「アカウントを追加」をクリックしてください", "更多操作": "その他の操作", + "更多设置": "その他の設定", "更新于 {time}": "更新日時 {time}", + "最先作废的那一份还剩多少(不是面额);按这一份的剩余量排序,缺失值恒排最后": "", + "最先作废的那一份还剩多少(不是面额:这一列回答「马上要作废掉多少」)": "", + "最快到期优先": "期限が近い順", + "最新添加优先": "新しい順", "最早 {soonest} 恢复": "最短リセット:{soonest}", "最早恢复": "最短リセット", + "最早添加优先": "古い順", + "最晚到期优先": "期限が遠い順", + "最近到期": "", + "最近到期积分": "", + "最近到期:{name}": "", + "最近到期:{name} · 这一份还剩 {value}(不是面额)": "", + "最近更新": "", "有效期": "有効期限", "有的账号过了 0 点额度就回来 —— 选「自然日」": "0 時を過ぎるとクォータが戻るアカウントもあります —— 「自然日」を選択", "未使用": "未使用", - "未分配端口": "ポート未割り当て", "未命名账号": "名称未設定のアカウント", "未查询": "未クエリ", "未检测到 Clash Verge": "Clash Verge が検出されません", "未检测到 Clash Verge 配置": "Clash Verge の設定が検出されません", + "未被使用": "未使用", "未触发{current}": "未発動{current}", "未触发{usage}{countdown}": "未発動{usage}{countdown}", + "未设备注名": "ラベル名未設定", "未返回余额数据": "残高データが返されません", "未返回可识别的余额数据": "識別可能な残高データが返されません", "未配置": "未設定", @@ -356,9 +462,12 @@ "本周期已用": "今周期の使用量", "本地台账里今天还领过这些,但上面的读数里还没有:{plans}(读数刷新可能有延迟)": "ローカル記録では本日ほかにも受け取っていますが、上の読み取り値にはまだ出ていません:{plans}(読み取り値の更新には時間がかかることがあります)", "本地领取于 {time}": "ローカルで {time} に受け取り", + "本组 {n} 个账号的可用余额之和,按整组统计(不受筛选、搜索影响)": "このグループ {n} 件の利用可能残高の合計(グループ全体で集計、絞り込み・検索の影響を受けません)", + "本组代理": "このグループのプロキシ", "本组操作": "グループ操作", "权益 {value}": "権益 {value}", "权益 {value} {unit}": "権益 {value} {unit}", + "条件即时生效,关掉面板就能看到结果。": "条件はすぐに反映されます。パネルを閉じると結果が表示されます。", "来源 {source}": "ソース {source}", "查看": "表示", "查看可领取的套餐与名下已有的套餐(每天一期,领取需过一次人机验证)": "受け取れるプランと保有中のプランを表示(毎日 1 回配布、受け取りには人機認証が 1 回必要)", @@ -374,12 +483,14 @@ "查询间隔": "クエリ間隔", "查询间隔必须是 {min} 秒 ~ 24 小时": "クエリ間隔は {min} 秒 ~ 24 時間でなければなりません", "桌面端实时登录态(凭证每次从客户端登录态文件读取)": "デスクトップ版のリアルタイムログイン状態(認証情報は毎回クライアントのログイン状態ファイルから読み込みます)", + "模型作用域下分组带随队列走,不折叠": "", "正在加载…": "読み込み中…", "正在探测可领取的套餐…": "受け取れるプランを確認しています…", "正在查询": "クエリ中", "正在读取 Clash Verge 配置…": "Clash Verge の設定を読み込み中…", "正在读取…": "読み込み中…", "正在读取代理列表…": "プロキシ一覧を読み込み中…", + "正在读取调度队列…": "", "正在连接上游…": "アップストリームに接続中…", "正常": "正常", "此刻正在使用这个账号的请求数(含还在下发内容的流式请求);为 0 时不显示": "現在このアカウントを使用しているリクエスト数(コンテンツ配信中のストリーミングリクエストを含む);0 のときは非表示", @@ -388,10 +499,13 @@ "每日消耗 ≥": "毎日の消費が", "每日消耗 ≥ {count} Token(每天 0 点重置)": "毎日の消費 ≥ {count} Token(毎日 0 時にリセット)", "每日重置": "毎日リセット", + "每日重置({time} 回满,不是作废)": "", "每日重置(额度按周期回满,不是作废)": "毎日リセット(枠は周期ごとに満額へ戻るだけで、失効ではありません)", "每条请求换一个出口(按顺序轮流),上游看到的 IP 因此是变化的;出口的地址与端口由「网络代理」页管理": "リクエストごとに次の出口へ順番に切り替わるため、上流から見える IP が変わります。出口のアドレスとポートは「ネットワークプロキシ」ページで管理します", + "没有匹配「{kw}」的账号": "「{kw}」に一致するアカウントはありません", "没有可全选的账号": "全選択できるアカウントがありません", "没有可用出口": "利用可能な出口がありません", + "没有将到期的积分(没有一次性额度包,或这一家不给逐份读数)": "", "没有选中的账号": "選択されたアカウントがありません", "活动套餐": "イベントプラン", "活动套餐的推理端点要求每条请求带一个阿里云验证码令牌;令牌由本应用在后台静默铸造,界面关闭时无法铸造": "イベントプランの推論エンドポイントは各リクエストに Alibaba Cloud の認証コードトークンを要求します;トークンは本アプリがバックグラウンドでサイレントに生成するため、画面を閉じている間は生成できません", @@ -400,7 +514,10 @@ "活动期内每天发一份新套餐;同一份当天重复领取会提示「已领取过」,次日可再领。领取要走一次人机验证(滑块)。": "キャンペーン期間中は毎日新しいプランが 1 件配布されます。同じプランを同日に再度受け取ると「受け取り済み」と表示され、翌日にまた受け取れます。受け取りには人機認証(スライダー)が 1 回必要です。", "测试中…": "テスト中…", "测试出口": "出口をテスト", + "添加 新→旧": "追加 新→旧", + "添加 旧→新": "追加 旧→新", "添加规则": "ルールを追加", + "添加账号请用桌面端或网页版面板": "", "添加限制": "制限を追加", "清掉本机的限流标记,立刻重新尝试该模型(上游若仍在限流会再次被标记)": "本機のレート制限マークを消去し、このモデルをすぐに再試行します(アップストリームがまだレート制限中なら再度マークされます)", "清掉该账号全部模型的限流标记": "このアカウントのすべてのモデルのレート制限マークを消去", @@ -413,7 +530,9 @@ "清除已配置的余额查询凭证": "設定済みの残高クエリ用認証情報を消去", "清除标记": "マークを消去", "清除筛选": "フィルタをクリア", + "点一下按这一列排序(升序 → 降序 → 恢复默认次序)": "", "点击{action}各模型的限流明细": "クリックで各モデルのレート制限明細を{action}", + "点击查看各模型的限流明细": "", "状态": "状態", "状态 {status}": "状態 {status}", "状态未知": "状態不明", @@ -429,7 +548,10 @@ "留空 = 不修改,清除用右边的按钮": "空欄なら現状を維持、クリアは右のボタンで", "登录 / 导入一个新账号": "ログイン / 新しいアカウントをインポート", "直连": "直接接続", + "确定删除账号「{name}」?这个操作不可恢复。": "", "确定删除选中的 {count} 个账号?此操作不可恢复,账号的登录态会一并移除。": "選択した {count} 個のアカウントを削除しますか?この操作は元に戻せません。アカウントのログイン状態も併せて削除されます。", + "确定清除全部模型的限流标记?": "すべてのモデルのレート制限を解除しますか?", + "确定清除模型「{model}」的限流标记?": "モデル「{model}」のレート制限を解除しますか?", "禁用": "無効化", "禁用该账号(需手动重新启用)": "このアカウントを無効化(手動での再有効化が必要です)", "禁用这一组里的全部账号(含被筛选隐藏的)": "このグループの全アカウントを無効化します(フィルタで非表示のものも含む)", @@ -438,6 +560,7 @@ "福利网关未读到:{error}": "特典ゲートウェイから読み取れません:{error}", "秒": "秒", "积分": "積分", + "积分到期时间": "クレジット有効期限", "积分包": "積分包", "积分明细": "積分明細", "积分查询已复用转发用的登录凭证,这里通常留空即可。只有旧版本填过、或从旧代理导入过凭证时才有值": "積分クエリは転送用のログイン認証情報を再利用するため、通常は空欄のままで構いません。旧バージョンで入力した場合、または旧プロキシから認証情報をインポートした場合のみ値が入ります", @@ -446,11 +569,17 @@ "窗口内已用 {used} / {limit} Token,转发时会跳过该账号(重置后自动恢复)": "ウィンドウ内で {used} / {limit} Token 使用済みのため、転送時にこのアカウントをスキップします(リセット後は自動復帰)", "窗口对齐自然时间(整点 / 整 N 分钟一轮回),重置即清零恢复;按账号在 requests 表聚合": "ウィンドウは自然時間に整列(毎時 / N 分ごとの周期)、リセットでゼロに戻り復帰;アカウントごとに requests テーブルで集計", "端口": "ポート", + "端口准备中": "ポート準備中", "第 {rank} 顺位": "全体 {rank} 番", + "策略 {strategy} · 会话粘性 {sticky}": "", + "筛选": "絞り込み", "筛选 / 搜索 / 分页生效时组顺位不可拖动(别家的组整组不可见,落点没法定位)": "フィルター / 検索 / ページング中はグループ順位をドラッグできません(他社のグループ全体が非表示のため、ドロップ位置を特定できません)", "级联删除名下全部账号,不可恢复": "配下のすべてのアカウントを連鎖削除、復元不可", "组内": "", "组内先后:按住左边的把手上下拖动即可调整;整组的先后拖分组带的把手": "グループ内の順序:左のハンドルを上下にドラッグで調整できます。グループ全体の順序はグループバーのハンドルをドラッグしてください", + "组内并发上限": "グループ内の同時実行上限", + "组内手动顺序": "グループ内の手動順序", + "组内排序:只在每一家内部重排,分组与组序不变(组内先后写回队列)": "", "组内第 {rank} 位": "グループ内 {rank} 番", "组顺位已锁定": "グループ順位はロック中", "组顺位:越小越先用(拖动分组带调整)": "グループ順位:小さいほど先に使われます(グループバーをドラッグして調整)", @@ -459,6 +588,7 @@ "编码套餐(Coding Plan)— 走开放平台端点,用自己订阅的额度": "コーディングプラン(Coding Plan)— オープンプラットフォームのエンドポイントを使用し、自身で購読したクォータを消費", "编辑": "編集", "编辑限制": "制限を編集", + "缺失": "", "网络": "ネットワーク", "自动查询余额": "残高を自動クエリ", "自动查询余额(按下面的间隔自动刷新余额列的读数)": "残高を自動クエリ(下の間隔で残高列の読み取り値を自動更新)", @@ -467,11 +597,14 @@ "自定义:": "カスタム:", "自然日(每天 0 点)": "自然日(毎日 0 時)", "表格": "", + "被跳过:这一轮不会用到它,所以没有位次": "", "视图": "", "解析失败": "解析失敗", "触发动作": "発動アクション", "触发后": "発動後", "触发条件": "発動条件", + "计入 {counted}/{total}": "集計 {counted}/{total}", + "订阅已移除": "サブスク削除済み", "订阅统计未读到:{error}": "サブスクリプション統計を読み取れません:{error}", "记录里没有过期时间": "記録に有効期限がありません", "设为首选": "最優先に設定", @@ -481,6 +614,8 @@ "该上游无需鉴权": "このアップストリームは認証不要", "该上游无需鉴权(转发与拉取模型都不发送鉴权头)": "このアップストリームは認証不要(転送とモデル取得のいずれも認証ヘッダーを送信しません)", "该提供商没有余额查询": "このプロバイダーには残高クエリがありません", + "该提供商没有余额查询,因此读不到积分包": "", + "该提供商没有余额查询,因此读不到积分包的到期时刻": "", "该提供商的推理转发尚未接入,账号不参与转发": "このプロバイダーの推論転送は未対応のため、アカウントは転送に参加しません", "该账号出网走的代理(Clash 出口 / 自定义 / 直连);点击可修改": "このアカウントの送信に使うプロキシ(Clash 出口 / カスタム / 直接接続);クリックで変更", "该账号当前限流中的模型;点徽章看明细": "このアカウントで現在レート制限中のモデル;バッジをクリックすると明細を表示", @@ -488,6 +623,7 @@ "该账号没有套餐登录态(jwt),活动套餐会失败:请重新登录该账号": "このアカウントにはプランのログイン状態(jwt)がないため、イベントプランは失敗します:このアカウントに再ログインしてください", "该账号没有福利模型额度(福利按活动下发,不是每个账号都有)": "このアカウントには特典モデルのクォータがありません(特典はイベントで配布されるため、すべてのアカウントにあるわけではありません)", "该账号没有积分类读数(额度只有福利 token 池)": "このアカウントには積分系の読み取り値がありません(クォータは特典 token プールのみ)", + "该账号的登录态已被上游注销,自动刷新不会再成功,请重新登录该账号": "", "该账号的额度只有福利 token 池(见余额格那两行),订阅统计没有积分类计量表": "このアカウントのクォータは特典 token プールのみ(残高セルの 2 行を参照)。サブスクリプション統計には積分系のメーターがありません", "请先勾选要操作的账号": "操作するアカウントを先にチェックしてください", "请先选择 Clash Verge 出口": "先に Clash Verge の出口を選択してください", @@ -502,21 +638,27 @@ "读取中…": "読み込み中…", "读取失败:{error}": "読み込み失敗:{error}", "读取自 {dir};端口由 Clash Verge 管理,这里实时同步": "{dir} から読み込み;ポートは Clash Verge が管理し、ここはリアルタイムで同期されます", + "读取调度队列失败:{error}": "", "读数来自自动余额查询;与余额列同一数字口径,严格小于才触发(等于阈值仍可用)": "読み取り値は自動残高クエリによるもの;残高列と同じ数値基準で、厳密に下回った場合のみ発動(しきい値と同じなら利用可能)", "读数里没有套餐清单(免费账号、或套餐的上游状态还没刷新;可点右上角「查询余额」重读)": "この読み取り値にプラン一覧がありません(無料アカウント、または上流のプラン状態がまだ更新されていません。右上の「残高を照会」で再取得できます)", "读数里没有额度桶,这里按套餐权益里的已用 / 总量显示(上游给的数)": "読み取り値に枠(バケット)がないため、プラン権益の已用 / 総量(上流の数値)を表示しています", + "调度队列:{n} 个可用 · 跳过 {m} 个": "", + "调整到全局队列第一位": "グローバルキューの先頭に移動します", "调整顺序失败:{message}": "順序の調整失敗:{message}", "账号": "アカウント", + "账号 ID": "", "账号不存在,请刷新后重试": "アカウントが存在しません。更新して再試行してください", "账号不存在,请刷新后重试。": "アカウントが存在しません。更新して再試行してください。", "账号名 / 邮箱 / 备注": "アカウント名 / メール / メモ", "账号已保存,但提供商未更新:{error}": "アカウントは保存されましたが、プロバイダーが更新されていません:{error}", "账号显示名称": "アカウントの表示名", + "账号添加时间": "アカウント追加日時", "账号表": "アカウントテーブル", "账号视图": "", "账号设置": "アカウント設定", "账号设置 · {name}": "アカウント設定 · {name}", "账号设置分组": "アカウント設定グループ", + "账号详情": "", "账号邮箱": "アカウントのメールアドレス", "跳过该账号(余额回升自动恢复)": "このアカウントをスキップ(残高の回復で自動復帰)", "跳过该账号(自动恢复)": "このアカウントをスキップ(自動復帰)", @@ -529,13 +671,24 @@ "轮询的代理": "ローテーションするプロキシ", "轮询至少要选择一条代理(或改选「全部」)": "ローテーションには少なくとも 1 つのプロキシを選択してください(「すべて」に戻すこともできます)", "轮询(换 IP)": "ローテーション(IP 切り替え)", + "还有 {d} 天": "", + "还有 {h} 小时": "", + "还有 {h} 小时 {m} 分": "", + "还有 {n} 分钟": "", "还没有代理(去「网络代理」页添加)": "プロキシがまだありません(「ネットワークプロキシ」ページで追加)", "还没有余额读数(已自动查询一次,稍候;也可以点右上角「查询余额」重读)": "まだ残高の読み取り値がありません(自動で 1 回照会済みです。しばらくお待ちいただくか、右上の「残高を照会」で再取得してください)", "还没有保存的代理": "保存済みのプロキシがまだありません", + "还没有账号,去桌面端添加": "アカウントがありません。デスクトップ版で追加してください。", "还没有限制规则:账号照常参与转发,余额与 Token 消耗都不设上限。": "制限ルールがまだありません:アカウントは通常どおり転送に参加し、残高と Token 消費に上限はありません。", "这一份今天已经领过了(重复领取上游会回「已领取过」)": "このプランは本日すでに受け取り済みです(再度受け取ると上流は「受け取り済み」を返します)", + "这一份还剩多少:{name} · 上游没给剩余量": "", + "这一家在这个模型上的倍率(与模型管理页同一份数据)": "", + "这一家查不到积分到期时间": "このプロバイダーではクレジット有効期限を取得できません", "这一栏改的是": "この欄で変更するのは", "这一段按提供商出现:有什么配什么。": "このセクションはプロバイダーごとに表示され、設定できる項目だけが現れます。", + "这一组开着排序规则:关掉规则后才能手动拖": "このグループは並べ替えルールが有効です。手動でドラッグするにはルールをオフにしてください", + "这个模型的队列里没有账号": "", + "这个模型队列里第 {rank} 位": "", "这个账号的余额走窗口限额通道(只配了编码套餐 API Key):只有每 N 小时的剩余比例,读不到套餐清单": "このアカウントの残高はウィンドウ上限チャネル経由です(コーディングプランの API Key のみ設定)。N 時間ごとの残り割合のみで、プラン一覧は取得できません", "这些分组里一个启用的账号都没有(禁用不参与转发),默认不显示;点一下切到「禁用」查看,也就能重新启用它们": "", "这会{what}所有启用中的账号,转发将不可用。确定继续?": "これは有効中のすべてのアカウントを{what}します。転送が利用できなくなります。続行しますか?", @@ -547,32 +700,46 @@ "连接数": "接続数", "选择": "選択", "选择要轮询的代理(可多选)": "ローテーションするプロキシを選択(複数可)", + "逐份明细见「余额」格的积分明细": "", "逐请求轮换出口(换 IP):OpenCode Zen 免费档按 IP 限流,单个 IP 的额度用完会对它一律 429": "リクエストごとに出口を切り替えます(IP 変更):OpenCode Zen の無料枠は IP 単位で制限され、ある IP の枠を使い切るとその IP からのリクエストは一律 429 になります", + "邮箱": "", "邮箱 {email}": "メール {email}", + "重新算一次这个模型的调度队列(限流冷却 / 并发释放后队列会变)": "", "重新读取": "再読み込み", "重新读取该账号的余额与套餐(走余额查询那条链)": "このアカウントの残高とプランを再取得(残高照会の経路を使用)", + "重置": "リセット", "重置 {date}": "リセット {date}", "重置也不恢复,需手动启用": "リセットしても復帰せず、手動での有効化が必要", "重置周期必须是 30 分钟 ~ 24 小时": "リセット周期は 30 分 ~ 24 時間でなければなりません", "重置方式": "リセット方式", + "长期有效": "長期有効", "间隔单位": "間隔単位", "阈值": "しきい値", + "降低优先级": "優先度を下げる", "限制器": "リミッター", "限制器:{window}已用 {used} / {threshold} Token": "リミッター:{window}使用済み {used} / {threshold} Token", "限制类型": "制限タイプ", "限制阈值必须是大于 0 的数字": "制限のしきい値は 0 より大きい数値でなければなりません", "限流": "レート制限", "限流中": "レート制限中", + "限流已清除": "レート制限を解除しました", + "限流明细": "", + "限流模型": "", "限额": "上限", "限额状态": "上限ステータス", "隐藏账号名(名字显示为星号)": "アカウント名を隠す(名前はアスタリスクで表示)", + "需要重新登录": "再ログインが必要", + "需重登": "再ログイン", "顺序": "順序", + "顺序已锁定": "", "顺序未保存:{message}": "順序を保存できませんでした:{message}", "领取 {time}": "受け取り {time}", "领套餐": "プランを受け取る", "额度": "枠", "额度窗口读数来自开放平台监控通道(套餐 JWT 那条通道没有额度桶):读的是每 N 小时 / 每周窗口的剩余比例": "枠(ウィンドウ)の読み取り値はオープンプラットフォームの監視チャネルから取得しています(コーディングプランの JWT チャネルには枠がありません)。N 時間ごと / 週ごとのウィンドウの残り割合を読んでいます", "验证码令牌:{counts}": "認証コードトークン:{counts}", + "默认不排序:组内顺序保持你拖出来的样子。需要自动排序时,勾选上面的规则即可。": "既定では並べ替えません:グループ内の順序はドラッグしたままになります。自動で並べ替えたいときは上のルールにチェックを入れてください。", + "默认展开 / 收起": "既定の展開 / 折りたたみ", "({n} 个未配置查询凭证)": "({n} 個はクエリ用認証情報が未設定)", "({plans})": "({plans})", "(不可用)": "(使用不可)", @@ -587,13 +754,5 @@ ",只对 OpenCode Zen 这类上游有意义,别的家请关掉。": "、OpenCode Zen のようなアップストリームでのみ意味があります。他のプロバイダーではオフにしてください。", ",已达到上限": "、上限に達しました", ":到「网络代理」页的「订阅管理」里点「同步 Clash Verge」把出口导进池后即可选 —— 那样能集中测试、也能被多个账号共用。": ":「ネットワークプロキシ」ページの「サブスク管理」で「Clash Verge を同期」をクリックして出口をプールにインポートすると選択できます —— そうすれば一括でテストでき、複数のアカウントでも共用できます。", - ";": ";", - "WorkBuddy 国内版": "WorkBuddy 中国版", - "WorkBuddy 国际版": "WorkBuddy 国際版", - "小浣熊": "小浣熊", - "Qoder 中国版": "Qoder 中国版", - "Qoder 国际版": "Qoder 国際版", - "Accio 国内版": "Accio 中国版", - "ZCode 国内版": "ZCode 中国版", - "ZCode 国际版": "ZCode 国際版" + ";": ";" } diff --git a/desktop-tauri/i18n/i18n-src/ja/add-account.json b/desktop-tauri/i18n/i18n-src/ja/add-account.json index 3f12ea03..4b5789af 100644 --- a/desktop-tauri/i18n/i18n-src/ja/add-account.json +++ b/desktop-tauri/i18n/i18n-src/ja/add-account.json @@ -10,6 +10,7 @@ "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "Anthropic 互換エンドポイントは https://api.deepseek.com/anthropic です(プロトコルを anthropic に切り替えるときにこれを入力)", "Anthropic 协议填根地址,不要带 /v1": "Anthropic プロトコルではルートアドレスを入力し、/v1 は付けないでください", "Antigravity": "Antigravity", + "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw 中国版", "AutoClaw 国际版": "AutoClaw 国際版", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS は必須です;**完全な Cookie** を貼り付けてください(Cookie エディターのワンクリック書き出しで構いません)—— このうち **PTOKEN は必ず残してください**。ゲートウェイはこれを使って Baidu から KukuAI の業務セッショントークンを発行してもらうため、欠けるとモデル更新時に「未ログイン」と表示されます。Cookie ヘッダー全体、Cookie エディターが書き出した JSON 配列、または {BDUSS, STOKEN} オブジェクトに対応しています。KukuAI には更新の仕組みがないため、ログイン状態の期限が切れたら貼り直せば済みます。", @@ -18,19 +19,35 @@ "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Cline Pass の API Key は app.cline.bot で取得します(サブスクリプション制)。エンドポイントは OpenAI 互換で、モデル一覧は Cline コンソールが正となります。モデル名はモデル管理で接頭辞なしの名前のまま追加します(例:glm-5.2 / kimi-k2.7-code / deepseek-v4-pro)。上流はクライアントの形態を検証するため、必要なクライアントリクエストヘッダーはこのカードにあらかじめ設定されています", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT(3 段のドット区切り)", "Command Code": "Command Code", + "Coze 扣子": "", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT(ドット区切り 3 パート。Bearer 行全体や data.bin の中身を貼っても可)", + "JWT(三段点分;也可直接粘贴 Bearer 整行)": "JWT(ドット区切り 3 パート;Bearer の行をそのまま貼り付けても可)", + "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT(3 つのドット区切り部分。chatglm_token=… の行をそのまま貼っても可)", + "LobsterAI": "LobsterAI", + "MiniMax Code": "MiniMax Code", + "MiniMax Code 国内版": "MiniMax Code(中国版)", + "MiniMax Code 国际版": "MiniMax Code(国際版)", + "ModelScope 国内版": "ModelScope 中国版", + "ModelScope 国际版": "ModelScope 国際版", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode 国際版", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM のマネージドエンドポイント。Key は integrate.api.nvidia.com で申請します", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken は Accio のログイン認証情報です(長い不透明な token 文字列で、JWT ではありません);refreshToken は任意で、入力すると期限切れ時に自動更新できます。{label}アカウントの認証情報を入力してください —— {siteNote}(最も簡単な方法:上の「ウェブログイン」を使えば token を手動で探す必要はありません。)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken は Trae の Cloud-IDE-JWT(3 段のドット区切り)です。refreshToken は期限切れ時の自動更新に使用します(本家の refreshToken は 1 回ごとに交換され、一度更新すると古いものは無効になります。そのため 2 つのプログラムで同じアカウントを同時に更新しないでください)。
手動で貼り付ける場合は machineId / deviceId も一緒に入力してください:アップストリームはこれらをログイン時にアップロードされたデバイス公開鍵と紐付けてデバイスを判定するため、根拠なく別のペアにすると 2xxxx 系のデバイスバインディング拒否に当たります。
この 3 つがない場合の正道は上の「ウェブログイン」を使うことです;コンテナ / リモートデプロイ形態では案内に従ってアドレスバーのコールバックアドレスを貼り付けてください。", + "mmoat_… 或客户端 auth.json 里的 accessToken 值": "", + "mmoat_…(国际站 account.minimax.io 签发)": "", + "mmort_…(可选;有它才能自动续期)": "", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "monkeycode_ai_session の値(Cookie 全体をそのまま貼り付けても可)", + "ms- 开头的 API 令牌": "ms- で始まる API トークン", + "openclaw.json 里 gateway.auth.token 的值": "", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken は任意で、入力すると期限切れ時に自動更新できます;どちらも小浣熊クライアントのログイン状態ファイルから取得できます。", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken は任意で、入力すると自動更新できます;どちらもクライアントのログイン状態ファイルから取得できます。同じ Cline アカウントはどちらのプールでも使え、ここでは {label} に分類されます。", + "sessionid_ss=…; sid_guard=…; ttwid=…(或 Cookie 编辑器导出的 JSON)": "", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "token は enc: プレフィックスに対応しています(Windows では自動復号)。refreshToken を入力しないと自動更新できません。", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "token はログイン状態 Cookie です;CatPaw には更新の仕組みがないため、期限切れ後はクライアントで再ログインが必要です。", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "user_ で始まる API Key(Authorization: Bearer … の行をそのまま貼り付けても可)", + "{\"access_token\":\"mv_…\",\"openid\":\"oAN1…\",\"guid\":\"a79a…\"}(整段粘贴)": "", "{label} 网页登录的打开方式": "{label} のウェブログインの開き方", "{label} 账号地区": "{label} アカウントの地域", "{label} 账号的添加方式": "{label} アカウントの追加方法", @@ -39,6 +56,7 @@ "{n} 个账号": "{n} 個のアカウント", "{n} 个账号,点击添加": "{n} 個のアカウント、クリックして追加", "{seconds}s 后重发": "{seconds} 秒後に再送", + "⚠️ {label}账号已添加,但探活未通过:{message}": "", "✅ {label}账号已添加": "✅ {label} アカウントを追加しました", "✅ {label}账号已添加{name}": "✅ {label} アカウントを追加しました{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ cc-switch から {n} 個のプロバイダーをインポートしました", @@ -58,23 +76,30 @@ "不是有效的凭证文件:需要包含 accessToken 与 uid": "有効な認証情報ファイルではありません:accessToken と uid が必要です", "不是自定义提供商": "カスタムプロバイダーではありません", "不需要 Key:转发与拉取模型都不发送鉴权头": "Key は不要:転送とモデル取得のいずれも認証ヘッダーを送信しません", + "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "ログイン不要です。下のボタンを押すと、ゲートウェイが智谱清言からゲスト資格情報を直接取得してアカウント一覧に追加します(先にメモ名を入力しても構いません)。ゲスト資格情報はそのまま会話に使え、無料枠は上流がデバイス単位で発行します。

注意:ゲストアカウントにはポイント体系がありません。会員ポイント・毎日ログインポイント・アクティビティ受け取りの各インターフェースはすべてログインが必要なため、これらの機能はゲストではスキップされます(アカウントページのポイントや签到センターには数値が表示されません)。ポイントが必要な場合は「資格情報を入力」でログイン済みアカウントのトークンを貼り付けるか、「デスクトップのログイン状態をインポート」してください。", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "両版のアカウントは同時に保存でき、それぞれ独自のモデルカタログを持ちます(中国版と国際版は別々のプロバイダーで、個別に有効化とマッピングができます)。", "中国版": "中国版", "今天没有需要领的活动(都已到账)": "本日受け取る必要のあるイベントはありません(すべて付与済み)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
": "本日(北京時間 {day})は {attempts} 回試行済み、あと {left} 回試せます{next}
", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "別のマシン(または OfficeAce デスクトップ版の models.json)から次の 3 項目を取り出して貼り付けてください:モデルゲートウェイのベース URL(baseUrl、https://modelgw-…/v2 の形式)と modelAppKey / modelAppSecret。この 3 項目だけで転送できます(ゲートウェイ認証情報は無期限)。コントロールプレーンの一時認証情報(accessKeyId など)は任意で、空欄の場合は残高照会と自動更新が使えませんが、転送には影響しません。", + "从本机 MiniMax Code 客户端读取国际版登录态(只读,不会改动客户端)": "", + "从本机 MiniMax Code 客户端读取登录态(只读,不会改动客户端)": "", "从本机导入桌面端登录态": "本機からデスクトップ版のログイン状態をインポート", + "从本机马维斯客户端读取登录凭证(需要客户端已登录;会弹一次管理员授权)": "", + "令牌当前不可用": "", "任务镜像 id": "タスクイメージ id", "余额查询": "残高照会", "使用 Google 账号登录": "Google アカウントでログイン", "使用 Zai 账号登录": "Zai アカウントでログイン", "使用个人访问令牌(PAT)": "個人アクセストークン(PAT)を使用", + "先用管理员身份运行 _recon/marvis-cred-extract.py(从已登录的马维斯客户端内存里提取),然后把输出的 JSON 整段粘贴进来。需要三项:accessToken(mv_ 开头)、openid、guid。令牌没有续期接口,过期后重新提取一次即可(会更新同一条账号记录)。": "", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "無料モデルでは Key の入力は不要です(既定で「このアップストリームは認証不要」がチェックされ、ゲートウェイは自動的に匿名認証情報を使用します);有料モデルでは Zen の API Key を入力します。無料枠のアップストリームは公式クライアントの形態しか受け付けないため、「OpenCode 公式クライアントの偽装」が既定で有効になっています", "内嵌窗口": "内蔵ウィンドウ", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "内蔵ウィンドウで開きます;ウィンドウを閉じると待機をキャンセルします(Google アカウントが拒否された場合はシステムブラウザをお使いください)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "内蔵ウィンドウで開きます;完了後は自動でリストに追加され、ウィンドウを閉じると待機をキャンセルします", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "内蔵ウィンドウで開きます。読み取りが完了すると自動で一覧に追加され、ウィンドウを閉じると待機を中止します", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "内蔵ウィンドウは新しい環境を使うため、複数のアカウントが互いに影響しません;ウィンドウを閉じると待機をキャンセルします", + "内嵌窗口(必须)": "", "内嵌窗口(推荐)": "内蔵ウィンドウ(推奨)", "准备验证…": "認証を準備中…", "凭据 JSON": "認証情報 JSON", @@ -91,29 +116,38 @@ "取消失败:{message}": "キャンセルに失敗しました:{message}", "取消等待": "待機をキャンセル", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "「内蔵ウィンドウ」でのみサードパーティの入り口を復元できます:システムブラウザではログインページを変更できないためです", + "只有在「导入本机登录态」不可用时才需要:以管理员身份运行 _recon/marvis-cred-extract.py,把生成的 JSON 整段粘贴进来。需要三项:accessToken(mv_ 开头)、openid、guid。令牌没有续期接口,过期后重新导入一次即可(会更新同一条账号记录)。": "", "可选": "任意", "可选(cloudaicompanionProject):留空由网关自动发现": "任意(cloudaicompanionProject):空欄の場合はゲートウェイが自動検出します", + "可选,仅当令牌里读不出用户 id 时需要": "任意。トークンからユーザー id を読み取れない場合のみ必要", "可选,展示用": "任意、表示用", "可选,没有则无法自动续期": "任意、ないと自動更新できません", + "可选,用于生成账号标识": "任意。アカウント識別子の生成に使用", "可选,留空使用凭据里的 user_name": "任意、空欄の場合は認証情報の user_name を使用", "可选,留空使用账号姓名、邮箱或令牌指纹": "任意、空欄の場合はアカウントの氏名、メールアドレス、またはトークンフィンガープリントを使用", "可选,留空使用账号昵称或邮箱": "任意、空欄の場合はアカウントのニックネームまたはメールアドレスを使用", "可选,留空则用 key 尾号": "任意、空欄の場合は key の末尾を使用", "可选,留空则用 userId": "任意、空欄の場合は userId を使用", + "可选,留空则用上游账号昵称": "任意・空欄なら上流のアカウント名を使用", "可选,留空则用上游返回的账号名或账号 id": "任意、空欄の場合はアップストリームが返したアカウント名またはアカウント id を使用", + "可选,留空则用令牌尾号": "", "可选,留空则用凭证里的账号名": "任意、空欄の場合は認証情報のアカウント名を使用", "可选,留空则用文件里的昵称": "任意。空欄の場合はファイル内のニックネームを使用", "可选,留空则用登录名或 uid": "任意、空欄の場合はログイン名または uid を使用", "可选,留空则用脱敏手机号": "任意、空欄の場合はマスク済み電話番号を使用", "可选,留空则用脱敏手机号或 userId": "任意、空欄の場合はマスク済み電話番号または userId を使用", "可选,留空则用账号 uk": "任意、空欄の場合はアカウントの uk を使用", + "可选,留空则用账号昵称": "任意。空欄ならアカウントのニックネームを使用", "可选,留空则用账号昵称或 userId": "任意。空欄の場合はアカウントのニックネームまたは userId を使用", "可选,留空则用邮箱或令牌尾号": "任意、空欄の場合はメールアドレスまたはトークンの末尾を使用", + "可选,留空用「AtomCode」": "任意。空欄なら「AtomCode」", "可选,留空用「OfficeAce 果办」": "任意、空欄の場合は「OfficeAce」を使用", "可选,留空自动生成": "任意、空欄の場合は自動生成", "可选,留空自动用昵称": "任意、空欄の場合はニックネームを自動使用", + "可选,给这个访客账号起个备注名": "任意。このゲストアカウントのメモ名", "可选,续期时带上": "任意、更新時に一緒に送信されます", "可选,讯飞侧 userid": "任意、iFlytek 側の userid", + "可选,默认 9434": "", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "任意;会話の開始に必須です。空欄の場合はゲートウェイがこのアカウントの既存タスクから自動検出します。見つからない場合は後で手動で補ってください。", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "任意;アカウントのメールアドレスで、表示と身元識別に使用します(新しいトークンに替えても同じアカウントとして認識されます)", "可选;UUID 形态,与凭据同生共死": "任意;UUID 形式で、認証情報と一体で管理されます", @@ -124,6 +158,7 @@ "可领取": "受け取り可能", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "同じプロバイダーに API Key ありのアカウントと認証不要アカウントを混在でき、それぞれの規則でルーティングされます。認証不要アカウントも複数併存でき、それぞれ別の送信プロキシを設定できます", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "同じアドレスでプロトコルを anthropic に変えると、その Claude 互換エンドポイントを使用します。転送には OpenCode Go の API Key が必須です(サブスクリプション制;モデルカタログの取得に Key は不要ですが、リクエストを送るとアップストリームが 401「Missing API key」を返します)", + "后端没有返回授权地址(登录发起失败)": "", "响应为空": "応答が空です", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "SenseNova の OpenAI 互換エンドポイント。残高照会はコンソールのアカウント / パスワードを使います(下の「残高照会」の節を参照)", "国内版": "中国版", @@ -141,11 +176,16 @@ "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "国際版の推論サイトは api.z.ai で、中国版とは別のサイトです;プランもそれぞれ独立しています。", "国际版(WorkBuddy AI)": "国際版(WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "国際版サイトの Coding プラン;OpenAI 互換アドレスは https://api.minimax.io/v1", + "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "AtomGit の AtomCode ページから accessToken と refreshToken の 2 つを取得して貼り付けてください。リフレッシュトークンの入力推奨 —— このプロバイダーのトークンは JWT ではなく(約 7 日で失効し、失効時刻も読み取れません)、これが無いと自動更新できず、失効後は再ログインが必要になります。", "在 cc-switch 里找到": "cc-switch で", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "内蔵ウィンドウで公式ログインを完了してください。成功すると自動でアカウント一覧に追加されます。", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "{label} の Qoder アカウント設定 → Integrations で PAT を生成してください(Google / GitHub のトークンは入力しないでください)。2 つの地域のアカウントと認証情報は共用できません。", - "在打开的官方 Web 控制台里用微信扫码登录(窗口会自动弹出登录面板;若没有,点左下角头像 → 立即登录)。登录成功后自动加入账号列表。": "開いた公式 Web コンソールで WeChat の QR コードを読み取ってログインします(ログインパネルは自動で開きます。開かない場合は左下のアバターをクリックし、「ログイン」を選んでください)。ログインが完了すると自動でアカウント一覧に追加されます。", + "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "開いた公式認可ページで WeChat の QR コードを読み取ってログインします(ページに QR コードが直接表示されます)。読み取りに成功すると自動でアカウント一覧に追加されます。", + "在打开的官方登录页里完成登录,成功后自动加入账号列表。": "開いた公式ログインページでログインすると、成功後に自動でアカウント一覧に追加されます。", + "在打开的登录页里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "開いたログインページで Baidu アカウントを使って KukuAI 本サイトにログインします(SMS コード / QR スキャンのどちらも可)。成功後は自動でアカウント一覧に追加されます。ゲートウェイは Baidu と KukuAI 業務セッショントークンを交換します(本機に庫庫AI クライアントのインストールが必要)。", + "在打开的登录页里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "開いたログインページ(Antigravity の認証ページ)で Google アカウントにログインすると、認証完了後に自動でアカウント一覧に追加されます。", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "開いたウィンドウで Baidu アカウントを使って KukuAI 本体サイトにログインしてください(SMS 認証コード / QR コードのいずれも可)。ログインに成功すると自動でアカウント一覧に追加されます。ゲートウェイは Baidu から KukuAI の業務セッショントークンを発行してもらいます(本機に KukuAI クライアントがインストールされている必要があります)。", + "在打开的窗口里登录 Coze(抖音账号 / 微信扫码 / 手机号均可),登录成功后自动加入账号列表。请用内嵌窗口:系统浏览器里网关读不到登录窗口的 Cookie。": "", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "開いたウィンドウで Google アカウントにログインしてください(Antigravity の認可ページ)。認可が完了すると自動でアカウントリストに追加されます。", "在浏览器中打开登录页": "ブラウザでログインページを開く", "地区": "地域", @@ -165,6 +205,8 @@ "导入所选": "選択項目をインポート", "导入所选({n})": "選択項目をインポート({n})", "导入桌面端登录态": "デスクトップ版のログイン状態をインポート", + "将在新标签页中打开授权页;完成后自动加入账号列表。": "新しいタブで認証ページを開きます。完了後は自動でアカウント一覧に追加されます。", + "将在新标签页中打开授权页;完成后自动加入账号列表。若页面跳回本机地址后账号没有出现,按提示把地址栏里的完整回调地址粘贴回来即可。": "新しいタブで認証ページを開きます。完了後は自動でアカウント一覧に追加されます。ローカルアドレスに戻った後もアカウントが表示されない場合は、プロンプトに従ってアドレスバーの完全なコールバック URL を貼り付けてください。", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "内蔵ウィンドウを開きます;認可が完了すると自動でアカウント一覧に追加されます。ウィンドウを閉じると待機をキャンセルします。認可ページの最後に zcode:// リンクを開けない旨の表示が出ることがありますが、これは正常です —— 結果はサーバー側で記録済みです", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "内蔵ウィンドウを開きます;認可が完了すると自動でアカウント一覧に追加されます。リモートパネルではコールバックアドレスを貼り付けられます。ウィンドウを閉じると待機をキャンセルします。リンクの有効期限は 5 分で、タイムアウトまたは未クリックの場合は無効になります(再発行できます)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "内蔵ウィンドウを開きます;ログイン完了後は自動でアカウント一覧に追加されます。ウィンドウを閉じると待機をキャンセルします", @@ -173,6 +215,7 @@ "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの Huawei Cloud アカウントが再利用されます);認可コードはゲートウェイがクラウドをポーリングして取得するため、ブラウザがどのマシンで動いていても関係ありません。完了後は自動でアカウント一覧に追加されます", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの Huawei Cloud アカウントが再利用されます);ブラウザとゲートウェイが別のマシンにある場合は、上の説明に従って最終的なコールバックアドレスをパネルに貼り付けてください", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "システムの既定ブラウザでログインページを開きます(ブラウザでログイン済みの Accio アカウントが再利用されます);ログイン完了後は自動でアカウント一覧に追加され、ポップアップを閉じると待機をキャンセルします", + "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "システムの既定ブラウザでログインページを開きます(ブラウザでログイン済みの LobsterAI アカウントが再利用されます);ログイン完了後は自動でアカウント一覧に追加され、ポップアップを閉じると待機をキャンセルします", "小米 MiMo Token Plan 套餐(新加坡站)": "小米 MiMo Token Plan プラン(シンガポールサイト)", "已到账": "付与済み", "已取消登录等待": "ログイン待ちをキャンセルしました", @@ -187,24 +230,32 @@ "已点发送,等待短信…": "送信しました。SMS を待っています…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "システムの既定ブラウザでログインページを開きました。ブラウザでログインを完了してください…", "已领取,等官方确认": "受け取り済み、公式の確認待ち", + "开始登录(获取设备码)": "", + "当前壳不支持直接打开浏览器,请手动访问:{url}": "", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "現在のシェルバージョンは AutoClaw のウェブログインに対応していません。アプリを更新してください", "当前没有可领取的套餐(活动尚未开始)": "現在受け取り可能なプランはありません(イベントはまだ開始されていません)", "当前环境不支持领取(桥接方法缺失)": "現在の環境では受け取りに対応していません(ブリッジメソッドがありません)", "必填,CatPaw 账号标识": "必須、CatPaw アカウントの識別子", "恢复 Google / GitHub 入口": "Google / GitHub の入り口を復元", + "手工粘贴凭证": "", "手机号": "電話番号", "手机验证码登录": "SMS 認証コードログイン", + "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "公式の LobsterAI ログインページ(lobsterai.youdao.com)を開き、LobsterAI アカウントでログインしてください:ログインに成功すると公式ページが本機に戻り、自動でアカウント一覧に追加されます —— コールバックはゲートウェイ自身のポートに届くため、ブラウザはゲートウェイと同じマシンで開く必要があります。認証コードは本機内でゲートウェイに渡されるだけで、画面に平文トークンは表示されません。", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Trae SOLO の公式認可ページ(trae.cn)を開き、あなたの Trae アカウントでログインしてください:認可が完了すると公式ページから本機の一時ポートにリダイレクトされます;同一マシンでのデプロイでは自動受信し、Docker / リモートパネルでは案内に従ってアドレスバーのコールバックアドレスをコピーしてください。", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Accio {label}の公式ログインページ({site})を開き、あなたの Accio アカウントでログインしてください:ログインに成功すると公式ページから本機にリダイレクトされます;同一マシンでは自動完了し、Docker / リモートパネルでは案内に従ってコールバックアドレスを貼り付けてください(認可コードは本機からゲートウェイに渡されるだけで、画面に平文 token は表示されません)。", "打开 Accio {label}登录页": "Accio {label} のログインページを開く", "打开 Antigravity 网页登录": "Antigravity のウェブログインを開く", + "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "AtomGit の認可ページを開いてログインしてください。完了後、ゲートウェイが自動的にブローカーをポーリングしてトークンを取得し、アカウントを追加します(コールバックはブローカー側で終端され本機には戻らないため、コールバック URL の手入力は不要です)。", "打开 CatPaw 网页登录": "CatPaw のウェブログインを開く", "打开 Cline 授权页": "Cline の認可ページを開く", "打开 CodeArts 授权页": "CodeArts の認可ページを開く", + "打开 LobsterAI 登录页": "LobsterAI のログインページを開く", "打开 Qoder 网页登录": "Qoder のウェブログインを開く", "打开 Trae 授权页": "Trae の認可ページを開く", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "ZCode {label}の公式認可ページを開き、あなたのアカウントでログインしてください。認可結果は ZCode サーバー側で記録され、ゲートウェイがバックグラウンドで自動的に認証情報を取得してアカウント一覧に追加します。", "打开 ZCode {label}授权页": "ZCode {label} の認可ページを開く", + "打开千问办公的官方授权页完成设备码授权:登录完成后网关自动拿到登录态并加入账号列表(不需要手工回填任何回调地址)。": "QwenWork の公式認可ページを開いてデバイスコード認可を完了します:ログイン完了後、ゲートウェイがログイン状態を自動的に取得し、アカウント一覧に追加します(コールバックアドレスを手動で埋める必要はありません)。", + "打开千问办公网页登录": "QwenWork のウェブログインを開く", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Huawei Cloud CodeArts の公式認可ページを開いてログインします:ログイン完了後、公式ページからブラウザがゲートウェイ自身の /oauth/callback に戻され、ゲートウェイはワンタイム認可コードで一時認証情報を取得し、アカウント一覧に追加します。
ゲートウェイが別のマシンで動作している場合は、ウェブ画面でアドレスバーの最終的なコールバックアドレスをそのままパネルに貼り付けるよう案内されます。", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Huawei Cloud の認可ページを開いてログインしてください:完了すると、ゲートウェイがクラウドをポーリングして認可コードを取得し、アカウント一覧に追加します(OfficeAce デスクトップ版は不要で、コールバック URL を手動で貼り戻す必要もありません)。", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "公式認可ページを開いてデバイスコード認可を完了します。ログインするのは {label}({site})のアカウントです。", @@ -238,23 +289,31 @@ "模型网关基址": "モデルゲートウェイのベース URL", "正在打开百度登录页…": "Baidu のログインページを開いています…", "正在扫描本机 cc-switch…": "本機の cc-switch をスキャンしています…", + "正在换发访客凭证…": "ゲスト資格情報を発行中…", + "正在添加…": "追加中…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "{name} のログイン完了を待っています;完了またはキャンセルしないと新しいログインを開始できません", "每周": "毎週", "每日": "毎日", "每月": "毎月", "没有则无法自动续期": "ないと自動更新できません", "没有匹配「{keyword}」的提供商": "「{keyword}」に一致するプロバイダーはありません", + "添加 QClaw 账号": "", "添加 {name} 账号": "{name} アカウントを追加", "添加中…": "追加中…", "添加到 ": "追加先 ", + "添加千问办公账号": "QwenWork アカウントを追加", "添加失败:{reason}": "追加に失敗しました:{reason}", "添加小浣熊账号": "小浣熊アカウントを追加", "添加方式": "追加方法", + "添加访客账号": "ゲストアカウントを追加", "添加账号": "アカウントを追加", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Volcano Engine Coding プラン;汎用推論エンドポイントは https://ark.cn-beijing.volces.com/api/v3", "点击添加账号": "クリックしてアカウントを追加", "点开即预填,填 Key 接入": "開くと自動入力済み、Key を入力して接続", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "ボタンを押すとまずスライダー認証(公式の不正対策)を 1 回行い、その後ログインページが開きます。ログイン完了で自動的にアカウントが追加されます。
これが国際版公式の唯一のログイン方式です。すでにクライアントでログイン済みの場合は、「デスクトップ版のログイン状態をインポート」を使う方が速いです。", + "点按钮后网关会申请一个设备码并在窗口里打开授权页;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
授权在浏览器里完成,网关不接触密码;跨机部署(Docker / 远程面板)同样可用。": "", + "点按钮后网关会申请一个设备码,并把授权页打开;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
国际版与国内版是两套账号,请用国际站(agent.minimax.io)的账号登录。": "", + "点按钮后网关会申请一个设备码,并把授权页打开;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
授权在浏览器里完成,网关不接触密码;跨机部署(Docker / 远程面板)同样可用。": "", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "AutoClaw アカウントに紐付けられた電話番号でログインします:「コードを取得」をクリックし、SMS を受け取ったら下の欄に入力してログインしてください。認証コードは本機から直接公式 API に送信され、画面に token は表示されません。", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "CatPaw アカウントでログインを完了します(Meituan passport)。成功すると自動でアカウント一覧に追加されます。", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Loomy アカウントに紐付けられた電話番号でログインします:「コードを取得」をクリックして入力するだけです。本家で最も手軽な入り口です。", @@ -270,38 +329,55 @@ "登录已结束": "ログインは終了しました", "登录并添加": "ログインして追加", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "ログイン状態ファイルのパス:~/.box-agent/config/auth.json(Windows:C:\\Users\\<ユーザー名>\\.box-agent\\config\\auth.json)", + "登录态由壳侧从窗口的 Cookie 存储读出;完成后自动加入列表": "", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "カスタムプロバイダー「{name}」を削除しますか?このプロバイダー配下の {count} 個のアカウントも同時に削除され、削除後は元に戻せません。", "等待 Accio {label}登录完成…": "Accio {label} のログイン完了を待っています…", + "等待 AtomCode 登录完成…": "AtomCode のログイン完了を待機中…", "等待 CatPaw 登录完成…": "CatPaw のログイン完了を待っています…", "等待 Cline 授权确认…": "Cline の認可確認を待っています…", "等待 CodeArts 登录完成…": "CodeArts のログイン完了を待っています…", + "等待 Coze 登录完成…": "", "等待 Google 授权完成…": "Google の認可完了を待っています…", "等待 KukuAI 登录完成…": "KukuAI のログイン完了を待っています…", + "等待 LobsterAI 登录完成…": "LobsterAI のログイン完了を待っています…", "等待 OfficeAce 登录完成…": "OfficeAce のログイン完了を待っています…", "等待 OrcaTerm 登录完成…": "OrcaTerm のログイン完了を待っています…", "等待 Qoder 授权完成…": "Qoder の認可完了を待っています…", "等待 Trae 授权完成…": "Trae の認可完了を待っています…", "等待 ZCode {label}授权完成…": "ZCode {label} の認可完了を待っています…", + "等待千问办公授权完成…": "QwenWork の認可完了を待っています…", "等待小浣熊登录完成…": "小浣熊のログイン完了を待っています…", + "等待授权超时,请重新发起": "", + "等待浏览器里确认授权…": "", "等待登录完成…": "ログインの完了を待っています…", "等待网页登录…": "ウェブログインを待っています…", "粘贴 API Key": "API Key を貼り付け", + "粘贴 API 令牌": "API トークンを貼り付け", "粘贴 Accio 的 accessToken": "Accio の accessToken を貼り付け", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Antigravity の Google refresh token を貼り付け(1// で始まる;refresh_token= を含む行全体をそのまま貼り付けても可)。
これはウェブログイン以外の手動入り口です(例:ログイン済みの公式 IDE からエクスポート)。refresh token は期限切れにならず、access token の期限切れはゲートウェイが自動で更新します;トークンを紛失すると Google の認可をやり直すしかないため、大切に保管してください。", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Command Code の API Key を貼り付け(user_ で始まる;公式 CLI の ~/.commandcode/auth.json またはウェブの studio から取得)。行全体 / 引用符付き / Bearer プレフィックス付きのまま貼り付けても構いません。ゲートウェイが自動で裸の key に剥がします。key は期限切れにならず、更新用 API もありません。無効になったら取得し直してください;追加時にゲートウェイが軽量な死活確認を 1 回行いますが、失敗しても追加には影響しません。", "粘贴 Cookie": "Cookie を貼り付け", "粘贴 Google refresh token": "Google refresh token を貼り付け", + "粘贴 ModelScope 的 API 令牌(ms- 开头,在 {site} 的「访问令牌」页获取)。
{siteNote}
这枚令牌两用:网关既用它调推理,也用它读账号身份、魔粒余额与每日签到。令牌长期有效、没有续期接口,失效后重新获取再粘一次即可。": "ModelScope の API トークン(ms- で始まる)を貼り付けてください({site} の「アクセストークン」ページで取得できます)。
{siteNote}
このトークンは二役です:ゲートウェイは推論の呼び出しにも、アカウント情報・魔粒残高・毎日のチェックインの取得にも使います。トークンは長期有効で更新 API はありません。使えなくなったら再取得して貼り直してください。", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "OrcaTerm の OAuth accessToken(ドット区切り 3 パートの JWT。デスクトップクライアントの data.bin か Web 版の Cookie ot_session から取得)を貼り付けます。Bearer / ot_session= を含む行をそのまま貼ってもよく、ゲートウェイが裸のトークンに剥がします。追加後はゲートウェイが自動で更新するため(上流に更新 API があります)、2 時間の有効期限を手動で追う必要はありません。", + "粘贴 QClaw 本机网关的令牌(~/.qclaw/openclaw.json 里 gateway.auth.token 的值)。一般用「从本机导入」就够了;这里适合读不到配置文件时手动兜底 —— 端口留空则用默认的 9434。粘贴后会先连一次网关做校验,通过才落账号。": "", "粘贴 Qoder 个人访问令牌(pt-…)": "Qoder の個人アクセストークン(pt-…)を貼り付け", "粘贴 accessToken": "accessToken を貼り付け", "粘贴 access_token(一长串 JWT)": "access_token(長い JWT)を貼り付け", "粘贴 session cookie": "session cookie を貼り付け", "粘贴 token / refreshToken": "token / refreshToken を貼り付け", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "workos:… で始まるトークンを貼り付け(プレフィックスがなくても自動で補われます)", + "粘贴令牌对": "トークンペアを貼り付け", + "粘贴凭证三件套": "", + "粘贴千问办公的登录令牌(JWT,三段点分)。一般用「从本机导入」就够了;这里适合换机器迁移、或本机解密失败时手动兜底 —— 此时还需要一并填写 userId(令牌里解不出时会提示)。粘贴后会先向上游校验一次,校验通过才落账号。": "QwenWork のログイントークン(3 つのドット区切り部分からなる JWT)を貼り付けます。通常は「本機からインポート」だけで十分です。ここはマシン移行時、またはローカルの復号に失敗した場合の手動フォールバックです —— その場合は userId も併記してください(トークンからユーザー id を読み取れない場合はプロンプトが出ます)。貼り付けたあと、アップストリームに対して一度検証され、通った場合のみアカウントが保存されます。", + "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "智谱清言のアクセストークン(3 つのドット区切り部分からなる JWT)を貼り付けてください。取得方法:ブラウザで chatglm.cn を開いてログインし、開発者ツールの Application → Cookies から chatglm_token の値をコピーします(chatglm_token=…; の行をそのまま貼っても可)。貼り付けたあと上流に対して一度検証し、通った場合のみアカウントを保存します。ログイン済みアカウントではポイントと签到の機能が使え、以降はゲートウェイが自動で更新します(上流に更新インターフェースがあります)。", + "粘贴登录令牌": "ログイントークンを貼り付け", "粘贴登录凭据": "ログイン認証情報を貼り付け", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "貼り付けるのはログイン session です(同時にモデルゲートウェイの token でもあります)。Loomy には更新用 API がなく、session の有効期間は約 14 日です。期限切れ後は再ログインしてください。", + "粘贴网关令牌": "", "粘贴网关凭据": "ゲートウェイ認証情報を貼り付け", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
{siteNote}
MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "ウェブのログイン状態 cookie monkeycode_ai_session の値を貼り付け(ブラウザーで Cookie 全体をコピーしてそのまま貼り付けても可。ゲートウェイが値を自動で取り出します)。
{siteNote}
MonkeyCode には更新用 API がありません:session の有効期間は約 30 日(ハードリミット)で、期限切れ後はウェブに再ログインして貼り付け直せば済みます。", + "粘贴访问令牌": "アクセストークンを貼り付け", "系统浏览器": "システムブラウザ", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "システムブラウザは既存のログイン状態を再利用できます(国際版で推奨);内蔵ウィンドウの方がクリーンです。", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みの Cline アカウントを再利用);完了後は自動でリストに追加されます", @@ -310,16 +386,25 @@ "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みの Meituan アカウントを再利用);完了後は自動でリストに追加されます", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みアカウントを再利用);完了後は自動でリストに追加されます", "系统默认浏览器": "システムの既定ブラウザ", + "续期令牌": "リフレッシュトークン", "编码套餐 API Key": "コーディングプラン API Key", "网关 Secret": "ゲートウェイシークレット", + "网关会以管理员身份读取马维斯客户端的内存来取凭证 —— 客户端的令牌不落明文(登录库是密文、密钥容器校验调用方签名),只在进程内存里,而客户端进程又设了访问限制,普通权限读不到。所以这一步会弹一次 UAC,点「是」即可,之后无需再操作。请确保客户端处于登录状态。": "", "网页登录": "ウェブログイン", "网页登录的打开方式": "ウェブログインの開き方", "网页登录(Zai / Google)": "ウェブログイン(Zai / Google)", + "网页登录(浏览器授权)": "", "自定义": "カスタム", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "自前のアグリゲーションパネル:自分のサイトアドレスを入力。残高照会はこのアカウントの API キーで {base}/v1/usage を叩きます。追加設定不要", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "自前のアグリゲーションパネル:自分のサイトアドレスを入力。残高照会には別途 NewAPI の個人アクセストークンが必要です(下の「残高照会」の節を参照)", "获取风控配置失败,将直接尝试领取:{message}": "不正対策設定の取得に失敗しました。そのまま受け取りを試みます:{message}", "获取验证码": "コードを取得", + "设备码": "", + "设备码 5 分钟内有效;在授权页确认后这里会自动完成": "", + "设备码 5 分钟内有效;窗口里显示「等待你在浏览器里确认」时去授权页点确认即可": "", + "访客模式": "ゲストモード", + "访客模式(免登录)": "ゲストモード(ログイン不要)", + "访问令牌": "アクセストークン", "该上游无需鉴权(不发送鉴权头)": "このアップストリームは認証不要(認証ヘッダーを送信しません)", "该提供商": "このプロバイダー", "该提供商账号添加功能即将上线": "このプロバイダーのアカウント追加機能は近日公開予定です", @@ -332,11 +417,18 @@ "请填写 {label}": "{label} を入力してください", "请填写名称": "名前を入力してください", "请完成验证…": "認証を完了してください…", + "请粘贴 www.coze.cn 的完整 Cookie(浏览器开发者工具 → Network → 任取一条 www.coze.cn 请求的 Cookie 头,或 Cookie 编辑器一键导出)。整包带上最稳:任务中心与机器人列表只认 Cookie,少了几项会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {name: value} 对象。Coze 没有刷新机制,登录态过期后重新登录或重新粘贴即可。": "", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "%APPDATA%/AutoClaw/auth.json を読み込んで復号します。Windows のみ", + "读取 %USERPROFILE%/.minimax/auth 下客户端已保存的令牌,只读不写 —— 客户端用一对必须相等的版本号判断「是否在别处登录过」,写一次就会让它要求你重新扫码。注意:导入是一次快照,之后网关与客户端各自续期,两边的刷新令牌会互相轮换,建议只在一处使用。": "", + "读取 %USERPROFILE%/.minimax/auth/prod/en 下客户端已保存的令牌,只读不写。客户端按「环境 / 地区」分目录存两套登录态,国际版在 en 目录里;没在国际站登录过时这里读不到,请改用上面的网页登录。": "", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "~/.cline/data/settings/providers.json を読み込みます。Cline クライアントでログイン済みである必要があります", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "~/.meituan-catpaw/auth.json を読み込みます。CatPaw クライアントでログイン済みである必要があります", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "本機の KukuAI クライアントのログイン状態を読み込みます(%APPDATA%\\baidugenflowpro\\Network\\Cookies)。KukuAI クライアントでログイン済みである必要があります", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "ローカルの OrcaTerm クライアントのログイン状態(%APPDATA%\\com.orcaterm-desktop.app\\data.bin)を読み取ります。OrcaTerm クライアントでのログインが必要です", + "读取本机 QClaw 客户端网关配置(%USERPROFILE%\\.qclaw\\openclaw.json),需 QClaw 正在运行": "", + "读取本机千问办公客户端登录态(%APPDATA%\\QwenWorkCN\\auth-v2.dat),需已在客户端登录过": "ローカルの QwenWork クライアントのログイン状態(%APPDATA%\\QwenWorkCN\\auth-v2.dat)を読み取ります。クライアントでのログインが必要です", + "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "本機の智谱清言クライアントのログイン状態を読み取ります(%APPDATA%\\chatglm\\Network\\Cookies)。クライアントでログイン済みである必要があります", + "读本机 QClaw 客户端拉起的 OpenClaw 网关配置(端口 + 令牌)。与别家不同,本家需要 QClaw 正在运行 —— 网关是客户端拉起的,端口和令牌都以它在运行为前提;后端会真连一次做校验。": "", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "このマシンの公式クライアント(Tencent WorkBuddy / WorkBuddy AI)で現在ログイン中のアカウントを読み取ります:認証情報ディレクトリ内の最新のログイン状態ファイルを自動で特定します(このマシンの公式クライアントにログイン済みである必要があります)。インポート後はゲートウェイが自動で更新するため、2 時間の有効期限に合わせて手動でトークンを交換する必要はありません。", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "本機クライアントの現在のログイン状態を読み込みます(平文 Cookie、復号不要)。クライアントの実行中は Cookies ファイルが排他占有されるため、インポート前に KukuAI クライアントを終了してください。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。", @@ -345,22 +437,29 @@ "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。Zai / Google でクライアントにログインしているユーザーはこちらを使ってください。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。これは {label} に分類されます。", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "ローカルクライアントが現在ログインしている OAuth トークンを読み取ります(平文 JSON、復号不要)。インポート後はゲートウェイが自動で更新するため、クライアントの 2 時間の有効期限に合わせて手動で切り替える必要はありません。", + "读本机客户端当前的登录态。它是 Chromium os_crypt 加密的(DPAPI 主密钥 + AES-256-GCM),因此仅 Windows 可用。DPAPI 按当前 Windows 用户解密,不需要客户端正在运行 —— 只要在本机登录过一次即可(换机器时请在目标机器上重新登录一次客户端)。": "ローカルクライアントの現在のログイン状態を読み取ります。Chromium os_crypt 暗号化(DPAPI マスターキー + AES-256-GCM)のためWindows でのみ利用できます。DPAPI は現在の Windows ユーザーとして復号するためクライアントが実行中である必要はありません —— このマシンで一度ログインすれば十分です(マシンを移る場合は、移行先でクライアントに一度ログインしてください)。", + "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "本機クライアントの現在のログイン状態を読み取ります。クライアントの Cookies は DPAPI 暗号化(Chromium os_crypt)されているため、Windows でのみ利用できます。クライアントの起動中はこのファイルを読めないことがあるので、クライアントを終了してから再試行してください(「資格情報を入力」でトークンを貼り付ける方法でも構いません)。", "账号信息": "アカウント情報", "账号信息不完整,请刷新后重试": "アカウント情報が不完全です。更新してから再試行してください", "账号已添加,但请留意:{warning}": "アカウントを追加しましたが、ご注意ください:{warning}", "账号版本": "アカウントバージョン", "账号类型": "アカウント種別", "账号:{name}
": "アカウント:{name}
", + "贴国际站的访问令牌(mmoat_ 开头)。国内站的令牌拿到这里会被上游拒(401)—— 两地账号体系不同,不能互换。": "", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Inference Providers ルートを使用します。Key は hf.co/settings/tokens で作成します", "还没有账号": "アカウントはまだありません", "这一家当前不支持网页登录,请改用填写凭证": "このプロバイダーは現在ウェブログインに対応していません。認証情報の入力に切り替えてください", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "このアカウントには現在自動で受け取れるイベントがありません(自動で受け取るのは毎日ログインで付与されるポイントのみです)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "このクォータは「イベントプラン」の経路を使用します:転送時にコーディングプランの期限切れが表示された場合は、", + "这里填国内站(modelscope.cn)的账号:请粘贴国内站的令牌。国际站(modelscope.ai)是另一家 provider、另一套账号,两边的令牌不通用。": "ここには中国版サイト(modelscope.cn)のアカウントを入力します。中国版サイトのトークンを貼り付けてください。国際版サイト(modelscope.ai)は別のプロバイダ・別のアカウント体系で、トークンは相互利用できません。", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "ここには中国サイト(monkeycode-ai.com)のアカウントを入力します:中国サイトの session を貼り付けてください。国際サイト(monkeycode-ai.net)は別のプロバイダーで、アカウントも別です。両者の認証情報は共用できません。", + "这里填国际站(modelscope.ai)的账号:请粘贴国际站的令牌。国内站(modelscope.cn)是另一家 provider、另一套账号,两边的令牌不通用。": "ここには国際版サイト(modelscope.ai)のアカウントを入力します。国際版サイトのトークンを貼り付けてください。中国版サイト(modelscope.cn)は別のプロバイダ・別のアカウント体系で、トークンは相互利用できません。", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "ここには国際サイト(monkeycode-ai.net)のアカウントを入力します:国際サイトの session を貼り付けてください。中国サイト(monkeycode-ai.com)は別のプロバイダーで、アカウントも別です。両者の認証情報は共用できません。", + "适合已在别处登录、或客户端数据目录被改过的场景。只需要 accessToken(mmoat_ 开头);JWT 里能读出身份与到期,无需再填别的。只给访问令牌时约 1 小时有效、无法自动续期 —— 想要自动续期请用上面的网页登录。": "", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "CatPaw クライアントのログイン状態ファイル auth.json を選択します(Windows:C:\\Users\\<ユーザー名>\\.meituan-catpaw\\auth.json、macOS / Linux:~/.meituan-catpaw/auth.json)。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります(本機クライアントに依存しません)。", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Cline クライアントのログイン状態ファイル providers.json を選択します(Windows:C:\\Users\\<ユーザー名>\\.cline\\data\\settings\\providers.json、macOS / Linux:~/.cline/data/settings/providers.json)。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります(本機クライアントに依存しません)。この項目は {label} に登録されます。", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "KukuAI クライアントの Cookies ファイルを選択します(%APPDATA%\\baidugenflowpro\\Network\\Cookies):これは**バイナリ**の SQLite データベースです(拡張子なし、JSON ではありません)。インポート時は選択したファイルのコピーを読み、実行中のクライアントと占有を争いません。したがって**クライアントを終了する必要はありません**。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります。", + "选择一份存着 www.coze.cn Cookie 的文本文件(整段 Cookie 头或 Cookie 编辑器导出的 JSON 都可以)。导入后凭证直接落在网关、可独立转发,不依赖本机客户端(Coze 客户端运行时占着 Cookie 库,读不了)。": "", "选择凭证文件导入": "認証情報ファイルからインポート", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "公式クライアントのログイン状態の認証情報ファイル(workbuddy-desktop.info / workbuddy-desktop-ai.info)または wb2api がエクスポートしたアカウント JSON(ネスト / フラット形式どちらも可)を選択してください。", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "小浣熊クライアントのログイン状態ファイル auth.json を選択します(Windows:C:\\Users\\<ユーザー名>\\.box-agent\\config\\auth.json、macOS / Linux:~/.box-agent/config/auth.json)。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります(本機クライアントに依存しません)。", @@ -379,6 +478,7 @@ "领取失败:{message}": "受け取りに失敗しました:{message}", "领取并让官方确认": "受け取って公式の確認を待つ", "风控验证配置不完整,请稍后重试": "不正対策認証の設定が不完全です。しばらくしてから再試行してください", + "马维斯 Marvis": "", "验证码": "認証コード", "验证码已发送。收到后填入下方并点「登录并添加」": "認証コードを送信しました。受け取ったら下の欄に入力し、「ログインして追加」をクリックしてください", "验证码已发送,请查看手机短信": "認証コードを送信しました。携帯の SMS をご確認ください", diff --git a/desktop-tauri/i18n/i18n-src/ja/agent.json b/desktop-tauri/i18n/i18n-src/ja/agent.json new file mode 100644 index 00000000..b834dfb1 --- /dev/null +++ b/desktop-tauri/i18n/i18n-src/ja/agent.json @@ -0,0 +1,102 @@ +{ + "· 走网关": "· ゲートウェイ経由", + "。这里的开关与还原作用在网关所在的机器上。": "", + "一键把本机 AI 客户端接到本地网关:改写它自己的配置文件,保留其它设置": "ローカルの AI クライアントをローカルゲートウェイに接続します。各クライアントの設定ファイルを書き換え、それ以外の設定はそのまま残します", + "上游模型 id,例如 raccoon-coder-pro": "上流モデル ID(例: raccoon-coder-pro)", + "下面是这个客户端文件里现有的配置(只读)。点「从客户端导入」把它收进我的配置,之后就能编辑和切换。": "以下はこのクライアントの設定ファイルにある既存の設定です(読み取り専用)。「クライアントから読み込む」を押すと自分の設定に取り込み、編集・切り替えができるようになります。", + "个 · 已安装": " 件 · インストール済み", + "中转": "中継", + "从客户端导入": "クライアントから読み込む", + "会用最近一次备份覆盖它的配置文件。这一步不会改动你在本页存的配置列表。": "最新のバックアップで設定ファイルを上書きします。このページに保存した設定リストは変更されません。", + "低": "低", + "保存修改": "変更を保存", + "共": "合計", + "写入中…": "書き込み中…", + "写入后需要重开会话才生效": "書き込み後はセッションを開き直すと反映されます", + "写入失败:{reason}": "書き込みに失敗しました: {reason}", + "写入客户端": "クライアントに書き込む", + "写入预览": "書き込みプレビュー", + "单配置": "単一設定", + "单配置文件": "", + "只改 enabled 与默认项,其余条目原样保留": "enabled と既定項目だけを変更し、その他の項目はそのまま残します", + "只改属于我们的那几个键,其它设置原样保留": "当方のキーだけを変更し、その他の設定はそのまま残します", + "可同时启用多条;切换只改 enabled 与默认项": "複数を同時に有効化できます。切り替えは enabled と既定項目だけを変更します", + "同一时刻只有一套生效;写入 = 把这一套整段写进客户端文件": "同時に有効なのは 1 つだけです。書き込み = その設定をクライアントのファイルへまとめて書き込みます", + "启用 {name}": "{name} を有効化", + "备注": "メモ", + "多套配置": "複数の設定", + "多配置 · 已启用 {n}": "複数設定 · {n} 件有効", + "官方直连": "公式直結", + "客户端列表加载失败:{reason}": "クライアント一覧の読み込みに失敗しました: {reason}", + "客户端清单": "クライアント一覧", + "客户端自己的设置优先;这里写的是写入时带进去的初值": "クライアント側の設定が優先されます。ここに書くのは書き込み時の初期値です", + "已保存": "保存しました", + "已写入 {name}": "{name} に書き込みました", + "已写入 {name};这个客户端只在启动时读配置,重开一个会话才生效": "{name} に書き込みました。このクライアントは起動時にしか設定を読まないため、セッションを開き直すと反映されます", + "已安装": "インストール済み", + "已按备份还原 {name}": "{name} をバックアップから復元しました", + "已收进我的配置:{n} 条": "自分の設定に取り込みました: {n} 件", + "已还原「{name}」": "", + "当前环境不支持保存(桥接方法缺失)": "この環境では保存できません(ブリッジメソッドが見つかりません)", + "当前环境不支持写入(桥接方法缺失)": "この環境では書き込めません(ブリッジメソッドが見つかりません)", + "当前环境不支持客户端接管(桥接方法缺失)": "この環境ではクライアント接続を利用できません(ブリッジメソッドが見つかりません)", + "当前环境不支持导入(桥接方法缺失)": "この環境では読み込めません(ブリッジメソッドが見つかりません)", + "当前环境不支持还原(桥接方法缺失)": "この環境では復元できません(ブリッジメソッドが見つかりません)", + "手机上只能查看与还原接管。改写配置内容请回桌面端": "", + "按备份还原": "バックアップから復元", + "按备份还原 {name}?": "{name} をバックアップから復元しますか?", + "接管会把客户端的 base_url 指向本网关,并在写入前自动备份原文件。": "", + "搜索客户端": "クライアントを検索", + "搜索客户端 / 配置文件…": "クライアント / 設定ファイルを検索…", + "改写网关所在机器上的客户端配置": "", + "改动": "変更", + "新增": "追加", + "新建配置": "新規設定", + "新配置": "新規設定", + "无配置": "設定なし", + "暂无预览": "プレビューはまだありません", + "未安装": "未インストール", + "未接管": "", + "未检测到": "未検出", + "未检测到安装": "インストールが検出されません", + "未走网关": "ゲートウェイ未経由", + "本机": "この端末", + "本机服务": "ローカルサービス", + "本机没有找到 {name}": "このマシンに {name} が見つかりません", + "本机网关": "ローカルゲートウェイ", + "来自客户端文件,只读": "クライアント設定ファイルから取得(読み取り専用)", + "模型 id": "モデル ID", + "正在生成预览…": "プレビューを生成中…", + "正在读取本机客户端…": "ローカルクライアントを読み込み中…", + "每个客户端可以有": "各クライアントは", + "没有匹配的客户端": "一致するクライアントがありません", + "没有可导入的新条目(可能还没配过,或已经在我的配置里)": "読み込める新しい項目がありません(未設定か、すでに自分の設定にあります)", + "没有新的条目可收(都已在我的配置里)": "取り込む新しい項目はありません(すべて自分の設定にあります)", + "没有检测到可接管的客户端": "", + "确定按最近一次备份还原「{name}」?": "", + "第三方中转": "サードパーティ中継", + "编辑:{name}": "編集: {name}", + "行号由后端逐行比对给出": "行番号はバックエンドの行単位の比較によるものです", + "装好后回到这一页点「重新检测」,它会出现在上面的列表里。": "インストール後にこのページへ戻り「再検出」を押すと、上の一覧に表示されます。", + "设为默认": "既定に設定", + "读取客户端配置失败:{reason}": "クライアント設定の読み込みに失敗しました: {reason}", + "走网关": "ゲートウェイ経由", + "还原中…": "", + "还原失败:{reason}": "復元に失敗しました: {reason}", + "还没有配置": "設定がまだありません", + "还没有配置。点「新建配置」建一套指向本机网关的,再写入客户端。": "設定がまだありません。「新規設定」でローカルゲートウェイを指す設定を作り、クライアントに書き込んでください。", + "还没有预览。选一套配置后这里会显示写入客户端之后的样子。": "プレビューはまだありません。設定を選ぶと、クライアントへ書き込んだ後の内容がここに表示されます。", + "这个客户端会自己重读配置,写入后开新会话即生效。": "このクライアントは設定を自動で読み直します。書き込み後に新しいセッションを開けば反映されます。", + "这个客户端只在启动时读配置:写入后重开一个会话才生效。": "このクライアントは起動時にしか設定を読みません。書き込み後にセッションを開き直すと反映されます。", + "这个客户端的配置里没有读出地址(可能还没配过,或形状不认识)": "このクライアントの設定からアドレスを読み取れませんでした(未設定か、形式を認識できません)", + "这个客户端自己支持多套配置并存": "このクライアントは複数の設定を並存できます", + "这个客户端自己管理多套配置,可同时启用多条;切换只改 enabled 与默认项,不动它的模型清单。": "このクライアントは複数の設定を自前で管理し、複数を同時に有効化できます。切り替えは enabled と既定項目だけを変更し、モデル一覧には触れません。", + "这套配置是干什么用的(只显示在本页)": "この設定の用途(このページにのみ表示)", + "选一个客户端开始": "クライアントを選んで開始", + "配置": "設定", + "配置来源": "設定の種類", + "需重开会话生效": "セッションを開き直すと反映されます", + "高": "高", + "(手机读不到本机文件)": "", + "(本机网关 / 官方直连 / 第三方中转 / 本机服务),随时切换。单配置客户端切换 = 整段覆写并备份原文件;多配置客户端(如 ZCode)只改 enabled 与默认项,它自己的条目一律不动。": "(ローカルゲートウェイ / 公式直結 / サードパーティ中継 / ローカルサービス)をいつでも切り替えられます。単一設定のクライアントでは設定全体を上書きし、元のファイルをバックアップします。ZCode のような複数設定のクライアントでは enabled と既定項目だけを変更し、独自の項目には一切触れません。" +} diff --git a/desktop-tauri/i18n/i18n-src/ja/assistant.json b/desktop-tauri/i18n/i18n-src/ja/assistant.json index 0b3d1a20..9c3a7c33 100644 --- a/desktop-tauri/i18n/i18n-src/ja/assistant.json +++ b/desktop-tauri/i18n/i18n-src/ja/assistant.json @@ -1,32 +1,147 @@ { "CodeArts 的账号余额还剩多少?": "CodeArts のアカウント残高はあといくら?", + "Enter 发送 · Shift+Enter 换行": "", + "section 只能是 models / mappings / both": "section は models / mappings / both のいずれかです", + "{n} / {total}": "", + "{n} 个账号正在限流:": "", + "{n} 家": "", + "{n} 条改动": "", + "{n} 条映射": "{n} 件のマッピング", + "✓ 完成": "", + "✗ 失败": "", + "「{name}」超过 {n} MB,换一张小一点的。": "", + "一次最多 {n} 张图片。": "", + "一次最多 {n} 张图片,多出的没有添加。": "", + "上面四个快捷查询直接读网关数据,不经过模型": "", + "不指定,交给映射绑定": "", + "今天用了 {tokens} Token,共 {requests} 次请求。": "", + "今天用了多少 Token?": "今日は何 Token 使いましたか?", + "会话响应不完整": "セッションの応答が不完全です", + "会话里的选择优先于这里的默认值": "", + "余额是缓存读数,点账号详情里的「刷新凭证」可触发实时查询。": "", + "作为 system 消息注入": "", + "保存思考等级失败:{message}": "思考レベルの保存に失敗しました:{message}", "修改模型能力": "モデル能力を変更", + "共 {n} 个模型条目。": "", + "共 {n} 个模型条目。映射与改名请在桌面端「模型管理」里做。": "", + "共 {n} 个账号,前 {m} 个的余额读数:": "", + "删除会话失败:{message}": "セッションの削除に失敗しました:{message}", + "删除这条对话": "この会話を削除", + "助手设置": "", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "アシスタント設定を読み込めなかったため、対話モデルをまだ保存できません。入力欄の下のヒントをご確認ください。", + "助手设置没读出来,暂时存不了对话模型。": "", + "助手设置没读出来,暂时存不了思考等级 —— 看输入框下方的提示。": "アシスタント設定を読み込めないため、思考レベルを保存できません —— 入力欄の下のヒントを確認してください。", + "助手设置没读出来,暂时存不了思考等级。": "", + "助手设置没读出来,现在还不能发消息 —— 点右上角「助手设置」看看。": "", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "アシスタント設定を読み込めなかったため、まだメッセージを送信できません。入力欄の下のヒントをご確認ください。", + "勾选即注入": "", + "历史对话": "会話履歴", + "原先在「设置 → AI 助手」的配置都搬到这里了": "", + "发消息或做任务…(Enter 发送,Shift+Enter 换行,可粘贴图片)": "", "发起模型测试": "モデルテストを実行", + "变更前确认": "", + "只支持图片文件(PNG / JPG / WebP / GIF)。": "", + "只读 · 不会改动配置": "", + "只跳过确认,不扩大工具权限;每次执行仍会记录在对话里。": "", + "各账号余额还剩多少?": "各アカウントの残高はいくらですか?", "启用 / 禁用模型": "モデルの有効化 / 無効化", "启用 / 禁用账号": "アカウントの有効化 / 無効化", + "哪些模型在多家重复了?": "複数のプロバイダに重複しているモデルは?", + "图片 {n}": "", + "完全访问": "", + "完全访问已开启:改动类工具会直接执行,不再弹出确认卡。": "", + "完整对话在桌面端「AI 助手」页": "", "对指定模型发一次真实请求,回报连通性与耗时。": "指定したモデルに実際のリクエストを 1 回送り、到達性と所要時間を報告します。", + "对话由网关转发到上游模型,用量计入请求日志": "会話はゲートウェイが上流モデルへ転送します。使用量はリクエストログに記録されます", + "已停止生成": "生成を停止しました", + "已全部勾选": "", + "已切换到变更前确认": "", + "已切换到完全访问": "", + "已启用的模型": "", + "已调用": "呼び出し", "开关模型的对外可用性,不改其它属性。": "モデルの対外的な可用性だけを切り替え、他の属性は変更しません。", + "当前一个工具都没勾选,助手只能纯聊天。点右上角「助手设置」勾上需要的工具。": "", + "当前模型没有声明图片能力,图可能发不上去。": "", + "当前没有同名模型分布在多家。": "", + "当前:已关闭(助手收不到任何系统提示词,工具定义照发)": "", + "当前:已开启 · 使用内置默认提示词": "", + "快捷查询走本地网关,不消耗额度": "", + "思考等级已切换": "", + "思考:跟随模型": "思考:モデルに従う", + "总请求": "", + "成功率": "", + "我可以直接查网关里的真实数据:余额、模型重复、今日用量、限流账号。": "", + "打开会话失败:{message}": "セッションを開けませんでした:{message}", + "执行中": "", + "找到 {n} 组同名模型:": "", "把重复的模型加上家名前缀区分开": "重複しているモデルにプロバイダー名の接頭辞を付けて区別する", + "推理等级": "", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "コンテキストウィンドウ / 最大出力トークン / ツール呼び出しなどの能力フラグを変更します。", + "改动先列清单": "", + "改动直接执行": "", + "改动类 · 默认需要确认": "", + "改动类工具先列清单,等你点「执行」": "", + "改动类工具直接执行,不再询问": "", + "改回变更前确认": "", "改某一家的整组顺位(先试哪一家)。": "あるプロバイダーのグループ全体の順位を変更します(どちらを先に試すか)。", "改账号在全局队列里的名次,决定主备与切换顺序。": "グローバルキュー内のアカウント順位を変更し、主系 / 予備と切り替え順を決めます。", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "マッピング名の追加 / 削除。一括での接頭辞付与や、同名を複数プロバイダーへ統合できます。", + "新建对话时使用": "", + "映射": "", + "有什么可以帮你?可以问配置、查余额、让我改模型设置。": "何をお手伝いしましょうか。設定の質問、残高確認、モデル設定の変更ができます。", + "未命名对话": "無題の会話", + "未知的工具:{name}": "不明なツール: {name}", + "未选择": "", + "权限模式": "", + "查询账号": "", "查询账号余额": "アカウント残高を照会", + "模型不支持时会自动忽略": "", + "模型没有返回内容": "モデルから内容が返されませんでした", + "正在思考…": "考え中…", + "正在生成,点右侧按钮可停止": "生成中 — 右のボタンで停止できます", "没有可用的模型": "利用可能なモデルがありません", + "没有已启用的模型,请先在「模型管理」里启用": "有効なモデルがありません。先に「モデル管理」で有効にしてください", "测一下当前最快的模型通不通": "現在いちばん速いモデルが通るか試す", "深度推理": "深い推論", + "添加图片": "", + "添加图片(也可粘贴或拖入)": "", + "清空全部": "すべて消去", + "清空历史失败:{message}": "履歴の消去に失敗しました:{message}", + "现在有哪些账号被限流?": "現在レート制限中のアカウントは?", + "现在没有账号被限流。": "", "看看哪些模型在多家重复了": "複数のプロバイダーで重複しているモデルを見る", "禁用会把账号移出转发队列,线路可能因此断供。": "無効化するとアカウントは転送キューから外れ、その経路が供給停止になる可能性があります。", + "自定义提供商模块未就绪(请刷新界面后重试)": "カスタムプロバイダーモジュールが準備できていません(画面を再読み込みしてから再試行してください)", + "自由对话请用桌面端…": "", "获取模型列表": "モデル一覧を取得", + "要改配置的话,桌面端的助手支持完整对话与工具调用。": "", "设置模型映射 ID": "モデルマッピング ID を設定", + "读取历史会话失败:{message}": "会話履歴の読み込みに失敗しました:{message}", + "读取图片失败": "", + "读取图片失败:{message}": "", + "读取模型清单": "", + "读取模型清单失败": "モデル一覧の取得に失敗しました", + "读取账号列表": "", "读各提供商的模型清单、映射名与启用状态。": "各プロバイダーのモデル一覧・マッピング名・有効状態を読み取ります。", "读各账号的额度 / 剩余用量。": "各アカウントの枠 / 残量を読み取ります。", "调整提供商顺位": "プロバイダーの順位を調整", "调整账号优先级": "アカウントの優先度を調整", + "走本地网关 · 消耗所选账号额度": "", + "跟随模型": "", + "跟随模型(不指定)": "モデルに従う(指定なし)", + "跟随网关设置": "ゲートウェイ設定に従う", + "还没有历史对话": "会話履歴はまだありません", + "还没有账号。": "", + "还没有选择对话模型:在输入框右下角选一个,或点右上角「助手设置」。": "", + "还没有配置默认模型": "既定モデルが未設定です", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "このバージョンのゲートウェイには AI アシスタント API がまだありません(HTTP 404):バックエンドが古いため、再ビルド / クライアント再起動後にお試しください。{message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "このターンは往復 {n} 回の上限に達したため、指示を待って停止しました(言い換えるか、いくつかの手順に分けてください)。", + "这个查询还没有实现。": "", + "这条会话没能存进历史:{message}": "この会話を履歴に保存できませんでした:{message}", + "这里只改本次会话用的模型,不会改网关里保存的默认值。": "ここでの変更はこの会話にのみ適用され、ゲートウェイに保存された既定値は変わりません。", "连接网关失败({base}):{message}": "ゲートウェイへの接続に失敗({base}):{message}", - "配置响应不完整": "設定レスポンスが不完全です" + "选择对话模型": "会話モデルを選択", + "配置响应不完整": "設定レスポンスが不完全です", + "问点什么…": "何か聞いてください…", + "默认行为": "" } diff --git a/desktop-tauri/i18n/i18n-src/ja/checkin.json b/desktop-tauri/i18n/i18n-src/ja/checkin.json index 49d2f0ac..c82569fe 100644 --- a/desktop-tauri/i18n/i18n-src/ja/checkin.json +++ b/desktop-tauri/i18n/i18n-src/ja/checkin.json @@ -1,12 +1,15 @@ { " · ": " · ", + "QClaw": "", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "SOLO の checkin_credits 受け取り · 自然日 0 時に更新", "UMID 组件安装失败:{error}": "UMID コンポーネントのインストールに失敗しました:{error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy 中国版 · タスクツリーを照会してワンクリックで点灯/受け取り · 新学期イベントとねこ旅行を含む · 行をクリックで詳細", "WorkBuddy 国际版保活模型,按顺序回退": "WorkBuddy 国際版のキープアライブ用モデル(順番にフォールバック)", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "WorkBuddy国際版のデイリーアクティビティ用の無料モデル · 全体で1つの設定、スケジュールとは無関係", "{date}(每天 {time})": "{date}(毎日 {time})", + "{done}/{total} 已签": "{done}/{total} チェックイン済み", "{label} ×{count}({reason})": "{label} ×{count}({reason})", + "{n} 积分": "{n} ポイント", "{n} 项可完成": "完了可能 {n} 件", "{n} 项待领": "{n} 件受け取り待ち", "{total} 账号 · {done} 已签": "{total} アカウント · {done} チェックイン済み", @@ -22,10 +25,15 @@ "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "「キープアライブモデルチェーン」は、WorkBuddy国際版のデイリーアクティビティでアカウントを活性化し続けるための無料モデルです。順に試し、最初の成功で止まります。リストを空にして保存すると既定のチェーン(現在の既定:{defaults})に戻ります。当日の重複排除+冪等な受け取り:再実行しても「受取済み」が返るだけで、ポイントが二重に加算されることはありません。", "「免费领积分」的每日任务 · 逐个领取": "「無料積分受け取り」のデイリータスク · 1 つずつ受け取る", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "一度きりの特典 · チェックイン後に自動で照会して受け取り、受け取り済みは本機に記録し以降はアップストリームに問い合わせません · 行をクリックしてタスク一覧を展開", + "一次性福利已全部结清": "一回限りの特典はすべて受け取り済みです", + "一次性福利已结清": "一回限りの特典は受け取り済みです", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "その日の報酬を一度にまとめて受け取ります(毎日チェックイン+新規特典) · 冪等性はアップストリームが保証", "一键安装组件": "ワンクリックでインストール", "一键完成": "一括完了", "一键领取({n})": "一括受け取り({n})", + "七天一轮共 4000 积分(400/400/400/1000/400/400/1000) · 每份 30 天后到期 · 当天重复领是幂等的": "", + "上一轮({time}):{n} 个动作,+{credit} 积分": "前回({time}):{n} 件のアクション、+{credit} クレジット", + "上一轮({time}):{n} 项,+{credit} 积分": "", "上次执行:{line}": "前回実行:{line}", "上次签到时间": "前回のチェックイン時刻", "上次领取 {time} · 领取需通过滑块验证码": "前回の受け取り {time} · 受け取りにはスライダー認証が必要", @@ -33,16 +41,27 @@ "下次 {date} · 点此管理条目": "次回 {date} · クリックしてスケジュールを管理", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "デイリーチェックインの対象外:{list}。CodeArts の一度きりの特典は下の「初心者ボーナス」に、CodeArts と ZCode の日次/プラン特典の受け取りは「アクティビティ特典」にあります。", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "デイリーチェックインの対象外:{list}。CodeArts の一度きりの特典は下の「初心者ボーナス」、CodeArts のデイリー特典はすぐ上の「デイリーチェックイン」にあります。ZCode のプラン受け取りは「アクティビティ特典」です。", + "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」;QClaw 已停运、积分随迁移到 WorkBuddy 发放,见下方「只读余额(已停运)」。": "", + "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」;QClaw 的积分随登录自动发放,见「自动发放」。": "", + "不参与签到": "チェックイン対象外", + "不可领": "受け取り不可", "与国内版同一套任务接口 · 站点不同": "中国版と同じタスク API · サイトが異なる", + "与国内版同一套链路 · 站点不同、令牌不通用": "中国版と同じ経路 · サイトが異なりトークンは相互利用不可", + "个账号不参与": "アカウントは対象外", + "今天 · 北京时间自然日": "今日 · 北京時間の暦日", + "今天已执行:{list}": "本日の実行済み:{list}", "今天已领 {n} 份": "本日 {n} 件受け取り済み", "今天未领": "本日は未受取", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "本日(北京時間 {day})受け取り済み · 公式に {n} 件の到着を確認 · 明日また受け取れます", "今日已执行": "本日実行済み", "今日已派出": "本日は出発済み", + "今日已签": "本日チェックイン済み", "今日已领取": "本日受け取り済み", "今日签到": "本日のチェックイン", "今日签到进度": "本日のチェックイン進捗", "从清单里移除": "リストから削除", + "余额查询中…": "", + "余额查询失败:{error}": "", "保活": "キープアライブ", "保活 {n}": "キープアライブ {n}", "保活+领取": "キープアライブ+受け取り", @@ -55,6 +74,8 @@ "全部结果": "すべての結果", "全部账号均可签到": "すべてのアカウントがチェックイン可能", "全部领取": "すべて受け取り済み", + "关着:不自动巡检(仍可手动巡检)": "オフ:自動巡回しません(手動巡回は可能)", + "关着:不自动执行(仍可手动执行)": "", "关着:所有条目都不执行": "オフ:どのスケジュールも実行されません", "删除失败:{error}": "削除に失敗しました:{error}", "删除定时条目": "スケジュールを削除", @@ -62,22 +83,27 @@ "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "時刻になると、チェックしたプロバイダーの利用可能なアカウントを自動でチェックインします(WorkBuddy 中国版はデイリーチェックイン API、国際版はデイリーアクティブタスク:イベントを検出して日活報酬を受け取り、無料モデルでキープアライブ。小浣熊はデスクトップ版のデイリー積分経路。AutoClaw は公式クライアントのデイリーチェックインタスク。Qoder 中国版はイベント受け取りで、イベントが配信されていないアカウントには中立的な案内が出ます。Qoder 国際版はデバイスリスク制御 ID(本機の Qoder クライアント / qodercli の UMID コンポーネントで生成)を付けて初めて「毎日 100 Credits」イベントが見えるようになり、コンポーネントが欠けている場合はエラーではなく明確な説明が出ます。Trae は SOLO のデイリーチェックイン積分を受け取り(自然日 0 時にリセット)、アップストリームがそのアカウントにイベントを開いていない場合や認証情報にデバイス ID がない場合は、エラーではなく「未受け取り + 具体的な理由」が返ります。Loomy は毎日アカウントの初回ログイン積分を 1 回付与します。KukuAI は「無料積分受け取り」のデイリータスクを消化します;CodeArts はその日の運営特典を受け取ります(受け取り → 確認 → 読み戻し検証。自動実行は 1 日 6 回・間隔 10 分の書き込み制限を受けます);OfficeAce はその日の報酬を一度の操作でまとめて受け取ります(毎日チェックイン+新規特典))。時刻を逃しても、起動後に自動で補完チェックインされ、当時電源が入っていなかったことを理由に漏れることはありません。各社のチェックイン API はすべて冪等なので、繰り返し実行しても二重に受け取ることはありません。", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "時刻になると、チェックしたプロバイダーの利用可能なアカウントを自動でチェックインします(WorkBuddy 中国版はデイリーチェックイン API、国際版はデイリーアクティブタスク:イベントを検出して日活報酬を受け取り、無料モデルでキープアライブ。小浣熊はデスクトップ版のデイリー積分経路。AutoClaw は公式クライアントのデイリーチェックインタスク。Qoder 中国版はイベント受け取りで、イベントが配信されていないアカウントには中立的な案内が出ます。Qoder 国際版はデバイスリスク制御 ID(本機の Qoder クライアント / qodercli の UMID コンポーネントで生成)を付けて初めて「毎日 100 Credits」イベントが見えるようになり、コンポーネントが欠けている場合はエラーではなく明確な説明が出ます。Trae は SOLO のデイリーチェックイン積分を受け取り(自然日 0 時にリセット)、アップストリームがそのアカウントにイベントを開いていない場合や認証情報にデバイス ID がない場合は、エラーではなく「未受け取り + 具体的な理由」が返ります。Loomy は毎日アカウントの初回ログイン積分を 1 回付与します。KukuAI は「無料積分受け取り」のデイリータスクを消化します;OfficeAce はその日の報酬を一度の操作でまとめて受け取ります(毎日チェックイン+新規特典))。時刻を逃しても、起動後に自動で補完チェックインされ、当時電源が入っていなかったことを理由に漏れることはありません。各社のチェックイン API はすべて冪等なので、繰り返し実行しても二重に受け取ることはありません。", "到点时逐个签到,已勾 {n} / {total} 家": "定刻に 1 社ずつチェックインします。選択 {n} / {total} 社", + "北京时间 · 逗号分隔 · 最多 6 条 · 到点后最多晚 30 秒执行;错过时点开机后会补跑一轮。": "北京時間 · カンマ区切り · 最大6件 · 定刻から最大30秒遅れて実行。時刻を逃した場合は起動後に1回だけ追い実行します。", "北京时间 · 错过时点开机后会自动补签": "北京時間 · 起動時に自動で補完します", "去领取": "受け取りへ", "另有 {n} 个账号不参与每日签到": "ほかにデイリーチェックインの対象外のアカウントが {n} 件", + "只读余额(已停运)": "", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "完了済みタスクの報酬のみ受け取り(冪等・連打しても二重加算されません)", "可完成": "完了可能", "可派出": "出発可能", "可选 · 缺省显示时刻": "任意 · 未指定では時刻を表示", + "可领": "受け取り可能", "可领奖": "受け取り可能", "后端未返回定时签到数据": "バックエンドがスケジュールデータを返しませんでした", "后端未返回签到中心数据": "バックエンドがチェックインセンターのデータを返しませんでした", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "国際版のチェックインには、UMID コンポーネントで生成するデバイスリスク制御 ID が必要です(本機では検出されませんでした)。", + "在桌面端设置": "デスクトップ版で設定", "在途": "旅行中", "填入缺省链": "既定のチェーンを入力", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "時刻と範囲を決めて「保存」を押してください。名前は空欄でも構いません(空欄なら時刻を表示)", "失败 {n}": "失敗 {n}", "套餐明细": "プラン明細", + "套餐领取": "プラン受け取り", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Qoder クライアントまたは qodercli をインストールすれば利用できます(ゲートウェイの再起動は不要)。", "安装中…": "インストール中…", "官方客户端的每日签到任务": "公式クライアントのデイリーチェックインタスク", @@ -86,30 +112,49 @@ "尚未查询": "未照会", "尚未查询任务状态": "タスク状態は未クエリ", "巡检一次": "1 回実行", + "巡检中…": "巡回中…", "已保存:{label}": "保存しました:{label}", "已保存:保活模型链": "保存しました:キープアライブモデルチェーン", "已保存:定时条目": "保存しました:スケジュール", + "已保存:成长任务排程": "", + "已保存:猫猫旅行排程": "保存しました:猫猫旅行スケジュール", + "已停运": "", + "已全部领取": "すべて受け取り済み", "已删除定时条目": "スケジュールを削除しました", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "アクティビティを報告しました(連続ログイン達成)が、キャンペーンは未実施です(今回の報酬はなし)", + "已开始批量签到": "一括チェックインを開始しました", + "已得 {earned} / {total} 积分": "{earned} / {total} ポイント獲得", "已抽 {n} 次": "{n} 回抽選しました", + "已提交「{label}」的签到": "「{label}」のチェックインを送信しました", "已有结果 {checked} / 共 {total} 个账号": "結果あり {checked} / 全 {total} アカウント", "已签": "チェックイン済み", + "已领": "", "已领 {claimed}/{total} · 累计 {earned} 积分": "受け取り済み {claimed}/{total} · 累計 {earned} 積分", "已领养": "飼い主になりました", "已领取": "受け取り済み", + "已领取「{name}」": "「{name}」を受け取りました", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "デバイスリスク制御 ID を付けて「毎日 100 Credits」を受け取り · UMID コンポーネントが必要", + "开关": "スイッチ", "开学季执行失败:{error}": "新学期の実行に失敗:{error}", "开学季执行完成:{message}": "新学期の実行が完了:{message}", "开学季活动": "新学期イベント", "开着:启用的条目按各自时刻执行": "オン:有効なスケジュールがそれぞれの時刻に実行されます", + "开着:按下面的时刻自动巡检": "オン:以下の時刻に自動巡回します", + "开着:按下面的时刻自动执行": "", + "当前没有可领取的套餐": "現在受け取れるプランはありません", "待签": "未チェックイン", + "待领取": "受け取り待ち", "待领新手福利": "受け取り待ちの初心者ボーナス", "总开关": "マスタースイッチ", "总开关已关闭 · 在「定时签到」页打开": "マスタースイッチがオフです · 「定期チェックイン」タブで有効化してください", "成功 {n}": "成功 {n}", "成长任务": "成長タスク", + "成长任务失败:{error}": "", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "成長タスク完了(成功 {done} 件・失敗 {failed} 件):クレジット +{credit} エネルギー +{energy}", + "成长任务定时排程": "", "成长任务执行失败:{error}": "成長タスクの実行に失敗:{error}", + "成长任务:{done} 项完成、{failed} 项失败,+{credit} 积分 +{energy} 能量": "", + "成长任务:{done} 项完成,+{credit} 积分 +{energy} 能量": "", "执行失败:{error}": "実行に失敗しました:{error}", "执行并抽奖": "実行して抽選", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "新学期タスクを実行し、抽選回数を使い切る(シェア/会話/エキスパートイベント)", @@ -125,14 +170,21 @@ "搜账号 / 收益 / 错误 / 触发原因": "アカウント / 収益 / エラー / トリガー理由を検索", "收起新建": "新規入力を閉じる", "新建定时条目": "新しい定時エントリ", + "新手任务": "初心者タスク", "新手福利": "初心者ボーナス", + "新手福利与活动": "初心者ボーナスとキャンペーン", + "新手福利是一次性任务:领完就没了,重复点不会重复加分。": "初心者ボーナスは一回限りのタスクです。受け取ると終了し、何度押してもポイントは重複加算されません。", "新手福利有 {n} 项领取失败,可重试": "初心者ボーナスで {n} 件の受け取りに失敗しました。再試行できます", "无": "なし", + "无独立签到接口 · 每天替账号打一次登录信息(即当日魔粒)": "専用のチェックイン API なし · 毎日アカウントのログイン情報を 1 回取得(当日の魔粒が付与される)", "无独立签到接口 · 每天替账号打一次首次登录积分": "専用のチェックイン API なし · 毎日アカウントの初回ログイン積分を 1 回付与", "无账号": "アカウントなし", + "时刻": "時刻", "暂不可领": "受け取り不可", + "暂不可领(未满足条件)": "まだ受け取れません(条件未達成)", "暂不支持": "未対応", "暂无启用的条目 · 点此添加": "有効なスケジュールがありません · クリックして追加", + "最近到期 {date}": "", "有未保存的改动": "未保存の変更があります", "未勾选提供商": "プロバイダー未選択", "未开启": "未有効", @@ -141,30 +193,45 @@ "未领取": "未受け取り", "本地台账里今天还没领过;活动期内每天发一份新套餐": "ローカルの記録では本日まだ受け取っていません。キャンペーン期間中は毎日新しいプランが 1 件配布されます", "本地台账里今天(北京时间)领过的套餐:{plans}": "ローカル記録で本日(北京時間)受け取ったプラン:{plans}", + "本站已停止发放积分,余额在原有效期内仍可用": "", "条启用": "件有効", "查询于 {time}": "{time} にクエリ", "查询任务": "タスクをクエリ", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "タスクツリーを照会してワンクリックで点灯/受け取り(新学期・ねこ旅行を含む)", "查询失败:{error}": "クエリに失敗しました:{error}", + "查询无结果": "", "桌面端每日积分链路": "デスクトップ版のデイリー積分経路", "正在读取定时签到数据…": "定期チェックインデータを読み込み中…", "正在读取签到数据…": "チェックインデータを読み込み中…", "每天 {time}": "毎日 {time}", "每天 {time} · {scope}": "毎日 {time} · {scope}", + "每天定点巡检 · 一趟推进一步 · 与签到排程互相独立": "毎日定時に巡回 · 1回で1ステップ · チェックインスケジュールとは独立", + "每天定点执行成长任务 · 与签到 / 旅行互相独立 · 默认关": "", + "每天按这里的时刻自动巡检一遍 WorkBuddy 国内版账号:没有猫就领养、在家就派出、到站就领奖。一趟只推进一步,所以建议留两个时刻(早上派出、晚上领奖)。企业版与国际版账号自动跳过。": "ここで指定した時刻に WorkBuddy 中国版アカウントを自動巡回します:猫がいなければ里親登録、待機中なら出発、到着済みなら報酬を受け取ります。1回につき1ステップだけ進むため、2つの時刻(朝に出発・夜に受取)を推奨します。エンタープライズ版と国際版のアカウントは自動的にスキップされます。", + "每天按这里的时刻自动执行 WorkBuddy 国内版账号的成长任务:逐个点亮、完成后领奖。夜猫子任务只有 23:00-08:00(北京时间)能点亮,所以缺省时刻是 23:30。默认关闭 —— 它会上报事件并领积分,请确认要自动执行再打开。企业版与国际版账号自动跳过,不可伪造的任务(如真实捐款)也跳过。": "", "每天时刻": "毎日の時刻", + "每天自动执行": "毎日自動で実行", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "チェックイン1回ごとに · アカウントごとに1行 · 結果と収益はバックエンドの記録が正", + "每日活动签到(+100 积分,已领则中性提示)": "毎日のアクティビティ签到(+100 クレジット、受領済みなら中立の案内)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "毎日のアクティブタスク · Webチャネルの会話で日活を達成、ポイントは受け取りで加算 · 両方は別々に実行可能", + "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "毎日ログインポイント · あわせて会員アクティビティを受け取り · ゲストアカウントはポイント体系なし(スキップ)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "デイリー特典 · 受け取り → 確認 → 読み戻し検証 · 北京時間の自然日でリセット", "每日签到": "デイリーチェックイン", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "スケジュールごとに時刻と範囲 · 当日はそれぞれ重複排除 · 起動時に自動補完", "每账号一条明细 · 点此查看「签到记录」": "アカウントごとに1行 · 「チェックイン記録」を開く", + "没有可签到的账号": "チェックインできるアカウントがありません", "没有可领套餐的账号(ZCode 未添加)。": "受け取れるプランのあるアカウントがありません(ZCode 未追加)。", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "受け取れる特典のあるアカウントがありません(CodeArts / ZCode が未追加)。", + "没有已停运但仍可查余额的账号(目前有 QClaw 一家)。": "", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "成長タスクに対応するアカウントがありません(WorkBuddy 中国版が必要)。", "没有支持新手福利的账号": "初心者ボーナスに対応するアカウントがありません", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "初心者ボーナスに対応するアカウントがありません —— 現在は Loomy、小浣熊、CodeArts の 3 社です。", "没有符合筛选条件的签到记录": "絞り込み条件に一致するチェックイン記録がありません", + "没有随登录自动发放积分的账号(目前有 QClaw 一家)。": "", "没有需要执行的任务": "実行するタスクはありません", + "活动奖励(30 天 / 7 天有效期)+ 下载客户端奖励 · 两条链都幂等": "", + "活动尚未开始(上游接口未部署)": "キャンペーンは未開始です(上流のエンドポイントが未デプロイ)", + "活动按自然日发新套餐,每天可领一次": "キャンペーンは暦日ごとに新しいプランを配布します。受け取りは 1 日 1 回までです", "活动福利": "イベント特典", "活动(campaign)领取 · 每天 10:00 刷新": "イベント(campaign)の受け取り · 毎日 10:00 に更新", "活跃上报与保活均未成功,且活动未开启": "アクティビティの報告とキープアライブの両方に失敗し、キャンペーンも未実施です", @@ -183,17 +250,26 @@ "清空签到记录": "チェックイン記録をクリア", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "「プラン明細」で受け取ったプランと有効になる時期を確認できます。キャンペーン期間中は毎日また受け取れます", "点击行展开账号明细 · 单账号签到不受范围限制": "行をクリックしてアカウントの詳細を展開 · 単一アカウントのチェックインは範囲の制限を受けません", + "点进去查看任务并领取": "タップしてタスクを確認し受け取る", "独立链路 · 只提供手动入口 · 不进自动签到": "独立した経路 · 手動入口のみ · 自動チェックインには含まれない", "猫猫旅行": "ねこ旅行", "猫猫旅行失败:{error}": "ねこ旅行に失敗:{error}", "猫猫旅行失败:{message}": "ねこ旅行に失敗:{message}", + "猫猫旅行定时排程": "猫猫旅行スケジュール", "猫猫旅行:{message}": "ねこ旅行:{message}", + "猫猫旅行:{n} 个动作,+{credit} 积分": "猫猫旅行:{n} 件のアクション、+{credit} クレジット", + "猫猫旅行:{n} 个动作,+{credit} 积分,{failed} 个失败": "猫猫旅行:{n} 件のアクション、+{credit} クレジット、{failed} 件失敗", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "無料モデルとWebチャネルの会話でアカウントのアクティブ状態を維持 · 報酬は受け取らない", "确定删除「{name}」?删除后到点不再执行。": "「{name}」を削除しますか?削除後は予定時刻に実行されません。", "确定清空全部签到记录?清空后无法恢复。": "すべてのチェックイン記録をクリアしますか?クリア後は復元できません。", "确定清空当前筛选出的签到记录?清空后无法恢复。": "現在の絞り込みに一致するチェックイン記録をクリアしますか?クリア後は復元できません。", + "福利与套餐的逐项领取在桌面端签到中心完成": "", + "积分随登录到账 · 无需签到 · 只读:余额与到期时间由上游直接给出": "", + "积分随登录自动发放,无需签到": "", "移除 {name}": "{name} を削除", "立即全部签到": "今すぐ一括チェックイン", + "立即巡检全部": "今すぐ全件巡回", + "立即执行全部": "", "签到": "チェックイン", "签到中…": "チェックイン中…", "签到中心分区": "チェックインセンターのセクション", @@ -210,6 +286,7 @@ "结算于 {time}": "{time} に確定", "能量 {n}": "エネルギー {n}", "腾讯每日签到接口 · 仅国内版": "Tencent のデイリーチェックイン API · 中国版のみ", + "自动发放": "", "至少勾一家才能保存(不勾等于这条条目不签到)": "保存するには 1 社以上選択してください(未選択=このエントリは何もチェックインしません)", "读取定时签到数据失败:{error}": "定期チェックインデータの読み込みに失敗しました:{error}", "读取签到数据失败:{error}": "チェックインデータの読み込みに失敗しました:{error}", @@ -219,6 +296,9 @@ "还没执行过": "まだ実行されていません", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "スケジュールがまだありません —— 「スケジュールを追加」で作成するか、「チェックインセンター」で手動チェックインしてください。", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "チェックイン記録はまだありません —— 手動チェックインまたは定期実行の後に、明細がここに表示されます。", + "还没有记录": "記録はまだありません", + "这个账号没有新手任务": "このアカウントに初心者タスクはありません", + "这些提供商已停止发放积分 · 余额在原有效期内仍可用 · 只读:余额与到期时间由上游直接给出": "", "连登 {n} 天": "連続ログイン {n} 日", "部分": "一部", "重签": "再チェックイン", @@ -231,8 +311,11 @@ "领取": "受け取る", "领取中": "受け取り中", "领取中…": "受け取り中…", + "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "無料 CodingPlan を取得(オンデマンド:プラン有効時はクォータを読むだけ)", "领取失败": "受け取りに失敗", "领取失败:{error}": "受け取りに失敗しました:{error}", + "领取每日积分 · 幂等(当天已领回 0,不报错)": "", + "领取通常需要过一次滑块验证;活动套餐多为当天稍晚生效,领取后可在桌面端「账号」里看套餐明细。": "受け取りには通常スライダー認証が必要です。キャンペーンのプランは当日の遅い時間に有効になることが多く、受け取り後はデスクトップ版「アカウント」でプラン明細を確認できます。", "(共 {n} 个账号)": "(全 {n} アカウント)", "(能量 / 连登读数本次未取到)": "(エネルギー/連続ログインの数値は今回取得できませんでした)", ",日活保活 {n} 个": "、日活キープアライブ {n} 件" diff --git a/desktop-tauri/i18n/i18n-src/ja/common.json b/desktop-tauri/i18n/i18n-src/ja/common.json index e2f46119..8f18b1db 100644 --- a/desktop-tauri/i18n/i18n-src/ja/common.json +++ b/desktop-tauri/i18n/i18n-src/ja/common.json @@ -1,7 +1,12 @@ { "无可用账号:{reason}": "利用可能なアカウントがありません:{reason}", "AI 助手": "AI アシスタント", + "Agent": "", + "Agent 接管": "", "Agent2API · 多提供商本地网关": "Agent2API · マルチプロバイダーのローカルゲートウェイ", + "QQ 群号": "QQ グループ番号", + "Webhook 地址(https://…)": "", + "{base} / {cat}": "", "{key} 需要 true / false / null": "{key} は true / false / null のいずれかである必要があります", "{key} 需要数字": "{key} は数値である必要があります", "{n} 个启用": "{n} 件有効", @@ -12,39 +17,76 @@ "{n} 个账号启用": "{n} 件のアカウントが有効", "{n} 条失败请求未读,点开「请求日志」查看": "{n} 件の失敗リクエストが未読です。「リクエストログ」で確認できます。", "{n} 条错误日志未读,点开「日志」查看": "{n} 件のエラーログが未読です。「ログ」で確認できます。", + "{n} 项 · {shown} 显示": "{n} 項目 · {shown} 表示", + "{size} · 下载后可直接安装,无需跳转浏览器": "{size} · ダウンロード後そのままインストールできます(ブラウザに移動しません)", + "{size} · 点击下载后可直接安装,无需跳转浏览器": "{size} · ダウンロード後すぐにインストールできます(ブラウザに移動しません)", + "{total} 个菜单 · 显示 {shown} · 隐藏 {hidden} · {groups} 个分组": "メニュー {total} 件 · 表示 {shown} · 非表示 {hidden} · グループ {groups} 件", "· 不会改动任何配置": "· 設定は一切変更しません", "· 每次执行前列改动清单,等你确认": "· 実行前に変更内容を一覧表示し、確認を待ちます", "—": "—", "★ 下一单走它": "", "✅ Token 已刷新": "✅ Token を更新しました", "✅ 已将账号优先级调整到全局第一位": "✅ アカウントの優先度をグローバルキューの先頭に変更しました", + "✅ 测试消息已发出,请查看对应群": "", + "✅ 通知设置已保存": "", "「{name}」匹配到多个账号:{list},请用完整账号名重试": "「{name}」が複数のアカウントに一致しました:{list}。完全なアカウント名で再試行してください。", "「工具全不勾选 → 不注入系统提示词」:想当纯聊天用时,不必再去关上面的开关。": "「ツールをすべて外す → システムプロンプトは注入されない」:純粋なチャットとして使いたいときは、上のスイッチを切る必要はありません。", + "「设置」是固定项,含它的分组不能整组隐藏;可以单独隐藏组内其它菜单": "「設定」は固定項目のため、そのグループはまとめて非表示にできません。他のメニューは個別に非表示にできます", + "一个都不勾 = 全部订阅(配好渠道就能收到东西)": "", + "下拉刷新": "引いて更新", "不限模型": "", + "与电脑端一致:每 {n} 分钟": "デスクトップ版と同じ:{n} 分ごと", + "主导航": "メインナビゲーション", "代价": "コスト", "代理:池": "", + "会恢复默认的分组、顺序与显隐设置。你自建的分组会被删除(组内菜单不会丢)。": "グループ・順序・表示設定を既定に戻します。作成したグループは削除されます(メニューは失われません)。", "会话内": "セッション単位", + "会话已过期。重新登录后会自动回到手机端。": "セッションが切れました。再度ログインするとモバイル版に自動で戻ります。", "会话粘性": "セッション粘着", "低于限制器的阈值,转发时跳过该账号": "", "余额不足或 Token 周期用量到顶,被限制器拦下,自动恢复后回来": "", "余额优先": "", "作为 system 消息注入给助手": "system メッセージとしてアシスタントに注入", + "例如:家里、公司": "例:自宅、会社", + "侧边栏布局模块未加载(ui/sidebar-layout.js 应在 app.js 之前引入)。": "サイドバーのレイアウトモジュールが読み込まれていません(ui/sidebar-layout.js を app.js より前に読み込む必要があります)。", + "侧边栏没有显示任何菜单。到「设置 → 显示 → 侧边栏菜单」里打开需要的项。": "サイドバーにメニューが表示されていません。「設定 → 表示 → サイドバーのメニュー」で必要な項目をオンにしてください。", + "侧边栏菜单": "サイドバーのメニュー", "保存对话模型失败:{message}": "対話モデルの保存に失敗:{message}", + "保存并继续": "保存して続行", "候选队列": "", "停止": "停止", + "先添加一台网关": "まずゲートウェイを追加してください", "全不选": "すべて解除", "全局队列位次(转发按策略在此序上再排)": "", + "全局(账号表)": "", + "共 {n} 条记录": "全 {n} 件", + "共 {total} 个账号,已读到 {queried} 个": "", "关": "オフ", + "关掉时不发任何消息(配置保留)。默认关 —— 没配好就发是打扰。": "", "冷却": "", + "出事了主动推给你(桌面端常驻托盘,不必自己盯着看)": "", + "分组名": "グループ名", + "分组名,例如「常用」": "グループ名(例:「よく使う」)", "列表 = 模型管理页里所有已启用的模型。助手走本地网关发起对话,消耗所选账号的额度。": "一覧は「モデル管理」ページで有効なすべてのモデルです。アシスタントはローカルゲートウェイ経由で対話し、選択したアカウントの枠を消費します。", + "创建并进入": "作成して開始", + "创建管理员账号": "管理者アカウントを作成", "删除 {n} 条": "{n} 件を削除", + "删除分组": "グループを削除", + "删除分组「{name}」?组内 {n} 个菜单会移到「{target}」,不会丢。": "グループ「{name}」を削除しますか?{n} 件のメニューは「{target}」へ移動するので失われません。", + "删除空分组「{name}」?": "空のグループ「{name}」を削除しますか?", + "删除这个分组(组内菜单移到第一组)": "このグループを削除(メニューは最初のグループへ移動)", "加载助手设置失败:{message}": "アシスタント設定の読み込みに失敗:{message}", "加载失败:{error}": "読み込みに失敗しました:{error}", + "加载更早的记录": "以前の記録を読み込む", "助手": "アシスタント", "助手自己用哪个模型生成回复": "アシスタントが返答の生成に使うモデル", "助手设置读取失败,工具可能已过时。": "アシスタント設定の読み取りに失敗しました。ツールが古い可能性があります。", "助手走本地网关,消耗所选账号的额度": "アシスタントはローカルゲートウェイ経由で、選択したアカウントの枠を消費します", "勾选即注入该工具;不勾选 = 助手看不到它": "チェックしたツールが注入されます。チェックを外すとアシスタントからは見えません", + "升序": "", + "单签": "個別", + "去下载": "ダウンロード", + "去登录": "ログインへ", "参数不完整(需要 account / enabled)": "パラメータが不足しています(account / enabled が必要)", "参数不完整(需要 account / position)": "パラメータが不足しています(account / position が必要)", "参数不完整(需要 provider / id / alias)": "パラメータが不足しています(provider / id / alias が必要)", @@ -52,21 +94,34 @@ "参数不完整(需要 provider / id / enabled)": "パラメータが不足しています(provider / id / enabled が必要)", "参数不完整(需要 provider / position)": "パラメータが不足しています(provider / position が必要)", "发现新版本 {latest}(当前 {current}),点开「设置 - 软件更新」可查看更新日志并下载": "新バージョン {latest} が見つかりました(現在 {current})。「設定 - ソフトウェア更新」を開くと更新履歴を確認してダウンロードできます", + "发现新版本 {version}": "新しいバージョン {version} があります", "发送": "送信", + "发送测试": "", "只改映射名,不动上游模型 ID;映射立即生效(新对话即可用)。": "変わるのはマッピング名だけで、アップストリームのモデル ID はそのままです。マッピングはすぐ反映されます(新しい対話から利用可能)。", "只注入工具定义": "ツール定義のみ注入", "只读": "読み取り専用", "只读工具": "読み取り専用ツール", "可用": "件利用可", + "含固定项「设置」,不可整组隐藏(可以单独隐藏组内其它菜单)": "固定項目「設定」を含むため、グループごと非表示にはできません(他のメニューは個別に非表示にできます)", "启用后重新参与转发": "", + "启用通知": "", "四种组合的实际效果": "4 通りの組み合わせの実際の効果", "回复由 {model} 生成 · 走本地网关": "返答は {model} が生成 · ローカルゲートウェイ経由", + "固定": "固定", "在全局队列里上移一位": "", "在全局队列里下移一位": "", + "地址已遮住 token(保存时不动它);要更换请整体重新粘贴": "", + "备注名(可留空)": "", + "备注名(可选)": "表示名(任意)", "复制失败,请手动选择复制": "コピーに失敗しました。手動で選択してコピーしてください", "复制订阅链接": "サブスクリプションリンクをコピー", "失败 · {status}": "失敗 · {status}", "失败退避": "失敗バックオフ", + "奖励 {bonus}": "ボーナス {bonus}", + "套餐 {plan}": "プラン {plan}", + "安装": "インストール", + "安装包已就绪,点「安装」即可完成更新": "インストールパッケージの準備ができました。「インストール」で更新が完了します", + "定时": "定期", "定时任务": "定期タスク", "实际注入": "実際の注入内容", "对话模型": "対話モデル", @@ -80,118 +135,203 @@ "将把账号「{account}」移到第 {position} 顺位:": "アカウント「{account}」を {position} 番目に移動します:", "将改动 {n} 条映射({parts}):": "マッピング {n} 件を変更します({parts}):", "尚未登录": "まだログインしていません", - "已保存": "保存しました", + "展开作用域栏": "", + "展开左栏,看 {label} 下的 {n} 个模型": "", + "展开来自{label}的 {n} 个模型": "", + "已下载,待安装": "ダウンロード済み・インストール待ち", "已修改 {n} 个模型": "{n} 個のモデルを変更しました", "已修改 {n} 个账号": "{n} 個のアカウントを変更しました", "已修改 {n} 项能力": "能力 {n} 項目を変更しました", "已停止这次回答。": "この回答を停止しました。", "已全部取消勾选": "すべてのチェックを外しました", + "已关闭自动检查": "自動チェックをオフにしました", + "已删除分组「{name}」": "グループ「{name}」を削除しました", "已勾选 {n} / {total}": "{n} / {total} 選択中", "已勾选「{label}」": "「{label}」を選択しました", "已勾选工具": "ツールあり", "已取消「{label}」": "「{label}」の選択を解除しました", + "已启动安装器": "インストーラーを起動しました", + "已复制{label}": "", "已复制:{text}": "コピーしました:{text}", + "已完成 {percent}%": "", + "已开启自动检查": "自動チェックをオンにしました", "已恢复推荐勾选": "推奨のチェックに戻しました", + "已恢复默认分组:概览 / 转发链路 / 诊断 / 系统": "既定のグループに戻しました:概要 / 転送経路 / 診断 / システム", "已改动 {n} 条映射": "マッピング {n} 件を変更しました", + "已新建分组「{name}」,点组头的 + 把菜单移进来": "グループ「{name}」を作成しました。見出しの + でメニューを移動できます", + "已是最新版本": "最新バージョンです", "已注入 {n} / {total} 个工具 · 系统提示词:{state}": "ツール {n} / {total} 個を注入 · システムプロンプト:{state}", + "已添加 {n} 台": "", + "已添加的服务器": "追加済みのサーバー", "已登录": "ログイン済み", + "已离线 · 显示的是缓存内容": "オフライン — キャッシュを表示中", "已移到第 {n} 顺位": "{n} 番目に移動しました", "已试过": "", "并发上限 {n}": "", "并发已满,等一个在途回落再参与": "", "并发满": "", "开": "オン", + "当前": "現在", "当前一个工具都没勾选,助手只能纯聊天。去「设置 → AI 助手 → 工具」里勾上需要的工具。": "ツールが 1 つも選ばれていないため、アシスタントはチャットしかできません。「設定 → AI アシスタント → ツール」で必要なツールにチェックしてください。", + "当前版本 {version}": "現在のバージョン {version}", + "当前环境不支持读取调度队列": "", "当前:已关闭(助手不收到任何系统提示词,工具定义照发)": "現在:オフ(アシスタントにシステムプロンプトは送られませんが、ツール定義は送信されます)", "当前:已开启 · 使用内置默认提示词(还没自定义)": "現在:オン · 組み込みの既定プロンプトを使用中(未カスタマイズ)", "当前:已开启 · 自定义内容 {n} 字": "現在:オン · カスタム内容 {n} 文字", "影响转发": "転送に影響", "待确认": "確認待ち", + "总剩余 {total}": "残り合計 {total}", "恢复上游原值": "アップストリームの元の値に戻す", "恢复推荐": "推奨に戻す", + "恢复默认侧边栏": "サイドバーを既定に戻す", + "成功领取": "受取済み", "我会在动手前把改动列给你确认。": "変更内容は実行前に一覧で示し、確認を待ちます。", + "所有菜单都已在这一组": "すべてのメニューがこのグループにあります", + "手机端打开": "", "打开账号设置": "", "执行": "実行", + "把菜单移入 {name}": "メニューを {name} へ移動", + "把菜单移入「{name}」": "メニューを「{name}」へ移動", "报表": "レポート", + "拖动分组 {name}": "グループ {name} をドラッグ", + "拖动左侧 ⠿ 调整顺序(菜单可以跨组拖);拨开关控制显隐;点分组名可重命名;组头的 + 把别的菜单移进来;眼睛按钮隐藏整组。改动即时生效并记住,左侧侧边栏就是结果。键盘也能排序:把手聚焦后按 Alt+↑ / ↓。": "左側の ⠿ をドラッグして順序を変更(メニューはグループをまたげます)。スイッチで表示/非表示、グループ名をクリックで名前変更、グループ見出しの + で他のメニューを移動、目のボタンでグループごと非表示。変更は即座に反映され保存されます(左のサイドバーが結果です)。キーボード操作:ハンドルにフォーカスして Alt+↑ / ↓。", + "拖动菜单 {name}": "メニュー {name} をドラッグ", + "拖动调整分组顺序(Alt+↑ / ↓ 也可)": "ドラッグでグループの順序を変更(Alt+↑ / ↓ でも可)", "拖动调整队列顺序": "", + "拖动调整顺序,可跨组(Alt+↑ / ↓ 也可)": "ドラッグで順序を変更、グループをまたげます(Alt+↑ / ↓ でも可)", + "按作用域选择:全局账号表,或某一个模型的调度视图": "", + "按厂商": "", "按模型展开队列": "", "按请求的模型展开 —— 与转发选路同一份排序,下一个请求会走 ★ 那位": "", + "按配置顺序逐个发送;一个失败不影响其它": "", "排第 1 的接走所有请求,直到它不能答(限流 / 故障 / 额度尽)才轮到下一位 —— 就是「从上往下」的现状行为。": "1 番目のアカウントが答えられなくなる(レート制限 / 障害 / 枠切れ)まで全リクエストを受け持ち、そこで初めて次に回ります —— 今までどおりの「上から順」の動作です。", + "接口文档": "", + "控制主侧边栏显示哪些菜单、按什么顺序、怎么分组。隐藏只影响导航显示,不会撤销页面访问权限;被隐藏的页面仍可通过地址直接打开。": "サイドバーに表示するメニュー、その順序、グループ分けを設定します。非表示はナビゲーションの表示だけに影響し、アクセス権は失われません。非表示にしたページも URL から直接開けます。", "提示词": "プロンプト", "提示词开关": "プロンプトのスイッチ", + "搜索模型或厂商": "", + "搜索模型或厂商…": "", + "收起作用域栏": "", + "收起来自{label}的 {n} 个模型": "", "改动立即生效;禁用后指向它的请求会落到下一个候选。": "変更はすぐ反映されます。無効化すると、それ宛のリクエストは次の候補に回ります。", "改动类工具": "変更系ツール", "改成": "変更後", "文档": "ドキュメント", "新": "新", - "新增": "追加", + "新分组名": "新しいグループ名", "新增 {n} 条": "{n} 件を追加", "新对话": "新しい対話", + "新建分组": "グループを追加", "无凭证": "", "日志": "ログ", "映射名": "マッピング名", + "显示 {name}": "{name} を表示", + "显示分组 {name}": "グループ {name} を表示", + "显示这一组": "このグループを表示", "智能": "AI", + "更多": "その他", + "最先作废的一份到期日": "", "最大化": "最大化", "最小化": "最小化", + "最早 {day} 到期": "", "最早 {time} 恢复": "", "有": "あり", + "有新版本,重启后生效": "新しいバージョンがあります。再起動で反映", + "服务器": "サーバー", "未同步": "未同期", "未就绪": "準備未完了", "未登录": "未ログイン", "未知工具「{name}」:它在设置里可能刚被取消勾选,或这一版助手还不认识它。": "不明なツール「{name}」:設定でチェックを外したばかりか、このバージョンのアシスタントがまだ認識していない可能性があります。", + "未选择服务器": "サーバー未選択", "本周期消耗达到限制器上限,转发时跳过": "", "本地网关监听状态": "ローカルゲートウェイのリッスン状態", + "松开刷新": "離して更新", "查余额": "", "查看参数": "パラメータを表示", "查看结果": "結果を表示", "查询该账号的余额": "", "桌面桥不可用(本页需要在 Agent2API 客户端里打开)": "デスクトップブリッジが利用できません(このページは Agent2API クライアント内で開く必要があります)", + "模型清单为空:先去模型管理页点「获取模型」": "", "模型管理": "モデル管理", "次熔断": "", + "正在下载…": "ダウンロード中…", + "正在完成人机验证…": "認証を完了しています…", + "正在检查…": "確認中…", "正在识别二维码…": "QR コードを認識中…", + "正在连接…": "接続中…", + "每台服务器的登录状态各自独立保存,切换后不用重新登录。地址改掉后需要重新登录。": "ログイン状態はサーバーごとに個別に保存されるため、切り替え直しても再ログインは不要です。アドレスを変更した場合は再ログインが必要です。", "每条请求轮换到下一个可用账号;新会话交给最近最闲的那位。适合想把手里的号雨露均沾。": "", "每轮": "毎ターン", "没有已启用的模型": "", + "没有带余额概念的账号。添加账号后,这里会显示各账号的额度余量与到期。": "", "没有找到提供商「{provider}」的账号": "プロバイダー「{provider}」のアカウントが見つかりません", "没有找到账号「{name}」": "アカウント「{name}」が見つかりません", "没给账号名": "アカウント名が指定されていません", "注入系统提示词": "システムプロンプトを注入", + "活动": "キャンペーン", "浅色": "ライト", + "测试未全部成功,详见下方": "", "消耗该账号额度": "このアカウントの枠を消費します", "消耗额度": "枠を消費", "深色": "ダーク", + "添加": "追加", + "添加服务器": "サーバーを追加", + "添加渠道": "", "清空 = 回到内置默认(与「恢复默认」等价)。": "空にすると組み込みの既定に戻ります(「デフォルトに戻す」と同じ)。", + "渠道类型": "", "点「编辑」弹窗改内容;开关关掉就整段不发。": "「編集」をクリックするとダイアログで内容を変更できます。スイッチを切ると丸ごと送信されません。", + "点击重命名": "クリックで名前を変更", "熔断": "サーキットブレーカー", "熔断时长(秒)": "", + "版本 {version} · 从 GitHub 直接下载安装包": "バージョン {version} · GitHub から直接ダウンロードします", + "版本 {version} · 从网关下载安装包": "バージョン {version} · ゲートウェイからパッケージをダウンロードします", + "版本 {version} · 更新包需从 Release 页面获取": "バージョン {version} · 更新パッケージは Release ページから取得してください", "用一句话让我查或改本机的模型转发配置。": "一言で指示してください。本機のモデル転送設定を調べたり変更したりします。", + "用量与额度": "", "用量占比最低的先用;没有读数的账号按队列位次排在有读数的后面。适合「谁空谁上」。": "", + "电脑端已关闭「软件版本检查」,此处只做手动检查": "デスクトップ版で「ソフトウェアのバージョン確認」がオフのため、ここでは手動チェックのみ行います", + "登录面板": "ログイン", "监听 127.0.0.1": "127.0.0.1 でリッスン中", "直接说要改什么 / 查什么…(Enter 发送,Shift+Enter 换行)": "変更したいこと / 調べたいことをそのまま入力…(Enter で送信、Shift+Enter で改行)", + "看这个模型的调度队列:{tip}": "", "确定删除账号「{name}」?{note}": "アカウント「{name}」を削除しますか?{note}", "确认后立即生效": "確認後すぐに反映されます", "禁用会把账号移出转发队列;若这条线路没有别的可用账号,请求会直接失败。": "無効化するとアカウントは転送キューから外れます。この経路に他に使えるアカウントがなければ、リクエストはそのまま失敗します。", "禁用后不参与转发,余额仍可查": "", + "移入「{name}」": "「{name}」へ移動", + "稍后": "あとで", + "空分组 · 点组头的 + 把菜单移进来": "空のグループ · 見出しの + でメニューを移動できます", + "等待窗口": "受取可能時刻待ち", "等这一轮跑完…": "このターンが終わるまでお待ちください…", "签到中心": "チェックインセンター", "系统提示词 + 工具定义": "システムプロンプト + ツール定義", "系统提示词已保存": "システムプロンプトを保存しました", "系统提示词已关闭": "システムプロンプトをオフにしました", "系统提示词已开启": "システムプロンプトをオンにしました", + "编辑服务器": "サーバーを編集", "编辑系统提示词": "システムプロンプトを編集", "网关 Key": "ゲートウェイキー", "网关运行中": "ゲートウェイ稼働中", "网络代理": "ネットワークプロキシ", "能力位决定下游看到的声明(能不能贴图、能塞多少上下文),不改上游的真实行为。": "能力フラグは下流に見える宣言(画像入力の可否、コンテキスト量)を決めるもので、アップストリームの実際の挙動は変わりません。", "自动": "自動", + "至少要保留一个分组": "グループは最低 1 つ必要です", + "补签": "補填", + "订阅哪些事": "", "订阅里的节点会自动同步进下方代理池,账号即可引用。": "", "订阅里的节点会被内核起成本地监听器,镜像到下方代理池(账号按 id 引用,与本页其它出口同一用法)。": "", "订阅链接必须是 http:// 或 https:// 开头": "サブスクリプションリンクは http:// または https:// で始まる必要があります", "设置": "設定", + "设置不可隐藏(隐藏了只能清缓存找回来)": "「設定」は非表示にできません(戻すにはデータの削除が必要になります)", "诊断": "診断", + "请允许安装未知来源应用后重试": "「提供元不明のアプリ」のインストールを許可してから再試行してください", + "请在 Release 页面下载后通过 TestFlight 或 App Store 安装": "Release ページからダウンロードし、TestFlight または App Store 経由でインストールしてください", + "请填一个分组名": "グループ名を入力してください", "请填写订阅名称": "サブスクリプション名を入力してください", "请求失败:{message}": "リクエスト失敗:{message}", "请求日志": "リクエストログ", + "读不到完整模型清单:{reason}": "", + "读取通知设置失败:{reason}": "", "账号不在当前队列里": "アカウントが現在のキューにありません", "账号已删除": "アカウントを削除しました", "账号已在全局队列第一位": "アカウントはすでにグローバルキューの先頭です", @@ -200,29 +340,54 @@ "转发失败的账号休息一会儿再回来(连续失败翻倍,上限 1 小时)": "", "转发能力": "転送機能", "轮流均摊": "", + "输入这台网关的管理员账号与密码。": "このゲートウェイの管理者アカウントとパスワードを入力してください。", + "运行日志": "", "运行正常 · {url}": "正常稼働中 · {url}", "运行状态": "稼働状態", + "返回": "", "还有额度时被 429 的账号排到队尾,轮一圈才回来 —— 负载摊开而不是每次回到第 1 个": "", + "还没有可用模型(添加账号后可见)": "", + "还没有渠道。点右上角「添加渠道」,把群机器人的 Webhook 地址填进来。": "", + "还没有签到记录": "チェックイン記録はまだありません", "还没有订阅:粘贴订阅链接,或从图片识别二维码导入。": "", "还没有选择对话模型:先在下方选一个(或去「设置 → AI 助手」里选)。": "対話モデルが未選択です:下から選んでください(または「設定 → AI アシスタント」で選択)。", + "还没查到余额。到「账号」页点一次「查询余额」,或等自动查询跑完。": "", + "这一版没有提供 Android 安装包": "このバージョンには Android 用パッケージがありません", + "这一项已隐藏": "この項目は非表示です", + "这一项正在显示": "この項目は表示中です", "这个模型下没有可用账号": "", + "这个面板没有管理员,用一把已启用的 API Key 访问(在桌面端「网关 Key」页创建)。": "このパネルには管理者がありません。有効な API Key でアクセスしてください(デスクトップ版「ゲートウェイキー」で作成)。", "这会真实发起上游请求并消耗额度": "実際にアップストリームへリクエストを送り、枠を消費します", + "这台网关没有管理员,用一把已启用的 API Key 访问(在桌面端「网关 Key」页创建)。": "このゲートウェイには管理者がいません。有効な API Key でアクセスしてください(デスクトップ版の「ゲートウェイ Key」ページで作成できます)。", + "这台网关还没有管理员。设置一个账号密码,之后用它登录。": "このゲートウェイにはまだ管理者がいません。アカウントとパスワードを設定し、それでログインしてください。", + "这家没有返回可用明细(可能额度都在上游侧,或全部已用尽)": "", "这是真实的上游请求,与生产走同一条链路(会记账,报表排除)。": "これは実際のアップストリームリクエストで、本番と同じ経路を通ります(課金されますが、レポートからは除外)。", "这段文本作为 system 消息排在对话最前面。工具的定义与当前配置概览由网关自动附加,不用写在这里。": "この文章は system メッセージとして対話の先頭に置かれます。ツール定義と現在の設定概要はゲートウェイが自動で付加するため、ここに書く必要はありません。", "连接 {n}": "", + "连接并保存": "接続して保存", "连续失败到阈值后熔断一段时间;到点放行试一次,连续成功才算恢复": "", "连续失败阈值": "", "连通 · {ms}ms": "接続 OK · {ms}ms", "选择对话模型…": "", "选择模型…": "モデルを選択…", + "通知与告警": "", + "通知渠道": "", "都不注入": "どちらも注入しない", "都不注入(助手退化为纯聊天)": "どちらも注入しない(アシスタントは純粋なチャットに)", + "重启更新": "再起動して更新", "队列是一次性整表提交(后端按新顺序重编优先级),移动会连带调整其它组的相对次序。": "キューは全体を一括で送信します(バックエンドが新しい順序で優先度を振り直すため)、移動すると他のグループの相対順も変わります。", "队列是一次性整表提交;位置越靠前,路由时越先被选中。": "キューは全体を一括で送信します。位置が前ほど、ルーティング時に先に選ばれます。", + "降序": "", "限流 · {n} 个模型": "", "限流垫底": "レート制限で後ろへ", + "隐藏 {name}": "{name} を非表示", + "隐藏分组 {name}": "グループ {name} を非表示", + "隐藏这一组": "このグループを非表示", + "需要网关 API Key": "ゲートウェイ API Key が必要です", "顺序优先": "順序優先", + "首页": "ホーム", "高危": "高リスク", + "({n} 个整组隐藏)": "({n} グループを非表示)", "(不会影响客户端登录态)": "(クライアントのログイン状態には影響しません)", "(仅显示前 20 条,共 {n} 条)": "(先頭 20 件のみ表示 · 全 {n} 件)", "(未指定)": "(未指定)", diff --git a/desktop-tauri/i18n/i18n-src/ja/docs.json b/desktop-tauri/i18n/i18n-src/ja/docs.json index 21a02245..69f89d49 100644 --- a/desktop-tauri/i18n/i18n-src/ja/docs.json +++ b/desktop-tauri/i18n/i18n-src/ja/docs.json @@ -1,18 +1,28 @@ { " 或 ": " または ", "Base URL 填进客户端的「API 地址 / Base URL」栏;对话协议按客户端支持的类型三选一(Chat Completions / Responses / Anthropic Messages),三者共用同一套模型与账号池,可随时切换;鉴权用「网关 Key」页里任一启用 Key。": "Base URL はクライアントの「API アドレス / Base URL」欄に記入します。対話プロトコルはクライアントが対応している種類を三つ(Chat Completions / Responses / Anthropic Messages)の中から一つ選んでください。三者は同じモデルとアカウントプールを共有し、いつでも切り替えられます。認証には「ゲートウェイキー」ページで有効な Key をいずれか一つ使用します。", + "Claude Code / Anthropic SDK": "", "Claude Code / Anthropic SDK(token 计数:/v1/messages/count_tokens)": "Claude Code / Anthropic SDK(token カウント:/v1/messages/count_tokens)", + "Codex、新版 SDK": "", "OpenAI 新协议(Codex、新版 SDK)": "OpenAI の新しいプロトコル(Codex、新版 SDK)", + "curl 命令": "", "。一把启用的 Key 都没有时不鉴权(默认网关只监听 127.0.0.1;在设置页开启「局域网访问」后监听所有网卡,鉴权与安全闸门随之启用)。": "。有効な Key が一つもない場合は認証を行いません(既定ではゲートウェイは 127.0.0.1 のみをリッスンします。設定ページで「LAN アクセス」を有効にするとすべてのネットワークインターフェースをリッスンし、認証とセキュリティゲートも有効になります)。", + "。用「网关 Key」页里任一启用的 Key;一把都没有时不鉴权。": "", + "不受鉴权限制,配 Key 前就能拉": "", "启用": "有効", "填进客户端的「API 地址 / Base URL」": "クライアントの「API アドレス / Base URL」に記入", + "复制命令": "", "复制地址": "アドレスをコピー", "多数 OpenAI 兼容客户端的默认协议": "多くの OpenAI 互換クライアントの既定プロトコル", + "多数兼容客户端的默认协议": "", "客户端自动拉取可用模型;不受鉴权限制,配 Key 之前就能拉": "クライアントが利用可能なモデルを自動で取得します。認証の制限を受けないため、Key を設定する前でも取得できます", "对话协议(按客户端支持的类型选一行)": "対話プロトコル(クライアントが対応する種類の行を一つ選択)", + "快速验证": "", + "手机端只读文档。完整的使用说明与部署指引在仓库的 docs/ 目录。": "", "接口地址": "エンドポイントアドレス", "模型列表": "モデル一覧", "模型清单": "モデルリスト", "的 Key:请求带 ": "な Key をいずれか一つ認証に使用:リクエストに ", + "请求带 ": "", "鉴权用「网关 Key」页里任一": "「ゲートウェイキー」ページで" } diff --git a/desktop-tauri/i18n/i18n-src/ja/keys.json b/desktop-tauri/i18n/i18n-src/ja/keys.json index 0b38aff7..c40c0a13 100644 --- a/desktop-tauri/i18n/i18n-src/ja/keys.json +++ b/desktop-tauri/i18n/i18n-src/ja/keys.json @@ -7,11 +7,13 @@ "✅ Key 已创建,记得复制给客户端": "✅ Key を作成しました。クライアントへのコピーをお忘れなく", "✅ 可用范围已保存": "✅ 利用範囲を保存しました", "不限制": "制限なし", + "不限制提供商与模型": "", "与": "と", "例如 Cursor / 公司电脑": "例:Cursor / 会社の PC", "保存失败:{reason}": "保存に失敗しました:{reason}", "保存范围": "範囲を保存", "创建": "作成", + "创建于 {time}": "", "创建时间": "作成日時", "删除网关 Key": "ゲートウェイキーを削除", "可用提供商": "利用可能なプロバイダー", @@ -22,13 +24,16 @@ "可用范围 · {name}": "利用範囲 · {name}", "启用「{name}」": "「{name}」を有効化", "复制 Key": "Key をコピー", + "客户端请求带上 ": "", "客户端请求需带 ": "クライアントのリクエストには ", + "已删除": "", "已启用鉴权 · {n} 把 Key 生效": "認証が有効 · {n} 本の Key が有効", "当前不限制:这把 Key 可以用全部提供商与全部对外模型": "現在は制限なし:この Key はすべてのプロバイダーとすべての外部公開モデルを利用できます", "提供商:{names}": "プロバイダー:{names}", "搜索模型…": "モデルを検索…", "操作失败:{reason}": "操作に失敗しました:{reason}", "新建 API Key": "新規 API Key", + "新建 Key 与设置可用范围(提供商 / 模型白名单)在桌面端完成。": "", "显示": "表示", "未启用鉴权": "認証は未有効", "未命名": "名称未設定", @@ -38,9 +43,11 @@ "留空自动生成;手填至少 8 个字符": "空欄なら自動生成;手動入力は 8 文字以上", "留空表示不限制;同时设置时请求需同时满足两个条件(模型在白名单内且路由到允许的提供商)。 「可用模型」的候选跟着上面勾选的提供商走:没勾提供商时它是空的(还没有约束范围), 勾了几家就列出这几家能收的全部对外名。": "空欄なら制限なし;両方を設定した場合、リクエストは 2 つの条件を同時に満たす必要があります(モデルがホワイトリスト内にあり、かつ許可されたプロバイダーにルーティングされる)。「利用可能なモデル」の候補は上で選択したプロバイダーに連動します:プロバイダーを選んでいないときは空です(まだ範囲を制約していないため)、数社を選ぶと、それらのプロバイダーが受け入れられるすべての外部公開名が表示されます。", "确定删除 Key「{name}」?使用它的客户端会立刻无法访问。": "Key「{name}」を削除しますか?使用中のクライアントは直ちにアクセスできなくなります。", + "确定删除「{name}」?用这把 Key 的客户端会立即失效。": "", "网关 Key 表": "ゲートウェイキーテーブル", "请先在上面选择可用提供商": "先に上で利用可能なプロバイダーを選択してください", "读取 Key 列表失败:{reason}": "Key 一覧の読み込みに失敗しました:{reason}", + "还没有 Key。去桌面端「网关 Key」页新建一把。": "", "还没有 Key,当前不鉴权": "Key がまだありません。現在は認証を行いません", "这几家当前没有可用模型(账号未登录或清单为空)": "これらのプロバイダーには現在利用可能なモデルがありません(アカウントが未ログイン、またはリストが空です)", "这把 Key 不限制提供商与模型(可用全部上游与全部对外模型)": "この Key はプロバイダーとモデルを制限しません(すべてのアップストリームとすべての外部公開モデルを利用可能)", @@ -49,5 +56,7 @@ "(模型候选需先选提供商;不选则沿用当前这几项,保存后仍按模型白名单生效)": "(モデルの候補には先にプロバイダーの選択が必要です;選択しない場合は現在の項目をそのまま使用し、保存後もモデルのホワイトリストとして有効です)", "(行内「可用范围」):留空 = 不限制,两个都设时 按交集生效 —— 被限制的模型对这把 Key 表现为「不存在」(拉 /v1/models 也看不到它), 提供它的家不在可用列表里时请求同样被拒。": "(行内の「利用範囲」):空欄 = 制限なし。両方を設定した場合は積集合で適用されます —— 制限されたモデルはこの Key からは「存在しない」ものとして扱われ(/v1/models を取得しても表示されません)、それを提供するプロバイダーが利用可能リストにない場合もリクエストは同様に拒否されます。", "+ 新建 Key": "+ 新規 Key", - "; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制": "; 変更はすぐに反映され、本アプリ自身は最初に有効な Key を自動的に使用します。各 Key で個別に制限できる項目は、" + "; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制": "; 変更はすぐに反映され、本アプリ自身は最初に有効な Key を自動的に使用します。各 Key で個別に制限できる項目は、", + ";一把启用的 Key 都没有时不鉴权。": "", + ";当前必须带 Key。": "" } diff --git a/desktop-tauri/i18n/i18n-src/ja/logs.json b/desktop-tauri/i18n/i18n-src/ja/logs.json index df557883..20eba3bc 100644 --- a/desktop-tauri/i18n/i18n-src/ja/logs.json +++ b/desktop-tauri/i18n/i18n-src/ja/logs.json @@ -10,9 +10,13 @@ "信息": "情報", "全部分类": "すべてのカテゴリ", "全部级别": "すべてのレベル", + "复制失败": "", + "复制当前筛选结果": "", "导出": "エクスポート", + "已复制 {n} 条日志": "", "按分类筛选": "カテゴリで絞り込み", "按最低级别筛选": "最低レベルで絞り込み", + "搜索关键字": "", "搜索消息关键词": "メッセージのキーワードを検索", "搜索消息关键词…": "メッセージのキーワードを検索…", "操作失败:{error}": "操作に失敗しました:{error}", @@ -20,6 +24,7 @@ "暂无日志可导出": "エクスポートできるログがありません", "模型 {model}": "モデル {model}", "正在加载日志…": "ログを読み込んでいます…", + "没有符合条件的日志": "", "没有符合筛选条件的日志": "絞り込み条件に一致するログがありません", "清空中…": "クリア中…", "清空运行日志": "実行ログをクリア", @@ -27,6 +32,7 @@ "确定清空当前筛选出的 {count} 条日志?清空后无法恢复。": "現在の絞り込みで抽出された {count} 件のログをクリアしますか?クリアすると元に戻せません。", "系统事件": "システムイベント", "警告": "警告", + "读取日志失败": "", "调试": "デバッグ", "运行日志已清空": "実行ログをクリアしました", "错误": "エラー" diff --git a/desktop-tauri/i18n/i18n-src/ja/models.json b/desktop-tauri/i18n/i18n-src/ja/models.json index 2cbe0cd6..f8f01cfb 100644 --- a/desktop-tauri/i18n/i18n-src/ja/models.json +++ b/desktop-tauri/i18n/i18n-src/ja/models.json @@ -36,6 +36,7 @@ "{name}({id})": "{name}({id})", "{name}(不在该家当前清单里)": "{name}(このプロバイダーの現在のカタログにありません)", "{n} 个": "{n} 個", + "{n} 个映射": "", "{n} 个模型": "{n} 個のモデル", "{total} 家 · 成功 {done}": "{total} 社 · 成功 {done}", "{total} 家 · 成功 {done} · 失败 {failed}": "{total} 社 · 成功 {done} · 失敗 {failed}", @@ -67,8 +68,10 @@ "上游没有给出可读的错误说明": "アップストリームが読み取り可能なエラー説明を返しませんでした", "上游没有返回任何模型": "アップストリームがモデルを 1 つも返しませんでした", "上游目录尚未拉到,用的是内置静态清单;点「刷新模型清单」可重试": "アップストリームのディレクトリをまだ取得できていません。内蔵の静的カタログを使用しています;「モデルカタログを更新」で再試行できます", + "上游能列出这个模型,但官方客户端不展示它;本仓已默认关闭它的默认绑定(点这一行的开关可以手动打开)": "上流はこのモデルを返しますが、公式クライアントは表示しません。本ゲートウェイは既定バインディングを無効にしています(行のスイッチで手動で有効化できます)", "下游客户端": "ダウンストリームクライアント", "下游请求 ": "ダウンストリームのリクエスト ", + "不展示": "非表示", "不影响网关的转发与路由": "ゲートウェイの転送とルーティングには影響しません", "不支持": "未対応", "不覆盖": "上書きしない", @@ -115,6 +118,7 @@ "刷新全部家(逐家打上游)": "すべてのプロバイダーを更新(各家ごとにアップストリームへ 1 回)", "刷新完成,但没有得到任何结果": "更新は完了しましたが、結果が得られませんでした", "刷新模型管理页里各提供商的远程模型目录,逐家结果列在弹窗里": "モデル管理ページの各プロバイダーのリモートモデルディレクトリを更新し、プロバイダーごとの結果をダイアログに一覧表示します", + "刷新清单": "", "加载中…": "読み込み中…", "勾上它,导入后即进入这家的清单": "チェックを入れると、インポート後にこのプロバイダーのカタログに入ります", "勾几个就并行测几次;默认选队列里最靠前的一个。": "チェックした数だけ並行してテストします;デフォルトではキューの先頭の 1 つを選択します。", @@ -126,6 +130,8 @@ "只刷新这一家的模型清单": "このプロバイダーのモデル一覧だけを更新", "只属于这一家": "このプロバイダーだけに属します", "可删除 {n} 个(手动登记 / 自定义家);": "削除可能 {n} 個(手動登録 / カスタムプロバイダー);", + "可见": "表示", + "可见性": "表示状態", "可配思考档位": "設定可能な思考レベル", "同一条错误出现在所有账号上": "同じエラーがすべてのアカウントで発生する", "后端桥不可用": "バックエンドブリッジが利用できません", @@ -140,6 +146,7 @@ "失败": "失敗", "失败 {n} 个 · 首个失败:{error}": "失敗 {n} 個 · 最初の失敗:{error}", "完成": "完了", + "官方客户端会展示这个模型": "公式クライアントはこのモデルを表示します", "对下游声明的能力(工具调用 / 图片识别 / 视频识别 / 支持思考)—— 点击修改": "ダウンストリームへ宣言する能力(ツール呼び出し / 画像認識 / 動画認識 / 思考対応)—— クリックして変更", "对外名可自由命名,允许与上游模型 ID 同名(同名时该上游的原生路由优先,映射作兜底); 支持字母、数字与 ": "外部公開名は自由に命名でき、アップストリームモデル ID と同じ名前でもかまいません(同名の場合はそのアップストリームのネイティブルートが優先され、マッピングはフォールバックになります); 使用できる文字は英字、数字と ", "对外映射名(下游请求时用)": "外部公開マッピング名(ダウンストリームのリクエストで使用)", @@ -183,12 +190,14 @@ "批量操作": "一括操作", "批量操作 · 已选 {n} 个模型": "一括操作 · 選択済み {n} 個のモデル", "按倍率排序": "倍率で並べ替え", + "按可见性筛选": "表示状態で絞り込む", "按所选网关对话协议组装请求并校验响应转换(Chat Completions / Responses / Anthropic Messages)。": "選択したゲートウェイ対話プロトコルでリクエストを組み立て、レスポンス変換を検証します(Chat Completions / Responses / Anthropic Messages)。", "按提供商选择": "プロバイダーで選択", "按状态筛选": "ステータスで絞り込み", "提供商": "プロバイダー", "搜索模型": "モデルを検索", "搜索模型 ID / 名称 / 映射名…": "モデル ID / 名前 / マッピング名を検索…", + "搜索模型 ID / 提供商": "", "搜索模型 ID…": "モデル ID を検索…", "搜索账号…": "アカウントを検索…", "操作": "操作", @@ -197,6 +206,7 @@ "收起 ▴": "折りたたむ ▴", "数字带虚线底 = 由 models.dev 补齐;右上角小点 = 你手动覆盖过;— = 未声明。点任意一格可改。": "数字の点線下線 = models.dev による補完;右上の小点 = 手動で上書き済み;— = 未宣言。どのセルもクリックで変更できます。", "新建一个自定义提供商(同时创建它的第一个账号)": "新しいカスタムプロバイダーを作成(同時に最初のアカウントも作成)", + "映射、思考等级、上下文窗口与工具调用能力在桌面端「模型管理」里改;手机端只做启停。": "", "映射「{alias} → {target}」不存在": "マッピング「{alias} → {target}」が存在しません", "映射上未绑定思考等级,「跟随映射」等于这次不注入": "マッピングに思考レベルがバインドされていないため、「マッピングに従う」は今回は注入なしになります", "映射上绑定的思考等级是 {level},「跟随映射」按它注入": "マッピングにバインドされている思考レベルは {level} で、「マッピングに従う」はそれに従って注入します", @@ -227,6 +237,7 @@ "模型数": "モデル数", "模型映射": "モデルマッピング", "模型来源": "モデルのソース", + "模型清单已刷新": "", "模型管理表": "モデル管理テーブル", "模型能力": "モデル能力", "正在刷新…": "更新中…", @@ -235,6 +246,7 @@ "每条映射 = 对外名 → 上游模型;原始 ID 与别名有独立开关,默认绑定永远存在(没有映射时是合成的那条)。": "各マッピング = 外部公開名 → アップストリームモデル;元の ID とエイリアスにはそれぞれ独立したスイッチがあり、デフォルトバインディングは常に存在します(マッピングがない場合は合成された 1 件です)。", "没有匹配「{keyword}」的模型": "「{keyword}」に一致するモデルはありません", "没有匹配当前筛选的模型": "現在の絞り込みに一致するモデルはありません", + "没有匹配的模型": "", "没有选中任何模型": "モデルが 1 つも選択されていません", "没有选中的模型": "選択されたモデルがありません", "流式请求": "ストリーミングリクエスト", @@ -324,8 +336,11 @@ "还没有可刷新的提供商:先在账号页添加一个账号": "更新できるプロバイダーがありません:先にアカウントページでアカウントを追加してください", "还没有自定义提供商": "カスタムプロバイダーはまだありません", "这一行已不在当前清单里(模型被移除、或目录刷新后上游不再提供它)。关闭后刷新列表再试。": "この行は現在のカタログに存在しません(モデルが削除されたか、ディレクトリ更新後にアップストリームが提供しなくなったためです)。閉じてからリストを更新して再試行してください。", + "这个模型在官方客户端里是否会被展列;不展示的那些默认已关闭(含默认绑定)": "このモデルが公式クライアントに表示されるかどうか。非表示のものは既定で無効化されています(既定バインディングを含む)", "这个模型已经在这家的清单里,不重复导入": "このモデルはすでにこのプロバイダーのカタログにあるため、重複インポートしません", "这几项是给": "これらの項目は", + "这家提供商没有可见性判据(目前只有 Antigravity / Qoder / 小浣熊 / CodeArts 有)": "このプロバイダーには表示状態の判定規則がありません(現在は Antigravity / Qoder / Raccoon / CodeArts のみ)", + "这家有可见性判据,但这一条上游没有表态 —— 按可见处理": "このプロバイダーには表示状態の判定規則がありますが、この項目は上流が表明していません — 表示として扱います", "这家没有可用账号(要在账号页启用一个、且凭证完整)": "このプロバイダーには利用可能なアカウントがありません(アカウントページで 1 つ有効化し、認証情報を揃えてください)", "这家用固定模型清单,无可刷新": "このプロバイダーは固定のモデルカタログを使用しているため、更新できません", "这家的模型清单是全局的,不跟账号走": "このプロバイダーのモデルカタログはグローバルで、アカウントには依存しません", diff --git a/desktop-tauri/i18n/i18n-src/ja/proxies.json b/desktop-tauri/i18n/i18n-src/ja/proxies.json index fe22d876..272e6d66 100644 --- a/desktop-tauri/i18n/i18n-src/ja/proxies.json +++ b/desktop-tauri/i18n/i18n-src/ja/proxies.json @@ -3,10 +3,13 @@ " 提供 AI 账号专用的原生静态住宅 IP(Static Residential ISP):IP 来自真实家庭宽带运营商,Scamalytics 低欺诈评分,每个 IP 独享,不限流量,$3.90 起。": " は AI アカウント専用のネイティブ静的住宅用 IP(Static Residential ISP)を提供しています。実在の家庭向けブロードバンド事業者の IP を使い、Scamalytics の不正スコアが低く、IP は 1 つずつ専有、通信量は無制限、$3.90 から。", " 那一类由「同步 Clash Verge」 镜像进来(进入本页时会自动同步一次):名称、端口、启用都跟随 Clash, 在 Clash 里改或删,这里跟着变 —— 所以它们只读,改请到 Clash Verge 里改。": " この種のエントリは「Clash Verge を同期」でまとめてミラー取り込みされます(このページを開くと自動で 1 回同期します):名前・ポート・有効状態はすべて Clash に従い、Clash 側で変更または削除すると、こちらも追随して変わります —— つまり読み取り専用なので、変更は Clash Verge 側で行ってください。", "Clash Verge": "Clash Verge", + "Clash 同步失败:{msg}": "", + "Clash 镜像": "", "YAML 文件": "YAML ファイル", "{names} 等 {n} 条": "{names} ほか {n} 件", "{n} 个出口": "エンドポイント {n} 件", "{n} 个账号在用": "{n} 件のアカウントが使用中", + "{n} 个账号引用": "", "{n} 条订阅": "{n} 件のサブスク", "{percent}%({received} / {total} MB)": "{percent}%({received} / {total} MB)", "✅ {ip}": "✅ {ip}", @@ -29,7 +32,7 @@ "也可以把二维码截图直接粘贴到弹窗里(Ctrl+V)": "QR コードのスクリーンショットをダイアログに直接貼り付けることもできます(Ctrl+V)", "二维码里没有订阅链接": "QR コードにサブスク URL がありません", "从图片识别二维码": "画像から QR コードを読み取る", - "代理内核未安装(在「订阅管理」里下载内核后,节点会自动分配监听端口)": "プロキシカーネルがインストールされていません(「サブスク管理」でダウンロードすると、ノードにリスニングポートが自動的に割り当てられます)", + "代理内核未安装(在「订阅管理」里下载内核后,被账号引用的节点会自动开监听端口)": "プロキシカーネルが未インストールです(「サブスク管理」でカーネルをダウンロードすると、アカウントから参照されているノードに自動でリスンポートが割り当てられます)", "代理列表加载失败:{reason}": "プロキシ一覧の読み込みに失敗しました:{reason}", "代理池": "プロキシプール", "代理池表": "プロキシプール表", @@ -67,6 +70,8 @@ "内核没有在超时内停下来": "タイムアウト内にカーネルが停止しませんでした", "内核目录没能删掉(可能仍被进程占用),稍后重试一次": "カーネルのフォルダーを削除できませんでした(プロセスがまだ使用している可能性があります)。しばらくしてからもう一度お試しください。", "内核运行中": "カーネル稼働中", + "出口 IP {ip}": "", + "出口可用": "", "出口测试网址": "接続テスト URL", "删除代理": "プロキシを削除", "删除内核": "カーネルを削除", @@ -81,6 +86,7 @@ "只读:在 Clash Verge 里改": "読み取り専用:Clash Verge で変更してください", "只读:在「订阅管理」里改": "読み取り専用:「サブスク管理」で変更してください", "同步 Clash Verge": "Clash Verge を同期", + "同步 Clash Verge 出口": "", "同步中…": "同期中…", "同步失败:{reason}": "同期に失敗しました:{reason}", "名字与端口在 Clash Verge 里改;启用可在本页关闭": "名前とポートは Clash Verge 側で変更してください。有効/無効はこのページで切り替えられます", @@ -94,6 +100,7 @@ "启用(禁用的订阅不参与内核同步,它的节点会从代理池移除)": "有効(無効なサブスクはカーネル同期の対象外で、そのノードはプロキシプールから削除されます)", "图片读取失败": "画像の読み込みに失敗", "地址": "アドレス", + "地址不完整": "アドレスが不完全", "失败 {n} 个({reason})": "失敗 {n} 件({reason})", "存放位置": "保存先", "存放位置 {dir}": "保存先 {dir}", @@ -106,13 +113,16 @@ "已删除 {n} 个代理": "プロキシを {n} 件削除しました", "已删除「{name}」": "「{name}」を削除しました", "已删除内核(订阅与账号引用保留,重新下载后会重新分配端口)": "カーネルを削除しました(サブスクとアカウントの参照は保持され、再ダウンロード後にポートが再割り当てされます)", + "已删除内核(订阅与账号引用保留;有文件仍被占用,将在下次启动时清理)": "カーネルを削除しました(サブスクリプションとアカウントの参照は保持されます。使用中のファイルがあり、次回起動時にクリーンアップされます)", "已刷新「{name}」,正在同步节点…": "「{name}」を更新しました。ノードを同期しています…", + "已同步 {n} 项变更": "", "已启用「{name}」": "「{name}」を有効化しました", "已启用「{name}」,正在补建监听端口(内核重建中)": "「{name}」を有効にしました。リスニングポートを再作成しています(カーネル再起動中)", "已在本页禁用(打开开关即恢复)": "このページで無効化しています(スイッチをオンにすると元に戻ります)", "已新增「{name}」": "「{name}」を追加しました", "已新增订阅「{name}」,内核正在同步节点…": "サブスク「{name}」を追加しました。カーネルがノードを同期しています…", "已是最新,没有需要同步的变更": "すでに最新です。同期が必要な変更はありません", + "已有账号引用它,但内核还没把监听端口补建出来 —— 稍等片刻,或重新启用这条节点": "アカウントからは参照されていますが、カーネルがまだリスンポートを作成していません — しばらく待つか、このノードを再度有効にしてください", "已禁用「{name}」": "「{name}」を無効化しました", "已禁用「{name}」,监听端口正在关闭(内核重建中)": "「{name}」を無効にしました。リスニングポートを閉じています(カーネル再起動中)", "已禁用(未开监听端口)": "無効(リスニングポートなし)", @@ -128,6 +138,7 @@ "当前环境不支持订阅(桥接方法缺失)": "現在の環境ではサブスクを利用できません(ブリッジメソッドが見つかりません)", "当前环境不支持选择文件夹,直接填写绝对路径即可": "この環境ではフォルダ選択を開けません。絶対パスを直接入力してください", "当前线路不可用:{error};重新选一条或切回直连": "現在の経路は使用できません:{error}。別の経路を選ぶか直結に戻してください", + "所属订阅已删除,这条节点不会再分配端口 —— 可以删除它": "所属サブスクが削除されたため、このノードにポートが割り当てられることはありません — 削除して構いません", "手动填写的地址与端口": "手動入力のアドレスとポート", "打开文件夹选择框失败:{reason}": "フォルダ選択を開けませんでした:{reason}", "批量删除": "一括削除", @@ -143,6 +154,7 @@ "搜索节点": "ノードを検索", "文件读取失败": "ファイルの読み取りに失敗しました", "新增代理": "プロキシを追加", + "新增出口、编辑地址与订阅管理在桌面端完成;手机端做启停与连通性测试。": "", "新增订阅": "サブスクを追加", "显示 {n} / 共 {total} 个出口": "{n} / 全 {total} 件の出口を表示", "更新中…": "更新中…", @@ -155,6 +167,7 @@ "本地文件不会自动更新:要更新节点,重新选择文件后保存;不重选则内容保持不变。": "ローカルファイルは自動更新されません:ノードを更新するにはファイルを選び直して保存してください。選び直さない場合は内容が変わらず維持されます。", "本地文件订阅无需更新(要换内容请编辑后重新选择文件)": "ローカルファイルのサブスクは更新不要です(内容を変えるには編集でファイルを選び直してください)", "本地文件订阅的内容在导入时固定:节点会由内核起成本地监听器并镜像进代理池,节点数稍等几秒由轮询刷新。": "ローカルファイルのサブスクの内容は取り込み時に固定されます:ノードはカーネルがローカルリスナーとして起動し、プロキシプールへミラーされます。ノード数は数秒後にポーリングで更新されます。", + "本机没有读到 Clash Verge 配置": "", "本页可编辑、可删除": "このページで編集・削除できます", "来自 Clash Verge 同步(名称、端口跟随 Clash),请到 Clash Verge 中修改;启用可在本页关闭": "Clash Verge からの同期(名前とポートは Clash に従います)。変更は Clash Verge で行ってください。有効/無効はこのページで切り替えられます", "正在下载内核": "カーネルをダウンロード中", @@ -172,7 +185,7 @@ "测试网址要以 http:// 或 https:// 开头": "テスト URL は http:// または https:// で始まる必要があります", "确定删除「{name}」?{used}": "「{name}」を削除しますか?{used}", "确定删除「{name}」?它在代理池里的节点会一并移除,引用这些节点的账号会回退直连。": "「{name}」を削除しますか?プロキシプール内の該当ノードも削除され、それらを参照しているアカウントは直接接続にフォールバックします。", - "确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会变成「未分配端口」,引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。": "ダウンロード済みのカーネル{version}を削除しますか?カーネルのファイルとポートマッピングが削除され、サブスクのノードは「ポート未割り当て」になり、それらを参照しているアカウントは一時的に直接接続にフォールバックします。サブスク自体は保持され、カーネルを再ダウンロードするとポートが再割り当てされます。", + "确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会失去监听端口(地址列显示「端口准备中」),引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。": "ダウンロード済みのカーネル{version}を削除しますか?カーネルのファイルとポートマッピングが削除され、サブスクのノードはリスンポートを失います(アドレス列は「ポート準備中」と表示されます)。それらを参照しているアカウントは一時的に直接接続にフォールバックします。サブスク自体は保持され、カーネルを再ダウンロードするとポートが再割り当てされます。", "确定删除选中的 {n} 个代理?": "選択した {n} 件のプロキシを削除しますか?", "禁用后本端不再用它(引用它的账号回退直连),并会关掉它的本地监听端口": "無効にするとこの端末では使用しなくなり(参照しているアカウントは直接接続に戻ります)、ローカルのリスニングポートも閉じます", "禁用后本端不再用它(引用它的账号回退直连);端口在 Clash Verge 侧,不受影响": "無効にするとこの端末では使用しなくなり(参照しているアカウントは直接接続に戻ります)。ポートは Clash Verge 側のままで、影響はありません", @@ -192,6 +205,7 @@ "订阅来源": "サブスクのソース", "订阅管理": "サブスク管理", "订阅管理({n})": "サブスク管理({n})", + "订阅节点": "", "订阅节点会在本机各占一个监听端口,从该范围里挑空闲的分配;节点多的大订阅把默认范围({base}-{end})用尽时会报「没有可用的节点端口」,这时把范围调大再重启内核。已分配出去的端口不会因改范围而失效。": "サブスクリプションのノードはローカルのリスナーポートを 1 つずつ占有し、この範囲の空きポートから割り当てられます。ノード数の多い大きなサブスクリプションで既定の範囲({base}-{end})を使い切ると「利用可能なノードポートがありません」と表示されます。その場合は範囲を広げてカーネルを再起動してください。割り当て済みのポートは範囲を変更しても無効になりません。", "订阅里的节点会自动同步进代理池,账号即可引用。": "サブスクのノードは自動的にプロキシプールへ同期され、アカウントから参照できます。", "订阅里的节点会被内核起成本地监听器,镜像到代理池(账号按 id 引用,与本页其它出口同一用法)。内核只在用到订阅时按需下载(约 20-40MB),不随安装包分发。": "サブスクのノードはカーネルがローカルリスナーとして起動し、プロキシプールにミラーされます(アカウントは id で参照、このページの他の出口と同じ扱いです)。カーネルはサブスク利用時にのみオンデマンドでダウンロードされ(約 20-40MB)、インストーラーには同梱されません。", @@ -207,8 +221,9 @@ "跳过 {n} 个(同步只读条目)": "{n} 件をスキップ(同期された読み取り専用エントリ)", "还没有代理。点右上角「+ 新增代理」手填一个,或在「订阅管理」里导入订阅节点、同步 Clash Verge 的出口。": "まだプロキシがありません。右上の「+ プロキシを追加」で手入力するか、「サブスク管理」でサブスクのノードを取り込む / Clash Verge の出口を同期してください。", "还没有代理。点右上角「+ 新增代理」手填一个,或点「同步 Clash Verge」把 Clash 的出口导进来。": "プロキシがまだありません。右上の「+ プロキシを追加」から手動で追加するか、「Clash Verge を同期」をクリックして Clash のエンドポイントを取り込んでください。", - "还没有监听端口(还没有账号引用它 —— 在账号里选它并保存后,内核会自动开端口)": "リスニングポートがまだありません(参照するアカウントがありません。アカウントで選んで保存すると、カーネルが自動でポートを開けます)", + "还没有出口。在桌面端「网络代理」页新增,或点上面同步 Clash。": "", "还没有订阅:粘贴订阅链接、导入本地 YAML 文件,或从图片识别二维码。": "まだサブスクがありません:サブスク URL を貼り付けるか、ローカル YAML ファイルを取り込むか、画像から QR コードを読み取ってください。", + "还没有账号使用它(端口只在被账号引用时才分配 —— 在账号里选它并保存后,内核会自动开端口)": "まだどのアカウントも使用していません(ポートはアカウントから参照されたときだけ割り当てられます — アカウントでこのノードを選んで保存すると、カーネルが自動でポートを開きます)", "这条在 Clash Verge 里是停用的,本页启用不生效 —— 到 Clash Verge 里打开它": "この項目は Clash Verge 側で無効になっています。このページで有効にしても反映されません —— Clash Verge でオンにしてください", "连接成功": "接続成功", "连通性": "接続性", @@ -220,6 +235,7 @@ "部分测试结果没能记下:{reason}": "一部のテスト結果を記録できませんでした:{reason}", "重启内核": "カーネルを再起動", "重新选择文件…": "ファイルを選び直す…", + "镜像当前出口集合,名称与端口跟随 Clash": "", "需要干净的出口?": "きれいな出口が必要ですか?", "首次购买使用优惠码 AGENT2API25 享 75 折。": "初回購入はコード AGENT2API25 で 25% オフ。", "+ 新增代理": "+ プロキシを追加", diff --git a/desktop-tauri/i18n/i18n-src/ja/report.json b/desktop-tauri/i18n/i18n-src/ja/report.json index cba1bfde..7f62a2f8 100644 --- a/desktop-tauri/i18n/i18n-src/ja/report.json +++ b/desktop-tauri/i18n/i18n-src/ja/report.json @@ -1,6 +1,7 @@ { "30 天": "30 日間", "7 天": "7 日間", + "Key": "", "Token": "Token", "Top 提供商": "Top プロバイダー", "Top 模型": "Top モデル", @@ -12,6 +13,11 @@ "{m}月{d}日 周{wd}": "{m}月{d}日({wd})", "{name} · {tokens} tokens · {n} 次请求 · {pct}": "{name} · {tokens} tokens · リクエスト {n} 件 · {pct}", "{name}:{tokens} tokens · 占 {pct}%": "{name}:{tokens} tokens · 占有率 {pct}%", + "{n} 个账号启用 · {m} 个限流中": "", + "{n} 个账号正在限流,转发会自动跳过它们": "", + "{n} 天": "", + "{n} 次请求": "", + "{start} ~ {end}": "", "{tokens} tokens · 占 {pct}": "{tokens} tokens · 占有率 {pct}", "…还有 {n} 个模型": "…ほか {n} 件のモデル", "一": "月", @@ -19,14 +25,21 @@ "二": "火", "五": "金", "今天": "今日", + "今天都签完了": "", + "今日签到 {done} / {total}": "", + "今日签到 {done}/{total}": "", + "今日请求": "", "六": "土", "区间共 {n} 天": "全 {n} 日間", + "区间内累计": "", + "可用账号": "", "右轴": "右軸", "含今天在内往前数": "本日を含めて遡ってカウント", "命中 {hit} / 输入 {input}": "ヒット {hit} / 入力 {input}", "命中率 = 缓存读取 Token / 输入 Token(promptTokens)。窗口越短越贴近「此刻」:上游把缓存读取与输入分开上报时,该比值合法地可能超过 100%,此处不夹取值、如实展示。": "ヒット率 = キャッシュ読み取り Token / 入力 Token(promptTokens)。ウィンドウが短いほど「現在」に近づきます。アップストリームがキャッシュ読み取りと入力を分けて報告する場合、この比率は 100% を超えることが正当にあり得ますが、ここでは値を切り詰めず、ありのままに表示します。", "命中率 {rate}(命中 {hit} / 输入 {input})": "ヒット率 {rate}(ヒット {hit} / 入力 {input})", "四": "木", + "失败 {n} 次": "", "左轴": "左軸", "开始日期": "開始日", "当前连续天数": "現在の連続日数", @@ -46,32 +59,49 @@ "所选范围内还没有请求记录": "選択期間内にリクエスト記録がまだありません", "所选范围内还没有账号用量": "選択期間内にアカウント使用量がまだありません", "报表时间范围": "レポートの期間", + "报表视角": "", + "按天 Token 用量": "", "按天 Token 趋势": "日別 Token トレンド", "按本地整点,无请求的整点不标注": "ローカル時刻の毎正時単位。リクエストがない正時はラベルを表示しません", "提供商用": "プロバイダー使用量", "提供商用占比": "プロバイダー使用量の割合", + "提供商用量": "", "提供商:{text}": "プロバイダー:{text}", "方格为本地自然日,无请求显示为空槽": "マスはローカルの暦日で、リクエストがない日は空のマスとして表示", "无请求的整点按 0% 计,但不写读数": "リクエストがない正時は 0% として扱いますが、値は表示しません", "日": "日", + "时间范围。「今天」是本地自然日今天 0 点到现在;「昨天」是昨天一整天(0 点到 24 点);「近 7 天 / 近 30 天」含今天在内往前数,即今天与之前的 6 / 29 天,不是「不含今天的 7 / 30 天」。": "期間。「今日」はローカル日付の今日 0 時から現在まで、「昨日」は昨日の丸一日(0 時から 24 時まで)。「直近 7 日 / 直近 30 日」は今日を含めて遡った日数で、今日とその前 6 / 29 日を指します(今日を除く 7 / 30 日ではありません)。", + "昨天": "昨日", "暂无热力图数据": "ヒートマップのデータがありません", "暂无缓存趋势数据": "キャッシュトレンドのデータがありません", "暂无趋势数据": "トレンドのデータがありません", + "最近一次请求使用的模型": "", + "最近请求": "", + "最近请求 {n} 条": "", "未知模型": "不明なモデル", + "未配置上游": "", "本月": "今月", "模型用量": "モデル使用量", "模型用量占比": "モデル使用量の割合", "每天的总 Token 用量(输入 + 输出)。柱顶标出当天的读数;区间很长、柱子挤到放不下时只标最高的那几天,其余悬停柱子即可读到。": "日ごとの総 Token 使用量(入力 + 出力)。棒の上にその日の値を表示します。期間が長く棒が密集して収まらない場合は最も高い数日だけを表示し、残りは棒にホバーすると確認できます。", + "活跃": "", "活跃天数": "アクティブ日数", "活跃热力图": "アクティビティヒートマップ", + "管理": "", + "累计请求 {n} 条": "累計リクエスト {n} 件", "结束日期": "終了日", "结束日期(含当天)": "終了日(当日を含む)", + "统计区间加载中": "", "统计概览": "統計概要", "缓存命中率": "キャッシュヒット率", "自定义区间": "カスタム期間", + "请求量": "", "读取报表失败:{error}": "レポートの読み込みに失敗しました:{error}", + "账号分布": "", + "账号用量": "", "起始日期(含当天)": "開始日(当日を含む)", "输入 + 输出": "入力 + 出力", + "运行中": "", "近 1 小时": "直近 1 時間", "近 10 分钟": "直近 10 分", "近 24 小时": "直近 24 時間", @@ -80,5 +110,9 @@ "近 30 天": "直近 30 日", "近 365 天活跃热力图": "直近 365 日のアクティビティヒートマップ", "近 365 天,按天 Token 用量着色(档位按本窗口峰值自动定)": "直近 365 日を日別 Token 使用量で色分け(段階はこのウィンドウのピークに応じて自動決定)", - "近 7 天": "直近 7 日" + "近 7 天": "直近 7 日", + "还有 {n} 个待签": "", + "还没有添加账号": "", + "还没有请求记录": "", + "连续天数": "" } diff --git a/desktop-tauri/i18n/i18n-src/ja/requests.json b/desktop-tauri/i18n/i18n-src/ja/requests.json index 16860b22..bdbc471c 100644 --- a/desktop-tauri/i18n/i18n-src/ja/requests.json +++ b/desktop-tauri/i18n/i18n-src/ja/requests.json @@ -41,6 +41,7 @@ "切换路径:": "切替経路:", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "リストのデータが更新されました。もう一度お試しください(閉じてからこの詳細を開き直してください)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "ログレコード自体を削除します。フィルター適用中はヒットした項目だけを削除し、日別集計レポートには影響しません。フィルターなしの場合はすべてをクリアしてレポートをリセットします。この方法では元に戻せません。", + "加载更早的请求": "", "压缩中…": "圧縮中…", "压缩失败:{message}": "圧縮に失敗しました:{message}", "压缩已在后台开始,完成后会自动提示": "圧縮はバックグラウンドで開始されました。完了すると自動で通知されます", @@ -49,6 +50,7 @@ "压缩数据库": "データベース圧縮", "压缩正在后台执行,完成后自动恢复": "圧縮はバックグラウンドで実行中です。完了すると自動的に復帰します", "原始报文": "生ペイロード", + "发给上游的模型": "", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "ほかに {missing} 回の試行は明細が記録されていません(古い方から {kept} 件のみ保持)", "另有 {n} 个词命中": "ほかに {n} 語がヒット", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "転送中のリクエストのみ表示します(ステータス列は段階ごとに表示:接続中 / 応答待ち / 応答中 / リトライ中、所要時間列は経過時間を表示)", @@ -62,14 +64,18 @@ "响应头": "レスポンスヘッダー", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "削除済みデータが占有していたディスク領域を回収します(checkpoint + VACUUM、バックグラウンドで実行)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "設定 → 一般で「デバッグモード」を有効にすると、以降の新しいリクエストでのみペイロードが保存されます", + "在途 {n}": "", "失败请求不记录用量": "失敗したリクエストは使用量を記録しません", "失败({status}):{error}": "失敗({status}):{error}", "失败:{error}": "失敗:{error}", "实际发给上游 {bytes} 字节": "実際にアップストリームへ送信した {bytes} バイト", "客户端": "クライアント", + "客户端请求的模型": "", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "{count} 件のログを削除 · うち {raw} 件はペイロード原文あり · DB サイズ {db}", "尝试 {n} ·": "試行 {n} ·", "尝试明细": "試行明細", + "尝试次数": "", + "尝试链": "", "工具结果": "ツール結果", "工具结果 · {id}": "ツール結果 · {id}", "工具结果图片": "ツール結果の画像", @@ -77,6 +83,7 @@ "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "中断を受け付けました:アップストリーム接続は切断されました。リストはまもなく「リクエストは手動で中断されました」に更新されます", "已抹掉 {count} 条报文原文": "{count} 件のペイロード原文を消去しました", "已用时(请求仍在转发中)": "経過時間(リクエストは転送中)", + "当前筛选下没有记录": "", "成功": "成功", "成功({status})": "成功({status})", "执行清理": "クリアを実行", @@ -88,6 +95,7 @@ "按模型筛选": "モデルで絞り込み", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "モデルで絞り込み(ログに出現したもののみ表示;モデル名は完全一致)", "按请求结果筛选": "リクエスト結果で絞り込み", + "换号链:{chain}": "", "排队中": "キュー中", "提供商 / 账号": "プロバイダー / アカウント", "提供商: {provider}": "プロバイダー:{provider}", @@ -98,12 +106,15 @@ "无报文原文(未开启「调试模式」,或已被清理)": "原文ペイロードがありません(デバッグモードが無効だったか、クリア済みです)", "无结果记录": "結果の記録なし", "时间": "時刻", + "暂无请求日志": "", "暂无请求日志,网关还没有转发过请求": "リクエストログはまだありません。ゲートウェイはまだリクエストを転送したことがありません", "最终承载:{picked}": "最終的に処理:{picked}", + "有重试": "", "本轮尝试失败,正在退避等待或切换到下一个账号": "今回の試行は失敗し、バックオフ待機中または次のアカウントへの切り替え中です", "来源客户端:{client}": "接続元クライアント:{client}", "查看该请求的上游原始报文": "このリクエストのアップストリーム生ペイロードを表示", "模型": "モデル", + "模型映射:{from} → {to}": "", "模型测试": "モデルテスト", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "モデル管理ページの操作列にある「テスト」から発行されたリクエスト:実際のリクエストと同じ転送経路を通りますが、レポート統計には含まれません", "正在加载请求日志…": "リクエストログを読み込んでいます…", @@ -130,6 +141,7 @@ "缓存读取: - / 命中率: -": "キャッシュ読み取り:- / ヒット率:-", "缓存读取: {cache} / 命中率: {rate}": "キャッシュ読み取り:{cache} / ヒット率:{rate}", "耗时": "所要時間", + "脱敏命中:{words}": "", "被跳过:": "スキップ:", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent はありますが、既知のクライアント種別ではありません:{ua}", "详情": "詳細", @@ -148,7 +160,9 @@ "路由回放": "ルーティングリプレイ", "转发到上游的模型:{model}": "アップストリームへ転送するモデル:{model}", "轮次": "回目", + "输入 Token": "", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "入力 {input} · 出力 {output} · 合計 {total} · キャッシュ読み取り {cache}", + "输出 Token": "", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "この明細はモデルテスト(手動実行)です:実際の転送経路を通りますが、レポート統計には含まれません", "这条明细没有记录提供商(旧数据或请求未走到转发)": "この明細にはプロバイダーが記録されていません(旧データか、リクエストが転送まで到達しなかったため)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "この記録には接続元クライアントがありません(旧データ、モデルテスト、または User-Agent なしのリクエスト)", @@ -160,11 +174,13 @@ "进行中…": "処理中…", "连接中": "接続中", "重试 {n}": "リトライ {n}", + "重试 {n} 次": "", "重试中": "リトライ中", "重试次数": "リトライ回数", "预览对话": "会話プレビュー", "预览读取失败": "プレビューの読み込みに失敗しました", "预览读取失败:{error}": "プレビューの読み込みに失敗しました:{error}", + "首响": "", "首响 {time}": "初回応答 {time}", "首响:上游首帧到达的耗时": "初回応答:アップストリームの初回フレーム到達までの所要時間", "首响:上游首帧到达的耗时(请求仍在转发中)": "初回応答:アップストリームの初回フレーム到達までの所要時間(リクエストは転送中)", diff --git a/desktop-tauri/i18n/i18n-src/ja/settings.json b/desktop-tauri/i18n/i18n-src/ja/settings.json index c86596df..b8187d14 100644 --- a/desktop-tauri/i18n/i18n-src/ja/settings.json +++ b/desktop-tauri/i18n/i18n-src/ja/settings.json @@ -2,30 +2,58 @@ " 到 {file}": " {file} へ", " 正文已改:{edited}(其余段用官方原文)。": " 本文変更あり:{edited}(残りの段は公式原文を使用)。", "(无 id)": "(id なし)", + "/v1 拒绝无 Key 请求": "/v1 はキーなしのリクエストを拒否", "0 B": "0 B", "100%(默认)": "100%(デフォルト)", "<本机IP>": "<本機の IP>", + "Access Key ID": "Access Key ID", + "Agent2API 手机端": "Agent2API モバイル", + "Android 应用(APK)": "Android アプリ(APK)", "Cline 上游按请求头判定「调用方是不是 Cline 自家产品」:X-CLIENT-TYPE: cline-sdk 是硬门槛(缺了它免费池模型一律 403),版本号与平台标识让请求更贴近官方客户端的形态。这里列出的是网关默认会发的头:改某一行的值 = 覆盖默认值(也可以新增默认清单之外的自定义头);把值清空保存 = 这个头不发送;在界面删掉被改过的行 = 回落默认值。改完点「保存」,下一个 Cline 请求立即生效。不懂某一行的含义就不要动它 —— 伪装头的价值在「与官方客户端逐字一致」。": "Cline のアップストリームはリクエストヘッダーで「呼び出し元が Cline 自社製品かどうか」を判定します:X-CLIENT-TYPE: cline-sdk は必須条件で(これが欠けると無料プールのモデルは一律 403)、バージョン番号とプラットフォーム識別子がリクエストを公式クライアントの形に近づけます。ここに並ぶのはゲートウェイがデフォルトで送信するヘッダーです:いずれかの行の値を変更 = デフォルト値を上書き(デフォルト一覧にないカスタムヘッダーの追加も可);値を空にして保存 = そのヘッダーを送信しない;画面で変更した行を削除 = デフォルト値にフォールバック。変更後は「保存」をクリックすると、次の Cline リクエストから即時有効です。各行の意味が分からない場合は触らないでください —— 偽装ヘッダーの価値は「公式クライアントとの逐語一致」にあります。", "Cline 伪装头": "Cline 偽装ヘッダー", "Cline 伪装头已刷新": "Cline 偽装ヘッダーを更新しました", + "Docker 容器": "Docker コンテナ", + "Endpoint": "Endpoint", + "Endpoint 填 https://<账户ID>.r2.cloudflarestorage.com,Region 固定填 auto。": "Endpoint に https://<アカウントID>.r2.cloudflarestorage.com を、Region には auto を指定します。", + "Endpoint 填 https://cos.ap-guangzhou.myqcloud.com,Region 填 ap-guangzhou(按实际地域改)。": "Endpoint に https://cos.ap-guangzhou.myqcloud.com を、Region には ap-guangzhou を指定します(実際のリージョンに合わせて変更)。", + "Endpoint 填 https://obs.cn-north-4.myhuaweicloud.com,Region 填 cn-north-4(按实际地域改)。": "Endpoint に https://obs.cn-north-4.myhuaweicloud.com を、Region には cn-north-4 を指定します(実際のリージョンに合わせて変更)。", + "Endpoint 填 https://oss-cn-hangzhou.aliyuncs.com,Region 填 cn-hangzhou(按实际地域改)。": "Endpoint に https://oss-cn-hangzhou.aliyuncs.com を、Region には cn-hangzhou を指定します(実際のリージョンに合わせて変更)。", + "Endpoint 形如 http://192.168.1.10:9000(自建通常用 http);Region 一般填 us-east-1。": "Endpoint は http://192.168.1.10:9000 のような形式(自前ホストでは通常 http)で、Region は通常 us-east-1 です。", + "Endpoint 留空即走 AWS 官方地址;Region 必须与桶所在区域一致。": "Endpoint を空欄にすると AWS 公式アドレスを使用します。Region はバケットのリージョンと一致させてください。", + "Linux 安装包(deb)": "Linux パッケージ(deb)", + "Nextcloud": "Nextcloud", "OpenAI 新版 API 用 developer 角色取代 system 承载指令,新版 OpenAI SDK 与部分客户端发的对话里会出现 role 为 developer 的消息;不少 OpenAI 兼容上游(尤其是自定义提供商的透传路径)不认识这个角色,收到就报 400。开启本项后,网关在每次转发前把 developer 角色的消息改写为 system —— 两类角色在 OpenAI 语义上等价(OpenAI 自己的迁移指南也是这么映射的),改写不影响对话内容与语义;对本来就只认 system 的各家内置上游同样无害。默认开启;关掉后 developer 消息会原样发往上游,不认识它的上游会报「角色不存在」一类的错误。": "新版の OpenAI API は system に代わって developer ロールで指示を伝えるため、最新の OpenAI SDK や一部クライアントが送る会話には role が developer のメッセージが含まれます。多くの OpenAI 互換アップストリーム(特にカスタムプロバイダーの透過パス)はこのロールを認識せず、受け取ると 400 を返します。有効にすると、ゲートウェイは毎回の転送前に developer ロールのメッセージを system に書き換えます —— 両ロールは OpenAI のセマンティクス上で等価(OpenAI 自身の移行ガイドも同じマッピング)なので、書き換えは会話の内容と意味に影響しません。もともと system しか受け付けない内蔵アップストリームにとっても無害です。デフォルトは有効。無効にすると developer メッセージはそのまま送られ、このロールを認識しないアップストリームは「ロールが存在しない」系のエラーを返します。", + "QQ 群 · Telegram · Discord": "QQ グループ · Telegram · Discord", + "S3 兼容存储": "S3 互換ストレージ", + "Safari 没有安装按钮:点底部的「分享」,选「添加到主屏幕」,再点「添加」。": "Safari にはインストールボタンがありません。下部の「共有」→「ホーム画面に追加」→「追加」の順にタップしてください。", + "Secret Access Key": "Secret Access Key", "Token 读数量级": "Token の数値表記", "UTF-8 文本文件路径。留空用内置默认;只在「替换 / 追加」下读取。": "", "UTF-8 文本文件路径,留空用内置默认提示词(通用编码助手提示词,不含任何会被上游拦截的模板句)。只在「替换 / 追加」模式下生效,透传模式不读它。": "UTF-8 テキストファイルのパス。空欄なら内蔵のデフォルトシステムプロンプトを使用(汎用コーディングアシスタント用プロンプトで、アップストリームにブロックされるテンプレート文は含みません)。「置換 / 追加」モードでのみ有効で、パススルーモードでは読み込まれません。", + "WebView / 浏览器内核": "WebView / ブラウザエンジン", + "Windows 安装包(NSIS)": "Windows インストーラー(NSIS)", + "Windows 服务": "Windows サービス", "ZCode 套餐池(Start Plan)通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。": "ZCode プランプール(Start Plan)経路の各リクエストは、その場で生成された CAPTCHA トークン(1 回限り、2 分で失効)を 1 個消費します。在庫がこの値を下回ると、UI がこの値まで自動補充します。既定は 3。大きくすると突発的な同時実行を吸収できますが、使い切れなかったトークンは 2 分で失効し、上流のリスク管理クォータを消耗します。1〜10 を指定できます。", "ZCode 活动套餐通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。": "", "ZCode「套餐池(Start Plan)」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。": "ZCode「プランプール(Start Plan)」経路の各推論リクエストには、その場で生成された Aliyun インビジブル CAPTCHA トークン(1 回限り、約 2 分で失効)を添える必要があります:トークンは UI 内のミントガードが静かに生成してゲートウェイのプールへ入れ、転送時にリクエストごとに取り出します。ここの 3 つの値は補充のリズムを管理します:①「在庫目標」= 在庫がこの値を下回ったときだけこの値まで補充(既定 3);②「ポーリング間隔」= ガードが在庫を確認する間隔、既定 2500 ミリ秒(反応速度にのみ影響。在庫が十分なら余計に生成しません);③「1 ラウンドの生成上限」= 在庫切れを検出した 1 ラウンドで生成する最大数、既定 1(連続生成の間は固定 500 ミリ秒)。リクエストが密で、リクエストログに「CAPTCHA トークンが必要」の 503 が続く場合は、目標と 1 ラウンド上限を適度に上げ、ポーリングを速めるとプールが消費に追いつきます —— ただし上流(Aliyun)は生成頻度にリスク管理があり、多く速く生成するほど制限に触れやすく、使い切れなかったトークンは 2 分で失効してクォータの無駄になるため、小刻みな調整を推奨します。保存すると次の補充ラウンドから即時反映、再起動不要。", "ZCode「活动套餐」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。": "", + "beta 版": "", + "iOS 应用": "iOS アプリ", + "models.dev 元数据": "models.dev メタデータ", "models.dev 元数据已更新({count} 条)": "models.dev メタデータを更新しました({count} 件)", "models.dev 是一个开源的模型资料库(社区维护),这里用它补齐「上游没告诉我们」的模型能力:上下文窗口、最大输出、工具调用、图片、视频、是否支持思考,以及这个模型认识哪些思考档位。优先级是:你在「模型能力」里手工改的值 > 上游清单自己声明的值 > models.dev 的值 —— 它只填上游没声明的空位,从不推翻上游已声明的。网关随包内嵌了一份快照,所以离线、首次启动、访问不通时也有数据;开启时会按「模型目录刷新」任务的间隔联网更新(同一轮里一并刷新,也可在这里立即刷新)。关闭只停联网刷新,已经拿到的数据继续用。": "models.dev はコミュニティが維持するオープンソースのモデルデータベースです。上流が教えてくれなかった情報(コンテキスト長、最大出力、ツール呼び出し、画像、動画、推論の可否、およびそのモデルが実際に受け付ける推論レベル)をここで補います。優先順位は「モデル能力」で手動変更した値 > 上流カタログの宣言値 > models.dev の値 —— 上流が宣言した値を覆すことはなく、空欄だけを埋めます。スナップショットを同梱しているため、オフライン・初回起動・通信不可でもデータがあります。有効時は「モデルカタログ更新」タスクの間隔でオンライン更新します(同じ回でまとめて、またはここで即時)。オフにするとオンライン更新だけが止まり、取得済みのデータは引き続き使われます。", "models.dev 模型元数据": "models.dev モデルメタデータ", + "systemd 服务": "systemd サービス", "{count} 条": "{count} 件", "{count} 条 · 来自{origin}{at} · {mode}": "{count} 件 · 出典: {origin}{at} · {mode}", + "{d} 天 {h} 小时": "{d} 日 {h} 時間", + "{h} 小时 {m} 分钟": "{h} 時間 {m} 分", "{label} 提示词文件": "{label} のシステムプロンプトファイル", "{label} 的提示词模式": "{label} のシステムプロンプトモード", "{label}({message})": "{label}({message})", "{lines} 行、{chars} 字符(按当前草稿计)": "{lines} 行、{chars} 文字(現在の下書きで算出)", "{mode}生效:上游收到的 system 来自{source}{lines}。": "{mode}が有効:アップストリームが受け取る system の出所は{source}{lines}。", + "{m} 分钟": "{m} 分", "{noun}不能为空(可填 {min}–{max})": "{noun}は空にできません({min}–{max} を入力できます)", "{noun}必须在 {min}–{max} 之间(当前填的是 {text})": "{noun}は {min}–{max} の範囲で入力してください(現在の入力値:{text})", "{noun}必须是整数": "{noun}は整数でなければなりません", @@ -34,22 +62,33 @@ "{n} KB": "{n} KB", "{n} MB": "{n} MB", "{n} 个头将随每个 Cline 请求发送": "{n} 個のヘッダーが各 Cline リクエストとともに送信されます", + "{n} 台可选 · 点此切换": "{n} 台 · タップして切り替え", + "{n} 项 · {shown} 显示": "{n} 項目 · {shown} 表示", + "{total} 个菜单 · 显示 {shown} · 隐藏 {hidden} · {groups} 个分组": "メニュー {total} 件 · 表示 {shown} · 非表示 {hidden} · グループ {groups} 件", + "…": "", "⚠️ 密钥警告": "⚠️ キーの警告", + "⚠️ 已关闭「{label}」的机器人校验": "⚠️「{label}」のボット対策を無効にしました", "⚠️ 机器人校验已关闭": "⚠️ ロボット検証をオフにしました", "✅ Cline 伪装头已保存": "✅ Cline 偽装ヘッダーを保存しました", "✅ models.dev 模型元数据已开启": "✅ models.dev モデルメタデータを有効にしました", + "✅ 云同步设置已保存": "✅ クラウド同期の設定を保存しました", "✅ 出站指纹脱敏已开启": "✅ フィンガープリントのマスキングをオンにしました", + "✅ 密码已修改,其他设备的登录已失效": "✅ パスワードを変更しました。他の端末のログインは無効になりました", "✅ 导入完成:{summary}": "✅ インポート完了:{summary}", + "✅ 已上传到云端({size})": "✅ クラウドにアップロードしました({size})", "✅ 已为「{label}」装上网关自带提示词": "✅ 「{label}」にゲートウェイ内蔵のシステムプロンプトを適用しました", + "✅ 已从云端恢复{note}": "✅ クラウドから復元しました{note}", "✅ 已保存:{label}": "✅ 保存しました:{label}", "✅ 已保存:{label} {value}": "✅ 保存しました:{label} {value}", "✅ 已保存:指定错误码直接换号": "✅ 保存しました:指定エラーコードで直接アカウント切替", "✅ 已保留 {days} 天": "✅ {days} 日間保持しました", "✅ 已保留 {days} 天,超出部分已清理": "✅ {days} 日間保持し、超過分を削除しました", "✅ 已备份 {n} 张表({size})到 {file}": "✅ {n} テーブル({size})を {file} へバックアップしました", + "✅ 已导入账号密钥,请继续下载": "✅ アカウントキーをインポートしました。ダウンロードを続けてください", "✅ 已导出 {count} 个账号{note}{file}": "✅ {count} 件のアカウントをエクスポートしました{note}{file}", + "✅ 已开启「{label}」的机器人校验": "✅「{label}」のボット対策を有効にしました", "✅ 已恢复 {n} 张表{note}": "✅ {n} テーブルを復元しました{note}", - "✅ 已改用 k / M 缩写": "✅ k / M 略記に切り替えました", + "✅ 已改用 k / M / B 缩写": "✅ k / M / B の略記に切り替えました", "✅ 已改用本地量级词(万 / 亿式)": "✅ ローカル量級語(万 / 億式)に切り替えました", "✅ 已自动创建网关 Key「默认」(可在「网关 Key」页查看)": "✅ ゲートウェイキー「デフォルト」を自動作成しました(「ゲートウェイキー」ページで確認できます)", "✅ 已解除内容拦截降级": "✅ コンテンツブロックのフォールバックを解除しました", @@ -59,7 +98,9 @@ "✅ 调试模式已关闭": "✅ デバッグモードをオフにしました", "✅ 调试模式已开启": "✅ デバッグモードをオンにしました", "✅ 路由设置已保存": "ルーティング設定を保存しました", + "✅ 连接成功:{path}": "✅ 接続に成功しました:{path}", "、{n} 个自定义提供商": "、カスタムプロバイダー {n} 件", + "。事件日志、请求明细、调试报文与面板登录态保持不变。替换前会自动把当前业务数据备份到配置目录的 backups/ 下。": "。イベントログ・リクエスト明細・デバッグ通信記録・パネルのログイン状態は変更されません。置き換え前に現在の業務データを設定ディレクトリの backups/ に自動でバックアップします。", "「{label}」将从 {previous} 天改为 {days} 天。": "「{label}」を {previous} 日から {days} 日へ変更します。", "「{label}」改用内置默认提示词": "「{label}」を内蔵のデフォルトシステムプロンプトに切り替えました", "「{label}」的提示词文件已更新": "「{label}」のシステムプロンプトファイルを更新しました", @@ -69,29 +110,58 @@ "「{label}」的网关自带提示词正文已更新": "「{label}」のゲートウェイ内蔵システムプロンプトの本文を更新しました", "「模型管理」里每个能力的来源标记(上游 / models.dev / 已覆盖)读的就是这份数据;映射弹窗的思考档位下拉也按这里的数据只列该模型真实认识的档位。": "「モデル管理」の各能力の出典表示(上流 / models.dev / 上書き済み)はこのデータを読んでいます。マッピングダイアログの推論レベル選択も、ここに基づいてそのモデルが実際に受け付けるレベルだけを並べます。", "「用哪份提示词」本来是按上游分别决定的事,所以每家可以有自己的模式、提示词文件与正文:不在这里列出的提供商一律沿用上面那份默认设置,「跟随默认」把这一家取消、回到默认。带「网关自带」那一行的家(目前是 ZCode 两家)默认就列在这里 —— 那一段是网关自己装上去的文本,不来自客户端也不来自提示词文件,默认开启、可以关掉,也可以改它的正文(改过的那一段以你的文本为准);关掉后还能不能通过上游校验,取决于上游当下的口径,行内说明了实测依据。「模式 / 提示词文件 / 正文」管的是客户端自己的 system 怎么处理,与那个开关是两件事。": "「どのシステムプロンプトを使うか」は本来アップストリームごとに決める話なので、各プロバイダーが独自のモード・システムプロンプトファイル・本文を持てます:ここに表示されないプロバイダーはすべて上記のデフォルト設定を引き継ぎ、「デフォルトに従う」でそのプロバイダーの設定を解除してデフォルトに戻します。「ゲートウェイ内蔵」の行があるプロバイダー(現在は ZCode の 2 社)はデフォルトでここに表示されます —— その部分はゲートウェイが自ら組み込むテキストで、クライアントからもシステムプロンプトファイルからも来ません。デフォルトはオンで、オフにもでき、本文も変更できます(変更した部分はあなたのテキストが優先)。オフにした後でアップストリームの検証を通過できるかは、アップストリームの現時点の基準次第です(行内に実測の根拠を記載)。「モード / システムプロンプトファイル / 本文」はクライアント自身の system をどう扱うかの設定で、あのスイッチとは別物です。", + "「设置」是固定项,含它的分组不能整组隐藏;可以单独隐藏组内其它菜单": "「設定」は固定項目のため、そのグループはまとめて非表示にできません。他のメニューは個別に非表示にできます", "「请求日志」页的逐条请求记录,默认 99999 天(近似全量保留),可填 1–99999 天。": "「リクエストログ」ページのリクエストごとの記録。既定は 99999 日(ほぼ全量保持)で、1〜99999 日を指定できます。", "一段内置装配": "内蔵の定型ブロック", "三档数据各自独立计时,超出保留天数的部分会被删除:事件日志是登录、账号切换、429 切换这类系统事件;请求日志是网关每次转发到上游的逐条记录;按天聚合供报表页的热力图与按天趋势使用。请求日志与按天聚合默认 99999 天(约 274 年,效果上等于全量保留),事件日志默认 30 天。把某一档改小(例如 30 天改成 7 天)保存后会立即删除超出的历史数据,此操作不可恢复;改大或保持不变不会删除任何数据。三档的可填范围均为 1–99999 天。": "3 種類のデータはそれぞれ独立して計時され、保存日数を超えた部分は削除されます:イベントログはログイン、アカウント切替、429 切替といったシステムイベント;リクエストログはゲートウェイがアップストリームへ転送するたびの記録;日次集計はレポートページのヒートマップと日次トレンドに使われます。リクエストログと日次集計の既定は 99999 日(約 274 年、実質的に全量保持)、イベントログの既定は 30 日です。いずれかを小さくして(例:30 日→7 日)保存すると、超えた履歴データは即座に削除され、元に戻せません。大きくするか変更しない場合は何も削除されません。3 つとも指定できる範囲は 1〜99999 日です。", "三段各自成块,所以分开编辑": "3 つの段はそれぞれ独立したブロックになるため、分けて編集します", "三段必须各自成块(上游按结构校验,并成一段会被回 405),所以这里分开编辑。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。": "", + "上传": "アップロード", + "上传中…": "アップロード中…", + "上传内容含加密后的账号凭证与网关配置": "アップロード内容には暗号化されたアカウント認証情報とゲートウェイ設定が含まれます", + "上传到云端": "クラウドへアップロード", + "上传失败:{message}": "アップロードに失敗しました:{message}", + "上次同步:{time}({action})": "前回の同期:{time}({action})", "上次成功刷新的结果(存在本机数据库里)": "前回の更新に成功した結果(ローカルデータベースに保存)", + "上游 CLI 版本": "上流 CLI のバージョン", + "上游客户端版本": "上流クライアントのバージョン", "上游按": "アップストリームは", "上游按「结构」校验身份:三段必须各自成块,所以这里分开编辑三段(不能合成一段)。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。第三段发出时前面会自带一个空行(官方形状)。": "アップストリームは「構造」で身元を検証します:3 つの段はそれぞれ独立したブロックとして並ぶ必要があるため、ここでは 3 つの段を別々に編集します(1 段にまとめることはできません)。動的段の実行値はプレースホルダーとして記述し、リクエスト送信時に置換されます:{cwd} 作業ディレクトリ、{platform} プラットフォーム、{shell} シェル、{os_version} OS バージョン、{git} git リポジトリかどうか、{provider} 提供元識別子、{model} モデル名。ある段を空にする = その段は公式原文に戻ります。第 3 段は送信時に先頭へ空行が 1 つ自動的に付きます(公式の形状)。", + "上游未配置": "上流が未設定", "上游模型繁忙时会先排队(回一句「建议 N 秒后再来」,Qoder 免费模型就是这样):网关等一会儿再发同一请求,最多等几次。默认 2 次,可填 0–10;填 0 表示不等待,排队直接返回 503 并说明「不是登录态或额度问题」。": "アップストリームのモデルが混雑していると、まずキューイングされます(「N 秒後に再試行することをおすすめします」と返ってくる形で、Qoder の無料モデルがまさにこれ):ゲートウェイはしばらく待ってから同じリクエストを再送し、最大で数回まで待ちます。デフォルトは 2 回で、0–10 を入力できます。0 を入力すると待たずに、キューイングが直接 503 を返し「ログイン状態やクォータの問題ではない」と説明します。", "上游用「逐字精确匹配」的方式审核请求体(不是语义审核):客户端注入的固定模板句、计费头字段名、以及某些裸错误码出现在报文里就会整单拦截,返回 400。开启本项后,网关在每次转发前改写这些指纹 —— 表头键值整段删除,承载语义的模板句只换一个词(如 official CLI for Claude → official CLI tool for Claude),对话内容与语义都不受影响。规则集是内置的,不需要也无法维护词表。关掉本项后客户端模板会原样发往上游,可能重新出现模板句被误拦的报错。": "アップストリームはリクエスト本文を「逐語完全一致」で審査します(意味的な審査ではありません):クライアントが注入する固定テンプレート文、課金ヘッダーのフィールド名、および一部の裸のエラーコードがメッセージに現れると、リクエスト全体がブロックされ 400 が返ります。本項目をオンにすると、ゲートウェイは転送のたびにこれらのフィンガープリントを書き換えます —— ヘッダーのキーと値は丸ごと削除し、意味を担うテンプレート文は 1 語だけ置き換えます(例:official CLI for Claude → official CLI tool for Claude)。会話の内容と意味は一切影響を受けません。ルールセットは内蔵で、単語リストを保守する必要もできません。本項目をオフにするとクライアントのテンプレートがそのままアップストリームへ送られ、テンプレート文が誤ってブロックされるエラーが再発する可能性があります。", "上游请求四个阶段各自的等待上限(秒,1~3600):①「连接中超时」= 建立 TCP/TLS 连接或代理隧道的最大等待,默认 30 秒;②「等待响应超时」= 请求发出后等上游响应头的最大时间,默认 300 秒 —— 网关请求上游恒带 stream,响应头在 SSE 建立时就到达,与模型思考多久无关;③「流式响应空闲超时」= 流式响应相邻两块数据之间允许的最大空闲,收到新数据即重新计时,默认 300 秒 —— 上游长时间不吐数据即判定连接僵死并断开;④「非流式响应超时」= 读完整份非流式响应体的总预算(一次性计时、不重置),默认 300 秒。四项都是「等待上限」,只要数据还在来,正常的流式回答就一直往下传。保存后对下一个请求立即生效,不用重启。": "アップストリームリクエストの 4 段階それぞれの待機上限(秒、1~3600):①「接続タイムアウト」= TCP/TLS 接続またはプロキシトンネルを確立する最大待機時間、デフォルト 30 秒;②「応答待ちタイムアウト」= リクエスト送出後、アップストリームのレスポンスヘッダーを待つ最大時間、デフォルト 300 秒 —— ゲートウェイはアップストリームへのリクエストに常に stream を付け、レスポンスヘッダーは SSE の確立時点で到着するため、モデルがどれだけ考えるかとは無関係です;③「ストリーミング応答アイドルタイムアウト」= ストリーミング応答の隣接する 2 つのデータ間に許される最大アイドル時間で、新しいデータを受信すると計時がリセットされます、デフォルト 300 秒 —— アップストリームが長時間データを出さなければ接続が死んだと判定して切断します;④「非ストリーミング応答タイムアウト」= 非ストリーミング応答本文全体を読み切る総予算(一度だけ計時、リセットなし)、デフォルト 300 秒。4 項目はいずれも「待機上限」で、データが流れ続けている限り、正常なストリーミング応答はそのまま流れ続けます。保存後は次のリクエストから即時有効で、再起動は不要です。", + "下载失败:{message}": "ダウンロードに失敗しました:{message}", + "不一致": "不一致", "不做会话识别,每条请求都重新按策略选路。": "セッションを識別せず、リクエストごとに戦略に従って選び直します。", "不可用": "利用不可", "不在这里列出的提供商都用上面的全局配置;「跟随默认」取消这一家的单独配置。带「网关自带」的家列在这里:那段文本是网关自己装上去的(可关、可改正文),与「模式」是两件事。": "", + "与网关同源的面板;完整管理功能在桌面端": "ゲートウェイと同一オリジンのパネルです。詳細な管理はデスクトップ版で行います", + "两张开关分别管桌面端(应用内窗口,也包含开启局域网访问后从其他设备访问的那份面板)与网页端(Docker / 服务端自托管的浏览器面板)。默认都开启;桌面端只在本机使用、嫌验证耗时可以关掉,网页端暴露在网络上,建议保持开启。": "2 つのスイッチはデスクトップ側(アプリ内ウィンドウ。LAN アクセス有効時に他端末から開くパネルも含む)と Web 側(Docker / サーバーが配信するブラウザパネル)を個別に制御します。既定はどちらも有効です。ローカル専用で認証の待ち時間が気になる場合はデスクトップ側を切っても構いませんが、ネットワークに露出する Web 側は有効のままを推奨します。", + "两次输入的新密码不一致": "新しいパスワードが一致しません", "两次重试之间的等待时间,两项共用,可填 0–300 秒。": "2 回のリトライ間の待機時間。2 項目で共用し、0–300 秒を入力できます。", "为了安全,开启前需要先注册一个": "安全のため、オンにする前に", "主进程未返回启动设置,请更新桌面端后重试": "メインプロセスが起動設定を返しませんでした。デスクトップアプリを更新してから再試行してください", "主进程桥不可用": "メインプロセスのブリッジを利用できません", + "主题": "テーマ", "事件日志": "イベントログ", "事件日志保留天数": "イベントログの保存日数", + "云同步": "クラウド同期", + "人机验证": "人間確認", + "人机验证失败": "人間確認に失敗しました", + "人机验证已完成": "人間確認が完了しました", + "人机验证还在计算,请稍候": "人間確認を計算中です。しばらくお待ちください", "仅用现有数据、不联网刷新": "既存データのみ使用、オンライン更新なし", + "从云端恢复": "クラウドから復元", + "从桌面图标启动,全屏、无地址栏。想卸载:长按图标删除,或清除浏览器站点数据。": "ホーム画面のアイコンから起動します。全画面・アドレスバーなし。削除するにはアイコンを長押しして削除するか、ブラウザのサイトデータを消去してください。", + "会恢复默认的分组、顺序与显隐设置。你自建的分组会被删除(组内菜单不会丢)。": "グループ・順序・表示設定を既定に戻します。作成したグループは削除されます(メニューは失われません)。", + "位置": "場所", + "位置与端口": "場所とポート", "使用中文单位(亿 / 万)": "中国語の単位を使う(億 / 万)", "使用本地量级词(万 / 亿式)": "ローカル量級語(万 / 億式)を使用", + "侧边栏布局模块未加载(ui/sidebar-layout.js 应在 app.js 之前引入)。": "サイドバーのレイアウトモジュールが読み込まれていません(ui/sidebar-layout.js を app.js より前に読み込む必要があります)。", + "侧边栏没有显示任何菜单。到「设置 → 显示 → 侧边栏菜单」里打开需要的项。": "サイドバーにメニューが表示されていません。「設定 → 表示 → サイドバーのメニュー」で必要な項目をオンにしてください。", + "侧边栏菜单": "サイドバーのメニュー", "保存上游原始报文(请求头、请求体、响应头、响应体)": "アップストリームの生メッセージを保存(リクエストヘッダー、リクエストボディ、レスポンスヘッダー、レスポンスボディ)", "保存后以这份为准(优先于上面的文件);清空回到文件 / 内置默认。": "", "保存后以这里的文本为准": "保存後はここのテキストが優先されます", @@ -104,16 +174,21 @@ "保存后立即生效,不用重启;超时按上游失败处理(计入请求日志的「错误」列,并按「请求重试」的设置决定是否重试)。": "保存後は即時有効で再起動は不要です。タイムアウトはアップストリームの失敗として扱われます(リクエストログの「エラー」列に計上され、「リクエストリトライ」の設定に従ってリトライするかどうかが決まります)。", "保存后立即生效:改小保留天数会立刻删除超出的历史数据,且不可恢复。": "保存後は即時有効:保存日数を小さくすると超過した履歴データがすぐに削除され、元に戻せません。", "保存并重启": "保存して再起動", + "保活换行丢弃": "keepalive 改行の除去", "保留天数已刷新": "保存日数を更新しました", + "修改密码": "パスワード変更", + "修改密码需要输入当前密码;修改成功后,其他设备 / 浏览器上的登录会立即失效,当前这条会话保留。": "パスワード変更には現在のパスワードが必要です。成功すると他の端末・ブラウザのログインは即座に無効になり、このセッションだけが残ります。", "值": "値", "值与默认值相同的行不占覆盖(存的是「改过的键」,不是全量配置);值留空保存表示这个头不发送,删除改过的行表示回落默认值。改完立即生效,不用重启。": "デフォルト値と同じ行は上書きとして扱われません(保存されるのは「変更されたキー」であり、全量の設定ではありません)。値を空にして保存するとそのヘッダーは送信されず、変更した行を削除するとデフォルト値にフォールバックします。変更後は即時有効で、再起動は不要です。", "值(留空 = 不发送)": "値(空欄 = 送信しない)", "允许局域网内的设备访问网关": "LAN 内のデバイスにゲートウェイへのアクセスを許可", + "允许无 Key 裸跑": "キーなし実行を許可", "允许浏览器里的页面跨来源调用网关(/v1/*)": "ブラウザー内のページからクロスオリジンでゲートウェイを呼び出すことを許可(/v1/*)", "全局配置": "グローバル設定", "全部已配置": "すべて設定済み", "全部数据(账号、事件日志、请求记录、调试报文、设置)保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "", "全部数据(账号、事件日志、请求记录、调试报文、设置)统一保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "すべてのデータ(アカウント、イベントログ、リクエスト記録、デバッグメッセージ、設定)は、設定ディレクトリ配下の agent2api.db という単一の SQLite データベースにまとめて保存されます。バックアップ時はこのファイルをコピーするだけで済みます。保存場所を変更する場合は、環境変数 AGENT2API_PROXY_HOME を設定してからプログラムを再起動してください。", + "关闭后手动上传 / 下载也会被拒绝。": "オフにすると手動のアップロード / ダウンロードも拒否されます。", "关闭后网关回到只监听 127.0.0.1,局域网内的设备将无法继续访问;已配置的网关 Key 与账号都不受影响。": "オフにするとゲートウェイは 127.0.0.1 のみの待ち受けに戻り、LAN 内のデバイスはアクセスできなくなります。設定済みのゲートウェイキーとアカウントは影響を受けません。", "关闭后,管理界面不再从局域网提供,只有本机的桌面程序可以管理;已开启的 API 转发不受影响。": "オフにすると、管理画面は LAN に公開されなくなり、本機のデスクトップアプリからのみ管理できます。有効になっている API 転送は影響を受けません。", "关闭局域网访问": "LAN アクセスをオフにする", @@ -124,16 +199,30 @@ "其余沿用上面的全局配置": "", "内容拦截降级": "コンテンツブロックのフォールバック", "内置默认提示词": "内蔵のデフォルトシステムプロンプト", + "再输入一次新密码": "", "凭据类请求头已脱敏。": "認証情報系のリクエストヘッダーはマスキング済みです。", + "凭证只保存在这台手机上,按服务器分别存放": "認証情報はこの端末にのみ保存され、サーバーごとに分けて管理されます", + "出问题时把这一页截图,或点「复制全部信息」把文本贴进反馈,一般就能定位到大致原因。": "問題が起きたらこのページをスクリーンショットするか、「すべての情報をコピー」でテキストを報告に貼ってください。大抵の原因はそれで絞り込めます。", + "分享图标在 Safari 底部工具栏中间": "共有アイコンは Safari 下部ツールバーの中央にあります", + "分组名": "グループ名", + "分组名,例如「常用」": "グループ名(例:「よく使う」)", "切换账号重试次数": "アカウント切替のリトライ回数", "刚刚从 models.dev 拉到的最新数据": "models.dev から取得したばかりの最新データ", "删除 {code}": "{code} を削除", + "删除分组": "グループを削除", + "删除分组「{name}」?组内 {n} 个菜单会移到「{target}」,不会丢。": "グループ「{name}」を削除しますか?{n} 件のメニューは「{target}」へ移動するので失われません。", "删除状态码 {code}": "ステータスコード {code} を削除", + "删除空分组「{name}」?": "空のグループ「{name}」を削除しますか?", + "删除这个分组(组内菜单移到第一组)": "このグループを削除(メニューは最初のグループへ移動)", "刷新": "更新", "刷新失败:{message}": "更新に失敗しました: {message}", + "前台运行": "フォアグラウンド実行", "剥离上游审核黑名单指纹(改写请求体里的模板句与表头)": "アップストリーム審査のブラックリストフィンガープリントを除去(リクエスト本文のテンプレート文とヘッダーを書き換え)", "功能建议": "機能提案", "包含日志与调试报文": "ログとデバッグペイロードを含める", + "包含:{items}": "含まれるもの:{items}", + "区域(Region)": "リージョン(Region)", + "华为云 OBS": "Huawei Cloud OBS", "单轮铸造上限": "1 回の発行上限", "占用大小": "使用サイズ", "去反馈": "フィードバックする", @@ -144,25 +233,48 @@ "变更网页管理面板": "Web 管理パネルの変更", "只列单独配置过的家,其余沿用上面的全局配置": "個別に設定したものだけを表示し、それ以外は上記のグローバル設定を引き継ぎます", "只对「排队制」的上游生效(目前是 Qoder 的免费模型):模型繁忙时上游不报错,只回一句「建议 N 秒后再来」(业务码 10605),网关按建议时长等一会儿再发同一请求,等满次数仍排不上才把「排队中」作为错误返回(HTTP 503,文案会说明这不是登录态或额度问题)。等待发生在首个字节之前,吃的是「等待响应超时」那份预算 —— 两项设置一起决定一次请求最多卡多久;排队不会标记账号限额、也不会换账号(换谁都一样在排队)。保存后对下一个请求立即生效,不用重启。": "「キューイング方式」のアップストリームにのみ有効です(現在は Qoder の無料モデル):モデルが混雑しているとき、アップストリームはエラーを返さず「N 秒後に再試行することをおすすめします」(ビジネスコード 10605)とだけ返します。ゲートウェイは推奨された時間だけ待ってから同じリクエストを再送し、規定回数まで待っても順番が回ってこない場合にのみ「キューイング中」をエラーとして返します(HTTP 503。メッセージにはこれがログイン状態やクォータの問題ではないことが明記されます)。待機は最初のバイトより前に発生し、「応答待ちタイムアウト」の予算を消費します —— 2 つの設定が合わさって 1 つのリクエストが最大どれだけ停滞しうるかを決めます。キューイングはアカウントをレート制限としてマークせず、アカウントの切り替えも行いません(誰に切り替えても同じようにキューイングされます)。保存後は次のリクエストから即時有効で、再起動は不要です。", + "只清掉这个浏览器存的 Key,不影响网关": "このブラウザに保存された Key のみ削除します。ゲートウェイには影響しません", + "可回滚到上一版本": "1 つ前のバージョンに戻せる", + "合计只累加当前筛选后可见的账号,筛选、搜索一改数字就跟着变。": "合計は現在の絞り込みで表示中のアカウントのみを合算します。絞り込みや検索を変えると数字も変わります。", + "合计按整组统计(默认):不受筛选、搜索影响,被筛掉与已禁用的账号也计入。": "合計はグループ全体で計算します(既定)。絞り込みや検索の影響を受けず、除外されたアカウントや無効なアカウントも含まれます。", "同一会话在 5 分钟内尽量跟住同一个账号;停了 5 分钟再发就算新会话。多数场景用它 —— 多轮对话少换号,也不至于把账号焊死。": "同じセッションは 5 分以内ならできるだけ同じアカウントを使い続け、5 分空くと新しいセッション扱い。多くの場面におすすめ —— 連続対話でアカウントが頻繁に切り替わらず、特定アカウントに固定もされません。", "同一会话在 6 小时内跟住同一个账号;适合一坐几个小时的长会话,上游的上下文缓存能吃到更多。": "同じセッションは最大 6 時間同じアカウントを使い続けます。何時間も続く長丁場に最適で、アップストリームのコンテキストキャッシュをより多く活かせます。", "同一账号重试次数": "同一アカウントのリトライ回数", "同时开放网页管理面板": "Web 管理パネルも同時に公開", + "同步密码": "同期パスワード", "名单最多 {max} 个状态码": "リストに入れられるステータスコードは最大 {max} 個", + "后端没有返回版本信息": "バックエンドがバージョン情報を返しませんでした", + "含固定项「设置」,不可整组隐藏(可以单独隐藏组内其它菜单)": "固定項目「設定」を含むため、グループごと非表示にはできません(他のメニューは個別に非表示にできます)", "启动与托盘": "起動とトレイ", + "启动时间": "起動時刻", + "启用云同步": "クラウド同期を有効にする", "命中这些上游状态码的失败不在同一账号重发,直接换下一个账号继续试(换号占用「切换账号重试次数」,换满仍失败才把错误原样返回给客户端)。输入 100–599 的状态码后回车添加,点 × 或退格删除;默认 402(积分不足)。清空全部表示任何错误都照常重试。": "これらのアップストリームステータスコードに該当する失敗は、同一アカウントでの再送を行わず、次のアカウントに切り替えて試行を続けます(アカウント切替は「アカウント切替のリトライ回数」を消費し、使い切ってもなお失敗する場合にのみエラーがそのままクライアントへ返されます)。100–599 のステータスコードを入力して Enter で追加、× または Backspace で削除します。デフォルトは 402(ポイント残高不足)。すべてクリアすると、あらゆるエラーで通常どおりリトライします。", + "固定": "固定", "在同一个账号上原地重发几次(不含首发;整条请求共用一份,用完后换来的账号只发一次就继续换)。可填 0–10,0 表示失败立即换号。": "同じアカウントでそのまま何回再送するか(初回は含みません。1 つのリクエスト全体で 1 つの回数を共用し、使い切った後に切り替えたアカウントでは 1 回送信するごとに次の切替へ進みます)。0–10 を入力でき、0 は失敗時に即座にアカウントを切り替えることを意味します。", "在这里直接编辑提示词正文:保存后以这份文本为准(优先于上面的提示词文件),清空则回到文件 / 内置默认。改一个字不必再去编辑器里开文件。": "ここでシステムプロンプトの本文を直接編集できます:保存後はこのテキストが優先されます(上記のシステムプロンプトファイルより優先)。クリアするとファイル / 内蔵デフォルトに戻ります。1 文字の変更のためにエディターでファイルを開く必要はもうありません。", + "在途请求": "処理中のリクエスト", + "地址格式不对。填「IP 或域名:端口」,可带 http:// 或 https://,例如 192.168.1.23:3065": "アドレスの形式が正しくありません。「IP またはドメイン:ポート」で入力してください(http:// または https:// も可)。例: 192.168.1.23:3065", + "坚果云": "Jianguoyun(Nutstore)", + "坚果云需使用「第三方应用密码」(账户信息 → 安全选项里生成),地址必须是 /dav/ 下的目录。": "Jianguoyun では「サードパーティアプリのパスワード」(アカウント情報 → セキュリティで生成)が必要で、URL は /dav/ 配下のディレクトリを指定してください。", + "填「IP 或域名:端口」。协议可写 http:// 或 https://,不写默认 http;http 不写端口默认 3065,https 默认 443。地址里的 /m、/v1 会自动去掉。": "「IP またはドメイン:ポート」を入力してください。プロトコルは http:// または https:// が使えます(省略時は http)。ポートを省略すると http は 3065、https は 443 になります。アドレス内の /m、/v1 は自動的に除去されます。", "备份中…": "バックアップ中…", "备份整库": "データベースをバックアップ", "备份文件内含全部账号凭证、网关 API Key 与面板管理员凭据,可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "バックアップファイルには全アカウントの認証情報・ゲートウェイ API キー・管理パネルの管理者認証情報が入っており、そのままログインに使えます。厳重に保管し、外部に漏らしたり公開場所へアップロードしたりしないでください。", + "复制全部信息": "すべての情報をコピー", + "复制给客户端": "クライアント用にコピー", + "外观": "外観", + "外观 · 接入 · 关于": "外観 · 接続 · 情報", "天": "日", "天数": "日数", "失败 {n} 个": "失敗 {n} 件", "失败 {n} 个:{detail}": "失敗 {n} 件:{detail}", "失败后最多再换几个账号试(填 N = 最多换 N 个,首发那个不算)。换到同一家名下另一个账号、或换到另一家,都各算一次;换满仍失败就返回错误。可填 0–10,0 表示失败立即报错。": "失敗後に最大であと何個のアカウントに切り替えて試すか(N を入力 = 最大 N 個まで切替。初回のアカウントは含みません)。同じプロバイダー配下の別のアカウントへの切替も、別のプロバイダーへの切替もそれぞれ 1 回と数えます。使い切ってもなお失敗する場合はエラーを返します。0–10 を入力でき、0 は失敗時に即座にエラーを返すことを意味します。", + "存储桶(Bucket)": "バケット(Bucket)", "存储概况已刷新": "ストレージ概要を更新しました", "安全": "セキュリティ", + "安装到主屏幕": "ホーム画面に追加", + "安装到桌面": "アプリとしてインストール", + "安装包形态": "インストーラの種類", "官方原文只有一句:声明自己是 ZCode。": "公式原文は 1 文だけです:自分が ZCode であることを宣言します。", "客户端 system 原样出站。": "クライアントの system をそのまま送信します。", "客户端 system 原样出站(只靠指纹脱敏改写模板句)。": "クライアントの system をそのまま送信します(テンプレート文の書き換えはフィンガープリントのマスキングのみ)。", @@ -170,19 +282,27 @@ "客户端(Claude Code / Codex 等 CLI)会往 system 里塞几十句固定模板,上游逐字审核,命中就整单拦截(HTTP 400)。「模式」决定网关怎么处理这份 system:透传 = 原样不动(默认);替换 = 删掉客户端的 system / developer,换成网关提示词;追加 = 保留客户端内容,只在最前面插一条网关提示词。网关提示词来自「提示词文件」或界面里编辑的正文(正文优先,清空回到文件 / 内置默认)。撞上拦截时会自动改用一段最小中性提示词,次日 00:00 自动恢复,也可以随时解除。": "", "家之间仍按优先级,只有同一家内部轮流用各账号 —— 想优先用某几家、又不愿把某一家的某个账号焊死时用它。": "プロバイダー間は優先度どおり、同じプロバイダー内だけでアカウントを順送りします —— いくつかのプロバイダーを優先しつつ、特定アカウントに固定したくないときに。", "家内轮询": "プロバイダー内ローテーション", + "容器内更新": "コンテナ内更新", "密码至少 8 位": "パスワードは 8 文字以上", + "导入前会把本机的 account.key 改名备份(account.key.bak-时间戳),不会直接删除。": "インポート前にローカルの account.key を account.key.bak-<タイムスタンプ> に改名して退避します(削除はしません)。", "导入完成:{summary}": "インポート完了:{summary}", + "导入密钥失败:{message}": "キーのインポートに失敗しました:{message}", + "导入密钥并继续": "キーをインポートして続行", "导入账号": "アカウントをインポート", "导出中…": "エクスポート中…", "导出会把全部账号与自定义提供商定义写入一个 JSON 文件,可以拷到另一台机器上导入后继续使用。导入采用合并策略:同提供商下按业务身份(UID / userId / apiKey 等)去重 —— 已存在的账号只更新凭证,保留本机原有的优先级顺序;新账号追加到转发顺序末尾,不会抢占当前正在使用的账号;自定义提供商定义按 id 合并,本机缺失时自动补建。": "エクスポートはすべてのアカウントとカスタムプロバイダー定義を 1 つの JSON ファイルに書き出し、別のマシンにコピーしてインポートすればそのまま使い続けられます。インポートはマージ方式を採用します:同じプロバイダー配下では業務上の身元(UID / userId / apiKey など)で重複を排除 —— 既存のアカウントは認証情報のみ更新し、本機の優先順はそのまま維持します。新しいアカウントは転送順の末尾に追加され、現在使用中のアカウントを奪いません。カスタムプロバイダー定義は id 単位でマージし、本機にない場合は自動的に作成します。", "导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义": "エクスポートファイルには accessToken / refreshToken / apiKey などの認証情報とカスタムプロバイダー定義が含まれます", "导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义,可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "エクスポートファイルには accessToken / refreshToken / apiKey などの認証情報とカスタムプロバイダー定義が含まれ、そのままログインに使用できます。厳重に保管し、第三者に渡したり公共の場所へアップロードしたりしないでください。", "导出账号": "アカウントをエクスポート", + "尚未注册管理员:管理接口不需要登录。开启「允许局域网访问」时会引导注册,也可以从登录页注册。": "管理者が未登録です:管理 API にログインは不要です。「LAN アクセスを許可」を有効にすると登録へ案内されます。ログインページから登録することもできます。", "局域网开放后,管理接口要求登录。请设置管理员账号与密码,注册完成会直接继续开启流程。": "LAN に公開すると、管理インターフェースはログインを要求します。管理者アカウントとパスワードを設定してください。登録が完了すると、そのまま有効化の処理を続行します。", "局域网访问": "LAN アクセス", + "展开或收起设置分类": "", "工具用法与环境说明那一大段。": "ツールの使い方と環境説明の大きなかたまりです。", "已为「{label}」单独配置提示词": "「{label}」にシステムプロンプトを個別設定しました", + "已作为独立应用运行": "独立アプリとして実行中", "已保存 {count} / {limit} 条报文(超出后丢弃最旧的)。": "{count} / {limit} 件のメッセージを保存済み(超過分は古いものから破棄)。", + "已保存,留空沿用": "保存済み(空欄ならそのまま使用)", "已关闭": "オフ", "已关闭 models.dev 联网刷新": "models.dev のオンライン更新をオフにしました", "已关闭。{note}": "オフです。{note}", @@ -190,26 +310,43 @@ "已关闭出站指纹脱敏": "フィンガープリントのマスキングをオフにしました", "已关闭开发者角色兼容": "developer ロール互換を無効にしました", "已关闭网关跨域访问": "ゲートウェイの CORS アクセスをオフにしました", + "已删除分组「{name}」": "グループ「{name}」を削除しました", "已取消「{label}」的单独配置": "「{label}」の個別設定を解除しました", + "已取消安装": "インストールをキャンセルしました", "已取消导入": "インポートをキャンセルしました", "已取消导出": "エクスポートをキャンセルしました", + "已取消恢复": "復元をキャンセルしました", + "已启用登录": "ログイン有効", + "已复制全部版本信息": "バージョン情報をすべてコピーしました", "已应用": "適用済み", "已开启": "オン", "已开启:其他设备把 API 地址指向 {url}{panel}。": "オン:他のデバイスが API アドレスを {url} に向ければ一緒に使えます{panel}。", + "已恢复默认分组:概览 / 转发链路 / 诊断 / 系统": "既定のグループに戻しました:概要 / 転送経路 / 診断 / システム", + "已指定出口": "出口を指定", "已接管": "引き継ぎ済み", "已改回内置默认提示词": "内蔵のデフォルトシステムプロンプトに戻しました", + "已新建分组「{name}」,点组头的 + 把菜单移进来": "グループ「{name}」を作成しました。見出しの + でメニューを移動できます", "已清掉「{label}」界面编辑的正文": "「{label}」の画面で編集した本文をクリアしました", "已清掉界面编辑的提示词(回落文件 / 内置默认)": "画面で編集したシステムプロンプトをクリアしました(ファイル / 内蔵デフォルトにフォールバック)", + "已清除本机保存的 API Key": "このブラウザに保存された API Key を削除しました", "已生效": "有効", "已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。": "最小限の中立的なプロンプトへ自動切替しました(アップストリームのコンテンツブロックに当たったもので、多くは system 指紋の誤検知です)。{until} に自動解除されます。", "已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。": "最小限の中立システムプロンプトに自動的に切り替えました(アップストリームのコンテンツブロックに当たりました。多くは system のフィンガープリントの誤検知です)。{until} に自動解除されます。期間中は本モード自身のシステムプロンプトは送信されません。システムプロンプトを修正すればすぐに解除できます。", "已自动改用最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。": "", + "已运行": "稼働時間", + "已退出登录": "ログアウトしました", + "已隐藏": "非表示", "巴西葡萄牙语": "ブラジルポルトガル語", "希望接入新的提供商或模型": "新しいプロバイダーやモデルへの対応を希望", + "库中 schema": "DB 内 schema", "库主文件的大小(不含运行期间的 WAL 临时文件,退出时已自动并回)。": "データベース本体ファイルのサイズ(実行中の WAL 一時ファイルは含みません。終了時に自動でマージし直されます)。", "库存目标": "在庫目標", + "应用版本:{version}": "アプリのバージョン:{version}", + "应用状态": "アプリの状態", + "应用窗口": "アプリウィンドウ", "建立上游 TCP/TLS 连接或代理隧道的最大等待时间(秒),默认 30 秒,范围 1~3600。": "アップストリームへの TCP/TLS 接続またはプロキシトンネルを確立する最大待機時間(秒)。デフォルト 30 秒、範囲 1~3600。", - "开发者角色兼容": "開発者ロール互換", + "开发构建": "開発ビルド", + "开发者角色兼容": "developer ロール互換", "开发者角色兼容设置已刷新": "developer ロール互換の設定を更新しました", "开启后新发生的请求才会被记录(已经过去的请求补不回来)。报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。": "オン以降に発生したリクエストだけが記録されます(過去の分は遡って取得できません)。ペイロードはローカルデータベースに保存され、直近 500 件のみ保持(超過分は古いものから破棄)。件数は左の「データ → データ保存」の「デバッグペイロード」に表示されます。", "开启后新发生的请求才会被记录(已经过去的请求补不回来),请求日志的「预览对话」也随之开始保存原始正文。上游报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。": "オンにした後に発生したリクエストのみ記録されます(過去のリクエストは遡って記録できません)。リクエストログの「会話プレビュー」もこれを機に原文の保存を開始します。アップストリームのメッセージはローカルデータベースにまとめて保存され、直近 500 件のみ保持します(超過分は古いものから破棄)。件数は左側の「データ → データストレージ」の「デバッグメッセージ」で確認できます。", @@ -218,111 +355,186 @@ "开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 活动套餐」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。与「开机自动启动」同时开启时,开机后也不建界面(只留托盘与后台进程),点托盘才打开 —— 效果与关窗后一致。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。": "", "开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 活动套餐」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。": "オンにすると、ウィンドウを閉じるのは単なる非表示ではなく、UI ウィンドウ全体を破棄します:基盤の WebView2 の常駐プロセス 6 つ(約 200MB のメモリ)も終了し、ゲートウェイの転送、定期チェックイン、残高照会はバックグラウンドプロセスで引き続き動作し、影響を受けません。UI が必要なときはトレイアイコンをクリックして開き直します(WebView のコールドスタートは約 1 秒)。注意:UI 破棄中は「ZCode アクティブプラン」チャネルの認証コードトークンが UI によって生成されるため補給できません —— このチャネルを通るリクエストは 503 となって理由とともにリクエストログに記録され、UI を開き直すと自動的に復旧します。このスイッチは「ウィンドウを閉じたときはトレイに最小化」がオンのときのみ意味を持ち、ウィンドウの最小化は影響を受けません。", "开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。": "オンにすると、ログインページはブラウザーのバックグラウンドで自動的に検証を完了します(人間には気づかれません)が、スクリプトは試行のたびにまず問題を 1 問解く必要があります —— ブルートフォース攻撃と大量登録のコストが大幅に上がります。影響するのはパネルのログイン / 登録のみで、API クライアントの API Key とは無関係です。", + "开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。桌面端与网页端各自独立:桌面端主要服务本机与局域网,网页端(Docker / 服务端)直接暴露在网络上的登录页风险更高,建议保持开启。": "有効にすると、ログインページはバックグラウンドで認証を完了します(人間には意識されません)が、スクリプトによる試行は毎回 1 問解く必要があり、総当たりやアカウントの先取りのコストが大幅に上がります。影響するのはパネルのログイン / 登録のみで、API クライアントの API Key とは無関係です。デスクトップと Web は独立しています。デスクトップは主に本機と LAN 向け、Web(Docker / サーバー)はネットワークに露出するため有効のままを推奨します。", "开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。": "オンにすると、ゲートウェイは転送ごとに**アップストリームへ送ったリクエスト**(ヘッダー + ボディ)と**アップストリームからの応答**(ステータスコード + ヘッダー + ボディ)をローカルに丸ごと保存し、リクエストログページの「詳細」で確認できます。リクエストヘッダーの Authorization・Cookie・API キーなどの認証フィールドはすべて [redacted] に置き換わり、平文では保存されません。リクエスト / レスポンスのボディはそのまま保存されます(あなたの対話内容が含まれる場合があります)。ペイロードは直近 500 件のみ保持し、超過分は古いものから破棄します。トラブルシュート用の一時スイッチなので、不要なときはオフを推奨します。", "开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看;请求日志详情的「预览对话」也依赖本开关 —— 它保存的是客户端发来的请求体与网关下发的响应体(两侧各截断到 128 KiB),不开这个开关时预览对话没有数据可看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。上游报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。": "オンにすると、ゲートウェイは転送のたびに**アップストリームへ送るリクエスト**(リクエストヘッダー + リクエストボディ)と**アップストリームが返すレスポンス**(ステータスコード + レスポンスヘッダー + レスポンスボディ)を完全にローカルへ保存し、リクエストログページの「詳細」で確認できるようにします。リクエスト詳細の「会話プレビュー」もこのスイッチに依存します —— 保存するのはクライアントから届いたリクエストボディとゲートウェイが返したレスポンスボディ(いずれも 128 KiB で切り詰め)で、スイッチがオフのときはプレビューできるデータがありません。リクエストヘッダー内の Authorization、Cookie、API Key などの認証情報フィールドは一律 [redacted] に置き換えられ、平文でディスクに残りません。リクエストボディとレスポンスボディはそのまま保存されます(会話内容が含まれる場合があります)。アップストリームのメッセージは直近 500 件のみ保持し、超過分は古いものから破棄します。これはトラブルシューティング用の一時的なスイッチですので、不要なときはオフにすることをおすすめします。", "开启局域网访问": "LAN アクセスをオンにする", "开放后,局域网内其他设备的浏览器打开本机 IP 即可进入管理面板(需管理员账号登录)。": "公開後は、LAN 内の他のデバイスのブラウザーで本機の IP を開くだけで管理パネルに入れます(管理者アカウントでのログインが必要)。", "开机自动启动": "自動起動", + "开(最多 {n} 轮)": "オン(最大 {n} ラウンド)", "当前三段都是官方原文。": "現在、3 つの段はすべて公式原文です。", "当前固定为浅色模式,不随系统变化。": "現在はライトモードに固定されており、システムには連動しません。", "当前固定为深色模式,不随系统变化。": "現在はダークモードに固定されており、システムには連動しません。", + "当前密码": "現在のパスワード", "当前已改过:{edited}。": "現在の変更箇所:{edited}。", + "当前形态": "実行形態", "当前按 100% 显示(默认比例)。": "現在は 100% で表示中(デフォルト比率)。", "当前按 k / M 缩写显示(如 1.20M / 8.4k)。": "現在は k / M 略記で表示中(例:1.20M / 8.4k)。", "当前按 {n}% 显示。": "現在は {n}% で表示中。", "当前按「万 / 亿」式本地量级词显示(如 1.2亿 / 8400万)。": "現在は「万 / 億」式のローカル量級語で表示中(例:1.2億 / 8400万)。", "当前是透传模式:存下来也不会发出,切成「替换 / 追加」之后才生效。": "現在はパススルーモードです:保存しても送信されず、「置換 / 追加」に切り替えてから有効になります。", - "当前显示为「1.20M / 8.4k」这类英文缩写。": "現在は「1.20M / 8.4k」のような英字略語で表示されています。", + "当前显示为「1.20M / 8.4k / 1.2B」这类英文缩写。": "現在は「1.20M / 8.4k / 1.2B」のような英語略記で表示しています。", "当前显示为「1.2亿 / 8400万」这类中文量级。": "現在は「1.2億 / 8400万」のような中国語の桁表記で表示されています。", "当前浏览器以管理员身份登录着本面板。「退出登录」会撤销这台设备的登录会话(30 天内的自动续期一并失效),需要重新输入账号密码;其他已登录的设备不受影响。": "現在のブラウザーは管理者としてこのパネルにログインしています。「ログアウト」はこのデバイスのログインセッションを取り消し(30 日以内の自動更新も無効になります)、アカウントとパスワードの再入力が必要です。他のログイン済みデバイスは影響を受けません。", + "当前环境不支持修改机器人校验设置": "", "当前环境不支持局域网访问设置": "現在の環境は LAN アクセス設定に対応していません", + "当前环境不支持整库备份": "この環境ではデータベース全体のバックアップは使えません", "当前环境不支持整库备份(仅桌面端可用)": "この環境ではデータベース全体のバックアップは使えません(デスクトップ版のみ)", + "当前环境不支持整库恢复": "この環境ではデータベース全体の復元は使えません", "当前环境不支持整库恢复(仅桌面端可用)": "この環境ではデータベース全体の復元は使えません(デスクトップ版のみ)", + "当前环境不支持读取版本信息(桥接方法缺失)": "この環境ではバージョン情報を取得できません(ブリッジメソッドが見つかりません)", + "当前环境不支持退出面板登录": "", "当前生效的正文来自{source}{file}。保存后这一份会盖过提示词文件与内置默认{note}。": "現在有効な本文の出所は{source}です{file}。保存するとこの本文がシステムプロンプトファイルと内蔵デフォルトより優先されます{note}。", + "当前界面": "現在の画面", + "当前管理员:{name}": "現在の管理者:{name}", "当前跟随操作系统的深浅色设置,系统切换时界面会自动跟上。": "現在は OS のライト / ダーク設定に連動しており、システムの切替時に UI も自動的に追従します。", + "待迁移旧数据": "移行待ちの旧データ", + "待配置": "未設定", + "忘记密码时无法在界面里找回:需要停掉程序后,从数据库删除管理员记录(kv 表的 panelAdmin 键),重启后按首次使用重新注册。具体命令与各形态的库文件路径见 docs 目录下的「重置管理员」文档。": "パスワードを忘れた場合、画面から復旧することはできません。プログラムを停止し、データベースから管理者レコード(kv テーブルの panelAdmin キー)を削除してから再起動し、初回と同様に登録し直してください。具体的なコマンドと形態別のデータベースパスは docs の「管理者のリセット」を参照してください。", "恢复中…": "復元中…", "恢复失败:{message}": "復元に失敗:{message}", "恢复官方原文": "公式原文に戻す", "恢复整库": "データベースを復元", "恢复整库备份": "全体バックアップを復元", "恢复默认": "デフォルトに戻す", + "恢复默认侧边栏": "サイドバーを既定に戻す", "想回到原来那份,把这里清空再保存即可(不会删掉你配的文件路径)。": "元のものに戻したい場合は、ここを空にして保存するだけです(設定したファイルパスは削除されません)。", "想要的新功能或改进想法": "実現したい新機能や改善のアイデア", "所有未单独配置的提供商都用这一份": "個別設定していないすべてのプロバイダーがこれを使用します", + "所有菜单都已在这一组": "すべてのメニューがこのグループにあります", + "手机端只做查看与高频操作;添加账号、编辑凭证、改写客户端配置仍需桌面端。": "", + "托管网页界面": "Web UI を配信", + "把 Base URL 复制给客户端": "", + "把地址里的 your-server 与 USERNAME 换成你自己的;目录不存在时会自动创建。": "URL 内の your-server と USERNAME を自分の環境に置き換えてください。ディレクトリが無い場合は自動で作成します。", "把整份数据库(账号、网关配置、事件日志、请求统计、调试报文与桌面设置)导出成一个 SQL 文件,恢复时整体替换本机全部数据 —— 与上面「账号导入 / 导出」的逐条合并是两种语义:恢复会连配置、日志与当前启用状态一起换成备份那台机器的状态。导出默认包含日志与调试报文(文件可能很大),不需要时可以取消勾选只备业务数据。恢复前会自动把当前数据备份到配置目录的 backups/ 下,恢复后需要重新登录管理面板。": "データベース全体(アカウント・ゲートウェイ設定・イベントログ・リクエスト統計・デバッグペイロード・デスクトップ設定)を 1 つの SQL ファイルに書き出し、復元時はこのマシンの全データをまるごと置き換えます —— 上の「アカウントのインポート / エクスポート」とは意味が別で、あちらは 1 件ずつのマージ、復元は設定・ログ・有効状態までバックアップしたマシンの状態にそっくり入れ替わります。書き出しは既定でログとデバッグペイロードを含みます(ファイルは大きくなりがち)。不要ならチェックを外して業務データだけにできます。復元の直前には現在のデータを設定フォルダの backups/ へ自動バックアップし、復元後は管理パネルへ再ログインが必要です。", + "把菜单移入 {name}": "メニューを {name} へ移動", + "把菜单移入「{name}」": "メニューを「{name}」へ移動", + "把账号、网关配置、自定义提供商、Agent 配置、代理池 / 订阅、签到与余额记录同步到 WebDAV 或 S3 兼容存储,换台机器后一键取回。事件日志、请求明细、调试报文与面板登录态不在同步范围内(它们是本机的运行痕迹)。上传的账号凭证仍是加密形态,解密用的账号密钥由「同步密码」单独加密后一并上传 —— 同步密码不会保存到云端,换机下载时需要重新输入。": "アカウント、ゲートウェイ設定、カスタムプロバイダー、Agent 設定、プロキシプール / サブスクリプション、チェックインと残高の記録を WebDAV または S3 互換ストレージに同期し、別のマシンでワンクリックで復元できます。イベントログ、リクエスト明細、デバッグ通信記録、パネルのログイン状態は対象外です(これらは本機の実行履歴です)。アップロードされるアカウント認証情報は暗号化されたままで、復号に使うアカウントキーは「同期パスワード」で別途暗号化して一緒にアップロードします。同期パスワードはクラウドに保存されないため、別のマシンで復元する際に再入力が必要です。", + "把路径里的主目录显示成 ~,截图分享时不暴露用户名": "パス内のホームディレクトリを ~ と表示し、スクリーンショットでユーザー名が漏れないようにします", + "把路径里的主目录(如 C:\\Users\\你的用户名)显示成 ~,截图分享时不暴露用户名。只影响本页显示与复制,不改任何文件。": "パス内のホームディレクトリ(例: C:\\Users\\ユーザー名)を ~ と表示し、スクリーンショットでユーザー名が漏れないようにします。このページの表示とコピーだけに影響し、ファイルは変更しません。", "报表天数": "レポートの日数", "报表页的热力图与按天趋势,默认 99999 天(近似全量保留),可填 1–99999 天。": "レポートページのヒートマップと日次トレンド。既定は 99999 日(ほぼ全量保持)で、1〜99999 日を指定できます。", + "拖动分组 {name}": "グループ {name} をドラッグ", + "拖动左侧 ⠿ 调整顺序(菜单可以跨组拖);拨开关控制显隐;点分组名可重命名;组头的 + 把别的菜单移进来;眼睛按钮隐藏整组。改动即时生效并记住,左侧侧边栏就是结果。键盘也能排序:把手聚焦后按 Alt+↑ / ↓。": "左側の ⠿ をドラッグして順序を変更(メニューはグループをまたげます)。スイッチで表示/非表示、グループ名をクリックで名前変更、グループ見出しの + で他のメニューを移動、目のボタンでグループごと非表示。変更は即座に反映され保存されます(左のサイドバーが結果です)。キーボード操作:ハンドルにフォーカスして Alt+↑ / ↓。", + "拖动菜单 {name}": "メニュー {name} をドラッグ", + "拖动调整分组顺序(Alt+↑ / ↓ 也可)": "ドラッグでグループの順序を変更(Alt+↑ / ↓ でも可)", + "拖动调整顺序,可跨组(Alt+↑ / ↓ 也可)": "ドラッグで順序を変更、グループをまたげます(Alt+↑ / ↓ でも可)", "指定错误码直接换号": "指定エラーコードで直接アカウント切替", - "指纹脱敏": "フィンガープリントのマスキング", + "指纹脱敏": "フィンガープリント除去", "指纹脱敏设置已刷新": "フィンガープリントのマスキング設定を更新しました", "按天聚合保留天数": "日次集計の保持日数", "按提供商配置": "プロバイダー別設定", + "换一台服务器": "別のサーバーに切り替える", "排队等待": "キューイング待機", "排队等待次数": "キューイングの待機回数", "排队等待秒数": "キューイングの待機秒数", "排队等待设置已刷新": "キューイング待機設定を更新しました", + "接入": "接続", + "控制主侧边栏显示哪些菜单、按什么顺序、怎么分组。隐藏只影响导航显示,不会撤销页面访问权限;被隐藏的页面仍可通过地址直接打开。": "サイドバーに表示するメニュー、その順序、グループ分けを設定します。非表示はナビゲーションの表示だけに影響し、アクセス権は失われません。非表示にしたページも URL から直接開けます。", "控制报表与请求日志里 Token 读数的写法:开启后按「万 / 亿」式的本地量级词显示(简体为万 / 亿,繁体为萬 / 億,日文为万 / 億,韩文为만 / 억),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。英文 / 葡语的本地量级词就是 k / M,这项对它们没有区别。只影响显示口径,不改变任何统计与存储的数值。": "レポートとリクエストログにおける Token の数値表記を制御します:オンにすると「万 / 億」式のローカル量級語で表示し(簡体字は万 / 亿、繁体字は萬 / 億、日本語は万 / 億、韓国語は만 / 억)、オフにすると k / M 略記を使用します(アップストリームのドキュメントやインターフェースのフィールド表記と一致)。英語 / ポルトガル語ではローカル量級語がそのまま k / M なので、この項目による違いはありません。影響するのは表示の形式のみで、統計や保存される数値は変わりません。", - "控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。": "レポートとリクエストログでのトークン数の表記を切り替えます:オンで中国語の桁表記(1.2億 / 8400万)、オフで k / M 略語(アップストリームのドキュメントや API フィールドと同じ書き方)。表示にだけ影響し、統計や保存値は一切変わりません。", + "控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M / B 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。": "レポートとリクエストログの Token 表記を切り替えます。オンでは中国語の数量単位(1.2億 / 8400万)、オフでは k / M / B の略記(上流のドキュメントや API フィールドと同じ書き方)で表示します。表示だけが変わり、統計値や保存値は変わりません。", "控制界面的深浅色,与左侧边栏底部的三个主题按钮是同一个设置(改哪一处,另一处立刻跟上)。「跟随系统」会随操作系统当前的浅色 / 深色自动切换,并在系统主题变化时即时跟上;选「浅色」或「深色」则把界面固定在该模式,不再随系统变化。窗口标题栏的深浅色会一并同步,不会出现深色界面配一条浅色标题栏的情况。": "UI のライト / ダークを制御します。左サイドバー下部の 3 つのテーマボタンと同じ設定です(どちらを変更しても、もう一方にすぐ反映されます)。「システム」は OS の現在のライト / ダークに自動で切り替わり、システムテーマの変化にも即座に追従します。「ライト」または「ダーク」を選ぶと UI はそのモードに固定され、システムには連動しなくなります。ウィンドウタイトルバーのライト / ダークも一緒に同期され、ダークの UI にライトのタイトルバーが付くようなことはありません。", "提建议": "提案する", "提示词文件": "システムプロンプトファイル", "提示词文件已更新": "システムプロンプトファイルを更新しました", "提示词正文": "システムプロンプト本文", "提示词正文已更新": "システムプロンプト本文を更新しました", + "操作系统": "オペレーティングシステム", + "改动仍保存在本机,修复网络或凭据后下一次改动会自动重试,也可以手动点「上传到云端」。": "変更は本機に保存されています。ネットワークや認証情報を修正すれば次回の変更で自動的に再試行されます。手動で「クラウドへアップロード」を押すこともできます。", "改完立即生效,不用重启(下一个请求就用新模式)。降级是自动的临时状态,解除后本模式自己的提示词立刻恢复。": "変更後は即時有効で再起動は不要です(次のリクエストから新しいモードが適用されます)。フォールバックは自動的な一時状態で、解除されると本モード自身のシステムプロンプトがすぐに復帰します。", "放大或缩小整个界面(文字与控件一起变),效果与浏览器 Ctrl +/- 相同:共 11 档,立即生效并记住。": "UI 全体を拡大または縮小します(文字とコントロールが一緒に変化)。効果はブラウザーの Ctrl +/- と同じ:全 11 段階で、即時有効かつ記憶されます。", "数据": "データ", "数据保留": "データ保持", "数据存储": "データストレージ", + "数据库 schema": "データベース schema", "数据库不可用": "データベース利用不可", "数据库文件": "データベースファイル", + "数据目录": "データディレクトリ", "数据统一保存在配置目录下的 agent2api.db;如需更换位置,请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "データは設定ディレクトリ配下の agent2api.db にまとめて保存されます。場所を変更する場合は、環境変数 AGENT2API_PROXY_HOME を設定してからプログラムを再起動してください。", "整体替换": "まるごと置き換え", "整库备份 / 恢复": "データベース全体のバックアップ / 復元", + "新分组名": "新しいグループ名", "新增 {added} 个、更新 {updated} 个{suffix}{note}": "新規 {added} 件、更新 {updated} 件{suffix}{note}", + "新密码": "新しいパスワード", + "新密码不能与当前密码相同": "新しいパスワードは現在のものと異なる必要があります", + "新密码至少 8 位": "パスワードは 8 文字以上にしてください", + "新建分组": "グループを追加", + "日志文件": "ログファイル", "日语": "日本語", + "显示 {name}": "{name} を表示", + "显示分组 {name}": "グループ {name} を表示", "显示模式": "表示モード", + "显示这一组": "このグループを表示", "更新": "更新", + "更新出网线路": "更新の出口経路", + "更新通道": "更新チャネル", "替换": "置換", "替换 = 客户端 system / developer 消息全部删掉,换成网关的提示词;追加 = 保留客户端内容,只在开头 system 块之后多插一条。两者都只改发给上游的副本,客户端看到的响应不变。": "置換 = クライアントの system / developer メッセージをすべて削除し、ゲートウェイのシステムプロンプトに差し替えます。追加 = クライアントの内容を保持し、先頭の system ブロックの後ろに 1 件挿入するだけ。どちらもアップストリームへ送るコピーのみを変更し、クライアントが見るレスポンスは変わりません。", "替换为网关提示词": "ゲートウェイのシステムプロンプトに置換", "替换前会自动把当前数据备份到配置目录的 backups/ 文件夹。": "置き換えの直前、現在のデータを設定フォルダの backups/ へ自動バックアップします。", + "替换并恢复": "置き換えて復元", "有未保存的修改": "未保存の変更があります", + "有未保存的改动:保存后才能同步(测试连接不受影响)。": "未保存の変更があります。同期するには先に保存してください(接続テストは可能です)。", + "服务商": "プロバイダー", + "服务器地址": "サーバーアドレス", "期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。": "このモードの間、独自のプロンプトは送信されません。プロンプトを修正できたらすぐ解除できます。", "未单独配置:模式、提示词文件与正文都跟随上面的全局配置(当前「{mode}」)。": "個別設定なし:モード、システムプロンプトファイル、本文はいずれも上記のグローバル設定に従います(現在は「{mode}」)。", + "未启用": "無効", "未开启轻量模式:关窗仅隐藏窗口,界面进程常驻内存,随时打开不等待": "軽量モードはオフ:ウィンドウを閉じても非表示になるだけで、UI プロセスはメモリに常駐し、いつでも待ち時間なく開けます", "未开启,developer 角色消息会原样发往上游,不认识该角色的上游会报错(400)。": "無効:developer ロールのメッセージはそのままアップストリームへ送られ、このロールを認識しないアップストリームはエラー(400)を返します。", "未开启,客户端 system 模板会原样发往上游,可能被内容审核误拦(400)。": "オフの場合、クライアントの system テンプレートはそのままアップストリームへ送られ、コンテンツ審査で誤ってブロックされる(400)可能性があります。", "未开启,浏览器跨域无法直连网关。": "オフの場合、ブラウザーから CORS でゲートウェイに直接接続できません。", "未开启,转发时不保存任何原始报文。": "オフの場合、転送時に生のメッセージは一切保存されません。", "未开启:网关只监听 127.0.0.1,仅本机可以访问。": "オフ:ゲートウェイは 127.0.0.1 のみで待ち受け、本機からのみアクセスできます。", + "未注册": "未登録", + "未注册闸门": "未登録ゲート", "未知原因": "不明な原因", "未知来源": "不明な出典", + "未知设备": "不明なデバイス", "未知账号": "不明なアカウント", "未能读取 Cline 伪装头设置,请稍后重试": "Cline 偽装ヘッダー設定を読み込めませんでした。しばらくしてから再試行してください", "未能读取 models.dev 元数据状态,请稍后重试": "models.dev メタデータの状態を取得できませんでした。しばらくして再試行してください。", + "未能读取云同步设置,请稍后重试。": "クラウド同期の設定を読み取れませんでした。しばらくしてから再試行してください。", "未能读取开发者角色兼容设置,请稍后重试": "developer ロール互換の設定を読み取れませんでした。しばらくしてから再試行してください", "未能读取指纹脱敏设置,请稍后重试": "フィンガープリントのマスキング設定を読み込めませんでした。しばらくしてから再試行してください", "未能读取系统提示词设置,请稍后重试": "システムプロンプト設定を読み込めませんでした。しばらくしてから再試行してください", "未能读取网关跨域访问设置,请稍后重试": "ゲートウェイの CORS アクセス設定を読み込めませんでした。しばらくしてから再試行してください", "未能读取调试模式设置,请稍后重试": "デバッグモード設定を読み込めませんでした。しばらくしてから再試行してください", + "未能读取账户信息,请稍后重试": "アカウント情報を取得できませんでした。しばらくしてから再試行してください", + "本地账号、配置、签到与余额记录变化后自动上传(合并 1 秒内的连续改动,最长 10 秒传一次;内容没变时不发请求)。": "ローカルのアカウント・設定・チェックイン・残高記録の変更後に自動アップロードします(1 秒以内の連続変更はまとめ、最大 10 秒に 1 回。内容が変わっていなければリクエストしません)。", + "本机 APK 版本": "この端末の APK バージョン", + "本机应用版本": "この端末のアプリバージョン", + "本机界面版本": "この端末の UI バージョン", + "本机的业务数据(账号、配置、Agent 配置、代理池 / 订阅、签到与余额记录)将上传并": "本機の業務データ(アカウント・設定・Agent 設定・プロキシプール / サブスクリプション・チェックインと残高の記録)をアップロードし、", "本机的全部数据(账号、网关配置、日志统计与桌面设置)将被所选备份文件": "このマシンの全データ(アカウント・ゲートウェイ設定・ログ統計・デスクトップ設定)は、選択したバックアップファイルで", + "本机的账号、网关配置、Agent 配置、代理池 / 订阅、签到与余额记录将被远端快照": "本機のアカウント・ゲートウェイ設定・Agent 設定・プロキシプール / サブスクリプション・チェックインと残高の記録は、リモートのスナップショットで", "本次无需刷新": "今回は更新の必要がありません", "机器人校验": "ロボット検証", + "来源设备:{device}": "取得元デバイス:{device}", + "来自 {device},生成于 {time}": "{device} から、{time} に作成", + "架构": "アーキテクチャ", "查询管理员状态失败:{message}": "管理者状態の照会に失敗しました:{message}", "校验身份:这三段必须各自成块排在最前(实测:三段并成一段会被回 405 / 3012)。因此这里按段编辑,不能合成一段。改过的段以你的文本为准,没改的段继续用官方原文;清空某一段 = 那一段恢复官方原文。": "で身元を検証します:この 3 つの段はそれぞれ独立したブロックとして先頭に並ぶ必要があります(実測:3 段をまとめて 1 段にすると 405 / 3012 が返ります)。そのためここでは段ごとに編集し、1 段にまとめることはできません。変更した段はあなたのテキストが優先され、変更していない段は引き続き公式原文を使用します。ある段を空にする = その段は公式原文に戻ります。", + "桌面端": "デスクトップ版", + "桌面端:登录 / 注册需要 ALTCHA 人机验证": "デスクトップ:ログイン / 登録に ALTCHA の認証が必要", + "桌面端:登录 / 注册需要 ALTCHA 人机验证(当前正在使用)": "デスクトップ:ログイン / 登録に ALTCHA の認証が必要(現在使用中)", "桌面运行时不可用,暂时无法保存": "デスクトップランタイムが利用できないため、保存できません", + "档案": "プロファイル", "检测中…": "確認中…", + "概览": "概要", "模式": "モード", "模式改为「{mode}」": "モードを「{mode}」に変更しました", "次": "回", "次日 00:00": "翌日 00:00", "正在保存上游原始报文:": "アップストリームの生メッセージを保存中:", + "正在修改…": "変更中…", "正在剥离出站请求里的审核指纹:表头键值整段删除,模板句最小改写。": "送信リクエスト内の審査フィンガープリントを除去しています:ヘッダーのキーと値は丸ごと削除し、テンプレート文は最小限に書き換えます。", + "正在安装…": "インストール中…", "正在应答跨源请求:浏览器跨域请求可直连 /v1/*。": "クロスオリジンリクエストに応答中:ブラウザーからの CORS リクエストは /v1/* に直接接続できます。", "正在归一角色:developer 角色消息转发前改写为 system。": "ロールを正規化中:developer ロールのメッセージは転送前に system に書き換えられます。", "正在注册…": "登録中…", "正在联网更新": "オンラインで更新中", + "正式版": "正式版", "每条请求轮换到下一个可用账号;新会话交给当前最闲的那位。适合想把手里的号摊开用、不让某一个一直扛。": "リクエストごとに次の利用可能なアカウントへ順送りし、新しいセッションはいちばん暇なアカウントに任せます。手持ちのアカウントをまんべんなく使いたいときに。", "每条请求都重新按策略选路 —— 行为与「关闭」一致,仅保留会话识别的记录。": "リクエストごとに戦略に従って選び直します —— 動作は「オフ」と同じで、セッションの記録だけが残ります。", "每次等待的时长,默认 0 = 跟随上游建议(实测 9~30 秒,上游没给建议时用 15 秒)。可填 0–120 秒强制一个固定时长。": "1 回の待機時間。デフォルト 0 = アップストリームの推奨に従う(実測 9~30 秒、アップストリームが推奨を示さない場合は 15 秒)。0–120 秒を入力して固定の待機時間を強制することもできます。", @@ -335,15 +547,32 @@ "注意:这一家的「网关自带」开关现在是关的,改过的正文要等开关打开才生效。": "注意:このプロバイダーの「ゲートウェイ内蔵」スイッチは現在オフです。変更した本文はスイッチをオンにするまで有効になりません。", "流式响应相邻数据之间允许的最大空闲时间(秒),收到新数据后重新计时,默认 300 秒,范围 1~3600。": "ストリーミング応答の隣接するデータ間に許される最大アイドル時間(秒)。新しいデータを受信すると計時がリセットされます。デフォルト 300 秒、範囲 1~3600。", "流式响应空闲超时": "ストリーミング応答アイドルタイムアウト", + "测试连接": "接続テスト", + "浏览器 / PWA": "ブラウザ / PWA", + "浏览器只在 https 或 localhost 下提供离线缓存;局域网 http 访问不影响正常使用": "ブラウザは https または localhost でのみオフラインキャッシュを提供します。LAN の http アクセスでも通常どおり使えます。", + "浏览器标签页": "ブラウザのタブ", + "浏览器面板": "ブラウザパネル", "添加提供商": "プロバイダーを追加", "添加提供商…": "プロバイダーを追加…", "添加自定义头": "カスタムヘッダーを追加", "添加要单独配置的提供商": "個別設定するプロバイダーを追加", + "清除本机凭证": "この端末の認証情報を削除", "清除这里的正文": "ここの本文をクリア", "点下面的入口会用系统默认浏览器打开 GitHub 的对应表单(需要 GitHub 账号,模板已预设好,填完直接提交即可)。": "下のリンクをクリックすると、システムのデフォルトブラウザーで GitHub の該当フォームが開きます(GitHub アカウントが必要。テンプレートは用意済みなので、記入してそのまま送信するだけです)。", + "点击重命名": "クリックで名前を変更", + "点此添加或切换服务器": "タップしてサーバーを追加・切り替え", + "点此添加服务器": "タップしてサーバーを追加", + "版本": "バージョン", + "版本信息": "バージョン情報", + "版本信息 · 所连网关环境": "バージョン情報 · 接続中のゲートウェイ", "状态码 {code} 已在名单里": "ステータスコード {code} はすでにリストにあります", "状态码必须是 {min}–{max} 的整数(收到: {text})": "ステータスコードは {min}–{max} の整数でなければなりません(受信値:{text})", + "环境": "環境", + "生成时间:{time}": "作成日時:{time}", "用 models.dev 补齐上游没声明的模型能力与思考档位(联网刷新,内嵌快照始终生效)": "上流が宣言していないモデル能力と推論レベルを models.dev で補完する(オンライン更新。同梱スナップショットは常に有効)", + "用于加密上云的账号密钥。请自己记牢 —— 它不保存到云端,换机下载时必须输入同一个密码,否则账号数据无法解密。": "クラウドに送るアカウントキーの暗号化に使います。忘れないでください —— クラウドには保存されず、別のマシンで復元する際に同じパスワードを入力しないとアカウントデータを復号できません。", + "用浏览器菜单里的「添加到主屏幕」把这一页装成 App:全屏打开、没有地址栏,和原生应用一样。": "ブラウザメニューの「ホーム画面に追加」でこのページをアプリとして入れられます。全画面・アドレスバーなしで、ネイティブアプリと同じ感覚です。", + "界面": "", "界面缩放": "UI ズーム", "界面缩放比例": "UI ズーム比率", "界面语言": "表示言語", @@ -351,15 +580,29 @@ "界面语言只保存在本机(localStorage)。切换后整页立即刷新,不影响账号、密钥与转发。": "表示言語は本機(localStorage)にのみ保存されます。切り替えるとページ全体が即座に再読み込みされ、アカウント、キー、転送には影響しません。", "界面里编辑的正文": "画面で編集した本文", "留空 = 用内置默认提示词": "空欄 = 内蔵のデフォルトシステムプロンプトを使用", + "留空使用 AWS 官方地址": "空欄なら AWS 公式アドレスを使用", "登录 / 注册需要通过 ALTCHA 人机验证(工作量证明)": "ログイン / 登録には ALTCHA の人間検証(プルーフ・オブ・ワーク)が必要です", "登录 / 账号切换 / 429 切换等系统事件,可填 1–3650 天。": "ログイン / アカウント切替 / 429 切替などのシステムイベント。1–3650 日の範囲で入力できます。", "登录 / 账号切换 / 429 切换等系统事件,可填 1–99999 天。": "ログイン / アカウント切り替え / 429 切り替えなどのシステムイベント。1〜99999 日で指定できます。", + "登录 Agent2API": "Agent2API にログイン", + "登录人机验证": "ログイン CAPTCHA", + "监听地址": "待受アドレス", + "目标路径:{path}": "保存先パス:{path}", "确定继续?": "続行しますか?", + "确认上传": "アップロードを確認", + "确认新密码": "新しいパスワード(確認)", "确认缩短保留天数": "保存日数の短縮を確認", + "社区与支持": "コミュニティとサポート", + "离线缓存未启用": "オフラインキャッシュは無効です", + "积分合计只统计当前筛选出来的账号": "ポイント合計を現在の絞り込み結果のみで計算する", + "移入「{name}」": "「{name}」へ移動", + "程序路径": "実行ファイルのパス", "空 = 不要这一份,回落提示词文件 / 内置默认提示词。": "空 = この本文を使用せず、システムプロンプトファイル / 内蔵デフォルトにフォールバックします。", + "空分组 · 点组头的 + 把菜单移进来": "空のグループ · 見出しの + でメニューを移動できます", "立即解除": "今すぐ解除", "第一段": "第 1 段", "第一段(身份句)": "第 1 段(アイデンティティ文)", + "第三方应用密码": "サードパーティアプリのパスワード", "第三段": "第 3 段", "第三段(动态段)": "第 3 段(動的段)", "第二段": "第 2 段", @@ -368,8 +611,12 @@ "等待期间请求日志的状态列会显示「排队中」阶段,详情弹窗的「内部重试」里能逐次看到等待时长;等待用尽后返回 HTTP 503(不是 401/429:既不会刷新凭证,也不会把账号标成限额)。": "待機中はリクエストログのステータス列に「キューイング中」の段階が表示され、詳細ダイアログの「内部リトライ」で待機時間を 1 回ずつ確認できます。待機を使い切ると HTTP 503 を返します(401/429 ではありません:認証情報の更新も、アカウントをレート制限としてマークすることも行いません)。", "等比放大或缩小整个界面(文字、控件、间距一起变),效果与浏览器按 Ctrl +/- 相同:80%–130%、5% 一档。窗口本身不缩放,变的是页面内容的显示比例,设置立即生效并记住,下次启动直接按这个比例打开。放得越大可视范围越小,窗口较窄或表格较宽时不建议调得太大。": "UI 全体を等比で拡大または縮小します(文字、コントロール、余白が一緒に変化)。効果はブラウザーで Ctrl +/- を押した場合と同じ:80%–130%、5% 刻み。ウィンドウ自体は拡大縮小されず、変わるのはページ内容の表示比率です。設定は即時有効かつ記憶され、次回起動時はその比率で直接開きます。大きくするほど表示できる範囲は狭くなるため、ウィンドウが狭い場合やテーブルが広い場合はあまり大きくしすぎないことをおすすめします。", "简体中文": "簡体字中国語", + "管理员已注册": "管理者登録済み", + "管理员账号": "管理者アカウント", + "系统": "", "系统提示词模式": "システムプロンプトモード", "系统提示词设置已刷新": "システムプロンプト設定を更新しました", + "累计请求": "累計リクエスト", "繁体中文": "繁体字中国語", "约 {n} 字符": "約 {n} 文字", "结构": "構造", @@ -380,19 +627,39 @@ "编辑网关自带提示词 · {label}": "ゲートウェイ内蔵システムプロンプトを編集 · {label}", "缩放比例": "ズーム比率", "网关": "ゲートウェイ", + "网关 Key 数量": "ゲートウェイキー数", + "网关状态": "", "网关自带": "ゲートウェイ内蔵", "网关跨域访问已开启": "ゲートウェイの CORS アクセスをオンにしました", "网关跨域访问设置已刷新": "ゲートウェイの CORS アクセス設定を更新しました", "网关跨域访问(CORS)": "ゲートウェイの CORS アクセス(クロスオリジン)", "网关面(/v1/*)默认不应答浏览器跨源请求:页面直连会被预检拦下,只报「无法连接 API」。注意来源是 * —— 未配「网关 Key」时任何网页都能借本机网关打上游,故默认关闭;只给自己用的话,让页面与网关同源(本地反代 /v1)更稳妥。面板接口(/api/*)不受影响。": "ゲートウェイ側(/v1/*)はデフォルトでブラウザーのクロスオリジンリクエストに応答しません:ページから直接接続するとプリフライトで拒否され、「API に接続できません」としか表示されません。許可オリジンが * である点に注意 —— 「ゲートウェイキー」を設定していない場合、どんな Web ページでも本機のゲートウェイ経由でアップストリームへアクセスできてしまうため、デフォルトではオフです。自分の利用だけであれば、ページとゲートウェイを同一オリジンにする(ローカルで /v1 をリバースプロキシする)ほうが安全です。パネルのインターフェース(/api/*)は影響を受けません。", + "网页端": "Web", "网页端的界面缩放由浏览器自己控制(Ctrl + / Ctrl -,或浏览器菜单里的缩放),此项不可调。": "Web 版の UI ズームはブラウザー側で制御します(Ctrl + / Ctrl -、またはブラウザーメニューのズーム)。この項目は調整できません。", + "网页端:登录 / 注册需要 ALTCHA 人机验证": "Web:ログイン / 登録に ALTCHA の認証が必要", + "网页端:登录 / 注册需要 ALTCHA 人机验证(当前正在使用)": "Web:ログイン / 登録に ALTCHA の認証が必要(現在使用中)", + "群晖 NAS": "Synology NAS", + "群晖需在「控制面板 → 应用程序门户 → WebDAV」里启用服务,端口默认 5005(HTTP)/ 5006(HTTPS)。": "Synology では「コントロールパネル → アプリケーションポータル → WebDAV」でサービスを有効にしてください。既定のポートは 5005(HTTP)/ 5006(HTTPS)です。", + "腾讯云 COS": "Tencent Cloud COS", + "自动同步": "自動同期", + "自动同步上次失败": "前回の自動同期に失敗しました", + "自动续写": "自動継続生成", "自定义提供商 {id}": "カスタムプロバイダー {id}", "自愈保护": "自動回復ガード", + "至少 8 位,换机下载时需要": "8 文字以上。別のマシンでの復元時に必要", + "至少要保留一个分组": "グループは最低 1 つ必要です", "英语": "英語", "装上{size}的官方身份提示词": "{size}の公式アイデンティティプロンプトを適用", + "装成 App 后从桌面图标直接进入:全屏打开、没有地址栏。": "アプリとして入れるとホーム画面のアイコンから直接開けます。全画面・アドレスバーなし。", + "覆盖": "上書き", "解除失败:{message}": "解除に失敗しました:{message}", + "计数": "カウント", "计量单位": "表記単位", + "设置不可隐藏(隐藏了只能清缓存找回来)": "「設定」は非表示にできません(戻すにはデータの削除が必要になります)", + "详细日志": "詳細ログ", "语言设置": "言語設定", + "请先保存设置再同步": "同期する前に設定を保存してください", + "请填一个分组名": "グループ名を入力してください", "请填写管理员账号(64 字符以内)": "管理者アカウントを入力してください(64 文字以内)", "请求发出后等待上游响应头的最大时间(秒),默认 300 秒,范围 1~3600。": "リクエスト送出後、アップストリームのレスポンスヘッダーを待つ最大時間(秒)。デフォルト 300 秒、範囲 1~3600。", "请求提供商 / 模型支持": "プロバイダー / モデルの対応をリクエスト", @@ -401,18 +668,30 @@ "请求超时": "リクエストタイムアウト", "请求转发到上游失败时(服务器瞬时错误、连接失败,以及 WorkBuddy 的 11-128 敏感词拦截),网关会等一段间隔后再发一次。次数分两档,管的是两件事:①「同一账号重试次数」在同一账号上原地重发几次 —— 一般是提示词被上游拦截、或链路瞬时抖动,等一会儿重发往往就好了;②「切换账号重试次数」这份请求最多再换几个账号试 —— 首发的那个账号不算,每换一个扣一次,不分是不是同一家(换到同一家的下一个账号也算一次),换满还失败就把错误返回给客户端。两项都设为 0 表示失败不重试、直接报错。注意账号级限额(429)不占这里的换号次数 —— 那类失败走「冷却该账号并换下一个账号」的降级,与这里的重试是两条路。另有「指定错误码直接换号」名单(默认 402):命中名单的上游状态码不在同一账号重发,直接按队列换下一个账号继续试(换号也占「切换账号重试次数」),换满仍失败才把错误返回给客户端。": "アップストリームへの転送が失敗した場合(サーバーの一時的なエラー、接続失敗、および WorkBuddy の 11-128 センシティブワードブロック)、ゲートウェイは一定の間隔を置いてからもう一度送信します。回数は 2 系統に分かれ、それぞれ別のことを司ります:①「同一アカウントのリトライ回数」は同じアカウントでそのまま何回再送するか —— 多くはシステムプロンプトがアップストリームでブロックされた、または経路の一時的な揺らぎで、しばらく待って再送すれば直ることがよくあります;②「アカウント切替のリトライ回数」はこのリクエストであと最大何個のアカウントに切り替えて試すか —— 初回のアカウントは含まず、切り替えるたびに 1 回消費し、同じプロバイダーかどうかは問いません(同じプロバイダーの次のアカウントへの切替も 1 回と数えます)。使い切ってもなお失敗する場合はエラーをクライアントへ返します。両方とも 0 にすると、失敗時にリトライせず直接エラーを返します。注意:アカウント単位のレート制限(429)はここの切替回数を消費しません —— その種の失敗は「そのアカウントを冷却して次のアカウントへ」というフォールバックを通り、ここのリトライとは別ルートです。また「指定エラーコードで直接アカウント切替」リスト(デフォルト 402)があります:リストに該当するアップストリームのステータスコードは同一アカウントでの再送を行わず、キューの次のアカウントへ直接切り替えて試行を続けます(切替は「アカウント切替のリトライ回数」を消費します)。使い切ってもなお失敗する場合にのみ、エラーがクライアントへ返されます。", "请求重试": "リクエストリトライ", + "请输入同步密码": "同期パスワードを入力してください", + "请输入当前密码": "現在のパスワードを入力してください", + "读取云同步设置失败:{message}": "クラウド同期の設定を読み取れませんでした:{message}", + "读取失败:{message}": "読み込みに失敗しました: {message}", "读取完整非流式响应体允许的最大时间(秒),默认 300 秒,范围 1~3600。": "非ストリーミングレスポンスボディ全体を読み切るために許される最大時間(秒)。デフォルト 300 秒、範囲 1~3600。", + "读取网关信息失败:{message}": "ゲートウェイ情報の読み込みに失敗しました: {message}", + "读取远端…": "リモートを読み取り中…", + "读取远端信息失败:{message}": "リモート情報の取得に失敗しました:{message}", "调试报文": "デバッグメッセージ", "调试模式": "デバッグモード", "调试模式设置已刷新": "デバッグモード設定を更新しました", "负载均衡": "負荷分散", + "账号列表": "アカウント一覧", "账号导入 / 导出": "アカウントのインポート / エクスポート", "账号路由": "アカウントルーティング", + "账号页表格的两项显示偏好,只存在本机(localStorage),不影响账号数据与转发。「积分合计只统计当前筛选出来的账号」:账号页每一家的分组带右侧有一枚「可用合计」,默认按整组求和 —— 不受筛选、搜索影响,被筛掉的账号也计入(余额是资产,禁用也只表示不参与转发,额度仍在账上)。打开这一项后改为只累加当前可见的行:例如筛出 1 个 100 积分的账号,合计就是 100,而不是同组两个账号之和 1,000。分页不参与这个口径 —— 翻页看的是同一份结果的不同屏,合计不随页码变化。「隐藏账号名」与表头「账号」旁边那颗眼睛是同一个开关:打开后账号列的名字整体显示成星号,适合截图分享时遮挡账号。": "アカウント一覧表の表示設定です。本機のみ(localStorage)に保存され、アカウントデータや転送には影響しません。「ポイント合計を現在の絞り込み結果のみで計算する」:アカウントページの各プロバイダ帯の右側に「利用可能合計」があります。既定ではグループ全体を合算し、絞り込みや検索の影響を受けず、除外されたアカウントも含まれます(残高は資産であり、無効化は転送に参加しないだけで、ポイントは残ります)。この項目をオンにすると、現在表示中の行だけを合算します。たとえば 100 ポイントのアカウント 1 件に絞り込むと合計は 100 になり、同じグループの 2 件の合計 1,000 にはなりません。ページ送りはこの計算に含まれません。ページは同じ結果を別の画面で見ているだけなので、ページを変えても合計は変わりません。「アカウント名を隠す」は表の「アカウント」列の横にある目のアイコンと同じスイッチです。オンにするとアカウント列の名前がアスタリスクで表示され、スクリーンショットの共有時に隠せます。", + "账户": "アカウント", "超出保留天数的部分会被删除:事件日志是登录、账号切换、429 切换这类系统事件。把天数改小(例如 30 天改成 7 天)保存后会立即删除超出的历史数据,此操作不可恢复;改大或保持不变不会删除任何数据。可填范围为 1–3650 天。请求日志 / 按天聚合 / 签到记录已全量保留、不再自动删除,要清历史请到对应页面手动清空。": "保存期間を超えた部分は削除されます:イベントログはログイン、アカウント切替、429 切替といったシステムイベントです。日数を小さくして(例:30 日→7 日)保存すると、超えた履歴データは即座に削除され、元に戻せません。大きくするか変更しない場合は何も削除されません。設定できる範囲は 1〜3650 日です。リクエストログ / 日次集計 / チェックイン記録は全量保持となり自動削除されません。履歴を消すには、それぞれのページで手動でクリアしてください。", "超出的历史数据会被立即删除,且不可恢复。": "超過した履歴データはすぐに削除され、元に戻せません。", "超时": "タイムアウト", "超时设置已刷新": "タイムアウト設定を更新しました", + "跟随系统 · 浅色 · 深色": "システム · ライト · ダーク", "跟随默认": "デフォルトに従う", + "跨域访问": "CORS", "路由": "ルーティング", "路由设置已刷新": "ルーティング設定を再読み込みしました", "跳过 {n} 个": "スキップ {n} 件", @@ -421,40 +700,79 @@ "转发一个请求时按什么顺序挑账号:①「路由策略」决定排队规则 —— 顺序优先是排第 1 的接走所有请求、直到它不能答(限流 / 故障 / 额度尽)才轮到下一位;轮流均摊是每条请求换下一位,雨露均沾;余额优先是用量占比最低的先用,「谁空谁上」。②「会话粘性」让同一会话(相同的模型 + 相同的开头消息)尽量跟住同一个账号 —— 多轮对话的上下文缓存能复用、少换号少浪费;自动档粘 5 分钟、会话内档粘 6 小时、每轮与关闭都不粘。账号的排队位次(账号表的第一列)在所有策略下都是地基,策略在它之上再排。此外路由还有三层自愈保护默认生效,不需要配置:转发失败的账号退避休息一会儿(15 分钟起步翻倍、封顶 1 小时),被 429 的账号垫底排到队尾一圈,余额不足 / Token 周期用量到顶的账号由限制器拦下 —— 恢复后自动回到队列。": "", "转发前将 developer 角色消息转换为 system,兼容不支持 developer 角色的 OpenAI 兼容上游": "転送前に developer ロールのメッセージを system に変換し、developer ロール非対応の OpenAI 互換アップストリームにも対応", "转发失败的账号休息一会儿再回来:15 分钟起步,连续失败翻倍,封顶 1 小时;成功一次立即恢复。目的是「让这次失败先凉一会儿」,免得一条条地反复撞同一个坏账号。": "転送に失敗したアカウントはしばらく休んでから戻ります:初回 15 分、連続失敗で倍増、上限 1 時間。1 回成功すれば即復帰。「いったん失敗を冷ます」ためのもので、同じ不調アカウントにリクエストを連打しないための仕組みです。", + "转发链路": "転送経路", + "转发链路 · 诊断 · 系统": "", "轮询间隔": "ポーリング間隔", + "软件版本": "ソフトウェアバージョン", "轻量模式": "軽量モード", "轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 套餐池(Start Plan)通道不可用,相关请求会在请求日志里注明原因": "軽量モードが有効です:ウィンドウを閉じると UI プロセスを破棄し(約 200MB のメモリを解放)、ゲートウェイはバックグラウンドで継続します。UI が破棄されている間、ZCode プランプール(Start Plan)経路は利用できず、該当リクエストはリクエストログに理由が記録されます", "轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 活动套餐通道不可用,相关请求会在请求日志里注明原因": "軽量モードがオン:ウィンドウを閉じると UI プロセスを破棄し(約 200MB のメモリを解放)、ゲートウェイはバックグラウンドで継続します。UI 破棄期間中は ZCode アクティブプランチャネルは利用できず、該当するリクエストは理由がリクエストログに記録されます", "轻量模式(关窗释放界面内存)": "軽量モード(閉じると UI のメモリを解放)", + "输入同步密码": "同期パスワードを入力", + "输入当前登录用的密码": "", "输入状态码,回车添加": "ステータスコードを入力し、Enter で追加", + "运行形态": "実行形態", "还原": "元に戻す", "还有额度时被 429 的账号排到队尾,30 分钟后自动回到队列(成功转发一次也会立即回来)。它标记的是「疑似共享池被别人抢」,不是「这个账号本身有问题」。⚠️ 注意它与路由策略的配合:在「家内轮询」下,队尾意味着这一家整体被往后排 —— 如果某个提供商的账号被批量限流,它们会集体沉到队尾,那个家可能长时间轮不到。这时可以关掉本项,让 429 过的账号只受冷却约束(冷却到点自动回来)。": "枠が残っているのに 429 を食らったアカウントは最後尾に回り、30 分後に自動復帰します(1 回でも転送が成功すればすぐ戻る)。これは「共有プールを他の人に取られている疑い」を示すもので、「このアカウント自体の問題」ではありません。⚠️ ルーティング戦略との組み合わせに注意:「プロバイダー内ローテーション」では最後尾 = プロバイダーごと後ろへ回ることを意味します —— あるプロバイダーのアカウントがまとめてレート制限されると全員が最後尾に沈み、そのプロバイダーが長く回ってこない可能性があります。その場合は本項をオフにして、429 済みアカウントをクールダウンだけに任せるとよいでしょう(時間が来れば自動で戻ります)。", + "还没有同步过。": "まだ同期していません。", + "还没有添加服务器。点右上角「添加」,填入网关所在机器的 IP 或域名与端口。": "サーバーがまだありません。右上の「追加」から、ゲートウェイが動作するマシンの IP またはドメインとポートを入力してください。", "还没有装载任何数据。": "まだデータが読み込まれていません。", + "还没有读到版本信息": "バージョン情報をまだ読み込んでいません", "这一份就是「替换 / 追加」模式下网关要发的 system 正文。": "これが「置換 / 追加」モードでゲートウェイが送信する system の本文です。", + "这一项已隐藏": "この項目は非表示です", + "这一项正在显示": "この項目は表示中です", + "这台网关未启用机器人校验": "このゲートウェイではボット確認が無効です", + "这台网关没有管理员,用一把已启用的 API Key 访问。": "このゲートウェイには管理者がいません。有効な API Key でアクセスしてください。", + "进程 PID": "プロセス PID", + "远程根目录": "リモートのルートディレクトリ", + "远端同一路径下的旧快照。事件日志、请求明细与调试报文不会上传。": "同じリモートパスにある古いスナップショットを上書きします。イベントログ・リクエスト明細・デバッグ通信記録はアップロードされません。", + "远端已有一份快照,将被覆盖": "リモートに既存のスナップショットがあり、上書きされます", + "远端快照使用了另一把账号密钥,但没有携带密钥文件,无法在本机解密。请在原来那台机器上重新上传一次。": "リモートのスナップショットは別のアカウントキーを使用していますが、キーファイルが含まれていないため本機では復号できません。元のマシンからもう一度アップロードしてください。", + "远端快照使用的账号密钥与本机不同。请输入上传那台机器设置的同步密码,用它解出密钥装到本机后再继续下载。": "リモートのスナップショットは本機と異なるアカウントキーを使用しています。アップロードしたマシンで設定した同期パスワードを入力し、キーを復号して本機に導入してからダウンロードを続けてください。", + "远端数据与当前版本不兼容": "リモートのデータは現在のバージョンと互換性がありません", + "远端该路径下还没有快照,本次是首次上传。": "そのリモートパスにはまだスナップショットがありません。今回が初回のアップロードです。", + "远端路径:{path}": "リモートパス:{path}", + "远端还没有可下载的同步数据": "リモートにダウンロード可能な同期データがありません", + "连不上这台网关。确认地址与端口正确、网关已启动,且已开启「局域网访问」;用 https 时还要确认证书有效": "このゲートウェイに接続できません。アドレスとポートが正しいこと、ゲートウェイが起動していること、「LAN アクセス」が有効なことを確認してください。https の場合は証明書が有効かも確認してください。", "连接中超时": "接続タイムアウト", + "连接失败:{message}": "接続に失敗しました:{message}", "连续失败到阈值后把这个账号整个打开(默认关)。与失败退避的分工:退避管「单次失败先凉一会儿」,惩罚轻、自动恢复;熔断管「这个账号现在整体不对劲,别再一条条地试」,要连续成功若干次才关。打开期间该账号被跳过,除非全场只剩它 —— 那时仍放行试一次。": "連続失敗がしきい値に達したらアカウント全体を切り離します(既定はオフ)。失敗バックオフとの役割分担:バックオフは「1 回の失敗をいったん冷ます」軽い罰で自動復帰、サーキットブレーカーは「このアカウントは全体としておかしい、1 件ずつ試すのはやめよう」で、連続成功が数回続かないと閉じません。開いている間そのアカウントはスキップされますが、他に使えるものがなければ 1 回だけ試します。", "追加": "追加", "追加网关提示词": "ゲートウェイのシステムプロンプトを追加", "退出失败:{message}": "ログアウトに失敗しました:{message}", "退出登录": "ログアウト", + "退出这台服务器的登录,不影响其他服务器": "このサーバーのみログアウトします。他への影響はありません", "选择备份文件并恢复": "バックアップファイルを選んで復元", "选路规则": "選択ルール", "透传": "パススルー", "透传 = 不动客户端 system;替换 = 删掉客户端的 system / developer,换成网关提示词;追加 = 保留客户端内容,在最前面多插一条。": "", "透传客户端 system": "クライアントの system をパススルー", "透传客户端 system(默认)": "クライアントの system をパススルー(デフォルト)", + "通常是网络问题,点右侧重试": "多くはネットワークの問題です。右の再試行を押してください", "通用": "一般", "遇到 Bug、报错或异常行为": "Bug、エラー、異常動作が発生した場合", "配置目录下的 agent2api.db(账号、日志、请求记录、调试报文与设置都在里面)。": "設定ディレクトリ配下の agent2api.db(アカウント、ログ、リクエスト記録、デバッグメッセージ、設定がすべて入っています)。", + "重启后生效,不影响当前操作": "再起動で反映されます。作業中の内容には影響しません", + "重置会清空管理员账号与密码,面板将回到「未注册」状态。": "リセットすると管理者アカウントとパスワードが消去され、パネルは「未登録」状態に戻ります。", + "重置管理员": "管理者のリセット", "重试设置已刷新": "リトライ設定を更新しました", "重试间隔": "リトライ間隔", "铸造守卫每隔多久看一次库存(毫秒)。只影响发现缺货的反应速度 —— 库存够用时轮询不会铸造任何令牌。默认 2500 毫秒,可填 1000–30000。": "発行ガードが在庫を何ミリ秒ごとに確認するか。品切れの検知速度にだけ影響します —— 在庫が足りているときはポーリングしても何も発行しません。既定 2500 ミリ秒、1000–30000 を指定可。", "问题反馈": "問題のフィードバック", + "阿里云 OSS": "Alibaba Cloud OSS", + "限流中账号": "レート制限中のアカウント", "随包内嵌的快照(发版时的那份)": "アプリ同梱のスナップショット(リリース時のもの)", + "隐藏 {name}": "{name} を非表示", + "隐藏分组 {name}": "グループ {name} を非表示", + "隐藏用户名": "ユーザー名を隠す", + "隐藏账号名(显示成星号)": "アカウント名を隠す(アスタリスク表示)", + "隐藏这一组": "このグループを非表示", "非流式响应超时": "非ストリーミング応答タイムアウト", "面板登录": "パネルログイン", + "面板端口": "パネルポート", "面板管理员账号": "パネル管理者アカウント", "韩语": "韓国語", + "预设": "プリセット", "验证码令牌池": "CAPTCHA トークンプール", "验证码令牌池设置已刷新": "CAPTCHA トークンプール設定を再読み込みしました", "默认值通常够用,出问题时才需要调": "既定値でたいてい足ります。問題が出たときだけ調整してください", @@ -463,6 +781,7 @@ "默认开启。只改 role 字段本身,消息内容与顺序都不动;本项只改发给上游的副本,客户端看到的响应内容不变。": "デフォルトは有効。role フィールドだけを書き換え、メッセージの内容と順序はそのまま。送信する副本だけが対象で、クライアントが見る応答は変わりません。", "默认开启。命中明细会记进请求日志的「敏」标签(悬停可看命中了哪几条规则)。本项只改发给上游的副本,客户端看到的响应内容不变。": "デフォルトはオン。ヒットした明細はリクエストログの「敏」タグに記録されます(ホバーするとどのルールにヒットしたか確認できます)。本項目はアップストリームへ送るコピーのみを変更し、クライアントが見るレスポンス内容は変わりません。", "默认网关只监听 127.0.0.1,只有本机能访问。开启后网关改听所有网卡,同一局域网内的设备可以把 API 地址指向本机 IP 一起使用。出于安全考虑,开启前必须先注册一个面板管理员:管理接口从此要求管理员会话或网关 Key,模型额度不会对局域网裸奔;一把启用的网关 Key 都没有时,转发接口也会拒绝服务,直到创建第一把。改动需要重启应用生效。「同时开放网页管理面板」把管理界面也出给局域网(其他设备的浏览器打开本机 IP 即可管理),默认关闭 —— 桌面端的面板仍只由本程序自己出。": "デフォルトでは、ゲートウェイは 127.0.0.1 のみで待ち受け、本機からのみアクセスできます。オンにするとゲートウェイはすべてのネットワークアダプターで待ち受けるようになり、同じ LAN 内のデバイスは API アドレスを本機の IP に向けて一緒に利用できます。セキュリティ上の理由から、オンにする前にまずパネル管理者を登録する必要があります:管理インターフェースは管理者セッションまたはゲートウェイキーを要求するようになり、モデルのクォータが LAN に無防備に晒されることはありません。有効なゲートウェイキーが 1 つもない場合、最初の 1 つを作成するまで転送インターフェースもサービスを拒否します。変更の反映にはアプリの再起動が必要です。「Web 管理パネルも同時に公開」は管理画面も LAN に公開します(他のデバイスのブラウザーで本機の IP を開けば管理できます)。デフォルトはオフ —— デスクトップ版のパネルは引き続き本プログラム自身のみが提供します。", + "({n} 个整组隐藏)": "({n} グループを非表示)", "({n} 行)": "({n} 行)", "({n} 项被覆盖)": "({n} 項目を上書き)", "(全部为默认值)": "(すべてデフォルト値)", @@ -475,6 +794,7 @@ "(未生效)": "(未適用)", ",{items}": "、{items}", ",{time}": "、{time}", + ",以及代理池 / 订阅里可能存在的代理凭据。请使用只有你能访问的私有存储;账号密钥由同步密码单独加密,泄露存储本身不等于泄露账号。": "、およびプロキシプール / サブスクリプションに存在しうるプロキシ認証情報が含まれます。自分だけがアクセスできるプライベートなストレージを使用してください。アカウントキーは同期パスワードで別途暗号化されているため、ストレージが漏れてもアカウントが漏れるわけではありません。", ",包括恢复后需要重新登录管理面板。": "まるごと置き換えられます。復元後は管理パネルへの再ログインが必要です。", ",可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "、そのままログインに使用できます。厳重に保管し、第三者に渡したり公共の場所へアップロードしたりしないでください。", ",文件里后续的改动在清空这里之前不再生效": "。ファイル側の以降の変更はここをクリアするまで有効になりません", diff --git a/desktop-tauri/i18n/i18n-src/ja/tasks.json b/desktop-tauri/i18n/i18n-src/ja/tasks.json index 34216c3a..38163390 100644 --- a/desktop-tauri/i18n/i18n-src/ja/tasks.json +++ b/desktop-tauri/i18n/i18n-src/ja/tasks.json @@ -7,6 +7,7 @@ "{n} 秒后": "{n} 秒後", "✅ {task}:{summary}": "✅ {task}:{summary}", "✅ 已更新「{label}」": "✅ 「{label}」を更新しました", + "「{name}」已执行": "", "上次执行 {time}": "前回の実行 {time}", "上次执行 {time}({result})": "前回の実行 {time}({result})", "下次执行 {time}({relative})": "次回の実行 {time}({relative})", @@ -17,21 +18,30 @@ "停留在「请求日志」页时按此间隔重新拉取请求日志;页面不可见时不请求。": "「リクエストログ」ページを開いている間、この間隔でリクエストログを再取得します;ページが非表示のときはリクエストしません。", "冷却中,{relative}重试": "クールダウン中、{relative}後に再試行", "凭证自动维护": "認証情報の自動メンテナンス", + "分": "", "即将执行": "まもなく実行", + "可填 {min}–{max} {unit}": "", "可填 {min}–{max} {unit}(默认 {def} {unit})": "{min}–{max} {unit} を入力可能(既定 {def} {unit})", + "后台任务": "", "定时任务已刷新": "定期タスクを更新しました", "定期向 GitHub 查询最新发布版本,默认每 20 分钟一次。检查结果、排期和限流冷却跨重启保留;手动检查也会合并重复请求并遵守冷却。开关与间隔在「软件更新」面板的「更新设置」弹窗里配置(定时任务页不再渲染这一条)。": "GitHub に最新のリリースバージョンを定期的に問い合わせます(既定は 20 分ごと)。チェック結果・スケジュール・レート制限のクールダウンは再起動をまたいで保持されます;手動チェックも重複リクエストをまとめ、クールダウンに従います。スイッチと間隔は「ソフトウェア更新」パネルの「更新設定」ダイアログで設定します(定期タスクページにはこの行を表示しません)。", "定期拉取最新模型清单,重启沿用排期与缓存。客户端读取模型列表的后台刷新也遵守本任务开关、间隔和失败冷却;手动获取模型可提前刷新。": "最新のモデルリストを定期的に取得します。再起動後もスケジュールとキャッシュを引き継ぎます。クライアントがモデル一覧を読み取る際のバックグラウンド更新も、このタスクのスイッチ・間隔・失敗時のクールダウンに従います;手動でのモデル取得なら前倒しで更新できます。", "定期拉取最新模型清单,重启沿用排期与缓存。客户端读取模型列表的后台刷新也遵守本任务开关、间隔和失败冷却;手动获取模型可提前刷新。同一轮顺带刷新 models.dev 模型元数据(能力位与思考档位,可在设置页单独关闭)。": "最新のモデルリストを定期的に取得します。再起動後もスケジュールとキャッシュを引き継ぎます。クライアントがモデル一覧を読み取る際のバックグラウンド更新も、このタスクのスイッチ・間隔・失敗時のクールダウンに従います;手動でのモデル取得なら前倒しで更新できます。同じラウンドで models.dev のモデルメタデータ(能力フラグと思考ティア、設定ページで個別にオフにできます)も更新します。", "定期遍历账号,只刷新已过期或临期的凭证。重启后沿用上次排期,失败账号在冷却后重试;转发需要的按需续期不受此开关影响。": "アカウントを定期的に走査し、期限切れまたは期限間近の認証情報だけを更新します。再起動後も前回のスケジュールを引き継ぎ、失敗したアカウントはクールダウン後に再試行します;転送時に必要なオンデマンド更新はこのスイッチの影響を受けません。", + "已{action}「{name}」": "", "已执行": "実行しました", + "已更新间隔": "", + "开启": "", "执行失败:{reason}": "実行に失敗しました:{reason}", "报表自动刷新": "レポートの自動更新", "改动立即生效,不需要重启程序": "変更はすぐに反映され、アプリの再起動は不要です", + "改间隔": "", "日志页自动刷新": "ログページの自動更新", "模型目录刷新": "モデルカタログ更新", "正在加载定时任务…": "定期タスクを読み込み中…", + "每 {n} {unit}": "", "没有可显示的定时任务。后端未返回任务清单,请确认网关正在运行。": "表示できる定期タスクがありません。バックエンドがタスク一覧を返しませんでした。ゲートウェイが稼働しているか確認してください。", + "没有后台任务": "", "状态每 20 秒自动同步": "状態は 20 秒ごとに自動同期", "由页面按间隔自动刷新": "ページが間隔に従って自動更新", "立即执行": "今すぐ実行", @@ -40,5 +50,8 @@ "还没有执行记录": "実行記録はまだありません", "间隔不能为空(可填 {min}–{max} {unit})": "間隔を空にはできません({min}–{max} {unit} を入力可能)", "间隔必须在 {min}–{max} {unit} 之间(当前填的是 {value})": "間隔は {min}–{max} {unit} の範囲で指定してください(現在の入力値は {value})", - "间隔必须是整数": "間隔は整数で入力してください" + "间隔必须是 {min}–{max} 的整数": "", + "间隔必须是整数": "間隔は整数で入力してください", + "页面刷新由浏览器执行,只在页面打开时生效": "", + "页面自动刷新": "" } diff --git a/desktop-tauri/i18n/i18n-src/ja/update.json b/desktop-tauri/i18n/i18n-src/ja/update.json index ce2bb5ba..33337b70 100644 --- a/desktop-tauri/i18n/i18n-src/ja/update.json +++ b/desktop-tauri/i18n/i18n-src/ja/update.json @@ -1,9 +1,14 @@ { " · 检查于 {time}": " · {time} に確認", " 已发布(当前 {current}),更新日志如下:": " が公開されました(現在 {current})。更新履歴は以下のとおりです:", + "Discord 群": "Discord サーバー", "GitHub Token 未填写": "GitHub トークン未入力", "GitHub 令牌": "GitHub トークン", - "QQ 群二维码": "QQ グループの二次元コード", + "QQ 群": "QQ グループ", + "QQ 群 / Telegram / Discord 都在这里": "QQ グループ・Telegram・Discord はこちら", + "QQ 群 1": "QQ グループ 1", + "QQ 群 2": "QQ グループ 2", + "Telegram 群": "Telegram グループ", "{version} 已就位,服务正在重启,页面会在恢复后自动刷新…({seconds}s)": "{version} を適用しました。サービスを再起動しています。復旧後にページは自動で再読み込みされます…({seconds} 秒)", "{version}(预发布)": "{version}(プレリリース)", "✅ GitHub 令牌已保存(加密存储,界面不再显示)": "✅ GitHub トークンを保存しました(暗号化して保存され、画面には表示されません)", @@ -30,8 +35,10 @@ "出网线路已切换,但写入磁盘失败(重启后会恢复原线路)": "送信経路を切り替えましたが、ディスクへの書き込みに失敗しました(再起動すると元の経路に戻ります)", "分钟({min} ~ {max})": "分({min} ~ {max})", "到点自动向 GitHub 查询新版本,查到就走「检测到更新」弹窗;关闭后「检查更新」按钮仍可手动触发": "時間になると自動で GitHub に新バージョンを問い合わせ、見つかると「更新を検出」ダイアログを表示します。閉じた後も「更新を確認」ボタンから手動で実行できます", - "加入 QQ 群": "QQ グループに参加", "加入 beta 版": "ベータ版に参加", + "加入交流群": "コミュニティに参加", + "即将满员": "まもなく満員", + "即将满员,请优先加群 2": "まもなく満員 — グループ 2 への参加をお願いします", "去更新": "更新へ移動", "发现新版本 {latest}(当前 {current})。{at}": "新バージョン {latest} が見つかりました(現在 {current})。{at}", "发现新版本 {latest}(当前 {current}),但该版本没有提供容器内更新包;请在宿主机执行 docker compose pull && docker compose up -d 升级。{at}": "新しいバージョン {latest}(現在 {current})が見つかりましたが、このバージョンにはコンテナ内更新パッケージが用意されていません。ホスト側で docker compose pull && docker compose up -d を実行してアップグレードしてください。{at}", @@ -45,14 +52,22 @@ "回滚失败": "ロールバック失敗", "回滚失败:{error}": "ロールバックに失敗しました:{error}", "在 GitHub 查看完整说明": "GitHub で完全な説明を見る", + "在 QQ 里搜索群号即可加入。": "QQ でグループ番号を検索すると参加できます。", + "在浏览器中打开": "ブラウザで開く", + "在浏览器中打开 GitHub 发布页": "GitHub のリリースページをブラウザで開く", "在浏览器中打开项目仓库,点 Star 支持作者": "ブラウザでプロジェクトリポジトリを開き、Star で作者を応援", "在默认浏览器中打开 GitHub 的令牌创建页(备注已预设,无需勾选任何权限,直接点 Generate token)": "既定のブラウザで GitHub のトークン作成ページを開きます(メモは入力済み。権限のチェックは不要で、そのまま Generate token をクリック)", "填写后检查限额 60 → 5000 次/小时(创建令牌无需勾选任何权限)": "入力すると確認の上限が 60 → 5000 回/時間に上がります(トークンの作成に権限のチェックは不要)", + "复制群号 {group}": "グループ番号 {group} をコピー", "安装中": "インストール中", "安装包": "インストーラー", "安装包已下载完成,正在启动安装程序(需要管理员权限,会弹出 UAC 确认框)…": "インストーラーのダウンロードが完了しました。インストーラーを起動しています(管理者権限が必要で、UAC の確認ダイアログが表示されます)…", + "安装包已下载完成,正在启动新的 AppImage…": "インストーラーのダウンロードが完了しました。新しい AppImage を起動しています…", + "安装包已下载完成,正在打开系统安装器…": "インストーラーのダウンロードが完了しました。システムのインストーラーを開いています…", "安装包已下载完成,正在挂载磁盘映像…": "インストーラーのダウンロードが完了しました。ディスクイメージをマウントしています…", + "安装包已就绪:{name}。点击「安装并重启」后会启动新的 AppImage;把它移动到你的应用目录并替换旧文件即完成升级(本程序会先退出以释放文件占用)。": "インストーラーの準備ができました: {name}。「インストールして再起動」で新しい AppImage が起動します。アプリ用フォルダーに移動して旧ファイルと置き換えれば更新完了です(本アプリはファイルを解放するため先に終了します)。", "安装包已就绪:{name}。点击「安装并重启」后需要管理员权限,会弹出 UAC 确认框。": "インストーラーの準備ができました:{name}。「インストールして再起動」をクリックすると管理者権限が必要で、UAC の確認ダイアログが表示されます。", + "安装包已就绪:{name}。点击「打开安装包」后会交给系统安装器,按提示输入密码即可完成安装(本程序无需退出)。": "インストーラーの準備ができました: {name}。「インストーラーを開く」でシステムのインストーラーに渡されます。案内に従ってパスワードを入力してください(本アプリの終了は不要です)。", "安装包已就绪:{name}。点击「打开安装包」后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装。": "インストーラーの準備ができました:{name}。「インストーラーを開く」をクリックするとディスクイメージがマウントされ、アプリを「アプリケーション」にドラッグすればインストールが完了します。", "安装包已挂载,请在弹出的窗口里把应用拖进「应用程序」完成安装。安装完成后重新打开本程序即可。": "インストーラーをマウントしました。表示されたウィンドウでアプリを「アプリケーション」にドラッグし、インストールを完了してください。インストール後は本アプリを開き直せば完了です。", "安装失败": "インストール失敗", @@ -64,6 +79,7 @@ "已取消": "キャンセル済み", "已填写": "入力済み", "已填写(粘贴新令牌可覆盖)": "入力済み(新しいトークンを貼り付けると上書き)", + "已打开系统安装器,请在它弹出的窗口里完成安装。安装完成后重新打开本程序即可。": "システムのインストーラーを開きました。表示されたウィンドウでインストールを完了し、その後このアプリを開き直してください。", "已是最新": "最新です", "已替换为 {version},正在重启服务…": "{version} に置き換えました。サービスを再起動しています…", "已还原上一版本,正在重启服务…": "前のバージョンに戻しました。サービスを再起動しています…", @@ -89,6 +105,7 @@ "更新包从 GitHub 发布页下载,下载完成后可直接启动安装程序并自动重启本程序。安装包会先校验来源域名与文件完整性。检查更新与下载安装包的出网线路、GitHub 令牌都在「更新设置」里:线路默认直连(直连失败时自动借 Clash Verge 的混合端口重试一次),选定指定出口后只走该出口;填写 GitHub 令牌可把检查限额从 60 次/小时提高到 5000 次/小时。": "更新パッケージは GitHub のリリースページからダウンロードし、完了後はそのままインストーラーを起動して本アプリを自動的に再起動できます。インストーラーはダウンロード元のドメインとファイルの完全性を先に検証します。更新の確認とインストーラーのダウンロードの送信経路、GitHub トークンは、いずれも「更新設定」にあります。経路は既定で直接接続(直接接続に失敗すると Clash Verge の混合ポートを借りて一度だけ再試行)で、指定の出口を選ぶとその出口だけを使用します。GitHub トークンを入力すると、確認の上限を 60 回/時間から 5000 回/時間に引き上げられます。", "更新包已就绪:{name}。点「安装并重启」后,面板会校验并替换容器内的程序与界面文件,然后重启服务(页面会自动刷新)。": "更新パッケージの準備ができました:{name}。「インストールして再起動」をクリックすると、パネルがコンテナ内のプログラムと UI ファイルを検証して置き換え、その後サービスを再起動します(ページは自動で再読み込みされます)。", "更新已应用到容器内,重启服务后生效。{at}": "更新はコンテナ内に適用されました。サービスを再起動すると有効になります。{at}", + "更新已应用到容器内,重启服务后生效。{at}(本次检查失败:{error})": "コンテナ内に更新が適用されました。サービスを再起動すると有効になります。{at}(今回の確認は失敗しました:{error})", "更新日志": "更新履歴", "更新设置": "更新設定", "替换完成,正在重启服务…": "置き換えが完了しました。サービスを再起動しています…", @@ -104,10 +121,13 @@ "检查更新": "更新を確認", "检查更新与下载安装包走哪条线路(当前:{current});选择即保存。选项来自「网络代理」页;直连失败时会自动借 Clash 混合端口重试一次,选定指定代理后只走它": "更新の確認とインストーラーのダウンロードがどの経路を通るか(現在:{current})。選択すると保存されます。選択肢は「ネットワークプロキシ」ページのものです。直接接続に失敗した場合は Clash の混合ポートを借りて一度だけ再試行し、指定プロキシを選ぶとそのプロキシだけを使用します", "检查更新失败:{error}": "更新の確認に失敗しました:{error}", + "检查更新失败:{error}(以下版本与更新日志来自上次成功检查,检查于 {time})": "更新の確認に失敗しました:{error}(以下のバージョンと更新履歴は前回の確認成功時({time})のものです)", "检查间隔必须是 {min} ~ {max} 分钟": "確認間隔は {min} ~ {max} 分にしてください", "检查间隔(分钟)": "確認間隔(分)", "检测到更新": "更新を検出", "正在下载 {name}:{percent}%({received} / {total} MB)": "{name} をダウンロード中:{percent}%({received} / {total} MB)", + "正在下载安装包…(下载完成后会交给系统安装器,按提示输入密码即可完成安装)": "インストーラーをダウンロード中…(完了後はシステムのインストーラーに渡されます。案内に従ってパスワードを入力してください)", + "正在下载安装包…(下载完成后会启动新的 AppImage;把它替换掉旧文件即完成升级)": "インストーラーをダウンロード中…(完了後は新しい AppImage が起動します。旧ファイルと置き換えれば更新完了です)", "正在下载安装包…(下载完成后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装)": "インストーラーをダウンロード中…(ダウンロード完了後にディスクイメージをマウントし、アプリを「アプリケーション」にドラッグすればインストールが完了します)", "正在下载安装包…(下载完成后启动安装程序需要管理员权限,会弹出 UAC 确认框)": "インストーラーをダウンロード中…(ダウンロード完了後に起動するインストーラーには管理者権限が必要で、UAC の確認ダイアログが表示されます)", "正在下载服务端更新包…(下载完成后由面板完成替换,重启容器内的服务后生效)": "サーバー更新パッケージをダウンロードしています…(ダウンロード完了後にパネルがファイルを置き換え、コンテナ内のサービスを再起動すると有効になります)", @@ -120,8 +140,9 @@ "点击「检查更新」后,这里会显示最新版本的更新说明。": "「更新を確認」をクリックすると、ここに最新バージョンの更新内容が表示されます。", "点击「检查更新」查询 GitHub 上的最新发布版本。": "「更新を確認」をクリックして GitHub 上の最新リリースを確認します。", "点击「检查更新」查询最新发布版本。": "「更新を確認」をクリックして最新リリースを確認します。", + "点击在浏览器中打开": "クリックでブラウザが開きます", + "点击复制群号": "クリックでグループ番号をコピー", "环境变量": "環境変数", - "用手机 QQ 扫上面的二维码,或在 QQ 里搜索群号加入。": "上の二次元コードをスマホの QQ で読み取るか、QQ でグループ番号を検索して参加してください。", "立即重启": "今すぐ再起動", "粘贴 GitHub 令牌(ghp_… / github_pat_…)": "GitHub トークンを貼り付け(ghp_… / github_pat_…)", "群号": "グループ番号", @@ -135,6 +156,7 @@ "这一版没有提供容器内更新包": "このバージョンにはコンテナ内更新パッケージがありません", "这个版本没有填写发布说明。": "このバージョンのリリースノートは登録されていません。", "通道已切换,但写入磁盘失败(重启后会回到原设置)": "チャンネルを切り替えましたが、ディスクへの書き込みに失敗しました(再起動すると元の設定に戻ります)", + "遇到问题或有使用心得,欢迎到群里交流。": "ご質問や使用感の共有は各グループへどうぞ。", "重启中": "再起動中", "重新粘贴保存一次即可自愈": "もう一度貼り付けて保存すれば復旧します", "重试下载": "ダウンロードを再試行", diff --git a/desktop-tauri/i18n/i18n-src/ko/accounts.json b/desktop-tauri/i18n/i18n-src/ko/accounts.json index b413eb75..d9ccd7d3 100644 --- a/desktop-tauri/i18n/i18n-src/ko/accounts.json +++ b/desktop-tauri/i18n/i18n-src/ko/accounts.json @@ -7,7 +7,9 @@ "0 = 不限": "0 = 제한 없음", "0 = 按本家默认 {default}": "0 = 자체 기본값 {default} 적용", "30 秒 ~ 24 小时;手动查询会顺延下一轮": "30초 ~ 24시간, 수동 조회 시 다음 회차로 순연됩니다", + "7 天内到期": "7일 이내 만료", "API Key": "API Key", + "Accio 国内版": "Accio 중국판", "Base URL": "Base URL", "Clash Verge 里还没有配置混合监听器或节点端口": "Clash Verge에 혼합 리스너나 노드 포트가 아직 구성되어 있지 않습니다", "Clash 出口:": "Clash 아웃바운드: ", @@ -15,12 +17,18 @@ "NewAPI 用户 ID": "NewAPI 사용자 ID", "OpenAI 兼容": "OpenAI 호환", "OpenAI 兼容填到 /v1;Anthropic 填根地址": "OpenAI 호환은 /v1까지, Anthropic은 루트 주소를 입력하세요", + "Qoder 中国版": "Qoder 중국판", + "Qoder 国际版": "Qoder 국제판", "Token 单位": "Token 단위", "Token 时": "Token 이상일 때", "Token 消耗": "Token 소비", "Token 限额": "Token 한도", "Token 限额 · 已禁用": "Token 한도 · 비활성", "Token 限额 · 已跳过": "Token 한도 · 건너뜀", + "WorkBuddy 国内版": "WorkBuddy 중국판", + "WorkBuddy 国际版": "WorkBuddy 국제판", + "ZCode 国内版": "ZCode 중국판", + "ZCode 国际版": "ZCode 국제판", "ZCode 套餐明细 — {name}": "ZCode 플랜 상세 — {name}", "bigmodel_token_production 的值": "bigmodel_token_production 의 값", "{action}{who}": "{action} {who}", @@ -32,13 +40,24 @@ "{label}({count})": "{label} ({count})", "{message}(去该账号的「设置」里填上查询凭证即可)": "{message} (해당 계정의 「설정」에서 조회 자격 증명을 입력하면 됩니다)", "{month}月{day}日 {clock}": "{month}월 {day}일 {clock}", + "{name} · 分组设置": "{name} · 그룹 설정", "{name} 移到 {group} 组内第 {rank} 位": "{name}을(를) {group} 그룹 내 {rank}번째로 이동했습니다", "{name} 移到第 {rank} 顺位": "{name}을(를) 전체 {rank}순위로 이동했습니다", + "{name}的方向": "{name} 방향", "{name}({parts})": "{name} ({parts})", "{name}({time} 起生效)": "{name}({time}부터 적용)", + "{n} 个已计入": "{n}개 합산", + "{n} 个查询失败": "{n}개 조회 실패", "{n} 个模型 ▾": "모델 {n}개 ▾", + "{n} 个模型限流中 · 到恢复时间自动解除": "", "{n} 个模型限流中 · 记录来自上游 429 / 限额码,到恢复时间自动解除": "모델 {n}개 속도 제한 중 · 업스트림 429 / 한도 코드에서 온 기록, 리셋 시간에 자동 해제", "{n} 个请求正在使用该账号(含还在下发内容的流式请求)": "이 계정을 사용 중인 요청 {n}개 (콘텐츠를 계속 내려보내는 스트리밍 요청 포함)", + "{n} 个读数只读到一半(读数可信但不完整)": "{n}개는 일부만 조회됨(값은 신뢰할 수 있으나 불완전)", + "{n} 个读数没有绝对数(只给比例或无额度),未计入": "{n}개는 절대값이 없어(비율만 제공 또는 한도 없음) 합산에서 제외", + "{n} 个账号 · {e} 启用 · {l} 限流": "{n}개 계정 · {e} 활성 · {l} 제한", + "{n} 个账号 · 启用 {e}": "{n}개 계정 · {e} 활성", + "{n} 个还没查到(未查询或正在查询)": "{n}개는 아직 조회되지 않음(미조회 또는 조회 중)", + "{n} 个还没查询": "{n}개는 아직 조회하지 않음", "{n} 分钟": "{n}분", "{n} 分钟后": "{n}분 후", "{n} 天后": "{n}일 후", @@ -47,6 +66,9 @@ "{n} 秒": "{n}초", "{period}窗口": "{period} 구간", "{plan}({time} 领取)": "{plan}({time} 수령)", + "{reason}。该账号的登录态已被上游注销,自动刷新不会再成功,请重新登录该账号": "{reason}. 이 계정의 로그인 상태가 업스트림에서 취소되었습니다. 자동 갱신은 더 이상 성공하지 않으므로 이 계정으로 다시 로그인하세요.", + "{reason}之后组顺位才可拖动(别家的组整组不可见,落点没法定位)": "", + "{reason}之后顺序才可拖动(模型作用域是全局队列的投影,这里没有可写的顺序)": "", "{scope} {used} / {quota} Token,{countdown}重置": "{scope} {used} / {quota} Token, 초기화까지 {countdown}", "{text}过期": "{text} 만료", "{text}过期({full})": "{text} 만료 ({full})", @@ -79,7 +101,9 @@ "✅ 顺序已调整": "", " 出口 IP {ip}": " 아웃바운드 IP {ip}", "、": ", ", - "、补官方会话头、请求体补两个占位工具(上游免费档的三道校验)。": ", 공식 세션 헤더 추가, 요청 본문에 자리 표시 도구 2개 추가 (업스트림 무료 등급의 3중 검증).", + "、补官方会话头、请求体补占位工具(上游免费档的过门校验)。": ", 공식 세션 헤더 추가, 요청 본문에 자리 표시 도구 추가 (업스트림 무료 등급의 형태 검증).", + "「{name}」已在「网络代理」页禁用,请先启用它或换一条": "\"{name}\"은(는) \"네트워크 프록시\" 페이지에서 비활성화되어 있습니다. 먼저 활성화하거나 다른 항목을 선택하세요.", + "「{name}」开着排序规则:先关掉规则,才能手动调整组内顺序": "「{name}」의 정렬 규칙이 켜져 있습니다. 그룹 내 순서를 수동으로 조정하려면 먼저 규칙을 끄세요", "「{name}」本身": "「{name}」 자체", "「已保存的代理」": "「저장된 프록시」", "「网络代理」页还没有出口 —— 去那里新增,或在「订阅管理」里点「同步 Clash Verge」把 Clash 的出口导进来": "「네트워크 프록시」 페이지에 아직 아웃바운드가 없습니다 — 거기서 추가하거나 「구독 관리」에서 「Clash Verge 동기화」를 눌러 Clash의 아웃바운드를 가져오세요", @@ -91,6 +115,8 @@ "上游的 API Key。留空 = 不修改(改凭证不会换账号 id);要清掉已配的凭证用右边的「清除」": "업스트림의 API Key입니다. 비워 두면 변경하지 않습니다 (자격 증명을 바꿔도 계정 id는 바뀌지 않음). 설정된 자격 증명을 지우려면 오른쪽 「지우기」를 사용하세요", "上游的余额读数里没有这个套餐的额度桶,剩余量读不到;余额列显示的是套餐发放的权益额度,不是剩余量": "업스트림 잔액 판독값에 이 플랜의 할당량 버킷이 없어 남은 양은 읽을 수 없습니다. 잔액 열에 표시되는 것은 플랜의 지급 권익이며 남은 양이 아닙니다", "上游返回 {status}": "업스트림이 {status} 반환", + "上面所有规则都相同时,保持你拖出来的顺序": "위의 모든 규칙이 같으면 직접 끌어 놓은 순서를 유지합니다", + "不到 1 分钟": "1분 미만", "不可用:{error}": "사용 불가: {error}", "不限": "제한 없음", "与队列里的上一个账号交换优先级(可能是另一家的账号)": "대기열의 이전 계정과 우선순위 교환 (다른 제공자의 계정일 수 있음)", @@ -98,11 +124,16 @@ "个": "개", "个 · 共": "개 · 총", "个人": "개인", + "主凭证没有固定到期时刻,到期由网关自动续期或本来就不会过期": "기본 자격 증명에는 고정된 만료 시각이 없습니다. 만료 시 게이트웨이가 자동 갱신하거나, 애초에 만료되지 않습니다", "主机": "호스트", "仅将优先级调整到全局第一位,不改变启用状态": "우선순위만 전역 1위로 조정하며 활성 상태는 바꾸지 않습니다", + "仅按手动顺序": "수동 순서만", "仅账号管理": "계정 관리 전용", + "今天 24 点": "", "今天 {clock}": "오늘 {clock}", "今天 {time}": "오늘 {time}", + "今天剩余:{name}": "", + "今天剩余:{name} · 这一份还剩 {value}(每天 24 点回满)": "", "今天还没有领过:上面「可领取的套餐」里逐份点「领取」即可": "오늘은 아직 받은 것이 없습니다. 위의 \"받을 수 있는 플랜\"에서 각 플랜의 \"받기\"를 누르면 됩니다.", "今天(北京时间 {day})已由官方确认到账 {n} 项;领到的是套餐赠送积分,不增加福利模型的 token 池;按自然日重置,明天可再领": "오늘(베이징 시간 {day}) 공식 확인으로 {n}건이 적립되었습니다. 받은 것은 플랜 보너스 포인트이며 혜택 모델의 token 풀은 늘지 않습니다. 자연일 기준으로 초기화되며 내일 다시 받을 수 있습니다", "今天(北京时间 {day})已试过 {n} 次但官方尚未确认到账,": "오늘(베이징 시간 {day}) {n}회 시도했지만 아직 공식 적립 확인이 되지 않았습니다, ", @@ -137,18 +168,22 @@ "余额不足": "잔액 부족", "余额不足 · 已跳过": "잔액 부족 · 건너뜀", "余额低于": "잔액이", + "余额低于阈值时的提示": "잔액이 기준값 아래로 떨어지면 알림", "余额低于阈值,转发时会跳过该账号(余额回升自动恢复)": "잔액이 임계값보다 낮아 포워딩 시 이 계정을 건너뜁니다 (잔액 회복 시 자동 리셋)", "余额低于限制器的阈值,转发时会跳过该账号(余额回升自动恢复)": "잔액이 리미터 임계값보다 낮아 포워딩 시 이 계정을 건너뜁니다 (잔액 회복 시 자동 리셋)", "余额列读数的自动刷新;也是「限制器」余额规则的数据来源。": "잔액 열 수치의 자동 갱신이며, 「리미터」 잔액 규칙의 데이터 소스입니다.", "余额响应为空": "잔액 응답이 비어 있습니다", + "余额已更新": "잔액을 갱신했습니다", "余额查询凭证": "잔액 조회 자격 증명", "余额查询失败:{message}": "잔액 조회 실패: {message}", "余额查询方案:{label}": "잔액 조회 방식: {label}", "余额沿用现有口径;Token 按重置方式内的累计消耗判定(切换类型会清空阈值)": "잔액은 기존 기준을 따르며, Token은 초기화 방식에 따른 누적 소비로 판정합니다 (유형을 바꾸면 임계값이 비워집니다)", + "余额详情": "잔액 상세", "使用套餐": "사용 플랜", "保存失败:{error}": "저장 실패: {error}", "保存规则": "규칙 저장", "修改代理": "프록시 변경", + "倍率 {rate}": "", "免费版": "무료 버전", "全局一条队列:数值越小越先用,不分提供商": "전역 대기열 하나: 숫자가 작을수록 먼저 사용되며 제공자를 가리지 않습니다", "全局唯一:所有提供商的账号都不能重号,数值越小越先用": "전역에서 유일: 모든 제공자의 계정이 같은 숫자를 쓸 수 없으며, 숫자가 작을수록 먼저 사용됩니다", @@ -165,18 +200,30 @@ "关闭则不参与转发(余额查询照常,见「查询设置」)": "끄면 포워딩에 참여하지 않습니다 (잔액 조회는 그대로 동작하며, 「조회 설정」 참고)", "内消耗 ≥": "내 소비 ≥", "再领一次": "한 번 더 받기", + "冷却进度 {percent}%": "", + "凭证": "", "凭证与套餐": "자격 증명과 플랜", + "凭证已刷新": "", + "凭证已被上游注销": "", "凭证已过期,转发时会先刷新": "자격 증명이 만료되었습니다. 포워딩 시 먼저 갱신합니다", "出口": "아웃바운드", "出网代理": "아웃바운드 프록시", + "分组设置": "그룹 설정", "分钟": "분", + "切回全局账号表": "", + "删除中…": "", "删除提供商": "제공자 삭제", "删除账号": "계정 삭제", "删除这条账号记录(不会影响客户端自己的登录态;之后可再点「导入桌面端登录态」加回来)": "이 계정 기록을 삭제합니다 (클라이언트 자체의 로그인 상태에는 영향을 주지 않습니다. 나중에 「데스크톱 로그인 상태 가져오기」를 다시 눌러 되돌릴 수 있습니다)", "删除选中的账号": "선택한 계정 삭제", "到恢复时间后自动解除,无需手动操作": "리셋 시간이 되면 자동 해제되며 수동 조작이 필요 없습니다", "到期 {time}": "{time} 만료", + "到期 近→远": "만료 가까운→먼", + "到期 远→近": "만료 먼→가까운", + "到期时刻 {time}": "", "刷新 Token": "Token 갱신", + "刷新凭证": "", + "刷新队列": "", "剩 {left} / {total}{unit}": "{total}{unit} 중 {left} 남음", "剩 {n} 分钟": "{n}분 남음", "剩 {remaining}": "잔여 {remaining}", @@ -186,6 +233,8 @@ "协议": "프로토콜", "卡片": "", "又不勾": "체크도 하지 않으면 ", + "取消排序": "", + "取消排序,回到默认次序(不影响已写回的队列顺序)": "", "取消选择": "선택 해제", "另 {n} 个分组全是禁用账号": "", "另有 {n} 个已勾选账号被当前筛选隐藏,仍会参与操作": "현재 필터에서 숨겨진 선택 계정 {n}개도 작업에 포함됩니다", @@ -196,9 +245,13 @@ "可用合计": "사용 가능 합계", "可留空:新版请求头只认令牌;旧版会把用户 ID 放进 New-Api-User 头": "비워 둘 수 있음: 새 버전 헤더는 토큰만 인식하며, 구버전은 사용자 ID를 New-Api-User 헤더에 넣습니다", "可领取的套餐": "받을 수 있는 플랜", + "各厂商组内排序:分组与组序不变;组内先后已写回调度队列": "", "同提供商内暂无其他账号占用优先级": "같은 제공자 내에 우선순위를 점유한 다른 계정이 없습니다", "同提供商已占用:{names}": "같은 제공자에서 사용 중: {names}", "名称": "이름", + "后续加入": "추후 추가", + "否": "", + "含 {n} 个不限额度账号,合计记为 ∞": "무제한 계정 {n}개 포함, 합계는 ∞로 표시", "启用状态": "활성 상태", "启用该规则": "이 규칙 활성화", "启用该账号": "이 계정 활성화", @@ -208,6 +261,7 @@ "周期单位": "주기 단위", "商汤日日新控制台的登录密码": "SenseNova 콘솔 로그인 비밀번호", "商汤日日新控制台的登录账号": "SenseNova 콘솔 로그인 계정", + "固定兜底,不受开关影响": "고정 대체 순서, 스위치의 영향을 받지 않습니다", "固定周期": "고정 주기", "在 NewAPI 站点「个人设置」里生成的访问令牌": "NewAPI 사이트 \"개인 설정\"에서 발급한 액세스 토큰", "在列表里拖:拖分组带调整整组顺位,拖行首把手调整组内先后(顺序由拖拽表达,不再填数字)": "목록에서 드래그: 그룹 바를 드래그해 그룹 전체 순위를, 행 머리 손잡이를 드래그해 그룹 내 순서를 조정합니다 (순서는 드래그로 표현, 숫자 입력은 폐지)", @@ -216,6 +270,8 @@ "基本": "기본", "备注名 / 启用 / 代理": "라벨명 / 활성 / 프록시", "备注名 {name}": "라벨명 {name}", + "备注名不能为空": "라벨명은 비울 수 없습니다", + "备注名已更新": "라벨명을 갱신했습니다", "失败 {count} 个:": "실패 {count}개: ", "套餐 {name}": "플랜 {name}", "套餐明细弹窗未加载,请重启应用后重试": "플랜 상세 창이 로드되지 않았습니다. 앱을 다시 시작한 뒤 시도해 주세요", @@ -225,10 +281,12 @@ "套餐池(Start Plan)— 走 zcode.z.ai 端点,消耗 ZCode 套餐池的额度(新用户体验额度 / 活动领取)": "플랜 풀 (Start Plan) — zcode.z.ai 엔드포인트를 사용하며, ZCode 플랜 풀의 할당량(신규 사용자 체험 할당량 / 이벤트 수령분)을 씁니다", "套餐积分的重置日期": "플랜 포인트 초기화 날짜", "它会改写请求体": "요청 본문을 재작성합니다", + "完整": "", "官方限时活动": "공식 기간 한정 이벤트", "对话 token": "대화 token", "将作用于以下账号": "다음 계정에 적용됩니다", "小时": "시간", + "小浣熊": "小浣熊", "尚未查询该账号的余额": "이 계정의 잔액을 아직 조회하지 않았습니다", "展开全部": "모두 펼치기", "展开全部分组": "모든 그룹 펼치기", @@ -236,16 +294,25 @@ "展开这一组": "이 그룹 펼치기", "已保存的代理": "저장된 프록시", "已停用": "중지됨", + "已删除账号": "", + "已启用 {name}": "{name} 활성화됨", "已启用,点击禁用(不参与转发)": "활성, 클릭하면 비활성화 (포워딩에 참여하지 않음)", "已在全局队列第一位": "이미 전역 대기열 1위입니다", + "已按「{label}」{dir}排序": "", + "已按「{label}」{dir}排序并保存": "", + "已是首选": "이미 최우선입니다", "已更新 {ok}/{total} 个账号,{failed} 个失败": "계정 {ok}/{total}개 갱신, {failed}개 실패", + "已查询": "조회됨", "已用 {used}": "사용 {used}", "已用 {used} / 共 {total}": "사용 {used} / 전체 {total}", "已用完 / 已过期": "소진 / 만료", "已禁用": "비활성", + "已禁用 {name}": "{name} 비활성화됨", "已禁用,点击启用": "비활성, 클릭하면 활성화", + "已筛选 {n} 项": "{n}개 표시 중", "已被「{name}」占用": "「{name}」이(가) 사용 중", "已触发 · 账号已禁用": "발동됨 · 계정 비활성화됨", + "已设为首选": "최우선으로 설정했습니다", "已设置": "설정됨", "已跳过{current}": "건너뜀{current}", "已跳过{usage}{countdown}": "건너뜀{usage}{countdown}", @@ -257,10 +324,10 @@ "已限流": "속도 제한됨", "已领到、还没到生效时间的套餐(生效后余额列会按它显示):\n{lines}": "받았지만 아직 적용되지 않은 플랜(적용되면 잔액 열에 표시됩니다):\n{lines}", "并发上限": "동시 접속 상한", - "并发上限 0~{max};有上游硬顶的家(如 CodeArts)以它的硬顶为准;达到上限的账号会被跳过、请求转给其他账号": "동시 접속 상한 0~{max}; 상류 하드 캡이 있는 제공자(예: CodeArts)는 그 캡이 우선합니다; 상한에 도달한 계정은 건너뛰고 요청은 다른 계정으로 전달됩니다", + "并发上限 0~{max};CodeArts 等有默认值的家填 0 即按本家默认;达到上限的账号会被跳过、请求转给其他账号": "동시 접속 상한 0~{max}; 기본값이 있는 제공자(예: CodeArts)는 0을 입력하면 해당 기본값이 적용됩니다; 상한에 도달한 계정은 건너뛰고 요청은 다른 계정으로 전달됩니다", "并发上限 0~{max};达到上限的账号会被跳过、请求转给其他账号": "동시 접속 상한 0~{max}; 상한에 도달한 계정은 건너뛰고 요청은 다른 계정으로 전달됩니다", "并发上限 0~{max};达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "동시 접속 상한 0~{max}; 상한에 도달한 계정은 건너뛰고 요청은 다른 계정으로 전달됩니다 (기존 목록 ⋯ 메뉴의 「동시 접속 상한」 항목은 여기로 통합되었습니다)", - "并发上限 受上游硬顶约束,达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "동시 접속 상한은 업스트림 하드 리밋의 제약을 받습니다. 상한에 도달한 계정은 건너뛰고 요청은 다른 계정으로 전달됩니다 (기존 목록 ⋯ 메뉴의 「동시 접속 상한」 항목은 여기로 통합되었습니다)", + "并发上限 填 0 即按本家默认 {default};填 1 或 2 就按填的算,更大的数会被本家上限截断(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "0을 입력하면 이 제공자의 기본값({default})이 적용됩니다; 1이나 2는 입력한 값 그대로 적용되고, 더 큰 값은 제공자 상한으로 잘립니다 (기존 목록 ⋯ 메뉴의 「동시 접속 상한」 항목은 여기로 통합되었습니다)", "并发上限必须是 0~{max} 的整数": "동시 접속 상한은 0~{max}의 정수여야 합니다", "并发上限:{value}": "동시 접속 상한: {value}", "开启后,网关按 OpenCode 官方 CLI 的形状发请求:没填 Key 的账号用匿名凭证": "켜면 게이트웨이가 OpenCode 공식 CLI와 같은 형태로 요청을 보냅니다: Key를 입력하지 않은 계정은 익명 자격 증명", @@ -268,10 +335,16 @@ "当前代理不可用:{error}(转发时会回退直连)": "현재 프록시 사용 불가: {error} (포워딩 시 직접 연결로 폴백)", "当前余额 {n};与余额列同一数字口径,严格小于才触发(等于阈值仍可用)": "현재 잔액 {n}; 잔액 열과 같은 수치 기준이며 임계값보다 엄격히 작을 때만 발동합니다 (임계값과 같으면 사용 가능)", "当前可用 ∞(不参与余额不足判定)": "현재 사용 가능 ∞ (잔액 부족 판정에 참여하지 않음)", + "当前已经是默认规则": "이미 기본 규칙입니다", + "当前排序规则:{rules}。点击调整": "현재 정렬 규칙: {rules}. 클릭하여 조정", "当前没有任何模型处于限流中": "현재 속도 제한 중인 모델이 없습니다", "当前没有可领取的套餐(活动期内每天发一份,明天可再领)": "지금 받을 수 있는 플랜이 없습니다(이벤트 기간에는 매일 한 건씩 지급되며, 내일 다시 받을 수 있습니다)", "当前没有限流中的模型。": "현재 속도 제한 중인 모델이 없습니다.", + "当前筛选下 {n} 个账号的可用余额之和(随筛选、搜索变化,不受分页影响)": "", + "当前筛选下没有账号": "현재 필터에 맞는 계정이 없습니다", "当前筛选条件下没有账号": "현재 필터 조건에 맞는 계정이 없습니다", + "当前规则:{rules}。自动排序生效时,组内拖动把手会锁定;关掉全部规则即回到手动顺序。": "현재 규칙: {rules}. 자동 정렬이 켜져 있는 동안 그룹 내 드래그 핸들이 잠깁니다. 모든 규칙을 끄면 수동 순서로 돌아갑니다.", + "当前视图不能调整组内顺序:{reason} 之后再拖动": "", "当前视图不能调整组顺位:{reason} 之后再拖动": "현재 화면에서는 그룹 순서를 변경할 수 없습니다: {reason}로 돌린 뒤 드래그하세요", "当前:{label}(未经过代理池 —— 可在「网络代理」页把出口同步进池后来这里改选)": "현재: {label} (프록시 풀을 거치지 않음 — 「네트워크 프록시」 페이지에서 아웃바운드를 풀에 동기화한 뒤 여기서 다시 선택할 수 있습니다)", "当前:{label}(每条请求换一个出口);「自定义代理…」打开完整设置": "현재: {label}(요청마다 아웃바운드가 바뀝니다); 「사용자 지정 프록시…」에서 전체 설정을 엽니다", @@ -284,13 +357,20 @@ "必填;留空 = 不修改,清除用右边的按钮": "필수. 비워 두면 변경 없음, 지우기는 오른쪽 버튼으로", "总剩余 {total} · 套餐 {plan} · 奖励 {bonus}": "총 잔여 {total} · 플랜 {plan} · 보너스 {bonus}", "总量 {value}": "총량 {value}", + "恢复到「积分到期 → 添加时间」这套默认规则": "기본 규칙 「크레딧 만료 → 추가 시간」으로 복원", "恢复时间未知": "리셋 시간 미정", "我的套餐": "내 플랜", + "所属厂商:{label}": "", "所有分组里都没有启用的账号(禁用不参与转发)": "", + "所有提供商都有这个字段": "모든 제공자에 이 필드가 있습니다", + "所有积分包里**最先作废**的那一份什么时候到期;逐份明细见「余额」格里的积分明细": "", + "所有积分包里最先作废的那一份什么时候到期;没有一次性包时退到每日重置额度、显示今天还剩多久;逐份明细见「余额」格里的积分明细": "", "手动添加": "수동 추가", "打开批量操作弹窗,并预选这一组的账号(改代理等动作在弹窗里挑参数)": "일괄 작업 대화상자를 열고 이 그룹의 계정을 미리 선택합니다 (프록시 변경 등의 매개변수는 대화상자에서 고릅니다)", + "打开筛选": "", "执行中…": "실행 중…", "执行({action})": "실행 ({action})", + "扫码 / 粘贴令牌 / 导入登录态需在桌面端完成": "", "批量动作": "일괄 동작", "批量操作 · 已选 {count} 个账号": "일괄 작업 · 계정 {count}개 선택됨", "批量操作失败:{error}": "일괄 작업 실패: {error}", @@ -303,20 +383,31 @@ "拖动调整列宽(双击还原)": "드래그하여 열 너비 조정 (더블클릭하면 복원)", "拖动调整整组顺序": "드래그로 그룹 전체 순서 조정", "拖动调整组内顺序": "드래그로 그룹 내 순서 조정", + "按优先级从上到下比较,勾选的项才参与": "위에서 아래로 우선순위를 비교하며, 체크한 항목만 참여합니다", "按住拖动,调整这一家的组顺位(整组一起动)": "눌러서 드래그하면 이 제공자의 그룹 순위가 조정됩니다 (그룹 전체가 함께 이동)", "按住拖动,调整这个账号在本组内的顺序": "눌러서 드래그하면 이 계정의 그룹 내 순서가 조정됩니다", "按提供商筛选账号": "제공자별 계정 필터", "按模型": "모델별", "按规则限制该账号参与转发:没有规则(或全部停用)= 不限制;跳过档自动恢复,禁用档需手动启用。": "규칙에 따라 이 계정의 포워딩 참여를 제한합니다: 규칙이 없거나 모두 중지된 경우 = 제한 없음. 건너뛰기 옵션은 자동 리셋되고, 비활성화 옵션은 수동으로 활성화해야 합니다.", + "按账号最早到期、且还有剩余的积分包比较": "잔액이 남아 있고 만료되지 않은 크레딧 팩 중 가장 이른 만료 시각으로 비교합니다", + "排序": "정렬", + "排序粒度": "", "提供商显示名,1~{max} 个字符": "제공자 표시 이름, 1~{max}자", "提供商:{name}": "제공자: {name}", + "提高优先级": "우선순위 올리기", "搜索": "검색", "搜索账号": "계정 검색", + "搜索账号 / 邮箱 / 备注": "계정 / 이메일 / 메모 검색", "操作失败:{message}": "작업 실패: {message}", "收起": "접기", + "改动即时生效": "변경 사항은 즉시 적용됩니다", "改协议 / Base URL 会改变它们的转发方式,正在进行的请求可能失败;模型清单与映射在「模型管理」页。": "프로토콜 / Base URL을 바꾸면 포워딩 방식이 달라져 진행 중인 요청이 실패할 수 있습니다. 모델 목록과 매핑은 「모델 관리」 페이지에 있습니다.", "数值越小越先用;": "숫자가 작을수록 먼저 사용됩니다. ", "数值越小越先用;{hint}": "숫자가 작을수록 먼저 사용됩니다. {hint}", + "整表": "", + "整表排序:厂商分组已散开,行里带厂商标记。此顺序仅用于查看,不写回调度队列 —— 写回会打破「同一家连续」的队列规则": "", + "整表排序:打散厂商分组,全表一起来(行里带厂商标记;仅用于查看,不写回队列)": "", + "新账号落到组首 / 组尾": "새 계정을 맨 위 / 맨 아래에 추가", "旗舰": "플래그십", "无上限 · 已用 {used}": "상한 없음 · 사용 {used}", "无上限按日计(上游到达上限后仍继续计数,超额如实显示)": "상한 없음은 일 단위로 계산됩니다 (업스트림이 상한에 도달해도 계속 집계하며, 초과분은 그대로 표시됩니다)", @@ -329,24 +420,39 @@ "明天 {clock}": "내일 {clock}", "明天 {time}": "내일 {time}", "明细": "상세", + "是": "", "昵称 {name}": "닉네임 {name}", "显示账号名": "계정 이름 표시", "暂无可查询余额的账号": "잔액을 조회할 수 있는 계정이 없습니다", + "暂无数据:先查询一次余额,之后会自动按到期排序": "데이터 없음: 잔액을 한 번 조회하면 이후 만료일 기준으로 자동 정렬됩니다", "暂无账号,请点击右上角「添加账号」": "계정이 없습니다. 오른쪽 위 「계정 추가」를 클릭하세요", "更多操作": "더 많은 작업", + "更多设置": "추가 설정", "更新于 {time}": "{time} 업데이트", + "最先作废的那一份还剩多少(不是面额);按这一份的剩余量排序,缺失值恒排最后": "", + "最先作废的那一份还剩多少(不是面额:这一列回答「马上要作废掉多少」)": "", + "最快到期优先": "만료가 빠른 순", + "最新添加优先": "최근 추가 순", "最早 {soonest} 恢复": "가장 빠른 리셋: {soonest}", "最早恢复": "가장 빠른 리셋 시간", + "最早添加优先": "오래된 추가 순", + "最晚到期优先": "만료가 늦은 순", + "最近到期": "", + "最近到期积分": "", + "最近到期:{name}": "", + "最近到期:{name} · 这一份还剩 {value}(不是面额)": "", + "最近更新": "", "有效期": "유효 기간", "有的账号过了 0 点额度就回来 —— 选「自然日」": "0시가 지나면 할당량이 돌아오는 계정도 있습니다 — 「자연일」을 선택하세요", "未使用": "미사용", - "未分配端口": "미할당 포트", "未命名账号": "이름 없는 계정", "未查询": "미조회", "未检测到 Clash Verge": "Clash Verge를 찾을 수 없습니다", "未检测到 Clash Verge 配置": "Clash Verge 구성을 찾을 수 없습니다", + "未被使用": "사용되지 않음", "未触发{current}": "미발동{current}", "未触发{usage}{countdown}": "미발동{usage}{countdown}", + "未设备注名": "라벨명 없음", "未返回余额数据": "잔액 데이터가 반환되지 않았습니다", "未返回可识别的余额数据": "인식할 수 있는 잔액 데이터가 반환되지 않았습니다", "未配置": "미설정", @@ -356,9 +462,12 @@ "本周期已用": "이번 주기 사용", "本地台账里今天还领过这些,但上面的读数里还没有:{plans}(读数刷新可能有延迟)": "로컬 기록상 오늘 더 받은 항목이 있지만 위의 판독값에는 아직 없습니다: {plans}(판독값 갱신에 시간이 걸릴 수 있습니다)", "本地领取于 {time}": "로컬에서 {time}에 받음", + "本组 {n} 个账号的可用余额之和,按整组统计(不受筛选、搜索影响)": "이 그룹 {n}개 계정의 사용 가능 잔액 합계(그룹 전체 기준, 필터·검색의 영향을 받지 않음)", + "本组代理": "이 그룹의 프록시", "本组操作": "그룹 작업", "权益 {value}": "권익 {value}", "权益 {value} {unit}": "권익 {value} {unit}", + "条件即时生效,关掉面板就能看到结果。": "조건은 즉시 적용됩니다. 패널을 닫으면 결과가 보입니다.", "来源 {source}": "출처 {source}", "查看": "보기", "查看可领取的套餐与名下已有的套餐(每天一期,领取需过一次人机验证)": "받을 수 있는 플랜과 보유 중인 플랜 보기(매일 1회 지급, 받으려면 사람 인증 1회 필요)", @@ -374,12 +483,14 @@ "查询间隔": "조회 간격", "查询间隔必须是 {min} 秒 ~ 24 小时": "조회 간격은 {min}초 ~ 24시간이어야 합니다", "桌面端实时登录态(凭证每次从客户端登录态文件读取)": "데스크톱 실시간 로그인 상태 (자격 증명은 매번 클라이언트 로그인 상태 파일에서 읽음)", + "模型作用域下分组带随队列走,不折叠": "", "正在加载…": "로딩 중…", "正在探测可领取的套餐…": "받을 수 있는 플랜을 확인하는 중…", "正在查询": "조회 중", "正在读取 Clash Verge 配置…": "Clash Verge 구성을 읽는 중…", "正在读取…": "읽는 중…", "正在读取代理列表…": "프록시 목록을 읽는 중…", + "正在读取调度队列…": "", "正在连接上游…": "업스트림에 연결하는 중…", "正常": "정상", "此刻正在使用这个账号的请求数(含还在下发内容的流式请求);为 0 时不显示": "지금 이 계정을 사용 중인 요청 수 (콘텐츠를 계속 내려보내는 스트리밍 요청 포함). 0이면 표시하지 않습니다", @@ -388,10 +499,13 @@ "每日消耗 ≥": "일일 소비 ≥", "每日消耗 ≥ {count} Token(每天 0 点重置)": "일일 소비 ≥ {count} Token (매일 0시 초기화)", "每日重置": "매일 초기화", + "每日重置({time} 回满,不是作废)": "", "每日重置(额度按周期回满,不是作废)": "매일 초기화(할당량은 주기마다 다시 채워지며 소멸되지 않습니다)", "每条请求换一个出口(按顺序轮流),上游看到的 IP 因此是变化的;出口的地址与端口由「网络代理」页管理": "요청마다 다음 아웃바운드로 차례로 바뀌므로 업스트림이 보는 IP가 달라집니다. 아웃바운드의 주소와 포트는 「네트워크 프록시」페이지에서 관리합니다", + "没有匹配「{kw}」的账号": "「{kw}」와 일치하는 계정이 없습니다", "没有可全选的账号": "전체 선택할 계정이 없습니다", "没有可用出口": "사용 가능한 아웃바운드가 없습니다", + "没有将到期的积分(没有一次性额度包,或这一家不给逐份读数)": "", "没有选中的账号": "선택된 계정이 없습니다", "活动套餐": "이벤트 플랜", "活动套餐的推理端点要求每条请求带一个阿里云验证码令牌;令牌由本应用在后台静默铸造,界面关闭时无法铸造": "이벤트 플랜의 추론 엔드포인트는 모든 요청에 알리바바 클라우드 캡차 토큰을 요구합니다. 토큰은 이 앱이 백그라운드에서 조용히 생성하며, 창을 닫으면 생성할 수 없습니다", @@ -400,7 +514,10 @@ "活动期内每天发一份新套餐;同一份当天重复领取会提示「已领取过」,次日可再领。领取要走一次人机验证(滑块)。": "이벤트 기간에는 매일 새 플랜이 한 건씩 지급됩니다. 같은 플랜을 같은 날 다시 받으면 \"이미 받음\"이 표시되며, 다음 날 다시 받을 수 있습니다. 받을 때는 사람 인증(슬라이더)을 한 번 거칩니다.", "测试中…": "테스트 중…", "测试出口": "아웃바운드 테스트", + "添加 新→旧": "추가 최신→오래된", + "添加 旧→新": "추가 오래된→최신", "添加规则": "규칙 추가", + "添加账号请用桌面端或网页版面板": "", "添加限制": "제한 추가", "清掉本机的限流标记,立刻重新尝试该模型(上游若仍在限流会再次被标记)": "이 기기의 속도 제한 플래그를 지우고 해당 모델을 즉시 다시 시도합니다 (업스트림이 여전히 속도 제한 중이면 다시 표시됩니다)", "清掉该账号全部模型的限流标记": "이 계정의 모든 모델에 걸린 속도 제한 플래그를 지웁니다", @@ -413,7 +530,9 @@ "清除已配置的余额查询凭证": "설정된 잔액 조회 자격 증명 지우기", "清除标记": "플래그 지우기", "清除筛选": "필터 지우기", + "点一下按这一列排序(升序 → 降序 → 恢复默认次序)": "", "点击{action}各模型的限流明细": "클릭하여 모델별 속도 제한 상세 {action}", + "点击查看各模型的限流明细": "", "状态": "상태", "状态 {status}": "상태 {status}", "状态未知": "상태 알 수 없음", @@ -429,7 +548,10 @@ "留空 = 不修改,清除用右边的按钮": "비워 두면 변경 없음, 지우기는 오른쪽 버튼으로", "登录 / 导入一个新账号": "로그인 / 새 계정 가져오기", "直连": "직접 연결", + "确定删除账号「{name}」?这个操作不可恢复。": "", "确定删除选中的 {count} 个账号?此操作不可恢复,账号的登录态会一并移除。": "선택한 계정 {count}개를 삭제하시겠습니까? 이 작업은 되돌릴 수 없으며, 계정의 로그인 상태도 함께 제거됩니다.", + "确定清除全部模型的限流标记?": "모든 모델의 레이트 리밋 표시를 지울까요?", + "确定清除模型「{model}」的限流标记?": "모델 \"{model}\"의 레이트 리밋 표시를 지울까요?", "禁用": "비활성화", "禁用该账号(需手动重新启用)": "이 계정 비활성화 (수동으로 다시 활성화해야 함)", "禁用这一组里的全部账号(含被筛选隐藏的)": "이 그룹의 모든 계정을 비활성화합니다 (필터로 숨겨진 것 포함)", @@ -438,6 +560,7 @@ "福利网关未读到:{error}": "혜택 게이트웨이를 읽지 못했습니다: {error}", "秒": "초", "积分": "포인트", + "积分到期时间": "크레딧 만료 시간", "积分包": "포인트 패키지", "积分明细": "포인트 상세", "积分查询已复用转发用的登录凭证,这里通常留空即可。只有旧版本填过、或从旧代理导入过凭证时才有值": "포인트 조회는 포워딩에 쓰는 로그인 자격 증명을 재사용하므로 보통 비워 두면 됩니다. 구버전에서 입력했거나 예전 프록시에서 자격 증명을 가져온 경우에만 값이 있습니다", @@ -446,11 +569,17 @@ "窗口内已用 {used} / {limit} Token,转发时会跳过该账号(重置后自动恢复)": "구간 내 사용 {used} / {limit} Token, 포워딩 시 이 계정을 건너뜁니다 (초기화 후 자동 리셋)", "窗口对齐自然时间(整点 / 整 N 分钟一轮回),重置即清零恢复;按账号在 requests 表聚合": "구간은 자연 시간에 맞춰집니다 (정시 / 정확히 N분 단위로 순환하며), 초기화 시 0으로 돌아가 리셋됩니다. 계정별로 requests 테이블에서 집계합니다", "端口": "포트", + "端口准备中": "포트 준비 중", "第 {rank} 顺位": "전체 {rank}순위", + "策略 {strategy} · 会话粘性 {sticky}": "", + "筛选": "필터", "筛选 / 搜索 / 分页生效时组顺位不可拖动(别家的组整组不可见,落点没法定位)": "필터 / 검색 / 페이지 설정 중에는 그룹 순서를 드래그할 수 없습니다(다른 그룹 전체가 숨겨져 놓을 위치를 정할 수 없음)", "级联删除名下全部账号,不可恢复": "소속된 모든 계정이 함께 삭제되며 되돌릴 수 없습니다", "组内": "", "组内先后:按住左边的把手上下拖动即可调整;整组的先后拖分组带的把手": "그룹 내 순서: 왼쪽 손잡이를 누른 채 위아래로 드래그해 조정하고, 그룹 전체 순서는 그룹 바의 손잡이를 드래그하세요", + "组内并发上限": "그룹 동시 실행 한도", + "组内手动顺序": "그룹 내 수동 순서", + "组内排序:只在每一家内部重排,分组与组序不变(组内先后写回队列)": "", "组内第 {rank} 位": "그룹 내 {rank}번째", "组顺位已锁定": "그룹 순서 잠김", "组顺位:越小越先用(拖动分组带调整)": "그룹 순위: 작을수록 먼저 사용됩니다 (그룹 바를 드래그해 조정)", @@ -459,6 +588,7 @@ "编码套餐(Coding Plan)— 走开放平台端点,用自己订阅的额度": "코딩 플랜 (Coding Plan) — 오픈 플랫폼 엔드포인트를 사용하며, 직접 구독한 할당량을 씁니다", "编辑": "편집", "编辑限制": "제한 편집", + "缺失": "", "网络": "네트워크", "自动查询余额": "잔액 자동 조회", "自动查询余额(按下面的间隔自动刷新余额列的读数)": "잔액 자동 조회 (아래 간격에 따라 잔액 열 수치를 자동 갱신)", @@ -467,11 +597,14 @@ "自定义:": "사용자 지정: ", "自然日(每天 0 点)": "자연일 (매일 0시)", "表格": "", + "被跳过:这一轮不会用到它,所以没有位次": "", "视图": "", "解析失败": "파싱 실패", "触发动作": "발동 동작", "触发后": "발동 후", "触发条件": "발동 조건", + "计入 {counted}/{total}": "합산 {counted}/{total}", + "订阅已移除": "구독 삭제됨", "订阅统计未读到:{error}": "구독 통계를 읽지 못했습니다: {error}", "记录里没有过期时间": "기록에 만료 시간이 없습니다", "设为首选": "최우선으로 설정", @@ -481,6 +614,8 @@ "该上游无需鉴权": "이 업스트림은 인증이 필요하지 않음", "该上游无需鉴权(转发与拉取模型都不发送鉴权头)": "이 업스트림은 인증이 필요하지 않음 (포워딩과 모델 조회 모두 인증 헤더를 보내지 않음)", "该提供商没有余额查询": "이 제공자는 잔액 조회 기능이 없습니다", + "该提供商没有余额查询,因此读不到积分包": "", + "该提供商没有余额查询,因此读不到积分包的到期时刻": "", "该提供商的推理转发尚未接入,账号不参与转发": "이 제공자의 추론 포워딩은 아직 연동되지 않아 계정이 포워딩에 참여하지 않습니다", "该账号出网走的代理(Clash 出口 / 自定义 / 直连);点击可修改": "이 계정의 아웃바운드 프록시 (Clash 아웃바운드 / 사용자 지정 / 직접 연결); 클릭하여 수정", "该账号当前限流中的模型;点徽章看明细": "이 계정에서 현재 속도 제한 중인 모델; 배지를 클릭하면 상세를 볼 수 있습니다", @@ -488,6 +623,7 @@ "该账号没有套餐登录态(jwt),活动套餐会失败:请重新登录该账号": "이 계정에는 플랜 로그인 상태(jwt)가 없어 이벤트 플랜이 실패합니다: 이 계정으로 다시 로그인하세요", "该账号没有福利模型额度(福利按活动下发,不是每个账号都有)": "이 계정에는 혜택 모델 할당량이 없습니다 (혜택은 이벤트로 지급되며 모든 계정에 있는 것은 아닙니다)", "该账号没有积分类读数(额度只有福利 token 池)": "이 계정에는 포인트 계열 수치가 없습니다 (할당량은 혜택 token 풀뿐입니다)", + "该账号的登录态已被上游注销,自动刷新不会再成功,请重新登录该账号": "", "该账号的额度只有福利 token 池(见余额格那两行),订阅统计没有积分类计量表": "이 계정의 할당량은 혜택 token 풀뿐이며 (잔액 칸의 두 줄 참고), 구독 통계에는 포인트 계열 측정표가 없습니다", "请先勾选要操作的账号": "먼저 작업할 계정을 선택하세요", "请先选择 Clash Verge 出口": "먼저 Clash Verge 아웃바운드를 선택하세요", @@ -499,24 +635,30 @@ "请填写重置周期": "초기화 주기를 입력하세요", "请选择代理": "프록시를 선택하세요", "请选择出口": "아웃바운드를 선택하세요", - "读取中…": "읽는 중…", + "读取中…": "불러오는 중…", "读取失败:{error}": "읽기 실패: {error}", "读取自 {dir};端口由 Clash Verge 管理,这里实时同步": "{dir}에서 읽음; 포트는 Clash Verge가 관리하며 여기서 실시간 동기화됩니다", + "读取调度队列失败:{error}": "", "读数来自自动余额查询;与余额列同一数字口径,严格小于才触发(等于阈值仍可用)": "수치는 자동 잔액 조회에서 오며 잔액 열과 같은 수치 기준입니다. 임계값보다 엄격히 작을 때만 발동합니다 (임계값과 같으면 사용 가능)", "读数里没有套餐清单(免费账号、或套餐的上游状态还没刷新;可点右上角「查询余额」重读)": "이 판독값에 플랜 목록이 없습니다(무료 계정이거나 업스트림 플랜 상태가 아직 갱신되지 않음). 오른쪽 위 \"잔액 조회\"로 다시 읽을 수 있습니다", "读数里没有额度桶,这里按套餐权益里的已用 / 总量显示(上游给的数)": "판독값에 할당량 버킷이 없어 플랜 권익의 사용량 / 총량(업스트림 수치)을 표시합니다", + "调度队列:{n} 个可用 · 跳过 {m} 个": "", + "调整到全局队列第一位": "전역 큐의 맨 앞으로 이동합니다", "调整顺序失败:{message}": "순서 조정 실패: {message}", "账号": "계정", + "账号 ID": "", "账号不存在,请刷新后重试": "계정이 존재하지 않습니다. 새로 고침 후 다시 시도하세요", "账号不存在,请刷新后重试。": "계정이 존재하지 않습니다. 새로 고침 후 다시 시도하세요.", "账号名 / 邮箱 / 备注": "계정 이름 / 이메일 / 메모", "账号已保存,但提供商未更新:{error}": "계정은 저장되었지만 제공자가 업데이트되지 않았습니다: {error}", "账号显示名称": "계정 표시 이름", + "账号添加时间": "계정 추가 시간", "账号表": "계정 표", "账号视图": "", "账号设置": "계정 설정", "账号设置 · {name}": "계정 설정 · {name}", "账号设置分组": "계정 설정 그룹", + "账号详情": "", "账号邮箱": "계정 이메일", "跳过该账号(余额回升自动恢复)": "이 계정 건너뛰기 (잔액 회복 시 자동 리셋)", "跳过该账号(自动恢复)": "이 계정 건너뛰기 (자동 리셋)", @@ -529,13 +671,24 @@ "轮询的代理": "순환할 프록시", "轮询至少要选择一条代理(或改选「全部」)": "순환하려면 프록시를 하나 이상 선택하세요(「전체」로 되돌릴 수도 있습니다)", "轮询(换 IP)": "순환 (IP 전환)", + "还有 {d} 天": "", + "还有 {h} 小时": "", + "还有 {h} 小时 {m} 分": "", + "还有 {n} 分钟": "", "还没有代理(去「网络代理」页添加)": "아직 프록시가 없습니다 (「네트워크 프록시」 페이지에서 추가하세요)", "还没有余额读数(已自动查询一次,稍候;也可以点右上角「查询余额」重读)": "아직 잔액 판독값이 없습니다(자동으로 한 번 조회했습니다. 잠시 기다리거나 오른쪽 위 \"잔액 조회\"를 누르세요)", "还没有保存的代理": "저장된 프록시가 없습니다", + "还没有账号,去桌面端添加": "계정이 없습니다. 데스크톱에서 추가하세요.", "还没有限制规则:账号照常参与转发,余额与 Token 消耗都不设上限。": "아직 제한 규칙이 없습니다: 계정이 평소처럼 포워딩에 참여하며, 잔액과 Token 소비 모두 상한을 두지 않습니다.", "这一份今天已经领过了(重复领取上游会回「已领取过」)": "이 플랜은 오늘 이미 받았습니다(다시 받으면 업스트림이 \"이미 받음\"을 반환합니다)", + "这一份还剩多少:{name} · 上游没给剩余量": "", + "这一家在这个模型上的倍率(与模型管理页同一份数据)": "", + "这一家查不到积分到期时间": "이 제공자는 크레딧 만료 시간을 읽을 수 없습니다", "这一栏改的是": "여기서 변경하는 대상은 ", "这一段按提供商出现:有什么配什么。": "이 섹션은 제공자에 따라 표시되며, 설정한 항목만 나옵니다.", + "这一组开着排序规则:关掉规则后才能手动拖": "이 그룹은 정렬 규칙이 켜져 있습니다. 수동으로 끌려면 규칙을 꺼야 합니다", + "这个模型的队列里没有账号": "", + "这个模型队列里第 {rank} 位": "", "这个账号的余额走窗口限额通道(只配了编码套餐 API Key):只有每 N 小时的剩余比例,读不到套餐清单": "이 계정의 잔액은 창 제한 채널로 읽습니다(코딩 플랜 API Key만 설정됨). N시간마다의 남은 비율만 있고 플랜 목록은 없습니다", "这些分组里一个启用的账号都没有(禁用不参与转发),默认不显示;点一下切到「禁用」查看,也就能重新启用它们": "", "这会{what}所有启用中的账号,转发将不可用。确定继续?": "활성 상태인 모든 계정을 {what}합니다. 포워딩을 사용할 수 없게 됩니다. 계속하시겠습니까?", @@ -547,32 +700,46 @@ "连接数": "연결 수", "选择": "선택", "选择要轮询的代理(可多选)": "순환할 프록시 선택(복수 선택 가능)", + "逐份明细见「余额」格的积分明细": "", "逐请求轮换出口(换 IP):OpenCode Zen 免费档按 IP 限流,单个 IP 的额度用完会对它一律 429": "요청마다 아웃바운드를 바꿉니다(IP 전환): OpenCode Zen 무료 등급은 IP 기준으로 제한되며, 한 IP의 할당량을 다 쓰면 그 IP의 요청은 모두 429가 됩니다", + "邮箱": "", "邮箱 {email}": "이메일 {email}", + "重新算一次这个模型的调度队列(限流冷却 / 并发释放后队列会变)": "", "重新读取": "다시 읽기", "重新读取该账号的余额与套餐(走余额查询那条链)": "이 계정의 잔액과 플랜을 다시 읽기(잔액 조회 경로 사용)", + "重置": "초기화", "重置 {date}": "{date} 초기화", "重置也不恢复,需手动启用": "초기화해도 리셋되지 않으며 수동으로 활성화해야 합니다", "重置周期必须是 30 分钟 ~ 24 小时": "초기화 주기는 30분 ~ 24시간이어야 합니다", "重置方式": "초기화 방식", + "长期有效": "장기 유효", "间隔单位": "간격 단위", "阈值": "임계값", + "降低优先级": "우선순위 내리기", "限制器": "리미터", "限制器:{window}已用 {used} / {threshold} Token": "리미터: {window} 사용 {used} / {threshold} Token", "限制类型": "제한 유형", "限制阈值必须是大于 0 的数字": "제한 임계값은 0보다 큰 숫자여야 합니다", "限流": "속도 제한", "限流中": "속도 제한 중", + "限流已清除": "레이트 리밋을 지웠습니다", + "限流明细": "", + "限流模型": "", "限额": "한도", "限额状态": "한도 상태", "隐藏账号名(名字显示为星号)": "계정 이름 숨기기 (이름이 별표로 표시됨)", + "需要重新登录": "재로그인 필요", + "需重登": "재로그인", "顺序": "순서", + "顺序已锁定": "", "顺序未保存:{message}": "순서가 저장되지 않았습니다: {message}", "领取 {time}": "수령 {time}", "领套餐": "플랜 받기", "额度": "할당량", "额度窗口读数来自开放平台监控通道(套餐 JWT 那条通道没有额度桶):读的是每 N 小时 / 每周窗口的剩余比例": "할당량 창 판독값은 오픈 플랫폼 모니터링 채널에서 가져옵니다(코딩 플랜 JWT 채널에는 버킷이 없습니다). N시간마다 / 주간 창의 남은 비율을 읽습니다", "验证码令牌:{counts}": "캡차 토큰: {counts}", + "默认不排序:组内顺序保持你拖出来的样子。需要自动排序时,勾选上面的规则即可。": "기본적으로 정렬하지 않습니다: 그룹 내 순서는 끌어 놓은 그대로 유지됩니다. 자동 정렬이 필요하면 위의 규칙을 체크하세요.", + "默认展开 / 收起": "기본 펼침 / 접힘", "({n} 个未配置查询凭证)": " ({n}개는 조회 자격 증명 미설정)", "({plans})": " ({plans})", "(不可用)": " (사용 불가)", @@ -587,13 +754,5 @@ ",只对 OpenCode Zen 这类上游有意义,别的家请关掉。": ", OpenCode Zen 같은 업스트림에만 유용하므로 다른 제공자에서는 꺼 두세요.", ",已达到上限": ", 상한에 도달했습니다", ":到「网络代理」页的「订阅管理」里点「同步 Clash Verge」把出口导进池后即可选 —— 那样能集中测试、也能被多个账号共用。": " 사용입니다 — 「네트워크 프록시」 페이지의 「구독 관리」에서 「Clash Verge 동기화」를 눌러 아웃바운드를 풀에 가져오면 선택할 수 있으며, 한곳에서 테스트하고 여러 계정이 공유할 수 있습니다.", - ";": "; ", - "WorkBuddy 国内版": "WorkBuddy 중국판", - "WorkBuddy 国际版": "WorkBuddy 국제판", - "小浣熊": "小浣熊", - "Qoder 中国版": "Qoder 중국판", - "Qoder 国际版": "Qoder 국제판", - "Accio 国内版": "Accio 중국판", - "ZCode 国内版": "ZCode 중국판", - "ZCode 国际版": "ZCode 국제판" + ";": "; " } diff --git a/desktop-tauri/i18n/i18n-src/ko/add-account.json b/desktop-tauri/i18n/i18n-src/ko/add-account.json index ceff43a1..42ada70d 100644 --- a/desktop-tauri/i18n/i18n-src/ko/add-account.json +++ b/desktop-tauri/i18n/i18n-src/ko/add-account.json @@ -10,6 +10,7 @@ "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "Anthropic 호환 엔드포인트는 https://api.deepseek.com/anthropic입니다 (프로토콜을 anthropic으로 바꿀 때 입력합니다)", "Anthropic 协议填根地址,不要带 /v1": "Anthropic 프로토콜은 루트 주소를 입력하고 /v1은 붙이지 마세요", "Antigravity": "Antigravity", + "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw 중국판", "AutoClaw 国际版": "AutoClaw 국제판", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS는 필수입니다; **전체 Cookie**를 붙여넣으세요 (Cookie 편집기에서 원클릭 내보내기 가능) — 이 중 **PTOKEN은 반드시 남겨 두세요**. 게이트웨이가 이것을 사용해 Baidu에서 KukuAI 비즈니스 세션 토큰을 교환 발급받으며, 없으면 모델 갱신 시 「미로그인」 오류가 발생합니다. 전체 Cookie 헤더, Cookie 편집기에서 내보낸 JSON 배열, 또는 {BDUSS, STOKEN} 객체를 지원합니다. KukuAI에는 갱신 메커니즘이 없으므로 로그인 상태가 만료되면 다시 붙여넣으면 됩니다.", @@ -18,19 +19,35 @@ "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Cline Pass의 API Key는 app.cline.bot에서 발급받습니다(구독제). 엔드포인트는 OpenAI 호환이며, 모델 목록은 Cline 콘솔을 기준으로 하고, 모델 이름은 모델 관리에서 접두사 없는 이름 그대로 추가합니다(예: glm-5.2 / kimi-k2.7-code / deepseek-v4-pro). 업스트림이 클라이언트 형태를 검증하므로 필요한 클라이언트 요청 헤더는 이 카드에 미리 설정되어 있습니다", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT (3단 점 구분)", "Command Code": "Command Code", + "Coze 扣子": "", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT(점 3개 구분. Bearer 전체 줄 또는 data.bin 내용을 붙여넣어도 됨)", + "JWT(三段点分;也可直接粘贴 Bearer 整行)": "JWT(점 3개로 구분; Bearer 전체 줄을 그대로 붙여넣어도 됨)", + "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT(점 3개로 구분된 형식. chatglm_token=… 줄을 그대로 붙여도 됩니다)", + "LobsterAI": "LobsterAI", + "MiniMax Code": "MiniMax Code", + "MiniMax Code 国内版": "MiniMax Code (중국판)", + "MiniMax Code 国际版": "MiniMax Code (국제판)", + "ModelScope 国内版": "ModelScope 중국판", + "ModelScope 国际版": "ModelScope 국제판", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode 국제판", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM 관리형 엔드포인트, Key는 integrate.api.nvidia.com에서 신청합니다", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken은 Accio의 로그인 자격 증명입니다 (길고 불투명한 token 문자열이며 JWT가 아닙니다); refreshToken은 선택이며, 입력하면 만료 시 자동 갱신됩니다. {label} 계정의 자격 증명을 입력하세요 — {siteNote} (가장 쉽게 얻는 방법: 위의 「웹 로그인」을 바로 사용하세요. token을 직접 찾을 필요가 없습니다.)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken은 Trae의 Cloud-IDE-JWT이며(3단 점 구분), refreshToken은 만료 시 자동 갱신에 사용됩니다 (이 제공자는 refreshToken이 교환할 때마다 바뀝니다: 한 번 교환하면 이전 것은 무효가 되므로 두 프로그램에서 같은 계정을 동시에 갱신하지 마세요).
수동으로 붙여넣을 때는 machineId / deviceId도 함께 입력하세요: 업스트림은 이를 로그인 시 업로드한 기기 공개 키와 묶어 기기를 판별하므로, 갑자기 새로운 값을 넣으면 2xxxx 계열 기기 바인딩 거부가 발생합니다.
이 세 가지가 없을 때의 올바른 방법은 위의 「웹 로그인」이며, 컨테이너 / 원격 배포 환경에서는 안내에 따라 주소 표시줄의 콜백 주소를 붙여넣으면 됩니다.", + "mmoat_… 或客户端 auth.json 里的 accessToken 值": "", + "mmoat_…(国际站 account.minimax.io 签发)": "", + "mmort_…(可选;有它才能自动续期)": "", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "monkeycode_ai_session의 값 (전체 Cookie를 붙여넣어도 됨)", + "ms- 开头的 API 令牌": "ms- 로 시작하는 API 토큰", + "openclaw.json 里 gateway.auth.token 的值": "", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken은 선택이며, 입력하면 만료 시 자동 갱신됩니다; 둘 다 小浣熊 클라이언트 로그인 상태 파일에서 얻을 수 있습니다.", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken은 선택이며, 입력하면 자동 갱신됩니다; 둘 다 클라이언트 로그인 상태 파일에서 얻을 수 있습니다. 같은 Cline 계정은 두 풀 모두에서 사용할 수 있으며, 여기서는 {label}로 분류됩니다.", + "sessionid_ss=…; sid_guard=…; ttwid=…(或 Cookie 编辑器导出的 JSON)": "", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "token은 enc: 접두사를 지원합니다 (Windows에서는 자동 복호화). refreshToken을 입력하지 않으면 자동 갱신할 수 없습니다.", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "token은 로그인 상태 Cookie입니다; CatPaw에는 갱신 메커니즘이 없어 만료 후 클라이언트에서 다시 로그인해야 합니다.", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "user_로 시작하는 API Key (Authorization: Bearer … 줄 전체를 붙여넣어도 됨)", + "{\"access_token\":\"mv_…\",\"openid\":\"oAN1…\",\"guid\":\"a79a…\"}(整段粘贴)": "", "{label} 网页登录的打开方式": "{label} 웹 로그인을 여는 방식", "{label} 账号地区": "{label} 계정 지역", "{label} 账号的添加方式": "{label} 계정 추가 방식", @@ -39,6 +56,7 @@ "{n} 个账号": "계정 {n}개", "{n} 个账号,点击添加": "계정 {n}개, 클릭하여 추가", "{seconds}s 后重发": "{seconds}초 후 재발송", + "⚠️ {label}账号已添加,但探活未通过:{message}": "", "✅ {label}账号已添加": "✅ {label} 계정을 추가했습니다", "✅ {label}账号已添加{name}": "✅ {label} 계정을 추가했습니다{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ cc-switch에서 제공자 {n}개를 가져왔습니다", @@ -58,23 +76,30 @@ "不是有效的凭证文件:需要包含 accessToken 与 uid": "유효한 자격 증명 파일이 아닙니다. accessToken과 uid가 필요합니다", "不是自定义提供商": "커스텀 제공자가 아닙니다", "不需要 Key:转发与拉取模型都不发送鉴权头": "Key 불필요: 포워딩과 모델 조회 모두 인증 헤더를 보내지 않습니다", + "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "로그인이 필요 없습니다. 아래 버튼을 누르면 게이트웨이가 智谱清言에서 게스트 자격 증명을 직접 발급받아 계정 목록에 추가합니다(먼저 메모 이름을 입력해도 됩니다). 게스트 자격 증명은 바로 대화에 쓸 수 있고, 무료 할당량은 업스트림이 기기 단위로 발급합니다.

주의: 게스트 계정에는 포인트 체계가 없습니다. 회원 포인트, 매일 로그인 포인트, 활동 수령 관련 인터페이스는 모두 로그인이 필요하므로 이러한 기능은 게스트에서 건너뜁니다(계정 페이지의 포인트와 签到 센터에 수치가 표시되지 않습니다). 포인트가 필요하면 \"자격 증명 입력\"으로 로그인된 계정 토큰을 붙여넣거나 \"데스크톱 로그인 상태 가져오기\"를 사용하세요.", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "두 판의 계정을 동시에 저장할 수 있으며 각각 별도의 모델 목록을 가집니다 (중국판과 국제판은 서로 다른 두 제공자로, 각각 활성화하고 매핑할 수 있습니다).", "中国版": "중국판", "今天没有需要领的活动(都已到账)": "오늘 수령할 이벤트가 없습니다 (모두 적립됨)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
": "오늘(베이징 시간 {day}) {attempts}회 시도했고, {left}회 더 시도할 수 있습니다{next}
", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "다른 컴퓨터(또는 OfficeAce 데스크톱 앱의 models.json)에서 다음 세 항목을 가져와 붙여넣으세요: 모델 게이트웨이 기본 주소(baseUrl, https://modelgw-…/v2 형태)와 modelAppKey / modelAppSecret. 이 세 항목만 있으면 전달할 수 있습니다(게이트웨이 자격 증명은 만료되지 않음). 제어 영역 임시 자격 증명(accessKeyId 등)은 선택이며, 비워 두면 잔액 조회와 자동 갱신을 쓸 수 없지만 전달에는 영향이 없습니다.", + "从本机 MiniMax Code 客户端读取国际版登录态(只读,不会改动客户端)": "", + "从本机 MiniMax Code 客户端读取登录态(只读,不会改动客户端)": "", "从本机导入桌面端登录态": "이 컴퓨터에서 데스크톱 로그인 상태 가져오기", + "从本机马维斯客户端读取登录凭证(需要客户端已登录;会弹一次管理员授权)": "", + "令牌当前不可用": "", "任务镜像 id": "작업 이미지 id", "余额查询": "잔액 조회", "使用 Google 账号登录": "Google 계정으로 로그인", "使用 Zai 账号登录": "Zai 계정으로 로그인", "使用个人访问令牌(PAT)": "개인 액세스 토큰(PAT) 사용", + "先用管理员身份运行 _recon/marvis-cred-extract.py(从已登录的马维斯客户端内存里提取),然后把输出的 JSON 整段粘贴进来。需要三项:accessToken(mv_ 开头)、openid、guid。令牌没有续期接口,过期后重新提取一次即可(会更新同一条账号记录)。": "", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "무료 모델은 Key를 입력하지 않아도 됩니다 (기본적으로 「이 업스트림은 인증이 필요하지 않음」이 체크되어 있고, 게이트웨이가 자동으로 익명 자격 증명을 사용합니다); 유료 모델은 Zen의 API Key를 입력하세요. 무료 등급 업스트림은 공식 클라이언트 형태만 허용하므로 「OpenCode 공식 클라이언트로 위장」이 기본으로 켜져 있습니다", "内嵌窗口": "내장 창", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "내장 창으로 엽니다; 창을 닫으면 대기가 취소됩니다 (Google 계정이 거부되면 시스템 브라우저를 사용하세요)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "내장 창으로 엽니다; 완료하면 자동으로 목록에 추가되고, 창을 닫으면 대기가 취소됩니다", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "내장 창으로 열립니다. 스캔이 끝나면 자동으로 목록에 추가되며, 창을 닫으면 대기가 취소됩니다", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "내장 창은 새 환경을 사용하므로 여러 계정이 서로 영향을 주지 않습니다; 창을 닫으면 대기가 취소됩니다", + "内嵌窗口(必须)": "", "内嵌窗口(推荐)": "내장 창(권장)", "准备验证…": "인증 준비 중…", "凭据 JSON": "자격 증명 JSON", @@ -91,29 +116,38 @@ "取消失败:{message}": "취소 실패: {message}", "取消等待": "대기 취소", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "「내장 창」에서만 제3자 로그인 항목을 복원할 수 있습니다: 시스템 브라우저에서는 로그인 페이지를 수정할 수 없습니다", + "只有在「导入本机登录态」不可用时才需要:以管理员身份运行 _recon/marvis-cred-extract.py,把生成的 JSON 整段粘贴进来。需要三项:accessToken(mv_ 开头)、openid、guid。令牌没有续期接口,过期后重新导入一次即可(会更新同一条账号记录)。": "", "可选": "선택", "可选(cloudaicompanionProject):留空由网关自动发现": "선택 (cloudaicompanionProject): 비워 두면 게이트웨이가 자동으로 찾습니다", + "可选,仅当令牌里读不出用户 id 时需要": "선택. 토큰에서 사용자 id를 읽을 수 없는 경우에만 필요", "可选,展示用": "선택, 표시용", "可选,没有则无法自动续期": "선택, 없으면 자동 갱신 불가", + "可选,用于生成账号标识": "선택 사항이며 계정 식별자 생성에 사용", "可选,留空使用凭据里的 user_name": "선택, 비워 두면 자격 증명의 user_name 사용", "可选,留空使用账号姓名、邮箱或令牌指纹": "선택, 비워 두면 계정 이름, 이메일 또는 토큰 지문 사용", "可选,留空使用账号昵称或邮箱": "선택, 비워 두면 계정 닉네임 또는 이메일 사용", "可选,留空则用 key 尾号": "선택, 비워 두면 key 끝자리 사용", "可选,留空则用 userId": "선택, 비워 두면 userId 사용", + "可选,留空则用上游账号昵称": "선택 사항, 비워 두면 업스트림 계정 닉네임 사용", "可选,留空则用上游返回的账号名或账号 id": "선택, 비워 두면 업스트림이 반환한 계정 이름 또는 계정 id 사용", + "可选,留空则用令牌尾号": "", "可选,留空则用凭证里的账号名": "선택, 비워 두면 자격 증명의 계정 이름 사용", "可选,留空则用文件里的昵称": "선택 사항, 비워 두면 파일의 닉네임 사용", "可选,留空则用登录名或 uid": "선택, 비워 두면 로그인 이름 또는 uid 사용", "可选,留空则用脱敏手机号": "선택, 비워 두면 마스킹된 휴대폰 번호 사용", "可选,留空则用脱敏手机号或 userId": "선택, 비워 두면 마스킹된 휴대폰 번호 또는 userId 사용", "可选,留空则用账号 uk": "선택, 비워 두면 계정 uk 사용", + "可选,留空则用账号昵称": "선택 사항. 비워 두면 계정 닉네임을 사용합니다", "可选,留空则用账号昵称或 userId": "선택 사항. 비워 두면 계정 닉네임 또는 userId 사용", "可选,留空则用邮箱或令牌尾号": "선택, 비워 두면 이메일 또는 토큰 끝자리 사용", + "可选,留空用「AtomCode」": "선택 사항이며 비워 두면 “AtomCode”", "可选,留空用「OfficeAce 果办」": "선택, 비워 두면 「OfficeAce」 사용", "可选,留空自动生成": "선택, 비워 두면 자동 생성", "可选,留空自动用昵称": "선택, 비워 두면 닉네임 자동 사용", + "可选,给这个访客账号起个备注名": "선택 사항. 이 게스트 계정의 메모 이름", "可选,续期时带上": "선택, 갱신 시 함께 전송", "可选,讯飞侧 userid": "선택, iFlytek 측 userid", + "可选,默认 9434": "", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "선택; 대화 시작에 필요합니다. 비워 두면 게이트웨이가 이 계정의 기존 작업에서 자동으로 찾습니다. 찾지 못하면 나중에 직접 입력하면 됩니다.", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "선택; 계정 이메일로, 표시와 신원 식별에 사용됩니다 (새 토큰으로 바꿔도 같은 계정으로 인식됩니다)", "可选;UUID 形态,与凭据同生共死": "선택; UUID 형식이며 자격 증명과 함께 생성되고 함께 사라집니다", @@ -124,6 +158,7 @@ "可领取": "수령 가능", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "한 제공사에 API Key 있는 계정과 인증 불필요 계정을 섞어 둘 수 있으며 각각의 규칙으로 라우팅됩니다. 인증 불필요 계정도 여러 개 둘 수 있고 각각 다른 아웃바운드 프록시를 설정할 수 있습니다", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "같은 주소에서 anthropic 프로토콜로 바꾸면 해당 Claude 호환 엔드포인트를 사용합니다. 포워딩에는 OpenCode Go의 API Key가 반드시 필요합니다 (구독제; 모델 목록 조회에는 Key가 필요 없지만, 요청을 보내면 업스트림이 401 「Missing API key」를 반환합니다)", + "后端没有返回授权地址(登录发起失败)": "", "响应为空": "응답이 비어 있습니다", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "SenseNova 의 OpenAI 호환 엔드포인트. 잔액 조회는 콘솔 계정 / 비밀번호를 사용합니다 (아래 \"잔액 조회\" 단락 참조)", "国内版": "중국판", @@ -141,11 +176,16 @@ "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "국제판의 추론 사이트는 api.z.ai이며 중국판과 다른 사이트입니다; 플랜도 각각 독립적입니다.", "国际版(WorkBuddy AI)": "국제판 (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "국제 사이트 Coding 플랜; OpenAI 호환 주소는 https://api.minimax.io/v1입니다", + "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "AtomGit의 AtomCode 페이지에서 accessToken과 refreshToken 두 값을 가져와 붙여넣으세요. 리프레시 토큰 입력을 권장합니다 —— 이 제공자의 토큰은 JWT가 아니며(약 7일 후 만료되고 만료 시각을 읽을 수 없습니다), 없으면 자동 갱신이 불가능해 만료 후 다시 로그인해야 합니다.", "在 cc-switch 里找到": "cc-switch에서 찾은", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "내장 창에서 공식 로그인을 완료하면 성공 시 자동으로 계정 목록에 추가됩니다.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "{label}의 Qoder 계정 설정 → Integrations에서 PAT를 생성하세요 (Google / GitHub 토큰을 입력하지 마세요). 두 지역의 계정과 자격 증명은 호환되지 않습니다.", - "在打开的官方 Web 控制台里用微信扫码登录(窗口会自动弹出登录面板;若没有,点左下角头像 → 立即登录)。登录成功后自动加入账号列表。": "열린 공식 웹 콘솔에서 WeChat QR 코드를 스캔해 로그인하세요(로그인 패널이 자동으로 열립니다. 열리지 않으면 왼쪽 아래 아바타를 클릭한 뒤 \"로그인\"을 선택하세요). 로그인이 끝나면 계정 목록에 자동으로 추가됩니다.", + "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "열린 공식 인증 페이지에서 위챗 QR 코드를 스캔하여 로그인하세요(페이지에 QR 코드가 바로 표시됩니다). 스캔에 성공하면 계정이 자동으로 목록에 추가됩니다.", + "在打开的官方登录页里完成登录,成功后自动加入账号列表。": "열린 공식 로그인 페이지에서 로그인하면 성공 후 자동으로 계정 목록에 추가됩니다.", + "在打开的登录页里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "열린 로그인 페이지에서 Baidu 계정으로 KukuAI 본사이트에 로그인하세요(휴대폰 인증번호 / QR 스캔 모두 가능). 성공하면 자동으로 계정 목록에 추가됩니다. 게이트웨이가 Baidu와 KukuAI 업무 세션 토큰을 교환합니다(이 PC에 KukuAI 클라이언트가 설치되어 있어야 함).", + "在打开的登录页里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "열린 로그인 페이지(Antigravity 인증 페이지)에서 Google 계정으로 로그인하면 인증 완료 후 자동으로 계정 목록에 추가됩니다.", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "열린 창에서 Baidu 계정으로 KukuAI 메인 사이트에 로그인하세요 (SMS 인증 코드 / QR 스캔 모두 가능). 로그인에 성공하면 자동으로 계정 목록에 추가됩니다. 게이트웨이는 Baidu에서 KukuAI 비즈니스 세션 토큰을 교환 발급받습니다 (이 컴퓨터에 KukuAI 클라이언트가 설치되어 있어야 합니다).", + "在打开的窗口里登录 Coze(抖音账号 / 微信扫码 / 手机号均可),登录成功后自动加入账号列表。请用内嵌窗口:系统浏览器里网关读不到登录窗口的 Cookie。": "", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "열린 창에서 Google 계정으로 로그인하세요(Antigravity의 인가 페이지). 인가가 완료되면 자동으로 계정 목록에 추가됩니다.", "在浏览器中打开登录页": "브라우저에서 로그인 페이지 열기", "地区": "지역", @@ -165,6 +205,8 @@ "导入所选": "선택 항목 가져오기", "导入所选({n})": "선택 항목 가져오기({n})", "导入桌面端登录态": "데스크톱 로그인 상태 가져오기", + "将在新标签页中打开授权页;完成后自动加入账号列表。": "새 탭에서 인증 페이지를 엽니다. 완료하면 자동으로 계정 목록에 추가됩니다.", + "将在新标签页中打开授权页;完成后自动加入账号列表。若页面跳回本机地址后账号没有出现,按提示把地址栏里的完整回调地址粘贴回来即可。": "새 탭에서 인증 페이지를 엽니다. 완료하면 자동으로 계정 목록에 추가됩니다. 로컬 주소로 돌아온 뒤에도 계정이 나타나지 않으면 안내에 따라 주소창의 전체 콜백 URL을 붙여넣으세요.", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "내장 창이 열립니다; 인가가 완료되면 자동으로 계정 목록에 추가됩니다. 창을 닫으면 대기가 취소됩니다. 인가 페이지 마지막에 zcode:// 링크를 열 수 없다는 안내가 나올 수 있는데, 정상입니다 — 결과는 이미 서버 측에 기록되었습니다", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "내장 창이 열립니다; 인가가 완료되면 자동으로 계정 목록에 추가됩니다. 원격 패널에서는 콜백 주소를 붙여넣을 수 있습니다. 창을 닫으면 대기가 취소됩니다. 링크는 5분간 유효하며, 시간이 초과되거나 클릭하지 않으면 무효가 됩니다 (다시 시작할 수 있습니다)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "내장 창이 열립니다; 로그인이 완료되면 자동으로 계정 목록에 추가됩니다. 창을 닫으면 대기가 취소됩니다", @@ -173,6 +215,7 @@ "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 Huawei Cloud 계정을 재사용합니다); 인가 코드는 게이트웨이가 클라우드를 폴링해 가져오므로 브라우저가 어느 컴퓨터에서 실행되는지는 상관없습니다. 완료하면 자동으로 계정 목록에 추가됩니다", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 Huawei Cloud 계정을 재사용합니다); 브라우저와 게이트웨이가 같은 컴퓨터에 있지 않다면 위 설명대로 최종 콜백 주소를 패널에 다시 붙여넣으세요", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "시스템 기본 브라우저로 로그인 페이지를 엽니다 (브라우저에 로그인된 Accio 계정을 재사용합니다); 로그인을 완료하면 자동으로 계정 목록에 추가되며, 팝업을 닫으면 대기가 취소됩니다", + "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "시스템 기본 브라우저로 로그인 페이지를 엽니다 (브라우저에 로그인된 LobsterAI 계정을 재사용합니다); 로그인을 완료하면 자동으로 계정 목록에 추가되며, 팝업을 닫으면 대기가 취소됩니다", "小米 MiMo Token Plan 套餐(新加坡站)": "小米 MiMo Token Plan 플랜 (싱가포르 사이트)", "已到账": "적립 완료", "已取消登录等待": "로그인 대기를 취소했습니다", @@ -187,24 +230,32 @@ "已点发送,等待短信…": "전송을 눌렀습니다. SMS 대기 중…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "시스템 기본 브라우저로 로그인 페이지를 열었습니다. 브라우저에서 로그인을 완료하세요…", "已领取,等官方确认": "수령 완료, 공식 확인 대기 중", + "开始登录(获取设备码)": "", + "当前壳不支持直接打开浏览器,请手动访问:{url}": "", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "현재 앱 버전에서는 AutoClaw 웹 로그인을 지원하지 않습니다. 앱을 업데이트하세요", "当前没有可领取的套餐(活动尚未开始)": "현재 수령 가능한 플랜이 없습니다 (이벤트가 아직 시작되지 않았습니다)", "当前环境不支持领取(桥接方法缺失)": "현재 환경에서는 수령을 지원하지 않습니다 (브리지 메서드 없음)", "必填,CatPaw 账号标识": "필수, CatPaw 계정 식별자", "恢复 Google / GitHub 入口": "Google / GitHub 항목 복원", + "手工粘贴凭证": "", "手机号": "휴대폰 번호", "手机验证码登录": "SMS 인증 코드 로그인", + "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "공식 LobsterAI 로그인 페이지(lobsterai.youdao.com)를 열고 LobsterAI 계정으로 로그인하세요: 로그인이 완료되면 공식 페이지가 이 컴퓨터로 되돌아오고 계정이 자동으로 목록에 추가됩니다 —— 콜백은 게이트웨이 자체 포트로 들어오므로 브라우저는 게이트웨이와 같은 컴퓨터에서 열어야 합니다. 인증 코드는 이 컴퓨터에서만 게이트웨이로 전달되며 화면에 평문 토큰은 표시되지 않습니다.", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Trae SOLO의 공식 인가 페이지(trae.cn)를 열고 Trae 계정으로 로그인하세요: 인가가 완료되면 공식 페이지가 이 컴퓨터의 임시 포트로 되돌아옵니다; 같은 컴퓨터 배포에서는 자동으로 수신하며, Docker / 원격 패널에서는 안내에 따라 주소 표시줄의 콜백 주소를 복사하세요.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Accio {label}의 공식 로그인 페이지({site})를 열고 Accio 계정으로 로그인하세요: 로그인에 성공하면 공식 페이지가 이 컴퓨터로 되돌아옵니다; 같은 컴퓨터에서는 자동으로 완료되며, Docker / 원격 패널에서는 안내에 따라 콜백 주소를 붙여넣으면 됩니다 (인가 코드는 이 컴퓨터에서만 게이트웨이에 전달되며, 화면에 평문 token이 표시되지 않습니다).", "打开 Accio {label}登录页": "Accio {label} 로그인 페이지 열기", "打开 Antigravity 网页登录": "Antigravity 웹 로그인 열기", + "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "AtomGit 승인 페이지를 열고 로그인하세요. 완료되면 게이트웨이가 자동으로 브로커를 폴링해 토큰을 가져오고 계정을 추가합니다(콜백은 브로커에서 종료되며 이 기기로 돌아오지 않으므로 콜백 URL을 직접 입력할 필요가 없습니다).", "打开 CatPaw 网页登录": "CatPaw 웹 로그인 열기", "打开 Cline 授权页": "Cline 인가 페이지 열기", "打开 CodeArts 授权页": "CodeArts 인가 페이지 열기", + "打开 LobsterAI 登录页": "LobsterAI 로그인 페이지 열기", "打开 Qoder 网页登录": "Qoder 웹 로그인 열기", "打开 Trae 授权页": "Trae 인가 페이지 열기", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "ZCode {label}의 공식 인가 페이지를 열고 계정으로 로그인하세요. 인가 결과는 ZCode 서버에 기록되며, 게이트웨이가 백그라운드에서 자격 증명을 자동으로 가져와 계정 목록에 추가합니다.", "打开 ZCode {label}授权页": "ZCode {label} 인가 페이지 열기", + "打开千问办公的官方授权页完成设备码授权:登录完成后网关自动拿到登录态并加入账号列表(不需要手工回填任何回调地址)。": "QwenWork의 공식 인가 페이지를 열어 기기 코드 인가를 완료하세요: 로그인하면 게이트웨이가 로그인 상태를 자동으로 가져와 계정 목록에 추가합니다(콜백 주소를 수동으로 채울 필요가 없습니다).", + "打开千问办公网页登录": "QwenWork 웹 로그인 열기", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Huawei Cloud CodeArts의 공식 인가 페이지를 열어 로그인하세요: 로그인이 완료되면 공식 페이지가 브라우저를 게이트웨이 자체의 /oauth/callback으로 되돌립니다. 게이트웨이는 일회성 인가 코드를 임시 자격 증명으로 교환하고 계정 목록에 추가합니다.
게이트웨이가 다른 컴퓨터에서 실행 중이면 웹 UI에서 주소 표시줄의 최종 콜백 주소를 패널에 그대로 붙여넣으라는 안내가 표시됩니다.", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Huawei Cloud 인가 페이지를 열고 로그인하세요: 완료되면 게이트웨이가 클라우드를 폴링해 인가 코드를 가져와 계정 목록에 추가합니다 (OfficeAce 데스크톱 앱이 필요 없고, 콜백 URL을 수동으로 다시 붙여넣을 필요도 없습니다).", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "공식 인가 페이지를 열어 기기 코드 인가를 완료하세요. 로그인되는 계정은 {label} ({site}) 계정입니다.", @@ -238,23 +289,31 @@ "模型网关基址": "모델 게이트웨이 기본 주소", "正在打开百度登录页…": "Baidu 로그인 페이지를 여는 중…", "正在扫描本机 cc-switch…": "이 컴퓨터의 cc-switch를 스캔하는 중…", + "正在换发访客凭证…": "게스트 자격 증명을 발급하는 중…", + "正在添加…": "추가하는 중…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "{name} 로그인 완료를 기다리는 중입니다; 완료하거나 취소한 후에 새로운 로그인을 시작할 수 있습니다", "每周": "매주", "每日": "매일", "每月": "매월", "没有则无法自动续期": "없으면 자동 갱신할 수 없습니다", "没有匹配「{keyword}」的提供商": "「{keyword}」와 일치하는 제공자가 없습니다", + "添加 QClaw 账号": "", "添加 {name} 账号": "{name} 계정 추가", "添加中…": "추가 중…", "添加到 ": "추가 대상: ", + "添加千问办公账号": "QwenWork 계정 추가", "添加失败:{reason}": "추가 실패: {reason}", "添加小浣熊账号": "小浣熊 계정 추가", "添加方式": "추가 방식", + "添加访客账号": "게스트 계정 추가", "添加账号": "계정 추가", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Volcano Engine Coding 플랜; 범용 추론 엔드포인트는 https://ark.cn-beijing.volces.com/api/v3입니다", "点击添加账号": "클릭하여 계정 추가", "点开即预填,填 Key 接入": "열면 값이 미리 채워져 있으며, Key를 입력하면 연결됩니다", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "버튼을 누르면 먼저 슬라이더 인증을 한 번 통과하고(공식 리스크 관리), 이어서 로그인 페이지가 열리며 로그인이 완료되면 계정이 자동으로 추가됩니다.
이것이 국제판의 공식 유일한 로그인 방식입니다; 이미 클라이언트에 로그인한 적이 있다면 「데스크톱 로그인 상태 가져오기」가 더 빠릅니다.", + "点按钮后网关会申请一个设备码并在窗口里打开授权页;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
授权在浏览器里完成,网关不接触密码;跨机部署(Docker / 远程面板)同样可用。": "", + "点按钮后网关会申请一个设备码,并把授权页打开;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
国际版与国内版是两套账号,请用国际站(agent.minimax.io)的账号登录。": "", + "点按钮后网关会申请一个设备码,并把授权页打开;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
授权在浏览器里完成,网关不接触密码;跨机部署(Docker / 远程面板)同样可用。": "", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "AutoClaw 계정에 연결된 휴대폰 번호로 로그인합니다: 「코드 받기」를 클릭하고 SMS를 받은 뒤 아래에 입력하여 로그인하세요. 인증 코드는 이 컴퓨터에서 공식 API로 직접 제출되며, 화면에 token이 표시되지 않습니다.", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "CatPaw 계정으로 로그인합니다 (Meituan passport), 성공하면 자동으로 계정 목록에 추가됩니다.", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Loomy 계정에 연결된 휴대폰 번호로 로그인합니다: 「코드 받기」를 클릭한 뒤 입력하면 됩니다. 이 제공자에서 가장 간편한 방법입니다.", @@ -270,38 +329,55 @@ "登录已结束": "로그인이 종료되었습니다", "登录并添加": "로그인 후 추가", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "로그인 상태 파일 경로: ~/.box-agent/config/auth.json (Windows: C:\\Users\\<사용자 이름>\\.box-agent\\config\\auth.json)", + "登录态由壳侧从窗口的 Cookie 存储读出;完成后自动加入列表": "", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "커스텀 제공자 「{name}」을(를) 삭제할까요? 이 제공자 아래의 계정 {count}개도 함께 삭제되며, 삭제 후에는 복구할 수 없습니다.", "等待 Accio {label}登录完成…": "Accio {label} 로그인 대기 중…", + "等待 AtomCode 登录完成…": "AtomCode 로그인 완료 대기 중…", "等待 CatPaw 登录完成…": "CatPaw 로그인 대기 중…", "等待 Cline 授权确认…": "Cline 인가 확인 대기 중…", "等待 CodeArts 登录完成…": "CodeArts 로그인 대기 중…", + "等待 Coze 登录完成…": "", "等待 Google 授权完成…": "Google 인가 대기 중…", "等待 KukuAI 登录完成…": "KukuAI 로그인 대기 중…", + "等待 LobsterAI 登录完成…": "LobsterAI 로그인 대기 중…", "等待 OfficeAce 登录完成…": "OfficeAce 로그인 대기 중…", "等待 OrcaTerm 登录完成…": "OrcaTerm 로그인 대기 중…", "等待 Qoder 授权完成…": "Qoder 인가 대기 중…", "等待 Trae 授权完成…": "Trae 인가 대기 중…", "等待 ZCode {label}授权完成…": "ZCode {label} 인가 대기 중…", + "等待千问办公授权完成…": "QwenWork 인가 완료 대기 중…", "等待小浣熊登录完成…": "小浣熊 로그인 대기 중…", + "等待授权超时,请重新发起": "", + "等待浏览器里确认授权…": "", "等待登录完成…": "로그인 완료 대기 중…", "等待网页登录…": "웹 로그인 대기 중…", "粘贴 API Key": "API Key 붙여넣기", + "粘贴 API 令牌": "API 토큰 붙여넣기", "粘贴 Accio 的 accessToken": "Accio의 accessToken 붙여넣기", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Antigravity의 Google refresh token을 붙여넣으세요 (1//로 시작; refresh_token=이 포함된 줄 전체를 붙여넣어도 됨).
웹 로그인 외의 수동 입구입니다(예: 로그인된 공식 IDE에서 내보내기). refresh token은 만료되지 않으며, access token 만료 시 게이트웨이가 자동으로 갱신합니다; 토큰을 잃어버리면 Google 인가를 다시 받아야 하므로 잘 보관하세요.", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Command Code의 API Key를 붙여넣으세요 (user_로 시작; 공식 CLI의 ~/.commandcode/auth.json 또는 웹 studio에서 획득). 줄 전체 / 따옴표 포함 / Bearer 접두사 포함 그대로 붙여넣어도 됩니다. 게이트웨이가 알아서 순수한 key만 추출합니다. key는 만료되지 않고 갱신 인터페이스도 없으므로, 무효가 되면 다시 받으면 됩니다; 추가 시 게이트웨이가 가벼운 활성 확인을 한 번 수행하며, 실패해도 추가에는 영향을 주지 않습니다.", "粘贴 Cookie": "Cookie 붙여넣기", "粘贴 Google refresh token": "Google refresh token 붙여넣기", + "粘贴 ModelScope 的 API 令牌(ms- 开头,在 {site} 的「访问令牌」页获取)。
{siteNote}
这枚令牌两用:网关既用它调推理,也用它读账号身份、魔粒余额与每日签到。令牌长期有效、没有续期接口,失效后重新获取再粘一次即可。": "ModelScope API 토큰(ms- 로 시작)을 붙여넣으세요({site} 의 '액세스 토큰' 페이지에서 발급).
{siteNote}
이 토큰은 두 가지 용도로 쓰입니다: 게이트웨이가 추론 호출에도, 계정 신원·마립 잔액·매일 출석 조회에도 사용합니다. 토큰은 장기 유효하며 갱신 API 가 없으므로, 사용할 수 없게 되면 다시 발급받아 붙여넣으면 됩니다.", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "OrcaTerm의 OAuth accessToken(점 3개로 구분된 JWT. 데스크톱 클라이언트의 data.bin 또는 웹 버전 Cookie ot_session에서 확인)을 붙여넣으세요. Bearer / ot_session=이 포함된 줄을 그대로 붙여넣어도 되며, 게이트웨이가 토큰만 추출합니다. 추가 후에는 게이트웨이가 자동으로 갱신하므로(업스트림에 갱신 API가 있음) 2시간 만료를 직접 따라갈 필요가 없습니다.", + "粘贴 QClaw 本机网关的令牌(~/.qclaw/openclaw.json 里 gateway.auth.token 的值)。一般用「从本机导入」就够了;这里适合读不到配置文件时手动兜底 —— 端口留空则用默认的 9434。粘贴后会先连一次网关做校验,通过才落账号。": "", "粘贴 Qoder 个人访问令牌(pt-…)": "Qoder 개인 액세스 토큰(pt-…) 붙여넣기", "粘贴 accessToken": "accessToken 붙여넣기", "粘贴 access_token(一长串 JWT)": "access_token 붙여넣기 (긴 JWT)", "粘贴 session cookie": "session cookie 붙여넣기", "粘贴 token / refreshToken": "token / refreshToken 붙여넣기", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "workos:…로 시작하는 토큰 붙여넣기 (접두사가 없어도 자동으로 붙습니다)", + "粘贴令牌对": "토큰 쌍 붙여넣기", + "粘贴凭证三件套": "", + "粘贴千问办公的登录令牌(JWT,三段点分)。一般用「从本机导入」就够了;这里适合换机器迁移、或本机解密失败时手动兜底 —— 此时还需要一并填写 userId(令牌里解不出时会提示)。粘贴后会先向上游校验一次,校验通过才落账号。": "QwenWork의 로그인 토큰(점 3개로 구분된 JWT)을 붙여넣습니다. 일반적으로 「이 머신에서 가져오기」면 충분합니다. 이 수동 경로는 머신 이전 시나 로컬 복호화 실패 시의 대안입니다 —— 이 경우 userId도 함께 입력하세요(토큰에서 사용자 id를 읽을 수 없으면 안내가 표시됩니다). 붙여넣은 뒤 업스트림에 한 번 검증되며, 통과해야 계정이 저장됩니다.", + "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "智谱清言 액세스 토큰(점 3개로 구분된 JWT)을 붙여넣으세요. 발급 방법: 브라우저에서 chatglm.cn을 열고 로그인한 뒤 개발자 도구의 Application → Cookies에서 chatglm_token 값을 복사합니다(chatglm_token=…; 줄을 그대로 붙여도 됩니다). 붙여넣은 뒤 업스트림에 한 번 검증하며, 통과한 경우에만 계정을 저장합니다. 로그인된 계정은 포인트와 签到 기능을 쓸 수 있고, 이후 게이트웨이가 자동으로 갱신합니다(업스트림에 갱신 인터페이스가 있습니다).", + "粘贴登录令牌": "로그인 토큰 붙여넣기", "粘贴登录凭据": "로그인 자격 증명 붙여넣기", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "붙여넣는 것은 로그인 session입니다 (동시에 모델 게이트웨이의 token입니다). Loomy에는 갱신 인터페이스가 없으며 session 유효 기간은 약 14일이므로, 만료되면 다시 로그인하면 됩니다.", + "粘贴网关令牌": "", "粘贴网关凭据": "게이트웨이 자격 증명 붙여넣기", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
{siteNote}
MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "웹 로그인 상태 cookie monkeycode_ai_session의 값을 붙여넣으세요 (브라우저에서 전체 Cookie를 복사해 그대로 붙여넣어도 됩니다. 게이트웨이가 값을 알아서 추출합니다).
{siteNote}
MonkeyCode에는 갱신 인터페이스가 없습니다: session 유효 기간은 약 30일(하드 제한)이며, 만료되면 웹에 다시 로그인해 한 번 더 붙여넣으면 됩니다.", + "粘贴访问令牌": "액세스 토큰 붙여넣기", "系统浏览器": "시스템 브라우저", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "시스템 브라우저는 기존 로그인 상태를 재사용할 수 있습니다 (국제판 권장); 내장 창이 더 깔끔합니다.", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 Cline 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", @@ -310,16 +386,25 @@ "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 Meituan 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统默认浏览器": "시스템 기본 브라우저", + "续期令牌": "리프레시 토큰", "编码套餐 API Key": "코딩 플랜 API Key", "网关 Secret": "게이트웨이 시크릿", + "网关会以管理员身份读取马维斯客户端的内存来取凭证 —— 客户端的令牌不落明文(登录库是密文、密钥容器校验调用方签名),只在进程内存里,而客户端进程又设了访问限制,普通权限读不到。所以这一步会弹一次 UAC,点「是」即可,之后无需再操作。请确保客户端处于登录状态。": "", "网页登录": "웹 로그인", "网页登录的打开方式": "웹 로그인을 여는 방식", "网页登录(Zai / Google)": "웹 로그인 (Zai / Google)", + "网页登录(浏览器授权)": "", "自定义": "커스텀", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "자체 구축 어그리게이션 패널: 자신의 사이트 주소를 입력하세요. 잔액 조회는 이 계정의 API 키로 {base}/v1/usage 를 호출하며 추가 설정이 필요 없습니다", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "자체 구축 어그리게이션 패널: 자신의 사이트 주소를 입력하세요. 잔액 조회에는 별도로 NewAPI 개인 액세스 토큰이 필요합니다 (아래 \"잔액 조회\" 단락 참조)", "获取风控配置失败,将直接尝试领取:{message}": "리스크 관리 구성을 가져오지 못했습니다. 바로 수령을 시도합니다: {message}", "获取验证码": "코드 받기", + "设备码": "", + "设备码 5 分钟内有效;在授权页确认后这里会自动完成": "", + "设备码 5 分钟内有效;窗口里显示「等待你在浏览器里确认」时去授权页点确认即可": "", + "访客模式": "게스트 모드", + "访客模式(免登录)": "게스트 모드(로그인 불필요)", + "访问令牌": "액세스 토큰", "该上游无需鉴权(不发送鉴权头)": "이 업스트림은 인증이 필요하지 않음 (인증 헤더를 보내지 않음)", "该提供商": "이 제공자", "该提供商账号添加功能即将上线": "이 제공자의 계정 추가 기능이 곧 출시됩니다", @@ -332,11 +417,18 @@ "请填写 {label}": "{label}을(를) 입력하세요", "请填写名称": "이름을 입력하세요", "请完成验证…": "인증을 완료하세요…", + "请粘贴 www.coze.cn 的完整 Cookie(浏览器开发者工具 → Network → 任取一条 www.coze.cn 请求的 Cookie 头,或 Cookie 编辑器一键导出)。整包带上最稳:任务中心与机器人列表只认 Cookie,少了几项会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {name: value} 对象。Coze 没有刷新机制,登录态过期后重新登录或重新粘贴即可。": "", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "%APPDATA%/AutoClaw/auth.json을 읽어 복호화합니다. Windows 전용", + "读取 %USERPROFILE%/.minimax/auth 下客户端已保存的令牌,只读不写 —— 客户端用一对必须相等的版本号判断「是否在别处登录过」,写一次就会让它要求你重新扫码。注意:导入是一次快照,之后网关与客户端各自续期,两边的刷新令牌会互相轮换,建议只在一处使用。": "", + "读取 %USERPROFILE%/.minimax/auth/prod/en 下客户端已保存的令牌,只读不写。客户端按「环境 / 地区」分目录存两套登录态,国际版在 en 目录里;没在国际站登录过时这里读不到,请改用上面的网页登录。": "", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "~/.cline/data/settings/providers.json을 읽습니다. Cline 클라이언트에 로그인되어 있어야 합니다", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "~/.meituan-catpaw/auth.json을 읽습니다. CatPaw 클라이언트에 로그인되어 있어야 합니다", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "이 컴퓨터의 KukuAI 클라이언트 로그인 상태를 읽습니다 (%APPDATA%\\baidugenflowpro\\Network\\Cookies). KukuAI 클라이언트에 로그인되어 있어야 합니다", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "로컬 OrcaTerm 클라이언트 로그인 상태(%APPDATA%\\com.orcaterm-desktop.app\\data.bin)를 읽습니다. OrcaTerm 클라이언트에 로그인되어 있어야 합니다", + "读取本机 QClaw 客户端网关配置(%USERPROFILE%\\.qclaw\\openclaw.json),需 QClaw 正在运行": "", + "读取本机千问办公客户端登录态(%APPDATA%\\QwenWorkCN\\auth-v2.dat),需已在客户端登录过": "로컬 QwenWork 클라이언트의 로그인 상태(%APPDATA%\\QwenWorkCN\\auth-v2.dat)를 읽습니다. 클라이언트에 로그인한 적이 있어야 합니다", + "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "이 PC의 智谱清言 클라이언트 로그인 상태를 읽습니다(%APPDATA%\\chatglm\\Network\\Cookies). 클라이언트에 로그인되어 있어야 합니다", + "读本机 QClaw 客户端拉起的 OpenClaw 网关配置(端口 + 令牌)。与别家不同,本家需要 QClaw 正在运行 —— 网关是客户端拉起的,端口和令牌都以它在运行为前提;后端会真连一次做校验。": "", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "이 컴퓨터의 공식 클라이언트(Tencent WorkBuddy / WorkBuddy AI)에 현재 로그인된 계정을 읽습니다. 자격 증명 디렉터리에서 최신 로그인 상태 파일을 자동으로 찾습니다(이 컴퓨터의 공식 클라이언트에 로그인되어 있어야 함). 가져온 후에는 게이트웨이가 자동 갱신하므로 2시간 유효 기간에 맞춰 토큰을 수동으로 교체할 필요가 없습니다.", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 읽습니다 (평문 Cookie, 복호화 불필요). 클라이언트 실행 중에는 Cookies 파일이 독점 점유되므로, 가져오기 전에 KukuAI 클라이언트를 먼저 종료하세요.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다).", @@ -345,22 +437,29 @@ "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다). Zai / Google로 클라이언트에 로그인하는 사용자는 이 항목을 사용하세요.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다). 이 항목은 {label}로 분류됩니다.", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "로컬 클라이언트가 현재 로그인한 OAuth 토큰을 읽습니다(평문 JSON, 복호화 불필요). 가져온 뒤에는 게이트웨이가 자동으로 갱신하므로 클라이언트의 2시간 만료에 맞춰 수동으로 교체할 필요가 없습니다.", + "读本机客户端当前的登录态。它是 Chromium os_crypt 加密的(DPAPI 主密钥 + AES-256-GCM),因此仅 Windows 可用。DPAPI 按当前 Windows 用户解密,不需要客户端正在运行 —— 只要在本机登录过一次即可(换机器时请在目标机器上重新登录一次客户端)。": "로컬 클라이언트의 현재 로그인 상태를 읽습니다. Chromium os_crypt 암호화(DPAPI 마스터 키 + AES-256-GCM)이므로 Windows에서만 사용할 수 있습니다. DPAPI는 현재 Windows 사용자 자격으로 복호화하며 클라이언트가 실행 중일 필요는 없습니다 —— 이 머신에서 한 번 로그인하면 됩니다(머신을 옮길 때는 대상 머신에서 클라이언트에 한 번 로그인하세요).", + "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "이 PC 클라이언트의 현재 로그인 상태를 읽습니다. 클라이언트의 Cookies는 DPAPI로 암호화되어 있어(Chromium os_crypt) Windows에서만 사용할 수 있습니다. 클라이언트가 실행 중이면 이 파일을 읽지 못할 수 있으니 클라이언트를 종료한 뒤 다시 시도하세요(\"자격 증명 입력\"으로 토큰을 붙여넣어도 됩니다).", "账号信息": "계정 정보", "账号信息不完整,请刷新后重试": "계정 정보가 불완전합니다. 새로 고친 후 다시 시도하세요", "账号已添加,但请留意:{warning}": "계정은 추가되었지만 확인해 주세요: {warning}", "账号版本": "계정 버전", "账号类型": "계정 유형", "账号:{name}
": "계정: {name}
", + "贴国际站的访问令牌(mmoat_ 开头)。国内站的令牌拿到这里会被上游拒(401)—— 两地账号体系不同,不能互换。": "", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Inference Providers 라우팅을 사용하며, Key는 hf.co/settings/tokens에서 만듭니다", "还没有账号": "아직 계정이 없습니다", "这一家当前不支持网页登录,请改用填写凭证": "이 제공자는 현재 웹 로그인을 지원하지 않습니다. 대신 자격 증명 입력을 사용하세요", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "이 계정에는 현재 자동으로 수령할 수 있는 이벤트가 없습니다 (매일 로그인 보너스 포인트만 자동 수령합니다)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "이 할당량은 「이벤트 플랜」 채널을 사용합니다: 포워딩 시 코딩 플랜이 만료되었다는 안내가 나오면, ", + "这里填国内站(modelscope.cn)的账号:请粘贴国内站的令牌。国际站(modelscope.ai)是另一家 provider、另一套账号,两边的令牌不通用。": "여기에는 중국판 사이트(modelscope.cn) 계정을 입력합니다. 중국판 사이트의 토큰을 붙여넣으세요. 국제판 사이트(modelscope.ai)는 다른 공급자·다른 계정 체계이며 토큰은 서로 호환되지 않습니다.", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "여기에는 중국 사이트(monkeycode-ai.com)의 계정을 입력합니다: 중국 사이트의 session을 붙여넣으세요. 국제 사이트(monkeycode-ai.net)는 다른 제공자이며 별도의 계정입니다. 양쪽 자격 증명은 호환되지 않습니다.", + "这里填国际站(modelscope.ai)的账号:请粘贴国际站的令牌。国内站(modelscope.cn)是另一家 provider、另一套账号,两边的令牌不通用。": "여기에는 국제판 사이트(modelscope.ai) 계정을 입력합니다. 국제판 사이트의 토큰을 붙여넣으세요. 중국판 사이트(modelscope.cn)는 다른 공급자·다른 계정 체계이며 토큰은 서로 호환되지 않습니다.", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "여기에는 국제 사이트(monkeycode-ai.net)의 계정을 입력합니다: 국제 사이트의 session을 붙여넣으세요. 중국 사이트(monkeycode-ai.com)는 다른 제공자이며 별도의 계정입니다. 양쪽 자격 증명은 호환되지 않습니다.", + "适合已在别处登录、或客户端数据目录被改过的场景。只需要 accessToken(mmoat_ 开头);JWT 里能读出身份与到期,无需再填别的。只给访问令牌时约 1 小时有效、无法自动续期 —— 想要自动续期请用上面的网页登录。": "", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "CatPaw 클라이언트의 로그인 상태 파일 auth.json을 선택하세요 (Windows: C:\\Users\\<사용자 이름>\\.meituan-catpaw\\auth.json, macOS / Linux: ~/.meituan-catpaw/auth.json). 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다 — 더 이상 이 컴퓨터의 클라이언트에 의존하지 않습니다.", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Cline 클라이언트의 로그인 상태 파일 providers.json을 선택하세요 (Windows: C:\\Users\\<사용자 이름>\\.cline\\data\\settings\\providers.json, macOS / Linux: ~/.cline/data/settings/providers.json). 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다 — 더 이상 이 컴퓨터의 클라이언트에 의존하지 않습니다. 이 항목은 {label}에 등록됩니다.", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "KukuAI 클라이언트의 Cookies 파일을 선택하세요 (%APPDATA%\\baidugenflowpro\\Network\\Cookies): **바이너리** SQLite 데이터베이스입니다 (확장자 없음, JSON 아님). 가져오기는 선택한 파일의 복사본을 읽어 실행 중인 클라이언트와 점유를 다투지 않으므로 **클라이언트를 먼저 종료할 필요가 없습니다**. 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다.", + "选择一份存着 www.coze.cn Cookie 的文本文件(整段 Cookie 头或 Cookie 编辑器导出的 JSON 都可以)。导入后凭证直接落在网关、可独立转发,不依赖本机客户端(Coze 客户端运行时占着 Cookie 库,读不了)。": "", "选择凭证文件导入": "자격 증명 파일에서 가져오기", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "공식 클라이언트의 로그인 자격 증명 파일(workbuddy-desktop.info / workbuddy-desktop-ai.info) 또는 wb2api에서 내보낸 계정 JSON(중첩/평면 형식 모두 가능)을 선택하세요.", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "小浣熊 클라이언트의 로그인 상태 파일 auth.json을 선택하세요 (Windows: C:\\Users\\<사용자 이름>\\.box-agent\\config\\auth.json, macOS / Linux: ~/.box-agent/config/auth.json). 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다 — 더 이상 이 컴퓨터의 클라이언트에 의존하지 않습니다.", @@ -379,6 +478,7 @@ "领取失败:{message}": "수령 실패: {message}", "领取并让官方确认": "수령하고 공식 확인 받기", "风控验证配置不完整,请稍后重试": "리스크 관리 인증 구성이 불완전합니다. 잠시 후 다시 시도하세요", + "马维斯 Marvis": "", "验证码": "인증 코드", "验证码已发送。收到后填入下方并点「登录并添加」": "인증 코드를 전송했습니다. 받은 후 아래에 입력하고 「로그인 후 추가」를 클릭하세요", "验证码已发送,请查看手机短信": "인증 코드를 전송했습니다. 휴대폰 SMS를 확인하세요", diff --git a/desktop-tauri/i18n/i18n-src/ko/agent.json b/desktop-tauri/i18n/i18n-src/ko/agent.json new file mode 100644 index 00000000..da49e11f --- /dev/null +++ b/desktop-tauri/i18n/i18n-src/ko/agent.json @@ -0,0 +1,102 @@ +{ + "· 走网关": "· 게이트웨이 경유", + "。这里的开关与还原作用在网关所在的机器上。": "", + "一键把本机 AI 客户端接到本地网关:改写它自己的配置文件,保留其它设置": "로컬 AI 클라이언트를 로컬 게이트웨이에 연결합니다. 각 클라이언트의 설정 파일을 고쳐 쓰고 나머지 설정은 그대로 둡니다", + "上游模型 id,例如 raccoon-coder-pro": "업스트림 모델 ID (예: raccoon-coder-pro)", + "下面是这个客户端文件里现有的配置(只读)。点「从客户端导入」把它收进我的配置,之后就能编辑和切换。": "아래는 이 클라이언트 설정 파일에 있는 기존 설정입니다 (읽기 전용). 「클라이언트에서 가져오기」를 누르면 내 설정으로 가져와 편집하고 전환할 수 있습니다.", + "个 · 已安装": "개 · 설치됨", + "中转": "중계", + "从客户端导入": "클라이언트에서 가져오기", + "会用最近一次备份覆盖它的配置文件。这一步不会改动你在本页存的配置列表。": "가장 최근 백업으로 설정 파일을 덮어씁니다. 이 페이지에 저장한 설정 목록은 변경되지 않습니다.", + "低": "낮음", + "保存修改": "변경 저장", + "共": "합계", + "写入中…": "쓰는 중…", + "写入后需要重开会话才生效": "쓴 뒤에는 세션을 다시 열어야 적용됩니다", + "写入失败:{reason}": "쓰기 실패: {reason}", + "写入客户端": "클라이언트에 쓰기", + "写入预览": "쓰기 미리보기", + "单配置": "단일 설정", + "单配置文件": "", + "只改 enabled 与默认项,其余条目原样保留": "enabled 와 기본 항목만 바꾸고 나머지 항목은 그대로 둡니다", + "只改属于我们的那几个键,其它设置原样保留": "저희 키만 바꾸고 나머지 설정은 그대로 둡니다", + "可同时启用多条;切换只改 enabled 与默认项": "여러 개를 동시에 켤 수 있습니다. 전환은 enabled 와 기본 항목만 바꿉니다", + "同一时刻只有一套生效;写入 = 把这一套整段写进客户端文件": "한 번에 하나만 적용됩니다. 쓰기 = 이 설정을 클라이언트 파일에 통째로 기록합니다", + "启用 {name}": "{name} 사용", + "备注": "메모", + "多套配置": "여러 설정", + "多配置 · 已启用 {n}": "다중 설정 · {n}개 사용 중", + "官方直连": "공식 직결", + "客户端列表加载失败:{reason}": "클라이언트 목록을 불러오지 못했습니다: {reason}", + "客户端清单": "클라이언트 목록", + "客户端自己的设置优先;这里写的是写入时带进去的初值": "클라이언트 자체 설정이 우선입니다. 여기 쓰는 값은 기록 시 넣는 초기값입니다", + "已保存": "저장됨", + "已写入 {name}": "{name}에 기록했습니다", + "已写入 {name};这个客户端只在启动时读配置,重开一个会话才生效": "{name}에 기록했습니다. 이 클라이언트는 시작할 때만 설정을 읽으므로 세션을 다시 열어야 적용됩니다", + "已安装": "설치됨", + "已按备份还原 {name}": "{name}을(를) 백업에서 복원했습니다", + "已收进我的配置:{n} 条": "내 설정으로 가져왔습니다: {n}개", + "已还原「{name}」": "", + "当前环境不支持保存(桥接方法缺失)": "이 환경에서는 저장할 수 없습니다 (브리지 메서드 없음)", + "当前环境不支持写入(桥接方法缺失)": "이 환경에서는 쓸 수 없습니다 (브리지 메서드 없음)", + "当前环境不支持客户端接管(桥接方法缺失)": "이 환경에서는 클라이언트 연결을 쓸 수 없습니다 (브리지 메서드 없음)", + "当前环境不支持导入(桥接方法缺失)": "이 환경에서는 가져올 수 없습니다 (브리지 메서드 없음)", + "当前环境不支持还原(桥接方法缺失)": "이 환경에서는 복원할 수 없습니다 (브리지 메서드 없음)", + "手机上只能查看与还原接管。改写配置内容请回桌面端": "", + "按备份还原": "백업에서 복원", + "按备份还原 {name}?": "{name}을(를) 백업에서 복원할까요?", + "接管会把客户端的 base_url 指向本网关,并在写入前自动备份原文件。": "", + "搜索客户端": "클라이언트 검색", + "搜索客户端 / 配置文件…": "클라이언트 / 설정 파일 검색…", + "改写网关所在机器上的客户端配置": "", + "改动": "변경", + "新增": "추가", + "新建配置": "새 설정", + "新配置": "새 설정", + "无配置": "설정 없음", + "暂无预览": "미리보기가 없습니다", + "未安装": "설치 안 됨", + "未接管": "", + "未检测到": "감지되지 않음", + "未检测到安装": "설치가 감지되지 않음", + "未走网关": "게이트웨이 미경유", + "本机": "이 기기", + "本机服务": "로컬 서비스", + "本机没有找到 {name}": "이 컴퓨터에서 {name}을(를) 찾지 못했습니다", + "本机网关": "로컬 게이트웨이", + "来自客户端文件,只读": "클라이언트 설정 파일에서 읽음 (읽기 전용)", + "模型 id": "모델 ID", + "正在生成预览…": "미리보기 생성 중…", + "正在读取本机客户端…": "로컬 클라이언트 읽는 중…", + "每个客户端可以有": "각 클라이언트는", + "没有匹配的客户端": "일치하는 클라이언트가 없습니다", + "没有可导入的新条目(可能还没配过,或已经在我的配置里)": "가져올 새 항목이 없습니다 (아직 설정하지 않았거나 이미 내 설정에 있음)", + "没有新的条目可收(都已在我的配置里)": "가져올 새 항목이 없습니다 (모두 내 설정에 있음)", + "没有检测到可接管的客户端": "", + "确定按最近一次备份还原「{name}」?": "", + "第三方中转": "서드파티 중계", + "编辑:{name}": "편집: {name}", + "行号由后端逐行比对给出": "줄 번호는 백엔드의 줄 단위 비교 결과입니다", + "装好后回到这一页点「重新检测」,它会出现在上面的列表里。": "설치한 뒤 이 페이지로 돌아와 「다시 감지」를 누르면 위 목록에 나타납니다.", + "设为默认": "기본으로 설정", + "读取客户端配置失败:{reason}": "클라이언트 설정을 읽지 못했습니다: {reason}", + "走网关": "게이트웨이 경유", + "还原中…": "", + "还原失败:{reason}": "복원 실패: {reason}", + "还没有配置": "설정이 아직 없습니다", + "还没有配置。点「新建配置」建一套指向本机网关的,再写入客户端。": "설정이 아직 없습니다. 「새 설정」으로 로컬 게이트웨이를 가리키는 설정을 만든 뒤 클라이언트에 쓰세요.", + "还没有预览。选一套配置后这里会显示写入客户端之后的样子。": "미리보기가 없습니다. 설정을 고르면 클라이언트에 쓴 뒤의 모습이 여기에 표시됩니다.", + "这个客户端会自己重读配置,写入后开新会话即生效。": "이 클라이언트는 설정을 스스로 다시 읽습니다. 쓴 뒤 새 세션을 열면 적용됩니다.", + "这个客户端只在启动时读配置:写入后重开一个会话才生效。": "이 클라이언트는 시작할 때만 설정을 읽습니다. 쓴 뒤 세션을 다시 열어야 적용됩니다.", + "这个客户端的配置里没有读出地址(可能还没配过,或形状不认识)": "이 클라이언트 설정에서 주소를 읽지 못했습니다 (아직 설정하지 않았거나 형식을 알 수 없음)", + "这个客户端自己支持多套配置并存": "이 클라이언트는 여러 설정을 함께 둘 수 있습니다", + "这个客户端自己管理多套配置,可同时启用多条;切换只改 enabled 与默认项,不动它的模型清单。": "이 클라이언트는 여러 설정을 스스로 관리하며 여러 개를 동시에 켤 수 있습니다. 전환은 enabled 와 기본 항목만 바꾸고 모델 목록은 건드리지 않습니다.", + "这套配置是干什么用的(只显示在本页)": "이 설정의 용도 (이 페이지에만 표시)", + "选一个客户端开始": "클라이언트를 선택해 시작", + "配置": "설정", + "配置来源": "설정 종류", + "需重开会话生效": "세션을 다시 열어야 적용됩니다", + "高": "높음", + "(手机读不到本机文件)": "", + "(本机网关 / 官方直连 / 第三方中转 / 本机服务),随时切换。单配置客户端切换 = 整段覆写并备份原文件;多配置客户端(如 ZCode)只改 enabled 与默认项,它自己的条目一律不动。": "(로컬 게이트웨이 / 공식 직결 / 서드파티 중계 / 로컬 서비스)를 언제든 전환할 수 있습니다. 단일 설정 클라이언트는 설정 전체를 덮어쓰고 원본 파일을 백업합니다. ZCode 같은 다중 설정 클라이언트는 enabled 와 기본 항목만 바꾸고 자체 항목은 건드리지 않습니다." +} diff --git a/desktop-tauri/i18n/i18n-src/ko/assistant.json b/desktop-tauri/i18n/i18n-src/ko/assistant.json index 817c4b66..0c9a9e4c 100644 --- a/desktop-tauri/i18n/i18n-src/ko/assistant.json +++ b/desktop-tauri/i18n/i18n-src/ko/assistant.json @@ -1,32 +1,147 @@ { "CodeArts 的账号余额还剩多少?": "CodeArts 계정 잔액이 얼마나 남았나요?", + "Enter 发送 · Shift+Enter 换行": "", + "section 只能是 models / mappings / both": "section 은 models / mappings / both 중 하나여야 합니다", + "{n} / {total}": "", + "{n} 个账号正在限流:": "", + "{n} 家": "", + "{n} 条改动": "", + "{n} 条映射": "{n}개 매핑", + "✓ 完成": "", + "✗ 失败": "", + "「{name}」超过 {n} MB,换一张小一点的。": "", + "一次最多 {n} 张图片。": "", + "一次最多 {n} 张图片,多出的没有添加。": "", + "上面四个快捷查询直接读网关数据,不经过模型": "", + "不指定,交给映射绑定": "", + "今天用了 {tokens} Token,共 {requests} 次请求。": "", + "今天用了多少 Token?": "오늘 Token을 얼마나 사용했나요?", + "会话响应不完整": "세션 응답이 불완전합니다", + "会话里的选择优先于这里的默认值": "", + "余额是缓存读数,点账号详情里的「刷新凭证」可触发实时查询。": "", + "作为 system 消息注入": "", + "保存思考等级失败:{message}": "사고 수준 저장 실패: {message}", "修改模型能力": "모델 기능 수정", + "共 {n} 个模型条目。": "", + "共 {n} 个模型条目。映射与改名请在桌面端「模型管理」里做。": "", + "共 {n} 个账号,前 {m} 个的余额读数:": "", + "删除会话失败:{message}": "세션 삭제 실패: {message}", + "删除这条对话": "이 대화 삭제", + "助手设置": "", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "어시스턴트 설정을 읽지 못해 대화 모델을 아직 저장할 수 없습니다. 입력창 아래의 안내를 확인하세요.", + "助手设置没读出来,暂时存不了对话模型。": "", + "助手设置没读出来,暂时存不了思考等级 —— 看输入框下方的提示。": "어시스턴트 설정을 읽을 수 없어 사고 수준을 저장할 수 없습니다 — 입력창 아래 안내를 확인하세요.", + "助手设置没读出来,暂时存不了思考等级。": "", + "助手设置没读出来,现在还不能发消息 —— 点右上角「助手设置」看看。": "", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "어시스턴트 설정을 읽지 못해 아직 메시지를 보낼 수 없습니다. 입력창 아래의 안내를 확인하세요.", + "勾选即注入": "", + "历史对话": "대화 기록", + "原先在「设置 → AI 助手」的配置都搬到这里了": "", + "发消息或做任务…(Enter 发送,Shift+Enter 换行,可粘贴图片)": "", "发起模型测试": "모델 테스트 실행", + "变更前确认": "", + "只支持图片文件(PNG / JPG / WebP / GIF)。": "", + "只读 · 不会改动配置": "", + "只跳过确认,不扩大工具权限;每次执行仍会记录在对话里。": "", + "各账号余额还剩多少?": "각 계정의 잔액이 얼마나 남았나요?", "启用 / 禁用模型": "모델 활성화 / 비활성화", "启用 / 禁用账号": "계정 활성화 / 비활성화", + "哪些模型在多家重复了?": "여러 제공업체에 중복된 모델은 무엇인가요?", + "图片 {n}": "", + "完全访问": "", + "完全访问已开启:改动类工具会直接执行,不再弹出确认卡。": "", + "完整对话在桌面端「AI 助手」页": "", "对指定模型发一次真实请求,回报连通性与耗时。": "지정한 모델에 실제 요청을 한 번 보내 연결성과 소요 시간을 보고합니다.", + "对话由网关转发到上游模型,用量计入请求日志": "대화는 게이트웨이가 업스트림 모델로 전달하며, 사용량은 요청 로그에 기록됩니다", + "已停止生成": "생성을 중지했습니다", + "已全部勾选": "", + "已切换到变更前确认": "", + "已切换到完全访问": "", + "已启用的模型": "", + "已调用": "호출함", "开关模型的对外可用性,不改其它属性。": "모델의 외부 가용성만 전환하고 다른 속성은 변경하지 않습니다.", + "当前一个工具都没勾选,助手只能纯聊天。点右上角「助手设置」勾上需要的工具。": "", + "当前模型没有声明图片能力,图可能发不上去。": "", + "当前没有同名模型分布在多家。": "", + "当前:已关闭(助手收不到任何系统提示词,工具定义照发)": "", + "当前:已开启 · 使用内置默认提示词": "", + "快捷查询走本地网关,不消耗额度": "", + "思考等级已切换": "", + "思考:跟随模型": "사고: 모델 따르기", + "总请求": "", + "成功率": "", + "我可以直接查网关里的真实数据:余额、模型重复、今日用量、限流账号。": "", + "打开会话失败:{message}": "세션 열기 실패: {message}", + "执行中": "", + "找到 {n} 组同名模型:": "", "把重复的模型加上家名前缀区分开": "중복된 모델에 제공자 이름 접두사를 붙여 구분하기", + "推理等级": "", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "컨텍스트 창 / 최대 출력 토큰 / 도구 호출 등의 기능 플래그를 수정합니다.", + "改动先列清单": "", + "改动直接执行": "", + "改动类 · 默认需要确认": "", + "改动类工具先列清单,等你点「执行」": "", + "改动类工具直接执行,不再询问": "", + "改回变更前确认": "", "改某一家的整组顺位(先试哪一家)。": "특정 제공자의 그룹 전체 순위를 변경합니다(어느 제공자를 먼저 시도할지).", "改账号在全局队列里的名次,决定主备与切换顺序。": "전역 큐에서 계정 순위를 변경해 주/예비와 전환 순서를 결정합니다.", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "매핑 이름 추가 / 삭제. 일괄 접두사 추가와 같은 이름을 여러 제공자에 통합하는 것을 지원합니다.", + "新建对话时使用": "", + "映射": "", + "有什么可以帮你?可以问配置、查余额、让我改模型设置。": "무엇을 도와드릴까요? 설정 질문, 잔액 확인, 모델 설정 변경이 가능합니다.", + "未命名对话": "제목 없는 대화", + "未知的工具:{name}": "알 수 없는 도구: {name}", + "未选择": "", + "权限模式": "", + "查询账号": "", "查询账号余额": "계정 잔액 조회", + "模型不支持时会自动忽略": "", + "模型没有返回内容": "모델이 내용을 반환하지 않았습니다", + "正在思考…": "생각 중…", + "正在生成,点右侧按钮可停止": "생성 중 — 오른쪽 버튼을 누르면 중지됩니다", "没有可用的模型": "사용 가능한 모델이 없습니다", + "没有已启用的模型,请先在「模型管理」里启用": "활성화된 모델이 없습니다. 먼저 「모델 관리」에서 활성화하세요", "测一下当前最快的模型通不通": "현재 가장 빠른 모델이 연결되는지 테스트하기", "深度推理": "심층 추론", + "添加图片": "", + "添加图片(也可粘贴或拖入)": "", + "清空全部": "모두 지우기", + "清空历史失败:{message}": "기록 지우기 실패: {message}", + "现在有哪些账号被限流?": "지금 어떤 계정이 속도 제한 중인가요?", + "现在没有账号被限流。": "", "看看哪些模型在多家重复了": "여러 제공자에서 중복된 모델 확인하기", "禁用会把账号移出转发队列,线路可能因此断供。": "비활성화하면 계정이 포워딩 큐에서 빠지며, 이 경로가 끊길 수 있습니다.", + "自定义提供商模块未就绪(请刷新界面后重试)": "사용자 지정 공급자 모듈이 준비되지 않았습니다(인터페이스를 새로 고친 후 다시 시도하세요)", + "自由对话请用桌面端…": "", "获取模型列表": "모델 목록 조회", + "要改配置的话,桌面端的助手支持完整对话与工具调用。": "", "设置模型映射 ID": "모델 매핑 ID 설정", + "读取历史会话失败:{message}": "대화 기록 읽기 실패: {message}", + "读取图片失败": "", + "读取图片失败:{message}": "", + "读取模型清单": "", + "读取模型清单失败": "모델 목록을 불러오지 못했습니다", + "读取账号列表": "", "读各提供商的模型清单、映射名与启用状态。": "각 제공자의 모델 목록, 매핑 이름, 활성화 상태를 읽습니다.", "读各账号的额度 / 剩余用量。": "각 계정의 할당량 / 남은 사용량을 읽습니다.", "调整提供商顺位": "제공자 순위 조정", "调整账号优先级": "계정 우선순위 조정", + "走本地网关 · 消耗所选账号额度": "", + "跟随模型": "", + "跟随模型(不指定)": "모델 따르기(지정 안 함)", + "跟随网关设置": "게이트웨이 설정 따르기", + "还没有历史对话": "대화 기록이 없습니다", + "还没有账号。": "", + "还没有选择对话模型:在输入框右下角选一个,或点右上角「助手设置」。": "", + "还没有配置默认模型": "기본 모델이 아직 설정되지 않았습니다", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "이 버전의 게이트웨이에는 AI 어시스턴트 API가 아직 없습니다 (HTTP 404): 백엔드가 오래되었습니다. 다시 빌드하거나 클라이언트를 재시작한 후 시도하세요. {message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "이번 턴이 왕복 {n}회 한도에 도달해 지시를 기다리며 멈췄습니다(다르게 표현하거나 몇 단계로 나눠 주세요).", + "这个查询还没有实现。": "", + "这条会话没能存进历史:{message}": "이 세션을 기록에 저장하지 못했습니다: {message}", + "这里只改本次会话用的模型,不会改网关里保存的默认值。": "여기서는 이번 세션에 쓸 모델만 바뀌며, 게이트웨이에 저장된 기본값은 그대로입니다.", "连接网关失败({base}):{message}": "게이트웨이 연결 실패 ({base}): {message}", - "配置响应不完整": "구성 응답이 불완전합니다" + "选择对话模型": "대화 모델 선택", + "配置响应不完整": "구성 응답이 불완전합니다", + "问点什么…": "무엇이든 물어보세요…", + "默认行为": "" } diff --git a/desktop-tauri/i18n/i18n-src/ko/checkin.json b/desktop-tauri/i18n/i18n-src/ko/checkin.json index 1df74672..56ada96e 100644 --- a/desktop-tauri/i18n/i18n-src/ko/checkin.json +++ b/desktop-tauri/i18n/i18n-src/ko/checkin.json @@ -1,12 +1,15 @@ { " · ": " · ", + "QClaw": "", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "SOLO의 checkin_credits 수령 · 자연일 0시 기준 새로고침", "UMID 组件安装失败:{error}": "UMID 컴포넌트 설치 실패: {error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy 중국판 · 작업 트리를 조회한 뒤 한 번에 점등/수령 · 개학 시즌 이벤트와 고양이 여행 포함 · 행을 클릭하면 상세", "WorkBuddy 国际版保活模型,按顺序回退": "WorkBuddy 국제판 키핑 모델, 순서대로 폴백", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "WorkBuddy 국제판 일일 활동용 무료 모델 · 전역 단일 설정, 항목과 무관", "{date}(每天 {time})": "{date} (매일 {time})", + "{done}/{total} 已签": "{done}/{total} 출석 완료", "{label} ×{count}({reason})": "{label} ×{count}({reason})", + "{n} 积分": "{n} 포인트", "{n} 项可完成": "{n}개 완료 가능", "{n} 项待领": "수령 대기 {n}건", "{total} 账号 · {done} 已签": "계정 {total}개 · 출석 완료 {done}개", @@ -22,10 +25,15 @@ "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "\"키핑 모델 체인\"은 WorkBuddy 국제판 일일 활동에서 계정을 활성 상태로 유지하는 무료 모델입니다. 순서대로 시도하며 첫 성공에서 멈춥니다. 목록을 비우고 저장하면 기본 체인(현재 기본값: {defaults})으로 복원됩니다. 당일 중복 제거 + 멱등한 수령: 반복 실행해도 \"이미 수령됨\"만 반환되며 포인트가 이중으로 적립되지 않습니다.", "「免费领积分」的每日任务 · 逐个领取": "「무료 포인트 받기」 일일 작업 · 하나씩 수령", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "일회성 혜택 · 출석 후 자동으로 조회하고 수령하며, 수령을 마치면 로컬에 기록해 업스트림을 더 조회하지 않음 · 행을 클릭해 작업 목록 펼치기", + "一次性福利已全部结清": "일회성 혜택이 모두 정산되었습니다", + "一次性福利已结清": "일회성 혜택이 정산되었습니다", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "그날의 보상을 한 번에 일괄 수령합니다(매일 출석 + 신규 혜택) · 멱등성은 업스트림이 보장", "一键安装组件": "컴포넌트 한 번에 설치", "一键完成": "한 번에 완료", "一键领取({n})": "전체 수령({n})", + "七天一轮共 4000 积分(400/400/400/1000/400/400/1000) · 每份 30 天后到期 · 当天重复领是幂等的": "", + "上一轮({time}):{n} 个动作,+{credit} 积分": "지난 회차({time}): {n}개 동작, +{credit} 크레딧", + "上一轮({time}):{n} 项,+{credit} 积分": "", "上次执行:{line}": "마지막 실행: {line}", "上次签到时间": "최근 출석 시간", "上次领取 {time} · 领取需通过滑块验证码": "최근 수령 {time} · 수령 시 슬라이더 캡차 필요", @@ -33,16 +41,27 @@ "下次 {date} · 点此管理条目": "다음 {date} · 클릭하여 항목 관리", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "일일 출석 제외: {list}. CodeArts의 일회성 보상은 아래 \"입문 혜택\"에 있으며, CodeArts와 ZCode의 일일/플랜 혜택 수령은 \"활동 혜택\"에서 볼 수 있습니다.", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "일일 출석 제외: {list}. CodeArts의 일회성 보상은 아래 \"입문 혜택\"에, CodeArts의 일일 혜택은 바로 위 \"일일 출석\"에 있습니다. ZCode의 플랜 수령은 \"활동 혜택\"에서 볼 수 있습니다.", + "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」;QClaw 已停运、积分随迁移到 WorkBuddy 发放,见下方「只读余额(已停运)」。": "", + "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」;QClaw 的积分随登录自动发放,见「自动发放」。": "", + "不参与签到": "출석 대상 아님", + "不可领": "수령 불가", "与国内版同一套任务接口 · 站点不同": "중국판과 동일한 작업 인터페이스 · 사이트만 다름", + "与国内版同一套链路 · 站点不同、令牌不通用": "중국판과 동일한 경로 · 사이트가 다르고 토큰은 호환되지 않음", + "个账号不参与": "개 계정 제외", + "今天 · 北京时间自然日": "오늘 · 베이징 시간 기준 날짜", + "今天已执行:{list}": "오늘 실행됨: {list}", "今天已领 {n} 份": "오늘 {n}건 받음", "今天未领": "오늘 미수령", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "오늘(베이징 시간 {day}) 수령 완료 · 공식 확인 반영 {n}건 · 내일 다시 수령 가능", "今日已执行": "오늘 실행됨", "今日已派出": "오늘 이미 출발", + "今日已签": "오늘 출석 완료", "今日已领取": "오늘 수령 완료", "今日签到": "오늘 출석", "今日签到进度": "오늘 출석 진행률", "从清单里移除": "목록에서 제거", + "余额查询中…": "", + "余额查询失败:{error}": "", "保活": "키핑", "保活 {n}": "키핑 {n}", "保活+领取": "키핑+수령", @@ -55,6 +74,8 @@ "全部结果": "모든 결과", "全部账号均可签到": "모든 계정이 출석할 수 있습니다", "全部领取": "전체 수령 완료", + "关着:不自动巡检(仍可手动巡检)": "꺼짐: 자동 순회하지 않습니다(수동 순회는 가능)", + "关着:不自动执行(仍可手动执行)": "", "关着:所有条目都不执行": "꺼짐: 어떤 항목도 실행되지 않습니다", "删除失败:{error}": "삭제 실패: {error}", "删除定时条目": "예약 항목 삭제", @@ -62,22 +83,27 @@ "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "정해진 시각이 되면 선택한 제공자의 사용 가능한 계정으로 자동 출석합니다 (WorkBuddy 중국판은 매일 출석 체크 인터페이스, 국제판은 일일 활성 작업으로 진행합니다: 이벤트 탐지, 일일 활성 보상 수령, 무료 모델로 키핑; 小浣熊은 데스크톱 앱 일일 포인트 경로를 사용하고; AutoClaw는 공식 클라이언트의 매일 출석 작업을 사용합니다; Qoder 중국판은 이벤트 수령으로 진행하며, 이벤트가 배정되지 않은 계정은 중립적인 안내를 받습니다; Qoder 국제판은 기기 리스크 관리 신원(로컬 Qoder 클라이언트 / qodercli의 UMID 컴포넌트로 생성)을 먼저 갖춰야 「매일 100 Credits」 이벤트가 보이며, 컴포넌트가 없으면 오류 대신 명확한 설명이 표시됩니다; Trae는 SOLO의 매일 출석 포인트를 수령하며(자연일 0시 기준 초기화), 업스트림이 해당 계정에 이벤트를 열지 않았거나 자격 증명에 기기 번호가 없으면 오류 대신 「미수령 + 구체적 사유」가 표시됩니다; Loomy는 매일 계정당 최초 로그인 포인트를 한 번 적립합니다; KukuAI는 「무료 포인트 받기」 일일 작업을 수령합니다; CodeArts는 당일 운영 혜택을 수령합니다(수령 → 확인 → 재조회 검증, 자동 실행은 하루 6회 / 간격 10분의 쓰기 요청 제한이 적용됩니다); OfficeAce 는 그날의 보상을 한 번의 동작으로 일괄 수령합니다(매일 출석 + 신규 혜택)). 시각을 놓치면 부팅 후 자동으로 보충 출석하며, 당시 컴퓨터가 꺼져 있었다고 해서 빠지지 않습니다. 각 사의 출석 인터페이스는 멱등이므로 반복 실행해도 중복 수령되지 않습니다.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "정해진 시각이 되면 선택한 제공자의 사용 가능한 계정으로 자동 출석합니다 (WorkBuddy 중국판은 매일 출석 체크 인터페이스, 국제판은 일일 활성 작업으로 진행합니다: 이벤트 탐지, 일일 활성 보상 수령, 무료 모델로 키핑; 小浣熊은 데스크톱 앱 일일 포인트 경로를 사용하고; AutoClaw는 공식 클라이언트의 매일 출석 작업을 사용합니다; Qoder 중국판은 이벤트 수령으로 진행하며, 이벤트가 배정되지 않은 계정은 중립적인 안내를 받습니다; Qoder 국제판은 기기 리스크 관리 신원(로컬 Qoder 클라이언트 / qodercli의 UMID 컴포넌트로 생성)을 먼저 갖춰야 「매일 100 Credits」 이벤트가 보이며, 컴포넌트가 없으면 오류 대신 명확한 설명이 표시됩니다; Trae는 SOLO의 매일 출석 포인트를 수령하며(자연일 0시 기준 초기화), 업스트림이 해당 계정에 이벤트를 열지 않았거나 자격 증명에 기기 번호가 없으면 오류 대신 「미수령 + 구체적 사유」가 표시됩니다; Loomy는 매일 계정당 최초 로그인 포인트를 한 번 적립합니다; KukuAI는 「무료 포인트 받기」 일일 작업을 수령합니다; OfficeAce 는 그날의 보상을 한 번의 동작으로 일괄 수령합니다(매일 출석 + 신규 혜택)). 시각을 놓치면 부팅 후 자동으로 보충 출석하며, 당시 컴퓨터가 꺼져 있었다고 해서 빠지지 않습니다. 각 사의 출석 인터페이스는 멱등이므로 반복 실행해도 중복 수령되지 않습니다.", "到点时逐个签到,已勾 {n} / {total} 家": "정해진 시각에 하나씩 출석합니다. 선택 {n} / {total}개", + "北京时间 · 逗号分隔 · 最多 6 条 · 到点后最多晚 30 秒执行;错过时点开机后会补跑一轮。": "베이징 시간 · 쉼표 구분 · 최대 6개 · 정시에서 최대 30초 늦게 실행. 시각을 놓치면 부팅 후 한 번 따라 실행합니다.", "北京时间 · 错过时点开机后会自动补签": "베이징 시간 · 시간을 놓쳐도 시작 후 자동으로 보완됩니다", "去领取": "수령하러 가기", "另有 {n} 个账号不参与每日签到": "그 외 {n}개 계정은 매일 출석 체크에 참여하지 않습니다", + "只读余额(已停运)": "", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "완료된 작업의 보상만 수령(멱등, 반복 클릭해도 중복 적립되지 않음)", "可完成": "완료 가능", "可派出": "출발 가능", "可选 · 缺省显示时刻": "선택 · 기본값은 시간 표시", + "可领": "수령 가능", "可领奖": "수령 가능", "后端未返回定时签到数据": "백엔드가 예약 데이터를 반환하지 않았습니다", "后端未返回签到中心数据": "백엔드가 출석 체크 센터 데이터를 반환하지 않았습니다", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "국제판 출석에는 기기 리스크 관리 신원을 생성할 UMID 컴포넌트가 필요합니다 (이 기기에서 감지되지 않음).", + "在桌面端设置": "데스크톱에서 설정", "在途": "여행 중", "填入缺省链": "기본 체인 채우기", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "시각과 출석 범위를 정한 뒤 「저장」을 누르세요. 이름은 비워도 됩니다(비우면 시각이 표시됩니다)", "失败 {n}": "실패 {n}", "套餐明细": "플랜 상세", + "套餐领取": "플랜 수령", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Qoder 클라이언트 또는 qodercli를 설치하면 됩니다 (게이트웨이 재시작 불필요).", "安装中…": "설치 중…", "官方客户端的每日签到任务": "공식 클라이언트의 매일 출석 작업", @@ -86,30 +112,49 @@ "尚未查询": "아직 조회하지 않음", "尚未查询任务状态": "작업 상태를 아직 조회하지 않았습니다", "巡检一次": "한 번 점검", + "巡检中…": "순회 중…", "已保存:{label}": "저장됨: {label}", "已保存:保活模型链": "저장됨: 키핑 모델 체인", "已保存:定时条目": "저장됨: 예약 항목", + "已保存:成长任务排程": "", + "已保存:猫猫旅行排程": "저장됨: 고양이 여행 스케줄", + "已停运": "", + "已全部领取": "모두 수령 완료", "已删除定时条目": "예약 항목이 삭제되었습니다", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "활동 보고 완료(연속 출석 점등), but 캠페인이 진행 중이 아닙니다(이번에 받을 보상 없음)", + "已开始批量签到": "일괄 출석을 시작했습니다", + "已得 {earned} / {total} 积分": "{earned} / {total} 포인트 획득", "已抽 {n} 次": "{n}회 추첨함", + "已提交「{label}」的签到": "「{label}」 출석을 제출했습니다", "已有结果 {checked} / 共 {total} 个账号": "결과 있음 {checked} / 전체 {total}개 계정", "已签": "출석 완료", + "已领": "", "已领 {claimed}/{total} · 累计 {earned} 积分": "수령 {claimed}/{total} · 누적 {earned} 포인트", "已领养": "입양 완료", "已领取": "수령 완료", + "已领取「{name}」": "「{name}」을(를) 수령했습니다", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "기기 리스크 관리 신원을 갖춰 「매일 100 Credits」 수령 · UMID 컴포넌트 필요", + "开关": "스위치", "开学季执行失败:{error}": "개학 시즌 실행 실패: {error}", "开学季执行完成:{message}": "개학 시즌 실행 완료: {message}", "开学季活动": "개학 시즌 이벤트", "开着:启用的条目按各自时刻执行": "켜짐: 활성화된 항목이 각자의 시간에 실행됩니다", + "开着:按下面的时刻自动巡检": "켜짐: 아래 시각에 자동 순회합니다", + "开着:按下面的时刻自动执行": "", + "当前没有可领取的套餐": "지금 수령할 수 있는 플랜이 없습니다", "待签": "출석 대기", + "待领取": "수령 대기", "待领新手福利": "수령 대기 입문 혜택", "总开关": "메인 스위치", "总开关已关闭 · 在「定时签到」页打开": "메인 스위치가 꺼져 있습니다 · \"예약 출석\" 탭에서 켜세요", "成功 {n}": "성공 {n}", "成长任务": "성장 과제", + "成长任务失败:{error}": "", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "성장 과제 완료(성공 {done}개, 실패 {failed}개): 크레딧 +{credit} 에너지 +{energy}", + "成长任务定时排程": "", "成长任务执行失败:{error}": "성장 과제 실행 실패: {error}", + "成长任务:{done} 项完成、{failed} 项失败,+{credit} 积分 +{energy} 能量": "", + "成长任务:{done} 项完成,+{credit} 积分 +{energy} 能量": "", "执行失败:{error}": "실행 실패: {error}", "执行并抽奖": "실행 및 추첨", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "개학 시즌 작업을 실행하고 추첨 기회를 모두 사용(공유/대화/전문가 이벤트)", @@ -125,14 +170,21 @@ "搜账号 / 收益 / 错误 / 触发原因": "계정 / 수익 / 오류 / 트리거 사유 검색", "收起新建": "새 항목 닫기", "新建定时条目": "새 예약 항목", + "新手任务": "입문 과제", "新手福利": "입문 혜택", + "新手福利与活动": "입문 혜택 및 이벤트", + "新手福利是一次性任务:领完就没了,重复点不会重复加分。": "입문 혜택은 일회성 과제입니다. 수령하면 끝나며, 반복해서 눌러도 포인트가 중복 지급되지 않습니다.", "新手福利有 {n} 项领取失败,可重试": "입문 혜택 {n}건의 수령에 실패했습니다. 재시도할 수 있습니다", "无": "없음", + "无独立签到接口 · 每天替账号打一次登录信息(即当日魔粒)": "독립 출석 인터페이스 없음 · 매일 계정당 로그인 정보를 한 번 조회(당일 마립 지급)", "无独立签到接口 · 每天替账号打一次首次登录积分": "독립 출석 인터페이스 없음 · 매일 계정당 최초 로그인 포인트를 한 번 적립", "无账号": "계정 없음", + "时刻": "시각", "暂不可领": "지금은 수령 불가", + "暂不可领(未满足条件)": "아직 수령할 수 없습니다 (조건 미충족)", "暂不支持": "아직 미지원", "暂无启用的条目 · 点此添加": "활성화된 항목이 없습니다 · 클릭하여 추가", + "最近到期 {date}": "", "有未保存的改动": "저장되지 않은 변경 사항이 있습니다", "未勾选提供商": "공급자 미선택", "未开启": "꺼짐", @@ -141,30 +193,45 @@ "未领取": "미수령", "本地台账里今天还没领过;活动期内每天发一份新套餐": "로컬 기록상 오늘은 아직 받지 않았습니다. 이벤트 기간에는 매일 새 플랜이 한 건씩 지급됩니다", "本地台账里今天(北京时间)领过的套餐:{plans}": "로컬 기록상 오늘(베이징 시간) 받은 플랜: {plans}", + "本站已停止发放积分,余额在原有效期内仍可用": "", "条启用": "개 활성화", "查询于 {time}": "조회 시각 {time}", "查询任务": "작업 조회", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "작업 트리를 조회하고 한 번에 점등/수령(개학 시즌과 고양이 여행 포함)", "查询失败:{error}": "조회 실패: {error}", + "查询无结果": "", "桌面端每日积分链路": "데스크톱 앱 일일 포인트 경로", "正在读取定时签到数据…": "예약 출석 데이터를 읽는 중…", "正在读取签到数据…": "출석 데이터를 읽는 중…", "每天 {time}": "매일 {time}", "每天 {time} · {scope}": "매일 {time} · {scope}", + "每天定点巡检 · 一趟推进一步 · 与签到排程互相独立": "매일 정시 순회 · 1회당 1단계 · 체크인 스케줄과 독립", + "每天定点执行成长任务 · 与签到 / 旅行互相独立 · 默认关": "", + "每天按这里的时刻自动巡检一遍 WorkBuddy 国内版账号:没有猫就领养、在家就派出、到站就领奖。一趟只推进一步,所以建议留两个时刻(早上派出、晚上领奖)。企业版与国际版账号自动跳过。": "여기 지정한 시각에 WorkBuddy 중국판 계정을 자동 순회합니다: 고양이가 없으면 입양, 대기 중이면 출발, 도착했으면 보상을 수령합니다. 1회당 1단계만 진행되므로 두 개의 시각(아침 출발, 저녁 수령)을 권장합니다. 엔터프라이즈 및 국제판 계정은 자동으로 건너뜁니다.", + "每天按这里的时刻自动执行 WorkBuddy 国内版账号的成长任务:逐个点亮、完成后领奖。夜猫子任务只有 23:00-08:00(北京时间)能点亮,所以缺省时刻是 23:30。默认关闭 —— 它会上报事件并领积分,请确认要自动执行再打开。企业版与国际版账号自动跳过,不可伪造的任务(如真实捐款)也跳过。": "", "每天时刻": "매일 시각", + "每天自动执行": "매일 자동 실행", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "출석 실행마다 · 계정별 한 행 · 결과와 수익은 백엔드 기록 기준", + "每日活动签到(+100 积分,已领则中性提示)": "일일 활동 체크인(+100 크레딧, 이미 받았으면 중립 안내)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "매일 활성 작업 · 웹 채널 세션으로 일일 활성 달성, 포인트는 수령 시 적립 · 두 가지는 따로 실행 가능", + "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "매일 로그인 포인트 · 회원 활동 함께 수령 · 게스트 계정은 포인트 체계 없음(건너뜀)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "일일 혜택 · 수령 → 확인 → 재조회 검증 · 베이징 시간 자연일 기준 초기화", "每日签到": "매일 출석 체크", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "항목별 시간과 범위 · 항목별 당일 중복 제거 · 시작 시 자동 보완", "每账号一条明细 · 点此查看「签到记录」": "계정별 한 행 · \"출석 기록\" 열기", + "没有可签到的账号": "출석할 수 있는 계정이 없습니다", "没有可领套餐的账号(ZCode 未添加)。": "수령할 플랜이 있는 계정이 없습니다 (ZCode 미추가).", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "혜택을 수령할 수 있는 계정이 없습니다 (CodeArts / ZCode 미추가).", + "没有已停运但仍可查余额的账号(目前有 QClaw 一家)。": "", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "성장 과제를 지원하는 계정이 없습니다(WorkBuddy 중국판 필요).", "没有支持新手福利的账号": "입문 혜택을 지원하는 계정이 없습니다", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "입문 혜택을 지원하는 계정이 없습니다 — 현재 Loomy, 小浣熊, CodeArts 세 곳입니다.", "没有符合筛选条件的签到记录": "필터 조건에 맞는 출석 기록이 없습니다", + "没有随登录自动发放积分的账号(目前有 QClaw 一家)。": "", "没有需要执行的任务": "실행할 작업이 없습니다", + "活动奖励(30 天 / 7 天有效期)+ 下载客户端奖励 · 两条链都幂等": "", + "活动尚未开始(上游接口未部署)": "이벤트가 아직 시작되지 않았습니다 (업스트림 엔드포인트 미배포)", + "活动按自然日发新套餐,每天可领一次": "이벤트는 날짜마다 새 플랜을 지급하며, 하루에 한 번 수령할 수 있습니다", "活动福利": "이벤트 혜택", "活动(campaign)领取 · 每天 10:00 刷新": "캠페인(campaign) 수령 · 매일 10:00 새로고침", "活跃上报与保活均未成功,且活动未开启": "활동 보고와 keep-alive 모두 실패했고 캠페인도 진행 중이 아닙니다", @@ -183,17 +250,26 @@ "清空签到记录": "출석 기록 비우기", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "\"플랜 상세\"에서 어떤 플랜을 받았고 언제 적용되는지 확인하세요. 이벤트 기간에는 매일 다시 받을 수 있습니다", "点击行展开账号明细 · 单账号签到不受范围限制": "행을 클릭해 계정 상세 펼치기 · 개별 계정 출석은 범위 제한을 받지 않습니다", + "点进去查看任务并领取": "눌러서 과제를 확인하고 수령하세요", "独立链路 · 只提供手动入口 · 不进自动签到": "독립 경로 · 수동 진입점만 제공 · 자동 출석에는 포함되지 않음", "猫猫旅行": "고양이 여행", "猫猫旅行失败:{error}": "고양이 여행 실패: {error}", "猫猫旅行失败:{message}": "고양이 여행 실패: {message}", + "猫猫旅行定时排程": "고양이 여행 스케줄", "猫猫旅行:{message}": "고양이 여행: {message}", + "猫猫旅行:{n} 个动作,+{credit} 积分": "고양이 여행: {n}개 동작, +{credit} 크레딧", + "猫猫旅行:{n} 个动作,+{credit} 积分,{failed} 个失败": "고양이 여행: {n}개 동작, +{credit} 크레딧, {failed}개 실패", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "무료 모델과 웹 채널 세션으로 계정 활성 유지 · 보상은 수령하지 않음", "确定删除「{name}」?删除后到点不再执行。": "\"{name}\"을(를) 삭제할까요? 삭제 후 예약 시간에 실행되지 않습니다.", "确定清空全部签到记录?清空后无法恢复。": "모든 출석 기록을 비울까요? 비운 후에는 복구할 수 없습니다.", "确定清空当前筛选出的签到记录?清空后无法恢复。": "현재 필터된 출석 기록을 비울까요? 비운 후에는 복구할 수 없습니다.", + "福利与套餐的逐项领取在桌面端签到中心完成": "", + "积分随登录到账 · 无需签到 · 只读:余额与到期时间由上游直接给出": "", + "积分随登录自动发放,无需签到": "", "移除 {name}": "{name} 제거", "立即全部签到": "지금 전체 출석", + "立即巡检全部": "지금 전체 순회", + "立即执行全部": "", "签到": "출석", "签到中…": "출석 중…", "签到中心分区": "출석 센터 구역", @@ -210,6 +286,7 @@ "结算于 {time}": "결산 시각 {time}", "能量 {n}": "에너지 {n}", "腾讯每日签到接口 · 仅国内版": "텐센트 매일 출석 인터페이스 · 중국판 전용", + "自动发放": "", "至少勾一家才能保存(不勾等于这条条目不签到)": "저장하려면 최소 한 곳을 선택하세요(선택하지 않으면 이 항목은 아무것도 출석하지 않습니다)", "读取定时签到数据失败:{error}": "예약 출석 데이터 로드 실패: {error}", "读取签到数据失败:{error}": "출석 데이터 읽기 실패: {error}", @@ -219,6 +296,9 @@ "还没执行过": "아직 실행된 적 없음", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "예약 항목이 아직 없습니다 — \"항목 추가\"로 만들거나 \"출석 센터\"에서 수동으로 출석하세요.", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "출석 기록이 아직 없습니다 — 수동 출석 또는 예약 실행 후 여기에 상세 내역이 표시됩니다.", + "还没有记录": "기록이 없습니다", + "这个账号没有新手任务": "이 계정에는 입문 과제가 없습니다", + "这些提供商已停止发放积分 · 余额在原有效期内仍可用 · 只读:余额与到期时间由上游直接给出": "", "连登 {n} 天": "연속 로그인 {n}일", "部分": "일부", "重签": "재출석", @@ -231,8 +311,11 @@ "领取": "수령", "领取中": "수령 중", "领取中…": "수령 중…", + "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "무료 CodingPlan 받기(필요 시에만: 플랜이 유효하면 할당량만 조회)", "领取失败": "수령 실패", "领取失败:{error}": "수령 실패: {error}", + "领取每日积分 · 幂等(当天已领回 0,不报错)": "", + "领取通常需要过一次滑块验证;活动套餐多为当天稍晚生效,领取后可在桌面端「账号」里看套餐明细。": "수령하려면 보통 슬라이더 인증을 통과해야 합니다. 이벤트 플랜은 대개 당일 늦게 적용되며, 수령 후 데스크톱 「계정」에서 플랜 상세를 확인할 수 있습니다.", "(共 {n} 个账号)": "(전체 {n}개 계정)", "(能量 / 连登读数本次未取到)": "(에너지/연속 로그인 수치는 이번에 가져오지 못했습니다)", ",日活保活 {n} 个": ", 일일 활성 키핑 {n}개" diff --git a/desktop-tauri/i18n/i18n-src/ko/common.json b/desktop-tauri/i18n/i18n-src/ko/common.json index b43901f5..418f28dc 100644 --- a/desktop-tauri/i18n/i18n-src/ko/common.json +++ b/desktop-tauri/i18n/i18n-src/ko/common.json @@ -1,7 +1,12 @@ { "无可用账号:{reason}": "사용 가능한 계정 없음: {reason}", "AI 助手": "AI 어시스턴트", + "Agent": "", + "Agent 接管": "", "Agent2API · 多提供商本地网关": "Agent2API · 다중 제공자 로컬 게이트웨이", + "QQ 群号": "QQ 그룹 번호", + "Webhook 地址(https://…)": "", + "{base} / {cat}": "", "{key} 需要 true / false / null": "{key}은(는) true / false / null 중 하나여야 합니다", "{key} 需要数字": "{key}은(는) 숫자여야 합니다", "{n} 个启用": "{n}개 활성", @@ -12,39 +17,76 @@ "{n} 个账号启用": "{n}개 계정 활성", "{n} 条失败请求未读,点开「请求日志」查看": "읽지 않은 실패 요청 {n}건, 「요청 로그」에서 확인하세요", "{n} 条错误日志未读,点开「日志」查看": "읽지 않은 오류 로그 {n}건, 「로그」에서 확인하세요", + "{n} 项 · {shown} 显示": "{n}개 · {shown}개 표시", + "{size} · 下载后可直接安装,无需跳转浏览器": "{size} · 다운로드 후 바로 설치할 수 있습니다(브라우저로 이동하지 않음)", + "{size} · 点击下载后可直接安装,无需跳转浏览器": "{size} · 다운로드 후 바로 설치할 수 있습니다(브라우저로 이동하지 않음)", + "{total} 个菜单 · 显示 {shown} · 隐藏 {hidden} · {groups} 个分组": "메뉴 {total}개 · 표시 {shown} · 숨김 {hidden} · 그룹 {groups}개", "· 不会改动任何配置": "· 설정을 변경하지 않습니다", "· 每次执行前列改动清单,等你确认": "· 실행 전에 변경 목록을 보여주고 확인을 기다립니다", "—": "—", "★ 下一单走它": "", "✅ Token 已刷新": "✅ Token을 갱신했습니다", "✅ 已将账号优先级调整到全局第一位": "✅ 계정 우선순위를 전역 1위로 조정했습니다", + "✅ 测试消息已发出,请查看对应群": "", + "✅ 通知设置已保存": "", "「{name}」匹配到多个账号:{list},请用完整账号名重试": "\"{name}\"이(가) 여러 계정과 일치합니다: {list}. 전체 계정 이름으로 다시 시도하세요.", "「工具全不勾选 → 不注入系统提示词」:想当纯聊天用时,不必再去关上面的开关。": "\"도구를 모두 해제 → 시스템 프롬프트 미주입\": 순수 채팅으로 쓰려면 위 스위치를 끌 필요가 없습니다.", + "「设置」是固定项,含它的分组不能整组隐藏;可以单独隐藏组内其它菜单": "\"설정\"은 고정 항목이라 그 그룹을 통째로 숨길 수 없습니다. 다른 메뉴는 개별로 숨길 수 있습니다", + "一个都不勾 = 全部订阅(配好渠道就能收到东西)": "", + "下拉刷新": "당겨서 새로고침", "不限模型": "", + "与电脑端一致:每 {n} 分钟": "데스크톱과 동일: {n}분마다", + "主导航": "기본 탐색", "代价": "비용", "代理:池": "", + "会恢复默认的分组、顺序与显隐设置。你自建的分组会被删除(组内菜单不会丢)。": "그룹·순서·표시 설정을 기본값으로 되돌립니다. 직접 만든 그룹은 삭제됩니다(메뉴는 유지됩니다).", "会话内": "세션 내", + "会话已过期。重新登录后会自动回到手机端。": "세션이 만료되었습니다. 다시 로그인하면 모바일 화면으로 자동 복귀합니다.", "会话粘性": "세션 고정", "低于限制器的阈值,转发时跳过该账号": "", "余额不足或 Token 周期用量到顶,被限制器拦下,自动恢复后回来": "", "余额优先": "", "作为 system 消息注入给助手": "system 메시지로 어시스턴트에 주입", + "例如:家里、公司": "예: 집, 회사", + "侧边栏布局模块未加载(ui/sidebar-layout.js 应在 app.js 之前引入)。": "사이드바 레이아웃 모듈이 로드되지 않았습니다(ui/sidebar-layout.js는 app.js보다 먼저 포함되어야 합니다).", + "侧边栏没有显示任何菜单。到「设置 → 显示 → 侧边栏菜单」里打开需要的项。": "사이드바에 표시되는 메뉴가 없습니다. 설정 → 표시 → 사이드바 메뉴에서 필요한 항목을 켜세요.", + "侧边栏菜单": "사이드바 메뉴", "保存对话模型失败:{message}": "대화 모델 저장 실패: {message}", + "保存并继续": "저장하고 계속", "候选队列": "", "停止": "중지", + "先添加一台网关": "먼저 게이트웨이를 추가하세요", "全不选": "모두 선택 해제", "全局队列位次(转发按策略在此序上再排)": "", + "全局(账号表)": "", + "共 {n} 条记录": "총 {n}건", + "共 {total} 个账号,已读到 {queried} 个": "", "关": "끄기", + "关掉时不发任何消息(配置保留)。默认关 —— 没配好就发是打扰。": "", "冷却": "", + "出事了主动推给你(桌面端常驻托盘,不必自己盯着看)": "", + "分组名": "그룹 이름", + "分组名,例如「常用」": "그룹 이름, 예: \"즐겨찾기\"", "列表 = 模型管理页里所有已启用的模型。助手走本地网关发起对话,消耗所选账号的额度。": "목록은 \"모델 관리\" 페이지에서 활성화된 모든 모델입니다. 어시스턴트는 로컬 게이트웨이를 통해 대화하며 선택한 계정의 할당량을 소모합니다.", + "创建并进入": "생성 후 시작", + "创建管理员账号": "관리자 계정 만들기", "删除 {n} 条": "{n}건 삭제", + "删除分组": "그룹 삭제", + "删除分组「{name}」?组内 {n} 个菜单会移到「{target}」,不会丢。": "그룹 \"{name}\"을(를) 삭제할까요? 메뉴 {n}개는 \"{target}\"(으)로 이동하며 사라지지 않습니다.", + "删除空分组「{name}」?": "빈 그룹 \"{name}\"을(를) 삭제할까요?", + "删除这个分组(组内菜单移到第一组)": "이 그룹 삭제(메뉴는 첫 그룹으로 이동)", "加载助手设置失败:{message}": "어시스턴트 설정 불러오기 실패: {message}", "加载失败:{error}": "불러오기 실패: {error}", + "加载更早的记录": "이전 기록 불러오기", "助手": "어시스턴트", "助手自己用哪个模型生成回复": "어시스턴트가 응답 생성에 사용할 모델", "助手设置读取失败,工具可能已过时。": "어시스턴트 설정 읽기에 실패했습니다. 도구 목록이 오래되었을 수 있습니다.", "助手走本地网关,消耗所选账号的额度": "어시스턴트는 로컬 게이트웨이를 거치며 선택한 계정의 할당량을 소모합니다", "勾选即注入该工具;不勾选 = 助手看不到它": "체크하면 해당 도구가 주입됩니다. 체크 해제 시 어시스턴트가 볼 수 없습니다", + "升序": "", + "单签": "개별", + "去下载": "다운로드", + "去登录": "로그인", "参数不完整(需要 account / enabled)": "매개변수가 불완전합니다 (account / enabled 필요)", "参数不完整(需要 account / position)": "매개변수가 불완전합니다 (account / position 필요)", "参数不完整(需要 provider / id / alias)": "매개변수가 불완전합니다 (provider / id / alias 필요)", @@ -52,21 +94,34 @@ "参数不完整(需要 provider / id / enabled)": "매개변수가 불완전합니다 (provider / id / enabled 필요)", "参数不完整(需要 provider / position)": "매개변수가 불완전합니다 (provider / position 필요)", "发现新版本 {latest}(当前 {current}),点开「设置 - 软件更新」可查看更新日志并下载": "새 버전 {latest} 발견 (현재 {current}), 「설정 - 소프트웨어 업데이트」에서 업데이트 노트를 보고 다운로드할 수 있습니다", + "发现新版本 {version}": "새 버전 {version}이(가) 있습니다", "发送": "전송", + "发送测试": "", "只改映射名,不动上游模型 ID;映射立即生效(新对话即可用)。": "매핑 이름만 바뀌고 업스트림 모델 ID는 그대로입니다. 매핑은 즉시 적용됩니다(새 대화에서 바로 사용 가능).", "只注入工具定义": "도구 정의만 주입", "只读": "읽기 전용", "只读工具": "읽기 전용 도구", "可用": "개 사용 가능", + "含固定项「设置」,不可整组隐藏(可以单独隐藏组内其它菜单)": "고정 항목 \"설정\"이 있어 그룹 전체를 숨길 수 없습니다(다른 메뉴는 개별로 숨길 수 있습니다)", "启用后重新参与转发": "", + "启用通知": "", "四种组合的实际效果": "네 가지 조합의 실제 효과", "回复由 {model} 生成 · 走本地网关": "응답 생성: {model} · 로컬 게이트웨이 경유", + "固定": "고정", "在全局队列里上移一位": "", "在全局队列里下移一位": "", + "地址已遮住 token(保存时不动它);要更换请整体重新粘贴": "", + "备注名(可留空)": "", + "备注名(可选)": "표시 이름 (선택)", "复制失败,请手动选择复制": "복사에 실패했습니다. 직접 선택해 복사하세요", "复制订阅链接": "구독 링크 복사", "失败 · {status}": "실패 · {status}", "失败退避": "실패 백오프", + "奖励 {bonus}": "보너스 {bonus}", + "套餐 {plan}": "플랜 {plan}", + "安装": "설치", + "安装包已就绪,点「安装」即可完成更新": "설치 패키지가 준비되었습니다. 「설치」를 누르면 업데이트가 완료됩니다", + "定时": "예약", "定时任务": "예약 작업", "实际注入": "실제 주입 내용", "对话模型": "대화 모델", @@ -80,118 +135,203 @@ "将把账号「{account}」移到第 {position} 顺位:": "계정 \"{account}\"을(를) {position}번째 순위로 이동합니다:", "将改动 {n} 条映射({parts}):": "매핑 {n}건을 변경합니다 ({parts}):", "尚未登录": "아직 로그인하지 않음", - "已保存": "저장됨", + "展开作用域栏": "", + "展开左栏,看 {label} 下的 {n} 个模型": "", + "展开来自{label}的 {n} 个模型": "", + "已下载,待安装": "다운로드 완료, 설치 대기 중", "已修改 {n} 个模型": "모델 {n}개를 변경했습니다", "已修改 {n} 个账号": "계정 {n}개를 변경했습니다", "已修改 {n} 项能力": "기능 {n}개를 변경했습니다", "已停止这次回答。": "이 답변을 중지했습니다.", "已全部取消勾选": "모두 선택 해제했습니다", + "已关闭自动检查": "자동 확인을 껐습니다", + "已删除分组「{name}」": "그룹 \"{name}\"을(를) 삭제했습니다", "已勾选 {n} / {total}": "{n} / {total} 선택됨", "已勾选「{label}」": "\"{label}\" 선택됨", "已勾选工具": "도구 선택됨", "已取消「{label}」": "\"{label}\" 선택 해제됨", + "已启动安装器": "설치 프로그램을 실행했습니다", + "已复制{label}": "", "已复制:{text}": "복사됨: {text}", + "已完成 {percent}%": "", + "已开启自动检查": "자동 확인을 켰습니다", "已恢复推荐勾选": "권장 선택으로 복원했습니다", + "已恢复默认分组:概览 / 转发链路 / 诊断 / 系统": "기본 그룹으로 복원했습니다: 개요 / 전달 경로 / 진단 / 시스템", "已改动 {n} 条映射": "매핑 {n}건을 변경했습니다", + "已新建分组「{name}」,点组头的 + 把菜单移进来": "그룹 \"{name}\"을(를) 만들었습니다. 머리의 +로 메뉴를 가져오세요", + "已是最新版本": "최신 버전입니다", "已注入 {n} / {total} 个工具 · 系统提示词:{state}": "도구 {n} / {total}개 주입 · 시스템 프롬프트: {state}", + "已添加 {n} 台": "", + "已添加的服务器": "추가된 서버", "已登录": "로그인됨", + "已离线 · 显示的是缓存内容": "오프라인 — 캐시된 내용을 표시 중", "已移到第 {n} 顺位": "{n}번째 순위로 이동했습니다", "已试过": "", "并发上限 {n}": "", "并发已满,等一个在途回落再参与": "", "并发满": "", "开": "켜기", + "当前": "현재", "当前一个工具都没勾选,助手只能纯聊天。去「设置 → AI 助手 → 工具」里勾上需要的工具。": "선택된 도구가 없어 어시스턴트는 순수 채팅만 가능합니다. \"설정 → AI 어시스턴트 → 도구\"에서 필요한 도구를 선택하세요.", + "当前版本 {version}": "현재 버전 {version}", + "当前环境不支持读取调度队列": "", "当前:已关闭(助手不收到任何系统提示词,工具定义照发)": "현재: 꺼짐 (어시스턴트는 시스템 프롬프트를 받지 않지만 도구 정의는 전송됩니다)", "当前:已开启 · 使用内置默认提示词(还没自定义)": "현재: 켜짐 · 내장 기본 프롬프트 사용 중 (아직 사용자 지정 안 함)", "当前:已开启 · 自定义内容 {n} 字": "현재: 켜짐 · 사용자 지정 내용 {n}자", "影响转发": "포워딩 영향", "待确认": "확인 대기", + "总剩余 {total}": "총 잔여 {total}", "恢复上游原值": "업스트림 원래 값으로 복원", "恢复推荐": "권장 복원", + "恢复默认侧边栏": "사이드바 기본값 복원", + "成功领取": "수령 완료", "我会在动手前把改动列给你确认。": "변경 사항은 실행 전에 목록으로 보여드리고 확인을 받습니다.", + "所有菜单都已在这一组": "모든 메뉴가 이미 이 그룹에 있습니다", + "手机端打开": "", "打开账号设置": "", "执行": "실행", + "把菜单移入 {name}": "메뉴를 {name}(으)로 이동", + "把菜单移入「{name}」": "메뉴를 \"{name}\"(으)로 이동", "报表": "리포트", + "拖动分组 {name}": "그룹 {name} 끌기", + "拖动左侧 ⠿ 调整顺序(菜单可以跨组拖);拨开关控制显隐;点分组名可重命名;组头的 + 把别的菜单移进来;眼睛按钮隐藏整组。改动即时生效并记住,左侧侧边栏就是结果。键盘也能排序:把手聚焦后按 Alt+↑ / ↓。": "왼쪽 ⠿를 끌어 순서를 바꾸세요(메뉴는 그룹 간 이동 가능). 스위치로 표시/숨김, 그룹 이름을 클릭해 이름 변경, 그룹 머리의 +로 다른 메뉴를 가져오기, 눈 버튼으로 그룹 전체 숨기기. 변경은 즉시 적용·저장되며 왼쪽 사이드바가 결과입니다. 키보드: 핸들에 포커스 후 Alt+↑ / ↓.", + "拖动菜单 {name}": "메뉴 {name} 끌기", + "拖动调整分组顺序(Alt+↑ / ↓ 也可)": "끌어서 그룹 순서 변경(Alt+↑ / ↓ 도 가능)", "拖动调整队列顺序": "", + "拖动调整顺序,可跨组(Alt+↑ / ↓ 也可)": "끌어서 순서 변경, 그룹 간 이동 가능(Alt+↑ / ↓ 도 가능)", + "按作用域选择:全局账号表,或某一个模型的调度视图": "", + "按厂商": "", "按模型展开队列": "", "按请求的模型展开 —— 与转发选路同一份排序,下一个请求会走 ★ 那位": "", + "按配置顺序逐个发送;一个失败不影响其它": "", "排第 1 的接走所有请求,直到它不能答(限流 / 故障 / 额度尽)才轮到下一位 —— 就是「从上往下」的现状行为。": "1 번째 계정이 응답하지 못하게 될 때(레이트 리밋 / 장애 / 잔여량 소진)까지 모든 요청을 처리하고 그다음 순서로 넘어갑니다 — 기존의 \"위에서 아래로\" 방식 그대로입니다.", + "接口文档": "", + "控制主侧边栏显示哪些菜单、按什么顺序、怎么分组。隐藏只影响导航显示,不会撤销页面访问权限;被隐藏的页面仍可通过地址直接打开。": "사이드바에 표시할 메뉴, 순서, 그룹을 설정합니다. 숨김은 탐색 표시에만 영향을 주며 접근 권한을 회수하지 않습니다. 숨긴 페이지도 주소로 직접 열 수 있습니다.", "提示词": "프롬프트", "提示词开关": "프롬프트 스위치", + "搜索模型或厂商": "", + "搜索模型或厂商…": "", + "收起作用域栏": "", + "收起来自{label}的 {n} 个模型": "", "改动立即生效;禁用后指向它的请求会落到下一个候选。": "변경은 즉시 적용됩니다. 비활성화하면 해당 모델로 가는 요청은 다음 후보로 넘어갑니다.", "改动类工具": "변경 도구", "改成": "변경 값", "文档": "문서", "新": "신규", - "新增": "추가", + "新分组名": "새 그룹 이름", "新增 {n} 条": "{n}건 추가", "新对话": "새 대화", + "新建分组": "그룹 추가", "无凭证": "", "日志": "로그", "映射名": "매핑 이름", + "显示 {name}": "{name} 표시", + "显示分组 {name}": "그룹 {name} 표시", + "显示这一组": "이 그룹 표시", "智能": "AI", + "更多": "더보기", + "最先作废的一份到期日": "", "最大化": "최대화", "最小化": "최소화", + "最早 {day} 到期": "", "最早 {time} 恢复": "", "有": "있음", + "有新版本,重启后生效": "새 버전이 있습니다. 재시작하면 적용됩니다", + "服务器": "서버", "未同步": "미동기화", "未就绪": "준비 안 됨", "未登录": "미로그인", "未知工具「{name}」:它在设置里可能刚被取消勾选,或这一版助手还不认识它。": "알 수 없는 도구 \"{name}\": 설정에서 방금 선택 해제되었거나, 이 버전의 어시스턴트가 아직 인식하지 못하는 도구일 수 있습니다.", + "未选择服务器": "선택된 서버 없음", "本周期消耗达到限制器上限,转发时跳过": "", "本地网关监听状态": "로컬 게이트웨이 리스닝 상태", + "松开刷新": "놓아서 새로고침", "查余额": "", "查看参数": "매개변수 보기", "查看结果": "결과 보기", "查询该账号的余额": "", "桌面桥不可用(本页需要在 Agent2API 客户端里打开)": "데스크톱 브리지를 사용할 수 없습니다 (이 페이지는 Agent2API 클라이언트에서 열어야 합니다)", + "模型清单为空:先去模型管理页点「获取模型」": "", "模型管理": "모델 관리", "次熔断": "", + "正在下载…": "다운로드 중…", + "正在完成人机验证…": "인증을 완료하는 중…", + "正在检查…": "확인 중…", "正在识别二维码…": "QR 코드 인식 중…", + "正在连接…": "연결 중…", + "每台服务器的登录状态各自独立保存,切换后不用重新登录。地址改掉后需要重新登录。": "로그인 상태는 서버마다 따로 저장되므로 다시 전환해도 재로그인이 필요 없습니다. 주소를 바꾸면 다시 로그인해야 합니다.", "每条请求轮换到下一个可用账号;新会话交给最近最闲的那位。适合想把手里的号雨露均沾。": "", "每轮": "매 턴", "没有已启用的模型": "", + "没有带余额概念的账号。添加账号后,这里会显示各账号的额度余量与到期。": "", "没有找到提供商「{provider}」的账号": "제공자 \"{provider}\"의 계정을 찾지 못했습니다", "没有找到账号「{name}」": "계정 \"{name}\"을(를) 찾지 못했습니다", "没给账号名": "계정 이름이 지정되지 않았습니다", "注入系统提示词": "시스템 프롬프트 주입", + "活动": "이벤트", "浅色": "라이트", + "测试未全部成功,详见下方": "", "消耗该账号额度": "이 계정의 할당량을 소모합니다", "消耗额度": "할당량 소모", "深色": "다크", + "添加": "추가", + "添加服务器": "서버 추가", + "添加渠道": "", "清空 = 回到内置默认(与「恢复默认」等价)。": "비우면 내장 기본값으로 돌아갑니다 (\"기본값으로 복원\"과 동일).", + "渠道类型": "", "点「编辑」弹窗改内容;开关关掉就整段不发。": "\"편집\"을 눌러 대화상자에서 내용을 수정하세요. 스위치를 끄면 전체가 전송되지 않습니다.", + "点击重命名": "클릭하여 이름 변경", "熔断": "서킷 브레이커", "熔断时长(秒)": "", + "版本 {version} · 从 GitHub 直接下载安装包": "버전 {version} · GitHub에서 직접 다운로드합니다", + "版本 {version} · 从网关下载安装包": "버전 {version} · 게이트웨이에서 패키지를 다운로드합니다", + "版本 {version} · 更新包需从 Release 页面获取": "버전 {version} · 업데이트 패키지는 Release 페이지에서 받으세요", "用一句话让我查或改本机的模型转发配置。": "한 문장으로 요청하세요. 이 PC의 모델 포워딩 설정을 조회하거나 변경합니다.", + "用量与额度": "", "用量占比最低的先用;没有读数的账号按队列位次排在有读数的后面。适合「谁空谁上」。": "", + "电脑端已关闭「软件版本检查」,此处只做手动检查": "데스크톱에서 「소프트웨어 버전 확인」이 꺼져 있어 여기서는 수동 확인만 합니다", + "登录面板": "로그인", "监听 127.0.0.1": "127.0.0.1 리스닝 중", "直接说要改什么 / 查什么…(Enter 发送,Shift+Enter 换行)": "바꿀 것 / 확인할 것을 바로 입력하세요… (Enter 전송, Shift+Enter 줄바꿈)", + "看这个模型的调度队列:{tip}": "", "确定删除账号「{name}」?{note}": "계정「{name}」을(를) 삭제할까요? {note}", "确认后立即生效": "확인 후 즉시 적용됩니다", "禁用会把账号移出转发队列;若这条线路没有别的可用账号,请求会直接失败。": "비활성화하면 계정이 포워딩 큐에서 빠집니다. 이 경로에 다른 사용 가능한 계정이 없으면 요청이 그대로 실패합니다.", "禁用后不参与转发,余额仍可查": "", + "移入「{name}」": "\"{name}\"(으)로 이동", + "稍后": "나중에", + "空分组 · 点组头的 + 把菜单移进来": "빈 그룹 · 머리의 +로 메뉴를 가져오세요", + "等待窗口": "수령 시간 대기", "等这一轮跑完…": "이번 턴이 끝날 때까지 기다리는 중…", "签到中心": "출석 체크 센터", "系统提示词 + 工具定义": "시스템 프롬프트 + 도구 정의", "系统提示词已保存": "시스템 프롬프트가 저장되었습니다", "系统提示词已关闭": "시스템 프롬프트를 껐습니다", "系统提示词已开启": "시스템 프롬프트를 켰습니다", + "编辑服务器": "서버 편집", "编辑系统提示词": "시스템 프롬프트 편집", "网关 Key": "게이트웨이 키", "网关运行中": "게이트웨이 실행 중", "网络代理": "네트워크 프록시", "能力位决定下游看到的声明(能不能贴图、能塞多少上下文),不改上游的真实行为。": "기능 플래그는 하위에 보이는 선언(이미지 입력 가능 여부, 컨텍스트 크기)을 결정하며 업스트림의 실제 동작은 바꾸지 않습니다.", "自动": "자동", + "至少要保留一个分组": "그룹은 최소 하나 남아야 합니다", + "补签": "보충", + "订阅哪些事": "", "订阅里的节点会自动同步进下方代理池,账号即可引用。": "", "订阅里的节点会被内核起成本地监听器,镜像到下方代理池(账号按 id 引用,与本页其它出口同一用法)。": "", "订阅链接必须是 http:// 或 https:// 开头": "구독 링크는 http:// 또는 https:// 로 시작해야 합니다", "设置": "설정", + "设置不可隐藏(隐藏了只能清缓存找回来)": "\"설정\"은 숨길 수 없습니다(숨기면 저장소를 지워야 되돌릴 수 있습니다)", "诊断": "진단", + "请允许安装未知来源应用后重试": "알 수 없는 출처의 앱 설치를 허용한 후 다시 시도하세요", + "请在 Release 页面下载后通过 TestFlight 或 App Store 安装": "Release 페이지에서 내려받은 뒤 TestFlight 또는 App Store를 통해 설치하세요", + "请填一个分组名": "그룹 이름을 입력하세요", "请填写订阅名称": "구독 이름을 입력하세요", "请求失败:{message}": "요청 실패: {message}", "请求日志": "요청 로그", + "读不到完整模型清单:{reason}": "", + "读取通知设置失败:{reason}": "", "账号不在当前队列里": "계정이 현재 큐에 없습니다", "账号已删除": "계정을 삭제했습니다", "账号已在全局队列第一位": "계정이 이미 전역 큐 1위입니다", @@ -200,29 +340,54 @@ "转发失败的账号休息一会儿再回来(连续失败翻倍,上限 1 小时)": "", "转发能力": "전달 기능", "轮流均摊": "", + "输入这台网关的管理员账号与密码。": "이 게이트웨이의 관리자 계정과 비밀번호를 입력하세요.", + "运行日志": "", "运行正常 · {url}": "정상 작동 중 · {url}", "运行状态": "실행 상태", + "返回": "", "还有额度时被 429 的账号排到队尾,轮一圈才回来 —— 负载摊开而不是每次回到第 1 个": "", + "还没有可用模型(添加账号后可见)": "", + "还没有渠道。点右上角「添加渠道」,把群机器人的 Webhook 地址填进来。": "", + "还没有签到记录": "아직 체크인 기록이 없습니다", "还没有订阅:粘贴订阅链接,或从图片识别二维码导入。": "", "还没有选择对话模型:先在下方选一个(或去「设置 → AI 助手」里选)。": "대화 모델을 아직 선택하지 않았습니다: 아래에서 선택하세요 (\"설정 → AI 어시스턴트\"에서도 선택할 수 있습니다).", + "还没查到余额。到「账号」页点一次「查询余额」,或等自动查询跑完。": "", + "这一版没有提供 Android 安装包": "이 버전에는 Android 설치 패키지가 없습니다", + "这一项已隐藏": "이 항목은 숨겨져 있습니다", + "这一项正在显示": "이 항목은 표시 중입니다", "这个模型下没有可用账号": "", + "这个面板没有管理员,用一把已启用的 API Key 访问(在桌面端「网关 Key」页创建)。": "이 패널에는 관리자가 없습니다. 활성화된 API Key로 접속하세요 (데스크톱 「게이트웨이 키」에서 생성).", "这会真实发起上游请求并消耗额度": "실제 업스트림 요청을 보내고 할당량을 소모합니다", + "这台网关没有管理员,用一把已启用的 API Key 访问(在桌面端「网关 Key」页创建)。": "이 게이트웨이에는 관리자가 없습니다. 활성화된 API Key로 접근하세요 (데스크톱의 \"게이트웨이 Key\" 페이지에서 생성).", + "这台网关还没有管理员。设置一个账号密码,之后用它登录。": "이 게이트웨이에는 아직 관리자가 없습니다. 계정과 비밀번호를 설정한 뒤 그것으로 로그인하세요.", + "这家没有返回可用明细(可能额度都在上游侧,或全部已用尽)": "", "这是真实的上游请求,与生产走同一条链路(会记账,报表排除)。": "실제 업스트림 요청으로 프로덕션과 같은 경로를 사용합니다(과금되지만 보고서에서는 제외).", "这段文本作为 system 消息排在对话最前面。工具的定义与当前配置概览由网关自动附加,不用写在这里。": "이 텍스트는 system 메시지로 대화 맨 앞에 배치됩니다. 도구 정의와 현재 구성 개요는 게이트웨이가 자동으로 덧붙이므로 여기에 쓸 필요가 없습니다.", "连接 {n}": "", + "连接并保存": "연결 후 저장", "连续失败到阈值后熔断一段时间;到点放行试一次,连续成功才算恢复": "", "连续失败阈值": "", "连通 · {ms}ms": "연결됨 · {ms}ms", "选择对话模型…": "", "选择模型…": "모델 선택…", + "通知与告警": "", + "通知渠道": "", "都不注入": "둘 다 주입 안 함", "都不注入(助手退化为纯聊天)": "둘 다 주입 안 함 (어시스턴트가 순수 채팅으로)", + "重启更新": "재시작 후 업데이트", "队列是一次性整表提交(后端按新顺序重编优先级),移动会连带调整其它组的相对次序。": "큐는 전체 표를 한 번에 제출합니다(백엔드가 새 순서로 우선순위를 다시 매깁니다). 이동하면 다른 그룹의 상대 순서도 함께 바뀝니다.", "队列是一次性整表提交;位置越靠前,路由时越先被选中。": "큐는 전체 표를 한 번에 제출합니다. 위치가 앞일수록 라우팅 시 먼저 선택됩니다.", + "降序": "", "限流 · {n} 个模型": "", "限流垫底": "레이트 리밋 후순위", + "隐藏 {name}": "{name} 숨기기", + "隐藏分组 {name}": "그룹 {name} 숨기기", + "隐藏这一组": "이 그룹 숨기기", + "需要网关 API Key": "게이트웨이 API Key가 필요합니다", "顺序优先": "순서 우선", + "首页": "홈", "高危": "고위험", + "({n} 个整组隐藏)": " (그룹 {n}개 전체 숨김)", "(不会影响客户端登录态)": "(클라이언트 로그인 상태에는 영향을 주지 않습니다)", "(仅显示前 20 条,共 {n} 条)": "(앞의 20건만 표시, 전체 {n}건)", "(未指定)": "(지정 안 됨)", diff --git a/desktop-tauri/i18n/i18n-src/ko/docs.json b/desktop-tauri/i18n/i18n-src/ko/docs.json index b25f2d8a..b521b598 100644 --- a/desktop-tauri/i18n/i18n-src/ko/docs.json +++ b/desktop-tauri/i18n/i18n-src/ko/docs.json @@ -1,18 +1,28 @@ { " 或 ": " 또는 ", "Base URL 填进客户端的「API 地址 / Base URL」栏;对话协议按客户端支持的类型三选一(Chat Completions / Responses / Anthropic Messages),三者共用同一套模型与账号池,可随时切换;鉴权用「网关 Key」页里任一启用 Key。": "Base URL은 클라이언트의 「API 주소 / Base URL」란에 입력하고, 대화 프로토콜은 클라이언트가 지원하는 유형에 따라 세 가지(Chat Completions / Responses / Anthropic Messages) 중 하나를 고르면 됩니다. 세 가지 모두 동일한 모델과 계정 풀을 공유하므로 언제든 전환할 수 있습니다. 인증에는 「게이트웨이 키」 페이지의 활성화된 Key를 아무거나 사용하세요.", + "Claude Code / Anthropic SDK": "", "Claude Code / Anthropic SDK(token 计数:/v1/messages/count_tokens)": "Claude Code / Anthropic SDK(token 카운트: /v1/messages/count_tokens)", + "Codex、新版 SDK": "", "OpenAI 新协议(Codex、新版 SDK)": "OpenAI 신규 프로토콜(Codex, 최신 SDK)", + "curl 命令": "", "。一把启用的 Key 都没有时不鉴权(默认网关只监听 127.0.0.1;在设置页开启「局域网访问」后监听所有网卡,鉴权与安全闸门随之启用)。": "를 담아 보내면 됩니다. 활성화된 Key가 하나도 없으면 인증하지 않습니다(기본 게이트웨이는 127.0.0.1만 수신하며, 설정 페이지에서 「LAN 접근」을 켜면 모든 네트워크 인터페이스를 수신하고 인증과 보안 게이트도 함께 활성화됩니다).", - "启用": "활성화된", + "。用「网关 Key」页里任一启用的 Key;一把都没有时不鉴权。": "", + "不受鉴权限制,配 Key 前就能拉": "", + "启用": "사용", "填进客户端的「API 地址 / Base URL」": "클라이언트의 「API 주소 / Base URL」란에 입력", + "复制命令": "", "复制地址": "주소 복사", "多数 OpenAI 兼容客户端的默认协议": "대부분의 OpenAI 호환 클라이언트 기본 프로토콜", + "多数兼容客户端的默认协议": "", "客户端自动拉取可用模型;不受鉴权限制,配 Key 之前就能拉": "클라이언트가 사용 가능한 모델을 자동으로 가져옵니다. 인증 제한을 받지 않으므로 Key를 설정하기 전에도 가져올 수 있습니다", "对话协议(按客户端支持的类型选一行)": "대화 프로토콜(클라이언트가 지원하는 유형에 맞는 줄을 하나 선택)", + "快速验证": "", + "手机端只读文档。完整的使用说明与部署指引在仓库的 docs/ 目录。": "", "接口地址": "인터페이스 주소", "模型列表": "모델 목록", "模型清单": "모델 목록", "的 Key:请求带 ": " Key 중 아무거나 사용하세요: 요청에 ", + "请求带 ": "", "鉴权用「网关 Key」页里任一": "인증에는 「게이트웨이 키」 페이지에서 " } diff --git a/desktop-tauri/i18n/i18n-src/ko/keys.json b/desktop-tauri/i18n/i18n-src/ko/keys.json index e24f32fc..ac0e3dad 100644 --- a/desktop-tauri/i18n/i18n-src/ko/keys.json +++ b/desktop-tauri/i18n/i18n-src/ko/keys.json @@ -7,11 +7,13 @@ "✅ Key 已创建,记得复制给客户端": "✅ Key를 생성했습니다. 클라이언트에 복사해 두세요", "✅ 可用范围已保存": "✅ 이용 범위를 저장했습니다", "不限制": "제한 없음", + "不限制提供商与模型": "", "与": "및", "例如 Cursor / 公司电脑": "예: Cursor / 회사 PC", "保存失败:{reason}": "저장 실패: {reason}", "保存范围": "범위 저장", "创建": "생성", + "创建于 {time}": "", "创建时间": "생성 시간", "删除网关 Key": "게이트웨이 키 삭제", "可用提供商": "사용 가능한 제공자", @@ -22,13 +24,16 @@ "可用范围 · {name}": "이용 범위 · {name}", "启用「{name}」": "「{name}」 활성화", "复制 Key": "Key 복사", + "客户端请求带上 ": "", "客户端请求需带 ": "클라이언트 요청에는 ", + "已删除": "", "已启用鉴权 · {n} 把 Key 生效": "인증 활성 · Key {n}개 적용 중", "当前不限制:这把 Key 可以用全部提供商与全部对外模型": "현재 제한 없음: 이 Key는 모든 제공자와 모든 외부 공개 모델을 사용할 수 있습니다", "提供商:{names}": "제공자: {names}", "搜索模型…": "모델 검색…", "操作失败:{reason}": "작업 실패: {reason}", "新建 API Key": "새 API Key", + "新建 Key 与设置可用范围(提供商 / 模型白名单)在桌面端完成。": "", "显示": "표시", "未启用鉴权": "인증 비활성", "未命名": "이름 없음", @@ -38,9 +43,11 @@ "留空自动生成;手填至少 8 个字符": "비워 두면 자동 생성, 직접 입력 시 8자 이상", "留空表示不限制;同时设置时请求需同时满足两个条件(模型在白名单内且路由到允许的提供商)。 「可用模型」的候选跟着上面勾选的提供商走:没勾提供商时它是空的(还没有约束范围), 勾了几家就列出这几家能收的全部对外名。": "비워 두면 제한 없음. 둘 다 설정한 경우 요청은 두 조건을 모두 충족해야 합니다 (모델이 화이트리스트에 있고 허용된 제공자로 라우팅되어야 함). 「사용 가능한 모델」 후보는 위에서 선택한 제공자를 따릅니다: 제공자를 선택하지 않으면 비어 있고(아직 범위를 제약하지 않음), 몇 곳을 선택하면 그 제공자들이 처리할 수 있는 모든 외부 공개 이름을 나열합니다.", "确定删除 Key「{name}」?使用它的客户端会立刻无法访问。": "Key「{name}」을(를) 삭제할까요? 이 Key를 사용하는 클라이언트는 즉시 접근할 수 없게 됩니다.", + "确定删除「{name}」?用这把 Key 的客户端会立即失效。": "", "网关 Key 表": "게이트웨이 키 표", "请先在上面选择可用提供商": "먼저 위에서 사용 가능한 제공자를 선택하세요", "读取 Key 列表失败:{reason}": "Key 목록 불러오기 실패: {reason}", + "还没有 Key。去桌面端「网关 Key」页新建一把。": "", "还没有 Key,当前不鉴权": "Key가 없습니다. 현재 인증하지 않음", "这几家当前没有可用模型(账号未登录或清单为空)": "이 제공자들은 현재 사용 가능한 모델이 없습니다 (계정 미로그인 또는 목록이 비어 있음)", "这把 Key 不限制提供商与模型(可用全部上游与全部对外模型)": "이 Key는 제공자와 모델을 제한하지 않습니다 (모든 업스트림과 모든 외부 공개 모델 사용 가능)", @@ -49,5 +56,7 @@ "(模型候选需先选提供商;不选则沿用当前这几项,保存后仍按模型白名单生效)": "(모델 후보는 제공자를 먼저 선택해야 합니다. 선택하지 않으면 현재 항목을 유지하며, 저장 후에도 모델 화이트리스트대로 적용됩니다)", "(行内「可用范围」):留空 = 不限制,两个都设时 按交集生效 —— 被限制的模型对这把 Key 表现为「不存在」(拉 /v1/models 也看不到它), 提供它的家不在可用列表里时请求同样被拒。": "(행의 「이용 범위」): 비워 두면 제한 없음, 둘 다 설정하면 교집합으로 적용됩니다 — 제한된 모델은 이 Key에 대해 「존재하지 않는」 것으로 취급되며(/v1/models로 조회해도 보이지 않음), 이를 제공하는 제공자가 사용 가능 목록에 없으면 요청도 거부됩니다.", "+ 新建 Key": "+ 새 Key", - "; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制": "; 수정 후 즉시 적용되며, 이 프로그램 자체는 첫 번째 활성화된 Key를 자동으로 사용합니다. 각 Key는 개별적으로 제한할 수 있습니다" + "; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制": "; 수정 후 즉시 적용되며, 이 프로그램 자체는 첫 번째 활성화된 Key를 자동으로 사용합니다. 각 Key는 개별적으로 제한할 수 있습니다", + ";一把启用的 Key 都没有时不鉴权。": "", + ";当前必须带 Key。": "" } diff --git a/desktop-tauri/i18n/i18n-src/ko/logs.json b/desktop-tauri/i18n/i18n-src/ko/logs.json index 58bab043..96d738c8 100644 --- a/desktop-tauri/i18n/i18n-src/ko/logs.json +++ b/desktop-tauri/i18n/i18n-src/ko/logs.json @@ -10,9 +10,13 @@ "信息": "정보", "全部分类": "전체 분류", "全部级别": "전체 레벨", + "复制失败": "", + "复制当前筛选结果": "", "导出": "내보내기", + "已复制 {n} 条日志": "", "按分类筛选": "분류로 필터링", "按最低级别筛选": "최소 레벨로 필터링", + "搜索关键字": "", "搜索消息关键词": "메시지 키워드 검색", "搜索消息关键词…": "메시지 키워드 검색…", "操作失败:{error}": "작업 실패: {error}", @@ -20,6 +24,7 @@ "暂无日志可导出": "내보낼 로그가 없습니다", "模型 {model}": "모델 {model}", "正在加载日志…": "로그 불러오는 중…", + "没有符合条件的日志": "", "没有符合筛选条件的日志": "필터 조건에 맞는 로그가 없습니다", "清空中…": "비우는 중…", "清空运行日志": "실행 로그 비우기", @@ -27,6 +32,7 @@ "确定清空当前筛选出的 {count} 条日志?清空后无法恢复。": "현재 필터링된 {count}건의 로그를 비울까요? 비운 후에는 복구할 수 없습니다.", "系统事件": "시스템 이벤트", "警告": "경고", + "读取日志失败": "", "调试": "디버그", "运行日志已清空": "실행 로그를 비웠습니다", "错误": "오류" diff --git a/desktop-tauri/i18n/i18n-src/ko/models.json b/desktop-tauri/i18n/i18n-src/ko/models.json index 4baa0e9c..1630ea1a 100644 --- a/desktop-tauri/i18n/i18n-src/ko/models.json +++ b/desktop-tauri/i18n/i18n-src/ko/models.json @@ -36,6 +36,7 @@ "{name}({id})": "{name}({id})", "{name}(不在该家当前清单里)": "{name}(이 제공자의 현재 카탈로그에 없음)", "{n} 个": "{n}개", + "{n} 个映射": "", "{n} 个模型": "{n}개 모델", "{total} 家 · 成功 {done}": "{total}곳 · 성공 {done}", "{total} 家 · 成功 {done} · 失败 {failed}": "{total}곳 · 성공 {done} · 실패 {failed}", @@ -67,8 +68,10 @@ "上游没有给出可读的错误说明": "업스트림이 읽을 수 있는 오류 설명을 제공하지 않았습니다", "上游没有返回任何模型": "업스트림이 어떤 모델도 반환하지 않았습니다", "上游目录尚未拉到,用的是内置静态清单;点「刷新模型清单」可重试": "업스트림 디렉터리를 아직 가져오지 못해 내장 정적 카탈로그를 사용 중입니다; '모델 카탈로그 새로 고침'을 누르면 다시 시도할 수 있습니다", + "上游能列出这个模型,但官方客户端不展示它;本仓已默认关闭它的默认绑定(点这一行的开关可以手动打开)": "업스트림은 이 모델을 반환하지만 공식 클라이언트는 표시하지 않습니다. 게이트웨이가 기본 바인딩을 비활성화했습니다(행 스위치로 수동 활성화 가능)", "下游客户端": "다운스트림 클라이언트", "下游请求 ": "다운스트림 요청 ", + "不展示": "표시 안 함", "不影响网关的转发与路由": "게이트웨이의 전달과 라우팅에는 영향을 주지 않습니다", "不支持": "미지원", "不覆盖": "덮어쓰지 않음", @@ -115,6 +118,7 @@ "刷新全部家(逐家打上游)": "모든 제공자 새로 고침(제공자마다 업스트림 1회)", "刷新完成,但没有得到任何结果": "새로 고침은 완료되었지만 결과가 없습니다", "刷新模型管理页里各提供商的远程模型目录,逐家结果列在弹窗里": "모델 관리 페이지에서 각 제공자의 원격 모델 디렉터리를 새로 고치고, 제공자별 결과를 대화상자에 표시합니다", + "刷新清单": "", "加载中…": "로드 중…", "勾上它,导入后即进入这家的清单": "체크하면 가져온 뒤 이 제공자의 카탈로그에 들어갑니다", "勾几个就并行测几次;默认选队列里最靠前的一个。": "체크한 수만큼 병렬로 테스트합니다; 기본값은 대기열에서 가장 앞선 하나입니다.", @@ -126,6 +130,8 @@ "只刷新这一家的模型清单": "이 제공자의 모델 목록만 새로 고침", "只属于这一家": "이 제공자에만 속함", "可删除 {n} 个(手动登记 / 自定义家);": "삭제 가능 {n}개(수동 등록 / 커스텀 제공자); ", + "可见": "표시됨", + "可见性": "표시 상태", "可配思考档位": "설정 가능한 추론 레벨", "同一条错误出现在所有账号上": "동일한 오류가 모든 계정에서 나타난다면", "后端桥不可用": "백엔드 브리지를 사용할 수 없습니다", @@ -140,6 +146,7 @@ "失败": "실패", "失败 {n} 个 · 首个失败:{error}": "{n}개 실패 · 첫 실패: {error}", "完成": "완료", + "官方客户端会展示这个模型": "공식 클라이언트가 이 모델을 표시합니다", "对下游声明的能力(工具调用 / 图片识别 / 视频识别 / 支持思考)—— 点击修改": "다운스트림에 선언한 기능(도구 호출 / 이미지 인식 / 비디오 인식 / 추론 지원) —— 클릭하여 수정", "对外名可自由命名,允许与上游模型 ID 同名(同名时该上游的原生路由优先,映射作兜底); 支持字母、数字与 ": "외부 노출명은 자유롭게 지정할 수 있고 업스트림 모델 ID와 같은 이름도 허용됩니다(이름이 같으면 해당 업스트림의 네이티브 라우팅이 우선하고 매핑이 폴백으로 동작합니다); 영문, 숫자 및 ", "对外映射名(下游请求时用)": "외부 매핑명(다운스트림 요청 시 사용)", @@ -183,12 +190,14 @@ "批量操作": "일괄 작업", "批量操作 · 已选 {n} 个模型": "일괄 작업 · {n}개 모델 선택됨", "按倍率排序": "배율로 정렬", + "按可见性筛选": "표시 상태로 필터", "按所选网关对话协议组装请求并校验响应转换(Chat Completions / Responses / Anthropic Messages)。": "선택한 게이트웨이 대화 프로토콜로 요청을 구성하고 응답 변환을 검증합니다(Chat Completions / Responses / Anthropic Messages).", "按提供商选择": "제공자별 선택", "按状态筛选": "상태별 필터", "提供商": "제공자", "搜索模型": "모델 검색", "搜索模型 ID / 名称 / 映射名…": "모델 ID / 이름 / 매핑명 검색…", + "搜索模型 ID / 提供商": "", "搜索模型 ID…": "모델 ID 검색…", "搜索账号…": "계정 검색…", "操作": "작업", @@ -197,6 +206,7 @@ "收起 ▴": "접기 ▴", "数字带虚线底 = 由 models.dev 补齐;右上角小点 = 你手动覆盖过;— = 未声明。点任意一格可改。": "숫자에 점선 밑줄 = models.dev가 채움; 오른쪽 위 작은 점 = 직접 덮어씀; — = 미선언. 아무 셀이나 클릭해 수정할 수 있습니다.", "新建一个自定义提供商(同时创建它的第一个账号)": "커스텀 제공자 새로 만들기(첫 계정도 함께 생성)", + "映射、思考等级、上下文窗口与工具调用能力在桌面端「模型管理」里改;手机端只做启停。": "", "映射「{alias} → {target}」不存在": "매핑 '{alias} → {target}'이(가) 존재하지 않습니다", "映射上未绑定思考等级,「跟随映射」等于这次不注入": "매핑에 추론 레벨이 바인딩되지 않아 '매핑 따르기'는 이번에 주입하지 않습니다", "映射上绑定的思考等级是 {level},「跟随映射」按它注入": "매핑에 바인딩된 추론 레벨은 {level}이며, '매핑 따르기'는 그 값으로 주입합니다", @@ -227,6 +237,7 @@ "模型数": "모델 수", "模型映射": "모델 매핑", "模型来源": "모델 소스", + "模型清单已刷新": "", "模型管理表": "모델 관리 표", "模型能力": "모델 기능", "正在刷新…": "새로 고치는 중…", @@ -235,6 +246,7 @@ "每条映射 = 对外名 → 上游模型;原始 ID 与别名有独立开关,默认绑定永远存在(没有映射时是合成的那条)。": "각 매핑 = 외부 노출명 → 업스트림 모델; 원본 ID와 별칭에는 각각 독립된 스위치가 있고, 기본 바인딩은 항상 존재합니다(매핑이 없을 때는 합성된 한 건).", "没有匹配「{keyword}」的模型": "'{keyword}'와 일치하는 모델이 없습니다", "没有匹配当前筛选的模型": "현재 필터와 일치하는 모델이 없습니다", + "没有匹配的模型": "", "没有选中任何模型": "선택된 모델이 없습니다", "没有选中的模型": "선택된 모델이 없습니다", "流式请求": "스트리밍 요청", @@ -324,8 +336,11 @@ "还没有可刷新的提供商:先在账号页添加一个账号": "새로 고칠 수 있는 제공자가 없습니다: 먼저 계정 페이지에서 계정을 추가하세요", "还没有自定义提供商": "아직 커스텀 제공자가 없습니다", "这一行已不在当前清单里(模型被移除、或目录刷新后上游不再提供它)。关闭后刷新列表再试。": "이 행은 더 이상 현재 카탈로그에 없습니다(모델이 제거되었거나 디렉터리 새로 고침 후 업스트림이 더 이상 제공하지 않습니다). 닫은 뒤 목록을 새로 고치고 다시 시도하세요.", + "这个模型在官方客户端里是否会被展列;不展示的那些默认已关闭(含默认绑定)": "이 모델이 공식 클라이언트에 표시되는지 여부. 표시되지 않는 항목은 기본적으로 비활성화됩니다(기본 바인딩 포함)", "这个模型已经在这家的清单里,不重复导入": "이 모델은 이미 이 제공자의 카탈로그에 있어 중복으로 가져오지 않습니다", "这几项是给": "이 항목들은 ", + "这家提供商没有可见性判据(目前只有 Antigravity / Qoder / 小浣熊 / CodeArts 有)": "이 제공업체에는 표시 상태 판정 규칙이 없습니다(현재 Antigravity / Qoder / Raccoon / CodeArts만 해당)", + "这家有可见性判据,但这一条上游没有表态 —— 按可见处理": "이 제공업체에는 표시 상태 판정 규칙이 있지만 이 항목에 대해서는 업스트림이 밝히지 않았습니다 — 표시로 처리합니다", "这家没有可用账号(要在账号页启用一个、且凭证完整)": "이 제공자에는 사용 가능한 계정이 없습니다(계정 페이지에서 하나를 활성화하고 자격 증명을 완성해야 합니다)", "这家用固定模型清单,无可刷新": "이 제공자는 고정 모델 카탈로그를 사용해 새로 고칠 수 없습니다", "这家的模型清单是全局的,不跟账号走": "이 제공자의 모델 카탈로그는 전역이며 계정을 따라가지 않습니다", diff --git a/desktop-tauri/i18n/i18n-src/ko/proxies.json b/desktop-tauri/i18n/i18n-src/ko/proxies.json index a7ed769a..b940f575 100644 --- a/desktop-tauri/i18n/i18n-src/ko/proxies.json +++ b/desktop-tauri/i18n/i18n-src/ko/proxies.json @@ -3,10 +3,13 @@ " 提供 AI 账号专用的原生静态住宅 IP(Static Residential ISP):IP 来自真实家庭宽带运营商,Scamalytics 低欺诈评分,每个 IP 独享,不限流量,$3.90 起。": "는 AI 계정 전용 네이티브 정적 주거용 IP(Static Residential ISP)를 제공합니다. 실제 가정용 브로드밴드 사업자의 IP를 사용하며 Scamalytics 사기 점수가 낮고, IP는 1개씩 전용, 트래픽 무제한, $3.90부터.", " 那一类由「同步 Clash Verge」 镜像进来(进入本页时会自动同步一次):名称、端口、启用都跟随 Clash, 在 Clash 里改或删,这里跟着变 —— 所以它们只读,改请到 Clash Verge 里改。": " 항목은 「Clash Verge 동기화」로 미러링됩니다 (이 페이지에 들어오면 자동으로 한 번 동기화): 이름, 포트, 활성 여부가 모두 Clash를 따르고, Clash에서 수정하거나 삭제하면 여기도 함께 바뀝니다 — 따라서 읽기 전용이며, 수정은 Clash Verge에서 하세요.", "Clash Verge": "Clash Verge", + "Clash 同步失败:{msg}": "", + "Clash 镜像": "", "YAML 文件": "YAML 파일", "{names} 等 {n} 条": "{names} 외 {n}건", "{n} 个出口": "엔드포인트 {n}개", "{n} 个账号在用": "사용 중인 계정 {n}개", + "{n} 个账号引用": "", "{n} 条订阅": "구독 {n}개", "{percent}%({received} / {total} MB)": "{percent}% ({received} / {total} MB)", "✅ {ip}": "✅ {ip}", @@ -29,7 +32,7 @@ "也可以把二维码截图直接粘贴到弹窗里(Ctrl+V)": "QR 코드 스크린샷을 대화상자에 바로 붙여넣을 수도 있습니다 (Ctrl+V)", "二维码里没有订阅链接": "QR 코드에 구독 링크가 없습니다", "从图片识别二维码": "이미지에서 QR 코드 인식", - "代理内核未安装(在「订阅管理」里下载内核后,节点会自动分配监听端口)": "프록시 커널이 설치되어 있지 않습니다(「구독 관리」에서 다운로드하면 노드에 리스닝 포트가 자동으로 할당됩니다)", + "代理内核未安装(在「订阅管理」里下载内核后,被账号引用的节点会自动开监听端口)": "프록시 커널이 설치되지 않았습니다 (「구독 관리」에서 커널을 다운로드하면 계정이 참조하는 노드에 자동으로 수신 포트가 할당됩니다)", "代理列表加载失败:{reason}": "프록시 목록 불러오기 실패: {reason}", "代理池": "프록시 풀", "代理池表": "프록시 풀 표", @@ -67,6 +70,8 @@ "内核没有在超时内停下来": "제한 시간 안에 커널이 멈추지 않았습니다", "内核目录没能删掉(可能仍被进程占用),稍后重试一次": "커널 폴더를 삭제하지 못했습니다(프로세스가 아직 사용 중일 수 있습니다). 잠시 후 다시 시도하세요.", "内核运行中": "커널 실행 중", + "出口 IP {ip}": "", + "出口可用": "", "出口测试网址": "연결 테스트 URL", "删除代理": "프록시 삭제", "删除内核": "커널 삭제", @@ -81,6 +86,7 @@ "只读:在 Clash Verge 里改": "읽기 전용: Clash Verge에서 수정하세요", "只读:在「订阅管理」里改": "읽기 전용: 「구독 관리」에서 수정하세요", "同步 Clash Verge": "Clash Verge 동기화", + "同步 Clash Verge 出口": "", "同步中…": "동기화 중…", "同步失败:{reason}": "동기화 실패: {reason}", "名字与端口在 Clash Verge 里改;启用可在本页关闭": "이름과 포트는 Clash Verge에서 변경하세요. 사용 여부는 이 페이지에서 끌 수 있습니다", @@ -94,6 +100,7 @@ "启用(禁用的订阅不参与内核同步,它的节点会从代理池移除)": "사용 (사용하지 않는 구독은 커널 동기화에 포함되지 않으며, 해당 노드는 프록시 풀에서 제거됩니다)", "图片读取失败": "이미지 읽기 실패", "地址": "주소", + "地址不完整": "주소 불완전", "失败 {n} 个({reason})": "실패 {n}개 ({reason})", "存放位置": "저장 위치", "存放位置 {dir}": "저장 위치 {dir}", @@ -106,13 +113,16 @@ "已删除 {n} 个代理": "프록시 {n}개를 삭제했습니다", "已删除「{name}」": "「{name}」을(를) 삭제했습니다", "已删除内核(订阅与账号引用保留,重新下载后会重新分配端口)": "커널을 삭제했습니다(구독과 계정 참조는 유지되며, 다시 다운로드하면 포트가 재할당됩니다)", + "已删除内核(订阅与账号引用保留;有文件仍被占用,将在下次启动时清理)": "커널을 삭제했습니다(구독과 계정 참조는 유지됩니다. 사용 중인 파일이 있어 다음 실행 시 정리됩니다)", "已刷新「{name}」,正在同步节点…": "「{name}」을(를) 새로 고쳤습니다. 노드 동기화 중…", + "已同步 {n} 项变更": "", "已启用「{name}」": "「{name}」을(를) 활성화했습니다", "已启用「{name}」,正在补建监听端口(内核重建中)": "\"{name}\"을(를) 사용하도록 설정했습니다. 수신 포트를 다시 만드는 중입니다(커널 재시작 중)", "已在本页禁用(打开开关即恢复)": "이 페이지에서 사용 중지했습니다(스위치를 켜면 되돌아갑니다)", "已新增「{name}」": "「{name}」을(를) 추가했습니다", "已新增订阅「{name}」,内核正在同步节点…": "구독 「{name}」을(를) 추가했습니다. 커널이 노드를 동기화하는 중…", "已是最新,没有需要同步的变更": "이미 최신 상태이며 동기화할 변경 사항이 없습니다", + "已有账号引用它,但内核还没把监听端口补建出来 —— 稍等片刻,或重新启用这条节点": "계정이 이미 이 노드를 참조하고 있지만 커널이 아직 수신 포트를 만들지 않았습니다 — 잠시 기다리거나 이 노드를 다시 활성화하세요", "已禁用「{name}」": "「{name}」을(를) 비활성화했습니다", "已禁用「{name}」,监听端口正在关闭(内核重建中)": "\"{name}\"을(를) 사용 중지했습니다. 수신 포트를 닫는 중입니다(커널 재시작 중)", "已禁用(未开监听端口)": "사용 중지됨(수신 포트 없음)", @@ -128,6 +138,7 @@ "当前环境不支持订阅(桥接方法缺失)": "현재 환경에서는 구독을 지원하지 않습니다 (브리지 메서드 없음)", "当前环境不支持选择文件夹,直接填写绝对路径即可": "이 환경에서는 폴더 선택을 열 수 없습니다. 절대 경로를 직접 입력하세요", "当前线路不可用:{error};重新选一条或切回直连": "현재 경로를 사용할 수 없습니다: {error}. 다른 경로를 선택하거나 직접 연결로 되돌리세요", + "所属订阅已删除,这条节点不会再分配端口 —— 可以删除它": "소속 구독이 삭제되어 이 노드에는 더 이상 포트가 할당되지 않습니다 — 삭제해도 됩니다", "手动填写的地址与端口": "직접 입력한 주소와 포트", "打开文件夹选择框失败:{reason}": "폴더 선택 창을 열지 못했습니다: {reason}", "批量删除": "일괄 삭제", @@ -143,6 +154,7 @@ "搜索节点": "노드 검색", "文件读取失败": "파일을 읽지 못했습니다", "新增代理": "프록시 추가", + "新增出口、编辑地址与订阅管理在桌面端完成;手机端做启停与连通性测试。": "", "新增订阅": "구독 추가", "显示 {n} / 共 {total} 个出口": "{n} / 전체 {total}개 표시", "更新中…": "업데이트 중…", @@ -155,6 +167,7 @@ "本地文件不会自动更新:要更新节点,重新选择文件后保存;不重选则内容保持不变。": "로컬 파일은 자동 갱신되지 않습니다: 노드를 업데이트하려면 파일을 다시 선택해 저장하세요. 다시 선택하지 않으면 내용이 그대로 유지됩니다.", "本地文件订阅无需更新(要换内容请编辑后重新选择文件)": "로컬 파일 구독은 업데이트가 필요 없습니다(내용을 바꾸려면 편집에서 파일을 다시 선택하세요)", "本地文件订阅的内容在导入时固定:节点会由内核起成本地监听器并镜像进代理池,节点数稍等几秒由轮询刷新。": "로컬 파일 구독의 내용은 가져올 때 고정됩니다: 노드는 커널이 로컬 리스너로 실행해 프록시 풀로 미러링되며, 노드 수는 몇 초 후 폴링으로 갱신됩니다.", + "本机没有读到 Clash Verge 配置": "", "本页可编辑、可删除": "이 페이지에서 편집 및 삭제 가능", "来自 Clash Verge 同步(名称、端口跟随 Clash),请到 Clash Verge 中修改;启用可在本页关闭": "Clash Verge에서 동기화됨(이름과 포트는 Clash를 따름). 변경은 Clash Verge에서 하세요. 사용 여부는 이 페이지에서 끌 수 있습니다", "正在下载内核": "커널 다운로드 중", @@ -172,7 +185,7 @@ "测试网址要以 http:// 或 https:// 开头": "테스트 URL은 http:// 또는 https:// 로 시작해야 합니다", "确定删除「{name}」?{used}": "「{name}」을(를) 삭제할까요? {used}", "确定删除「{name}」?它在代理池里的节点会一并移除,引用这些节点的账号会回退直连。": "「{name}」을(를) 삭제하시겠습니까? 프록시 풀의 해당 노드도 함께 제거되며, 이를 참조하는 계정은 직접 연결로 되돌아갑니다.", - "确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会变成「未分配端口」,引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。": "다운로드한 커널{version}을(를) 삭제하시겠습니까? 커널 파일과 포트 매핑이 제거되고, 구독 노드는「포트 미할당」이 되며, 이를 참조하는 계정은 일시적으로 직접 연결로 폴백합니다. 구독 자체는 유지되며, 커널을 다시 다운로드하면 포트가 재할당됩니다.", + "确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会失去监听端口(地址列显示「端口准备中」),引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。": "다운로드한 커널{version}을(를) 삭제하시겠습니까? 커널 파일과 포트 매핑이 제거되어 구독 노드는 수신 포트를 잃습니다(주소 열에「포트 준비 중」이 표시됩니다). 이를 참조하는 계정은 일시적으로 직접 연결로 폴백합니다. 구독 자체는 유지되며, 커널을 다시 다운로드하면 포트가 재할당됩니다.", "确定删除选中的 {n} 个代理?": "선택한 프록시 {n}개를 삭제할까요?", "禁用后本端不再用它(引用它的账号回退直连),并会关掉它的本地监听端口": "사용을 중지하면 이 기기에서는 더 이상 쓰지 않고(참조하는 계정은 직접 연결로 돌아갑니다) 로컬 수신 포트도 닫습니다", "禁用后本端不再用它(引用它的账号回退直连);端口在 Clash Verge 侧,不受影响": "사용을 중지하면 이 기기에서는 더 이상 쓰지 않습니다(참조하는 계정은 직접 연결로 돌아갑니다). 포트는 Clash Verge 쪽에 그대로 있으며 영향을 받지 않습니다", @@ -192,6 +205,7 @@ "订阅来源": "구독 소스", "订阅管理": "구독 관리", "订阅管理({n})": "구독 관리 ({n})", + "订阅节点": "", "订阅节点会在本机各占一个监听端口,从该范围里挑空闲的分配;节点多的大订阅把默认范围({base}-{end})用尽时会报「没有可用的节点端口」,这时把范围调大再重启内核。已分配出去的端口不会因改范围而失效。": "구독 노드는 로컬 리스너 포트를 하나씩 차지하며 이 범위의 비어 있는 포트에서 할당됩니다. 노드가 많은 대형 구독이 기본 범위({base}-{end})를 모두 사용하면 \"사용 가능한 노드 포트가 없습니다\"라고 표시되며, 이때 범위를 넓히고 커널을 재시작하세요. 이미 할당된 포트는 범위를 변경해도 유효합니다.", "订阅里的节点会自动同步进代理池,账号即可引用。": "구독의 노드는 자동으로 프록시 풀에 동기화되어 계정에서 참조할 수 있습니다.", "订阅里的节点会被内核起成本地监听器,镜像到代理池(账号按 id 引用,与本页其它出口同一用法)。内核只在用到订阅时按需下载(约 20-40MB),不随安装包分发。": "구독의 노드는 커널이 로컬 리스너로 실행하고 프록시 풀에 미러링합니다 (계정은 id로 참조하며, 이 페이지의 다른 항목과 동일합니다). 커널은 구독을 사용할 때만 필요할 때 다운로드되며(약 20-40MB) 설치 패키지에 포함되지 않습니다.", @@ -207,8 +221,9 @@ "跳过 {n} 个(同步只读条目)": "{n}개 건너뜀 (동기화된 읽기 전용 항목)", "还没有代理。点右上角「+ 新增代理」手填一个,或在「订阅管理」里导入订阅节点、同步 Clash Verge 的出口。": "아직 프록시가 없습니다. 오른쪽 위 「+ 프록시 추가」로 직접 입력하거나, 「구독 관리」에서 구독 노드를 가져오거나 Clash Verge 항목을 동기화하세요.", "还没有代理。点右上角「+ 新增代理」手填一个,或点「同步 Clash Verge」把 Clash 的出口导进来。": "아직 프록시가 없습니다. 오른쪽 위 「+ 프록시 추가」로 직접 입력하거나, 「Clash Verge 동기화」를 눌러 Clash의 엔드포인트를 가져오세요.", - "还没有监听端口(还没有账号引用它 —— 在账号里选它并保存后,内核会自动开端口)": "아직 수신 포트가 없습니다(참조하는 계정이 없습니다. 계정에서 선택 후 저장하면 커널이 자동으로 포트를 엽니다)", + "还没有出口。在桌面端「网络代理」页新增,或点上面同步 Clash。": "", "还没有订阅:粘贴订阅链接、导入本地 YAML 文件,或从图片识别二维码。": "아직 구독이 없습니다: 구독 링크를 붙여넣거나, 로컬 YAML 파일을 가져오거나, 이미지에서 QR 코드를 인식하세요.", + "还没有账号使用它(端口只在被账号引用时才分配 —— 在账号里选它并保存后,内核会自动开端口)": "아직 이 노드를 사용하는 계정이 없습니다 (포트는 계정이 참조할 때만 할당됩니다 — 계정에서 이 노드를 선택하고 저장하면 커널이 자동으로 포트를 엽니다)", "这条在 Clash Verge 里是停用的,本页启用不生效 —— 到 Clash Verge 里打开它": "이 항목은 Clash Verge에서 사용 중지되어 있어 이 페이지에서 켜도 효과가 없습니다 — Clash Verge에서 켜세요", "连接成功": "연결 성공", "连通性": "연결 상태", @@ -220,6 +235,7 @@ "部分测试结果没能记下:{reason}": "일부 테스트 결과를 기록하지 못했습니다: {reason}", "重启内核": "커널 재시작", "重新选择文件…": "파일 다시 선택…", + "镜像当前出口集合,名称与端口跟随 Clash": "", "需要干净的出口?": "깨끗한 아웃바운드가 필요하신가요?", "首次购买使用优惠码 AGENT2API25 享 75 折。": "첫 구매 시 코드 AGENT2API25로 25% 할인됩니다.", "+ 新增代理": "+ 프록시 추가", diff --git a/desktop-tauri/i18n/i18n-src/ko/report.json b/desktop-tauri/i18n/i18n-src/ko/report.json index 110ee128..2e98b0fc 100644 --- a/desktop-tauri/i18n/i18n-src/ko/report.json +++ b/desktop-tauri/i18n/i18n-src/ko/report.json @@ -1,6 +1,7 @@ { "30 天": "30일", "7 天": "7일", + "Key": "", "Token": "Token", "Top 提供商": "Top 제공자", "Top 模型": "Top 모델", @@ -12,6 +13,11 @@ "{m}月{d}日 周{wd}": "{m}월 {d}일 ({wd})", "{name} · {tokens} tokens · {n} 次请求 · {pct}": "{name} · {tokens} tokens · 요청 {n}건 · {pct}", "{name}:{tokens} tokens · 占 {pct}%": "{name}: {tokens} tokens · 비중 {pct}%", + "{n} 个账号启用 · {m} 个限流中": "", + "{n} 个账号正在限流,转发会自动跳过它们": "", + "{n} 天": "", + "{n} 次请求": "", + "{start} ~ {end}": "", "{tokens} tokens · 占 {pct}": "{tokens} tokens · 비중 {pct}", "…还有 {n} 个模型": "…외 {n}개 모델", "一": "월", @@ -19,14 +25,21 @@ "二": "화", "五": "금", "今天": "오늘", + "今天都签完了": "", + "今日签到 {done} / {total}": "", + "今日签到 {done}/{total}": "", + "今日请求": "", "六": "토", "区间共 {n} 天": "기간 총 {n}일", + "区间内累计": "", + "可用账号": "", "右轴": "오른쪽 축", "含今天在内往前数": "오늘 포함 거슬러 계산", "命中 {hit} / 输入 {input}": "적중 {hit} / 입력 {input}", "命中率 = 缓存读取 Token / 输入 Token(promptTokens)。窗口越短越贴近「此刻」:上游把缓存读取与输入分开上报时,该比值合法地可能超过 100%,此处不夹取值、如实展示。": "적중률 = 캐시 읽기 Token / 입력 Token(promptTokens). 창이 짧을수록 「지금」에 가깝습니다. 업스트림이 캐시 읽기와 입력을 따로 보고하면 이 비율은 정당하게 100%를 넘을 수 있으며, 여기서는 값을 제한하지 않고 그대로 표시합니다.", "命中率 {rate}(命中 {hit} / 输入 {input})": "적중률 {rate} (적중 {hit} / 입력 {input})", "四": "목", + "失败 {n} 次": "", "左轴": "왼쪽 축", "开始日期": "시작 날짜", "当前连续天数": "현재 연속 일수", @@ -46,32 +59,49 @@ "所选范围内还没有请求记录": "선택한 범위에 아직 요청 기록이 없습니다", "所选范围内还没有账号用量": "선택한 범위에 아직 계정 사용량이 없습니다", "报表时间范围": "리포트 시간 범위", + "报表视角": "", + "按天 Token 用量": "", "按天 Token 趋势": "일별 Token 추이", "按本地整点,无请求的整点不标注": "로컬 정시 기준, 요청이 없는 정시는 표시하지 않음", "提供商用": "제공자 사용량", "提供商用占比": "제공자 사용량 비중", + "提供商用量": "", "提供商:{text}": "제공자: {text}", "方格为本地自然日,无请求显示为空槽": "칸은 로컬 기준 날짜이며, 요청이 없으면 빈 칸으로 표시됩니다", "无请求的整点按 0% 计,但不写读数": "요청이 없는 정시는 0%로 계산하되 수치는 표시하지 않습니다", "日": "일", + "时间范围。「今天」是本地自然日今天 0 点到现在;「昨天」是昨天一整天(0 点到 24 点);「近 7 天 / 近 30 天」含今天在内往前数,即今天与之前的 6 / 29 天,不是「不含今天的 7 / 30 天」。": "기간. \"오늘\"은 현지 날짜 기준 오늘 0시부터 현재까지이고, \"어제\"는 어제 하루 종일(0시~24시)입니다. \"최근 7일 / 최근 30일\"은 오늘을 포함해 거슬러 올라간 일수로, 오늘과 그 이전 6 / 29일을 뜻합니다(오늘을 제외한 7 / 30일이 아닙니다).", + "昨天": "어제", "暂无热力图数据": "히트맵 데이터가 없습니다", "暂无缓存趋势数据": "캐시 추이 데이터가 없습니다", "暂无趋势数据": "추이 데이터가 없습니다", + "最近一次请求使用的模型": "", + "最近请求": "", + "最近请求 {n} 条": "", "未知模型": "알 수 없는 모델", + "未配置上游": "", "本月": "이번 달", "模型用量": "모델 사용량", "模型用量占比": "모델 사용량 비중", "每天的总 Token 用量(输入 + 输出)。柱顶标出当天的读数;区间很长、柱子挤到放不下时只标最高的那几天,其余悬停柱子即可读到。": "일별 총 Token 사용량(입력 + 출력)입니다. 막대 위에 해당 날짜의 수치를 표시하며, 기간이 길어 막대가 빽빽할 때는 가장 높은 며칠만 표시하고 나머지는 막대에 마우스를 올리면 확인할 수 있습니다.", + "活跃": "", "活跃天数": "활동 일수", "活跃热力图": "활동 히트맵", + "管理": "", + "累计请求 {n} 条": "누적 요청 {n}건", "结束日期": "종료 날짜", "结束日期(含当天)": "종료 날짜(당일 포함)", + "统计区间加载中": "", "统计概览": "통계 개요", "缓存命中率": "캐시 적중률", "自定义区间": "사용자 지정 기간", + "请求量": "", "读取报表失败:{error}": "리포트를 불러오지 못했습니다: {error}", + "账号分布": "", + "账号用量": "", "起始日期(含当天)": "시작 날짜(당일 포함)", "输入 + 输出": "입력 + 출력", + "运行中": "", "近 1 小时": "최근 1시간", "近 10 分钟": "최근 10분", "近 24 小时": "최근 24시간", @@ -80,5 +110,9 @@ "近 30 天": "최근 30일", "近 365 天活跃热力图": "최근 365일 활동 히트맵", "近 365 天,按天 Token 用量着色(档位按本窗口峰值自动定)": "최근 365일, 일별 Token 사용량으로 색상 지정(단계는 이 창의 최댓값 기준으로 자동 결정)", - "近 7 天": "최근 7일" + "近 7 天": "최근 7일", + "还有 {n} 个待签": "", + "还没有添加账号": "", + "还没有请求记录": "", + "连续天数": "" } diff --git a/desktop-tauri/i18n/i18n-src/ko/requests.json b/desktop-tauri/i18n/i18n-src/ko/requests.json index 8850a0da..a6298080 100644 --- a/desktop-tauri/i18n/i18n-src/ko/requests.json +++ b/desktop-tauri/i18n/i18n-src/ko/requests.json @@ -41,6 +41,7 @@ "切换路径:": "전환 경로: ", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "목록 데이터가 갱신되었습니다. 다시 시도하세요(닫은 후 이 상세를 다시 열어 주세요)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "로그 레코드 자체를 삭제합니다. 현재 필터가 있으면 적중한 항목만 삭제하며 일별 집계 리포트는 영향을 받지 않습니다. 필터가 없으면 전부 비우고 리포트를 초기화합니다. 이 방식은 복구할 수 없습니다.", + "加载更早的请求": "", "压缩中…": "압축 중…", "压缩失败:{message}": "압축 실패: {message}", "压缩已在后台开始,完成后会自动提示": "압축이 백그라운드에서 시작되었습니다. 완료되면 자동으로 알려드립니다", @@ -49,6 +50,7 @@ "压缩数据库": "DB 압축", "压缩正在后台执行,完成后自动恢复": "압축이 백그라운드에서 실행 중입니다. 완료되면 자동으로 복원됩니다", "原始报文": "원시 페이로드", + "发给上游的模型": "", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "그 외 {missing}회 시도는 상세가 기록되지 않았습니다(가장 이른 {kept}건만 보관)", "另有 {n} 个词命中": "그 외 {n}개 단어 적중", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "포워딩 중인 요청만 표시합니다(상태 열은 단계별로 표시: 연결 중 / 응답 대기 / 응답 중 / 재시도 중, 소요 시간 열은 경과 시간 표시)", @@ -62,14 +64,18 @@ "响应头": "응답 헤더", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "삭제된 데이터가 차지한 디스크 공간 회수(checkpoint + VACUUM, 백그라운드 실행)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "설정 → 일반에서 「디버그 모드」를 켜야 새로 발생하는 요청부터 페이로드가 저장됩니다", + "在途 {n}": "", "失败请求不记录用量": "실패한 요청은 사용량을 기록하지 않음", "失败({status}):{error}": "실패({status}): {error}", "失败:{error}": "실패: {error}", "实际发给上游 {bytes} 字节": "업스트림에 실제 전송한 {bytes} 바이트", "客户端": "클라이언트", + "客户端请求的模型": "", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "{count}건의 로그를 삭제합니다 · 그중 {raw}건은 페이로드 원문이 남아 있음 · DB 크기 {db}", "尝试 {n} ·": "시도 {n} ·", "尝试明细": "시도 상세", + "尝试次数": "", + "尝试链": "", "工具结果": "도구 결과", "工具结果 · {id}": "도구 결과 · {id}", "工具结果图片": "도구 결과 이미지", @@ -77,6 +83,7 @@ "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "중단 요청 접수됨: 업스트림 연결이 끊어졌으며, 목록은 잠시 후 「요청이 수동으로 중단됨」으로 갱신됩니다", "已抹掉 {count} 条报文原文": "{count}건의 페이로드 원문을 지웠습니다", "已用时(请求仍在转发中)": "경과 시간(요청이 아직 포워딩 중)", + "当前筛选下没有记录": "", "成功": "성공", "成功({status})": "성공({status})", "执行清理": "정리 실행", @@ -88,6 +95,7 @@ "按模型筛选": "모델별 필터", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "모델별 필터(로그에 나타난 모델만 표시; 모델 이름 정확히 일치)", "按请求结果筛选": "요청 결과별 필터", + "换号链:{chain}": "", "排队中": "대기 중", "提供商 / 账号": "제공자 / 계정", "提供商: {provider}": "제공자: {provider}", @@ -98,12 +106,15 @@ "无报文原文(未开启「调试模式」,或已被清理)": "원문 페이로드 없음(디버그 모드가 꺼져 있었거나 정리되었음)", "无结果记录": "결과 미기록", "时间": "시각", + "暂无请求日志": "", "暂无请求日志,网关还没有转发过请求": "아직 요청 로그가 없습니다. 게이트웨이가 아직 요청을 포워딩한 적이 없습니다", "最终承载:{picked}": "최종 처리: {picked}", + "有重试": "", "本轮尝试失败,正在退避等待或切换到下一个账号": "이번 시도가 실패하여 백오프 대기 중이거나 다음 계정으로 전환 중입니다", "来源客户端:{client}": "요청 클라이언트: {client}", "查看该请求的上游原始报文": "이 요청의 업스트림 원시 페이로드 보기", "模型": "모델", + "模型映射:{from} → {to}": "", "模型测试": "모델 테스트", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "모델 관리 페이지 작업 열의 「테스트」로 시작된 요청: 실제 요청과 같은 포워딩 경로를 사용하지만 리포트 통계에는 포함되지 않습니다", "正在加载请求日志…": "요청 로그 불러오는 중…", @@ -130,6 +141,7 @@ "缓存读取: - / 命中率: -": "캐시 읽기: - / 적중률: -", "缓存读取: {cache} / 命中率: {rate}": "캐시 읽기: {cache} / 적중률: {rate}", "耗时": "소요 시간", + "脱敏命中:{words}": "", "被跳过:": "건너뜀: ", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent가 있지만 알려진 클라이언트 유형이 아닙니다: {ua}", "详情": "상세", @@ -148,7 +160,9 @@ "路由回放": "라우팅 리플레이", "转发到上游的模型:{model}": "업스트림으로 포워딩된 모델: {model}", "轮次": "회차", + "输入 Token": "", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "입력 {input} · 출력 {output} · 합계 {total} · 캐시 읽기 {cache}", + "输出 Token": "", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "이 상세는 모델 테스트(수동 실행)입니다: 실제 포워딩 경로를 사용하지만 리포트 통계에는 포함되지 않습니다", "这条明细没有记录提供商(旧数据或请求未走到转发)": "이 상세에는 제공자가 기록되지 않았습니다(오래된 데이터이거나 요청이 포워딩 단계까지 가지 못함)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "이 기록에는 요청 클라이언트가 없습니다(이전 데이터, 모델 테스트 또는 User-Agent 없는 요청)", @@ -160,11 +174,13 @@ "进行中…": "처리 중…", "连接中": "연결 중", "重试 {n}": "재시도 {n}", + "重试 {n} 次": "", "重试中": "재시도 중", "重试次数": "재시도 횟수", "预览对话": "대화 미리보기", "预览读取失败": "미리보기 읽기 실패", "预览读取失败:{error}": "미리보기 읽기 실패: {error}", + "首响": "", "首响 {time}": "첫 응답 {time}", "首响:上游首帧到达的耗时": "첫 응답: 업스트림 첫 프레임 도착까지 걸린 시간", "首响:上游首帧到达的耗时(请求仍在转发中)": "첫 응답: 업스트림 첫 프레임 도착까지 걸린 시간(요청이 아직 포워딩 중)", diff --git a/desktop-tauri/i18n/i18n-src/ko/settings.json b/desktop-tauri/i18n/i18n-src/ko/settings.json index 3ee1df20..721bd3ab 100644 --- a/desktop-tauri/i18n/i18n-src/ko/settings.json +++ b/desktop-tauri/i18n/i18n-src/ko/settings.json @@ -2,30 +2,58 @@ " 到 {file}": " → {file}", " 正文已改:{edited}(其余段用官方原文)。": " 본문 수정됨: {edited}(나머지 단락은 공식 원문 사용).", "(无 id)": "(id 없음)", + "/v1 拒绝无 Key 请求": "/v1은 키 없는 요청 거부", "0 B": "0 B", "100%(默认)": "100%(기본값)", "<本机IP>": "<로컬 IP>", + "Access Key ID": "Access Key ID", + "Agent2API 手机端": "Agent2API 모바일", + "Android 应用(APK)": "Android 앱(APK)", "Cline 上游按请求头判定「调用方是不是 Cline 自家产品」:X-CLIENT-TYPE: cline-sdk 是硬门槛(缺了它免费池模型一律 403),版本号与平台标识让请求更贴近官方客户端的形态。这里列出的是网关默认会发的头:改某一行的值 = 覆盖默认值(也可以新增默认清单之外的自定义头);把值清空保存 = 这个头不发送;在界面删掉被改过的行 = 回落默认值。改完点「保存」,下一个 Cline 请求立即生效。不懂某一行的含义就不要动它 —— 伪装头的价值在「与官方客户端逐字一致」。": "Cline 업스트림은 요청 헤더로 「호출자가 Cline 자사 제품인지」를 판별합니다. X-CLIENT-TYPE: cline-sdk는 필수 조건이며(이게 없으면 무료 풀 모델은 모두 403), 버전 번호와 플랫폼 식별자는 요청을 공식 클라이언트의 형태에 더 가깝게 만듭니다. 여기에 나열된 것은 게이트웨이가 기본으로 보내는 헤더입니다. 특정 행의 값을 바꾸면 기본값을 덮어쓰고(기본 목록에 없는 사용자 지정 헤더도 추가할 수 있습니다), 값을 비워 저장하면 그 헤더를 보내지 않으며, 화면에서 수정된 행을 삭제하면 기본값으로 돌아갑니다. 수정 후 「저장」을 누르면 다음 Cline 요청부터 즉시 적용됩니다. 의미를 모르는 행은 건드리지 마세요. 위장 헤더의 가치는 「공식 클라이언트와 글자 하나까지 동일」한 데 있습니다.", "Cline 伪装头": "Cline 위장 헤더", "Cline 伪装头已刷新": "Cline 위장 헤더를 새로 고쳤습니다", + "Docker 容器": "Docker 컨테이너", + "Endpoint": "Endpoint", + "Endpoint 填 https://<账户ID>.r2.cloudflarestorage.com,Region 固定填 auto。": "Endpoint에 https://<계정 ID>.r2.cloudflarestorage.com 을, Region에는 auto를 입력하세요.", + "Endpoint 填 https://cos.ap-guangzhou.myqcloud.com,Region 填 ap-guangzhou(按实际地域改)。": "Endpoint에 https://cos.ap-guangzhou.myqcloud.com 을, Region에는 ap-guangzhou를 입력하세요(실제 리전에 맞게 변경).", + "Endpoint 填 https://obs.cn-north-4.myhuaweicloud.com,Region 填 cn-north-4(按实际地域改)。": "Endpoint에 https://obs.cn-north-4.myhuaweicloud.com 을, Region에는 cn-north-4를 입력하세요(실제 리전에 맞게 변경).", + "Endpoint 填 https://oss-cn-hangzhou.aliyuncs.com,Region 填 cn-hangzhou(按实际地域改)。": "Endpoint에 https://oss-cn-hangzhou.aliyuncs.com 을, Region에는 cn-hangzhou를 입력하세요(실제 리전에 맞게 변경).", + "Endpoint 形如 http://192.168.1.10:9000(自建通常用 http);Region 一般填 us-east-1。": "Endpoint는 http://192.168.1.10:9000 형식이며(자체 호스팅은 보통 http), Region은 일반적으로 us-east-1입니다.", + "Endpoint 留空即走 AWS 官方地址;Region 必须与桶所在区域一致。": "Endpoint를 비워 두면 AWS 공식 주소를 사용합니다. Region은 버킷의 리전과 일치해야 합니다.", + "Linux 安装包(deb)": "Linux 패키지(deb)", + "Nextcloud": "Nextcloud", "OpenAI 新版 API 用 developer 角色取代 system 承载指令,新版 OpenAI SDK 与部分客户端发的对话里会出现 role 为 developer 的消息;不少 OpenAI 兼容上游(尤其是自定义提供商的透传路径)不认识这个角色,收到就报 400。开启本项后,网关在每次转发前把 developer 角色的消息改写为 system —— 两类角色在 OpenAI 语义上等价(OpenAI 自己的迁移指南也是这么映射的),改写不影响对话内容与语义;对本来就只认 system 的各家内置上游同样无害。默认开启;关掉后 developer 消息会原样发往上游,不认识它的上游会报「角色不存在」一类的错误。": "새 버전 OpenAI API는 system 대신 developer 역할로 지시를 전달하므로, 최신 OpenAI SDK와 일부 클라이언트가 보내는 대화에 role이 developer인 메시지가 포함됩니다. 많은 OpenAI 호환 업스트림(특히 커스텀 제공자의 투과 경로)은 이 역할을 인식하지 못해 받자마자 400을 반환합니다. 이 항목을 켜면 게이트웨이가 매 전달 전에 developer 역할 메시지를 system으로 수정합니다 — 두 역할은 OpenAI 의미론에서 동등하고(OpenAI 자체 마이그레이션 가이드도 같은 매핑을 사용), 수정은 대화 내용과 의미에 영향을 주지 않으며, 애초에 system만 받는 내장 업스트림에도 무해합니다. 기본값은 켜짐. 끄면 developer 메시지가 그대로 업스트림으로 전송되며, 이 역할을 모르는 업스트림은 \"역할이 존재하지 않는다\"류의 오류를 반환합니다.", + "QQ 群 · Telegram · Discord": "QQ 그룹 · Telegram · Discord", + "S3 兼容存储": "S3 호환 스토리지", + "Safari 没有安装按钮:点底部的「分享」,选「添加到主屏幕」,再点「添加」。": "Safari에는 설치 버튼이 없습니다. 하단의 「공유」 → 「홈 화면에 추가」 → 「추가」 순서로 누르세요.", + "Secret Access Key": "Secret Access Key", "Token 读数量级": "Token 수치 표기", "UTF-8 文本文件路径。留空用内置默认;只在「替换 / 追加」下读取。": "", "UTF-8 文本文件路径,留空用内置默认提示词(通用编码助手提示词,不含任何会被上游拦截的模板句)。只在「替换 / 追加」模式下生效,透传模式不读它。": "UTF-8 텍스트 파일 경로입니다. 비워 두면 내장 기본 시스템 프롬프트를 사용합니다(범용 코딩 어시스턴트 프롬프트로, 업스트림에 차단될 만한 템플릿 문구가 없습니다). 「교체 / 추가」 모드에서만 적용되며, 패스스루 모드에서는 읽지 않습니다.", + "WebView / 浏览器内核": "WebView / 브라우저 엔진", + "Windows 安装包(NSIS)": "Windows 설치 프로그램(NSIS)", + "Windows 服务": "Windows 서비스", "ZCode 套餐池(Start Plan)通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。": "ZCode 플랜 풀 (Start Plan) 채널의 각 요청은 그때그때 생성된 캡차 토큰(1회용, 2분 후 만료)을 하나 소비하며, 재고가 이 값 아래로 내려가면 UI가 이 값까지 자동으로 보충합니다. 기본값 3. 크게 잡으면 갑작스러운 동시 요청을 흡수하지만, 쓰지 못한 토큰은 2분 만에 만료되어 상류의 리스크 관리 쿼터를 태웁니다. 1–10을 입력할 수 있습니다.", "ZCode 活动套餐通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。": "", "ZCode「套餐池(Start Plan)」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。": "ZCode \"플랜 풀 (Start Plan)\" 채널의 모든 추론 요청에는 그때그때 생성된 Aliyun 인비저블 캡차 토큰(1회용, 약 2분 후 만료)을 붙여야 합니다: 토큰은 UI 안의 민트 가드가 조용히 생성해 게이트웨이 풀에 밀어넣고, 포워딩은 요청마다 하나씩 꺼내 씁니다. 여기의 세 값은 보충 리듬을 관리합니다: ① \"재고 목표\" = 재고가 이 값 아래로 내려갈 때만 이 값까지 보충(기본 3); ② \"폴링 간격\" = 가드가 재고를 확인하는 주기, 기본 2500ms(반응 속도에만 영향 — 재고가 충분하면 굳이 더 만들지 않음); ③ \"1회 생성 한도\" = 재고 부족 발견 시 한 라운드에 최대 몇 개까지 만들지, 기본 1(연속 생성 사이는 고정 500ms). 요청이 많고 요청 로그에 \"캡차 토큰 필요\" 503이 계속 나온다면 목표와 1회 한도를 적당히 올리고 폴링을 빠르게 하면 풀이 소비를 따라갑니다 — 대가로 상류(Aliyun)는 생성 빈도에 리스크 관리를 두어 많이 빠르게 만들수록 제한에 걸리기 쉽고, 쓰지 못한 토큰은 어차피 2분 만에 만료되어 쿼터를 낭비하므로 조금씩 조정을 권장합니다. 저장하면 다음 보충 라운드부터 즉시 적용되며 재시작이 필요 없습니다.", "ZCode「活动套餐」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。": "", + "beta 版": "", + "iOS 应用": "iOS 앱", + "models.dev 元数据": "models.dev 메타데이터", "models.dev 元数据已更新({count} 条)": "models.dev 메타데이터를 갱신했습니다({count}건)", "models.dev 是一个开源的模型资料库(社区维护),这里用它补齐「上游没告诉我们」的模型能力:上下文窗口、最大输出、工具调用、图片、视频、是否支持思考,以及这个模型认识哪些思考档位。优先级是:你在「模型能力」里手工改的值 > 上游清单自己声明的值 > models.dev 的值 —— 它只填上游没声明的空位,从不推翻上游已声明的。网关随包内嵌了一份快照,所以离线、首次启动、访问不通时也有数据;开启时会按「模型目录刷新」任务的间隔联网更新(同一轮里一并刷新,也可在这里立即刷新)。关闭只停联网刷新,已经拿到的数据继续用。": "models.dev는 커뮤니티가 유지하는 오픈소스 모델 데이터베이스입니다. 업스트림이 알려주지 않은 정보(컨텍스트 창, 최대 출력, 도구 호출, 이미지, 동영상, 추론 지원 여부, 그리고 이 모델이 실제로 받는 추론 레벨)를 여기서 채웁니다. 우선순위는 「모델 능력」에서 직접 수정한 값 > 업스트림 카탈로그의 선언값 > models.dev 값이며, 업스트림이 선언한 값을 뒤집지 않고 빈 자리만 채웁니다. 스냅샷이 앱에 내장되어 있어 오프라인·최초 실행·통신 불가 상황에서도 데이터가 있습니다. 켜져 있으면 「모델 카탈로그 새로고침」 주기에 맞춰 온라인으로 갱신합니다(같은 회차에 함께, 또는 여기서 즉시). 끄면 온라인 갱신만 멈추고 이미 받은 데이터는 계속 사용됩니다.", "models.dev 模型元数据": "models.dev 모델 메타데이터", + "systemd 服务": "systemd 서비스", "{count} 条": "{count}건", "{count} 条 · 来自{origin}{at} · {mode}": "{count}건 · 출처: {origin}{at} · {mode}", + "{d} 天 {h} 小时": "{d}일 {h}시간", + "{h} 小时 {m} 分钟": "{h}시간 {m}분", "{label} 提示词文件": "{label} 시스템 프롬프트 파일", "{label} 的提示词模式": "{label}의 시스템 프롬프트 모드", "{label}({message})": "{label}({message})", "{lines} 行、{chars} 字符(按当前草稿计)": "{lines}행, {chars}자(현재 초안 기준)", "{mode}生效:上游收到的 system 来自{source}{lines}。": "{mode} 적용: 업스트림이 받는 system은 {source}에서 가져옵니다{lines}.", + "{m} 分钟": "{m}분", "{noun}不能为空(可填 {min}–{max})": "{noun} 값은 비워 둘 수 없습니다({min}–{max} 입력 가능)", "{noun}必须在 {min}–{max} 之间(当前填的是 {text})": "{noun} 값은 {min}–{max} 사이여야 합니다(현재 입력: {text})", "{noun}必须是整数": "{noun} 값은 정수여야 합니다", @@ -34,22 +62,33 @@ "{n} KB": "{n} KB", "{n} MB": "{n} MB", "{n} 个头将随每个 Cline 请求发送": "헤더 {n}개가 모든 Cline 요청과 함께 전송됩니다", + "{n} 台可选 · 点此切换": "{n}대 · 눌러서 전환", + "{n} 项 · {shown} 显示": "{n}개 · {shown}개 표시", + "{total} 个菜单 · 显示 {shown} · 隐藏 {hidden} · {groups} 个分组": "메뉴 {total}개 · 표시 {shown} · 숨김 {hidden} · 그룹 {groups}개", + "…": "", "⚠️ 密钥警告": "⚠️ 키 경고", + "⚠️ 已关闭「{label}」的机器人校验": "⚠️ \"{label}\"의 봇 확인을 껐습니다", "⚠️ 机器人校验已关闭": "⚠️ 로봇 검증이 꺼져 있습니다", "✅ Cline 伪装头已保存": "✅ Cline 위장 헤더가 저장되었습니다", "✅ models.dev 模型元数据已开启": "✅ models.dev 모델 메타데이터를 켰습니다", + "✅ 云同步设置已保存": "✅ 클라우드 동기화 설정이 저장되었습니다", "✅ 出站指纹脱敏已开启": "✅ 아웃바운드 핑거프린트 마스킹이 켜졌습니다", + "✅ 密码已修改,其他设备的登录已失效": "✅ 비밀번호를 변경했습니다. 다른 기기의 로그인은 해제되었습니다", "✅ 导入完成:{summary}": "✅ 가져오기 완료: {summary}", + "✅ 已上传到云端({size})": "✅ 클라우드에 업로드했습니다({size})", "✅ 已为「{label}」装上网关自带提示词": "✅ 「{label}」에 게이트웨이 내장 시스템 프롬프트를 설치했습니다", + "✅ 已从云端恢复{note}": "✅ 클라우드에서 복원했습니다{note}", "✅ 已保存:{label}": "✅ 저장됨: {label}", "✅ 已保存:{label} {value}": "✅ 저장됨: {label} {value}", "✅ 已保存:指定错误码直接换号": "✅ 저장됨: 지정 오류 코드 즉시 계정 전환", "✅ 已保留 {days} 天": "✅ {days}일 보관합니다", "✅ 已保留 {days} 天,超出部分已清理": "✅ {days}일 보관하며, 초과분은 정리했습니다", "✅ 已备份 {n} 张表({size})到 {file}": "✅ 테이블 {n}개({size})를 {file}(으)로 백업했습니다", + "✅ 已导入账号密钥,请继续下载": "✅ 계정 키를 가져왔습니다. 계속 다운로드하세요", "✅ 已导出 {count} 个账号{note}{file}": "✅ 계정 {count}개를 내보냈습니다{note}{file}", + "✅ 已开启「{label}」的机器人校验": "✅ \"{label}\"의 봇 확인을 켰습니다", "✅ 已恢复 {n} 张表{note}": "✅ 테이블 {n}개를 복원했습니다{note}", - "✅ 已改用 k / M 缩写": "✅ k / M 축약 표기로 전환했습니다", + "✅ 已改用 k / M / B 缩写": "✅ k / M / B 약어로 변경했습니다", "✅ 已改用本地量级词(万 / 亿式)": "✅ 로컬 규모 단어(만 / 억 단위)로 전환했습니다", "✅ 已自动创建网关 Key「默认」(可在「网关 Key」页查看)": "✅ 게이트웨이 키 「기본」을 자동으로 만들었습니다(「게이트웨이 키」 페이지에서 확인할 수 있습니다)", "✅ 已解除内容拦截降级": "✅ 콘텐츠 차단 폴백을 해제했습니다", @@ -59,7 +98,9 @@ "✅ 调试模式已关闭": "✅ 디버그 모드가 꺼졌습니다", "✅ 调试模式已开启": "✅ 디버그 모드가 켜졌습니다", "✅ 路由设置已保存": "라우팅 설정이 저장되었습니다", + "✅ 连接成功:{path}": "✅ 연결 성공: {path}", "、{n} 个自定义提供商": ", 커스텀 제공자 {n}개", + "。事件日志、请求明细、调试报文与面板登录态保持不变。替换前会自动把当前业务数据备份到配置目录的 backups/ 下。": ". 이벤트 로그, 요청 상세, 디버그 패킷, 패널 로그인 상태는 그대로 유지됩니다. 교체 전에 현재 업무 데이터를 설정 디렉터리의 backups/ 에 자동으로 백업합니다.", "「{label}」将从 {previous} 天改为 {days} 天。": "「{label}」이(가) {previous}일에서 {days}일로 변경됩니다.", "「{label}」改用内置默认提示词": "「{label}」이(가) 내장 기본 시스템 프롬프트로 전환되었습니다", "「{label}」的提示词文件已更新": "「{label}」의 시스템 프롬프트 파일이 업데이트되었습니다", @@ -69,29 +110,58 @@ "「{label}」的网关自带提示词正文已更新": "「{label}」의 게이트웨이 내장 시스템 프롬프트 본문이 업데이트되었습니다", "「模型管理」里每个能力的来源标记(上游 / models.dev / 已覆盖)读的就是这份数据;映射弹窗的思考档位下拉也按这里的数据只列该模型真实认识的档位。": "「모델 관리」의 각 능력 출처 표시(업스트림 / models.dev / 직접 설정)가 이 데이터를 읽습니다. 매핑 대화상자의 추론 레벨 목록도 이 데이터를 기준으로 해당 모델이 실제로 받는 레벨만 표시합니다.", "「用哪份提示词」本来是按上游分别决定的事,所以每家可以有自己的模式、提示词文件与正文:不在这里列出的提供商一律沿用上面那份默认设置,「跟随默认」把这一家取消、回到默认。带「网关自带」那一行的家(目前是 ZCode 两家)默认就列在这里 —— 那一段是网关自己装上去的文本,不来自客户端也不来自提示词文件,默认开启、可以关掉,也可以改它的正文(改过的那一段以你的文本为准);关掉后还能不能通过上游校验,取决于上游当下的口径,行内说明了实测依据。「模式 / 提示词文件 / 正文」管的是客户端自己的 system 怎么处理,与那个开关是两件事。": "「어느 프롬프트를 쓸지」는 원래 업스트림별로 결정하는 일이므로, 제공자마다 자체 모드와 시스템 프롬프트 파일, 본문을 가질 수 있습니다. 여기에 나열되지 않은 제공자는 모두 위의 기본 설정을 따르며, 「기본값 따르기」를 선택하면 해당 제공자의 개별 설정이 취소되고 기본값으로 돌아갑니다. 「게이트웨이 내장」 항목이 있는 제공자(현재 ZCode 두 곳)는 기본적으로 여기에 나열됩니다. 그 단락은 게이트웨이가 직접 설치한 텍스트로, 클라이언트에서도 시스템 프롬프트 파일에서도 온 것이 아닙니다. 기본적으로 켜져 있으며 끌 수 있고 본문도 수정할 수 있습니다(수정한 단락은 입력한 텍스트가 우선). 끈 뒤에도 업스트림 검증을 통과할 수 있는지는 업스트림의 현재 기준에 달려 있으며, 실측 근거는 행 안에 설명되어 있습니다. 「모드 / 시스템 프롬프트 파일 / 본문」은 클라이언트 자체의 system을 어떻게 처리할지를 다루며, 그 스위치와는 별개의 일입니다.", + "「设置」是固定项,含它的分组不能整组隐藏;可以单独隐藏组内其它菜单": "\"설정\"은 고정 항목이라 그 그룹을 통째로 숨길 수 없습니다. 다른 메뉴는 개별로 숨길 수 있습니다", "「请求日志」页的逐条请求记录,默认 99999 天(近似全量保留),可填 1–99999 天。": "「요청 로그」페이지의 요청별 기록. 기본 99999일(거의 전체 보관)이며 1~99999일까지 입력할 수 있습니다.", "一段内置装配": "내장 기본 구성", "三档数据各自独立计时,超出保留天数的部分会被删除:事件日志是登录、账号切换、429 切换这类系统事件;请求日志是网关每次转发到上游的逐条记录;按天聚合供报表页的热力图与按天趋势使用。请求日志与按天聚合默认 99999 天(约 274 年,效果上等于全量保留),事件日志默认 30 天。把某一档改小(例如 30 天改成 7 天)保存后会立即删除超出的历史数据,此操作不可恢复;改大或保持不变不会删除任何数据。三档的可填范围均为 1–99999 天。": "세 가지 데이터는 각각 독립적으로 계산되며, 보관 일수를 초과한 부분은 삭제됩니다: 이벤트 로그는 로그인, 계정 전환, 429 전환 같은 시스템 이벤트이고, 요청 로그는 게이트웨이가 업스트림으로 전달할 때마다 남기는 기록이며, 일별 집계는 리포트 페이지의 히트맵과 일별 추세에 사용됩니다. 요청 로그와 일별 집계의 기본값은 99999일(약 274년, 사실상 전량 보관)이고 이벤트 로그의 기본값은 30일입니다. 어느 하나를 줄여(예: 30일 → 7일) 저장하면 초과된 과거 데이터가 즉시 삭제되며 되돌릴 수 없습니다. 늘리거나 그대로 두면 아무것도 삭제되지 않습니다. 세 항목 모두 입력 범위는 1~99999일입니다.", "三段各自成块,所以分开编辑": "세 단락이 각각 별도 블록이라 따로 편집합니다", "三段必须各自成块(上游按结构校验,并成一段会被回 405),所以这里分开编辑。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。": "", + "上传": "업로드", + "上传中…": "업로드 중…", + "上传内容含加密后的账号凭证与网关配置": "업로드 내용에는 암호화된 계정 자격 증명과 게이트웨이 설정이 포함됩니다", + "上传到云端": "클라우드로 업로드", + "上传失败:{message}": "업로드 실패: {message}", + "上次同步:{time}({action})": "마지막 동기화: {time} ({action})", "上次成功刷新的结果(存在本机数据库里)": "마지막으로 성공한 새로고침 결과(로컬 데이터베이스에 저장)", + "上游 CLI 版本": "업스트림 CLI 버전", + "上游客户端版本": "업스트림 클라이언트 버전", "上游按": "업스트림이 ", "上游按「结构」校验身份:三段必须各自成块,所以这里分开编辑三段(不能合成一段)。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。第三段发出时前面会自带一个空行(官方形状)。": "업스트림은 「구조」로 신원을 검증합니다. 세 단락이 각각 별도 블록이어야 하므로 여기서는 세 단락을 따로 편집합니다(하나로 합칠 수 없습니다). 동적 단락의 런타임 값은 플레이스홀더로 작성하며 요청을 보낼 때 치환됩니다: {cwd} 작업 디렉터리, {platform} 플랫폼, {shell} shell, {os_version} OS 버전, {git} git 저장소 여부, {provider} 제공자 식별자, {model} 모델 이름. 단락을 비우면 그 단락은 공식 원문으로 복원됩니다. 세 번째 단락은 전송 시 앞에 빈 줄이 하나 자동으로 붙습니다(공식 형태).", + "上游未配置": "업스트림 미설정", "上游模型繁忙时会先排队(回一句「建议 N 秒后再来」,Qoder 免费模型就是这样):网关等一会儿再发同一请求,最多等几次。默认 2 次,可填 0–10;填 0 表示不等待,排队直接返回 503 并说明「不是登录态或额度问题」。": "업스트림 모델이 바쁘면 먼저 큐잉합니다(「N초 후에 다시 오세요」라고 응답하며, Qoder 무료 모델이 이렇습니다). 게이트웨이는 잠시 기다렸다가 같은 요청을 다시 보내며, 최대 몇 번까지 기다립니다. 기본값 2회, 0–10 입력 가능하며, 0은 기다리지 않고 큐잉을 곧바로 503으로 반환하면서 「로그인 상태나 할당량 문제가 아닙니다」라고 설명합니다.", "上游用「逐字精确匹配」的方式审核请求体(不是语义审核):客户端注入的固定模板句、计费头字段名、以及某些裸错误码出现在报文里就会整单拦截,返回 400。开启本项后,网关在每次转发前改写这些指纹 —— 表头键值整段删除,承载语义的模板句只换一个词(如 official CLI for Claude → official CLI tool for Claude),对话内容与语义都不受影响。规则集是内置的,不需要也无法维护词表。关掉本项后客户端模板会原样发往上游,可能重新出现模板句被误拦的报错。": "업스트림은 요청 본문을 「글자 단위 정확 일치」 방식으로 검사합니다(의미 검사가 아닙니다). 클라이언트가 주입한 고정 템플릿 문구, 과금 헤더 필드 이름, 일부 날것의 오류 코드가 메시지에 나타나면 요청 전체가 차단되어 400을 반환합니다. 이 항목을 켜면 게이트웨이가 전달 전에 이런 핑거프린트를 매번 다시 씁니다. 헤더 키-값은 통째로 삭제하고, 의미를 담은 템플릿 문구는 단어 하나만 바꿉니다(예: official CLI for Claude → official CLI tool for Claude). 대화 내용과 의미는 영향을 받지 않습니다. 규칙 집합은 내장되어 있어 단어 목록을 유지할 필요도, 할 수도 없습니다. 이 항목을 끄면 클라이언트 템플릿이 그대로 업스트림으로 전송되어 템플릿 문구가 잘못 차단되는 오류가 다시 나타날 수 있습니다.", "上游请求四个阶段各自的等待上限(秒,1~3600):①「连接中超时」= 建立 TCP/TLS 连接或代理隧道的最大等待,默认 30 秒;②「等待响应超时」= 请求发出后等上游响应头的最大时间,默认 300 秒 —— 网关请求上游恒带 stream,响应头在 SSE 建立时就到达,与模型思考多久无关;③「流式响应空闲超时」= 流式响应相邻两块数据之间允许的最大空闲,收到新数据即重新计时,默认 300 秒 —— 上游长时间不吐数据即判定连接僵死并断开;④「非流式响应超时」= 读完整份非流式响应体的总预算(一次性计时、不重置),默认 300 秒。四项都是「等待上限」,只要数据还在来,正常的流式回答就一直往下传。保存后对下一个请求立即生效,不用重启。": "업스트림 요청의 네 단계별 대기 상한(초, 1~3600)입니다. ①「연결 타임아웃」= TCP/TLS 연결 또는 프록시 터널을 설정할 때 기다리는 최대 시간, 기본 30초. ②「응답 대기 타임아웃」= 요청을 보낸 뒤 업스트림 응답 헤더를 기다리는 최대 시간, 기본 300초. 게이트웨이는 업스트림에 항상 stream으로 요청하므로 응답 헤더는 SSE가 연결될 때 도착하며 모델이 얼마나 오래 생각하는지와는 무관합니다. ③「스트리밍 응답 유휴 타임아웃」= 스트리밍 응답의 인접한 두 데이터 블록 사이에 허용되는 최대 유휴 시간으로, 새 데이터를 받으면 다시 시간을 재며, 기본 300초입니다. 업스트림이 오랫동안 데이터를 내보내지 않으면 연결이 멈춘 것으로 판단해 끊습니다. ④「비스트리밍 응답 타임아웃」= 비스트리밍 응답 본문 전체를 읽는 총 예산(한 번만 시간을 재고 재설정하지 않음), 기본 300초. 네 항목 모두 「대기 상한」이며, 데이터가 계속 오는 한 정상적인 스트리밍 응답은 계속 전달됩니다. 저장 후 다음 요청부터 즉시 적용되며 재시작이 필요 없습니다.", + "下载失败:{message}": "다운로드 실패: {message}", + "不一致": "불일치", "不做会话识别,每条请求都重新按策略选路。": "세션을 식별하지 않고 요청마다 전략에 따라 다시 선택합니다.", "不可用": "사용 불가", "不在这里列出的提供商都用上面的全局配置;「跟随默认」取消这一家的单独配置。带「网关自带」的家列在这里:那段文本是网关自己装上去的(可关、可改正文),与「模式」是两件事。": "", + "与网关同源的面板;完整管理功能在桌面端": "게이트웨이와 같은 출처의 패널입니다. 전체 관리는 데스크톱에서 하세요", + "两张开关分别管桌面端(应用内窗口,也包含开启局域网访问后从其他设备访问的那份面板)与网页端(Docker / 服务端自托管的浏览器面板)。默认都开启;桌面端只在本机使用、嫌验证耗时可以关掉,网页端暴露在网络上,建议保持开启。": "두 스위치는 데스크톱 쪽(앱 내 창, LAN 액세스가 켜졌을 때 다른 기기에서 여는 패널 포함)과 웹 쪽(Docker / 서버가 호스팅하는 브라우저 패널)을 따로 제어합니다. 기본값은 둘 다 켜짐입니다. 로컬에서만 쓰고 인증 대기 시간이 부담되면 데스크톱 쪽을 꺼도 되지만, 네트워크에 노출되는 웹 쪽은 켜 두는 것을 권장합니다.", + "两次输入的新密码不一致": "새 비밀번호가 일치하지 않습니다", "两次重试之间的等待时间,两项共用,可填 0–300 秒。": "두 재시도 사이의 대기 시간이며 두 항목이 공유합니다. 0–300초를 입력할 수 있습니다.", "为了安全,开启前需要先注册一个": "보안을 위해, 켜기 전에 다음을 먼저 등록해야 합니다: ", "主进程未返回启动设置,请更新桌面端后重试": "메인 프로세스가 시작 설정을 반환하지 않았습니다. 데스크톱 앱을 업데이트한 후 다시 시도하세요", "主进程桥不可用": "메인 프로세스 브리지를 사용할 수 없습니다", + "主题": "테마", "事件日志": "이벤트 로그", "事件日志保留天数": "이벤트 로그 보관 일수", + "云同步": "클라우드 동기화", + "人机验证": "사람 확인", + "人机验证失败": "사람 확인 실패", + "人机验证已完成": "사람 확인 완료", + "人机验证还在计算,请稍候": "사람 확인을 계산 중입니다. 잠시 기다려 주세요", "仅用现有数据、不联网刷新": "보유 데이터만 사용, 온라인 갱신 안 함", + "从云端恢复": "클라우드에서 복원", + "从桌面图标启动,全屏、无地址栏。想卸载:长按图标删除,或清除浏览器站点数据。": "홈 화면 아이콘으로 실행합니다. 전체 화면, 주소 표시줄 없음. 삭제하려면 아이콘을 길게 눌러 제거하거나 브라우저 사이트 데이터를 지우세요.", + "会恢复默认的分组、顺序与显隐设置。你自建的分组会被删除(组内菜单不会丢)。": "그룹·순서·표시 설정을 기본값으로 되돌립니다. 직접 만든 그룹은 삭제됩니다(메뉴는 유지됩니다).", + "位置": "위치", + "位置与端口": "위치 및 포트", "使用中文单位(亿 / 万)": "중국어 단위 사용 (억 / 만)", "使用本地量级词(万 / 亿式)": "로컬 규모 단어 사용(만 / 억 단위)", + "侧边栏布局模块未加载(ui/sidebar-layout.js 应在 app.js 之前引入)。": "사이드바 레이아웃 모듈이 로드되지 않았습니다(ui/sidebar-layout.js는 app.js보다 먼저 포함되어야 합니다).", + "侧边栏没有显示任何菜单。到「设置 → 显示 → 侧边栏菜单」里打开需要的项。": "사이드바에 표시되는 메뉴가 없습니다. 설정 → 표시 → 사이드바 메뉴에서 필요한 항목을 켜세요.", + "侧边栏菜单": "사이드바 메뉴", "保存上游原始报文(请求头、请求体、响应头、响应体)": "업스트림 원본 메시지 저장(요청 헤더, 요청 본문, 응답 헤더, 응답 본문)", "保存后以这份为准(优先于上面的文件);清空回到文件 / 内置默认。": "", "保存后以这里的文本为准": "저장하면 여기의 텍스트가 우선합니다", @@ -104,16 +174,21 @@ "保存后立即生效,不用重启;超时按上游失败处理(计入请求日志的「错误」列,并按「请求重试」的设置决定是否重试)。": "저장 후 재시작 없이 즉시 적용됩니다. 타임아웃은 업스트림 실패로 처리됩니다(요청 로그의 「오류」 열에 집계되며, 「요청 재시도」 설정에 따라 재시도 여부가 결정됩니다).", "保存后立即生效:改小保留天数会立刻删除超出的历史数据,且不可恢复。": "저장 후 즉시 적용됩니다. 보관 일수를 줄이면 초과된 과거 데이터가 즉시 삭제되며 복구할 수 없습니다.", "保存并重启": "저장 후 재시작", + "保活换行丢弃": "keepalive 개행 제거", "保留天数已刷新": "보관 일수를 새로 고쳤습니다", + "修改密码": "비밀번호 변경", + "修改密码需要输入当前密码;修改成功后,其他设备 / 浏览器上的登录会立即失效,当前这条会话保留。": "비밀번호 변경에는 현재 비밀번호가 필요합니다. 성공하면 다른 기기·브라우저의 로그인은 즉시 해제되고 현재 세션만 유지됩니다.", "值": "값", "值与默认值相同的行不占覆盖(存的是「改过的键」,不是全量配置);值留空保存表示这个头不发送,删除改过的行表示回落默认值。改完立即生效,不用重启。": "기본값과 같은 값의 행은 덮어쓰기에 포함되지 않습니다(저장되는 것은 「수정된 키」이며 전체 설정이 아닙니다). 값을 비워 저장하면 해당 헤더를 전송하지 않고, 수정한 행을 삭제하면 기본값으로 돌아갑니다. 수정 후 재시작 없이 즉시 적용됩니다.", "值(留空 = 不发送)": "값(비우면 = 전송 안 함)", "允许局域网内的设备访问网关": "LAN 내 기기의 게이트웨이 접근 허용", + "允许无 Key 裸跑": "키 없이 실행 허용", "允许浏览器里的页面跨来源调用网关(/v1/*)": "브라우저 페이지가 교차 출처로 게이트웨이를 호출하도록 허용(/v1/*)", "全局配置": "전역 설정", "全部已配置": "모두 설정됨", "全部数据(账号、事件日志、请求记录、调试报文、设置)保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "", "全部数据(账号、事件日志、请求记录、调试报文、设置)统一保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "모든 데이터(계정, 이벤트 로그, 요청 기록, 디버그 메시지, 설정)는 구성 디렉터리의 agent2api.db라는 하나의 SQLite 데이터베이스에 통합 저장됩니다. 백업할 때는 이 파일만 복사하면 됩니다. 저장 위치를 바꾸려면 환경 변수 AGENT2API_PROXY_HOME을 설정한 후 프로그램을 다시 시작하세요.", + "关闭后手动上传 / 下载也会被拒绝。": "끄면 수동 업로드/다운로드도 거부됩니다.", "关闭后网关回到只监听 127.0.0.1,局域网内的设备将无法继续访问;已配置的网关 Key 与账号都不受影响。": "끄면 게이트웨이가 다시 127.0.0.1만 수신하며 LAN 내 기기는 더 이상 접근할 수 없습니다. 이미 설정한 게이트웨이 키와 계정은 영향을 받지 않습니다.", "关闭后,管理界面不再从局域网提供,只有本机的桌面程序可以管理;已开启的 API 转发不受影响。": "끄면 관리 화면이 더 이상 LAN에 제공되지 않고 로컬 데스크톱 앱에서만 관리할 수 있습니다. 이미 켜 둔 API 포워딩은 영향을 받지 않습니다.", "关闭局域网访问": "LAN 접근 끄기", @@ -124,16 +199,30 @@ "其余沿用上面的全局配置": "", "内容拦截降级": "콘텐츠 차단 폴백", "内置默认提示词": "내장 기본 시스템 프롬프트", + "再输入一次新密码": "", "凭据类请求头已脱敏。": "자격 증명 계열 요청 헤더는 마스킹되었습니다.", + "凭证只保存在这台手机上,按服务器分别存放": "자격 증명은 이 기기에만 저장되며 서버별로 따로 보관됩니다", + "出问题时把这一页截图,或点「复制全部信息」把文本贴进反馈,一般就能定位到大致原因。": "문제가 생기면 이 페이지를 캡처하거나 \"모든 정보 복사\"로 텍스트를 복사해 제보에 붙여 주세요. 대개 그것만으로 원인을 좁힐 수 있습니다.", + "分享图标在 Safari 底部工具栏中间": "공유 아이콘은 Safari 하단 도구 막대 가운데에 있습니다", + "分组名": "그룹 이름", + "分组名,例如「常用」": "그룹 이름, 예: \"즐겨찾기\"", "切换账号重试次数": "계정 전환 재시도 횟수", "刚刚从 models.dev 拉到的最新数据": "models.dev에서 방금 가져온 최신 데이터", "删除 {code}": "{code} 삭제", + "删除分组": "그룹 삭제", + "删除分组「{name}」?组内 {n} 个菜单会移到「{target}」,不会丢。": "그룹 \"{name}\"을(를) 삭제할까요? 메뉴 {n}개는 \"{target}\"(으)로 이동하며 사라지지 않습니다.", "删除状态码 {code}": "상태 코드 {code} 삭제", - "刷新": "새로 고침", + "删除空分组「{name}」?": "빈 그룹 \"{name}\"을(를) 삭제할까요?", + "删除这个分组(组内菜单移到第一组)": "이 그룹 삭제(메뉴는 첫 그룹으로 이동)", + "刷新": "새로고침", "刷新失败:{message}": "새로고침 실패: {message}", + "前台运行": "포그라운드 실행", "剥离上游审核黑名单指纹(改写请求体里的模板句与表头)": "업스트림 검열 블랙리스트 핑거프린트 제거(요청 본문의 템플릿 문구와 헤더 재작성)", "功能建议": "기능 제안", "包含日志与调试报文": "로그와 디버그 페이로드 포함", + "包含:{items}": "포함: {items}", + "区域(Region)": "리전(Region)", + "华为云 OBS": "화웨이 클라우드 OBS", "单轮铸造上限": "회당 발행 한도", "占用大小": "사용 용량", "去反馈": "신고하기", @@ -144,25 +233,48 @@ "变更网页管理面板": "웹 관리 패널 변경", "只列单独配置过的家,其余沿用上面的全局配置": "개별 설정한 제공자만 표시되며, 나머지는 위의 전역 설정을 따릅니다", "只对「排队制」的上游生效(目前是 Qoder 的免费模型):模型繁忙时上游不报错,只回一句「建议 N 秒后再来」(业务码 10605),网关按建议时长等一会儿再发同一请求,等满次数仍排不上才把「排队中」作为错误返回(HTTP 503,文案会说明这不是登录态或额度问题)。等待发生在首个字节之前,吃的是「等待响应超时」那份预算 —— 两项设置一起决定一次请求最多卡多久;排队不会标记账号限额、也不会换账号(换谁都一样在排队)。保存后对下一个请求立即生效,不用重启。": "「큐잉 방식」 업스트림에만 적용됩니다(현재 Qoder 무료 모델). 모델이 바쁠 때 업스트림은 오류를 내지 않고 「N초 후에 다시 오세요」(비즈니스 코드 10605)라고만 응답합니다. 게이트웨이는 안내된 시간만큼 기다렸다가 같은 요청을 다시 보내며, 횟수를 다 채울 때까지 큐에 들어가지 못하면 「큐잉 중」을 오류로 반환합니다(HTTP 503, 이는 로그인 상태나 할당량 문제가 아니라는 설명이 함께 표시됩니다). 대기는 첫 바이트 이전에 발생하며 「응답 대기 타임아웃」 예산을 소모합니다. 두 설정이 함께 요청 하나가 최대 얼마나 지연될 수 있는지를 결정합니다. 큐잉은 계정에 할당량 초과를 표시하지 않고 계정도 바꾸지 않습니다(누구든 마찬가지로 큐에 대기합니다). 저장 후 다음 요청부터 즉시 적용되며 재시작이 필요 없습니다.", + "只清掉这个浏览器存的 Key,不影响网关": "이 브라우저에 저장된 Key만 삭제합니다. 게이트웨이는 영향 없음", + "可回滚到上一版本": "이전 버전으로 롤백 가능", + "合计只累加当前筛选后可见的账号,筛选、搜索一改数字就跟着变。": "합계는 현재 필터에서 보이는 계정만 더합니다. 필터나 검색을 바꾸면 숫자도 함께 바뀝니다.", + "合计按整组统计(默认):不受筛选、搜索影响,被筛掉与已禁用的账号也计入。": "합계는 전체 그룹 기준입니다(기본값). 필터와 검색의 영향을 받지 않으며, 필터에서 제외된 계정과 비활성 계정도 포함됩니다.", "同一会话在 5 分钟内尽量跟住同一个账号;停了 5 分钟再发就算新会话。多数场景用它 —— 多轮对话少换号,也不至于把账号焊死。": "같은 세션은 5 분 안에 가능한 한 같은 계정을 유지하고, 5 분 이상 비면 새 세션으로 간주합니다. 대부분의 상황에 권장 — 여러 턴의 대화에서 계정이 자주 바뀌지 않으면서 특정 계정에 고정되지도 않습니다.", "同一会话在 6 小时内跟住同一个账号;适合一坐几个小时的长会话,上游的上下文缓存能吃到更多。": "같은 세션은 최대 6 시간 동안 같은 계정을 유지합니다. 몇 시간씩 이어지는 긴 세션에 적합하며, 업스트림 컨텍스트 캐시를 훨씬 더 활용할 수 있습니다.", "同一账号重试次数": "동일 계정 재시도 횟수", "同时开放网页管理面板": "웹 관리 패널도 함께 공개", + "同步密码": "동기화 비밀번호", "名单最多 {max} 个状态码": "목록에는 상태 코드를 최대 {max}개까지 넣을 수 있습니다", + "后端没有返回版本信息": "백엔드가 버전 정보를 반환하지 않았습니다", + "含固定项「设置」,不可整组隐藏(可以单独隐藏组内其它菜单)": "고정 항목 \"설정\"이 있어 그룹 전체를 숨길 수 없습니다(다른 메뉴는 개별로 숨길 수 있습니다)", "启动与托盘": "시작 및 트레이", + "启动时间": "시작 시각", + "启用云同步": "클라우드 동기화 사용", "命中这些上游状态码的失败不在同一账号重发,直接换下一个账号继续试(换号占用「切换账号重试次数」,换满仍失败才把错误原样返回给客户端)。输入 100–599 的状态码后回车添加,点 × 或退格删除;默认 402(积分不足)。清空全部表示任何错误都照常重试。": "이 업스트림 상태 코드에 해당하는 실패는 같은 계정에서 다시 보내지 않고 바로 다음 계정으로 넘어가 계속 시도합니다(계정 전환은 「계정 전환 재시도 횟수」를 소모하며, 횟수를 다 써도 실패하면 오류가 그대로 클라이언트에 반환됩니다). 100–599 사이의 상태 코드를 입력하고 Enter를 누르면 추가되고, ×를 누르거나 백스페이스로 삭제합니다. 기본값은 402(크레딧 부족)입니다. 모두 비우면 모든 오류를 평소처럼 재시도합니다.", + "固定": "고정", "在同一个账号上原地重发几次(不含首发;整条请求共用一份,用完后换来的账号只发一次就继续换)。可填 0–10,0 表示失败立即换号。": "같은 계정에서 제자리로 재전송하는 횟수입니다(최초 전송은 제외. 요청 전체가 이 횟수를 공유하며, 소진 후 전환된 계정은 한 번만 보내고 다시 전환합니다). 0–10 입력 가능하며, 0이면 실패 시 즉시 계정을 전환합니다.", "在这里直接编辑提示词正文:保存后以这份文本为准(优先于上面的提示词文件),清空则回到文件 / 内置默认。改一个字不必再去编辑器里开文件。": "여기서 시스템 프롬프트 본문을 직접 편집합니다. 저장하면 이 텍스트가 우선하며(위의 시스템 프롬프트 파일보다 우선), 비우면 파일 / 내장 기본값으로 돌아갑니다. 한 글자만 고치려고 편집기에서 파일을 열 필요가 없습니다.", + "在途请求": "처리 중 요청", + "地址格式不对。填「IP 或域名:端口」,可带 http:// 或 https://,例如 192.168.1.23:3065": "주소 형식이 올바르지 않습니다. \"IP 또는 도메인:포트\"로 입력하세요 (http:// 또는 https:// 사용 가능). 예: 192.168.1.23:3065", + "坚果云": "Jianguoyun(Nutstore)", + "坚果云需使用「第三方应用密码」(账户信息 → 安全选项里生成),地址必须是 /dav/ 下的目录。": "Jianguoyun은 '타사 앱 비밀번호'(계정 정보 → 보안에서 생성)를 사용해야 하며, 주소는 /dav/ 아래 디렉터리여야 합니다.", + "填「IP 或域名:端口」。协议可写 http:// 或 https://,不写默认 http;http 不写端口默认 3065,https 默认 443。地址里的 /m、/v1 会自动去掉。": "\"IP 또는 도메인:포트\"를 입력하세요. 프로토콜은 http:// 또는 https://를 쓸 수 있으며(생략 시 http), 포트를 생략하면 http는 3065, https는 443이 됩니다. 주소의 /m, /v1은 자동으로 제거됩니다.", "备份中…": "백업 중…", "备份整库": "데이터베이스 백업", "备份文件内含全部账号凭证、网关 API Key 与面板管理员凭据,可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "백업 파일에는 모든 계정 자격 증명, 게이트웨이 API 키, 패널 관리자 자격 증명이 들어 있어 그대로 로그인에 사용할 수 있습니다. 안전하게 보관하고 외부로 유출하거나 공개된 곳에 업로드하지 마세요.", + "复制全部信息": "모든 정보 복사", + "复制给客户端": "클라이언트용으로 복사", + "外观": "화면", + "外观 · 接入 · 关于": "화면 · 연결 · 정보", "天": "일", "天数": "일수", "失败 {n} 个": "실패 {n}개", "失败 {n} 个:{detail}": "실패 {n}개: {detail}", "失败后最多再换几个账号试(填 N = 最多换 N 个,首发那个不算)。换到同一家名下另一个账号、或换到另一家,都各算一次;换满仍失败就返回错误。可填 0–10,0 表示失败立即报错。": "실패 후 최대 몇 개의 계정으로 더 시도할지 정합니다(N 입력 = 최대 N개 전환, 최초 계정은 제외). 같은 제공자의 다른 계정으로 바꾸거나 다른 제공자로 바꾸는 것 모두 1회로 계산하며, 횟수를 다 써도 실패하면 오류를 반환합니다. 0–10 입력 가능하며, 0이면 실패 시 즉시 오류를 반환합니다.", + "存储桶(Bucket)": "버킷(Bucket)", "存储概况已刷新": "저장소 개요를 새로 고쳤습니다", "安全": "보안", + "安装到主屏幕": "홈 화면에 추가", + "安装到桌面": "앱으로 설치", + "安装包形态": "설치 프로그램 종류", "官方原文只有一句:声明自己是 ZCode。": "공식 원문은 한 문장뿐입니다: 자신이 ZCode임을 선언합니다.", "客户端 system 原样出站。": "클라이언트 system을 그대로 전송합니다.", "客户端 system 原样出站(只靠指纹脱敏改写模板句)。": "클라이언트 system을 그대로 전송합니다(핑거프린트 마스킹으로 템플릿 문구만 재작성).", @@ -170,19 +282,27 @@ "客户端(Claude Code / Codex 等 CLI)会往 system 里塞几十句固定模板,上游逐字审核,命中就整单拦截(HTTP 400)。「模式」决定网关怎么处理这份 system:透传 = 原样不动(默认);替换 = 删掉客户端的 system / developer,换成网关提示词;追加 = 保留客户端内容,只在最前面插一条网关提示词。网关提示词来自「提示词文件」或界面里编辑的正文(正文优先,清空回到文件 / 内置默认)。撞上拦截时会自动改用一段最小中性提示词,次日 00:00 自动恢复,也可以随时解除。": "", "家之间仍按优先级,只有同一家内部轮流用各账号 —— 想优先用某几家、又不愿把某一家的某个账号焊死时用它。": "제공자 간에는 우선순위를 따르고, 같은 제공자 내에서만 계정이 순환합니다 — 몇몇 제공자를 우선하되 특정 계정에 고정하고 싶지 않을 때 사용합니다.", "家内轮询": "제공자 내 순환", + "容器内更新": "컨테이너 내 업데이트", "密码至少 8 位": "비밀번호는 8자리 이상", + "导入前会把本机的 account.key 改名备份(account.key.bak-时间戳),不会直接删除。": "가져오기 전에 로컬 account.key를 account.key.bak-<타임스탬프>로 이름을 바꿔 백업하며, 삭제하지는 않습니다.", "导入完成:{summary}": "가져오기 완료: {summary}", + "导入密钥失败:{message}": "키 가져오기 실패: {message}", + "导入密钥并继续": "키 가져오고 계속", "导入账号": "계정 가져오기", "导出中…": "내보내는 중…", "导出会把全部账号与自定义提供商定义写入一个 JSON 文件,可以拷到另一台机器上导入后继续使用。导入采用合并策略:同提供商下按业务身份(UID / userId / apiKey 等)去重 —— 已存在的账号只更新凭证,保留本机原有的优先级顺序;新账号追加到转发顺序末尾,不会抢占当前正在使用的账号;自定义提供商定义按 id 合并,本机缺失时自动补建。": "내보내기는 모든 계정과 커스텀 제공자 정의를 하나의 JSON 파일에 기록하며, 다른 컴퓨터로 복사해 가져온 뒤 계속 사용할 수 있습니다. 가져오기는 병합 방식을 사용합니다. 같은 제공자 아래에서는 비즈니스 신원(UID / userId / apiKey 등)으로 중복을 제거합니다. 이미 있는 계정은 자격 증명만 업데이트하고 이 컴퓨터의 기존 우선순위 순서를 유지하며, 새 계정은 포워딩 순서 끝에 추가되어 현재 사용 중인 계정을 밀어내지 않습니다. 커스텀 제공자 정의는 id 기준으로 병합하고, 이 컴퓨터에 없으면 자동으로 생성합니다.", "导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义": "내보내기 파일에 accessToken / refreshToken / apiKey 등 자격 증명과 커스텀 제공자 정의가 포함됩니다", "导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义,可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "내보내기 파일에는 accessToken / refreshToken / apiKey 등 자격 증명과 커스텀 제공자 정의가 포함되어 있으며, 바로 로그인에 사용할 수 있습니다. 안전하게 보관하고 외부에 전달하거나 공공장소에 업로드하지 마세요.", "导出账号": "계정 내보내기", + "尚未注册管理员:管理接口不需要登录。开启「允许局域网访问」时会引导注册,也可以从登录页注册。": "관리자가 등록되지 않았습니다: 관리 API에는 로그인이 필요하지 않습니다. \"LAN 액세스 허용\"을 켜면 등록으로 안내되며, 로그인 페이지에서도 등록할 수 있습니다.", "局域网开放后,管理接口要求登录。请设置管理员账号与密码,注册完成会直接继续开启流程。": "LAN에 공개하면 관리 인터페이스가 로그인을 요구합니다. 관리자 계정과 비밀번호를 설정하세요. 등록이 완료되면 바로 이어서 활성화 절차가 진행됩니다.", "局域网访问": "LAN 접근", + "展开或收起设置分类": "", "工具用法与环境说明那一大段。": "도구 사용법과 환경 설명에 해당하는 긴 단락입니다.", "已为「{label}」单独配置提示词": "「{label}」에 시스템 프롬프트를 개별 설정했습니다", + "已作为独立应用运行": "독립 앱으로 실행 중", "已保存 {count} / {limit} 条报文(超出后丢弃最旧的)。": "메시지 {count} / {limit}건을 저장했습니다(초과분은 오래된 것부터 버립니다).", + "已保存,留空沿用": "저장됨, 비워 두면 유지", "已关闭": "꺼짐", "已关闭 models.dev 联网刷新": "models.dev 온라인 갱신을 껐습니다", "已关闭。{note}": "꺼짐. {note}", @@ -190,26 +310,43 @@ "已关闭出站指纹脱敏": "아웃바운드 핑거프린트 마스킹을 껐습니다", "已关闭开发者角色兼容": "개발자 역할 호환이 꺼졌습니다", "已关闭网关跨域访问": "게이트웨이 CORS 접근을 껐습니다", + "已删除分组「{name}」": "그룹 \"{name}\"을(를) 삭제했습니다", "已取消「{label}」的单独配置": "「{label}」의 개별 설정을 취소했습니다", + "已取消安装": "설치를 취소했습니다", "已取消导入": "가져오기를 취소했습니다", "已取消导出": "내보내기를 취소했습니다", + "已取消恢复": "복원이 취소되었습니다", + "已启用登录": "로그인 사용 중", + "已复制全部版本信息": "모든 버전 정보를 복사했습니다", "已应用": "반영됨", "已开启": "켜짐", "已开启:其他设备把 API 地址指向 {url}{panel}。": "켜짐: 다른 기기에서 API 주소를 {url}(으)로 지정하면 됩니다{panel}.", + "已恢复默认分组:概览 / 转发链路 / 诊断 / 系统": "기본 그룹으로 복원했습니다: 개요 / 전달 경로 / 진단 / 시스템", + "已指定出口": "출구 지정됨", "已接管": "인계됨", "已改回内置默认提示词": "내장 기본 시스템 프롬프트로 되돌렸습니다", + "已新建分组「{name}」,点组头的 + 把菜单移进来": "그룹 \"{name}\"을(를) 만들었습니다. 머리의 +로 메뉴를 가져오세요", "已清掉「{label}」界面编辑的正文": "「{label}」의 화면 편집 본문을 삭제했습니다", "已清掉界面编辑的提示词(回落文件 / 内置默认)": "화면에서 편집한 시스템 프롬프트를 삭제했습니다(파일 / 내장 기본값으로 폴백)", + "已清除本机保存的 API Key": "이 브라우저에 저장된 API Key를 삭제했습니다", "已生效": "적용됨", "已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。": "최소 중립 프롬프트로 자동 전환했습니다(업스트림 콘텐츠 차단에 걸린 것으로, 대부분 system 지문 오탐입니다). {until} 에 자동 해제됩니다.", "已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。": "최소 중립 시스템 프롬프트로 자동 전환했습니다(업스트림 콘텐츠 차단에 걸렸으며 대부분 system 핑거프린트 오탐입니다). {until}에 자동 해제됩니다. 기간 중에는 이 모드의 시스템 프롬프트가 전송되지 않습니다. 시스템 프롬프트를 수정한 뒤 즉시 해제할 수 있습니다.", "已自动改用最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。": "", + "已运行": "가동 시간", + "已退出登录": "로그아웃했습니다", + "已隐藏": "숨김", "巴西葡萄牙语": "브라질 포르투갈어", "希望接入新的提供商或模型": "새 제공자나 모델 연동을 원함", + "库中 schema": "DB 내 schema", "库主文件的大小(不含运行期间的 WAL 临时文件,退出时已自动并回)。": "DB 본체 파일의 크기입니다(실행 중의 WAL 임시 파일은 포함하지 않으며, 종료 시 자동으로 병합됩니다).", "库存目标": "재고 목표", + "应用版本:{version}": "앱 버전: {version}", + "应用状态": "앱 상태", + "应用窗口": "앱 창", "建立上游 TCP/TLS 连接或代理隧道的最大等待时间(秒),默认 30 秒,范围 1~3600。": "업스트림 TCP/TLS 연결 또는 프록시 터널을 설정할 때 기다리는 최대 시간(초). 기본 30초, 범위 1~3600.", - "开发者角色兼容": "개발자 역할 호환", + "开发构建": "개발 빌드", + "开发者角色兼容": "developer 역할 호환", "开发者角色兼容设置已刷新": "개발자 역할 호환 설정이 새로 고쳐졌습니다", "开启后新发生的请求才会被记录(已经过去的请求补不回来)。报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。": "켠 이후에 발생한 요청만 기록됩니다(과거 요청은 소급되지 않음). 페이로드는 로컬 데이터베이스에 저장되며 최근 500 건만 유지합니다(초과분은 오래된 것부터 버림). 건수는 왼쪽 \"데이터 → 데이터 저장\"의 \"디버그 페이로드\"에서 볼 수 있습니다.", "开启后新发生的请求才会被记录(已经过去的请求补不回来),请求日志的「预览对话」也随之开始保存原始正文。上游报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。": "켜면 이후 새로 발생하는 요청만 기록됩니다(이미 지나간 요청은 소급 기록되지 않습니다). 요청 로그의 「대화 미리보기」도 이때부터 원문을 저장하기 시작합니다. 업스트림 메시지는 로컬 데이터베이스에 통합 저장되며 최근 500건만 보관합니다(초과분은 오래된 것부터 버립니다). 건수는 왼쪽 「데이터 → 데이터 저장」의 「디버그 메시지」에서 확인할 수 있습니다.", @@ -218,111 +355,186 @@ "开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 活动套餐」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。与「开机自动启动」同时开启时,开机后也不建界面(只留托盘与后台进程),点托盘才打开 —— 效果与关窗后一致。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。": "", "开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 活动套餐」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。": "켜면 창을 닫을 때 단순히 숨기는 것이 아니라 인터페이스 창을 통째로 파괴합니다. 기반이 되는 WebView2 상주 프로세스 6개(약 200MB 메모리)도 함께 종료되며, 게이트웨이 포워딩, 예약 출석 체크, 잔액 조회는 백그라운드 프로세스에서 계속 실행되어 영향을 받지 않습니다. 인터페이스가 필요하면 트레이 아이콘을 클릭해 다시 열면 됩니다(WebView 콜드 스타트 약 1초). 주의: 인터페이스가 파괴된 동안 「ZCode 활동 요금제」 채널의 인증 코드 토큰은 인터페이스가 발급하므로 보충할 수 없습니다. 이 채널을 사용하는 요청은 503으로 요청 로그에 기록되며(사유 표시), 인터페이스를 다시 열면 자동으로 복구됩니다. 이 스위치는 「창을 닫을 때 트레이로 최소화」가 켜져 있을 때만 의미가 있으며, 창 최소화는 영향을 받지 않습니다.", "开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。": "켜면 로그인 페이지가 브라우저 백그라운드에서 자동으로 검증을 완료합니다(실제 사람은 느낄 수 없음). 반면 스크립트는 시도할 때마다 문제를 풀어야 하므로 무차별 대입 공격과 선점 등록의 비용이 크게 올라갑니다. 패널의 로그인 / 가입에만 영향을 주며 API 클라이언트의 API Key와는 무관합니다.", + "开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。桌面端与网页端各自独立:桌面端主要服务本机与局域网,网页端(Docker / 服务端)直接暴露在网络上的登录页风险更高,建议保持开启。": "켜면 로그인 페이지가 백그라운드에서 인증을 마칩니다(사람에게는 보이지 않음). 반면 스크립트 시도는 매번 문제를 하나 풀어야 하므로 무차별 대입과 계정 선점 비용이 크게 올라갑니다. 패널의 로그인 / 등록에만 영향을 주며 API 클라이언트의 API Key와는 무관합니다. 데스크톱과 웹은 서로 독립적입니다. 데스크톱은 주로 이 컴퓨터와 LAN을 대상으로 하고, 웹(Docker / 서버)은 네트워크에 노출되므로 켜 두는 것을 권장합니다.", "开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。": "켜면 게이트웨이가 전송마다 **업스트림으로 보낸 요청**(헤더 + 본문)과 **업스트림이 돌려준 응답**(상태 코드 + 헤더 + 본문)을 로컬에 통째로 저장해 요청 로그 페이지의 \"상세\"에서 볼 수 있게 합니다. 요청 헤더의 Authorization, Cookie, API 키 등 자격 증명 필드는 모두 [redacted] 로 대체되어 평문으로 저장되지 않으며, 요청 / 응답 본문은 원본 그대로 저장됩니다(대화 내용이 포함될 수 있음). 페이로드는 최근 500 건만 유지하고 초과분은 오래된 것부터 버립니다. 문제 해결용 임시 스위치이므로 필요 없을 때는 끄는 것을 권장합니다.", "开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看;请求日志详情的「预览对话」也依赖本开关 —— 它保存的是客户端发来的请求体与网关下发的响应体(两侧各截断到 128 KiB),不开这个开关时预览对话没有数据可看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。上游报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。": "켜면 게이트웨이가 각 전달 시 **업스트림으로 보낸 요청**(요청 헤더 + 요청 본문)과 **업스트림이 반환한 응답**(상태 코드 + 응답 헤더 + 응답 본문)을 로컬에 그대로 저장하여 요청 로그 페이지의 「상세」에서 볼 수 있게 합니다. 요청 상세의 「대화 미리보기」도 이 스위치에 의존합니다 —— 저장되는 것은 클라이언트가 보낸 요청 본문과 게이트웨이가 내려보낸 응답 본문(각각 128 KiB로 잘림)이며, 스위치가 꺼져 있으면 미리 볼 데이터가 없습니다. 요청 헤더의 Authorization, Cookie, API Key 등 자격 증명 필드는 모두 [redacted]로 치환되어 평문으로 디스크에 기록되지 않습니다. 요청 본문과 응답 본문은 원본 그대로 저장됩니다(대화 내용이 포함될 수 있습니다). 업스트림 메시지는 최근 500건만 보관하며 초과분은 오래된 것부터 버립니다. 장애 진단용 임시 스위치이므로 필요 없으면 끄는 것을 권장합니다.", "开启局域网访问": "LAN 접근 켜기", "开放后,局域网内其他设备的浏览器打开本机 IP 即可进入管理面板(需管理员账号登录)。": "공개하면 LAN 내 다른 기기의 브라우저에서 로컬 IP를 열면 관리 패널에 들어갈 수 있습니다(관리자 계정 로그인 필요).", "开机自动启动": "자동 시작", + "开(最多 {n} 轮)": "켜짐(최대 {n}회)", "当前三段都是官方原文。": "현재 세 단락 모두 공식 원문입니다.", "当前固定为浅色模式,不随系统变化。": "현재 라이트 모드로 고정되어 있으며 시스템 설정을 따르지 않습니다.", "当前固定为深色模式,不随系统变化。": "현재 다크 모드로 고정되어 있으며 시스템 설정을 따르지 않습니다.", + "当前密码": "현재 비밀번호", "当前已改过:{edited}。": "현재 수정된 단락: {edited}.", + "当前形态": "실행 형태", "当前按 100% 显示(默认比例)。": "현재 100%로 표시됩니다(기본 비율).", "当前按 k / M 缩写显示(如 1.20M / 8.4k)。": "현재 k / M 축약으로 표시됩니다(예: 1.20M / 8.4k).", "当前按 {n}% 显示。": "현재 {n}%로 표시됩니다.", "当前按「万 / 亿」式本地量级词显示(如 1.2亿 / 8400万)。": "현재 「만 / 억」 단위의 로컬 규모 단어로 표시됩니다(예: 1.2억 / 8400만).", "当前是透传模式:存下来也不会发出,切成「替换 / 追加」之后才生效。": "현재 패스스루 모드입니다. 저장해도 전송되지 않으며, 「교체 / 추가」로 전환한 후에 적용됩니다.", - "当前显示为「1.20M / 8.4k」这类英文缩写。": "현재는 \"1.20M / 8.4k\" 같은 영문 약어로 표시됩니다.", + "当前显示为「1.20M / 8.4k / 1.2B」这类英文缩写。": "현재 \"1.20M / 8.4k / 1.2B\" 같은 영문 약어로 표시 중입니다.", "当前显示为「1.2亿 / 8400万」这类中文量级。": "현재는 \"1.2억 / 8400만\" 같은 중국어 단위 표기로 표시됩니다.", "当前浏览器以管理员身份登录着本面板。「退出登录」会撤销这台设备的登录会话(30 天内的自动续期一并失效),需要重新输入账号密码;其他已登录的设备不受影响。": "현재 브라우저가 관리자로 이 패널에 로그인되어 있습니다. 「로그아웃」은 이 기기의 로그인 세션을 해제하며(30일 이내의 자동 갱신도 함께 무효화), 계정과 비밀번호를 다시 입력해야 합니다. 이미 로그인한 다른 기기는 영향을 받지 않습니다.", + "当前环境不支持修改机器人校验设置": "", "当前环境不支持局域网访问设置": "현재 환경에서는 LAN 접근 설정을 지원하지 않습니다", + "当前环境不支持整库备份": "이 환경에서는 전체 DB 백업을 지원하지 않습니다", "当前环境不支持整库备份(仅桌面端可用)": "이 환경에서는 전체 DB 백업을 지원하지 않습니다(데스크톱 전용)", + "当前环境不支持整库恢复": "이 환경에서는 전체 DB 복원을 지원하지 않습니다", "当前环境不支持整库恢复(仅桌面端可用)": "이 환경에서는 전체 DB 복원을 지원하지 않습니다(데스크톱 전용)", + "当前环境不支持读取版本信息(桥接方法缺失)": "이 환경에서는 버전 정보를 읽을 수 없습니다(브리지 메서드 없음)", + "当前环境不支持退出面板登录": "", "当前生效的正文来自{source}{file}。保存后这一份会盖过提示词文件与内置默认{note}。": "현재 적용 중인 본문은 {source}에서 가져옵니다{file}. 저장하면 이 본문이 시스템 프롬프트 파일과 내장 기본값을 덮어씁니다{note}.", + "当前界面": "현재 화면", + "当前管理员:{name}": "현재 관리자: {name}", "当前跟随操作系统的深浅色设置,系统切换时界面会自动跟上。": "현재 운영 체제의 라이트/다크 설정을 따르며, 시스템이 전환되면 인터페이스도 자동으로 따라갑니다.", + "待迁移旧数据": "이전 대기 중인 레거시 데이터", + "待配置": "미설정", + "忘记密码时无法在界面里找回:需要停掉程序后,从数据库删除管理员记录(kv 表的 panelAdmin 键),重启后按首次使用重新注册。具体命令与各形态的库文件路径见 docs 目录下的「重置管理员」文档。": "비밀번호를 잊으면 화면에서 복구할 수 없습니다. 프로그램을 중지한 뒤 데이터베이스에서 관리자 레코드(kv 테이블의 panelAdmin 키)를 삭제하고, 재시작 후 최초 사용처럼 다시 등록하세요. 구체적인 명령과 형태별 데이터베이스 경로는 docs의 \"관리자 재설정\" 문서를 참고하세요.", "恢复中…": "복원 중…", "恢复失败:{message}": "복원 실패: {message}", "恢复官方原文": "공식 원문으로 복원", "恢复整库": "데이터베이스 복원", "恢复整库备份": "전체 백업 복원", "恢复默认": "기본값으로 복원", + "恢复默认侧边栏": "사이드바 기본값 복원", "想回到原来那份,把这里清空再保存即可(不会删掉你配的文件路径)。": "원래 본문으로 돌아가려면 여기를 비우고 저장하면 됩니다(설정한 파일 경로는 삭제되지 않습니다).", "想要的新功能或改进想法": "원하는 새 기능이나 개선 아이디어", "所有未单独配置的提供商都用这一份": "개별 설정하지 않은 모든 제공자가 이 설정을 사용합니다", + "所有菜单都已在这一组": "모든 메뉴가 이미 이 그룹에 있습니다", + "手机端只做查看与高频操作;添加账号、编辑凭证、改写客户端配置仍需桌面端。": "", + "托管网页界面": "웹 UI 제공", + "把 Base URL 复制给客户端": "", + "把地址里的 your-server 与 USERNAME 换成你自己的;目录不存在时会自动创建。": "주소의 your-server와 USERNAME을 자신의 값으로 바꾸세요. 디렉터리가 없으면 자동으로 만듭니다.", "把整份数据库(账号、网关配置、事件日志、请求统计、调试报文与桌面设置)导出成一个 SQL 文件,恢复时整体替换本机全部数据 —— 与上面「账号导入 / 导出」的逐条合并是两种语义:恢复会连配置、日志与当前启用状态一起换成备份那台机器的状态。导出默认包含日志与调试报文(文件可能很大),不需要时可以取消勾选只备业务数据。恢复前会自动把当前数据备份到配置目录的 backups/ 下,恢复后需要重新登录管理面板。": "데이터베이스 전체(계정, 게이트웨이 설정, 이벤트 로그, 요청 통계, 디버그 페이로드, 데스크톱 설정)를 SQL 파일 하나로 내보내고, 복원 시 이 PC의 모든 데이터를 통째로 교체합니다 — 위의 \"계정 가져오기 / 내보내기\"의 건별 병합과는 다른 의미입니다. 복원은 설정, 로그, 활성화 상태까지 백업한 그 PC의 상태로 바꿔치기합니다. 내보내기는 기본적으로 로그와 디버그 페이로드를 포함하며(파일이 커질 수 있음), 필요 없으면 체크를 해제해 업무 데이터만 백업할 수 있습니다. 복원 직전에 현재 데이터를 설정 디렉터리의 backups/ 에 자동 백업하며, 복원 후에는 패널에 다시 로그인해야 합니다.", + "把菜单移入 {name}": "메뉴를 {name}(으)로 이동", + "把菜单移入「{name}」": "메뉴를 \"{name}\"(으)로 이동", + "把账号、网关配置、自定义提供商、Agent 配置、代理池 / 订阅、签到与余额记录同步到 WebDAV 或 S3 兼容存储,换台机器后一键取回。事件日志、请求明细、调试报文与面板登录态不在同步范围内(它们是本机的运行痕迹)。上传的账号凭证仍是加密形态,解密用的账号密钥由「同步密码」单独加密后一并上传 —— 同步密码不会保存到云端,换机下载时需要重新输入。": "계정, 게이트웨이 설정, 사용자 지정 공급자, Agent 설정, 프록시 풀/구독, 체크인 및 잔액 기록을 WebDAV 또는 S3 호환 스토리지에 동기화하고 다른 컴퓨터에서 한 번에 복원합니다. 이벤트 로그, 요청 상세, 디버그 패킷, 패널 로그인 상태는 대상이 아닙니다(이 컴퓨터의 실행 기록입니다). 업로드되는 계정 자격 증명은 암호화된 상태로 유지되며, 이를 복호화하는 계정 키는 '동기화 비밀번호'로 별도 암호화해 함께 업로드합니다. 동기화 비밀번호는 클라우드에 저장되지 않으므로 다른 컴퓨터에서 복원할 때 다시 입력해야 합니다.", + "把路径里的主目录显示成 ~,截图分享时不暴露用户名": "경로의 홈 디렉터리를 ~ 로 표시해 캡처를 공유할 때 사용자 이름이 드러나지 않게 합니다", + "把路径里的主目录(如 C:\\Users\\你的用户名)显示成 ~,截图分享时不暴露用户名。只影响本页显示与复制,不改任何文件。": "경로의 홈 디렉터리(예: C:\\Users\\사용자이름)를 ~ 로 표시해 캡처를 공유할 때 사용자 이름이 드러나지 않게 합니다. 이 페이지 표시와 복사에만 영향을 주며 파일은 변경하지 않습니다.", "报表天数": "리포트 일수", "报表页的热力图与按天趋势,默认 99999 天(近似全量保留),可填 1–99999 天。": "리포트 페이지의 히트맵과 일별 추세. 기본 99999일(거의 전체 보관)이며 1~99999일까지 입력할 수 있습니다.", + "拖动分组 {name}": "그룹 {name} 끌기", + "拖动左侧 ⠿ 调整顺序(菜单可以跨组拖);拨开关控制显隐;点分组名可重命名;组头的 + 把别的菜单移进来;眼睛按钮隐藏整组。改动即时生效并记住,左侧侧边栏就是结果。键盘也能排序:把手聚焦后按 Alt+↑ / ↓。": "왼쪽 ⠿를 끌어 순서를 바꾸세요(메뉴는 그룹 간 이동 가능). 스위치로 표시/숨김, 그룹 이름을 클릭해 이름 변경, 그룹 머리의 +로 다른 메뉴를 가져오기, 눈 버튼으로 그룹 전체 숨기기. 변경은 즉시 적용·저장되며 왼쪽 사이드바가 결과입니다. 키보드: 핸들에 포커스 후 Alt+↑ / ↓.", + "拖动菜单 {name}": "메뉴 {name} 끌기", + "拖动调整分组顺序(Alt+↑ / ↓ 也可)": "끌어서 그룹 순서 변경(Alt+↑ / ↓ 도 가능)", + "拖动调整顺序,可跨组(Alt+↑ / ↓ 也可)": "끌어서 순서 변경, 그룹 간 이동 가능(Alt+↑ / ↓ 도 가능)", "指定错误码直接换号": "지정 오류 코드 즉시 계정 전환", - "指纹脱敏": "핑거프린트 마스킹", + "指纹脱敏": "지문 마스킹", "指纹脱敏设置已刷新": "핑거프린트 마스킹 설정을 새로 고쳤습니다", "按天聚合保留天数": "일일 집계 보존 일수", "按提供商配置": "제공자별 설정", + "换一台服务器": "다른 서버로 전환", "排队等待": "큐잉 대기", "排队等待次数": "큐잉 대기 횟수", "排队等待秒数": "큐잉 대기 시간(초)", "排队等待设置已刷新": "큐잉 대기 설정을 새로 고쳤습니다", + "接入": "연결", + "控制主侧边栏显示哪些菜单、按什么顺序、怎么分组。隐藏只影响导航显示,不会撤销页面访问权限;被隐藏的页面仍可通过地址直接打开。": "사이드바에 표시할 메뉴, 순서, 그룹을 설정합니다. 숨김은 탐색 표시에만 영향을 주며 접근 권한을 회수하지 않습니다. 숨긴 페이지도 주소로 직접 열 수 있습니다.", "控制报表与请求日志里 Token 读数的写法:开启后按「万 / 亿」式的本地量级词显示(简体为万 / 亿,繁体为萬 / 億,日文为万 / 億,韩文为만 / 억),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。英文 / 葡语的本地量级词就是 k / M,这项对它们没有区别。只影响显示口径,不改变任何统计与存储的数值。": "리포트와 요청 로그에서 Token 수치를 쓰는 방식을 제어합니다. 켜면 「만 / 억」 단위의 로컬 규모 단어로 표시하고(중국어 간체는 万 / 亿, 중국어 번체는 萬 / 億, 일본어는 万 / 億, 한국어는 만 / 억), 끄면 k / M 축약을 사용합니다(업스트림 문서와 인터페이스 필드의 표기와 동일). 영어 / 포르투갈어의 로컬 규모 단어는 k / M이므로 이 항목은 이들 언어에 차이가 없습니다. 표시 방식에만 영향을 주며 통계와 저장된 수치는 바뀌지 않습니다.", - "控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。": "보고서와 요청 로그에서 토큰 수 표기를 결정합니다: 켜면 중국어 단위 표기(1.2억 / 8400만), 끄면 k / M 약어(업스트림 문서와 API 필드의 표기와 동일). 표시에만 영향을 주며 통계나 저장된 값은 전혀 바뀌지 않습니다.", + "控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M / B 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。": "보고서와 요청 로그의 Token 표기 방식을 바꿉니다. 켜면 중국어 단위(1.2억 / 8400만), 끄면 k / M / B 약어(업스트림 문서와 API 필드와 같은 표기)로 표시합니다. 표시 방식만 바뀌며 통계와 저장 값은 달라지지 않습니다.", "控制界面的深浅色,与左侧边栏底部的三个主题按钮是同一个设置(改哪一处,另一处立刻跟上)。「跟随系统」会随操作系统当前的浅色 / 深色自动切换,并在系统主题变化时即时跟上;选「浅色」或「深色」则把界面固定在该模式,不再随系统变化。窗口标题栏的深浅色会一并同步,不会出现深色界面配一条浅色标题栏的情况。": "인터페이스의 라이트/다크를 제어하며, 왼쪽 사이드바 하단의 테마 버튼 세 개와 같은 설정입니다(어느 쪽을 바꾸든 다른 쪽이 즉시 따라갑니다). 「시스템」은 운영 체제의 현재 라이트 / 다크 설정에 따라 자동으로 전환되고 시스템 테마가 바뀌면 즉시 따라갑니다. 「라이트」나 「다크」를 선택하면 인터페이스가 해당 모드로 고정되어 시스템 설정을 따르지 않습니다. 창 제목 표시줄의 색상도 함께 동기화되어, 다크 인터페이스에 라이트 제목 표시줄이 붙는 일은 없습니다.", "提建议": "제안하기", "提示词文件": "시스템 프롬프트 파일", "提示词文件已更新": "시스템 프롬프트 파일이 업데이트되었습니다", "提示词正文": "시스템 프롬프트 본문", "提示词正文已更新": "시스템 프롬프트 본문이 업데이트되었습니다", + "操作系统": "운영 체제", + "改动仍保存在本机,修复网络或凭据后下一次改动会自动重试,也可以手动点「上传到云端」。": "변경 사항은 로컬에 저장되어 있습니다. 네트워크나 자격 증명을 고치면 다음 변경 시 자동으로 다시 시도하며, '클라우드로 업로드'를 직접 눌러도 됩니다.", "改完立即生效,不用重启(下一个请求就用新模式)。降级是自动的临时状态,解除后本模式自己的提示词立刻恢复。": "수정 후 재시작 없이 즉시 적용됩니다(다음 요청부터 새 모드가 사용됩니다). 폴백은 자동으로 생기는 임시 상태이며, 해제하면 이 모드의 시스템 프롬프트가 즉시 복구됩니다.", "放大或缩小整个界面(文字与控件一起变),效果与浏览器 Ctrl +/- 相同:共 11 档,立即生效并记住。": "인터페이스 전체를 확대하거나 축소합니다(텍스트와 컨트롤이 함께 바뀜). 효과는 브라우저의 Ctrl +/-와 동일합니다. 총 11단계이며 즉시 적용되고 기억됩니다.", "数据": "데이터", "数据保留": "데이터 보관", "数据存储": "데이터 저장", + "数据库 schema": "데이터베이스 schema", "数据库不可用": "데이터베이스 사용 불가", "数据库文件": "데이터베이스 파일", + "数据目录": "데이터 디렉터리", "数据统一保存在配置目录下的 agent2api.db;如需更换位置,请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "데이터는 구성 디렉터리의 agent2api.db에 통합 저장됩니다. 위치를 바꾸려면 환경 변수 AGENT2API_PROXY_HOME을 설정한 후 프로그램을 다시 시작하세요.", "整体替换": "통째로 교체", "整库备份 / 恢复": "전체 DB 백업 / 복원", + "新分组名": "새 그룹 이름", "新增 {added} 个、更新 {updated} 个{suffix}{note}": "{added}개 추가, {updated}개 업데이트{suffix}{note}", + "新密码": "새 비밀번호", + "新密码不能与当前密码相同": "새 비밀번호는 현재 비밀번호와 달라야 합니다", + "新密码至少 8 位": "비밀번호는 8자 이상이어야 합니다", + "新建分组": "그룹 추가", + "日志文件": "로그 파일", "日语": "일본어", + "显示 {name}": "{name} 표시", + "显示分组 {name}": "그룹 {name} 표시", "显示模式": "테마", + "显示这一组": "이 그룹 표시", "更新": "업데이트", + "更新出网线路": "업데이트 출구 경로", + "更新通道": "업데이트 채널", "替换": "교체", "替换 = 客户端 system / developer 消息全部删掉,换成网关的提示词;追加 = 保留客户端内容,只在开头 system 块之后多插一条。两者都只改发给上游的副本,客户端看到的响应不变。": "교체 = 클라이언트의 system / developer 메시지를 모두 삭제하고 게이트웨이 시스템 프롬프트로 바꿉니다. 추가 = 클라이언트 내용을 유지하고 맨 앞 system 블록 뒤에 한 건만 더 삽입합니다. 둘 다 업스트림으로 보내는 사본만 바꾸며 클라이언트가 받는 응답은 달라지지 않습니다.", "替换为网关提示词": "게이트웨이 시스템 프롬프트로 교체", "替换前会自动把当前数据备份到配置目录的 backups/ 文件夹。": "교체 직전에 현재 데이터를 설정 디렉터리의 backups/ 폴더에 자동 백업합니다.", + "替换并恢复": "교체하고 복원", "有未保存的修改": "저장되지 않은 변경 사항이 있습니다", + "有未保存的改动:保存后才能同步(测试连接不受影响)。": "저장되지 않은 변경 사항이 있습니다. 동기화하려면 먼저 저장하세요(연결 테스트는 가능).", + "服务商": "공급자", + "服务器地址": "서버 주소", "期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。": "이 모드 동안 자체 프롬프트는 전송되지 않습니다. 프롬프트를 고친 뒤 즉시 해제할 수 있습니다.", "未单独配置:模式、提示词文件与正文都跟随上面的全局配置(当前「{mode}」)。": "개별 설정 없음: 모드, 시스템 프롬프트 파일과 본문 모두 위의 전역 설정을 따릅니다(현재 「{mode}」).", + "未启用": "사용 안 함", "未开启轻量模式:关窗仅隐藏窗口,界面进程常驻内存,随时打开不等待": "경량 모드 꺼짐: 창을 닫아도 창만 숨겨지고 인터페이스 프로세스가 메모리에 상주하여 언제든 기다림 없이 열립니다", "未开启,developer 角色消息会原样发往上游,不认识该角色的上游会报错(400)。": "꺼짐: developer 역할 메시지가 그대로 업스트림으로 전송되며, 이 역할을 모르는 업스트림은 오류(400)를 반환합니다.", "未开启,客户端 system 模板会原样发往上游,可能被内容审核误拦(400)。": "꺼져 있어 클라이언트 system 템플릿이 그대로 업스트림으로 전송되어 콘텐츠 검열에 오탐으로 차단될 수 있습니다(400).", "未开启,浏览器跨域无法直连网关。": "꺼져 있어 브라우저에서 CORS로 게이트웨이에 직접 연결할 수 없습니다.", "未开启,转发时不保存任何原始报文。": "꺼져 있어 전달 시 원본 메시지를 일절 저장하지 않습니다.", "未开启:网关只监听 127.0.0.1,仅本机可以访问。": "꺼짐: 게이트웨이가 127.0.0.1만 수신하여 로컬에서만 접근할 수 있습니다.", + "未注册": "미등록", + "未注册闸门": "미등록 게이트", "未知原因": "알 수 없는 사유", "未知来源": "알 수 없는 출처", + "未知设备": "알 수 없는 기기", "未知账号": "알 수 없는 계정", "未能读取 Cline 伪装头设置,请稍后重试": "Cline 위장 헤더 설정을 읽지 못했습니다. 잠시 후 다시 시도하세요", "未能读取 models.dev 元数据状态,请稍后重试": "models.dev 메타데이터 상태를 읽지 못했습니다. 잠시 후 다시 시도하세요.", + "未能读取云同步设置,请稍后重试。": "클라우드 동기화 설정을 읽지 못했습니다. 잠시 후 다시 시도하세요.", "未能读取开发者角色兼容设置,请稍后重试": "개발자 역할 호환 설정을 읽지 못했습니다. 잠시 후 다시 시도해 주세요", "未能读取指纹脱敏设置,请稍后重试": "핑거프린트 마스킹 설정을 읽지 못했습니다. 잠시 후 다시 시도하세요", "未能读取系统提示词设置,请稍后重试": "시스템 프롬프트 설정을 읽지 못했습니다. 잠시 후 다시 시도하세요", "未能读取网关跨域访问设置,请稍后重试": "게이트웨이 CORS 접근 설정을 읽지 못했습니다. 잠시 후 다시 시도하세요", "未能读取调试模式设置,请稍后重试": "디버그 모드 설정을 읽지 못했습니다. 잠시 후 다시 시도하세요", + "未能读取账户信息,请稍后重试": "계정 정보를 읽지 못했습니다. 잠시 후 다시 시도하세요", + "本地账号、配置、签到与余额记录变化后自动上传(合并 1 秒内的连续改动,最长 10 秒传一次;内容没变时不发请求)。": "로컬 계정, 설정, 체크인, 잔액 기록이 변경되면 자동 업로드합니다(1초 이내 연속 변경은 병합, 최대 10초에 한 번. 내용이 그대로면 요청을 보내지 않습니다).", + "本机 APK 版本": "이 기기 APK 버전", + "本机应用版本": "이 기기의 앱 버전", + "本机界面版本": "이 기기 UI 버전", + "本机的业务数据(账号、配置、Agent 配置、代理池 / 订阅、签到与余额记录)将上传并": "이 컴퓨터의 업무 데이터(계정, 설정, Agent 설정, 프록시 풀/구독, 체크인 및 잔액 기록)를 업로드하여", "本机的全部数据(账号、网关配置、日志统计与桌面设置)将被所选备份文件": "이 PC의 모든 데이터(계정, 게이트웨이 설정, 로그 통계, 데스크톱 설정)는 선택한 백업 파일로", + "本机的账号、网关配置、Agent 配置、代理池 / 订阅、签到与余额记录将被远端快照": "이 컴퓨터의 계정, 게이트웨이 설정, Agent 설정, 프록시 풀/구독, 체크인 및 잔액 기록이 원격 스냅샷으로", "本次无需刷新": "이번에는 새로고침이 필요하지 않습니다", "机器人校验": "로봇 검증", + "来源设备:{device}": "원본 기기: {device}", + "来自 {device},生成于 {time}": "{device}에서 {time}에 생성", + "架构": "아키텍처", "查询管理员状态失败:{message}": "관리자 상태 조회 실패: {message}", "校验身份:这三段必须各自成块排在最前(实测:三段并成一段会被回 405 / 3012)。因此这里按段编辑,不能合成一段。改过的段以你的文本为准,没改的段继续用官方原文;清空某一段 = 那一段恢复官方原文。": "를 기준으로 신원을 검증합니다: 이 세 단락은 각각 별도 블록으로 맨 앞에 있어야 합니다(실측: 세 단락을 하나로 합치면 405 / 3012가 반환됩니다). 따라서 단락별로 편집하며 하나로 합칠 수 없습니다. 수정한 단락은 입력한 텍스트가 우선하고, 수정하지 않은 단락은 계속 공식 원문을 사용합니다. 단락을 비우면 그 단락은 공식 원문으로 복원됩니다.", + "桌面端": "데스크톱 앱", + "桌面端:登录 / 注册需要 ALTCHA 人机验证": "데스크톱: 로그인 / 등록에 ALTCHA 인증 필요", + "桌面端:登录 / 注册需要 ALTCHA 人机验证(当前正在使用)": "데스크톱: 로그인 / 등록에 ALTCHA 인증 필요(현재 사용 중)", "桌面运行时不可用,暂时无法保存": "데스크톱 런타임을 사용할 수 없어 지금은 저장할 수 없습니다", + "档案": "프로필", "检测中…": "확인 중…", + "概览": "개요", "模式": "모드", "模式改为「{mode}」": "모드를 「{mode}」(으)로 변경", "次": "회", "次日 00:00": "다음 날 00:00", "正在保存上游原始报文:": "업스트림 원본 메시지를 저장하는 중: ", + "正在修改…": "변경 중…", "正在剥离出站请求里的审核指纹:表头键值整段删除,模板句最小改写。": "아웃바운드 요청의 검열 핑거프린트를 제거하는 중: 헤더 키-값은 통째로 삭제하고 템플릿 문구는 최소한으로 재작성합니다.", + "正在安装…": "설치 중…", "正在应答跨源请求:浏览器跨域请求可直连 /v1/*。": "교차 출처 요청에 응답 중: 브라우저의 CORS 요청이 /v1/*에 직접 연결될 수 있습니다.", "正在归一角色:developer 角色消息转发前改写为 system。": "역할 정규화 중: developer 역할 메시지는 전달 전에 system으로 수정됩니다.", "正在注册…": "등록 중…", "正在联网更新": "온라인으로 갱신 중", + "正式版": "정식 버전", "每条请求轮换到下一个可用账号;新会话交给当前最闲的那位。适合想把手里的号摊开用、不让某一个一直扛。": "요청마다 다음 사용 가능한 계정으로 순환하고, 새 세션은 현재 한가한 계정에 맡깁니다. 보유 계정을 고르게 쓰고 특정 계정에 부하가 몰리지 않게 하고 싶을 때 적합합니다.", "每条请求都重新按策略选路 —— 行为与「关闭」一致,仅保留会话识别的记录。": "요청마다 전략에 따라 다시 선택합니다 — 동작은 \"끄기\"와 같으며 세션 기록만 유지됩니다.", "每次等待的时长,默认 0 = 跟随上游建议(实测 9~30 秒,上游没给建议时用 15 秒)。可填 0–120 秒强制一个固定时长。": "대기 1회의 길이입니다. 기본값 0 = 업스트림 권장 시간을 따릅니다(실측 9~30초, 업스트림이 권장 시간을 주지 않으면 15초 사용). 0–120초를 입력하면 고정 시간으로 강제할 수 있습니다.", @@ -335,15 +547,32 @@ "注意:这一家的「网关自带」开关现在是关的,改过的正文要等开关打开才生效。": "주의: 이 제공자의 「게이트웨이 내장」 스위치가 현재 꺼져 있습니다. 수정한 본문은 스위치를 켠 후에야 적용됩니다.", "流式响应相邻数据之间允许的最大空闲时间(秒),收到新数据后重新计时,默认 300 秒,范围 1~3600。": "스트리밍 응답에서 인접한 데이터 사이에 허용되는 최대 유휴 시간(초). 새 데이터가 오면 다시 시간을 재며, 기본 300초, 범위 1~3600.", "流式响应空闲超时": "스트리밍 응답 유휴 타임아웃", + "测试连接": "연결 테스트", + "浏览器 / PWA": "브라우저 / PWA", + "浏览器只在 https 或 localhost 下提供离线缓存;局域网 http 访问不影响正常使用": "브라우저는 https 또는 localhost에서만 오프라인 캐시를 제공합니다. LAN의 http 접속에서도 평소대로 사용할 수 있습니다.", + "浏览器标签页": "브라우저 탭", + "浏览器面板": "브라우저 패널", "添加提供商": "제공자 추가", "添加提供商…": "제공자 추가…", "添加自定义头": "사용자 지정 헤더 추가", "添加要单独配置的提供商": "개별 설정할 제공자 추가", + "清除本机凭证": "이 기기의 자격 증명 삭제", "清除这里的正文": "여기의 본문 지우기", "点下面的入口会用系统默认浏览器打开 GitHub 的对应表单(需要 GitHub 账号,模板已预设好,填完直接提交即可)。": "아래 항목을 클릭하면 시스템 기본 브라우저에서 GitHub의 해당 양식이 열립니다(GitHub 계정 필요, 템플릿이 미리 채워져 있으므로 작성 후 바로 제출하면 됩니다).", + "点击重命名": "클릭하여 이름 변경", + "点此添加或切换服务器": "눌러서 서버 추가 또는 전환", + "点此添加服务器": "눌러서 서버 추가", + "版本": "버전", + "版本信息": "버전 정보", + "版本信息 · 所连网关环境": "버전 정보 · 연결된 게이트웨이", "状态码 {code} 已在名单里": "상태 코드 {code}는 이미 목록에 있습니다", "状态码必须是 {min}–{max} 的整数(收到: {text})": "상태 코드는 {min}–{max} 범위의 정수여야 합니다(입력값: {text})", + "环境": "환경", + "生成时间:{time}": "생성 시간: {time}", "用 models.dev 补齐上游没声明的模型能力与思考档位(联网刷新,内嵌快照始终生效)": "업스트림이 선언하지 않은 모델 능력과 추론 레벨을 models.dev로 채웁니다(온라인 새로고침, 내장 스냅샷은 항상 적용)", + "用于加密上云的账号密钥。请自己记牢 —— 它不保存到云端,换机下载时必须输入同一个密码,否则账号数据无法解密。": "클라우드로 보내는 계정 키를 암호화하는 데 사용합니다. 반드시 기억하세요 — 클라우드에 저장되지 않으며, 다른 컴퓨터에서 복원할 때 같은 비밀번호를 입력해야 계정 데이터를 복호화할 수 있습니다.", + "用浏览器菜单里的「添加到主屏幕」把这一页装成 App:全屏打开、没有地址栏,和原生应用一样。": "브라우저 메뉴의 「홈 화면에 추가」로 이 페이지를 앱처럼 설치할 수 있습니다. 전체 화면, 주소 표시줄 없이 네이티브 앱과 같습니다.", + "界面": "", "界面缩放": "인터페이스 줌", "界面缩放比例": "인터페이스 줌 비율", "界面语言": "인터페이스 언어", @@ -351,15 +580,29 @@ "界面语言只保存在本机(localStorage)。切换后整页立即刷新,不影响账号、密钥与转发。": "인터페이스 언어는 이 컴퓨터에만 저장됩니다(localStorage). 전환하면 페이지 전체가 즉시 새로 고침되며 계정, 키, 포워딩에는 영향을 주지 않습니다.", "界面里编辑的正文": "화면에서 편집한 본문", "留空 = 用内置默认提示词": "비워 두면 = 내장 기본 시스템 프롬프트 사용", + "留空使用 AWS 官方地址": "비워 두면 AWS 공식 주소 사용", "登录 / 注册需要通过 ALTCHA 人机验证(工作量证明)": "로그인 / 가입에는 ALTCHA 인간 검증(작업 증명)이 필요합니다", "登录 / 账号切换 / 429 切换等系统事件,可填 1–3650 天。": "로그인 / 계정 전환 / 429 전환 등 시스템 이벤트이며, 1–3650일을 입력할 수 있습니다.", "登录 / 账号切换 / 429 切换等系统事件,可填 1–99999 天。": "로그인 / 계정 전환 / 429 전환 등 시스템 이벤트. 1~99999일까지 입력할 수 있습니다.", + "登录 Agent2API": "Agent2API 로그인", + "登录人机验证": "로그인 CAPTCHA", + "监听地址": "수신 주소", + "目标路径:{path}": "대상 경로: {path}", "确定继续?": "계속할까요?", + "确认上传": "업로드 확인", + "确认新密码": "새 비밀번호 확인", "确认缩短保留天数": "보관 일수 단축 확인", + "社区与支持": "커뮤니티 및 지원", + "离线缓存未启用": "오프라인 캐시가 꺼져 있습니다", + "积分合计只统计当前筛选出来的账号": "포인트 합계를 현재 필터에 표시된 계정만으로 계산", + "移入「{name}」": "\"{name}\"(으)로 이동", + "程序路径": "실행 파일 경로", "空 = 不要这一份,回落提示词文件 / 内置默认提示词。": "비우면 = 이 본문을 사용하지 않고 시스템 프롬프트 파일 / 내장 기본 시스템 프롬프트로 폴백합니다.", + "空分组 · 点组头的 + 把菜单移进来": "빈 그룹 · 머리의 +로 메뉴를 가져오세요", "立即解除": "즉시 해제", "第一段": "첫 번째 단락", "第一段(身份句)": "첫 번째 단락(아이덴티티 문장)", + "第三方应用密码": "타사 앱 비밀번호", "第三段": "세 번째 단락", "第三段(动态段)": "세 번째 단락(동적 단락)", "第二段": "두 번째 단락", @@ -368,8 +611,12 @@ "等待期间请求日志的状态列会显示「排队中」阶段,详情弹窗的「内部重试」里能逐次看到等待时长;等待用尽后返回 HTTP 503(不是 401/429:既不会刷新凭证,也不会把账号标成限额)。": "대기 중에는 요청 로그의 상태 열에 「큐잉 중」 단계가 표시되고, 상세 팝업의 「내부 재시도」에서 회차별 대기 시간을 볼 수 있습니다. 대기가 소진되면 HTTP 503을 반환합니다(401/429가 아닙니다: 자격 증명을 갱신하지도, 계정을 할당량 초과로 표시하지도 않습니다).", "等比放大或缩小整个界面(文字、控件、间距一起变),效果与浏览器按 Ctrl +/- 相同:80%–130%、5% 一档。窗口本身不缩放,变的是页面内容的显示比例,设置立即生效并记住,下次启动直接按这个比例打开。放得越大可视范围越小,窗口较窄或表格较宽时不建议调得太大。": "인터페이스 전체를 비율에 맞춰 확대하거나 축소합니다(텍스트, 컨트롤, 간격이 함께 바뀜). 효과는 브라우저에서 Ctrl +/-를 누른 것과 같습니다: 80%–130%, 5% 단위. 창 자체는 확대되지 않고 페이지 내용의 표시 비율만 바뀝니다. 설정은 즉시 적용되고 기억되어 다음 시작 시 이 비율로 열립니다. 크게 할수록 보이는 범위가 좁아지므로 창이 좁거나 표가 넓을 때는 너무 크게 하지 않는 것이 좋습니다.", "简体中文": "중국어 간체", + "管理员已注册": "관리자 등록됨", + "管理员账号": "관리자 계정", + "系统": "", "系统提示词模式": "시스템 프롬프트 모드", "系统提示词设置已刷新": "시스템 프롬프트 설정을 새로 고쳤습니다", + "累计请求": "누적 요청", "繁体中文": "중국어 번체", "约 {n} 字符": "약 {n}자", "结构": "구조", @@ -380,19 +627,39 @@ "编辑网关自带提示词 · {label}": "게이트웨이 내장 시스템 프롬프트 편집 · {label}", "缩放比例": "줌 비율", "网关": "게이트웨이", + "网关 Key 数量": "게이트웨이 키 수", + "网关状态": "", "网关自带": "게이트웨이 내장", "网关跨域访问已开启": "게이트웨이 CORS 접근이 켜졌습니다", "网关跨域访问设置已刷新": "게이트웨이 CORS 접근 설정을 새로 고쳤습니다", "网关跨域访问(CORS)": "게이트웨이 CORS 접근", "网关面(/v1/*)默认不应答浏览器跨源请求:页面直连会被预检拦下,只报「无法连接 API」。注意来源是 * —— 未配「网关 Key」时任何网页都能借本机网关打上游,故默认关闭;只给自己用的话,让页面与网关同源(本地反代 /v1)更稳妥。面板接口(/api/*)不受影响。": "게이트웨이 API(/v1/*)는 기본적으로 브라우저의 교차 출처 요청에 응답하지 않습니다. 페이지에서 직접 연결하면 프리플라이트에 막히고 「API에 연결할 수 없음」만 표시됩니다. 출처가 *이므로 「게이트웨이 키」를 설정하지 않으면 어떤 웹 페이지든 로컬 게이트웨이를 거쳐 업스트림을 호출할 수 있기에 기본적으로 꺼져 있습니다. 혼자만 사용한다면 페이지와 게이트웨이를 같은 출처로 두는 편(로컬 리버스 프록시 /v1)이 더 안전합니다. 패널 인터페이스(/api/*)는 영향을 받지 않습니다.", + "网页端": "웹", "网页端的界面缩放由浏览器自己控制(Ctrl + / Ctrl -,或浏览器菜单里的缩放),此项不可调。": "웹 버전의 인터페이스 줌은 브라우저가 직접 제어하며(Ctrl + / Ctrl -, 또는 브라우저 메뉴의 줌), 이 항목은 조정할 수 없습니다.", + "网页端:登录 / 注册需要 ALTCHA 人机验证": "웹: 로그인 / 등록에 ALTCHA 인증 필요", + "网页端:登录 / 注册需要 ALTCHA 人机验证(当前正在使用)": "웹: 로그인 / 등록에 ALTCHA 인증 필요(현재 사용 중)", + "群晖 NAS": "Synology NAS", + "群晖需在「控制面板 → 应用程序门户 → WebDAV」里启用服务,端口默认 5005(HTTP)/ 5006(HTTPS)。": "Synology는 '제어판 → 응용 프로그램 포털 → WebDAV'에서 서비스를 활성화하세요. 기본 포트는 5005(HTTP)/5006(HTTPS)입니다.", + "腾讯云 COS": "텐센트 클라우드 COS", + "自动同步": "자동 동기화", + "自动同步上次失败": "마지막 자동 동기화 실패", + "自动续写": "자동 이어쓰기", "自定义提供商 {id}": "커스텀 제공자 {id}", "自愈保护": "자가 복구 보호", + "至少 8 位,换机下载时需要": "8자 이상, 다른 컴퓨터에서 복원할 때 필요", + "至少要保留一个分组": "그룹은 최소 하나 남아야 합니다", "英语": "영어", "装上{size}的官方身份提示词": "공식 아이덴티티 프롬프트 설치({size})", + "装成 App 后从桌面图标直接进入:全屏打开、没有地址栏。": "앱으로 설치하면 홈 화면 아이콘에서 바로 열립니다. 전체 화면, 주소 표시줄 없음.", + "覆盖": "덮어쓰기", "解除失败:{message}": "해제 실패: {message}", + "计数": "집계", "计量单位": "표기 단위", + "设置不可隐藏(隐藏了只能清缓存找回来)": "\"설정\"은 숨길 수 없습니다(숨기면 저장소를 지워야 되돌릴 수 있습니다)", + "详细日志": "상세 로그", "语言设置": "언어 설정", + "请先保存设置再同步": "동기화하기 전에 설정을 저장하세요", + "请填一个分组名": "그룹 이름을 입력하세요", "请填写管理员账号(64 字符以内)": "관리자 계정을 입력하세요(64자 이내)", "请求发出后等待上游响应头的最大时间(秒),默认 300 秒,范围 1~3600。": "요청을 보낸 뒤 업스트림 응답 헤더를 기다리는 최대 시간(초). 기본 300초, 범위 1~3600.", "请求提供商 / 模型支持": "제공자 / 모델 지원 요청", @@ -401,18 +668,30 @@ "请求超时": "요청 타임아웃", "请求转发到上游失败时(服务器瞬时错误、连接失败,以及 WorkBuddy 的 11-128 敏感词拦截),网关会等一段间隔后再发一次。次数分两档,管的是两件事:①「同一账号重试次数」在同一账号上原地重发几次 —— 一般是提示词被上游拦截、或链路瞬时抖动,等一会儿重发往往就好了;②「切换账号重试次数」这份请求最多再换几个账号试 —— 首发的那个账号不算,每换一个扣一次,不分是不是同一家(换到同一家的下一个账号也算一次),换满还失败就把错误返回给客户端。两项都设为 0 表示失败不重试、直接报错。注意账号级限额(429)不占这里的换号次数 —— 那类失败走「冷却该账号并换下一个账号」的降级,与这里的重试是两条路。另有「指定错误码直接换号」名单(默认 402):命中名单的上游状态码不在同一账号重发,直接按队列换下一个账号继续试(换号也占「切换账号重试次数」),换满仍失败才把错误返回给客户端。": "업스트림으로 전달이 실패하면(서버의 일시적 오류, 연결 실패, WorkBuddy의 11-128 금칙어 차단) 게이트웨이가 일정 간격을 기다린 뒤 한 번 더 보냅니다. 횟수는 두 가지로 나뉘며 서로 다른 일을 담당합니다. ①「동일 계정 재시도 횟수」는 같은 계정에서 제자리로 몇 번 재전송할지를 정합니다. 보통 시스템 프롬프트가 업스트림에 차단되었거나 경로가 순간적으로 흔들린 경우로, 잠시 후 다시 보내면 대개 해결됩니다. ②「계정 전환 재시도 횟수」는 이 요청이 최대 몇 개의 계정으로 더 시도할지를 정합니다. 최초 전송 계정은 제외하고 계정을 바꿀 때마다 1회씩 차감되며, 같은 제공자인지 여부는 따지지 않습니다(같은 제공자의 다음 계정으로 바꿔도 1회로 계산). 횟수를 다 써도 실패하면 오류를 클라이언트에 반환합니다. 두 항목을 모두 0으로 설정하면 실패 시 재시도하지 않고 바로 오류를 반환합니다. 계정 단위 할당량 초과(429)는 여기의 계정 전환 횟수를 소모하지 않습니다. 그런 실패는 「해당 계정을 쿨다운하고 다음 계정으로 전환」하는 폴백 경로를 타며, 여기의 재시도와는 별개입니다. 또한 「지정 오류 코드 즉시 계정 전환」 목록(기본 402)이 있습니다. 목록에 해당하는 업스트림 상태 코드는 같은 계정에서 재전송하지 않고 바로 큐의 다음 계정으로 넘어가 계속 시도하며(계정 전환도 「계정 전환 재시도 횟수」를 소모), 횟수를 다 써도 실패하면 오류를 클라이언트에 반환합니다.", "请求重试": "요청 재시도", + "请输入同步密码": "동기화 비밀번호를 입력하세요", + "请输入当前密码": "현재 비밀번호를 입력하세요", + "读取云同步设置失败:{message}": "클라우드 동기화 설정을 읽지 못했습니다: {message}", + "读取失败:{message}": "읽기 실패: {message}", "读取完整非流式响应体允许的最大时间(秒),默认 300 秒,范围 1~3600。": "비스트리밍 응답 본문 전체를 읽는 데 허용되는 최대 시간(초). 기본 300초, 범위 1~3600.", + "读取网关信息失败:{message}": "게이트웨이 정보 읽기 실패: {message}", + "读取远端…": "원격 읽는 중…", + "读取远端信息失败:{message}": "원격 정보를 읽지 못했습니다: {message}", "调试报文": "디버그 메시지", "调试模式": "디버그 모드", "调试模式设置已刷新": "디버그 모드 설정을 새로 고쳤습니다", "负载均衡": "부하 분산", + "账号列表": "계정 목록", "账号导入 / 导出": "계정 가져오기 / 내보내기", "账号路由": "계정 라우팅", + "账号页表格的两项显示偏好,只存在本机(localStorage),不影响账号数据与转发。「积分合计只统计当前筛选出来的账号」:账号页每一家的分组带右侧有一枚「可用合计」,默认按整组求和 —— 不受筛选、搜索影响,被筛掉的账号也计入(余额是资产,禁用也只表示不参与转发,额度仍在账上)。打开这一项后改为只累加当前可见的行:例如筛出 1 个 100 积分的账号,合计就是 100,而不是同组两个账号之和 1,000。分页不参与这个口径 —— 翻页看的是同一份结果的不同屏,合计不随页码变化。「隐藏账号名」与表头「账号」旁边那颗眼睛是同一个开关:打开后账号列的名字整体显示成星号,适合截图分享时遮挡账号。": "계정 표의 표시 설정 두 가지입니다. 이 컴퓨터에만 저장되며(localStorage) 계정 데이터나 포워딩에는 영향을 주지 않습니다. \"포인트 합계를 현재 필터에 표시된 계정만으로 계산\": 계정 페이지의 각 제공자 그룹 띠 오른쪽에 \"사용 가능 합계\"가 있습니다. 기본값은 그룹 전체를 합산하며 필터와 검색의 영향을 받지 않고, 필터에서 제외된 계정도 포함됩니다(잔액은 자산이며, 비활성화는 포워딩에 참여하지 않는다는 뜻일 뿐 포인트는 그대로 남습니다). 이 항목을 켜면 현재 보이는 행만 더합니다. 예를 들어 100포인트 계정 하나로 필터링하면 합계는 100이며, 같은 그룹 두 계정의 합인 1,000이 아닙니다. 페이지 나누기는 이 계산에 포함되지 않습니다. 페이지는 같은 결과를 다른 화면에서 보는 것일 뿐이므로 페이지를 넘겨도 합계는 바뀌지 않습니다. \"계정 이름 숨기기\"는 표의 \"계정\" 열 옆 눈 아이콘과 같은 스위치입니다. 켜면 계정 열의 이름이 별표로 표시되어 스크린샷 공유 시 가릴 수 있습니다.", + "账户": "계정", "超出保留天数的部分会被删除:事件日志是登录、账号切换、429 切换这类系统事件。把天数改小(例如 30 天改成 7 天)保存后会立即删除超出的历史数据,此操作不可恢复;改大或保持不变不会删除任何数据。可填范围为 1–3650 天。请求日志 / 按天聚合 / 签到记录已全量保留、不再自动删除,要清历史请到对应页面手动清空。": "보존 기간을 초과한 부분은 삭제됩니다: 이벤트 로그는 로그인, 계정 전환, 429 전환 같은 시스템 이벤트입니다. 일수를 줄여(예: 30일 → 7일) 저장하면 초과된 기록이 즉시 삭제되며 되돌릴 수 없습니다. 늘리거나 그대로 두면 아무것도 삭제되지 않습니다. 입력 범위는 1–3650일입니다. 요청 로그 / 일별 집계 / 출석 기록은 전량 보존되어 자동 삭제되지 않습니다. 기록을 지우려면 해당 페이지에서 직접 비워 주세요.", "超出的历史数据会被立即删除,且不可恢复。": "초과된 과거 데이터는 즉시 삭제되며 복구할 수 없습니다.", "超时": "타임아웃", "超时设置已刷新": "타임아웃 설정을 새로 고쳤습니다", + "跟随系统 · 浅色 · 深色": "시스템 · 라이트 · 다크", "跟随默认": "기본값 따르기", + "跨域访问": "CORS", "路由": "라우팅", "路由设置已刷新": "라우팅 설정을 새로고침했습니다", "跳过 {n} 个": "{n}개 건너뜀", @@ -421,40 +700,79 @@ "转发一个请求时按什么顺序挑账号:①「路由策略」决定排队规则 —— 顺序优先是排第 1 的接走所有请求、直到它不能答(限流 / 故障 / 额度尽)才轮到下一位;轮流均摊是每条请求换下一位,雨露均沾;余额优先是用量占比最低的先用,「谁空谁上」。②「会话粘性」让同一会话(相同的模型 + 相同的开头消息)尽量跟住同一个账号 —— 多轮对话的上下文缓存能复用、少换号少浪费;自动档粘 5 分钟、会话内档粘 6 小时、每轮与关闭都不粘。账号的排队位次(账号表的第一列)在所有策略下都是地基,策略在它之上再排。此外路由还有三层自愈保护默认生效,不需要配置:转发失败的账号退避休息一会儿(15 分钟起步翻倍、封顶 1 小时),被 429 的账号垫底排到队尾一圈,余额不足 / Token 周期用量到顶的账号由限制器拦下 —— 恢复后自动回到队列。": "", "转发前将 developer 角色消息转换为 system,兼容不支持 developer 角色的 OpenAI 兼容上游": "전달 전에 developer 역할 메시지를 system으로 변환하여 developer 역할을 지원하지 않는 OpenAI 호환 업스트림과 호환", "转发失败的账号休息一会儿再回来:15 分钟起步,连续失败翻倍,封顶 1 小时;成功一次立即恢复。目的是「让这次失败先凉一会儿」,免得一条条地反复撞同一个坏账号。": "전송에 실패한 계정은 잠시 쉬고 돌아옵니다: 15 분부터 시작해 연속 실패마다 2 배, 최대 1 시간. 한 번 성공하면 즉시 복구됩니다. \"실패를 잠시 식히는\" 것이 목적으로, 같은 불량 계정을 요청마다 반복해서 두드리지 않게 합니다.", + "转发链路": "전달 경로", + "转发链路 · 诊断 · 系统": "", "轮询间隔": "폴링 간격", + "软件版本": "소프트웨어 버전", "轻量模式": "경량 모드", "轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 套餐池(Start Plan)通道不可用,相关请求会在请求日志里注明原因": "경량 모드가 켜져 있습니다: 창을 닫으면 UI 프로세스가 파괴되고(약 200MB 메모리 해제) 게이트웨이는 백그라운드에서 계속됩니다. UI가 파괴된 동안 ZCode 플랜 풀 (Start Plan) 채널을 사용할 수 없으며, 해당 요청은 요청 로그에 사유가 기록됩니다", "轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 活动套餐通道不可用,相关请求会在请求日志里注明原因": "경량 모드 켜짐: 창을 닫으면 인터페이스 프로세스가 파괴되고(약 200MB 메모리 해제) 게이트웨이는 백그라운드에서 계속됩니다. 인터페이스가 파괴된 동안에는 ZCode 활동 요금제 채널을 사용할 수 없으며, 관련 요청은 요청 로그에 사유가 기록됩니다", "轻量模式(关窗释放界面内存)": "경량 모드(창을 닫아 인터페이스 메모리 해제)", + "输入同步密码": "동기화 비밀번호 입력", + "输入当前登录用的密码": "", "输入状态码,回车添加": "상태 코드를 입력하고 Enter로 추가", + "运行形态": "실행 형태", "还原": "되돌리기", "还有额度时被 429 的账号排到队尾,30 分钟后自动回到队列(成功转发一次也会立即回来)。它标记的是「疑似共享池被别人抢」,不是「这个账号本身有问题」。⚠️ 注意它与路由策略的配合:在「家内轮询」下,队尾意味着这一家整体被往后排 —— 如果某个提供商的账号被批量限流,它们会集体沉到队尾,那个家可能长时间轮不到。这时可以关掉本项,让 429 过的账号只受冷却约束(冷却到点自动回来)。": "잔여량이 남아 있는데 429 응답을 받은 계정은 맨 뒤로 밀렸다가 30 분 후 자동으로 복귀합니다(전송 한 번 성공 시 즉시 복귀). 이는 \"공유 풀을 남이 가져가고 있는 것으로 의심\"을 표시하는 것이지 \"이 계정 자체의 문제\"가 아닙니다. ⚠️ 라우팅 전략과의 조합에 주의: \"제공자 내 순환\"에서는 맨 뒤 = 제공자 전체가 뒤로 밀린다는 뜻 — 한 제공자의 계정들이 일괄 레이트 리밋을 당하면 전부 맨 뒤로 가라앉아 그 제공자가 오랫동안 순서가 오지 않을 수 있습니다. 이럴 때는 이 항목을 꺼서 429 를 받은 계정이 쿨다운 규칙만 따르도록 하면 좋습니다(쿨다운이 끝나면 자동 복귀).", + "还没有同步过。": "아직 동기화한 적이 없습니다.", + "还没有添加服务器。点右上角「添加」,填入网关所在机器的 IP 或域名与端口。": "서버가 없습니다. 오른쪽 위 \"추가\"를 눌러 게이트웨이가 실행 중인 기기의 IP 또는 도메인과 포트를 입력하세요.", "还没有装载任何数据。": "아직 로드된 데이터가 없습니다.", + "还没有读到版本信息": "버전 정보를 아직 읽지 못했습니다", "这一份就是「替换 / 追加」模式下网关要发的 system 正文。": "이 본문이 「교체 / 추가」 모드에서 게이트웨이가 보낼 system 본문입니다.", + "这一项已隐藏": "이 항목은 숨겨져 있습니다", + "这一项正在显示": "이 항목은 표시 중입니다", + "这台网关未启用机器人校验": "이 게이트웨이는 봇 확인을 사용하지 않습니다", + "这台网关没有管理员,用一把已启用的 API Key 访问。": "이 게이트웨이에는 관리자가 없습니다. 활성화된 API Key로 접속하세요.", + "进程 PID": "프로세스 PID", + "远程根目录": "원격 루트 디렉터리", + "远端同一路径下的旧快照。事件日志、请求明细与调试报文不会上传。": "같은 원격 경로의 이전 스냅샷을 덮어씁니다. 이벤트 로그, 요청 상세, 디버그 패킷은 업로드되지 않습니다.", + "远端已有一份快照,将被覆盖": "원격에 이미 스냅샷이 있으며 덮어씁니다", + "远端快照使用了另一把账号密钥,但没有携带密钥文件,无法在本机解密。请在原来那台机器上重新上传一次。": "원격 스냅샷이 다른 계정 키를 사용하지만 키 파일이 포함되어 있지 않아 이 컴퓨터에서 복호화할 수 없습니다. 원래 컴퓨터에서 다시 업로드하세요.", + "远端快照使用的账号密钥与本机不同。请输入上传那台机器设置的同步密码,用它解出密钥装到本机后再继续下载。": "원격 스냅샷은 이 컴퓨터와 다른 계정 키를 사용합니다. 업로드한 컴퓨터에서 설정한 동기화 비밀번호를 입력해 키를 복호화하고 이 컴퓨터에 설치한 뒤 다운로드를 계속하세요.", + "远端数据与当前版本不兼容": "원격 데이터가 현재 버전과 호환되지 않습니다", + "远端该路径下还没有快照,本次是首次上传。": "해당 원격 경로에 아직 스냅샷이 없습니다. 이번이 첫 업로드입니다.", + "远端路径:{path}": "원격 경로: {path}", + "远端还没有可下载的同步数据": "원격에 다운로드할 동기화 데이터가 없습니다", + "连不上这台网关。确认地址与端口正确、网关已启动,且已开启「局域网访问」;用 https 时还要确认证书有效": "이 게이트웨이에 연결할 수 없습니다. 주소와 포트가 올바른지, 게이트웨이가 실행 중인지, \"LAN 접근\"이 켜져 있는지 확인하세요. https를 쓰는 경우 인증서가 유효한지도 확인하세요.", "连接中超时": "연결 타임아웃", + "连接失败:{message}": "연결 실패: {message}", "连续失败到阈值后把这个账号整个打开(默认关)。与失败退避的分工:退避管「单次失败先凉一会儿」,惩罚轻、自动恢复;熔断管「这个账号现在整体不对劲,别再一条条地试」,要连续成功若干次才关。打开期间该账号被跳过,除非全场只剩它 —— 那时仍放行试一次。": "연속 실패가 임계값에 도달하면 이 계정 전체를 차단합니다(기본값 꺼짐). 실패 백오프와의 역할 분담: 백오프는 \"단일 실패를 잠시 식히는\" 가벼운 처벌로 자동 복구되고, 서킷 브레이커는 \"이 계정이 전반적으로 이상하니 하나씩 시도하지 말 것\"으로, 연속 성공이 여러 번 이어져야 닫힙니다. 차단 중에는 해당 계정이 건너뛰어지지만, 남은 것이 이것뿐이면 한 번은 시도해 봅니다.", "追加": "추가", "追加网关提示词": "게이트웨이 시스템 프롬프트 추가", "退出失败:{message}": "로그아웃 실패: {message}", "退出登录": "로그아웃", + "退出这台服务器的登录,不影响其他服务器": "이 서버에서만 로그아웃합니다. 다른 서버는 영향 없음", "选择备份文件并恢复": "백업 파일을 선택해 복원", "选路规则": "선택 규칙", "透传": "패스스루", "透传 = 不动客户端 system;替换 = 删掉客户端的 system / developer,换成网关提示词;追加 = 保留客户端内容,在最前面多插一条。": "", "透传客户端 system": "클라이언트 system 패스스루", "透传客户端 system(默认)": "클라이언트 system 패스스루(기본값)", + "通常是网络问题,点右侧重试": "대개 네트워크 문제입니다. 오른쪽 재시도를 누르세요", "通用": "일반", "遇到 Bug、报错或异常行为": "버그, 오류 또는 비정상 동작 발생 시", "配置目录下的 agent2api.db(账号、日志、请求记录、调试报文与设置都在里面)。": "구성 디렉터리의 agent2api.db입니다(계정, 로그, 요청 기록, 디버그 메시지와 설정이 모두 들어 있습니다).", + "重启后生效,不影响当前操作": "재시작하면 적용됩니다. 하던 작업은 중단되지 않습니다", + "重置会清空管理员账号与密码,面板将回到「未注册」状态。": "재설정하면 관리자 계정과 비밀번호가 지워지고 패널이 \"미등록\" 상태로 돌아갑니다.", + "重置管理员": "관리자 재설정", "重试设置已刷新": "재시도 설정을 새로 고쳤습니다", "重试间隔": "재시도 간격", "铸造守卫每隔多久看一次库存(毫秒)。只影响发现缺货的反应速度 —— 库存够用时轮询不会铸造任何令牌。默认 2500 毫秒,可填 1000–30000。": "발행 가드가 재고를 몇 밀리초마다 확인할지 정합니다. 부족 발견 반응 속도에만 영향을 주며 — 재고가 충분하면 폴링해도 아무 토큰도 발행하지 않습니다. 기본값 2500 밀리초, 1000–30000 입력 가능.", "问题反馈": "문제 신고", + "阿里云 OSS": "알리바바 클라우드 OSS", + "限流中账号": "속도 제한 중 계정", "随包内嵌的快照(发版时的那份)": "앱에 내장된 스냅샷(릴리스 시점 기준)", + "隐藏 {name}": "{name} 숨기기", + "隐藏分组 {name}": "그룹 {name} 숨기기", + "隐藏用户名": "사용자 이름 숨기기", + "隐藏账号名(显示成星号)": "계정 이름 숨기기(별표로 표시)", + "隐藏这一组": "이 그룹 숨기기", "非流式响应超时": "비스트리밍 응답 타임아웃", "面板登录": "패널 로그인", + "面板端口": "패널 포트", "面板管理员账号": "패널 관리자 계정", "韩语": "한국어", + "预设": "프리셋", "验证码令牌池": "캡차 토큰 풀", "验证码令牌池设置已刷新": "캡차 토큰 풀 설정을 새로고침했습니다", "默认值通常够用,出问题时才需要调": "기본값으로 대부분 충분하며, 문제가 생겼을 때만 조정하세요", @@ -463,6 +781,7 @@ "默认开启。只改 role 字段本身,消息内容与顺序都不动;本项只改发给上游的副本,客户端看到的响应内容不变。": "기본값은 켜짐. role 필드만 수정하며 메시지 내용과 순서는 그대로입니다. 업스트림으로 보내는 사본에만 적용되고 클라이언트가 보는 응답은 변하지 않습니다.", "默认开启。命中明细会记进请求日志的「敏」标签(悬停可看命中了哪几条规则)。本项只改发给上游的副本,客户端看到的响应内容不变。": "기본적으로 켜져 있습니다. 적중 내역은 요청 로그의 「민감」 태그에 기록됩니다(마우스를 올리면 어떤 규칙에 걸렸는지 볼 수 있습니다). 이 항목은 업스트림으로 보내는 사본만 바꾸며 클라이언트가 받는 응답 내용은 달라지지 않습니다.", "默认网关只监听 127.0.0.1,只有本机能访问。开启后网关改听所有网卡,同一局域网内的设备可以把 API 地址指向本机 IP 一起使用。出于安全考虑,开启前必须先注册一个面板管理员:管理接口从此要求管理员会话或网关 Key,模型额度不会对局域网裸奔;一把启用的网关 Key 都没有时,转发接口也会拒绝服务,直到创建第一把。改动需要重启应用生效。「同时开放网页管理面板」把管理界面也出给局域网(其他设备的浏览器打开本机 IP 即可管理),默认关闭 —— 桌面端的面板仍只由本程序自己出。": "기본적으로 게이트웨이는 127.0.0.1만 수신하며 로컬에서만 접근할 수 있습니다. 켜면 게이트웨이가 모든 네트워크 인터페이스를 수신하고, 같은 LAN의 기기에서 API 주소를 로컬 IP로 지정해 함께 사용할 수 있습니다. 보안을 위해 켜기 전에 패널 관리자를 먼저 등록해야 합니다. 관리 인터페이스는 이때부터 관리자 세션 또는 게이트웨이 키를 요구하므로 모델 할당량이 LAN에 무방비로 노출되지 않습니다. 활성화된 게이트웨이 키가 하나도 없으면 첫 키를 만들 때까지 포워딩 인터페이스도 서비스를 거부합니다. 변경 사항은 앱을 다시 시작해야 적용됩니다. 「웹 관리 패널도 함께 공개」는 관리 화면까지 LAN에 공개하며(다른 기기의 브라우저에서 로컬 IP를 열면 관리 가능), 기본적으로 꺼져 있습니다. 데스크톱 패널은 계속 이 프로그램에서만 제공됩니다.", + "({n} 个整组隐藏)": " (그룹 {n}개 전체 숨김)", "({n} 行)": "({n}행)", "({n} 项被覆盖)": "({n}개 덮어씀)", "(全部为默认值)": "(모두 기본값)", @@ -475,6 +794,7 @@ "(未生效)": "(미적용)", ",{items}": ", {items}", ",{time}": ", {time}", + ",以及代理池 / 订阅里可能存在的代理凭据。请使用只有你能访问的私有存储;账号密钥由同步密码单独加密,泄露存储本身不等于泄露账号。": ", 그리고 프록시 풀/구독에 있을 수 있는 프록시 자격 증명이 포함됩니다. 본인만 접근할 수 있는 전용 스토리지를 사용하세요. 계정 키는 동기화 비밀번호로 별도 암호화되므로 스토리지가 유출되어도 계정이 유출되지는 않습니다.", ",包括恢复后需要重新登录管理面板。": " 통째로 교체됩니다. 복원 후에는 패널에 다시 로그인해야 합니다.", ",可直接用于登录。请妥善保管,不要外传或上传到公共位置。": ", 바로 로그인에 사용할 수 있습니다. 안전하게 보관하고 외부에 전달하거나 공공장소에 업로드하지 마세요.", ",文件里后续的改动在清空这里之前不再生效": ", 파일의 이후 변경 사항은 여기를 비우기 전까지 적용되지 않습니다", diff --git a/desktop-tauri/i18n/i18n-src/ko/tasks.json b/desktop-tauri/i18n/i18n-src/ko/tasks.json index d2d79092..ab6de593 100644 --- a/desktop-tauri/i18n/i18n-src/ko/tasks.json +++ b/desktop-tauri/i18n/i18n-src/ko/tasks.json @@ -7,6 +7,7 @@ "{n} 秒后": "{n}초 후", "✅ {task}:{summary}": "✅ {task}: {summary}", "✅ 已更新「{label}」": "✅ 「{label}」을(를) 업데이트했습니다", + "「{name}」已执行": "", "上次执行 {time}": "최근 실행 {time}", "上次执行 {time}({result})": "최근 실행 {time}({result})", "下次执行 {time}({relative})": "다음 실행 {time}({relative})", @@ -17,22 +18,31 @@ "停留在「请求日志」页时按此间隔重新拉取请求日志;页面不可见时不请求。": "「요청 로그」 페이지에 머무는 동안 이 간격으로 요청 로그를 다시 가져옵니다. 페이지가 보이지 않으면 요청하지 않습니다.", "冷却中,{relative}重试": "쿨다운 중, {relative} 재시도", "凭证自动维护": "자격 증명 자동 관리", + "分": "", "即将执行": "곧 실행", + "可填 {min}–{max} {unit}": "", "可填 {min}–{max} {unit}(默认 {def} {unit})": "{min}–{max} {unit} 입력 가능 (기본 {def} {unit})", + "后台任务": "", "定时任务已刷新": "예약 작업을 새로고침했습니다", "定时查询积分": "정기 포인트 조회", "定期向 GitHub 查询最新发布版本,默认每 20 分钟一次。检查结果、排期和限流冷却跨重启保留;手动检查也会合并重复请求并遵守冷却。开关与间隔在「软件更新」面板的「更新设置」弹窗里配置(定时任务页不再渲染这一条)。": "정기적으로 GitHub에서 최신 릴리스 버전을 조회하며, 기본 20분에 한 번입니다. 확인 결과, 예약 주기, 속도 제한 쿨다운은 재시작 후에도 유지됩니다. 수동 확인도 중복 요청을 병합하고 쿨다운을 따릅니다. 스위치와 간격은 「소프트웨어 업데이트」 패널의 「업데이트 설정」 팝업에서 구성합니다 (예약 작업 페이지에는 이 항목이 더 이상 표시되지 않습니다).", "定期拉取最新模型清单,重启沿用排期与缓存。客户端读取模型列表的后台刷新也遵守本任务开关、间隔和失败冷却;手动获取模型可提前刷新。": "정기적으로 최신 모델 목록을 가져오며, 재시작 후에도 예약 주기와 캐시를 유지합니다. 클라이언트가 모델 목록을 읽을 때의 백그라운드 새로고침도 이 작업의 스위치, 간격, 실패 쿨다운을 따릅니다. 모델을 수동으로 가져오면 더 일찍 새로고침할 수 있습니다.", "定期拉取最新模型清单,重启沿用排期与缓存。客户端读取模型列表的后台刷新也遵守本任务开关、间隔和失败冷却;手动获取模型可提前刷新。同一轮顺带刷新 models.dev 模型元数据(能力位与思考档位,可在设置页单独关闭)。": "정기적으로 최신 모델 목록을 가져오며, 재시작 후에도 예약 주기와 캐시를 유지합니다. 클라이언트가 모델 목록을 읽을 때의 백그라운드 새로고침도 이 작업의 스위치, 간격, 실패 쿨다운을 따릅니다. 모델을 수동으로 가져오면 더 일찍 새로고침할 수 있습니다。 같은 라운드에서 models.dev 모델 메타데이터(능력 플래그와 사고 등급, 설정 페이지에서 별도로 끌 수 있습니다)도 함께 새로고침합니다.", "定期遍历账号,只刷新已过期或临期的凭证。重启后沿用上次排期,失败账号在冷却后重试;转发需要的按需续期不受此开关影响。": "정기적으로 계정을 순회하며 만료되었거나 만료가 임박한 자격 증명만 갱신합니다. 재시작 후에도 마지막 예약 주기를 유지하고, 실패한 계정은 쿨다운 후 다시 시도합니다. 포워딩에 필요한 온디맨드 갱신은 이 스위치의 영향을 받지 않습니다.", + "已{action}「{name}」": "", "已执行": "실행됨", + "已更新间隔": "", + "开启": "", "执行失败:{reason}": "실행 실패: {reason}", "报表自动刷新": "리포트 자동 새로고침", "改动立即生效,不需要重启程序": "변경 사항은 즉시 적용되며 프로그램을 재시작할 필요가 없습니다", + "改间隔": "", "日志页自动刷新": "로그 페이지 자동 새로고침", "模型目录刷新": "모델 카탈로그 새로고침", "正在加载定时任务…": "예약 작업을 불러오는 중…", + "每 {n} {unit}": "", "没有可显示的定时任务。后端未返回任务清单,请确认网关正在运行。": "표시할 예약 작업이 없습니다. 백엔드가 작업 목록을 반환하지 않았습니다. 게이트웨이가 실행 중인지 확인하세요.", + "没有后台任务": "", "状态每 20 秒自动同步": "상태는 20초마다 자동 동기화", "由页面按间隔自动刷新": "페이지가 간격에 따라 자동 새로고침", "立即执行": "지금 실행", @@ -41,5 +51,8 @@ "还没有执行记录": "실행 기록이 없습니다", "间隔不能为空(可填 {min}–{max} {unit})": "간격은 비울 수 없습니다 ({min}–{max} {unit} 입력 가능)", "间隔必须在 {min}–{max} {unit} 之间(当前填的是 {value})": "간격은 {min}–{max} {unit} 사이여야 합니다 (현재 입력: {value})", - "间隔必须是整数": "간격은 정수여야 합니다" + "间隔必须是 {min}–{max} 的整数": "", + "间隔必须是整数": "간격은 정수여야 합니다", + "页面刷新由浏览器执行,只在页面打开时生效": "", + "页面自动刷新": "" } diff --git a/desktop-tauri/i18n/i18n-src/ko/update.json b/desktop-tauri/i18n/i18n-src/ko/update.json index b1a927b8..5cda3f25 100644 --- a/desktop-tauri/i18n/i18n-src/ko/update.json +++ b/desktop-tauri/i18n/i18n-src/ko/update.json @@ -1,9 +1,14 @@ { " · 检查于 {time}": " · 확인 시각 {time}", " 已发布(当前 {current}),更新日志如下:": "이(가) 출시되었습니다(현재 {current}). 업데이트 노트는 다음과 같습니다:", + "Discord 群": "Discord 서버", "GitHub Token 未填写": "GitHub 토큰 미입력", "GitHub 令牌": "GitHub 토큰", - "QQ 群二维码": "QQ 그룹 QR 코드", + "QQ 群": "QQ 그룹", + "QQ 群 / Telegram / Discord 都在这里": "QQ 그룹, Telegram, Discord가 모두 여기에 있습니다", + "QQ 群 1": "QQ 그룹 1", + "QQ 群 2": "QQ 그룹 2", + "Telegram 群": "Telegram 그룹", "{version} 已就位,服务正在重启,页面会在恢复后自动刷新…({seconds}s)": "{version} 적용이 완료되었습니다. 서비스를 재시작하는 중이며, 복구되면 페이지가 자동으로 새로 고쳐집니다…({seconds}초)", "{version}(预发布)": "{version}(프리릴리스)", "✅ GitHub 令牌已保存(加密存储,界面不再显示)": "✅ GitHub 토큰이 저장되었습니다(암호화되어 저장되며 화면에 다시 표시되지 않습니다)", @@ -30,8 +35,10 @@ "出网线路已切换,但写入磁盘失败(重启后会恢复原线路)": "아웃바운드 경로가 전환되었지만 디스크에 저장하지 못했습니다(재시작하면 원래 경로로 돌아갑니다)", "分钟({min} ~ {max})": "분({min} ~ {max})", "到点自动向 GitHub 查询新版本,查到就走「检测到更新」弹窗;关闭后「检查更新」按钮仍可手动触发": "지정한 시각이 되면 GitHub에 새 버전을 자동으로 조회하고, 찾으면 「업데이트 감지」 팝업을 띄웁니다. 팝업을 닫아도 「업데이트 확인」 버튼으로 수동 확인할 수 있습니다", - "加入 QQ 群": "QQ 그룹 참여", "加入 beta 版": "베타 버전 참여", + "加入交流群": "커뮤니티 참여", + "即将满员": "곧 정원 마감", + "即将满员,请优先加群 2": "곧 정원 마감 — 그룹 2에 참여해 주세요", "去更新": "지금 업데이트", "发现新版本 {latest}(当前 {current})。{at}": "새 버전 감지: {latest} (현재 {current}).{at}", "发现新版本 {latest}(当前 {current}),但该版本没有提供容器内更新包;请在宿主机执行 docker compose pull && docker compose up -d 升级。{at}": "새 버전 {latest}(현재 {current})이 발견되었지만, 이 버전에는 컨테이너 내 업데이트 패키지가 제공되지 않습니다. 호스트에서 docker compose pull && docker compose up -d 를 실행해 업그레이드하세요.{at}", @@ -45,14 +52,22 @@ "回滚失败": "롤백 실패", "回滚失败:{error}": "롤백 실패: {error}", "在 GitHub 查看完整说明": "GitHub에서 전체 안내 보기", + "在 QQ 里搜索群号即可加入。": "QQ에서 그룹 번호를 검색하면 참여할 수 있습니다.", + "在浏览器中打开": "브라우저에서 열기", + "在浏览器中打开 GitHub 发布页": "브라우저에서 GitHub 릴리스 페이지 열기", "在浏览器中打开项目仓库,点 Star 支持作者": "브라우저에서 프로젝트 저장소를 열고 Star를 눌러 제작자를 응원하세요", "在默认浏览器中打开 GitHub 的令牌创建页(备注已预设,无需勾选任何权限,直接点 Generate token)": "기본 브라우저에서 GitHub 토큰 생성 페이지를 엽니다(메모는 미리 채워져 있고, 어떤 권한도 선택할 필요 없이 Generate token을 바로 누르면 됩니다)", "填写后检查限额 60 → 5000 次/小时(创建令牌无需勾选任何权限)": "입력하면 확인 한도가 시간당 60 → 5000회로 올라갑니다(토큰 생성 시 어떤 권한도 선택할 필요가 없습니다)", + "复制群号 {group}": "그룹 번호 {group} 복사", "安装中": "설치 중", "安装包": "설치 패키지", "安装包已下载完成,正在启动安装程序(需要管理员权限,会弹出 UAC 确认框)…": "설치 패키지 다운로드가 완료되어 설치 프로그램을 실행하는 중입니다(관리자 권한이 필요하며 UAC 확인 창이 표시됩니다)…", + "安装包已下载完成,正在启动新的 AppImage…": "설치 프로그램 다운로드 완료. 새 AppImage를 실행하는 중…", + "安装包已下载完成,正在打开系统安装器…": "설치 프로그램 다운로드 완료. 시스템 설치 프로그램을 여는 중…", "安装包已下载完成,正在挂载磁盘映像…": "설치 패키지 다운로드가 완료되어 디스크 이미지를 마운트하는 중입니다…", + "安装包已就绪:{name}。点击「安装并重启」后会启动新的 AppImage;把它移动到你的应用目录并替换旧文件即完成升级(本程序会先退出以释放文件占用)。": "설치 프로그램 준비 완료: {name}. \"설치 후 재시작\"을 누르면 새 AppImage가 실행됩니다. 앱 폴더로 옮겨 이전 파일을 바꿔 놓으면 업그레이드가 끝납니다(파일 점유를 풀기 위해 이 앱이 먼저 종료됩니다).", "安装包已就绪:{name}。点击「安装并重启」后需要管理员权限,会弹出 UAC 确认框。": "설치 패키지가 준비되었습니다: {name}. 「설치 후 재시작」을 클릭하면 관리자 권한이 필요하며 UAC 확인 창이 표시됩니다.", + "安装包已就绪:{name}。点击「打开安装包」后会交给系统安装器,按提示输入密码即可完成安装(本程序无需退出)。": "설치 프로그램 준비 완료: {name}. \"설치 프로그램 열기\"를 누르면 시스템 설치 프로그램으로 넘어갑니다. 안내에 따라 비밀번호를 입력하세요(이 앱은 종료할 필요가 없습니다).", "安装包已就绪:{name}。点击「打开安装包」后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装。": "설치 패키지가 준비되었습니다: {name}. 「설치 패키지 열기」를 클릭하면 디스크 이미지가 마운트되며, 앱을 「응용 프로그램」 폴더로 끌어다 놓으면 설치가 완료됩니다.", "安装包已挂载,请在弹出的窗口里把应用拖进「应用程序」完成安装。安装完成后重新打开本程序即可。": "설치 패키지가 마운트되었습니다. 열린 창에서 앱을 「응용 프로그램」 폴더로 끌어다 놓으면 설치가 완료됩니다. 설치 후 이 프로그램을 다시 열면 됩니다.", "安装失败": "설치 실패", @@ -64,6 +79,7 @@ "已取消": "취소됨", "已填写": "입력됨", "已填写(粘贴新令牌可覆盖)": "입력됨(새 토큰을 붙여넣으면 덮어쓰기)", + "已打开系统安装器,请在它弹出的窗口里完成安装。安装完成后重新打开本程序即可。": "시스템 설치 프로그램을 열었습니다. 표시된 창에서 설치를 마친 뒤 이 앱을 다시 여세요.", "已是最新": "최신 버전입니다", "已替换为 {version},正在重启服务…": "{version}(으)로 교체했습니다. 서비스를 재시작하는 중…", "已还原上一版本,正在重启服务…": "이전 버전으로 복원했습니다. 서비스를 재시작하는 중…", @@ -89,6 +105,7 @@ "更新包从 GitHub 发布页下载,下载完成后可直接启动安装程序并自动重启本程序。安装包会先校验来源域名与文件完整性。检查更新与下载安装包的出网线路、GitHub 令牌都在「更新设置」里:线路默认直连(直连失败时自动借 Clash Verge 的混合端口重试一次),选定指定出口后只走该出口;填写 GitHub 令牌可把检查限额从 60 次/小时提高到 5000 次/小时。": "업데이트 패키지는 GitHub 릴리스 페이지에서 다운로드하며, 다운로드가 끝나면 바로 설치 프로그램을 실행하고 이 프로그램을 자동으로 재시작할 수 있습니다. 설치 패키지는 먼저 출처 도메인과 파일 무결성을 검증합니다. 업데이트 확인과 설치 패키지 다운로드의 아웃바운드 경로, GitHub 토큰은 모두 「업데이트 설정」에 있습니다. 경로는 기본적으로 직접 연결이며(직접 연결 실패 시 Clash Verge의 혼합 포트를 빌려 한 번 재시도), 지정 프록시를 선택하면 해당 경로만 사용합니다. GitHub 토큰을 입력하면 확인 한도를 시간당 60회에서 5000회로 높일 수 있습니다.", "更新包已就绪:{name}。点「安装并重启」后,面板会校验并替换容器内的程序与界面文件,然后重启服务(页面会自动刷新)。": "업데이트 패키지가 준비되었습니다: {name}. 「설치 후 재시작」을 클릭하면 패널이 컨테이너 내부의 프로그램과 UI 파일을 검증해 교체한 뒤 서비스를 재시작합니다(페이지는 자동으로 새로 고쳐집니다).", "更新已应用到容器内,重启服务后生效。{at}": "업데이트가 컨테이너 내부에 적용되었습니다. 서비스를 재시작하면 적용됩니다.{at}", + "更新已应用到容器内,重启服务后生效。{at}(本次检查失败:{error})": "컨테이너 내부에 업데이트가 적용되었습니다. 서비스를 다시 시작하면 적용됩니다.{at}(이번 확인 실패: {error})", "更新日志": "업데이트 노트", "更新设置": "업데이트 설정", "替换完成,正在重启服务…": "교체가 완료되었습니다. 서비스를 재시작하는 중…", @@ -104,10 +121,13 @@ "检查更新": "업데이트 확인", "检查更新与下载安装包走哪条线路(当前:{current});选择即保存。选项来自「网络代理」页;直连失败时会自动借 Clash 混合端口重试一次,选定指定代理后只走它": "업데이트 확인과 설치 패키지 다운로드가 어느 경로를 사용할지 지정합니다(현재: {current}). 선택하면 바로 저장됩니다. 옵션은 「네트워크 프록시」 페이지에서 가져옵니다. 직접 연결 실패 시 Clash 혼합 포트를 빌려 한 번 재시도하며, 지정 프록시를 선택하면 해당 프록시만 사용합니다", "检查更新失败:{error}": "업데이트 확인 실패: {error}", + "检查更新失败:{error}(以下版本与更新日志来自上次成功检查,检查于 {time})": "업데이트 확인 실패: {error} (아래 버전과 업데이트 내역은 마지막으로 성공한 확인 시점({time})의 것입니다)", "检查间隔必须是 {min} ~ {max} 分钟": "확인 간격은 {min} ~ {max}분이어야 합니다", "检查间隔(分钟)": "확인 간격(분)", "检测到更新": "업데이트 감지", "正在下载 {name}:{percent}%({received} / {total} MB)": "{name} 다운로드 중: {percent}%({received} / {total} MB)", + "正在下载安装包…(下载完成后会交给系统安装器,按提示输入密码即可完成安装)": "설치 프로그램을 내려받는 중…(완료되면 시스템 설치 프로그램으로 넘어갑니다. 안내에 따라 비밀번호를 입력하세요)", + "正在下载安装包…(下载完成后会启动新的 AppImage;把它替换掉旧文件即完成升级)": "설치 프로그램을 내려받는 중…(완료되면 새 AppImage가 실행됩니다. 이전 파일을 바꿔 놓으면 업그레이드가 끝납니다)", "正在下载安装包…(下载完成后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装)": "설치 패키지 다운로드 중…(다운로드가 끝나면 디스크 이미지를 마운트합니다. 앱을 「응용 프로그램」 폴더로 끌어다 놓으면 설치가 완료됩니다)", "正在下载安装包…(下载完成后启动安装程序需要管理员权限,会弹出 UAC 确认框)": "설치 패키지 다운로드 중…(다운로드가 끝나면 설치 프로그램을 실행하며 관리자 권한이 필요하고 UAC 확인 창이 표시됩니다)", "正在下载服务端更新包…(下载完成后由面板完成替换,重启容器内的服务后生效)": "서버 업데이트 패키지를 다운로드하는 중…(다운로드가 끝나면 패널이 파일을 교체하고, 컨테이너 내부 서비스를 재시작하면 적용됩니다)", @@ -120,8 +140,9 @@ "点击「检查更新」后,这里会显示最新版本的更新说明。": "「업데이트 확인」을 클릭하면 여기에 최신 버전의 업데이트 노트가 표시됩니다.", "点击「检查更新」查询 GitHub 上的最新发布版本。": "「업데이트 확인」을 클릭하면 GitHub의 최신 릴리스 버전을 조회합니다.", "点击「检查更新」查询最新发布版本。": "「업데이트 확인」을 클릭하면 최신 릴리스 버전을 조회합니다.", + "点击在浏览器中打开": "클릭하면 브라우저에서 열립니다", + "点击复制群号": "클릭하면 그룹 번호가 복사됩니다", "环境变量": "환경 변수", - "用手机 QQ 扫上面的二维码,或在 QQ 里搜索群号加入。": "위 QR 코드를 모바일 QQ로 스캔하거나, QQ에서 그룹 번호를 검색해 참여하세요.", "立即重启": "지금 재시작", "粘贴 GitHub 令牌(ghp_… / github_pat_…)": "GitHub 토큰 붙여넣기(ghp_… / github_pat_…)", "群号": "그룹 번호", @@ -135,6 +156,7 @@ "这一版没有提供容器内更新包": "이 버전에는 컨테이너 내 업데이트 패키지가 없습니다", "这个版本没有填写发布说明。": "이 버전에는 릴리스 노트가 없습니다.", "通道已切换,但写入磁盘失败(重启后会回到原设置)": "채널이 전환되었지만 디스크에 저장하지 못했습니다(재시작하면 원래 설정으로 돌아갑니다)", + "遇到问题或有使用心得,欢迎到群里交流。": "질문이나 사용 팁은 각 그룹에서 나눠 주세요.", "重启中": "재시작 중", "重新粘贴保存一次即可自愈": "토큰을 다시 붙여넣고 저장하면 해결됩니다", "重试下载": "다운로드 다시 시도", diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/accounts.json b/desktop-tauri/i18n/i18n-src/pt-BR/accounts.json index 49cfec8b..6688f295 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/accounts.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/accounts.json @@ -7,7 +7,9 @@ "0 = 不限": "0 = sem limite", "0 = 按本家默认 {default}": "0 = padrão do provedor {default}", "30 秒 ~ 24 小时;手动查询会顺延下一轮": "30 segundos ~ 24 horas; consultas manuais entram na próxima rodada", + "7 天内到期": "Expira em até 7 dias", "API Key": "API Key", + "Accio 国内版": "Accio edição da China", "Base URL": "Base URL", "Clash Verge 里还没有配置混合监听器或节点端口": "Ainda não há listener misto ou porta de nó configurados no Clash Verge", "Clash 出口:": "Saída do Clash: ", @@ -15,12 +17,18 @@ "NewAPI 用户 ID": "ID de usuário do NewAPI", "OpenAI 兼容": "Compatível com OpenAI", "OpenAI 兼容填到 /v1;Anthropic 填根地址": "Compatível com OpenAI: preencha até /v1; Anthropic: preencha o endereço raiz", + "Qoder 中国版": "Qoder edição da China", + "Qoder 国际版": "Qoder edição internacional", "Token 单位": "Unidade do Token", "Token 时": "tokens", "Token 消耗": "Consumo de tokens", "Token 限额": "Limite de tokens", "Token 限额 · 已禁用": "Limite de tokens · Desativada", "Token 限额 · 已跳过": "Limite de tokens · Ignorada", + "WorkBuddy 国内版": "WorkBuddy edição da China", + "WorkBuddy 国际版": "WorkBuddy edição internacional", + "ZCode 国内版": "ZCode edição da China", + "ZCode 国际版": "ZCode edição internacional", "ZCode 套餐明细 — {name}": "Planos do ZCode — {name}", "bigmodel_token_production 的值": "Valor de bigmodel_token_production", "{action}{who}": "{action} {who}", @@ -32,13 +40,24 @@ "{label}({count})": "{label} ({count})", "{message}(去该账号的「设置」里填上查询凭证即可)": "{message} (basta preencher a credencial de consulta nas 「Configurações」 desta conta)", "{month}月{day}日 {clock}": "{day}/{month} {clock}", + "{name} · 分组设置": "{name} · Configurações do grupo", "{name} 移到 {group} 组内第 {rank} 位": "{name} movida para a posição {rank} dentro do grupo {group}", "{name} 移到第 {rank} 顺位": "{name} movida para a posição global {rank}", + "{name}的方向": "Direção de {name}", "{name}({parts})": "{name} ({parts})", "{name}({time} 起生效)": "{name} (entra em vigor em {time})", + "{n} 个已计入": "{n} contabilizados", + "{n} 个查询失败": "{n} falharam na consulta", "{n} 个模型 ▾": "{n} modelos ▾", + "{n} 个模型限流中 · 到恢复时间自动解除": "", "{n} 个模型限流中 · 记录来自上游 429 / 限额码,到恢复时间自动解除": "{n} modelos com rate limit · registro vindo do 429 / código de limite do upstream; liberação automática no horário de recuperação", "{n} 个请求正在使用该账号(含还在下发内容的流式请求)": "{n} requisições usando esta conta (incluindo requisições de streaming que ainda enviam conteúdo)", + "{n} 个读数只读到一半(读数可信但不完整)": "{n} lidos apenas em parte (valor confiável, mas incompleto)", + "{n} 个读数没有绝对数(只给比例或无额度),未计入": "{n} sem valor absoluto (apenas percentual ou sem cota), não contabilizados", + "{n} 个账号 · {e} 启用 · {l} 限流": "{n} contas · {e} ativas · {l} limitadas", + "{n} 个账号 · 启用 {e}": "{n} contas · {e} ativas", + "{n} 个还没查到(未查询或正在查询)": "{n} ainda não lidos (não consultados ou em andamento)", + "{n} 个还没查询": "{n} ainda não consultados", "{n} 分钟": "{n} minutos", "{n} 分钟后": "daqui a {n} minutos", "{n} 天后": "daqui a {n} dias", @@ -47,6 +66,9 @@ "{n} 秒": "{n} segundos", "{period}窗口": "Janela de {period}", "{plan}({time} 领取)": "{plan} (resgatado em {time})", + "{reason}。该账号的登录态已被上游注销,自动刷新不会再成功,请重新登录该账号": "{reason}. O upstream revogou a sessão desta conta; a atualização automática nunca mais vai funcionar — entre novamente nesta conta.", + "{reason}之后组顺位才可拖动(别家的组整组不可见,落点没法定位)": "", + "{reason}之后顺序才可拖动(模型作用域是全局队列的投影,这里没有可写的顺序)": "", "{scope} {used} / {quota} Token,{countdown}重置": "{scope}: {used} / {quota} Token, {countdown} até a redefinição", "{text}过期": "Expira {text}", "{text}过期({full})": "Expira {text} ({full})", @@ -79,7 +101,9 @@ "✅ 顺序已调整": "", " 出口 IP {ip}": " IP de saída {ip}", "、": ", ", - "、补官方会话头、请求体补两个占位工具(上游免费档的三道校验)。": ", adiciona os cabeçalhos de sessão oficiais e inclui dois tools de preenchimento no corpo da requisição (as três validações do plano gratuito do upstream).", + "、补官方会话头、请求体补占位工具(上游免费档的过门校验)。": ", adiciona os cabeçalhos de sessão oficiais e um tool de preenchimento no corpo da requisição (as validações de formato do plano gratuito do upstream).", + "「{name}」已在「网络代理」页禁用,请先启用它或换一条": "\"{name}\" está desativado na página \"Proxy de rede\". Ative-o primeiro ou escolha outro.", + "「{name}」开着排序规则:先关掉规则,才能手动调整组内顺序": "“{name}” está com regras de ordenação ativas: desligue-as antes de ajustar a ordem dentro do grupo", "「{name}」本身": "o próprio 「{name}」", "「已保存的代理」": "「Proxies salvos」", "「网络代理」页还没有出口 —— 去那里新增,或在「订阅管理」里点「同步 Clash Verge」把 Clash 的出口导进来": "A página 「Proxy de rede」 ainda não tem saídas — crie uma por lá ou clique em 「Sincronizar Clash Verge」 em 「Gerenciar assinaturas」 para importar as saídas do Clash", @@ -91,6 +115,8 @@ "上游的 API Key。留空 = 不修改(改凭证不会换账号 id);要清掉已配的凭证用右边的「清除」": "API Key do upstream. Em branco = não alterar (mudar a credencial não troca o id da conta); para remover a credencial configurada, use 「Limpar」 à direita", "上游的余额读数里没有这个套餐的额度桶,剩余量读不到;余额列显示的是套餐发放的权益额度,不是剩余量": "A leitura de saldo do upstream não tem um balde de cota para este plano, então o restante não está disponível; a coluna de saldo mostra a concessão do plano, não o restante", "上游返回 {status}": "O upstream retornou {status}", + "上面所有规则都相同时,保持你拖出来的顺序": "Quando todas as regras acima empatarem, mantenha a ordem que você arrastou", + "不到 1 分钟": "menos de 1 minuto", "不可用:{error}": "Indisponível: {error}", "不限": "Sem limite", "与队列里的上一个账号交换优先级(可能是另一家的账号)": "Troca a prioridade com a conta anterior da fila (pode ser uma conta de outro provedor)", @@ -98,11 +124,16 @@ "个": "unid.", "个 · 共": "de", "个人": "Pessoal", + "主凭证没有固定到期时刻,到期由网关自动续期或本来就不会过期": "A credencial principal não tem prazo fixo — o gateway a renova automaticamente, ou ela simplesmente não expira", "主机": "Host", "仅将优先级调整到全局第一位,不改变启用状态": "Apenas move a prioridade para o primeiro lugar global, sem alterar o estado de ativação", + "仅按手动顺序": "Somente ordem manual", "仅账号管理": "Somente gestão de contas", + "今天 24 点": "", "今天 {clock}": "Hoje {clock}", "今天 {time}": "hoje às {time}", + "今天剩余:{name}": "", + "今天剩余:{name} · 这一份还剩 {value}(每天 24 点回满)": "", "今天还没有领过:上面「可领取的套餐」里逐份点「领取」即可": "Nada resgatado hoje ainda: basta clicar em \"Resgatar\" em cada plano de \"Planos resgatáveis\" acima.", "今天(北京时间 {day})已由官方确认到账 {n} 项;领到的是套餐赠送积分,不增加福利模型的 token 池;按自然日重置,明天可再领": "Hoje (horário de Pequim, {day}), o serviço oficial confirmou o recebimento de {n} itens; o que foi recebido são pontos de plano e não aumentam o pool de tokens dos modelos de benefício; a redefinição é por dia natural e amanhã é possível resgatar novamente", "今天(北京时间 {day})已试过 {n} 次但官方尚未确认到账,": "Hoje (horário de Pequim, {day}) já foram feitas {n} tentativas, mas o serviço oficial ainda não confirmou o recebimento. ", @@ -137,18 +168,22 @@ "余额不足": "Saldo insuficiente", "余额不足 · 已跳过": "Saldo insuficiente · Ignorada", "余额低于": "Saldo abaixo de", + "余额低于阈值时的提示": "Avisar quando o saldo cair abaixo do limite", "余额低于阈值,转发时会跳过该账号(余额回升自动恢复)": "Saldo abaixo do limite; a conta será ignorada no encaminhamento (recuperada automaticamente quando o saldo subir)", "余额低于限制器的阈值,转发时会跳过该账号(余额回升自动恢复)": "Saldo abaixo do limite do Limitador; a conta será ignorada no encaminhamento (recuperada automaticamente quando o saldo subir)", "余额列读数的自动刷新;也是「限制器」余额规则的数据来源。": "Atualização automática da leitura da coluna de saldo; também é a fonte de dados das regras de saldo do 「Limitador」.", "余额响应为空": "Resposta de saldo vazia", + "余额已更新": "Saldo atualizado", "余额查询凭证": "Credencial de consulta de saldo", "余额查询失败:{message}": "Falha na consulta de saldo: {message}", "余额查询方案:{label}": "Esquema de consulta de saldo: {label}", "余额沿用现有口径;Token 按重置方式内的累计消耗判定(切换类型会清空阈值)": "O saldo segue o critério atual; o Token é avaliado pelo consumo acumulado dentro do modo de redefinição (trocar o tipo limpa o limite)", + "余额详情": "Detalhes do saldo", "使用套餐": "Usar plano", "保存失败:{error}": "Falha ao salvar: {error}", "保存规则": "Salvar regra", "修改代理": "Alterar proxy", + "倍率 {rate}": "", "免费版": "Plano gratuito", "全局一条队列:数值越小越先用,不分提供商": "Uma única fila global: quanto menor o valor, antes é usada, sem distinção de provedor", "全局唯一:所有提供商的账号都不能重号,数值越小越先用": "Único global: o número não pode se repetir entre contas de qualquer provedor; quanto menor, antes é usada", @@ -165,18 +200,30 @@ "关闭则不参与转发(余额查询照常,见「查询设置」)": "Desligado, a conta não participa do encaminhamento (a consulta de saldo continua; veja 「Configurações de consulta」)", "内消耗 ≥": "com consumo ≥", "再领一次": "Resgatar de novo", + "冷却进度 {percent}%": "", + "凭证": "", "凭证与套餐": "Credenciais e plano", + "凭证已刷新": "", + "凭证已被上游注销": "", "凭证已过期,转发时会先刷新": "Credencial expirada; será renovada antes do encaminhamento", "出口": "Saída", "出网代理": "Proxy de saída", + "分组设置": "Configurações do grupo", "分钟": "minutos", + "切回全局账号表": "", + "删除中…": "", "删除提供商": "Excluir provedor", "删除账号": "Excluir conta", "删除这条账号记录(不会影响客户端自己的登录态;之后可再点「导入桌面端登录态」加回来)": "Exclui este registro de conta (não afeta o estado de login do próprio cliente; depois é possível adicioná-la de volta com 「Importar estado de login do desktop」)", "删除选中的账号": "Excluir contas selecionadas", "到恢复时间后自动解除,无需手动操作": "Liberado automaticamente ao chegar o horário de recuperação, sem ação manual", "到期 {time}": "Expira em {time}", + "到期 近→远": "Expiração perto→longe", + "到期 远→近": "Expiração longe→perto", + "到期时刻 {time}": "", "刷新 Token": "Renovar Token", + "刷新凭证": "", + "刷新队列": "", "剩 {left} / {total}{unit}": "{left} / {total}{unit} restantes", "剩 {n} 分钟": "restam {n} minutos", "剩 {remaining}": "Restam {remaining}", @@ -186,6 +233,8 @@ "协议": "Protocolo", "卡片": "", "又不勾": " e não marque esta opção:", + "取消排序": "", + "取消排序,回到默认次序(不影响已写回的队列顺序)": "", "取消选择": "Limpar seleção", "另 {n} 个分组全是禁用账号": "", "另有 {n} 个已勾选账号被当前筛选隐藏,仍会参与操作": "Outras {n} contas marcadas estão ocultas pelo filtro atual, mas ainda participam da operação", @@ -196,9 +245,13 @@ "可用合计": "Total disponível", "可留空:新版请求头只认令牌;旧版会把用户 ID 放进 New-Api-User 头": "Pode ficar vazio: versões novas usam só o token; as antigas colocam o ID de usuário no cabeçalho New-Api-User", "可领取的套餐": "Planos resgatáveis", + "各厂商组内排序:分组与组序不变;组内先后已写回调度队列": "", "同提供商内暂无其他账号占用优先级": "Nenhuma outra conta deste provedor ocupa uma prioridade", "同提供商已占用:{names}": "Já em uso neste provedor: {names}", "名称": "Nome", + "后续加入": "Em breve", + "否": "", + "含 {n} 个不限额度账号,合计记为 ∞": "inclui {n} conta(s) sem limite; total exibido como ∞", "启用状态": "Estado de ativação", "启用该规则": "Ativar esta regra", "启用该账号": "Ativar esta conta", @@ -208,6 +261,7 @@ "周期单位": "Unidade do período", "商汤日日新控制台的登录密码": "Senha de login do console SenseNova", "商汤日日新控制台的登录账号": "Conta de login do console SenseNova", + "固定兜底,不受开关影响": "Fallback fixo, não afetado pelos interruptores", "固定周期": "Período fixo", "在 NewAPI 站点「个人设置」里生成的访问令牌": "Token de acesso gerado em \"Configurações pessoais\" no site do NewAPI", "在列表里拖:拖分组带调整整组顺位,拖行首把手调整组内先后(顺序由拖拽表达,不再填数字)": "Arraste na lista: arraste a faixa do grupo para reordenar grupos inteiros e a alça da linha para reordenar dentro do grupo (a ordem se expressa arrastando — nada de digitar números)", @@ -216,6 +270,8 @@ "基本": "Básico", "备注名 / 启用 / 代理": "Rótulo / Ativação / Proxy", "备注名 {name}": "Rótulo {name}", + "备注名不能为空": "O rótulo não pode ficar vazio", + "备注名已更新": "Rótulo atualizado", "失败 {count} 个:": "{count} com falha: ", "套餐 {name}": "Plano {name}", "套餐明细弹窗未加载,请重启应用后重试": "A janela de detalhes dos planos não foi carregada; reinicie o aplicativo e tente de novo", @@ -225,10 +281,12 @@ "套餐池(Start Plan)— 走 zcode.z.ai 端点,消耗 ZCode 套餐池的额度(新用户体验额度 / 活动领取)": "Pool de planos (Start Plan) — usa os endpoints de zcode.z.ai e a cota do pool de planos do ZCode (cota de experiência de novos usuários / resgates de evento)", "套餐积分的重置日期": "Data de redefinição dos pontos do plano", "它会改写请求体": "Ele reescreve o corpo da requisição", + "完整": "", "官方限时活动": "Evento oficial por tempo limitado", "对话 token": "Tokens de conversa", "将作用于以下账号": "Será aplicado às seguintes contas", "小时": "horas", + "小浣熊": "Raccoon", "尚未查询该账号的余额": "O saldo desta conta ainda não foi consultado", "展开全部": "Expandir tudo", "展开全部分组": "Expandir todos os grupos", @@ -236,16 +294,25 @@ "展开这一组": "Expandir este grupo", "已保存的代理": "Proxies salvos", "已停用": "Desativada", + "已删除账号": "", + "已启用 {name}": "{name} ativada", "已启用,点击禁用(不参与转发)": "Ativada; clique para desativar (não participa do encaminhamento)", "已在全局队列第一位": "Já está em primeiro lugar na fila global", + "已按「{label}」{dir}排序": "", + "已按「{label}」{dir}排序并保存": "", + "已是首选": "Já é a primeira", "已更新 {ok}/{total} 个账号,{failed} 个失败": "{ok}/{total} contas atualizadas, {failed} com falha", + "已查询": "Consultado", "已用 {used}": "Usado {used}", "已用 {used} / 共 {total}": "Usado {used} / {total}", "已用完 / 已过期": "Esgotados / expirados", "已禁用": "Desativada", + "已禁用 {name}": "{name} desativada", "已禁用,点击启用": "Desativada; clique para ativar", + "已筛选 {n} 项": "{n} exibidos", "已被「{name}」占用": "Já em uso por 「{name}」", "已触发 · 账号已禁用": "Disparado · Conta desativada", + "已设为首选": "Definida como primeira", "已设置": "Configurado", "已跳过{current}": "Ignorada{current}", "已跳过{usage}{countdown}": "Ignorada{usage}{countdown}", @@ -257,10 +324,10 @@ "已限流": "Com rate limit", "已领到、还没到生效时间的套餐(生效后余额列会按它显示):\n{lines}": "Planos já resgatados que ainda não entraram em vigor (a coluna de saldo passa a mostrá-los depois):\n{lines}", "并发上限": "Limite de concorrência", - "并发上限 0~{max};有上游硬顶的家(如 CodeArts)以它的硬顶为准;达到上限的账号会被跳过、请求转给其他账号": "Limite de concorrência 0~{max}; provedores com limite rígido upstream (ex.: CodeArts) seguem esse limite; ao atingir o limite a conta é ignorada e a requisição é repassada a outra conta", + "并发上限 0~{max};CodeArts 等有默认值的家填 0 即按本家默认;达到上限的账号会被跳过、请求转给其他账号": "Limite de concorrência 0~{max}; provedores com valor padrão (ex.: CodeArts) usam esse padrão quando o valor é 0; ao atingir o limite a conta é ignorada e a requisição é repassada a outra conta", "并发上限 0~{max};达到上限的账号会被跳过、请求转给其他账号": "Limite de concorrência 0~{max}; ao atingir o limite a conta é ignorada e a requisição é repassada a outra conta", "并发上限 0~{max};达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "Limite de concorrência 0~{max}; ao atingir o limite, a conta é ignorada e a requisição é repassada a outra conta (a entrada 「Limite de concorrência」 do menu ⋯ da lista foi incorporada aqui)", - "并发上限 受上游硬顶约束,达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "O limite de concorrência está sujeito ao teto rígido do upstream; ao atingir o limite, a conta é ignorada e a requisição é repassada a outra conta (a entrada 「Limite de concorrência」 do menu ⋯ da lista foi incorporada aqui)", + "并发上限 填 0 即按本家默认 {default};填 1 或 2 就按填的算,更大的数会被本家上限截断(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "0 usa o padrão do provedor ({default}); 1 ou 2 valem como informados, valores maiores são limitados pelo teto do provedor (a entrada 「Limite de concorrência」 do menu ⋯ da lista foi incorporada aqui)", "并发上限必须是 0~{max} 的整数": "O limite de concorrência deve ser um número inteiro de 0 a {max}", "并发上限:{value}": "Limite de concorrência: {value}", "开启后,网关按 OpenCode 官方 CLI 的形状发请求:没填 Key 的账号用匿名凭证": "Quando ativado, o Gateway envia requisições no formato do CLI oficial do OpenCode: contas sem Key usam credencial anônima", @@ -268,10 +335,16 @@ "当前代理不可用:{error}(转发时会回退直连)": "Proxy atual indisponível: {error} (o encaminhamento volta para conexão direta)", "当前余额 {n};与余额列同一数字口径,严格小于才触发(等于阈值仍可用)": "Saldo atual {n}; mesmo critério numérico da coluna de saldo; só dispara se for estritamente menor (igual ao limite ainda é utilizável)", "当前可用 ∞(不参与余额不足判定)": "Disponível atual ∞ (não participa da avaliação de saldo insuficiente)", + "当前已经是默认规则": "Já são as regras padrão", + "当前排序规则:{rules}。点击调整": "Ordenação atual: {rules}. Clique para ajustar", "当前没有任何模型处于限流中": "No momento, nenhum modelo está com rate limit", "当前没有可领取的套餐(活动期内每天发一份,明天可再领)": "Nenhum plano resgatável agora (um é emitido por dia durante o evento; dá para resgatar de novo amanhã)", "当前没有限流中的模型。": "No momento, não há modelos com rate limit.", + "当前筛选下 {n} 个账号的可用余额之和(随筛选、搜索变化,不受分页影响)": "", + "当前筛选下没有账号": "Nenhuma conta corresponde aos filtros atuais", "当前筛选条件下没有账号": "Nenhuma conta com o filtro atual", + "当前规则:{rules}。自动排序生效时,组内拖动把手会锁定;关掉全部规则即回到手动顺序。": "Regras atuais: {rules}. Enquanto a ordenação automática estiver ativa, a alça de arrastar dentro do grupo fica bloqueada; desligue todas as regras para voltar à ordem manual.", + "当前视图不能调整组内顺序:{reason} 之后再拖动": "", "当前视图不能调整组顺位:{reason} 之后再拖动": "Não é possível alterar a ordem dos grupos nesta visualização: volte para {reason} antes de arrastar", "当前:{label}(未经过代理池 —— 可在「网络代理」页把出口同步进池后来这里改选)": "Atual: {label} (não passa pelo pool de proxies — sincronize a saída para o pool na página 「Proxy de rede」 e depois volte aqui para alterar)", "当前:{label}(每条请求换一个出口);「自定义代理…」打开完整设置": "Atual: {label} (uma saída diferente por requisição); \"Proxy personalizado…\" abre as configurações completas", @@ -284,13 +357,20 @@ "必填;留空 = 不修改,清除用右边的按钮": "Obrigatório; vazio = não altera, limpe com o botão à direita", "总剩余 {total} · 套餐 {plan} · 奖励 {bonus}": "Total restante {total} · Plano {plan} · Bônus {bonus}", "总量 {value}": "Total {value}", + "恢复到「积分到期 → 添加时间」这套默认规则": "Restaurar as regras padrão: expiração → data de adição", "恢复时间未知": "Horário de recuperação desconhecido", "我的套餐": "Meus planos", + "所属厂商:{label}": "", "所有分组里都没有启用的账号(禁用不参与转发)": "", + "所有提供商都有这个字段": "Todos os provedores têm este campo", + "所有积分包里**最先作废**的那一份什么时候到期;逐份明细见「余额」格里的积分明细": "", + "所有积分包里最先作废的那一份什么时候到期;没有一次性包时退到每日重置额度、显示今天还剩多久;逐份明细见「余额」格里的积分明细": "", "手动添加": "Adição manual", "打开批量操作弹窗,并预选这一组的账号(改代理等动作在弹窗里挑参数)": "Abre a janela de operações em lote com as contas deste grupo pré-selecionadas (os parâmetros, como o proxy, escolhem-se na janela)", + "打开筛选": "", "执行中…": "Executando…", "执行({action})": "Executar ({action})", + "扫码 / 粘贴令牌 / 导入登录态需在桌面端完成": "", "批量动作": "Ação em lote", "批量操作 · 已选 {count} 个账号": "Operação em lote · {count} contas selecionadas", "批量操作失败:{error}": "Falha na operação em lote: {error}", @@ -303,20 +383,31 @@ "拖动调整列宽(双击还原)": "Arraste para ajustar a largura da coluna (clique duas vezes para restaurar)", "拖动调整整组顺序": "Arraste para reordenar grupos inteiros", "拖动调整组内顺序": "Arraste para reordenar dentro do grupo", + "按优先级从上到下比较,勾选的项才参与": "Comparadas de cima para baixo; só as regras marcadas participam", "按住拖动,调整这一家的组顺位(整组一起动)": "Segure e arraste para mudar a posição de grupo deste provedor (o grupo inteiro se move junto)", "按住拖动,调整这个账号在本组内的顺序": "Segure e arraste para mudar a ordem desta conta dentro do seu grupo", "按提供商筛选账号": "Filtrar contas por provedor", "按模型": "Por modelo", "按规则限制该账号参与转发:没有规则(或全部停用)= 不限制;跳过档自动恢复,禁用档需手动启用。": "Restringe a participação desta conta no encaminhamento por regras: sem regras (ou todas desativadas) = sem restrição; a ação de ignorar se recupera automaticamente e a de desativar exige ativação manual.", + "按账号最早到期、且还有剩余的积分包比较": "Compara cada conta pelo pacote de créditos mais próximo do vencimento que ainda tem saldo e não expirou", + "排序": "Ordenação", + "排序粒度": "", "提供商显示名,1~{max} 个字符": "Nome de exibição do provedor, de 1 a {max} caracteres", "提供商:{name}": "Provedor: {name}", + "提高优先级": "Aumentar prioridade", "搜索": "Pesquisar", "搜索账号": "Pesquisar contas", + "搜索账号 / 邮箱 / 备注": "Buscar conta / e-mail / nota", "操作失败:{message}": "Falha na operação: {message}", "收起": "Recolher", + "改动即时生效": "As alterações entram em vigor imediatamente", "改协议 / Base URL 会改变它们的转发方式,正在进行的请求可能失败;模型清单与映射在「模型管理」页。": "Alterar o protocolo / Base URL muda a forma de encaminhamento delas e requisições em andamento podem falhar; a lista de modelos e o mapeamento ficam na página 「Gerenciamento de modelos」.", "数值越小越先用;": "Quanto menor o valor, antes é usada; ", "数值越小越先用;{hint}": "Quanto menor o valor, antes é usada; {hint}", + "整表": "", + "整表排序:厂商分组已散开,行里带厂商标记。此顺序仅用于查看,不写回调度队列 —— 写回会打破「同一家连续」的队列规则": "", + "整表排序:打散厂商分组,全表一起来(行里带厂商标记;仅用于查看,不写回队列)": "", + "新账号落到组首 / 组尾": "Novas contas no topo / no fim", "旗舰": "Ultimate", "无上限 · 已用 {used}": "Sem limite · Usado {used}", "无上限按日计(上游到达上限后仍继续计数,超额如实显示)": "Sem limite, contado por dia (o upstream continua contando após atingir o limite e o excedente é exibido com fidelidade)", @@ -329,24 +420,39 @@ "明天 {clock}": "Amanhã {clock}", "明天 {time}": "amanhã às {time}", "明细": "Detalhes", + "是": "", "昵称 {name}": "Apelido {name}", "显示账号名": "Mostrar nome da conta", "暂无可查询余额的账号": "Nenhuma conta com saldo consultável", + "暂无数据:先查询一次余额,之后会自动按到期排序": "Sem dados ainda: consulte o saldo uma vez e a ordenação por vencimento passa a valer automaticamente", "暂无账号,请点击右上角「添加账号」": "Nenhuma conta; clique em 「Adicionar conta」 no canto superior direito", "更多操作": "Mais ações", + "更多设置": "Mais configurações", "更新于 {time}": "Atualizado em {time}", + "最先作废的那一份还剩多少(不是面额);按这一份的剩余量排序,缺失值恒排最后": "", + "最先作废的那一份还剩多少(不是面额:这一列回答「马上要作废掉多少」)": "", + "最快到期优先": "Vencimento mais próximo primeiro", + "最新添加优先": "Mais recentes primeiro", "最早 {soonest} 恢复": "Recuperação mais cedo: {soonest}", "最早恢复": "Recuperação mais cedo", + "最早添加优先": "Mais antigas primeiro", + "最晚到期优先": "Vencimento mais distante primeiro", + "最近到期": "", + "最近到期积分": "", + "最近到期:{name}": "", + "最近到期:{name} · 这一份还剩 {value}(不是面额)": "", + "最近更新": "", "有效期": "Validade", "有的账号过了 0 点额度就回来 —— 选「自然日」": "Algumas contas têm a cota restaurada após as 0h — escolha 「Dia natural」", "未使用": "Não usado", - "未分配端口": "Sem porta atribuída", "未命名账号": "Conta sem nome", "未查询": "Não consultado", "未检测到 Clash Verge": "Clash Verge não detectado", "未检测到 Clash Verge 配置": "Configuração do Clash Verge não detectada", + "未被使用": "Não utilizado", "未触发{current}": "Não acionada{current}", "未触发{usage}{countdown}": "Não acionada{usage}{countdown}", + "未设备注名": "Sem rótulo", "未返回余额数据": "Nenhum dado de saldo retornado", "未返回可识别的余额数据": "Nenhum dado de saldo reconhecível retornado", "未配置": "Não configurado", @@ -356,9 +462,12 @@ "本周期已用": "Uso do período", "本地台账里今天还领过这些,但上面的读数里还没有:{plans}(读数刷新可能有延迟)": "O registro local mostra outros resgates de hoje que ainda não aparecem nas leituras acima: {plans} (a leitura pode demorar a atualizar)", "本地领取于 {time}": "Resgatado localmente em {time}", + "本组 {n} 个账号的可用余额之和,按整组统计(不受筛选、搜索影响)": "Soma do saldo disponível das {n} contas deste grupo, contabilizada sobre o grupo inteiro (não afetada por filtros ou busca)", + "本组代理": "Proxy deste grupo", "本组操作": "Ações do grupo", "权益 {value}": "Concedido {value}", "权益 {value} {unit}": "Concedido {value} {unit}", + "条件即时生效,关掉面板就能看到结果。": "Os filtros aplicam na hora — feche o painel para ver o resultado.", "来源 {source}": "Origem: {source}", "查看": "Ver", "查看可领取的套餐与名下已有的套餐(每天一期,领取需过一次人机验证)": "Ver os planos resgatáveis e os planos que você já tem (um por dia; resgatar exige uma verificação humana)", @@ -374,12 +483,14 @@ "查询间隔": "Intervalo de consulta", "查询间隔必须是 {min} 秒 ~ 24 小时": "O intervalo de consulta deve ser de {min} segundos ~ 24 horas", "桌面端实时登录态(凭证每次从客户端登录态文件读取)": "Estado de login em tempo real do desktop (a credencial é lida do arquivo de login do cliente a cada uso)", + "模型作用域下分组带随队列走,不折叠": "", "正在加载…": "Carregando…", "正在探测可领取的套餐…": "Verificando planos resgatáveis…", "正在查询": "Consultando", "正在读取 Clash Verge 配置…": "Lendo a configuração do Clash Verge…", "正在读取…": "Lendo…", "正在读取代理列表…": "Lendo a lista de proxies…", + "正在读取调度队列…": "", "正在连接上游…": "Conectando ao upstream…", "正常": "Normal", "此刻正在使用这个账号的请求数(含还在下发内容的流式请求);为 0 时不显示": "Número de requisições usando esta conta neste momento (incluindo requisições de streaming que ainda enviam conteúdo); não é exibido quando é 0", @@ -388,10 +499,13 @@ "每日消耗 ≥": "Consumo diário ≥", "每日消耗 ≥ {count} Token(每天 0 点重置)": "Consumo diário ≥ {count} Token (redefinido às 0h de cada dia)", "每日重置": "Renova diariamente", + "每日重置({time} 回满,不是作废)": "", "每日重置(额度按周期回满,不是作废)": "Renova diariamente (a cota volta ao total a cada ciclo; ela não é perdida)", "每条请求换一个出口(按顺序轮流),上游看到的 IP 因此是变化的;出口的地址与端口由「网络代理」页管理": "Cada requisição passa para a próxima saída, então o upstream vê um IP variável; endereços e portas das saídas são gerenciados na página Proxy de rede", + "没有匹配「{kw}」的账号": "Nenhuma conta corresponde a \"{kw}\"", "没有可全选的账号": "Nenhuma conta para selecionar", "没有可用出口": "Nenhuma saída disponível", + "没有将到期的积分(没有一次性额度包,或这一家不给逐份读数)": "", "没有选中的账号": "Nenhuma conta selecionada", "活动套餐": "Plano de evento", "活动套餐的推理端点要求每条请求带一个阿里云验证码令牌;令牌由本应用在后台静默铸造,界面关闭时无法铸造": "O endpoint de inferência do plano de evento exige um token de captcha da Alibaba Cloud em cada requisição; o token é gerado silenciosamente pelo aplicativo em segundo plano e não pode ser gerado com a interface fechada", @@ -400,7 +514,10 @@ "活动期内每天发一份新套餐;同一份当天重复领取会提示「已领取过」,次日可再领。领取要走一次人机验证(滑块)。": "Durante o evento um novo plano é emitido todo dia; resgatar o mesmo de novo no mesmo dia retorna \"já resgatado\", e é possível resgatar de novo no dia seguinte. O resgate passa por uma verificação humana (slider).", "测试中…": "Testando…", "测试出口": "Testar saída", + "添加 新→旧": "Adição nova→antiga", + "添加 旧→新": "Adição antiga→nova", "添加规则": "Adicionar regra", + "添加账号请用桌面端或网页版面板": "", "添加限制": "Adicionar limite", "清掉本机的限流标记,立刻重新尝试该模型(上游若仍在限流会再次被标记)": "Remove a marca de rate limit local e tenta novamente este modelo imediatamente (se o upstream ainda estiver limitando, ele será marcado de novo)", "清掉该账号全部模型的限流标记": "Remove as marcas de rate limit de todos os modelos desta conta", @@ -413,7 +530,9 @@ "清除已配置的余额查询凭证": "Remove a credencial de consulta de saldo configurada", "清除标记": "Limpar marcas", "清除筛选": "Limpar filtros", + "点一下按这一列排序(升序 → 降序 → 恢复默认次序)": "", "点击{action}各模型的限流明细": "Clique para {action} os detalhes de rate limit de cada modelo", + "点击查看各模型的限流明细": "", "状态": "Status", "状态 {status}": "Status {status}", "状态未知": "Desconhecido", @@ -429,7 +548,10 @@ "留空 = 不修改,清除用右边的按钮": "Vazio = não altera; limpe com o botão à direita", "登录 / 导入一个新账号": "Fazer login / importar uma nova conta", "直连": "Conexão direta", + "确定删除账号「{name}」?这个操作不可恢复。": "", "确定删除选中的 {count} 个账号?此操作不可恢复,账号的登录态会一并移除。": "Confirmar a exclusão de {count} contas selecionadas? Esta operação é irreversível e o estado de login das contas também será removido.", + "确定清除全部模型的限流标记?": "Limpar os limites de todos os modelos?", + "确定清除模型「{model}」的限流标记?": "Limpar o limite do modelo “{model}”?", "禁用": "Desativar", "禁用该账号(需手动重新启用)": "Desativar esta conta (precisa ser reativada manualmente)", "禁用这一组里的全部账号(含被筛选隐藏的)": "Desabilita todas as contas deste grupo (incluindo as ocultas pelo filtro)", @@ -438,6 +560,7 @@ "福利网关未读到:{error}": "Gateway de benefícios não lido: {error}", "秒": "segundos", "积分": "Pontos", + "积分到期时间": "Expiração dos créditos", "积分包": "Pacote de pontos", "积分明细": "Detalhes dos pontos", "积分查询已复用转发用的登录凭证,这里通常留空即可。只有旧版本填过、或从旧代理导入过凭证时才有值": "A consulta de pontos reutiliza a credencial de login usada no encaminhamento; normalmente basta deixar em branco. Só há valor quando versões antigas o preencheram ou quando a credencial foi importada de um proxy antigo", @@ -446,11 +569,17 @@ "窗口内已用 {used} / {limit} Token,转发时会跳过该账号(重置后自动恢复)": "Uso na janela {used} / {limit} Token; a conta será ignorada no encaminhamento (recuperada automaticamente após a redefinição)", "窗口对齐自然时间(整点 / 整 N 分钟一轮回),重置即清零恢复;按账号在 requests 表聚合": "A janela se alinha ao tempo natural (ciclos em horas cheias / a cada N minutos); a redefinição zera e recupera; agregação por conta na tabela requests", "端口": "Porta", + "端口准备中": "Porta em preparação", "第 {rank} 顺位": "Posição global {rank}", + "策略 {strategy} · 会话粘性 {sticky}": "", + "筛选": "Filtrar", "筛选 / 搜索 / 分页生效时组顺位不可拖动(别家的组整组不可见,落点没法定位)": "A ordem dos grupos não pode ser arrastada com filtro / busca / paginação ativos (grupos de outros provedores ficam ocultos, então o destino não pode ser localizado)", "级联删除名下全部账号,不可恢复": "Exclui em cascata todas as contas vinculadas, sem possibilidade de recuperação", "组内": "", "组内先后:按住左边的把手上下拖动即可调整;整组的先后拖分组带的把手": "Dentro do grupo: segure a alça à esquerda e arraste para cima / baixo para reordenar; para a ordem dos grupos inteiros, arraste a alça da faixa do grupo", + "组内并发上限": "Limite de concorrência do grupo", + "组内手动顺序": "Ordem manual dentro do grupo", + "组内排序:只在每一家内部重排,分组与组序不变(组内先后写回队列)": "", "组内第 {rank} 位": "Posição {rank} no grupo", "组顺位已锁定": "Ordem dos grupos bloqueada", "组顺位:越小越先用(拖动分组带调整)": "Posição do grupo: menor vai primeiro (arraste a faixa do grupo para ajustar)", @@ -459,6 +588,7 @@ "编码套餐(Coding Plan)— 走开放平台端点,用自己订阅的额度": "Plano de programação (Coding Plan) — usa o endpoint da plataforma aberta e a cota da própria assinatura", "编辑": "Editar", "编辑限制": "Editar limite", + "缺失": "", "网络": "Rede", "自动查询余额": "Consultar saldo automaticamente", "自动查询余额(按下面的间隔自动刷新余额列的读数)": "Consultar saldo automaticamente (atualiza a leitura da coluna de saldo no intervalo abaixo)", @@ -467,11 +597,14 @@ "自定义:": "Personalizado: ", "自然日(每天 0 点)": "Dia natural (0h de cada dia)", "表格": "", + "被跳过:这一轮不会用到它,所以没有位次": "", "视图": "", "解析失败": "Falha na análise", "触发动作": "Ação ao disparar", "触发后": "Após disparar", "触发条件": "Condição de disparo", + "计入 {counted}/{total}": "contabilizados {counted}/{total}", + "订阅已移除": "Assinatura removida", "订阅统计未读到:{error}": "Estatísticas de assinatura não lidas: {error}", "记录里没有过期时间": "Não há data de expiração no registro", "设为首选": "Tornar preferida", @@ -481,6 +614,8 @@ "该上游无需鉴权": "Este upstream não exige autenticação", "该上游无需鉴权(转发与拉取模型都不发送鉴权头)": "Este upstream não exige autenticação (não envia cabeçalho de autenticação no encaminhamento nem ao buscar modelos)", "该提供商没有余额查询": "Este provedor não tem consulta de saldo", + "该提供商没有余额查询,因此读不到积分包": "", + "该提供商没有余额查询,因此读不到积分包的到期时刻": "", "该提供商的推理转发尚未接入,账号不参与转发": "O encaminhamento de inferência deste provedor ainda não está integrado; a conta não participa do encaminhamento", "该账号出网走的代理(Clash 出口 / 自定义 / 直连);点击可修改": "Proxy usado na saída desta conta (saída do Clash / personalizado / conexão direta); clique para alterar", "该账号当前限流中的模型;点徽章看明细": "Modelos desta conta atualmente com rate limit; clique no selo para ver os detalhes", @@ -488,6 +623,7 @@ "该账号没有套餐登录态(jwt),活动套餐会失败:请重新登录该账号": "Esta conta não tem estado de login do plano (jwt) e o plano de evento falhará: faça login novamente nesta conta", "该账号没有福利模型额度(福利按活动下发,不是每个账号都有)": "Esta conta não tem cota de modelos de benefício (os benefícios são distribuídos por eventos e não estão em todas as contas)", "该账号没有积分类读数(额度只有福利 token 池)": "Esta conta não tem leituras de pontos (a cota é apenas o pool de tokens de benefício)", + "该账号的登录态已被上游注销,自动刷新不会再成功,请重新登录该账号": "", "该账号的额度只有福利 token 池(见余额格那两行),订阅统计没有积分类计量表": "A cota desta conta é apenas o pool de tokens de benefício (veja as duas linhas na célula de saldo); as estatísticas de assinatura não têm medidores de pontos", "请先勾选要操作的账号": "Marque primeiro as contas que deseja operar", "请先选择 Clash Verge 出口": "Selecione primeiro uma saída do Clash Verge", @@ -499,24 +635,30 @@ "请填写重置周期": "Preencha o período de redefinição", "请选择代理": "Selecione um proxy", "请选择出口": "Selecione uma saída", - "读取中…": "Lendo…", + "读取中…": "Carregando…", "读取失败:{error}": "Falha na leitura: {error}", "读取自 {dir};端口由 Clash Verge 管理,这里实时同步": "Lido de {dir}; a porta é gerenciada pelo Clash Verge e sincronizada aqui em tempo real", + "读取调度队列失败:{error}": "", "读数来自自动余额查询;与余额列同一数字口径,严格小于才触发(等于阈值仍可用)": "A leitura vem da consulta automática de saldo; mesmo critério numérico da coluna de saldo; só dispara se for estritamente menor (igual ao limite ainda é utilizável)", "读数里没有套餐清单(免费账号、或套餐的上游状态还没刷新;可点右上角「查询余额」重读)": "A leitura não contém a lista de planos (conta gratuita, ou o estado do plano no upstream ainda não foi atualizado; clique em \"Consultar saldo\" no canto superior direito para reler)", "读数里没有额度桶,这里按套餐权益里的已用 / 总量显示(上游给的数)": "A leitura não tem balde de cota; isto mostra o usado / total da concessão do plano (os números do upstream)", + "调度队列:{n} 个可用 · 跳过 {m} 个": "", + "调整到全局队列第一位": "Mover para o início da fila global", "调整顺序失败:{message}": "Falha ao ajustar a ordem: {message}", "账号": "Contas", + "账号 ID": "", "账号不存在,请刷新后重试": "A conta não existe; atualize e tente novamente", "账号不存在,请刷新后重试。": "A conta não existe; atualize e tente novamente.", "账号名 / 邮箱 / 备注": "Nome da conta / e-mail / nota", "账号已保存,但提供商未更新:{error}": "Conta salva, mas o provedor não foi atualizado: {error}", "账号显示名称": "Nome de exibição da conta", + "账号添加时间": "Data de adição da conta", "账号表": "Tabela de contas", "账号视图": "", "账号设置": "Configurações da conta", "账号设置 · {name}": "Configurações da conta · {name}", "账号设置分组": "Grupo de configurações da conta", + "账号详情": "", "账号邮箱": "E-mail da conta", "跳过该账号(余额回升自动恢复)": "Ignorar esta conta (recuperada automaticamente quando o saldo subir)", "跳过该账号(自动恢复)": "Ignorar esta conta (recuperação automática)", @@ -529,13 +671,24 @@ "轮询的代理": "Proxies para rotacionar", "轮询至少要选择一条代理(或改选「全部」)": "Selecione pelo menos um proxy para rotacionar (ou volte para \"Todos\")", "轮询(换 IP)": "Rotação (troca de IP)", + "还有 {d} 天": "", + "还有 {h} 小时": "", + "还有 {h} 小时 {m} 分": "", + "还有 {n} 分钟": "", "还没有代理(去「网络代理」页添加)": "Ainda não há proxies (adicione na página 「Proxy de rede」)", "还没有余额读数(已自动查询一次,稍候;也可以点右上角「查询余额」重读)": "Ainda não há leitura de saldo (uma consulta automática já foi iniciada, aguarde; você também pode clicar em \"Consultar saldo\" no canto superior direito)", "还没有保存的代理": "Ainda não há proxies salvos", + "还没有账号,去桌面端添加": "Ainda sem contas. Adicione uma no desktop.", "还没有限制规则:账号照常参与转发,余额与 Token 消耗都不设上限。": "Ainda não há regras de limite: a conta participa normalmente do encaminhamento, sem limite de saldo nem de consumo de Token.", "这一份今天已经领过了(重复领取上游会回「已领取过」)": "Este plano já foi resgatado hoje (resgatar de novo devolve \"já resgatado\" no upstream)", + "这一份还剩多少:{name} · 上游没给剩余量": "", + "这一家在这个模型上的倍率(与模型管理页同一份数据)": "", + "这一家查不到积分到期时间": "Este provedor não tem dados de expiração de créditos", "这一栏改的是": "Esta seção altera ", "这一段按提供商出现:有什么配什么。": "Esta seção aparece por provedor: configure o que estiver disponível.", + "这一组开着排序规则:关掉规则后才能手动拖": "A ordenação automática está ativa neste grupo: desligue as regras para arrastar manualmente", + "这个模型的队列里没有账号": "", + "这个模型队列里第 {rank} 位": "", "这个账号的余额走窗口限额通道(只配了编码套餐 API Key):只有每 N 小时的剩余比例,读不到套餐清单": "O saldo desta conta é lido pelo canal de limite de janela (só há uma API Key do plano de codificação): só a porcentagem restante de cada janela de N horas, sem a lista de planos", "这些分组里一个启用的账号都没有(禁用不参与转发),默认不显示;点一下切到「禁用」查看,也就能重新启用它们": "", "这会{what}所有启用中的账号,转发将不可用。确定继续?": "Isto vai {what} todas as contas ativadas e o encaminhamento ficará indisponível. Confirmar?", @@ -547,32 +700,46 @@ "连接数": "Número de conexões", "选择": "Selecionar", "选择要轮询的代理(可多选)": "Selecione os proxies para rotacionar (múltiplos)", + "逐份明细见「余额」格的积分明细": "", "逐请求轮换出口(换 IP):OpenCode Zen 免费档按 IP 限流,单个 IP 的额度用完会对它一律 429": "Troca a saída a cada requisição (mudando o IP): o nível gratuito do OpenCode Zen limita por IP — quando a cota de um IP acaba, as requisições vindas dele recebem 429", + "邮箱": "", "邮箱 {email}": "E-mail {email}", + "重新算一次这个模型的调度队列(限流冷却 / 并发释放后队列会变)": "", "重新读取": "Ler novamente", "重新读取该账号的余额与套餐(走余额查询那条链)": "Reler o saldo e os planos desta conta (passa pela cadeia de consulta de saldo)", + "重置": "Redefinir", "重置 {date}": "Redefine em {date}", "重置也不恢复,需手动启用": "A redefinição não a recupera; é preciso ativar manualmente", "重置周期必须是 30 分钟 ~ 24 小时": "O período de redefinição deve ser de 30 minutos ~ 24 horas", "重置方式": "Modo de redefinição", + "长期有效": "Longa duração", "间隔单位": "Unidade do intervalo", "阈值": "Limite", + "降低优先级": "Diminuir prioridade", "限制器": "Limitador", "限制器:{window}已用 {used} / {threshold} Token": "Limitador: {window} — usado {used} / {threshold} Token", "限制类型": "Tipo de limite", "限制阈值必须是大于 0 的数字": "O valor limite deve ser um número maior que 0", "限流": "Rate limit", "限流中": "Com rate limit", + "限流已清除": "Limite removido", + "限流明细": "", + "限流模型": "", "限额": "Limite", "限额状态": "Status do limite", "隐藏账号名(名字显示为星号)": "Ocultar nome da conta (o nome aparece com asteriscos)", + "需要重新登录": "Login necessário", + "需重登": "Reentrar", "顺序": "Ordem", + "顺序已锁定": "", "顺序未保存:{message}": "Ordem não salva: {message}", "领取 {time}": "Recebido {time}", "领套餐": "Resgatar plano", "额度": "Cota", "额度窗口读数来自开放平台监控通道(套餐 JWT 那条通道没有额度桶):读的是每 N 小时 / 每周窗口的剩余比例": "A leitura da janela de cota vem do canal de monitoramento da plataforma aberta (o canal do JWT do Coding Plan não tem balde de cota): ela lê a porcentagem restante de cada janela de N horas / semanal", "验证码令牌:{counts}": "Tokens de captcha: {counts}", + "默认不排序:组内顺序保持你拖出来的样子。需要自动排序时,勾选上面的规则即可。": "Sem ordenação por padrão: a ordem dentro do grupo fica exatamente como você arrastou. Marque uma regra acima quando quiser ordenação automática.", + "默认展开 / 收起": "Expandido / recolhido por padrão", "({n} 个未配置查询凭证)": " ({n} sem credencial de consulta)", "({plans})": "({plans})", "(不可用)": " (indisponível)", @@ -587,13 +754,5 @@ ",只对 OpenCode Zen 这类上游有意义,别的家请关掉。": ", o que só faz sentido para upstreams como o OpenCode Zen; desligue esta opção para os outros.", ",已达到上限": ", limite atingido", ":到「网络代理」页的「订阅管理」里点「同步 Clash Verge」把出口导进池后即可选 —— 那样能集中测试、也能被多个账号共用。": ": acesse a página 「Proxy de rede」, abra 「Gerenciar assinaturas」 e clique em 「Sincronizar Clash Verge」 para importar as saídas para o pool; assim elas poderão ser selecionadas — desse jeito é possível testar de forma centralizada e compartilhar entre várias contas.", - ";": "; ", - "WorkBuddy 国内版": "WorkBuddy edição da China", - "WorkBuddy 国际版": "WorkBuddy edição internacional", - "小浣熊": "Raccoon", - "Qoder 中国版": "Qoder edição da China", - "Qoder 国际版": "Qoder edição internacional", - "Accio 国内版": "Accio edição da China", - "ZCode 国内版": "ZCode edição da China", - "ZCode 国际版": "ZCode edição internacional" + ";": "; " } diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/add-account.json b/desktop-tauri/i18n/i18n-src/pt-BR/add-account.json index 236c1428..8d3699ab 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/add-account.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/add-account.json @@ -10,6 +10,7 @@ "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "O endpoint compatível com Anthropic é https://api.deepseek.com/anthropic (preencha-o ao trocar o protocolo para anthropic)", "Anthropic 协议填根地址,不要带 /v1": "No protocolo Anthropic, preencha o endereço raiz, sem /v1", "Antigravity": "Antigravity", + "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw edição da China", "AutoClaw 国际版": "AutoClaw edição internacional", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS é obrigatório; cole o **Cookie completo** (um editor de Cookie exporta com um clique) — o **PTOKEN deve ser mantido**, pois o gateway o usa para trocar com o Baidu por um token de sessão de negócio do KukuAI; sem ele, a atualização de modelos reporta \"não conectado\". São aceitos o cabeçalho Cookie inteiro, um array JSON exportado por um editor de Cookie, ou um objeto {BDUSS, STOKEN}. O KukuAI não tem mecanismo de atualização; quando a sessão de login expirar, basta colar de novo.", @@ -18,19 +19,35 @@ "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Obtenha a API Key do Cline Pass em app.cline.bot (por assinatura); o endpoint é compatível com OpenAI, o catálogo de modelos segue o console da Cline e os nomes dos modelos são adicionados no gerenciamento de modelos como nomes simples, sem prefixo (ex.: glm-5.2 / kimi-k2.7-code / deepseek-v4-pro); o upstream valida o formato do cliente, e os cabeçalhos de requisição do cliente necessários já vêm predefinidos neste cartão", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT (três segmentos separados por ponto)", "Command Code": "Command Code", + "Coze 扣子": "", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT (três partes separadas por pontos; também pode colar a linha Bearer inteira ou o conteúdo do data.bin)", + "JWT(三段点分;也可直接粘贴 Bearer 整行)": "JWT (três partes separadas por pontos; colar uma linha Bearer inteira também funciona)", + "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT (três partes separadas por ponto; colar a linha chatglm_token=… inteira também funciona)", + "LobsterAI": "LobsterAI", + "MiniMax Code": "MiniMax Code", + "MiniMax Code 国内版": "MiniMax Code (China)", + "MiniMax Code 国际版": "MiniMax Code (Internacional)", + "ModelScope 国内版": "ModelScope (China)", + "ModelScope 国际版": "ModelScope (Internacional)", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode edição internacional", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "Endpoint gerenciado do NVIDIA NIM; solicite a Key em integrate.api.nvidia.com", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken é a credencial de login do Accio (um token opaco bem longo, não é JWT); refreshToken é opcional e, se preenchido, permite renovação automática ao expirar. Preencha a credencial da conta {label} — {siteNote} (a forma mais fácil de obter: use o \"Login web\" acima; não é preciso procurar o token manualmente.)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken é o Cloud-IDE-JWT do Trae (três segmentos separados por ponto) e o refreshToken serve para renovação automática ao expirar (este provedor troca o refreshToken a cada uso: assim que é trocado, o antigo é invalidado, então não atualize a mesma conta em dois programas ao mesmo tempo).
Ao colar manualmente, preencha também machineId / deviceId: o upstream os vincula à chave pública do dispositivo enviada no login para identificar o dispositivo, e inventar um novo par dispara a família de rejeições de vínculo de dispositivo 2xxxx.
Sem esses três itens, o caminho correto é o \"Login web\" acima; em contêiner / implantação remota, basta colar o endereço de callback da barra de endereços conforme as instruções.", + "mmoat_… 或客户端 auth.json 里的 accessToken 值": "", + "mmoat_…(国际站 account.minimax.io 签发)": "", + "mmort_…(可选;有它才能自动续期)": "", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "O valor de monkeycode_ai_session (também pode colar o Cookie inteiro)", + "ms- 开头的 API 令牌": "Token de API começando com ms-", + "openclaw.json 里 gateway.auth.token 的值": "", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken é opcional e, se preenchido, permite renovação automática ao expirar; ambos podem ser obtidos do arquivo de sessão de login do cliente Raccoon.", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken é opcional e, se preenchido, permite renovação automática; ambos podem ser obtidos do arquivo de sessão de login do cliente. A mesma conta Cline funciona nos dois pools; aqui ela entra em {label}.", + "sessionid_ss=…; sid_guard=…; ttwid=…(或 Cookie 编辑器导出的 JSON)": "", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "O token aceita o prefixo enc: (descriptografado automaticamente no Windows). Sem preencher o refreshToken, não é possível renovar automaticamente.", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "O token é o Cookie da sessão de login; o CatPaw não tem mecanismo de atualização, então faça login novamente no cliente após expirar.", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "API Key começando com user_ (também pode colar a linha Authorization: Bearer … inteira)", + "{\"access_token\":\"mv_…\",\"openid\":\"oAN1…\",\"guid\":\"a79a…\"}(整段粘贴)": "", "{label} 网页登录的打开方式": "Como abrir o login web de {label}", "{label} 账号地区": "Região da conta {label}", "{label} 账号的添加方式": "Como adicionar uma conta {label}", @@ -39,6 +56,7 @@ "{n} 个账号": "{n} contas", "{n} 个账号,点击添加": "{n} contas, clique para adicionar", "{seconds}s 后重发": "Reenviar em {seconds}s", + "⚠️ {label}账号已添加,但探活未通过:{message}": "", "✅ {label}账号已添加": "✅ Conta {label} adicionada", "✅ {label}账号已添加{name}": "✅ Conta {label} adicionada{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ {n} provedores importados do cc-switch", @@ -58,23 +76,30 @@ "不是有效的凭证文件:需要包含 accessToken 与 uid": "Não é um arquivo de credenciais válido: accessToken e uid são obrigatórios", "不是自定义提供商": "Não é um provedor personalizado", "不需要 Key:转发与拉取模型都不发送鉴权头": "Não precisa de Key: nem o encaminhamento nem a busca de modelos enviam cabeçalhos de autenticação", + "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "Não é preciso fazer login: clique no botão abaixo e o gateway vai obter uma credencial de visitante diretamente do Zhipu Qingyan e adicioná-la à lista de contas (você pode preencher um nome de observação antes). A credencial de visitante pode ser usada para conversar imediatamente; a cota gratuita é emitida pela origem por dispositivo.

Atenção: uma conta de visitante não tem sistema de pontos — as interfaces de pontos de membro, pontos de login diário e resgate de atividades exigem login, então esses recursos são ignorados para visitantes (nenhuma leitura aparece na página de contas nem na central de check-in). Para obter pontos, use \"Preencher credenciais\" para colar o token de uma conta conectada, ou \"Importar estado de login do desktop\".", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "As contas das duas edições podem ser salvas ao mesmo tempo, cada uma com sua própria lista de modelos (as edições da China e internacional são dois provedores, que podem ser ativados e mapeados separadamente).", "中国版": "Edição da China", "今天没有需要领的活动(都已到账)": "Hoje não há campanhas para resgatar (todas já creditadas)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
": "Hoje (horário de Pequim, {day}) você já tentou {attempts} vezes; restam {left} tentativas{next}
", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "Pegue três itens de outra máquina (ou do models.json do aplicativo de desktop do OfficeAce) e cole aqui: o endereço base do gateway de modelos (baseUrl, no formato https://modelgw-…/v2) e modelAppKey / modelAppSecret. Esses três já bastam para encaminhar requisições (a credencial do gateway não expira); as credenciais temporárias do plano de controle (accessKeyId etc.) são opcionais — se ficarem em branco, a consulta de saldo e a renovação automática ficam indisponíveis, mas o encaminhamento não é afetado.", + "从本机 MiniMax Code 客户端读取国际版登录态(只读,不会改动客户端)": "", + "从本机 MiniMax Code 客户端读取登录态(只读,不会改动客户端)": "", "从本机导入桌面端登录态": "Importar sessão de login do desktop desta máquina", + "从本机马维斯客户端读取登录凭证(需要客户端已登录;会弹一次管理员授权)": "", + "令牌当前不可用": "", "任务镜像 id": "id da imagem da tarefa", "余额查询": "Consulta de saldo", "使用 Google 账号登录": "Entrar com conta Google", "使用 Zai 账号登录": "Entrar com conta Zai", "使用个人访问令牌(PAT)": "Usar token de acesso pessoal (PAT)", + "先用管理员身份运行 _recon/marvis-cred-extract.py(从已登录的马维斯客户端内存里提取),然后把输出的 JSON 整段粘贴进来。需要三项:accessToken(mv_ 开头)、openid、guid。令牌没有续期接口,过期后重新提取一次即可(会更新同一条账号记录)。": "", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "Modelos gratuitos não precisam de Key (a opção \"Este upstream não exige autenticação\" já vem marcada e o gateway usa credenciais anônimas automaticamente); para modelos pagos, preencha a API Key do Zen. O nível gratuito do upstream só aceita o formato do cliente oficial, e a opção \"Simular o cliente oficial do OpenCode\" já vem ativada", "内嵌窗口": "Janela embutida", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "Abre na janela embutida; fechar a janela cancela a espera (se a conta Google for recusada, use o navegador do sistema)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "Abre na janela embutida; ao concluir, entra automaticamente na lista; fechar a janela cancela a espera", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "Abre em uma janela incorporada; a conta é adicionada automaticamente após o escaneamento — fechar a janela cancela a espera", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "A janela embutida usa um ambiente totalmente novo, então várias contas não interferem entre si; fechar a janela cancela a espera", + "内嵌窗口(必须)": "", "内嵌窗口(推荐)": "Janela embutida (recomendado)", "准备验证…": "Preparando a verificação…", "凭据 JSON": "JSON da credencial", @@ -91,29 +116,38 @@ "取消失败:{message}": "Falha ao cancelar: {message}", "取消等待": "Cancelar espera", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "Somente a \"Janela embutida\" pode restaurar os acessos de terceiros: no navegador do sistema não podemos alterar a página de login", + "只有在「导入本机登录态」不可用时才需要:以管理员身份运行 _recon/marvis-cred-extract.py,把生成的 JSON 整段粘贴进来。需要三项:accessToken(mv_ 开头)、openid、guid。令牌没有续期接口,过期后重新导入一次即可(会更新同一条账号记录)。": "", "可选": "Opcional", "可选(cloudaicompanionProject):留空由网关自动发现": "Opcional (cloudaicompanionProject); se vazio, o gateway descobre automaticamente", + "可选,仅当令牌里读不出用户 id 时需要": "Opcional; necessário apenas quando o id de usuário não pode ser lido do token", "可选,展示用": "Opcional, para exibição", "可选,没有则无法自动续期": "Opcional; sem ele, não é possível renovar automaticamente", + "可选,用于生成账号标识": "Opcional; usado para gerar o identificador da conta", "可选,留空使用凭据里的 user_name": "Opcional; se vazio, usa o user_name da credencial", "可选,留空使用账号姓名、邮箱或令牌指纹": "Opcional; se vazio, usa o nome, o e-mail ou a impressão digital do token", "可选,留空使用账号昵称或邮箱": "Opcional; se vazio, usa o apelido ou o e-mail da conta", "可选,留空则用 key 尾号": "Opcional; se vazio, usa o final da key", "可选,留空则用 userId": "Opcional; se vazio, usa o userId", + "可选,留空则用上游账号昵称": "Opcional; se vazio, usa o apelido da conta no upstream", "可选,留空则用上游返回的账号名或账号 id": "Opcional; se vazio, usa o nome ou o id da conta retornados pelo upstream", + "可选,留空则用令牌尾号": "", "可选,留空则用凭证里的账号名": "Opcional; se vazio, usa o nome da conta na credencial", "可选,留空则用文件里的昵称": "Opcional; deixe vazio para usar o apelido do arquivo", "可选,留空则用登录名或 uid": "Opcional; se vazio, usa o nome de login ou o uid", "可选,留空则用脱敏手机号": "Opcional; se vazio, usa o celular mascarado", "可选,留空则用脱敏手机号或 userId": "Opcional; se vazio, usa o celular mascarado ou o userId", "可选,留空则用账号 uk": "Opcional; se vazio, usa o uk da conta", + "可选,留空则用账号昵称": "Opcional; em branco usa o apelido da conta", "可选,留空则用账号昵称或 userId": "Opcional; deixe em branco para usar o apelido da conta ou o userId", "可选,留空则用邮箱或令牌尾号": "Opcional; se vazio, usa o e-mail ou o final do token", + "可选,留空用「AtomCode」": "Opcional; padrão “AtomCode”", "可选,留空用「OfficeAce 果办」": "Opcional; se vazio, usa “OfficeAce”", "可选,留空自动生成": "Opcional; se vazio, gerado automaticamente", "可选,留空自动用昵称": "Opcional; se vazio, usa o apelido", + "可选,给这个访客账号起个备注名": "Opcional; um nome de observação para esta conta de visitante", "可选,续期时带上": "Opcional; enviado na renovação", "可选,讯飞侧 userid": "Opcional; userid do lado iFlytek", + "可选,默认 9434": "", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "Opcional; necessário para iniciar uma conversa. Se vazio, o gateway descobre automaticamente nas tarefas existentes da conta; se não encontrar, preencha manualmente depois.", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "Opcional; e-mail da conta, usado para exibição e identificação (a mesma conta é reconhecida mesmo ao trocar por um token novo)", "可选;UUID 形态,与凭据同生共死": "Opcional; formato UUID, vive e morre com a credencial", @@ -124,6 +158,7 @@ "可领取": "Resgatável", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "Contas com chave e sem autenticação podem coexistir no mesmo provedor, cada uma roteada pelas próprias regras; várias contas sem autenticação também são permitidas (cada uma com seu próprio proxy de saída)", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "No mesmo endereço, troque o protocolo para anthropic e ele usará o endpoint compatível com Claude. Para encaminhar, é obrigatório preencher a API Key do OpenCode Go (por assinatura; obter a lista de modelos não exige Key, mas as requisições recebem 401 \"Missing API key\" do upstream)", + "后端没有返回授权地址(登录发起失败)": "", "响应为空": "Resposta vazia", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "Endpoint compatível com OpenAI do SenseNova; a consulta de saldo usa a conta e a senha do console (veja \"Consulta de saldo\" abaixo)", "国内版": "Edição da China", @@ -141,11 +176,16 @@ "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "O site de inferência da edição internacional é api.z.ai, diferente do site da edição da China; os planos também são independentes.", "国际版(WorkBuddy AI)": "Edição internacional (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "Plano Coding do site internacional; o endereço compatível com OpenAI é https://api.minimax.io/v1", + "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "Obtenha os dois valores na página do AtomCode no AtomGit: accessToken e refreshToken. O refresh token é fortemente recomendado — o token deste provedor não é um JWT (expira em cerca de 7 dias e não traz expiração legível); sem ele a conta não consegue se renovar e precisará ser autorizada de novo ao expirar.", "在 cc-switch 里找到": "Encontradas", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "Conclua o login oficial na janela embutida; em caso de sucesso, a conta entra automaticamente na lista de contas.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "Gere um PAT nas configurações da conta Qoder do {label} → Integrations (não use um token do Google / GitHub). Contas e credenciais não são compartilhadas entre as duas regiões.", - "在打开的官方 Web 控制台里用微信扫码登录(窗口会自动弹出登录面板;若没有,点左下角头像 → 立即登录)。登录成功后自动加入账号列表。": "Escaneie o QR code do WeChat no console web oficial que será aberto (o painel de login aparece automaticamente; se não aparecer, clique no avatar no canto inferior esquerdo e escolha \"Entrar\"). A conta é adicionada automaticamente após o login.", + "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "Escaneie o QR code do WeChat na página de autorização oficial que abrir (o QR code aparece imediatamente). A conta é adicionada à lista automaticamente após a leitura bem-sucedida.", + "在打开的官方登录页里完成登录,成功后自动加入账号列表。": "Conclua o login na página oficial que abrir; a conta é adicionada automaticamente à lista após o sucesso.", + "在打开的登录页里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "Entre no site principal do KukuAI com sua conta Baidu na página de login que abrir (código por SMS ou leitura de QR são válidos); a conta é adicionada automaticamente à lista após o sucesso. O gateway troca com o Baidu um token de sessão de negócios do KukuAI (requer o cliente KukuAI instalado nesta máquina).", + "在打开的登录页里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "Entre com sua conta Google na página de login que abrir (página de autorização do Antigravity); a conta é adicionada automaticamente à lista ao concluir a autorização.", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "Na janela aberta, entre no site principal do KukuAI com sua conta Baidu (código SMS ou QR code, tanto faz); após o login, a conta entra automaticamente na lista. O gateway troca com o Baidu um token de sessão de negócio do KukuAI (requer o cliente KukuAI instalado nesta máquina).", + "在打开的窗口里登录 Coze(抖音账号 / 微信扫码 / 手机号均可),登录成功后自动加入账号列表。请用内嵌窗口:系统浏览器里网关读不到登录窗口的 Cookie。": "", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "Faça login com sua conta Google na janela que abrir (página de autorização do Antigravity); a conta é adicionada à lista automaticamente quando a autorização for concluída.", "在浏览器中打开登录页": "Abrir a página de login no navegador", "地区": "Região", @@ -165,6 +205,8 @@ "导入所选": "Importar selecionados", "导入所选({n})": "Importar selecionados ({n})", "导入桌面端登录态": "Importar sessão de login do desktop", + "将在新标签页中打开授权页;完成后自动加入账号列表。": "A página de autorização abre em uma nova aba; a conta é adicionada automaticamente à lista ao concluir.", + "将在新标签页中打开授权页;完成后自动加入账号列表。若页面跳回本机地址后账号没有出现,按提示把地址栏里的完整回调地址粘贴回来即可。": "A página de autorização abre em uma nova aba; a conta é adicionada automaticamente à lista ao concluir. Se a página voltar para um endereço local e a conta não aparecer, cole a URL de callback completa da barra de endereços quando solicitado.", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "A janela embutida será aberta; após a autorização, a conta entra automaticamente na lista de contas. Fechar a janela cancela a espera. No final, a página de autorização pode avisar que não conseguiu abrir o link zcode:// — isso é normal; o resultado já foi registrado no servidor", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "A janela embutida será aberta; após a autorização, a conta entra automaticamente na lista de contas. No painel remoto, é possível colar o endereço de callback. Fechar a janela cancela a espera. O link é válido por 5 minutos; se expirar ou não for clicado, ele é invalidado (você pode iniciar de novo)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "A janela embutida será aberta; após o login, a conta entra automaticamente na lista. Fechar a janela cancela a espera", @@ -173,6 +215,7 @@ "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta Huawei Cloud já conectada nele); o gateway consulta a nuvem em polling para obter o código de autorização, então não importa em qual máquina o navegador roda. Ao concluir, a conta entra automaticamente na lista", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta Huawei Cloud já conectada nele); se o navegador e o gateway não estiverem na mesma máquina, cole o endereço de callback final de volta no painel conforme as instruções acima", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "A página de login será aberta no navegador padrão do sistema (reaproveitando a conta Accio já conectada nele); após o login, a conta entra automaticamente na lista; fechar a janela cancela a espera", + "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "A página de login será aberta no navegador padrão do sistema (reaproveitando a conta LobsterAI já conectada nele); após o login, a conta entra automaticamente na lista; fechar a janela cancela a espera", "小米 MiMo Token Plan 套餐(新加坡站)": "Plano Token Plan do Xiaomi MiMo (site de Singapura)", "已到账": "Creditado", "已取消登录等待": "Espera de login cancelada", @@ -187,24 +230,32 @@ "已点发送,等待短信…": "Enviado; aguardando o SMS…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "Página de login aberta no navegador padrão do sistema; conclua o login no navegador…", "已领取,等官方确认": "Resgatado; aguardando confirmação oficial", + "开始登录(获取设备码)": "", + "当前壳不支持直接打开浏览器,请手动访问:{url}": "", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "Esta versão do aplicativo não suporta o login web do AutoClaw; atualize o aplicativo", "当前没有可领取的套餐(活动尚未开始)": "Nenhum plano disponível para resgate no momento (a campanha ainda não começou)", "当前环境不支持领取(桥接方法缺失)": "O resgate não é suportado neste ambiente (método de ponte ausente)", "必填,CatPaw 账号标识": "Obrigatório; identificador da conta CatPaw", "恢复 Google / GitHub 入口": "Restaurar acessos Google / GitHub", + "手工粘贴凭证": "", "手机号": "Celular", "手机验证码登录": "Login com código SMS", + "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "Abra a página de login oficial do LobsterAI (lobsterai.youdao.com) e entre com sua conta LobsterAI: após o login, a página oficial retorna a esta máquina e a conta entra automaticamente na lista — o callback chega à porta do próprio gateway, portanto o navegador precisa estar na mesma máquina que o gateway; o código de autorização só é entregue ao gateway localmente e nenhum token em texto claro é exibido na interface.", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Abra a página de autorização oficial do Trae SOLO (trae.cn) e entre com sua conta Trae: ao concluir a autorização, a página oficial redireciona de volta para uma porta temporária desta máquina; implantações na mesma máquina recebem automaticamente, e no Docker / painel remoto copie o endereço de callback da barra de endereços conforme as instruções.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Abra a página de login oficial do Accio {label} ({site}) e entre com sua conta Accio: após o login, a página oficial redireciona de volta para esta máquina; na mesma máquina tudo se completa automaticamente, e no Docker / painel remoto basta colar o endereço de callback conforme as instruções (o código de autorização é passado ao gateway apenas nesta máquina, e nenhum token em texto claro é exibido na interface).", "打开 Accio {label}登录页": "Abrir a página de login do Accio {label}", "打开 Antigravity 网页登录": "Abrir login web do Antigravity", + "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "Abra a página de autorização do AtomGit e entre: ao concluir, o gateway consulta o broker automaticamente para obter o token e adiciona a conta (o callback é encerrado no broker e nunca volta para esta máquina, então não é preciso preencher a URL de callback).", "打开 CatPaw 网页登录": "Abrir o login web do CatPaw", "打开 Cline 授权页": "Abrir a página de autorização do Cline", "打开 CodeArts 授权页": "Abrir a página de autorização do CodeArts", + "打开 LobsterAI 登录页": "Abrir a página de login do LobsterAI", "打开 Qoder 网页登录": "Abrir o login web do Qoder", "打开 Trae 授权页": "Abrir a página de autorização do Trae", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "Abra a página de autorização oficial do ZCode {label} e entre com sua conta. O resultado da autorização é registrado pelo servidor do ZCode; o gateway busca a credencial em segundo plano e a adiciona à lista de contas.", "打开 ZCode {label}授权页": "Abrir a página de autorização do ZCode {label}", + "打开千问办公的官方授权页完成设备码授权:登录完成后网关自动拿到登录态并加入账号列表(不需要手工回填任何回调地址)。": "Abra a página de autorização oficial do QwenWork e conclua a autorização por código de dispositivo: após o login, o gateway obtém o estado de login automaticamente e adiciona a conta à lista (não é necessário preencher nenhum endereço de callback manualmente).", + "打开千问办公网页登录": "Abrir o login web do QwenWork", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Abra a página de autorização oficial do Huawei Cloud CodeArts para entrar: após o login, a página oficial leva o navegador de volta ao /oauth/callback do próprio gateway, e o gateway troca o código de autorização de uso único por credenciais temporárias e adiciona a conta à lista.
Quando o gateway roda em outra máquina, a interface web pede para colar o endereço de callback final da barra de endereços de volta no painel.", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Abra a página de autorização do Huawei Cloud e faça login: ao concluir, o gateway consulta a nuvem automaticamente para obter o código de autorização e adiciona a conta à lista (não é preciso instalar o aplicativo de desktop do OfficeAce nem colar manualmente uma URL de retorno).", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "Abra a página de autorização oficial e conclua a autorização por código de dispositivo; a conta usada é do {label} ({site}).", @@ -238,23 +289,31 @@ "模型网关基址": "Endereço base do gateway de modelos", "正在打开百度登录页…": "Abrindo a página de login do Baidu…", "正在扫描本机 cc-switch…": "Escaneando o cc-switch desta máquina…", + "正在换发访客凭证…": "Solicitando uma credencial de visitante…", + "正在添加…": "Adicionando…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "Aguardando a conclusão do login de {name}; só após concluir ou cancelar você pode iniciar um novo login", "每周": "Semanal", "每日": "Diário", "每月": "Mensal", "没有则无法自动续期": "Sem ele, não é possível renovar automaticamente", "没有匹配「{keyword}」的提供商": "Nenhum provedor corresponde a \"{keyword}\"", + "添加 QClaw 账号": "", "添加 {name} 账号": "Adicionar conta {name}", "添加中…": "Adicionando…", "添加到 ": "Adicionar a ", + "添加千问办公账号": "Adicionar conta QwenWork", "添加失败:{reason}": "Falha ao adicionar: {reason}", "添加小浣熊账号": "Adicionar conta Raccoon", "添加方式": "Forma de adicionar", + "添加访客账号": "Adicionar uma conta de visitante", "添加账号": "Adicionar conta", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Plano Coding do Volcano Engine; o endpoint de inferência geral é https://ark.cn-beijing.volces.com/api/v3", "点击添加账号": "Clique para adicionar uma conta", "点开即预填,填 Key 接入": "Abre já preenchido; informe a Key para conectar", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "Após clicar no botão, primeiro passe por uma verificação de slider (controle de risco do provedor); em seguida a página de login abre e a conta é adicionada automaticamente ao concluir o login.
Esta é a única forma oficial de login da edição internacional; se você já entrou no cliente, \"Importar sessão de login do desktop\" é mais rápido.", + "点按钮后网关会申请一个设备码并在窗口里打开授权页;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
授权在浏览器里完成,网关不接触密码;跨机部署(Docker / 远程面板)同样可用。": "", + "点按钮后网关会申请一个设备码,并把授权页打开;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
国际版与国内版是两套账号,请用国际站(agent.minimax.io)的账号登录。": "", + "点按钮后网关会申请一个设备码,并把授权页打开;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
授权在浏览器里完成,网关不接触密码;跨机部署(Docker / 远程面板)同样可用。": "", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "Entre com o celular vinculado à conta AutoClaw: clique em \"Obter código\", informe o código recebido por SMS abaixo e entre. O código é enviado desta máquina diretamente à API oficial, e nenhum token é exibido na interface.", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "Conclua o login com sua conta CatPaw (Meituan Passport); em caso de sucesso, a conta entra automaticamente na lista.", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Entre com o celular vinculado à conta Loomy: clique em \"Obter código\" e informe o código. Esta é a forma mais simples neste provedor.", @@ -270,38 +329,55 @@ "登录已结束": "Login encerrado", "登录并添加": "Entrar e adicionar", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "Caminho do arquivo de sessão de login: ~/.box-agent/config/auth.json (Windows: C:\\Users\\\\.box-agent\\config\\auth.json)", + "登录态由壳侧从窗口的 Cookie 存储读出;完成后自动加入列表": "", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "Excluir o provedor personalizado \"{name}\"? Isso também excluirá {count} contas desse provedor; a exclusão não pode ser desfeita.", "等待 Accio {label}登录完成…": "Aguardando a conclusão do login do Accio {label}…", + "等待 AtomCode 登录完成…": "Aguardando o login do AtomCode…", "等待 CatPaw 登录完成…": "Aguardando a conclusão do login do CatPaw…", "等待 Cline 授权确认…": "Aguardando a confirmação de autorização do Cline…", "等待 CodeArts 登录完成…": "Aguardando a conclusão do login do CodeArts…", + "等待 Coze 登录完成…": "", "等待 Google 授权完成…": "Aguardando a conclusão da autorização do Google…", "等待 KukuAI 登录完成…": "Aguardando a conclusão do login do KukuAI…", + "等待 LobsterAI 登录完成…": "Aguardando a conclusão do login do LobsterAI…", "等待 OfficeAce 登录完成…": "Aguardando a conclusão do login do OfficeAce…", "等待 OrcaTerm 登录完成…": "Aguardando o login no OrcaTerm…", "等待 Qoder 授权完成…": "Aguardando a conclusão da autorização do Qoder…", "等待 Trae 授权完成…": "Aguardando a conclusão da autorização do Trae…", "等待 ZCode {label}授权完成…": "Aguardando a conclusão da autorização do ZCode {label}…", + "等待千问办公授权完成…": "Aguardando a autorização do QwenWork…", "等待小浣熊登录完成…": "Aguardando a conclusão do login do Raccoon…", + "等待授权超时,请重新发起": "", + "等待浏览器里确认授权…": "", "等待登录完成…": "Aguardando a conclusão do login…", "等待网页登录…": "Aguardando o login web…", "粘贴 API Key": "Colar API Key", + "粘贴 API 令牌": "Colar token de API", "粘贴 Accio 的 accessToken": "Colar o accessToken do Accio", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Cole o Google refresh token do Antigravity (começa com 1//; também pode colar a linha inteira que contém refresh_token=).
Esta é uma entrada manual além do login web (por exemplo, exportada do IDE oficial em que você já está conectado). O refresh token não expira, e o gateway renova o access token automaticamente ao expirar; se o token for perdido, só resta refazer a autorização do Google, então guarde-o bem.", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Cole a API Key do Command Code (começa com user_; obtenha no ~/.commandcode/auth.json do CLI oficial ou no studio web). Colar a linha inteira / com aspas / com o prefixo Bearer funciona; o gateway extrai a key pura sozinho. A key não expira e não há API de renovação, então basta obter uma nova se ela deixar de funcionar. Ao adicionar, o gateway faz uma verificação leve de atividade; uma falha não impede a adição.", "粘贴 Cookie": "Colar Cookie", "粘贴 Google refresh token": "Colar Google refresh token", + "粘贴 ModelScope 的 API 令牌(ms- 开头,在 {site} 的「访问令牌」页获取)。
{siteNote}
这枚令牌两用:网关既用它调推理,也用它读账号身份、魔粒余额与每日签到。令牌长期有效、没有续期接口,失效后重新获取再粘一次即可。": "Cole um token de API do ModelScope (começa com ms-; crie um na página \"Tokens de acesso\" de {site}).
{siteNote}
O mesmo token serve para duas finalidades: o gateway o usa tanto para chamar a inferência quanto para ler a identidade da conta, o saldo de créditos e o check-in diário. O token é de longa duração e não há API de renovação — se parar de funcionar, basta criar outro e colar novamente.", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "Cole o accessToken OAuth do OrcaTerm (um JWT com três partes separadas por pontos; obtido do data.bin do cliente desktop ou do cookie ot_session da versão web). Também pode colar a linha inteira contendo Bearer / ot_session= — o gateway extrai apenas o token. Após adicionar, o gateway renova automaticamente (o upstream tem API de renovação), sem precisar acompanhar a expiração de 2 horas manualmente.", + "粘贴 QClaw 本机网关的令牌(~/.qclaw/openclaw.json 里 gateway.auth.token 的值)。一般用「从本机导入」就够了;这里适合读不到配置文件时手动兜底 —— 端口留空则用默认的 9434。粘贴后会先连一次网关做校验,通过才落账号。": "", "粘贴 Qoder 个人访问令牌(pt-…)": "Colar o token de acesso pessoal do Qoder (pt-…)", "粘贴 accessToken": "Colar accessToken", "粘贴 access_token(一长串 JWT)": "Colar o access_token (um JWT longo)", "粘贴 session cookie": "Colar session cookie", "粘贴 token / refreshToken": "Colar token / refreshToken", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "Cole um token começando com workos:… (o prefixo é adicionado automaticamente se faltar)", + "粘贴令牌对": "Colar par de tokens", + "粘贴凭证三件套": "", + "粘贴千问办公的登录令牌(JWT,三段点分)。一般用「从本机导入」就够了;这里适合换机器迁移、或本机解密失败时手动兜底 —— 此时还需要一并填写 userId(令牌里解不出时会提示)。粘贴后会先向上游校验一次,校验通过才落账号。": "Cole o token de login do QwenWork (um JWT com três partes separadas por pontos). Normalmente \"Importar desta máquina\" basta; esta entrada manual serve para migrar de máquina ou como alternativa quando a descriptografia local falha — nesse caso, preencha também o userId (você será avisado quando o id de usuário não puder ser lido do token). Após colar, ele é verificado uma vez na origem e a conta só é salva se passar.", + "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "Cole um token de acesso do Zhipu Qingyan (um JWT com três partes separadas por ponto). Como obter: abra chatglm.cn no navegador, faça login e copie o valor do cookie chatglm_token em Application → Cookies nas ferramentas de desenvolvedor (colar a linha chatglm_token=…; inteira também funciona). O token é verificado uma vez na origem após a colagem, e a conta só é salva se passar; contas conectadas têm pontos e check-in, e o gateway renova automaticamente depois (a origem tem API de renovação).", + "粘贴登录令牌": "Colar o token de login", "粘贴登录凭据": "Colar credenciais de login", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "O que você cola é a sessão de login (que também é o token do gateway de modelos). O Loomy não tem API de renovação; a sessão vale cerca de 14 dias, então basta entrar de novo após expirar.", + "粘贴网关令牌": "", "粘贴网关凭据": "Colar credenciais do gateway", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
{siteNote}
MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "Cole o valor do cookie de sessão de login web monkeycode_ai_session (copiar o Cookie inteiro no navegador e colar diretamente também funciona; o gateway extrai o valor sozinho).
{siteNote}
O MonkeyCode não tem API de renovação: a sessão vale cerca de 30 dias (limite rígido); quando expirar, entre novamente no site e cole mais uma vez.", + "粘贴访问令牌": "Cole o token de acesso", "系统浏览器": "Navegador do sistema", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "O navegador do sistema pode reaproveitar a sessão de login existente (recomendado para a edição internacional); a janela embutida é mais limpa.", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta Cline já conectada); ao concluir, entra automaticamente na lista", @@ -310,16 +386,25 @@ "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta Meituan já conectada); ao concluir, entra automaticamente na lista", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta já conectada); ao concluir, entra automaticamente na lista", "系统默认浏览器": "Navegador padrão do sistema", + "续期令牌": "Token de atualização", "编码套餐 API Key": "API Key do Coding Plan", "网关 Secret": "Segredo do gateway", + "网关会以管理员身份读取马维斯客户端的内存来取凭证 —— 客户端的令牌不落明文(登录库是密文、密钥容器校验调用方签名),只在进程内存里,而客户端进程又设了访问限制,普通权限读不到。所以这一步会弹一次 UAC,点「是」即可,之后无需再操作。请确保客户端处于登录状态。": "", "网页登录": "Login web", "网页登录的打开方式": "Como abrir o login web", "网页登录(Zai / Google)": "Login web (Zai / Google)", + "网页登录(浏览器授权)": "", "自定义": "Personalizado", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "Painel de agregação próprio: preencha o endereço do seu site. A consulta de saldo chama {base}/v1/usage com a chave de API desta conta; sem configuração extra", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "Painel de agregação próprio: preencha o endereço do seu site; a consulta de saldo precisa também de um token de acesso pessoal do NewAPI (veja \"Consulta de saldo\" abaixo)", "获取风控配置失败,将直接尝试领取:{message}": "Falha ao obter a configuração de controle de risco; tentando resgatar diretamente: {message}", "获取验证码": "Obter código", + "设备码": "", + "设备码 5 分钟内有效;在授权页确认后这里会自动完成": "", + "设备码 5 分钟内有效;窗口里显示「等待你在浏览器里确认」时去授权页点确认即可": "", + "访客模式": "Modo visitante", + "访客模式(免登录)": "Modo visitante (sem login)", + "访问令牌": "Token de acesso", "该上游无需鉴权(不发送鉴权头)": "Este upstream não exige autenticação (nenhum cabeçalho de autenticação é enviado)", "该提供商": "Este provedor", "该提供商账号添加功能即将上线": "A adição de contas deste provedor chega em breve", @@ -332,11 +417,18 @@ "请填写 {label}": "Preencha {label}", "请填写名称": "Preencha o nome", "请完成验证…": "Conclua a verificação…", + "请粘贴 www.coze.cn 的完整 Cookie(浏览器开发者工具 → Network → 任取一条 www.coze.cn 请求的 Cookie 头,或 Cookie 编辑器一键导出)。整包带上最稳:任务中心与机器人列表只认 Cookie,少了几项会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {name: value} 对象。Coze 没有刷新机制,登录态过期后重新登录或重新粘贴即可。": "", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "Lê e descriptografa %APPDATA%/AutoClaw/auth.json; somente no Windows", + "读取 %USERPROFILE%/.minimax/auth 下客户端已保存的令牌,只读不写 —— 客户端用一对必须相等的版本号判断「是否在别处登录过」,写一次就会让它要求你重新扫码。注意:导入是一次快照,之后网关与客户端各自续期,两边的刷新令牌会互相轮换,建议只在一处使用。": "", + "读取 %USERPROFILE%/.minimax/auth/prod/en 下客户端已保存的令牌,只读不写。客户端按「环境 / 地区」分目录存两套登录态,国际版在 en 目录里;没在国际站登录过时这里读不到,请改用上面的网页登录。": "", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "Lê ~/.cline/data/settings/providers.json; requer login no cliente Cline", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "Lê ~/.meituan-catpaw/auth.json; requer login no cliente CatPaw", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "Lê a sessão de login do cliente KukuAI desta máquina (%APPDATA%\\baidugenflowpro\\Network\\Cookies); requer login no cliente KukuAI", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "Lê o estado de login do cliente OrcaTerm local (%APPDATA%\\com.orcaterm-desktop.app\\data.bin); é preciso estar logado no cliente OrcaTerm", + "读取本机 QClaw 客户端网关配置(%USERPROFILE%\\.qclaw\\openclaw.json),需 QClaw 正在运行": "", + "读取本机千问办公客户端登录态(%APPDATA%\\QwenWorkCN\\auth-v2.dat),需已在客户端登录过": "Lê o estado de login do cliente QwenWork local (%APPDATA%\\QwenWorkCN\\auth-v2.dat); é necessário ter feito login no cliente", + "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "Lê o estado de login do cliente 智谱清言 local (%APPDATA%\\chatglm\\Network\\Cookies); o cliente precisa estar conectado", + "读本机 QClaw 客户端拉起的 OpenClaw 网关配置(端口 + 令牌)。与别家不同,本家需要 QClaw 正在运行 —— 网关是客户端拉起的,端口和令牌都以它在运行为前提;后端会真连一次做校验。": "", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "Lê a conta atualmente conectada no cliente oficial (Tencent WorkBuddy / WorkBuddy AI) nesta máquina: o arquivo de sessão mais recente no diretório de credenciais é localizado automaticamente (é preciso estar conectado ao cliente oficial nesta máquina). Após a importação, o gateway renova o token automaticamente — não é preciso trocar tokens manualmente conforme a validade de 2 horas.", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "Lê a sessão de login atual do cliente desta máquina (Cookie em texto claro, sem descriptografia). Com o cliente em execução, o arquivo Cookies fica em uso exclusivo; feche o cliente KukuAI antes de importar.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente).", @@ -345,22 +437,29 @@ "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente). Usuários que entram no cliente com Zai / Google devem usar esta opção.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente). Esta opção fica em {label}.", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "Lê o token OAuth com que o cliente local está logado (JSON em texto claro, sem descriptografia). Após importar, o gateway renova automaticamente — não é preciso acompanhar a expiração de 2 horas do cliente.", + "读本机客户端当前的登录态。它是 Chromium os_crypt 加密的(DPAPI 主密钥 + AES-256-GCM),因此仅 Windows 可用。DPAPI 按当前 Windows 用户解密,不需要客户端正在运行 —— 只要在本机登录过一次即可(换机器时请在目标机器上重新登录一次客户端)。": "Lê o estado de login atual do cliente local. Ele é criptografado com o Chromium os_crypt (chave mestra DPAPI + AES-256-GCM), portanto funciona somente no Windows. O DPAPI descriptografa como o usuário atual do Windows e não exige que o cliente esteja em execução — basta fazer login uma vez nesta máquina (ao migrar de máquina, faça login no cliente uma vez na máquina de destino).", + "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "Lê o estado de login atual do cliente local. Os cookies do cliente são criptografados com DPAPI (Chromium os_crypt), portanto isto funciona somente no Windows; o arquivo pode ficar ilegível enquanto o cliente está aberto — feche o cliente e tente de novo (ou use \"Preencher credenciais\" para colar um token).", "账号信息": "Informações da conta", "账号信息不完整,请刷新后重试": "Informações da conta incompletas; atualize e tente novamente", "账号已添加,但请留意:{warning}": "Conta adicionada, mas atenção: {warning}", "账号版本": "Edição da conta", "账号类型": "Tipo de conta", "账号:{name}
": "Conta: {name}
", + "贴国际站的访问令牌(mmoat_ 开头)。国内站的令牌拿到这里会被上游拒(401)—— 两地账号体系不同,不能互换。": "", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Usa a rota Inference Providers; crie a Key em hf.co/settings/tokens", "还没有账号": "Ainda não há contas", "这一家当前不支持网页登录,请改用填写凭证": "Este provedor não suporta login web no momento; use \"Preencher credenciais\"", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "Esta conta não tem campanhas com resgate automático no momento (apenas os pontos de brinde do login diário são resgatados automaticamente)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "Esta cota passa pelo canal \"Start Plan\": se o encaminhamento avisar que o Coding Plan expirou, ", + "这里填国内站(modelscope.cn)的账号:请粘贴国内站的令牌。国际站(modelscope.ai)是另一家 provider、另一套账号,两边的令牌不通用。": "Aqui é a conta do site chinês (modelscope.cn): cole o token do site chinês. O site internacional (modelscope.ai) é outro provedor com outra conta; os tokens não são intercambiáveis entre os dois.", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "Aqui é a conta do site da China (monkeycode-ai.com): cole a sessão do site da China. O site internacional (monkeycode-ai.net) é outro provedor, com conta própria; as credenciais não são compartilhadas entre os dois.", + "这里填国际站(modelscope.ai)的账号:请粘贴国际站的令牌。国内站(modelscope.cn)是另一家 provider、另一套账号,两边的令牌不通用。": "Aqui é a conta do site internacional (modelscope.ai): cole o token do site internacional. O site chinês (modelscope.cn) é outro provedor com outra conta; os tokens não são intercambiáveis entre os dois.", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "Aqui é a conta do site internacional (monkeycode-ai.net): cole a sessão do site internacional. O site da China (monkeycode-ai.com) é outro provedor, com conta própria; as credenciais não são compartilhadas entre os dois.", + "适合已在别处登录、或客户端数据目录被改过的场景。只需要 accessToken(mmoat_ 开头);JWT 里能读出身份与到期,无需再填别的。只给访问令牌时约 1 小时有效、无法自动续期 —— 想要自动续期请用上面的网页登录。": "", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Selecione o arquivo de sessão de login auth.json do cliente CatPaw (Windows: C:\\Users\\\\.meituan-catpaw\\auth.json; macOS / Linux: ~/.meituan-catpaw/auth.json). Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente — sem depender mais do cliente desta máquina.", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Selecione o arquivo de sessão de login providers.json do cliente Cline (Windows: C:\\Users\\\\.cline\\data\\settings\\providers.json; macOS / Linux: ~/.cline/data/settings/providers.json). Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente — sem depender mais do cliente desta máquina; este registro entra em {label}.", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "Selecione o arquivo Cookies do cliente KukuAI (%APPDATA%\\baidugenflowpro\\Network\\Cookies): é um banco SQLite **binário** (sem extensão, não é JSON). A importação lê uma cópia do arquivo selecionado e não disputa o bloqueio com o cliente em execução, então **não é preciso fechar o cliente antes**. Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente.", + "选择一份存着 www.coze.cn Cookie 的文本文件(整段 Cookie 头或 Cookie 编辑器导出的 JSON 都可以)。导入后凭证直接落在网关、可独立转发,不依赖本机客户端(Coze 客户端运行时占着 Cookie 库,读不了)。": "", "选择凭证文件导入": "Importar de arquivo de credenciais", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "Selecione o arquivo de credenciais de sessão do cliente oficial (workbuddy-desktop.info / workbuddy-desktop-ai.info) ou um JSON de conta exportado pelo wb2api (formato aninhado ou plano).", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Selecione o arquivo de sessão de login auth.json do cliente Raccoon (Windows: C:\\Users\\\\.box-agent\\config\\auth.json; macOS / Linux: ~/.box-agent/config/auth.json). Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente — sem depender mais do cliente desta máquina.", @@ -379,6 +478,7 @@ "领取失败:{message}": "Falha ao resgatar: {message}", "领取并让官方确认": "Resgatar e aguardar confirmação oficial", "风控验证配置不完整,请稍后重试": "Configuração de verificação de controle de risco incompleta; tente novamente mais tarde", + "马维斯 Marvis": "", "验证码": "Código de verificação", "验证码已发送。收到后填入下方并点「登录并添加」": "Código enviado. Informe-o abaixo e clique em \"Entrar e adicionar\"", "验证码已发送,请查看手机短信": "Código enviado; verifique o SMS", diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/agent.json b/desktop-tauri/i18n/i18n-src/pt-BR/agent.json new file mode 100644 index 00000000..430e6056 --- /dev/null +++ b/desktop-tauri/i18n/i18n-src/pt-BR/agent.json @@ -0,0 +1,102 @@ +{ + "· 走网关": "· via gateway", + "。这里的开关与还原作用在网关所在的机器上。": "", + "一键把本机 AI 客户端接到本地网关:改写它自己的配置文件,保留其它设置": "Conecta os clientes de IA locais ao gateway local: reescreve o arquivo de configuração de cada um e mantém as demais configurações intactas", + "上游模型 id,例如 raccoon-coder-pro": "ID do modelo upstream, por exemplo raccoon-coder-pro", + "下面是这个客户端文件里现有的配置(只读)。点「从客户端导入」把它收进我的配置,之后就能编辑和切换。": "Estes são os perfis já existentes no arquivo deste cliente (somente leitura). Clique em \"Importar do cliente\" para copiá-los para os meus perfis — depois poderão ser editados e alternados.", + "个 · 已安装": " · instalados", + "中转": "Relay", + "从客户端导入": "Importar do cliente", + "会用最近一次备份覆盖它的配置文件。这一步不会改动你在本页存的配置列表。": "Substitui o arquivo de configuração pelo backup mais recente. A lista de perfis desta página não é alterada.", + "低": "Baixo", + "保存修改": "Salvar alterações", + "共": "Total", + "写入中…": "Gravando…", + "写入后需要重开会话才生效": "Reabra a sessão após gravar para que tenha efeito", + "写入失败:{reason}": "Falha ao gravar: {reason}", + "写入客户端": "Gravar no cliente", + "写入预览": "Prévia da gravação", + "单配置": "Perfil único", + "单配置文件": "", + "只改 enabled 与默认项,其余条目原样保留": "Apenas as flags enabled e padrão mudam; os demais itens ficam intactos", + "只改属于我们的那几个键,其它设置原样保留": "Apenas as nossas chaves mudam; as demais configurações ficam intactas", + "可同时启用多条;切换只改 enabled 与默认项": "Vários itens podem ficar ativos ao mesmo tempo; a troca altera apenas as flags enabled e padrão", + "同一时刻只有一套生效;写入 = 把这一套整段写进客户端文件": "Só um perfil fica ativo por vez; gravar copia esse perfil para o arquivo do cliente", + "启用 {name}": "Ativar {name}", + "备注": "Observação", + "多套配置": "Vários perfis", + "多配置 · 已启用 {n}": "Vários perfis · {n} ativos", + "官方直连": "Oficial direto", + "客户端列表加载失败:{reason}": "Falha ao carregar a lista de clientes: {reason}", + "客户端清单": "Clientes", + "客户端自己的设置优先;这里写的是写入时带进去的初值": "As configurações do próprio cliente prevalecem; aqui é apenas o valor inicial gravado", + "已保存": "Salvo", + "已写入 {name}": "Gravado em {name}", + "已写入 {name};这个客户端只在启动时读配置,重开一个会话才生效": "Gravado em {name}; este cliente só lê a configuração na inicialização, então reabra uma sessão para aplicar", + "已安装": "Instalado", + "已按备份还原 {name}": "{name} restaurado a partir do backup", + "已收进我的配置:{n} 条": "Importados para os meus perfis: {n}", + "已还原「{name}」": "", + "当前环境不支持保存(桥接方法缺失)": "Não é possível salvar neste ambiente (método de bridge ausente)", + "当前环境不支持写入(桥接方法缺失)": "Não é possível gravar neste ambiente (método de bridge ausente)", + "当前环境不支持客户端接管(桥接方法缺失)": "A conexão de clientes não está disponível neste ambiente (método de bridge ausente)", + "当前环境不支持导入(桥接方法缺失)": "Não é possível importar neste ambiente (método de bridge ausente)", + "当前环境不支持还原(桥接方法缺失)": "Não é possível restaurar neste ambiente (método de bridge ausente)", + "手机上只能查看与还原接管。改写配置内容请回桌面端": "", + "按备份还原": "Restaurar do backup", + "按备份还原 {name}?": "Restaurar {name} a partir do backup?", + "接管会把客户端的 base_url 指向本网关,并在写入前自动备份原文件。": "", + "搜索客户端": "Pesquisar clientes", + "搜索客户端 / 配置文件…": "Pesquisar clientes ou arquivos de configuração…", + "改写网关所在机器上的客户端配置": "", + "改动": "Alterado", + "新增": "Adicionado", + "新建配置": "Novo perfil", + "新配置": "Novo perfil", + "无配置": "Sem perfil", + "暂无预览": "Ainda sem prévia", + "未安装": "Não instalado", + "未接管": "", + "未检测到": "Não detectado", + "未检测到安装": "Nenhuma instalação detectada", + "未走网关": "Sem passar pelo gateway", + "本机": "Este aparelho", + "本机服务": "Serviço local", + "本机没有找到 {name}": "{name} não foi encontrado nesta máquina", + "本机网关": "Gateway local", + "来自客户端文件,只读": "Do arquivo do cliente, somente leitura", + "模型 id": "ID do modelo", + "正在生成预览…": "Gerando prévia…", + "正在读取本机客户端…": "Lendo clientes locais…", + "每个客户端可以有": "Cada cliente pode ter", + "没有匹配的客户端": "Nenhum cliente correspondente", + "没有可导入的新条目(可能还没配过,或已经在我的配置里)": "Nenhuma entrada nova para importar (ainda não configurado ou já está nos meus perfis)", + "没有新的条目可收(都已在我的配置里)": "Nenhuma entrada nova para importar (todas já estão nos meus perfis)", + "没有检测到可接管的客户端": "", + "确定按最近一次备份还原「{name}」?": "", + "第三方中转": "Relay de terceiros", + "编辑:{name}": "Editar: {name}", + "行号由后端逐行比对给出": "Os números de linha vêm da comparação linha a linha do backend", + "装好后回到这一页点「重新检测」,它会出现在上面的列表里。": "Depois de instalar, volte a esta página e clique em \"Verificar novamente\" — ele aparecerá na lista acima.", + "设为默认": "Definir como padrão", + "读取客户端配置失败:{reason}": "Falha ao ler a configuração do cliente: {reason}", + "走网关": "Via gateway", + "还原中…": "", + "还原失败:{reason}": "Falha ao restaurar: {reason}", + "还没有配置": "Nenhum perfil ainda", + "还没有配置。点「新建配置」建一套指向本机网关的,再写入客户端。": "Nenhum perfil ainda. Clique em \"Novo perfil\" para criar um apontando para o gateway local e depois grave no cliente.", + "还没有预览。选一套配置后这里会显示写入客户端之后的样子。": "Ainda sem prévia. Escolha um perfil e aqui será mostrado como o arquivo do cliente ficará após a gravação.", + "这个客户端会自己重读配置,写入后开新会话即生效。": "Este cliente recarrega a configuração sozinho; abra uma nova sessão após gravar.", + "这个客户端只在启动时读配置:写入后重开一个会话才生效。": "Este cliente só lê a configuração na inicialização: reabra uma sessão após gravar.", + "这个客户端的配置里没有读出地址(可能还没配过,或形状不认识)": "Não foi possível ler um endereço da configuração deste cliente (talvez ainda não esteja configurado ou o formato seja desconhecido)", + "这个客户端自己支持多套配置并存": "Este cliente suporta vários perfis lado a lado", + "这个客户端自己管理多套配置,可同时启用多条;切换只改 enabled 与默认项,不动它的模型清单。": "Este cliente gerencia vários perfis e pode ativar mais de um; a troca altera apenas as flags enabled e padrão, nunca a lista de modelos dele.", + "这套配置是干什么用的(只显示在本页)": "Para que serve este perfil (exibido apenas nesta página)", + "选一个客户端开始": "Escolha um cliente para começar", + "配置": "Perfil", + "配置来源": "Origem do perfil", + "需重开会话生效": "Reabra a sessão para aplicar", + "高": "Alto", + "(手机读不到本机文件)": "", + "(本机网关 / 官方直连 / 第三方中转 / 本机服务),随时切换。单配置客户端切换 = 整段覆写并备份原文件;多配置客户端(如 ZCode)只改 enabled 与默认项,它自己的条目一律不动。": "(gateway local / oficial direto / relay de terceiros / serviço local) e alterne entre eles quando quiser. Em clientes de perfil único, a troca reescreve o perfil inteiro e faz backup do arquivo original; em clientes multiperfil como o ZCode, apenas as flags enabled e padrão mudam, sem tocar nos itens próprios deles." +} diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/assistant.json b/desktop-tauri/i18n/i18n-src/pt-BR/assistant.json index aa04c2c7..6661a7b9 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/assistant.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/assistant.json @@ -1,32 +1,147 @@ { "CodeArts 的账号余额还剩多少?": "Quanto crédito ainda resta na conta CodeArts?", + "Enter 发送 · Shift+Enter 换行": "", + "section 只能是 models / mappings / both": "section deve ser models / mappings / both", + "{n} / {total}": "", + "{n} 个账号正在限流:": "", + "{n} 家": "", + "{n} 条改动": "", + "{n} 条映射": "{n} mapeamentos", + "✓ 完成": "", + "✗ 失败": "", + "「{name}」超过 {n} MB,换一张小一点的。": "", + "一次最多 {n} 张图片。": "", + "一次最多 {n} 张图片,多出的没有添加。": "", + "上面四个快捷查询直接读网关数据,不经过模型": "", + "不指定,交给映射绑定": "", + "今天用了 {tokens} Token,共 {requests} 次请求。": "", + "今天用了多少 Token?": "Quantos tokens usei hoje?", + "会话响应不完整": "Resposta de sessão incompleta", + "会话里的选择优先于这里的默认值": "", + "余额是缓存读数,点账号详情里的「刷新凭证」可触发实时查询。": "", + "作为 system 消息注入": "", + "保存思考等级失败:{message}": "Falha ao salvar o nível de raciocínio: {message}", "修改模型能力": "Editar capacidades do modelo", + "共 {n} 个模型条目。": "", + "共 {n} 个模型条目。映射与改名请在桌面端「模型管理」里做。": "", + "共 {n} 个账号,前 {m} 个的余额读数:": "", + "删除会话失败:{message}": "Falha ao excluir a sessão: {message}", + "删除这条对话": "Excluir esta conversa", + "助手设置": "", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "As configurações do assistente não puderam ser lidas, então o modelo de chat ainda não pode ser salvo. Veja a dica abaixo da caixa de entrada.", + "助手设置没读出来,暂时存不了对话模型。": "", + "助手设置没读出来,暂时存不了思考等级 —— 看输入框下方的提示。": "Não foi possível carregar as configurações do assistente, então o nível de raciocínio não pode ser salvo agora — veja a dica abaixo da caixa de entrada.", + "助手设置没读出来,暂时存不了思考等级。": "", + "助手设置没读出来,现在还不能发消息 —— 点右上角「助手设置」看看。": "", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "As configurações do assistente não puderam ser lidas, então ainda não é possível enviar mensagens. Veja a dica abaixo da caixa de entrada.", + "勾选即注入": "", + "历史对话": "Histórico de conversas", + "原先在「设置 → AI 助手」的配置都搬到这里了": "", + "发消息或做任务…(Enter 发送,Shift+Enter 换行,可粘贴图片)": "", "发起模型测试": "Executar teste de modelo", + "变更前确认": "", + "只支持图片文件(PNG / JPG / WebP / GIF)。": "", + "只读 · 不会改动配置": "", + "只跳过确认,不扩大工具权限;每次执行仍会记录在对话里。": "", + "各账号余额还剩多少?": "Quanto de saldo resta em cada conta?", "启用 / 禁用模型": "Habilitar / desabilitar modelos", "启用 / 禁用账号": "Habilitar / desabilitar contas", + "哪些模型在多家重复了?": "Quais modelos estão duplicados entre provedores?", + "图片 {n}": "", + "完全访问": "", + "完全访问已开启:改动类工具会直接执行,不再弹出确认卡。": "", + "完整对话在桌面端「AI 助手」页": "", "对指定模型发一次真实请求,回报连通性与耗时。": "Envia uma requisição real ao modelo indicado e informa conectividade e latência.", + "对话由网关转发到上游模型,用量计入请求日志": "A conversa é encaminhada ao modelo upstream pelo gateway; o uso é registrado no log de requisições", + "已停止生成": "Geração interrompida", + "已全部勾选": "", + "已切换到变更前确认": "", + "已切换到完全访问": "", + "已启用的模型": "", + "已调用": "Chamado", "开关模型的对外可用性,不改其它属性。": "Alterna a disponibilidade externa do modelo sem alterar outros atributos.", + "当前一个工具都没勾选,助手只能纯聊天。点右上角「助手设置」勾上需要的工具。": "", + "当前模型没有声明图片能力,图可能发不上去。": "", + "当前没有同名模型分布在多家。": "", + "当前:已关闭(助手收不到任何系统提示词,工具定义照发)": "", + "当前:已开启 · 使用内置默认提示词": "", + "快捷查询走本地网关,不消耗额度": "", + "思考等级已切换": "", + "思考:跟随模型": "Raciocínio: seguir modelo", + "总请求": "", + "成功率": "", + "我可以直接查网关里的真实数据:余额、模型重复、今日用量、限流账号。": "", + "打开会话失败:{message}": "Falha ao abrir a sessão: {message}", + "执行中": "", + "找到 {n} 组同名模型:": "", "把重复的模型加上家名前缀区分开": "Prefixar modelos duplicados com o nome do provedor para diferenciá-los", + "推理等级": "", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "Edita flags de capacidade como janela de contexto, máximo de tokens de saída e chamadas de ferramentas.", + "改动先列清单": "", + "改动直接执行": "", + "改动类 · 默认需要确认": "", + "改动类工具先列清单,等你点「执行」": "", + "改动类工具直接执行,不再询问": "", + "改回变更前确认": "", "改某一家的整组顺位(先试哪一家)。": "Reordena o grupo inteiro de um provedor (qual deles é tentado primeiro).", "改账号在全局队列里的名次,决定主备与切换顺序。": "Altera a posição da conta na fila global, definindo titular/reserva e ordem de failover.", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "Adiciona/remove nomes de mapeamento, com prefixação em lote e fusão de um mesmo nome em vários provedores.", + "新建对话时使用": "", + "映射": "", + "有什么可以帮你?可以问配置、查余额、让我改模型设置。": "Como posso ajudar? Pergunte sobre configuração, consulte saldos ou peça para eu mudar ajustes de modelo.", + "未命名对话": "Conversa sem título", + "未知的工具:{name}": "Ferramenta desconhecida: {name}", + "未选择": "", + "权限模式": "", + "查询账号": "", "查询账号余额": "Consultar saldo da conta", + "模型不支持时会自动忽略": "", + "模型没有返回内容": "O modelo não retornou conteúdo", + "正在思考…": "Pensando…", + "正在生成,点右侧按钮可停止": "Gerando — toque no botão à direita para parar", "没有可用的模型": "Nenhum modelo disponível", + "没有已启用的模型,请先在「模型管理」里启用": "Nenhum modelo habilitado. Habilite algum em Gerenciamento de modelos primeiro.", "测一下当前最快的模型通不通": "Testar se o modelo mais rápido no momento responde", "深度推理": "Raciocínio profundo", + "添加图片": "", + "添加图片(也可粘贴或拖入)": "", + "清空全部": "Limpar tudo", + "清空历史失败:{message}": "Falha ao limpar o histórico: {message}", + "现在有哪些账号被限流?": "Quais contas estão com limite de taxa agora?", + "现在没有账号被限流。": "", "看看哪些模型在多家重复了": "Ver quais modelos se repetem em vários provedores", "禁用会把账号移出转发队列,线路可能因此断供。": "Desabilitar remove a conta da fila de encaminhamento; a rota pode ficar sem fornecimento.", + "自定义提供商模块未就绪(请刷新界面后重试)": "O módulo de provedores personalizados não está pronto (atualize a interface e tente novamente)", + "自由对话请用桌面端…": "", "获取模型列表": "Listar modelos", + "要改配置的话,桌面端的助手支持完整对话与工具调用。": "", "设置模型映射 ID": "Definir ID de mapeamento do modelo", + "读取历史会话失败:{message}": "Falha ao carregar o histórico de conversas: {message}", + "读取图片失败": "", + "读取图片失败:{message}": "", + "读取模型清单": "", + "读取模型清单失败": "Falha ao carregar a lista de modelos", + "读取账号列表": "", "读各提供商的模型清单、映射名与启用状态。": "Lê a lista de modelos, os nomes de mapeamento e o estado de cada provedor.", "读各账号的额度 / 剩余用量。": "Lê a cota e o uso restante de cada conta.", "调整提供商顺位": "Ajustar ordem dos provedores", "调整账号优先级": "Ajustar prioridade da conta", + "走本地网关 · 消耗所选账号额度": "", + "跟随模型": "", + "跟随模型(不指定)": "Seguir modelo (não especificado)", + "跟随网关设置": "Usar a configuração do gateway", + "还没有历史对话": "Ainda não há histórico de conversas", + "还没有账号。": "", + "还没有选择对话模型:在输入框右下角选一个,或点右上角「助手设置」。": "", + "还没有配置默认模型": "Nenhum modelo padrão configurado ainda", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "Esta versão do gateway ainda não tem a API do assistente de IA (HTTP 404): o backend está desatualizado. Recompile / reinicie o cliente e tente de novo. {message}", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "Este turno atingiu o limite de {n} idas e voltas, então parei para aguardar sua instrução (reformule, ou divida em algumas etapas).", + "这个查询还没有实现。": "", + "这条会话没能存进历史:{message}": "Esta conversa não pôde ser salva no histórico: {message}", + "这里只改本次会话用的模型,不会改网关里保存的默认值。": "Isto muda apenas o modelo desta sessão; o padrão salvo no gateway não é alterado.", "连接网关失败({base}):{message}": "Falha ao conectar ao gateway ({base}): {message}", - "配置响应不完整": "Resposta de configuração incompleta" + "选择对话模型": "Escolher modelo de conversa", + "配置响应不完整": "Resposta de configuração incompleta", + "问点什么…": "Pergunte alguma coisa…", + "默认行为": "" } diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/checkin.json b/desktop-tauri/i18n/i18n-src/pt-BR/checkin.json index d012af6a..f0977a12 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/checkin.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/checkin.json @@ -1,12 +1,15 @@ { " · ": " · ", + "QClaw": "", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "Resgate de checkin_credits do SOLO · redefinido às 0h do dia natural", "UMID 组件安装失败:{error}": "Falha ao instalar o componente UMID: {error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy CN · Consulte a árvore de tarefas e acenda / resgate com um clique · inclui o evento de volta às aulas e a viagem do gato · clique na linha para detalhes", "WorkBuddy 国际版保活模型,按顺序回退": "Modelos do WorkBuddy internacional para manter ativo, com fallback em ordem", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "Modelos gratuitos da atividade diária do WorkBuddy Internacional · uma configuração global, independente dos agendamentos", "{date}(每天 {time})": "{date} (todos os dias às {time})", + "{done}/{total} 已签": "{done}/{total} com check-in", "{label} ×{count}({reason})": "{label} ×{count} ({reason})", + "{n} 积分": "{n} pontos", "{n} 项可完成": "{n} para concluir", "{n} 项待领": "{n} itens a resgatar", "{total} 账号 · {done} 已签": "{total} contas · {done} com check-in", @@ -22,10 +25,15 @@ "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "A \"cadeia de modelos de keep-alive\" são os modelos gratuitos usados para manter contas do WorkBuddy Internacional ativas na atividade diária — tentados em ordem, parando no primeiro sucesso; limpar a lista e salvar restaura a cadeia padrão (padrão atual: {defaults}). Deduplicação no dia + resgate idempotente: repetir a execução só retorna \"já resgatado\" e nunca pontua duas vezes.", "「免费领积分」的每日任务 · 逐个领取": "Tarefa diária do “Ganhe pontos grátis” · resgate um a um", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "Benefício único · consultado e resgatado automaticamente após o check-in; depois de resgatado, fica registrado localmente e o upstream não é mais consultado · clique na linha para expandir a lista de tarefas", + "一次性福利已全部结清": "Todos os benefícios únicos foram liquidados", + "一次性福利已结清": "Benefícios únicos liquidados", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "Resgata todas as recompensas do dia em uma única ação (check-in diário + presente de novos usuários) · idempotência garantida pelo upstream", "一键安装组件": "Instalar componente", "一键完成": "Concluir tudo", "一键领取({n})": "Resgatar todos ({n})", + "七天一轮共 4000 积分(400/400/400/1000/400/400/1000) · 每份 30 天后到期 · 当天重复领是幂等的": "", + "上一轮({time}):{n} 个动作,+{credit} 积分": "Última execução ({time}): {n} ações, +{credit} créditos", + "上一轮({time}):{n} 项,+{credit} 积分": "", "上次执行:{line}": "Última execução: {line}", "上次签到时间": "Último check-in", "上次领取 {time} · 领取需通过滑块验证码": "Último resgate: {time} · o resgate exige captcha deslizante", @@ -33,16 +41,27 @@ "下次 {date} · 点此管理条目": "Próxima {date} · clique para gerenciar os agendamentos", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "Fora do check-in diário: {list}. As recompensas únicas do CodeArts ficam em \"Benefícios iniciais\" abaixo; o resgate diário/de planos dele e do ZCode fica em \"Benefícios de atividade\".", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "Fora do check-in diário: {list}. As recompensas únicas do CodeArts ficam em \"Benefícios iniciais\" abaixo, e o benefício diário dele fica logo acima em \"Check-in diário\"; o resgate de planos do ZCode fica em \"Benefícios de atividade\".", + "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」;QClaw 已停运、积分随迁移到 WorkBuddy 发放,见下方「只读余额(已停运)」。": "", + "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」;QClaw 的积分随登录自动发放,见「自动发放」。": "", + "不参与签到": "Fora do check-in", + "不可领": "Indisponível", "与国内版同一套任务接口 · 站点不同": "Mesma interface de tarefas da edição da China · site diferente", + "与国内版同一套链路 · 站点不同、令牌不通用": "Mesmo fluxo da edição chinesa · site diferente, tokens não intercambiáveis", + "个账号不参与": "contas fora", + "今天 · 北京时间自然日": "Hoje · dia civil de Pequim", + "今天已执行:{list}": "Executado hoje: {list}", "今天已领 {n} 份": "{n} resgatado(s) hoje", "今天未领": "Não resgatado hoje", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "Resgatado hoje (horário de Pequim, {day}) · {n} itens confirmados oficialmente · amanhã é possível resgatar novamente", "今日已执行": "Executado hoje", "今日已派出": "Já enviado hoje", + "今日已签": "Check-in feito hoje", "今日已领取": "Resgatado hoje", "今日签到": "Check-in de hoje", "今日签到进度": "Progresso do check-in de hoje", "从清单里移除": "Remover da lista", + "余额查询中…": "", + "余额查询失败:{error}": "", "保活": "Manter ativo", "保活 {n}": "{n} mantidos ativos", "保活+领取": "Manter ativo + Resgatar", @@ -55,6 +74,8 @@ "全部结果": "Todos os resultados", "全部账号均可签到": "Todas as contas podem fazer check-in", "全部领取": "Resgatar todos", + "关着:不自动巡检(仍可手动巡检)": "Desligado: sem inspeção automática (a inspeção manual continua disponível)", + "关着:不自动执行(仍可手动执行)": "", "关着:所有条目都不执行": "Desligado: nenhum agendamento é executado", "删除失败:{error}": "Falha ao excluir: {error}", "删除定时条目": "Excluir agendamento", @@ -62,22 +83,27 @@ "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "No horário definido, o check-in automático é feito nas contas disponíveis dos provedores marcados (o WorkBuddy edição da China usa a interface de check-in diário; a edição internacional usa as tarefas de atividade diária: detectar o evento, resgatar a recompensa de atividade diária e então manter a conta ativa com modelos gratuitos; o Raccoon usa o fluxo diário de pontos do aplicativo desktop; o AutoClaw usa a tarefa de check-in diário do cliente oficial; o Qoder China usa o resgate de evento, e contas sem evento distribuído recebem um aviso neutro; o Qoder internacional precisa primeiro da identidade de controle de risco do dispositivo (gerada pelo componente UMID do cliente Qoder / qodercli local) para enxergar o evento “100 Credits diários”, e sem o componente recebe uma explicação clara em vez de um erro; o Trae resgata os pontos de check-in diário do SOLO (redefinidos às 0h do dia natural), e quando o upstream não abriu o evento para a conta ou a credencial não tem o ID do dispositivo, o resultado é “não resgatado + motivo específico” em vez de um erro; o Loomy credita uma vez por dia os pontos de primeiro login da conta; o KukuAI resgata a tarefa diária do “Ganhe pontos grátis”); o CodeArts resgata o benefício de campanha do dia (resgatar → confirmar → reler para verificar; execuções automáticas têm limite de 6 gravações por dia com intervalo de 10 minutos); o OfficeAce resgata todas as recompensas do dia em uma única ação (check-in diário + presente de novos usuários)). Se o horário for perdido, o check-in é compensado automaticamente após o computador ser ligado, sem que nada se perca por o computador estar desligado no horário. As interfaces de check-in de cada provedor são idempotentes; execuções repetidas não resgatam duas vezes.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "No horário definido, o check-in automático é feito nas contas disponíveis dos provedores marcados (o WorkBuddy edição da China usa a interface de check-in diário; a edição internacional usa as tarefas de atividade diária: detectar o evento, resgatar a recompensa de atividade diária e então manter a conta ativa com modelos gratuitos; o Raccoon usa o fluxo diário de pontos do aplicativo desktop; o AutoClaw usa a tarefa de check-in diário do cliente oficial; o Qoder China usa o resgate de evento, e contas sem evento distribuído recebem um aviso neutro; o Qoder internacional precisa primeiro da identidade de controle de risco do dispositivo (gerada pelo componente UMID do cliente Qoder / qodercli local) para enxergar o evento “100 Credits diários”, e sem o componente recebe uma explicação clara em vez de um erro; o Trae resgata os pontos de check-in diário do SOLO (redefinidos às 0h do dia natural), e quando o upstream não abriu o evento para a conta ou a credencial não tem o ID do dispositivo, o resultado é “não resgatado + motivo específico” em vez de um erro; o Loomy credita uma vez por dia os pontos de primeiro login da conta; o KukuAI resgata a tarefa diária do “Ganhe pontos grátis”); o OfficeAce resgata todas as recompensas do dia em uma única ação (check-in diário + presente de novos usuários)). Se o horário for perdido, o check-in é compensado automaticamente após o computador ser ligado, sem que nada se perca por o computador estar desligado no horário. As interfaces de check-in de cada provedor são idempotentes; execuções repetidas não resgatam duas vezes.", "到点时逐个签到,已勾 {n} / {total} 家": "{n} de {total} marcados — o check-in é feito um por um no horário", + "北京时间 · 逗号分隔 · 最多 6 条 · 到点后最多晚 30 秒执行;错过时点开机后会补跑一轮。": "Horário de Pequim · separados por vírgula · até 6 entradas · executa com no máximo 30 segundos de atraso; um horário perdido é recuperado uma vez após iniciar.", "北京时间 · 错过时点开机后会自动补签": "Horário de Pequim · execuções perdidas são compensadas automaticamente ao iniciar", "去领取": "Resgatar", "另有 {n} 个账号不参与每日签到": "Outras {n} contas não participam do check-in diário", + "只读余额(已停运)": "", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "Resgatar apenas recompensas de tarefas concluídas (idempotente; cliques repetidos não creditam em dobro)", "可完成": "Pode concluir", "可派出": "Pode enviar", "可选 · 缺省显示时刻": "Opcional · usa o horário como padrão", + "可领": "Disponível", "可领奖": "Pode resgatar", "后端未返回定时签到数据": "O backend não retornou dados de agendamento", "后端未返回签到中心数据": "O backend não retornou os dados da central de check-in", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "O check-in da edição internacional exige o componente UMID para gerar a identidade de controle de risco do dispositivo (não detectado nesta máquina).", + "在桌面端设置": "Configure no desktop", "在途": "Em viagem", "填入缺省链": "Preencher cadeia padrão", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "Escolha o horário e os provedores e clique em Salvar; o nome é opcional (quando vazio, mostra-se o horário)", "失败 {n}": "{n} com falha", "套餐明细": "Detalhes dos planos", + "套餐领取": "Resgate de plano", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Basta instalar o cliente Qoder ou o qodercli (não é preciso reiniciar o gateway).", "安装中…": "Instalando…", "官方客户端的每日签到任务": "Tarefa de check-in diário do cliente oficial", @@ -86,30 +112,49 @@ "尚未查询": "Ainda não consultado", "尚未查询任务状态": "Status das tarefas ainda não consultado", "巡检一次": "Executar uma rodada", + "巡检中…": "Inspecionando…", "已保存:{label}": "Salvo: {label}", "已保存:保活模型链": "Salvo: cadeia de modelos para manter ativo", "已保存:定时条目": "Salvo: agendamento", + "已保存:成长任务排程": "", + "已保存:猫猫旅行排程": "Salvo: agenda da viagem do gato", + "已停运": "", + "已全部领取": "Tudo resgatado", "已删除定时条目": "Agendamento excluído", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "Atividade reportada (ofensiva acesa), mas a campanha não está ativa (nada para resgatar desta vez)", + "已开始批量签到": "Check-in em lote iniciado", + "已得 {earned} / {total} 积分": "{earned} / {total} pontos ganhos", "已抽 {n} 次": "Sorteou {n} vezes", + "已提交「{label}」的签到": "Check-in de {label} enviado", "已有结果 {checked} / 共 {total} 个账号": "Já com resultado: {checked} / {total} contas", "已签": "Com check-in", + "已领": "", "已领 {claimed}/{total} · 累计 {earned} 积分": "Resgatado {claimed}/{total} · {earned} pontos acumulados", "已领养": "Adotado", "已领取": "Resgatado", + "已领取「{name}」": "{name} resgatado", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "Resgatar “100 Credits diários” com a identidade de controle de risco do dispositivo · requer o componente UMID", + "开关": "Opções", "开学季执行失败:{error}": "Falha na execução de volta às aulas: {error}", "开学季执行完成:{message}": "Execução de volta às aulas concluída: {message}", "开学季活动": "Evento de volta às aulas", "开着:启用的条目按各自时刻执行": "Ligado: agendamentos habilitados rodam em seus próprios horários", + "开着:按下面的时刻自动巡检": "Ligado: inspeciona automaticamente nos horários abaixo", + "开着:按下面的时刻自动执行": "", + "当前没有可领取的套餐": "Nenhum plano disponível no momento", "待签": "Pendente", + "待领取": "A resgatar", "待领新手福利": "Benefícios iniciais pendentes", "总开关": "Interruptor principal", "总开关已关闭 · 在「定时签到」页打开": "Interruptor principal desligado · ative na aba \"Check-in agendado\"", "成功 {n}": "{n} com sucesso", "成长任务": "Tarefas de crescimento", + "成长任务失败:{error}": "", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "Tarefas de crescimento concluídas ({done} com sucesso, {failed} com falha): créditos +{credit}, energia +{energy}", + "成长任务定时排程": "", "成长任务执行失败:{error}": "Falha na execução das tarefas de crescimento: {error}", + "成长任务:{done} 项完成、{failed} 项失败,+{credit} 积分 +{energy} 能量": "", + "成长任务:{done} 项完成,+{credit} 积分 +{energy} 能量": "", "执行失败:{error}": "Falha na execução: {error}", "执行并抽奖": "Executar e sortear", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "Executar as tarefas de volta às aulas e gastar todas as chances de sorteio (compartilhar / conversar / eventos de especialista)", @@ -125,14 +170,21 @@ "搜账号 / 收益 / 错误 / 触发原因": "Pesquisar conta / ganho / erro / motivo do disparo", "收起新建": "Fechar novo item", "新建定时条目": "Nova entrada agendada", + "新手任务": "Tarefas iniciais", "新手福利": "Benefícios iniciais", + "新手福利与活动": "Benefícios iniciais e campanhas", + "新手福利是一次性任务:领完就没了,重复点不会重复加分。": "Os benefícios iniciais são tarefas únicas: depois de resgatados acabam, e toques repetidos não somam pontos de novo.", "新手福利有 {n} 项领取失败,可重试": "{n} item(ns) dos benefícios iniciais falharam no resgate — é possível tentar novamente", "无": "Nenhum", + "无独立签到接口 · 每天替账号打一次登录信息(即当日魔粒)": "Sem interface de check-in própria · consulta as informações de login uma vez por dia por conta (o que concede os créditos do dia)", "无独立签到接口 · 每天替账号打一次首次登录积分": "Sem interface de check-in própria · credita uma vez por dia os pontos de primeiro login da conta", "无账号": "Sem contas", + "时刻": "Horários", "暂不可领": "Indisponível", + "暂不可领(未满足条件)": "Ainda não resgatável (requisitos não atendidos)", "暂不支持": "Ainda não suportado", "暂无启用的条目 · 点此添加": "Nenhum agendamento habilitado · clique para adicionar", + "最近到期 {date}": "", "有未保存的改动": "Alterações não salvas", "未勾选提供商": "Nenhum provedor selecionado", "未开启": "Desativado", @@ -141,30 +193,45 @@ "未领取": "Não resgatado", "本地台账里今天还没领过;活动期内每天发一份新套餐": "O registro local ainda não tem resgate nenhum hoje; um novo plano é emitido todo dia durante o evento", "本地台账里今天(北京时间)领过的套餐:{plans}": "Planos resgatados hoje (horário de Pequim) conforme o registro local: {plans}", + "本站已停止发放积分,余额在原有效期内仍可用": "", "条启用": "habilitado(s)", "查询于 {time}": "Consultado em {time}", "查询任务": "Consultar tarefas", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "Consulte a árvore de tarefas e acenda / resgate com um clique (inclui volta às aulas e viagem do gato)", "查询失败:{error}": "Falha na consulta: {error}", + "查询无结果": "", "桌面端每日积分链路": "Fluxo diário de pontos do aplicativo desktop", "正在读取定时签到数据…": "Carregando dados de agendamento…", "正在读取签到数据…": "Lendo os dados de check-in…", "每天 {time}": "Todos os dias às {time}", "每天 {time} · {scope}": "Diário {time} · {scope}", + "每天定点巡检 · 一趟推进一步 · 与签到排程互相独立": "Inspeção diária em horários fixos · um passo por execução · independente da agenda de check-in", + "每天定点执行成长任务 · 与签到 / 旅行互相独立 · 默认关": "", + "每天按这里的时刻自动巡检一遍 WorkBuddy 国内版账号:没有猫就领养、在家就派出、到站就领奖。一趟只推进一步,所以建议留两个时刻(早上派出、晚上领奖)。企业版与国际版账号自动跳过。": "Inspeciona automaticamente todas as contas WorkBuddy China nos horários definidos: adota um gato se não houver, envia quando ocioso e resgata a recompensa ao chegar. Cada execução avança um passo, então recomenda-se dois horários (enviar pela manhã, resgatar à noite). Contas empresariais e internacionais são ignoradas automaticamente.", + "每天按这里的时刻自动执行 WorkBuddy 国内版账号的成长任务:逐个点亮、完成后领奖。夜猫子任务只有 23:00-08:00(北京时间)能点亮,所以缺省时刻是 23:30。默认关闭 —— 它会上报事件并领积分,请确认要自动执行再打开。企业版与国际版账号自动跳过,不可伪造的任务(如真实捐款)也跳过。": "", "每天时刻": "Horário diário", + "每天自动执行": "Executa automaticamente todos os dias", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "Uma linha por conta a cada execução · resultados e ganhos conforme registrado pelo backend", + "每日活动签到(+100 积分,已领则中性提示)": "Check-in de atividade diária (+100 créditos; aviso neutro se já resgatado)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "Tarefas de atividade diária · uma conversa no canal web acende a atividade diária, só o resgate adiciona pontos · os dois podem ser executados separadamente", + "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "Pontos de login diário · resgata atividades de membro junto · contas de visitante não têm sistema de pontos (ignorado)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "Benefício diário · resgatar → confirmar → reler para verificar · redefinido no dia natural (horário de Pequim)", "每日签到": "Check-in diário", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "Cada agendamento tem horário e escopo próprios · deduplicação diária por item · execuções perdidas são compensadas ao iniciar", "每账号一条明细 · 点此查看「签到记录」": "Uma linha por conta · abra \"Registros de check-in\"", + "没有可签到的账号": "Nenhuma conta disponível para check-in", "没有可领套餐的账号(ZCode 未添加)。": "Nenhuma conta com planos resgatáveis (ZCode não adicionado).", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "Nenhuma conta com benefícios para resgatar (CodeArts / ZCode não foram adicionados).", + "没有已停运但仍可查余额的账号(目前有 QClaw 一家)。": "", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "Nenhuma conta suporta tarefas de crescimento (requer WorkBuddy CN).", "没有支持新手福利的账号": "Nenhuma conta suporta benefícios iniciais", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "Nenhuma conta suporta benefícios iniciais — atualmente Loomy, Raccoon e CodeArts.", "没有符合筛选条件的签到记录": "Nenhum registro de check-in corresponde aos filtros", + "没有随登录自动发放积分的账号(目前有 QClaw 一家)。": "", "没有需要执行的任务": "Nada para executar", + "活动奖励(30 天 / 7 天有效期)+ 下载客户端奖励 · 两条链都幂等": "", + "活动尚未开始(上游接口未部署)": "A campanha ainda não começou (endpoint upstream não implantado)", + "活动按自然日发新套餐,每天可领一次": "A campanha libera um novo plano por dia civil; um resgate por dia", "活动福利": "Recompensas de evento", "活动(campaign)领取 · 每天 10:00 刷新": "Resgate de eventos (campaign) · atualizado todos os dias às 10:00", "活跃上报与保活均未成功,且活动未开启": "Tanto o relatório de atividade quanto o keep-alive falharam, e a campanha não está ativa", @@ -183,17 +250,26 @@ "清空签到记录": "Limpar registros de check-in", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "Clique em \"Detalhes dos planos\" para ver quais você recebeu e quando entram em vigor; dá para resgatar de novo todo dia durante o evento", "点击行展开账号明细 · 单账号签到不受范围限制": "Clique na linha para expandir os detalhes das contas · o check-in individual não é limitado pelo escopo", + "点进去查看任务并领取": "Toque para ver as tarefas e resgatar", "独立链路 · 只提供手动入口 · 不进自动签到": "Fluxo independente · apenas entrada manual · não participa do check-in automático", "猫猫旅行": "Viagem do gato", "猫猫旅行失败:{error}": "Falha na viagem do gato: {error}", "猫猫旅行失败:{message}": "Falha na viagem do gato: {message}", + "猫猫旅行定时排程": "Agenda da viagem do gato", "猫猫旅行:{message}": "Viagem do gato: {message}", + "猫猫旅行:{n} 个动作,+{credit} 积分": "Viagem do gato: {n} ações, +{credit} créditos", + "猫猫旅行:{n} 个动作,+{credit} 积分,{failed} 个失败": "Viagem do gato: {n} ações, +{credit} créditos, {failed} falhas", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "Manter a conta ativa com modelos gratuitos e uma conversa no canal web · sem resgatar recompensas", "确定删除「{name}」?删除后到点不再执行。": "Excluir \"{name}\"? Ele não será mais executado no horário agendado.", "确定清空全部签到记录?清空后无法恢复。": "Limpar todos os registros de check-in? Isso não pode ser desfeito.", "确定清空当前筛选出的签到记录?清空后无法恢复。": "Limpar os registros de check-in filtrados? Isso não pode ser desfeito.", + "福利与套餐的逐项领取在桌面端签到中心完成": "", + "积分随登录到账 · 无需签到 · 只读:余额与到期时间由上游直接给出": "", + "积分随登录自动发放,无需签到": "", "移除 {name}": "Remover {name}", "立即全部签到": "Fazer check-in de todos agora", + "立即巡检全部": "Inspecionar tudo agora", + "立即执行全部": "", "签到": "Check-in", "签到中…": "Fazendo check-in…", "签到中心分区": "Seções do centro de check-in", @@ -210,6 +286,7 @@ "结算于 {time}": "Liquidado em {time}", "能量 {n}": "Energia {n}", "腾讯每日签到接口 · 仅国内版": "Interface de check-in diário da Tencent · apenas edição da China", + "自动发放": "", "至少勾一家才能保存(不勾等于这条条目不签到)": "Selecione ao menos um provedor para salvar (sem seleção, esta entrada não faz check-in)", "读取定时签到数据失败:{error}": "Falha ao carregar dados de agendamento: {error}", "读取签到数据失败:{error}": "Falha ao ler os dados de check-in: {error}", @@ -219,6 +296,9 @@ "还没执行过": "Ainda não executado", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "Nenhum agendamento ainda — clique em \"Adicionar agendamento\" para criar um, ou faça check-in manualmente no \"Centro de check-in\".", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "Nenhum registro de check-in ainda — os detalhes aparecem aqui após um check-in manual ou uma execução agendada.", + "还没有记录": "Ainda sem registros", + "这个账号没有新手任务": "Esta conta não tem tarefas iniciais", + "这些提供商已停止发放积分 · 余额在原有效期内仍可用 · 只读:余额与到期时间由上游直接给出": "", "连登 {n} 天": "Sequência de {n} dias", "部分": "Parcial", "重签": "Refazer check-in", @@ -231,8 +311,11 @@ "领取": "Resgatar", "领取中": "Resgatando", "领取中…": "Resgatando…", + "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "Resgatar o CodingPlan gratuito (sob demanda: com o plano ativo apenas lê a cota)", "领取失败": "Falha no resgate", "领取失败:{error}": "Falha no resgate: {error}", + "领取每日积分 · 幂等(当天已领回 0,不报错)": "", + "领取通常需要过一次滑块验证;活动套餐多为当天稍晚生效,领取后可在桌面端「账号」里看套餐明细。": "O resgate geralmente exige passar por uma verificação de slider. Os planos de campanha costumam entrar em vigor mais tarde no mesmo dia; após resgatar, veja os detalhes em Contas no desktop.", "(共 {n} 个账号)": "({n} contas no total)", "(能量 / 连登读数本次未取到)": "(Leituras de energia / sequência indisponíveis desta vez)", ",日活保活 {n} 个": ", {n} mantidas ativas" diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/common.json b/desktop-tauri/i18n/i18n-src/pt-BR/common.json index 0723b151..2f1ce94d 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/common.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/common.json @@ -1,7 +1,12 @@ { "无可用账号:{reason}": "Nenhuma conta disponível: {reason}", "AI 助手": "Assistente de IA", + "Agent": "", + "Agent 接管": "", "Agent2API · 多提供商本地网关": "Agent2API · Gateway local multi-provedor", + "QQ 群号": "Número do grupo do QQ", + "Webhook 地址(https://…)": "", + "{base} / {cat}": "", "{key} 需要 true / false / null": "{key} deve ser true / false / null", "{key} 需要数字": "{key} deve ser um número", "{n} 个启用": "{n} habilitadas", @@ -12,39 +17,76 @@ "{n} 个账号启用": "{n} contas habilitadas", "{n} 条失败请求未读,点开「请求日志」查看": "{n} requisições com falha não lidas; abra “Logs de requisições” para ver", "{n} 条错误日志未读,点开「日志」查看": "{n} logs de erro não lidos; abra “Logs” para ver", + "{n} 项 · {shown} 显示": "{n} itens · {shown} visíveis", + "{size} · 下载后可直接安装,无需跳转浏览器": "{size} · Baixa e instala na hora — sem abrir o navegador", + "{size} · 点击下载后可直接安装,无需跳转浏览器": "{size} · Baixe e instale na hora — sem abrir o navegador", + "{total} 个菜单 · 显示 {shown} · 隐藏 {hidden} · {groups} 个分组": "{total} menus · {shown} visíveis · {hidden} ocultos · {groups} grupos", "· 不会改动任何配置": "· não altera nenhuma configuração", "· 每次执行前列改动清单,等你确认": "· lista as alterações antes de cada execução e aguarda sua confirmação", "—": "—", "★ 下一单走它": "", "✅ Token 已刷新": "✅ Token atualizado", "✅ 已将账号优先级调整到全局第一位": "✅ Prioridade da conta ajustada para o primeiro lugar na fila global", + "✅ 测试消息已发出,请查看对应群": "", + "✅ 通知设置已保存": "", "「{name}」匹配到多个账号:{list},请用完整账号名重试": "\"{name}\" corresponde a várias contas: {list}. Tente de novo com o nome completo da conta.", "「工具全不勾选 → 不注入系统提示词」:想当纯聊天用时,不必再去关上面的开关。": "\"Nenhuma ferramenta marcada → nenhum prompt de sistema\": para usar como chat comum, não precisa desligar o interruptor acima.", + "「设置」是固定项,含它的分组不能整组隐藏;可以单独隐藏组内其它菜单": "\"Configurações\" é fixo, então o grupo dele não pode ser ocultado inteiro; os outros menus podem ser ocultados individualmente", + "一个都不勾 = 全部订阅(配好渠道就能收到东西)": "", + "下拉刷新": "Puxe para atualizar", "不限模型": "", + "与电脑端一致:每 {n} 分钟": "Igual ao desktop: a cada {n} min", + "主导航": "Navegação principal", "代价": "Custo", "代理:池": "", + "会恢复默认的分组、顺序与显隐设置。你自建的分组会被删除(组内菜单不会丢)。": "Restaura os grupos, a ordem e a visibilidade padrão. Grupos criados por você são removidos (os menus são mantidos).", "会话内": "Na sessão", + "会话已过期。重新登录后会自动回到手机端。": "Sua sessão expirou. Ao entrar novamente você volta ao app móvel automaticamente.", "会话粘性": "Aderência de sessão", "低于限制器的阈值,转发时跳过该账号": "", "余额不足或 Token 周期用量到顶,被限制器拦下,自动恢复后回来": "", "余额优先": "", "作为 system 消息注入给助手": "Injetado no assistente como mensagem system", + "例如:家里、公司": "ex.: Casa, Escritório", + "侧边栏布局模块未加载(ui/sidebar-layout.js 应在 app.js 之前引入)。": "O módulo de layout da barra lateral não foi carregado (ui/sidebar-layout.js deve vir antes de app.js).", + "侧边栏没有显示任何菜单。到「设置 → 显示 → 侧边栏菜单」里打开需要的项。": "Nenhum menu aparece na barra lateral. Ative os que precisar em Configurações → Exibição → Menus da barra lateral.", + "侧边栏菜单": "Menus da barra lateral", "保存对话模型失败:{message}": "Falha ao salvar o modelo de chat: {message}", + "保存并继续": "Salvar e continuar", "候选队列": "", "停止": "Parar", + "先添加一台网关": "Adicione um gateway primeiro", "全不选": "Desmarcar tudo", "全局队列位次(转发按策略在此序上再排)": "", + "全局(账号表)": "", + "共 {n} 条记录": "{n} registros", + "共 {total} 个账号,已读到 {queried} 个": "", "关": "Desligado", + "关掉时不发任何消息(配置保留)。默认关 —— 没配好就发是打扰。": "", "冷却": "", + "出事了主动推给你(桌面端常驻托盘,不必自己盯着看)": "", + "分组名": "Nome do grupo", + "分组名,例如「常用」": "Nome do grupo, ex.: \"Favoritos\"", "列表 = 模型管理页里所有已启用的模型。助手走本地网关发起对话,消耗所选账号的额度。": "A lista é todos os modelos habilitados na página Gerenciamento de modelos. O assistente conversa pelo gateway local e consome a cota da conta selecionada.", + "创建并进入": "Criar e entrar", + "创建管理员账号": "Criar conta de administrador", "删除 {n} 条": "Excluir {n} itens", + "删除分组": "Excluir grupo", + "删除分组「{name}」?组内 {n} 个菜单会移到「{target}」,不会丢。": "Excluir o grupo \"{name}\"? Os {n} menus vão para \"{target}\" — nada é perdido.", + "删除空分组「{name}」?": "Excluir o grupo vazio \"{name}\"?", + "删除这个分组(组内菜单移到第一组)": "Excluir este grupo (os menus vão para o primeiro grupo)", "加载助手设置失败:{message}": "Falha ao carregar as configurações do assistente: {message}", "加载失败:{error}": "Falha ao carregar: {error}", + "加载更早的记录": "Carregar registros anteriores", "助手": "Assistente", "助手自己用哪个模型生成回复": "Qual modelo o assistente usa para gerar respostas", "助手设置读取失败,工具可能已过时。": "Falha ao ler as configurações do assistente; a lista de ferramentas pode estar desatualizada.", "助手走本地网关,消耗所选账号的额度": "O assistente usa o gateway local e consome a cota da conta selecionada", "勾选即注入该工具;不勾选 = 助手看不到它": "Ferramentas marcadas são injetadas; desmarcadas ficam invisíveis ao assistente", + "升序": "", + "单签": "Individual", + "去下载": "Baixar", + "去登录": "Entrar", "参数不完整(需要 account / enabled)": "Parâmetros incompletos (account / enabled obrigatórios)", "参数不完整(需要 account / position)": "Parâmetros incompletos (account / position obrigatórios)", "参数不完整(需要 provider / id / alias)": "Parâmetros incompletos (provider / id / alias obrigatórios)", @@ -52,21 +94,34 @@ "参数不完整(需要 provider / id / enabled)": "Parâmetros incompletos (provider / id / enabled obrigatórios)", "参数不完整(需要 provider / position)": "Parâmetros incompletos (provider / position obrigatórios)", "发现新版本 {latest}(当前 {current}),点开「设置 - 软件更新」可查看更新日志并下载": "Nova versão {latest} disponível (atual: {current}); abra “Configurações - Atualização do software” para ver as notas de versão e baixar", + "发现新版本 {version}": "Nova versão {version} disponível", "发送": "Enviar", + "发送测试": "", "只改映射名,不动上游模型 ID;映射立即生效(新对话即可用)。": "Só o nome de mapeamento muda; o ID do modelo upstream permanece. Os mapeamentos valem na hora (já valem em uma nova conversa).", "只注入工具定义": "Somente definições de ferramentas são injetadas", "只读": "Somente leitura", "只读工具": "Ferramentas somente leitura", "可用": "utilizáveis", + "含固定项「设置」,不可整组隐藏(可以单独隐藏组内其它菜单)": "Contém o item fixo \"Configurações\", então o grupo não pode ser ocultado inteiro (os outros menus podem ser ocultados individualmente)", "启用后重新参与转发": "", + "启用通知": "", "四种组合的实际效果": "Efeito real de cada combinação", "回复由 {model} 生成 · 走本地网关": "Resposta gerada por {model} · via gateway local", + "固定": "Fixo", "在全局队列里上移一位": "", "在全局队列里下移一位": "", + "地址已遮住 token(保存时不动它);要更换请整体重新粘贴": "", + "备注名(可留空)": "", + "备注名(可选)": "Nome (opcional)", "复制失败,请手动选择复制": "Falha ao copiar; selecione e copie manualmente", "复制订阅链接": "Copiar link da assinatura", "失败 · {status}": "Falha · {status}", "失败退避": "Recuo por falha", + "奖励 {bonus}": "Bônus {bonus}", + "套餐 {plan}": "Plano {plan}", + "安装": "Instalar", + "安装包已就绪,点「安装」即可完成更新": "Pacote pronto — toque em \"Instalar\" para concluir", + "定时": "Agendado", "定时任务": "Tarefas agendadas", "实际注入": "Injetado de fato", "对话模型": "Modelo de chat", @@ -80,118 +135,203 @@ "将把账号「{account}」移到第 {position} 顺位:": "Vai mover a conta \"{account}\" para a posição {position}:", "将改动 {n} 条映射({parts}):": "Vai alterar {n} mapeamentos ({parts}):", "尚未登录": "Sessão não iniciada", - "已保存": "Salvo", + "展开作用域栏": "", + "展开左栏,看 {label} 下的 {n} 个模型": "", + "展开来自{label}的 {n} 个模型": "", + "已下载,待安装": "Baixado, pronto para instalar", "已修改 {n} 个模型": "{n} modelos alterados", "已修改 {n} 个账号": "{n} contas alteradas", "已修改 {n} 项能力": "{n} capacidades alteradas", "已停止这次回答。": "Esta resposta foi interrompida.", "已全部取消勾选": "Todas desmarcadas", + "已关闭自动检查": "Verificação automática desativada", + "已删除分组「{name}」": "Grupo \"{name}\" excluído", "已勾选 {n} / {total}": "{n} / {total} marcadas", "已勾选「{label}」": "\"{label}\" marcada", "已勾选工具": "Ferramentas marcadas", "已取消「{label}」": "\"{label}\" desmarcada", + "已启动安装器": "Instalador iniciado", + "已复制{label}": "", "已复制:{text}": "Copiado: {text}", + "已完成 {percent}%": "", + "已开启自动检查": "Verificação automática ativada", "已恢复推荐勾选": "Seleção recomendada restaurada", + "已恢复默认分组:概览 / 转发链路 / 诊断 / 系统": "Grupos padrão restaurados: Visão geral / Encaminhamento / Diagnóstico / Sistema", "已改动 {n} 条映射": "{n} mapeamentos alterados", + "已新建分组「{name}」,点组头的 + 把菜单移进来": "Grupo \"{name}\" criado — use + no cabeçalho para mover menus", + "已是最新版本": "Você está na versão mais recente", "已注入 {n} / {total} 个工具 · 系统提示词:{state}": "{n} / {total} ferramentas injetadas · Prompt de sistema: {state}", + "已添加 {n} 台": "", + "已添加的服务器": "Servidores adicionados", "已登录": "Sessão iniciada", + "已离线 · 显示的是缓存内容": "Offline — exibindo conteúdo em cache", "已移到第 {n} 顺位": "Movida para a posição {n}", "已试过": "", "并发上限 {n}": "", "并发已满,等一个在途回落再参与": "", "并发满": "", "开": "Ligado", + "当前": "Atual", "当前一个工具都没勾选,助手只能纯聊天。去「设置 → AI 助手 → 工具」里勾上需要的工具。": "Nenhuma ferramenta marcada, então o assistente só pode conversar. Marque as ferramentas necessárias em Configurações → Assistente de IA → Ferramentas.", + "当前版本 {version}": "Versão atual {version}", + "当前环境不支持读取调度队列": "", "当前:已关闭(助手不收到任何系统提示词,工具定义照发)": "Atual: desligado (o assistente não recebe prompt de sistema; as definições de ferramentas continuam sendo enviadas)", "当前:已开启 · 使用内置默认提示词(还没自定义)": "Atual: ligado · usando o prompt padrão interno (ainda não personalizado)", "当前:已开启 · 自定义内容 {n} 字": "Atual: ligado · conteúdo personalizado, {n} caracteres", "影响转发": "Afeta o roteamento", "待确认": "Aguardando confirmação", + "总剩余 {total}": "Total restante {total}", "恢复上游原值": "Restaurar valores do upstream", "恢复推荐": "Restaurar recomendado", + "恢复默认侧边栏": "Restaurar barra lateral padrão", + "成功领取": "Resgatado", "我会在动手前把改动列给你确认。": "Vou listar as alterações para você confirmar antes de aplicá-las.", + "所有菜单都已在这一组": "Todos os menus já estão neste grupo", + "手机端打开": "", "打开账号设置": "", "执行": "Executar", + "把菜单移入 {name}": "Mover um menu para {name}", + "把菜单移入「{name}」": "Mover um menu para \"{name}\"", "报表": "Relatórios", + "拖动分组 {name}": "Arrastar grupo {name}", + "拖动左侧 ⠿ 调整顺序(菜单可以跨组拖);拨开关控制显隐;点分组名可重命名;组头的 + 把别的菜单移进来;眼睛按钮隐藏整组。改动即时生效并记住,左侧侧边栏就是结果。键盘也能排序:把手聚焦后按 Alt+↑ / ↓。": "Arraste o ⠿ para reordenar (menus podem mudar de grupo); use os interruptores para mostrar ou ocultar; clique no nome do grupo para renomear; o + no cabeçalho move outro menu para o grupo; o botão de olho oculta o grupo inteiro. As mudanças valem na hora e ficam salvas — a barra lateral à esquerda é o resultado. Teclado: foque a alça e pressione Alt+↑ / ↓.", + "拖动菜单 {name}": "Arrastar menu {name}", + "拖动调整分组顺序(Alt+↑ / ↓ 也可)": "Arraste para reordenar os grupos (Alt+↑ / ↓ também funciona)", "拖动调整队列顺序": "", + "拖动调整顺序,可跨组(Alt+↑ / ↓ 也可)": "Arraste para reordenar, inclusive entre grupos (Alt+↑ / ↓ também funciona)", + "按作用域选择:全局账号表,或某一个模型的调度视图": "", + "按厂商": "", "按模型展开队列": "", "按请求的模型展开 —— 与转发选路同一份排序,下一个请求会走 ★ 那位": "", + "按配置顺序逐个发送;一个失败不影响其它": "", "排第 1 的接走所有请求,直到它不能答(限流 / 故障 / 额度尽)才轮到下一位 —— 就是「从上往下」的现状行为。": "A posição 1 atende todas as requisições até não conseguir responder (limite / falha / sem cota) antes de passar para a próxima — o mesmo comportamento de cima para baixo de hoje.", + "接口文档": "", + "控制主侧边栏显示哪些菜单、按什么顺序、怎么分组。隐藏只影响导航显示,不会撤销页面访问权限;被隐藏的页面仍可通过地址直接打开。": "Define quais menus a barra lateral mostra, em que ordem e como são agrupados. Ocultar afeta apenas a navegação — não revoga acesso, e páginas ocultas continuam abrindo pela URL.", "提示词": "Prompt", "提示词开关": "Interruptor do prompt", + "搜索模型或厂商": "", + "搜索模型或厂商…": "", + "收起作用域栏": "", + "收起来自{label}的 {n} 个模型": "", "改动立即生效;禁用后指向它的请求会落到下一个候选。": "As alterações valem na hora; após desabilitar, as requisições para ele caem no próximo candidato.", "改动类工具": "Ferramentas de alteração", "改成": "Alterar para", "文档": "Documentação", "新": "Novo", - "新增": "Adicionar", + "新分组名": "Nome do novo grupo", "新增 {n} 条": "Adicionar {n} itens", "新对话": "Nova conversa", + "新建分组": "Novo grupo", "无凭证": "", "日志": "Logs", "映射名": "Nome de mapeamento", + "显示 {name}": "Mostrar {name}", + "显示分组 {name}": "Mostrar grupo {name}", + "显示这一组": "Mostrar este grupo", "智能": "IA", + "更多": "Mais", + "最先作废的一份到期日": "", "最大化": "Maximizar", "最小化": "Minimizar", + "最早 {day} 到期": "", "最早 {time} 恢复": "", "有": "Sim", + "有新版本,重启后生效": "Nova versão pronta — reinicie para aplicar", + "服务器": "Servidores", "未同步": "Não sincronizado", "未就绪": "Indisponível", "未登录": "Sem sessão", "未知工具「{name}」:它在设置里可能刚被取消勾选,或这一版助手还不认识它。": "Ferramenta desconhecida \"{name}\": ela pode ter sido desmarcada agora nas configurações, ou esta versão do assistente ainda não a conhece.", + "未选择服务器": "Nenhum servidor selecionado", "本周期消耗达到限制器上限,转发时跳过": "", "本地网关监听状态": "Status de escuta do gateway local", + "松开刷新": "Solte para atualizar", "查余额": "", "查看参数": "Ver parâmetros", "查看结果": "Ver resultado", "查询该账号的余额": "", "桌面桥不可用(本页需要在 Agent2API 客户端里打开)": "Ponte do desktop indisponível (esta página precisa ser aberta dentro do cliente Agent2API)", + "模型清单为空:先去模型管理页点「获取模型」": "", "模型管理": "Gerenciamento de modelos", "次熔断": "", + "正在下载…": "Baixando…", + "正在完成人机验证…": "Concluindo a verificação humana…", + "正在检查…": "Verificando…", "正在识别二维码…": "Reconhecendo o QR code…", + "正在连接…": "Conectando…", + "每台服务器的登录状态各自独立保存,切换后不用重新登录。地址改掉后需要重新登录。": "Cada servidor guarda o próprio login, então voltar a ele não exige entrar de novo. Alterar o endereço de um servidor exige entrar de novo.", "每条请求轮换到下一个可用账号;新会话交给最近最闲的那位。适合想把手里的号雨露均沾。": "", "每轮": "Por turno", "没有已启用的模型": "", + "没有带余额概念的账号。添加账号后,这里会显示各账号的额度余量与到期。": "", "没有找到提供商「{provider}」的账号": "Nenhuma conta encontrada para o provedor \"{provider}\"", "没有找到账号「{name}」": "Nenhuma conta encontrada com o nome \"{name}\"", "没给账号名": "Nenhum nome de conta informado", "注入系统提示词": "Injetar prompt de sistema", + "活动": "Campanha", "浅色": "Claro", + "测试未全部成功,详见下方": "", "消耗该账号额度": "Consome a cota desta conta", "消耗额度": "Consome cota", "深色": "Escuro", + "添加": "Adicionar", + "添加服务器": "Adicionar servidor", + "添加渠道": "", "清空 = 回到内置默认(与「恢复默认」等价)。": "Limpar volta ao padrão interno (equivale a \"Restaurar padrões\").", + "渠道类型": "", "点「编辑」弹窗改内容;开关关掉就整段不发。": "Clique em \"Editar\" para alterar o conteúdo em uma janela; com o interruptor desligado, nada disso é enviado.", + "点击重命名": "Clique para renomear", "熔断": "Disjuntor", "熔断时长(秒)": "", + "版本 {version} · 从 GitHub 直接下载安装包": "Versão {version} · baixa o pacote direto do GitHub", + "版本 {version} · 从网关下载安装包": "Versão {version} · baixa o pacote pelo gateway", + "版本 {version} · 更新包需从 Release 页面获取": "Versão {version} · obtenha a atualização na página de Release", "用一句话让我查或改本机的模型转发配置。": "Peça em uma frase para consultar ou alterar a configuração de roteamento de modelos desta máquina.", + "用量与额度": "", "用量占比最低的先用;没有读数的账号按队列位次排在有读数的后面。适合「谁空谁上」。": "", + "电脑端已关闭「软件版本检查」,此处只做手动检查": "A verificação de versão está desligada no desktop; aqui só há checagem manual", + "登录面板": "Entrar", "监听 127.0.0.1": "Escutando em 127.0.0.1", "直接说要改什么 / 查什么…(Enter 发送,Shift+Enter 换行)": "Diga o que quer alterar ou consultar… (Enter envia, Shift+Enter quebra linha)", + "看这个模型的调度队列:{tip}": "", "确定删除账号「{name}」?{note}": "Excluir a conta “{name}”?{note}", "确认后立即生效": "Vale na hora após a confirmação", "禁用会把账号移出转发队列;若这条线路没有别的可用账号,请求会直接失败。": "Desabilitar remove a conta da fila de encaminhamento; se esta rota não tiver outra conta utilizável, as requisições falharão direto.", "禁用后不参与转发,余额仍可查": "", + "移入「{name}」": "Mover para \"{name}\"", + "稍后": "Depois", + "空分组 · 点组头的 + 把菜单移进来": "Grupo vazio · use + no cabeçalho para mover menus", + "等待窗口": "Aguardando janela", "等这一轮跑完…": "Aguardando o fim deste turno…", "签到中心": "Central de check-in", "系统提示词 + 工具定义": "Prompt de sistema + definições de ferramentas", "系统提示词已保存": "Prompt de sistema salvo", "系统提示词已关闭": "Prompt de sistema desligado", "系统提示词已开启": "Prompt de sistema ligado", + "编辑服务器": "Editar servidor", "编辑系统提示词": "Editar prompt de sistema", "网关 Key": "Chave do gateway", "网关运行中": "Gateway em execução", "网络代理": "Proxy de rede", "能力位决定下游看到的声明(能不能贴图、能塞多少上下文),不改上游的真实行为。": "Os flags de capacidade definem o que o downstream vê declarado (entrada de imagem, tamanho de contexto) sem alterar o comportamento real do upstream.", "自动": "Automático", + "至少要保留一个分组": "Pelo menos um grupo deve permanecer", + "补签": "Reposição", + "订阅哪些事": "", "订阅里的节点会自动同步进下方代理池,账号即可引用。": "", "订阅里的节点会被内核起成本地监听器,镜像到下方代理池(账号按 id 引用,与本页其它出口同一用法)。": "", "订阅链接必须是 http:// 或 https:// 开头": "O link da assinatura precisa começar com http:// ou https://", "设置": "Configurações", + "设置不可隐藏(隐藏了只能清缓存找回来)": "\"Configurações\" não pode ser ocultado (seria preciso limpar o armazenamento para recuperá-lo)", "诊断": "Diagnóstico", + "请允许安装未知来源应用后重试": "Permita instalar apps de fontes desconhecidas e tente novamente", + "请在 Release 页面下载后通过 TestFlight 或 App Store 安装": "Baixe na página de Release e instale pelo TestFlight ou pela App Store", + "请填一个分组名": "Informe um nome de grupo", "请填写订阅名称": "Digite o nome da assinatura", "请求失败:{message}": "Falha na requisição: {message}", "请求日志": "Logs de requisições", + "读不到完整模型清单:{reason}": "", + "读取通知设置失败:{reason}": "", "账号不在当前队列里": "A conta não está na fila atual", "账号已删除": "Conta excluída", "账号已在全局队列第一位": "A conta já está em primeiro lugar na fila global", @@ -200,29 +340,54 @@ "转发失败的账号休息一会儿再回来(连续失败翻倍,上限 1 小时)": "", "转发能力": "Encaminhamento", "轮流均摊": "", + "输入这台网关的管理员账号与密码。": "Informe a conta de administrador e a senha deste gateway.", + "运行日志": "", "运行正常 · {url}": "Funcionando normalmente · {url}", "运行状态": "Status", + "返回": "", "还有额度时被 429 的账号排到队尾,轮一圈才回来 —— 负载摊开而不是每次回到第 1 个": "", + "还没有可用模型(添加账号后可见)": "", + "还没有渠道。点右上角「添加渠道」,把群机器人的 Webhook 地址填进来。": "", + "还没有签到记录": "Ainda não há registros de check-in", "还没有订阅:粘贴订阅链接,或从图片识别二维码导入。": "", "还没有选择对话模型:先在下方选一个(或去「设置 → AI 助手」里选)。": "Nenhum modelo de chat selecionado ainda: escolha um abaixo (ou em Configurações → Assistente de IA).", + "还没查到余额。到「账号」页点一次「查询余额」,或等自动查询跑完。": "", + "这一版没有提供 Android 安装包": "Esta versão não inclui pacote para Android", + "这一项已隐藏": "Este item está oculto", + "这一项正在显示": "Este item está visível", "这个模型下没有可用账号": "", + "这个面板没有管理员,用一把已启用的 API Key 访问(在桌面端「网关 Key」页创建)。": "Este painel não tem administrador. Acesse com uma API key ativa (crie uma em Chaves do gateway no desktop).", "这会真实发起上游请求并消耗额度": "Isso envia uma requisição real ao upstream e consome cota", + "这台网关没有管理员,用一把已启用的 API Key 访问(在桌面端「网关 Key」页创建)。": "Este gateway não tem administrador. Use uma API key ativa (crie uma na página \"Chave do gateway\" do app desktop).", + "这台网关还没有管理员。设置一个账号密码,之后用它登录。": "Este gateway ainda não tem administrador. Defina uma conta e senha e use-as para entrar.", + "这家没有返回可用明细(可能额度都在上游侧,或全部已用尽)": "", "这是真实的上游请求,与生产走同一条链路(会记账,报表排除)。": "Esta é uma requisição real ao upstream, pelo mesmo caminho da produção (entra na cobrança, mas fica fora dos relatórios).", "这段文本作为 system 消息排在对话最前面。工具的定义与当前配置概览由网关自动附加,不用写在这里。": "Este texto entra no início da conversa como mensagem system. As definições de ferramentas e o resumo da configuração atual são anexados automaticamente pelo gateway, então não os escreva aqui.", "连接 {n}": "", + "连接并保存": "Conectar e salvar", "连续失败到阈值后熔断一段时间;到点放行试一次,连续成功才算恢复": "", "连续失败阈值": "", "连通 · {ms}ms": "Conectado · {ms}ms", "选择对话模型…": "", "选择模型…": "Escolher modelo…", + "通知与告警": "", + "通知渠道": "", "都不注入": "Nada é injetado", "都不注入(助手退化为纯聊天)": "Nada é injetado (o assistente vira um chat comum)", + "重启更新": "Reiniciar e atualizar", "队列是一次性整表提交(后端按新顺序重编优先级),移动会连带调整其它组的相对次序。": "A fila é enviada como uma tabela inteira de uma vez (o backend renumera as prioridades na nova ordem), então mover um item também desloca a ordem relativa dos outros grupos.", "队列是一次性整表提交;位置越靠前,路由时越先被选中。": "A fila é enviada como uma tabela inteira de uma vez; quanto mais à frente a posição, antes ela é escolhida no roteamento.", + "降序": "", "限流 · {n} 个模型": "", "限流垫底": "Rebaixamento por limite", + "隐藏 {name}": "Ocultar {name}", + "隐藏分组 {name}": "Ocultar grupo {name}", + "隐藏这一组": "Ocultar este grupo", + "需要网关 API Key": "É preciso uma API key do gateway", "顺序优先": "Ordem estrita", + "首页": "Início", "高危": "Alto risco", + "({n} 个整组隐藏)": " ({n} grupos ocultos)", "(不会影响客户端登录态)": " (não afeta a sessão de login do cliente)", "(仅显示前 20 条,共 {n} 条)": "(mostrando apenas os primeiros 20 de {n})", "(未指定)": "(não especificado)", diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/docs.json b/desktop-tauri/i18n/i18n-src/pt-BR/docs.json index d2c447ef..406be51a 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/docs.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/docs.json @@ -1,18 +1,28 @@ { " 或 ": " ou ", "Base URL 填进客户端的「API 地址 / Base URL」栏;对话协议按客户端支持的类型三选一(Chat Completions / Responses / Anthropic Messages),三者共用同一套模型与账号池,可随时切换;鉴权用「网关 Key」页里任一启用 Key。": "Preencha o Base URL no campo “Endereço da API / Base URL” do cliente; escolha um dos três protocolos de conversa conforme o tipo compatível com o cliente (Chat Completions / Responses / Anthropic Messages) — os três compartilham o mesmo conjunto de modelos e o mesmo pool de contas e podem ser alternados a qualquer momento; para autenticação, use qualquer Key habilitada na página “Chave do gateway”.", + "Claude Code / Anthropic SDK": "", "Claude Code / Anthropic SDK(token 计数:/v1/messages/count_tokens)": "Claude Code / Anthropic SDK (contagem de tokens: /v1/messages/count_tokens)", + "Codex、新版 SDK": "", "OpenAI 新协议(Codex、新版 SDK)": "Novo protocolo da OpenAI (Codex, SDKs mais recentes)", + "curl 命令": "", "。一把启用的 Key 都没有时不鉴权(默认网关只监听 127.0.0.1;在设置页开启「局域网访问」后监听所有网卡,鉴权与安全闸门随之启用)。": ". Se nenhuma Key estiver habilitada, não há autenticação (por padrão, o gateway escuta apenas 127.0.0.1; após habilitar “Acesso LAN” na página de configurações, ele passa a escutar todas as interfaces de rede, e a autenticação e as barreiras de segurança entram em vigor).", - "启用": "habilitada", + "。用「网关 Key」页里任一启用的 Key;一把都没有时不鉴权。": "", + "不受鉴权限制,配 Key 前就能拉": "", + "启用": "ativos", "填进客户端的「API 地址 / Base URL」": "Preencha no campo “Endereço da API / Base URL” do cliente", + "复制命令": "", "复制地址": "Copiar endereço", "多数 OpenAI 兼容客户端的默认协议": "Protocolo padrão da maioria dos clientes compatíveis com OpenAI", + "多数兼容客户端的默认协议": "", "客户端自动拉取可用模型;不受鉴权限制,配 Key 之前就能拉": "O cliente busca automaticamente os modelos disponíveis; não exige autenticação e pode ser consultado antes de configurar uma Key", "对话协议(按客户端支持的类型选一行)": "Protocolo de conversa (escolha uma linha conforme o tipo compatível com o cliente)", + "快速验证": "", + "手机端只读文档。完整的使用说明与部署指引在仓库的 docs/ 目录。": "", "接口地址": "Endereço da API", "模型列表": "Lista de modelos", "模型清单": "Catálogo de modelos", "的 Key:请求带 ": " da página “Chave do gateway”; as requisições enviam ", + "请求带 ": "", "鉴权用「网关 Key」页里任一": "Para autenticação, use qualquer Key " } diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/keys.json b/desktop-tauri/i18n/i18n-src/pt-BR/keys.json index 16b066cc..ab8fc836 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/keys.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/keys.json @@ -7,11 +7,13 @@ "✅ Key 已创建,记得复制给客户端": "✅ Key criada; lembre-se de copiá-la para o cliente", "✅ 可用范围已保存": "✅ Escopo permitido salvo", "不限制": "Sem restrições", + "不限制提供商与模型": "", "与": " e ", "例如 Cursor / 公司电脑": "Ex.: Cursor / computador da empresa", "保存失败:{reason}": "Falha ao salvar: {reason}", "保存范围": "Salvar escopo", "创建": "Criar", + "创建于 {time}": "", "创建时间": "Criado em", "删除网关 Key": "Excluir Key do gateway", "可用提供商": "Provedores permitidos", @@ -22,14 +24,17 @@ "可用范围 · {name}": "Escopo permitido · {name}", "启用「{name}」": "Habilitar “{name}”", "复制 Key": "Copiar Key", + "客户端请求带上 ": "", "客户端请求需带 ": "As requisições do cliente precisam enviar ", + "已删除": "", "已启用鉴权 · {n} 把 Key 生效": "Autenticação habilitada · {n} Keys em vigor", "当前不限制:这把 Key 可以用全部提供商与全部对外模型": "Sem restrições no momento: esta Key pode usar todos os provedores e todos os modelos expostos", "提供商:{names}": "Provedores: {names}", "搜索模型…": "Buscar modelos…", "操作失败:{reason}": "Falha na operação: {reason}", "新建 API Key": "Nova API Key", - "显示": "Mostrar", + "新建 Key 与设置可用范围(提供商 / 模型白名单)在桌面端完成。": "", + "显示": "Visível", "未启用鉴权": "Sem autenticação", "未命名": "Sem nome", "模型:{names}": "Modelos: {names}", @@ -38,9 +43,11 @@ "留空自动生成;手填至少 8 个字符": "Em branco: gerada automaticamente; se preenchida, mínimo de 8 caracteres", "留空表示不限制;同时设置时请求需同时满足两个条件(模型在白名单内且路由到允许的提供商)。 「可用模型」的候选跟着上面勾选的提供商走:没勾提供商时它是空的(还没有约束范围), 勾了几家就列出这几家能收的全部对外名。": "Em branco significa sem restrições; quando ambos são definidos, a requisição precisa atender às duas condições (o modelo estar na lista permitida e ser roteado para um provedor permitido). Os candidatos de “Modelos permitidos” seguem os provedores marcados acima: sem nenhum provedor marcado, a lista fica vazia (ainda não há escopo definido); com alguns marcados, são listados todos os nomes expostos aceitos por eles.", "确定删除 Key「{name}」?使用它的客户端会立刻无法访问。": "Excluir a Key “{name}”? Os clientes que a usam perderão o acesso imediatamente.", + "确定删除「{name}」?用这把 Key 的客户端会立即失效。": "", "网关 Key 表": "Tabela de Keys do gateway", "请先在上面选择可用提供商": "Selecione primeiro os provedores permitidos acima", "读取 Key 列表失败:{reason}": "Falha ao ler a lista de Keys: {reason}", + "还没有 Key。去桌面端「网关 Key」页新建一把。": "", "还没有 Key,当前不鉴权": "Ainda não há Keys; sem autenticação no momento", "这几家当前没有可用模型(账号未登录或清单为空)": "Estes provedores não têm modelos disponíveis no momento (contas sem sessão ou catálogo vazio)", "这把 Key 不限制提供商与模型(可用全部上游与全部对外模型)": "Esta Key não restringe provedores nem modelos (pode usar todos os upstreams e todos os modelos expostos)", @@ -49,5 +56,7 @@ "(模型候选需先选提供商;不选则沿用当前这几项,保存后仍按模型白名单生效)": "(os candidatos de modelo exigem escolher primeiro os provedores; sem escolha, os itens atuais são mantidos e, após salvar, a lista de modelos continua valendo)", "(行内「可用范围」):留空 = 不限制,两个都设时 按交集生效 —— 被限制的模型对这把 Key 表现为「不存在」(拉 /v1/models 也看不到它), 提供它的家不在可用列表里时请求同样被拒。": "(coluna “Escopo permitido”): em branco = sem restrições; quando ambos são definidos, vale a interseção — um modelo restringido “não existe” para esta Key (nem ao consultar /v1/models ele aparece) e, se o provedor que o oferece não estiver na lista de permitidos, a requisição também é recusada.", "+ 新建 Key": "+ Nova Key", - "; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制": "; as alterações entram em vigor imediatamente, e este próprio programa usará automaticamente a primeira Key habilitada. Cada Key pode restringir individualmente " + "; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制": "; as alterações entram em vigor imediatamente, e este próprio programa usará automaticamente a primeira Key habilitada. Cada Key pode restringir individualmente ", + ";一把启用的 Key 都没有时不鉴权。": "", + ";当前必须带 Key。": "" } diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/logs.json b/desktop-tauri/i18n/i18n-src/pt-BR/logs.json index 0ea2b057..c71dbbb1 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/logs.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/logs.json @@ -10,9 +10,13 @@ "信息": "Info", "全部分类": "Todas as categorias", "全部级别": "Todos os níveis", + "复制失败": "", + "复制当前筛选结果": "", "导出": "Exportar", + "已复制 {n} 条日志": "", "按分类筛选": "Filtrar por categoria", "按最低级别筛选": "Filtrar por nível mínimo", + "搜索关键字": "", "搜索消息关键词": "Pesquisar palavra-chave da mensagem", "搜索消息关键词…": "Pesquisar palavra-chave da mensagem…", "操作失败:{error}": "Falha na operação: {error}", @@ -20,6 +24,7 @@ "暂无日志可导出": "Não há logs para exportar", "模型 {model}": "Modelo {model}", "正在加载日志…": "Carregando logs…", + "没有符合条件的日志": "", "没有符合筛选条件的日志": "Nenhum log corresponde aos filtros", "清空中…": "Limpando…", "清空运行日志": "Limpar logs de execução", @@ -27,6 +32,7 @@ "确定清空当前筛选出的 {count} 条日志?清空后无法恢复。": "Tem certeza de que deseja limpar os {count} logs filtrados? Isso não pode ser desfeito.", "系统事件": "Eventos do sistema", "警告": "Aviso", + "读取日志失败": "", "调试": "Debug", "运行日志已清空": "Logs de execução limpos", "错误": "Erro" diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/models.json b/desktop-tauri/i18n/i18n-src/pt-BR/models.json index 8c2e65bb..05a4de1a 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/models.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/models.json @@ -36,6 +36,7 @@ "{name}({id})": "{name} ({id})", "{name}(不在该家当前清单里)": "{name} (fora do catálogo atual deste provedor)", "{n} 个": "{n}", + "{n} 个映射": "", "{n} 个模型": "{n} modelos", "{total} 家 · 成功 {done}": "{total} provedores · {done} com sucesso", "{total} 家 · 成功 {done} · 失败 {failed}": "{total} provedores · {done} com sucesso · {failed} com falha", @@ -67,8 +68,10 @@ "上游没有给出可读的错误说明": "O upstream não forneceu uma descrição de erro legível", "上游没有返回任何模型": "O upstream não retornou nenhum modelo", "上游目录尚未拉到,用的是内置静态清单;点「刷新模型清单」可重试": "O catálogo upstream ainda não foi obtido; usando o catálogo estático integrado. Clique em “Atualizar catálogo de modelos” para tentar de novo", + "上游能列出这个模型,但官方客户端不展示它;本仓已默认关闭它的默认绑定(点这一行的开关可以手动打开)": "O upstream lista este modelo, mas o cliente oficial não o exibe; o gateway desativou a vinculação padrão (use o interruptor da linha para ativar manualmente)", "下游客户端": "cliente downstream", "下游请求 ": "Requisição do downstream ", + "不展示": "Não exibido", "不影响网关的转发与路由": "Não afeta o encaminhamento nem o roteamento do Gateway", "不支持": "Não suportado", "不覆盖": "Não sobrescrever", @@ -115,6 +118,7 @@ "刷新全部家(逐家打上游)": "Atualizar todos os provedores (cada um acessa o upstream uma vez)", "刷新完成,但没有得到任何结果": "Atualização concluída, mas nenhum resultado foi obtido", "刷新模型管理页里各提供商的远程模型目录,逐家结果列在弹窗里": "Atualiza o catálogo remoto de modelos de cada provedor na página de gerenciamento de modelos; os resultados por provedor aparecem num diálogo", + "刷新清单": "", "加载中…": "Carregando…", "勾上它,导入后即进入这家的清单": "Marque para que ele entre no catálogo deste provedor após a importação", "勾几个就并行测几次;默认选队列里最靠前的一个。": "Cada conta marcada roda um teste em paralelo; por padrão, a primeira da fila é selecionada.", @@ -126,6 +130,8 @@ "只刷新这一家的模型清单": "Atualizar apenas o catálogo deste provedor", "只属于这一家": "exclusivo deste provedor", "可删除 {n} 个(手动登记 / 自定义家);": "{n} podem ser excluídos (registro manual / provedor personalizado);", + "可见": "Visível", + "可见性": "Visibilidade", "可配思考档位": "Níveis de raciocínio configuráveis", "同一条错误出现在所有账号上": "o mesmo erro apareça em todas as contas", "后端桥不可用": "A ponte do backend não está disponível", @@ -140,6 +146,7 @@ "失败": "Falha", "失败 {n} 个 · 首个失败:{error}": "{n} com falha · primeira falha: {error}", "完成": "Concluir", + "官方客户端会展示这个模型": "O cliente oficial exibe este modelo", "对下游声明的能力(工具调用 / 图片识别 / 视频识别 / 支持思考)—— 点击修改": "Capacidades declaradas ao downstream (chamada de ferramentas / reconhecimento de imagens / reconhecimento de vídeo / suporte a raciocínio) — clique para editar", "对外名可自由命名,允许与上游模型 ID 同名(同名时该上游的原生路由优先,映射作兜底); 支持字母、数字与 ": "O nome externo é livre, podendo até ser igual ao ID do modelo upstream (em caso de nome igual, a rota nativa do upstream tem prioridade e o mapeamento serve de reserva); aceita letras, números e ", "对外映射名(下游请求时用)": "Nome externo do mapeamento (usado nas requisições do downstream)", @@ -183,12 +190,14 @@ "批量操作": "Ações em lote", "批量操作 · 已选 {n} 个模型": "Ações em lote · {n} modelos selecionados", "按倍率排序": "Ordenar por multiplicador", + "按可见性筛选": "Filtrar por visibilidade", "按所选网关对话协议组装请求并校验响应转换(Chat Completions / Responses / Anthropic Messages)。": "Monta a requisição e valida a conversão da resposta usando o protocolo selecionado do gateway (Chat Completions / Responses / Anthropic Messages).", "按提供商选择": "Selecionar por provedor", "按状态筛选": "Filtrar por estado", "提供商": "Provedor", "搜索模型": "Buscar modelos", "搜索模型 ID / 名称 / 映射名…": "Buscar ID / nome / nome de mapeamento do modelo…", + "搜索模型 ID / 提供商": "", "搜索模型 ID…": "Buscar ID do modelo…", "搜索账号…": "Buscar conta…", "操作": "Ação", @@ -197,6 +206,7 @@ "收起 ▴": "Recolher ▴", "数字带虚线底 = 由 models.dev 补齐;右上角小点 = 你手动覆盖过;— = 未声明。点任意一格可改。": "Número com sublinhado pontilhado = preenchido pelo models.dev; ponto no canto = você substituiu; — = não declarado. Clique em qualquer célula para editar.", "新建一个自定义提供商(同时创建它的第一个账号)": "Criar um novo provedor personalizado (criando também sua primeira conta)", + "映射、思考等级、上下文窗口与工具调用能力在桌面端「模型管理」里改;手机端只做启停。": "", "映射「{alias} → {target}」不存在": "O mapeamento “{alias} → {target}” não existe", "映射上未绑定思考等级,「跟随映射」等于这次不注入": "Não há nível de raciocínio vinculado ao mapeamento; “Seguir mapeamento” equivale a não injetar desta vez", "映射上绑定的思考等级是 {level},「跟随映射」按它注入": "O nível de raciocínio vinculado ao mapeamento é {level}; “Seguir mapeamento” injeta conforme ele", @@ -227,6 +237,7 @@ "模型数": "Nº de modelos", "模型映射": "Mapeamento de modelo", "模型来源": "Origem do modelo", + "模型清单已刷新": "", "模型管理表": "Tabela de gerenciamento de modelos", "模型能力": "Capacidades do modelo", "正在刷新…": "Atualizando…", @@ -235,6 +246,7 @@ "每条映射 = 对外名 → 上游模型;原始 ID 与别名有独立开关,默认绑定永远存在(没有映射时是合成的那条)。": "Cada mapeamento = nome externo → modelo upstream; o ID original e os aliases têm interruptores próprios, e o vínculo padrão sempre existe (quando não há mapeamento, é o sintético).", "没有匹配「{keyword}」的模型": "Nenhum modelo corresponde a “{keyword}”", "没有匹配当前筛选的模型": "Nenhum modelo corresponde ao filtro atual", + "没有匹配的模型": "", "没有选中任何模型": "Nenhum modelo selecionado", "没有选中的模型": "Sem modelos selecionados", "流式请求": "Requisição em streaming", @@ -324,8 +336,11 @@ "还没有可刷新的提供商:先在账号页添加一个账号": "Ainda não há provedores para atualizar: adicione uma conta na página de contas primeiro", "还没有自定义提供商": "Ainda não há provedores personalizados", "这一行已不在当前清单里(模型被移除、或目录刷新后上游不再提供它)。关闭后刷新列表再试。": "Esta linha não está mais no catálogo atual (o modelo foi removido ou o upstream não o oferece mais após a atualização do catálogo). Feche e atualize a lista para tentar de novo.", + "这个模型在官方客户端里是否会被展列;不展示的那些默认已关闭(含默认绑定)": "Se o cliente oficial exibe este modelo; os não exibidos ficam desativados por padrão (incluindo a vinculação padrão)", "这个模型已经在这家的清单里,不重复导入": "Este modelo já está no catálogo deste provedor e não será importado de novo", "这几项是给": "Estes itens são a declaração de capacidades que o ", + "这家提供商没有可见性判据(目前只有 Antigravity / Qoder / 小浣熊 / CodeArts 有)": "Este provedor não tem regra de visibilidade (atualmente apenas Antigravity / Qoder / Raccoon / CodeArts)", + "这家有可见性判据,但这一条上游没有表态 —— 按可见处理": "Este provedor tem regra de visibilidade, mas o upstream não se pronunciou sobre esta entrada — tratada como visível", "这家没有可用账号(要在账号页启用一个、且凭证完整)": "Este provedor não tem contas disponíveis (ative uma na página de contas e garanta credenciais completas)", "这家用固定模型清单,无可刷新": "Este provedor usa um catálogo fixo de modelos; não há o que atualizar", "这家的模型清单是全局的,不跟账号走": "O catálogo de modelos deste provedor é global e não depende da conta", diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/proxies.json b/desktop-tauri/i18n/i18n-src/pt-BR/proxies.json index d4886de4..8a82427d 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/proxies.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/proxies.json @@ -3,10 +3,13 @@ " 提供 AI 账号专用的原生静态住宅 IP(Static Residential ISP):IP 来自真实家庭宽带运营商,Scamalytics 低欺诈评分,每个 IP 独享,不限流量,$3.90 起。": " oferece proxies de IP residencial estático nativos para contas de IA (Static Residential ISP): os IPs vêm de operadoras de banda larga residenciais reais, com pontuação de fraude baixa no Scamalytics, cada IP é dedicado a você, com tráfego ilimitado, a partir de $3.90.", " 那一类由「同步 Clash Verge」 镜像进来(进入本页时会自动同步一次):名称、端口、启用都跟随 Clash, 在 Clash 里改或删,这里跟着变 —— 所以它们只读,改请到 Clash Verge 里改。": " Os itens desse tipo são espelhados por “Sincronizar Clash Verge” (uma sincronização automática também é feita ao entrar nesta página): nome, porta e estado de habilitação seguem o Clash; se você alterar ou excluir no Clash, isso se reflete aqui — por isso são somente leitura. Para alterar, faça-o no Clash Verge.", "Clash Verge": "Clash Verge", + "Clash 同步失败:{msg}": "", + "Clash 镜像": "", "YAML 文件": "Arquivo YAML", "{names} 等 {n} 条": "{names} etc. ({n} no total)", "{n} 个出口": "{n} endpoints", "{n} 个账号在用": "{n} contas usando", + "{n} 个账号引用": "", "{n} 条订阅": "{n} assinaturas", "{percent}%({received} / {total} MB)": "{percent}% ({received} / {total} MB)", "✅ {ip}": "✅ {ip}", @@ -29,7 +32,7 @@ "也可以把二维码截图直接粘贴到弹窗里(Ctrl+V)": "Você também pode colar um print do QR code diretamente na janela (Ctrl+V)", "二维码里没有订阅链接": "Nenhum link de assinatura no QR code", "从图片识别二维码": "Ler QR code de uma imagem", - "代理内核未安装(在「订阅管理」里下载内核后,节点会自动分配监听端口)": "O kernel de proxy não está instalado (baixe-o em \"Gerenciar assinaturas\" e os nós recebem portas de escuta automaticamente)", + "代理内核未安装(在「订阅管理」里下载内核后,被账号引用的节点会自动开监听端口)": "O kernel do proxy não está instalado (após baixá-lo em \"Gerenciamento de assinaturas\", os nós referenciados por contas recebem uma porta de escuta automaticamente)", "代理列表加载失败:{reason}": "Falha ao carregar a lista de proxies: {reason}", "代理池": "Pool de proxies", "代理池表": "Tabela do pool de proxies", @@ -67,6 +70,8 @@ "内核没有在超时内停下来": "o kernel não parou dentro do tempo limite", "内核目录没能删掉(可能仍被进程占用),稍后重试一次": "Não foi possível remover a pasta do kernel — algum processo ainda pode estar usando-a. Tente novamente em instantes.", "内核运行中": "Kernel em execução", + "出口 IP {ip}": "", + "出口可用": "", "出口测试网址": "URL do teste de conectividade", "删除代理": "Excluir proxy", "删除内核": "Excluir kernel", @@ -81,6 +86,7 @@ "只读:在 Clash Verge 里改": "Somente leitura: edite no Clash Verge", "只读:在「订阅管理」里改": "Somente leitura: edite em \"Gerenciar assinaturas\"", "同步 Clash Verge": "Sincronizar Clash Verge", + "同步 Clash Verge 出口": "", "同步中…": "Sincronizando…", "同步失败:{reason}": "Falha na sincronização: {reason}", "名字与端口在 Clash Verge 里改;启用可在本页关闭": "Edite o nome e a porta no Clash Verge; o interruptor de ativação pode ser desligado nesta página", @@ -94,6 +100,7 @@ "启用(禁用的订阅不参与内核同步,它的节点会从代理池移除)": "Ativada (assinaturas desativadas não são sincronizadas pelo kernel e seus nós são removidos do pool de proxies)", "图片读取失败": "Falha ao ler a imagem", "地址": "Endereço", + "地址不完整": "Endereço incompleto", "失败 {n} 个({reason})": "{n} com falha ({reason})", "存放位置": "Pasta", "存放位置 {dir}": "Pasta {dir}", @@ -106,13 +113,16 @@ "已删除 {n} 个代理": "{n} proxies excluídos", "已删除「{name}」": "“{name}” excluído", "已删除内核(订阅与账号引用保留,重新下载后会重新分配端口)": "Kernel excluído (as assinaturas e as referências das contas são mantidas; as portas são redistribuídas depois de baixar novamente)", + "已删除内核(订阅与账号引用保留;有文件仍被占用,将在下次启动时清理)": "Kernel excluído (as assinaturas e as referências de conta são mantidas; alguns arquivos ainda estão em uso e serão limpos na próxima inicialização)", "已刷新「{name}」,正在同步节点…": "\"{name}\" atualizada; sincronizando nós…", + "已同步 {n} 项变更": "", "已启用「{name}」": "“{name}” habilitado", "已启用「{name}」,正在补建监听端口(内核重建中)": "\"{name}\" ativado; recriando a porta de escuta (kernel reiniciando)", "已在本页禁用(打开开关即恢复)": "Desativado nesta página (ligue o interruptor para restaurar)", "已新增「{name}」": "“{name}” adicionado", "已新增订阅「{name}」,内核正在同步节点…": "Assinatura \"{name}\" adicionada; o kernel está sincronizando os nós…", "已是最新,没有需要同步的变更": "Já está atualizado; não há alterações para sincronizar", + "已有账号引用它,但内核还没把监听端口补建出来 —— 稍等片刻,或重新启用这条节点": "Uma conta já referencia este nó, mas o kernel ainda não criou a porta de escuta — aguarde um instante ou reative este nó", "已禁用「{name}」": "“{name}” desabilitado", "已禁用「{name}」,监听端口正在关闭(内核重建中)": "\"{name}\" desativado; a porta de escuta está sendo fechada (kernel reiniciando)", "已禁用(未开监听端口)": "Desativado (sem porta de escuta)", @@ -128,6 +138,7 @@ "当前环境不支持订阅(桥接方法缺失)": "Assinaturas não são suportadas neste ambiente (método de ponte ausente)", "当前环境不支持选择文件夹,直接填写绝对路径即可": "Este ambiente não abre seletor de pastas - digite o caminho absoluto", "当前线路不可用:{error};重新选一条或切回直连": "A rota atual está indisponível: {error}. Escolha outra ou volte para conexão direta", + "所属订阅已删除,这条节点不会再分配端口 —— 可以删除它": "A assinatura deste nó foi excluída, então ele nunca receberá uma porta — você pode excluí-lo", "手动填写的地址与端口": "Endereço e porta preenchidos manualmente", "打开文件夹选择框失败:{reason}": "Falha ao abrir o seletor de pastas: {reason}", "批量删除": "Excluir em lote", @@ -143,6 +154,7 @@ "搜索节点": "Buscar nós", "文件读取失败": "Falha ao ler o arquivo", "新增代理": "Adicionar proxy", + "新增出口、编辑地址与订阅管理在桌面端完成;手机端做启停与连通性测试。": "", "新增订阅": "Nova assinatura", "显示 {n} / 共 {total} 个出口": "Mostrando {n} / {total} endpoints", "更新中…": "Atualizando…", @@ -155,6 +167,7 @@ "本地文件不会自动更新:要更新节点,重新选择文件后保存;不重选则内容保持不变。": "Arquivos locais não são atualizados automaticamente: para atualizar os nós, escolha o arquivo novamente e salve; sem reescolher, o conteúdo permanece o mesmo.", "本地文件订阅无需更新(要换内容请编辑后重新选择文件)": "Assinatura de arquivo local não precisa de atualização (para mudar o conteúdo, edite e escolha o arquivo novamente)", "本地文件订阅的内容在导入时固定:节点会由内核起成本地监听器并镜像进代理池,节点数稍等几秒由轮询刷新。": "O conteúdo de uma assinatura de arquivo local é fixado na importação: o kernel inicia os nós como listeners locais e os espelha no pool de proxies; a contagem de nós é atualizada por polling em alguns segundos.", + "本机没有读到 Clash Verge 配置": "", "本页可编辑、可删除": "Editável e removível nesta página", "来自 Clash Verge 同步(名称、端口跟随 Clash),请到 Clash Verge 中修改;启用可在本页关闭": "Sincronizado do Clash Verge (nome e porta seguem o Clash); edite-os no Clash Verge. O interruptor de ativação pode ser desligado nesta página.", "正在下载内核": "Baixando o kernel", @@ -172,7 +185,7 @@ "测试网址要以 http:// 或 https:// 开头": "A URL de teste deve começar com http:// ou https://", "确定删除「{name}」?{used}": "Excluir “{name}”?{used}", "确定删除「{name}」?它在代理池里的节点会一并移除,引用这些节点的账号会回退直连。": "Excluir \"{name}\"? Seus nós no pool de proxies também serão removidos, e as contas que os referenciam voltarão à conexão direta.", - "确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会变成「未分配端口」,引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。": "Excluir o kernel baixado{version}? Os arquivos do kernel e o mapeamento de portas serão removidos, os nós da assinatura passam a \"sem porta atribuída\", e as contas que os referenciam voltam para conexão direta por enquanto. As assinaturas são mantidas, e as portas são redistribuídas depois de baixar o kernel novamente.", + "确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会失去监听端口(地址列显示「端口准备中」),引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。": "Excluir o kernel baixado{version}? Os arquivos do kernel e o mapeamento de portas serão removidos, os nós da assinatura perdem as portas de escuta (a coluna de endereço mostra \"Porta em preparação\"), e as contas que os referenciam voltam para conexão direta por enquanto. As assinaturas são mantidas e as portas são redistribuídas depois de baixar o kernel novamente.", "确定删除选中的 {n} 个代理?": "Excluir os {n} proxies selecionados?", "禁用后本端不再用它(引用它的账号回退直连),并会关掉它的本地监听端口": "Ao desativar, esta máquina deixa de usá-lo (contas que o referenciam voltam à conexão direta) e a porta de escuta local é fechada", "禁用后本端不再用它(引用它的账号回退直连);端口在 Clash Verge 侧,不受影响": "Ao desativar, esta máquina deixa de usá-lo (contas que o referenciam voltam à conexão direta); a porta permanece no lado do Clash Verge, sem alterações", @@ -192,6 +205,7 @@ "订阅来源": "Origem da assinatura", "订阅管理": "Gerenciar assinaturas", "订阅管理({n})": "Gerenciar assinaturas ({n})", + "订阅节点": "", "订阅节点会在本机各占一个监听端口,从该范围里挑空闲的分配;节点多的大订阅把默认范围({base}-{end})用尽时会报「没有可用的节点端口」,这时把范围调大再重启内核。已分配出去的端口不会因改范围而失效。": "Cada nó de assinatura ocupa uma porta de escuta local, alocada entre as portas livres desta faixa; quando uma assinatura grande esgota a faixa padrão ({base}-{end}), o app informa \"sem portas de nó livres\" — aumente a faixa e reinicie o kernel. Portas já alocadas continuam válidas quando a faixa muda.", "订阅里的节点会自动同步进代理池,账号即可引用。": "Os nós da assinatura são sincronizados automaticamente no pool de proxies, prontos para as contas usarem.", "订阅里的节点会被内核起成本地监听器,镜像到代理池(账号按 id 引用,与本页其它出口同一用法)。内核只在用到订阅时按需下载(约 20-40MB),不随安装包分发。": "Os nós das assinaturas são iniciados como listeners locais pelo kernel e espelhados no pool de proxies (as contas os referenciam por id, igual a qualquer outro endpoint desta página). O kernel só é baixado sob demanda quando há assinaturas em uso (~20-40MB) e não vem com o instalador.", @@ -207,8 +221,9 @@ "跳过 {n} 个(同步只读条目)": "{n} ignorados (entradas somente leitura sincronizadas)", "还没有代理。点右上角「+ 新增代理」手填一个,或在「订阅管理」里导入订阅节点、同步 Clash Verge 的出口。": "Ainda não há proxies. Clique em \"+ Adicionar proxy\" no canto superior direito para preencher manualmente, ou importe nós de assinatura / sincronize os endpoints do Clash Verge em \"Gerenciar assinaturas\".", "还没有代理。点右上角「+ 新增代理」手填一个,或点「同步 Clash Verge」把 Clash 的出口导进来。": "Ainda não há proxies. Clique em “+ Adicionar proxy” no canto superior direito para preencher um manualmente, ou em “Sincronizar Clash Verge” para importar os endpoints do Clash.", - "还没有监听端口(还没有账号引用它 —— 在账号里选它并保存后,内核会自动开端口)": "Ainda sem porta de escuta (nenhuma conta o referencia — selecione-o em uma conta e salve, o kernel abre a porta automaticamente)", + "还没有出口。在桌面端「网络代理」页新增,或点上面同步 Clash。": "", "还没有订阅:粘贴订阅链接、导入本地 YAML 文件,或从图片识别二维码。": "Nenhuma assinatura ainda: cole um link de assinatura, importe um arquivo YAML local ou leia um QR code de uma imagem.", + "还没有账号使用它(端口只在被账号引用时才分配 —— 在账号里选它并保存后,内核会自动开端口)": "Nenhuma conta usa este nó ainda (a porta só é atribuída quando uma conta referencia o nó — selecione-o em uma conta e salve, e o kernel abrirá a porta automaticamente)", "这条在 Clash Verge 里是停用的,本页启用不生效 —— 到 Clash Verge 里打开它": "Este item está desativado no Clash Verge, então ativá-lo aqui não tem efeito — ligue-o no Clash Verge", "连接成功": "Conectado", "连通性": "Conectividade", @@ -220,6 +235,7 @@ "部分测试结果没能记下:{reason}": "Alguns resultados de teste não puderam ser registrados: {reason}", "重启内核": "Reiniciar kernel", "重新选择文件…": "Escolher outro arquivo…", + "镜像当前出口集合,名称与端口跟随 Clash": "", "需要干净的出口?": "Precisa de uma saída limpa?", "首次购买使用优惠码 AGENT2API25 享 75 折。": "Use o código AGENT2API25 para 25% de desconto na primeira compra.", "+ 新增代理": "+ Adicionar proxy", diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/report.json b/desktop-tauri/i18n/i18n-src/pt-BR/report.json index 1a651f90..b88ba588 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/report.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/report.json @@ -1,6 +1,7 @@ { "30 天": "30 dias", "7 天": "7 dias", + "Key": "", "Token": "Token", "Top 提供商": "Top provedores", "Top 模型": "Top modelos", @@ -12,6 +13,11 @@ "{m}月{d}日 周{wd}": "{d}/{m} ({wd})", "{name} · {tokens} tokens · {n} 次请求 · {pct}": "{name} · {tokens} tokens · {n} requisições · {pct}", "{name}:{tokens} tokens · 占 {pct}%": "{name}: {tokens} tokens · {pct}% do total", + "{n} 个账号启用 · {m} 个限流中": "", + "{n} 个账号正在限流,转发会自动跳过它们": "", + "{n} 天": "", + "{n} 次请求": "", + "{start} ~ {end}": "", "{tokens} tokens · 占 {pct}": "{tokens} tokens · {pct} do total", "…还有 {n} 个模型": "…e mais {n} modelos", "一": "S", @@ -19,14 +25,21 @@ "二": "T", "五": "S", "今天": "Hoje", + "今天都签完了": "", + "今日签到 {done} / {total}": "", + "今日签到 {done}/{total}": "", + "今日请求": "", "六": "S", "区间共 {n} 天": "{n} dias no período", + "区间内累计": "", + "可用账号": "", "右轴": "Eixo direito", "含今天在内往前数": "Contando para trás, incluindo hoje", "命中 {hit} / 输入 {input}": "Acertos {hit} / entrada {input}", "命中率 = 缓存读取 Token / 输入 Token(promptTokens)。窗口越短越贴近「此刻」:上游把缓存读取与输入分开上报时,该比值合法地可能超过 100%,此处不夹取值、如实展示。": "Taxa de acerto = Token lidos do cache / Token de entrada (promptTokens). Quanto menor a janela, mais próximo de “agora”: quando o upstream reporta leituras de cache e entrada separadamente, essa razão pode legalmente passar de 100%; aqui o valor não é limitado e é exibido como está.", "命中率 {rate}(命中 {hit} / 输入 {input})": "Taxa de acerto {rate} ({hit} acertos / {input} de entrada)", "四": "Q", + "失败 {n} 次": "", "左轴": "Eixo esquerdo", "开始日期": "Data inicial", "当前连续天数": "Sequência atual", @@ -46,32 +59,49 @@ "所选范围内还没有请求记录": "Ainda não há registros de requisições no período selecionado", "所选范围内还没有账号用量": "Ainda não há uso por conta no período selecionado", "报表时间范围": "Período do relatório", + "报表视角": "", + "按天 Token 用量": "", "按天 Token 趋势": "Tendência diária de Token", "按本地整点,无请求的整点不标注": "Por hora local exata; horas sem requisições não são rotuladas", "提供商用": "Uso por provedor", "提供商用占比": "Proporção de uso por provedor", + "提供商用量": "", "提供商:{text}": "Provedor: {text}", "方格为本地自然日,无请求显示为空槽": "Cada célula é um dia local; sem requisições, ela aparece vazia", "无请求的整点按 0% 计,但不写读数": "Horas sem requisições contam como 0%, mas sem exibir o valor", "日": "D", + "时间范围。「今天」是本地自然日今天 0 点到现在;「昨天」是昨天一整天(0 点到 24 点);「近 7 天 / 近 30 天」含今天在内往前数,即今天与之前的 6 / 29 天,不是「不含今天的 7 / 30 天」。": "Período. \"Hoje\" é o dia local de 00:00 até agora; \"Ontem\" é o dia de ontem inteiro (00:00–24:00); \"Últimos 7 dias\" / \"Últimos 30 dias\" contam para trás incluindo hoje, ou seja, hoje mais os 6 / 29 dias anteriores — não 7 / 30 dias excluindo hoje.", + "昨天": "Ontem", "暂无热力图数据": "Ainda não há dados do mapa de calor", "暂无缓存趋势数据": "Ainda não há dados de tendência de cache", "暂无趋势数据": "Ainda não há dados de tendência", + "最近一次请求使用的模型": "", + "最近请求": "", + "最近请求 {n} 条": "", "未知模型": "Modelo desconhecido", + "未配置上游": "", "本月": "Este mês", "模型用量": "Uso por modelo", "模型用量占比": "Proporção de uso por modelo", "每天的总 Token 用量(输入 + 输出)。柱顶标出当天的读数;区间很长、柱子挤到放不下时只标最高的那几天,其余悬停柱子即可读到。": "Uso total de Token por dia (entrada + saída). O valor do dia é marcado no topo da barra; quando o período é longo e as barras ficam sem espaço, apenas os dias mais altos são rotulados — para os demais, basta passar o mouse sobre a barra para ler o valor.", + "活跃": "", "活跃天数": "Dias ativos", "活跃热力图": "Mapa de calor de atividade", + "管理": "", + "累计请求 {n} 条": "{n} requisições no total", "结束日期": "Data final", "结束日期(含当天)": "Data final (inclusive)", + "统计区间加载中": "", "统计概览": "Visão geral", "缓存命中率": "Taxa de acerto de cache", "自定义区间": "Período personalizado", + "请求量": "", "读取报表失败:{error}": "Falha ao ler o relatório: {error}", + "账号分布": "", + "账号用量": "", "起始日期(含当天)": "Data inicial (inclusive)", "输入 + 输出": "Entrada + saída", + "运行中": "", "近 1 小时": "Última hora", "近 10 分钟": "Últimos 10 minutos", "近 24 小时": "Últimas 24 horas", @@ -80,5 +110,9 @@ "近 30 天": "Últimos 30 dias", "近 365 天活跃热力图": "Mapa de calor de atividade dos últimos 365 dias", "近 365 天,按天 Token 用量着色(档位按本窗口峰值自动定)": "Últimos 365 dias, coloridos pelo uso diário de Token (as faixas são definidas automaticamente pelo pico desta janela)", - "近 7 天": "Últimos 7 dias" + "近 7 天": "Últimos 7 dias", + "还有 {n} 个待签": "", + "还没有添加账号": "", + "还没有请求记录": "", + "连续天数": "" } diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/requests.json b/desktop-tauri/i18n/i18n-src/pt-BR/requests.json index 49ee5c8a..3a9aea8d 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/requests.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/requests.json @@ -41,6 +41,7 @@ "切换路径:": "Caminho de troca: ", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "Os dados da lista foram atualizados; tente novamente (feche e reabra este detalhe)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "Exclui os próprios registros de log. Com filtros ativos, exclui apenas os itens correspondentes e os relatórios agregados por dia não são afetados; sem filtros, limpa tudo e redefine os relatórios. Esta ação não pode ser desfeita.", + "加载更早的请求": "", "压缩中…": "Compactando…", "压缩失败:{message}": "Falha na compactação: {message}", "压缩已在后台开始,完成后会自动提示": "A compactação começou em segundo plano; você será avisado quando ela terminar", @@ -49,6 +50,7 @@ "压缩数据库": "Compactar banco de dados", "压缩正在后台执行,完成后自动恢复": "A compactação está em execução em segundo plano; tudo volta ao normal automaticamente quando ela terminar", "原始报文": "Payload bruto", + "发给上游的模型": "", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "Mais {missing} tentativas sem detalhes registrados (mantidas apenas as {kept} primeiras)", "另有 {n} 个词命中": "Mais {n} palavras atingidas", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "Mostra apenas requisições em encaminhamento (a coluna Status mostra a fase: Conectando / Aguardando resposta / Respondendo / Retentando; a coluna Duração mostra o tempo decorrido)", @@ -62,14 +64,18 @@ "响应头": "Cabeçalhos da resposta", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "Recupera o espaço em disco ocupado por dados excluídos (checkpoint + VACUUM, em segundo plano)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "Ative o “Modo debug” em Configurações → Geral para que novas requisições passem a salvar o payload", + "在途 {n}": "", "失败请求不记录用量": "Requisições com falha não registram uso", "失败({status}):{error}": "Falhou ({status}): {error}", "失败:{error}": "Falhou: {error}", "实际发给上游 {bytes} 字节": "{bytes} bytes enviados de fato ao upstream", "客户端": "Cliente", + "客户端请求的模型": "", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "Serão excluídos {count} logs · {raw} ainda com payload · tamanho do banco: {db}", "尝试 {n} ·": "Tentativa {n} ·", "尝试明细": "Detalhes das tentativas", + "尝试次数": "", + "尝试链": "", "工具结果": "Resultado da ferramenta", "工具结果 · {id}": "Resultado da ferramenta · {id}", "工具结果图片": "Imagem do resultado da ferramenta", @@ -77,6 +83,7 @@ "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "Abortagem aceita: a conexão com o upstream foi encerrada; a lista será atualizada em instantes como “Requisição abortada manualmente”", "已抹掉 {count} 条报文原文": "{count} payloads removidos", "已用时(请求仍在转发中)": "Tempo decorrido (a requisição ainda está sendo encaminhada)", + "当前筛选下没有记录": "", "成功": "Sucesso", "成功({status})": "Sucesso ({status})", "执行清理": "Executar limpeza", @@ -88,6 +95,7 @@ "按模型筛选": "Filtrar por modelo", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "Filtrar por modelo (lista apenas os que aparecem nos logs; correspondência exata com o nome do modelo)", "按请求结果筛选": "Filtrar pelo resultado da requisição", + "换号链:{chain}": "", "排队中": "Na fila", "提供商 / 账号": "Provedor / Conta", "提供商: {provider}": "Provedor: {provider}", @@ -98,12 +106,15 @@ "无报文原文(未开启「调试模式」,或已被清理)": "Sem payload original (o modo de depuração estava desativado, ou o conteúdo foi limpo)", "无结果记录": "Sem resultado registrado", "时间": "Data/hora", + "暂无请求日志": "", "暂无请求日志,网关还没有转发过请求": "Ainda não há logs de requisições; o Gateway ainda não encaminhou nenhuma requisição", "最终承载:{picked}": "Atendida por fim por: {picked}", + "有重试": "", "本轮尝试失败,正在退避等待或切换到下一个账号": "Esta rodada falhou; aguardando o backoff ou trocando para a próxima conta", "来源客户端:{client}": "Cliente de origem: {client}", "查看该请求的上游原始报文": "Ver o payload bruto do upstream desta requisição", "模型": "Modelo", + "模型映射:{from} → {to}": "", "模型测试": "Teste de modelo", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "Requisição iniciada pelo “Teste” na coluna de ações da página de gerenciamento de modelos: usa a mesma rota de encaminhamento das requisições reais, mas não entra nas estatísticas dos relatórios", "正在加载请求日志…": "Carregando logs de requisições…", @@ -130,6 +141,7 @@ "缓存读取: - / 命中率: -": "Leituras de cache: - / taxa de acerto: -", "缓存读取: {cache} / 命中率: {rate}": "Leituras de cache: {cache} / taxa de acerto: {rate}", "耗时": "Duração", + "脱敏命中:{words}": "", "被跳过:": "Puladas: ", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent detectado, mas não é um tipo de cliente conhecido: {ua}", "详情": "Detalhes", @@ -148,7 +160,9 @@ "路由回放": "Replay de roteamento", "转发到上游的模型:{model}": "Modelo encaminhado ao upstream: {model}", "轮次": "Rodada", + "输入 Token": "", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "Entrada {input} · Saída {output} · Total {total} · Leitura de cache {cache}", + "输出 Token": "", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "Este detalhe é um teste de modelo (iniciado manualmente): usa a mesma rota de encaminhamento das requisições reais, mas não entra nas estatísticas dos relatórios", "这条明细没有记录提供商(旧数据或请求未走到转发)": "Este detalhe não registrou provedor (dados antigos ou a requisição falhou antes do encaminhamento)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "Nenhum cliente de origem registrado nesta entrada (dados antigos, teste de modelo ou solicitação sem User-Agent)", @@ -160,11 +174,13 @@ "进行中…": "Em andamento…", "连接中": "Conectando", "重试 {n}": "Retry {n}", + "重试 {n} 次": "", "重试中": "Retentando", "重试次数": "Retries", "预览对话": "Prévia da conversa", "预览读取失败": "Falha ao ler a prévia", "预览读取失败:{error}": "Falha ao ler a prévia: {error}", + "首响": "", "首响 {time}": "1ª resposta {time}", "首响:上游首帧到达的耗时": "1ª resposta: tempo até o primeiro frame do upstream chegar", "首响:上游首帧到达的耗时(请求仍在转发中)": "1ª resposta: tempo até o primeiro frame do upstream chegar (a requisição ainda está sendo encaminhada)", diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/settings.json b/desktop-tauri/i18n/i18n-src/pt-BR/settings.json index b3355ec4..a3f64f74 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/settings.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/settings.json @@ -2,30 +2,58 @@ " 到 {file}": " para {file}", " 正文已改:{edited}(其余段用官方原文)。": " Texto alterado: {edited} (os demais blocos usam o texto oficial).", "(无 id)": "(sem id)", + "/v1 拒绝无 Key 请求": "/v1 rejeita requisições sem chave", "0 B": "0 B", "100%(默认)": "100% (padrão)", "<本机IP>": "", + "Access Key ID": "Access Key ID", + "Agent2API 手机端": "Agent2API Mobile", + "Android 应用(APK)": "Aplicativo Android (APK)", "Cline 上游按请求头判定「调用方是不是 Cline 自家产品」:X-CLIENT-TYPE: cline-sdk 是硬门槛(缺了它免费池模型一律 403),版本号与平台标识让请求更贴近官方客户端的形态。这里列出的是网关默认会发的头:改某一行的值 = 覆盖默认值(也可以新增默认清单之外的自定义头);把值清空保存 = 这个头不发送;在界面删掉被改过的行 = 回落默认值。改完点「保存」,下一个 Cline 请求立即生效。不懂某一行的含义就不要动它 —— 伪装头的价值在「与官方客户端逐字一致」。": "O upstream da Cline decide pelos cabeçalhos da requisição se o chamador é um produto da própria Cline: X-CLIENT-TYPE: cline-sdk é um requisito obrigatório (sem ele, os modelos do pool gratuito retornam 403 sempre), e o número de versão e o identificador de plataforma aproximam a requisição do formato do cliente oficial. Os cabeçalhos listados aqui são os que o gateway envia por padrão: alterar o valor de uma linha = sobrescrever o padrão (também é possível adicionar cabeçalhos personalizados além da lista padrão); salvar com o valor vazio = esse cabeçalho não é enviado; excluir na interface uma linha alterada = voltar ao padrão. Ao terminar, clique em “Salvar” e a próxima requisição da Cline já usará as mudanças. Se você não entende o significado de uma linha, não a altere — o valor dos cabeçalhos spoofed está em ser idêntico palavra por palavra ao cliente oficial.", "Cline 伪装头": "Cabeçalhos spoofed do Cline", "Cline 伪装头已刷新": "Cabeçalhos spoofed do Cline atualizados", + "Docker 容器": "Contêiner Docker", + "Endpoint": "Endpoint", + "Endpoint 填 https://<账户ID>.r2.cloudflarestorage.com,Region 固定填 auto。": "Defina o Endpoint como https://.r2.cloudflarestorage.com e a Region como auto.", + "Endpoint 填 https://cos.ap-guangzhou.myqcloud.com,Region 填 ap-guangzhou(按实际地域改)。": "Defina o Endpoint como https://cos.ap-guangzhou.myqcloud.com e a Region como ap-guangzhou (ajuste para a sua região).", + "Endpoint 填 https://obs.cn-north-4.myhuaweicloud.com,Region 填 cn-north-4(按实际地域改)。": "Defina o Endpoint como https://obs.cn-north-4.myhuaweicloud.com e a Region como cn-north-4 (ajuste para a sua região).", + "Endpoint 填 https://oss-cn-hangzhou.aliyuncs.com,Region 填 cn-hangzhou(按实际地域改)。": "Defina o Endpoint como https://oss-cn-hangzhou.aliyuncs.com e a Region como cn-hangzhou (ajuste para a sua região).", + "Endpoint 形如 http://192.168.1.10:9000(自建通常用 http);Region 一般填 us-east-1。": "O Endpoint tem o formato http://192.168.1.10:9000 (auto-hospedado geralmente usa http); a Region costuma ser us-east-1.", + "Endpoint 留空即走 AWS 官方地址;Region 必须与桶所在区域一致。": "Deixe o Endpoint vazio para usar o endereço oficial da AWS; a Region deve corresponder à região do bucket.", + "Linux 安装包(deb)": "Pacote Linux (deb)", + "Nextcloud": "Nextcloud", "OpenAI 新版 API 用 developer 角色取代 system 承载指令,新版 OpenAI SDK 与部分客户端发的对话里会出现 role 为 developer 的消息;不少 OpenAI 兼容上游(尤其是自定义提供商的透传路径)不认识这个角色,收到就报 400。开启本项后,网关在每次转发前把 developer 角色的消息改写为 system —— 两类角色在 OpenAI 语义上等价(OpenAI 自己的迁移指南也是这么映射的),改写不影响对话内容与语义;对本来就只认 system 的各家内置上游同样无害。默认开启;关掉后 developer 消息会原样发往上游,不认识它的上游会报「角色不存在」一类的错误。": "A API mais recente da OpenAI carrega instruções num papel developer em vez de system, então conversas enviadas por SDKs OpenAI atuais e alguns clientes contêm mensagens com role \"developer\". Muitos upstreams compatíveis com OpenAI (especialmente o caminho de passagem dos provedores personalizados) não reconhecem esse papel e rejeitam a solicitação inteira com 400. Quando ativado, o gateway reescreve mensagens do papel developer para system antes de cada encaminhamento — os dois papéis são equivalentes na semântica da OpenAI (o próprio guia de migração da OpenAI faz o mesmo mapeamento), então a reescrita não altera conteúdo nem significado, e é inofensiva aos upstreams integrados que só aceitam system. Ativado por padrão; desativado, as mensagens developer vão para o upstream como estão e upstreams que não conhecem o papel retornam erros do tipo \"papel inexistente\".", + "QQ 群 · Telegram · Discord": "Grupos do QQ · Telegram · Discord", + "S3 兼容存储": "Armazenamento compatível com S3", + "Safari 没有安装按钮:点底部的「分享」,选「添加到主屏幕」,再点「添加」。": "O Safari não tem botão de instalar: toque em Compartilhar na parte de baixo, escolha \"Adicionar à Tela de Início\" e toque em \"Adicionar\".", + "Secret Access Key": "Secret Access Key", "Token 读数量级": "Escala de leitura de Token", "UTF-8 文本文件路径。留空用内置默认;只在「替换 / 追加」下读取。": "", "UTF-8 文本文件路径,留空用内置默认提示词(通用编码助手提示词,不含任何会被上游拦截的模板句)。只在「替换 / 追加」模式下生效,透传模式不读它。": "Caminho de um arquivo de texto UTF-8; deixe vazio para usar o system prompt padrão integrado (um system prompt genérico de assistente de código, sem nenhuma frase de template que o upstream bloqueie). Só tem efeito nos modos “Substituir / Anexar”; o modo Passthrough não o lê.", + "WebView / 浏览器内核": "WebView / motor do navegador", + "Windows 安装包(NSIS)": "Instalador do Windows (NSIS)", + "Windows 服务": "Serviço do Windows", "ZCode 套餐池(Start Plan)通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。": "Cada solicitação no canal do pool de planos do ZCode (Start Plan) consome um token de CAPTCHA recém-cunhado (de uso único, expira em 2 minutos); quando o estoque cair abaixo deste valor, a interface cunha mais até ele. Padrão 3. Um valor maior absorve picos, mas tokens não usados expiram em 2 minutos e queimam a cota de controle de risco do upstream. Aceita 1-10.", "ZCode 活动套餐通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。": "", "ZCode「套餐池(Start Plan)」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。": "Cada solicitação de inferência no canal \"Pool de planos (Start Plan)\" do ZCode deve carregar um token invisível de CAPTCHA da Aliyun recém-cunhado (de uso único, expira em cerca de 2 minutos): os tokens são cunhados silenciosamente pelo guardião de cunhagem na interface e empurrados para o pool do gateway, de onde o encaminhamento toma um por solicitação. Os três valores aqui governam o ritmo de reposição: ① \"Meta de estoque\" = o guardião só cunha mais quando o estoque cai abaixo dela, até ela (padrão 3); ② \"Intervalo de sondagem\" = com que frequência o guardião verifica o estoque, padrão 2500 ms (afeta apenas a velocidade de reação — não cunha além quando o estoque basta); ③ \"Limite de cunhagem por rodada\" = o máximo de tokens cunhados em uma rodada ao detectar falta, padrão 1 (cunhagens consecutivas são fixadas a 500 ms). Quando as solicitações forem densas e o registro mostrar 503 \"precisa de token de CAPTCHA\" em sequência, aumente a meta e o limite por rodada e acelere a sondagem para o pool acompanhar — a contrapartida é que o upstream (Aliyun) limita a frequência de cunhagem: quanto mais e mais rápido cunhar, maior a chance de esbarrar no limite, e tokens não usados expiram em 2 minutos de qualquer forma, queimando cota à toa — ajuste em passos pequenos. Salvar entra em vigor na próxima rodada de reposição, sem reinício.", "ZCode「活动套餐」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。": "", + "beta 版": "", + "iOS 应用": "Aplicativo iOS", + "models.dev 元数据": "Metadados models.dev", "models.dev 元数据已更新({count} 条)": "Metadados do models.dev atualizados ({count} entradas)", "models.dev 是一个开源的模型资料库(社区维护),这里用它补齐「上游没告诉我们」的模型能力:上下文窗口、最大输出、工具调用、图片、视频、是否支持思考,以及这个模型认识哪些思考档位。优先级是:你在「模型能力」里手工改的值 > 上游清单自己声明的值 > models.dev 的值 —— 它只填上游没声明的空位,从不推翻上游已声明的。网关随包内嵌了一份快照,所以离线、首次启动、访问不通时也有数据;开启时会按「模型目录刷新」任务的间隔联网更新(同一轮里一并刷新,也可在这里立即刷新)。关闭只停联网刷新,已经拿到的数据继续用。": "O models.dev é um banco de dados de modelos aberto e mantido pela comunidade. Ele preenche o que o upstream não informa: janela de contexto, saída máxima, chamada de ferramentas, imagem, vídeo, suporte a raciocínio e quais níveis de raciocínio o modelo realmente aceita. Prioridade: valores que você editou em \"Capacidades do modelo\" > o que o catálogo do upstream declara > models.dev — ele só preenche lacunas deixadas pelo upstream e nunca sobrepõe um valor declarado. Um snapshot vem embutido no app, então offline, primeira execução e rede bloqueada continuam tendo dados; quando ativado, atualiza online no intervalo da tarefa \"Atualização do catálogo de modelos\" (na mesma rodada, ou sob demanda aqui). Desativar apenas interrompe a atualização online; os dados já obtidos continuam em uso.", "models.dev 模型元数据": "Metadados de modelos do models.dev", + "systemd 服务": "Serviço systemd", "{count} 条": "{count} entradas", "{count} 条 · 来自{origin}{at} · {mode}": "{count} entradas · de {origin}{at} · {mode}", + "{d} 天 {h} 小时": "{d}d {h}h", + "{h} 小时 {m} 分钟": "{h}h {m}min", "{label} 提示词文件": "Arquivo de prompt de {label}", "{label} 的提示词模式": "Modo de prompt de {label}", "{label}({message})": "{label} ({message})", "{lines} 行、{chars} 字符(按当前草稿计)": "{lines} linhas, {chars} caracteres (com base no rascunho atual)", "{mode}生效:上游收到的 system 来自{source}{lines}。": "Modo {mode} ativo: o system enviado ao upstream vem de {source}{lines}.", + "{m} 分钟": "{m}min", "{noun}不能为空(可填 {min}–{max})": "{noun}: não pode ficar em branco (valores aceitos: {min}–{max})", "{noun}必须在 {min}–{max} 之间(当前填的是 {text})": "{noun}: deve estar entre {min} e {max} (valor atual: {text})", "{noun}必须是整数": "{noun}: deve ser um número inteiro", @@ -34,22 +62,33 @@ "{n} KB": "{n} KB", "{n} MB": "{n} MB", "{n} 个头将随每个 Cline 请求发送": "{n} cabeçalhos serão enviados em cada requisição da Cline", + "{n} 台可选 · 点此切换": "{n} servidores · toque para trocar", + "{n} 项 · {shown} 显示": "{n} itens · {shown} visíveis", + "{total} 个菜单 · 显示 {shown} · 隐藏 {hidden} · {groups} 个分组": "{total} menus · {shown} visíveis · {hidden} ocultos · {groups} grupos", + "…": "", "⚠️ 密钥警告": "⚠️ Aviso de chaves", + "⚠️ 已关闭「{label}」的机器人校验": "⚠️ Verificação de bot desativada para \"{label}\"", "⚠️ 机器人校验已关闭": "⚠️ Captcha desativado", "✅ Cline 伪装头已保存": "✅ Cabeçalhos spoofed do Cline salvos", "✅ models.dev 模型元数据已开启": "✅ Metadados de modelos do models.dev ativados", + "✅ 云同步设置已保存": "✅ Configurações de sincronização na nuvem salvas", "✅ 出站指纹脱敏已开启": "✅ Mascaramento de fingerprint de saída ativado", + "✅ 密码已修改,其他设备的登录已失效": "✅ Senha alterada. Os acessos em outros dispositivos foram revogados", "✅ 导入完成:{summary}": "✅ Importação concluída: {summary}", + "✅ 已上传到云端({size})": "✅ Enviado para a nuvem ({size})", "✅ 已为「{label}」装上网关自带提示词": "✅ System prompt interno do gateway instalado para “{label}”", + "✅ 已从云端恢复{note}": "✅ Restaurado da nuvem{note}", "✅ 已保存:{label}": "✅ Salvo: {label}", "✅ 已保存:{label} {value}": "✅ Salvo: {label} {value}", "✅ 已保存:指定错误码直接换号": "✅ Salvo: troca de conta direta em códigos de erro especificados", "✅ 已保留 {days} 天": "✅ Retenção definida para {days} dias", "✅ 已保留 {days} 天,超出部分已清理": "✅ Retenção definida para {days} dias; o excedente foi removido", "✅ 已备份 {n} 张表({size})到 {file}": "✅ {n} tabelas ({size}) backupadas em {file}", + "✅ 已导入账号密钥,请继续下载": "✅ Chave da conta importada; continue o download", "✅ 已导出 {count} 个账号{note}{file}": "✅ {count} contas exportadas{note}{file}", + "✅ 已开启「{label}」的机器人校验": "✅ Verificação de bot ativada para \"{label}\"", "✅ 已恢复 {n} 张表{note}": "✅ {n} tabelas restauradas{note}", - "✅ 已改用 k / M 缩写": "✅ Agora usando abreviações k / M", + "✅ 已改用 k / M / B 缩写": "✅ Agora usando abreviações k / M / B", "✅ 已改用本地量级词(万 / 亿式)": "✅ Agora usando magnitudes locais (k / M)", "✅ 已自动创建网关 Key「默认」(可在「网关 Key」页查看)": "✅ Chave do gateway “Padrão” criada automaticamente (veja na página “Chave do gateway”)", "✅ 已解除内容拦截降级": "✅ Fallback de bloqueio de conteúdo removido", @@ -59,7 +98,9 @@ "✅ 调试模式已关闭": "✅ Modo debug desativado", "✅ 调试模式已开启": "✅ Modo debug ativado", "✅ 路由设置已保存": "Configurações de roteamento salvas", + "✅ 连接成功:{path}": "✅ Conectado: {path}", "、{n} 个自定义提供商": ", {n} provedores personalizados", + "。事件日志、请求明细、调试报文与面板登录态保持不变。替换前会自动把当前业务数据备份到配置目录的 backups/ 下。": ". Logs de eventos, detalhes de requisições, pacotes de depuração e o estado de login do painel permanecem intactos. Os dados de negócio atuais são copiados para backups/ no diretório de configuração antes da substituição.", "「{label}」将从 {previous} 天改为 {days} 天。": "“{label}” passará de {previous} para {days} dias.", "「{label}」改用内置默认提示词": "“{label}” agora usa o system prompt padrão integrado", "「{label}」的提示词文件已更新": "Arquivo de prompt de “{label}” atualizado", @@ -69,29 +110,58 @@ "「{label}」的网关自带提示词正文已更新": "Texto do system prompt interno do gateway de “{label}” atualizado", "「模型管理」里每个能力的来源标记(上游 / models.dev / 已覆盖)读的就是这份数据;映射弹窗的思考档位下拉也按这里的数据只列该模型真实认识的档位。": "As marcas de origem por capacidade em \"Gerenciamento de modelos\" (upstream / models.dev / substituído) vêm destes dados, e a lista de níveis de raciocínio no diálogo de mapeamento mostra apenas os níveis que o modelo realmente aceita, com base neles.", "「用哪份提示词」本来是按上游分别决定的事,所以每家可以有自己的模式、提示词文件与正文:不在这里列出的提供商一律沿用上面那份默认设置,「跟随默认」把这一家取消、回到默认。带「网关自带」那一行的家(目前是 ZCode 两家)默认就列在这里 —— 那一段是网关自己装上去的文本,不来自客户端也不来自提示词文件,默认开启、可以关掉,也可以改它的正文(改过的那一段以你的文本为准);关掉后还能不能通过上游校验,取决于上游当下的口径,行内说明了实测依据。「模式 / 提示词文件 / 正文」管的是客户端自己的 system 怎么处理,与那个开关是两件事。": "Qual prompt usar é uma decisão tomada por upstream, então cada provedor pode ter seu próprio modo, arquivo de prompt e texto: provedores não listados aqui seguem a configuração padrão acima, e “Seguir padrão” remove a configuração individual desse provedor, voltando ao padrão. Os provedores com a linha “Interno do gateway” (atualmente os dois da ZCode) já aparecem aqui por padrão — esse trecho é um texto instalado pelo próprio gateway, que não vem do cliente nem de um arquivo de prompt, fica ativado por padrão, pode ser desativado e também pode ter o texto editado (os trechos editados usam o seu texto); se ele ainda passa na validação do upstream depois de desativado depende dos critérios atuais do upstream, e a própria linha documenta a base dos testes. “Modo / arquivo de prompt / texto” trata de como o system do próprio cliente é processado — é uma coisa separada daquele switch.", + "「设置」是固定项,含它的分组不能整组隐藏;可以单独隐藏组内其它菜单": "\"Configurações\" é fixo, então o grupo dele não pode ser ocultado inteiro; os outros menus podem ser ocultados individualmente", "「请求日志」页的逐条请求记录,默认 99999 天(近似全量保留),可填 1–99999 天。": "Registros de cada requisição na página “Logs de requisições”. Padrão de 99999 dias (retenção praticamente total); aceita de 1 a 99999 dias.", "一段内置装配": "um conjunto integrado", "三档数据各自独立计时,超出保留天数的部分会被删除:事件日志是登录、账号切换、429 切换这类系统事件;请求日志是网关每次转发到上游的逐条记录;按天聚合供报表页的热力图与按天趋势使用。请求日志与按天聚合默认 99999 天(约 274 年,效果上等于全量保留),事件日志默认 30 天。把某一档改小(例如 30 天改成 7 天)保存后会立即删除超出的历史数据,此操作不可恢复;改大或保持不变不会删除任何数据。三档的可填范围均为 1–99999 天。": "Os três tipos de dados são contados de forma independente e o que excede o período de retenção é excluído: os logs de eventos são eventos do sistema, como login, troca de conta e switch 429; os logs de requisições são o registro de cada encaminhamento do gateway ao upstream; os agregados diários alimentam o mapa de calor e as tendências diárias da página de relatórios. Logs de requisições e agregados diários têm padrão de 99999 dias (cerca de 274 anos — na prática, retenção total), e os logs de eventos, de 30 dias. Reduzir qualquer um deles (por exemplo, de 30 para 7 dias) e salvar exclui imediatamente os dados históricos excedentes, sem possibilidade de recuperação; aumentar ou manter o valor não exclui nada. Os três aceitam de 1 a 99999 dias.", "三段各自成块,所以分开编辑": "Os três blocos são separados, por isso a edição é individual", "三段必须各自成块(上游按结构校验,并成一段会被回 405),所以这里分开编辑。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。": "", + "上传": "Enviar", + "上传中…": "Enviando…", + "上传内容含加密后的账号凭证与网关配置": "O envio contém credenciais de conta criptografadas e configuração do gateway", + "上传到云端": "Enviar para a nuvem", + "上传失败:{message}": "Falha no envio: {message}", + "上次同步:{time}({action})": "Última sincronização: {time} ({action})", "上次成功刷新的结果(存在本机数据库里)": "Resultado da última atualização bem-sucedida (guardado no banco local)", + "上游 CLI 版本": "Versão da CLI upstream", + "上游客户端版本": "Versão do cliente upstream", "上游按": "O upstream valida a identidade pela ", "上游按「结构」校验身份:三段必须各自成块,所以这里分开编辑三段(不能合成一段)。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。第三段发出时前面会自带一个空行(官方形状)。": "O upstream valida a identidade por “estrutura”: os três blocos precisam ser blocos separados, então os três são editados aqui individualmente (não é possível fundi-los em um só). Valores de execução no bloco dinâmico são escritos como placeholders e substituídos apenas no envio da requisição: {cwd} diretório de trabalho, {platform} plataforma, {shell} shell, {os_version} versão do sistema, {git} se é um repositório git, {provider} identificador do provedor, {model} nome do modelo. Limpar um bloco = aquele bloco volta ao texto oficial. O terceiro bloco é enviado com uma linha em branco na frente (o formato oficial).", + "上游未配置": "Upstream não configurado", "上游模型繁忙时会先排队(回一句「建议 N 秒后再来」,Qoder 免费模型就是这样):网关等一会儿再发同一请求,最多等几次。默认 2 次,可填 0–10;填 0 表示不等待,排队直接返回 503 并说明「不是登录态或额度问题」。": "Quando o modelo no upstream está ocupado, ele primeiro entra na fila (respondendo “recomendamos voltar em N segundos” — é o caso dos modelos gratuitos do Qoder): o gateway aguarda um pouco e reenvia a mesma requisição, até um número máximo de esperas. Padrão 2; valores aceitos 0–10; 0 significa não esperar — a fila retorna 503 diretamente, informando que “não é um problema de login nem de cota”.", "上游用「逐字精确匹配」的方式审核请求体(不是语义审核):客户端注入的固定模板句、计费头字段名、以及某些裸错误码出现在报文里就会整单拦截,返回 400。开启本项后,网关在每次转发前改写这些指纹 —— 表头键值整段删除,承载语义的模板句只换一个词(如 official CLI for Claude → official CLI tool for Claude),对话内容与语义都不受影响。规则集是内置的,不需要也无法维护词表。关掉本项后客户端模板会原样发往上游,可能重新出现模板句被误拦的报错。": "O upstream revisa o corpo da requisição por “correspondência exata, palavra por palavra” (não é revisão semântica): frases de template fixas injetadas pelo cliente, nomes de campos de cabeçalhos de cobrança e certos códigos de erro soltos, se aparecerem no payload, bloqueiam a requisição inteira com retorno 400. Com esta opção ativada, o gateway reescreve esses fingerprints antes de cada encaminhamento — pares de chave/valor de cabeçalho são removidos por completo e frases de template que carregam significado têm apenas uma palavra trocada (por exemplo, official CLI for Claude → official CLI tool for Claude), sem afetar o conteúdo nem o significado da conversa. O conjunto de regras é integrado; não há lista de palavras a manter, nem seria possível. Com a opção desativada, os templates do cliente vão ao upstream como estão e podem voltar a ocorrer bloqueios indevidos de frases de template.", "上游请求四个阶段各自的等待上限(秒,1~3600):①「连接中超时」= 建立 TCP/TLS 连接或代理隧道的最大等待,默认 30 秒;②「等待响应超时」= 请求发出后等上游响应头的最大时间,默认 300 秒 —— 网关请求上游恒带 stream,响应头在 SSE 建立时就到达,与模型思考多久无关;③「流式响应空闲超时」= 流式响应相邻两块数据之间允许的最大空闲,收到新数据即重新计时,默认 300 秒 —— 上游长时间不吐数据即判定连接僵死并断开;④「非流式响应超时」= 读完整份非流式响应体的总预算(一次性计时、不重置),默认 300 秒。四项都是「等待上限」,只要数据还在来,正常的流式回答就一直往下传。保存后对下一个请求立即生效,不用重启。": "Limites de espera de cada uma das quatro fases da requisição ao upstream (segundos, 1~3600): ① “Timeout de conexão” = espera máxima para estabelecer a conexão TCP/TLS ou o túnel de proxy, padrão 30 segundos; ② “Timeout de espera de resposta” = tempo máximo de espera pelos cabeçalhos de resposta do upstream após o envio da requisição, padrão 300 segundos — as requisições do gateway ao upstream sempre usam stream, e os cabeçalhos de resposta chegam quando o SSE é estabelecido, independentemente de quanto tempo o modelo pense; ③ “Timeout de inatividade do streaming” = inatividade máxima permitida entre dois blocos adjacentes de uma resposta em streaming, com a contagem reiniciada a cada novo dado recebido, padrão 300 segundos — se o upstream ficar muito tempo sem enviar dados, a conexão é considerada travada e é encerrada; ④ “Timeout de resposta não streaming” = orçamento total para ler o corpo inteiro de uma resposta não streaming (contagem única, sem reinício), padrão 300 segundos. Os quatro são limites de espera: enquanto os dados continuarem chegando, uma resposta em streaming normal segue fluindo. Depois de salvar, os valores passam a valer para a próxima requisição, sem reiniciar.", + "下载失败:{message}": "Falha no download: {message}", + "不一致": "Divergente", "不做会话识别,每条请求都重新按策略选路。": "Sem detecção de sessão: cada requisição é roteada pela estratégia do zero.", "不可用": "Indisponível", "不在这里列出的提供商都用上面的全局配置;「跟随默认」取消这一家的单独配置。带「网关自带」的家列在这里:那段文本是网关自己装上去的(可关、可改正文),与「模式」是两件事。": "", + "与网关同源的面板;完整管理功能在桌面端": "Painel servido pelo próprio gateway; o gerenciamento completo fica no desktop", + "两张开关分别管桌面端(应用内窗口,也包含开启局域网访问后从其他设备访问的那份面板)与网页端(Docker / 服务端自托管的浏览器面板)。默认都开启;桌面端只在本机使用、嫌验证耗时可以关掉,网页端暴露在网络上,建议保持开启。": "Os dois interruptores controlam o lado desktop (a janela do aplicativo, incluindo o painel aberto em outros dispositivos quando o acesso pela LAN está ativo) e o lado web (o painel de navegador hospedado pelo Docker / servidor). Ambos vêm ativados. Desative o do desktop se usar apenas localmente e a verificação incomodar; mantenha o da web ativado, pois aquela página fica exposta à rede.", + "两次输入的新密码不一致": "As novas senhas não coincidem", "两次重试之间的等待时间,两项共用,可填 0–300 秒。": "Tempo de espera entre duas tentativas, compartilhado pelas duas opções; valores de 0–300 segundos.", "为了安全,开启前需要先注册一个": "Por segurança, antes de ativar é preciso registrar uma ", "主进程未返回启动设置,请更新桌面端后重试": "O processo principal não retornou as configurações de inicialização; atualize o aplicativo desktop e tente novamente.", "主进程桥不可用": "Ponte com o processo principal indisponível", + "主题": "Tema", "事件日志": "Logs de eventos", "事件日志保留天数": "Dias de retenção dos logs de eventos", + "云同步": "Sincronização na nuvem", + "人机验证": "Verificação humana", + "人机验证失败": "Falha na verificação humana", + "人机验证已完成": "Verificação humana concluída", + "人机验证还在计算,请稍候": "A verificação humana ainda está sendo calculada, aguarde", "仅用现有数据、不联网刷新": "Usando apenas os dados existentes, sem atualização online", + "从云端恢复": "Restaurar da nuvem", + "从桌面图标启动,全屏、无地址栏。想卸载:长按图标删除,或清除浏览器站点数据。": "Abra pelo ícone na tela inicial: em tela cheia, sem barra de endereço. Para desinstalar, mantenha o ícone pressionado e remova, ou limpe os dados do site no navegador.", + "会恢复默认的分组、顺序与显隐设置。你自建的分组会被删除(组内菜单不会丢)。": "Restaura os grupos, a ordem e a visibilidade padrão. Grupos criados por você são removidos (os menus são mantidos).", + "位置": "Local", + "位置与端口": "Local e portas", "使用中文单位(亿 / 万)": "Usar unidades chinesas (yi / wan)", "使用本地量级词(万 / 亿式)": "Usar magnitudes locais (estilo k / M)", + "侧边栏布局模块未加载(ui/sidebar-layout.js 应在 app.js 之前引入)。": "O módulo de layout da barra lateral não foi carregado (ui/sidebar-layout.js deve vir antes de app.js).", + "侧边栏没有显示任何菜单。到「设置 → 显示 → 侧边栏菜单」里打开需要的项。": "Nenhum menu aparece na barra lateral. Ative os que precisar em Configurações → Exibição → Menus da barra lateral.", + "侧边栏菜单": "Menus da barra lateral", "保存上游原始报文(请求头、请求体、响应头、响应体)": "Salvar payloads brutos do upstream (cabeçalhos da requisição, corpo da requisição, cabeçalhos da resposta, corpo da resposta)", "保存后以这份为准(优先于上面的文件);清空回到文件 / 内置默认。": "", "保存后以这里的文本为准": "Após salvar, este texto passa a valer", @@ -104,16 +174,21 @@ "保存后立即生效,不用重启;超时按上游失败处理(计入请求日志的「错误」列,并按「请求重试」的设置决定是否重试)。": "Passa a valer imediatamente após salvar, sem reiniciar; o timeout é tratado como falha do upstream (contabilizado na coluna “Erro” dos logs de requisições e sujeito às configurações de “Retry de requisições” para nova tentativa).", "保存后立即生效:改小保留天数会立刻删除超出的历史数据,且不可恢复。": "Passa a valer imediatamente após salvar: reduzir os dias de retenção exclui na hora os dados históricos excedentes, sem possibilidade de recuperação.", "保存并重启": "Salvar e reiniciar", + "保活换行丢弃": "Descartar quebras keepalive", "保留天数已刷新": "Dias de retenção atualizados", + "修改密码": "Alterar senha", + "修改密码需要输入当前密码;修改成功后,其他设备 / 浏览器上的登录会立即失效,当前这条会话保留。": "Alterar a senha exige a senha atual. Após o sucesso, os acessos em outros dispositivos e navegadores são revogados imediatamente; esta sessão permanece ativa.", "值": "Valor", "值与默认值相同的行不占覆盖(存的是「改过的键」,不是全量配置);值留空保存表示这个头不发送,删除改过的行表示回落默认值。改完立即生效,不用重启。": "Linhas com valor igual ao padrão não contam como sobrescrita (o que é armazenado são as “chaves alteradas”, não a configuração completa); salvar com o valor vazio significa que esse cabeçalho não é enviado, e excluir uma linha alterada significa voltar ao valor padrão. As mudanças passam a valer na hora, sem reiniciar.", "值(留空 = 不发送)": "Valor (vazio = não enviar)", "允许局域网内的设备访问网关": "Permitir que dispositivos na LAN acessem o gateway", + "允许无 Key 裸跑": "Permitir execução sem chaves", "允许浏览器里的页面跨来源调用网关(/v1/*)": "Permitir que páginas do navegador chamem o gateway de outra origem (/v1/*)", "全局配置": "Configuração global", "全部已配置": "Tudo já configurado", "全部数据(账号、事件日志、请求记录、调试报文、设置)保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "", "全部数据(账号、事件日志、请求记录、调试报文、设置)统一保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "Todos os dados (contas, logs de eventos, registros de requisições, payloads de debug e configurações) ficam em um único banco de dados SQLite, o agent2api.db, no diretório de configuração. Para fazer backup, basta copiar esse arquivo; para mudar o local de armazenamento, defina a variável de ambiente AGENT2API_PROXY_HOME e reinicie o programa.", + "关闭后手动上传 / 下载也会被拒绝。": "Quando desativado, envio/download manual também é recusado.", "关闭后网关回到只监听 127.0.0.1,局域网内的设备将无法继续访问;已配置的网关 Key 与账号都不受影响。": "Após desativar, o gateway volta a escutar apenas 127.0.0.1 e os dispositivos da LAN deixam de conseguir acessá-lo; as chaves do gateway e as contas já configuradas não são afetadas.", "关闭后,管理界面不再从局域网提供,只有本机的桌面程序可以管理;已开启的 API 转发不受影响。": "Após desativar, a interface de administração deixa de ser servida na LAN e só o programa desktop local pode gerenciá-la; o encaminhamento de API já ativado não é afetado.", "关闭局域网访问": "Desativar acesso LAN", @@ -124,16 +199,30 @@ "其余沿用上面的全局配置": "", "内容拦截降级": "Fallback de bloqueio de conteúdo", "内置默认提示词": "System prompt padrão integrado", + "再输入一次新密码": "", "凭据类请求头已脱敏。": "Os cabeçalhos de requisição com credenciais foram mascarados.", + "凭证只保存在这台手机上,按服务器分别存放": "As credenciais ficam apenas neste celular, separadas por servidor", + "出问题时把这一页截图,或点「复制全部信息」把文本贴进反馈,一般就能定位到大致原因。": "Quando algo der errado, tire um print desta página ou use \"Copiar todas as informações\" e cole o texto no relato — normalmente isso já basta para identificar a causa.", + "分享图标在 Safari 底部工具栏中间": "O ícone de Compartilhar fica no meio da barra inferior do Safari", + "分组名": "Nome do grupo", + "分组名,例如「常用」": "Nome do grupo, ex.: \"Favoritos\"", "切换账号重试次数": "Retries trocando de conta", "刚刚从 models.dev 拉到的最新数据": "Dados recém-buscados do models.dev", "删除 {code}": "Remover {code}", + "删除分组": "Excluir grupo", + "删除分组「{name}」?组内 {n} 个菜单会移到「{target}」,不会丢。": "Excluir o grupo \"{name}\"? Os {n} menus vão para \"{target}\" — nada é perdido.", "删除状态码 {code}": "Remover o status code {code}", + "删除空分组「{name}」?": "Excluir o grupo vazio \"{name}\"?", + "删除这个分组(组内菜单移到第一组)": "Excluir este grupo (os menus vão para o primeiro grupo)", "刷新": "Atualizar", "刷新失败:{message}": "Falha na atualização: {message}", + "前台运行": "Execução em primeiro plano", "剥离上游审核黑名单指纹(改写请求体里的模板句与表头)": "Remover fingerprints da lista de bloqueio da revisão do upstream (reescrever frases de template e cabeçalhos no corpo da requisição)", "功能建议": "Sugestões de funcionalidades", "包含日志与调试报文": "Incluir logs e payloads de depuração", + "包含:{items}": "Inclui: {items}", + "区域(Region)": "Região (Region)", + "华为云 OBS": "Huawei Cloud OBS", "单轮铸造上限": "Limite de cunhagem por rodada", "占用大小": "Tamanho ocupado", "去反馈": "Enviar feedback", @@ -144,25 +233,48 @@ "变更网页管理面板": "Alterar o painel de administração web", "只列单独配置过的家,其余沿用上面的全局配置": "Só lista os provedores com configuração individual; os demais seguem a configuração global acima", "只对「排队制」的上游生效(目前是 Qoder 的免费模型):模型繁忙时上游不报错,只回一句「建议 N 秒后再来」(业务码 10605),网关按建议时长等一会儿再发同一请求,等满次数仍排不上才把「排队中」作为错误返回(HTTP 503,文案会说明这不是登录态或额度问题)。等待发生在首个字节之前,吃的是「等待响应超时」那份预算 —— 两项设置一起决定一次请求最多卡多久;排队不会标记账号限额、也不会换账号(换谁都一样在排队)。保存后对下一个请求立即生效,不用重启。": "Só tem efeito em upstreams com “sistema de fila” (atualmente os modelos gratuitos do Qoder): quando o modelo está ocupado, o upstream não retorna erro, apenas responde “recomendamos voltar em N segundos” (código de negócio 10605); o gateway espera o tempo sugerido e reenvia a mesma requisição, e só quando o número de esperas se esgota a “fila” é devolvida como erro (HTTP 503, com um texto explicando que não é um problema de login nem de cota). A espera acontece antes do primeiro byte e consome o orçamento do “Timeout de espera de resposta” — as duas configurações, juntas, determinam quanto tempo no máximo uma requisição pode ficar travada; a fila não marca a conta como limitada nem troca de conta (todos estão igualmente na fila). Passa a valer para a próxima requisição após salvar, sem reiniciar.", + "只清掉这个浏览器存的 Key,不影响网关": "Remove apenas a chave salva neste navegador; o gateway não é afetado", + "可回滚到上一版本": "Pode voltar uma versão", + "合计只累加当前筛选后可见的账号,筛选、搜索一改数字就跟着变。": "O total soma apenas as contas visíveis no filtro atual; mudar o filtro ou a busca altera o número.", + "合计按整组统计(默认):不受筛选、搜索影响,被筛掉与已禁用的账号也计入。": "O total abrange o grupo inteiro (padrão): filtros e busca não o alteram, e contas filtradas ou desativadas ainda contam.", "同一会话在 5 分钟内尽量跟住同一个账号;停了 5 分钟再发就算新会话。多数场景用它 —— 多轮对话少换号,也不至于把账号焊死。": "A mesma sessão tenta ficar na mesma conta por 5 minutos; pausar mais de 5 minutos conta como sessão nova. A escolha para a maioria dos casos — menos troca de conta em conversas de vários turnos, sem prender uma conta.", "同一会话在 6 小时内跟住同一个账号;适合一坐几个小时的长会话,上游的上下文缓存能吃到更多。": "A mesma sessão fica na mesma conta por até 6 horas; ideal para sessões longas de horas — o cache de contexto do upstream é aproveitado bem mais.", "同一账号重试次数": "Retries na mesma conta", "同时开放网页管理面板": "Também abrir o painel de administração web", + "同步密码": "Senha de sincronização", "名单最多 {max} 个状态码": "No máximo {max} status codes na lista", + "后端没有返回版本信息": "O backend não retornou informações de versão", + "含固定项「设置」,不可整组隐藏(可以单独隐藏组内其它菜单)": "Contém o item fixo \"Configurações\", então o grupo não pode ser ocultado inteiro (os outros menus podem ser ocultados individualmente)", "启动与托盘": "Inicialização e bandeja", + "启动时间": "Iniciado em", + "启用云同步": "Ativar sincronização na nuvem", "命中这些上游状态码的失败不在同一账号重发,直接换下一个账号继续试(换号占用「切换账号重试次数」,换满仍失败才把错误原样返回给客户端)。输入 100–599 的状态码后回车添加,点 × 或退格删除;默认 402(积分不足)。清空全部表示任何错误都照常重试。": "Falhas que correspondam a esses status codes do upstream não são reenviadas na mesma conta: o gateway passa direto para a próxima conta e continua tentando (a troca de conta consome uma das “Retries trocando de conta”; só quando elas se esgotam e a falha persiste é que o erro é retornado ao cliente como está). Digite um status code de 100–599 e pressione Enter para adicionar; clique em × ou use Backspace para remover. Padrão: 402 (pontos insuficientes). Limpar tudo significa que qualquer erro é tratado com o retry normal.", + "固定": "Fixo", "在同一个账号上原地重发几次(不含首发;整条请求共用一份,用完后换来的账号只发一次就继续换)。可填 0–10,0 表示失败立即换号。": "Quantas vezes o gateway reenvia na própria conta (sem contar o primeiro envio; a requisição inteira compartilha um único saldo e, esgotado ele, cada conta obtida na troca envia uma vez e a troca segue). Valores de 0–10; 0 significa trocar de conta imediatamente após a falha.", "在这里直接编辑提示词正文:保存后以这份文本为准(优先于上面的提示词文件),清空则回到文件 / 内置默认。改一个字不必再去编辑器里开文件。": "Edite o texto do prompt diretamente aqui: após salvar, este texto passa a valer (com prioridade sobre o arquivo de prompt acima); se você limpar, volta para o arquivo / padrão integrado. Não é preciso abrir um arquivo em um editor só para mudar uma palavra.", + "在途请求": "Requisições em andamento", + "地址格式不对。填「IP 或域名:端口」,可带 http:// 或 https://,例如 192.168.1.23:3065": "Endereço inválido. Informe \"IP ou domínio:porta\", opcionalmente com http:// ou https:// — por exemplo 192.168.1.23:3065", + "坚果云": "Jianguoyun (Nutstore)", + "坚果云需使用「第三方应用密码」(账户信息 → 安全选项里生成),地址必须是 /dav/ 下的目录。": "O Jianguoyun exige uma senha de aplicativo (gerada em Informações da conta → Segurança) e o endereço deve apontar para um diretório em /dav/.", + "填「IP 或域名:端口」。协议可写 http:// 或 https://,不写默认 http;http 不写端口默认 3065,https 默认 443。地址里的 /m、/v1 会自动去掉。": "Informe \"IP ou domínio:porta\". O protocolo pode ser http:// ou https:// (http é assumido se omitido). Sem porta, http usa 3065 e https usa 443. Qualquer /m ou /v1 no endereço é removido automaticamente.", "备份中…": "Fazendo backup…", "备份整库": "Fazer backup do banco", "备份文件内含全部账号凭证、网关 API Key 与面板管理员凭据,可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "O arquivo de backup contém todas as credenciais de contas, as chaves de API do gateway e as credenciais de administrador do painel — dá para logar com ele diretamente. Guarde com cuidado; não compartilhe nem envie para lugar público.", + "复制全部信息": "Copiar todas as informações", + "复制给客户端": "Copiar para o cliente", + "外观": "Aparência", + "外观 · 接入 · 关于": "Aparência · Acesso · Sobre", "天": "dias", "天数": "Dias", "失败 {n} 个": "Falhas: {n}", "失败 {n} 个:{detail}": "Falhas: {n} — {detail}", "失败后最多再换几个账号试(填 N = 最多换 N 个,首发那个不算)。换到同一家名下另一个账号、或换到另一家,都各算一次;换满仍失败就返回错误。可填 0–10,0 表示失败立即报错。": "Quantas contas a mais tentar em caso de falha (preencha N = trocar por até N contas; a que fez o primeiro envio não conta). Trocar para outra conta do mesmo provedor ou para outro provedor conta igualmente como uma troca; se o limite se esgotar e a falha persistir, um erro é retornado. Valores de 0–10; 0 significa retornar o erro imediatamente após a falha.", + "存储桶(Bucket)": "Bucket", "存储概况已刷新": "Visão geral do armazenamento atualizada", "安全": "Segurança", + "安装到主屏幕": "Adicionar à tela inicial", + "安装到桌面": "Instalar como app", + "安装包形态": "Tipo de instalador", "官方原文只有一句:声明自己是 ZCode。": "O texto oficial tem apenas uma frase: declara que ele é o ZCode.", "客户端 system 原样出站。": "O system do cliente é enviado como está.", "客户端 system 原样出站(只靠指纹脱敏改写模板句)。": "O system do cliente é enviado como está (somente o mascaramento de fingerprint reescreve frases de template).", @@ -170,19 +282,27 @@ "客户端(Claude Code / Codex 等 CLI)会往 system 里塞几十句固定模板,上游逐字审核,命中就整单拦截(HTTP 400)。「模式」决定网关怎么处理这份 system:透传 = 原样不动(默认);替换 = 删掉客户端的 system / developer,换成网关提示词;追加 = 保留客户端内容,只在最前面插一条网关提示词。网关提示词来自「提示词文件」或界面里编辑的正文(正文优先,清空回到文件 / 内置默认)。撞上拦截时会自动改用一段最小中性提示词,次日 00:00 自动恢复,也可以随时解除。": "", "家之间仍按优先级,只有同一家内部轮流用各账号 —— 想优先用某几家、又不愿把某一家的某个账号焊死时用它。": "Os provedores seguem a prioridade; só as contas dentro do mesmo provedor se alternam. Para preferir alguns provedores sem prender nenhuma conta específica.", "家内轮询": "Rodízio por provedor", + "容器内更新": "Atualização no contêiner", "密码至少 8 位": "A senha deve ter pelo menos 8 caracteres", + "导入前会把本机的 account.key 改名备份(account.key.bak-时间戳),不会直接删除。": "Antes de importar, o account.key local é renomeado para backup (account.key.bak-); ele nunca é excluído.", "导入完成:{summary}": "Importação concluída: {summary}", + "导入密钥失败:{message}": "Falha ao importar a chave: {message}", + "导入密钥并继续": "Importar chave e continuar", "导入账号": "Importar contas", "导出中…": "Exportando…", "导出会把全部账号与自定义提供商定义写入一个 JSON 文件,可以拷到另一台机器上导入后继续使用。导入采用合并策略:同提供商下按业务身份(UID / userId / apiKey 等)去重 —— 已存在的账号只更新凭证,保留本机原有的优先级顺序;新账号追加到转发顺序末尾,不会抢占当前正在使用的账号;自定义提供商定义按 id 合并,本机缺失时自动补建。": "A exportação grava todas as contas e definições de provedores personalizados em um arquivo JSON, que pode ser copiado para outra máquina e importado para continuar usando. A importação usa uma estratégia de mesclagem: dentro do mesmo provedor, as contas são deduplicadas pela identidade de negócio (UID / userId / apiKey etc.) — contas já existentes têm apenas as credenciais atualizadas e mantêm a ordem de prioridade original desta máquina; contas novas são anexadas ao final da ordem de encaminhamento e não tomam o lugar da conta em uso no momento; as definições de provedores personalizados são mescladas por id e criadas automaticamente quando ausentes nesta máquina.", "导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义": "O arquivo exportado contém credenciais como accessToken / refreshToken / apiKey e definições de provedores personalizados", "导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义,可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "O arquivo exportado contém credenciais como accessToken / refreshToken / apiKey e definições de provedores personalizados, e pode ser usado diretamente para login. Guarde-o com cuidado; não o compartilhe nem o envie para locais públicos.", "导出账号": "Exportar contas", + "尚未注册管理员:管理接口不需要登录。开启「允许局域网访问」时会引导注册,也可以从登录页注册。": "Nenhum administrador registrado: a API de gerenciamento não exige login. Ativar \"Permitir acesso pela LAN\" conduz ao registro; você também pode registrar pela página de login.", "局域网开放后,管理接口要求登录。请设置管理员账号与密码,注册完成会直接继续开启流程。": "Com o acesso LAN ativado, a interface de administração exige login. Defina a conta de administrador e a senha; concluído o registro, o fluxo de ativação continua automaticamente.", "局域网访问": "Acesso LAN", + "展开或收起设置分类": "", "工具用法与环境说明那一大段。": "O bloco grande de uso das ferramentas e descrição do ambiente.", "已为「{label}」单独配置提示词": "Prompt configurado individualmente para “{label}”", + "已作为独立应用运行": "App independente", "已保存 {count} / {limit} 条报文(超出后丢弃最旧的)。": "{count} / {limit} payloads salvos (os mais antigos são descartados quando o limite é excedido).", + "已保存,留空沿用": "Salvo; deixe em branco para manter", "已关闭": "Desativado", "已关闭 models.dev 联网刷新": "Atualização online do models.dev desativada", "已关闭。{note}": "Desativado. {note}", @@ -190,26 +310,43 @@ "已关闭出站指纹脱敏": "Mascaramento de fingerprint de saída desativado", "已关闭开发者角色兼容": "Compatibilidade com papel developer desativada", "已关闭网关跨域访问": "CORS do gateway desativado", + "已删除分组「{name}」": "Grupo \"{name}\" excluído", "已取消「{label}」的单独配置": "Configuração individual de “{label}” removida", + "已取消安装": "Instalação cancelada", "已取消导入": "Importação cancelada", "已取消导出": "Exportação cancelada", + "已取消恢复": "Restauração cancelada", + "已启用登录": "Login ativado", + "已复制全部版本信息": "Todas as informações de versão foram copiadas", "已应用": "Aplicado", "已开启": "Ativado", "已开启:其他设备把 API 地址指向 {url}{panel}。": "Ativado: em outros dispositivos, aponte o endereço da API para {url}{panel}.", + "已恢复默认分组:概览 / 转发链路 / 诊断 / 系统": "Grupos padrão restaurados: Visão geral / Encaminhamento / Diagnóstico / Sistema", + "已指定出口": "Saída fixada", "已接管": "Assumido pelo gateway", "已改回内置默认提示词": "Revertido para o system prompt padrão integrado", + "已新建分组「{name}」,点组头的 + 把菜单移进来": "Grupo \"{name}\" criado — use + no cabeçalho para mover menus", "已清掉「{label}」界面编辑的正文": "Texto editado na interface de “{label}” removido", "已清掉界面编辑的提示词(回落文件 / 内置默认)": "Prompt editado na interface removido (volta para o arquivo / padrão integrado)", + "已清除本机保存的 API Key": "A API key salva neste navegador foi removida", "已生效": "Em vigor", "已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。": "Mudou automaticamente para um prompt neutro mínimo (bateu num bloqueio de conteúdo do upstream — na maioria das vezes um falso positivo da impressão digital do system); levanta sozinho em {until}.", "已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。": "Mudança automática para o system prompt neutro mínimo (houve bloqueio de conteúdo no upstream, provavelmente um falso positivo de fingerprint do system); o fallback será encerrado automaticamente em {until}. Durante esse período, o prompt próprio deste modo não é enviado; depois de corrigir o prompt, é possível encerrar o fallback imediatamente.", "已自动改用最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。": "", + "已运行": "Tempo de atividade", + "已退出登录": "Sessão encerrada", + "已隐藏": "Oculto", "巴西葡萄牙语": "Português (Brasil)", "希望接入新的提供商或模型": "Quero integrar um novo provedor ou modelo", + "库中 schema": "Schema no banco", "库主文件的大小(不含运行期间的 WAL 临时文件,退出时已自动并回)。": "Tamanho do arquivo principal do banco (não inclui os arquivos temporários WAL em tempo de execução, que já são incorporados automaticamente ao sair).", "库存目标": "Meta de estoque", + "应用版本:{version}": "Versão do app: {version}", + "应用状态": "Status do app", + "应用窗口": "Janela do aplicativo", "建立上游 TCP/TLS 连接或代理隧道的最大等待时间(秒),默认 30 秒,范围 1~3600。": "Tempo máximo de espera para estabelecer a conexão TCP/TLS ou o túnel de proxy com o upstream (segundos), padrão 30 segundos, faixa 1~3600.", - "开发者角色兼容": "Compatibilidade com papel developer", + "开发构建": "Build de desenvolvimento", + "开发者角色兼容": "Compatibilidade de papel developer", "开发者角色兼容设置已刷新": "Configuração de compatibilidade com papel developer atualizada", "开启后新发生的请求才会被记录(已经过去的请求补不回来)。报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。": "Só requisições que acontecerem depois de ligar são registradas (as passadas não podem ser recuperadas). Os payloads ficam no banco de dados local e só os 500 mais recentes são mantidos (os mais antigos são descartados); a contagem aparece em \"Dados → Armazenamento de dados\", à esquerda, como \"Payloads de depuração\".", "开启后新发生的请求才会被记录(已经过去的请求补不回来),请求日志的「预览对话」也随之开始保存原始正文。上游报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。": "Com isto ativado, apenas requisições novas são registradas (as requisições passadas não podem ser recuperadas), e a “Prévia da conversa” nos logs de requisições também passa a salvar os corpos originais. Os payloads de upstream ficam no banco de dados local, mantendo somente os 500 mais recentes (os mais antigos são descartados quando o limite é excedido); a contagem aparece em “Payloads de debug”, na seção “Dados → Armazenamento de dados”, à esquerda.", @@ -218,111 +355,186 @@ "开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 活动套餐」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。与「开机自动启动」同时开启时,开机后也不建界面(只留托盘与后台进程),点托盘才打开 —— 效果与关窗后一致。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。": "", "开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 活动套餐」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。": "Com isto ativado, fechar a janela não é apenas ocultá-la: a janela da interface é destruída por completo — os seis processos residentes do WebView2 por baixo dela (cerca de 200MB de memória) são encerrados junto, enquanto o encaminhamento do gateway, o check-in agendado e a consulta de saldo continuam nos processos em segundo plano, sem serem afetados; quando precisar da interface, clique no ícone da bandeja para reabri-la (a inicialização a frio do WebView leva cerca de 1 segundo). Atenção: enquanto a interface está destruída, os tokens de captcha do canal do Plano de evento da ZCode são forjados pela interface e não podem ser repostos — requisições por esse canal caem nos logs de requisições com status 503 (com o motivo registrado) e se recuperam automaticamente ao reabrir a interface. Este switch só faz sentido quando “Minimizar para a bandeja ao fechar a janela” está ativado; minimizar a janela não é afetado.", "开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。": "Com isto ativado, a página de login conclui a verificação automaticamente em segundo plano no navegador (imperceptível para pessoas reais), enquanto cada tentativa de script precisa resolver um desafio — o custo de força bruta e de registro em massa sobe bastante. Afeta apenas o login / registro do painel, nada a ver com a API Key dos clientes de API.", + "开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。桌面端与网页端各自独立:桌面端主要服务本机与局域网,网页端(Docker / 服务端)直接暴露在网络上的登录页风险更高,建议保持开启。": "Quando ativado, a página de login resolve o desafio em segundo plano (invisível para pessoas), enquanto cada tentativa automatizada precisa calcular um antes — força bruta e registro indevido ficam bem mais caros. Afeta apenas o login / registro do painel, não as API keys dos clientes. Desktop e web são independentes: o desktop atende principalmente esta máquina e a LAN, enquanto o web (Docker / servidor) fica exposto à rede e se beneficia mais de permanecer ativado.", "开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。": "Quando ligado, o gateway salva localmente, por inteiro, **a requisição enviada ao upstream** (cabeçalhos + corpo) e **a resposta do upstream** (código de status + cabeçalhos + corpo) de cada encaminhamento, para a visualização em \"Detalhes\" na página de logs de requisições. Campos de credenciais nos cabeçalhos — Authorization, Cookie, API Key e afins — são sempre trocados por [redacted] e nunca tocam o disco em texto puro; corpos de requisição e resposta ficam como estão (podem conter suas conversas). Só os 500 payloads mais recentes ficam guardados; os mais antigos são descartados. É um interruptor temporário para diagnóstico — mantenha desligado quando não precisar.", "开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看;请求日志详情的「预览对话」也依赖本开关 —— 它保存的是客户端发来的请求体与网关下发的响应体(两侧各截断到 128 KiB),不开这个开关时预览对话没有数据可看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。上游报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。": "Com isto ativado, o gateway salva localmente, na íntegra, **as requisições enviadas ao upstream** (cabeçalhos + corpo) e **as respostas retornadas pelo upstream** (status code + cabeçalhos + corpo) de cada encaminhamento, para consulta em “Detalhes” na página de logs de requisições; a “Prévia da conversa” nos detalhes da requisição também depende deste switch — ela salva o corpo da requisição do cliente e o corpo da resposta do gateway (cada um truncado em 128 KiB), e com o switch desativado não há dados para pré-visualizar. Campos de credenciais nos cabeçalhos, como Authorization, Cookie e API Key, são sempre substituídos por [redacted] e nunca gravados em texto puro; corpos de requisição e de resposta são salvos como estão (podem conter o conteúdo das suas conversas). Só os 500 payloads de upstream mais recentes são mantidos; os mais antigos são descartados quando o limite é excedido. É um switch temporário para diagnóstico; recomenda-se desativá-lo quando não for necessário.", "开启局域网访问": "Ativar acesso LAN", "开放后,局域网内其他设备的浏览器打开本机 IP 即可进入管理面板(需管理员账号登录)。": "Após abrir, o navegador de outros dispositivos na LAN pode acessar o painel de administração abrindo o IP desta máquina (é preciso entrar com a conta de administrador).", "开机自动启动": "Iniciar com o sistema", + "开(最多 {n} 轮)": "Ligado (até {n} rodadas)", "当前三段都是官方原文。": "No momento, os três blocos estão com o texto oficial.", "当前固定为浅色模式,不随系统变化。": "No momento, fixo no modo claro, sem seguir o sistema.", "当前固定为深色模式,不随系统变化。": "No momento, fixo no modo escuro, sem seguir o sistema.", + "当前密码": "Senha atual", "当前已改过:{edited}。": "Editados no momento: {edited}.", + "当前形态": "Forma de execução", "当前按 100% 显示(默认比例)。": "Exibindo no momento a 100% (proporção padrão).", "当前按 k / M 缩写显示(如 1.20M / 8.4k)。": "Exibindo no momento com abreviações k / M (ex.: 1.20M / 8.4k).", "当前按 {n}% 显示。": "Exibindo no momento a {n}%.", "当前按「万 / 亿」式本地量级词显示(如 1.2亿 / 8400万)。": "Exibindo no momento com magnitudes locais no estilo k / M (ex.: 120M / 84M).", "当前是透传模式:存下来也不会发出,切成「替换 / 追加」之后才生效。": "No momento, o modo é Passthrough: mesmo salvando, o texto não é enviado; só passa a valer após mudar para “Substituir / Anexar”.", - "当前显示为「1.20M / 8.4k」这类英文缩写。": "Atualmente exibido como abreviações no estilo \"1.20M / 8.4k\".", + "当前显示为「1.20M / 8.4k / 1.2B」这类英文缩写。": "Exibindo no momento com abreviações k / M / B (ex.: 1.20M / 8.4k / 1.2B).", "当前显示为「1.2亿 / 8400万」这类中文量级。": "Atualmente exibido como magnitudes chinesas no estilo \"1.2亿 / 8400万\".", "当前浏览器以管理员身份登录着本面板。「退出登录」会撤销这台设备的登录会话(30 天内的自动续期一并失效),需要重新输入账号密码;其他已登录的设备不受影响。": "Este navegador está conectado a este painel como administrador. “Sair” revoga a sessão de login deste dispositivo (a renovação automática dentro de 30 dias também é invalidada) e será preciso digitar usuário e senha novamente; outros dispositivos já conectados não são afetados.", + "当前环境不支持修改机器人校验设置": "", "当前环境不支持局域网访问设置": "O ambiente atual não oferece suporte à configuração de acesso LAN", + "当前环境不支持整库备份": "O backup do banco completo não está disponível neste ambiente", "当前环境不支持整库备份(仅桌面端可用)": "Backup do banco completo não está disponível aqui (somente desktop)", + "当前环境不支持整库恢复": "A restauração do banco completo não está disponível neste ambiente", "当前环境不支持整库恢复(仅桌面端可用)": "Restauração do banco completo não está disponível aqui (somente desktop)", + "当前环境不支持读取版本信息(桥接方法缺失)": "Este ambiente não consegue ler as informações de versão (método de ponte ausente)", + "当前环境不支持退出面板登录": "", "当前生效的正文来自{source}{file}。保存后这一份会盖过提示词文件与内置默认{note}。": "O texto em vigor no momento vem de {source}{file}. Após salvar, ele prevalece sobre o arquivo de prompt e o padrão integrado{note}.", + "当前界面": "Interface atual", + "当前管理员:{name}": "Conectado como: {name}", "当前跟随操作系统的深浅色设置,系统切换时界面会自动跟上。": "No momento, seguindo o tema claro / escuro do sistema operacional; quando o sistema mudar, a interface acompanha automaticamente.", + "待迁移旧数据": "Dados antigos pendentes de migração", + "待配置": "Não configurado", + "忘记密码时无法在界面里找回:需要停掉程序后,从数据库删除管理员记录(kv 表的 panelAdmin 键),重启后按首次使用重新注册。具体命令与各形态的库文件路径见 docs 目录下的「重置管理员」文档。": "Se você esquecer a senha, ela não pode ser recuperada pela interface: pare o programa, exclua o registro do administrador no banco (a chave panelAdmin na tabela kv), reinicie e registre novamente como no primeiro uso. Os comandos e o caminho do banco em cada forma de instalação estão no documento \"Redefinir administrador\" em docs.", "恢复中…": "Restaurando…", "恢复失败:{message}": "Falha na restauração: {message}", "恢复官方原文": "Restaurar o texto oficial", "恢复整库": "Restaurar o banco", "恢复整库备份": "Restaurar backup completo", "恢复默认": "Restaurar padrões", + "恢复默认侧边栏": "Restaurar barra lateral padrão", "想回到原来那份,把这里清空再保存即可(不会删掉你配的文件路径)。": "Para voltar ao texto anterior, basta limpar este campo e salvar (o caminho de arquivo que você configurou não é apagado).", "想要的新功能或改进想法": "Funcionalidades novas ou melhorias que você gostaria de ver", "所有未单独配置的提供商都用这一份": "Todos os provedores sem configuração individual usam esta", + "所有菜单都已在这一组": "Todos os menus já estão neste grupo", + "手机端只做查看与高频操作;添加账号、编辑凭证、改写客户端配置仍需桌面端。": "", + "托管网页界面": "Serve a interface web", + "把 Base URL 复制给客户端": "", + "把地址里的 your-server 与 USERNAME 换成你自己的;目录不存在时会自动创建。": "Substitua your-server e USERNAME na URL pelos seus dados; o diretório é criado automaticamente se não existir.", "把整份数据库(账号、网关配置、事件日志、请求统计、调试报文与桌面设置)导出成一个 SQL 文件,恢复时整体替换本机全部数据 —— 与上面「账号导入 / 导出」的逐条合并是两种语义:恢复会连配置、日志与当前启用状态一起换成备份那台机器的状态。导出默认包含日志与调试报文(文件可能很大),不需要时可以取消勾选只备业务数据。恢复前会自动把当前数据备份到配置目录的 backups/ 下,恢复后需要重新登录管理面板。": "Exporta o banco inteiro (contas, configuração do gateway, logs de eventos, estatísticas de requisições, payloads de depuração e configurações do desktop) em um arquivo SQL; a restauração substitui todos os dados locais por inteiro. É uma semântica diferente do \"Importar / exportar contas\" acima, que mescla item a item: restaurar troca configuração, logs e estados de ativação para o que a máquina do backup tinha. A exportação inclui logs e payloads de depuração por padrão (o arquivo pode ser grande); desmarque para guardar só os dados de negócio. Antes de restaurar, os dados atuais são automaticamente backupados em backups/ no diretório de configuração, e você precisará logar no painel de novo depois.", + "把菜单移入 {name}": "Mover um menu para {name}", + "把菜单移入「{name}」": "Mover um menu para \"{name}\"", + "把账号、网关配置、自定义提供商、Agent 配置、代理池 / 订阅、签到与余额记录同步到 WebDAV 或 S3 兼容存储,换台机器后一键取回。事件日志、请求明细、调试报文与面板登录态不在同步范围内(它们是本机的运行痕迹)。上传的账号凭证仍是加密形态,解密用的账号密钥由「同步密码」单独加密后一并上传 —— 同步密码不会保存到云端,换机下载时需要重新输入。": "Sincronize contas, configuração do gateway, provedores personalizados, perfis do Agent, pool de proxies / assinaturas e registros de check-in e saldo com WebDAV ou armazenamento compatível com S3, e recupere tudo em outra máquina com um clique. Logs de eventos, detalhes de requisições, pacotes de depuração e o estado de login do painel ficam de fora (são vestígios de execução desta máquina). As credenciais das contas enviadas permanecem criptografadas; a chave que as descriptografa é criptografada com a senha de sincronização e enviada junto — a senha nunca é armazenada na nuvem, então você precisa digitá-la novamente ao restaurar em outra máquina.", + "把路径里的主目录显示成 ~,截图分享时不暴露用户名": "Mostra o diretório pessoal nos caminhos como ~, para que prints não revelem seu nome de usuário", + "把路径里的主目录(如 C:\\Users\\你的用户名)显示成 ~,截图分享时不暴露用户名。只影响本页显示与复制,不改任何文件。": "Mostra o diretório pessoal nos caminhos (ex.: C:\\Users\\seu-nome) como ~, para que prints não revelem seu nome de usuário. Afeta apenas esta página e o texto copiado; nenhum arquivo é alterado.", "报表天数": "Dias do relatório", "报表页的热力图与按天趋势,默认 99999 天(近似全量保留),可填 1–99999 天。": "O mapa de calor e as tendências diárias da página de relatórios. Padrão de 99999 dias (retenção praticamente total); aceita de 1 a 99999 dias.", + "拖动分组 {name}": "Arrastar grupo {name}", + "拖动左侧 ⠿ 调整顺序(菜单可以跨组拖);拨开关控制显隐;点分组名可重命名;组头的 + 把别的菜单移进来;眼睛按钮隐藏整组。改动即时生效并记住,左侧侧边栏就是结果。键盘也能排序:把手聚焦后按 Alt+↑ / ↓。": "Arraste o ⠿ para reordenar (menus podem mudar de grupo); use os interruptores para mostrar ou ocultar; clique no nome do grupo para renomear; o + no cabeçalho move outro menu para o grupo; o botão de olho oculta o grupo inteiro. As mudanças valem na hora e ficam salvas — a barra lateral à esquerda é o resultado. Teclado: foque a alça e pressione Alt+↑ / ↓.", + "拖动菜单 {name}": "Arrastar menu {name}", + "拖动调整分组顺序(Alt+↑ / ↓ 也可)": "Arraste para reordenar os grupos (Alt+↑ / ↓ também funciona)", + "拖动调整顺序,可跨组(Alt+↑ / ↓ 也可)": "Arraste para reordenar, inclusive entre grupos (Alt+↑ / ↓ também funciona)", "指定错误码直接换号": "Troca de conta direta em códigos de erro especificados", - "指纹脱敏": "Mascaramento de fingerprint", + "指纹脱敏": "Sanitização de fingerprint", "指纹脱敏设置已刷新": "Configurações de mascaramento de fingerprint atualizadas", "按天聚合保留天数": "Retenção dos agregados diários (dias)", "按提供商配置": "Por provedor", + "换一台服务器": "Trocar de servidor", "排队等待": "Espera na fila", "排队等待次数": "Tentativas de espera na fila", "排队等待秒数": "Segundos de espera na fila", "排队等待设置已刷新": "Configurações de espera na fila atualizadas", + "接入": "Acesso", + "控制主侧边栏显示哪些菜单、按什么顺序、怎么分组。隐藏只影响导航显示,不会撤销页面访问权限;被隐藏的页面仍可通过地址直接打开。": "Define quais menus a barra lateral mostra, em que ordem e como são agrupados. Ocultar afeta apenas a navegação — não revoga acesso, e páginas ocultas continuam abrindo pela URL.", "控制报表与请求日志里 Token 读数的写法:开启后按「万 / 亿」式的本地量级词显示(简体为万 / 亿,繁体为萬 / 億,日文为万 / 億,韩文为만 / 억),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。英文 / 葡语的本地量级词就是 k / M,这项对它们没有区别。只影响显示口径,不改变任何统计与存储的数值。": "Controla como o Token é exibido no relatório e nos logs de requisições: quando ativado, exibe com magnitudes locais no estilo “k / M” (chinês simplificado: 万 / 亿; chinês tradicional: 萬 / 億; japonês: 万 / 億; coreano: 만 / 억); quando desativado, usa abreviações k / M (igual à forma usada na documentação e nos campos da API do upstream). Para inglês e português, as magnitudes locais são exatamente k / M, então esta opção não faz diferença para eles. Afeta apenas a forma de exibição; não altera nenhum valor de estatística ou de armazenamento.", - "控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。": "Controla como os contadores de tokens aparecem nos relatórios e logs de requisições: ligado usa magnitudes chinesas (1.2亿 / 8400万), desligado usa abreviações k / M (consistente com a documentação e os campos de API do upstream). Muda apenas a exibição; nenhuma estatística ou valor armazenado é alterado.", + "控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M / B 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。": "Controla como as contagens de token aparecem nos relatórios e no log de requisições: ativado mostra magnitudes chinesas (1.2亿 / 8400万), desativado usa abreviações k / M / B (iguais às dos documentos e campos de API do upstream). Isso altera apenas a exibição; nenhum número estatístico ou armazenado muda.", "控制界面的深浅色,与左侧边栏底部的三个主题按钮是同一个设置(改哪一处,另一处立刻跟上)。「跟随系统」会随操作系统当前的浅色 / 深色自动切换,并在系统主题变化时即时跟上;选「浅色」或「深色」则把界面固定在该模式,不再随系统变化。窗口标题栏的深浅色会一并同步,不会出现深色界面配一条浅色标题栏的情况。": "Controla o tema claro / escuro da interface; é a mesma configuração dos três botões de tema no rodapé da barra lateral esquerda (mudar em um lugar reflete imediatamente no outro). “Sistema” acompanha automaticamente o tema claro / escuro atual do sistema operacional e reage na hora a mudanças no tema do sistema; escolher “Claro” ou “Escuro” fixa a interface nesse modo, sem seguir o sistema. A barra de título da janela acompanha junto, então não ocorre de a interface ficar escura com uma barra de título clara.", "提建议": "Enviar sugestão", "提示词文件": "Arquivo de prompt", "提示词文件已更新": "Arquivo de prompt atualizado", "提示词正文": "Texto do prompt", "提示词正文已更新": "Texto do prompt atualizado", + "操作系统": "Sistema operacional", + "改动仍保存在本机,修复网络或凭据后下一次改动会自动重试,也可以手动点「上传到云端」。": "Suas alterações continuam salvas localmente. Depois de corrigir a rede ou as credenciais, a próxima alteração tenta de novo automaticamente, ou clique em \"Enviar para a nuvem\".", "改完立即生效,不用重启(下一个请求就用新模式)。降级是自动的临时状态,解除后本模式自己的提示词立刻恢复。": "As mudanças passam a valer na hora, sem reiniciar (a próxima requisição já usa o novo modo). O fallback é um estado temporário automático; depois de encerrado, o prompt próprio deste modo volta imediatamente.", "放大或缩小整个界面(文字与控件一起变),效果与浏览器 Ctrl +/- 相同:共 11 档,立即生效并记住。": "Amplia ou reduz a interface inteira (texto e controles juntos), com o mesmo efeito de Ctrl +/- no navegador: 11 níveis; a mudança vale na hora e é memorizada.", "数据": "Dados", "数据保留": "Retenção de dados", "数据存储": "Armazenamento de dados", + "数据库 schema": "Schema do banco", "数据库不可用": "Banco de dados indisponível", - "数据库文件": "Arquivo do banco de dados", + "数据库文件": "Arquivo do banco", + "数据目录": "Diretório de dados", "数据统一保存在配置目录下的 agent2api.db;如需更换位置,请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "Os dados ficam todos em agent2api.db, no diretório de configuração; para mudar de local, defina a variável de ambiente AGENT2API_PROXY_HOME e reinicie o programa.", "整体替换": "substituídos por inteiro", "整库备份 / 恢复": "Backup / restauração do banco completo", + "新分组名": "Nome do novo grupo", "新增 {added} 个、更新 {updated} 个{suffix}{note}": "{added} adicionadas, {updated} atualizadas{suffix}{note}", + "新密码": "Nova senha", + "新密码不能与当前密码相同": "A nova senha deve ser diferente da atual", + "新密码至少 8 位": "A senha deve ter pelo menos 8 caracteres", + "新建分组": "Novo grupo", + "日志文件": "Arquivo de log", "日语": "japonês", + "显示 {name}": "Mostrar {name}", + "显示分组 {name}": "Mostrar grupo {name}", "显示模式": "Modo de exibição", + "显示这一组": "Mostrar este grupo", "更新": "Atualizar", + "更新出网线路": "Rota de saída da atualização", + "更新通道": "Canal de atualização", "替换": "Substituir", "替换 = 客户端 system / developer 消息全部删掉,换成网关的提示词;追加 = 保留客户端内容,只在开头 system 块之后多插一条。两者都只改发给上游的副本,客户端看到的响应不变。": "Substituir = todas as mensagens system / developer do cliente são apagadas e trocadas pelo prompt do gateway; Anexar = o conteúdo do cliente é mantido e apenas um item a mais é inserido depois dos blocos system do início. Os dois alteram somente a cópia enviada ao upstream; a resposta que o cliente vê não muda.", "替换为网关提示词": "Substituir pelo prompt do gateway", "替换前会自动把当前数据备份到配置目录的 backups/ 文件夹。": "Antes da substituição, os dados atuais são automaticamente backupados na pasta backups/ do diretório de configuração.", + "替换并恢复": "Substituir e restaurar", "有未保存的修改": "Há alterações não salvas", + "有未保存的改动:保存后才能同步(测试连接不受影响)。": "Alterações não salvas: salve antes de sincronizar (o teste de conexão não é afetado).", + "服务商": "Provedor", + "服务器地址": "Endereço do servidor", "期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。": "Enquanto este modo estiver ativo, o prompt próprio dele não é enviado; levante assim que ajustar o seu prompt.", "未单独配置:模式、提示词文件与正文都跟随上面的全局配置(当前「{mode}」)。": "Sem configuração individual: modo, arquivo de prompt e texto seguem a configuração global acima (no momento, “{mode}”).", + "未启用": "Desativado", "未开启轻量模式:关窗仅隐藏窗口,界面进程常驻内存,随时打开不等待": "Modo leve desativado: fechar a janela apenas a oculta, o processo da interface permanece na memória e reabrir é instantâneo", "未开启,developer 角色消息会原样发往上游,不认识该角色的上游会报错(400)。": "Desativado: mensagens com papel developer vão para o upstream como estão; upstreams que não conhecem o papel retornam erro (400).", "未开启,客户端 system 模板会原样发往上游,可能被内容审核误拦(400)。": "Desativado: os templates do system do cliente vão ao upstream como estão e podem ser bloqueados por engano na revisão de conteúdo (400).", "未开启,浏览器跨域无法直连网关。": "Desativado: o navegador não consegue se conectar diretamente ao gateway de outra origem.", "未开启,转发时不保存任何原始报文。": "Desativado: nenhum payload bruto é salvo durante o encaminhamento.", "未开启:网关只监听 127.0.0.1,仅本机可以访问。": "Desativado: o gateway escuta apenas 127.0.0.1 e somente esta máquina pode acessá-lo.", + "未注册": "Não registrado", + "未注册闸门": "Bloqueio de não registrado", "未知原因": "Motivo desconhecido", "未知来源": "Origem desconhecida", + "未知设备": "Dispositivo desconhecido", "未知账号": "Conta desconhecida", "未能读取 Cline 伪装头设置,请稍后重试": "Não foi possível ler as configurações de cabeçalhos spoofed do Cline; tente novamente mais tarde", "未能读取 models.dev 元数据状态,请稍后重试": "Não foi possível ler o estado dos metadados do models.dev. Tente novamente mais tarde.", + "未能读取云同步设置,请稍后重试。": "Não foi possível ler as configurações de sincronização na nuvem. Tente novamente mais tarde.", "未能读取开发者角色兼容设置,请稍后重试": "Não foi possível ler a configuração de compatibilidade com papel developer, tente novamente mais tarde", "未能读取指纹脱敏设置,请稍后重试": "Não foi possível ler as configurações de mascaramento de fingerprint; tente novamente mais tarde", "未能读取系统提示词设置,请稍后重试": "Não foi possível ler as configurações do system prompt; tente novamente mais tarde", "未能读取网关跨域访问设置,请稍后重试": "Não foi possível ler as configurações de CORS do gateway; tente novamente mais tarde", "未能读取调试模式设置,请稍后重试": "Não foi possível ler as configurações do modo debug; tente novamente mais tarde", + "未能读取账户信息,请稍后重试": "Não foi possível ler as informações da conta. Tente novamente mais tarde", + "本地账号、配置、签到与余额记录变化后自动上传(合并 1 秒内的连续改动,最长 10 秒传一次;内容没变时不发请求)。": "Envia automaticamente após alterações locais em contas, configuração, check-in e registros de saldo (alterações em até 1 segundo são agrupadas, no máximo um envio a cada 10 segundos; nenhuma requisição é feita se nada mudou).", + "本机 APK 版本": "Versão do APK neste aparelho", + "本机应用版本": "Versão do app neste aparelho", + "本机界面版本": "Versão da interface neste aparelho", + "本机的业务数据(账号、配置、Agent 配置、代理池 / 订阅、签到与余额记录)将上传并": "Os dados de negócio desta máquina (contas, configuração, perfis do Agent, pool de proxies / assinaturas, registros de check-in e saldo) serão enviados e", "本机的全部数据(账号、网关配置、日志统计与桌面设置)将被所选备份文件": "Todos os dados desta máquina (contas, configuração do gateway, estatísticas de logs e configurações do desktop) serão ", + "本机的账号、网关配置、Agent 配置、代理池 / 订阅、签到与余额记录将被远端快照": "As contas, a configuração do gateway, os perfis do Agent, o pool de proxies / assinaturas e os registros de check-in e saldo desta máquina serão substituídos pelo snapshot remoto", "本次无需刷新": "Nenhuma atualização necessária desta vez", "机器人校验": "Captcha", + "来源设备:{device}": "Dispositivo de origem: {device}", + "来自 {device},生成于 {time}": "De {device}, criado em {time}", + "架构": "Arquitetura", "查询管理员状态失败:{message}": "Falha ao consultar o status do administrador: {message}", "校验身份:这三段必须各自成块排在最前(实测:三段并成一段会被回 405 / 3012)。因此这里按段编辑,不能合成一段。改过的段以你的文本为准,没改的段继续用官方原文;清空某一段 = 那一段恢复官方原文。": ": os três blocos precisam vir no início, cada um como um bloco separado (testado: três blocos fundidos em um só retornam 405 / 3012). Por isso a edição aqui é por bloco — não é possível fundi-los. Blocos alterados usam o seu texto; os não alterados continuam com o texto oficial; limpar um bloco = aquele bloco volta ao texto oficial.", + "桌面端": "Aplicativo desktop", + "桌面端:登录 / 注册需要 ALTCHA 人机验证": "Desktop: login / registro exige verificação ALTCHA", + "桌面端:登录 / 注册需要 ALTCHA 人机验证(当前正在使用)": "Desktop: login / registro exige verificação ALTCHA (em uso agora)", "桌面运行时不可用,暂时无法保存": "Runtime do desktop indisponível; não é possível salvar agora", + "档案": "Perfil", "检测中…": "Verificando…", + "概览": "Visão geral", "模式": "Modo", "模式改为「{mode}」": "Modo alterado para “{mode}”", "次": "vezes", "次日 00:00": "00:00 do dia seguinte", "正在保存上游原始报文:": "Salvando payloads brutos do upstream:", + "正在修改…": "Alterando…", "正在剥离出站请求里的审核指纹:表头键值整段删除,模板句最小改写。": "Removendo os fingerprints de revisão das requisições de saída: pares chave/valor de cabeçalho excluídos por completo, frases de template com reescrita mínima.", + "正在安装…": "Instalando…", "正在应答跨源请求:浏览器跨域请求可直连 /v1/*。": "Respondendo a requisições de outra origem: requisições de navegador com CORS podem se conectar diretamente a /v1/*.", "正在归一角色:developer 角色消息转发前改写为 system。": "Normalizando papéis: mensagens com papel developer são reescritas para system antes do encaminhamento.", "正在注册…": "Registrando…", "正在联网更新": "Atualizando online", + "正式版": "Estável", "每条请求轮换到下一个可用账号;新会话交给当前最闲的那位。适合想把手里的号摊开用、不让某一个一直扛。": "Alterna para a próxima conta utilizável a cada requisição; uma sessão nova vai para quem estiver mais livre. Para distribuir o uso entre as contas em vez de deixar uma carregar tudo.", "每条请求都重新按策略选路 —— 行为与「关闭」一致,仅保留会话识别的记录。": "Cada requisição é roteada pela estratégia do zero — mesmo comportamento de \"Desligado\", apenas os registros de sessão são mantidos.", "每次等待的时长,默认 0 = 跟随上游建议(实测 9~30 秒,上游没给建议时用 15 秒)。可填 0–120 秒强制一个固定时长。": "Duração de cada espera; padrão 0 = seguir a sugestão do upstream (medido: 9~30 segundos; quando o upstream não sugere nada, usa 15 segundos). Aceita 0–120 segundos para forçar uma duração fixa.", @@ -335,15 +547,32 @@ "注意:这一家的「网关自带」开关现在是关的,改过的正文要等开关打开才生效。": "Atenção: o switch “Interno do gateway” deste provedor está desativado; o texto alterado só passa a valer quando o switch for ativado.", "流式响应相邻数据之间允许的最大空闲时间(秒),收到新数据后重新计时,默认 300 秒,范围 1~3600。": "Tempo máximo de inatividade permitido entre dados adjacentes de uma resposta em streaming (segundos), com a contagem reiniciada a cada novo dado recebido; padrão 300 segundos, faixa 1~3600.", "流式响应空闲超时": "Timeout de inatividade do streaming", + "测试连接": "Testar conexão", + "浏览器 / PWA": "Navegador / PWA", + "浏览器只在 https 或 localhost 下提供离线缓存;局域网 http 访问不影响正常使用": "Os navegadores só oferecem cache offline em https ou localhost. O acesso por http na LAN continua funcionando normalmente.", + "浏览器标签页": "Aba do navegador", + "浏览器面板": "Painel no navegador", "添加提供商": "Adicionar provedor", "添加提供商…": "Adicionar provedor…", "添加自定义头": "Adicionar cabeçalho personalizado", "添加要单独配置的提供商": "Adicionar provedor para configurar individualmente", + "清除本机凭证": "Limpar credenciais locais", "清除这里的正文": "Limpar o texto deste campo", "点下面的入口会用系统默认浏览器打开 GitHub 的对应表单(需要 GitHub 账号,模板已预设好,填完直接提交即可)。": "Os atalhos abaixo abrem o formulário correspondente no GitHub usando o navegador padrão do sistema (é preciso ter uma conta GitHub; o template já vem preenchido, basta completar e enviar).", + "点击重命名": "Clique para renomear", + "点此添加或切换服务器": "Toque para adicionar ou trocar de servidor", + "点此添加服务器": "Toque para adicionar um servidor", + "版本": "Versão", + "版本信息": "Informações de versão", + "版本信息 · 所连网关环境": "Informações de versão · gateway conectado", "状态码 {code} 已在名单里": "O status code {code} já está na lista", "状态码必须是 {min}–{max} 的整数(收到: {text})": "O status code deve ser um número inteiro de {min}–{max} (recebido: {text})", + "环境": "Ambiente", + "生成时间:{time}": "Criado em: {time}", "用 models.dev 补齐上游没声明的模型能力与思考档位(联网刷新,内嵌快照始终生效)": "Preenche capacidades e níveis de raciocínio que o upstream não declara, usando o models.dev (atualização online; o snapshot embutido sempre vale)", + "用于加密上云的账号密钥。请自己记牢 —— 它不保存到云端,换机下载时必须输入同一个密码,否则账号数据无法解密。": "Usada para criptografar a chave da conta enviada à nuvem. Memorize-a — ela não é armazenada na nuvem e você precisa digitar a mesma senha ao restaurar em outra máquina, caso contrário os dados das contas não poderão ser descriptografados.", + "用浏览器菜单里的「添加到主屏幕」把这一页装成 App:全屏打开、没有地址栏,和原生应用一样。": "Use \"Adicionar à Tela de Início\" no menu do navegador para instalar esta página como app: tela cheia, sem barra de endereço, igual a um app nativo.", + "界面": "", "界面缩放": "Zoom da interface", "界面缩放比例": "Nível de zoom da interface", "界面语言": "Idioma da interface", @@ -351,15 +580,29 @@ "界面语言只保存在本机(localStorage)。切换后整页立即刷新,不影响账号、密钥与转发。": "O idioma da interface fica salvo apenas nesta máquina (localStorage). Após a troca, a página inteira é atualizada na hora; não afeta contas, chaves nem o encaminhamento.", "界面里编辑的正文": "Texto editado na interface", "留空 = 用内置默认提示词": "Vazio = usar o system prompt padrão integrado", + "留空使用 AWS 官方地址": "Deixe em branco para usar o endereço oficial da AWS", "登录 / 注册需要通过 ALTCHA 人机验证(工作量证明)": "Login / registro exigem a verificação humana ALTCHA (proof of work)", "登录 / 账号切换 / 429 切换等系统事件,可填 1–3650 天。": "Eventos do sistema como login / troca de conta / troca por 429; intervalo de 1–3650 dias.", "登录 / 账号切换 / 429 切换等系统事件,可填 1–99999 天。": "Eventos de sistema como login, troca de conta e switch 429. Aceita de 1 a 99999 dias.", + "登录 Agent2API": "Entrar no Agent2API", + "登录人机验证": "CAPTCHA de login", + "监听地址": "Endereço de escuta", + "目标路径:{path}": "Caminho de destino: {path}", "确定继续?": "Confirmar e continuar?", + "确认上传": "Confirmar envio", + "确认新密码": "Confirmar nova senha", "确认缩短保留天数": "Confirmar a redução dos dias de retenção", + "社区与支持": "Comunidade e suporte", + "离线缓存未启用": "O cache offline está desativado", + "积分合计只统计当前筛选出来的账号": "Somar apenas as contas visíveis no filtro atual", + "移入「{name}」": "Mover para \"{name}\"", + "程序路径": "Caminho do executável", "空 = 不要这一份,回落提示词文件 / 内置默认提示词。": "Vazio = não usar este texto e voltar para o arquivo de prompt / system prompt padrão integrado.", + "空分组 · 点组头的 + 把菜单移进来": "Grupo vazio · use + no cabeçalho para mover menus", "立即解除": "Encerrar agora", "第一段": "Primeiro bloco", "第一段(身份句)": "Primeiro bloco (frase de identidade)", + "第三方应用密码": "Senha de aplicativo", "第三段": "Terceiro bloco", "第三段(动态段)": "Terceiro bloco (dinâmico)", "第二段": "Segundo bloco", @@ -368,8 +611,12 @@ "等待期间请求日志的状态列会显示「排队中」阶段,详情弹窗的「内部重试」里能逐次看到等待时长;等待用尽后返回 HTTP 503(不是 401/429:既不会刷新凭证,也不会把账号标成限额)。": "Durante a espera, a coluna de status dos logs de requisições mostra a fase “Na fila”, e o diálogo de detalhes, em “Retries internos”, mostra a duração de cada espera uma a uma; quando as esperas se esgotam, é retornado HTTP 503 (não 401/429: nem atualiza credenciais, nem marca a conta como limitada).", "等比放大或缩小整个界面(文字、控件、间距一起变),效果与浏览器按 Ctrl +/- 相同:80%–130%、5% 一档。窗口本身不缩放,变的是页面内容的显示比例,设置立即生效并记住,下次启动直接按这个比例打开。放得越大可视范围越小,窗口较窄或表格较宽时不建议调得太大。": "Amplia ou reduz a interface inteira proporcionalmente (texto, controles e espaçamentos juntos), com o mesmo efeito de Ctrl +/- no navegador: 80%–130%, em passos de 5%. A janela em si não é redimensionada; o que muda é a proporção de exibição do conteúdo da página. A configuração vale imediatamente e é memorizada, e a próxima inicialização já abre nesta proporção. Quanto maior o zoom, menor a área visível; com janelas estreitas ou tabelas largas, não é recomendável aumentá-lo demais.", "简体中文": "chinês simplificado", + "管理员已注册": "Administrador registrado", + "管理员账号": "Conta de administrador", + "系统": "", "系统提示词模式": "Modo do system prompt", "系统提示词设置已刷新": "Configurações do system prompt atualizadas", + "累计请求": "Requisições totais", "繁体中文": "chinês tradicional", "约 {n} 字符": "cerca de {n} caracteres", "结构": "estrutura", @@ -380,19 +627,39 @@ "编辑网关自带提示词 · {label}": "Editar o system prompt interno do gateway · {label}", "缩放比例": "Nível de zoom", "网关": "Gateway", + "网关 Key 数量": "Quantidade de chaves do gateway", + "网关状态": "", "网关自带": "Interno do gateway", "网关跨域访问已开启": "CORS do gateway ativado", "网关跨域访问设置已刷新": "Configurações de CORS do gateway atualizadas", "网关跨域访问(CORS)": "Acesso de outra origem ao gateway (CORS)", "网关面(/v1/*)默认不应答浏览器跨源请求:页面直连会被预检拦下,只报「无法连接 API」。注意来源是 * —— 未配「网关 Key」时任何网页都能借本机网关打上游,故默认关闭;只给自己用的话,让页面与网关同源(本地反代 /v1)更稳妥。面板接口(/api/*)不受影响。": "A API do gateway (/v1/*) não responde por padrão a requisições de outra origem feitas por navegador: a conexão direta da página é barrada no preflight e só mostra “não foi possível conectar à API”. Atenção: a origem é * — sem uma “Chave do gateway” configurada, qualquer página pode usar o gateway local para atingir o upstream; por isso o padrão é desativado. Se for só para você, é mais seguro deixar a página e o gateway na mesma origem (proxy reverso local em /v1). A API do painel (/api/*) não é afetada.", + "网页端": "Web", "网页端的界面缩放由浏览器自己控制(Ctrl + / Ctrl -,或浏览器菜单里的缩放),此项不可调。": "No painel web, o zoom da interface é controlado pelo próprio navegador (Ctrl + / Ctrl -, ou o zoom no menu do navegador); esta opção não pode ser ajustada.", + "网页端:登录 / 注册需要 ALTCHA 人机验证": "Web: login / registro exige verificação ALTCHA", + "网页端:登录 / 注册需要 ALTCHA 人机验证(当前正在使用)": "Web: login / registro exige verificação ALTCHA (em uso agora)", + "群晖 NAS": "Synology NAS", + "群晖需在「控制面板 → 应用程序门户 → WebDAV」里启用服务,端口默认 5005(HTTP)/ 5006(HTTPS)。": "No Synology, ative o serviço em Painel de Controle → Portal de Aplicativos → WebDAV; as portas padrão são 5005 (HTTP) / 5006 (HTTPS).", + "腾讯云 COS": "Tencent Cloud COS", + "自动同步": "Sincronização automática", + "自动同步上次失败": "A última sincronização automática falhou", + "自动续写": "Continuação automática", "自定义提供商 {id}": "Provedor personalizado {id}", "自愈保护": "Proteções de autorrecuperação", + "至少 8 位,换机下载时需要": "Pelo menos 8 caracteres; necessário ao restaurar em outra máquina", + "至少要保留一个分组": "Pelo menos um grupo deve permanecer", "英语": "inglês", "装上{size}的官方身份提示词": "Instalar o system prompt de identidade oficial ({size})", + "装成 App 后从桌面图标直接进入:全屏打开、没有地址栏。": "Instalado como app, ele abre direto pelo ícone da tela inicial: em tela cheia, sem barra de endereço.", + "覆盖": "sobrescrever", "解除失败:{message}": "Falha ao encerrar: {message}", + "计数": "Contagens", "计量单位": "Notação numérica", + "设置不可隐藏(隐藏了只能清缓存找回来)": "\"Configurações\" não pode ser ocultado (seria preciso limpar o armazenamento para recuperá-lo)", + "详细日志": "Logs detalhados", "语言设置": "Configurações de idioma", + "请先保存设置再同步": "Salve as configurações antes de sincronizar", + "请填一个分组名": "Informe um nome de grupo", "请填写管理员账号(64 字符以内)": "Preencha a conta de administrador (até 64 caracteres)", "请求发出后等待上游响应头的最大时间(秒),默认 300 秒,范围 1~3600。": "Tempo máximo de espera pelos cabeçalhos de resposta do upstream após o envio da requisição (segundos), padrão 300 segundos, faixa 1~3600.", "请求提供商 / 模型支持": "Solicitar suporte a provedor / modelo", @@ -401,18 +668,30 @@ "请求超时": "Timeout de requisição", "请求转发到上游失败时(服务器瞬时错误、连接失败,以及 WorkBuddy 的 11-128 敏感词拦截),网关会等一段间隔后再发一次。次数分两档,管的是两件事:①「同一账号重试次数」在同一账号上原地重发几次 —— 一般是提示词被上游拦截、或链路瞬时抖动,等一会儿重发往往就好了;②「切换账号重试次数」这份请求最多再换几个账号试 —— 首发的那个账号不算,每换一个扣一次,不分是不是同一家(换到同一家的下一个账号也算一次),换满还失败就把错误返回给客户端。两项都设为 0 表示失败不重试、直接报错。注意账号级限额(429)不占这里的换号次数 —— 那类失败走「冷却该账号并换下一个账号」的降级,与这里的重试是两条路。另有「指定错误码直接换号」名单(默认 402):命中名单的上游状态码不在同一账号重发,直接按队列换下一个账号继续试(换号也占「切换账号重试次数」),换满仍失败才把错误返回给客户端。": "Quando o encaminhamento ao upstream falha (erro momentâneo do servidor, falha de conexão e bloqueio por palavra sensível 11-128 do WorkBuddy), o gateway espera um intervalo e envia de novo. As tentativas se dividem em duas opções, que tratam de duas coisas: ① “Retries na mesma conta” — quantas vezes reenviar na própria conta; normalmente é o prompt bloqueado pelo upstream ou uma oscilação momentânea do link, e reenviar depois de um tempo costuma resolver; ② “Retries trocando de conta” — por quantas contas a mais esta requisição pode tentar; a conta do primeiro envio não conta, cada troca desconta uma, independentemente de ser o mesmo provedor (trocar para a conta seguinte do mesmo provedor também conta), e se o limite se esgotar com falha, o erro é retornado ao cliente. Definir ambas como 0 significa não tentar de novo em caso de falha e retornar o erro direto. Atenção: o limite no nível da conta (429) não consome as trocas de conta daqui — esse tipo de falha segue o fallback de “esfriar a conta e passar para a próxima”, um caminho diferente do retry daqui. Existe ainda a lista de “Troca de conta direta em códigos de erro especificados” (padrão 402): falhas que correspondem aos status codes do upstream da lista não são reenviadas na mesma conta — o gateway passa direto para a próxima conta da fila e continua tentando (a troca também consome “Retries trocando de conta”), e só quando elas se esgotam com falha é que o erro é retornado ao cliente.", "请求重试": "Retry de requisições", + "请输入同步密码": "Digite a senha de sincronização", + "请输入当前密码": "Digite a senha atual", + "读取云同步设置失败:{message}": "Falha ao ler as configurações de sincronização na nuvem: {message}", + "读取失败:{message}": "Falha ao ler: {message}", "读取完整非流式响应体允许的最大时间(秒),默认 300 秒,范围 1~3600。": "Tempo máximo permitido para ler o corpo completo de uma resposta não streaming (segundos), padrão 300 segundos, faixa 1~3600.", + "读取网关信息失败:{message}": "Falha ao ler as informações do gateway: {message}", + "读取远端…": "Lendo o remoto…", + "读取远端信息失败:{message}": "Falha ao ler as informações remotas: {message}", "调试报文": "Payloads de debug", - "调试模式": "Modo debug", + "调试模式": "Modo de depuração", "调试模式设置已刷新": "Configurações do modo debug atualizadas", "负载均衡": "Balanceamento de carga", + "账号列表": "Lista de contas", "账号导入 / 导出": "Importar / exportar contas", "账号路由": "Roteamento de contas", + "账号页表格的两项显示偏好,只存在本机(localStorage),不影响账号数据与转发。「积分合计只统计当前筛选出来的账号」:账号页每一家的分组带右侧有一枚「可用合计」,默认按整组求和 —— 不受筛选、搜索影响,被筛掉的账号也计入(余额是资产,禁用也只表示不参与转发,额度仍在账上)。打开这一项后改为只累加当前可见的行:例如筛出 1 个 100 积分的账号,合计就是 100,而不是同组两个账号之和 1,000。分页不参与这个口径 —— 翻页看的是同一份结果的不同屏,合计不随页码变化。「隐藏账号名」与表头「账号」旁边那颗眼睛是同一个开关:打开后账号列的名字整体显示成星号,适合截图分享时遮挡账号。": "Duas preferências de exibição da tabela de contas. Ficam apenas nesta máquina (localStorage) e não afetam dados de conta nem o encaminhamento. \"Somar apenas as contas visíveis no filtro atual\": cada faixa de provedor na página de contas mostra um selo \"Total disponível\" e, por padrão, ele soma o grupo inteiro — filtros e busca não o alteram, e contas filtradas ainda contam (saldo é ativo; desativar apenas mantém a conta fora do encaminhamento, o crédito permanece). Ative para somar apenas as linhas visíveis: filtre até uma conta com 100 créditos e o total mostra 100, não 1.000 (a soma das duas contas do grupo). A paginação não participa desta regra — páginas são apenas telas diferentes do mesmo resultado, então o total não muda ao virar páginas. \"Ocultar nomes das contas\" é o mesmo botão do olho ao lado do cabeçalho \"Conta\": quando ativado, os nomes na coluna de contas aparecem como asteriscos, útil para capturas de tela.", + "账户": "Conta", "超出保留天数的部分会被删除:事件日志是登录、账号切换、429 切换这类系统事件。把天数改小(例如 30 天改成 7 天)保存后会立即删除超出的历史数据,此操作不可恢复;改大或保持不变不会删除任何数据。可填范围为 1–3650 天。请求日志 / 按天聚合 / 签到记录已全量保留、不再自动删除,要清历史请到对应页面手动清空。": "Qualquer coisa além do período de retenção é excluída: os logs de eventos são eventos do sistema, como login, troca de conta e switch 429. Diminuir o valor (por exemplo, de 30 para 7 dias) e salvar exclui imediatamente os dados históricos excedentes, e isso não pode ser desfeito; aumentar ou manter o valor não exclui nada. O intervalo permitido é de 1–3650 dias. Logs de requisições, agregados diários e registros de check-in agora são mantidos integralmente e nunca excluídos automaticamente; para limpar o histórico, use a página correspondente.", "超出的历史数据会被立即删除,且不可恢复。": "Os dados históricos excedentes serão excluídos imediatamente, sem possibilidade de recuperação.", "超时": "Timeout", "超时设置已刷新": "Configurações de timeout atualizadas", + "跟随系统 · 浅色 · 深色": "Sistema · Claro · Escuro", "跟随默认": "Seguir padrão", + "跨域访问": "CORS", "路由": "Roteamento", "路由设置已刷新": "Configurações de roteamento atualizadas", "跳过 {n} 个": "Ignorados: {n}", @@ -421,40 +700,79 @@ "转发一个请求时按什么顺序挑账号:①「路由策略」决定排队规则 —— 顺序优先是排第 1 的接走所有请求、直到它不能答(限流 / 故障 / 额度尽)才轮到下一位;轮流均摊是每条请求换下一位,雨露均沾;余额优先是用量占比最低的先用,「谁空谁上」。②「会话粘性」让同一会话(相同的模型 + 相同的开头消息)尽量跟住同一个账号 —— 多轮对话的上下文缓存能复用、少换号少浪费;自动档粘 5 分钟、会话内档粘 6 小时、每轮与关闭都不粘。账号的排队位次(账号表的第一列)在所有策略下都是地基,策略在它之上再排。此外路由还有三层自愈保护默认生效,不需要配置:转发失败的账号退避休息一会儿(15 分钟起步翻倍、封顶 1 小时),被 429 的账号垫底排到队尾一圈,余额不足 / Token 周期用量到顶的账号由限制器拦下 —— 恢复后自动回到队列。": "", "转发前将 developer 角色消息转换为 system,兼容不支持 developer 角色的 OpenAI 兼容上游": "Converte mensagens com papel developer para system antes do encaminhamento, para upstreams compatíveis com OpenAI que não suportam o papel developer", "转发失败的账号休息一会儿再回来:15 分钟起步,连续失败翻倍,封顶 1 小时;成功一次立即恢复。目的是「让这次失败先凉一会儿」,免得一条条地反复撞同一个坏账号。": "Uma conta que falha no encaminhamento descansa um pouco antes de voltar: começa em 15 minutos, dobra a cada falha consecutiva, com teto de 1 hora; um único sucesso a restaura na hora. A ideia é deixar a falha esfriar em vez de martelar a mesma conta ruim requisição após requisição.", + "转发链路": "Encaminhamento", + "转发链路 · 诊断 · 系统": "", "轮询间隔": "Intervalo de polling", + "软件版本": "Versão do software", "轻量模式": "Modo leve", "轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 套餐池(Start Plan)通道不可用,相关请求会在请求日志里注明原因": "O modo leve está ativado: fechar a janela destrói o processo da interface (liberando cerca de 200 MB de memória) enquanto o gateway continua em segundo plano; com a interface destruída, o canal do pool de planos do ZCode (Start Plan) fica indisponível e as solicitações afetadas registrarão o motivo no log de solicitações", "轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 活动套餐通道不可用,相关请求会在请求日志里注明原因": "Modo leve ativado: fechar a janela destrói o processo da interface (libera cerca de 200MB de memória) e o gateway continua em segundo plano; enquanto a interface está destruída, o canal do Plano de evento da ZCode fica indisponível e as requisições relacionadas registram o motivo nos logs de requisições", "轻量模式(关窗释放界面内存)": "Modo leve (liberar a memória da interface ao fechar a janela)", + "输入同步密码": "Digitar senha de sincronização", + "输入当前登录用的密码": "", "输入状态码,回车添加": "Digite o status code e pressione Enter para adicionar", + "运行形态": "Forma de execução", "还原": "Restaurar", "还有额度时被 429 的账号排到队尾,30 分钟后自动回到队列(成功转发一次也会立即回来)。它标记的是「疑似共享池被别人抢」,不是「这个账号本身有问题」。⚠️ 注意它与路由策略的配合:在「家内轮询」下,队尾意味着这一家整体被往后排 —— 如果某个提供商的账号被批量限流,它们会集体沉到队尾,那个家可能长时间轮不到。这时可以关掉本项,让 429 过的账号只受冷却约束(冷却到点自动回来)。": "Uma conta com cota que recebe 429 vai para o fim da fila e volta automaticamente após 30 minutos (um encaminhamento bem-sucedido também a traz de volta na hora). Isso sinaliza \"provavelmente o pool compartilhado está sendo drenado por outro\", e não \"esta conta está com problema\". ⚠️ Atenção à interação com a estratégia de roteamento: no \"Rodízio por provedor\", o fim da fila significa que o provedor inteiro é empurrado para trás — se as contas de um provedor forem limitadas em massa, todas afundam para o fim e aquele provedor pode não ser alcançado por muito tempo. Nesse caso desligue isto, deixando as contas 429 sujeitas só ao cooldown (elas voltam sozinhas quando ele expira).", + "还没有同步过。": "Ainda não sincronizado.", + "还没有添加服务器。点右上角「添加」,填入网关所在机器的 IP 或域名与端口。": "Nenhum servidor ainda. Toque em \"Adicionar\" no canto superior direito e informe o IP ou domínio e a porta da máquina que roda o gateway.", "还没有装载任何数据。": "Nenhum dado carregado ainda.", + "还没有读到版本信息": "As informações de versão ainda não foram carregadas", "这一份就是「替换 / 追加」模式下网关要发的 system 正文。": "Este é o texto do system que o gateway envia nos modos “Substituir / Anexar”.", + "这一项已隐藏": "Este item está oculto", + "这一项正在显示": "Este item está visível", + "这台网关未启用机器人校验": "Este gateway não exige verificação de bot", + "这台网关没有管理员,用一把已启用的 API Key 访问。": "Este gateway não tem administrador. Acesse com uma API key ativa.", + "进程 PID": "PID do processo", + "远程根目录": "Diretório raiz remoto", + "远端同一路径下的旧快照。事件日志、请求明细与调试报文不会上传。": "o snapshot antigo no mesmo caminho remoto. Logs de eventos, detalhes de requisições e pacotes de depuração não são enviados.", + "远端已有一份快照,将被覆盖": "Já existe um snapshot no remoto e ele será sobrescrito", + "远端快照使用了另一把账号密钥,但没有携带密钥文件,无法在本机解密。请在原来那台机器上重新上传一次。": "O snapshot remoto usa uma chave de conta diferente, mas não inclui o arquivo de chave, então não pode ser descriptografado aqui. Envie novamente a partir da máquina original.", + "远端快照使用的账号密钥与本机不同。请输入上传那台机器设置的同步密码,用它解出密钥装到本机后再继续下载。": "O snapshot remoto usa uma chave de conta diferente desta máquina. Digite a senha de sincronização definida na máquina que enviou para descriptografar a chave e instalá-la aqui, depois continue o download.", + "远端数据与当前版本不兼容": "Os dados remotos são incompatíveis com esta versão", + "远端该路径下还没有快照,本次是首次上传。": "Ainda não há snapshot nesse caminho remoto; este é o primeiro envio.", + "远端路径:{path}": "Caminho remoto: {path}", + "远端还没有可下载的同步数据": "Não há dados de sincronização para baixar no remoto", + "连不上这台网关。确认地址与端口正确、网关已启动,且已开启「局域网访问」;用 https 时还要确认证书有效": "Não foi possível alcançar este gateway. Confira o endereço e a porta, se o gateway está rodando e se o \"Acesso pela rede local\" está ativo; no caso de https, confira também se o certificado é válido.", "连接中超时": "Timeout de conexão", + "连接失败:{message}": "Falha na conexão: {message}", "连续失败到阈值后把这个账号整个打开(默认关)。与失败退避的分工:退避管「单次失败先凉一会儿」,惩罚轻、自动恢复;熔断管「这个账号现在整体不对劲,别再一条条地试」,要连续成功若干次才关。打开期间该账号被跳过,除非全场只剩它 —— 那时仍放行试一次。": "Desliga esta conta por inteiro depois que as falhas consecutivas atingem um limiar (desligado por padrão). Divisão de papéis com o recuo por falha: o recuo cuida de \"deixar uma falha única esfriar\" — penalidade leve, recuperação automática; o disjuntor cuida de \"esta conta está ruim como um todo, pare de tentar uma a uma\" e só fecha após vários sucessos consecutivos. Enquanto aberto, a conta é pulada — a menos que seja a única restante; nesse caso ela ainda recebe uma tentativa.", "追加": "Anexar", "追加网关提示词": "Anexar prompt do gateway", "退出失败:{message}": "Falha ao sair: {message}", "退出登录": "Sair", + "退出这台服务器的登录,不影响其他服务器": "Sai apenas deste servidor; os demais não são afetados", "选择备份文件并恢复": "Escolher um arquivo de backup e restaurar", "选路规则": "Regras de seleção", "透传": "Passthrough", "透传 = 不动客户端 system;替换 = 删掉客户端的 system / developer,换成网关提示词;追加 = 保留客户端内容,在最前面多插一条。": "", "透传客户端 system": "Passthrough do system do cliente", "透传客户端 system(默认)": "Passthrough do system do cliente (padrão)", + "通常是网络问题,点右侧重试": "Normalmente é um problema de rede — toque em Repetir", "通用": "Geral", "遇到 Bug、报错或异常行为": "Bugs, erros ou comportamentos anormais", "配置目录下的 agent2api.db(账号、日志、请求记录、调试报文与设置都在里面)。": "O agent2api.db no diretório de configuração (contas, logs, registros de requisições, payloads de debug e configurações ficam todos nele).", + "重启后生效,不影响当前操作": "Entra em vigor após reiniciar; nada do que você está fazendo é interrompido", + "重置会清空管理员账号与密码,面板将回到「未注册」状态。": "A redefinição apaga a conta e a senha do administrador e devolve o painel ao estado \"não registrado\".", + "重置管理员": "Redefinir administrador", "重试设置已刷新": "Configurações de retry atualizadas", "重试间隔": "Intervalo entre retries", "铸造守卫每隔多久看一次库存(毫秒)。只影响发现缺货的反应速度 —— 库存够用时轮询不会铸造任何令牌。默认 2500 毫秒,可填 1000–30000。": "De quanto em quanto tempo o guarda de cunhagem olha o estoque (milissegundos). Afeta só a rapidez de perceber a falta — com estoque suficiente, o polling não cunha nada. Padrão 2500 ms; aceita 1000–30000.", "问题反馈": "Relatar um problema", + "阿里云 OSS": "Alibaba Cloud OSS", + "限流中账号": "Contas limitadas", "随包内嵌的快照(发版时的那份)": "Snapshot embutido no app (o da versão lançada)", + "隐藏 {name}": "Ocultar {name}", + "隐藏分组 {name}": "Ocultar grupo {name}", + "隐藏用户名": "Ocultar nome de usuário", + "隐藏账号名(显示成星号)": "Ocultar nomes das contas (mostrar asteriscos)", + "隐藏这一组": "Ocultar este grupo", "非流式响应超时": "Timeout de resposta não streaming", "面板登录": "Login do painel", + "面板端口": "Porta do painel", "面板管理员账号": "conta de administrador do painel", "韩语": "coreano", + "预设": "Predefinição", "验证码令牌池": "Pool de tokens de captcha", "验证码令牌池设置已刷新": "Configurações do pool de tokens de captcha atualizadas", "默认值通常够用,出问题时才需要调": "Os padrões costumam bastar; ajuste só quando algo der errado", @@ -463,6 +781,7 @@ "默认开启。只改 role 字段本身,消息内容与顺序都不动;本项只改发给上游的副本,客户端看到的响应内容不变。": "Ativado por padrão. Só o campo role muda — conteúdo e ordem das mensagens ficam intactos; isto reescreve apenas a cópia enviada ao upstream, e o que o cliente vê nunca muda.", "默认开启。命中明细会记进请求日志的「敏」标签(悬停可看命中了哪几条规则)。本项只改发给上游的副本,客户端看到的响应内容不变。": "Ativado por padrão. Os detalhes das ocorrências são registrados na tag “PS” dos logs de requisições (passe o mouse para ver quais regras foram acionadas). Esta opção altera apenas a cópia enviada ao upstream; o conteúdo da resposta que o cliente vê não muda.", "默认网关只监听 127.0.0.1,只有本机能访问。开启后网关改听所有网卡,同一局域网内的设备可以把 API 地址指向本机 IP 一起使用。出于安全考虑,开启前必须先注册一个面板管理员:管理接口从此要求管理员会话或网关 Key,模型额度不会对局域网裸奔;一把启用的网关 Key 都没有时,转发接口也会拒绝服务,直到创建第一把。改动需要重启应用生效。「同时开放网页管理面板」把管理界面也出给局域网(其他设备的浏览器打开本机 IP 即可管理),默认关闭 —— 桌面端的面板仍只由本程序自己出。": "Por padrão, o gateway escuta apenas 127.0.0.1 e só esta máquina tem acesso. Ao ativar, o gateway passa a escutar todas as interfaces de rede e dispositivos na mesma LAN podem usar a API apontando o endereço para o IP desta máquina. Por segurança, antes de ativar é obrigatório registrar um administrador do painel: a interface de administração passa a exigir sessão de administrador ou chave do gateway, e a cota dos modelos não fica exposta na LAN; quando não há nenhuma chave do gateway ativa, a interface de encaminhamento também recusa atendimento até que a primeira seja criada. A mudança exige reiniciar o aplicativo para ter efeito. “Também abrir o painel de administração web” disponibiliza a interface de administração na LAN (o navegador de outros dispositivos abre o IP desta máquina para gerenciar); desativado por padrão — o painel do desktop continua sendo servido apenas pelo próprio programa.", + "({n} 个整组隐藏)": " ({n} grupos ocultos)", "({n} 行)": " ({n} linhas)", "({n} 项被覆盖)": " ({n} itens sobrescritos)", "(全部为默认值)": " (todos com valores padrão)", @@ -475,6 +794,7 @@ "(未生效)": "(sem efeito)", ",{items}": ", {items}", ",{time}": ", {time}", + ",以及代理池 / 订阅里可能存在的代理凭据。请使用只有你能访问的私有存储;账号密钥由同步密码单独加密,泄露存储本身不等于泄露账号。": ", além de eventuais credenciais de proxy no pool de proxies / assinaturas. Use armazenamento privado acessível apenas por você; a chave da conta é criptografada separadamente com a senha de sincronização, então vazar o armazenamento não expõe suas contas.", ",包括恢复后需要重新登录管理面板。": " pelo arquivo de backup selecionado, inclusive o novo login no painel após a restauração.", ",可直接用于登录。请妥善保管,不要外传或上传到公共位置。": ", e pode ser usado diretamente para login. Guarde-o com cuidado; não o compartilhe nem o envie para locais públicos.", ",文件里后续的改动在清空这里之前不再生效": ", e alterações posteriores no arquivo deixam de valer até que este campo seja limpo", diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/tasks.json b/desktop-tauri/i18n/i18n-src/pt-BR/tasks.json index 75dcdb66..f1686884 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/tasks.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/tasks.json @@ -7,6 +7,7 @@ "{n} 秒后": "em {n} segundos", "✅ {task}:{summary}": "✅ {task}: {summary}", "✅ 已更新「{label}」": "✅ “{label}” atualizado", + "「{name}」已执行": "", "上次执行 {time}": "Última execução às {time}", "上次执行 {time}({result})": "Última execução às {time} ({result})", "下次执行 {time}({relative})": "Próxima execução às {time} ({relative})", @@ -17,22 +18,31 @@ "停留在「请求日志」页时按此间隔重新拉取请求日志;页面不可见时不请求。": "Enquanto você permanece na página “Logs de requisições”, busca novamente os logs de requisições neste intervalo; nenhuma requisição é feita quando a página não está visível.", "冷却中,{relative}重试": "Em cooldown; nova tentativa {relative}", "凭证自动维护": "Manutenção automática de credenciais", + "分": "", "即将执行": "Prestes a executar", + "可填 {min}–{max} {unit}": "", "可填 {min}–{max} {unit}(默认 {def} {unit})": "Intervalo aceito: {min}–{max} {unit} (padrão: {def} {unit})", + "后台任务": "", "定时任务已刷新": "Tarefas agendadas atualizadas", "定时查询积分": "Consulta agendada de pontos", "定期向 GitHub 查询最新发布版本,默认每 20 分钟一次。检查结果、排期和限流冷却跨重启保留;手动检查也会合并重复请求并遵守冷却。开关与间隔在「软件更新」面板的「更新设置」弹窗里配置(定时任务页不再渲染这一条)。": "Consulta periodicamente o GitHub em busca da versão mais recente publicada, por padrão a cada 20 minutos. O resultado da verificação, o agendamento e o cooldown de rate limit são mantidos após reiniciar; a verificação manual também agrupa requisições repetidas e respeita o cooldown. O interruptor e o intervalo são configurados no diálogo “Configurações de atualização” do painel “Atualização do software” (esta linha não é mais exibida na página de tarefas agendadas).", "定期拉取最新模型清单,重启沿用排期与缓存。客户端读取模型列表的后台刷新也遵守本任务开关、间隔和失败冷却;手动获取模型可提前刷新。": "Busca periodicamente o catálogo de modelos mais recente; após reiniciar, mantém o agendamento e o cache. A atualização em segundo plano acionada quando o cliente lê a lista de modelos também respeita o interruptor, o intervalo e o cooldown de falhas desta tarefa; obter modelos manualmente permite atualizar antes.", "定期拉取最新模型清单,重启沿用排期与缓存。客户端读取模型列表的后台刷新也遵守本任务开关、间隔和失败冷却;手动获取模型可提前刷新。同一轮顺带刷新 models.dev 模型元数据(能力位与思考档位,可在设置页单独关闭)。": "Busca periodicamente o catálogo de modelos mais recente; após reiniciar, mantém o agendamento e o cache. A atualização em segundo plano acionada quando o cliente lê a lista de modelos também respeita o interruptor, o intervalo e o cooldown de falhas desta tarefa; obter modelos manualmente permite atualizar antes. A mesma rodada também atualiza os metadados de modelos do models.dev (sinalizadores de capacidade e níveis de raciocínio, que podem ser desativados separadamente na página de configurações).", "定期遍历账号,只刷新已过期或临期的凭证。重启后沿用上次排期,失败账号在冷却后重试;转发需要的按需续期不受此开关影响。": "Percorre periodicamente as contas e atualiza apenas as credenciais expiradas ou próximas do vencimento. Após reiniciar, mantém o agendamento anterior; contas com falha são repetidas após o cooldown. As renovações sob demanda exigidas pelo encaminhamento não são afetadas por este interruptor.", + "已{action}「{name}」": "", "已执行": "Executado", + "已更新间隔": "", + "开启": "", "执行失败:{reason}": "Falha na execução: {reason}", "报表自动刷新": "Atualização automática de relatórios", "改动立即生效,不需要重启程序": "As alterações entram em vigor imediatamente; não é preciso reiniciar o programa", + "改间隔": "", "日志页自动刷新": "Atualização automática da página de logs", "模型目录刷新": "Atualização do catálogo de modelos", "正在加载定时任务…": "Carregando tarefas agendadas…", + "每 {n} {unit}": "", "没有可显示的定时任务。后端未返回任务清单,请确认网关正在运行。": "Nenhuma tarefa agendada para exibir. O backend não retornou a lista de tarefas; verifique se o gateway está em execução.", + "没有后台任务": "", "状态每 20 秒自动同步": "Status sincronizado automaticamente a cada 20 segundos", "由页面按间隔自动刷新": "Atualizada automaticamente pela página conforme o intervalo", "立即执行": "Executar agora", @@ -41,5 +51,8 @@ "还没有执行记录": "Ainda sem registros de execução", "间隔不能为空(可填 {min}–{max} {unit})": "O intervalo não pode ficar vazio (aceita {min}–{max} {unit})", "间隔必须在 {min}–{max} {unit} 之间(当前填的是 {value})": "O intervalo deve estar entre {min}–{max} {unit} (valor atual: {value})", - "间隔必须是整数": "O intervalo deve ser um número inteiro" + "间隔必须是 {min}–{max} 的整数": "", + "间隔必须是整数": "O intervalo deve ser um número inteiro", + "页面刷新由浏览器执行,只在页面打开时生效": "", + "页面自动刷新": "" } diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/update.json b/desktop-tauri/i18n/i18n-src/pt-BR/update.json index 66d42cba..5adfdd61 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/update.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/update.json @@ -1,9 +1,14 @@ { " · 检查于 {time}": " · verificado em {time}", " 已发布(当前 {current}),更新日志如下:": " lançada (atual: {current}); notas de versão abaixo:", + "Discord 群": "Servidor do Discord", "GitHub Token 未填写": "Token do GitHub não preenchido", "GitHub 令牌": "Token do GitHub", - "QQ 群二维码": "QR code do grupo do QQ", + "QQ 群": "Grupo do QQ", + "QQ 群 / Telegram / Discord 都在这里": "Grupos do QQ, Telegram e Discord estão todos aqui", + "QQ 群 1": "Grupo do QQ 1", + "QQ 群 2": "Grupo do QQ 2", + "Telegram 群": "Grupo do Telegram", "{version} 已就位,服务正在重启,页面会在恢复后自动刷新…({seconds}s)": "{version} está no lugar. O serviço está reiniciando; a página será atualizada automaticamente quando ele voltar… ({seconds} s)", "{version}(预发布)": "{version} (pré-lançamento)", "✅ GitHub 令牌已保存(加密存储,界面不再显示)": "✅ Token do GitHub salvo (armazenado criptografado, não será mais exibido na interface)", @@ -30,8 +35,10 @@ "出网线路已切换,但写入磁盘失败(重启后会恢复原线路)": "A conexão de saída foi alterada, mas não foi possível gravar no disco (a conexão original será restaurada após reiniciar)", "分钟({min} ~ {max})": "minutos ({min} ~ {max})", "到点自动向 GitHub 查询新版本,查到就走「检测到更新」弹窗;关闭后「检查更新」按钮仍可手动触发": "No horário definido, consulta automaticamente o GitHub em busca de novas versões e, se encontrar, exibe o diálogo “Atualização disponível”. Se desativado, o botão “Verificar atualizações” continua funcionando manualmente", - "加入 QQ 群": "Entrar no grupo do QQ", "加入 beta 版": "Entrar no canal beta", + "加入交流群": "Entrar na comunidade", + "即将满员": "Quase lotado", + "即将满员,请优先加群 2": "Quase lotado — prefira o grupo 2", "去更新": "Atualizar", "发现新版本 {latest}(当前 {current})。{at}": "Nova versão disponível: {latest} (atual: {current}). {at}", "发现新版本 {latest}(当前 {current}),但该版本没有提供容器内更新包;请在宿主机执行 docker compose pull && docker compose up -d 升级。{at}": "Nova versão {latest} encontrada (atual: {current}), mas esta versão não fornece pacote de atualização dentro do contêiner; faça o upgrade no host com docker compose pull && docker compose up -d.{at}", @@ -45,14 +52,22 @@ "回滚失败": "Falha ao reverter", "回滚失败:{error}": "Falha ao reverter: {error}", "在 GitHub 查看完整说明": "Ver as notas completas no GitHub", + "在 QQ 里搜索群号即可加入。": "Pesquise o número do grupo no QQ para entrar.", + "在浏览器中打开": "Abrir no navegador", + "在浏览器中打开 GitHub 发布页": "Abrir a página de releases no GitHub no navegador", "在浏览器中打开项目仓库,点 Star 支持作者": "Abrir o repositório do projeto no navegador e dar uma Star para apoiar o autor", "在默认浏览器中打开 GitHub 的令牌创建页(备注已预设,无需勾选任何权限,直接点 Generate token)": "Abrir a página de criação de token do GitHub no navegador padrão (a descrição já vem preenchida, não é preciso marcar nenhuma permissão; basta clicar em Generate token)", "填写后检查限额 60 → 5000 次/小时(创建令牌无需勾选任何权限)": "Após preencher, o limite de verificações passa de 60 → 5000 por hora (criar o token não exige marcar nenhuma permissão)", + "复制群号 {group}": "Copiar o número do grupo {group}", "安装中": "Instalando", "安装包": "Instalador", "安装包已下载完成,正在启动安装程序(需要管理员权限,会弹出 UAC 确认框)…": "Instalador baixado; iniciando o instalador (requer permissões de administrador, uma janela de confirmação do UAC será exibida)…", + "安装包已下载完成,正在启动新的 AppImage…": "Instalador baixado; iniciando o novo AppImage…", + "安装包已下载完成,正在打开系统安装器…": "Instalador baixado; abrindo o instalador do sistema…", "安装包已下载完成,正在挂载磁盘映像…": "Instalador baixado; montando a imagem de disco…", + "安装包已就绪:{name}。点击「安装并重启」后会启动新的 AppImage;把它移动到你的应用目录并替换旧文件即完成升级(本程序会先退出以释放文件占用)。": "Instalador pronto: {name}. \"Instalar e reiniciar\" inicia o novo AppImage; mova-o para a pasta de aplicativos e substitua o arquivo antigo para concluir a atualização (este app é encerrado antes para liberar o arquivo).", "安装包已就绪:{name}。点击「安装并重启」后需要管理员权限,会弹出 UAC 确认框。": "Instalador pronto: {name}. Após clicar em “Instalar e reiniciar”, serão necessárias permissões de administrador e uma janela de confirmação do UAC será exibida.", + "安装包已就绪:{name}。点击「打开安装包」后会交给系统安装器,按提示输入密码即可完成安装(本程序无需退出)。": "Instalador pronto: {name}. \"Abrir instalador\" entrega o arquivo ao instalador do sistema — informe sua senha quando solicitado (este app não precisa ser encerrado).", "安装包已就绪:{name}。点击「打开安装包」后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装。": "Instalador pronto: {name}. Ao clicar em “Abrir instalador”, a imagem de disco será montada; arraste o aplicativo para “Aplicativos” para concluir a instalação.", "安装包已挂载,请在弹出的窗口里把应用拖进「应用程序」完成安装。安装完成后重新打开本程序即可。": "Instalador montado. Na janela exibida, arraste o aplicativo para “Aplicativos” para concluir a instalação; depois basta reabrir este programa.", "安装失败": "Falha na instalação", @@ -64,6 +79,7 @@ "已取消": "Cancelado", "已填写": "Preenchido", "已填写(粘贴新令牌可覆盖)": "Preenchido (cole um novo token para substituir)", + "已打开系统安装器,请在它弹出的窗口里完成安装。安装完成后重新打开本程序即可。": "O instalador do sistema está aberto — conclua a instalação na janela dele e reabra este app depois.", "已是最新": "Já está na versão mais recente", "已替换为 {version},正在重启服务…": "Substituído por {version}; reiniciando o serviço…", "已还原上一版本,正在重启服务…": "Versão anterior restaurada; reiniciando o serviço…", @@ -89,6 +105,7 @@ "更新包从 GitHub 发布页下载,下载完成后可直接启动安装程序并自动重启本程序。安装包会先校验来源域名与文件完整性。检查更新与下载安装包的出网线路、GitHub 令牌都在「更新设置」里:线路默认直连(直连失败时自动借 Clash Verge 的混合端口重试一次),选定指定出口后只走该出口;填写 GitHub 令牌可把检查限额从 60 次/小时提高到 5000 次/小时。": "O pacote de atualização é baixado da página de releases do GitHub; ao concluir o download, o instalador pode ser iniciado diretamente e este programa será reiniciado automaticamente. O instalador primeiro valida o domínio de origem e a integridade do arquivo. A conexão de saída e o token do GitHub usados para verificar atualizações e baixar o instalador ficam em “Configurações de atualização”: por padrão, a conexão é direta (se a conexão direta falhar, é feita automaticamente uma tentativa pela porta híbrida do Clash Verge) e, ao escolher uma saída específica, apenas ela é usada; preencher o token do GitHub eleva o limite de verificações de 60 para 5000 por hora.", "更新包已就绪:{name}。点「安装并重启」后,面板会校验并替换容器内的程序与界面文件,然后重启服务(页面会自动刷新)。": "Pacote de atualização pronto: {name}. Após clicar em “Instalar e reiniciar”, o painel valida e substitui os arquivos do programa e da interface dentro do contêiner e então reinicia o serviço (a página é atualizada automaticamente).", "更新已应用到容器内,重启服务后生效。{at}": "A atualização foi aplicada dentro do contêiner e passa a valer após reiniciar o serviço.{at}", + "更新已应用到容器内,重启服务后生效。{at}(本次检查失败:{error})": "A atualização foi aplicada dentro do contêiner e entra em vigor após reiniciar o serviço.{at} (esta verificação falhou: {error})", "更新日志": "Notas de versão", "更新设置": "Configurações de atualização", "替换完成,正在重启服务…": "Substituição concluída; reiniciando o serviço…", @@ -104,10 +121,13 @@ "检查更新": "Verificar atualizações", "检查更新与下载安装包走哪条线路(当前:{current});选择即保存。选项来自「网络代理」页;直连失败时会自动借 Clash 混合端口重试一次,选定指定代理后只走它": "Define por qual conexão passam a verificação de atualizações e o download do instalador (atual: {current}); a seleção é salva imediatamente. As opções vêm da página “Proxy de rede”; se a conexão direta falhar, será feita automaticamente uma nova tentativa pela porta híbrida do Clash; ao selecionar um proxy específico, apenas ele será usado", "检查更新失败:{error}": "Falha ao verificar atualizações: {error}", + "检查更新失败:{error}(以下版本与更新日志来自上次成功检查,检查于 {time})": "Falha ao verificar atualizações: {error} (as versões e notas abaixo são da última verificação bem-sucedida, em {time})", "检查间隔必须是 {min} ~ {max} 分钟": "O intervalo de verificação deve ser de {min} ~ {max} minutos", "检查间隔(分钟)": "Intervalo de verificação (minutos)", "检测到更新": "Atualização disponível", "正在下载 {name}:{percent}%({received} / {total} MB)": "Baixando {name}: {percent}% ({received} / {total} MB)", + "正在下载安装包…(下载完成后会交给系统安装器,按提示输入密码即可完成安装)": "Baixando o instalador… (ao terminar, ele é entregue ao instalador do sistema; informe sua senha quando solicitado)", + "正在下载安装包…(下载完成后会启动新的 AppImage;把它替换掉旧文件即完成升级)": "Baixando o instalador… (ao terminar, o novo AppImage é iniciado; mova-o sobre o arquivo antigo para concluir a atualização)", "正在下载安装包…(下载完成后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装)": "Baixando o instalador… (ao concluir o download, a imagem de disco será montada; arraste o aplicativo para “Aplicativos” para concluir a instalação)", "正在下载安装包…(下载完成后启动安装程序需要管理员权限,会弹出 UAC 确认框)": "Baixando o instalador… (ao concluir o download, iniciar o instalador exigirá permissões de administrador e uma janela de confirmação do UAC será exibida)", "正在下载服务端更新包…(下载完成后由面板完成替换,重启容器内的服务后生效)": "Baixando o pacote de atualização do servidor… (o painel faz a substituição assim que o download termina; a atualização passa a valer após reiniciar o serviço dentro do contêiner)", @@ -120,8 +140,9 @@ "点击「检查更新」后,这里会显示最新版本的更新说明。": "Após clicar em “Verificar atualizações”, as notas da versão mais recente serão exibidas aqui.", "点击「检查更新」查询 GitHub 上的最新发布版本。": "Clique em “Verificar atualizações” para consultar a versão mais recente publicada no GitHub.", "点击「检查更新」查询最新发布版本。": "Clique em “Verificar atualizações” para consultar a versão mais recente publicada.", + "点击在浏览器中打开": "Clique para abrir no navegador", + "点击复制群号": "Clique para copiar o número do grupo", "环境变量": "Variável de ambiente", - "用手机 QQ 扫上面的二维码,或在 QQ 里搜索群号加入。": "Escaneie o QR code acima com o QQ no celular, ou pesquise o número do grupo no QQ para entrar.", "立即重启": "Reiniciar agora", "粘贴 GitHub 令牌(ghp_… / github_pat_…)": "Cole o token do GitHub (ghp_… / github_pat_…)", "群号": "Número do grupo", @@ -135,6 +156,7 @@ "这一版没有提供容器内更新包": "Esta versão não fornece pacote de atualização dentro do contêiner", "这个版本没有填写发布说明。": "Esta versão não tem notas de versão.", "通道已切换,但写入磁盘失败(重启后会回到原设置)": "O canal foi alterado, mas não foi possível gravar no disco (a configuração anterior volta após reiniciar)", + "遇到问题或有使用心得,欢迎到群里交流。": "Dúvidas e dicas são bem-vindas em qualquer um dos grupos.", "重启中": "Reiniciando", "重新粘贴保存一次即可自愈": "Cole e salve novamente para corrigir automaticamente", "重试下载": "Baixar novamente", diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/accounts.json b/desktop-tauri/i18n/i18n-src/zh-Hant/accounts.json index 5fba0e68..b2021678 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/accounts.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/accounts.json @@ -7,6 +7,7 @@ "0 = 不限": "", "0 = 按本家默认 {default}": "", "30 秒 ~ 24 小时;手动查询会顺延下一轮": "", + "7 天内到期": "", "API Key": "", "Base URL": "", "Clash Verge 里还没有配置混合监听器或节点端口": "", @@ -32,13 +33,24 @@ "{label}({count})": "", "{message}(去该账号的「设置」里填上查询凭证即可)": "", "{month}月{day}日 {clock}": "", + "{name} · 分组设置": "{name} · 分組設定", "{name} 移到 {group} 组内第 {rank} 位": "", "{name} 移到第 {rank} 顺位": "", + "{name}的方向": "{name} 的方向", "{name}({parts})": "", "{name}({time} 起生效)": "", + "{n} 个已计入": "{n} 個已計入", + "{n} 个查询失败": "{n} 個查詢失敗", "{n} 个模型 ▾": "", + "{n} 个模型限流中 · 到恢复时间自动解除": "", "{n} 个模型限流中 · 记录来自上游 429 / 限额码,到恢复时间自动解除": "", "{n} 个请求正在使用该账号(含还在下发内容的流式请求)": "", + "{n} 个读数只读到一半(读数可信但不完整)": "{n} 個讀數只讀到一半(讀數可信但不完整)", + "{n} 个读数没有绝对数(只给比例或无额度),未计入": "{n} 個讀數沒有絕對數(只給比例或無額度),未計入", + "{n} 个账号 · {e} 启用 · {l} 限流": "", + "{n} 个账号 · 启用 {e}": "", + "{n} 个还没查到(未查询或正在查询)": "{n} 個還沒查到(未查詢或正在查詢)", + "{n} 个还没查询": "{n} 個還沒查詢", "{n} 分钟": "", "{n} 分钟后": "", "{n} 天后": "", @@ -47,6 +59,9 @@ "{n} 秒": "", "{period}窗口": "", "{plan}({time} 领取)": "", + "{reason}。该账号的登录态已被上游注销,自动刷新不会再成功,请重新登录该账号": "{reason}。該帳號的登入狀態已被上游註銷,自動重新整理不會再成功,請重新登入該帳號", + "{reason}之后组顺位才可拖动(别家的组整组不可见,落点没法定位)": "", + "{reason}之后顺序才可拖动(模型作用域是全局队列的投影,这里没有可写的顺序)": "", "{scope} {used} / {quota} Token,{countdown}重置": "", "{text}过期": "", "{text}过期({full})": "", @@ -79,7 +94,9 @@ "✅ 顺序已调整": "", " 出口 IP {ip}": "", "、": "", - "、补官方会话头、请求体补两个占位工具(上游免费档的三道校验)。": "", + "、补官方会话头、请求体补占位工具(上游免费档的过门校验)。": "", + "「{name}」已在「网络代理」页禁用,请先启用它或换一条": "", + "「{name}」开着排序规则:先关掉规则,才能手动调整组内顺序": "「{name}」開著排序規則:先關掉規則,才能手動調整組內順序", "「{name}」本身": "", "「已保存的代理」": "", "「网络代理」页还没有出口 —— 去那里新增,或在「订阅管理」里点「同步 Clash Verge」把 Clash 的出口导进来": "", @@ -91,6 +108,8 @@ "上游的 API Key。留空 = 不修改(改凭证不会换账号 id);要清掉已配的凭证用右边的「清除」": "", "上游的余额读数里没有这个套餐的额度桶,剩余量读不到;余额列显示的是套餐发放的权益额度,不是剩余量": "", "上游返回 {status}": "", + "上面所有规则都相同时,保持你拖出来的顺序": "上面所有規則都相同時,保持你拖出來的順序", + "不到 1 分钟": "", "不可用:{error}": "", "不限": "", "与队列里的上一个账号交换优先级(可能是另一家的账号)": "", @@ -98,11 +117,16 @@ "个": "", "个 · 共": "", "个人": "", + "主凭证没有固定到期时刻,到期由网关自动续期或本来就不会过期": "主憑證沒有固定到期時刻,到期由網關自動續期,或本來就不會過期", "主机": "", "仅将优先级调整到全局第一位,不改变启用状态": "", + "仅按手动顺序": "僅按手動順序", "仅账号管理": "", + "今天 24 点": "", "今天 {clock}": "", "今天 {time}": "", + "今天剩余:{name}": "", + "今天剩余:{name} · 这一份还剩 {value}(每天 24 点回满)": "", "今天还没有领过:上面「可领取的套餐」里逐份点「领取」即可": "", "今天(北京时间 {day})已由官方确认到账 {n} 项;领到的是套餐赠送积分,不增加福利模型的 token 池;按自然日重置,明天可再领": "", "今天(北京时间 {day})已试过 {n} 次但官方尚未确认到账,": "", @@ -137,18 +161,22 @@ "余额不足": "", "余额不足 · 已跳过": "", "余额低于": "", + "余额低于阈值时的提示": "餘額低於閾值時的提示", "余额低于阈值,转发时会跳过该账号(余额回升自动恢复)": "", "余额低于限制器的阈值,转发时会跳过该账号(余额回升自动恢复)": "", "余额列读数的自动刷新;也是「限制器」余额规则的数据来源。": "", "余额响应为空": "", + "余额已更新": "", "余额查询凭证": "", "余额查询失败:{message}": "", "余额查询方案:{label}": "", "余额沿用现有口径;Token 按重置方式内的累计消耗判定(切换类型会清空阈值)": "", + "余额详情": "", "使用套餐": "", "保存失败:{error}": "", "保存规则": "", "修改代理": "", + "倍率 {rate}": "", "免费版": "", "全局一条队列:数值越小越先用,不分提供商": "", "全局唯一:所有提供商的账号都不能重号,数值越小越先用": "", @@ -165,18 +193,30 @@ "关闭则不参与转发(余额查询照常,见「查询设置」)": "", "内消耗 ≥": "", "再领一次": "", + "冷却进度 {percent}%": "", + "凭证": "", "凭证与套餐": "", + "凭证已刷新": "", + "凭证已被上游注销": "", "凭证已过期,转发时会先刷新": "", "出口": "", "出网代理": "", + "分组设置": "分組設定", "分钟": "", + "切回全局账号表": "", + "删除中…": "", "删除提供商": "", "删除账号": "", "删除这条账号记录(不会影响客户端自己的登录态;之后可再点「导入桌面端登录态」加回来)": "", "删除选中的账号": "", "到恢复时间后自动解除,无需手动操作": "", "到期 {time}": "", + "到期 近→远": "到期 近→遠", + "到期 远→近": "到期 遠→近", + "到期时刻 {time}": "", "刷新 Token": "", + "刷新凭证": "", + "刷新队列": "", "剩 {left} / {total}{unit}": "", "剩 {n} 分钟": "", "剩 {remaining}": "", @@ -186,6 +226,8 @@ "协议": "", "卡片": "", "又不勾": "", + "取消排序": "", + "取消排序,回到默认次序(不影响已写回的队列顺序)": "", "取消选择": "", "另 {n} 个分组全是禁用账号": "", "另有 {n} 个已勾选账号被当前筛选隐藏,仍会参与操作": "", @@ -193,12 +235,16 @@ "可用 {total}": "", "可用 {value}": "", "可用 {value} {unit}": "", - "可用合计": "", + "可用合计": "可用合計", "可留空:新版请求头只认令牌;旧版会把用户 ID 放进 New-Api-User 头": "", "可领取的套餐": "", + "各厂商组内排序:分组与组序不变;组内先后已写回调度队列": "", "同提供商内暂无其他账号占用优先级": "", "同提供商已占用:{names}": "", "名称": "", + "后续加入": "後續加入", + "否": "", + "含 {n} 个不限额度账号,合计记为 ∞": "含 {n} 個不限額度帳號,合計記為 ∞", "启用状态": "", "启用该规则": "", "启用该账号": "", @@ -208,6 +254,7 @@ "周期单位": "", "商汤日日新控制台的登录密码": "", "商汤日日新控制台的登录账号": "", + "固定兜底,不受开关影响": "固定兜底,不受開關影響", "固定周期": "", "在 NewAPI 站点「个人设置」里生成的访问令牌": "", "在列表里拖:拖分组带调整整组顺位,拖行首把手调整组内先后(顺序由拖拽表达,不再填数字)": "", @@ -216,6 +263,8 @@ "基本": "", "备注名 / 启用 / 代理": "", "备注名 {name}": "", + "备注名不能为空": "", + "备注名已更新": "", "失败 {count} 个:": "", "套餐 {name}": "", "套餐明细弹窗未加载,请重启应用后重试": "", @@ -225,6 +274,7 @@ "套餐池(Start Plan)— 走 zcode.z.ai 端点,消耗 ZCode 套餐池的额度(新用户体验额度 / 活动领取)": "", "套餐积分的重置日期": "", "它会改写请求体": "", + "完整": "", "官方限时活动": "", "对话 token": "", "将作用于以下账号": "", @@ -236,16 +286,25 @@ "展开这一组": "", "已保存的代理": "", "已停用": "", + "已删除账号": "", + "已启用 {name}": "", "已启用,点击禁用(不参与转发)": "", "已在全局队列第一位": "", + "已按「{label}」{dir}排序": "", + "已按「{label}」{dir}排序并保存": "", + "已是首选": "", "已更新 {ok}/{total} 个账号,{failed} 个失败": "", + "已查询": "", "已用 {used}": "", "已用 {used} / 共 {total}": "", "已用完 / 已过期": "", "已禁用": "", + "已禁用 {name}": "", "已禁用,点击启用": "", + "已筛选 {n} 项": "", "已被「{name}」占用": "", "已触发 · 账号已禁用": "", + "已设为首选": "", "已设置": "", "已跳过{current}": "", "已跳过{usage}{countdown}": "", @@ -257,10 +316,10 @@ "已限流": "", "已领到、还没到生效时间的套餐(生效后余额列会按它显示):\n{lines}": "", "并发上限": "", - "并发上限 0~{max};有上游硬顶的家(如 CodeArts)以它的硬顶为准;达到上限的账号会被跳过、请求转给其他账号": "", + "并发上限 0~{max};CodeArts 等有默认值的家填 0 即按本家默认;达到上限的账号会被跳过、请求转给其他账号": "", "并发上限 0~{max};达到上限的账号会被跳过、请求转给其他账号": "", "并发上限 0~{max};达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "", - "并发上限 受上游硬顶约束,达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "", + "并发上限 填 0 即按本家默认 {default};填 1 或 2 就按填的算,更大的数会被本家上限截断(原列表 ⋯ 菜单的「并发上限」入口已并入这里)": "", "并发上限必须是 0~{max} 的整数": "", "并发上限:{value}": "", "开启后,网关按 OpenCode 官方 CLI 的形状发请求:没填 Key 的账号用匿名凭证": "", @@ -268,10 +327,16 @@ "当前代理不可用:{error}(转发时会回退直连)": "", "当前余额 {n};与余额列同一数字口径,严格小于才触发(等于阈值仍可用)": "", "当前可用 ∞(不参与余额不足判定)": "", + "当前已经是默认规则": "目前已經是預設規則", + "当前排序规则:{rules}。点击调整": "目前排序規則:{rules}。點擊調整", "当前没有任何模型处于限流中": "", "当前没有可领取的套餐(活动期内每天发一份,明天可再领)": "", "当前没有限流中的模型。": "", + "当前筛选下 {n} 个账号的可用余额之和(随筛选、搜索变化,不受分页影响)": "", + "当前筛选下没有账号": "", "当前筛选条件下没有账号": "", + "当前规则:{rules}。自动排序生效时,组内拖动把手会锁定;关掉全部规则即回到手动顺序。": "目前規則:{rules}。自動排序生效時,組內拖曳把手會鎖定;關掉全部規則即回到手動順序。", + "当前视图不能调整组内顺序:{reason} 之后再拖动": "", "当前视图不能调整组顺位:{reason} 之后再拖动": "當前檢視不能調整組順位:{reason} 之後再拖動", "当前:{label}(未经过代理池 —— 可在「网络代理」页把出口同步进池后来这里改选)": "", "当前:{label}(每条请求换一个出口);「自定义代理…」打开完整设置": "", @@ -284,13 +349,20 @@ "必填;留空 = 不修改,清除用右边的按钮": "", "总剩余 {total} · 套餐 {plan} · 奖励 {bonus}": "", "总量 {value}": "", + "恢复到「积分到期 → 添加时间」这套默认规则": "恢復到「積分到期 → 新增時間」這套預設規則", "恢复时间未知": "", "我的套餐": "", + "所属厂商:{label}": "", "所有分组里都没有启用的账号(禁用不参与转发)": "", + "所有提供商都有这个字段": "所有供應商都有這個欄位", + "所有积分包里**最先作废**的那一份什么时候到期;逐份明细见「余额」格里的积分明细": "", + "所有积分包里最先作废的那一份什么时候到期;没有一次性包时退到每日重置额度、显示今天还剩多久;逐份明细见「余额」格里的积分明细": "", "手动添加": "", "打开批量操作弹窗,并预选这一组的账号(改代理等动作在弹窗里挑参数)": "", + "打开筛选": "", "执行中…": "", "执行({action})": "", + "扫码 / 粘贴令牌 / 导入登录态需在桌面端完成": "", "批量动作": "", "批量操作 · 已选 {count} 个账号": "", "批量操作失败:{error}": "", @@ -303,20 +375,31 @@ "拖动调整列宽(双击还原)": "", "拖动调整整组顺序": "", "拖动调整组内顺序": "", + "按优先级从上到下比较,勾选的项才参与": "按優先順序從上到下比較,勾選的項目才參與", "按住拖动,调整这一家的组顺位(整组一起动)": "", "按住拖动,调整这个账号在本组内的顺序": "", "按提供商筛选账号": "", "按模型": "", "按规则限制该账号参与转发:没有规则(或全部停用)= 不限制;跳过档自动恢复,禁用档需手动启用。": "", + "按账号最早到期、且还有剩余的积分包比较": "", + "排序": "排序", + "排序粒度": "", "提供商显示名,1~{max} 个字符": "", "提供商:{name}": "", + "提高优先级": "提高優先順序", "搜索": "", "搜索账号": "", + "搜索账号 / 邮箱 / 备注": "", "操作失败:{message}": "", "收起": "", + "改动即时生效": "改動即時生效", "改协议 / Base URL 会改变它们的转发方式,正在进行的请求可能失败;模型清单与映射在「模型管理」页。": "", "数值越小越先用;": "", "数值越小越先用;{hint}": "", + "整表": "", + "整表排序:厂商分组已散开,行里带厂商标记。此顺序仅用于查看,不写回调度队列 —— 写回会打破「同一家连续」的队列规则": "", + "整表排序:打散厂商分组,全表一起来(行里带厂商标记;仅用于查看,不写回队列)": "", + "新账号落到组首 / 组尾": "新帳號落到組首 / 組尾", "旗舰": "", "无上限 · 已用 {used}": "", "无上限按日计(上游到达上限后仍继续计数,超额如实显示)": "", @@ -329,24 +412,39 @@ "明天 {clock}": "", "明天 {time}": "", "明细": "", + "是": "", "昵称 {name}": "", "显示账号名": "", "暂无可查询余额的账号": "", + "暂无数据:先查询一次余额,之后会自动按到期排序": "暫無資料:先查詢一次餘額,之後會自動按到期排序", "暂无账号,请点击右上角「添加账号」": "", "更多操作": "", + "更多设置": "更多設定", "更新于 {time}": "", + "最先作废的那一份还剩多少(不是面额);按这一份的剩余量排序,缺失值恒排最后": "", + "最先作废的那一份还剩多少(不是面额:这一列回答「马上要作废掉多少」)": "", + "最快到期优先": "最快到期優先", + "最新添加优先": "最新新增優先", "最早 {soonest} 恢复": "", "最早恢复": "", + "最早添加优先": "最早新增優先", + "最晚到期优先": "最晚到期優先", + "最近到期": "", + "最近到期积分": "", + "最近到期:{name}": "", + "最近到期:{name} · 这一份还剩 {value}(不是面额)": "", + "最近更新": "", "有效期": "", "有的账号过了 0 点额度就回来 —— 选「自然日」": "", "未使用": "", - "未分配端口": "", "未命名账号": "", "未查询": "", "未检测到 Clash Verge": "", "未检测到 Clash Verge 配置": "", + "未被使用": "未被使用", "未触发{current}": "", "未触发{usage}{countdown}": "", + "未设备注名": "", "未返回余额数据": "", "未返回可识别的余额数据": "", "未配置": "", @@ -356,9 +454,12 @@ "本周期已用": "", "本地台账里今天还领过这些,但上面的读数里还没有:{plans}(读数刷新可能有延迟)": "", "本地领取于 {time}": "", + "本组 {n} 个账号的可用余额之和,按整组统计(不受筛选、搜索影响)": "本組 {n} 個帳號的可用餘額之和,按整組統計(不受篩選、搜尋影響)", + "本组代理": "本組代理", "本组操作": "", "权益 {value}": "", "权益 {value} {unit}": "", + "条件即时生效,关掉面板就能看到结果。": "", "来源 {source}": "", "查看": "", "查看可领取的套餐与名下已有的套餐(每天一期,领取需过一次人机验证)": "", @@ -374,12 +475,14 @@ "查询间隔": "", "查询间隔必须是 {min} 秒 ~ 24 小时": "", "桌面端实时登录态(凭证每次从客户端登录态文件读取)": "", + "模型作用域下分组带随队列走,不折叠": "", "正在加载…": "", "正在探测可领取的套餐…": "", "正在查询": "", "正在读取 Clash Verge 配置…": "", "正在读取…": "", "正在读取代理列表…": "", + "正在读取调度队列…": "", "正在连接上游…": "", "正常": "", "此刻正在使用这个账号的请求数(含还在下发内容的流式请求);为 0 时不显示": "", @@ -388,10 +491,13 @@ "每日消耗 ≥": "", "每日消耗 ≥ {count} Token(每天 0 点重置)": "", "每日重置": "", + "每日重置({time} 回满,不是作废)": "", "每日重置(额度按周期回满,不是作废)": "", "每条请求换一个出口(按顺序轮流),上游看到的 IP 因此是变化的;出口的地址与端口由「网络代理」页管理": "", + "没有匹配「{kw}」的账号": "", "没有可全选的账号": "", "没有可用出口": "", + "没有将到期的积分(没有一次性额度包,或这一家不给逐份读数)": "", "没有选中的账号": "", "活动套餐": "", "活动套餐的推理端点要求每条请求带一个阿里云验证码令牌;令牌由本应用在后台静默铸造,界面关闭时无法铸造": "", @@ -400,7 +506,10 @@ "活动期内每天发一份新套餐;同一份当天重复领取会提示「已领取过」,次日可再领。领取要走一次人机验证(滑块)。": "", "测试中…": "", "测试出口": "", + "添加 新→旧": "新增 新→舊", + "添加 旧→新": "新增 舊→新", "添加规则": "", + "添加账号请用桌面端或网页版面板": "", "添加限制": "", "清掉本机的限流标记,立刻重新尝试该模型(上游若仍在限流会再次被标记)": "", "清掉该账号全部模型的限流标记": "", @@ -413,7 +522,9 @@ "清除已配置的余额查询凭证": "", "清除标记": "", "清除筛选": "", + "点一下按这一列排序(升序 → 降序 → 恢复默认次序)": "", "点击{action}各模型的限流明细": "", + "点击查看各模型的限流明细": "", "状态": "", "状态 {status}": "", "状态未知": "", @@ -429,7 +540,10 @@ "留空 = 不修改,清除用右边的按钮": "", "登录 / 导入一个新账号": "", "直连": "", + "确定删除账号「{name}」?这个操作不可恢复。": "", "确定删除选中的 {count} 个账号?此操作不可恢复,账号的登录态会一并移除。": "", + "确定清除全部模型的限流标记?": "", + "确定清除模型「{model}」的限流标记?": "", "禁用": "", "禁用该账号(需手动重新启用)": "", "禁用这一组里的全部账号(含被筛选隐藏的)": "", @@ -438,6 +552,7 @@ "福利网关未读到:{error}": "", "秒": "", "积分": "", + "积分到期时间": "積分到期時間", "积分包": "", "积分明细": "", "积分查询已复用转发用的登录凭证,这里通常留空即可。只有旧版本填过、或从旧代理导入过凭证时才有值": "", @@ -446,11 +561,17 @@ "窗口内已用 {used} / {limit} Token,转发时会跳过该账号(重置后自动恢复)": "", "窗口对齐自然时间(整点 / 整 N 分钟一轮回),重置即清零恢复;按账号在 requests 表聚合": "", "端口": "", + "端口准备中": "連接埠準備中", "第 {rank} 顺位": "", + "策略 {strategy} · 会话粘性 {sticky}": "", + "筛选": "", "筛选 / 搜索 / 分页生效时组顺位不可拖动(别家的组整组不可见,落点没法定位)": "篩選 / 搜尋 / 分頁生效時組順位不可拖動(別家的組整組不可見,落點沒法定位)", "级联删除名下全部账号,不可恢复": "", "组内": "", "组内先后:按住左边的把手上下拖动即可调整;整组的先后拖分组带的把手": "", + "组内并发上限": "組內並發上限", + "组内手动顺序": "組內手動順序", + "组内排序:只在每一家内部重排,分组与组序不变(组内先后写回队列)": "", "组内第 {rank} 位": "", "组顺位已锁定": "組順位已鎖定", "组顺位:越小越先用(拖动分组带调整)": "", @@ -459,6 +580,7 @@ "编码套餐(Coding Plan)— 走开放平台端点,用自己订阅的额度": "", "编辑": "", "编辑限制": "", + "缺失": "", "网络": "", "自动查询余额": "", "自动查询余额(按下面的间隔自动刷新余额列的读数)": "", @@ -467,11 +589,14 @@ "自定义:": "", "自然日(每天 0 点)": "", "表格": "", + "被跳过:这一轮不会用到它,所以没有位次": "", "视图": "", "解析失败": "", "触发动作": "", "触发后": "", "触发条件": "", + "计入 {counted}/{total}": "計入 {counted}/{total}", + "订阅已移除": "訂閱已移除", "订阅统计未读到:{error}": "", "记录里没有过期时间": "", "设为首选": "", @@ -481,6 +606,8 @@ "该上游无需鉴权": "", "该上游无需鉴权(转发与拉取模型都不发送鉴权头)": "", "该提供商没有余额查询": "", + "该提供商没有余额查询,因此读不到积分包": "", + "该提供商没有余额查询,因此读不到积分包的到期时刻": "", "该提供商的推理转发尚未接入,账号不参与转发": "", "该账号出网走的代理(Clash 出口 / 自定义 / 直连);点击可修改": "", "该账号当前限流中的模型;点徽章看明细": "", @@ -488,6 +615,7 @@ "该账号没有套餐登录态(jwt),活动套餐会失败:请重新登录该账号": "", "该账号没有福利模型额度(福利按活动下发,不是每个账号都有)": "", "该账号没有积分类读数(额度只有福利 token 池)": "", + "该账号的登录态已被上游注销,自动刷新不会再成功,请重新登录该账号": "", "该账号的额度只有福利 token 池(见余额格那两行),订阅统计没有积分类计量表": "", "请先勾选要操作的账号": "", "请先选择 Clash Verge 出口": "", @@ -502,21 +630,27 @@ "读取中…": "", "读取失败:{error}": "", "读取自 {dir};端口由 Clash Verge 管理,这里实时同步": "", + "读取调度队列失败:{error}": "", "读数来自自动余额查询;与余额列同一数字口径,严格小于才触发(等于阈值仍可用)": "", "读数里没有套餐清单(免费账号、或套餐的上游状态还没刷新;可点右上角「查询余额」重读)": "", "读数里没有额度桶,这里按套餐权益里的已用 / 总量显示(上游给的数)": "", + "调度队列:{n} 个可用 · 跳过 {m} 个": "", + "调整到全局队列第一位": "", "调整顺序失败:{message}": "", "账号": "", + "账号 ID": "", "账号不存在,请刷新后重试": "", "账号不存在,请刷新后重试。": "", "账号名 / 邮箱 / 备注": "", "账号已保存,但提供商未更新:{error}": "", "账号显示名称": "", + "账号添加时间": "帳號新增時間", "账号表": "", "账号视图": "", "账号设置": "", "账号设置 · {name}": "", "账号设置分组": "", + "账号详情": "", "账号邮箱": "", "跳过该账号(余额回升自动恢复)": "", "跳过该账号(自动恢复)": "", @@ -529,13 +663,24 @@ "轮询的代理": "", "轮询至少要选择一条代理(或改选「全部」)": "", "轮询(换 IP)": "", + "还有 {d} 天": "", + "还有 {h} 小时": "", + "还有 {h} 小时 {m} 分": "", + "还有 {n} 分钟": "", "还没有代理(去「网络代理」页添加)": "", "还没有余额读数(已自动查询一次,稍候;也可以点右上角「查询余额」重读)": "", "还没有保存的代理": "", + "还没有账号,去桌面端添加": "", "还没有限制规则:账号照常参与转发,余额与 Token 消耗都不设上限。": "", "这一份今天已经领过了(重复领取上游会回「已领取过」)": "", + "这一份还剩多少:{name} · 上游没给剩余量": "", + "这一家在这个模型上的倍率(与模型管理页同一份数据)": "", + "这一家查不到积分到期时间": "這一家查不到積分到期時間", "这一栏改的是": "", "这一段按提供商出现:有什么配什么。": "", + "这一组开着排序规则:关掉规则后才能手动拖": "這一組開著排序規則:關掉規則後才能手動拖曳", + "这个模型的队列里没有账号": "", + "这个模型队列里第 {rank} 位": "", "这个账号的余额走窗口限额通道(只配了编码套餐 API Key):只有每 N 小时的剩余比例,读不到套餐清单": "", "这些分组里一个启用的账号都没有(禁用不参与转发),默认不显示;点一下切到「禁用」查看,也就能重新启用它们": "", "这会{what}所有启用中的账号,转发将不可用。确定继续?": "", @@ -547,32 +692,46 @@ "连接数": "", "选择": "", "选择要轮询的代理(可多选)": "", + "逐份明细见「余额」格的积分明细": "", "逐请求轮换出口(换 IP):OpenCode Zen 免费档按 IP 限流,单个 IP 的额度用完会对它一律 429": "", + "邮箱": "", "邮箱 {email}": "", + "重新算一次这个模型的调度队列(限流冷却 / 并发释放后队列会变)": "", "重新读取": "", "重新读取该账号的余额与套餐(走余额查询那条链)": "", + "重置": "", "重置 {date}": "", "重置也不恢复,需手动启用": "", "重置周期必须是 30 分钟 ~ 24 小时": "", "重置方式": "", + "长期有效": "長期有效", "间隔单位": "", "阈值": "", + "降低优先级": "降低優先順序", "限制器": "", "限制器:{window}已用 {used} / {threshold} Token": "", "限制类型": "", "限制阈值必须是大于 0 的数字": "", "限流": "", "限流中": "", + "限流已清除": "", + "限流明细": "", + "限流模型": "", "限额": "", "限额状态": "", "隐藏账号名(名字显示为星号)": "", + "需要重新登录": "需要重新登入", + "需重登": "", "顺序": "", + "顺序已锁定": "", "顺序未保存:{message}": "", "领取 {time}": "", "领套餐": "", "额度": "", "额度窗口读数来自开放平台监控通道(套餐 JWT 那条通道没有额度桶):读的是每 N 小时 / 每周窗口的剩余比例": "", "验证码令牌:{counts}": "", + "默认不排序:组内顺序保持你拖出来的样子。需要自动排序时,勾选上面的规则即可。": "預設不排序:組內順序保持你拖出來的樣子。需要自動排序時,勾選上面的規則即可。", + "默认展开 / 收起": "預設展開 / 收合", "({n} 个未配置查询凭证)": "", "({plans})": "", "(不可用)": "", diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/add-account.json b/desktop-tauri/i18n/i18n-src/zh-Hant/add-account.json index 163e402b..4c2adeaf 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/add-account.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/add-account.json @@ -10,6 +10,7 @@ "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "", "Anthropic 协议填根地址,不要带 /v1": "", "Antigravity": "", + "AtomCode": "AtomCode", "AutoClaw 国内版": "", "AutoClaw 国际版": "", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "", @@ -18,19 +19,35 @@ "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "", "Cloud-IDE-JWT(三段点分)": "", "Command Code": "", + "Coze 扣子": "", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "", + "JWT(三段点分;也可直接粘贴 Bearer 整行)": "", + "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "", + "LobsterAI": "LobsterAI", + "MiniMax Code": "MiniMax Code", + "MiniMax Code 国内版": "MiniMax Code(國內版)", + "MiniMax Code 国际版": "MiniMax Code(國際版)", + "ModelScope 国内版": "", + "ModelScope 国际版": "", "MonkeyCode": "", "MonkeyCode 国际版": "", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "", "OfficeAce 果办": "", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "", + "mmoat_… 或客户端 auth.json 里的 accessToken 值": "", + "mmoat_…(国际站 account.minimax.io 签发)": "", + "mmort_…(可选;有它才能自动续期)": "", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "", + "ms- 开头的 API 令牌": "", + "openclaw.json 里 gateway.auth.token 的值": "", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "", + "sessionid_ss=…; sid_guard=…; ttwid=…(或 Cookie 编辑器导出的 JSON)": "", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "", + "{\"access_token\":\"mv_…\",\"openid\":\"oAN1…\",\"guid\":\"a79a…\"}(整段粘贴)": "", "{label} 网页登录的打开方式": "", "{label} 账号地区": "", "{label} 账号的添加方式": "", @@ -39,6 +56,7 @@ "{n} 个账号": "", "{n} 个账号,点击添加": "", "{seconds}s 后重发": "", + "⚠️ {label}账号已添加,但探活未通过:{message}": "", "✅ {label}账号已添加": "", "✅ {label}账号已添加{name}": "", "✅ 已从 cc-switch 导入 {n} 个供应商": "", @@ -58,23 +76,30 @@ "不是有效的凭证文件:需要包含 accessToken 与 uid": "", "不是自定义提供商": "", "不需要 Key:转发与拉取模型都不发送鉴权头": "", + "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "", "中国版": "", "今天没有需要领的活动(都已到账)": "", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
": "", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "", + "从本机 MiniMax Code 客户端读取国际版登录态(只读,不会改动客户端)": "", + "从本机 MiniMax Code 客户端读取登录态(只读,不会改动客户端)": "", "从本机导入桌面端登录态": "", + "从本机马维斯客户端读取登录凭证(需要客户端已登录;会弹一次管理员授权)": "", + "令牌当前不可用": "", "任务镜像 id": "", "余额查询": "", "使用 Google 账号登录": "", "使用 Zai 账号登录": "", "使用个人访问令牌(PAT)": "", + "先用管理员身份运行 _recon/marvis-cred-extract.py(从已登录的马维斯客户端内存里提取),然后把输出的 JSON 整段粘贴进来。需要三项:accessToken(mv_ 开头)、openid、guid。令牌没有续期接口,过期后重新提取一次即可(会更新同一条账号记录)。": "", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "", "内嵌窗口": "", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "", + "内嵌窗口(必须)": "", "内嵌窗口(推荐)": "", "准备验证…": "", "凭据 JSON": "", @@ -91,29 +116,38 @@ "取消失败:{message}": "", "取消等待": "", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "", + "只有在「导入本机登录态」不可用时才需要:以管理员身份运行 _recon/marvis-cred-extract.py,把生成的 JSON 整段粘贴进来。需要三项:accessToken(mv_ 开头)、openid、guid。令牌没有续期接口,过期后重新导入一次即可(会更新同一条账号记录)。": "", "可选": "", "可选(cloudaicompanionProject):留空由网关自动发现": "", + "可选,仅当令牌里读不出用户 id 时需要": "", "可选,展示用": "", "可选,没有则无法自动续期": "", + "可选,用于生成账号标识": "選填,用於產生帳號識別碼", "可选,留空使用凭据里的 user_name": "", "可选,留空使用账号姓名、邮箱或令牌指纹": "", "可选,留空使用账号昵称或邮箱": "", "可选,留空则用 key 尾号": "", "可选,留空则用 userId": "", + "可选,留空则用上游账号昵称": "", "可选,留空则用上游返回的账号名或账号 id": "", + "可选,留空则用令牌尾号": "", "可选,留空则用凭证里的账号名": "", "可选,留空则用文件里的昵称": "", "可选,留空则用登录名或 uid": "", "可选,留空则用脱敏手机号": "", "可选,留空则用脱敏手机号或 userId": "", "可选,留空则用账号 uk": "", + "可选,留空则用账号昵称": "", "可选,留空则用账号昵称或 userId": "", "可选,留空则用邮箱或令牌尾号": "", + "可选,留空用「AtomCode」": "選填,留空用「AtomCode」", "可选,留空用「OfficeAce 果办」": "", "可选,留空自动生成": "", "可选,留空自动用昵称": "", + "可选,给这个访客账号起个备注名": "", "可选,续期时带上": "", "可选,讯飞侧 userid": "", + "可选,默认 9434": "", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "", "可选;UUID 形态,与凭据同生共死": "", @@ -124,6 +158,7 @@ "可领取": "", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "", + "后端没有返回授权地址(登录发起失败)": "", "响应为空": "", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "", "国内版": "", @@ -141,11 +176,16 @@ "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "", "国际版(WorkBuddy AI)": "", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "", + "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "在 AtomGit 的 AtomCode 頁面取得兩項貼進來:accessToken 與 refreshToken。續期權杖建議填寫——本家權杖不是 JWT(約 7 天到期,讀不到到期時間),沒有它帳號無法自動續期,到期只能重新登入。", "在 cc-switch 里找到": "", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "", - "在打开的官方 Web 控制台里用微信扫码登录(窗口会自动弹出登录面板;若没有,点左下角头像 → 立即登录)。登录成功后自动加入账号列表。": "", + "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "", + "在打开的官方登录页里完成登录,成功后自动加入账号列表。": "", + "在打开的登录页里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "", + "在打开的登录页里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "", + "在打开的窗口里登录 Coze(抖音账号 / 微信扫码 / 手机号均可),登录成功后自动加入账号列表。请用内嵌窗口:系统浏览器里网关读不到登录窗口的 Cookie。": "", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "", "在浏览器中打开登录页": "", "地区": "", @@ -165,6 +205,8 @@ "导入所选": "", "导入所选({n})": "", "导入桌面端登录态": "", + "将在新标签页中打开授权页;完成后自动加入账号列表。": "", + "将在新标签页中打开授权页;完成后自动加入账号列表。若页面跳回本机地址后账号没有出现,按提示把地址栏里的完整回调地址粘贴回来即可。": "", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "", @@ -173,6 +215,7 @@ "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "", + "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "將用系統預設瀏覽器開啟登入頁(會重用瀏覽器裡已登入的 LobsterAI 帳號);完成登入後自動加入帳號清單,關掉彈窗即取消等待", "小米 MiMo Token Plan 套餐(新加坡站)": "", "已到账": "", "已取消登录等待": "", @@ -187,24 +230,32 @@ "已点发送,等待短信…": "", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "", "已领取,等官方确认": "", + "开始登录(获取设备码)": "", + "当前壳不支持直接打开浏览器,请手动访问:{url}": "", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "", "当前没有可领取的套餐(活动尚未开始)": "", "当前环境不支持领取(桥接方法缺失)": "", "必填,CatPaw 账号标识": "", "恢复 Google / GitHub 入口": "", + "手工粘贴凭证": "", "手机号": "", "手机验证码登录": "", + "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "開啟 LobsterAI(有道龍蝦)的官方登入頁(lobsterai.youdao.com)並用你的 LobsterAI 帳號登入:登入成功後官方頁面會跳回本機並自動加入帳號清單 —— 回呼落在閘道自己的連接埠上,所以瀏覽器要與閘道在同一台機器上;授權碼只在本機傳給閘道,介面不顯示明文權杖。", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "", "打开 Accio {label}登录页": "", "打开 Antigravity 网页登录": "", + "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "開啟 AtomGit 授權頁登入:完成後閘道會自動輪詢代理取回權杖並加入帳號清單(授權回呼由代理終結,不會回到本機,因此無需手動回填回呼位址)。", "打开 CatPaw 网页登录": "", "打开 Cline 授权页": "", "打开 CodeArts 授权页": "", + "打开 LobsterAI 登录页": "開啟 LobsterAI 登入頁", "打开 Qoder 网页登录": "", "打开 Trae 授权页": "", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "", "打开 ZCode {label}授权页": "", + "打开千问办公的官方授权页完成设备码授权:登录完成后网关自动拿到登录态并加入账号列表(不需要手工回填任何回调地址)。": "", + "打开千问办公网页登录": "", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "", @@ -238,23 +289,31 @@ "模型网关基址": "", "正在打开百度登录页…": "", "正在扫描本机 cc-switch…": "", + "正在换发访客凭证…": "", + "正在添加…": "", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "", "每周": "", "每日": "", "每月": "", "没有则无法自动续期": "", "没有匹配「{keyword}」的提供商": "", + "添加 QClaw 账号": "", "添加 {name} 账号": "", "添加中…": "", "添加到 ": "", + "添加千问办公账号": "", "添加失败:{reason}": "", "添加小浣熊账号": "", "添加方式": "", + "添加访客账号": "", "添加账号": "", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "", "点击添加账号": "", "点开即预填,填 Key 接入": "", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "", + "点按钮后网关会申请一个设备码并在窗口里打开授权页;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
授权在浏览器里完成,网关不接触密码;跨机部署(Docker / 远程面板)同样可用。": "", + "点按钮后网关会申请一个设备码,并把授权页打开;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
国际版与国内版是两套账号,请用国际站(agent.minimax.io)的账号登录。": "", + "点按钮后网关会申请一个设备码,并把授权页打开;用你的 MiniMax 账号确认授权,网关拿到令牌后自动加入账号列表。
授权在浏览器里完成,网关不接触密码;跨机部署(Docker / 远程面板)同样可用。": "", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "", @@ -270,38 +329,55 @@ "登录已结束": "", "登录并添加": "", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "", + "登录态由壳侧从窗口的 Cookie 存储读出;完成后自动加入列表": "", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "", "等待 Accio {label}登录完成…": "", + "等待 AtomCode 登录完成…": "等待 AtomCode 登入完成…", "等待 CatPaw 登录完成…": "", "等待 Cline 授权确认…": "", "等待 CodeArts 登录完成…": "", + "等待 Coze 登录完成…": "", "等待 Google 授权完成…": "", "等待 KukuAI 登录完成…": "", + "等待 LobsterAI 登录完成…": "等待 LobsterAI 登入完成…", "等待 OfficeAce 登录完成…": "", "等待 OrcaTerm 登录完成…": "", "等待 Qoder 授权完成…": "", "等待 Trae 授权完成…": "", "等待 ZCode {label}授权完成…": "", + "等待千问办公授权完成…": "", "等待小浣熊登录完成…": "", + "等待授权超时,请重新发起": "", + "等待浏览器里确认授权…": "", "等待登录完成…": "", "等待网页登录…": "", "粘贴 API Key": "", + "粘贴 API 令牌": "", "粘贴 Accio 的 accessToken": "", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "", "粘贴 Cookie": "", "粘贴 Google refresh token": "", + "粘贴 ModelScope 的 API 令牌(ms- 开头,在 {site} 的「访问令牌」页获取)。
{siteNote}
这枚令牌两用:网关既用它调推理,也用它读账号身份、魔粒余额与每日签到。令牌长期有效、没有续期接口,失效后重新获取再粘一次即可。": "", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "", + "粘贴 QClaw 本机网关的令牌(~/.qclaw/openclaw.json 里 gateway.auth.token 的值)。一般用「从本机导入」就够了;这里适合读不到配置文件时手动兜底 —— 端口留空则用默认的 9434。粘贴后会先连一次网关做校验,通过才落账号。": "", "粘贴 Qoder 个人访问令牌(pt-…)": "", "粘贴 accessToken": "", "粘贴 access_token(一长串 JWT)": "", "粘贴 session cookie": "", "粘贴 token / refreshToken": "", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "", + "粘贴令牌对": "貼上權杖對", + "粘贴凭证三件套": "", + "粘贴千问办公的登录令牌(JWT,三段点分)。一般用「从本机导入」就够了;这里适合换机器迁移、或本机解密失败时手动兜底 —— 此时还需要一并填写 userId(令牌里解不出时会提示)。粘贴后会先向上游校验一次,校验通过才落账号。": "", + "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "", + "粘贴登录令牌": "", "粘贴登录凭据": "", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "", + "粘贴网关令牌": "", "粘贴网关凭据": "", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
{siteNote}
MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "", + "粘贴访问令牌": "", "系统浏览器": "", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "", @@ -310,16 +386,25 @@ "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "", "系统默认浏览器": "", + "续期令牌": "續期權杖", "编码套餐 API Key": "", "网关 Secret": "", + "网关会以管理员身份读取马维斯客户端的内存来取凭证 —— 客户端的令牌不落明文(登录库是密文、密钥容器校验调用方签名),只在进程内存里,而客户端进程又设了访问限制,普通权限读不到。所以这一步会弹一次 UAC,点「是」即可,之后无需再操作。请确保客户端处于登录状态。": "", "网页登录": "", "网页登录的打开方式": "", "网页登录(Zai / Google)": "", + "网页登录(浏览器授权)": "", "自定义": "", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "", "获取风控配置失败,将直接尝试领取:{message}": "", "获取验证码": "", + "设备码": "", + "设备码 5 分钟内有效;在授权页确认后这里会自动完成": "", + "设备码 5 分钟内有效;窗口里显示「等待你在浏览器里确认」时去授权页点确认即可": "", + "访客模式": "", + "访客模式(免登录)": "", + "访问令牌": "存取權杖", "该上游无需鉴权(不发送鉴权头)": "", "该提供商": "", "该提供商账号添加功能即将上线": "", @@ -332,11 +417,18 @@ "请填写 {label}": "", "请填写名称": "", "请完成验证…": "", + "请粘贴 www.coze.cn 的完整 Cookie(浏览器开发者工具 → Network → 任取一条 www.coze.cn 请求的 Cookie 头,或 Cookie 编辑器一键导出)。整包带上最稳:任务中心与机器人列表只认 Cookie,少了几项会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {name: value} 对象。Coze 没有刷新机制,登录态过期后重新登录或重新粘贴即可。": "", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "", + "读取 %USERPROFILE%/.minimax/auth 下客户端已保存的令牌,只读不写 —— 客户端用一对必须相等的版本号判断「是否在别处登录过」,写一次就会让它要求你重新扫码。注意:导入是一次快照,之后网关与客户端各自续期,两边的刷新令牌会互相轮换,建议只在一处使用。": "", + "读取 %USERPROFILE%/.minimax/auth/prod/en 下客户端已保存的令牌,只读不写。客户端按「环境 / 地区」分目录存两套登录态,国际版在 en 目录里;没在国际站登录过时这里读不到,请改用上面的网页登录。": "", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "", + "读取本机 QClaw 客户端网关配置(%USERPROFILE%\\.qclaw\\openclaw.json),需 QClaw 正在运行": "", + "读取本机千问办公客户端登录态(%APPDATA%\\QwenWorkCN\\auth-v2.dat),需已在客户端登录过": "", + "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "", + "读本机 QClaw 客户端拉起的 OpenClaw 网关配置(端口 + 令牌)。与别家不同,本家需要 QClaw 正在运行 —— 网关是客户端拉起的,端口和令牌都以它在运行为前提;后端会真连一次做校验。": "", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "", @@ -345,22 +437,29 @@ "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "", + "读本机客户端当前的登录态。它是 Chromium os_crypt 加密的(DPAPI 主密钥 + AES-256-GCM),因此仅 Windows 可用。DPAPI 按当前 Windows 用户解密,不需要客户端正在运行 —— 只要在本机登录过一次即可(换机器时请在目标机器上重新登录一次客户端)。": "", + "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "", "账号信息": "", "账号信息不完整,请刷新后重试": "", "账号已添加,但请留意:{warning}": "", "账号版本": "", "账号类型": "", "账号:{name}
": "", + "贴国际站的访问令牌(mmoat_ 开头)。国内站的令牌拿到这里会被上游拒(401)—— 两地账号体系不同,不能互换。": "", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "", "还没有账号": "", "这一家当前不支持网页登录,请改用填写凭证": "", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "", + "这里填国内站(modelscope.cn)的账号:请粘贴国内站的令牌。国际站(modelscope.ai)是另一家 provider、另一套账号,两边的令牌不通用。": "", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "", + "这里填国际站(modelscope.ai)的账号:请粘贴国际站的令牌。国内站(modelscope.cn)是另一家 provider、另一套账号,两边的令牌不通用。": "", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "", + "适合已在别处登录、或客户端数据目录被改过的场景。只需要 accessToken(mmoat_ 开头);JWT 里能读出身份与到期,无需再填别的。只给访问令牌时约 1 小时有效、无法自动续期 —— 想要自动续期请用上面的网页登录。": "", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "", + "选择一份存着 www.coze.cn Cookie 的文本文件(整段 Cookie 头或 Cookie 编辑器导出的 JSON 都可以)。导入后凭证直接落在网关、可独立转发,不依赖本机客户端(Coze 客户端运行时占着 Cookie 库,读不了)。": "", "选择凭证文件导入": "", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "", @@ -379,6 +478,7 @@ "领取失败:{message}": "", "领取并让官方确认": "", "风控验证配置不完整,请稍后重试": "", + "马维斯 Marvis": "", "验证码": "", "验证码已发送。收到后填入下方并点「登录并添加」": "", "验证码已发送,请查看手机短信": "", diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/agent.json b/desktop-tauri/i18n/i18n-src/zh-Hant/agent.json new file mode 100644 index 00000000..defa6bfa --- /dev/null +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/agent.json @@ -0,0 +1,102 @@ +{ + "· 走网关": "", + "。这里的开关与还原作用在网关所在的机器上。": "", + "一键把本机 AI 客户端接到本地网关:改写它自己的配置文件,保留其它设置": "", + "上游模型 id,例如 raccoon-coder-pro": "", + "下面是这个客户端文件里现有的配置(只读)。点「从客户端导入」把它收进我的配置,之后就能编辑和切换。": "", + "个 · 已安装": "", + "中转": "", + "从客户端导入": "", + "会用最近一次备份覆盖它的配置文件。这一步不会改动你在本页存的配置列表。": "", + "低": "", + "保存修改": "", + "共": "", + "写入中…": "", + "写入后需要重开会话才生效": "", + "写入失败:{reason}": "", + "写入客户端": "", + "写入预览": "", + "单配置": "", + "单配置文件": "", + "只改 enabled 与默认项,其余条目原样保留": "", + "只改属于我们的那几个键,其它设置原样保留": "", + "可同时启用多条;切换只改 enabled 与默认项": "", + "同一时刻只有一套生效;写入 = 把这一套整段写进客户端文件": "", + "启用 {name}": "", + "备注": "", + "多套配置": "", + "多配置 · 已启用 {n}": "", + "官方直连": "", + "客户端列表加载失败:{reason}": "", + "客户端清单": "", + "客户端自己的设置优先;这里写的是写入时带进去的初值": "", + "已保存": "", + "已写入 {name}": "", + "已写入 {name};这个客户端只在启动时读配置,重开一个会话才生效": "", + "已安装": "", + "已按备份还原 {name}": "", + "已收进我的配置:{n} 条": "", + "已还原「{name}」": "", + "当前环境不支持保存(桥接方法缺失)": "", + "当前环境不支持写入(桥接方法缺失)": "", + "当前环境不支持客户端接管(桥接方法缺失)": "", + "当前环境不支持导入(桥接方法缺失)": "", + "当前环境不支持还原(桥接方法缺失)": "", + "手机上只能查看与还原接管。改写配置内容请回桌面端": "", + "按备份还原": "", + "按备份还原 {name}?": "", + "接管会把客户端的 base_url 指向本网关,并在写入前自动备份原文件。": "", + "搜索客户端": "", + "搜索客户端 / 配置文件…": "", + "改写网关所在机器上的客户端配置": "", + "改动": "", + "新增": "", + "新建配置": "", + "新配置": "", + "无配置": "", + "暂无预览": "", + "未安装": "", + "未接管": "", + "未检测到": "", + "未检测到安装": "", + "未走网关": "", + "本机": "", + "本机服务": "", + "本机没有找到 {name}": "", + "本机网关": "", + "来自客户端文件,只读": "", + "模型 id": "", + "正在生成预览…": "", + "正在读取本机客户端…": "", + "每个客户端可以有": "", + "没有匹配的客户端": "", + "没有可导入的新条目(可能还没配过,或已经在我的配置里)": "", + "没有新的条目可收(都已在我的配置里)": "", + "没有检测到可接管的客户端": "", + "确定按最近一次备份还原「{name}」?": "", + "第三方中转": "", + "编辑:{name}": "", + "行号由后端逐行比对给出": "", + "装好后回到这一页点「重新检测」,它会出现在上面的列表里。": "", + "设为默认": "", + "读取客户端配置失败:{reason}": "", + "走网关": "", + "还原中…": "", + "还原失败:{reason}": "", + "还没有配置": "", + "还没有配置。点「新建配置」建一套指向本机网关的,再写入客户端。": "", + "还没有预览。选一套配置后这里会显示写入客户端之后的样子。": "", + "这个客户端会自己重读配置,写入后开新会话即生效。": "", + "这个客户端只在启动时读配置:写入后重开一个会话才生效。": "", + "这个客户端的配置里没有读出地址(可能还没配过,或形状不认识)": "", + "这个客户端自己支持多套配置并存": "", + "这个客户端自己管理多套配置,可同时启用多条;切换只改 enabled 与默认项,不动它的模型清单。": "", + "这套配置是干什么用的(只显示在本页)": "", + "选一个客户端开始": "", + "配置": "", + "配置来源": "", + "需重开会话生效": "", + "高": "", + "(手机读不到本机文件)": "", + "(本机网关 / 官方直连 / 第三方中转 / 本机服务),随时切换。单配置客户端切换 = 整段覆写并备份原文件;多配置客户端(如 ZCode)只改 enabled 与默认项,它自己的条目一律不动。": "" +} diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/assistant.json b/desktop-tauri/i18n/i18n-src/zh-Hant/assistant.json index 130022dd..8f57a461 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/assistant.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/assistant.json @@ -1,32 +1,148 @@ { "CodeArts 的账号余额还剩多少?": "", + "Enter 发送 · Shift+Enter 换行": "", + "section 只能是 models / mappings / both": "section 只能是 models / mappings / both", + "{n} / {total}": "", + "{n} 个账号正在限流:": "", + "{n} 家": "", + "{n} 条改动": "", + "{n} 条映射": "{n} 條對應", + "✓ 完成": "", + "✗ 失败": "", + "「{name}」超过 {n} MB,换一张小一点的。": "", + "一次最多 {n} 张图片。": "", + "一次最多 {n} 张图片,多出的没有添加。": "", + "上面四个快捷查询直接读网关数据,不经过模型": "", + "不指定,交给映射绑定": "", + "今天用了 {tokens} Token,共 {requests} 次请求。": "", + "今天用了多少 Token?": "", + "会话响应不完整": "工作階段回應不完整", + "会话里的选择优先于这里的默认值": "", + "余额是缓存读数,点账号详情里的「刷新凭证」可触发实时查询。": "", + "作为 system 消息注入": "", + "保存思考等級失敗:{message}": "儲存思考等級失敗:{message}", + "保存思考等级失败:{message}": "", "修改模型能力": "", + "共 {n} 个模型条目。": "", + "共 {n} 个模型条目。映射与改名请在桌面端「模型管理」里做。": "", + "共 {n} 个账号,前 {m} 个的余额读数:": "", + "删除会话失败:{message}": "刪除工作階段失敗:{message}", + "删除这条对话": "刪除這段對話", + "助手设置": "", "助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。": "", + "助手设置没读出来,暂时存不了对话模型。": "", + "助手设置没读出来,暂时存不了思考等级 —— 看输入框下方的提示。": "助手設定讀不出來,暫時存不了思考等級 —— 看輸入框下方的提示。", + "助手设置没读出来,暂时存不了思考等级。": "", + "助手设置没读出来,现在还不能发消息 —— 点右上角「助手设置」看看。": "", "助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。": "", + "勾选即注入": "", + "历史对话": "歷史對話", + "原先在「设置 → AI 助手」的配置都搬到这里了": "", + "发消息或做任务…(Enter 发送,Shift+Enter 换行,可粘贴图片)": "", "发起模型测试": "", + "变更前确认": "", + "只支持图片文件(PNG / JPG / WebP / GIF)。": "", + "只读 · 不会改动配置": "", + "只跳过确认,不扩大工具权限;每次执行仍会记录在对话里。": "", + "各账号余额还剩多少?": "", "启用 / 禁用模型": "", "启用 / 禁用账号": "", + "哪些模型在多家重复了?": "", + "图片 {n}": "", + "完全访问": "", + "完全访问已开启:改动类工具会直接执行,不再弹出确认卡。": "", + "完整对话在桌面端「AI 助手」页": "", "对指定模型发一次真实请求,回报连通性与耗时。": "", + "对话由网关转发到上游模型,用量计入请求日志": "", + "已停止生成": "", + "已全部勾选": "", + "已切换到变更前确认": "", + "已切换到完全访问": "", + "已启用的模型": "", + "已调用": "", "开关模型的对外可用性,不改其它属性。": "", + "当前一个工具都没勾选,助手只能纯聊天。点右上角「助手设置」勾上需要的工具。": "", + "当前模型没有声明图片能力,图可能发不上去。": "", + "当前没有同名模型分布在多家。": "", + "当前:已关闭(助手收不到任何系统提示词,工具定义照发)": "", + "当前:已开启 · 使用内置默认提示词": "", + "快捷查询走本地网关,不消耗额度": "", + "思考等级已切换": "", + "思考:跟随模型": "思考:跟隨模型", + "总请求": "", + "成功率": "", + "我可以直接查网关里的真实数据:余额、模型重复、今日用量、限流账号。": "", + "打开会话失败:{message}": "開啟工作階段失敗:{message}", + "执行中": "", + "找到 {n} 组同名模型:": "", "把重复的模型加上家名前缀区分开": "", + "推理等级": "", "改上下文窗口 / 最大输出 Token / 工具调用等能力位。": "", + "改动先列清单": "", + "改动直接执行": "", + "改动类 · 默认需要确认": "", + "改动类工具先列清单,等你点「执行」": "", + "改动类工具直接执行,不再询问": "", + "改回变更前确认": "", "改某一家的整组顺位(先试哪一家)。": "", "改账号在全局队列里的名次,决定主备与切换顺序。": "", "新增 / 删除映射名,支持批量加前缀与同名合并到多家。": "", + "新建对话时使用": "", + "映射": "", + "有什么可以帮你?可以问配置、查余额、让我改模型设置。": "", + "未命名对话": "未命名對話", + "未知的工具:{name}": "", + "未选择": "", + "权限模式": "", + "查询账号": "", "查询账号余额": "", + "模型不支持时会自动忽略": "", + "模型没有返回内容": "", + "正在思考…": "", + "正在生成,点右侧按钮可停止": "", "没有可用的模型": "", + "没有已启用的模型,请先在「模型管理」里启用": "", "测一下当前最快的模型通不通": "", "深度推理": "", + "添加图片": "", + "添加图片(也可粘贴或拖入)": "", + "清空全部": "清空全部", + "清空历史失败:{message}": "清空歷史失敗:{message}", + "现在有哪些账号被限流?": "", + "现在没有账号被限流。": "", "看看哪些模型在多家重复了": "", "禁用会把账号移出转发队列,线路可能因此断供。": "", + "自定义提供商模块未就绪(请刷新界面后重试)": "自訂提供商模組尚未就緒(請重新整理介面後重試)", + "自由对话请用桌面端…": "", "获取模型列表": "", + "要改配置的话,桌面端的助手支持完整对话与工具调用。": "", "设置模型映射 ID": "", + "读取历史会话失败:{message}": "讀取歷史工作階段失敗:{message}", + "读取图片失败": "", + "读取图片失败:{message}": "", + "读取模型清单": "", + "读取模型清单失败": "", + "读取账号列表": "", "读各提供商的模型清单、映射名与启用状态。": "", "读各账号的额度 / 剩余用量。": "", "调整提供商顺位": "", "调整账号优先级": "", + "走本地网关 · 消耗所选账号额度": "", + "跟随模型": "", + "跟随模型(不指定)": "跟隨模型(不指定)", + "跟随网关设置": "", + "还没有历史对话": "還沒有歷史對話", + "还没有账号。": "", + "还没有选择对话模型:在输入框右下角选一个,或点右上角「助手设置」。": "", + "还没有配置默认模型": "", "这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}": "", "这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。": "", + "这个查询还没有实现。": "", + "这条会话没能存进历史:{message}": "這段工作階段沒能存進歷史:{message}", + "这里只改本次会话用的模型,不会改网关里保存的默认值。": "", "连接网关失败({base}):{message}": "", - "配置响应不完整": "" + "选择对话模型": "", + "配置响应不完整": "", + "问点什么…": "", + "默认行为": "" } diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/checkin.json b/desktop-tauri/i18n/i18n-src/zh-Hant/checkin.json index 111c6d9a..59f55654 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/checkin.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/checkin.json @@ -1,12 +1,15 @@ { " · ": "", + "QClaw": "", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "", "UMID 组件安装失败:{error}": "", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy 國內版 · 查詢任務樹後一鍵點亮/領獎 · 含開學季與貓貓旅行 · 點擊行展開明細", "WorkBuddy 国际版保活模型,按顺序回退": "", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "", "{date}(每天 {time})": "", + "{done}/{total} 已签": "", "{label} ×{count}({reason})": "", + "{n} 积分": "", "{n} 项可完成": "{n} 項可完成", "{n} 项待领": "", "{total} 账号 · {done} 已签": "", @@ -22,10 +25,15 @@ "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "", "「免费领积分」的每日任务 · 逐个领取": "", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "", + "一次性福利已全部结清": "", + "一次性福利已结清": "", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "", "一键安装组件": "", "一键完成": "一鍵完成", "一键领取({n})": "", + "七天一轮共 4000 积分(400/400/400/1000/400/400/1000) · 每份 30 天后到期 · 当天重复领是幂等的": "", + "上一轮({time}):{n} 个动作,+{credit} 积分": "上一輪({time}):{n} 個動作,+{credit} 積分", + "上一轮({time}):{n} 项,+{credit} 积分": "", "上次执行:{line}": "", "上次签到时间": "", "上次领取 {time} · 领取需通过滑块验证码": "", @@ -33,16 +41,27 @@ "下次 {date} · 点此管理条目": "", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "", + "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」;QClaw 已停运、积分随迁移到 WorkBuddy 发放,见下方「只读余额(已停运)」。": "", + "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」;QClaw 的积分随登录自动发放,见「自动发放」。": "", + "不参与签到": "", + "不可领": "", "与国内版同一套任务接口 · 站点不同": "", + "与国内版同一套链路 · 站点不同、令牌不通用": "", + "个账号不参与": "", + "今天 · 北京时间自然日": "", + "今天已执行:{list}": "今天已執行:{list}", "今天已领 {n} 份": "", "今天未领": "", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "", "今日已执行": "", "今日已派出": "今日已派出", + "今日已签": "", "今日已领取": "", "今日签到": "", "今日签到进度": "", "从清单里移除": "", + "余额查询中…": "", + "余额查询失败:{error}": "", "保活": "", "保活 {n}": "", "保活+领取": "", @@ -55,6 +74,8 @@ "全部结果": "", "全部账号均可签到": "", "全部领取": "", + "关着:不自动巡检(仍可手动巡检)": "關著:不自動巡檢(仍可手動巡檢)", + "关着:不自动执行(仍可手动执行)": "", "关着:所有条目都不执行": "", "删除失败:{error}": "", "删除定时条目": "", @@ -62,22 +83,27 @@ "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "", "到点时逐个签到,已勾 {n} / {total} 家": "", + "北京时间 · 逗号分隔 · 最多 6 条 · 到点后最多晚 30 秒执行;错过时点开机后会补跑一轮。": "北京時間 · 逗號分隔 · 最多 6 條 · 到點後最多晚 30 秒執行;錯過時點開機後會補跑一輪。", "北京时间 · 错过时点开机后会自动补签": "", "去领取": "", "另有 {n} 个账号不参与每日签到": "", + "只读余额(已停运)": "", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "只領取已完成任務的獎勵(冪等,重複點不會重複入帳)", "可完成": "可完成", "可派出": "可派出", "可选 · 缺省显示时刻": "", + "可领": "", "可领奖": "可領獎", "后端未返回定时签到数据": "", "后端未返回签到中心数据": "", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "", + "在桌面端设置": "", "在途": "在途", "填入缺省链": "", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "", "失败 {n}": "", "套餐明细": "", + "套餐领取": "", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "", "安装中…": "", "官方客户端的每日签到任务": "", @@ -86,29 +112,48 @@ "尚未查询": "尚未查詢", "尚未查询任务状态": "", "巡检一次": "巡檢一次", + "巡检中…": "巡檢中…", "已保存:{label}": "", "已保存:保活模型链": "", "已保存:定时条目": "", + "已保存:成长任务排程": "", + "已保存:猫猫旅行排程": "已儲存:貓貓旅行排程", + "已停运": "", + "已全部领取": "", "已删除定时条目": "", + "已开始批量签到": "", + "已得 {earned} / {total} 积分": "", "已抽 {n} 次": "已抽 {n} 次", + "已提交「{label}」的签到": "", "已有结果 {checked} / 共 {total} 个账号": "", "已签": "", + "已领": "", "已领 {claimed}/{total} · 累计 {earned} 积分": "", "已领养": "已領養", "已领取": "", + "已领取「{name}」": "", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "", + "开关": "開關", "开学季执行失败:{error}": "開學季執行失敗:{error}", "开学季执行完成:{message}": "開學季執行完成:{message}", "开学季活动": "開學季活動", "开着:启用的条目按各自时刻执行": "", + "开着:按下面的时刻自动巡检": "開著:按下面的時刻自動巡檢", + "开着:按下面的时刻自动执行": "", + "当前没有可领取的套餐": "", "待签": "", + "待领取": "", "待领新手福利": "", "总开关": "", "总开关已关闭 · 在「定时签到」页打开": "", "成功 {n}": "", "成长任务": "成長任務", + "成长任务失败:{error}": "", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "成長任務完成({done} 項成功、{failed} 項失敗),積分 +{credit} 能量 +{energy}", + "成长任务定时排程": "", "成长任务执行失败:{error}": "成長任務執行失敗:{error}", + "成长任务:{done} 项完成、{failed} 项失败,+{credit} 积分 +{energy} 能量": "", + "成长任务:{done} 项完成,+{credit} 积分 +{energy} 能量": "", "执行失败:{error}": "", "执行并抽奖": "執行並抽獎", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "執行開學季任務並抽空抽獎機會(分享/對話/專家事件)", @@ -124,14 +169,21 @@ "搜账号 / 收益 / 错误 / 触发原因": "", "收起新建": "", "新建定时条目": "", + "新手任务": "", "新手福利": "", + "新手福利与活动": "", + "新手福利是一次性任务:领完就没了,重复点不会重复加分。": "", "新手福利有 {n} 项领取失败,可重试": "", "无": "", + "无独立签到接口 · 每天替账号打一次登录信息(即当日魔粒)": "", "无独立签到接口 · 每天替账号打一次首次登录积分": "", "无账号": "", + "时刻": "時刻", "暂不可领": "", + "暂不可领(未满足条件)": "", "暂不支持": "暫不支援", "暂无启用的条目 · 点此添加": "", + "最近到期 {date}": "", "有未保存的改动": "", "未勾选提供商": "", "未开启": "", @@ -140,30 +192,45 @@ "未领取": "", "本地台账里今天还没领过;活动期内每天发一份新套餐": "", "本地台账里今天(北京时间)领过的套餐:{plans}": "", + "本站已停止发放积分,余额在原有效期内仍可用": "", "条启用": "", "查询于 {time}": "", "查询任务": "", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "查詢任務樹並一鍵點亮/領獎(含開學季與貓貓旅行)", "查询失败:{error}": "", + "查询无结果": "", "桌面端每日积分链路": "", "正在读取定时签到数据…": "", "正在读取签到数据…": "", "每天 {time}": "", "每天 {time} · {scope}": "", + "每天定点巡检 · 一趟推进一步 · 与签到排程互相独立": "每天定點巡檢 · 一趟推進一步 · 與簽到排程互相獨立", + "每天定点执行成长任务 · 与签到 / 旅行互相独立 · 默认关": "", + "每天按这里的时刻自动巡检一遍 WorkBuddy 国内版账号:没有猫就领养、在家就派出、到站就领奖。一趟只推进一步,所以建议留两个时刻(早上派出、晚上领奖)。企业版与国际版账号自动跳过。": "每天按這裡的時刻自動巡檢一遍 WorkBuddy 國內版帳號:沒有貓就領養、在家就派出、到站就領獎。一趟只推進一步,所以建議留兩個時刻(早上派出、晚上領獎)。企業版與國際版帳號自動跳過。", + "每天按这里的时刻自动执行 WorkBuddy 国内版账号的成长任务:逐个点亮、完成后领奖。夜猫子任务只有 23:00-08:00(北京时间)能点亮,所以缺省时刻是 23:30。默认关闭 —— 它会上报事件并领积分,请确认要自动执行再打开。企业版与国际版账号自动跳过,不可伪造的任务(如真实捐款)也跳过。": "", "每天时刻": "", + "每天自动执行": "", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "", + "每日活动签到(+100 积分,已领则中性提示)": "每日活動簽到(+100 積分,已領則中性提示)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "", + "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "", "每日签到": "", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "", "每账号一条明细 · 点此查看「签到记录」": "", + "没有可签到的账号": "", "没有可领套餐的账号(ZCode 未添加)。": "", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "", + "没有已停运但仍可查余额的账号(目前有 QClaw 一家)。": "", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "沒有支援成長任務的帳號(需要 WorkBuddy 國內版)。", "没有支持新手福利的账号": "", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "", "没有符合筛选条件的签到记录": "", + "没有随登录自动发放积分的账号(目前有 QClaw 一家)。": "", "没有需要执行的任务": "沒有需要執行的任務", + "活动奖励(30 天 / 7 天有效期)+ 下载客户端奖励 · 两条链都幂等": "", + "活动尚未开始(上游接口未部署)": "", + "活动按自然日发新套餐,每天可领一次": "", "活动福利": "", "活动(campaign)领取 · 每天 10:00 刷新": "", "活跃上报完成,但活动未开启": "", @@ -178,17 +245,26 @@ "清空签到记录": "", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "", "点击行展开账号明细 · 单账号签到不受范围限制": "", + "点进去查看任务并领取": "", "独立链路 · 只提供手动入口 · 不进自动签到": "", "猫猫旅行": "貓貓旅行", "猫猫旅行失败:{error}": "貓貓旅行失敗:{error}", "猫猫旅行失败:{message}": "貓貓旅行失敗:{message}", + "猫猫旅行定时排程": "貓貓旅行定時排程", "猫猫旅行:{message}": "貓貓旅行:{message}", + "猫猫旅行:{n} 个动作,+{credit} 积分": "貓貓旅行:{n} 個動作,+{credit} 積分", + "猫猫旅行:{n} 个动作,+{credit} 积分,{failed} 个失败": "貓貓旅行:{n} 個動作,+{credit} 積分,{failed} 個失敗", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "", "确定删除「{name}」?删除后到点不再执行。": "", "确定清空全部签到记录?清空后无法恢复。": "", "确定清空当前筛选出的签到记录?清空后无法恢复。": "", + "福利与套餐的逐项领取在桌面端签到中心完成": "", + "积分随登录到账 · 无需签到 · 只读:余额与到期时间由上游直接给出": "", + "积分随登录自动发放,无需签到": "", "移除 {name}": "", "立即全部签到": "", + "立即巡检全部": "立即巡檢全部", + "立即执行全部": "", "签到": "", "签到中…": "", "签到中心分区": "", @@ -205,6 +281,7 @@ "结算于 {time}": "", "能量 {n}": "能量 {n}", "腾讯每日签到接口 · 仅国内版": "", + "自动发放": "", "至少勾一家才能保存(不勾等于这条条目不签到)": "", "读取定时签到数据失败:{error}": "", "读取签到数据失败:{error}": "", @@ -214,6 +291,9 @@ "还没执行过": "", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "", + "还没有记录": "", + "这个账号没有新手任务": "", + "这些提供商已停止发放积分 · 余额在原有效期内仍可用 · 只读:余额与到期时间由上游直接给出": "", "连登 {n} 天": "連登 {n} 天", "部分": "", "重签": "", @@ -226,8 +306,11 @@ "领取": "", "领取中": "", "领取中…": "", + "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "領取免費 CodingPlan(按需:方案有效時只回讀額度)", "领取失败": "", "领取失败:{error}": "", + "领取每日积分 · 幂等(当天已领回 0,不报错)": "", + "领取通常需要过一次滑块验证;活动套餐多为当天稍晚生效,领取后可在桌面端「账号」里看套餐明细。": "", "(共 {n} 个账号)": "", "(能量 / 连登读数本次未取到)": "(能量/連登讀數本次未取到)", ",日活保活 {n} 个": "" diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/common.json b/desktop-tauri/i18n/i18n-src/zh-Hant/common.json index f4c6afd2..77e27011 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/common.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/common.json @@ -1,7 +1,12 @@ { "无可用账号:{reason}": "", "AI 助手": "", + "Agent": "", + "Agent 接管": "", "Agent2API · 多提供商本地网关": "", + "QQ 群号": "", + "Webhook 地址(https://…)": "", + "{base} / {cat}": "", "{key} 需要 true / false / null": "", "{key} 需要数字": "", "{n} 个启用": "", @@ -12,39 +17,76 @@ "{n} 个账号启用": "", "{n} 条失败请求未读,点开「请求日志」查看": "", "{n} 条错误日志未读,点开「日志」查看": "", + "{n} 项 · {shown} 显示": "", + "{size} · 下载后可直接安装,无需跳转浏览器": "", + "{size} · 点击下载后可直接安装,无需跳转浏览器": "", + "{total} 个菜单 · 显示 {shown} · 隐藏 {hidden} · {groups} 个分组": "", "· 不会改动任何配置": "", "· 每次执行前列改动清单,等你确认": "", "—": "", "★ 下一单走它": "", "✅ Token 已刷新": "", "✅ 已将账号优先级调整到全局第一位": "", + "✅ 测试消息已发出,请查看对应群": "", + "✅ 通知设置已保存": "", "「{name}」匹配到多个账号:{list},请用完整账号名重试": "", "「工具全不勾选 → 不注入系统提示词」:想当纯聊天用时,不必再去关上面的开关。": "", + "「设置」是固定项,含它的分组不能整组隐藏;可以单独隐藏组内其它菜单": "", + "一个都不勾 = 全部订阅(配好渠道就能收到东西)": "", + "下拉刷新": "", "不限模型": "", + "与电脑端一致:每 {n} 分钟": "", + "主导航": "", "代价": "", "代理:池": "", + "会恢复默认的分组、顺序与显隐设置。你自建的分组会被删除(组内菜单不会丢)。": "", "会话内": "", + "会话已过期。重新登录后会自动回到手机端。": "", "会话粘性": "", "低于限制器的阈值,转发时跳过该账号": "", "余额不足或 Token 周期用量到顶,被限制器拦下,自动恢复后回来": "", "余额优先": "", "作为 system 消息注入给助手": "", + "例如:家里、公司": "", + "侧边栏布局模块未加载(ui/sidebar-layout.js 应在 app.js 之前引入)。": "", + "侧边栏没有显示任何菜单。到「设置 → 显示 → 侧边栏菜单」里打开需要的项。": "", + "侧边栏菜单": "", "保存对话模型失败:{message}": "", + "保存并继续": "", "候选队列": "", "停止": "", + "先添加一台网关": "", "全不选": "", "全局队列位次(转发按策略在此序上再排)": "", + "全局(账号表)": "", + "共 {n} 条记录": "", + "共 {total} 个账号,已读到 {queried} 个": "", "关": "", + "关掉时不发任何消息(配置保留)。默认关 —— 没配好就发是打扰。": "", "冷却": "", + "出事了主动推给你(桌面端常驻托盘,不必自己盯着看)": "", + "分组名": "", + "分组名,例如「常用」": "", "列表 = 模型管理页里所有已启用的模型。助手走本地网关发起对话,消耗所选账号的额度。": "", + "创建并进入": "", + "创建管理员账号": "", "删除 {n} 条": "", + "删除分组": "", + "删除分组「{name}」?组内 {n} 个菜单会移到「{target}」,不会丢。": "", + "删除空分组「{name}」?": "", + "删除这个分组(组内菜单移到第一组)": "", "加载助手设置失败:{message}": "", "加载失败:{error}": "", + "加载更早的记录": "", "助手": "", "助手自己用哪个模型生成回复": "", "助手设置读取失败,工具可能已过时。": "", "助手走本地网关,消耗所选账号的额度": "", "勾选即注入该工具;不勾选 = 助手看不到它": "", + "升序": "", + "单签": "", + "去下载": "", + "去登录": "", "参数不完整(需要 account / enabled)": "", "参数不完整(需要 account / position)": "", "参数不完整(需要 provider / id / alias)": "", @@ -52,21 +94,34 @@ "参数不完整(需要 provider / id / enabled)": "", "参数不完整(需要 provider / position)": "", "发现新版本 {latest}(当前 {current}),点开「设置 - 软件更新」可查看更新日志并下载": "", + "发现新版本 {version}": "", "发送": "", + "发送测试": "", "只改映射名,不动上游模型 ID;映射立即生效(新对话即可用)。": "", "只注入工具定义": "", "只读": "", "只读工具": "", "可用": "", + "含固定项「设置」,不可整组隐藏(可以单独隐藏组内其它菜单)": "", "启用后重新参与转发": "", + "启用通知": "", "四种组合的实际效果": "", "回复由 {model} 生成 · 走本地网关": "", + "固定": "", "在全局队列里上移一位": "", "在全局队列里下移一位": "", + "地址已遮住 token(保存时不动它);要更换请整体重新粘贴": "", + "备注名(可留空)": "", + "备注名(可选)": "", "复制失败,请手动选择复制": "", "复制订阅链接": "", "失败 · {status}": "", "失败退避": "", + "奖励 {bonus}": "", + "套餐 {plan}": "", + "安装": "", + "安装包已就绪,点「安装」即可完成更新": "", + "定时": "", "定时任务": "", "实际注入": "", "对话模型": "", @@ -80,118 +135,203 @@ "将把账号「{account}」移到第 {position} 顺位:": "", "将改动 {n} 条映射({parts}):": "", "尚未登录": "", - "已保存": "", + "展开作用域栏": "", + "展开左栏,看 {label} 下的 {n} 个模型": "", + "展开来自{label}的 {n} 个模型": "", + "已下载,待安装": "", "已修改 {n} 个模型": "", "已修改 {n} 个账号": "", "已修改 {n} 项能力": "", "已停止这次回答。": "", "已全部取消勾选": "", + "已关闭自动检查": "", + "已删除分组「{name}」": "", "已勾选 {n} / {total}": "", "已勾选「{label}」": "", "已勾选工具": "", "已取消「{label}」": "", + "已启动安装器": "", + "已复制{label}": "", "已复制:{text}": "", + "已完成 {percent}%": "", + "已开启自动检查": "", "已恢复推荐勾选": "", + "已恢复默认分组:概览 / 转发链路 / 诊断 / 系统": "", "已改动 {n} 条映射": "", + "已新建分组「{name}」,点组头的 + 把菜单移进来": "", + "已是最新版本": "", "已注入 {n} / {total} 个工具 · 系统提示词:{state}": "", + "已添加 {n} 台": "", + "已添加的服务器": "", "已登录": "", + "已离线 · 显示的是缓存内容": "", "已移到第 {n} 顺位": "", "已试过": "", "并发上限 {n}": "", "并发已满,等一个在途回落再参与": "", "并发满": "", "开": "", + "当前": "", "当前一个工具都没勾选,助手只能纯聊天。去「设置 → AI 助手 → 工具」里勾上需要的工具。": "", + "当前版本 {version}": "", + "当前环境不支持读取调度队列": "", "当前:已关闭(助手不收到任何系统提示词,工具定义照发)": "", "当前:已开启 · 使用内置默认提示词(还没自定义)": "", "当前:已开启 · 自定义内容 {n} 字": "", "影响转发": "", "待确认": "", + "总剩余 {total}": "", "恢复上游原值": "", "恢复推荐": "", + "恢复默认侧边栏": "", + "成功领取": "", "我会在动手前把改动列给你确认。": "", + "所有菜单都已在这一组": "", + "手机端打开": "", "打开账号设置": "", "执行": "", + "把菜单移入 {name}": "", + "把菜单移入「{name}」": "", "报表": "", + "拖动分组 {name}": "", + "拖动左侧 ⠿ 调整顺序(菜单可以跨组拖);拨开关控制显隐;点分组名可重命名;组头的 + 把别的菜单移进来;眼睛按钮隐藏整组。改动即时生效并记住,左侧侧边栏就是结果。键盘也能排序:把手聚焦后按 Alt+↑ / ↓。": "", + "拖动菜单 {name}": "", + "拖动调整分组顺序(Alt+↑ / ↓ 也可)": "", "拖动调整队列顺序": "", + "拖动调整顺序,可跨组(Alt+↑ / ↓ 也可)": "", + "按作用域选择:全局账号表,或某一个模型的调度视图": "", + "按厂商": "", "按模型展开队列": "", "按请求的模型展开 —— 与转发选路同一份排序,下一个请求会走 ★ 那位": "", + "按配置顺序逐个发送;一个失败不影响其它": "", "排第 1 的接走所有请求,直到它不能答(限流 / 故障 / 额度尽)才轮到下一位 —— 就是「从上往下」的现状行为。": "", + "接口文档": "", + "控制主侧边栏显示哪些菜单、按什么顺序、怎么分组。隐藏只影响导航显示,不会撤销页面访问权限;被隐藏的页面仍可通过地址直接打开。": "", "提示词": "", "提示词开关": "", + "搜索模型或厂商": "", + "搜索模型或厂商…": "", + "收起作用域栏": "", + "收起来自{label}的 {n} 个模型": "", "改动立即生效;禁用后指向它的请求会落到下一个候选。": "", "改动类工具": "", "改成": "", "文档": "", "新": "", - "新增": "", + "新分组名": "", "新增 {n} 条": "", "新对话": "", + "新建分组": "", "无凭证": "", "日志": "", "映射名": "", + "显示 {name}": "", + "显示分组 {name}": "", + "显示这一组": "", "智能": "", + "更多": "", + "最先作废的一份到期日": "", "最大化": "", "最小化": "", + "最早 {day} 到期": "", "最早 {time} 恢复": "", "有": "", + "有新版本,重启后生效": "", + "服务器": "", "未同步": "", "未就绪": "", "未登录": "", "未知工具「{name}」:它在设置里可能刚被取消勾选,或这一版助手还不认识它。": "", + "未选择服务器": "", "本周期消耗达到限制器上限,转发时跳过": "", "本地网关监听状态": "", + "松开刷新": "", "查余额": "", "查看参数": "", "查看结果": "", "查询该账号的余额": "", "桌面桥不可用(本页需要在 Agent2API 客户端里打开)": "", + "模型清单为空:先去模型管理页点「获取模型」": "", "模型管理": "", "次熔断": "", + "正在下载…": "", + "正在完成人机验证…": "", + "正在检查…": "", "正在识别二维码…": "", + "正在连接…": "", + "每台服务器的登录状态各自独立保存,切换后不用重新登录。地址改掉后需要重新登录。": "", "每条请求轮换到下一个可用账号;新会话交给最近最闲的那位。适合想把手里的号雨露均沾。": "", "每轮": "", "没有已启用的模型": "", + "没有带余额概念的账号。添加账号后,这里会显示各账号的额度余量与到期。": "", "没有找到提供商「{provider}」的账号": "", "没有找到账号「{name}」": "", "没给账号名": "", "注入系统提示词": "", + "活动": "", "浅色": "", + "测试未全部成功,详见下方": "", "消耗该账号额度": "", "消耗额度": "", "深色": "", + "添加": "", + "添加服务器": "", + "添加渠道": "", "清空 = 回到内置默认(与「恢复默认」等价)。": "", + "渠道类型": "", "点「编辑」弹窗改内容;开关关掉就整段不发。": "", + "点击重命名": "", "熔断": "", "熔断时长(秒)": "", + "版本 {version} · 从 GitHub 直接下载安装包": "", + "版本 {version} · 从网关下载安装包": "", + "版本 {version} · 更新包需从 Release 页面获取": "", "用一句话让我查或改本机的模型转发配置。": "", + "用量与额度": "", "用量占比最低的先用;没有读数的账号按队列位次排在有读数的后面。适合「谁空谁上」。": "", + "电脑端已关闭「软件版本检查」,此处只做手动检查": "", + "登录面板": "", "监听 127.0.0.1": "", "直接说要改什么 / 查什么…(Enter 发送,Shift+Enter 换行)": "", + "看这个模型的调度队列:{tip}": "", "确定删除账号「{name}」?{note}": "", "确认后立即生效": "", "禁用会把账号移出转发队列;若这条线路没有别的可用账号,请求会直接失败。": "", "禁用后不参与转发,余额仍可查": "", + "移入「{name}」": "", + "稍后": "", + "空分组 · 点组头的 + 把菜单移进来": "", + "等待窗口": "", "等这一轮跑完…": "", "签到中心": "", "系统提示词 + 工具定义": "", "系统提示词已保存": "", "系统提示词已关闭": "", "系统提示词已开启": "", + "编辑服务器": "", "编辑系统提示词": "", "网关 Key": "", "网关运行中": "", "网络代理": "", "能力位决定下游看到的声明(能不能贴图、能塞多少上下文),不改上游的真实行为。": "", "自动": "", + "至少要保留一个分组": "", + "补签": "", + "订阅哪些事": "", "订阅里的节点会自动同步进下方代理池,账号即可引用。": "", "订阅里的节点会被内核起成本地监听器,镜像到下方代理池(账号按 id 引用,与本页其它出口同一用法)。": "", "订阅链接必须是 http:// 或 https:// 开头": "", "设置": "", + "设置不可隐藏(隐藏了只能清缓存找回来)": "", "诊断": "", + "请允许安装未知来源应用后重试": "", + "请在 Release 页面下载后通过 TestFlight 或 App Store 安装": "", + "请填一个分组名": "", "请填写订阅名称": "", "请求失败:{message}": "", "请求日志": "", + "读不到完整模型清单:{reason}": "", + "读取通知设置失败:{reason}": "", "账号不在当前队列里": "", "账号已删除": "", "账号已在全局队列第一位": "", @@ -200,29 +340,54 @@ "转发失败的账号休息一会儿再回来(连续失败翻倍,上限 1 小时)": "", "转发能力": "", "轮流均摊": "", + "输入这台网关的管理员账号与密码。": "", + "运行日志": "", "运行正常 · {url}": "", "运行状态": "", + "返回": "", "还有额度时被 429 的账号排到队尾,轮一圈才回来 —— 负载摊开而不是每次回到第 1 个": "", + "还没有可用模型(添加账号后可见)": "", + "还没有渠道。点右上角「添加渠道」,把群机器人的 Webhook 地址填进来。": "", + "还没有签到记录": "", "还没有订阅:粘贴订阅链接,或从图片识别二维码导入。": "", "还没有选择对话模型:先在下方选一个(或去「设置 → AI 助手」里选)。": "", + "还没查到余额。到「账号」页点一次「查询余额」,或等自动查询跑完。": "", + "这一版没有提供 Android 安装包": "", + "这一项已隐藏": "", + "这一项正在显示": "", "这个模型下没有可用账号": "", + "这个面板没有管理员,用一把已启用的 API Key 访问(在桌面端「网关 Key」页创建)。": "", "这会真实发起上游请求并消耗额度": "", + "这台网关没有管理员,用一把已启用的 API Key 访问(在桌面端「网关 Key」页创建)。": "", + "这台网关还没有管理员。设置一个账号密码,之后用它登录。": "", + "这家没有返回可用明细(可能额度都在上游侧,或全部已用尽)": "", "这是真实的上游请求,与生产走同一条链路(会记账,报表排除)。": "", "这段文本作为 system 消息排在对话最前面。工具的定义与当前配置概览由网关自动附加,不用写在这里。": "", "连接 {n}": "", + "连接并保存": "", "连续失败到阈值后熔断一段时间;到点放行试一次,连续成功才算恢复": "", "连续失败阈值": "", "连通 · {ms}ms": "", "选择对话模型…": "", "选择模型…": "", + "通知与告警": "", + "通知渠道": "", "都不注入": "", "都不注入(助手退化为纯聊天)": "", + "重启更新": "", "队列是一次性整表提交(后端按新顺序重编优先级),移动会连带调整其它组的相对次序。": "", "队列是一次性整表提交;位置越靠前,路由时越先被选中。": "", + "降序": "", "限流 · {n} 个模型": "", "限流垫底": "", + "隐藏 {name}": "", + "隐藏分组 {name}": "", + "隐藏这一组": "", + "需要网关 API Key": "", "顺序优先": "", + "首页": "", "高危": "", + "({n} 个整组隐藏)": "", "(不会影响客户端登录态)": "", "(仅显示前 20 条,共 {n} 条)": "", "(未指定)": "", diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/docs.json b/desktop-tauri/i18n/i18n-src/zh-Hant/docs.json index 57830feb..72a3f2dd 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/docs.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/docs.json @@ -1,18 +1,28 @@ { " 或 ": "", "Base URL 填进客户端的「API 地址 / Base URL」栏;对话协议按客户端支持的类型三选一(Chat Completions / Responses / Anthropic Messages),三者共用同一套模型与账号池,可随时切换;鉴权用「网关 Key」页里任一启用 Key。": "", + "Claude Code / Anthropic SDK": "", "Claude Code / Anthropic SDK(token 计数:/v1/messages/count_tokens)": "", + "Codex、新版 SDK": "", "OpenAI 新协议(Codex、新版 SDK)": "", + "curl 命令": "", "。一把启用的 Key 都没有时不鉴权(默认网关只监听 127.0.0.1;在设置页开启「局域网访问」后监听所有网卡,鉴权与安全闸门随之启用)。": "", + "。用「网关 Key」页里任一启用的 Key;一把都没有时不鉴权。": "", + "不受鉴权限制,配 Key 前就能拉": "", "启用": "", "填进客户端的「API 地址 / Base URL」": "", + "复制命令": "", "复制地址": "", "多数 OpenAI 兼容客户端的默认协议": "", + "多数兼容客户端的默认协议": "", "客户端自动拉取可用模型;不受鉴权限制,配 Key 之前就能拉": "", "对话协议(按客户端支持的类型选一行)": "", + "快速验证": "", + "手机端只读文档。完整的使用说明与部署指引在仓库的 docs/ 目录。": "", "接口地址": "", "模型列表": "", "模型清单": "", "的 Key:请求带 ": "", + "请求带 ": "", "鉴权用「网关 Key」页里任一": "" } diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/keys.json b/desktop-tauri/i18n/i18n-src/zh-Hant/keys.json index 946d8b2a..b3a51fe8 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/keys.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/keys.json @@ -7,11 +7,13 @@ "✅ Key 已创建,记得复制给客户端": "", "✅ 可用范围已保存": "", "不限制": "", + "不限制提供商与模型": "", "与": "", "例如 Cursor / 公司电脑": "", "保存失败:{reason}": "", "保存范围": "", "创建": "", + "创建于 {time}": "", "创建时间": "", "删除网关 Key": "", "可用提供商": "", @@ -22,13 +24,16 @@ "可用范围 · {name}": "", "启用「{name}」": "", "复制 Key": "", + "客户端请求带上 ": "", "客户端请求需带 ": "", + "已删除": "", "已启用鉴权 · {n} 把 Key 生效": "", "当前不限制:这把 Key 可以用全部提供商与全部对外模型": "", "提供商:{names}": "", "搜索模型…": "", "操作失败:{reason}": "", "新建 API Key": "", + "新建 Key 与设置可用范围(提供商 / 模型白名单)在桌面端完成。": "", "显示": "", "未启用鉴权": "", "未命名": "", @@ -38,9 +43,11 @@ "留空自动生成;手填至少 8 个字符": "", "留空表示不限制;同时设置时请求需同时满足两个条件(模型在白名单内且路由到允许的提供商)。 「可用模型」的候选跟着上面勾选的提供商走:没勾提供商时它是空的(还没有约束范围), 勾了几家就列出这几家能收的全部对外名。": "", "确定删除 Key「{name}」?使用它的客户端会立刻无法访问。": "", + "确定删除「{name}」?用这把 Key 的客户端会立即失效。": "", "网关 Key 表": "", "请先在上面选择可用提供商": "", "读取 Key 列表失败:{reason}": "", + "还没有 Key。去桌面端「网关 Key」页新建一把。": "", "还没有 Key,当前不鉴权": "", "这几家当前没有可用模型(账号未登录或清单为空)": "", "这把 Key 不限制提供商与模型(可用全部上游与全部对外模型)": "", @@ -49,5 +56,7 @@ "(模型候选需先选提供商;不选则沿用当前这几项,保存后仍按模型白名单生效)": "", "(行内「可用范围」):留空 = 不限制,两个都设时 按交集生效 —— 被限制的模型对这把 Key 表现为「不存在」(拉 /v1/models 也看不到它), 提供它的家不在可用列表里时请求同样被拒。": "", "+ 新建 Key": "", - "; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制": "" + "; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制": "", + ";一把启用的 Key 都没有时不鉴权。": "", + ";当前必须带 Key。": "" } diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/logs.json b/desktop-tauri/i18n/i18n-src/zh-Hant/logs.json index 63877023..91684f5c 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/logs.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/logs.json @@ -10,9 +10,13 @@ "信息": "", "全部分类": "", "全部级别": "", + "复制失败": "", + "复制当前筛选结果": "", "导出": "", + "已复制 {n} 条日志": "", "按分类筛选": "", "按最低级别筛选": "", + "搜索关键字": "", "搜索消息关键词": "", "搜索消息关键词…": "", "操作失败:{error}": "", @@ -20,6 +24,7 @@ "暂无日志可导出": "", "模型 {model}": "", "正在加载日志…": "", + "没有符合条件的日志": "", "没有符合筛选条件的日志": "", "清空中…": "", "清空运行日志": "", @@ -27,6 +32,7 @@ "确定清空当前筛选出的 {count} 条日志?清空后无法恢复。": "", "系统事件": "", "警告": "", + "读取日志失败": "", "调试": "", "运行日志已清空": "", "错误": "" diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/models.json b/desktop-tauri/i18n/i18n-src/zh-Hant/models.json index 6a6fe043..7304649b 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/models.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/models.json @@ -36,6 +36,7 @@ "{name}({id})": "", "{name}(不在该家当前清单里)": "", "{n} 个": "", + "{n} 个映射": "", "{n} 个模型": "", "{total} 家 · 成功 {done}": "", "{total} 家 · 成功 {done} · 失败 {failed}": "", @@ -67,8 +68,10 @@ "上游没有给出可读的错误说明": "", "上游没有返回任何模型": "", "上游目录尚未拉到,用的是内置静态清单;点「刷新模型清单」可重试": "", + "上游能列出这个模型,但官方客户端不展示它;本仓已默认关闭它的默认绑定(点这一行的开关可以手动打开)": "上游能列出這個模型,但官方客戶端不展示它;本倉已預設關閉它的預設綁定(點這一列的開關可以手動打開)", "下游客户端": "", "下游请求 ": "", + "不展示": "不展示", "不影响网关的转发与路由": "", "不支持": "", "不覆盖": "", @@ -115,6 +118,7 @@ "刷新全部家(逐家打上游)": "", "刷新完成,但没有得到任何结果": "", "刷新模型管理页里各提供商的远程模型目录,逐家结果列在弹窗里": "", + "刷新清单": "", "加载中…": "", "勾上它,导入后即进入这家的清单": "", "勾几个就并行测几次;默认选队列里最靠前的一个。": "", @@ -126,6 +130,8 @@ "只刷新这一家的模型清单": "", "只属于这一家": "", "可删除 {n} 个(手动登记 / 自定义家);": "", + "可见": "可見", + "可见性": "可見性", "可配思考档位": "", "同一条错误出现在所有账号上": "", "后端桥不可用": "", @@ -140,6 +146,7 @@ "失败": "", "失败 {n} 个 · 首个失败:{error}": "", "完成": "", + "官方客户端会展示这个模型": "官方客戶端會展示這個模型", "对下游声明的能力(工具调用 / 图片识别 / 视频识别 / 支持思考)—— 点击修改": "", "对外名可自由命名,允许与上游模型 ID 同名(同名时该上游的原生路由优先,映射作兜底); 支持字母、数字与 ": "", "对外映射名(下游请求时用)": "", @@ -183,12 +190,14 @@ "批量操作": "", "批量操作 · 已选 {n} 个模型": "", "按倍率排序": "", + "按可见性筛选": "按可見性篩選", "按所选网关对话协议组装请求并校验响应转换(Chat Completions / Responses / Anthropic Messages)。": "按所選閘道對話協定組裝請求並校驗回應轉換(Chat Completions / Responses / Anthropic Messages)。", "按提供商选择": "", "按状态筛选": "", "提供商": "", "搜索模型": "", "搜索模型 ID / 名称 / 映射名…": "", + "搜索模型 ID / 提供商": "", "搜索模型 ID…": "", "搜索账号…": "", "操作": "", @@ -197,6 +206,7 @@ "收起 ▴": "", "数字带虚线底 = 由 models.dev 补齐;右上角小点 = 你手动覆盖过;— = 未声明。点任意一格可改。": "", "新建一个自定义提供商(同时创建它的第一个账号)": "", + "映射、思考等级、上下文窗口与工具调用能力在桌面端「模型管理」里改;手机端只做启停。": "", "映射「{alias} → {target}」不存在": "", "映射上未绑定思考等级,「跟随映射」等于这次不注入": "", "映射上绑定的思考等级是 {level},「跟随映射」按它注入": "", @@ -227,6 +237,7 @@ "模型数": "", "模型映射": "", "模型来源": "", + "模型清单已刷新": "", "模型管理表": "", "模型能力": "", "正在刷新…": "", @@ -235,6 +246,7 @@ "每条映射 = 对外名 → 上游模型;原始 ID 与别名有独立开关,默认绑定永远存在(没有映射时是合成的那条)。": "", "没有匹配「{keyword}」的模型": "", "没有匹配当前筛选的模型": "", + "没有匹配的模型": "", "没有选中任何模型": "", "没有选中的模型": "", "流式请求": "", @@ -324,8 +336,11 @@ "还没有可刷新的提供商:先在账号页添加一个账号": "", "还没有自定义提供商": "", "这一行已不在当前清单里(模型被移除、或目录刷新后上游不再提供它)。关闭后刷新列表再试。": "", + "这个模型在官方客户端里是否会被展列;不展示的那些默认已关闭(含默认绑定)": "這個模型在官方客戶端裡是否會被展列;不展示的那些預設已關閉(含預設綁定)", "这个模型已经在这家的清单里,不重复导入": "", "这几项是给": "", + "这家提供商没有可见性判据(目前只有 Antigravity / Qoder / 小浣熊 / CodeArts 有)": "這家提供商沒有可見性判據(目前只有 Antigravity / Qoder / 小浣熊 / CodeArts 有)", + "这家有可见性判据,但这一条上游没有表态 —— 按可见处理": "這家有可見性判據,但這一條上游沒有表態 —— 按可見處理", "这家没有可用账号(要在账号页启用一个、且凭证完整)": "", "这家用固定模型清单,无可刷新": "", "这家的模型清单是全局的,不跟账号走": "", diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/proxies.json b/desktop-tauri/i18n/i18n-src/zh-Hant/proxies.json index 8f2e7927..de91058d 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/proxies.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/proxies.json @@ -3,10 +3,13 @@ " 提供 AI 账号专用的原生静态住宅 IP(Static Residential ISP):IP 来自真实家庭宽带运营商,Scamalytics 低欺诈评分,每个 IP 独享,不限流量,$3.90 起。": "", " 那一类由「同步 Clash Verge」 镜像进来(进入本页时会自动同步一次):名称、端口、启用都跟随 Clash, 在 Clash 里改或删,这里跟着变 —— 所以它们只读,改请到 Clash Verge 里改。": "", "Clash Verge": "", + "Clash 同步失败:{msg}": "", + "Clash 镜像": "", "YAML 文件": "", "{names} 等 {n} 条": "", "{n} 个出口": "", "{n} 个账号在用": "", + "{n} 个账号引用": "", "{n} 条订阅": "", "{percent}%({received} / {total} MB)": "", "✅ {ip}": "", @@ -29,7 +32,7 @@ "也可以把二维码截图直接粘贴到弹窗里(Ctrl+V)": "", "二维码里没有订阅链接": "", "从图片识别二维码": "", - "代理内核未安装(在「订阅管理」里下载内核后,节点会自动分配监听端口)": "", + "代理内核未安装(在「订阅管理」里下载内核后,被账号引用的节点会自动开监听端口)": "代理核心未安裝(在「訂閱管理」裡下載核心後,被帳號引用的節點會自動開監聽埠)", "代理列表加载失败:{reason}": "", "代理池": "", "代理池表": "", @@ -67,6 +70,8 @@ "内核没有在超时内停下来": "", "内核目录没能删掉(可能仍被进程占用),稍后重试一次": "", "内核运行中": "", + "出口 IP {ip}": "", + "出口可用": "", "出口测试网址": "出口測試網址", "删除代理": "", "删除内核": "", @@ -81,6 +86,7 @@ "只读:在 Clash Verge 里改": "", "只读:在「订阅管理」里改": "", "同步 Clash Verge": "", + "同步 Clash Verge 出口": "", "同步中…": "", "同步失败:{reason}": "", "名字与端口在 Clash Verge 里改;启用可在本页关闭": "", @@ -94,6 +100,7 @@ "启用(禁用的订阅不参与内核同步,它的节点会从代理池移除)": "", "图片读取失败": "", "地址": "", + "地址不完整": "位址不完整", "失败 {n} 个({reason})": "", "存放位置": "", "存放位置 {dir}": "", @@ -106,13 +113,16 @@ "已删除 {n} 个代理": "", "已删除「{name}」": "", "已删除内核(订阅与账号引用保留,重新下载后会重新分配端口)": "", + "已删除内核(订阅与账号引用保留;有文件仍被占用,将在下次启动时清理)": "已刪除核心(訂閱與帳號參照保留;有檔案仍被占用,將在下次啟動時清理)", "已刷新「{name}」,正在同步节点…": "", + "已同步 {n} 项变更": "", "已启用「{name}」": "", "已启用「{name}」,正在补建监听端口(内核重建中)": "", "已在本页禁用(打开开关即恢复)": "", "已新增「{name}」": "", "已新增订阅「{name}」,内核正在同步节点…": "", "已是最新,没有需要同步的变更": "", + "已有账号引用它,但内核还没把监听端口补建出来 —— 稍等片刻,或重新启用这条节点": "已有帳號引用它,但核心還沒把監聽埠補建出來 —— 稍等片刻,或重新啟用這條節點", "已禁用「{name}」": "", "已禁用「{name}」,监听端口正在关闭(内核重建中)": "", "已禁用(未开监听端口)": "", @@ -128,6 +138,7 @@ "当前环境不支持订阅(桥接方法缺失)": "", "当前环境不支持选择文件夹,直接填写绝对路径即可": "", "当前线路不可用:{error};重新选一条或切回直连": "", + "所属订阅已删除,这条节点不会再分配端口 —— 可以删除它": "所屬訂閱已刪除,這條節點不會再分配連接埠 —— 可以刪除它", "手动填写的地址与端口": "", "打开文件夹选择框失败:{reason}": "", "批量删除": "", @@ -143,6 +154,7 @@ "搜索节点": "", "文件读取失败": "", "新增代理": "", + "新增出口、编辑地址与订阅管理在桌面端完成;手机端做启停与连通性测试。": "", "新增订阅": "", "显示 {n} / 共 {total} 个出口": "", "更新中…": "", @@ -155,6 +167,7 @@ "本地文件不会自动更新:要更新节点,重新选择文件后保存;不重选则内容保持不变。": "", "本地文件订阅无需更新(要换内容请编辑后重新选择文件)": "", "本地文件订阅的内容在导入时固定:节点会由内核起成本地监听器并镜像进代理池,节点数稍等几秒由轮询刷新。": "", + "本机没有读到 Clash Verge 配置": "", "本页可编辑、可删除": "", "来自 Clash Verge 同步(名称、端口跟随 Clash),请到 Clash Verge 中修改;启用可在本页关闭": "", "正在下载内核": "", @@ -172,7 +185,7 @@ "测试网址要以 http:// 或 https:// 开头": "測試網址要以 http:// 或 https:// 開頭", "确定删除「{name}」?{used}": "", "确定删除「{name}」?它在代理池里的节点会一并移除,引用这些节点的账号会回退直连。": "", - "确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会变成「未分配端口」,引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。": "", + "确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会失去监听端口(地址列显示「端口准备中」),引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。": "確定刪除已下載的核心{version}?核心檔案與連接埠對應會被移除,訂閱節點會失去監聽埠(位址欄顯示「連接埠準備中」),引用它們的帳號會暫時回退直連;訂閱本身保留,重新下載核心後會重新分配連接埠。", "确定删除选中的 {n} 个代理?": "", "禁用后本端不再用它(引用它的账号回退直连),并会关掉它的本地监听端口": "", "禁用后本端不再用它(引用它的账号回退直连);端口在 Clash Verge 侧,不受影响": "", @@ -192,6 +205,7 @@ "订阅来源": "", "订阅管理": "", "订阅管理({n})": "", + "订阅节点": "", "订阅节点会在本机各占一个监听端口,从该范围里挑空闲的分配;节点多的大订阅把默认范围({base}-{end})用尽时会报「没有可用的节点端口」,这时把范围调大再重启内核。已分配出去的端口不会因改范围而失效。": "訂閱節點會在本機各佔一個監聽連接埠,從該範圍裡挑空閒的分配;節點多的大型訂閱把預設範圍({base}-{end})用盡時會報「沒有可用的節點連接埠」,這時把範圍調大再重啟核心。已分配出去的連接埠不會因改範圍而失效。", "订阅里的节点会自动同步进代理池,账号即可引用。": "", "订阅里的节点会被内核起成本地监听器,镜像到代理池(账号按 id 引用,与本页其它出口同一用法)。内核只在用到订阅时按需下载(约 20-40MB),不随安装包分发。": "", @@ -207,8 +221,9 @@ "跳过 {n} 个(同步只读条目)": "", "还没有代理。点右上角「+ 新增代理」手填一个,或在「订阅管理」里导入订阅节点、同步 Clash Verge 的出口。": "", "还没有代理。点右上角「+ 新增代理」手填一个,或点「同步 Clash Verge」把 Clash 的出口导进来。": "", - "还没有监听端口(还没有账号引用它 —— 在账号里选它并保存后,内核会自动开端口)": "", + "还没有出口。在桌面端「网络代理」页新增,或点上面同步 Clash。": "", "还没有订阅:粘贴订阅链接、导入本地 YAML 文件,或从图片识别二维码。": "", + "还没有账号使用它(端口只在被账号引用时才分配 —— 在账号里选它并保存后,内核会自动开端口)": "還沒有帳號使用它(連接埠只在被帳號引用時才分配 —— 在帳號裡選它並儲存後,核心會自動開埠)", "这条在 Clash Verge 里是停用的,本页启用不生效 —— 到 Clash Verge 里打开它": "", "连接成功": "", "连通性": "", @@ -220,6 +235,7 @@ "部分测试结果没能记下:{reason}": "", "重启内核": "", "重新选择文件…": "", + "镜像当前出口集合,名称与端口跟随 Clash": "", "需要干净的出口?": "", "首次购买使用优惠码 AGENT2API25 享 75 折。": "", "+ 新增代理": "", diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/report.json b/desktop-tauri/i18n/i18n-src/zh-Hant/report.json index d7aa34d5..5724b89f 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/report.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/report.json @@ -1,6 +1,7 @@ { "30 天": "", "7 天": "", + "Key": "", "Token": "", "Top 提供商": "", "Top 模型": "", @@ -12,6 +13,11 @@ "{m}月{d}日 周{wd}": "", "{name} · {tokens} tokens · {n} 次请求 · {pct}": "", "{name}:{tokens} tokens · 占 {pct}%": "", + "{n} 个账号启用 · {m} 个限流中": "", + "{n} 个账号正在限流,转发会自动跳过它们": "", + "{n} 天": "", + "{n} 次请求": "", + "{start} ~ {end}": "", "{tokens} tokens · 占 {pct}": "", "…还有 {n} 个模型": "", "一": "", @@ -19,14 +25,21 @@ "二": "", "五": "", "今天": "", + "今天都签完了": "", + "今日签到 {done} / {total}": "", + "今日签到 {done}/{total}": "", + "今日请求": "", "六": "", "区间共 {n} 天": "", + "区间内累计": "", + "可用账号": "", "右轴": "", "含今天在内往前数": "", "命中 {hit} / 输入 {input}": "", "命中率 = 缓存读取 Token / 输入 Token(promptTokens)。窗口越短越贴近「此刻」:上游把缓存读取与输入分开上报时,该比值合法地可能超过 100%,此处不夹取值、如实展示。": "", "命中率 {rate}(命中 {hit} / 输入 {input})": "", "四": "", + "失败 {n} 次": "", "左轴": "", "开始日期": "", "当前连续天数": "", @@ -46,32 +59,49 @@ "所选范围内还没有请求记录": "", "所选范围内还没有账号用量": "", "报表时间范围": "", + "报表视角": "", + "按天 Token 用量": "", "按天 Token 趋势": "", "按本地整点,无请求的整点不标注": "", "提供商用": "", "提供商用占比": "", + "提供商用量": "", "提供商:{text}": "", "方格为本地自然日,无请求显示为空槽": "", "无请求的整点按 0% 计,但不写读数": "", "日": "", + "时间范围。「今天」是本地自然日今天 0 点到现在;「昨天」是昨天一整天(0 点到 24 点);「近 7 天 / 近 30 天」含今天在内往前数,即今天与之前的 6 / 29 天,不是「不含今天的 7 / 30 天」。": "時間範圍。「今天」是本地自然日今天 0 點到現在;「昨天」是昨天一整天(0 點到 24 點);「近 7 天 / 近 30 天」含今天在內往前數,即今天與之前的 6 / 29 天,不是「不含今天的 7 / 30 天」。", + "昨天": "昨天", "暂无热力图数据": "", "暂无缓存趋势数据": "", "暂无趋势数据": "", + "最近一次请求使用的模型": "", + "最近请求": "", + "最近请求 {n} 条": "", "未知模型": "", + "未配置上游": "", "本月": "", "模型用量": "", "模型用量占比": "", "每天的总 Token 用量(输入 + 输出)。柱顶标出当天的读数;区间很长、柱子挤到放不下时只标最高的那几天,其余悬停柱子即可读到。": "", + "活跃": "", "活跃天数": "", "活跃热力图": "", + "管理": "", + "累计请求 {n} 条": "", "结束日期": "", "结束日期(含当天)": "", + "统计区间加载中": "", "统计概览": "", "缓存命中率": "", "自定义区间": "", + "请求量": "", "读取报表失败:{error}": "", + "账号分布": "", + "账号用量": "", "起始日期(含当天)": "", "输入 + 输出": "", + "运行中": "", "近 1 小时": "", "近 10 分钟": "", "近 24 小时": "", @@ -80,5 +110,9 @@ "近 30 天": "", "近 365 天活跃热力图": "", "近 365 天,按天 Token 用量着色(档位按本窗口峰值自动定)": "", - "近 7 天": "" + "近 7 天": "", + "还有 {n} 个待签": "", + "还没有添加账号": "", + "还没有请求记录": "", + "连续天数": "" } diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/requests.json b/desktop-tauri/i18n/i18n-src/zh-Hant/requests.json index 4434a318..bbc58101 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/requests.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/requests.json @@ -41,6 +41,7 @@ "切换路径:": "", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "", + "加载更早的请求": "", "压缩中…": "", "压缩失败:{message}": "", "压缩已在后台开始,完成后会自动提示": "", @@ -49,6 +50,7 @@ "压缩数据库": "", "压缩正在后台执行,完成后自动恢复": "", "原始报文": "", + "发给上游的模型": "", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "", "另有 {n} 个词命中": "", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "", @@ -62,14 +64,18 @@ "响应头": "", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "", + "在途 {n}": "", "失败请求不记录用量": "", "失败({status}):{error}": "", "失败:{error}": "", "实际发给上游 {bytes} 字节": "", "客户端": "", + "客户端请求的模型": "", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "", "尝试 {n} ·": "", "尝试明细": "", + "尝试次数": "", + "尝试链": "", "工具结果": "", "工具结果 · {id}": "", "工具结果图片": "", @@ -77,6 +83,7 @@ "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "", "已抹掉 {count} 条报文原文": "", "已用时(请求仍在转发中)": "", + "当前筛选下没有记录": "", "成功": "", "成功({status})": "", "执行清理": "", @@ -88,6 +95,7 @@ "按模型筛选": "", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "", "按请求结果筛选": "", + "换号链:{chain}": "", "排队中": "", "提供商 / 账号": "", "提供商: {provider}": "", @@ -98,12 +106,15 @@ "无报文原文(未开启「调试模式」,或已被清理)": "", "无结果记录": "", "时间": "", + "暂无请求日志": "", "暂无请求日志,网关还没有转发过请求": "", "最终承载:{picked}": "", + "有重试": "", "本轮尝试失败,正在退避等待或切换到下一个账号": "", "来源客户端:{client}": "", "查看该请求的上游原始报文": "", "模型": "", + "模型映射:{from} → {to}": "", "模型测试": "", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "", "正在加载请求日志…": "", @@ -130,6 +141,7 @@ "缓存读取: - / 命中率: -": "", "缓存读取: {cache} / 命中率: {rate}": "", "耗时": "", + "脱敏命中:{words}": "", "被跳过:": "", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "", "详情": "", @@ -148,7 +160,9 @@ "路由回放": "", "转发到上游的模型:{model}": "", "轮次": "", + "输入 Token": "", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "", + "输出 Token": "", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "", "这条明细没有记录提供商(旧数据或请求未走到转发)": "", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "", @@ -160,11 +174,13 @@ "进行中…": "", "连接中": "", "重试 {n}": "", + "重试 {n} 次": "", "重试中": "", "重试次数": "", "预览对话": "", "预览读取失败": "", "预览读取失败:{error}": "", + "首响": "", "首响 {time}": "", "首响:上游首帧到达的耗时": "", "首响:上游首帧到达的耗时(请求仍在转发中)": "", diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/settings.json b/desktop-tauri/i18n/i18n-src/zh-Hant/settings.json index 41558681..41f45741 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/settings.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/settings.json @@ -2,29 +2,58 @@ " 到 {file}": "", " 正文已改:{edited}(其余段用官方原文)。": "", "(无 id)": "", + "/v1 拒绝无 Key 请求": "", "0 B": "", "100%(默认)": "", "<本机IP>": "", + "Access Key ID": "", + "Agent2API 手机端": "", + "Android 应用(APK)": "", "Cline 上游按请求头判定「调用方是不是 Cline 自家产品」:X-CLIENT-TYPE: cline-sdk 是硬门槛(缺了它免费池模型一律 403),版本号与平台标识让请求更贴近官方客户端的形态。这里列出的是网关默认会发的头:改某一行的值 = 覆盖默认值(也可以新增默认清单之外的自定义头);把值清空保存 = 这个头不发送;在界面删掉被改过的行 = 回落默认值。改完点「保存」,下一个 Cline 请求立即生效。不懂某一行的含义就不要动它 —— 伪装头的价值在「与官方客户端逐字一致」。": "", "Cline 伪装头": "", "Cline 伪装头已刷新": "", + "Docker 容器": "", + "Endpoint": "", + "Endpoint 填 https://<账户ID>.r2.cloudflarestorage.com,Region 固定填 auto。": "", + "Endpoint 填 https://cos.ap-guangzhou.myqcloud.com,Region 填 ap-guangzhou(按实际地域改)。": "", + "Endpoint 填 https://obs.cn-north-4.myhuaweicloud.com,Region 填 cn-north-4(按实际地域改)。": "", + "Endpoint 填 https://oss-cn-hangzhou.aliyuncs.com,Region 填 cn-hangzhou(按实际地域改)。": "", + "Endpoint 形如 http://192.168.1.10:9000(自建通常用 http);Region 一般填 us-east-1。": "", + "Endpoint 留空即走 AWS 官方地址;Region 必须与桶所在区域一致。": "", + "Linux 安装包(deb)": "", + "Nextcloud": "", + "OpenAI 新版 API 用 developer 角色取代 system 承载指令,新版 OpenAI SDK 与部分客户端发的对话里会出现 role 为 developer 的消息;不少 OpenAI 兼容上游(尤其是自定义提供商的透传路径)不认识这个角色,收到就报 400。开启本项后,网关在每次转发前把 developer 角色的消息改写为 system —— 两类角色在 OpenAI 语义上等价(OpenAI 自己的迁移指南也是这么映射的),改写不影响对话内容与语义;对本来就只认 system 的各家内置上游同样无害。默认开启;关掉后 developer 消息会原样发往上游,不认识它的上游会报「角色不存在」一类的错误。": "", + "QQ 群 · Telegram · Discord": "", + "S3 兼容存储": "", + "Safari 没有安装按钮:点底部的「分享」,选「添加到主屏幕」,再点「添加」。": "", + "Secret Access Key": "", "Token 读数量级": "", "UTF-8 文本文件路径。留空用内置默认;只在「替换 / 追加」下读取。": "", "UTF-8 文本文件路径,留空用内置默认提示词(通用编码助手提示词,不含任何会被上游拦截的模板句)。只在「替换 / 追加」模式下生效,透传模式不读它。": "", + "WebView / 浏览器内核": "", + "Windows 安装包(NSIS)": "", + "Windows 服务": "", "ZCode 套餐池(Start Plan)通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。": "", "ZCode 活动套餐通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。": "", "ZCode「套餐池(Start Plan)」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。": "", "ZCode「活动套餐」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。": "", + "beta 版": "", + "iOS 应用": "", + "models.dev 元数据": "", "models.dev 元数据已更新({count} 条)": "", "models.dev 是一个开源的模型资料库(社区维护),这里用它补齐「上游没告诉我们」的模型能力:上下文窗口、最大输出、工具调用、图片、视频、是否支持思考,以及这个模型认识哪些思考档位。优先级是:你在「模型能力」里手工改的值 > 上游清单自己声明的值 > models.dev 的值 —— 它只填上游没声明的空位,从不推翻上游已声明的。网关随包内嵌了一份快照,所以离线、首次启动、访问不通时也有数据;开启时会按「模型目录刷新」任务的间隔联网更新(同一轮里一并刷新,也可在这里立即刷新)。关闭只停联网刷新,已经拿到的数据继续用。": "", "models.dev 模型元数据": "", + "systemd 服务": "", "{count} 条": "", "{count} 条 · 来自{origin}{at} · {mode}": "", + "{d} 天 {h} 小时": "", + "{h} 小时 {m} 分钟": "", "{label} 提示词文件": "", "{label} 的提示词模式": "", "{label}({message})": "", "{lines} 行、{chars} 字符(按当前草稿计)": "", "{mode}生效:上游收到的 system 来自{source}{lines}。": "", + "{m} 分钟": "", "{noun}不能为空(可填 {min}–{max})": "", "{noun}必须在 {min}–{max} 之间(当前填的是 {text})": "", "{noun}必须是整数": "", @@ -33,31 +62,43 @@ "{n} KB": "", "{n} MB": "", "{n} 个头将随每个 Cline 请求发送": "", + "{n} 台可选 · 点此切换": "", + "…": "", "⚠️ 密钥警告": "", + "⚠️ 已关闭「{label}」的机器人校验": "", "⚠️ 机器人校验已关闭": "", "✅ Cline 伪装头已保存": "", "✅ models.dev 模型元数据已开启": "", + "✅ 云同步设置已保存": "", "✅ 出站指纹脱敏已开启": "", + "✅ 密码已修改,其他设备的登录已失效": "", "✅ 导入完成:{summary}": "", + "✅ 已上传到云端({size})": "", "✅ 已为「{label}」装上网关自带提示词": "", + "✅ 已从云端恢复{note}": "", "✅ 已保存:{label}": "", "✅ 已保存:{label} {value}": "", "✅ 已保存:指定错误码直接换号": "", "✅ 已保留 {days} 天": "", "✅ 已保留 {days} 天,超出部分已清理": "", "✅ 已备份 {n} 张表({size})到 {file}": "", + "✅ 已导入账号密钥,请继续下载": "", "✅ 已导出 {count} 个账号{note}{file}": "", + "✅ 已开启「{label}」的机器人校验": "", "✅ 已恢复 {n} 张表{note}": "", - "✅ 已改用 k / M 缩写": "", + "✅ 已改用 k / M / B 缩写": "✅ 已改用 k / M / B 縮寫", "✅ 已改用本地量级词(万 / 亿式)": "", "✅ 已自动创建网关 Key「默认」(可在「网关 Key」页查看)": "", "✅ 已解除内容拦截降级": "", + "✅ 开发者角色兼容已开启": "", "✅ 机器人校验已开启": "", "✅ 设置已保存,应用正在重启…": "", "✅ 调试模式已关闭": "", "✅ 调试模式已开启": "", "✅ 路由设置已保存": "", + "✅ 连接成功:{path}": "", "、{n} 个自定义提供商": "", + "。事件日志、请求明细、调试报文与面板登录态保持不变。替换前会自动把当前业务数据备份到配置目录的 backups/ 下。": "", "「{label}」将从 {previous} 天改为 {days} 天。": "", "「{label}」改用内置默认提示词": "", "「{label}」的提示词文件已更新": "", @@ -72,22 +113,46 @@ "三档数据各自独立计时,超出保留天数的部分会被删除:事件日志是登录、账号切换、429 切换这类系统事件;请求日志是网关每次转发到上游的逐条记录;按天聚合供报表页的热力图与按天趋势使用。请求日志与按天聚合默认 99999 天(约 274 年,效果上等于全量保留),事件日志默认 30 天。把某一档改小(例如 30 天改成 7 天)保存后会立即删除超出的历史数据,此操作不可恢复;改大或保持不变不会删除任何数据。三档的可填范围均为 1–99999 天。": "", "三段各自成块,所以分开编辑": "", "三段必须各自成块(上游按结构校验,并成一段会被回 405),所以这里分开编辑。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。": "", + "上传": "", + "上传中…": "", + "上传内容含加密后的账号凭证与网关配置": "", + "上传到云端": "", + "上传失败:{message}": "", + "上次同步:{time}({action})": "", "上次成功刷新的结果(存在本机数据库里)": "", + "上游 CLI 版本": "", + "上游客户端版本": "", "上游按": "", "上游按「结构」校验身份:三段必须各自成块,所以这里分开编辑三段(不能合成一段)。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。第三段发出时前面会自带一个空行(官方形状)。": "", + "上游未配置": "", "上游模型繁忙时会先排队(回一句「建议 N 秒后再来」,Qoder 免费模型就是这样):网关等一会儿再发同一请求,最多等几次。默认 2 次,可填 0–10;填 0 表示不等待,排队直接返回 503 并说明「不是登录态或额度问题」。": "", "上游用「逐字精确匹配」的方式审核请求体(不是语义审核):客户端注入的固定模板句、计费头字段名、以及某些裸错误码出现在报文里就会整单拦截,返回 400。开启本项后,网关在每次转发前改写这些指纹 —— 表头键值整段删除,承载语义的模板句只换一个词(如 official CLI for Claude → official CLI tool for Claude),对话内容与语义都不受影响。规则集是内置的,不需要也无法维护词表。关掉本项后客户端模板会原样发往上游,可能重新出现模板句被误拦的报错。": "", "上游请求四个阶段各自的等待上限(秒,1~3600):①「连接中超时」= 建立 TCP/TLS 连接或代理隧道的最大等待,默认 30 秒;②「等待响应超时」= 请求发出后等上游响应头的最大时间,默认 300 秒 —— 网关请求上游恒带 stream,响应头在 SSE 建立时就到达,与模型思考多久无关;③「流式响应空闲超时」= 流式响应相邻两块数据之间允许的最大空闲,收到新数据即重新计时,默认 300 秒 —— 上游长时间不吐数据即判定连接僵死并断开;④「非流式响应超时」= 读完整份非流式响应体的总预算(一次性计时、不重置),默认 300 秒。四项都是「等待上限」,只要数据还在来,正常的流式回答就一直往下传。保存后对下一个请求立即生效,不用重启。": "", + "下载失败:{message}": "", + "不一致": "", "不做会话识别,每条请求都重新按策略选路。": "", "不可用": "", "不在这里列出的提供商都用上面的全局配置;「跟随默认」取消这一家的单独配置。带「网关自带」的家列在这里:那段文本是网关自己装上去的(可关、可改正文),与「模式」是两件事。": "", + "与网关同源的面板;完整管理功能在桌面端": "", + "两张开关分别管桌面端(应用内窗口,也包含开启局域网访问后从其他设备访问的那份面板)与网页端(Docker / 服务端自托管的浏览器面板)。默认都开启;桌面端只在本机使用、嫌验证耗时可以关掉,网页端暴露在网络上,建议保持开启。": "", + "两次输入的新密码不一致": "", "两次重试之间的等待时间,两项共用,可填 0–300 秒。": "", "为了安全,开启前需要先注册一个": "", "主进程未返回启动设置,请更新桌面端后重试": "", "主进程桥不可用": "", + "主题": "", "事件日志": "", "事件日志保留天数": "", + "云同步": "", + "人机验证": "", + "人机验证失败": "", + "人机验证已完成": "", + "人机验证还在计算,请稍候": "", "仅用现有数据、不联网刷新": "", + "从云端恢复": "", + "从桌面图标启动,全屏、无地址栏。想卸载:长按图标删除,或清除浏览器站点数据。": "", + "位置": "", + "位置与端口": "", "使用中文单位(亿 / 万)": "", "使用本地量级词(万 / 亿式)": "", "保存上游原始报文(请求头、请求体、响应头、响应体)": "", @@ -102,16 +167,21 @@ "保存后立即生效,不用重启;超时按上游失败处理(计入请求日志的「错误」列,并按「请求重试」的设置决定是否重试)。": "", "保存后立即生效:改小保留天数会立刻删除超出的历史数据,且不可恢复。": "", "保存并重启": "", + "保活换行丢弃": "", "保留天数已刷新": "", + "修改密码": "", + "修改密码需要输入当前密码;修改成功后,其他设备 / 浏览器上的登录会立即失效,当前这条会话保留。": "", "值": "", "值与默认值相同的行不占覆盖(存的是「改过的键」,不是全量配置);值留空保存表示这个头不发送,删除改过的行表示回落默认值。改完立即生效,不用重启。": "", "值(留空 = 不发送)": "", "允许局域网内的设备访问网关": "", + "允许无 Key 裸跑": "", "允许浏览器里的页面跨来源调用网关(/v1/*)": "", "全局配置": "", "全部已配置": "", "全部数据(账号、事件日志、请求记录、调试报文、设置)保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "", "全部数据(账号、事件日志、请求记录、调试报文、设置)统一保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "", + "关闭后手动上传 / 下载也会被拒绝。": "", "关闭后网关回到只监听 127.0.0.1,局域网内的设备将无法继续访问;已配置的网关 Key 与账号都不受影响。": "", "关闭后,管理界面不再从局域网提供,只有本机的桌面程序可以管理;已开启的 API 转发不受影响。": "", "关闭局域网访问": "", @@ -122,16 +192,24 @@ "其余沿用上面的全局配置": "", "内容拦截降级": "", "内置默认提示词": "", + "再输入一次新密码": "", "凭据类请求头已脱敏。": "", + "凭证只保存在这台手机上,按服务器分别存放": "", + "出问题时把这一页截图,或点「复制全部信息」把文本贴进反馈,一般就能定位到大致原因。": "", + "分享图标在 Safari 底部工具栏中间": "", "切换账号重试次数": "", "刚刚从 models.dev 拉到的最新数据": "", "删除 {code}": "", "删除状态码 {code}": "", "刷新": "", "刷新失败:{message}": "", + "前台运行": "", "剥离上游审核黑名单指纹(改写请求体里的模板句与表头)": "", "功能建议": "", "包含日志与调试报文": "", + "包含:{items}": "", + "区域(Region)": "", + "华为云 OBS": "", "单轮铸造上限": "", "占用大小": "", "去反馈": "", @@ -142,25 +220,46 @@ "变更网页管理面板": "", "只列单独配置过的家,其余沿用上面的全局配置": "", "只对「排队制」的上游生效(目前是 Qoder 的免费模型):模型繁忙时上游不报错,只回一句「建议 N 秒后再来」(业务码 10605),网关按建议时长等一会儿再发同一请求,等满次数仍排不上才把「排队中」作为错误返回(HTTP 503,文案会说明这不是登录态或额度问题)。等待发生在首个字节之前,吃的是「等待响应超时」那份预算 —— 两项设置一起决定一次请求最多卡多久;排队不会标记账号限额、也不会换账号(换谁都一样在排队)。保存后对下一个请求立即生效,不用重启。": "", + "只清掉这个浏览器存的 Key,不影响网关": "", + "可回滚到上一版本": "", + "合计只累加当前筛选后可见的账号,筛选、搜索一改数字就跟着变。": "合計只累加目前篩選後可見的帳號,篩選、搜尋一改數字就跟著變。", + "合计按整组统计(默认):不受筛选、搜索影响,被筛掉与已禁用的账号也计入。": "合計按整組統計(預設):不受篩選、搜尋影響,被篩掉與已停用的帳號也計入。", "同一会话在 5 分钟内尽量跟住同一个账号;停了 5 分钟再发就算新会话。多数场景用它 —— 多轮对话少换号,也不至于把账号焊死。": "", "同一会话在 6 小时内跟住同一个账号;适合一坐几个小时的长会话,上游的上下文缓存能吃到更多。": "", "同一账号重试次数": "", "同时开放网页管理面板": "", + "同步密码": "", "名单最多 {max} 个状态码": "", + "后端没有返回版本信息": "", "启动与托盘": "", + "启动时间": "", + "启用云同步": "", "命中这些上游状态码的失败不在同一账号重发,直接换下一个账号继续试(换号占用「切换账号重试次数」,换满仍失败才把错误原样返回给客户端)。输入 100–599 的状态码后回车添加,点 × 或退格删除;默认 402(积分不足)。清空全部表示任何错误都照常重试。": "", "在同一个账号上原地重发几次(不含首发;整条请求共用一份,用完后换来的账号只发一次就继续换)。可填 0–10,0 表示失败立即换号。": "", "在这里直接编辑提示词正文:保存后以这份文本为准(优先于上面的提示词文件),清空则回到文件 / 内置默认。改一个字不必再去编辑器里开文件。": "", + "在途请求": "", + "地址格式不对。填「IP 或域名:端口」,可带 http:// 或 https://,例如 192.168.1.23:3065": "", + "坚果云": "", + "坚果云需使用「第三方应用密码」(账户信息 → 安全选项里生成),地址必须是 /dav/ 下的目录。": "", + "填「IP 或域名:端口」。协议可写 http:// 或 https://,不写默认 http;http 不写端口默认 3065,https 默认 443。地址里的 /m、/v1 会自动去掉。": "", "备份中…": "", "备份整库": "", "备份文件内含全部账号凭证、网关 API Key 与面板管理员凭据,可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "", + "复制全部信息": "", + "复制给客户端": "", + "外观": "", + "外观 · 接入 · 关于": "", "天": "", "天数": "", "失败 {n} 个": "", "失败 {n} 个:{detail}": "", "失败后最多再换几个账号试(填 N = 最多换 N 个,首发那个不算)。换到同一家名下另一个账号、或换到另一家,都各算一次;换满仍失败就返回错误。可填 0–10,0 表示失败立即报错。": "", + "存储桶(Bucket)": "", "存储概况已刷新": "", "安全": "", + "安装到主屏幕": "", + "安装到桌面": "", + "安装包形态": "", "官方原文只有一句:声明自己是 ZCode。": "", "客户端 system 原样出站。": "", "客户端 system 原样出站(只靠指纹脱敏改写模板句)。": "", @@ -168,44 +267,69 @@ "客户端(Claude Code / Codex 等 CLI)会往 system 里塞几十句固定模板,上游逐字审核,命中就整单拦截(HTTP 400)。「模式」决定网关怎么处理这份 system:透传 = 原样不动(默认);替换 = 删掉客户端的 system / developer,换成网关提示词;追加 = 保留客户端内容,只在最前面插一条网关提示词。网关提示词来自「提示词文件」或界面里编辑的正文(正文优先,清空回到文件 / 内置默认)。撞上拦截时会自动改用一段最小中性提示词,次日 00:00 自动恢复,也可以随时解除。": "", "家之间仍按优先级,只有同一家内部轮流用各账号 —— 想优先用某几家、又不愿把某一家的某个账号焊死时用它。": "", "家内轮询": "", + "容器内更新": "", "密码至少 8 位": "", + "导入前会把本机的 account.key 改名备份(account.key.bak-时间戳),不会直接删除。": "", "导入完成:{summary}": "", + "导入密钥失败:{message}": "", + "导入密钥并继续": "", "导入账号": "", "导出中…": "", "导出会把全部账号与自定义提供商定义写入一个 JSON 文件,可以拷到另一台机器上导入后继续使用。导入采用合并策略:同提供商下按业务身份(UID / userId / apiKey 等)去重 —— 已存在的账号只更新凭证,保留本机原有的优先级顺序;新账号追加到转发顺序末尾,不会抢占当前正在使用的账号;自定义提供商定义按 id 合并,本机缺失时自动补建。": "", "导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义": "", "导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义,可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "", "导出账号": "", + "尚未注册管理员:管理接口不需要登录。开启「允许局域网访问」时会引导注册,也可以从登录页注册。": "", "局域网开放后,管理接口要求登录。请设置管理员账号与密码,注册完成会直接继续开启流程。": "", "局域网访问": "", + "展开或收起设置分类": "", "工具用法与环境说明那一大段。": "", "已为「{label}」单独配置提示词": "", + "已作为独立应用运行": "", "已保存 {count} / {limit} 条报文(超出后丢弃最旧的)。": "", + "已保存,留空沿用": "", "已关闭": "", "已关闭 models.dev 联网刷新": "", "已关闭。{note}": "", "已关闭「{label}」的网关自带提示词:能否通过上游校验取决于上游当前口径": "", "已关闭出站指纹脱敏": "", + "已关闭开发者角色兼容": "", "已关闭网关跨域访问": "", "已取消「{label}」的单独配置": "", + "已取消安装": "", "已取消导入": "", "已取消导出": "", + "已取消恢复": "已取消恢復", + "已启用登录": "", + "已复制全部版本信息": "", "已应用": "", "已开启": "", "已开启:其他设备把 API 地址指向 {url}{panel}。": "", + "已指定出口": "", "已接管": "", "已改回内置默认提示词": "", "已清掉「{label}」界面编辑的正文": "", "已清掉界面编辑的提示词(回落文件 / 内置默认)": "", + "已清除本机保存的 API Key": "", "已生效": "", "已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。": "", "已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。": "", "已自动改用最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。": "", + "已运行": "", + "已退出登录": "", + "已隐藏": "", "巴西葡萄牙语": "", "希望接入新的提供商或模型": "", + "库中 schema": "", "库主文件的大小(不含运行期间的 WAL 临时文件,退出时已自动并回)。": "", "库存目标": "", + "应用版本:{version}": "", + "应用状态": "", + "应用窗口": "", "建立上游 TCP/TLS 连接或代理隧道的最大等待时间(秒),默认 30 秒,范围 1~3600。": "", + "开发构建": "", + "开发者角色兼容": "", + "开发者角色兼容设置已刷新": "", "开启后新发生的请求才会被记录(已经过去的请求补不回来)。报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。": "", "开启后新发生的请求才会被记录(已经过去的请求补不回来),请求日志的「预览对话」也随之开始保存原始正文。上游报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。": "", "开启后网关将监听所有网卡,同一局域网内的设备即可把 API 地址指向本机 IP 一起使用;管理接口从此要求管理员会话或网关 Key,转发接口在没有一把启用的 Key 时也会拒绝服务(届时会自动创建一把名为「默认」的 Key)。": "", @@ -213,28 +337,42 @@ "开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 活动套餐」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。与「开机自动启动」同时开启时,开机后也不建界面(只留托盘与后台进程),点托盘才打开 —— 效果与关窗后一致。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。": "", "开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 活动套餐」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。": "", "开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。": "", + "开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。桌面端与网页端各自独立:桌面端主要服务本机与局域网,网页端(Docker / 服务端)直接暴露在网络上的登录页风险更高,建议保持开启。": "", "开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。": "", "开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看;请求日志详情的「预览对话」也依赖本开关 —— 它保存的是客户端发来的请求体与网关下发的响应体(两侧各截断到 128 KiB),不开这个开关时预览对话没有数据可看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。上游报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。": "", "开启局域网访问": "", "开放后,局域网内其他设备的浏览器打开本机 IP 即可进入管理面板(需管理员账号登录)。": "", "开机自动启动": "", + "开(最多 {n} 轮)": "", "当前三段都是官方原文。": "", "当前固定为浅色模式,不随系统变化。": "", "当前固定为深色模式,不随系统变化。": "", + "当前密码": "", "当前已改过:{edited}。": "", + "当前形态": "", "当前按 100% 显示(默认比例)。": "", "当前按 k / M 缩写显示(如 1.20M / 8.4k)。": "", "当前按 {n}% 显示。": "", "当前按「万 / 亿」式本地量级词显示(如 1.2亿 / 8400万)。": "", "当前是透传模式:存下来也不会发出,切成「替换 / 追加」之后才生效。": "", - "当前显示为「1.20M / 8.4k」这类英文缩写。": "", + "当前显示为「1.20M / 8.4k / 1.2B」这类英文缩写。": "目前顯示為「1.20M / 8.4k / 1.2B」這類英文縮寫。", "当前显示为「1.2亿 / 8400万」这类中文量级。": "", "当前浏览器以管理员身份登录着本面板。「退出登录」会撤销这台设备的登录会话(30 天内的自动续期一并失效),需要重新输入账号密码;其他已登录的设备不受影响。": "", + "当前环境不支持修改机器人校验设置": "", "当前环境不支持局域网访问设置": "", + "当前环境不支持整库备份": "當前環境不支援整庫備份", "当前环境不支持整库备份(仅桌面端可用)": "", + "当前环境不支持整库恢复": "當前環境不支援整庫恢復", "当前环境不支持整库恢复(仅桌面端可用)": "", + "当前环境不支持读取版本信息(桥接方法缺失)": "", + "当前环境不支持退出面板登录": "", "当前生效的正文来自{source}{file}。保存后这一份会盖过提示词文件与内置默认{note}。": "", + "当前界面": "", + "当前管理员:{name}": "", "当前跟随操作系统的深浅色设置,系统切换时界面会自动跟上。": "", + "待迁移旧数据": "", + "待配置": "", + "忘记密码时无法在界面里找回:需要停掉程序后,从数据库删除管理员记录(kv 表的 panelAdmin 键),重启后按首次使用重新注册。具体命令与各形态的库文件路径见 docs 目录下的「重置管理员」文档。": "", "恢复中…": "", "恢复失败:{message}": "", "恢复官方原文": "", @@ -244,7 +382,14 @@ "想回到原来那份,把这里清空再保存即可(不会删掉你配的文件路径)。": "", "想要的新功能或改进想法": "", "所有未单独配置的提供商都用这一份": "", + "手机端只做查看与高频操作;添加账号、编辑凭证、改写客户端配置仍需桌面端。": "", + "托管网页界面": "", + "把 Base URL 复制给客户端": "", + "把地址里的 your-server 与 USERNAME 换成你自己的;目录不存在时会自动创建。": "", "把整份数据库(账号、网关配置、事件日志、请求统计、调试报文与桌面设置)导出成一个 SQL 文件,恢复时整体替换本机全部数据 —— 与上面「账号导入 / 导出」的逐条合并是两种语义:恢复会连配置、日志与当前启用状态一起换成备份那台机器的状态。导出默认包含日志与调试报文(文件可能很大),不需要时可以取消勾选只备业务数据。恢复前会自动把当前数据备份到配置目录的 backups/ 下,恢复后需要重新登录管理面板。": "", + "把账号、网关配置、自定义提供商、Agent 配置、代理池 / 订阅、签到与余额记录同步到 WebDAV 或 S3 兼容存储,换台机器后一键取回。事件日志、请求明细、调试报文与面板登录态不在同步范围内(它们是本机的运行痕迹)。上传的账号凭证仍是加密形态,解密用的账号密钥由「同步密码」单独加密后一并上传 —— 同步密码不会保存到云端,换机下载时需要重新输入。": "", + "把路径里的主目录显示成 ~,截图分享时不暴露用户名": "", + "把路径里的主目录(如 C:\\Users\\你的用户名)显示成 ~,截图分享时不暴露用户名。只影响本页显示与复制,不改任何文件。": "", "报表天数": "", "报表页的热力图与按天趋势,默认 99999 天(近似全量保留),可填 1–99999 天。": "", "指定错误码直接换号": "", @@ -252,69 +397,111 @@ "指纹脱敏设置已刷新": "", "按天聚合保留天数": "", "按提供商配置": "", + "换一台服务器": "", "排队等待": "", "排队等待次数": "", "排队等待秒数": "", "排队等待设置已刷新": "", + "接入": "", "控制报表与请求日志里 Token 读数的写法:开启后按「万 / 亿」式的本地量级词显示(简体为万 / 亿,繁体为萬 / 億,日文为万 / 億,韩文为만 / 억),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。英文 / 葡语的本地量级词就是 k / M,这项对它们没有区别。只影响显示口径,不改变任何统计与存储的数值。": "", - "控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。": "", + "控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M / B 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。": "控制報表與請求日誌裡 Token 讀數的寫法:開啟後按中文量級顯示(1.2億 / 8400萬),關閉則用 k / M / B 縮寫(與上游文件、介面欄位的寫法一致)。這只影響顯示口徑,不改變任何統計與儲存的數值。", "控制界面的深浅色,与左侧边栏底部的三个主题按钮是同一个设置(改哪一处,另一处立刻跟上)。「跟随系统」会随操作系统当前的浅色 / 深色自动切换,并在系统主题变化时即时跟上;选「浅色」或「深色」则把界面固定在该模式,不再随系统变化。窗口标题栏的深浅色会一并同步,不会出现深色界面配一条浅色标题栏的情况。": "", "提建议": "", "提示词文件": "", "提示词文件已更新": "", "提示词正文": "", "提示词正文已更新": "", + "操作系统": "", + "改动仍保存在本机,修复网络或凭据后下一次改动会自动重试,也可以手动点「上传到云端」。": "", "改完立即生效,不用重启(下一个请求就用新模式)。降级是自动的临时状态,解除后本模式自己的提示词立刻恢复。": "", "放大或缩小整个界面(文字与控件一起变),效果与浏览器 Ctrl +/- 相同:共 11 档,立即生效并记住。": "", "数据": "", "数据保留": "", "数据存储": "", + "数据库 schema": "", "数据库不可用": "", "数据库文件": "", + "数据目录": "", "数据统一保存在配置目录下的 agent2api.db;如需更换位置,请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。": "", "整体替换": "", "整库备份 / 恢复": "", "新增 {added} 个、更新 {updated} 个{suffix}{note}": "", + "新密码": "", + "新密码不能与当前密码相同": "", + "新密码至少 8 位": "", + "日志文件": "", "日语": "", "显示模式": "", "更新": "", + "更新出网线路": "", + "更新通道": "", "替换": "", "替换 = 客户端 system / developer 消息全部删掉,换成网关的提示词;追加 = 保留客户端内容,只在开头 system 块之后多插一条。两者都只改发给上游的副本,客户端看到的响应不变。": "", "替换为网关提示词": "", "替换前会自动把当前数据备份到配置目录的 backups/ 文件夹。": "", + "替换并恢复": "", "有未保存的修改": "", + "有未保存的改动:保存后才能同步(测试连接不受影响)。": "", + "服务商": "", + "服务器地址": "", "期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。": "", "未单独配置:模式、提示词文件与正文都跟随上面的全局配置(当前「{mode}」)。": "", + "未启用": "", "未开启轻量模式:关窗仅隐藏窗口,界面进程常驻内存,随时打开不等待": "", + "未开启,developer 角色消息会原样发往上游,不认识该角色的上游会报错(400)。": "", "未开启,客户端 system 模板会原样发往上游,可能被内容审核误拦(400)。": "", "未开启,浏览器跨域无法直连网关。": "", "未开启,转发时不保存任何原始报文。": "", "未开启:网关只监听 127.0.0.1,仅本机可以访问。": "", + "未注册": "", + "未注册闸门": "", "未知原因": "", "未知来源": "", + "未知设备": "", "未知账号": "", "未能读取 Cline 伪装头设置,请稍后重试": "", "未能读取 models.dev 元数据状态,请稍后重试": "", + "未能读取云同步设置,请稍后重试。": "", + "未能读取开发者角色兼容设置,请稍后重试": "", "未能读取指纹脱敏设置,请稍后重试": "", "未能读取系统提示词设置,请稍后重试": "", "未能读取网关跨域访问设置,请稍后重试": "", "未能读取调试模式设置,请稍后重试": "", + "未能读取账户信息,请稍后重试": "", + "本地账号、配置、签到与余额记录变化后自动上传(合并 1 秒内的连续改动,最长 10 秒传一次;内容没变时不发请求)。": "", + "本机 APK 版本": "", + "本机应用版本": "", + "本机界面版本": "", + "本机的业务数据(账号、配置、Agent 配置、代理池 / 订阅、签到与余额记录)将上传并": "", "本机的全部数据(账号、网关配置、日志统计与桌面设置)将被所选备份文件": "", + "本机的账号、网关配置、Agent 配置、代理池 / 订阅、签到与余额记录将被远端快照": "", "本次无需刷新": "", "机器人校验": "", + "来源设备:{device}": "", + "来自 {device},生成于 {time}": "", + "架构": "", "查询管理员状态失败:{message}": "", "校验身份:这三段必须各自成块排在最前(实测:三段并成一段会被回 405 / 3012)。因此这里按段编辑,不能合成一段。改过的段以你的文本为准,没改的段继续用官方原文;清空某一段 = 那一段恢复官方原文。": "", + "桌面端": "", + "桌面端:登录 / 注册需要 ALTCHA 人机验证": "", + "桌面端:登录 / 注册需要 ALTCHA 人机验证(当前正在使用)": "", "桌面运行时不可用,暂时无法保存": "", + "档案": "", "检测中…": "", + "概览": "", "模式": "", "模式改为「{mode}」": "", "次": "", "次日 00:00": "", "正在保存上游原始报文:": "", + "正在修改…": "", "正在剥离出站请求里的审核指纹:表头键值整段删除,模板句最小改写。": "", + "正在安装…": "", "正在应答跨源请求:浏览器跨域请求可直连 /v1/*。": "", + "正在归一角色:developer 角色消息转发前改写为 system。": "", "正在注册…": "", "正在联网更新": "", + "正式版": "", "每条请求轮换到下一个可用账号;新会话交给当前最闲的那位。适合想把手里的号摊开用、不让某一个一直扛。": "", "每条请求都重新按策略选路 —— 行为与「关闭」一致,仅保留会话识别的记录。": "", "每次等待的时长,默认 0 = 跟随上游建议(实测 9~30 秒,上游没给建议时用 15 秒)。可填 0–120 秒强制一个固定时长。": "", @@ -327,15 +514,31 @@ "注意:这一家的「网关自带」开关现在是关的,改过的正文要等开关打开才生效。": "", "流式响应相邻数据之间允许的最大空闲时间(秒),收到新数据后重新计时,默认 300 秒,范围 1~3600。": "", "流式响应空闲超时": "", + "测试连接": "", + "浏览器 / PWA": "", + "浏览器只在 https 或 localhost 下提供离线缓存;局域网 http 访问不影响正常使用": "", + "浏览器标签页": "", + "浏览器面板": "", "添加提供商": "", "添加提供商…": "", "添加自定义头": "", "添加要单独配置的提供商": "", + "清除本机凭证": "", "清除这里的正文": "", "点下面的入口会用系统默认浏览器打开 GitHub 的对应表单(需要 GitHub 账号,模板已预设好,填完直接提交即可)。": "", + "点此添加或切换服务器": "", + "点此添加服务器": "", + "版本": "", + "版本信息": "", + "版本信息 · 所连网关环境": "", "状态码 {code} 已在名单里": "", "状态码必须是 {min}–{max} 的整数(收到: {text})": "", + "环境": "", + "生成时间:{time}": "", "用 models.dev 补齐上游没声明的模型能力与思考档位(联网刷新,内嵌快照始终生效)": "", + "用于加密上云的账号密钥。请自己记牢 —— 它不保存到云端,换机下载时必须输入同一个密码,否则账号数据无法解密。": "", + "用浏览器菜单里的「添加到主屏幕」把这一页装成 App:全屏打开、没有地址栏,和原生应用一样。": "", + "界面": "", "界面缩放": "", "界面缩放比例": "", "界面语言": "", @@ -343,15 +546,27 @@ "界面语言只保存在本机(localStorage)。切换后整页立即刷新,不影响账号、密钥与转发。": "", "界面里编辑的正文": "", "留空 = 用内置默认提示词": "", + "留空使用 AWS 官方地址": "", "登录 / 注册需要通过 ALTCHA 人机验证(工作量证明)": "", "登录 / 账号切换 / 429 切换等系统事件,可填 1–3650 天。": "", "登录 / 账号切换 / 429 切换等系统事件,可填 1–99999 天。": "", + "登录 Agent2API": "", + "登录人机验证": "", + "监听地址": "", + "目标路径:{path}": "", "确定继续?": "", + "确认上传": "", + "确认新密码": "", "确认缩短保留天数": "", + "社区与支持": "", + "离线缓存未启用": "", + "积分合计只统计当前筛选出来的账号": "積分合計只統計目前篩選出來的帳號", + "程序路径": "", "空 = 不要这一份,回落提示词文件 / 内置默认提示词。": "", "立即解除": "", "第一段": "", "第一段(身份句)": "", + "第三方应用密码": "", "第三段": "", "第三段(动态段)": "", "第二段": "", @@ -360,8 +575,11 @@ "等待期间请求日志的状态列会显示「排队中」阶段,详情弹窗的「内部重试」里能逐次看到等待时长;等待用尽后返回 HTTP 503(不是 401/429:既不会刷新凭证,也不会把账号标成限额)。": "", "等比放大或缩小整个界面(文字、控件、间距一起变),效果与浏览器按 Ctrl +/- 相同:80%–130%、5% 一档。窗口本身不缩放,变的是页面内容的显示比例,设置立即生效并记住,下次启动直接按这个比例打开。放得越大可视范围越小,窗口较窄或表格较宽时不建议调得太大。": "", "简体中文": "", + "管理员已注册": "", + "系统": "", "系统提示词模式": "", "系统提示词设置已刷新": "", + "累计请求": "", "繁体中文": "", "约 {n} 字符": "", "结构": "", @@ -372,19 +590,36 @@ "编辑网关自带提示词 · {label}": "", "缩放比例": "", "网关": "", + "网关 Key 数量": "", + "网关状态": "", "网关自带": "", "网关跨域访问已开启": "", "网关跨域访问设置已刷新": "", "网关跨域访问(CORS)": "", "网关面(/v1/*)默认不应答浏览器跨源请求:页面直连会被预检拦下,只报「无法连接 API」。注意来源是 * —— 未配「网关 Key」时任何网页都能借本机网关打上游,故默认关闭;只给自己用的话,让页面与网关同源(本地反代 /v1)更稳妥。面板接口(/api/*)不受影响。": "", + "网页端": "", "网页端的界面缩放由浏览器自己控制(Ctrl + / Ctrl -,或浏览器菜单里的缩放),此项不可调。": "", + "网页端:登录 / 注册需要 ALTCHA 人机验证": "", + "网页端:登录 / 注册需要 ALTCHA 人机验证(当前正在使用)": "", + "群晖 NAS": "", + "群晖需在「控制面板 → 应用程序门户 → WebDAV」里启用服务,端口默认 5005(HTTP)/ 5006(HTTPS)。": "", + "腾讯云 COS": "", + "自动同步": "", + "自动同步上次失败": "", + "自动续写": "", "自定义提供商 {id}": "", "自愈保护": "", + "至少 8 位,换机下载时需要": "", "英语": "", "装上{size}的官方身份提示词": "", + "装成 App 后从桌面图标直接进入:全屏打开、没有地址栏。": "", + "覆盖": "", "解除失败:{message}": "", + "计数": "", "计量单位": "", + "详细日志": "", "语言设置": "", + "请先保存设置再同步": "", "请填写管理员账号(64 字符以内)": "", "请求发出后等待上游响应头的最大时间(秒),默认 300 秒,范围 1~3600。": "", "请求提供商 / 模型支持": "", @@ -393,64 +628,112 @@ "请求超时": "", "请求转发到上游失败时(服务器瞬时错误、连接失败,以及 WorkBuddy 的 11-128 敏感词拦截),网关会等一段间隔后再发一次。次数分两档,管的是两件事:①「同一账号重试次数」在同一账号上原地重发几次 —— 一般是提示词被上游拦截、或链路瞬时抖动,等一会儿重发往往就好了;②「切换账号重试次数」这份请求最多再换几个账号试 —— 首发的那个账号不算,每换一个扣一次,不分是不是同一家(换到同一家的下一个账号也算一次),换满还失败就把错误返回给客户端。两项都设为 0 表示失败不重试、直接报错。注意账号级限额(429)不占这里的换号次数 —— 那类失败走「冷却该账号并换下一个账号」的降级,与这里的重试是两条路。另有「指定错误码直接换号」名单(默认 402):命中名单的上游状态码不在同一账号重发,直接按队列换下一个账号继续试(换号也占「切换账号重试次数」),换满仍失败才把错误返回给客户端。": "", "请求重试": "", + "请输入同步密码": "", + "请输入当前密码": "", + "读取云同步设置失败:{message}": "", + "读取失败:{message}": "", "读取完整非流式响应体允许的最大时间(秒),默认 300 秒,范围 1~3600。": "", + "读取网关信息失败:{message}": "", + "读取远端…": "", + "读取远端信息失败:{message}": "", "调试报文": "", "调试模式": "", "调试模式设置已刷新": "", "负载均衡": "", + "账号列表": "帳號列表", "账号导入 / 导出": "", "账号路由": "", + "账号页表格的两项显示偏好,只存在本机(localStorage),不影响账号数据与转发。「积分合计只统计当前筛选出来的账号」:账号页每一家的分组带右侧有一枚「可用合计」,默认按整组求和 —— 不受筛选、搜索影响,被筛掉的账号也计入(余额是资产,禁用也只表示不参与转发,额度仍在账上)。打开这一项后改为只累加当前可见的行:例如筛出 1 个 100 积分的账号,合计就是 100,而不是同组两个账号之和 1,000。分页不参与这个口径 —— 翻页看的是同一份结果的不同屏,合计不随页码变化。「隐藏账号名」与表头「账号」旁边那颗眼睛是同一个开关:打开后账号列的名字整体显示成星号,适合截图分享时遮挡账号。": "帳號頁表格的兩項顯示偏好,只存在本機(localStorage),不影響帳號資料與轉發。「積分合計只統計目前篩選出來的帳號」:帳號頁每一家的分組帶右側有一枚「可用合計」,預設按整組求和 —— 不受篩選、搜尋影響,被篩掉的帳號也計入(餘額是資產,停用也只表示不參與轉發,額度仍在帳上)。開啟這一項後改為只累加目前可見的列:例如篩出 1 個 100 積分的帳號,合計就是 100,而不是同組兩個帳號之和 1,000。分頁不參與這個口徑 —— 翻頁看的是同一份結果的不同螢幕,合計不隨頁碼變化。「隱藏帳號名」與表頭「帳號」旁邊那顆眼睛是同一個開關:開啟後帳號欄的名字整體顯示成星號,適合截圖分享時遮蔽帳號。", + "账户": "", "超出保留天数的部分会被删除:事件日志是登录、账号切换、429 切换这类系统事件。把天数改小(例如 30 天改成 7 天)保存后会立即删除超出的历史数据,此操作不可恢复;改大或保持不变不会删除任何数据。可填范围为 1–3650 天。请求日志 / 按天聚合 / 签到记录已全量保留、不再自动删除,要清历史请到对应页面手动清空。": "", "超出的历史数据会被立即删除,且不可恢复。": "", "超时": "", "超时设置已刷新": "", + "跟随系统 · 浅色 · 深色": "", "跟随默认": "", + "跨域访问": "", "路由": "", "路由设置已刷新": "", "跳过 {n} 个": "", "转发一个请求时按什么顺序挑账号:①「路由策略」决定排队规则 —— 顺序优先是排第 1 的接走所有请求、直到它不能答(限流 / 故障 / 额度尽)才轮到下一位;负载均衡是每条请求换下一位,把负载摊到各个账号上。②「会话粘性」让同一会话(相同的模型 + 相同的开头消息)尽量跟住同一个账号 —— 多轮对话的上下文缓存能复用、少换号少浪费;自动档粘 5 分钟、会话内档粘 6 小时、每轮与关闭都不粘。账号的排队位次(账号表的第一列)在所有策略下都是地基,策略在它之上再排。此外路由还有三层自愈保护默认生效,不需要配置:转发失败的账号退避休息一会儿(15 分钟起步翻倍、封顶 1 小时),被 429 的账号垫底排到队尾一圈,余额不足 / Token 周期用量到顶的账号由限制器拦下 —— 恢复后自动回到队列。": "", "转发一个请求时按什么顺序挑账号:①「路由策略」决定排队规则 —— 顺序优先是排第 1 的接走所有请求、直到它不能答(限流 / 故障 / 额度尽)才轮到下一位;负载均衡是每条请求换下一位,把负载摊到各个账号上。②「会话粘性」让同一会话(相同的模型 + 相同的开头消息)尽量跟住同一个账号 —— 多轮对话的上下文缓存能复用、少换号少浪费;自动档粘 5 分钟、会话内档粘 6 小时、每轮与关闭都不粘。账号的排队位次(账号表的第一列)在所有策略下都是地基,策略在它之上再排。此外路由还有三层自愈保护:转发失败的账号退避休息一会儿(15 分钟起步翻倍、封顶 1 小时)、被 429 的账号垫底排到队尾 30 分钟、余额不足 / Token 周期用量到顶的账号由限制器拦下 —— 前两层可以在下面按需开关,限制器由账号的「限制器」设置管。": "", "转发一个请求时按什么顺序挑账号:①「路由策略」决定排队规则 —— 顺序优先是排第 1 的接走所有请求、直到它不能答(限流 / 故障 / 额度尽)才轮到下一位;轮流均摊是每条请求换下一位,雨露均沾;余额优先是用量占比最低的先用,「谁空谁上」。②「会话粘性」让同一会话(相同的模型 + 相同的开头消息)尽量跟住同一个账号 —— 多轮对话的上下文缓存能复用、少换号少浪费;自动档粘 5 分钟、会话内档粘 6 小时、每轮与关闭都不粘。账号的排队位次(账号表的第一列)在所有策略下都是地基,策略在它之上再排。此外路由还有三层自愈保护默认生效,不需要配置:转发失败的账号退避休息一会儿(15 分钟起步翻倍、封顶 1 小时),被 429 的账号垫底排到队尾一圈,余额不足 / Token 周期用量到顶的账号由限制器拦下 —— 恢复后自动回到队列。": "", + "转发前将 developer 角色消息转换为 system,兼容不支持 developer 角色的 OpenAI 兼容上游": "", "转发失败的账号休息一会儿再回来:15 分钟起步,连续失败翻倍,封顶 1 小时;成功一次立即恢复。目的是「让这次失败先凉一会儿」,免得一条条地反复撞同一个坏账号。": "", + "转发链路": "", + "转发链路 · 诊断 · 系统": "", "轮询间隔": "", + "软件版本": "", "轻量模式": "", "轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 套餐池(Start Plan)通道不可用,相关请求会在请求日志里注明原因": "", "轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 活动套餐通道不可用,相关请求会在请求日志里注明原因": "", "轻量模式(关窗释放界面内存)": "", + "输入同步密码": "", + "输入当前登录用的密码": "", "输入状态码,回车添加": "", + "运行形态": "", "还原": "", "还有额度时被 429 的账号排到队尾,30 分钟后自动回到队列(成功转发一次也会立即回来)。它标记的是「疑似共享池被别人抢」,不是「这个账号本身有问题」。⚠️ 注意它与路由策略的配合:在「家内轮询」下,队尾意味着这一家整体被往后排 —— 如果某个提供商的账号被批量限流,它们会集体沉到队尾,那个家可能长时间轮不到。这时可以关掉本项,让 429 过的账号只受冷却约束(冷却到点自动回来)。": "", + "还没有同步过。": "", + "还没有添加服务器。点右上角「添加」,填入网关所在机器的 IP 或域名与端口。": "", "还没有装载任何数据。": "", + "还没有读到版本信息": "", "这一份就是「替换 / 追加」模式下网关要发的 system 正文。": "", + "这台网关未启用机器人校验": "", + "这台网关没有管理员,用一把已启用的 API Key 访问。": "", + "进程 PID": "", + "远程根目录": "", + "远端同一路径下的旧快照。事件日志、请求明细与调试报文不会上传。": "", + "远端已有一份快照,将被覆盖": "", + "远端快照使用了另一把账号密钥,但没有携带密钥文件,无法在本机解密。请在原来那台机器上重新上传一次。": "", + "远端快照使用的账号密钥与本机不同。请输入上传那台机器设置的同步密码,用它解出密钥装到本机后再继续下载。": "", + "远端数据与当前版本不兼容": "", + "远端该路径下还没有快照,本次是首次上传。": "", + "远端路径:{path}": "", + "远端还没有可下载的同步数据": "", + "连不上这台网关。确认地址与端口正确、网关已启动,且已开启「局域网访问」;用 https 时还要确认证书有效": "", "连接中超时": "", + "连接失败:{message}": "", "连续失败到阈值后把这个账号整个打开(默认关)。与失败退避的分工:退避管「单次失败先凉一会儿」,惩罚轻、自动恢复;熔断管「这个账号现在整体不对劲,别再一条条地试」,要连续成功若干次才关。打开期间该账号被跳过,除非全场只剩它 —— 那时仍放行试一次。": "", "追加": "", "追加网关提示词": "", "退出失败:{message}": "", "退出登录": "", + "退出这台服务器的登录,不影响其他服务器": "", "选择备份文件并恢复": "", "选路规则": "", "透传": "", "透传 = 不动客户端 system;替换 = 删掉客户端的 system / developer,换成网关提示词;追加 = 保留客户端内容,在最前面多插一条。": "", "透传客户端 system": "", "透传客户端 system(默认)": "", + "通常是网络问题,点右侧重试": "", "通用": "", "遇到 Bug、报错或异常行为": "", "配置目录下的 agent2api.db(账号、日志、请求记录、调试报文与设置都在里面)。": "", + "重启后生效,不影响当前操作": "", + "重置会清空管理员账号与密码,面板将回到「未注册」状态。": "", + "重置管理员": "", "重试设置已刷新": "", "重试间隔": "", "铸造守卫每隔多久看一次库存(毫秒)。只影响发现缺货的反应速度 —— 库存够用时轮询不会铸造任何令牌。默认 2500 毫秒,可填 1000–30000。": "", "问题反馈": "", + "阿里云 OSS": "", + "限流中账号": "", "随包内嵌的快照(发版时的那份)": "", + "隐藏用户名": "", + "隐藏账号名(显示成星号)": "隱藏帳號名(顯示成星號)", "非流式响应超时": "", "面板登录": "", + "面板端口": "", "面板管理员账号": "", "韩语": "", + "预设": "", "验证码令牌池": "", "验证码令牌池设置已刷新": "", "默认值通常够用,出问题时才需要调": "", "默认关闭。改动立即生效,不用重启;关闭期间的跨源请求会被预检拦下。": "", "默认关闭窗口不会退出程序,而是把窗口缩到系统托盘,转发继续在后台运行(OpenAI 客户端不受影响);要彻底退出程序,请在托盘图标上右键选「退出」。关掉这个开关后,点关闭按钮即退出程序、转发随之中断。「开机自动启动」开启后,登录系统时会自动启动本程序(通常直接驻留托盘),不需要手动打开。": "", + "默认开启。只改 role 字段本身,消息内容与顺序都不动;本项只改发给上游的副本,客户端看到的响应内容不变。": "", "默认开启。命中明细会记进请求日志的「敏」标签(悬停可看命中了哪几条规则)。本项只改发给上游的副本,客户端看到的响应内容不变。": "", "默认网关只监听 127.0.0.1,只有本机能访问。开启后网关改听所有网卡,同一局域网内的设备可以把 API 地址指向本机 IP 一起使用。出于安全考虑,开启前必须先注册一个面板管理员:管理接口从此要求管理员会话或网关 Key,模型额度不会对局域网裸奔;一把启用的网关 Key 都没有时,转发接口也会拒绝服务,直到创建第一把。改动需要重启应用生效。「同时开放网页管理面板」把管理界面也出给局域网(其他设备的浏览器打开本机 IP 即可管理),默认关闭 —— 桌面端的面板仍只由本程序自己出。": "", "({n} 行)": "", @@ -465,20 +748,11 @@ "(未生效)": "", ",{items}": "", ",{time}": "", + ",以及代理池 / 订阅里可能存在的代理凭据。请使用只有你能访问的私有存储;账号密钥由同步密码单独加密,泄露存储本身不等于泄露账号。": "", ",包括恢复后需要重新登录管理面板。": "", ",可直接用于登录。请妥善保管,不要外传或上传到公共位置。": "", ",文件里后续的改动在清空这里之前不再生效": "", ",替换前的数据已备份为 {name}": "", ",自定义提供商新增 {added} 个、更新 {updated} 个": "", - ";网页管理面板已一并开放(浏览器打开同一地址)": "", - "开发者角色兼容": "", - "转发前将 developer 角色消息转换为 system,兼容不支持 developer 角色的 OpenAI 兼容上游": "", - "OpenAI 新版 API 用 developer 角色取代 system 承载指令,新版 OpenAI SDK 与部分客户端发的对话里会出现 role 为 developer 的消息;不少 OpenAI 兼容上游(尤其是自定义提供商的透传路径)不认识这个角色,收到就报 400。开启本项后,网关在每次转发前把 developer 角色的消息改写为 system —— 两类角色在 OpenAI 语义上等价(OpenAI 自己的迁移指南也是这么映射的),改写不影响对话内容与语义;对本来就只认 system 的各家内置上游同样无害。默认开启;关掉后 developer 消息会原样发往上游,不认识它的上游会报「角色不存在」一类的错误。": "", - "未能读取开发者角色兼容设置,请稍后重试": "", - "正在归一角色:developer 角色消息转发前改写为 system。": "", - "未开启,developer 角色消息会原样发往上游,不认识该角色的上游会报错(400)。": "", - "✅ 开发者角色兼容已开启": "", - "已关闭开发者角色兼容": "", - "开发者角色兼容设置已刷新": "", - "默认开启。只改 role 字段本身,消息内容与顺序都不动;本项只改发给上游的副本,客户端看到的响应内容不变。": "" + ";网页管理面板已一并开放(浏览器打开同一地址)": "" } diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/tasks.json b/desktop-tauri/i18n/i18n-src/zh-Hant/tasks.json index c6cbfc6a..e143ed40 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/tasks.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/tasks.json @@ -7,6 +7,7 @@ "{n} 秒后": "", "✅ {task}:{summary}": "", "✅ 已更新「{label}」": "", + "「{name}」已执行": "", "上次执行 {time}": "", "上次执行 {time}({result})": "", "下次执行 {time}({relative})": "", @@ -17,20 +18,29 @@ "停留在「请求日志」页时按此间隔重新拉取请求日志;页面不可见时不请求。": "", "冷却中,{relative}重试": "", "凭证自动维护": "", + "分": "", "即将执行": "", + "可填 {min}–{max} {unit}": "", "可填 {min}–{max} {unit}(默认 {def} {unit})": "", + "后台任务": "", "定时任务已刷新": "", "定期向 GitHub 查询最新发布版本,默认每 20 分钟一次。检查结果、排期和限流冷却跨重启保留;手动检查也会合并重复请求并遵守冷却。开关与间隔在「软件更新」面板的「更新设置」弹窗里配置(定时任务页不再渲染这一条)。": "", "定期拉取最新模型清单,重启沿用排期与缓存。客户端读取模型列表的后台刷新也遵守本任务开关、间隔和失败冷却;手动获取模型可提前刷新。": "", "定期遍历账号,只刷新已过期或临期的凭证。重启后沿用上次排期,失败账号在冷却后重试;转发需要的按需续期不受此开关影响。": "", + "已{action}「{name}」": "", "已执行": "", + "已更新间隔": "", + "开启": "", "执行失败:{reason}": "", "报表自动刷新": "", "改动立即生效,不需要重启程序": "", + "改间隔": "", "日志页自动刷新": "", "模型目录刷新": "", "正在加载定时任务…": "", + "每 {n} {unit}": "", "没有可显示的定时任务。后端未返回任务清单,请确认网关正在运行。": "", + "没有后台任务": "", "状态每 20 秒自动同步": "", "由页面按间隔自动刷新": "", "立即执行": "", @@ -39,5 +49,8 @@ "还没有执行记录": "", "间隔不能为空(可填 {min}–{max} {unit})": "", "间隔必须在 {min}–{max} {unit} 之间(当前填的是 {value})": "", - "间隔必须是整数": "" + "间隔必须是 {min}–{max} 的整数": "", + "间隔必须是整数": "", + "页面刷新由浏览器执行,只在页面打开时生效": "", + "页面自动刷新": "" } diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/update.json b/desktop-tauri/i18n/i18n-src/zh-Hant/update.json index 299764df..74dd6f0c 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/update.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/update.json @@ -1,9 +1,14 @@ { " · 检查于 {time}": "", " 已发布(当前 {current}),更新日志如下:": "", + "Discord 群": "", "GitHub Token 未填写": "", "GitHub 令牌": "", - "QQ 群二维码": "", + "QQ 群": "", + "QQ 群 / Telegram / Discord 都在这里": "", + "QQ 群 1": "", + "QQ 群 2": "", + "Telegram 群": "", "{version} 已就位,服务正在重启,页面会在恢复后自动刷新…({seconds}s)": "", "{version}(预发布)": "", "✅ GitHub 令牌已保存(加密存储,界面不再显示)": "", @@ -30,8 +35,10 @@ "出网线路已切换,但写入磁盘失败(重启后会恢复原线路)": "", "分钟({min} ~ {max})": "", "到点自动向 GitHub 查询新版本,查到就走「检测到更新」弹窗;关闭后「检查更新」按钮仍可手动触发": "", - "加入 QQ 群": "", "加入 beta 版": "", + "加入交流群": "", + "即将满员": "", + "即将满员,请优先加群 2": "", "去更新": "", "发现新版本 {latest}(当前 {current})。{at}": "", "发现新版本 {latest}(当前 {current}),但该版本没有提供容器内更新包;请在宿主机执行 docker compose pull && docker compose up -d 升级。{at}": "", @@ -45,14 +52,22 @@ "回滚失败": "", "回滚失败:{error}": "", "在 GitHub 查看完整说明": "", + "在 QQ 里搜索群号即可加入。": "", + "在浏览器中打开": "", + "在浏览器中打开 GitHub 发布页": "", "在浏览器中打开项目仓库,点 Star 支持作者": "", "在默认浏览器中打开 GitHub 的令牌创建页(备注已预设,无需勾选任何权限,直接点 Generate token)": "", "填写后检查限额 60 → 5000 次/小时(创建令牌无需勾选任何权限)": "", + "复制群号 {group}": "", "安装中": "", "安装包": "", "安装包已下载完成,正在启动安装程序(需要管理员权限,会弹出 UAC 确认框)…": "", + "安装包已下载完成,正在启动新的 AppImage…": "", + "安装包已下载完成,正在打开系统安装器…": "", "安装包已下载完成,正在挂载磁盘映像…": "", + "安装包已就绪:{name}。点击「安装并重启」后会启动新的 AppImage;把它移动到你的应用目录并替换旧文件即完成升级(本程序会先退出以释放文件占用)。": "", "安装包已就绪:{name}。点击「安装并重启」后需要管理员权限,会弹出 UAC 确认框。": "", + "安装包已就绪:{name}。点击「打开安装包」后会交给系统安装器,按提示输入密码即可完成安装(本程序无需退出)。": "", "安装包已就绪:{name}。点击「打开安装包」后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装。": "", "安装包已挂载,请在弹出的窗口里把应用拖进「应用程序」完成安装。安装完成后重新打开本程序即可。": "", "安装失败": "", @@ -64,6 +79,7 @@ "已取消": "", "已填写": "", "已填写(粘贴新令牌可覆盖)": "", + "已打开系统安装器,请在它弹出的窗口里完成安装。安装完成后重新打开本程序即可。": "", "已是最新": "", "已替换为 {version},正在重启服务…": "", "已还原上一版本,正在重启服务…": "", @@ -89,6 +105,7 @@ "更新包从 GitHub 发布页下载,下载完成后可直接启动安装程序并自动重启本程序。安装包会先校验来源域名与文件完整性。检查更新与下载安装包的出网线路、GitHub 令牌都在「更新设置」里:线路默认直连(直连失败时自动借 Clash Verge 的混合端口重试一次),选定指定出口后只走该出口;填写 GitHub 令牌可把检查限额从 60 次/小时提高到 5000 次/小时。": "", "更新包已就绪:{name}。点「安装并重启」后,面板会校验并替换容器内的程序与界面文件,然后重启服务(页面会自动刷新)。": "", "更新已应用到容器内,重启服务后生效。{at}": "", + "更新已应用到容器内,重启服务后生效。{at}(本次检查失败:{error})": "", "更新日志": "", "更新设置": "", "替换完成,正在重启服务…": "", @@ -104,10 +121,13 @@ "检查更新": "", "检查更新与下载安装包走哪条线路(当前:{current});选择即保存。选项来自「网络代理」页;直连失败时会自动借 Clash 混合端口重试一次,选定指定代理后只走它": "", "检查更新失败:{error}": "", + "检查更新失败:{error}(以下版本与更新日志来自上次成功检查,检查于 {time})": "", "检查间隔必须是 {min} ~ {max} 分钟": "", "检查间隔(分钟)": "", "检测到更新": "", "正在下载 {name}:{percent}%({received} / {total} MB)": "", + "正在下载安装包…(下载完成后会交给系统安装器,按提示输入密码即可完成安装)": "", + "正在下载安装包…(下载完成后会启动新的 AppImage;把它替换掉旧文件即完成升级)": "", "正在下载安装包…(下载完成后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装)": "", "正在下载安装包…(下载完成后启动安装程序需要管理员权限,会弹出 UAC 确认框)": "", "正在下载服务端更新包…(下载完成后由面板完成替换,重启容器内的服务后生效)": "", @@ -120,8 +140,9 @@ "点击「检查更新」后,这里会显示最新版本的更新说明。": "", "点击「检查更新」查询 GitHub 上的最新发布版本。": "", "点击「检查更新」查询最新发布版本。": "", + "点击在浏览器中打开": "", + "点击复制群号": "", "环境变量": "", - "用手机 QQ 扫上面的二维码,或在 QQ 里搜索群号加入。": "", "立即重启": "", "粘贴 GitHub 令牌(ghp_… / github_pat_…)": "", "群号": "", @@ -135,6 +156,7 @@ "这一版没有提供容器内更新包": "", "这个版本没有填写发布说明。": "", "通道已切换,但写入磁盘失败(重启后会回到原设置)": "", + "遇到问题或有使用心得,欢迎到群里交流。": "", "重启中": "", "重新粘贴保存一次即可自愈": "", "重试下载": "", diff --git a/desktop-tauri/i18n/scan.mjs b/desktop-tauri/i18n/scan.mjs index 9deb8678..bebc3c6d 100644 --- a/desktop-tauri/i18n/scan.mjs +++ b/desktop-tauri/i18n/scan.mjs @@ -39,6 +39,13 @@ const HERE = path.dirname(fileURLToPath(import.meta.url)) // desktop-tauri/i18n const DESKTOP = path.resolve(HERE, '..') // desktop-tauri const UI = path.join(DESKTOP, 'ui') const ISLANDS_SRC = path.join(DESKTOP, 'ui-islands', 'src') +/** + * 手机端源码目录(`desktop-tauri/mobile/src`)。 + * + * 手机端是独立工程(自己的 Vite + React),产物落 `ui/m/` —— 那是压缩后的 + * bundle,键名早被压没了,扫它没有意义。这里扫源码,与 ui-islands 同一口径。 + */ +const MOBILE_SRC = path.join(DESKTOP, 'mobile', 'src') const SRC_DIR = path.join(HERE, 'i18n-src') /** 目标语言目录(简体不建目录:查不到就回落简体原文,不需要词典) */ @@ -49,7 +56,7 @@ const LOCALES = ['en', 'zh-Hant', 'ja', 'ko', 'pt-BR'] * 调整顺序会改变某些跨界键的归属,非必要不要动。 */ const DOMAIN_ORDER = [ - 'common', 'login', 'accounts', 'add-account', 'models', 'settings', 'assistant', 'update', + 'common', 'login', 'accounts', 'add-account', 'models', 'agent', 'settings', 'assistant', 'update', 'keys', 'proxies', 'docs', 'report', 'checkin', 'tasks', 'logs', 'requests', 'tables', 'port', ] @@ -59,24 +66,37 @@ const DOMAIN_ORDER = [ * 未命中的文件归入 'common' 并在报告里 warning(提示把新文件登记进来)。 */ const DOMAIN_RULES = [ - { domain: 'common', files: ['index.html', 'titlebar.js', 'tooltip.js', 'clipboard.js', 'filter-memory.js', 'app.js', 'confirm-dialog.tsx', 'input.tsx', 'input-control.tsx', 'segmented.tsx', 'index.tsx', 'markdown.js', 'icons.js'] }, + { domain: 'common', files: ['index.html', 'm.html', 'titlebar.js', 'tooltip.js', 'clipboard.js', 'filter-memory.js', 'app.js', 'confirm-dialog.tsx', 'input.tsx', 'input-control.tsx', 'segmented.tsx', 'index.tsx', 'markdown.js', 'icons.js', 'App.tsx', 'main.tsx', 'Shell.tsx', 'NavBar.tsx', 'Icon.tsx', 'ProviderLogo.tsx', 'ui.tsx', 'PullToRefresh.tsx', 'AppUpdatePanel.tsx', 'CommunitySheet.tsx', 'i18n.ts', 'client.ts', 'gateway.ts', 'format.ts', 'providers.ts', 'providerFacts.ts', 'usePolling.ts', 'pwa.ts', 'usePwa.ts', 'appUpdate.ts', 'aliyunCaptcha.ts', 'community.ts', 'servers.ts', 'auth.ts', 'store.ts'] }, { domain: 'login', files: ['login.html', 'login-page.tsx', 'panel-captcha.js'] }, - { domain: 'accounts', files: ['accounts-page.tsx', 'accounts-shared.ts', 'accounts-columns.ts', 'accounts-store.ts', 'accounts-panels.tsx', 'accounts-queue-drag.ts', 'accounts-dialogs.tsx', 'accounts-dialog-limiter.tsx', 'accounts-dialog-usage.tsx', 'accounts-credit-detail.tsx', 'accounts-data.ts', 'accounts-domain.ts', 'zcode-plans-modal.tsx', 'balance-query.tsx'] }, + { domain: 'accounts', files: ['accounts-page.tsx', 'AccountsPage.tsx', 'AccountSheet.tsx', 'AccountsFilterSheet.tsx', 'accounts-shared.ts', 'accounts-columns.ts', 'accounts-store.ts', 'accounts-panels.tsx', 'accounts-queue-drag.ts', 'accounts-dialogs.tsx', 'accounts-dialog-limiter.tsx', 'accounts-dialog-usage.tsx', 'accounts-credit-detail.tsx', 'accounts-limit-detail.tsx', 'accounts-data.ts', 'accounts-domain.ts', 'accounts-group-settings.tsx', 'usage.ts', 'zcode-plans-modal.tsx', 'balance-query.tsx'] }, { domain: 'add-account', files: ['add-custom-provider.tsx', 'custom-provider-ui.js', 'providers.js', 'preset-providers.js', 'web-login.js', 'sms-login.js', 'autoclaw-oauth.js', 'aliyun-captcha.js', 'zcode-claim.js', 'zcode-captcha-pool.js', 'codearts-welfare.js'], prefixes: ['add-account-', 'add-provider-'] }, - { domain: 'models', files: ['model-capability.ts', 'model-capability-dialog.tsx', 'model-test-dialog.tsx'], prefixes: ['models-'] }, - { domain: 'settings', files: ['units.js'], prefixes: ['settings-'] }, + { domain: 'models', files: ['ModelsPage.tsx', 'model-capability.ts', 'model-capability-dialog.tsx', 'model-test-dialog.tsx'], prefixes: ['models-'] }, + // 「Agent」页:本机 AI 客户端的配置接管(客户端清单 / 配置编辑 / 写入预览) + { domain: 'agent', files: ['AgentPage.tsx'], prefixes: ['agent-'] }, + // 「设置 → 版本信息」面板(桌面端 system-info-pane.tsx 与手机端 + // SystemInfoSheet.tsx 共用同一批文案:版本 / 环境 / 路径 / 开关)。 + // 归 settings 域 —— 它读的就是设置页的分类之一,文案与设置页其它面板同类。 + // + // ⚠ 补 `settings-model.ts`(下游 2026-10-20):设置页的分类表 CATEGORIES + // 就在这个文件里,而分类名(通用 / 显示 / 路由 / 重试 / …)**只有那一处** + // 是权威来源。它原先不在清单中,症状是部分分类名没进词典 —— + // 凡是同一串文字恰好也出现在别处(如「通用」出现在提示词说明里)的能被动 + // 扫到,而「显示」「重试」「通知与告警」这类只此一处的就永远缺,英文界面 + // 直接露中文。侧栏现在把这 13 个分类平铺成了一级菜单(sidebar-layout.js + // 另有一份同源表),缺译会直接显眼。 + { domain: 'settings', files: ['units.js', 'SettingsPage.tsx', 'settings-model.ts', 'MorePage.tsx', 'PwaNotice.tsx', 'ServersPage.tsx', 'LoginPage.tsx', 'system-info-pane.tsx', 'SystemInfoSheet.tsx', 'sidebar-layout.js'], prefixes: ['settings-', 'sidebar-'] }, // AI 助手(侧边栏「智能」组的一级页 + 设置页「AI 助手」分类): // 对话页、工具集、设置面板三块 - { domain: 'assistant', prefixes: ['assistant-'] }, + { domain: 'assistant', files: ['AssistantPage.tsx', 'assistant.ts', 'assistantTools.ts', 'MarkdownLite.tsx'], prefixes: ['assistant-'] }, { domain: 'update', files: ['upgrade-panel.js'], prefixes: ['update-'] }, - { domain: 'keys', files: ['keys-page.tsx'] }, - { domain: 'proxies', files: ['proxies-page.tsx', 'proxies-columns.ts', 'proxies-state.ts', 'proxies-dialogs.tsx', 'proxies-sub-state.ts', 'proxies-sub-view.tsx', 'proxies-kernel-dialog.tsx', 'sponsor.ts'] }, - { domain: 'docs', files: ['docs-page.tsx'] }, - { domain: 'report', files: ['report-page.tsx', 'report-charts.ts'] }, - { domain: 'checkin', files: ['checkin-page.tsx', 'checkin-state.ts', 'checkin-schedule-tab.tsx', 'checkin-records-tab.tsx', 'checkin-schedules-state.ts'] }, - { domain: 'tasks', files: ['tasks-panel.tsx'] }, - { domain: 'logs', files: ['logs-panel.tsx'] }, - { domain: 'requests', files: ['requests-page.tsx', 'request-detail.tsx', 'request-hover.tsx', 'request-clear-modal.tsx', 'conversation-preview.js'] }, + { domain: 'keys', files: ['keys-page.tsx', 'KeysPage.tsx'] }, + { domain: 'proxies', files: ['ProxiesPage.tsx', 'proxies-page.tsx', 'proxies-columns.ts', 'proxies-state.ts', 'proxies-dialogs.tsx', 'proxies-sub-state.ts', 'proxies-sub-view.tsx', 'proxies-kernel-dialog.tsx', 'sponsor.ts'] }, + { domain: 'docs', files: ['docs-page.tsx', 'DocsPage.tsx'] }, + { domain: 'report', files: ['report-page.tsx', 'report-charts.ts', 'ReportPage.tsx', 'HomePage.tsx'] }, + { domain: 'checkin', files: ['CheckinPage.tsx', 'CheckinRecordsPage.tsx', 'CheckinClaimSheets.tsx', 'checkin-page.tsx', 'checkin-state.ts', 'checkin-schedule-tab.tsx', 'checkin-records-tab.tsx', 'checkin-schedules-state.ts'] }, + { domain: 'tasks', files: ['tasks-panel.tsx', 'TasksPage.tsx'] }, + { domain: 'logs', files: ['logs-panel.tsx', 'LogsPage.tsx'] }, + { domain: 'requests', files: ['RequestsPage.tsx', 'requests-page.tsx', 'request-detail.tsx', 'request-hover.tsx', 'request-clear-modal.tsx', 'conversation-preview.js'] }, { domain: 'tables', files: ['table-columns.js', 'table-col-settings.tsx', 'table-shell.tsx'] }, { domain: 'port', files: ['port-panel.tsx'] }, ] @@ -114,6 +134,26 @@ function islandSources() { return out.filter(file => !EXCLUDED.has(file)) } +/** + * mobile/src 下所有 .ts / .tsx(递归)。 + * + * 目录不存在时返回空数组:手机端是后加的工程,别的分支 / 旧检出里没有它, + * 扫描器不该因为一个可选目录直接崩掉。 + */ +function mobileSources() { + if (!fs.existsSync(MOBILE_SRC)) return [] + const out = [] + const walk = dir => { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name) + if (entry.isDirectory()) walk(full) + else if (/\.tsx?$/.test(entry.name)) out.push(full) + } + } + walk(MOBILE_SRC) + return out.filter(file => !EXCLUDED.has(file)) +} + /** 域解析:先精确名、再前缀;都命不中返回 null(调用方归 common + warning) */ function domainOf(file) { const base = path.basename(file) @@ -218,7 +258,16 @@ function extractFromHtml(source, file, warnings) { function scan() { const perDomain = new Map(DOMAIN_ORDER.map(domain => [domain, new Set()])) const warnings = [] - const files = [...uiScripts(), path.join(UI, 'index.html'), path.join(UI, 'login.html'), ...islandSources()] + const files = [ + ...uiScripts(), + path.join(UI, 'index.html'), + path.join(UI, 'login.html'), + // 手机端入口页(`/m` 直接命中它)。它的文案是 data-i18n 与内联 t(), + // 与 index.html 同一套提取通道。 + path.join(UI, 'm.html'), + ...islandSources(), + ...mobileSources(), + ] for (const file of files) { if (!fs.existsSync(file)) continue diff --git a/desktop-tauri/mobile/android/.gitignore b/desktop-tauri/mobile/android/.gitignore new file mode 100644 index 00000000..48354a3d --- /dev/null +++ b/desktop-tauri/mobile/android/.gitignore @@ -0,0 +1,101 @@ +# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore + +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +# Android Profiling +*.hprof + +# Cordova plugins for Capacitor +capacitor-cordova-android-plugins + +# Copied web assets +app/src/main/assets/public + +# Generated Config files +app/src/main/assets/capacitor.config.json +app/src/main/assets/capacitor.plugins.json +app/src/main/res/xml/config.xml diff --git a/desktop-tauri/mobile/android/app/.gitignore b/desktop-tauri/mobile/android/app/.gitignore new file mode 100644 index 00000000..043df802 --- /dev/null +++ b/desktop-tauri/mobile/android/app/.gitignore @@ -0,0 +1,2 @@ +/build/* +!/build/.npmkeep diff --git a/desktop-tauri/mobile/android/app/build.gradle b/desktop-tauri/mobile/android/app/build.gradle new file mode 100644 index 00000000..c2b2c0a2 --- /dev/null +++ b/desktop-tauri/mobile/android/app/build.gradle @@ -0,0 +1,92 @@ +apply plugin: 'com.android.application' + +// APK 签名密钥的路径与密码:**不进仓库**。 +// +// 密钥(agent2api-release.jks)与密码一起公开,等于把「签一个同包名的假 +// APK 覆盖安装」的能力交给所有人;本仓库是公开的,所以两者都只在打包机上。 +// 文件缺失时 release 构建会明确报错(见下面 signingConfigs),而不是悄悄 +// 产出一个未签名包。 +def keystorePropertiesFile = rootProject.file('keystore.properties') +def keystoreProperties = new Properties() +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} + +android { + namespace = "com.agent2api.mobile" + compileSdk = rootProject.ext.compileSdkVersion + defaultConfig { + applicationId "com.agent2api.mobile" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + // 与 package.json 的版本对齐:apk 文件名与「关于」页都取这里。 + versionCode rootProject.ext.apkVersionCode + versionName rootProject.ext.apkVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + aaptOptions { + // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. + // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61 + ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~' + } + } + + signingConfigs { + release { + // 换密钥会导致老版本无法覆盖升级(签名不一致),这份密钥要一直 + // 用下去 —— 备份它,别只留在这一台机器上。 + storeFile file(keystoreProperties['storeFile'] ?: '../agent2api-release.jks') + storePassword keystoreProperties['storePassword'] + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + } + } + buildTypes { + release { + signingConfig signingConfigs.release + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +// release 包缺密钥或密码时,在配置阶段就失败:否则 AGP 会走到打包末尾才报 +// 「keystore was tampered with, or password was incorrect」这类看不出根因的错。 +gradle.taskGraph.whenReady { graph -> + def buildsRelease = graph.allTasks.any { it.name.toLowerCase().contains('release') } + if (buildsRelease && !keystorePropertiesFile.exists()) { + throw new GradleException( + "缺少 android/keystore.properties —— release 包需要签名密钥。" + + "照 keystore.properties.example 建一份,并把 agent2api-release.jks 放回 android/ 目录。" + ) + } +} + + +repositories { + flatDir{ + dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" + implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" + implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" + implementation project(':capacitor-android') + testImplementation "junit:junit:$junitVersion" + androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" + implementation project(':capacitor-cordova-android-plugins') +} + +apply from: 'capacitor.build.gradle' + +try { + def servicesJSON = file('google-services.json') + if (servicesJSON.text) { + apply plugin: 'com.google.gms.google-services' + } +} catch(Exception e) { + logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work") +} diff --git a/desktop-tauri/mobile/android/app/capacitor.build.gradle b/desktop-tauri/mobile/android/app/capacitor.build.gradle new file mode 100644 index 00000000..cc355a8c --- /dev/null +++ b/desktop-tauri/mobile/android/app/capacitor.build.gradle @@ -0,0 +1,20 @@ +// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN + +android { + compileOptions { + sourceCompatibility JavaVersion.VERSION_21 + targetCompatibility JavaVersion.VERSION_21 + } +} + +apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" +dependencies { + implementation project(':capacitor-app') + implementation project(':capacitor-keyboard') + +} + + +if (hasProperty('postBuildExtras')) { + postBuildExtras() +} diff --git a/desktop-tauri/mobile/android/app/proguard-rules.pro b/desktop-tauri/mobile/android/app/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/desktop-tauri/mobile/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/desktop-tauri/mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java b/desktop-tauri/mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java new file mode 100644 index 00000000..f2c2217e --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.getcapacitor.myapp; + +import static org.junit.Assert.*; + +import android.content.Context; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("com.getcapacitor.app", appContext.getPackageName()); + } +} diff --git a/desktop-tauri/mobile/android/app/src/main/AndroidManifest.xml b/desktop-tauri/mobile/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..6446d4df --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/desktop-tauri/mobile/android/app/src/main/java/com/agent2api/mobile/ApkUpdaterPlugin.java b/desktop-tauri/mobile/android/app/src/main/java/com/agent2api/mobile/ApkUpdaterPlugin.java new file mode 100644 index 00000000..e5964f0e --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/java/com/agent2api/mobile/ApkUpdaterPlugin.java @@ -0,0 +1,283 @@ +package com.agent2api.mobile; + +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.provider.Settings; +import androidx.core.content.FileProvider; +import com.getcapacitor.JSObject; +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * 应用内更新:下载 APK 到应用私有缓存目录,再交给系统安装器。 + * + * ── 为什么自己写而不用 @capacitor/filesystem ────────────────────── + * 那个插件没有「下载」能力(只有读写),而 `CapacitorHttp` 会把整个响应读进 + * 内存 —— 几十 MB 的安装包会直接把 WebView 撑爆。这里用 HttpURLConnection + * 边读边写,内存占用只有缓冲区大小。 + * + * ── 为什么放 cacheDir 而不是外部存储 ──────────────────────────── + * 不需要任何存储权限;FileProvider 通过 `file_paths.xml` 的 `` + * 已经把这个目录纳管(见 `res/xml/file_paths.xml`)。 + * + * ── 安装权限 ──────────────────────────────────────────────────── + * Android 8.0 起「安装未知来源应用」是按应用授权的。没授权时 `startActivity` + * 会抛 ActivityNotFoundException 或静默失败,所以先查 + * `canRequestPackageInstalls()`;没有就返回 `needsPermission: true`, + * 由界面引导用户去系统设置页打开(见 `openInstallPermissionSettings`)。 + * 不在这里直接跳设置页 —— 那样用户不知道发生了什么就被弹出应用。 + */ +@CapacitorPlugin(name = "ApkUpdater") +public class ApkUpdaterPlugin extends Plugin { + + /** 下载线程:单线程足够(同一时刻只该有一个下载任务) */ + private final ExecutorService executor = Executors.newSingleThreadExecutor(); + + /** 下载中的取消标志(`download` 与 `cancel` 之间共享) */ + private final AtomicBoolean cancelled = new AtomicBoolean(false); + + /** 目标文件:应用缓存目录下的固定名,重下时覆盖 */ + private File targetFile() { + File dir = new File(getContext().getCacheDir(), "updates"); + if (!dir.exists()) { + //noinspection ResultOfMethodCallIgnored + dir.mkdirs(); + } + return new File(dir, "agent2api-update.apk"); + } + + /** + * 下载 APK。参数 `{ url }`,`url` 是 Release 资产在 GitHub 上的 + * `browser_download_url`(手机自己出网,不经网关中转)。 + * + * 进度通过 `downloadProgress` 事件推送(`{received, total, percent}`), + * 界面据此画进度条 —— 不用轮询,省一次往返。 + */ + @PluginMethod + public void download(PluginCall call) { + String url = call.getString("url", ""); + if (url == null || url.isEmpty()) { + call.reject("缺少下载地址"); + return; + } + cancelled.set(false); + + executor.execute(() -> { + FileOutputStream output = null; + try { + File file = targetFile(); + // 先删旧包:上一次的残留不能当成本次的结果 + if (file.exists()) { + //noinspection ResultOfMethodCallIgnored + file.delete(); + } + + HttpURLConnection connection = openFollowingRedirects(url); + int status = connection.getResponseCode(); + if (status != HttpURLConnection.HTTP_OK) { + connection.disconnect(); + call.reject("下载失败:服务器返回 HTTP " + status); + return; + } + + long total = connection.getContentLengthLong(); + InputStream input = connection.getInputStream(); + output = new FileOutputStream(file); + byte[] buffer = new byte[64 * 1024]; + long received = 0; + long lastEmit = 0; + int read; + while ((read = input.read(buffer)) != -1) { + if (cancelled.get()) { + // 取消:删掉半成品,别让它被误当成完整包 + input.close(); + output.close(); + output = null; + //noinspection ResultOfMethodCallIgnored + file.delete(); + call.reject("已取消下载"); + return; + } + output.write(buffer, 0, read); + received += read; + // 每 ~256KB 推一次进度:太频繁会让 WebView 忙于渲染 + long now = System.currentTimeMillis(); + if (now - lastEmit > 200) { + lastEmit = now; + emitProgress(received, total); + } + } + output.flush(); + output.close(); + output = null; + input.close(); + emitProgress(received, total); + + JSObject result = new JSObject(); + result.put("path", file.getAbsolutePath()); + result.put("size", received); + call.resolve(result); + } catch (Exception error) { + if (output != null) { + try { + output.close(); + } catch (Exception ignored) { + // 关闭失败不影响错误上报 + } + } + // 失败时不留半成品 + File file = targetFile(); + if (file.exists()) { + //noinspection ResultOfMethodCallIgnored + file.delete(); + } + call.reject("下载失败:" + error.getMessage()); + } + }); + } + + /** + * 打开连接并**手动**跟随重定向。 + * + * GitHub 的 Release 资产地址(`github.com/…/releases/download/…`)会 302 到 + * `objects.githubusercontent.com`。`setInstanceFollowRedirects(true)` 看似 + * 能处理,但它在跨协议(https → https 没问题、https → http 会抛)以及 + * 某些情况下会丢掉请求头 —— 而 GitHub 要求每个请求都带 User-Agent, + * 丢了就直接 403。所以这里自己跟,最多 5 跳。 + */ + private HttpURLConnection openFollowingRedirects(String startUrl) throws Exception { + String current = startUrl; + for (int hop = 0; hop < 5; hop++) { + HttpURLConnection connection = (HttpURLConnection) new URL(current).openConnection(); + connection.setRequestMethod("GET"); + connection.setConnectTimeout(30_000); + // 读超时给足:几十 MB 的包在慢网下也要能下完 + connection.setReadTimeout(60_000); + connection.setInstanceFollowRedirects(false); + // GitHub 对没有 User-Agent 的请求直接 403 + connection.setRequestProperty("User-Agent", "agent2api-mobile"); + connection.setRequestProperty("Accept", "application/octet-stream"); + + int status = connection.getResponseCode(); + if (status == HttpURLConnection.HTTP_MOVED_PERM + || status == HttpURLConnection.HTTP_MOVED_TEMP + || status == HttpURLConnection.HTTP_SEE_OTHER + || status == 307 + || status == 308) { + String location = connection.getHeaderField("Location"); + connection.disconnect(); + if (location == null || location.isEmpty()) { + throw new Exception("重定向缺少 Location"); + } + // 相对地址按当前 URL 解析(GitHub 目前给绝对地址,但别假定) + current = new URL(new URL(current), location).toString(); + continue; + } + return connection; + } + throw new Exception("重定向次数过多"); + } + + private void emitProgress(long received, long total) { + JSObject data = new JSObject(); + data.put("received", received); + data.put("total", total); + data.put("percent", total > 0 ? (int) (received * 100 / total) : -1); + notifyListeners("downloadProgress", data); + } + + /** 取消正在进行的下载(已下好的包不动 —— 取消只针对「正在下」这个动作) */ + @PluginMethod + public void cancel(PluginCall call) { + cancelled.set(true); + call.resolve(); + } + + /** + * 当前是否允许安装未知来源应用。 + * + * Android 8.0 以下没有这个概念(默认允许),直接返回 true —— 那些版本 + * 由系统安装界面自己提示。 + */ + @PluginMethod + public void canInstall(PluginCall call) { + JSObject result = new JSObject(); + boolean allowed = true; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + allowed = getContext().getPackageManager().canRequestPackageInstalls(); + } + result.put("allowed", allowed); + call.resolve(result); + } + + /** 跳到本应用的「安装未知应用」设置页(`canInstall` 为 false 时由界面调用) */ + @PluginMethod + public void openInstallPermissionSettings(PluginCall call) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES); + intent.setData(Uri.parse("package:" + getContext().getPackageName())); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getContext().startActivity(intent); + } + call.resolve(); + } + + /** + * 调起系统安装器。 + * + * 用 FileProvider 生成 `content://` URI:Android 7.0 起直接传 `file://` + * 会抛 FileUriExposedException。`FLAG_GRANT_READ_URI_PERMISSION` 把临时读 + * 权限授给安装器进程 —— 少了这个,安装器打不开文件、表现为「解析包出错」。 + */ + @PluginMethod + public void install(PluginCall call) { + File file = targetFile(); + if (!file.exists()) { + call.reject("安装包不存在,请先下载"); + return; + } + try { + Uri uri = FileProvider.getUriForFile( + getContext(), + getContext().getPackageName() + ".fileprovider", + file + ); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(uri, "application/vnd.android.package-archive"); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getContext().startActivity(intent); + call.resolve(); + } catch (Exception error) { + call.reject("无法启动安装器:" + error.getMessage()); + } + } + + /** 当前已下载的安装包信息(重进设置页时据此显示「已下载,待安装」) */ + @PluginMethod + public void downloaded(PluginCall call) { + File file = targetFile(); + JSObject result = new JSObject(); + result.put("exists", file.exists()); + result.put("size", file.exists() ? file.length() : 0); + call.resolve(result); + } + + @Override + protected void handleOnDestroy() { + cancelled.set(true); + executor.shutdownNow(); + super.handleOnDestroy(); + } +} diff --git a/desktop-tauri/mobile/android/app/src/main/java/com/agent2api/mobile/MainActivity.java b/desktop-tauri/mobile/android/app/src/main/java/com/agent2api/mobile/MainActivity.java new file mode 100644 index 00000000..741c50d3 --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/java/com/agent2api/mobile/MainActivity.java @@ -0,0 +1,15 @@ +package com.agent2api.mobile; + +import android.os.Bundle; +import com.getcapacitor.BridgeActivity; + +public class MainActivity extends BridgeActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + // 自定义插件必须在 super.onCreate 之前注册:BridgeActivity 在那里 + // 就把插件表建好了(见 BridgeActivity.load())。 + registerPlugin(ApkUpdaterPlugin.class); + super.onCreate(savedInstanceState); + } +} diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable-land-hdpi/splash.png b/desktop-tauri/mobile/android/app/src/main/res/drawable-land-hdpi/splash.png new file mode 100644 index 00000000..0bf75f3a Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/drawable-land-hdpi/splash.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable-land-mdpi/splash.png b/desktop-tauri/mobile/android/app/src/main/res/drawable-land-mdpi/splash.png new file mode 100644 index 00000000..6daa0026 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/drawable-land-mdpi/splash.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable-land-xhdpi/splash.png b/desktop-tauri/mobile/android/app/src/main/res/drawable-land-xhdpi/splash.png new file mode 100644 index 00000000..ff28de5e Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/drawable-land-xhdpi/splash.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable-land-xxhdpi/splash.png b/desktop-tauri/mobile/android/app/src/main/res/drawable-land-xxhdpi/splash.png new file mode 100644 index 00000000..6a5ee253 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/drawable-land-xxhdpi/splash.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable-land-xxxhdpi/splash.png b/desktop-tauri/mobile/android/app/src/main/res/drawable-land-xxxhdpi/splash.png new file mode 100644 index 00000000..f495420d Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/drawable-land-xxxhdpi/splash.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable-port-hdpi/splash.png b/desktop-tauri/mobile/android/app/src/main/res/drawable-port-hdpi/splash.png new file mode 100644 index 00000000..ae8704d1 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/drawable-port-hdpi/splash.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable-port-mdpi/splash.png b/desktop-tauri/mobile/android/app/src/main/res/drawable-port-mdpi/splash.png new file mode 100644 index 00000000..11230448 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/drawable-port-mdpi/splash.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable-port-xhdpi/splash.png b/desktop-tauri/mobile/android/app/src/main/res/drawable-port-xhdpi/splash.png new file mode 100644 index 00000000..501896a3 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/drawable-port-xhdpi/splash.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable-port-xxhdpi/splash.png b/desktop-tauri/mobile/android/app/src/main/res/drawable-port-xxhdpi/splash.png new file mode 100644 index 00000000..bd269ebe Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/drawable-port-xxhdpi/splash.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable-port-xxxhdpi/splash.png b/desktop-tauri/mobile/android/app/src/main/res/drawable-port-xxxhdpi/splash.png new file mode 100644 index 00000000..766fd840 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/drawable-port-xxxhdpi/splash.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/desktop-tauri/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..c7bd21db --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml b/desktop-tauri/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..d5fccc53 --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/desktop-tauri/mobile/android/app/src/main/res/drawable/splash.png b/desktop-tauri/mobile/android/app/src/main/res/drawable/splash.png new file mode 100644 index 00000000..6daa0026 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/drawable/splash.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/layout/activity_main.xml b/desktop-tauri/mobile/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..b5ad1387 --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/desktop-tauri/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..036d09bc --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/desktop-tauri/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..036d09bc --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..11ced1c3 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..b2a8b50d Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..a4cd3835 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..2e341d93 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..f249138f Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..2d29f569 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..3bd52207 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..5e2a8107 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..68b85a6c Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..6eae3813 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..0cf9d9f6 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..95828f6f Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..abdb581c Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..e92f7749 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..d0cadd36 Binary files /dev/null and b/desktop-tauri/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/desktop-tauri/mobile/android/app/src/main/res/values/ic_launcher_background.xml b/desktop-tauri/mobile/android/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 00000000..293c98c1 --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,5 @@ + + + + #007AFF + \ No newline at end of file diff --git a/desktop-tauri/mobile/android/app/src/main/res/values/strings.xml b/desktop-tauri/mobile/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..c48b745f --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + + Agent2API + Agent2API + com.agent2api.mobile + com.agent2api.mobile + diff --git a/desktop-tauri/mobile/android/app/src/main/res/values/styles.xml b/desktop-tauri/mobile/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..be874e54 --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/res/values/styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/desktop-tauri/mobile/android/app/src/main/res/xml/file_paths.xml b/desktop-tauri/mobile/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 00000000..ad00d016 --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/desktop-tauri/mobile/android/app/src/main/res/xml/network_security_config.xml b/desktop-tauri/mobile/android/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 00000000..06c289ac --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/desktop-tauri/mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java b/desktop-tauri/mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java new file mode 100644 index 00000000..02973278 --- /dev/null +++ b/desktop-tauri/mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java @@ -0,0 +1,18 @@ +package com.getcapacitor.myapp; + +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} diff --git a/desktop-tauri/mobile/android/build.gradle b/desktop-tauri/mobile/android/build.gradle new file mode 100644 index 00000000..f8f0e43b --- /dev/null +++ b/desktop-tauri/mobile/android/build.gradle @@ -0,0 +1,29 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.13.0' + classpath 'com.google.gms:google-services:4.4.4' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +apply from: "variables.gradle" + +allprojects { + repositories { + google() + mavenCentral() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/desktop-tauri/mobile/android/capacitor.settings.gradle b/desktop-tauri/mobile/android/capacitor.settings.gradle new file mode 100644 index 00000000..9ec17151 --- /dev/null +++ b/desktop-tauri/mobile/android/capacitor.settings.gradle @@ -0,0 +1,9 @@ +// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN +include ':capacitor-android' +project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') + +include ':capacitor-app' +project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android') + +include ':capacitor-keyboard' +project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android') diff --git a/desktop-tauri/mobile/android/gradle.properties b/desktop-tauri/mobile/android/gradle.properties new file mode 100644 index 00000000..98c47c8d --- /dev/null +++ b/desktop-tauri/mobile/android/gradle.properties @@ -0,0 +1,27 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true + +# Capacitor 8 要求 Java 21 编译(JavaVersion.VERSION_21)。 +# 本机默认 JAVA_HOME 指向 JDK 17(其他项目在用),这里显式指定 21, +# 不依赖环境变量 —— 否则构建报「无效的源发行版:21」。 +org.gradle.java.home=C:/Program Files/Eclipse Adoptium/jdk-21.0.11.10-hotspot diff --git a/desktop-tauri/mobile/android/gradle/wrapper/gradle-wrapper.jar b/desktop-tauri/mobile/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..1b33c55b Binary files /dev/null and b/desktop-tauri/mobile/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/desktop-tauri/mobile/android/gradle/wrapper/gradle-wrapper.properties b/desktop-tauri/mobile/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..414ba5d2 --- /dev/null +++ b/desktop-tauri/mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.14.3-all.zip +networkTimeout=60000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/desktop-tauri/mobile/android/gradlew b/desktop-tauri/mobile/android/gradlew new file mode 100644 index 00000000..23d15a93 --- /dev/null +++ b/desktop-tauri/mobile/android/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/desktop-tauri/mobile/android/gradlew.bat b/desktop-tauri/mobile/android/gradlew.bat new file mode 100644 index 00000000..db3a6ac2 --- /dev/null +++ b/desktop-tauri/mobile/android/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/desktop-tauri/mobile/android/keystore.properties.example b/desktop-tauri/mobile/android/keystore.properties.example new file mode 100644 index 00000000..988103bd --- /dev/null +++ b/desktop-tauri/mobile/android/keystore.properties.example @@ -0,0 +1,11 @@ +# APK 签名密钥的路径与密码。 +# +# 这个文件**不入库**(见根目录 .gitignore)。打 release 包前复制一份为 +# keystore.properties,填上真实值,并把 agent2api-release.jks 放回本目录。 +# +# 换密钥会导致已安装的老版本无法覆盖升级(签名不一致),所以这份密钥要一直 +# 用下去 —— 除了打包机,另存一份到安全的地方。 +storeFile=../agent2api-release.jks +storePassword=在这里填 +keyAlias=agent2api +keyPassword=在这里填 diff --git a/desktop-tauri/mobile/android/settings.gradle b/desktop-tauri/mobile/android/settings.gradle new file mode 100644 index 00000000..3b4431d7 --- /dev/null +++ b/desktop-tauri/mobile/android/settings.gradle @@ -0,0 +1,5 @@ +include ':app' +include ':capacitor-cordova-android-plugins' +project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/') + +apply from: 'capacitor.settings.gradle' \ No newline at end of file diff --git a/desktop-tauri/mobile/android/variables.gradle b/desktop-tauri/mobile/android/variables.gradle new file mode 100644 index 00000000..d52d58ba --- /dev/null +++ b/desktop-tauri/mobile/android/variables.gradle @@ -0,0 +1,33 @@ +ext { + minSdkVersion = 24 + compileSdkVersion = 36 + targetSdkVersion = 36 + androidxActivityVersion = '1.11.0' + androidxAppCompatVersion = '1.7.1' + androidxCoordinatorLayoutVersion = '1.3.0' + androidxCoreVersion = '1.17.0' + androidxFragmentVersion = '1.8.9' + coreSplashScreenVersion = '1.2.0' + androidxWebkitVersion = '1.14.0' + junitVersion = '4.13.2' + androidxJunitVersion = '1.3.0' + androidxEspressoCoreVersion = '3.7.0' + cordovaAndroidVersion = '14.0.1' + + // APK 的 versionName / versionCode 取自 mobile/package.json —— 单一来源, + // 不必在打每次包前手改 gradle。版本号本身由根 package.json 统一维护。 + // + // versionCode 必须是整数且只增不减(Android 靠它判「这是不是升级」): + // 把 semver 的前三段按 major*10000 + minor*100 + patch 折算, + // 3.1.0 → 30100、3.1.1 → 30101、3.10.0 → 31000。 + // 预发布后缀(-beta.1)不参与折算 —— 它比同号正式版小,但 Android + // 只认整数,这里取同号值;beta 与正式版不会同时发布,不构成冲突。 + def mobilePkg = new groovy.json.JsonSlurper().parse( + new File(rootProject.projectDir, '../package.json') + ) + def semver = mobilePkg.version.tokenize('-')[0].tokenize('.') + apkVersionName = mobilePkg.version + apkVersionCode = semver[0].toInteger() * 10000 + + semver[1].toInteger() * 100 + + (semver.size() > 2 ? semver[2].toInteger() : 0) +} diff --git a/desktop-tauri/mobile/apk.html b/desktop-tauri/mobile/apk.html new file mode 100644 index 00000000..f26765ae --- /dev/null +++ b/desktop-tauri/mobile/apk.html @@ -0,0 +1,76 @@ + + + + + + + + + + +Agent2API + + + + + + + + +
+ + + + diff --git a/desktop-tauri/mobile/capacitor.config.ts b/desktop-tauri/mobile/capacitor.config.ts new file mode 100644 index 00000000..b9bfaccb --- /dev/null +++ b/desktop-tauri/mobile/capacitor.config.ts @@ -0,0 +1,74 @@ +import type { CapacitorConfig } from '@capacitor/cli' + +/** + * Capacitor 配置(Android APK + iOS IPA 共用一份 Web 产物)。 + * + * ── 为什么 androidScheme 是 https,而 cleartext 打开 ────────── + * 这两个不矛盾,各管一件事: + * · `androidScheme: 'https'` 决定**应用自己页面**的 origin(`https://localhost`)。 + * 它必须是安全上下文,否则 `crypto.subtle` 不可用 —— 登录页的人机验证 + * 要用它算 SHA-256。改成 http 会让验证退回纯 JS 实现,慢且没必要。 + * · `cleartext: true` + manifest 里的 `usesCleartextTraffic` 决定**能不能 + * 访问 http:// 的网关**。局域网网关都是 http(没有证书),不开这个 + * 所有请求会被 Android 9+ 直接拦掉。 + * + * 页面是 https、请求是 http —— 这是 mixed content,WebView 默认禁止。 + * 所以还要 `allowMixedContent: true`。它被官方标注「不用于生产」,但这个 + * 应用的场景就是「访问局域网里的 http 服务」,没有别的选择;且页面内容是 + * 我们自己打进包里的,不存在被第三方脚本注入的风险面。 + * + * ── iOS 这边为什么不能照抄 androidScheme ────────────────────── + * `iosScheme` 不允许是 `https` / `http` —— 那是 WKWebView 自己处理的协议, + * Capacitor 在 `InstanceDescriptor.normalize()` 里会把它判为非法并**静默退回** + * 默认值 `capacitor`(见 @capacitor/ios 的 CAPInstanceDescriptor.swift:169)。 + * 所以 iOS 的页面 origin 固定是 `capacitor://localhost`。 + * + * 代价是它不算「安全上下文」,`crypto.subtle` 在 iOS 上不可用 —— 登录页的 + * 人机验证会自动走 `src/api/auth.ts` 里的纯 JS SHA-256 兜底(那条路径本来 + * 就是为 http 局域网准备的,只是慢一点,结果一致)。 + * + * ── iOS 的明文 http 网关怎么放行 ────────────────────────────── + * 靠 `ios/App/App/Info.plist` 的 `NSAppTransportSecurity`(ATS)例外,不在 + * 这里配。网关是局域网 IP + http,iOS 17 起 ATS 默认连 IP 地址都拦,所以 + * 那边同时开了 `NSAllowsArbitraryLoads` 与 `NSAllowsLocalNetworking`。 + * + * ── 为什么不用 server.url 指向网关 ──────────────────────────── + * 那种做法把 App 变成一个去掉了地址栏的浏览器:切服务器要重载整个 WebView, + * 登录也还是服务器那份网页,而且离线时连界面都起不来。这里要的是「界面在 + * 包里 + 地址可配 + 多台切换」,所以走打包 + 跨源请求。 + */ +const config: CapacitorConfig = { + appId: 'com.agent2api.mobile', + appName: 'Agent2API', + // 构建产物目录(npm run build:native 的输出,Android / iOS 共用) + webDir: 'android-www', + android: { + // 局域网网关普遍是 http,不开这个请求全被拦 + allowMixedContent: true, + }, + ios: { + // 安全区由页面自己的 env(safe-area-inset-*) 接管:WKWebView 不再额外 + // 加一圈内边距,否则刘海 / 底部横条会被算两次,顶部多出一条空白。 + contentInset: 'never', + // 与 Android 一致:底色交给页面主题,避免深色模式下边缘闪白 + backgroundColor: '#f3f3f6', + }, + server: { + // 页面 origin 用 https://localhost:安全上下文,crypto.subtle 可用 + androidScheme: 'https', + // 允许访问明文 http 的网关 + cleartext: true, + }, + plugins: { + Keyboard: { + // iOS 默认的 native 模式会把整个 WebView 缩短,页面里的 fixed / flex + // 布局会跟着抖一下;body 模式只改 body 高度,配合 visualViewport 的 + // 键盘判断(Shell.tsx 的 useKeyboardOpen)更稳。 + resize: 'body', + // Android 全屏时键盘不会触发 resize,插件需要额外处理 + resizeOnFullScreen: true, + }, + }, +} + +export default config diff --git a/desktop-tauri/mobile/ios/.gitignore b/desktop-tauri/mobile/ios/.gitignore new file mode 100644 index 00000000..f4702997 --- /dev/null +++ b/desktop-tauri/mobile/ios/.gitignore @@ -0,0 +1,13 @@ +App/build +App/Pods +App/output +App/App/public +DerivedData +xcuserdata + +# Cordova plugins for Capacitor +capacitor-cordova-ios-plugins + +# Generated Config files +App/App/capacitor.config.json +App/App/config.xml diff --git a/desktop-tauri/mobile/ios/App/App.xcodeproj/project.pbxproj b/desktop-tauri/mobile/ios/App/App.xcodeproj/project.pbxproj new file mode 100644 index 00000000..0d7380d4 --- /dev/null +++ b/desktop-tauri/mobile/ios/App/App.xcodeproj/project.pbxproj @@ -0,0 +1,380 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + 2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; }; + 4D22ABE92AF431CB00220026 /* CapApp-SPM in Frameworks */ = {isa = PBXBuildFile; productRef = 4D22ABE82AF431CB00220026 /* CapApp-SPM */; }; + 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; }; + 504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; }; + 504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; }; + 504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; }; + 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; + 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; + 9582B6832FE993A70072D4E8 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9582B6822FE993A50072D4E8 /* SceneDelegate.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = ""; }; + 50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = ""; }; + 504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; + 9582B6822FE993A50072D4E8 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 958DCC722DB07C7200EA8C5F /* debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = debug.xcconfig; path = ../debug.xcconfig; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 504EC3011FED79650016851F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D22ABE92AF431CB00220026 /* CapApp-SPM in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 504EC2FB1FED79650016851F = { + isa = PBXGroup; + children = ( + 958DCC722DB07C7200EA8C5F /* debug.xcconfig */, + 504EC3061FED79650016851F /* App */, + 504EC3051FED79650016851F /* Products */, + ); + sourceTree = ""; + }; + 504EC3051FED79650016851F /* Products */ = { + isa = PBXGroup; + children = ( + 504EC3041FED79650016851F /* App.app */, + ); + name = Products; + sourceTree = ""; + }; + 504EC3061FED79650016851F /* App */ = { + isa = PBXGroup; + children = ( + 9582B6822FE993A50072D4E8 /* SceneDelegate.swift */, + 50379B222058CBB4000EE86E /* capacitor.config.json */, + 504EC3071FED79650016851F /* AppDelegate.swift */, + 504EC30B1FED79650016851F /* Main.storyboard */, + 504EC30E1FED79650016851F /* Assets.xcassets */, + 504EC3101FED79650016851F /* LaunchScreen.storyboard */, + 504EC3131FED79650016851F /* Info.plist */, + 2FAD9762203C412B000D30F8 /* config.xml */, + 50B271D01FEDC1A000F3C39B /* public */, + ); + path = App; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 504EC3031FED79650016851F /* App */ = { + isa = PBXNativeTarget; + buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */; + buildPhases = ( + 504EC3001FED79650016851F /* Sources */, + 504EC3011FED79650016851F /* Frameworks */, + 504EC3021FED79650016851F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = App; + packageProductDependencies = ( + 4D22ABE82AF431CB00220026 /* CapApp-SPM */, + ); + productName = App; + productReference = 504EC3041FED79650016851F /* App.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 504EC2FC1FED79650016851F /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0920; + TargetAttributes = { + 504EC3031FED79650016851F = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 504EC2FB1FED79650016851F; + packageReferences = ( + D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference "CapApp-SPM" */, + ); + productRefGroup = 504EC3051FED79650016851F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 504EC3031FED79650016851F /* App */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 504EC3021FED79650016851F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */, + 50B271D11FEDC1A000F3C39B /* public in Resources */, + 504EC30F1FED79650016851F /* Assets.xcassets in Resources */, + 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */, + 504EC30D1FED79650016851F /* Main.storyboard in Resources */, + 2FAD9763203C412B000D30F8 /* config.xml in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 504EC3001FED79650016851F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 504EC3081FED79650016851F /* AppDelegate.swift in Sources */, + 9582B6832FE993A70072D4E8 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 504EC30B1FED79650016851F /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 504EC30C1FED79650016851F /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 504EC3101FED79650016851F /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 504EC3111FED79650016851F /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 504EC3141FED79650016851F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 958DCC722DB07C7200EA8C5F /* debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 504EC3151FED79650016851F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 504EC3171FED79650016851F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 958DCC722DB07C7200EA8C5F /* debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = App/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; + PRODUCT_BUNDLE_IDENTIFIER = com.agent2api.mobile; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 504EC3181FED79650016851F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = App/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.agent2api.mobile; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 504EC3141FED79650016851F /* Debug */, + 504EC3151FED79650016851F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 504EC3171FED79650016851F /* Debug */, + 504EC3181FED79650016851F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference "CapApp-SPM" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "CapApp-SPM"; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 4D22ABE82AF431CB00220026 /* CapApp-SPM */ = { + isa = XCSwiftPackageProductDependency; + package = D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference "CapApp-SPM" */; + productName = "CapApp-SPM"; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 504EC2FC1FED79650016851F /* Project object */; +} diff --git a/desktop-tauri/mobile/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/desktop-tauri/mobile/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/desktop-tauri/mobile/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/desktop-tauri/mobile/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme b/desktop-tauri/mobile/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme new file mode 100644 index 00000000..ab40088e --- /dev/null +++ b/desktop-tauri/mobile/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/desktop-tauri/mobile/ios/App/App/AppDelegate.swift b/desktop-tauri/mobile/ios/App/App/AppDelegate.swift new file mode 100644 index 00000000..fec95aca --- /dev/null +++ b/desktop-tauri/mobile/ios/App/App/AppDelegate.swift @@ -0,0 +1,44 @@ +import UIKit +import Capacitor + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + func application(_ application: UIApplication, + configurationForConnecting connectingSceneSession: UISceneSession, + options: UIScene.ConnectionOptions) -> UISceneConfiguration { + let config = UISceneConfiguration(name: "Default Configuration", + sessionRole: connectingSceneSession.role) + config.delegateClass = SceneDelegate.self + return config + } +} diff --git a/desktop-tauri/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png new file mode 100644 index 00000000..6767a516 Binary files /dev/null and b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png differ diff --git a/desktop-tauri/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..9b7d382d --- /dev/null +++ b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "AppIcon-512@2x.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Contents.json b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json new file mode 100644 index 00000000..d7d96a67 --- /dev/null +++ b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "splash-2732x2732-2.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "splash-2732x2732-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "splash-2732x2732.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png new file mode 100644 index 00000000..7c59ff92 Binary files /dev/null and b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png differ diff --git a/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png new file mode 100644 index 00000000..7c59ff92 Binary files /dev/null and b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png differ diff --git a/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png new file mode 100644 index 00000000..7c59ff92 Binary files /dev/null and b/desktop-tauri/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png differ diff --git a/desktop-tauri/mobile/ios/App/App/Base.lproj/LaunchScreen.storyboard b/desktop-tauri/mobile/ios/App/App/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..e7ae5d78 --- /dev/null +++ b/desktop-tauri/mobile/ios/App/App/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/desktop-tauri/mobile/ios/App/App/Base.lproj/Main.storyboard b/desktop-tauri/mobile/ios/App/App/Base.lproj/Main.storyboard new file mode 100644 index 00000000..b44df7be --- /dev/null +++ b/desktop-tauri/mobile/ios/App/App/Base.lproj/Main.storyboard @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/desktop-tauri/mobile/ios/App/App/Info.plist b/desktop-tauri/mobile/ios/App/App/Info.plist new file mode 100644 index 00000000..9805ab98 --- /dev/null +++ b/desktop-tauri/mobile/ios/App/App/Info.plist @@ -0,0 +1,118 @@ + + + + + CAPACITOR_DEBUG + $(CAPACITOR_DEBUG) + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Agent2API + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSRequiresIPhoneOS + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsArbitraryLoadsInWebContent + + NSAllowsLocalNetworking + + + + NSLocalNetworkUsageDescription + 用于连接你在局域网里运行的 Agent2API 网关。 + NSBonjourServices + + _http._tcp + _https._tcp + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + ITSAppUsesNonExemptEncryption + + + diff --git a/desktop-tauri/mobile/ios/App/App/SceneDelegate.swift b/desktop-tauri/mobile/ios/App/App/SceneDelegate.swift new file mode 100644 index 00000000..f352e1e9 --- /dev/null +++ b/desktop-tauri/mobile/ios/App/App/SceneDelegate.swift @@ -0,0 +1,24 @@ +import UIKit +import Capacitor + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = scene as? UIWindowScene else { return } + + window = UIWindow(windowScene: windowScene) + window?.rootViewController = CAPBridgeViewController() + window?.makeKeyAndVisible() + + SceneDelegateProxy.shared.scene(scene, willConnectTo: session, options: connectionOptions) + } + + func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { + SceneDelegateProxy.shared.scene(scene, openURLContexts: URLContexts) + } + + func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { + SceneDelegateProxy.shared.scene(scene, continue: userActivity) + } +} diff --git a/desktop-tauri/mobile/ios/App/CapApp-SPM/.gitignore b/desktop-tauri/mobile/ios/App/CapApp-SPM/.gitignore new file mode 100644 index 00000000..3b298120 --- /dev/null +++ b/desktop-tauri/mobile/ios/App/CapApp-SPM/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/desktop-tauri/mobile/ios/App/CapApp-SPM/Package.swift b/desktop-tauri/mobile/ios/App/CapApp-SPM/Package.swift new file mode 100644 index 00000000..1f774238 --- /dev/null +++ b/desktop-tauri/mobile/ios/App/CapApp-SPM/Package.swift @@ -0,0 +1,29 @@ +// swift-tools-version: 5.9 +import PackageDescription + +// DO NOT MODIFY THIS FILE - managed by Capacitor CLI commands +let package = Package( + name: "CapApp-SPM", + platforms: [.iOS(.v15)], + products: [ + .library( + name: "CapApp-SPM", + targets: ["CapApp-SPM"]) + ], + dependencies: [ + .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "8.5.3"), + .package(name: "CapacitorApp", path: "../../../node_modules/@capacitor/app"), + .package(name: "CapacitorKeyboard", path: "../../../node_modules/@capacitor/keyboard") + ], + targets: [ + .target( + name: "CapApp-SPM", + dependencies: [ + .product(name: "Capacitor", package: "capacitor-swift-pm"), + .product(name: "Cordova", package: "capacitor-swift-pm"), + .product(name: "CapacitorApp", package: "CapacitorApp"), + .product(name: "CapacitorKeyboard", package: "CapacitorKeyboard") + ] + ) + ] +) diff --git a/desktop-tauri/mobile/ios/App/CapApp-SPM/README.md b/desktop-tauri/mobile/ios/App/CapApp-SPM/README.md new file mode 100644 index 00000000..03964db9 --- /dev/null +++ b/desktop-tauri/mobile/ios/App/CapApp-SPM/README.md @@ -0,0 +1,5 @@ +# CapApp-SPM + +This package is used to host SPM dependencies for your Capacitor project + +Do not modify the contents of it or there may be unintended consequences. diff --git a/desktop-tauri/mobile/ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift b/desktop-tauri/mobile/ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift new file mode 100644 index 00000000..945afec8 --- /dev/null +++ b/desktop-tauri/mobile/ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift @@ -0,0 +1 @@ +public let isCapacitorApp = true diff --git a/desktop-tauri/mobile/ios/debug.xcconfig b/desktop-tauri/mobile/ios/debug.xcconfig new file mode 100644 index 00000000..53ce18de --- /dev/null +++ b/desktop-tauri/mobile/ios/debug.xcconfig @@ -0,0 +1 @@ +CAPACITOR_DEBUG = true diff --git a/desktop-tauri/mobile/package-lock.json b/desktop-tauri/mobile/package-lock.json new file mode 100644 index 00000000..b031894a --- /dev/null +++ b/desktop-tauri/mobile/package-lock.json @@ -0,0 +1,3852 @@ +{ + "name": "agent2api-mobile", + "version": "3.2.0-beta.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "agent2api-mobile", + "version": "3.2.0-beta.1", + "dependencies": { + "@base-ui/react": "^1.8.0", + "@capacitor/android": "^8.5.3", + "@capacitor/app": "^8.1.2", + "@capacitor/core": "^8.5.3", + "@capacitor/ios": "^8.5.3", + "@capacitor/keyboard": "^8.0.6", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@capacitor/cli": "^8.5.3", + "@tailwindcss/vite": "^4.3.3", + "@types/node": "^26.6.3", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "@vitejs/plugin-react": "^5.0.0", + "tailwindcss": "^4.3.3", + "typescript": "^5.9.0", + "vite": "^7.1.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.10", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.10.tgz", + "integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "8.0.7", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-8.0.7.tgz", + "integrity": "sha512-k0hagDDq/6w4v+0Sp4wFvIz/yswH5O7Va6aRTnD7N62Tnborp4X+YB+H3XUcWVSR4aE47WPEXiIIZchBs0GmYg==", + "license": "MIT" + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.10", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.10.tgz", + "integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.9", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@base-ui/react": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@base-ui/react/-/react-1.9.0.tgz", + "integrity": "sha512-S8CSYosASBKtpL8SWUfS78rFElfLijiCgA7oHOK22jCJ95FULsYyKwBNFOgudt0RA+/7yNc7F81bOvQnRgbBiQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7 || ^8.0.0", + "@base-ui/utils": "0.5.0", + "@floating-ui/react-dom": "^2.1.9", + "@floating-ui/utils": "^0.2.12", + "use-sync-external-store": "^1.7.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@date-fns/tz": "^1.2.0", + "@types/react": "^17 || ^18 || ^19", + "date-fns": "^4.0.0", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@date-fns/tz": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "date-fns": { + "optional": true + } + } + }, + "node_modules/@base-ui/utils": { + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/@base-ui/utils/-/utils-0.5.0.tgz", + "integrity": "sha512-dYGGcEttjkYiMoa5nHm8vLaJ2UcuTWxo3BFAwOYXeRg/q7UVVvA6cacEIXEs3FgBfK1CGtW1ptElqs7wsx+t9A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7 || ^8.0.0", + "@floating-ui/utils": "^0.2.12", + "reselect": "^5.3.0", + "use-sync-external-store": "^1.7.0" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@capacitor/android": { + "version": "8.5.3", + "resolved": "https://registry.npmmirror.com/@capacitor/android/-/android-8.5.3.tgz", + "integrity": "sha512-ilH4gSSz3DrketMkzDVAvY9JsWJ//rCQnECLTdcFRO7xXDogwAbUvQAnIAbiqefwV/D1xfjEL4Gw1MJXEcaA9g==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^8.5.0" + } + }, + "node_modules/@capacitor/app": { + "version": "8.1.2", + "resolved": "https://registry.npmmirror.com/@capacitor/app/-/app-8.1.2.tgz", + "integrity": "sha512-uKBlsrXH0IdvUn69e/KKYbZ32A06pcrUdG8LWuZ/6y33YMXZXLEEdNSwl+jKS/SrsTWqnePs8EqSWaiQM3mUGA==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/@capacitor/cli": { + "version": "8.5.3", + "resolved": "https://registry.npmmirror.com/@capacitor/cli/-/cli-8.5.3.tgz", + "integrity": "sha512-/G6xwna+p4GS3lthSdWN3OThCH2F85O0Ukdy8rv3oAXvgEMeyFgPEIA3PMAS++IkHwUuBYBAKOQJnZfcrh1n0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/cli-framework-output": "^2.2.8", + "@ionic/utils-subprocess": "^3.0.1", + "@ionic/utils-terminal": "^2.3.5", + "commander": "^12.1.0", + "debug": "^4.4.0", + "env-paths": "^2.2.0", + "fs-extra": "^11.2.0", + "kleur": "^4.1.5", + "native-run": "^2.0.3", + "open": "^8.4.0", + "plist": "^3.1.0", + "prompts": "^2.4.2", + "rimraf": "^6.0.1", + "semver": "^7.6.3", + "tar": "^7.5.3", + "tslib": "^2.8.1", + "xcode": "^3.0.1", + "xml2js": "^0.6.2" + }, + "bin": { + "cap": "bin/capacitor", + "capacitor": "bin/capacitor" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@capacitor/core": { + "version": "8.5.3", + "resolved": "https://registry.npmmirror.com/@capacitor/core/-/core-8.5.3.tgz", + "integrity": "sha512-oBE1oGuYE4ZzYYrulC75OuHdSsD6zLUy7d5o1tFwDLG4govGoubY15cxpiI5l0+q87Lm5p0bhbehMdzJg4aFag==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@capacitor/ios": { + "version": "8.5.3", + "resolved": "https://registry.npmmirror.com/@capacitor/ios/-/ios-8.5.3.tgz", + "integrity": "sha512-HnnV92Jl8+3x0JUloXUMjFcUsQI8wHFVsKwkLn72DfRZXYGZW11tQbmNOWOnRTljaU18FdI1b9ipT09SzzPDNw==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^8.5.0" + } + }, + "node_modules/@capacitor/keyboard": { + "version": "8.0.6", + "resolved": "https://registry.npmmirror.com/@capacitor/keyboard/-/keyboard-8.0.6.tgz", + "integrity": "sha512-Bbe+9V0qaT6dzEFJEK7BUWR01l2+vZE8FM5ITF/WgKtx+G6eGZ6d5+YiSMRJBAK0VGjdO59v8yiQ10Doa5KyvQ==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "license": "MIT" + }, + "node_modules/@ionic/cli-framework-output": { + "version": "2.2.8", + "resolved": "https://registry.npmmirror.com/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz", + "integrity": "sha512-TshtaFQsovB4NWRBydbNFawql6yul7d5bMiW1WYYf17hd99V6xdDdk3vtF51bw6sLkxON3bDQpWsnUc9/hVo3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-array": { + "version": "2.1.6", + "resolved": "https://registry.npmmirror.com/@ionic/utils-array/-/utils-array-2.1.6.tgz", + "integrity": "sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs": { + "version": "3.1.7", + "resolved": "https://registry.npmmirror.com/@ionic/utils-fs/-/utils-fs-3.1.7.tgz", + "integrity": "sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ionic/utils-object": { + "version": "2.1.6", + "resolved": "https://registry.npmmirror.com/@ionic/utils-object/-/utils-object-2.1.6.tgz", + "integrity": "sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-process": { + "version": "2.1.12", + "resolved": "https://registry.npmmirror.com/@ionic/utils-process/-/utils-process-2.1.12.tgz", + "integrity": "sha512-Jqkgyq7zBs/v/J3YvKtQQiIcxfJyplPgECMWgdO0E1fKrrH8EF0QGHNJ9mJCn6PYe2UtHNS8JJf5G21e09DfYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmmirror.com/@ionic/utils-stream/-/utils-stream-3.1.7.tgz", + "integrity": "sha512-eSELBE7NWNFIHTbTC2jiMvh1ABKGIpGdUIvARsNPMNQhxJB3wpwdiVnoBoTYp+5a6UUIww4Kpg7v6S7iTctH1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-subprocess": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/@ionic/utils-subprocess/-/utils-subprocess-3.0.1.tgz", + "integrity": "sha512-cT4te3AQQPeIM9WCwIg8ohroJ8TjsYaMb2G4ZEgv9YzeDqHZ4JpeIKqG2SoaA3GmVQ3sOfhPM6Ox9sxphV/d1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.12", + "@ionic/utils-stream": "3.1.7", + "@ionic/utils-terminal": "2.3.5", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-terminal": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@ionic/utils-terminal/-/utils-terminal-2.3.5.tgz", + "integrity": "sha512-3cKScz9Jx2/Pr9ijj1OzGlBDfcmx7OMVBt4+P1uRR0SSW4cm1/y3Mo4OY3lfkuaYifMNBW8Wz6lQHbs1bihr7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.9.tgz", + "integrity": "sha512-qJmhckvOleXp8R+BaXiVW/S34/3M7pvN17/QcG90A74BDaSvKv8ffY+GYG+a/xU0vEoITzdMSiM7o9HZSQWbfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.9.tgz", + "integrity": "sha512-dqQgds39BNWGB2ughJ6n2LTQFXk8RAOwnXj6m387OHpygfD93QRS1ODIfpiWyAa9gsFB9l34pSqeLmA0Xk/VBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.9.tgz", + "integrity": "sha512-scrMx4SGikOSbWknLmPD0v6OeJ1Wih3kyRLdNZnlVbBeKVWp1nRTVcpM0IxDJdc3u8UjesPHTmnX649u8cQpBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.9.tgz", + "integrity": "sha512-fkwi2OQR+QWoViRFt08Bsca6G0e9pk/4z+JgmqHq6jSRTT0LtI27KRVK8bu++6L/CBtQ3OZePbqC3bcrwGzTqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.9.tgz", + "integrity": "sha512-8Apr0Khn0r2MUsDtcOMbp85fDdXsX/E+iLhO17WUjG24m3O8ChArqI0isI7CAdj9Ouq177PHtVtEUpMNE1C4Gw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.9.tgz", + "integrity": "sha512-H+upy4DhBcBZlUsCAMyr8wR0Dg9kj8sGloVZdZ7va2heJM/5jnROxI47dtXseJjlD/VoQe6MrXhY3b5ikOq/Vw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.9.tgz", + "integrity": "sha512-b50NS9TbAEK/b16vBoKrkeNwy+HvGBXmtBbzX4wcodWOh9dQZvRTN9rUyElIXa36QnwXhU9PAVVkZpqRzSi2Tg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.9.tgz", + "integrity": "sha512-SdqcudB/FzjLESsRMgXhV+FXGtf6usxXwlMoRW7D/XS3f9CZ4T4wycRBu2h9bVl2AOaPpunoA8ftAM/MF4altQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.9.tgz", + "integrity": "sha512-dg19FFRoovN+KZ+gMHBAq4Do8PgQmBcxpuhux57OuiR0QLavfn8uPBY8CP+UO50bO+xf0tK0GlqI/jld++5E6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.9.tgz", + "integrity": "sha512-+jyv5ofGmpAvtrzLjSr/e4syskeikEp5L82bUzB76auwOgK1VNVwZV/F2g1Pes9bhTznbejOefAqPglHSKYmTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.9.tgz", + "integrity": "sha512-MKFXxqmkzCbtNvx/vjiJdss44eRtVabhCtDWcCFD9NHGwP+YkqnM/slnEsv/3fsoq9ukTtudraAjGhptrUPCVg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.9.tgz", + "integrity": "sha512-lhARcOT6n7Ni9hpQRq8LZjbuoW+G/GXaW4Jfhx2MbmjnCj9XxLMRTceJIMpiMLQVWm+qxe7oq1sYvlIGOAW34Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.9.tgz", + "integrity": "sha512-oORQzGxZ1oHghJwLdwc8Ni0SY9otILAeXsWdiImTF4F5HkWJUwh5s/269PF5tYILNC1z/3RwFsrgcHPguBcb0g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.9.tgz", + "integrity": "sha512-J2JQHQt7oR1uutd2OSwbV2Qpb2LI5gggPolbkUwDe7vn/0dnduZ0w6EIbGnZUAw7uIKaMgfVylcH6V+Aov8kRA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.9.tgz", + "integrity": "sha512-bWaKLrfzeQ9mOzJJxXbPgw2RH6VAaTJrb/60Tqf7YSMxubG8780Hzm7Us+D6qVW8QRGJYl4VUvVXxnRB0o9zvQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.9.tgz", + "integrity": "sha512-TFJyaCB9NsvSyvVGQgTSRD9XgxsAq99ivDDkt9KAySlrNW5Obsd1kQpvoCvBCOLq+JlJTjmtroLH0wp8M8mEsA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.9.tgz", + "integrity": "sha512-DvmOM5xz/wDnUJFVjqP32ahalxn7BDuvO25bpuFOrb50AIFCCcFPN4lWWUDD1c8Ddv9sMO9SXyqtOet8G8u9cQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.9.tgz", + "integrity": "sha512-aFzokHK3yE00mzAvKm/PY76OsOOTjvoTGE82Vp89iBDyD2wUBlV4ZsR2tWkRECTEFk6TC4Yc3zaEWjPqq1vstQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.9.tgz", + "integrity": "sha512-t/yMdeaaQUuNNpmT7taBmH2VvHzgT4YA/IaKVE3qVr4PYQemn/kmJkL4uG4UvTLcWXQd0MSoOa/3URYSWGmUEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.9.tgz", + "integrity": "sha512-4dnxKQ8GHARyjqAU87q5UWhywZTpIbR2VWhtACdC37GD1SGszmvb3TaBFVA4n+fnDHSKMut4ZJ63hVFwgnxVtg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.9.tgz", + "integrity": "sha512-1qdQj6IrNoR0xFvrCQHmhBXe9HMbBGNTFi+3n4jKMt2rTx4k0aeAdraZU4WDOMfL9tAwS8/TRSuquvNuPQ+Gpw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.9.tgz", + "integrity": "sha512-yc1GSEHIYhKGN5WSaJc+eMy8bZvzcvAmflEgFh8L3xcy8dYxGmfrudyBnBFde9f6JzOa1kFqocF8BHaWybjQoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.9.tgz", + "integrity": "sha512-VzHGvosAkLIzrwqvSgJBPDmGFTF/yE8OtMT4H5z8pM1NA0NiPpvTYnUh+9xPd+d5oy/wqF317FmJ1I8Kk8pp0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.9.tgz", + "integrity": "sha512-FB77txy1PiAcJgG72nOLA6R0S3PRZahs8Spx0z2yseDUkoMmJcrQghiebJ2JHZ74cCOVlX/MYd4dUbJtJJ8fow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.9.tgz", + "integrity": "sha512-IoIgQLpK1DbOcQf1vX+i5oe5LXypsCVb1j/mU6BLAbOGmXH4E+j4kMfgZRwPYUohVBurNyVMgIKVM5dR6xA0kA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/fs-extra": { + "version": "8.1.5", + "resolved": "https://registry.npmmirror.com/@types/fs-extra/-/fs-extra-8.1.5.tgz", + "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "26.6.5", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-26.6.5.tgz", + "integrity": "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmmirror.com/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.3.0" + } + }, + "node_modules/@types/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.12", + "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.9.12.tgz", + "integrity": "sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.29", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.29.tgz", + "integrity": "sha512-YeesxhUmR4h5y5V8KF8iBCqJb8YFrBJ++XBTeGcWIaQOYXGN19JM0FoiOjAhwuDoln8owMgXcWBmgHtYSkky2w==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmmirror.com/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-creator": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/bplist-creator/-/bplist-creator-0.1.0.tgz", + "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "stream-buffers": "2.2.x" + } + }, + "node_modules/bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.29.3", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.29.3.tgz", + "integrity": "sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.26", + "caniuse-lite": "^1.0.30001813", + "electron-to-chromium": "^1.5.439", + "node-releases": "^2.0.57", + "update-browserslist-db": "^1.3.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001816", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001816.tgz", + "integrity": "sha512-+8Yx7BXCoqk6IF8yTicjfUR6D/ldJzdlsodlEOiOSPmhK844WIgk67/zIQaGAOxNkZyyvBlrRFnLvqKIoqbAFw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.459", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.459.tgz", + "integrity": "sha512-4SC9HHSFjO/liAhifpdOL5hG9b4BP7/5G3y3UPZVO8trlAXr1CaUEEJL0gWK3kOcYcZrOA5rkwG45Gra/lTIgg==", + "dev": true, + "license": "ISC" + }, + "node_modules/elementtree": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "sax": "1.1.4" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.26.0", + "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", + "integrity": "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fs-extra": { + "version": "11.4.1", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.4.1.tgz", + "integrity": "sha512-KYAb4c9BJQI6QqGKthV68OHe0badztdXJWKo0WtBA9IuCFPTKvE5ZdUBglP833aMjhaSPNO4A5j/EkzZtGlKjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmmirror.com/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmmirror.com/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/native-run": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/native-run/-/native-run-2.0.3.tgz", + "integrity": "sha512-U1PllBuzW5d1gfan+88L+Hky2eZx+9gv3Pf6rNBxKbORxi7boHzqiA6QFGSnqMem4j0A9tZ08NMIs5+0m/VS1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-fs": "^3.1.7", + "@ionic/utils-terminal": "^2.3.4", + "bplist-parser": "^0.3.2", + "debug": "^4.3.4", + "elementtree": "^0.1.7", + "ini": "^4.1.1", + "plist": "^3.1.0", + "split2": "^4.2.0", + "through2": "^4.0.2", + "tslib": "^2.6.2", + "yauzl": "^2.10.0" + }, + "bin": { + "native-run": "bin/native-run" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.59", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.59.tgz", + "integrity": "sha512-5qr2okC4ItAI0hG5SaV0HoYMpnM/Y6+Oqr7AmPYy/2uEoGvS+GbSKLeRxSS1uAdu59yz0ouzcuXievSgg///WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmmirror.com/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/plist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/plist/-/plist-3.1.1.tgz", + "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/postcss": { + "version": "8.5.30", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.30.tgz", + "integrity": "sha512-zkd+e9S1AQHhmaBKye9zlFHWjOwVgqXl1q3ECYy8561K7qxR2cREf/STLskulBFd78k6AOFeA+ff1TtEzk9bAA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.3.1", + "resolved": "https://registry.npmmirror.com/react/-/react-19.3.1.tgz", + "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.1", + "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-19.3.1.tgz", + "integrity": "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reselect": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/reselect/-/reselect-5.3.0.tgz", + "integrity": "sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.64.9", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.64.9.tgz", + "integrity": "sha512-mnPSQINChN7PpT+a5CI0/+87hm/+6zxSKBK1XonrzQ0zFnglaTjhkTP2Br4yNCTcLHUEvrAfZffH57jGf+8xBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.64.9", + "@rollup/rollup-android-arm64": "4.64.9", + "@rollup/rollup-darwin-arm64": "4.64.9", + "@rollup/rollup-darwin-x64": "4.64.9", + "@rollup/rollup-freebsd-arm64": "4.64.9", + "@rollup/rollup-freebsd-x64": "4.64.9", + "@rollup/rollup-linux-arm-gnueabihf": "4.64.9", + "@rollup/rollup-linux-arm-musleabihf": "4.64.9", + "@rollup/rollup-linux-arm64-gnu": "4.64.9", + "@rollup/rollup-linux-arm64-musl": "4.64.9", + "@rollup/rollup-linux-loong64-gnu": "4.64.9", + "@rollup/rollup-linux-loong64-musl": "4.64.9", + "@rollup/rollup-linux-ppc64-gnu": "4.64.9", + "@rollup/rollup-linux-ppc64-musl": "4.64.9", + "@rollup/rollup-linux-riscv64-gnu": "4.64.9", + "@rollup/rollup-linux-riscv64-musl": "4.64.9", + "@rollup/rollup-linux-s390x-gnu": "4.64.9", + "@rollup/rollup-linux-x64-gnu": "4.64.9", + "@rollup/rollup-linux-x64-musl": "4.64.9", + "@rollup/rollup-openbsd-x64": "4.64.9", + "@rollup/rollup-openharmony-arm64": "4.64.9", + "@rollup/rollup-win32-arm64-msvc": "4.64.9", + "@rollup/rollup-win32-ia32-msvc": "4.64.9", + "@rollup/rollup-win32-x64-gnu": "4.64.9", + "@rollup/rollup-win32-x64-msvc": "4.64.9", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/sax/-/sax-1.1.4.tgz", + "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==", + "dev": true, + "license": "ISC" + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-plist": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/simple-plist/-/simple-plist-1.3.1.tgz", + "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bplist-creator": "0.1.0", + "bplist-parser": "0.3.1", + "plist": "^3.0.5" + } + }, + "node_modules/simple-plist/node_modules/bplist-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/bplist-parser/-/bplist-parser-0.3.1.tgz", + "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stream-buffers": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/stream-buffers/-/stream-buffers-2.2.0.tgz", + "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.5.22", + "resolved": "https://registry.npmmirror.com/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.4", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.7.0.tgz", + "integrity": "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vite": { + "version": "7.3.7", + "resolved": "https://registry.npmmirror.com/vite/-/vite-7.3.7.tgz", + "integrity": "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xcode": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/xcode/-/xcode-3.0.1.tgz", + "integrity": "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "simple-plist": "^1.1.0", + "uuid": "^7.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmmirror.com/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/desktop-tauri/mobile/package.json b/desktop-tauri/mobile/package.json new file mode 100644 index 00000000..dd4716fe --- /dev/null +++ b/desktop-tauri/mobile/package.json @@ -0,0 +1,38 @@ +{ + "name": "agent2api-mobile", + "version": "3.2.0-beta.1", + "private": true, + "type": "module", + "description": "Agent2API 手机端 —— 响应式 Web / PWA,消费 ui-kit(Base UI + Tailwind v4),产物落 ui/m/", + "scripts": { + "build": "tsc --noEmit && vite build", + "build:native": "tsc --noEmit && vite build --mode apk", + "build:apk": "npm run build:native && cap sync android", + "build:ios": "npm run build:native && cap sync ios", + "watch": "vite build --watch", + "typecheck": "tsc --noEmit", + "dev": "vite", + "apk": "npm run build:apk && cd android && gradlew.bat assembleRelease" + }, + "dependencies": { + "@base-ui/react": "^1.8.0", + "@capacitor/android": "^8.5.3", + "@capacitor/app": "^8.1.2", + "@capacitor/core": "^8.5.3", + "@capacitor/ios": "^8.5.3", + "@capacitor/keyboard": "^8.0.6", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@capacitor/cli": "^8.5.3", + "@tailwindcss/vite": "^4.3.3", + "@types/node": "^26.6.3", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "@vitejs/plugin-react": "^5.0.0", + "tailwindcss": "^4.3.3", + "typescript": "^5.9.0", + "vite": "^7.1.0" + } +} diff --git a/desktop-tauri/mobile/public/manifest.webmanifest b/desktop-tauri/mobile/public/manifest.webmanifest new file mode 100644 index 00000000..ba58ffe8 --- /dev/null +++ b/desktop-tauri/mobile/public/manifest.webmanifest @@ -0,0 +1,57 @@ +{ + "id": "/m.html", + "name": "Agent2API 手机端", + "short_name": "Agent2API", + "description": "多提供商本地 AI 网关的手机面板", + "start_url": "/m.html", + "scope": "/", + "display": "standalone", + "display_override": ["window-controls-overlay", "standalone", "minimal-ui"], + "orientation": "portrait", + "background_color": "#f3f3f6", + "theme_color": "#f3f3f6", + "lang": "zh-CN", + "dir": "ltr", + "categories": ["productivity", "utilities"], + "prefer_related_applications": false, + "icons": [ + { + "src": "/assets/app-icon-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/assets/app-icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/assets/app-icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "shortcuts": [ + { + "name": "签到中心", + "short_name": "签到", + "url": "/m.html#checkin", + "icons": [{ "src": "/assets/app-icon-192.png", "sizes": "192x192" }] + }, + { + "name": "账号", + "short_name": "账号", + "url": "/m.html#accounts", + "icons": [{ "src": "/assets/app-icon-192.png", "sizes": "192x192" }] + }, + { + "name": "请求日志", + "short_name": "日志", + "url": "/m.html#requests", + "icons": [{ "src": "/assets/app-icon-192.png", "sizes": "192x192" }] + } + ] +} diff --git a/desktop-tauri/mobile/public/sw.js b/desktop-tauri/mobile/public/sw.js new file mode 100644 index 00000000..8d7fa584 --- /dev/null +++ b/desktop-tauri/mobile/public/sw.js @@ -0,0 +1,178 @@ +/* + * Agent2API 手机端 Service Worker。 + * + * ── 为什么放在 `ui/` 根、而不是 `ui/m/` ────────────────────── + * SW 的默认 scope 上限是它自己所在的目录:`ui/m/sw.js` 最多只能控制 + * `/m/*`,而手机端的入口是 `/m.html` —— 它落在 scope 之外,注册成 `/` + * 会被浏览器拒绝(`The path of the provided scope is not under the max + * scope`),注册成 `/m/` 又管不到入口页。放在 `ui/` 根目录就没有这个 + * 矛盾:`/sw.js` 天然可以 scope 到 `/`。 + * + * 构建脚本(vite.config.ts 的 swBuildStamp)把 `mobile/public/sw.js` + * 复制到这里并写入时间戳,所以这个文件是产物 —— 改它请改源文件。 + * + * ── 只拦截手机端,桌面端一字未变 ───────────────────────────── + * scope 是 `/`,但下面的 fetch 处理只认 `/m` 与 `/m/*`,其余请求直接 + * return(浏览器按原样处理)。桌面面板与 `/api/*` 都不受影响。 + * + * ── 三条策略,按资源性质分 ──────────────────────────────────── + * · **入口页**(`/m`、`/m.html`):network-first + 离线回退缓存。 + * 不能 stale-while-revalidate —— 入口页决定加载哪个版本的 JS, + * 先给旧的再在后台更新会让「新 JS + 旧 HTML」错配一轮。 + * · **外壳**(mobile.js / mobile.css / manifest / 图标):stale-while-revalidate。 + * 首屏秒开,后台拉新版留给下次打开。 + * · **数据**(`/api/*`、`/v1/*`):**完全不碰**。网关数据必须实时, + * 缓存一份只会让用户看到过期读数却以为是真的。 + * + * ── 版本 ──────────────────────────────────────────────────── + * SW 的更新判据是文件内容。产物变了但 sw.js 不变时浏览器不会重装 SW, + * 旧缓存会一直生效 —— 时间戳让每次构建都必然更新。 + * 新 SW 装好后不立刻 skipWaiting,而是等页面发消息 —— 否则用户正在 + * 填的表单会被一次自动刷新清掉。页面侧在设置页给「重启以更新」按钮。 + */ + +const CACHE = 'agent2api-mobile-__BUILD__' + +/** 预缓存的外壳文件(相对 SW 所在目录,即 `ui/` 根) */ +const SHELL = [ + './m.html', + './m/mobile.js', + './m/mobile.css', + './m/manifest.webmanifest', + './assets/app-icon-192.png', + './assets/app-icon-512.png', + './assets/app-icon-maskable-512.png', +] + +/** 这个请求属不属于手机端(决定要不要拦) */ +function isMobileAsset(pathname) { + if (pathname === '/m' || pathname === '/m.html') return true + return pathname.startsWith('/m/') +} + +/** 是不是入口页导航(`/m` 或 `/m.html`,含 query / hash 之外的路径部分) */ +function isEntryNavigation(request, url) { + if (request.mode !== 'navigate') return false + return url.pathname === '/m' || url.pathname === '/m.html' +} + +/** + * 预缓存一个文件。 + * + * 不用 `cache.add` 是因为它会**跟随重定向**:未登录时 `/m.html` 会 302 到 + * `/login`,`cache.add` 会把那张登录页存成入口页 —— 之后离线打开就是登录页。 + * 这里显式取一次,跟随了重定向的响应直接丢弃(留给下次在线访问时再存)。 + */ +async function precache(cache, item) { + try { + const response = await fetch(item, { cache: 'no-store' }) + if (response.ok && !response.redirected) await cache.put(item, response) + } catch { + /* 单个文件失败不影响其余(比如图标缺失) */ + } +} + +self.addEventListener('install', event => { + event.waitUntil( + caches + .open(CACHE) + // 逐个而不是 addAll:任何一个文件缺失不该让整次预缓存失败 + .then(cache => Promise.all(SHELL.map(item => precache(cache, item)))) + .then(() => { + // 不在这里 skipWaiting:等页面决定何时切换(见 message 分支) + }), + ) +}) + +self.addEventListener('activate', event => { + event.waitUntil( + caches + .keys() + .then(keys => Promise.all(keys.filter(key => key !== CACHE).map(key => caches.delete(key)))) + .then(() => self.clients.claim()), + ) +}) + +/** + * 页面发来的消息。 + * + * `SKIP_WAITING`:用户点了「重启以更新」。新 SW 立刻接管,页面随后自己 + * reload 拿到新外壳。 + */ +self.addEventListener('message', event => { + const data = event.data + if (data && data.type === 'SKIP_WAITING') { + void self.skipWaiting() + } +}) + +self.addEventListener('fetch', event => { + const request = event.request + if (request.method !== 'GET') return + + const url = new URL(request.url) + if (url.origin !== self.location.origin) return + // 桌面面板与接口一律不碰 + if (!isMobileAsset(url.pathname)) return + + // 入口页:network-first,离线回退缓存 + if (isEntryNavigation(request, url)) { + event.respondWith( + fetch(request) + .then(response => { + // 只缓存「直接命中」的响应。会话过期时服务器会 302 到 /login, + // fetch 跟随重定向后拿到的是一张 200 的登录页 —— 存进入口页缓存 + // 的话,下次离线打开看到的就是登录页(而离线根本登不了)。 + if (response.ok && !response.redirected) { + const copy = response.clone() + void caches + .open(CACHE) + .then(cache => cache.put('./m.html', copy)) + .catch(() => undefined) + } + return response + }) + .catch(async () => { + const cached = await caches.match('./m.html') + if (cached) return cached + // 连外壳都没有(首次访问就断网):给一句可读的话,别让浏览器报网络错误 + return new Response( + '' + + '' + + '

离线

' + + '

连不上网关。确认手机与网关在同一局域网,然后刷新。

' + + '', + { headers: { 'Content-Type': 'text/html; charset=utf-8' } }, + ) + }), + ) + return + } + + const isShell = SHELL.some(item => url.pathname.endsWith(item.slice(1))) + if (isShell) { + // 外壳:stale-while-revalidate —— 首屏秒开,后台拉新版留给下次 + event.respondWith( + caches.open(CACHE).then(async cache => { + const cached = await cache.match(request) + const network = fetch(request) + .then(response => { + if (response.ok) void cache.put(request, response.clone()) + return response + }) + .catch(() => cached) + return cached || network + }), + ) + return + } + + // 手机端其余资源:network-first,离线时回退缓存 + event.respondWith( + fetch(request).catch(async () => { + const cached = await caches.match(request) + if (cached) return cached + throw new Error('offline') + }), + ) +}) diff --git a/desktop-tauri/mobile/src/App.tsx b/desktop-tauri/mobile/src/App.tsx new file mode 100644 index 00000000..94d5ad4a --- /dev/null +++ b/desktop-tauri/mobile/src/App.tsx @@ -0,0 +1,168 @@ +import React, { useEffect } from 'react' +import { store, useStore, bootstrapTheme } from './state/store' +import { TabBar, Toast, AuthGate, ThemeSync } from './components/Shell' +import { PwaNotice } from './components/PwaNotice' +import { usePwa } from './lib/usePwa' +import { HomePage } from './pages/HomePage' +import { ReportPage } from './pages/ReportPage' +import { AccountsPage } from './pages/AccountsPage' +import { AssistantPage } from './pages/AssistantPage' +import { MorePage } from './pages/MorePage' +import { CheckinPage } from './pages/CheckinPage' +import { CheckinRecordsPage } from './pages/CheckinRecordsPage' +import { ModelsPage } from './pages/ModelsPage' +import { KeysPage } from './pages/KeysPage' +import { ProxiesPage } from './pages/ProxiesPage' +import { TasksPage } from './pages/TasksPage' +import { RequestsPage } from './pages/RequestsPage' +import { LogsPage } from './pages/LogsPage' +import { SettingsPage } from './pages/SettingsPage' +import { DocsPage } from './pages/DocsPage' +import { AgentPage } from './pages/AgentPage' +import { ServersPage } from './pages/ServersPage' +import { NavBar } from './components/NavBar' +import { t } from './i18n' + +/** + * 路由表。 + * + * 一级页(`primary: true`)显示底部标签栏;二级页显示返回栏。 + * 与桌面端不同:桌面端 13 个一级菜单在手机上收成 5 个标签,其余进 + * 「更多」的宫格 —— 层级只有两层,不再有第三层。 + */ +type ScreenDef = { + title: string + component: () => React.ReactElement + primary?: boolean +} + +const SCREENS: Record = { + home: { title: t('首页'), component: HomePage, primary: true }, + report: { title: t('报表'), component: ReportPage, primary: true }, + accounts: { title: t('账号'), component: AccountsPage, primary: true }, + assistant: { title: t('AI 助手'), component: AssistantPage, primary: true }, + more: { title: t('更多'), component: MorePage, primary: true }, + checkin: { title: t('签到中心'), component: CheckinPage }, + 'checkin-records': { title: t('签到记录'), component: CheckinRecordsPage }, + models: { title: t('模型管理'), component: ModelsPage }, + keys: { title: t('网关 Key'), component: KeysPage }, + proxies: { title: t('网络代理'), component: ProxiesPage }, + tasks: { title: t('定时任务'), component: TasksPage }, + requests: { title: t('请求日志'), component: RequestsPage }, + logs: { title: t('运行日志'), component: LogsPage }, + settings: { title: t('设置'), component: SettingsPage }, + docs: { title: t('接口文档'), component: DocsPage }, + agent: { title: t('Agent 接管'), component: AgentPage }, + servers: { title: t('服务器'), component: ServersPage }, +} + +export function App() { + const state = useStore() + const pwa = usePwa() + const def = SCREENS[state.route.screen] || SCREENS.home + const Page = def.component + const isPrimary = Boolean(def.primary) + // 助手页要撑满:它自己内部滚动(对话区),外层不能滚 + const fillHeight = state.route.screen === 'assistant' + /** + * APK 首次启动、一台服务器都没配:**没有地址就什么都拉不到**,所以直接 + * 顶掉整个界面(不显示标签栏 —— 切到别的标签也是空的)。配好一台之后 + * 这里自然放行。 + */ + const needsServer = state.packaged && !state.server + + useEffect(() => { + bootstrapTheme() + store.restoreFromHash() + // 没有可用服务器时不要发请求:那会以「无法连接网关」的形式刷一屏错误, + // 而真实原因是「还没填地址」。 + if (!state.packaged || state.server) void store.refresh(true) + // 浏览器前进 / 后退(PWA 与 APK 的 WebView 都会触发)。走 store 的 + // handlePopState 而不是直接 restoreFromHash:前者会同步弹掉二级栈, + // 后者只看 hash,会把「返回」变成「重进当前页」。 + const onPop = () => store.handlePopState() + window.addEventListener('popstate', onPop) + return () => window.removeEventListener('popstate', onPop) + // 只在挂载时跑一次:后续的 refresh 由各页面与写操作自己触发 + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + /** + * 安卓物理返回键。 + * + * 不处理的话 Capacitor 的默认行为是直接 finish 掉 Activity —— 用户在 + * 任何二级页按一下都会回到桌面,而不是退回上一页。 + * + * 优先级:先让已打开的弹层自己关(Sheet / Dialog 走各自的关闭逻辑), + * 再交给 store 退栈。首页且无弹层时才退出应用 —— 这是安卓的通行预期。 + */ + useEffect(() => { + if (!state.packaged) return + let dispose: (() => void) | undefined + let cancelled = false + void import('@capacitor/app') + .then(({ App: CapApp }) => { + if (cancelled) return + void CapApp.addListener('backButton', () => { + // 弹层开着时先关弹层。Base UI 的弹层挂 data-open,且关闭按钮带 + // data-slot='sheet-close' / 'dialog-close';点它等价于用户按关闭。 + const popup = document.querySelector( + '[data-slot="sheet-content"][data-open], [data-slot="dialog-content"][data-open]', + ) + if (popup) { + const closer = popup.querySelector('[data-slot="sheet-close"], [data-slot="dialog-close"]') + closer?.click() + return + } + if (!store.handleHardwareBack()) void CapApp.exitApp() + }).then(handle => { + if (cancelled) void handle.remove() + else dispose = () => void handle.remove() + }) + }) + .catch(() => { + /* 插件缺失(例如浏览器形态)时什么都不做:浏览器自己有返回键 */ + }) + return () => { + cancelled = true + dispose?.() + } + // packaged 在应用生命周期内不变 + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + useEffect(() => { + // 页面标题跟着当前页走(PWA 里影响任务切换器的显示) + document.title = needsServer ? `${t('服务器')} · Agent2API` : `${def.title} · Agent2API` + }, [def.title, needsServer]) + + if (needsServer) { + return ( +
+ +
+ +
+ +
+ ) + } + + return ( +
+ + + {isPrimary ? null : store.back()} />} +
+ +
+ + + +
+ ) +} diff --git a/desktop-tauri/mobile/src/api/assistant.ts b/desktop-tauri/mobile/src/api/assistant.ts new file mode 100644 index 00000000..84bec273 --- /dev/null +++ b/desktop-tauri/mobile/src/api/assistant.ts @@ -0,0 +1,227 @@ +/** + * AI 助手的对话流(SSE)。 + * + * ── 请求走哪条路 ────────────────────────────────────────────── + * `POST /api/assistant/chat`,**由网关转发到上游**。手机端只负责把消息发出去、 + * 把流回来的 delta 画出来 —— 模型路由、账号选取、429 换号、协议翻译、记账 + * 全在网关那一侧(见 `api::assistant` 的模块头)。手机不持有任何上游凭证, + * 也不直连任何模型服务。 + * + * ── 为什么不复用 apiCall ────────────────────────────────────── + * `apiCall` 会把响应体整个读成文本再解 `{success, data}` 信封;对话是 SSE, + * 要边读边渲染。所以这里单独发一次 fetch,只共用 client.ts 的地址拼接、 + * 鉴权头与 401 续期(那三件事必须与其它请求同一份实现,否则「登录过期」 + * 在对话页会表现成另一种样子)。 + * + * ── 帧格式 ──────────────────────────────────────────────────── + * 与 OpenAI 流式一致:`data: {choices:[{delta:{content, tool_calls}}]}`, + * 以 `data: [DONE]` 收尾。正文按 delta 累加;`tool_calls` 按 `index` 聚合 + * (同一只工具的参数会被拆成多片,不能逐片当独立调用)。 + */ + +import { ApiError, authHeaders, refreshPanelAuth, resolveUrl } from '../api/client' + +export type ChatRole = 'user' | 'assistant' | 'tool' + +/** 一次工具调用(聚合后的完整形态) */ +export type ToolCall = { id: string; name: string; arguments: string } + +export type ChatMessage = { + role: ChatRole + content: string + /** assistant 消息上的工具调用(回灌给上游时要带上) */ + toolCalls?: ToolCall[] + /** role = 'tool' 时对应哪一次调用 */ + toolCallId?: string +} + +export type ChatEvent = + | { type: 'delta'; text: string } + | { type: 'toolCalls'; calls: ToolCall[] } + | { type: 'done'; finishReason: string } + +/** 助手配置(只读用得到的几项) */ +export type AssistantConfig = { + model?: string | null + reasoning?: string | null + /** 工具勾选表:tool id → 是否勾选(后端已把缺省值展开) */ + tools?: Record +} + +export async function fetchAssistantConfig(): Promise { + const response = await fetch(resolveUrl('/api/assistant/config'), { + headers: authHeaders(), + }) + const payload = await response.json().catch(() => null) + if (!response.ok || payload?.success === false) { + throw new ApiError( + String(payload?.error?.message || payload?.error || `HTTP ${response.status}`), + response.status, + ) + } + return (payload?.data?.config || {}) as AssistantConfig +} + +/** + * 发一轮对话并把流式事件回调出去。 + * + * `signal` 用来实现「停止」:abort 之后读取循环以 AbortError 结束 —— 那是 + * 用户的意图,不是错误(调用方按 `signal.aborted` 区分)。 + */ +export async function streamAssistantChat(options: { + messages: ChatMessage[] + /** 勾选过的工具表(OpenAI tools 形态);空数组 = 纯聊天 */ + tools?: Array> + model?: string | null + reasoning?: string | null + signal?: AbortSignal + onEvent: (event: ChatEvent) => void +}): Promise { + const body: Record = { + messages: options.messages.map(wireMessage), + } + // 不传 model 时由网关用「助手设置」里存的默认模型兜底 + if (options.model) body.model = options.model + if (options.reasoning) body.reasoning = options.reasoning + // 工具表为空时**不要发空数组**:后端按「有没有 tools」决定注不注入系统提示词 + // (见 config::AssistantConfig::system_prompt),空数组与不发不是一回事 + if (options.tools && options.tools.length) body.tools = options.tools + + const send = async (): Promise => + fetch(resolveUrl('/api/assistant/chat'), { + method: 'POST', + headers: { ...authHeaders(), 'Content-Type': 'application/json', Accept: 'text/event-stream' }, + body: JSON.stringify(body), + signal: options.signal, + }) + + let response = await send() + // 401 静默续期一次(与 apiCall 同一语义):续上了就重发,续不上交给界面 + if (response.status === 401 && (await refreshPanelAuth())) { + response = await send() + } + + if (!response.ok || !response.body) { + const text = await response.text().catch(() => '') + let message = text.trim() + try { + const parsed = JSON.parse(text) + message = String(parsed?.error?.message || parsed?.error || message) + } catch { + /* 非 JSON 错误体:原样用文本 */ + } + if (response.status === 404) { + throw new ApiError( + `这一版网关还没有 AI 助手接口(HTTP 404),请升级网关后重试。${message}`, + 404, + ) + } + throw new ApiError(message || `HTTP ${response.status}`, response.status) + } + + const reader = response.body.getReader() + const decoder = new TextDecoder() + let buffer = '' + let finished = false + /** 正文累积 + 工具调用按 index 聚合(参数会被拆成多片) */ + const calls = new Map() + let finishReason = '' + + /** 处理一帧;返回 true 表示收到 [DONE] */ + const handleFrame = (frame: string): boolean => { + for (const rawLine of frame.split('\n')) { + const line = rawLine.trimEnd() + if (!line.startsWith('data:')) continue + const payload = line.slice(5).trim() + if (!payload) continue + if (payload === '[DONE]') return true + let chunk: Record + try { + chunk = JSON.parse(payload) as Record + } catch { + continue + } + const choice = Array.isArray(chunk.choices) + ? (chunk.choices[0] as Record | undefined) + : undefined + const delta = (choice?.delta || {}) as Record + // 思考型模型的 reasoning_content 不进正文:手机上只显示最终回答, + // 把思考过程混进气泡会让长回答读起来像两段话 + const content = typeof delta.content === 'string' ? delta.content : '' + if (content) options.onEvent({ type: 'delta', text: content }) + + if (Array.isArray(delta.tool_calls)) { + for (const raw of delta.tool_calls as Array>) { + const index = Number(raw.index ?? 0) + const slot = calls.get(index) || { id: '', name: '', arguments: '' } + if (typeof raw.id === 'string' && raw.id) slot.id = raw.id + const fn = (raw.function || {}) as Record + if (typeof fn.name === 'string' && fn.name) slot.name = fn.name + if (typeof fn.arguments === 'string') slot.arguments += fn.arguments + calls.set(index, slot) + } + } + if (typeof choice?.finish_reason === 'string') finishReason = choice.finish_reason + } + return false + } + + try { + for (;;) { + const { done, value } = await reader.read() + if (done) break + buffer += decoder.decode(value, { stream: true }) + let index = buffer.indexOf('\n\n') + while (index >= 0) { + const frame = buffer.slice(0, index) + buffer = buffer.slice(index + 2) + if (handleFrame(frame)) { + finished = true + // 收到 [DONE]:剩下的字节不必再读 + await reader.cancel().catch(() => {}) + index = -1 + break + } + index = buffer.indexOf('\n\n') + } + if (finished) break + } + // 尾帧没有空行收尾时也要处理(部分上游不发最后的 \n\n) + if (!finished && buffer.trim()) handleFrame(buffer) + // 聚合好的工具调用一次性交出去(正文已逐段给过) + const done = [...calls.entries()].sort((a, b) => a[0] - b[0]).map(([, call]) => call) + if (done.length) options.onEvent({ type: 'toolCalls', calls: done }) + options.onEvent({ type: 'done', finishReason }) + } finally { + try { + reader.releaseLock() + } catch { + /* 已取消 */ + } + } +} + +/** + * 一条消息 → 发给上游的 wire 形态。 + * + * 与 OpenAI 的约定逐字对齐:assistant 带工具调用时要有 `tool_calls`, + * 工具结果用 `role: "tool"` + `tool_call_id` 回灌 —— 少任何一个字段, + * 上游都会拒掉整轮请求(或在下一轮丢掉上下文)。 + */ +function wireMessage(message: ChatMessage): Record { + if (message.role === 'tool') { + return { role: 'tool', tool_call_id: message.toolCallId, content: message.content } + } + if (message.role === 'assistant') { + const wire: Record = { role: 'assistant', content: message.content || '' } + if (message.toolCalls?.length) { + wire.tool_calls = message.toolCalls.map(call => ({ + id: call.id, + type: 'function', + function: { name: call.name, arguments: call.arguments }, + })) + } + return wire + } + return { role: 'user', content: message.content } +} diff --git a/desktop-tauri/mobile/src/api/auth.ts b/desktop-tauri/mobile/src/api/auth.ts new file mode 100644 index 00000000..bbd4e930 --- /dev/null +++ b/desktop-tauri/mobile/src/api/auth.ts @@ -0,0 +1,282 @@ +/** + * 面板认证:登录 / 首次注册 / 人机验证。 + * + * ── 为什么要自己实现一遍 ALTCHA ─────────────────────────────── + * 桌面网页端用的是 `ui/panel-captcha.js`(自定义元素 ``), + * 那是给 `login.html` 准备的经典脚本,APK 里没有那个页面、也不该为了它去 + * 加载桌面端的整套资源。协议本身很简单(服务端见 `server::altcha`): + * + * 领题 → `{algorithm, challenge, salt, maxnumber, signature}` + * 枚举 n 使 `sha256(salt + n) === challenge` + * 提交 base64(JSON({algorithm, challenge, number, salt, signature})) + * + * 这里把求解实现成 Promise,不做成自定义元素 —— APK 的登录页是 React 组件, + * 用状态驱动比 DOM 事件更自然。 + * + * ── 为什么不用 crypto.subtle 一把梭 ──────────────────────────── + * `crypto.subtle` 只在安全上下文提供。APK 里页面 origin 是 `http://localhost`, + * **算安全上下文**(localhost 被视为可信),所以这里可以放心用原生实现 —— + * 与网页端那份「http 局域网要纯 JS 兜底」的顾虑不同。 + * 但为了在浏览器里调试时不炸,仍然保留了纯 JS 兜底。 + */ + +import { apiCall, url, type PanelStatus } from './client' +import { activeServerId, readServerAuth, storeServerAuth } from '../lib/servers' + +/** 题面(服务端 `altcha::challenge()` 的原样输出) */ +type Challenge = { + algorithm: string + challenge: string + salt: string + maxnumber: number + signature: string +} + +/** 求解进度回调(0–1),用于界面上给一点反馈 */ +export type SolveProgress = (ratio: number) => void + +/* ── SHA-256 ─────────────────────────────────────────────── */ + +const K = new Int32Array([ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +]) + +function rotr(value: number, bits: number): number { + return (value >>> bits) | (value << (32 - bits)) +} + +/** 纯 JS SHA-256(UTF-8 输入 → 32 字节摘要) */ +function sha256Bytes(bytes: Uint8Array): Uint8Array { + const H = new Int32Array([ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, + ]) + const length = bytes.length + // 补位:0x80 + 若干 0 + 64 位长度 + const padded = new Uint8Array(((length + 9 + 63) >> 6) << 6) + padded.set(bytes) + padded[length] = 0x80 + const bitLength = length * 8 + // 长度只有低 32 位可能非零(题面几十字节,够用) + const view = new DataView(padded.buffer) + view.setUint32(padded.length - 4, bitLength >>> 0, false) + view.setUint32(padded.length - 8, Math.floor(bitLength / 0x100000000), false) + + const w = new Int32Array(64) + for (let offset = 0; offset < padded.length; offset += 64) { + for (let i = 0; i < 16; i++) w[i] = view.getInt32(offset + i * 4, false) + for (let i = 16; i < 64; i++) { + const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3) + const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10) + w[i] = (w[i - 16] + s0 + w[i - 7] + s1) | 0 + } + let a = H[0] + let b = H[1] + let c = H[2] + let d = H[3] + let e = H[4] + let f = H[5] + let g = H[6] + let h = H[7] + for (let i = 0; i < 64; i++) { + const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25) + const ch = (e & f) ^ (~e & g) + const t1 = (h + S1 + ch + K[i] + w[i]) | 0 + const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22) + const maj = (a & b) ^ (a & c) ^ (b & c) + const t2 = (S0 + maj) | 0 + h = g + g = f + f = e + e = (d + t1) | 0 + d = c + c = b + b = a + a = (t1 + t2) | 0 + } + H[0] = (H[0] + a) | 0 + H[1] = (H[1] + b) | 0 + H[2] = (H[2] + c) | 0 + H[3] = (H[3] + d) | 0 + H[4] = (H[4] + e) | 0 + H[5] = (H[5] + f) | 0 + H[6] = (H[6] + g) | 0 + H[7] = (H[7] + h) | 0 + } + + const out = new Uint8Array(32) + const outView = new DataView(out.buffer) + for (let i = 0; i < 8; i++) outView.setInt32(i * 4, H[i], false) + return out +} + +function toHex(bytes: Uint8Array): string { + let text = '' + for (const byte of bytes) text += byte.toString(16).padStart(2, '0') + return text +} + +function hasNativeCrypto(): boolean { + return typeof crypto !== 'undefined' && Boolean(crypto.subtle) && typeof crypto.subtle.digest === 'function' +} + +const encoder = new TextEncoder() + +/** 一次 sha256 的 hex 结果(优先原生实现) */ +async function sha256Hex(text: string): Promise { + if (hasNativeCrypto()) { + const digest = await crypto.subtle.digest('SHA-256', encoder.encode(text)) + return toHex(new Uint8Array(digest)) + } + return toHex(sha256Bytes(encoder.encode(text))) +} + +/* ── 求解 ─────────────────────────────────────────────────── */ + +/** + * 枚举 number 使 `sha256(salt + number) === challenge`。 + * + * 每 2000 次让出一次主线程:纯 JS 路径的 await 只进微任务队列,不让出的话 + * 界面会冻住。原生路径每次 digest 本身就是异步的,天然让出。 + * 服务端上限 5–10 万次,平均命中在一半。 + */ +async function solve(challenge: Challenge, onProgress?: SolveProgress): Promise { + const target = String(challenge.challenge || '').toLowerCase() + const salt = String(challenge.salt || '') + const max = Number(challenge.maxnumber) || 0 + if (!target || !salt) throw new Error('题面不完整') + for (let n = 0; n <= max; n++) { + if ((await sha256Hex(salt + n)) === target) return n + if (n % 2000 === 1999) { + onProgress?.(Math.min(1, n / Math.max(1, max))) + await new Promise(resolve => setTimeout(resolve, 0)) + } + } + throw new Error('未找到答案') +} + +/** + * 领题并求解,返回可直接放进登录请求体的 payload。 + * + * 服务端关闭校验时领题回 400 —— 这时返回 `null`,调用方提交时不带 captcha 字段。 + */ +export async function solveCaptcha(onProgress?: SolveProgress): Promise { + let challenge: Challenge + try { + const response = await fetch(url('/api/panel/captcha'), { headers: { Accept: 'application/json' } }) + if (!response.ok) return null + challenge = (await response.json()) as Challenge + } catch { + // 网关不可达:交给登录请求自己报错,不在这里把「验证失败」当结论 + return null + } + const number = await solve(challenge, onProgress) + // 服务端要的是 base64(JSON)(std 与 url-safe 都认,btoa 给的是 std) + return btoa( + JSON.stringify({ + algorithm: challenge.algorithm, + challenge: challenge.challenge, + number, + salt: challenge.salt, + signature: challenge.signature, + }), + ) +} + +/* ── 登录 / 注册 ─────────────────────────────────────────── */ + +export type AuthResult = { access: string; refresh: string } | { error: string } + +/** + * 登录或首次注册。 + * + * 两者共用一条路径:服务端 `/api/panel/status` 说没注册过就走 setup, + * 否则走 login。成功后令牌一定在响应体里 —— 请求带了 `x-panel-auth-mode: + * body`,服务端据此把令牌放进 body(见 `tokens_in_body`)。 + * + * 为什么必须要求令牌进 body:APK 的页面 origin 与网关不同源,服务端种的 + * HttpOnly cookie 属于网关那个 origin,WebView 的 fetch 跨源时不会带上它 + * (`Allow-Origin: *` 下浏览器拒绝带 cookie)。只有拿 body 里的令牌走请求头 + * 这条路。 + */ +export async function authenticate(input: { + mode: 'login' | 'setup' + username: string + password: string + captcha: string | null +}): Promise { + const body: Record = { + username: input.username, + password: input.password, + } + if (input.captcha) body.captcha = input.captcha + + const path = input.mode === 'setup' ? '/api/panel/setup' : '/api/panel/login' + try { + const response = await fetch(url(path), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + // 令牌进响应体(两条通道都给:有的中转会剥自定义头) + 'x-panel-auth-mode': 'body', + }, + body: JSON.stringify(body), + }) + const payload = (await response.json().catch(() => null)) as + | { success?: boolean; data?: { accessToken?: string; refreshToken?: string }; error?: unknown } + | null + if (!response.ok) { + return { error: readError(payload?.error) || `HTTP ${response.status}` } + } + const data = payload?.data + if (!data?.accessToken || !data?.refreshToken) { + // 服务端没按标记给令牌(老版本网关):告诉用户升级,而不是让他反复试 + return { error: '网关没有返回登录令牌,请确认网关版本(需要支持 x-panel-auth-mode 的版本)' } + } + // 先落盘再返回:调用方紧接着就会用当前服务器 id 去读 + storeServerAuth(activeServerId(), data.accessToken, data.refreshToken) + return { access: data.accessToken, refresh: data.refreshToken } + } catch (error) { + return { error: `无法连接网关:${error instanceof Error ? error.message : String(error)}` } + } +} + +function readError(error: unknown): string { + if (typeof error === 'string') return error + if (error && typeof error === 'object') { + const message = (error as { message?: unknown }).message + if (typeof message === 'string') return message + } + return '' +} + +/** 退出登录:撤掉服务端会话(失败也无所谓),再清本地 */ +export async function logout(): Promise { + const auth = readServerAuth(activeServerId()) + const headers: Record = { + Accept: 'application/json', + 'x-panel-auth-mode': 'body', + } + if (auth?.access) { + headers['x-panel-token'] = auth.access + headers.Authorization = `Bearer ${auth.access}` + } + if (auth?.refresh) headers['x-panel-refresh'] = auth.refresh + try { + await fetch(url('/api/panel/logout'), { method: 'POST', headers, body: '{}' }) + } catch { + /* 网关不可达也照样清本地 */ + } +} + +/** 读当前面板状态(未登录也能调) */ +export async function fetchPanelStatus(): Promise { + return apiCall('/api/panel/status', { quiet: true }) +} diff --git a/desktop-tauri/mobile/src/api/client.ts b/desktop-tauri/mobile/src/api/client.ts new file mode 100644 index 00000000..53eeb101 --- /dev/null +++ b/desktop-tauri/mobile/src/api/client.ts @@ -0,0 +1,269 @@ +/** + * 移动端与网关之间的 HTTP 客户端。 + * + * ── 两种运行形态,一套代码 ──────────────────────────────────── + * · **PWA / 浏览器**:页面由网关出,`/api/*` 是同源相对路径,服务端用 + * HttpOnly cookie 认会话 —— 与桌面网页端完全一致。 + * · **APK**:界面打进包里,页面 origin 是 `http://localhost`,请求网关 + * 必然跨源。跨源请求浏览器不会带 cookie,所以必须显式把令牌放进请求头 + * (`x-panel-token` / `x-panel-refresh`),并且登录时要让服务端把令牌 + * 放进响应体(`x-panel-auth-mode: body`,见服务端 `tokens_in_body`)。 + * + * 两条路径的差别只在「地址怎么拼」与「令牌从哪来」,收敛在 `request()` + * 一处,业务层不必区分。 + * + * ── 凭证按服务器隔离 ────────────────────────────────────────── + * 见 lib/servers.ts:令牌是按服务器签发的,混着存会让切换后立刻 401。 + * + * ── 401 的两种语义 ──────────────────────────────────────────── + * · `error.type === 'panel_login_required'` → 管理员已注册但会话没了: + * 先静默 refresh,失败才交给界面弹登录; + * · 其余 401 → 兼容模式没配 Key:交给界面提示输入。 + */ + +import { + activeServerId, + apiBase, + clearServerAuth, + isPackaged, + readServerAuth, + readServerKey, + storeServerAuth, + storeServerKey, + type PanelAuth, +} from '../lib/servers' + +/** 管理 API 的统一错误 */ +export class ApiError extends Error { + status: number + /** 后端 `error.type`(`panel_login_required` 之类),没有则为空串 */ + type: string + constructor(message: string, status: number, type = '') { + super(message) + this.name = 'ApiError' + this.status = status + this.type = type + } +} + +/** 把相对路径拼成完整地址(PWA 下原样返回,交给浏览器按同源处理) */ +export function url(path: string): string { + if (!isPackaged()) return path + const base = apiBase() + if (!base) throw new ApiError('还没有配置服务器地址', 0) + return `${base}${path}` +} + +/** 把相对路径拼成完整地址(PWA 下原样返回,交给浏览器按同源处理) */ +export function resolveUrl(path: string): string { + return url(path) +} + +/** + * 当前会话的鉴权头(给「不能走 apiCall」的调用方用,例如 SSE 流)。 + * + * 与 {@link apiCall} 内部 `buildHeaders` 是同一份实现 —— 两处各写一份的话, + * 「登录过期」在不同页面上会表现成不同的样子。 + */ +export function authHeaders(extra?: Record): Record { + return buildHeaders(extra) +} + +/** 静默续期(对话流 401 时用)。成功返回 true */ +export function refreshPanelAuth(): Promise { + return tryRefresh() +} + +export function readApiKey(): string { + return readServerKey(activeServerId()) +} + +export function storeApiKey(value: string): void { + storeServerKey(activeServerId(), value) +} + +export function readPanelAuth(): PanelAuth | null { + return readServerAuth(activeServerId()) +} + +export function storePanelTokens(access: string, refresh: string): void { + storeServerAuth(activeServerId(), access, refresh) +} + +export function clearPanelAuth(): void { + clearServerAuth(activeServerId()) +} + +/** 会话是否还有效(只用于界面提示,不阻塞渲染) */ +export function hasStoredCredential(): boolean { + const auth = readPanelAuth() + return Boolean(readApiKey() || auth?.access) +} + +/** 401 时由界面接管(弹登录 / 输入 Key);客户端自己不做跳转 */ +let onAuthLost: ((mode: 'login' | 'key') => void) | null = null +export function setAuthLostHandler(handler: ((mode: 'login' | 'key') => void) | null): void { + onAuthLost = handler +} + +function buildHeaders(extra?: Record): Record { + const headers: Record = { Accept: 'application/json', ...extra } + const apiKey = readApiKey() + if (apiKey) headers['x-api-key'] = apiKey + const auth = readPanelAuth() + if (auth?.access) { + headers['x-panel-token'] = auth.access + // Authorization 是标准头:连自定义头都剥的中转也会放它过去 + headers.Authorization = `Bearer ${auth.access}` + } + return headers +} + +/** 在飞的续期请求:同一时刻最多一个,其余 401 等它(见 tryRefresh) */ +let refreshInFlight: Promise | null = null + +/** + * 静默续期,并发 401 单飞:access 过期那一刻页面会同时收到多个 401, + * 各自去换的话,后到的带着同一把(刚被轮换的)refresh 上行,只能靠服务端 + * 的宽限窗兜底(`access::ROTATION_GRACE_MS`)。这里同一时刻只放行一次轮换 + * 请求,其余等同一个结果 —— 先到者换到的新令牌直接被所有等待者共用。 + * 成功返回 true + */ +async function tryRefresh(): Promise { + if (refreshInFlight) return refreshInFlight + refreshInFlight = doRefresh().finally(() => { + refreshInFlight = null + }) + return refreshInFlight +} + +async function doRefresh(): Promise { + const auth = readPanelAuth() + const headers = buildHeaders() + if (auth?.refresh) { + headers['x-panel-refresh'] = auth.refresh + // 要求令牌进响应体:APK 下没有可用的 cookie 通道,只认 body + headers['x-panel-auth-mode'] = 'body' + } + try { + const response = await fetch(url('/api/panel/refresh'), { + method: 'POST', + headers, + body: '{}', + }) + if (!response.ok) return false + const payload = await response.json().catch(() => null) + const data = payload?.data + if (data?.accessToken && data?.refreshToken) { + storePanelTokens(data.accessToken, data.refreshToken) + } + return true + } catch { + return false + } +} + +type RequestInitLite = { + method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' + body?: unknown + /** 不触发 401 接管(探活类请求用) */ + quiet?: boolean + signal?: AbortSignal +} + +/** + * 发一条管理 API 请求并解开 `{success, data}` 信封。 + * + * 返回值是 `data` 本身;失败一律抛 {@link ApiError}。 + */ +export async function apiCall(path: string, init: RequestInitLite = {}): Promise { + const method = init.method || 'GET' + const headers = buildHeaders() + const requestInit: RequestInit = { method, headers, signal: init.signal } + if (init.body !== undefined || method !== 'GET') { + headers['Content-Type'] = 'application/json' + requestInit.body = JSON.stringify(init.body ?? {}) + } + + let response: Response + try { + response = await fetch(url(path), requestInit) + } catch (error) { + throw new ApiError(`无法连接网关:${error instanceof Error ? error.message : String(error)}`, 0) + } + + const text = await response.text() + let payload: unknown = null + try { + payload = text ? JSON.parse(text) : null + } catch { + throw new ApiError(text.trim() || `网关返回了空响应(HTTP ${response.status})`, response.status) + } + + if (response.status === 401 && !init.quiet) { + const type = readErrorType(payload) + if (type === 'panel_login_required') { + if (await tryRefresh()) return apiCall(path, init) + clearPanelAuth() + onAuthLost?.('login') + } else { + onAuthLost?.('key') + } + } + + const envelope = payload as { success?: boolean; data?: unknown; error?: unknown } | null + const ok = response.status >= 200 && response.status < 300 + const failed = envelope?.success === false + if (!ok || failed) { + throw new ApiError(errorMessage(envelope?.error, response.status), response.status, readErrorType(payload)) + } + return (envelope && 'data' in envelope ? envelope.data : envelope) as T +} + +function readErrorType(payload: unknown): string { + const error = (payload as { error?: unknown } | null)?.error + if (error && typeof error === 'object' && 'type' in error) { + const type = (error as { type?: unknown }).type + return typeof type === 'string' ? type : '' + } + return '' +} + +function errorMessage(error: unknown, status: number): string { + if (typeof error === 'string' && error) return error + if (error && typeof error === 'object') { + const message = (error as { message?: unknown }).message + if (typeof message === 'string' && message) return message + } + return `HTTP ${status}` +} + +/** 探活:不带凭证也能打(`/health` 免鉴权) */ +export async function ping(): Promise { + try { + const response = await fetch(url('/health'), { headers: { Accept: 'application/json' } }) + return response.ok + } catch { + return false + } +} + +/** 探活并带回版本信息(配置服务器时用,能顺便显示是不是本产品) */ +export async function probe(base: string): Promise<{ ok: boolean; version?: string }> { + try { + const response = await fetch(`${base}/health`, { headers: { Accept: 'application/json' } }) + if (!response.ok) return { ok: false } + const payload = (await response.json().catch(() => null)) as { version?: string; data?: { version?: string } } | null + return { ok: true, version: payload?.version || payload?.data?.version } + } catch { + return { ok: false } + } +} + +/** 面板登录状态(决定「登录」还是「创建管理员」) */ +export type PanelStatus = { registered?: boolean; captchaEnabled?: boolean } +export async function panelStatus(): Promise { + const response = await fetch(url('/api/panel/status'), { headers: { Accept: 'application/json' } }) + const payload = (await response.json().catch(() => null)) as { data?: PanelStatus } | null + return payload?.data || {} +} diff --git a/desktop-tauri/mobile/src/api/gateway.ts b/desktop-tauri/mobile/src/api/gateway.ts new file mode 100644 index 00000000..476e190b --- /dev/null +++ b/desktop-tauri/mobile/src/api/gateway.ts @@ -0,0 +1,661 @@ +/** + * 网关 API 的类型与薄封装。 + * + * 只声明**移动端真正读到**的字段(后端还会给更多,这里按 unknown 收)。 + * 字段名一律照后端契约(`record.rs` / `session.rs` / `checkin_center.rs`), + * 不在前端另起一套命名 —— 改口径要能一眼看出改的是哪一边。 + */ + +import { apiCall } from './client' + +/* ─── 会话 / 账号 ─────────────────────────────── */ + +export type ProviderSummary = { id?: string; label?: string; count?: number } + +export type RateLimitInfo = { + status?: number | string + code?: string | number + resetAt?: number + message?: string +} + +export type Account = { + id: string + provider?: string + name?: string + nickname?: string + email?: string + enabled?: boolean + available?: boolean + hasCredentials?: boolean + priority?: number + edition?: string + editionLabel?: string + tokenExpiresAt?: number + expiresAt?: number + needsRelogin?: boolean + needsReloginReason?: string + /** 按模型记的限流记录 */ + rateLimits?: Record + [key: string]: unknown +} + +export type SessionPayload = { + health?: { + upstreamConfigured?: boolean + upstreamBaseUrl?: string | null + authSource?: string | null + unavailableReason?: string | null + } + session?: unknown + accounts?: { + currentAccountId?: string | null + currentAccountIds?: Record + accounts?: Account[] + providers?: ProviderSummary[] + } + lastRequestModel?: string | null + routedAccountId?: string | null + defaultModel?: string | null + proxies?: { clashAvailable?: boolean; clashError?: string | null; optionCount?: number } + models?: Array<{ id?: string; name?: string; provider?: string; providerLabel?: string }> +} + +export function fetchSession(signal?: AbortSignal): Promise { + return apiCall('/api/session', { signal }) +} + +/** 账号列表(`GET /api/accounts` 返回的就是快照形状) */ +export function fetchAccounts(signal?: AbortSignal): Promise<{ + accounts?: Account[] + providers?: ProviderSummary[] + currentAccountId?: string | null + currentAccountIds?: Record +}> { + return apiCall('/api/accounts', { signal }) +} + +export function setAccountEnabled(id: string, enabled: boolean): Promise { + return apiCall(`/api/accounts/${encodeURIComponent(id)}`, { method: 'PATCH', body: { enabled } }) +} + +export function deleteAccount(id: string): Promise { + return apiCall(`/api/accounts/${encodeURIComponent(id)}`, { method: 'DELETE' }) +} + +export function refreshAccount(id: string): Promise { + return apiCall('/api/accounts/refresh', { method: 'POST', body: { id } }) +} + +export function refreshExpiringAccounts(): Promise { + return apiCall('/api/accounts/refresh-expiring', { method: 'POST' }) +} + +/* ─── 报表 ─────────────────────────────────────── */ + +export type StatsOverview = { + requests?: number + successful?: number + tokens?: number + activeDays?: number + streak?: number + topModel?: { model?: string; tokens?: number; percentage?: number } | null +} + +export type StatsDimension = { + id?: string + label?: string + requests?: number + success?: number + failures?: number + totalTokens?: number + tokens?: number + percentage?: number +} + +export type StatsSummary = { + range?: string + startDate?: string + endDate?: string + overview?: StatsOverview + providers?: StatsDimension[] + accounts?: StatsDimension[] + models?: StatsDimension[] + cacheRates?: Record + dailyTrend?: Array<{ date?: string; tokens?: number; requests?: number }> + cacheTrend24h?: unknown +} + +export type StatsRange = 'today' | 'yesterday' | '7' | '30' | 'month' | 'all' + +export function fetchStatsSummary(range: StatsRange, signal?: AbortSignal): Promise { + return apiCall(`/api/stats/summary?range=${encodeURIComponent(range)}`, { signal }) +} + +/* ─── 请求日志 ─────────────────────────────────── */ + +export type AttemptDetail = { provider?: string; status?: number; error?: string } + +export type RequestRow = { + ts?: number + id?: string + model?: string + accountId?: string + accountName?: string + status?: number + durationMs?: number + firstResponseMs?: number | null + attempts?: number + error?: string | null + promptTokens?: number + completionTokens?: number + totalTokens?: number + cacheReadTokens?: number + provider?: string + providerLabel?: string + clientModel?: string + upstreamModel?: string + attemptDetails?: AttemptDetail[] + sensitiveHits?: Array<{ word?: string; count?: number }> + route?: unknown +} + +export type RequestPage = { + entries?: RequestRow[] + total?: number + matched?: number + running?: number +} + +export function fetchRequests( + params: { + offset?: number + limit?: number + /** 后端只认这三个值,其余忽略(见 report::normalize_status_filter) */ + status?: 'ok' | 'error' | 'running' + model?: string + provider?: string + }, + signal?: AbortSignal, +): Promise { + const query = new URLSearchParams() + if (params.offset) query.set('offset', String(params.offset)) + if (params.limit) query.set('limit', String(params.limit)) + if (params.status) query.set('status', params.status) + if (params.model) query.set('model', params.model) + if (params.provider) query.set('provider', params.provider) + const suffix = query.toString() + return apiCall(`/api/stats/requests${suffix ? `?${suffix}` : ''}`, { signal }) +} + +/* ─── 运行日志 ─────────────────────────────────── */ + +export type LogEntry = { + id?: number + ts?: number + level?: string + category?: string + message?: string + data?: unknown +} + +export type LogPage = { + entries?: LogEntry[] + total?: number + stats?: unknown +} + +export function fetchLogs( + params: { level?: string; category?: string; keyword?: string; start?: number; limit?: number }, + signal?: AbortSignal, +): Promise { + const query = new URLSearchParams() + if (params.level) query.set('level', params.level) + if (params.category) query.set('category', params.category) + if (params.keyword) query.set('keyword', params.keyword) + if (params.start) query.set('start', String(params.start)) + query.set('limit', String(params.limit ?? 60)) + return apiCall(`/api/logs?${query.toString()}`, { signal }) +} + +/* ─── 签到中心 ─────────────────────────────────── */ + +export type CheckinAccount = { + id?: string + name?: string + available?: boolean + checkinAt?: number + checkedInToday?: boolean +} + +export type CheckinProviderRow = { + id?: string + label?: string + accounts?: CheckinAccount[] + doneCount?: number + totalCount?: number +} + +export type CheckinCenter = { + daily?: { + providers?: CheckinProviderRow[] + outOfScope?: Array<{ label?: string; reason?: string; count?: number }> + todayDone?: number + todayEligible?: number + } + extras?: { + /** 新手福利:`settled` 非空 = 一次性福利已结清(零上游即可渲染) */ + onboarding?: Array<{ id?: string; name?: string; provider?: string; settled?: unknown }> + /** ZCode 活动套餐:`claimPlans` 是本地领取台账 `{planId: 毫秒}` */ + plans?: Array<{ + id?: string + name?: string + provider?: string + claimAt?: number | null + claimPlans?: Record | null + }> + } + auto?: { enabled?: boolean; time?: string; providers?: string[] } + history?: Array<{ + at?: number + date?: string + reason?: string + succeeded?: number + total?: number + skipped?: number + failed?: number + failedCount?: number + }> +} + +export function fetchCheckinCenter(signal?: AbortSignal): Promise { + return apiCall('/api/checkin-center', { signal }) +} + +/** 批量签到(不带 id = 全部可签账号) */ +export function runCheckin(id?: string): Promise { + return apiCall('/api/accounts/checkin', { method: 'POST', body: id ? { id } : {} }) +} + +/* ─── 新手福利(一次性任务) ─────────────────────── */ + +export type OnboardingTask = { + key?: string + title?: string + group?: string + points?: number + done?: boolean + blocked?: boolean +} + +export type OnboardingStatus = { + tasks?: OnboardingTask[] + earned?: number + total?: number + unclaimed?: number + /** 一次性福利已全部结清:界面此后按本地记忆渲染,不再查上游 */ + settled?: boolean + settledAt?: number | null + /** OfficeAce 之类会回一句「本次到账 N 积分」,有就优先显示它 */ + message?: string +} + +/** + * 新手福利任务状态。 + * + * `refresh = true` 才真的去问上游(桌面端「查询任务」那颗按钮的语义); + * 平时走服务端的结算记忆,零上游请求。 + */ +export function fetchOnboarding(id: string, refresh = false): Promise { + const suffix = refresh ? '?refresh=1' : '' + return apiCall(`/api/accounts/${encodeURIComponent(id)}/onboarding${suffix}`) +} + +export type OnboardingClaimResult = OnboardingStatus & { + results?: Array<{ key?: string; ok?: boolean; already?: boolean; error?: string }> + claimed?: number + failed?: number + claimedPoints?: number +} + +/** 一键领取全部未完成的新手任务(服务端幂等,重复点不会重复加分) */ +export function claimOnboarding(id: string): Promise { + return apiCall(`/api/accounts/${encodeURIComponent(id)}/onboarding/claim`, { + method: 'POST', + body: {}, + }) +} + +/* ─── ZCode 活动套餐 ─────────────────────────────── */ + +export type ZcodePlan = { + planId?: string + name?: string + description?: string + priority?: number + startsAt?: number + endsAt?: number + entitlements?: Array<{ + showName?: string + unitType?: string + grantUnits?: number + period?: string + effectiveAt?: number + }> +} + +export type ZcodePreview = { + plans?: ZcodePlan[] + /** false = 上游活动接口还没部署(活动未开始,不是错误) */ + deployed?: boolean + activated?: boolean | null + activationError?: string | null +} + +/** 探测可领套餐(只读;列表为空时后端会补报一次激活事件再重探) */ +export function previewZcodePlans(id: string): Promise { + return apiCall(`/api/accounts/${encodeURIComponent(id)}/zcode-claim/preview`, { + method: 'POST', + body: {}, + }) +} + +export type ZcodeCaptchaConfig = { + enabled?: boolean + prefix?: string + sceneId?: string + region?: string + skipModelRequest?: boolean +} + +/** 上游此刻要不要滑块验证码(不要就别弹) */ +export function fetchZcodeCaptchaConfig(id: string): Promise { + return apiCall( + `/api/accounts/${encodeURIComponent(id)}/zcode-claim/captcha-config`, + { method: 'POST', body: {} }, + ) +} + +export type ZcodeClaimResult = { + ok?: boolean + planId?: string + startsAt?: number + endsAt?: number + claimedAt?: number + /** 失败语义键:already_claimed / quota_exhausted / captcha … */ + failure?: string + /** 后端归一过的中文失败原因(界面直接显示,不再自己写一套) */ + failureLabel?: string + code?: number + message?: string + failureEndsAt?: number + activated?: boolean | null + activationError?: string | null +} + +/** + * 领取一份套餐。 + * + * 注意失败也走 200(`ok: false` + `failure`)——「已领过」「额度用尽」都是 + * 正常业务结果,不是 HTTP 错误,所以调用方要读 `ok` 而不是只 catch。 + */ +export function claimZcodePlan( + id: string, + payload: { planId?: string; captchaVerifyParam?: string; captchaRegion?: string }, +): Promise { + return apiCall(`/api/accounts/${encodeURIComponent(id)}/zcode-claim`, { + method: 'POST', + body: payload, + }) +} + +/* ─── 模型管理 ─────────────────────────────────── */ + +export type ManageModel = { + id?: string + name?: string + provider?: string + providerLabel?: string + source?: string + enabled?: boolean + aliases?: string[] + visibility?: string | null + capabilities?: unknown + reasoningLevels?: unknown + refreshedAt?: unknown +} + +export type ManageView = { models?: ManageModel[] } + +export function fetchManage(signal?: AbortSignal): Promise { + return apiCall('/api/models/manage', { signal }) +} + +export function setModelEnabled(payload: { + id: string + provider: string + enabled: boolean +}): Promise { + return apiCall('/api/models/state', { method: 'POST', body: payload }) +} + +export function refreshModels(): Promise { + return apiCall('/api/models/refresh', { method: 'POST' }) +} + +/* ─── 网关 Key ─────────────────────────────────── */ + +export type GatewayKey = { + id?: string + name?: string + key?: string + masked?: string + enabled?: boolean + createdAt?: number + allowedProviders?: string[] + allowedModels?: string[] +} + +export type KeysPayload = { + keys?: GatewayKey[] + authRequired?: boolean + providers?: ProviderSummary[] + modelsByProvider?: Record +} + +export function fetchKeys(signal?: AbortSignal): Promise { + return apiCall('/api/keys', { signal }) +} + +export function setKeyEnabled(id: string, enabled: boolean): Promise { + return apiCall(`/api/keys/${encodeURIComponent(id)}`, { method: 'PATCH', body: { enabled } }) +} + +export function deleteKey(id: string): Promise { + return apiCall(`/api/keys/${encodeURIComponent(id)}`, { method: 'DELETE' }) +} + +/* ─── 网络代理 ─────────────────────────────────── */ + +export type PoolItem = { + id?: string + name?: string + source?: string + enabled?: boolean + protocol?: string + host?: string + port?: number + address?: string + lastTest?: { at?: number; success?: boolean; ip?: string; error?: string } | null + /** 引用它的账号(后端带的是**数组**,桌面端同一字段也是这个形状) */ + usedBy?: Array<{ id?: string; name?: string; enabled?: boolean }> + /** 孤儿订阅节点:所属订阅已不存在(后端标注) */ + orphan?: boolean + [key: string]: unknown +} + +export type PoolPayload = { + items?: PoolItem[] + clash?: { available?: boolean; error?: string | null; options?: unknown[] } + /** 内核装没装:订阅节点没有端口时的首要成因(没内核就没有监听器) */ + kernelInstalled?: boolean +} + +export function fetchProxyPool(signal?: AbortSignal): Promise { + return apiCall('/api/proxies/pool', { signal }) +} + +export function syncClashProxies(): Promise { + return apiCall('/api/proxies/pool/sync-clash', { method: 'POST' }) +} + +export function setProxyEnabled(id: string, enabled: boolean): Promise { + return apiCall('/api/proxies/pool/enable', { method: 'POST', body: { id, enabled } }) +} + +export function testProxy(id: string): Promise { + return apiCall('/api/proxies/pool/test', { method: 'POST', body: { id } }) +} + +/* ─── 定时任务 ─────────────────────────────────── */ + +export type ScheduledTask = { + id?: string + label?: string + description?: string + unit?: string + runner?: 'backend' | 'frontend' + enabled?: boolean + interval?: number + min?: number + max?: number + defaultInterval?: number + running?: boolean + lastRunAt?: number | null + lastSuccessAt?: number | null + lastResult?: string | null + lastError?: string | null + nextRunAt?: number | null + canRun?: boolean +} + +export function fetchScheduledTasks(signal?: AbortSignal): Promise<{ tasks?: ScheduledTask[] }> { + return apiCall<{ tasks?: ScheduledTask[] }>('/api/scheduled-tasks', { signal }) +} + +export function patchScheduledTask( + id: string, + patch: { enabled?: boolean; interval?: number }, +): Promise<{ tasks?: ScheduledTask[] }> { + return apiCall(`/api/scheduled-tasks/${encodeURIComponent(id)}`, { method: 'PATCH', body: patch }) +} + +export function runScheduledTask(id: string): Promise { + return apiCall(`/api/scheduled-tasks/${encodeURIComponent(id)}/run`, { method: 'POST' }) +} + +/* ─── Agent 接管 ───────────────────────────────── */ + +export type AgentClient = { + id?: string + name?: string + path?: string + applied?: boolean + mode?: string + [key: string]: unknown +} + +export function fetchAgents(signal?: AbortSignal): Promise<{ clients?: AgentClient[] }> { + return apiCall<{ clients?: AgentClient[] }>('/api/agents', { signal }) +} + +export function restoreAgentClient(id: string): Promise { + return apiCall('/api/agents/restore', { method: 'POST', body: { id } }) +} + +/* ─── 账号余额 ─────────────────────────────── */ + +export type UsageRow = { + id?: string + name?: string | null + /** 后端按家给不同形状;前端只读其中几个通用键,故用 Record */ + usage?: Record | null + error?: string | null + code?: string | number | null + /** 这一行的结论时刻(毫秒) */ + at?: number +} + +export type UsageSnapshot = { + /** 全部行里最新的结论时刻 */ + at?: number + results?: UsageRow[] + skipped?: number +} + +/** 最近一次查询结果的快照(只读记录表,不触发上游查询) */ +export function fetchUsageSnapshot(signal?: AbortSignal): Promise { + return apiCall('/api/accounts/usage/snapshot', { signal }) +} + +/** 手动查询单个账号余额(会真的打上游,可能慢) */ +export function fetchAccountUsage(id: string): Promise<{ results?: UsageRow[]; skipped?: number }> { + return apiCall<{ results?: UsageRow[]; skipped?: number }>( + `/api/accounts/usage?id=${encodeURIComponent(id)}`, + ) +} + +/** 改备注名(后端 PATCH 支持 name 字段,空串会被 400 拒绝) */ +export function renameAccount(id: string, name: string): Promise { + return apiCall(`/api/accounts/${encodeURIComponent(id)}`, { method: 'PATCH', body: { name } }) +} + +/** 清除限流标记;不传 model = 清除该账号全部模型 */ +export function clearAccountRateLimits(id: string, model?: string): Promise { + return apiCall(`/api/accounts/${encodeURIComponent(id)}/rate-limits/clear`, { + method: 'POST', + body: model ? { model } : {}, + }) +} + +/** 把账号设为全局队列第一位(设为首选) */ +export function promoteAccount(id: string): Promise { + return apiCall('/api/accounts/current', { method: 'POST', body: { id } }) +} + +/* ─── 签到记录 ─────────────────────────────── */ + +export type CheckinRecordRow = { + rowId?: number + ts?: number + /** 北京时间的自然日 */ + date?: string + /** schedule / startup / manual / single / activity */ + source?: string + reason?: string + accountId?: string + accountName?: string + provider?: string + /** success / already / pending / failed */ + outcome?: string + ok?: boolean + message?: string | null + error?: string | null + keptActive?: boolean +} + +export type CheckinRecordPage = { records?: CheckinRecordRow[]; total?: number } + +export function fetchCheckinRecords( + params: { limit?: number; offset?: number; outcome?: string; provider?: string; keyword?: string; start?: number }, + signal?: AbortSignal, +): Promise { + const query = new URLSearchParams() + if (params.offset) query.set('offset', String(params.offset)) + query.set('limit', String(params.limit ?? 50)) + if (params.outcome) query.set('outcome', params.outcome) + if (params.provider) query.set('provider', params.provider) + if (params.keyword) query.set('keyword', params.keyword) + if (params.start) query.set('start', String(params.start)) + return apiCall(`/api/checkin-records?${query.toString()}`, { signal }) +} diff --git a/desktop-tauri/mobile/src/components/AccountsFilterSheet.tsx b/desktop-tauri/mobile/src/components/AccountsFilterSheet.tsx new file mode 100644 index 00000000..ac688f73 --- /dev/null +++ b/desktop-tauri/mobile/src/components/AccountsFilterSheet.tsx @@ -0,0 +1,253 @@ +import { Sheet, SheetContent, SheetHeader, SheetBody, SheetFooter, SheetTitle } from '@ui' +import { Icon } from './Icon' +import { ProviderLogo } from './ProviderLogo' +import { providerLabel } from '../lib/providers' +import { t } from '../i18n' + +/** + * 账号筛选面板(账号页右上角那颗「筛选」按钮打开)。 + * + * ── 为什么从横排 chip 换成侧边面板 ──────────────────────────── + * 原来只有「全部 / 启用 / 限流 / 禁用」四颗横排 chip。四颗还放得下,但账号 + * 页真正要筛的维度不止这一个:提供商、有效期、余额状态各自都是一组。全都 + * 横排会变成一条要左右滑动的长条 —— 手机上滑条本身比内容还难操作,而且 + * 用户看不出「现在到底筛了什么」。 + * + * 面板里每个维度占一行、各自单选(提供商多选),底部常驻「重置」与「完成」。 + * 条件**即时生效**:关掉面板就能看到结果,不需要先点应用再看 —— 手机上多 + * 一步确认只会让人怀疑自己点没点对。 + * + * ── 为什么是右侧而不是底部 ──────────────────────────────────── + * 底部抽屉会盖住列表的下半部分,而筛选恰恰是「边看结果边调条件」的动作。 + * 右侧抽屉只占 340px(窄屏 86vw),左边还能看到列表在变。 + */ + +export type StatusFilter = 'all' | 'enabled' | 'disabled' | 'limited' | 'relogin' | 'expired' +export type ExpiryFilter = 'all' | 'soon' | 'expired' | 'longlived' +export type UsageFilter = 'all' | 'has' | 'unqueried' | 'failed' + +export type AccountFilters = { + status: StatusFilter + /** 选中的提供商 id;空数组 = 不限制 */ + providers: string[] + expiry: ExpiryFilter + usage: UsageFilter +} + +export const DEFAULT_FILTERS: AccountFilters = { + status: 'all', + providers: [], + expiry: 'all', + usage: 'all', +} + +/** 生效中的筛选维度个数(按钮上的角标;提供商按一个维度算) */ +export function activeFilterCount(filters: AccountFilters): number { + let count = 0 + if (filters.status !== 'all') count++ + if (filters.providers.length) count++ + if (filters.expiry !== 'all') count++ + if (filters.usage !== 'all') count++ + return count +} + +export function AccountsFilterSheet({ + open, + onClose, + filters, + onChange, + providers, + counts, +}: { + open: boolean + onClose: () => void + filters: AccountFilters + onChange: (next: AccountFilters) => void + /** 当前账号里出现过的提供商(id + 展示名) */ + providers: Array<{ id: string; label: string }> + /** 各状态的账号数,显示在选项右侧 */ + counts: { + status: Record + expiry: Record + usage: Record + } +}) { + const patch = (next: Partial) => onChange({ ...filters, ...next }) + + const toggleProvider = (id: string) => { + const picked = filters.providers.includes(id) + ? filters.providers.filter(item => item !== id) + : [...filters.providers, id] + patch({ providers: picked }) + } + + return ( + { if (!next) onClose() }}> + + + {t('筛选')} + + +
+ patch({ status: 'all' })} + /> + patch({ status: 'enabled' })} + /> + patch({ status: 'disabled' })} + /> + patch({ status: 'limited' })} + /> + patch({ status: 'relogin' })} + /> + patch({ status: 'expired' })} + /> +
+ + {providers.length > 1 ? ( +
+ {providers.map(item => ( + + ))} +
+ ) : null} + +
+ patch({ expiry: 'all' })} + /> + patch({ expiry: 'soon' })} + /> + patch({ expiry: 'expired' })} + /> + patch({ expiry: 'longlived' })} + /> +
+ +
+ patch({ usage: 'all' })} + /> + patch({ usage: 'has' })} + /> + patch({ usage: 'unqueried' })} + /> + patch({ usage: 'failed' })} + /> +
+ +

+ {t('条件即时生效,关掉面板就能看到结果。')} +

+
+ + + + +
+
+ ) +} + +function Section({ title, children }: { title: string; children: React.ReactNode }) { + return ( +
+
+ {title} +
+
{children}
+
+ ) +} + +function Chip({ + label, + count, + on, + onClick, +}: { + label: string + count: number + on: boolean + onClick: () => void +}) { + return ( + + ) +} diff --git a/desktop-tauri/mobile/src/components/AppUpdatePanel.tsx b/desktop-tauri/mobile/src/components/AppUpdatePanel.tsx new file mode 100644 index 00000000..f3614910 --- /dev/null +++ b/desktop-tauri/mobile/src/components/AppUpdatePanel.tsx @@ -0,0 +1,399 @@ +import { useEffect, useRef, useState } from 'react' +import { SectionHead } from './ui' +import { Icon } from './Icon' +import { store } from '../state/store' +import { + APP_VERSION, + autoCheckEnabled, + autoCheckIntervalMs, + canInstallApk, + canSelfUpdate, + cancelDownload, + checkApkUpdate, + downloadApk, + hasDownloadedApk, + installApk, + openInstallPermission, + setAutoCheckEnabled, + type ApkUpdateInfo, + type DownloadProgress, +} from '../lib/appUpdate' +import { openExternal } from '../lib/community' +import { t } from '../i18n' + +/** + * 应用内更新面板(Android / iOS 共用,行为按平台分叉)。 + * + * ── 状态机 ────────────────────────────────────────────────────── + * idle → checking → (up-to-date | available) → downloading → downloaded + * 任一步失败回落到 idle 并给错误提示。 + * + * ── iOS 为什么只有「去下载」 ──────────────────────────────────── + * 苹果不允许 App 自行下载并安装可执行代码,IPA 只能由系统装(App Store / + * TestFlight / 侧载工具)。所以 iOS 上这个面板到「发现新版本」为止,动作 + * 按钮改成打开 GitHub Release 页面,由用户自己选渠道 —— 不渲染下载进度条 + * 与「安装」按钮,避免给一个必然失败的承诺。 + * + * ── 自动检查 ──────────────────────────────────────────────────── + * 挂载时按电脑端配置的间隔轮询检查(间隔读 `/api/scheduled-tasks` 的 + * `updateCheck` 条目,见 lib/appUpdate)。页面不可见时停表 —— 与各列表页的 + * 轮询同一套可见性规则,手机上不该在后台耗流量。 + * + * ── 为什么「已下载」要单独一档 ────────────────────────────────── + * 用户下完包、跳去授权、再回来时组件会重新挂载 —— 如果只按内存状态判断, + * 界面会回到「有新版本」让他重下一遍(几十 MB)。所以挂载时问一次原生 + * `downloaded()`,有包就直接进「待安装」。 + * + * ── 授权顺序 ──────────────────────────────────────────────────── + * 先下载再要授权:下载不需要任何权限,而授权会跳走系统设置页 —— 先要权限 + * 再下载,用户跳回来时下载还没开始,体验是断的。 + */ +type Phase = 'idle' | 'checking' | 'latest' | 'available' | 'downloading' | 'downloaded' + +export function AppUpdatePanel() { + const [phase, setPhase] = useState('idle') + const [info, setInfo] = useState(null) + const [progress, setProgress] = useState(null) + const [error, setError] = useState('') + const [busy, setBusy] = useState(false) + const [auto, setAuto] = useState(autoCheckEnabled) + /** 自动检查的间隔(0 = 电脑端关了这条任务) */ + const [interval, setIntervalMs] = useState(0) + /** 当前壳能不能自装安装包:只有 Android 能(见 lib/appUpdate) */ + const selfUpdate = canSelfUpdate() + + // 挂载时先看缓存里有没有上次下好的包(iOS 上恒为 false) + useEffect(() => { + let cancelled = false + void hasDownloadedApk().then(result => { + if (!cancelled && result.exists) setPhase('downloaded') + }) + return () => { + cancelled = true + } + }, []) + + /** + * 自动检查:间隔读电脑端配置,页面不可见时停表。 + * + * 为什么把 interval 放进依赖:用户在电脑端改了间隔,手机端下次进来就该 + * 按新间隔走 —— 组件重挂时重新读一次即可(设置页每次进入都会重挂)。 + */ + useEffect(() => { + if (!auto) return + let disposed = false + let timer: number | null = null + + const clear = () => { + if (timer !== null) { + window.clearInterval(timer) + timer = null + } + } + const start = (ms: number) => { + clear() + if (disposed || ms <= 0) return + timer = window.setInterval(() => { + if (document.visibilityState === 'visible') void runCheck(true) + }, ms) + } + + void (async () => { + const ms = await autoCheckIntervalMs() + if (disposed) return + setIntervalMs(ms) + // 进页面先查一次(用户打开设置就是想看有没有新版),再按间隔轮询 + await runCheck(true) + start(ms) + })() + + const onVisibility = () => { + if (document.visibilityState === 'visible') void runCheck(true) + } + document.addEventListener('visibilitychange', onVisibility) + + return () => { + disposed = true + clear() + document.removeEventListener('visibilitychange', onVisibility) + } + // auto 变化时重来一轮;interval 由 effect 内部读取 + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [auto]) + + // 避免自动检查覆盖用户正在进行的下载 / 已下载待安装 + const phaseRef = useRef(phase) + phaseRef.current = phase + + /** `silent` = 自动检查:失败不弹错误、不打断当前状态 */ + const runCheck = async (silent = false) => { + const current = phaseRef.current + if (current === 'downloading' || current === 'downloaded') return + if (!silent) { + setBusy(true) + setPhase('checking') + } + if (!silent) setError('') + try { + const result = await checkApkUpdate() + setInfo(result) + // hasUpdate 为 null = 版本无法比较(上游 tag 不是 semver); + // 这时只要挂了 APK 就允许手动更新,而不是卡在「未知」上 + const canUpdate = result.hasUpdate === true || (result.hasUpdate === null && result.asset !== null) + setPhase(prev => (prev === 'downloading' || prev === 'downloaded' ? prev : canUpdate ? 'available' : 'latest')) + } catch (failure) { + if (!silent) { + setError(failure instanceof Error ? failure.message : String(failure)) + setPhase('idle') + } + } finally { + if (!silent) setBusy(false) + } + } + + const download = async () => { + const url = info?.asset?.url + if (!url) { + setError(t('这一版没有提供 Android 安装包')) + return + } + setBusy(true) + setError('') + setProgress({ received: 0, total: 0, percent: -1 }) + setPhase('downloading') + try { + await downloadApk(url, setProgress) + setPhase('downloaded') + } catch (failure) { + setError(failure instanceof Error ? failure.message : String(failure)) + setPhase('available') + } finally { + setBusy(false) + setProgress(null) + } + } + + const install = async () => { + setBusy(true) + setError('') + try { + // 先确认授权:没有的话跳到系统设置页,回来后用户再点一次安装 + if (!(await canInstallApk())) { + store.toast(t('请允许安装未知来源应用后重试')) + await openInstallPermission() + return + } + await installApk() + store.toast(t('已启动安装器')) + } catch (failure) { + setError(failure instanceof Error ? failure.message : String(failure)) + } finally { + setBusy(false) + } + } + + const stop = async () => { + await cancelDownload() + setPhase('available') + setProgress(null) + setBusy(false) + } + + /** + * iOS 的「更新」只能跳转:打开 GitHub Release 页面,由用户自己选渠道 + * (TestFlight / App Store / 侧载)。`pageUrl` 是网关从 GitHub API 拿到的 + * release 页面地址,没有它时退回仓库的 releases 列表。 + */ + const openReleasePage = () => { + const target = info?.pageUrl || 'https://github.com/shadyrispy/Agent2API/releases' + openExternal(target) + } + + const toggleAuto = () => { + const next = !auto + setAutoCheckEnabled(next) + setAuto(next) + store.toast(next ? t('已开启自动检查') : t('已关闭自动检查')) + } + + const percent = progress && progress.percent >= 0 ? progress.percent : null + const sizeText = (bytes?: number) => { + if (!bytes) return '' + const mb = bytes / 1024 / 1024 + return `${mb >= 100 ? mb.toFixed(0) : mb.toFixed(1)} MB` + } + + return ( +
+ +
+
+ + + +
+
+ {phase === 'latest' + ? t('已是最新版本') + : phase === 'available' + ? t('发现新版本 {version}', { version: info?.latestVersion || '' }) + : phase === 'downloading' + ? t('正在下载…') + : phase === 'downloaded' + ? t('已下载,待安装') + : phase === 'checking' + ? t('正在检查…') + : t('当前版本 {version}', { version: APP_VERSION })} +
+
+ {phase === 'available' && !selfUpdate + ? t('请在 Release 页面下载后通过 TestFlight 或 App Store 安装') + : phase === 'available' && info?.asset + ? t('{size} · 下载后可直接安装,无需跳转浏览器', { size: sizeText(info.asset.size) }) + : phase === 'downloaded' + ? t('安装包已就绪,点「安装」即可完成更新') + : phase === 'downloading' && percent !== null + ? t('已完成 {percent}%', { percent }) + : phase === 'latest' + ? t('当前版本 {version}', { version: APP_VERSION }) + : selfUpdate + ? t('版本 {version} · 从 GitHub 直接下载安装包', { version: APP_VERSION }) + : t('版本 {version} · 更新包需从 Release 页面获取', { version: APP_VERSION })} +
+ + {/* 进度条:总长度未知(percent = null)时不画,只显示已下载量 */} + {phase === 'downloading' ? ( +
+
+
+
+ {progress ? ( +
+ {sizeText(progress.received)} + {progress.total > 0 ? ` / ${sizeText(progress.total)}` : ''} +
+ ) : null} +
+ ) : null} + + {error ? ( +
+ {error} +
+ ) : null} +
+ + {/* 动作按钮:按状态给唯一的主动作,避免一屏好几个可点的东西 */} + {phase === 'downloading' ? ( + + ) : phase === 'downloaded' ? ( + + ) : phase === 'available' && !selfUpdate ? ( + // iOS:只能跳出去,由系统渠道安装 + + ) : phase === 'available' ? ( + + ) : ( + + )} +
+ + {/* 更新说明:有内容才展开,避免空卡片 */} + {info?.notes && (phase === 'available' || phase === 'downloaded') ? ( +
+ {info.notes} +
+ ) : null} + + {/* 自动检查开关:间隔跟电脑端一致,这里只控制「这台手机查不查」 */} +
+ + + +
+
{t('自动检查更新')}
+
+ {interval > 0 + ? t('与电脑端一致:每 {n} 分钟', { n: Math.round(interval / 60_000) }) + : t('电脑端已关闭「软件版本检查」,此处只做手动检查')} +
+
+ +
+
+
+ ) +} diff --git a/desktop-tauri/mobile/src/components/CheckinClaimSheets.tsx b/desktop-tauri/mobile/src/components/CheckinClaimSheets.tsx new file mode 100644 index 00000000..bcb892ca --- /dev/null +++ b/desktop-tauri/mobile/src/components/CheckinClaimSheets.tsx @@ -0,0 +1,491 @@ +import { useEffect, useState } from 'react' +import { Sheet, SheetContent, SheetHeader, SheetBody, SheetTitle, SheetFooter } from '@ui' +import { Icon } from './Icon' +import { ProviderLogo } from './ProviderLogo' +import { Badge, Empty, ErrorNote } from './ui' +import { store } from '../state/store' +import { + claimOnboarding, + claimZcodePlan, + fetchOnboarding, + fetchZcodeCaptchaConfig, + previewZcodePlans, + type OnboardingStatus, + type OnboardingTask, + type ZcodeCaptchaConfig, + type ZcodePlan, +} from '../api/gateway' +import { CaptchaCancelledError, cancelCaptcha, solveCaptcha } from '../lib/aliyunCaptcha' +import { isToday } from '../lib/format' +import { t } from '../i18n' + +/** + * 签到中心「新手福利与活动」两块的领取抽屉。 + * + * ── 为什么这两块原本是「点不动」的 ──────────────────────────── + * 它们最初只是**入口清单**(后端 `extras.onboarding` / `extras.plans` 只给 + * 账号 id 与名字,任务详情要按账号惰性查),手机端第一版没接动作,只画了 + * 徽章。徽章写着「去领取」却点不动 —— 这是明确的坏体验,所以补上。 + * + * ── 两条链的动作形态不同 ────────────────────────────────────── + * · **新手福利**(Loomy / 小浣熊 / CodeArts / OfficeAce / 智谱):一条 + * `POST .../onboarding/claim` 领全部,服务端幂等,重复点不重复加分; + * · **活动套餐**(ZCode):先 `preview` 探测,再逐份 `zcode-claim` 领取。 + * 领取可能要过阿里云滑块(上游的风控配置说了算),所以那条链路多一步 + * 验证码 —— 见 lib/aliyunCaptcha。 + * + * ── 为什么任务详情是「打开才查」──────────────────────────────── + * 快照里的行是零上游请求的(后端刻意如此:进签到中心不该为每个账号打一次 + * 上游)。查询是用户点开这一行才发生的动作,失败也只影响这张抽屉。 + */ + +/* ─── 新手福利 ─────────────────────────────── */ + +export function OnboardingSheet({ + account, + onClose, + onChanged, +}: { + account: { id: string; name?: string; provider?: string } | null + onClose: () => void + onChanged: () => void +}) { + const [data, setData] = useState(null) + const [error, setError] = useState('') + const [busy, setBusy] = useState(false) + const id = account?.id || '' + + useEffect(() => { + if (!id) return + let cancelled = false + setData(null) + setError('') + fetchOnboarding(id) + .then(next => { + if (!cancelled) setData(next) + }) + .catch(failure => { + if (!cancelled) setError(failure instanceof Error ? failure.message : String(failure)) + }) + return () => { + cancelled = true + } + }, [id]) + + const claim = async () => { + if (!id || busy) return + setBusy(true) + setError('') + try { + const result = await claimOnboarding(id) + setData(result) + // 后端对本次结果有交代时以它为准(OfficeAce 会回「本次到账 N 积分」) + const message = typeof result.message === 'string' ? result.message.trim() : '' + if (result.failed) { + store.toast(t('新手福利有 {n} 项领取失败,可重试', { n: result.failed }), 'err') + } else if (message) { + store.toast(message) + } else { + store.toast( + t('✅ 新手福利领取完成,累计 {n} 积分', { n: Number(result.earned) || 0 }), + ) + } + onChanged() + } catch (failure) { + setError(failure instanceof Error ? failure.message : String(failure)) + } finally { + setBusy(false) + } + } + + const tasks: OnboardingTask[] = Array.isArray(data?.tasks) ? data?.tasks || [] : [] + const pending = tasks.filter(task => !task.done && !task.blocked).length + const settled = data?.settled === true + + return ( + { if (!next) onClose() }}> + + + {t('新手福利')} + + + {account ? ( + <> +
+ +
+
{account.name || account.id}
+
+ {settled + ? t('一次性福利已全部结清') + : t('已得 {earned} / {total} 积分', { + earned: Number(data?.earned) || 0, + total: Number(data?.total) || 0, + })} +
+
+ {settled ? ( + {t('已领取')} + ) : pending > 0 ? ( + {t('{n} 项待领', { n: pending })} + ) : null} +
+ + {error ? : null} + {!data && !error ? ( +
+ ) : null} + + {data && !tasks.length ? ( + + ) : null} + + {tasks.length ? ( +
+ {tasks.map((task, index) => ( +
+ + + +
+
{task.title || task.key || '—'}
+ {task.blocked ? ( +
+ {t('暂不可领(未满足条件)')} +
+ ) : task.points ? ( +
+ {t('{n} 积分', { n: task.points })} +
+ ) : null} +
+ + {task.done ? t('已领取') : task.blocked ? t('不可领') : t('待领取')} + +
+ ))} +
+ ) : null} + +

+ {t('新手福利是一次性任务:领完就没了,重复点不会重复加分。')} +

+ + ) : null} + + + + + + + + ) +} + +/* ─── ZCode 活动套餐 ───────────────────────── */ + +type PlanRow = ZcodePlan + +export function ZcodePlansSheet({ + account, + onClose, + onChanged, +}: { + account: { + id: string + name?: string + provider?: string + claimPlans?: Record | null + } | null + onClose: () => void + onChanged: () => void +}) { + const [plans, setPlans] = useState(null) + const [deployed, setDeployed] = useState(true) + const [error, setError] = useState('') + const [busy, setBusy] = useState('') + const [claiming, setClaiming] = useState('') + /** + * 本次打开抽屉里刚领到的套餐 id。 + * + * 不能只靠 `account.claimPlans`:那是打开抽屉那一刻的快照,领完之后父组件 + * 虽然会重拉签到中心,但这张抽屉拿到的 prop 不会跟着换 —— 按钮会一直停在 + * 「领取」上,用户会以为没领到。本地记一份,领成功立刻标上。 + */ + const [claimedNow, setClaimedNow] = useState>(new Set()) + const id = account?.id || '' + + /** 本地台账里「今天(北京时间)已领」的套餐 id */ + const claimedToday = (planId: string): boolean => { + if (claimedNow.has(planId)) return true + const ledger = account?.claimPlans + if (!ledger || typeof ledger !== 'object') return false + const at = Number(ledger[planId]) + return at > 0 && isToday(at) + } + + const load = async () => { + if (!id) return + setError('') + try { + const result = await previewZcodePlans(id) + setPlans(Array.isArray(result.plans) ? result.plans : []) + setDeployed(result.deployed !== false) + } catch (failure) { + setError(failure instanceof Error ? failure.message : String(failure)) + setPlans([]) + } + } + + useEffect(() => { + if (!id) return + setPlans(null) + setError('') + // 换账号要清掉上一个账号的「刚领过」,否则会标错行 + setClaimedNow(new Set()) + void load() + // 换账号才重查;load 每次渲染都会重建,不能进依赖 + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [id]) + + /** + * 领一份。 + * + * 先问上游「此刻要不要滑块」:不要就一次请求直接领;要就走验证码 —— + * 验证串与领取请求都在 `solveCaptcha` 的 `request` 回调里完成(与桌面端 + * `ui/zcode-claim.js` 同一顺序)。 + */ + const claim = async (plan: PlanRow) => { + if (!id || !plan.planId || claiming) return + setClaiming(plan.planId) + setError('') + try { + const config: ZcodeCaptchaConfig = await fetchZcodeCaptchaConfig(id).catch(() => ({ + enabled: false, + })) + const planId = plan.planId + + const submit = async (verifyParam: string) => { + const result = await claimZcodePlan(id, { + planId, + captchaVerifyParam: verifyParam, + captchaRegion: config.region || '', + }) + if (!result.ok) { + // 失败也是 200:`failureLabel` 是后端归一过的中文原因,直接显示 + throw new Error(result.failureLabel || result.message || t('领取失败')) + } + return result + } + + if (!config.enabled) { + await submit('') + } else { + await solveCaptcha( + { region: config.region || 'ga', prefix: config.prefix, sceneId: config.sceneId }, + async verifyParam => { + await submit(verifyParam) + return { captchaResult: true, bizResult: true } + }, + ) + } + + store.toast(t('已领取「{name}」', { name: plan.name || plan.planId })) + setClaimedNow(prev => new Set(prev).add(planId)) + onChanged() + await load() + } catch (failure) { + if (failure instanceof CaptchaCancelledError) { + store.toast(t('已取消领取')) + } else { + setError(failure instanceof Error ? failure.message : String(failure)) + } + } finally { + setClaiming('') + setBusy('') + } + } + + return ( + { + if (next) return + // 关抽屉时把还在等的滑块一起收掉,否则它会在后台超时 + cancelCaptcha() + onClose() + }} + > + + + {t('活动套餐')} + + + {account ? ( + <> +
+ +
+
{account.name || account.id}
+
+ {t('活动按自然日发新套餐,每天可领一次')} +
+
+ +
+ + {error ? : null} + {!plans && !error ?
: null} + + {plans && !plans.length && !error ? ( + + ) : null} + + {(plans || []).map(plan => { + // 领取状态在渲染时算,不用 load 时缓存的字段:刚领完那一刻 + // claimedNow 与台账要合并判断,缓存会把新领的那份又标回「可领」 + const claimed = claimedToday(plan.planId || '') + return ( +
+
+
+ {plan.name || plan.planId} +
+ {claimed ? {t('今日已领')} : null} +
+ + {plan.description ? ( +
+ {plan.description} +
+ ) : null} + + {plan.entitlements?.length ? ( +
+ {plan.entitlements.map((item, index) => ( +
+ {item.showName || '—'} + + {[item.grantUnits, item.unitType].filter(Boolean).join(' ')} + {item.period ? ` · ${periodLabel(item.period)}` : ''} + +
+ ))} +
+ ) : null} + +
+ {windowText(plan)} +
+ + +
+ ) + })} + +

+ {t('领取通常需要过一次滑块验证;活动套餐多为当天稍晚生效,领取后可在桌面端「账号」里看套餐明细。')} +

+ + ) : null} + + + + + + + ) +} + +/** 权益周期 → 文案(与后端 `period_label` 同一套措辞) */ +function periodLabel(period?: string): string { + const value = String(period || '').toLowerCase() + if (value === 'daily') return t('每日') + if (value === 'weekly') return t('每周') + if (value === 'monthly') return t('每月') + return '' +} + +/** 时间窗:开始 → 到期;只有一个时只说那一个 */ +function windowText(plan: ZcodePlan): string { + const start = stamp(plan.startsAt) + const end = stamp(plan.endsAt) + if (start && end) return `${start} → ${end}` + if (end) return t('到期 {time}', { time: end }) + if (start) return t('开始 {time}', { time: start }) + return '' +} + +/** unix 秒 → 本地时间串(后端给的是秒) */ +function stamp(seconds?: number): string { + const value = Number(seconds) + if (!Number.isFinite(value) || value <= 0) return '' + return new Date(value * 1000).toLocaleString() +} diff --git a/desktop-tauri/mobile/src/components/CommunitySheet.tsx b/desktop-tauri/mobile/src/components/CommunitySheet.tsx new file mode 100644 index 00000000..f16184e8 --- /dev/null +++ b/desktop-tauri/mobile/src/components/CommunitySheet.tsx @@ -0,0 +1,147 @@ +import { Sheet, SheetContent, SheetHeader, SheetBody, SheetTitle, SheetFooter } from '@ui' +import { Icon } from './Icon' +import { Badge, copyWithToast } from './ui' +import { COMMUNITY_LINKS, QQ_GROUPS, openExternal } from '../lib/community' +import { t } from '../i18n' + +/** + * 「社区与支持」面板(设置页底部那一行点开)。 + * + * ── 为什么是抽屉而不是二级页 ────────────────────────────────── + * 里面只有四张卡(两个 QQ 群 + Telegram + Discord),没有需要记住的状态、 + * 也没有下一步。做成二级页会多一层返回栏、多一条浏览器历史,用户点完就退。 + * + * ── QQ 卡片是「复制群号」,另两张是「打开链接」─────────────── + * 群号本身就是用户要拿走的东西(去 QQ 里搜),所以点一下复制;Telegram / + * Discord 是 https 链接,交给系统浏览器。两类卡片的副标题把这个差别写出来, + * 不用用户猜。 + * + * ── 为什么不再摆二维码 ──────────────────────────────────────── + * 与桌面端同一理由:二维码是打进包里的静态图,换群 / 换码得跟着发版。 + */ +export function CommunitySheet({ + open, + onClose, +}: { + open: boolean + onClose: () => void +}) { + return ( + { if (!next) onClose() }}> + + + {t('社区与支持')} + + +

+ {t('遇到问题或有使用心得,欢迎到群里交流。')} +

+ + {QQ_GROUPS.map((group, index) => ( + + ))} + + {COMMUNITY_LINKS.map(link => ( + + ))} + +

+ {t('在 QQ 里搜索群号即可加入。')} +

+
+ + + +
+
+ ) +} + +/** + * 品牌标(QQ / Telegram / Discord)。 + * + * 路径取自 simple-icons(与桌面端 `ui/icons.js` 的 brand 集同一来源、同一份 + * 路径),填充色写在 path 上 —— 这三个品牌有官方色,跟着文字色走反而不像。 + */ +function BrandGlyph({ id }: { id: 'qq' | 'telegram' | 'discord' }) { + const path = + id === 'qq' + ? 'M21.395 15.035a40 40 0 0 0-.803-2.264l-1.079-2.695c.001-.032.014-.562.014-.836C19.526 4.632 17.351 0 12 0S4.474 4.632 4.474 9.241c0 .274.013.804.014.836l-1.08 2.695a39 39 0 0 0-.802 2.264c-1.021 3.283-.69 4.643-.438 4.673.54.065 2.103-2.472 2.103-2.472 0 1.469.756 3.387 2.394 4.771-.612.188-1.363.479-1.845.835-.434.32-.379.646-.301.778.343.578 5.883.369 7.482.189 1.6.18 7.14.389 7.483-.189.078-.132.132-.458-.301-.778-.483-.356-1.233-.646-1.846-.836 1.637-1.384 2.393-3.302 2.393-4.771 0 0 1.563 2.537 2.103 2.472.251-.03.581-1.39-.438-4.673' + : id === 'telegram' + ? 'M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z' + : 'M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z' + return ( + + ) +} diff --git a/desktop-tauri/mobile/src/components/Icon.tsx b/desktop-tauri/mobile/src/components/Icon.tsx new file mode 100644 index 00000000..e31d3934 --- /dev/null +++ b/desktop-tauri/mobile/src/components/Icon.tsx @@ -0,0 +1,120 @@ +import type { SVGProps } from 'react' + +/** + * 手机端图标集。 + * + * 与 `ui/icons.js` 同风格(24×24 画布、currentColor、描边 1.8),但只挑手机端 + * 真正用到的那批:桌面端那套有 60 多个图标,移动端菜单只有 5+9 个入口, + * 全量搬过来只会让 bundle 白胖一圈。 + * + * 用法:``,尺寸由外层控制(默认 24)。 + */ + +const PATHS: Record = { + home: '', + chart: '', + users: + '', + grid: + '', + back: '', + chev: '', + plus: '', + search: '', + close: '', + check: '', + copy: + '', + refresh: '', + play: '', + bell: '', + settings: + '', + spark: + '', + checkin: + '', + model: + '', + proxy: + '', + key: '', + doc: '', + log: '', + request: + '', + task: '', + agent: + '', + wallet: + '', + shield: + '', + download: '', + trash: + '', + edit: '', + filter: '', + link: + '', + qr: + '', + moon: '', + info: '', + clock: '', + up: '', + bolt: '', + globe: '', + eye: + '', + heart: + '', + gift: + '', + alert: '', + logout: + '', + pause: '', + install: + '', + share: + '', + server: + '', +} + +export type IconName = keyof typeof PATHS + +type IconProps = SVGProps & { name: IconName } + +export function Icon({ name, ...props }: IconProps) { + const inner = PATHS[name] || PATHS.info + return ( + + ) +} + +/** 品牌标:与桌面端 icons.js 的 brand 同一造型(蓝底双向箭头) */ +export function BrandMark({ size = 32 }: { size?: number }) { + return ( + + ) +} diff --git a/desktop-tauri/mobile/src/components/MarkdownLite.tsx b/desktop-tauri/mobile/src/components/MarkdownLite.tsx new file mode 100644 index 00000000..c4116188 --- /dev/null +++ b/desktop-tauri/mobile/src/components/MarkdownLite.tsx @@ -0,0 +1,97 @@ +import type { ReactNode } from 'react' + +/** + * 极简 Markdown 渲染(只服务助手的回答气泡)。 + * + * ── 为什么需要它 ────────────────────────────────────────────── + * 模型输出天然带 Markdown:`**账号名**`、`` `模型ID` ``、`- 列表项`。直接当 + * 纯文本铺在气泡里,用户看到的是满屏星号和反引号 —— 尤其是账号名 / 模型名 + * 这类本来就该用等宽字显示的内容,反而被符号糊住了。 + * + * ── 为什么不上一个 Markdown 库 ──────────────────────────────── + * 手机上只需要三样:粗体、行内代码、列表。引 markdown-it / react-markdown + * 会给包体加几十 KB,还要处理 HTML 白名单(模型输出是不可信内容,直接 + * `dangerouslySetInnerHTML` 等于开一个 XSS 口子)。这里用 React 元素拼, + * 天然转义,没有注入面。 + * + * 代码块(```)只按普通段落显示 —— 手机上长代码块本来就该去桌面端看, + * 为它做一套横向滚动 + 语法高亮不值。 + */ +export function MarkdownLite({ text }: { text: string }) { + const lines = String(text || '').split('\n') + const blocks: ReactNode[] = [] + let list: string[] = [] + + const flushList = (key: string) => { + if (!list.length) return + blocks.push( +
    + {list.map((item, index) => ( +
  • + {inline(item)} +
  • + ))} +
, + ) + list = [] + } + + lines.forEach((line, index) => { + const trimmed = line.trim() + // 代码围栏:整行丢掉(内容按普通行渲染,见文件头) + if (trimmed.startsWith('```')) return + const bullet = /^[-*+]\s+(.*)$/.exec(trimmed) + const numbered = /^\d+[.)]\s+(.*)$/.exec(trimmed) + if (bullet) { + list.push(bullet[1]) + return + } + if (numbered) { + list.push(numbered[1]) + return + } + flushList(`list-${index}`) + if (!trimmed) { + blocks.push(
) + return + } + // 标题(## / ###)当成加粗段落:手机气泡里字号层级够多了 + const heading = /^#{1,6}\s+(.*)$/.exec(trimmed) + blocks.push( +
+ {inline(heading ? heading[1] : trimmed)} +
, + ) + }) + flushList('list-tail') + + return <>{blocks} +} + +/** 行内:**粗体** 与 `代码`,其余原样 */ +function inline(text: string): ReactNode[] { + const parts = text.split(/(\*\*[^*]+\*\*|`[^`]+`)/g) + return parts.filter(Boolean).map((part, index) => { + if (part.startsWith('**') && part.endsWith('**') && part.length > 4) { + return {part.slice(2, -2)} + } + if (part.startsWith('`') && part.endsWith('`') && part.length > 2) { + return ( + + {part.slice(1, -1)} + + ) + } + return {part} + }) +} diff --git a/desktop-tauri/mobile/src/components/NavBar.tsx b/desktop-tauri/mobile/src/components/NavBar.tsx new file mode 100644 index 00000000..f36e0d75 --- /dev/null +++ b/desktop-tauri/mobile/src/components/NavBar.tsx @@ -0,0 +1,30 @@ +import { Icon } from './Icon' +import { t } from '../i18n' + +/** + * 二级页顶部返回栏。 + * + * 结构与桌面端的移动版设置页返回栏同形(返回按钮 + 居中标题 + 右侧动作位), + * 但这里做成通用组件,所有二级页共用,避免每页各写一遍。 + * + * 右侧 `actions` 由页面自己给(刷新、新建…),没有就不占位。 + */ +export function NavBar({ + title, + onBack, + actions, +}: { + title: string + onBack: () => void + actions?: React.ReactNode +}) { + return ( +
+ + {title} + {actions} +
+ ) +} diff --git a/desktop-tauri/mobile/src/components/ProviderLogo.tsx b/desktop-tauri/mobile/src/components/ProviderLogo.tsx new file mode 100644 index 00000000..7f658b17 --- /dev/null +++ b/desktop-tauri/mobile/src/components/ProviderLogo.tsx @@ -0,0 +1,44 @@ +import { Icon } from './Icon' +import { providerIcon, providerLabel } from '../lib/providers' + +/** + * 提供商图标块:有图用图,没图用首字。 + * + * 尺寸档与 `.m-logo` 一致(34px / `lg` 40px),这里只负责内容。 + */ +export function ProviderLogo({ + id, + label, + size = 'default', +}: { + id?: string + label?: string + size?: 'default' | 'lg' +}) { + const icon = providerIcon(id) + const text = providerLabel(id, label) + return ( + + {icon ? ( + + ) : ( + {text.slice(0, 1)} + )} + + ) +} + +/** 通用图标块:给「不是提供商」的入口用(签到、Key、代理…) */ +export function GlyphLogo({ + name, + size = 'default', +}: { + name: Parameters[0]['name'] + size?: 'default' | 'lg' +}) { + return ( + + + + ) +} diff --git a/desktop-tauri/mobile/src/components/PullToRefresh.tsx b/desktop-tauri/mobile/src/components/PullToRefresh.tsx new file mode 100644 index 00000000..7d7c60ff --- /dev/null +++ b/desktop-tauri/mobile/src/components/PullToRefresh.tsx @@ -0,0 +1,223 @@ +/** + * 下拉刷新基础设施。 + * + * 只提供 hook + 指示器,不负责接线:调用方把 `bindRef` 绑到页面根节点、 + * 把 `` 放进页面即可。 + * + * 定位前提:`PullIndicator` 用 `position: absolute` 贴住滚动容器顶部, + * 它的父级必须是定位上下文。mobile.css 末尾已给 `.m-page` 追加 + * `position: relative`,所以把指示器放在 `.m-page` 内即可。 + */ +import { useEffect, useRef, useState } from 'react' +import type { ReactElement, RefObject } from 'react' +import { t } from '../i18n' +import { Icon } from './Icon' + +type PullToRefreshOptions = { + /** 下拉超过这个距离(px)才触发刷新,默认 64 */ + threshold?: number + /** 刷新回调;返回 Promise 时组件会等它结束再收起 */ + onRefresh: () => Promise | unknown + /** 滚动容器。不传时自动找最近的 '.m-scroll' 或 '.m-body' 祖先 */ + scrollRef?: RefObject +} + +type PullToRefreshResult = { + /** 绑到页面根节点上(自动找滚动容器时用) */ + bindRef: RefObject + /** 当前下拉距离(px),给指示器用 */ + pull: number + /** 是否正在刷新 */ + refreshing: boolean +} + +/** 阻尼系数:手指移动 2px,指示器只长 1px */ +const DAMPING = 0.5 +/** 指示器最多展开到阈值的 1.8 倍,避免越拉越长 */ +const MAX_RATIO = 1.8 + +/** + * 找真正滚动的容器。 + * + * 本应用有两种滚动形态(见 mobile.css): + * 1. 整页滚:滚动发生在外层 `.m-scroll`; + * 2. 固定头 + 独立列表:`.m-scroll` 被 `overflow: hidden` 锁住,真正滚的是 + * 页面里的 `.m-body`。 + * + * 所以第 2 种必须优先判断:页面根节点是 `.m-page.m-fixed` 时,祖先 + * `.m-scroll` 虽然存在却不滚动,直接返回内部的 `.m-body`,绝不能落到 + * `.m-scroll` 上。 + */ +function resolveScrollContainer(bind: HTMLElement | null): HTMLElement | null { + if (!bind) return null + const fixedPage = bind.classList.contains('m-fixed') + ? bind + : bind.closest('.m-page.m-fixed') + if (fixedPage) { + const body = fixedPage.querySelector('.m-body') + if (body) return body + } + const scroller = bind.closest('.m-scroll') + if (scroller) return scroller + return bind.querySelector('.m-body') +} + +export function usePullToRefresh(options: PullToRefreshOptions): PullToRefreshResult { + const { threshold = 64, onRefresh, scrollRef } = options + const bindRef = useRef(null) + const [pull, setPull] = useState(0) + const [refreshing, setRefreshing] = useState(false) + + // 手势过程中的即时值放 ref:原生监听里读 state 会拿到旧闭包 + const startYRef = useRef(0) + const activeRef = useRef(false) + const pullRef = useRef(0) + const refreshingRef = useRef(false) + + // onRefresh 通常写成内联箭头函数,每次 render 都是新引用;放进 ref, + // 避免它变化时反复解绑 / 重绑触摸监听 + const onRefreshRef = useRef(onRefresh) + useEffect(() => { + onRefreshRef.current = onRefresh + }, [onRefresh]) + + useEffect(() => { + const el = scrollRef?.current ?? resolveScrollContainer(bindRef.current) + // 找不到滚动容器就静默不绑定,不报错 + if (!el) return + let alive = true + + const updatePull = (value: number) => { + pullRef.current = value + if (alive) setPull(value) + } + + const onTouchStart = (event: TouchEvent) => { + // 刷新中不接新手势 + if (refreshingRef.current) { + activeRef.current = false + return + } + const touch = event.touches[0] + if (!touch) { + activeRef.current = false + return + } + // 只有停在顶部才可能是下拉刷新;列表中间滑动交给原生滚动 + if (el.scrollTop > 0) { + activeRef.current = false + return + } + startYRef.current = touch.clientY + activeRef.current = true + } + + const onTouchMove = (event: TouchEvent) => { + if (!activeRef.current || refreshingRef.current) return + const touch = event.touches[0] + if (!touch) return + const deltaY = touch.clientY - startYRef.current + // 向上滑不归下拉刷新管,顺手把已经拉出来的距离收回去 + if (deltaY <= 0) { + updatePull(0) + return + } + // 起始时在顶部、滑动过程中又被滚下去了(例如惯性),本次手势作废 + if (el.scrollTop > 0) { + activeRef.current = false + updatePull(0) + return + } + // 只有确定是下拉才拦原生 overscroll;否则会吃掉正常滚动 + event.preventDefault() + updatePull(Math.min(deltaY * DAMPING, threshold * MAX_RATIO)) + } + + const onTouchEnd = () => { + if (!activeRef.current) return + activeRef.current = false + if (refreshingRef.current) return + // 没到阈值:直接收起,不触发刷新 + if (pullRef.current < threshold) { + updatePull(0) + return + } + refreshingRef.current = true + setRefreshing(true) + // 停在触发位置,让指示器不跟着手指回弹 + updatePull(threshold) + void (async () => { + try { + await onRefreshRef.current() + } catch { + // 失败提示由调用方负责,这里只保证指示器一定收起 + } finally { + refreshingRef.current = false + if (!alive) return + setRefreshing(false) + updatePull(0) + } + })() + } + + // 手势被系统打断(来电、通知中心等):收起,但不触发刷新 + const onTouchCancel = () => { + activeRef.current = false + if (!refreshingRef.current) updatePull(0) + } + + el.addEventListener('touchstart', onTouchStart, { passive: false }) + el.addEventListener('touchmove', onTouchMove, { passive: false }) + el.addEventListener('touchend', onTouchEnd, { passive: false }) + el.addEventListener('touchcancel', onTouchCancel, { passive: false }) + + return () => { + alive = false + el.removeEventListener('touchstart', onTouchStart) + el.removeEventListener('touchmove', onTouchMove) + el.removeEventListener('touchend', onTouchEnd) + el.removeEventListener('touchcancel', onTouchCancel) + // 容器切换 / 卸载时收起指示器 + pullRef.current = 0 + setPull(0) + } + // 不把 bindRef.current 放进依赖:ref 在 commit 阶段就挂好了(早于 passive + // effect),effect 首次运行时拿得到节点;若把它写进依赖,首次 setPull 触发的 + // 重渲染会让依赖从 null 变成节点,effect 重建并把指示器复位 —— 下拉手势会被 + // 打断一次。页面根节点在组件生命周期内不变,无需重绑。 + }, [scrollRef, threshold]) + + return { bindRef, pull, refreshing } +} + +/** 下拉 / 松开 / 刷新中的提示条,绝对定位在滚动容器顶部 */ +export function PullIndicator(props: { + pull: number + refreshing: boolean + threshold?: number +}): ReactElement | null { + const { pull, refreshing, threshold = 64 } = props + if (pull === 0 && !refreshing) return null + + const ready = pull >= threshold + const text = refreshing ? t('正在刷新…') : ready ? t('松开刷新') : t('下拉刷新') + // 未触发刷新时让图标跟着下拉角度转,到位后交给 .m-ptr-spin 匀速转 + const tilt = refreshing + ? undefined + : { transform: `rotate(${Math.min(pull / threshold, 1) * 180}deg)` } + + return ( +
+
+ + {text} +
+
+ ) +} diff --git a/desktop-tauri/mobile/src/components/PwaNotice.tsx b/desktop-tauri/mobile/src/components/PwaNotice.tsx new file mode 100644 index 00000000..e20b50ae --- /dev/null +++ b/desktop-tauri/mobile/src/components/PwaNotice.tsx @@ -0,0 +1,61 @@ +import { useState } from 'react' +import { Icon } from './Icon' +import { usePwa } from '../lib/usePwa' +import { useStore } from '../state/store' +import { applyUpdate } from '../lib/pwa' +import { t } from '../i18n' + +/** + * PWA 状态条。 + * + * 两件事,位置不同: + * · **离线**:常驻顶部细条。它解释的是「为什么页面上的数字不动了」, + * 放在内容上方、一眼能看到,且不抢操作。 + * · **有新版本**:底部浮条。它需要一个明确的动作(重启), + * 放在拇指够得到的地方;同时给「稍后」,不打断当前操作。 + * + * 两者都是 `.m-app` 的 flex 子项 / 浮层,不遮挡导航:离线条把内容整体 + * 往下推,更新条浮在标签栏之上。 + * + * 设置页不弹更新浮条:那一页的「应用状态」卡片常驻同样的按钮, + * 两处一起出现只是重复。 + */ +export function PwaNotice() { + const pwa = usePwa() + const { route } = useStore() + const [busy, setBusy] = useState(false) + // 收起只对本次会话有效:新 SW 仍在等待,下次打开会再提示一次 + const [snoozed, setSnoozed] = useState(false) + const showUpdate = pwa.updateReady && !snoozed && route.screen !== 'settings' + + return ( + <> + {pwa.offline ? ( +
+ + {t('已离线 · 显示的是缓存内容')} +
+ ) : null} + + {showUpdate ? ( +
+ {t('有新版本,重启后生效')} + + +
+ ) : null} + + ) +} diff --git a/desktop-tauri/mobile/src/components/Shell.tsx b/desktop-tauri/mobile/src/components/Shell.tsx new file mode 100644 index 00000000..c9e15169 --- /dev/null +++ b/desktop-tauri/mobile/src/components/Shell.tsx @@ -0,0 +1,238 @@ +import { useEffect, useState } from 'react' +import { Icon, type IconName } from './Icon' +import { useStore, store } from '../state/store' +import { LoginPage } from '../pages/LoginPage' +import { isRateLimited } from '../lib/format' +import { t } from '../i18n' + +/** + * 应用外壳:页面容器 + 底部标签栏 + 轻提示 + 凭证缺失层。 + * + * ── 标签栏的显示规则 ───────────────────────────────────────── + * 一级页显示,二级页隐藏(把高度让给内容)。助手页要撑满视口,所以它的 + * 滚动容器自己管(见 pages/AssistantPage 的 as-mode 处理)。 + */ + +export type TabDef = { id: string; label: string; icon: IconName } + +const TABS: TabDef[] = [ + { id: 'home', label: t('首页'), icon: 'home' }, + { id: 'report', label: t('报表'), icon: 'chart' }, + { id: 'accounts', label: t('账号'), icon: 'users' }, + { id: 'assistant', label: t('助手'), icon: 'spark' }, + { id: 'more', label: t('更多'), icon: 'grid' }, +] + +/** 一级页集合:这些 screen 显示底部标签栏 */ +export const PRIMARY_SCREENS = new Set(TABS.map(tab => tab.id)) + +/** + * 软键盘是否弹起。 + * + * ── 为什么需要这个判断 ──────────────────────────────────────── + * Android 的 `adjustResize` 会让 WebView 随键盘一起缩小,标签栏是 flex 布局 + * 的最后一个子项,于是它被顶到键盘正上方 —— 用户看到的是「输入框 + 一条 + * 导航栏 + 键盘」,而他要的是「输入框 + 键盘」。聊天类应用的通行做法就是 + * 键盘弹起时把标签栏收起来(WhatsApp / Telegram 都是这样)。 + * + * ── 怎么判 ──────────────────────────────────────────────────── + * `visualViewport` 的高度比 `innerHeight` 小出一大截(> 120px)就是键盘在 + * 占位。阈值取 120 而不是 0:移动浏览器的地址栏收起 / 展开也会让两者差几十 + * 像素,那是正常滚动,不该把标签栏闪一下。 + * + * 没有 `visualViewport` 的老 WebView 直接返回 false —— 标签栏常驻,行为与 + * 改动前一致,不会因为拿不到 API 就少一条导航。 + */ +export function useKeyboardOpen(): boolean { + const [open, setOpen] = useState(false) + + useEffect(() => { + const viewport = window.visualViewport + if (!viewport) return + const threshold = 120 + const check = () => { + const covered = window.innerHeight - viewport.height - viewport.offsetTop + setOpen(covered > threshold) + } + viewport.addEventListener('resize', check) + viewport.addEventListener('scroll', check) + check() + return () => { + viewport.removeEventListener('resize', check) + viewport.removeEventListener('scroll', check) + } + }, []) + + return open +} + +export function TabBar() { + const state = useStore() + const keyboardOpen = useKeyboardOpen() + const active = state.stack.length === 0 ? state.route.screen : '' + if (!PRIMARY_SCREENS.has(state.route.screen) || state.stack.length > 0) return null + // 键盘弹起时让位:那一条高度留给输入框,而不是导航 + if (keyboardOpen) return null + const limited = state.session?.accounts?.accounts?.filter(a => a.enabled && isRateLimited(a)).length || 0 + return ( + + ) +} + +/** 轻提示 */ +export function Toast() { + const state = useStore() + const toast = state.toast + return ( +
+ {toast?.text || ''} +
+ ) +} + +/** + * 凭证缺失层。 + * + * 两种来路,文案与动作都不同: + * · `login` —— 管理员已注册、会话过期:APK 弹应用内登录表单; + * 浏览器(PWA)跳服务端登录页,带回跳。 + * · `key` —— 兼容模式(没注册管理员)没配 Key:就地输入,存 localStorage + * + * 为什么 APK 不能跳 `/login`:那个页面由网关出,而 APK 的界面在包里 —— + * 跳过去等于离开应用、进一个风格不一致的网页,而且跨源之后服务端种的 + * cookie 也带不回来(见 lib/servers.ts 的说明)。 + */ +export function AuthGate() { + const state = useStore() + const [key, setKey] = useState('') + const [busy, setBusy] = useState(false) + if (!state.authLost) return null + + /** + * APK 里登录页是整屏的,用户被登出后看不到「设置 → 服务器」。 + * 登录页给了「换一台服务器」入口,它把用户压进服务器页 —— 这一屏必须让开, + * 否则登录页盖住整个界面,列表点不到。authLost 故意不清:用户取消返回后 + * 登录页会重新盖上来,换到新服务器也会按新地址重新弹。 + */ + if (state.packaged && state.route.screen === 'servers') return null + + // 打包形态走整屏的应用内登录页(login / key 两种模式它都覆盖) + if (state.packaged) return + + const submit = async () => { + const value = key.trim() + if (!value) return + setBusy(true) + const { storeApiKey } = await import('../api/client') + storeApiKey(value) + store.setAuthLost(null) + await store.refresh(true) + setBusy(false) + } + + return ( +
+
+

+ {state.authLost === 'login' ? t('需要重新登录') : t('需要网关 API Key')} +

+

+ {state.authLost === 'login' + ? t('会话已过期。重新登录后会自动回到手机端。') + : t('这个面板没有管理员,用一把已启用的 API Key 访问(在桌面端「网关 Key」页创建)。')} +

+ {state.authLost === 'key' ? ( + setKey(event.target.value)} + placeholder='sk-…' + autoComplete='off' + style={{ + width: '100%', + boxSizing: 'border-box', + marginTop: 14, + height: 42, + padding: '0 12px', + borderRadius: 11, + border: '1px solid var(--m-line-strong)', + background: 'var(--m-surface-2)', + color: 'var(--m-text)', + fontFamily: 'var(--m-mono)', + // 16px 是 iOS 的阈值:小于它聚焦时 Safari 会自动放大整页 + fontSize: 16, + }} + /> + ) : null} +
+ {state.authLost === 'login' ? ( + + ) : ( + + )} +
+
+
+ ) +} + +/** + * 主题同步:把 store 的主题写到 。 + * + * 单独一个组件而不是在 store 构造时做,是为了让 React 的首帧与 DOM 属性 + * 一致 —— 首帧闪烁(先浅色再切深色)在手机上是可见的。 + */ +export function ThemeSync() { + const { theme } = useStore() + useEffect(() => { + document.documentElement.dataset.theme = theme + }, [theme]) + return null +} diff --git a/desktop-tauri/mobile/src/components/SystemInfoSheet.tsx b/desktop-tauri/mobile/src/components/SystemInfoSheet.tsx new file mode 100644 index 00000000..4229a2f0 --- /dev/null +++ b/desktop-tauri/mobile/src/components/SystemInfoSheet.tsx @@ -0,0 +1,330 @@ +import { useEffect, useState } from 'react' +import { Sheet, SheetContent, SheetHeader, SheetBody, SheetTitle, SheetFooter } from '@ui' +import { apiCall } from '../api/client' +import { APP_VERSION } from '../lib/appUpdate' +import { isPackaged, nativePlatform } from '../lib/servers' +import { Badge, copyWithToast } from './ui' +import { Icon } from './Icon' +import { t } from '../i18n' + +/** + * 「版本信息」抽屉(手机端设置页底部那一行点开)。 + * + * ── 为什么手机端也要这一块 ────────────────────────────────── + * 手机端原来只在设置页底部显示**自己的** `v{APP_VERSION}`,用户连的是哪台 + * 网关、那台网关是什么形态 / 什么版本,一概看不到。而手机端出问题时, + * 第一个要问的恰恰是「网关那边是哪一版、跑在什么环境」—— 让用户再跑到 + * 电脑前翻一遍设置页不现实。 + * + * ── 两段版本号分开显示 ────────────────────────────────────── + * · **本机**:APK 的 versionName(构建期注入)/ PWA 的界面版本。它来自 + * 安装包,与网关无关; + * · **网关**:`GET /api/system-info` 里那份(版本 / 形态 / 系统 / 路径)。 + * 两者可能不同(手机装 3.1.0、网关还是 3.0.9),混成一个「版本」会让人 + * 拿着手机版本去比对网关的更新日志。 + * + * ── 复用桌面端的接口与脱敏口径 ────────────────────────────── + * 同一个 `/api/system-info`,同样的 `?hideUsername=1`(后端脱敏,见 + * `api::system_info` 的模块头)。手机端不单独造一份后端契约。 + */ + +type GatewayInfo = { + app?: { + version?: string | null + channel?: string | null + schemaVersion?: number | null + dbSchemaVersion?: number | null + } + runtime?: { + deployment?: string | null + os?: string | null + osVersion?: string | null + arch?: string | null + webviewVersion?: string | null + uptimeSeconds?: number | null + debugBuild?: boolean | null + } + deployment?: { + host?: string | null + port?: number | null + panelPort?: number | null + configDir?: string | null + dbFile?: string | null + dbBytes?: number | null + logFile?: string | null + exePath?: string | null + } + security?: { + adminRegistered?: boolean | null + apiKeyCount?: number | null + v1FailClosed?: boolean | null + } + stats?: { + accounts?: number | null + enabledAccounts?: number | null + models?: number | null + inFlight?: number | null + } +} + +/** 运行形态 → 展示文案(与桌面端 system-info-pane 同一份取值表) */ +const DEPLOYMENT_LABEL: Record = { + desktop: t('桌面端'), + 'windows-service': t('Windows 服务'), + docker: t('Docker 容器'), + systemd: t('systemd 服务'), + foreground: t('前台运行'), +} + +const OS_LABEL: Record = { + windows: 'Windows', + macos: 'macOS', + linux: 'Linux', +} + +function text(value: unknown): string { + if (value === null || value === undefined) return '—' + const result = String(value).trim() + return result || '—' +} + +/** + * 系统名 + 版本拼成一句。 + * + * Windows 的 `ProductName` 自带 "Windows"、Linux 的 `PRETTY_NAME` 自带发行版名, + * 直接加前缀会变成「Windows Windows 11 Pro」。与桌面端 system-info-pane.tsx + * 的同名函数同一口径(两处各写一份:手机端不 import 桌面端源码)。 + */ +function formatOs(os: string | null | undefined, version: string | null | undefined): string { + const name = OS_LABEL[String(os)] || text(os) + const detail = text(version) + if (detail === '—') return name + if (name !== '—' && detail.toLowerCase().startsWith(name.toLowerCase())) return detail + return `${name} ${detail}` +} + +function formatBytes(bytes: number | null | undefined): string { + const value = Number(bytes) + if (!Number.isFinite(value) || value <= 0) return '0 B' + if (value < 1024) return `${value} B` + const kb = value / 1024 + if (kb < 1024) return `${kb.toFixed(1)} KB` + const mb = kb / 1024 + if (mb < 1024) return `${mb.toFixed(1)} MB` + return `${(mb / 1024).toFixed(2)} GB` +} + +function formatUptime(seconds: number | null | undefined): string { + const total = Number(seconds) + if (!Number.isFinite(total) || total <= 0) return '—' + const days = Math.floor(total / 86400) + const hours = Math.floor((total % 86400) / 3600) + const minutes = Math.floor((total % 3600) / 60) + if (days > 0) return t('{d} 天 {h} 小时', { d: days, h: hours }) + if (hours > 0) return t('{h} 小时 {m} 分钟', { h: hours, m: minutes }) + return t('{m} 分钟', { m: minutes }) +} + +/** 一行「键 —— 值」 */ +function Row({ label, value, mono }: { label: string; value: string; mono?: boolean }) { + return ( +
+
+
{label}
+
+ {value} +
+
+
+ ) +} + +export function SystemInfoSheet({ open, onClose }: { open: boolean; onClose: () => void }) { + const [info, setInfo] = useState(null) + const [error, setError] = useState('') + const [loading, setLoading] = useState(false) + const [hideUsername, setHideUsername] = useState(false) + + const load = async (hide: boolean) => { + setLoading(true) + try { + const result = await apiCall( + `/api/system-info${hide ? '?hideUsername=1' : ''}`, + ) + setInfo(result || null) + setError('') + } catch (failure) { + setError(failure instanceof Error ? failure.message : String(failure)) + } finally { + setLoading(false) + } + } + + // 每次打开都重拉:运行时长、在途请求这些读数会随时间变 + useEffect(() => { + if (open) void load(hideUsername) + // hideUsername 变化由下面的开关自己触发,不放进依赖(否则拨开关会双拉) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [open]) + + const toggleHide = () => { + const next = !hideUsername + setHideUsername(next) + void load(next) + } + + const app = info?.app || {} + const runtime = info?.runtime || {} + const deployment = info?.deployment || {} + const security = info?.security || {} + const stats = info?.stats || {} + const deploymentLabel = DEPLOYMENT_LABEL[String(runtime.deployment)] || text(runtime.deployment) + const osFull = formatOs(runtime.os, runtime.osVersion) + // 原生壳里「本机版本」是包版本;PWA / 浏览器里是构建期注入的界面版本 + const platform = nativePlatform() + const localLabel = isPackaged() ? t('本机应用版本') : t('本机界面版本') + const formLabel = + platform === 'android' ? t('Android 应用(APK)') : platform === 'ios' ? t('iOS 应用') : t('浏览器 / PWA') + + const copyAll = () => { + const lines = [ + `[${t('本机')}]`, + `${localLabel}: ${APP_VERSION}`, + `platform: ${platform === 'android' ? 'android-apk' : platform === 'ios' ? 'ios-ipa' : 'browser'}`, + '', + `[${t('网关')}]`, + `${t('版本')}: ${text(app.version)}`, + `${t('更新通道')}: ${app.channel === 'beta' ? t('beta 版') : t('正式版')}`, + `${t('运行形态')}: ${deploymentLabel}`, + `${t('操作系统')}: ${osFull} (${text(runtime.arch)})`, + `${t('监听地址')}: ${text(deployment.host)}:${text(deployment.port)}`, + `${t('已运行')}: ${formatUptime(runtime.uptimeSeconds)}`, + `${t('数据目录')}: ${text(deployment.configDir)}`, + `${t('日志文件')}: ${text(deployment.logFile)}`, + `${t('账号')}: ${text(stats.enabledAccounts)} / ${text(stats.accounts)}`, + `${t('模型')}: ${text(stats.models)}`, + `${t('网关 Key 数量')}: ${text(security.apiKeyCount)}`, + ] + void copyWithToast(lines.join('\n'), t('版本信息')) + } + + return ( + { if (!next) onClose() }}> + + + {t('版本信息')} + + + {/* 本机:与网关无关的那一半 */} +
+ {t('本机')} +
+
+ + +
+ + {/* 网关:连的那台服务器的读数 */} +
+ {t('网关')} +
+ {error ? ( +
+ {t('读取网关信息失败:{message}', { message: error })} +
+ ) : ( +
+ + + + + + + + + +
+ )} + + {/* 路径:只在拿得到时显示(读不到就不占版面) */} + {deployment.configDir || deployment.logFile ? ( + <> +
+ {t('位置')} +
+
+ {deployment.configDir ? ( + + ) : null} + {deployment.dbFile ? ( + + ) : null} + {deployment.logFile ? ( + + ) : null} +
+ + ) : null} + + {/* 隐藏用户名:与桌面端同一个后端参数 */} + + + {loading ? ( +
+ {t('读取中…')} +
+ ) : null} +
+ + + +
+
+ ) +} diff --git a/desktop-tauri/mobile/src/components/ui.tsx b/desktop-tauri/mobile/src/components/ui.tsx new file mode 100644 index 00000000..20c70eef --- /dev/null +++ b/desktop-tauri/mobile/src/components/ui.tsx @@ -0,0 +1,210 @@ +import { Icon, type IconName } from './Icon' +import { t } from '../i18n' +import { store } from '../state/store' + +/** 通用页头(一级页):大标题 + 副标题 + 右侧动作 */ +export function PageHead({ + title, + subtitle, + actions, +}: { + title: string + subtitle?: string + actions?: React.ReactNode +}) { + return ( +
+
+

{title}

+ {subtitle ?

{subtitle}

: null} +
+ {actions ?
{actions}
: null} +
+ ) +} + +/** + * 二级页的页内说明条。 + * + * 二级页的标题已经由顶部返回栏(NavBar)承载,页内再写一遍大标题就是重复。 + * 但副标题(统计读数 / 时间范围)有信息量,保留成一行小字。右侧动作放这里, + * 因为它属于页面内容而不是导航。 + */ +export function PageIntro({ + subtitle, + actions, +}: { + subtitle?: string + actions?: React.ReactNode +}) { + if (!subtitle && !actions) return null + return ( +
+ {subtitle} + {actions ?
{actions}
: null} +
+ ) +} + +/** 圆形图标按钮(页头 / 返回栏右侧都用它) */ +export function IconButton({ + name, + label, + onClick, + primary, +}: { + name: IconName + label: string + onClick?: () => void + primary?: boolean +}) { + return ( + + ) +} + +/** 区块标题:左侧标题 + 右侧可选动作 */ +export function SectionHead({ + title, + action, +}: { + title: string + action?: { label: string; onClick: () => void } +}) { + return ( +
+

{title}

+ {action ? ( + + ) : null} +
+ ) +} + +/** 徽章:语义色与桌面端一致(绿=正常、黄=告警、红=失效、灰=中性、蓝=待办) */ +export function Badge({ + tone, + dot, + children, +}: { + tone: 'ok' | 'warn' | 'bad' | 'mute' | 'brand' + dot?: boolean + children: React.ReactNode +}) { + return ( + + {dot ? : null} + {children} + + ) +} + +/** 空态 */ +export function Empty({ text }: { text: string }) { + return
{text}
+} + +/** 错误条(页内,不弹窗) */ +export function ErrorNote({ text }: { text: string }) { + return
{text}
+} + +/** 加载骨架:给列表页首屏用,避免「空白 → 突然出现内容」的跳动 */ +export function SkeletonList({ rows = 4, height = 64 }: { rows?: number; height?: number }) { + return ( +
+ {Array.from({ length: rows }).map((_, index) => ( +
+ ))} +
+ ) +} + +/** 复制到剪贴板(失败时退回 execCommand,老 WebView 也能用) */ +export async function copyText(text: string): Promise { + try { + await navigator.clipboard.writeText(text) + } catch { + const area = document.createElement('textarea') + area.value = text + area.style.position = 'fixed' + area.style.opacity = '0' + document.body.appendChild(area) + area.select() + try { + document.execCommand('copy') + } catch { + /* 复制失败:提示由调用方给 */ + } + area.remove() + } +} + +/** 复制 + 提示的常用组合 */ +export async function copyWithToast(text: string, label?: string): Promise { + await copyText(text) + store.toast(t('已复制{label}', { label: label ? `:${label}` : '' })) +} + +/** 数值卡片(报表 / 首页共用) */ +export function StatCard({ + label, + value, + sub, + tone, +}: { + label: string + value: string + sub?: string + tone?: 'up' | 'ok' +}) { + return ( +
+
{label}
+
+ {value} +
+ {sub ? ( +
+ {sub} +
+ ) : null} +
+ ) +} diff --git a/desktop-tauri/mobile/src/i18n.ts b/desktop-tauri/mobile/src/i18n.ts new file mode 100644 index 00000000..090bfd32 --- /dev/null +++ b/desktop-tauri/mobile/src/i18n.ts @@ -0,0 +1,82 @@ +/** + * 移动端 i18n 垫片。 + * + * 与 ui-islands/src/i18n.ts 同一套机制:词典由 `ui/i18n.js` 在页面 head 阶段 + * 注入到 `window.wbI18nDict`,本模块只查表、不打包词典。中文即键,缺译回落 + * 简体原文。手机端与桌面端共用同一份词典,重叠文案(账号、签到、模型…) + * 自动就有译文,不必另起一套 ARB。 + */ + +type TParams = Record + +type WbI18nApi = { + t(key: string, params?: TParams): string + locale(): string + rawLocale(): string + setLocale(value: string): void + sweep(root?: ParentNode): void + LOCALES: ReadonlyArray<{ code: string; label: string; nameKey: string }> +} + +declare global { + interface Window { + wbI18n?: WbI18nApi + wbI18nDict?: Record + __WB_I18N_DOMAINS?: string[] + } +} + +/** 取键的译文;params 把 {name} 形参替换成值。缺译 / 无运行时都回落键本身 */ +export function t(key: string, params?: TParams): string { + const api = typeof window !== 'undefined' ? window.wbI18n : undefined + if (api && typeof api.t === 'function') return api.t(key, params) + // 运行时缺失时自己查一次词典:页面把词典脚本排在本脚本之前加载, + // 极端时序下(i18n.js 未就位)也能出正确译文 + const dict = typeof window !== 'undefined' ? window.wbI18nDict : undefined + const hit = dict?.[key] + let text = typeof hit === 'string' && hit ? hit : key + if (params) { + for (const [name, value] of Object.entries(params)) { + text = text.split(`{${name}}`).join(String(value)) + } + } + return text +} + +/** 当前界面语言代码(`zh-Hans` / `en` / …) */ +export function locale(): string { + const api = typeof window !== 'undefined' ? window.wbI18n : undefined + if (api && typeof api.locale === 'function') return api.locale() + return 'zh-Hans' +} + +export type LocaleOption = { code: string; label: string; nameKey: string } + +/** 可切换的语言清单(语言自名,不翻译) */ +export function localeOptions(): readonly LocaleOption[] { + const api = typeof window !== 'undefined' ? window.wbI18n : undefined + if (api && Array.isArray(api.LOCALES)) return api.LOCALES + return [ + { code: 'zh-Hans', label: '简体中文', nameKey: '简体中文' }, + { code: 'zh-Hant', label: '繁體中文', nameKey: '繁体中文' }, + { code: 'en', label: 'English', nameKey: '英语' }, + { code: 'ja', label: '日本語', nameKey: '日语' }, + { code: 'ko', label: '한국어', nameKey: '韩语' }, + { code: 'pt-BR', label: 'Português (BR)', nameKey: '葡萄牙语(巴西)' }, + ] +} + +/** 切语言 = 整页刷新(与桌面端同一策略,见 ui/i18n.js 的说明) */ +export function setLocale(code: string): void { + const api = typeof window !== 'undefined' ? window.wbI18n : undefined + if (api && typeof api.setLocale === 'function') { + api.setLocale(code) + return + } + try { + localStorage.setItem('workbuddy-desktop-locale', code) + } catch { + /* 存储被禁:刷新后回到自动判定 */ + } + window.location.reload() +} diff --git a/desktop-tauri/mobile/src/lib/aliyunCaptcha.ts b/desktop-tauri/mobile/src/lib/aliyunCaptcha.ts new file mode 100644 index 00000000..12478527 --- /dev/null +++ b/desktop-tauri/mobile/src/lib/aliyunCaptcha.ts @@ -0,0 +1,449 @@ +/** + * 阿里云无痕验证(滑块)求解器 —— 手机端移植版。 + * + * ── 为什么需要它 ────────────────────────────────────────────── + * ZCode 的「领套餐」要过阿里云风控:上游的领取接口要 + * `X-Aliyun-Captcha-Verify-Param`,要不要由它下发的风控配置说了算 + * (`enabled: false` 的那一刻**不该**弹滑块)。桌面端这份逻辑在 + * `ui/aliyun-captcha.js`(从官方客户端逐条移植),手机端是独立工程, + * 不能直接引用那个经典脚本,所以按同一份流程重写一遍。 + * + * ── 与桌面版的差异(只有三处,都是工程形态带来的)────────── + * 1. 文案走手机端的 `t()`(那边是 `wbI18n.t`); + * 2. 去掉静默铸串(`mintTraceless`)—— 那是活动套餐**转发**通道的令牌 + * 来源,手机端不跑那条后台链路; + * 3. 去掉 AutoClaw 登录那一半(手机端没有 OAuth 添加账号的入口)。 + * 流程本身(容器、预热、代际号、超时、取消)逐条照搬:这些坑都在细节里, + * 抄一半会得到「滑块弹出来但一直转圈」这种最难查的形态。 + * + * ── 一次只允许一个验证码流程 ────────────────────────────────── + * 模块级状态:同时发起两轮会互相踩。界面上的领取按钮互斥,这里再用代际号 + * 让后发起的那一轮作废前一轮(迟到的回调发现代际不符就丢弃)。 + */ + +import { t } from '../i18n' + +const SCRIPT_URL = 'https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js' +const SCRIPT_ID = 'aliyun-captcha-sdk' +const ELEMENT_ID = 'aliyun-captcha-element' +const BUTTON_ID = 'aliyun-captcha-trigger' + +const SCRIPT_LOAD_TIMEOUT_MS = 40_000 +const INIT_TIMEOUT_MS = 40_000 +const VERIFY_TIMEOUT_MS = 120_000 +/** 初始化后至少等 2.1 秒再点按钮(官方客户端实测:SDK 预热没完成时点击无效) */ +const MINIMUM_WARMUP_MS = 2_100 +/** 初始化结果最多复用 19 分钟(超过则重建,避免实例内部状态过期) */ +const INITIALIZATION_MAX_AGE_MS = 19 * 60_000 + +export type CaptchaConfig = { + region?: string + prefix?: string + sceneId?: string +} + +/** 验证码流程失败(带一句人话,界面直接展示) */ +export class CaptchaError extends Error { + constructor(message: string) { + super(message) + this.name = 'CaptchaError' + } +} + +/** 用户主动取消(与「失败」分开,界面不报红) */ +export class CaptchaCancelledError extends CaptchaError { + constructor() { + super(t('已取消验证码')) + this.name = 'CaptchaCancelledError' + } +} + +type CaptchaInstance = { destroy?: () => void } + +type InitOptions = { + SceneId?: string + mode?: string + element?: string + button?: string + captchaVerifyCallback?: (param: string) => Promise<{ captchaResult: boolean; bizResult: boolean }> + onBizResultCallback?: () => void + getInstance?: (instance: CaptchaInstance) => void + slideStyle?: { width: number; height: number } + language?: string + onError?: (error: { message?: string } | null) => void +} + +type CaptchaGlobal = { + initAliyunCaptcha?: (options: InitOptions) => void + AliyunCaptchaConfig?: { region?: string; prefix?: string } +} + +const globals = window as unknown as CaptchaGlobal & { + AliyunCaptchaConfig?: { region?: string; prefix?: string } +} + +/* ─── 模块级状态 ─────────────────────────────── */ + +let scriptLoadPromise: Promise | null = null +let initializationPromise: Promise | null = null +let initializationKey = '' +let initializationGeneration = 0 +let initializedAt = 0 +let captchaInstance: CaptchaInstance | null = null + +type Pending = { + generation: number + instance: CaptchaInstance + request: (verifyParam: string) => Promise<{ captchaResult: boolean; bizResult: boolean }> + resolve: (value: unknown) => void + reject: (error: Error) => void + timer: number + settled: boolean +} + +let pendingVerification: Pending | null = null + +const sleep = (ms: number) => new Promise(resolve => window.setTimeout(resolve, ms)) + +/** + * 语言映射(官方客户端的 `resolveAliyunCaptchaLanguage`)。 + * + * 阿里云只认这几个短码,传别的会被它当成不认识而回落到英文 —— 必须在这里 + * 归一,不能直接把 `zh-CN` 递给 SDK。 + */ +function resolveLanguage(language: string): string { + const normalized = String(language || '').trim().replace(/_/g, '-').toLowerCase() + if (normalized === 'zh-tw' || normalized.startsWith('zh-hant')) return 'tw' + if (normalized.startsWith('zh')) return 'cn' + if (normalized.startsWith('ar')) return 'ar' + if (normalized.startsWith('de')) return 'de' + if (normalized.startsWith('es')) return 'es' + if (normalized.startsWith('fr')) return 'fr' + if (normalized.startsWith('id') || normalized.startsWith('in')) return 'in' + if (normalized.startsWith('it')) return 'it' + if (normalized.startsWith('ja')) return 'ja' + if (normalized.startsWith('ko')) return 'ko' + if (normalized.startsWith('pt')) return 'pt' + if (normalized.startsWith('ru')) return 'ru' + if (normalized.startsWith('th')) return 'th' + if (normalized.startsWith('tr')) return 'tr' + if (normalized.startsWith('vi')) return 'vi' + return 'en' +} + +const currentLanguage = () => document.documentElement.lang || 'zh-CN' + +function removeCaptchaElements(): void { + document.getElementById(ELEMENT_ID)?.remove() + document.getElementById(BUTTON_ID)?.remove() +} + +/** 作废当前实例(配置变了 / 验证码用完了要重建时调) */ +function invalidateInitialization(): void { + const instance = captchaInstance + initializationGeneration += 1 + captchaInstance = null + initializationPromise = null + initializedAt = 0 + removeCaptchaElements() + try { + instance?.destroy?.() + } catch { + /* 实例已不可用 */ + } +} + +/** + * 备好滑块容器与触发按钮(官方客户端的 `ensureCaptchaElements`)。 + * + * 两个元素都由 SDK 按 id 找:`element` 是滑块面板的落点,`button` 是 + * 「点它才弹」的触发器。按钮做成 1×1 透明且 `pointer-events: none` —— + * 用户看不到也点不到,触发完全由代码控制。 + */ +function ensureCaptchaElements(): HTMLButtonElement { + let element = document.getElementById(ELEMENT_ID) + if (!element) { + element = document.createElement('div') + element.id = ELEMENT_ID + element.style.position = 'relative' + element.style.zIndex = '2147483000' + document.body.appendChild(element) + } + let button = document.getElementById(BUTTON_ID) as HTMLButtonElement | null + if (!button) { + button = document.createElement('button') + button.id = BUTTON_ID + button.type = 'button' + button.tabIndex = -1 + button.setAttribute('aria-hidden', 'true') + button.style.position = 'fixed' + button.style.width = '1px' + button.style.height = '1px' + button.style.opacity = '0' + button.style.pointerEvents = 'none' + button.style.overflow = 'hidden' + document.body.appendChild(button) + } + return button +} + +/** + * 加载 SDK 脚本。 + * + * `window.AliyunCaptchaConfig` 必须在脚本加载**之前**设好 —— SDK 读它决定 + * 打哪个阿里云站点(`region` / `prefix`)。设晚了 SDK 会用默认站点,表现是 + * 「滑块弹出来但一直转圈」。 + */ +function loadScript(config: CaptchaConfig): Promise { + globals.AliyunCaptchaConfig = { region: config.region, prefix: config.prefix } + if (typeof globals.initAliyunCaptcha === 'function') return Promise.resolve() + if (scriptLoadPromise) return scriptLoadPromise + + scriptLoadPromise = new Promise((resolve, reject) => { + const existing = document.getElementById(SCRIPT_ID) as HTMLScriptElement | null + const script = existing || document.createElement('script') + let timer = 0 + const cleanup = () => { + window.clearTimeout(timer) + script.removeEventListener('load', onLoad) + script.removeEventListener('error', onError) + } + const fail = (error: Error) => { + cleanup() + scriptLoadPromise = null + script.remove() + reject(error) + } + const onLoad = () => { + cleanup() + if (typeof globals.initAliyunCaptcha === 'function') resolve() + else fail(new CaptchaError(t('验证码组件加载异常,请重试'))) + } + const onError = () => fail(new CaptchaError(t('验证码组件加载失败,请检查网络后重试'))) + timer = window.setTimeout( + () => fail(new CaptchaError(t('验证码组件加载超时,请检查网络后重试'))), + SCRIPT_LOAD_TIMEOUT_MS, + ) + script.addEventListener('load', onLoad) + script.addEventListener('error', onError) + if (!existing) { + script.id = SCRIPT_ID + script.async = true + script.src = SCRIPT_URL + document.head.appendChild(script) + } + }) + return scriptLoadPromise +} + +/** 把一次等待落定 */ +function settlePending(pending: Pending, outcome: { error?: Error; value?: unknown }): boolean { + if (pendingVerification !== pending || pending.settled) return false + pending.settled = true + window.clearTimeout(pending.timer) + if (outcome.error) pending.reject(outcome.error) + else pending.resolve(outcome.value) + return true +} + +/** + * SDK 回调:拿到不透明验证串 → 交给调用方去干它那件事。 + * + * 返回值必须是 `{captchaResult, bizResult}`:SDK 据此决定「这一关过了没有」。 + * `captchaResult` 是验证码本身是否通过,`bizResult` 是调用方的业务是否接受 + * 它。两个都为 true SDK 才收起滑块。 + */ +async function verifyCallback(generation: number, param: string) { + const pending = pendingVerification + if (!pending || pending.generation !== generation) { + return { captchaResult: false, bizResult: false } + } + if (typeof param !== 'string' || param.length === 0) { + settlePending(pending, { error: new CaptchaError(t('验证码校验失败,请重试')) }) + return { captchaResult: false, bizResult: false } + } + try { + const result = await pending.request(param) + if (pendingVerification !== pending || pending.settled) { + return { + captchaResult: Boolean(result?.captchaResult), + bizResult: Boolean(result?.bizResult), + } + } + settlePending(pending, { value: result }) + return { + captchaResult: Boolean(result?.captchaResult), + bizResult: Boolean(result?.bizResult), + } + } catch (error) { + settlePending(pending, { error: error instanceof Error ? error : new Error(String(error)) }) + return { captchaResult: false, bizResult: false } + } +} + +/** + * 初始化 SDK 实例。 + * + * 同一个配置只初始化一次,19 分钟内复用;配置变了或过期就重建。 + * `getInstance` 回调是「SDK 准备好了」的信号。 + */ +function initialize(config: CaptchaConfig): Promise { + const language = resolveLanguage(currentLanguage()) + const key = `${config.region}:${config.prefix}:${config.sceneId}:${language}` + if (initializationPromise && initializationKey === key) { + const inFlight = initializedAt === 0 + const fresh = Date.now() - initializedAt < INITIALIZATION_MAX_AGE_MS + if (inFlight || fresh) return initializationPromise + invalidateInitialization() + } + if (initializationKey && initializationKey !== key) invalidateInitialization() + initializationKey = key + const generation = ++initializationGeneration + + initializationPromise = (async () => { + await loadScript(config) + ensureCaptchaElements() + const init = globals.initAliyunCaptcha + if (typeof init !== 'function') throw new CaptchaError(t('验证码组件不可用,请重试')) + + await new Promise((resolve, reject) => { + let settled = false + let boundInstance: CaptchaInstance | null = null + const timer = window.setTimeout(() => { + if (settled) return + settled = true + reject(new CaptchaError(t('验证码组件初始化超时,请重试'))) + }, INIT_TIMEOUT_MS) + const settle = (callback: () => void) => { + if (settled) return false + settled = true + window.clearTimeout(timer) + callback() + return true + } + try { + init({ + SceneId: config.sceneId, + mode: 'popup', + element: `#${ELEMENT_ID}`, + button: `#${BUTTON_ID}`, + captchaVerifyCallback: param => verifyCallback(generation, param), + onBizResultCallback: () => {}, + getInstance: instance => { + boundInstance = instance + if (generation !== initializationGeneration) { + try { + instance.destroy?.() + } catch { + /* 忽略 */ + } + return + } + if ( + !settle(() => { + captchaInstance = instance + initializedAt = Date.now() + resolve() + }) + ) { + try { + instance.destroy?.() + } catch { + /* 忽略 */ + } + } + }, + slideStyle: { width: 360, height: 40 }, + language, + onError: error => { + const pending = pendingVerification + if (pending && pending.generation === generation && pending.instance === boundInstance) { + settlePending(pending, { + error: new CaptchaError(error?.message || t('验证码校验失败,请重试')), + }) + return + } + if (settled) { + if ( + generation === initializationGeneration && + (!boundInstance || captchaInstance === boundInstance) + ) { + invalidateInitialization() + } + return + } + settle(() => reject(new CaptchaError(error?.message || t('验证码组件不可用,请重试')))) + }, + }) + } catch (error) { + settle(() => + reject( + new CaptchaError( + error instanceof Error ? error.message : t('验证码组件不可用,请重试'), + ), + ), + ) + } + }) + if (!captchaInstance) throw new CaptchaError(t('验证码组件不可用,请重试')) + })() + return initializationPromise +} + +/** 等 SDK 预热完成(太快点击弹不出滑块) */ +async function waitForWarmup(): Promise { + const remaining = MINIMUM_WARMUP_MS - (Date.now() - initializedAt) + if (remaining > 0) await sleep(remaining) +} + +/** + * 走一次完整验证码:初始化 → 预热 → 弹滑块 → 用户拖 → 拿串交给调用方。 + * + * `request(verifyParam)` 是调用方注入的「用这个串去干那件事」,返回 + * `{captchaResult, bizResult}`;它 resolve 出来的值会原样成为 `solve()` 的 + * 返回值(调用方借它把业务结果带回来)。 + * + * 超时只包着「拖滑块 + 那一次请求」:调用方不要在 `request` 里做长流程。 + */ +export async function solveCaptcha( + config: CaptchaConfig, + request: (verifyParam: string) => Promise<{ captchaResult: boolean; bizResult: boolean; result?: T }>, +): Promise { + await initialize(config) + await waitForWarmup() + const button = ensureCaptchaElements() + const instance = captchaInstance + if (!instance) throw new CaptchaError(t('验证码组件不可用,请重试')) + const generation = initializationGeneration + + return new Promise((resolve, reject) => { + const pending: Pending = { + generation, + instance, + request, + resolve: value => resolve(value as T | undefined), + reject, + timer: 0, + settled: false, + } + pending.timer = window.setTimeout(() => { + settlePending(pending, { error: new CaptchaError(t('验证码校验超时,请重试')) }) + }, VERIFY_TIMEOUT_MS) + pendingVerification = pending + button.click() + }) +} + +/** 用户取消:把等待中的那次落定成「已取消」并作废实例 */ +export function cancelCaptcha(): boolean { + const pending = pendingVerification + if (!pending) return false + const cancelled = settlePending(pending, { error: new CaptchaCancelledError() }) + if (cancelled) invalidateInitialization() + return cancelled +} + +/** 有没有正在等的验证码流程(关抽屉时据此决定要不要提示) */ +export function captchaBusy(): boolean { + return pendingVerification !== null +} diff --git a/desktop-tauri/mobile/src/lib/appUpdate.ts b/desktop-tauri/mobile/src/lib/appUpdate.ts new file mode 100644 index 00000000..5ac864ba --- /dev/null +++ b/desktop-tauri/mobile/src/lib/appUpdate.ts @@ -0,0 +1,257 @@ +/** + * 手机端应用更新。 + * + * ── 三种形态,三条路 ──────────────────────────────────────────── + * · **PWA / 浏览器**:更新走 Service Worker(见 lib/pwa.ts),这里不参与。 + * 浏览器里的「新版本」是网关托管的 `ui/m/*` 变了,刷新即可。 + * · **APK**:界面打在包里,必须换安装包。这条路是这里实现的: + * 1. 调网关 `GET /api/update/check` 拿最新版本与 `androidAsset`; + * 2. 用原生插件**直连 GitHub** 下载 APK(手机自己出网); + * 3. 调起系统安装器。 + * 下载地址是 GitHub 的 `browser_download_url`,不经过网关中转 —— + * 手机出不了网时用户自己开代理即可,网关不替它代劳。 + * · **iOS(IPA)**:苹果不允许 App 自行下载并安装可执行代码,所以这里 + * **只能跳转** —— 拿 `pageUrl` 打开 GitHub Release 页面,由用户自己走 + * TestFlight / App Store / AltStore。`downloadApk` / `installApk` 在 iOS + * 上直接报错,界面也据此不渲染那几个按钮(见 AppUpdatePanel)。 + * + * ── 为什么下载走原生而不是 fetch ──────────────────────────────── + * APK 有几十 MB,`fetch` + `blob` 会把整包读进 WebView 内存;`CapacitorHttp` + * 也一样(它把响应体整个序列化回来)。原生插件用 HttpURLConnection 边读边 + * 写文件,内存占用只有缓冲区大小,还能实时上报进度。 + * + * ── 版本号从哪来 ──────────────────────────────────────────────── + * `__APP_VERSION__` 是构建期由 vite 从 `mobile/package.json` 注入的常量 + * (见 vite.config.ts),与 APK 的 `versionName`(android/variables.gradle + * 读同一个字段)永远一致;iOS 的 `MARKETING_VERSION` 由 CI 从同一个字段写进 + * xcodebuild 参数,所以两边不会漂。 + * + * ── 自动检查 ──────────────────────────────────────────────────── + * 间隔与开关**读电脑端的「软件版本检查」任务配置**(`/api/scheduled-tasks` + * 里的 `updateCheck` 条目),不另起一套设置 —— 用户在一处配好,两端一致。 + * 手机端本地只留一个「本机不自动检查」的开关(存在 localStorage), + * 用于「我不想让手机耗流量」这种纯本地偏好。 + */ + +import { apiCall } from '../api/client' +import { nativePlatform } from '../lib/servers' + +/** 构建期注入的本工程版本号 */ +declare const __APP_VERSION__: string + +export const APP_VERSION: string = typeof __APP_VERSION__ === 'string' ? __APP_VERSION__ : '0.0.0' + +/** 更新检查结果里手机端关心的那部分 */ +export type ApkUpdateInfo = { + currentVersion: string + latestVersion: string | null + hasUpdate: boolean | null + notes: string + publishedAt: string + pageUrl: string + prerelease: boolean + /** 这一版挂的 APK 资产;null = 该版本没提供 Android 包 */ + asset: { name?: string; size?: number; url?: string } | null +} + +type RawCheckResult = { + currentVersion?: string | null + latestVersion?: string | null + hasUpdate?: boolean | null + notes?: string | null + publishedAt?: string | null + pageUrl?: string | null + prerelease?: boolean + androidAsset?: { name?: string; size?: number; url?: string } | null +} + +/** + * 检查更新。 + * + * 传本机版本号而不是让服务端猜:网关可能跑在电脑上、版本与手机端不同, + * 「手机端有没有新版本」只有手机自己知道当前是哪个版本。 + * + * 走网关的 `/api/update/check` 而不是手机直接打 GitHub API:这样能**复用 + * 电脑端已配置的 GitHub 令牌**(匿名限额 60 次/小时,带令牌 5000), + * 也不必让手机再存一份凭证。注意这里只复用「查询」,下载仍是手机直连。 + */ +export async function checkApkUpdate(): Promise { + const raw = await apiCall( + `/api/update/check?current=${encodeURIComponent(APP_VERSION)}`, + ) + const asset = raw.androidAsset && raw.androidAsset.url ? raw.androidAsset : null + return { + currentVersion: APP_VERSION, + latestVersion: raw.latestVersion || null, + // 有新版但没挂 APK 时,对手机端来说等于「没有可更新的包」: + // 不谎报「有更新」把用户引到一个点不动的按钮上。 + hasUpdate: raw.hasUpdate === true && asset !== null ? true : raw.hasUpdate === false ? false : null, + notes: raw.notes || '', + publishedAt: raw.publishedAt || '', + pageUrl: raw.pageUrl || '', + prerelease: Boolean(raw.prerelease), + asset, + } +} + +/* ─── 自动检查的配置(读电脑端) ─────────────────────────────── */ + +/** 手机端本地的「不自动检查」开关(纯本地偏好,不上报) */ +const AUTO_CHECK_KEY = 'agent2api.mobile.updateAutoCheck' + +/** 本机是否允许自动检查(默认允许) */ +export function autoCheckEnabled(): boolean { + try { + return localStorage.getItem(AUTO_CHECK_KEY) !== 'off' + } catch { + return true + } +} + +export function setAutoCheckEnabled(enabled: boolean): void { + try { + if (enabled) localStorage.removeItem(AUTO_CHECK_KEY) + else localStorage.setItem(AUTO_CHECK_KEY, 'off') + } catch { + /* 隐私模式:只在本页生效 */ + } +} + +/** + * 从电脑端的「软件版本检查」任务读检查间隔(毫秒)。 + * + * 电脑端把这条任务藏在「更新设置」弹窗里(定时任务页不渲染它),但配置本身 + * 仍由 `/api/scheduled-tasks` 返回 —— 手机端直接读同一个来源,不另做设置页。 + * 任务被关掉时返回 0(= 不自动检查),与电脑端语义一致。 + * + * 读不到(老网关 / 请求失败)时退回 20 分钟:那是电脑端的缺省值 + * (`DEFAULT_UPDATE_CHECK_MINUTES`),行为与「没配过」一致。 + */ +export async function autoCheckIntervalMs(): Promise { + try { + const payload = await apiCall<{ + tasks?: Array<{ id?: string; enabled?: boolean; interval?: number; unit?: string }> + }>('/api/scheduled-tasks') + const task = (payload.tasks || []).find(item => item.id === 'updateCheck') + if (!task) return 20 * 60_000 + if (task.enabled === false) return 0 + const interval = Number(task.interval) || 0 + if (interval <= 0) return 20 * 60_000 + // 电脑端这条任务的单位是分钟(见 scheduled_tasks.rs 的 TaskDef) + return interval * 60_000 + } catch { + return 20 * 60_000 + } +} + +/* ─── 原生插件(只在 APK 里存在) ─────────────────────────────── */ + +type PluginListenerHandle = { remove: () => Promise } + +type ApkUpdaterPlugin = { + download(options: { url: string }): Promise<{ path: string; size: number }> + cancel(): Promise + canInstall(): Promise<{ allowed: boolean }> + openInstallPermissionSettings(): Promise + install(): Promise + downloaded(): Promise<{ exists: boolean; size: number }> + addListener( + event: 'downloadProgress', + handler: (data: { received: number; total: number; percent: number }) => void, + ): Promise +} + +type CapacitorGlobal = { + isNativePlatform?: () => boolean + Plugins?: Record +} + +/** 取原生插件;不在 APK 里(或插件没注册)时返回 null */ +function plugin(): ApkUpdaterPlugin | null { + // iOS 没有这个插件(也不可能有:苹果不允许 App 自装可执行代码) + if (nativePlatform() !== 'android') return null + const capacitor = (window as unknown as { Capacitor?: CapacitorGlobal }).Capacitor + const found = capacitor?.Plugins?.ApkUpdater + return (found as ApkUpdaterPlugin | undefined) || null +} + +/** 当前壳能不能在应用内下载并安装安装包(只有 Android 能) */ +export function canSelfUpdate(): boolean { + return nativePlatform() === 'android' && plugin() !== null +} + +export type DownloadProgress = { + received: number + total: number + /** 0–100;总长度未知时为 -1 */ + percent: number +} + +/** + * 下载 APK 到应用缓存。返回下载完成后的本地路径。 + * + * `url` 必须是 GitHub 的资产地址(由 `checkApkUpdate` 的 `asset.url` 给出)。 + * 手机自己出网 —— 连不上 GitHub 时用户自己开代理,不在这里做中转。 + */ +export async function downloadApk( + url: string, + onProgress?: (progress: DownloadProgress) => void, +): Promise<{ path: string; size: number }> { + const api = plugin() + if (!api) { + // iOS 与浏览器都会走到这里:两者都不允许应用内自装安装包 + throw new Error( + nativePlatform() === 'ios' + ? 'iOS 不支持应用内安装,请到 GitHub Release 页面下载' + : '当前不是 APK 形态,无法在应用内更新', + ) + } + if (!url) throw new Error('缺少下载地址') + + let handle: PluginListenerHandle | null = null + if (onProgress) { + handle = await api.addListener('downloadProgress', data => onProgress(data)) + } + try { + return await api.download({ url }) + } finally { + if (handle) void handle.remove() + } +} + +export async function cancelDownload(): Promise { + await plugin()?.cancel() +} + +/** 是否已获「安装未知来源应用」授权 */ +export async function canInstallApk(): Promise { + const api = plugin() + if (!api) return false + const result = await api.canInstall() + return Boolean(result.allowed) +} + +/** 跳到系统的「安装未知应用」授权页 */ +export async function openInstallPermission(): Promise { + await plugin()?.openInstallPermissionSettings() +} + +/** 调起系统安装器(APK 已下载时) */ +export async function installApk(): Promise { + const api = plugin() + if (!api) { + throw new Error( + nativePlatform() === 'ios' + ? 'iOS 不支持应用内安装,请到 GitHub Release 页面下载' + : '当前不是 APK 形态,无法安装', + ) + } + await api.install() +} + +/** 缓存里是否已有下载好的安装包(重进设置页时显示「已下载,待安装」) */ +export async function hasDownloadedApk(): Promise<{ exists: boolean; size: number }> { + const api = plugin() + if (!api) return { exists: false, size: 0 } + return api.downloaded() +} diff --git a/desktop-tauri/mobile/src/lib/assistantTools.ts b/desktop-tauri/mobile/src/lib/assistantTools.ts new file mode 100644 index 00000000..1a0295f3 --- /dev/null +++ b/desktop-tauri/mobile/src/lib/assistantTools.ts @@ -0,0 +1,600 @@ +/** + * AI 助手的工具集(手机端)。 + * + * ── 为什么手机端也要有工具表 ────────────────────────────────── + * 后端的 `/api/assistant/chat` 有一条产品口径(见 `config::AssistantConfig:: + * system_prompt`):**一只工具都没勾 → 不注入系统提示词**。而系统提示词正是 + * 告诉模型「你是 Agent2API 的内置助手,管的是本机模型转发配置」的那段话。 + * 手机端早先只发 messages、不带 tools,于是模型拿到的是一个没有身份设定的 + * 通用对话 —— 用户问「各账号余额」,它答成了银行账户余额。 + * + * 所以这里移植桌面端的九只工具。执行全部走**网关的管理 API**(手机端本来就 + * 在用那些接口),不经过桌面壳的桥。 + * + * ── 与桌面端的差异 ──────────────────────────────────────────── + * · 执行层从 `window.workbuddyDesktop` 换成 `api/gateway` 的 HTTP 调用; + * · 自定义提供商(`custom-` 前缀)的读写走 `/api/custom-providers`; + * · 去掉「确认卡」:手机屏幕小,一次确认卡要占掉半屏。这里的做法是**只让 + * 模型看到用户在设置里勾选过的工具**,并把写操作的结果如实回报 —— + * 与桌面端「勾选表即权限」同一套语义(未勾选的工具根本不在请求里, + * 模型看不见也就调不到)。 + */ + +import { + fetchAccounts, + fetchAccountUsage, + fetchManage, + fetchUsageSnapshot, + setAccountEnabled, + setModelEnabled, + type Account, + type ManageModel, +} from '../api/gateway' +import { apiCall } from '../api/client' +import { accountName } from '../lib/format' +import { usageIndex, usageText } from '../lib/usage' +import { t } from '../i18n' + +export type ToolResult = { text: string; summary: string } + +export type AssistantTool = { + id: string + label: string + kind: 'read' | 'write' | 'run' + desc: string + params: Record + run: (args: Record) => Promise +} + +/* ─── 入参读取(模型给的东西不一定守规矩,坏值一律当缺)── */ + +function str(value: unknown, max = 256): string { + return typeof value === 'string' ? value.trim().slice(0, max) : '' +} +function bool(value: unknown): boolean | undefined { + return typeof value === 'boolean' ? value : undefined +} +function num(value: unknown): number | undefined { + return typeof value === 'number' && Number.isFinite(value) ? value : undefined +} +function rows(value: unknown): Array> { + return Array.isArray(value) + ? value.filter((item): item is Record => Boolean(item) && typeof item === 'object') + : [] +} + +/** 分页:模型与映射共用(一页塞太多会撑爆上下文窗口) */ +const PAGE_DEFAULT = 100 +const PAGE_MAX = 500 + +function paginate(items: T[], offset: number, limit: number) { + const start = Math.min(offset, items.length) + const end = Math.min(start + limit, items.length) + return { + meta: { + total: items.length, + offset: start, + limit, + returned: end - start, + hasMore: end < items.length, + ...(end < items.length ? { nextOffset: end } : {}), + }, + items: items.slice(start, end), + } +} + +/** 账号点名:id 精确 > 名字精确 > 名字子串(唯一命中才算) */ +function findAccount(accounts: Account[], ref: string): Account { + const needle = ref.trim() + if (!needle) throw new Error(t('没给账号名')) + const byId = accounts.find(row => row.id === needle) + if (byId) return byId + const exact = accounts.filter(row => accountName(row) === needle) + if (exact.length === 1) return exact[0] + const fuzzy = accounts.filter(row => accountName(row).includes(needle)) + if (fuzzy.length === 1) return fuzzy[0] + const candidates = (exact.length ? exact : fuzzy).map(accountName).slice(0, 6) + if (!candidates.length) throw new Error(t('没有找到账号「{name}」', { name: needle })) + throw new Error( + t('「{name}」匹配到多个账号:{list},请用完整账号名重试', { + name: needle, + list: candidates.join('、'), + }), + ) +} + +async function readAccounts(): Promise { + const data = await fetchAccounts() + // 全局队列 = priority 升序(后端 priority 越小越靠前) + return (data.accounts || []) + .slice() + .sort((a, b) => Number(a.priority ?? 0) - Number(b.priority ?? 0)) +} + +/** 模型行 + 映射(自定义提供商由 `/api/models/manage` 一并给出,见后端说明) */ +async function readModels(): Promise { + const view = await fetchManage() + return Array.isArray(view.models) ? view.models : [] +} + +/* ─── 九只工具 ─────────────────────────────── */ + +/** 1 · 获取模型列表(只读,分页) */ +const listModels: AssistantTool = { + id: 'list_models', + label: t('获取模型列表'), + kind: 'read', + desc: '分页读取各提供商的模型清单(模型 ID、所属提供商、是否启用、已挂的映射名)。回答「有哪些模型」「哪些模型在多家重复」「某个名字挂在哪」这类问题之前先调用它。\n\n结果分页:响应里带 total / offset / limit / returned / hasMore / nextOffset。hasMore 为 true 时用 nextOffset 继续取,直到取完;不要只看第一页就下结论。', + params: { + type: 'object', + properties: { + provider: { type: 'string', description: '只看某一家(提供商 id);不填 = 全部' }, + keyword: { type: 'string', description: '按模型 ID 或映射名做子串过滤;不填 = 不过滤' }, + enabledOnly: { type: 'boolean', description: 'true = 只看已启用的模型' }, + offset: { type: 'number', description: '本页起点(从 0 开始);翻页时传上一页的 nextOffset' }, + limit: { type: 'number', description: `每页条数(默认 ${PAGE_DEFAULT},最多 ${PAGE_MAX})` }, + }, + }, + run: async args => { + const provider = str(args.provider, 64).toLowerCase() + const keyword = str(args.keyword, 128).toLowerCase() + const enabledOnly = bool(args.enabledOnly) === true + const offset = Math.max(0, Math.round(num(args.offset) ?? 0)) + const limit = Math.max(1, Math.min(PAGE_MAX, Math.round(num(args.limit) ?? PAGE_DEFAULT))) + + const models = await readModels() + const filtered = models.filter(row => { + if (provider && str(row.provider).toLowerCase() !== provider) return false + if (enabledOnly && row.enabled !== true) return false + if (!keyword) return true + const hay = [str(row.id), str(row.name), ...(row.aliases || [])].join(' ').toLowerCase() + return hay.includes(keyword) + }) + const page = paginate( + filtered.map(row => ({ + provider: str(row.provider), + providerLabel: str(row.providerLabel) || str(row.provider), + id: str(row.id), + enabled: row.enabled === true, + aliases: Array.isArray(row.aliases) ? row.aliases : [], + })), + offset, + limit, + ) + return { + text: JSON.stringify({ models: { ...page.meta, items: page.items } }), + summary: t('{n} 个模型', { n: page.meta.total }), + } + }, +} + +/** 2 · 查询账号余额(只读) */ +const listAccountBalance: AssistantTool = { + id: 'list_account_balance', + label: t('查询账号余额'), + kind: 'read', + desc: '查询各账号的余额 / 剩余额度。回答「还剩多少」「哪个账号快用完了」这类问题之前先调用它。读的是网关的余额快照(不逐个打上游);要给某个账号做实时查询,用户需要在账号详情里点「查询余额」。', + params: { + type: 'object', + properties: { + account: { type: 'string', description: '只查某个账号(账号名或 id);不填 = 全部账号' }, + refresh: { type: 'boolean', description: 'true = 顺带向上游做一次实时查询(较慢,会打上游)' }, + }, + }, + run: async args => { + const ref = str(args.account, 128) + const live = bool(args.refresh) === true + + if (live && ref) { + const target = findAccount(await readAccounts(), ref) + const payload = await fetchAccountUsage(target.id) + const row = payload.results?.[0] + const view = usageText(row) + return { + text: JSON.stringify({ account: accountName(target), usage: view.text, detail: view.detail }), + summary: view.text, + } + } + + const snapshot = await fetchUsageSnapshot() + const index = usageIndex(snapshot) + const accounts = await readAccounts() + const picked = ref ? [findAccount(accounts, ref)] : accounts + const out = picked.map(row => { + const view = usageText(index.get(row.id)) + return { + account: accountName(row), + provider: row.provider || '', + enabled: row.enabled === true, + usage: view.text, + detail: view.detail, + } + }) + return { + text: JSON.stringify({ accounts: out.slice(0, 60) }).slice(0, 12000), + summary: t('{n} 个账号', { n: out.length }), + } + }, +} + +/** 3 · 启用 / 禁用模型(改动) */ +const setModelEnabledTool: AssistantTool = { + id: 'set_model_enabled', + label: t('启用 / 禁用模型'), + kind: 'write', + desc: '开启或关闭某几个模型在网关上的可用性(粒度是「提供商 × 模型」)。禁用后下游请求不再路由到它。一次可以改多个。', + params: { + type: 'object', + properties: { + items: { + type: 'array', + description: '要改的模型列表', + items: { + type: 'object', + properties: { + provider: { type: 'string', description: '提供商 id' }, + id: { type: 'string', description: '模型 ID' }, + enabled: { type: 'boolean', description: 'true = 启用,false = 禁用' }, + }, + required: ['provider', 'id', 'enabled'], + }, + }, + }, + required: ['items'], + }, + run: async args => { + const items = rows(args.items) + const done: string[] = [] + for (const item of items) { + const provider = str(item.provider) + const id = str(item.id) + const enabled = bool(item.enabled) + if (!provider || !id || enabled === undefined) { + throw new Error(t('参数不完整(需要 provider / id / enabled)')) + } + await setModelEnabled({ provider, id, enabled }) + done.push(`${enabled ? '启用' : '禁用'} ${provider}/${id}`) + } + return { + text: JSON.stringify({ ok: true, changed: done }), + summary: t('已修改 {n} 个模型', { n: done.length }), + } + }, +} + +/** 4 · 设置模型映射 ID(改动) */ +const setModelMapping: AssistantTool = { + id: 'set_model_mapping', + label: t('设置模型映射 ID'), + kind: 'write', + desc: '给模型新增或删除映射名(对外名)。同一对外名可以在多个提供商各挂一条:下游用同一个名字请求,网关按账号优先级主备切换;也可以给不同家的同名模型各加前缀来精确指定某一家。一次可以改多条。删掉的映射名只是不再对外可用,不删任何模型。', + params: { + type: 'object', + properties: { + items: { + type: 'array', + description: '要改的映射列表', + items: { + type: 'object', + properties: { + provider: { type: 'string', description: '提供商 id' }, + id: { type: 'string', description: '上游模型 ID' }, + alias: { type: 'string', description: '要新增 / 删除的映射名(对外名)' }, + action: { type: 'string', enum: ['add', 'remove'], description: 'add = 新增,remove = 删除' }, + }, + required: ['provider', 'id', 'alias', 'action'], + }, + }, + }, + required: ['items'], + }, + run: async args => { + const items = rows(args.items) + const done: string[] = [] + for (const item of items) { + const provider = str(item.provider) + const target = str(item.id) + const alias = str(item.alias, 128) + const action = str(item.action) === 'remove' ? 'remove' : 'add' + if (!provider || !target || !alias) { + throw new Error(t('参数不完整(需要 provider / id / alias)')) + } + if (action === 'remove') { + await apiCall('/api/models/mappings/remove', { + method: 'POST', + body: { provider, target, alias }, + }) + done.push(`删除 ${alias} → ${provider}/${target}`) + } else { + await apiCall('/api/models/mappings', { + method: 'POST', + body: { provider, target, alias }, + }) + done.push(`新增 ${alias} → ${provider}/${target}`) + } + } + return { + text: JSON.stringify({ ok: true, changed: done }), + summary: t('已改动 {n} 条映射', { n: done.length }), + } + }, +} + +/** 5 · 修改模型能力(改动) */ +const CAPABILITY_KEYS = [ + 'maxInputTokens', + 'maxOutputTokens', + 'supportsToolCall', + 'supportsImages', + 'supportsVideo', + 'supportsReasoning', +] as const + +const CAPABILITY_LABELS: Record = { + maxInputTokens: t('上下文窗口'), + maxOutputTokens: t('最大输出 Token'), + supportsToolCall: t('工具调用'), + supportsImages: t('图片识别'), + supportsVideo: t('视频识别'), + supportsReasoning: t('深度推理'), +} + +const setModelCapability: AssistantTool = { + id: 'set_model_capability', + label: t('修改模型能力'), + kind: 'write', + desc: '修改某个模型对下游声明的能力位:上下文窗口、最大输出 Token、工具调用 / 图片 / 视频 / 深度推理四个布尔位。只传要改的键;布尔位给 null 表示恢复上游原值。', + params: { + type: 'object', + properties: { + provider: { type: 'string', description: '提供商 id' }, + id: { type: 'string', description: '模型 ID' }, + capabilities: { + type: 'object', + description: '要改的能力位(只写要改的键)', + properties: { + maxInputTokens: { type: ['number', 'null'], description: '上下文窗口(token 数)' }, + maxOutputTokens: { type: ['number', 'null'], description: '最大输出 token 数' }, + supportsToolCall: { type: ['boolean', 'null'], description: '是否支持工具调用' }, + supportsImages: { type: ['boolean', 'null'], description: '是否支持图片识别' }, + supportsVideo: { type: ['boolean', 'null'], description: '是否支持视频识别' }, + supportsReasoning: { type: ['boolean', 'null'], description: '是否支持深度推理' }, + }, + }, + }, + required: ['provider', 'id', 'capabilities'], + }, + run: async args => { + const provider = str(args.provider) + const id = str(args.id) + const raw = (args.capabilities && typeof args.capabilities === 'object' + ? args.capabilities + : {}) as Record + const caps: Record = {} + for (const key of CAPABILITY_KEYS) { + if (!(key in raw)) continue + const value = raw[key] + if (value === null) { + caps[key] = null + continue + } + if (key === 'maxInputTokens' || key === 'maxOutputTokens') { + const value2 = num(value) + if (value2 === undefined) throw new Error(t('{key} 需要数字', { key: CAPABILITY_LABELS[key] })) + caps[key] = Math.round(value2) + } else { + const value2 = bool(value) + if (value2 === undefined) { + throw new Error(t('{key} 需要 true / false / null', { key: CAPABILITY_LABELS[key] })) + } + caps[key] = value2 + } + } + if (!provider || !id || !Object.keys(caps).length) { + throw new Error(t('参数不完整(需要 provider / id / capabilities)')) + } + await apiCall('/api/models/capabilities', { + method: 'POST', + body: { provider, id, capabilities: caps }, + }) + return { + text: JSON.stringify({ ok: true, provider, id, capabilities: caps }), + summary: t('已修改 {n} 项能力', { n: Object.keys(caps).length }), + } + }, +} + +/** 6 · 发起模型测试(消耗额度) */ +const testModel: AssistantTool = { + id: 'test_model', + label: t('发起模型测试'), + kind: 'run', + desc: '对某个模型发一次真实的对话请求,回报连通性、耗时与首字延迟。会消耗该账号的额度。回答「这个模型通不通 / 快不快」时用它。', + params: { + type: 'object', + properties: { + provider: { type: 'string', description: '提供商 id' }, + id: { type: 'string', description: '模型 ID' }, + prompt: { type: 'string', description: '测试用的用户提示词;不填 = 默认一句问候' }, + }, + required: ['provider', 'id'], + }, + run: async args => { + const payload: Record = { + provider: str(args.provider), + model: str(args.id), + } + const prompt = str(args.prompt, 4000) + if (prompt) payload.prompt = prompt + const result = (await apiCall('/api/models/test', { method: 'POST', body: payload })) as + | Record + | undefined + const status = String(result?.status ?? '') + const ok = result?.success === true + const slim = { + ok, + status, + error: result?.error ?? null, + reply: String(result?.reply ?? '').slice(0, 1500), + duration_ms: result?.duration_ms ?? null, + ttfb_ms: result?.ttfb_ms ?? null, + account: result?.account_name ?? null, + } + return { + text: JSON.stringify(slim).slice(0, 6000), + summary: ok + ? t('连通 · {ms}ms', { ms: String(slim.duration_ms ?? '') }) + : t('失败 · {status}', { status: status || '—' }), + } + }, +} + +/** 7 · 调整提供商顺位(改动)—— 整组移动 */ +const setProviderOrder: AssistantTool = { + id: 'set_provider_order', + label: t('调整提供商顺位'), + kind: 'write', + desc: '调整某个提供商在账号全局队列里的**整组顺位**(第几组),决定路由时先试哪一家。这一家的所有账号会作为一整块移动。位置从 1 开始数。', + params: { + type: 'object', + properties: { + provider: { type: 'string', description: '提供商 id' }, + position: { type: 'number', description: '目标组顺位(1 = 最前)' }, + }, + required: ['provider', 'position'], + }, + run: async args => { + const provider = str(args.provider).toLowerCase() + const position = num(args.position) + if (!provider || position === undefined) { + throw new Error(t('参数不完整(需要 provider / position)')) + } + const accounts = await readAccounts() + // 按家分组,保持组内原有相对顺序 + const groups: Array<{ provider: string; ids: string[] }> = [] + for (const row of accounts) { + const id = str(row.id) + if (!id) continue + const key = str(row.provider).toLowerCase() + const last = groups[groups.length - 1] + if (last && last.provider === key) last.ids.push(id) + else groups.push({ provider: key, ids: [id] }) + } + const from = groups.findIndex(group => group.provider === provider) + if (from < 0) throw new Error(t('没有找到提供商「{provider}」的账号', { provider })) + const to = Math.max(0, Math.min(groups.length - 1, Math.round(position) - 1)) + const [moved] = groups.splice(from, 1) + groups.splice(to, 0, moved) + await apiCall('/api/accounts/reorder', { + method: 'POST', + body: { queue: groups.flatMap(group => group.ids) }, + }) + return { + text: JSON.stringify({ ok: true, provider, from: from + 1, to: to + 1, moved: moved.ids.length }), + summary: t('已移到第 {n} 顺位', { n: to + 1 }), + } + }, +} + +/** 8 · 调整账号优先级(改动) */ +const setAccountOrder: AssistantTool = { + id: 'set_account_order', + label: t('调整账号优先级'), + kind: 'write', + desc: '把某个账号移动到账号全局队列的第 N 顺位(1 = 最前)。位置决定主备与切换顺序:越靠前越先被使用。', + params: { + type: 'object', + properties: { + account: { type: 'string', description: '账号名或账号 id' }, + position: { type: 'number', description: '目标顺位(1 = 最前)' }, + }, + required: ['account', 'position'], + }, + run: async args => { + const ref = str(args.account, 128) + const position = num(args.position) + if (!ref || position === undefined) { + throw new Error(t('参数不完整(需要 account / position)')) + } + const accounts = await readAccounts() + const target = findAccount(accounts, ref) + const ids = accounts.map(row => row.id).filter(Boolean) + const from = ids.indexOf(target.id) + if (from < 0) throw new Error(t('账号不在当前队列里')) + ids.splice(from, 1) + const to = Math.max(0, Math.min(ids.length, Math.round(position) - 1)) + ids.splice(to, 0, target.id) + await apiCall('/api/accounts/reorder', { method: 'POST', body: { queue: ids } }) + return { + text: JSON.stringify({ ok: true, account: accountName(target), from: from + 1, to: to + 1 }), + summary: t('已移到第 {n} 顺位', { n: to + 1 }), + } + }, +} + +/** 9 · 启用 / 禁用账号(高危) */ +const setAccountEnabledTool: AssistantTool = { + id: 'set_account_enabled', + label: t('启用 / 禁用账号'), + kind: 'write', + desc: '开启或关闭某几个账号的转发资格。禁用只是把它移出队列(余额仍可查),但如果某条线路只剩它,禁用会直接断供。一次可以改多个。', + params: { + type: 'object', + properties: { + items: { + type: 'array', + description: '要改的账号列表', + items: { + type: 'object', + properties: { + account: { type: 'string', description: '账号名或账号 id' }, + enabled: { type: 'boolean', description: 'true = 启用,false = 禁用' }, + }, + required: ['account', 'enabled'], + }, + }, + }, + required: ['items'], + }, + run: async args => { + const items = rows(args.items) + const accounts = await readAccounts() + const done: string[] = [] + for (const item of items) { + const enabled = bool(item.enabled) + if (enabled === undefined) throw new Error(t('参数不完整(需要 account / enabled)')) + const target = findAccount(accounts, str(item.account, 128)) + await setAccountEnabled(target.id, enabled) + done.push(`${enabled ? '启用' : '禁用'} ${accountName(target)}`) + } + return { + text: JSON.stringify({ ok: true, changed: done }), + summary: t('已修改 {n} 个账号', { n: done.length }), + } + }, +} + +/** 全部工具(顺序 = 设置页清单顺序:只读在前,改动在后,高危最后) */ +export const ASSISTANT_TOOLS: AssistantTool[] = [ + listModels, + listAccountBalance, + setModelEnabledTool, + setModelMapping, + setModelCapability, + testModel, + setProviderOrder, + setAccountOrder, + setAccountEnabledTool, +] + +export function toolById(id: string): AssistantTool | undefined { + return ASSISTANT_TOOLS.find(tool => tool.id === id) +} + +/** 按勾选表组装 OpenAI tools 数组(顺序固定 = ASSISTANT_TOOLS 的顺序) */ +export function toolsPayload(enabled: Record): Array> { + return ASSISTANT_TOOLS.filter(tool => enabled[tool.id]).map(tool => ({ + type: 'function', + function: { name: tool.id, description: tool.desc, parameters: tool.params }, + })) +} diff --git a/desktop-tauri/mobile/src/lib/community.ts b/desktop-tauri/mobile/src/lib/community.ts new file mode 100644 index 00000000..dbc6ab2a --- /dev/null +++ b/desktop-tauri/mobile/src/lib/community.ts @@ -0,0 +1,52 @@ +/** + * 社区入口常量。 + * + * ── 为什么单独一份 ──────────────────────────────────────────── + * 桌面端同一份数据在 `ui-islands/src/islands/update-shared.ts` 的 + * `QQ_GROUPS` / `COMMUNITY_LINKS`。手机端是独立工程(自己的 Vite 构建), + * 不能 import 那个文件(它依赖桌面壳的桥)。**改群号 / 换链接时两处一起改** + * —— 这里与那边是同一份事实的两个落点。 + * + * ── 为什么只给群号,不给二维码 ──────────────────────────────── + * 与桌面端同一理由(见那边 `QQ_GROUPS` 的注释):二维码是打进包里的静态图, + * 换群 / 换码就得跟着发一次版;群号 + 复制按钮改一个常量即可。 + */ + +export type QqGroup = { no: string; nearlyFull: boolean } + +export const QQ_GROUPS: readonly QqGroup[] = [ + { no: '1127656296', nearlyFull: true }, + { no: '1130894674', nearlyFull: false }, +] + +export type CommunityLink = { id: 'telegram' | 'discord'; url: string } + +export const COMMUNITY_LINKS: readonly CommunityLink[] = [ + { id: 'telegram', url: 'https://t.me/+9gM8YPvgRL02NGRl' }, + { id: 'discord', url: 'https://discord.gg/z9GzSJMWp' }, +] + +/** + * 用系统浏览器打开一个外链。 + * + * ── 为什么 APK 里是「同窗跳转」而不是 window.open ───────────── + * Capacitor 的 WebView 没开多窗口支持,`window.open` 会被直接忽略(返回 + * null,什么也不发生)。而主框架导航会走 `BridgeWebViewClient` 的 + * `shouldOverrideUrlLoading` → `bridge.launchIntent`:目标不是应用自己的 + * origin 时,它抛一个 `ACTION_VIEW` Intent 给系统,由默认浏览器接管, + * WebView 自己停在原地。所以这里用 `location.href`。 + * + * PWA 里没有那层壳,`window.open` 才是对的(新标签页,不丢当前页面)。 + */ +export function openExternal(url: string): void { + const target = String(url || '').trim() + if (!/^https?:\/\//i.test(target)) return + const packaged = Boolean( + (window as Window & { Capacitor?: { isNativePlatform?: () => boolean } }).Capacitor?.isNativePlatform?.(), + ) + if (packaged) { + window.location.href = target + return + } + window.open(target, '_blank', 'noopener') +} diff --git a/desktop-tauri/mobile/src/lib/format.ts b/desktop-tauri/mobile/src/lib/format.ts new file mode 100644 index 00000000..86e5e97c --- /dev/null +++ b/desktop-tauri/mobile/src/lib/format.ts @@ -0,0 +1,147 @@ +/** 通用格式化与少量纯函数工具(无 React 依赖,页面与数据层共用) */ + +/** 紧凑数字:1.2 万 / 3.4 亿;小于一万保留原样 */ +export function formatCompact(value: number): string { + if (!Number.isFinite(value)) return '—' + const abs = Math.abs(value) + if (abs >= 1e8) return `${(value / 1e8).toFixed(abs >= 1e9 ? 0 : 2)} 亿` + if (abs >= 1e4) return `${(value / 1e4).toFixed(abs >= 1e6 ? 0 : 1)} 万` + return String(Math.round(value)) +} + +/** 千分位整数 */ +export function formatInt(value: number): string { + if (!Number.isFinite(value)) return '—' + return Math.round(value).toLocaleString('zh-CN') +} + +/** Token 数:走紧凑口径(报表与日志都用这一套,避免两页读数不一致) */ +export function formatTokens(value: number): string { + return formatCompact(value) +} + +/** 百分比:输入 0~1 的比值或 0~100 的百分数由 `asRatio` 决定 */ +export function formatPercent(value: number | null | undefined, digits = 1, asRatio = true): string { + if (value === null || value === undefined || !Number.isFinite(value)) return '—' + const percent = asRatio ? value * 100 : value + return `${percent.toFixed(digits)}%` +} + +/** 毫秒 → 人类可读时长(1.2s / 860ms / 2m 05s) */ +export function formatDuration(ms: number | null | undefined): string { + if (ms === null || ms === undefined || !Number.isFinite(ms) || ms < 0) return '—' + if (ms < 1000) return `${Math.round(ms)}ms` + if (ms < 60_000) return `${(ms / 1000).toFixed(ms < 10_000 ? 2 : 1)}s` + const total = Math.floor(ms / 1000) + return `${Math.floor(total / 60)}m ${String(total % 60).padStart(2, '0')}s` +} + +/** 时间戳 → HH:MM:SS */ +export function formatClock(ts: number | null | undefined): string { + if (!ts) return '—' + const date = new Date(ts) + if (Number.isNaN(date.getTime())) return '—' + return date.toLocaleTimeString('zh-CN', { hour12: false }) +} + +/** 时间戳 → MM-DD HH:MM */ +export function formatStamp(ts: number | null | undefined): string { + if (!ts) return '—' + const date = new Date(ts) + if (Number.isNaN(date.getTime())) return '—' + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const time = date.toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit' }) + return `${month}-${day} ${time}` +} + +/** 相对时间:3 分钟后 / 2 小时后 / 4 天后 */ +export function formatRelative(ts: number | null | undefined): string { + if (!ts) return '—' + const diff = ts - Date.now() + const abs = Math.abs(diff) + const suffix = diff >= 0 ? '后' : '前' + if (abs < 60_000) return diff >= 0 ? '即将' : '刚刚' + if (abs < 3_600_000) return `${Math.round(abs / 60_000)} 分钟${suffix}` + if (abs < 86_400_000) return `${Math.round(abs / 3_600_000)} 小时${suffix}` + return `${Math.round(abs / 86_400_000)} 天${suffix}` +} + +/** + * 有效期读数:把「绝对时间戳」翻成手机上更好读的相对量。 + * 已过期返回「已过期」;缺失返回「—」。 + */ +export function formatExpiry(ts: number | null | undefined): string { + if (!ts) return '—' + const diff = ts - Date.now() + if (diff <= 0) return '已过期' + if (diff < 3_600_000) return `${Math.max(1, Math.round(diff / 60_000))} 分钟后` + if (diff < 86_400_000) return `${Math.round(diff / 3_600_000)} 小时后` + return `${Math.round(diff / 86_400_000)} 天后` +} + +/** 北京时间自然日的键(与后端签到日界同一口径) */ +export function beijingDayKey(ts = Date.now()): string { + const date = new Date(ts + 8 * 3_600_000) + return date.toISOString().slice(0, 10) +} + +/** 今天是否已经过某个时间戳(北京时间自然日) */ +export function isToday(ts: number | null | undefined): boolean { + if (!ts) return false + return beijingDayKey(ts) === beijingDayKey() +} + +/** 取账号的展示名:备注名 → 昵称 → 邮箱 → id */ +export function accountName(account: { + name?: string + nickname?: string + email?: string + id?: string +}): string { + return account.name || account.nickname || account.email || account.id || '未命名' +} + +/** 脱敏:保留首尾各 3 个字符,中间用 • 代替 */ +export function maskMiddle(text: string, keep = 3): string { + const value = String(text || '') + if (value.length <= keep * 2) return value + return `${value.slice(0, keep)}${'•'.repeat(Math.min(8, value.length - keep * 2))}${value.slice(-keep)}` +} + +/** 模型名太长时保留尾部(同名模型常靠尾缀区分) */ +export function ellipsisMiddle(text: string, max = 26): string { + const value = String(text || '') + if (value.length <= max) return value + const head = Math.ceil((max - 1) / 2) + const tail = Math.floor((max - 1) / 2) + return `${value.slice(0, head)}…${value.slice(-tail)}` +} + +/** + * 账号**当前生效**的限流记录(`rateLimits[model]`,按恢复时间升序)。 + * + * 口径与后端 `routing::rate_limit_reset_at`、桌面端 `activeLimits()` 一致: + * 只有 `resetAt` 在**未来**才算限流;记录还在但已过期 = 冷却自然结束, + * 不该再显示成「限流」。后端从不主动清理过期记录,所以这里必须自己过滤 —— + * 只判断「有没有键」会把所有历史限流过的账号永远标红。 + */ +export function activeLimits(account: { + rateLimits?: Record +}): Array<{ model: string; resetAt: number; status?: number | string; code?: string | number; message?: string }> { + const limits = account.rateLimits + if (!limits || typeof limits !== 'object') return [] + const now = Date.now() + return Object.entries(limits) + .map(([model, info]) => ({ model, resetAt: Number(info?.resetAt) || 0, ...(info || {}) })) + .filter(item => item.resetAt > now) + .sort((a, b) => a.resetAt - b.resetAt) +} + +/** 账号此刻是否处于限流中(与 {@link activeLimits} 同一口径) */ +export function isRateLimited(account: { + rateLimits?: Record +}): boolean { + return activeLimits(account).length > 0 +} + diff --git a/desktop-tauri/mobile/src/lib/providerFacts.ts b/desktop-tauri/mobile/src/lib/providerFacts.ts new file mode 100644 index 00000000..124d9493 --- /dev/null +++ b/desktop-tauri/mobile/src/lib/providerFacts.ts @@ -0,0 +1,135 @@ +/** + * 按提供商区分的事实(有效期字段 / 展示名 / 标识字段)。 + * + * ── 为什么要这张表 ──────────────────────────────────────────── + * 「有效期」落在账号记录的哪个键上**按家不同**:WorkBuddy 写 `expiresAt`, + * 小浣熊 / CatPaw / AutoClaw 写 `tokenExpiresAt`,KukuAI 压根没有这个字段。 + * 统一读一个键会出现「状态正常但有效期显示已过期」这种自相矛盾 —— 那是读了 + * 另一个家留下的空键。 + * + * 这张表从桌面端 `ui-islands/src/islands/accounts-domain.ts` 的 + * `PROVIDER_FEATURES` 摘出来,只保留手机端用到的四列(有效期 / 标识 / + * 长期有效 / 余额查询能力)。桌面端那张表还有 creditDetail / claim 等 + * 能力位,手机端不消费,不搬。 + * + * ── 加一家怎么办 ────────────────────────────────────────────── + * 认不出的 provider 走兜底:两个键都看,取先有值的那个。宁可兜底, + * 也不要因为漏登记而在界面上少一行读数。 + */ + +export type ProviderFacts = { + /** 有效期字段名;空串 = 这家没有有效期概念(不渲染那一行) */ + expiry: 'expiresAt' | 'tokenExpiresAt' | '' + /** 账号标识字段名(`uid` / `userId` / `account`),手机端只在详情里显示 */ + identifier: string + /** + * 主凭证**长期有效**(没有固定到期时刻):有效期显示「长期有效」而不是「—」。 + * + * 与 `expiry: ''` 的区别是语义 —— 后者只说「读不到到期时间」(可能是数据 + * 缺失,也可能上游根本不给),前者是明确断言「本来就没有到期这回事」。 + * 判据见桌面端 `accounts-domain.ts` 的同名字段:只收「令牌本身不过期」与 + * 「有不过期的主凭证 + 短期令牌自动续期」两类,**不收**「无续期、只能重登」 + * 的家(Loomy / MonkeyCode / CatPaw 的凭证有固定寿命)。 + */ + longLived?: boolean + /** 这家有没有余额查询能力(决定账号行/详情里渲不渲染余额读数) */ + usage: boolean +} + +const FACTS: Record = { + workbuddy: { expiry: 'expiresAt', identifier: 'uid', usage: true }, + 'workbuddy-intl': { expiry: 'expiresAt', identifier: 'uid', usage: true }, + raccoon: { expiry: 'tokenExpiresAt', identifier: 'userId', usage: true }, + catpaw: { expiry: 'tokenExpiresAt', identifier: 'uid', usage: true }, + autoclaw: { expiry: 'tokenExpiresAt', identifier: 'userId', usage: true }, + 'autoclaw-intl': { expiry: 'tokenExpiresAt', identifier: 'userId', usage: true }, + qoder: { expiry: 'expiresAt', identifier: 'userId', usage: true }, + 'qoder-intl': { expiry: 'expiresAt', identifier: 'userId', usage: true }, + 'cline-free': { expiry: 'expiresAt', identifier: 'account', usage: true }, + 'cline-pass': { expiry: 'expiresAt', identifier: 'account', usage: true }, + accio: { expiry: 'expiresAt', identifier: 'userId', usage: true }, + 'accio-cn': { expiry: 'expiresAt', identifier: 'userId', usage: true }, + zcode: { expiry: 'expiresAt', identifier: 'userId', usage: true }, + 'zcode-intl': { expiry: 'expiresAt', identifier: 'userId', usage: true }, + codearts: { expiry: 'expiresAt', identifier: 'userId', usage: true }, + trae: { expiry: 'expiresAt', identifier: 'uid', usage: true }, + loomy: { expiry: 'expiresAt', identifier: 'userId', usage: true }, + // BDUSS 读不出到期时刻,但上游随时可能作废、且没有续期接口 —— 只算「没有 + // 可显示的到期时间」,不算长期有效(与桌面端同一处置)。 + kuku: { expiry: '', identifier: 'uid', usage: true }, + officeace: { expiry: 'expiresAt', identifier: 'userId', usage: true }, + // 令牌有效期写在 JWT 的 `exp` 里、由网关自动续期(访客则由网关用同一设备号 + // 重新换发),用户不需要关心到期时刻。 + chatglm: { expiry: '', identifier: 'uid', longLived: true, usage: true }, // refresh token 不过期、access token 由网关提前 15 分钟自动续期。 + antigravity: { expiry: '', identifier: 'userId', longLived: true, usage: true }, + // `ms-` 令牌是长期有效的 API key,没有到期时刻可算。 + modelscope: { expiry: '', identifier: 'userId', longLived: true, usage: true }, + 'modelscope-intl': { expiry: '', identifier: 'userId', longLived: true, usage: true }, + // 单个静态 API Key,上游没有续期接口、key 本身也没有到期时刻。 + // 这家**没有余额查询**(`/alpha/billing/*` 只在添加账号时用),故 usage: false。 + commandcode: { expiry: '', identifier: 'id', longLived: true, usage: false }, + orcaterm: { expiry: 'expiresAt', identifier: 'userId', usage: true }, + // 千问办公:与桌面端 `accounts-domain.ts` 同口径(expiry: 'expiresAt'、 + // identifier: 'uid')—— JWT 的 exp 由导入时写入,账号标识存 userId + // (落库时 uid / userId 两个键写同一个值,见 account_store 的 add 路径)。 + qwenwork: { expiry: 'expiresAt', identifier: 'uid', usage: true }, + // QClaw:导入本机网关配置,令牌长期有效(本机回环网关,无到期概念)。 + qclaw: { expiry: '', identifier: 'userId', longLived: true, usage: true }, + // Coze(扣子):Cookie 读不出到期时刻,上游没有刷新接口 —— 与 KukuAI 同一 + // 处置(`expiry: ''`、不标 longLived)。余额来自 `trade/credit/balance`。 + coze: { expiry: '', identifier: 'uid', usage: true }, +} + +const FALLBACK: ProviderFacts = { expiry: 'expiresAt', identifier: 'userId', usage: false } + +export function providerFacts(id: string | undefined): ProviderFacts { + return (id && FACTS[id]) || FALLBACK +} + +/** 这家账号能不能查余额:自定义提供商看 balanceQuery.scheme,内置家看能力位 */ +export function supportsUsage(account: { provider?: string; balanceQuery?: unknown }): boolean { + const id = String(account.provider || '') + if (id.startsWith('custom-')) { + const scheme = (account.balanceQuery as { scheme?: unknown } | undefined)?.scheme + return Boolean(scheme) + } + return providerFacts(id).usage +} + +/** + * 取账号的有效期时间戳。 + * + * 顺序:本家登记的键 → 另一个键 → `tokenExpiresAt`(旧版通用字段)。 + * 全部为空时返回 0(界面显示「—」,不显示「已过期」—— 那两件事不一样)。 + */ +export function expiryOf(account: { + provider?: string + expiresAt?: number + tokenExpiresAt?: number +}): number { + const facts = providerFacts(account.provider) + if (facts.expiry === 'expiresAt') { + return Number(account.expiresAt) || Number(account.tokenExpiresAt) || 0 + } + if (facts.expiry === 'tokenExpiresAt') { + return Number(account.tokenExpiresAt) || Number(account.expiresAt) || 0 + } + // 这家没有有效期概念(KukuAI 之类):两个键都不看 + return 0 +} + +/** 这家有没有「有效期」这个概念(决定详情里渲不渲染那一格) */ +export function hasExpiry(provider: string | undefined): boolean { + const facts = providerFacts(provider) + return facts.expiry !== '' || facts.longLived === true +} + +/** + * 这家的主凭证是不是**长期有效**(没有固定到期时刻)。 + * + * 有它的家照样渲染「有效期」那一格,只是值写「长期有效」而不是「—」: + * 「—」读作「记录里没有到期时间」,长期有效读作「本来就不会到期」。 + */ +export function isLongLivedCredential(provider: string | undefined): boolean { + return providerFacts(provider).longLived === true +} diff --git a/desktop-tauri/mobile/src/lib/providers.ts b/desktop-tauri/mobile/src/lib/providers.ts new file mode 100644 index 00000000..cfbb5966 --- /dev/null +++ b/desktop-tauri/mobile/src/lib/providers.ts @@ -0,0 +1,88 @@ +/** + * 提供商图标与展示名。 + * + * ── 图标为什么用 而不是内联 SVG ──────────────────────── + * 提供商图标是一批彩色 PNG(`ui/assets/providers/*.png`),由桌面端同一套 + * 资源提供。移动端页面与它们同源(都在 `ui/` 下),直接引相对路径即可, + * 不必再打包一份。 + * + * ── 为什么没有「第二份提供商清单」───────────────────────────── + * 项目里明确禁止前端维护写死的 provider 列表(见 account_store 模块头): + * 加一家内置提供商时,后端注册表是唯一事实来源。这里的映射只解决一件 + * 后端不管的事 —— **图标文件叫什么、中文展示名怎么念**。认不出的 id + * 一律回落到首字徽章,界面不会空一块。 + */ + +/** 提供商 id → 图标文件(相对 `ui/` 根,页面在 `ui/m.html` 所以前缀 `m/` 不用加) */ +const ICONS: Record = { + workbuddy: 'assets/providers/workbuddy.png', + 'workbuddy-intl': 'assets/providers/workbuddy.png', + raccoon: 'assets/providers/raccoon.png', + catpaw: 'assets/providers/catpaw.png', + autoclaw: 'assets/providers/autoclaw.png', + 'autoclaw-intl': 'assets/providers/autoclaw.png', + qoder: 'assets/providers/qoder.png', + 'qoder-intl': 'assets/providers/qoder.png', + cline: 'assets/providers/cline.png', + 'cline-pass': 'assets/providers/cline.png', + accio: 'assets/providers/accio.png', + 'accio-intl': 'assets/providers/accio.png', + zcode: 'assets/providers/zcode.png', + 'zcode-intl': 'assets/providers/zcode.png', + codearts: 'assets/providers/codearts.png', + trae: 'assets/providers/trae.png', + loomy: 'assets/providers/loomy.png', + kuku: 'assets/providers/kuku.png', + monkeycode: 'assets/providers/monkeycode.png', + 'monkeycode-intl': 'assets/providers/monkeycode.png', + commandcode: 'assets/providers/commandcode.png', + antigravity: 'assets/providers/antigravity.png', + officeace: 'assets/providers/officeace.png', + orcaterm: 'assets/providers/orcaterm.png', + chatglm: 'assets/providers/chatglm.png', + qwenwork: 'assets/providers/qwenwork.png', + coze: 'assets/providers/coze.png', +} + +/** 后端没给 label 时的兜底展示名(认不出的 id 原样显示) */ +const LABELS: Record = { + workbuddy: 'WorkBuddy 国内版', + 'workbuddy-intl': 'WorkBuddy 国际版', + raccoon: '小浣熊', + catpaw: 'CatPaw', + autoclaw: 'AutoClaw 国内版', + 'autoclaw-intl': 'AutoClaw 国际版', + qoder: 'Qoder 中国版', + 'qoder-intl': 'Qoder 国际版', + cline: 'Cline Free', + 'cline-pass': 'Cline Pass', + accio: 'Accio 国际版', + 'accio-intl': 'Accio 国内版', + zcode: 'ZCode 国内版', + 'zcode-intl': 'ZCode 国际版', + codearts: 'CodeArts', + trae: 'Trae', + loomy: 'Loomy', + kuku: 'KukuAI', + monkeycode: 'MonkeyCode 国内版', + 'monkeycode-intl': 'MonkeyCode 国际版', + commandcode: 'Command Code', + antigravity: 'Antigravity', + officeace: 'OfficeAce 果办', + orcaterm: 'OrcaTerm', + chatglm: '智谱清言', + qwenwork: '千问办公', + coze: 'Coze 扣子', +} + +/** 图标地址;认不出的 id 返回空串(调用方画首字徽章) */ +export function providerIcon(id: string | undefined): string { + return (id && ICONS[id]) || '' +} + +/** 展示名:优先用后端给的 label,其次本地映射,最后原样回显 id */ +export function providerLabel(id: string | undefined, fallback?: string): string { + if (fallback) return fallback + if (!id) return '未知' + return LABELS[id] || id +} diff --git a/desktop-tauri/mobile/src/lib/pwa.ts b/desktop-tauri/mobile/src/lib/pwa.ts new file mode 100644 index 00000000..fdf5a9cd --- /dev/null +++ b/desktop-tauri/mobile/src/lib/pwa.ts @@ -0,0 +1,281 @@ +/** + * PWA 运行时:安装引导、更新通知、离线状态、独立窗口检测。 + * + * ── 为什么要自己包一层 ──────────────────────────────────────── + * 这几件事都有平台差异,散在页面里写会变成一堆 if: + * · **安装**:Chrome / Edge 有 `beforeinstallprompt`,可以弹原生安装框; + * iOS Safari 没有这个事件,只能教用户「分享 → 添加到主屏幕」; + * 已经在独立窗口里运行时两者都不该出现。 + * · **更新**:新 Service Worker 装好后要等页面发消息才接管(见 sw.js), + * 这里负责检测「有新版在等」并通知界面。 + * · **离线**:`navigator.onLine` 只说明有没有网卡连接,不代表网关可达 —— + * 这里只把它当作「可以提示」的信号,真正的可达性仍由接口请求判。 + * + * ── 为什么状态放模块级而不是 React context ──────────────────── + * `beforeinstallprompt` 可能在 React 挂载之前就触发(页面加载极快时), + * 事件对象只能捕获一次,错过就没有了。所以监听器必须在模块加载时就挂上, + * 状态再通过订阅分发给组件(见 lib/usePwa.ts)。 + */ + +export type PwaState = { + /** 可以弹原生安装框(Chrome / Edge 且未安装) */ + canInstall: boolean + /** iOS Safari:没有原生安装框,只能给「添加到主屏幕」的操作指引 */ + showIosHint: boolean + /** 已在独立窗口(standalone)里运行 */ + standalone: boolean + /** 有新版 Service Worker 在等待接管 */ + updateReady: boolean + /** 浏览器报告离线(网卡层,不代表网关可达) */ + offline: boolean + /** + * Service Worker 的状态。 + * · `checking` —— 注册还没返回,界面不该据此下结论; + * · `ready` —— 已注册,离线外壳可用; + * · `unsupported` —— 非安全上下文(局域网 http)或注册失败。 + */ + swState: 'checking' | 'ready' | 'unsupported' +} + +/** `beforeinstallprompt` 事件(TS 标准库没有,这里补最小形状) */ +type InstallPromptEvent = Event & { + prompt: () => Promise + userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }> +} + +let deferredPrompt: InstallPromptEvent | null = null +let started = false + +const listeners = new Set<() => void>() + +const state: PwaState = { + canInstall: false, + showIosHint: false, + standalone: false, + updateReady: false, + offline: false, + swState: 'checking', +} + +/** 缓存的快照对象:useSyncExternalStore 要求同一状态返回同一引用 */ +let snapshot: PwaState = { ...state } + +function emit(): void { + snapshot = { ...state } + listeners.forEach(listener => listener()) +} + +function set(patch: Partial): void { + let changed = false + for (const key of Object.keys(patch) as Array) { + if (state[key] !== patch[key]) { + state[key] = patch[key] as never + changed = true + } + } + if (changed) emit() +} + +/** 是否在独立窗口里运行(主屏图标启动 / 桌面 PWA 窗口) */ +function detectStandalone(): boolean { + if (typeof window === 'undefined') return false + // display-mode 是标准判据;iOS 早期只认 navigator.standalone + const queries = ['(display-mode: standalone)', '(display-mode: fullscreen)', '(display-mode: minimal-ui)'] + for (const query of queries) { + if (window.matchMedia(query).matches) return true + } + const legacy = (window.navigator as Navigator & { standalone?: boolean }).standalone + return legacy === true +} + +/** iOS / iPadOS 上的 Safari(含 iPadOS 13+ 伪装成 macOS 的情况) */ +function isIosSafari(): boolean { + if (typeof navigator === 'undefined') return false + const ua = navigator.userAgent + const isIos = /iPad|iPhone|iPod/.test(ua) || (ua.includes('Macintosh') && 'ontouchend' in document) + if (!isIos) return false + // Chrome / Firefox / Edge 在 iOS 上都是 WebKit 壳,但安装入口不同 —— + // 只有 Safari 有「添加到主屏幕」,其余浏览器打开时提示去 Safari + return /Safari/.test(ua) && !/CriOS|FxiOS|EdgiOS|OPiOS/.test(ua) +} + +/** + * 初始化。幂等:重复调用直接返回(模块级调用一次即可)。 + * + * 返回清理函数,主要是给「页面要卸载」的场景用;单页应用整个生命周期 + * 只调一次,不调清理也没问题。 + */ +export function initPwa(): () => void { + if (typeof window === 'undefined' || started) return () => undefined + started = true + + set({ standalone: detectStandalone() }) + + // ── 安装提示 ────────────────────────────────────────────── + const onBeforeInstall = (event: Event) => { + // 阻止默认的迷你信息条:我们要自己决定在设置页里什么时候展示 + event.preventDefault() + deferredPrompt = event as InstallPromptEvent + set({ canInstall: true, showIosHint: false }) + } + const onInstalled = () => { + deferredPrompt = null + set({ canInstall: false, showIosHint: false, standalone: true }) + } + window.addEventListener('beforeinstallprompt', onBeforeInstall) + window.addEventListener('appinstalled', onInstalled) + + // iOS 没有 beforeinstallprompt:用「未安装 + iOS Safari」推出操作指引 + if (!detectStandalone() && isIosSafari()) { + set({ showIosHint: true }) + } + + // display-mode 变化:从浏览器标签页切到独立窗口(安装后立刻发生) + const mediaQueries = [ + '(display-mode: standalone)', + '(display-mode: fullscreen)', + '(display-mode: minimal-ui)', + ].map(query => window.matchMedia(query)) + const onDisplayMode = () => { + const standalone = detectStandalone() + set({ standalone, showIosHint: standalone ? false : state.showIosHint }) + } + mediaQueries.forEach(mq => { + if (typeof mq.addEventListener === 'function') mq.addEventListener('change', onDisplayMode) + else mq.addListener(onDisplayMode) + }) + + // ── 离线状态 ────────────────────────────────────────────── + const onOnline = () => set({ offline: false }) + const onOffline = () => set({ offline: true }) + set({ offline: !navigator.onLine }) + window.addEventListener('online', onOnline) + window.addEventListener('offline', onOffline) + + // ── Service Worker 更新 ─────────────────────────────────── + let swCleanup: (() => void) | null = null + let disposed = false + + // 只在安全上下文里注册:局域网 http:// 下浏览器不提供 SW + if ('serviceWorker' in navigator && window.isSecureContext) { + void navigator.serviceWorker + // updateViaCache: 'none' —— 让浏览器每次都直接问服务器要 sw.js, + // 不走 HTTP 缓存。网关的静态文件带缓存头,默认策略下浏览器可能 + // 拿缓存里的旧脚本做更新比对,于是「有新版本」永远检测不到。 + .register('sw.js', { scope: '/', updateViaCache: 'none' }) + .then(registration => { + if (disposed) return + set({ swState: 'ready' }) + swCleanup = watchUpdates(registration) + }) + .catch(() => { + // 注册失败不影响使用(只是没有离线外壳) + set({ swState: 'unsupported' }) + }) + } else { + set({ swState: 'unsupported' }) + } + + return () => { + disposed = true + window.removeEventListener('beforeinstallprompt', onBeforeInstall) + window.removeEventListener('appinstalled', onInstalled) + mediaQueries.forEach(mq => { + if (typeof mq.removeEventListener === 'function') mq.removeEventListener('change', onDisplayMode) + else mq.removeListener(onDisplayMode) + }) + window.removeEventListener('online', onOnline) + window.removeEventListener('offline', onOffline) + swCleanup?.() + } +} + +/** + * 盯着 SW 的更新。 + * + * 两个入口: + * · `updatefound` —— 页面打开期间浏览器发现新版本并开始安装; + * · 页面加载时已经有一个 `waiting` 的 SW(上次访问时下好了但没接管)。 + * 两者都归结为「有新版在等」,界面给一个「重启以更新」。 + */ +function watchUpdates(registration: ServiceWorkerRegistration): () => void { + const markReady = () => set({ updateReady: true }) + + if (registration.waiting && navigator.serviceWorker.controller) { + markReady() + } + + const onUpdateFound = () => { + const installing = registration.installing + if (!installing) return + installing.addEventListener('statechange', () => { + // `installed` 且已有 controller = 有旧版在跑,新的在等 + if (installing.state === 'installed' && navigator.serviceWorker.controller) { + markReady() + } + }) + } + registration.addEventListener('updatefound', onUpdateFound) + + // 页面重新可见时主动查一次更新(手机上切回前台是常见时机) + const onVisible = () => { + if (document.visibilityState === 'visible') void registration.update().catch(() => undefined) + } + document.addEventListener('visibilitychange', onVisible) + + return () => { + registration.removeEventListener('updatefound', onUpdateFound) + document.removeEventListener('visibilitychange', onVisible) + } +} + +/** 订阅状态变化(配 React 的 useSyncExternalStore 用) */ +export function subscribePwa(listener: () => void): () => void { + listeners.add(listener) + return () => { + listeners.delete(listener) + } +} + +/** 读当前快照(同一状态返回同一引用,满足 useSyncExternalStore 的要求) */ +export function getPwaSnapshot(): PwaState { + return snapshot +} + +/** + * 弹原生安装框。返回用户的选择;没有可用事件时返回 `'unavailable'` + * (调用方据此展示手动指引)。 + */ +export async function promptInstall(): Promise<'accepted' | 'dismissed' | 'unavailable'> { + if (!deferredPrompt) return 'unavailable' + const prompt = deferredPrompt + deferredPrompt = null + set({ canInstall: false }) + try { + await prompt.prompt() + const choice = await prompt.userChoice + return choice.outcome + } catch { + return 'dismissed' + } +} + +/** 让等待中的新 SW 立刻接管(页面随后自己 reload) */ +export async function applyUpdate(): Promise { + const registration = await navigator.serviceWorker.getRegistration() + registration?.waiting?.postMessage({ type: 'SKIP_WAITING' }) + // 等 controller 换人再刷新,避免刷新后还是旧 SW 在服务 + await new Promise(resolve => { + let done = false + const finish = () => { + if (done) return + done = true + navigator.serviceWorker.removeEventListener('controllerchange', finish) + resolve() + } + navigator.serviceWorker.addEventListener('controllerchange', finish) + // 兜底:600ms 内没换人也刷新(比如 SW 已被回收) + window.setTimeout(finish, 600) + }) + window.location.reload() +} diff --git a/desktop-tauri/mobile/src/lib/servers.ts b/desktop-tauri/mobile/src/lib/servers.ts new file mode 100644 index 00000000..ae26c4ac --- /dev/null +++ b/desktop-tauri/mobile/src/lib/servers.ts @@ -0,0 +1,253 @@ +/** + * 服务器(网关)列表与当前选择。 + * + * ── 两种运行形态 ────────────────────────────────────────────── + * · **PWA / 浏览器**:页面就是网关出的,同源,地址恒为当前 origin。 + * 这时不该让用户填地址 —— 填了也只会把请求打到别处去。 + * · **APK**:界面打进包里,页面 origin 是 `http://localhost`,与网关 + * 必然跨源。用户要自己填地址,还可能有多台。 + * + * 判据是 `isPackaged()`:Capacitor 在原生壳里会往 window 挂 `Capacitor`, + * 浏览器里没有。不用 UA 判断 —— 桌面浏览器开 `/m` 与手机浏览器开是同一份代码。 + * + * ── 为什么每台服务器各存一份登录态 ──────────────────────────── + * 会话令牌是按服务器签发的,混着存会让「切到 B 却带着 A 的令牌」—— + * 表现是切换后立刻 401、再切回 A 又好了,很难查。所以令牌按服务器 id 分键。 + */ + +export type ServerEntry = { + /** 稳定 id(创建时生成,切换 / 删除都靠它,不随地址改而变) */ + id: string + /** 用户给这台起的名字,空则显示地址 */ + name: string + /** 归一化后的基地址,形如 `http://192.168.1.23:3065`(不带结尾斜杠) */ + base: string +} + +const SERVERS_KEY = 'agent2api.servers' +const ACTIVE_KEY = 'agent2api.activeServer' + +/** 是否跑在打包的原生壳里(Capacitor 注入的全局标记) */ +export function isPackaged(): boolean { + if (typeof window === 'undefined') return false + const cap = (window as Window & { Capacitor?: { isNativePlatform?: () => boolean } }).Capacitor + return Boolean(cap?.isNativePlatform?.()) +} + +/** + * 当前原生平台:`android` / `ios`,浏览器里是 `web`。 + * + * ── 为什么要区分到这一步 ────────────────────────────────────── + * 两种壳的「应用内更新」根本不同: + * · Android 能自己下载 APK 并调起安装器(ApkUpdaterPlugin); + * · iOS 不允许 App 自行安装可执行代码 —— 只能跳到 App Store / TestFlight, + * 所以那边不该出现「立即更新」这种下载按钮。 + * 只用 `isPackaged()` 判断会把 iOS 也当成 APK 形态,点下去只会报 + * 「当前不是 APK 形态」。 + */ +export type NativePlatform = 'android' | 'ios' | 'web' + +export function nativePlatform(): NativePlatform { + if (typeof window === 'undefined') return 'web' + const cap = (window as Window & { Capacitor?: { getPlatform?: () => string } }).Capacitor + const platform = cap?.getPlatform?.() + if (platform === 'android') return 'android' + if (platform === 'ios') return 'ios' + return 'web' +} + +/** + * 归一化用户输入的地址。 + * + * 用户会输 `192.168.1.23`、`192.168.1.23:3065`、`http://192.168.1.23:3065/`、 + * `192.168.1.23:3065/m`、`https://gw.example.com` 等各种形态。统一成 + * `scheme://host[:port]`: + * · 协议只认 http / https,写了别的(ftp:// 之类)视为非法; + * · 没写协议就补 `http://`(局域网网关基本都是 http); + * · 去掉结尾斜杠与路径(用户可能直接粘了 `/m` 或 `/v1`); + * · http 没写端口时补网关默认端口 3065;https 没写端口时不补,交给 + * 浏览器按标准 443 处理 —— 显式写 `:443` 反而会让某些反代/CDN 多一层歧义。 + * 非法输入返回空串,由调用方提示。 + */ +export function normalizeBase(raw: string): string { + let text = raw.trim() + if (!text) return '' + // 协议是「用户想走哪条链路」的唯一表达,必须保留;只接受 http / https + const schemeMatch = /^([a-zA-Z][a-zA-Z0-9+.-]*):\/\//.exec(text) + const scheme = schemeMatch ? schemeMatch[1].toLowerCase() : '' + if (scheme && scheme !== 'http' && scheme !== 'https') return '' + // 用户从浏览器地址栏复制时会带 /m、/v1、#hash 之类,一律砍掉 + text = text.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, '') + text = text.split(/[/?#]/)[0] + if (!text) return '' + // host 可以是 IPv4 / 主机名 / 方括号 IPv6;方括号里的冒号要整体匹配 + const match = /^(\[[0-9a-fA-F:]+\]|[^:]+)(?::(\d+))?$/.exec(text) + if (!match) return '' + const host = match[1].toLowerCase() + const port = match[2] + // 只接受 IPv4 / 主机名 / 方括号 IPv6;localhost 也走这条 + if (!/^[\w.-]+$/.test(host) && !/^\[[0-9a-fA-F:]+\]$/.test(host)) return '' + if (port && (Number(port) < 1 || Number(port) > 65535)) return '' + if (port) return `${scheme || 'http'}://${host}:${port}` + // 没写端口:http 补网关默认端口,https 走标准 443(不显式写出来) + return scheme === 'https' ? `https://${host}` : `http://${host}:3065` +} + +/** + * 从基地址里取出给用户看的 host:port。 + * + * 只省略 `http://`:明文是局域网网关的默认形态,省掉更省地方;`https://` + * 必须留着 —— 用户要能一眼看出这条地址走的是不是加密链路。 + */ +export function baseLabel(base: string): string { + return base.replace(/^http:\/\//, '') +} + +function readServers(): ServerEntry[] { + try { + const raw = localStorage.getItem(SERVERS_KEY) + if (!raw) return [] + const parsed = JSON.parse(raw) as unknown + if (!Array.isArray(parsed)) return [] + return parsed.filter( + (item): item is ServerEntry => + Boolean(item) && typeof item === 'object' && typeof (item as ServerEntry).base === 'string', + ) + } catch { + return [] + } +} + +function writeServers(list: ServerEntry[]): void { + try { + localStorage.setItem(SERVERS_KEY, JSON.stringify(list)) + } catch { + /* 隐私模式:本次会话有效,下次要重填 */ + } +} + +export function listServers(): ServerEntry[] { + return readServers() +} + +/** 当前服务器;未配置过返回 null(APK 首次启动就是这个状态) */ +export function activeServer(): ServerEntry | null { + if (!isPackaged()) { + // 浏览器 / PWA:永远是当前 origin,不进列表 + return { id: 'origin', name: '', base: location.origin } + } + const list = readServers() + let id = '' + try { + id = localStorage.getItem(ACTIVE_KEY) || '' + } catch { + /* 忽略 */ + } + return list.find(item => item.id === id) || list[0] || null +} + +/** 请求用的基地址;没有可用服务器时返回空串(调用方应先进配置页) */ +export function apiBase(): string { + return activeServer()?.base || '' +} + +/** 当前服务器 id(登录态按它分键) */ +export function activeServerId(): string { + return activeServer()?.id || '' +} + +/** + * 新增或更新一台服务器。返回它本身(已存在同地址则复用那条记录, + * 不产生重复项)。 + */ +export function saveServer(input: { id?: string; name?: string; base: string }): ServerEntry { + const base = normalizeBase(input.base) + if (!base) throw new Error('地址不合法') + const list = readServers() + const existing = input.id + ? list.find(item => item.id === input.id) + : list.find(item => item.base === base) + if (existing) { + existing.base = base + existing.name = (input.name || '').trim() + writeServers(list) + return existing + } + const entry: ServerEntry = { + id: `srv_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`, + name: (input.name || '').trim(), + base, + } + writeServers([...list, entry]) + return entry +} + +export function removeServer(id: string): void { + const list = readServers().filter(item => item.id !== id) + writeServers(list) + clearServerAuth(id) + if (activeServerId() === id) { + try { + localStorage.setItem(ACTIVE_KEY, list[0]?.id || '') + } catch { + /* 忽略 */ + } + } +} + +export function setActiveServer(id: string): void { + try { + localStorage.setItem(ACTIVE_KEY, id) + } catch { + /* 忽略 */ + } +} + +/* ── 按服务器隔离的凭证 ───────────────────────────────────── */ + +const KEY_STORAGE = (id: string) => `agent2api.webKey.${id}` +const PANEL_AUTH = (id: string) => `agent2api.panelAuth.${id}` + +export type PanelAuth = { access?: string; refresh?: string } + +export function readServerKey(id: string): string { + try { + return localStorage.getItem(KEY_STORAGE(id)) || '' + } catch { + return '' + } +} + +export function storeServerKey(id: string, value: string): void { + try { + if (value) localStorage.setItem(KEY_STORAGE(id), value) + else localStorage.removeItem(KEY_STORAGE(id)) + } catch { + /* 隐私模式:只留在内存 */ + } +} + +export function readServerAuth(id: string): PanelAuth | null { + try { + const raw = localStorage.getItem(PANEL_AUTH(id)) + return raw ? (JSON.parse(raw) as PanelAuth) : null + } catch { + return null + } +} + +export function storeServerAuth(id: string, access: string, refresh: string): void { + try { + localStorage.setItem(PANEL_AUTH(id), JSON.stringify({ access, refresh })) + } catch { + /* 同上 */ + } +} + +export function clearServerAuth(id: string): void { + try { + localStorage.removeItem(PANEL_AUTH(id)) + } catch { + /* 无害 */ + } +} diff --git a/desktop-tauri/mobile/src/lib/usage.ts b/desktop-tauri/mobile/src/lib/usage.ts new file mode 100644 index 00000000..76c07745 --- /dev/null +++ b/desktop-tauri/mobile/src/lib/usage.ts @@ -0,0 +1,170 @@ +/** + * 余额读数解析。 + * + * 后端的 `usage` 对象按家给不同形状(workbuddy 的 `totalLeft` 既有契约、 + * 归一化的 `available` + `wallets`、以及只给展示串的 `availableView`)。 + * 这里**按字段形状探测**而不是按 provider 分派:provider 只决定谁去查, + * 不决定查回来长什么样。桌面端 `accounts-panels.tsx` 的 `usageSummary` + * 是同一套口径,手机端只保留「一行读数 + 一句完整说明」两档。 + * + * 解析失败一律降级成中性读数(`muted`),不抛错 —— 余额查不到是常态, + * 不该把账号页拖红。 + */ + +import type { UsageRow, UsageSnapshot } from '../api/gateway' +import { t } from '../i18n' + +export type UsageView = { + kind: 'ok' | 'warn' | 'bad' | 'muted' + /** 行内短读数,例如「可用 12.3 万」「未查询」「查询失败」 */ + text: string + /** 完整说明(给详情页/长按提示用) */ + detail: string +} + +/** + * 紧凑数字:整数走千分位,超过一万用「万」、超过一亿用「亿」。 + * + * 刻意不 import `lib/format` 的同名能力:那个模块依赖页面层,反过来被 + * 数据层引用会绕出循环;这里只服务余额一种口径,自己写一份更稳。 + */ +function compact(value: unknown): string { + // null / 空串不能被 Number() 悄悄翻成 0:那会把「读不到」显示成「可用 0」 + if (value === null || value === undefined || value === '') return '—' + const num = typeof value === 'number' ? value : Number(value) + if (!Number.isFinite(num)) return '—' + const abs = Math.abs(num) + // 「万 / 亿」只保留 1~2 位小数:行内读数要短,精度对余额没有行动价值 + if (abs >= 1e8) return `${(num / 1e8).toFixed(2)} 亿` + if (abs >= 1e4) return `${(num / 1e4).toFixed(1)} 万` + if (Number.isInteger(num)) return num.toLocaleString('zh-CN') + return num.toLocaleString('zh-CN', { maximumFractionDigits: 2 }) +} + +/** usage 里可读的「单位」:缺失时按积分兜底 */ +function unitOf(usage: Record): string { + const unit = usage.unit + return typeof unit === 'string' && unit.trim() ? unit : t('积分') +} + +/** 把「部分失败」的两条错误拼成 detail 尾巴(CodeArts 这类两份账的家) */ +function partialErrors(usage: Record): string[] { + const parts: string[] = [] + if (usage.statisticsError) { + parts.push(t('订阅统计未读到:{error}', { error: String(usage.statisticsError) })) + } + if (usage.benefitError) { + parts.push(t('福利网关未读到:{error}', { error: String(usage.benefitError) })) + } + return parts +} + +/** 单行余额读数:把快照里的 `usage` / `error` 翻成一行文本 + 完整说明 */ +export function usageText(row?: UsageRow): UsageView { + // ① 还没查过(快照里没有这一行) + if (row === undefined) { + return { kind: 'muted', text: t('未查询'), detail: t('尚未查询该账号的余额') } + } + + // ② 这一行明确失败:「未配置查询凭证」是中性提示,其余才是红色失败 + if (row.error) { + if (row.code === 'usage_not_configured') { + return { kind: 'muted', text: t('未配置'), detail: row.error } + } + return { kind: 'bad', text: t('查询失败'), detail: row.error } + } + + const usage = row.usage + if (!usage || typeof usage !== 'object' || Array.isArray(usage)) { + return { kind: 'muted', text: t('无数据'), detail: t('未返回可识别的余额数据') } + } + + const data = usage as Record + const errors = partialErrors(data) + const baseKind: UsageView['kind'] = errors.length ? 'warn' : 'ok' + const withErrors = (view: UsageView): UsageView => { + if (!errors.length) return view + return { + kind: 'warn', + text: view.text, + detail: [view.detail, ...errors].filter(Boolean).join(' · '), + } + } + + // ④ workbuddy 既有形状:totalLeft / planLeft / bonusLeft + if (Object.prototype.hasOwnProperty.call(data, 'totalLeft')) { + const total = data.unlimited === true ? '∞' : compact(data.totalLeft) + const parts = [t('总剩余 {total}', { total })] + if (data.planLeft !== undefined && data.planLeft !== null) { + parts.push(t('套餐 {plan}', { plan: compact(data.planLeft) })) + } + if (data.bonusLeft !== undefined && data.bonusLeft !== null) { + parts.push(t('奖励 {bonus}', { bonus: compact(data.bonusLeft) })) + } + return withErrors({ kind: baseKind, text: t('可用 {total}', { total }), detail: parts.join(' · ') }) + } + + // ⑤ 后端已经给好的紧凑展示串(ZCode 的「1亿 token」之类)优先 + if (typeof data.availableView === 'string' && data.availableView.trim()) { + const text = data.availableView.trim() + const parts = [text] + if (data.available !== undefined && data.available !== null) { + parts.push(t('可用 {value} {unit}', { value: compact(data.available), unit: unitOf(data) })) + } + return withErrors({ kind: baseKind, text, detail: parts.join(' · ') }) + } + + // ⑥ 归一化形状:available 是数字 + if (typeof data.available === 'number' && Number.isFinite(data.available)) { + return withErrors({ + kind: baseKind, + text: t('可用 {value} {unit}', { value: compact(data.available), unit: unitOf(data) }), + detail: t('可用 {value} {unit}', { value: compact(data.available), unit: unitOf(data) }), + }) + } + + // ⑦ 只有钱包明细:取第一个带 balance 的钱包做读数,全部钱包进 detail + if (Array.isArray(data.wallets)) { + const wallets = data.wallets.filter( + (item): item is Record => Boolean(item) && typeof item === 'object', + ) + const lead = wallets.find(item => item.balance !== undefined && item.balance !== null) + if (lead) { + const walletUnit = typeof lead.unit === 'string' && lead.unit.trim() ? lead.unit : unitOf(data) + const detail = wallets + .map(item => { + const name = + (typeof item.displayName === 'string' && item.displayName) || + (typeof item.type === 'string' && item.type) || + t('明细') + const balance = + item.balance !== undefined && item.balance !== null + ? compact(item.balance) + : typeof item.balanceView === 'string' && item.balanceView + ? item.balanceView + : '—' + return `${name} ${balance}` + }) + .join(' · ') + return withErrors({ + kind: baseKind, + text: t('可用 {value} {unit}', { value: compact(lead.balance), unit: walletUnit }), + detail, + }) + } + } + + // ⑧ 形状认不出来 + return { kind: 'muted', text: t('无数据'), detail: t('未返回可识别的余额数据') } +} + +/** 把快照行按账号 id 索引(没有 id 的行跳过) */ +export function usageIndex(snapshot: UsageSnapshot | null): Map { + const map = new Map() + const rows = snapshot?.results + if (!Array.isArray(rows)) return map + rows.forEach(row => { + if (row && typeof row.id === 'string' && row.id) map.set(row.id, row) + }) + return map +} diff --git a/desktop-tauri/mobile/src/lib/usePolling.ts b/desktop-tauri/mobile/src/lib/usePolling.ts new file mode 100644 index 00000000..5fe6662a --- /dev/null +++ b/desktop-tauri/mobile/src/lib/usePolling.ts @@ -0,0 +1,75 @@ +import * as React from 'react' + +/** + * 按页面可见性刷新的轮询 hook。 + * + * 手机端的轮询必须满足三件事,缺一个就会变成「后台偷偷烧流量」: + * ① 页面不可见(切到别的 App / 锁屏)时停表; + * ② 上一次请求还没回来时不叠加下一次; + * ③ 卸载即停,不留悬挂定时器。 + * + * `interval` 传 0 表示不轮询(只跑首次)。 + */ +export function usePolling( + run: () => Promise | void, + interval: number, + deps: React.DependencyList = [], +): { refreshing: boolean; refresh: () => void } { + const runRef = React.useRef(run) + runRef.current = run + const [refreshing, setRefreshing] = React.useState(false) + const busyRef = React.useRef(false) + const timerRef = React.useRef(null) + + const tick = React.useCallback(async () => { + if (busyRef.current) return + busyRef.current = true + setRefreshing(true) + try { + await runRef.current() + } finally { + busyRef.current = false + setRefreshing(false) + } + }, []) + + React.useEffect(() => { + let disposed = false + const clear = () => { + if (timerRef.current !== null) { + window.clearInterval(timerRef.current) + timerRef.current = null + } + } + const start = () => { + clear() + if (interval <= 0 || disposed) return + timerRef.current = window.setInterval(() => { + if (document.visibilityState === 'visible') void tick() + }, interval) + } + + void tick() + start() + + const onVisibility = () => { + if (document.visibilityState === 'visible') { + // 回到前台立刻补一次,用户不会盯着旧数据 + void tick() + start() + } else { + clear() + } + } + document.addEventListener('visibilitychange', onVisibility) + + return () => { + disposed = true + clear() + document.removeEventListener('visibilitychange', onVisibility) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [interval, tick, ...deps]) + + return { refreshing, refresh: () => void tick() } +} diff --git a/desktop-tauri/mobile/src/lib/usePwa.ts b/desktop-tauri/mobile/src/lib/usePwa.ts new file mode 100644 index 00000000..bcbcaba9 --- /dev/null +++ b/desktop-tauri/mobile/src/lib/usePwa.ts @@ -0,0 +1,13 @@ +import { useSyncExternalStore } from 'react' +import { getPwaSnapshot, subscribePwa, type PwaState } from './pwa' + +/** + * 订阅 PWA 状态。 + * + * `getPwaSnapshot` 返回的是同一份缓存快照(状态没变时引用不变), + * 满足 useSyncExternalStore 对「同状态必须同引用」的要求 —— 否则每次 + * 渲染都会被判定为变了,进而无限重渲染。 + */ +export function usePwa(): PwaState { + return useSyncExternalStore(subscribePwa, getPwaSnapshot, getPwaSnapshot) +} diff --git a/desktop-tauri/mobile/src/main.tsx b/desktop-tauri/mobile/src/main.tsx new file mode 100644 index 00000000..6bfa72b3 --- /dev/null +++ b/desktop-tauri/mobile/src/main.tsx @@ -0,0 +1,37 @@ +import { createRoot } from 'react-dom/client' +import { App } from './App' +import { setAuthLostHandler } from './api/client' +import { store } from './state/store' +import { initPwa } from './lib/pwa' +import './styles/mobile.css' + +/** + * 手机端入口。 + * + * 与 ui-islands 的区别:那个是「一堆岛挂到既有页面上」,这个是**独占整个 + * 页面**的应用 —— 所以用 createRoot 挂到 #root,不需要 `display: contents` + * 那套宿主技巧。 + * + * 401 接管:客户端自己不做跳转(它不知道当前界面在做什么),由这里注册 + * 回调,交给 store 弹「重新登录 / 补 Key」层。 + */ +setAuthLostHandler(mode => { + store.setAuthLost(mode) +}) + +/** + * PWA 运行时必须在 render 之前初始化,不能放进组件的 useEffect: + * `beforeinstallprompt` 在页面加载后不久就会触发,而事件对象只能捕获一次 —— + * 等 React 挂载完再挂监听,有可能已经错过(首屏快的时候)。 + * 这里的返回值是清理函数,单页应用整个生命周期不卸载,忽略即可。 + */ +initPwa() + +const host = document.getElementById('root') +if (host) { + createRoot(host).render() +} else { + // 挂载点缺失时给出可读错误,而不是静默白屏 + document.body.innerHTML = + '
手机端挂载失败:页面缺少 #root 容器。
' +} diff --git a/desktop-tauri/mobile/src/pages/AccountSheet.tsx b/desktop-tauri/mobile/src/pages/AccountSheet.tsx new file mode 100644 index 00000000..9172a63a --- /dev/null +++ b/desktop-tauri/mobile/src/pages/AccountSheet.tsx @@ -0,0 +1,450 @@ +import { useEffect, useState } from 'react' +import { Sheet, SheetContent, SheetHeader, SheetBody, SheetFooter, SheetTitle } from '@ui' +import { Icon } from '../components/Icon' +import { ProviderLogo } from '../components/ProviderLogo' +import { Badge } from '../components/ui' +import { store } from '../state/store' +import { + clearAccountRateLimits, + deleteAccount, + fetchAccountUsage, + fetchUsageSnapshot, + promoteAccount, + refreshAccount, + renameAccount, + setAccountEnabled, + type Account, + type UsageRow, +} from '../api/gateway' +import { accountName, activeLimits, formatExpiry, formatRelative, formatStamp, isRateLimited } from '../lib/format' +import { expiryOf, hasExpiry, isLongLivedCredential } from '../lib/providerFacts' +import { usageIndex, usageText } from '../lib/usage' +import { providerLabel } from '../lib/providers' +import { t } from '../i18n' + +/** + * 账号详情抽屉。 + * + * 手机上不做「编辑表单」:账号的凭证、代理、限流规则都是多字段表单,窄屏填 + * 起来痛苦且容易出错。这一层只给**查看 + 几个高频动作**(启停、刷新凭证、 + * 查询余额、设为首选、清除限流、删除),编辑仍回桌面端。 + */ +export function AccountSheet({ + account, + onClose, + onChanged, +}: { + account: Account | null + onClose: () => void + onChanged: () => void +}) { + const [busy, setBusy] = useState('') + /** 快照里这一行的余额结论;undefined = 还没查过 */ + const [usageRow, setUsageRow] = useState(undefined) + const [editingName, setEditingName] = useState(false) + const [nameDraft, setNameDraft] = useState('') + const open = Boolean(account) + const accountId = account?.id || '' + + // 换账号(或关掉抽屉)时重置:编辑态不能带到下一个账号上。 + // 余额走快照而不是逐个查询 —— 快照只读记录表,不触发上游,打开抽屉不会变慢。 + useEffect(() => { + setEditingName(false) + setNameDraft('') + setUsageRow(undefined) + if (!accountId) return + let cancelled = false + fetchUsageSnapshot() + .then(snapshot => { + if (!cancelled) setUsageRow(usageIndex(snapshot).get(accountId)) + }) + .catch(() => { + /* 余额快照读不到是常态:保持「未查询」,不打扰用户 */ + }) + return () => { + cancelled = true + } + }, [accountId]) + + const run = async (kind: string, task: () => Promise, success: string) => { + setBusy(kind) + try { + await task() + store.toast(success) + onChanged() + } catch (error) { + store.toast(error instanceof Error ? error.message : String(error), 'err') + } finally { + setBusy('') + } + } + + const remove = async () => { + if (!account) return + const name = accountName(account) + // 删除不可恢复:手机上用两次确认(第一次进二次确认态) + if (!window.confirm(t('确定删除账号「{name}」?这个操作不可恢复。', { name }))) return + await run('delete', () => deleteAccount(account.id), t('已删除账号')) + onClose() + } + + /** 手动查这一个账号:会真的打上游,可能慢,所以单独走 busy='usage' */ + const queryUsage = async () => { + if (!account) return + setBusy('usage') + try { + const payload = await fetchAccountUsage(account.id) + setUsageRow(payload.results?.[0]) + store.toast(t('余额已更新')) + } catch (error) { + store.toast(error instanceof Error ? error.message : String(error), 'err') + } finally { + setBusy('') + } + } + + const saveName = async () => { + if (!account) return + const next = nameDraft.trim() + if (!next) { + store.toast(t('备注名不能为空'), 'err') + return + } + setBusy('name') + try { + await renameAccount(account.id, next) + store.toast(t('备注名已更新')) + setEditingName(false) + onChanged() + } catch (error) { + store.toast(error instanceof Error ? error.message : String(error), 'err') + } finally { + setBusy('') + } + } + + /** 清除限流:不传 model = 清掉这个账号全部模型 */ + const clearLimit = async (model?: string) => { + if (!account) return + const question = model + ? t('确定清除模型「{model}」的限流标记?', { model }) + : t('确定清除全部模型的限流标记?') + if (!window.confirm(question)) return + await run('clear', () => clearAccountRateLimits(account.id, model), t('限流已清除')) + } + + const promote = async () => { + if (!account) return + await run('promote', () => promoteAccount(account.id), t('已设为首选')) + } + + const limits = account ? activeLimits(account) : [] + const expiresAt = account ? expiryOf(account) : 0 + const usageView = usageText(usageRow) + const usageColor = + usageView.kind === 'bad' + ? 'var(--m-danger)' + : usageView.kind === 'warn' + ? 'var(--m-warn)' + : 'var(--m-text)' + const isPrimary = account?.priority === 1 + + return ( + { if (!next) onClose() }}> + + + {t('账号详情')} + + + {account ? ( + <> +
+ +
+
+ {accountName(account)} +
+
+ {providerLabel(account.provider, account.editionLabel)} + {account.priority !== undefined ? ` · #${account.priority}` : ''} +
+
+ + {statusText(account)} + +
+ + {/* 备注名就地编辑:手机上没有表单页,改一个字段不该跳走 */} +
+ {editingName ? ( + <> + setNameDraft(event.target.value)} + placeholder={t('备注名')} + style={{ + flex: 1, + minWidth: 0, + border: 0, + outline: 'none', + background: 'transparent', + color: 'var(--m-text)', + // 16px 以下 iOS Safari 聚焦时会自动放大页面 + fontSize: 16, + }} + /> + + + + ) : ( + + )} +
+ +
+ {hasExpiry(account.provider) ? ( +
+
{t('有效期')}
+
+ {isLongLivedCredential(account.provider) ? t('长期有效') : formatExpiry(expiresAt)} +
+
+ ) : null} +
+
{t('余额')}
+
+ {usageView.text} +
+
+
+
{t('凭证')}
+
{account.hasCredentials === false ? t('缺失') : t('完整')}
+
+
+
{t('限流模型')}
+
{limits.length}
+
+
+
{t('最近更新')}
+
+ {formatStamp(account.updatedAt as number)} +
+
+
+ + {/* 首选是队列位次,不是账号属性:给它一个整行按钮,别混进删除那一排 */} + + +
+
+ {t('账号 ID')} + + {account.id} + +
+ {account.email ? ( +
+ {t('邮箱')} + {account.email} +
+ ) : null} +
+ {t('启用')} + {account.enabled ? t('是') : t('否')} +
+ {/* 读数之外还有话要说时才占一行(部分失败、钱包明细) */} + {usageView.detail && usageView.detail !== usageView.text ? ( +
+ {t('余额详情')} + + {usageView.detail} + +
+ ) : null} + {account.needsRelogin ? ( +
+ {t('需要重新登录')} + + {account.needsReloginReason || t('凭证已被上游注销')} + +
+ ) : null} +
+ + {limits.length ? ( + <> +
+ {t('限流明细')} + +
+
+ {limits.map(({ model, resetAt }) => ( +
+ + {model} + + + {t('{time} 恢复', { time: formatRelative(resetAt) })} + + +
+ ))} +
+ + ) : null} + + ) : null} +
+ + + +
+ + +
+
+
+
+ ) +} + +function statusTone(account: Account): 'ok' | 'warn' | 'bad' | 'mute' { + if (!account.enabled) return 'mute' + if (account.needsRelogin) return 'bad' + if (isRateLimited(account)) return 'warn' + const expiresAt = expiryOf(account) + if (expiresAt > 0 && expiresAt <= Date.now()) return 'bad' + return 'ok' +} + +function statusText(account: Account): string { + if (!account.enabled) return t('已禁用') + if (account.needsRelogin) return t('需重登') + if (isRateLimited(account)) return t('限流中') + const expiresAt = expiryOf(account) + if (expiresAt > 0 && expiresAt <= Date.now()) return t('已过期') + return t('正常') +} diff --git a/desktop-tauri/mobile/src/pages/AccountsPage.tsx b/desktop-tauri/mobile/src/pages/AccountsPage.tsx new file mode 100644 index 00000000..ee2dad48 --- /dev/null +++ b/desktop-tauri/mobile/src/pages/AccountsPage.tsx @@ -0,0 +1,577 @@ +import { useMemo, useState } from 'react' +import { PageHead, IconButton, Badge, Empty, ErrorNote, SkeletonList } from '../components/ui' +import { ProviderLogo } from '../components/ProviderLogo' +import { Icon } from '../components/Icon' +import { AccountSheet } from './AccountSheet' +import { + AccountsFilterSheet, + DEFAULT_FILTERS, + activeFilterCount, + type AccountFilters, +} from '../components/AccountsFilterSheet' +import { useStore, store } from '../state/store' +import { + fetchUsageSnapshot, + setAccountEnabled, + type Account, + type UsageRow, + type UsageSnapshot, +} from '../api/gateway' +import { accountName, formatExpiry, formatRelative, isRateLimited } from '../lib/format' +import { expiryOf, hasExpiry, isLongLivedCredential, supportsUsage } from '../lib/providerFacts' +import { usageIndex, usageText } from '../lib/usage' +import { providerLabel } from '../lib/providers' +import { usePolling } from '../lib/usePolling' +import { usePullToRefresh, PullIndicator } from '../components/PullToRefresh' +import { t } from '../i18n' + +/** + * 账号。 + * + * ── 为什么是分组卡片而不是表格 ──────────────────────────────── + * 桌面端账号表有 10 列(顺序 / 账号 / 代理 / 连接数 / 状态 / 限流 / 有效期 / + * 余额 / 操作…),压不进 390px。手机上只留五个关键读数:邮箱、状态、有效期、 + * 余额、代理;提供商信息上移到分组头,行里不再重复。 + * + * ── 分组口径 ────────────────────────────────────────────────── + * 与后端一致:同一提供商的账号是一组,组内按 priority 升序(= 转发尝试顺序)。 + * 组本身的先后也按最小 priority 排 —— 这就是全局队列在手机上的投影。 + */ + +type Filter = 'all' | 'enabled' | 'limited' | 'disabled' + +export function AccountsPage() { + const state = useStore() + const [filters, setFilters] = useState(DEFAULT_FILTERS) + const [filterOpen, setFilterOpen] = useState(false) + const [query, setQuery] = useState('') + const [openId, setOpenId] = useState(null) + const [error, setError] = useState('') + const [busy, setBusy] = useState(false) + const [usage, setUsage] = useState(null) + const accounts = state.session?.accounts?.accounts || [] + + const load = async () => { + // 余额快照与账号刷新并行:快照失败不影响账号列表(余额查不到是常态, + // 静默降级成「未查询」,不弹 toast 也不进页内错误条) + void fetchUsageSnapshot() + .then(setUsage) + .catch(() => {}) + try { + await store.refresh(true) + setError('') + } catch (failure) { + setError(failure instanceof Error ? failure.message : String(failure)) + } + } + + usePolling(load, 30_000) + + const pull = usePullToRefresh({ onRefresh: load }) + + // 快照行按 id 索引:每次 render 重建 Map 太浪费,且行组件会因此白白重渲 + const usageById = useMemo(() => usageIndex(usage), [usage]) + + /** 这家有没有「有效期」概念、是不是长期有效、到期时刻 —— 筛选与行渲染共用 */ + const expiryInfo = (account: Account) => { + const at = expiryOf(account) + return { + at, + has: hasExpiry(account.provider), + longLived: isLongLivedCredential(account.provider), + expired: at > 0 && at <= Date.now(), + } + } + + /** 余额读数归成三档(未查询 / 失败 / 有数)—— 与 usageText 的 kind 对应 */ + const usageState = (account: Account): 'has' | 'unqueried' | 'failed' => { + if (!supportsUsage(account)) return 'unqueried' + const row = usageById.get(account.id) + if (!row) return 'unqueried' + if (row.error) return 'failed' + return 'has' + } + + /** 各维度选项的计数:基于**搜索词过滤后**的集合,数字与点进去看到的一致 */ + const counts = useMemo(() => { + const keyword = query.trim().toLowerCase() + const base = accounts.filter(item => { + if (!keyword) return true + const haystack = [item.name, item.nickname, item.email, item.id, item.provider] + .filter(Boolean) + .join(' ') + .toLowerCase() + return haystack.includes(keyword) + }) + const expiryOfAccount = (account: Account) => { + const at = expiryOf(account) + return { + at, + longLived: isLongLivedCredential(account.provider), + expired: at > 0 && at <= Date.now(), + soon: at > Date.now() && at - Date.now() <= 7 * 86_400_000, + } + } + const usageOfAccount = (account: Account): 'has' | 'unqueried' | 'failed' => { + if (!supportsUsage(account)) return 'unqueried' + const row = usageById.get(account.id) + if (!row) return 'unqueried' + if (row.error) return 'failed' + return 'has' + } + return { + status: { + all: base.length, + enabled: base.filter(item => item.enabled).length, + disabled: base.filter(item => !item.enabled).length, + limited: base.filter(item => item.enabled && isRateLimited(item)).length, + relogin: base.filter(item => item.needsRelogin).length, + expired: base.filter(item => expiryOfAccount(item).expired).length, + }, + expiry: { + all: base.length, + soon: base.filter(item => expiryOfAccount(item).soon).length, + expired: base.filter(item => expiryOfAccount(item).expired).length, + longlived: base.filter(item => expiryOfAccount(item).longLived).length, + }, + usage: { + all: base.length, + has: base.filter(item => usageOfAccount(item) === 'has').length, + unqueried: base.filter(item => usageOfAccount(item) === 'unqueried').length, + failed: base.filter(item => usageOfAccount(item) === 'failed').length, + }, + } + // usageById 变化(余额刷新)要重算余额那组计数 + }, [accounts, query, usageById]) + + /** 可选的提供商(按当前账号里出现过的去重,保持后端给的先后) */ + const providerOptions = useMemo(() => { + const seen = new Set() + const list: Array<{ id: string; label: string }> = [] + accounts.forEach(item => { + const id = String(item.provider || 'unknown') + if (seen.has(id)) return + seen.add(id) + list.push({ id, label: providerLabel(id) }) + }) + return list + }, [accounts]) + + const filtered = useMemo(() => { + const keyword = query.trim().toLowerCase() + return accounts.filter(item => { + // ① 状态(单选) + if (filters.status === 'enabled' && !item.enabled) return false + if (filters.status === 'disabled' && item.enabled) return false + if (filters.status === 'limited' && !(item.enabled && isRateLimited(item))) return false + if (filters.status === 'relogin' && !item.needsRelogin) return false + if (filters.status === 'expired' && !expiryInfo(item).expired) return false + // ② 提供商(多选,空 = 不限) + if (filters.providers.length && !filters.providers.includes(String(item.provider || 'unknown'))) { + return false + } + // ③ 有效期 + if (filters.expiry !== 'all') { + const info = expiryInfo(item) + if (filters.expiry === 'longlived' && !info.longLived) return false + if (filters.expiry === 'expired' && !info.expired) return false + if (filters.expiry === 'soon') { + if (!(info.at > Date.now() && info.at - Date.now() <= 7 * 86_400_000)) return false + } + } + // ④ 余额 + if (filters.usage !== 'all' && usageState(item) !== filters.usage) return false + // ⑤ 搜索词 + if (!keyword) return true + const haystack = [item.name, item.nickname, item.email, item.id, item.provider] + .filter(Boolean) + .join(' ') + .toLowerCase() + return haystack.includes(keyword) + }) + // expiryInfo / usageState 是纯函数(只读 Date.now 与 props),不必进依赖 + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [accounts, filters, query, usageById]) + + const groups = useMemo(() => { + const map = new Map() + filtered.forEach(item => { + const provider = String(item.provider || 'unknown') + const list = map.get(provider) || [] + list.push(item) + map.set(provider, list) + }) + return [...map.entries()] + .map(([provider, list]) => ({ + provider, + list: [...list].sort((a, b) => (a.priority ?? 999) - (b.priority ?? 999)), + })) + .sort((a, b) => (a.list[0]?.priority ?? 999) - (b.list[0]?.priority ?? 999)) + }, [filtered]) + + const enabledCount = accounts.filter(item => item.enabled).length + const limitedCount = accounts.filter(item => item.enabled && isRateLimited(item)).length + + const toggle = async (account: Account, next: boolean) => { + setBusy(true) + try { + await setAccountEnabled(account.id, next) + store.toast(next ? t('已启用 {name}', { name: accountName(account) }) : t('已禁用 {name}', { name: accountName(account) })) + await store.refresh(true) + } catch (failure) { + store.toast(failure instanceof Error ? failure.message : String(failure), 'err') + } finally { + setBusy(false) + } + } + + const openAccount = openId ? accounts.find(item => item.id === openId) || null : null + + return ( +
+ +
+ + + { + void store.refresh(true) + store.toast(t('已刷新')) + }} + /> + + } + /> + +
+
+ + setQuery(event.target.value)} + placeholder={t('搜索账号 / 邮箱 / 备注')} + style={{ + flex: 1, + minWidth: 0, + border: 0, + outline: 'none', + background: 'transparent', + color: 'var(--m-text)', + fontSize: 16, + }} + /> + {query ? ( + + ) : null} +
+
+ + {/* 生效中的条件用一条摘要显示:横排 chip 只留「全部」与当前选中的, + 避免四颗常驻 chip 与面板里的选项各说一套 */} +
+ + {activeFilterCount(filters) > 0 ? ( + + ) : null} +
+
+ + {/* 只有这一块跟着手指滚:筛选条常驻,换筛选不用先滑回顶部 */} +
+ {error ? : null} + {!state.session && state.loading ? : null} + + {state.session && !groups.length ? ( +
+
+ 0 + ? t('当前筛选下没有账号') + : t('还没有账号,去桌面端添加') + } + /> +
+
+ ) : null} + + {groups.map(group => { + const enabled = group.list.filter(item => item.enabled).length + const limited = group.list.filter(item => item.enabled && isRateLimited(item)).length + return ( +
+
+ +
+
{providerLabel(group.provider)}
+
+ {t('{n} 个账号 · 启用 {e}', { n: group.list.length, e: enabled })} + {limited ? t(' · 限流 {n}', { n: limited }) : ''} +
+
+
+
+ {group.list.map((account, index) => ( + setOpenId(account.id)} + onToggle={next => void toggle(account, next)} + busy={busy} + /> + ))} +
+
+ ) + })} + +
+ + setOpenId(null)} + onChanged={() => void store.refresh(true)} + /> + setFilterOpen(false)} + filters={filters} + onChange={setFilters} + providers={providerOptions} + counts={counts} + /> +
+ ) +} + +function AccountRow({ + account, + rank, + usageRow, + onOpen, + onToggle, + busy, +}: { + account: Account + /** 组内序号(1 起):后端 priority 是全局唯一队列里的绝对位次,手机上按组内序号读 */ + rank: number + /** 该账号在余额快照里的那一行;快照没有这个账号时是 undefined(= 未查询) */ + usageRow?: UsageRow + onOpen: () => void + onToggle: (next: boolean) => void + busy: boolean +}) { + const limited = isRateLimited(account) + const expiresAt = expiryOf(account) + const expired = expiresAt > 0 && expiresAt <= Date.now() + const tone = !account.enabled + ? 'mute' + : account.needsRelogin + ? 'bad' + : limited + ? 'warn' + : expired + ? 'bad' + : 'ok' + const badgeText = !account.enabled + ? t('已禁用') + : account.needsRelogin + ? t('需重登') + : limited + ? t('限流') + : expired + ? t('已过期') + : t('正常') + // 有效期:这家没有这个概念时不显示,而不是显示「—」(少一行噪音); + // 长期有效的家写「长期有效」—— 与「—」(读不到到期时间)是两件事 + const expiryText = isLongLivedCredential(account.provider) + ? t('长期有效') + : hasExpiry(account.provider) ? formatExpiry(expiresAt) : '' + // 余额:只有这家有查询能力时才占一行;快照里没有这一行 = 还没查过 + const usageView = usageText(usageRow) + const showUsage = supportsUsage(account) + const usageColor = + usageView.kind === 'bad' + ? 'var(--m-danger)' + : usageView.kind === 'warn' + ? 'var(--m-warn)' + : 'var(--m-text-3)' + + return ( +
+ +
+ + + {account.editionLabel || formatRelative(account.updatedAt as number)} + +
+
+ ) +} + +/** 小号开关:账号行里用它,尺寸比设置页的档位小一档 */ +function SwitchSmall({ + checked, + onChange, + disabled, + label, +}: { + checked: boolean + onChange: (next: boolean) => void + disabled?: boolean + label: string +}) { + return ( + + ) +} diff --git a/desktop-tauri/mobile/src/pages/AgentPage.tsx b/desktop-tauri/mobile/src/pages/AgentPage.tsx new file mode 100644 index 00000000..53e38b59 --- /dev/null +++ b/desktop-tauri/mobile/src/pages/AgentPage.tsx @@ -0,0 +1,149 @@ +import { useState } from 'react' +import { PageIntro, IconButton, Badge, Empty, ErrorNote, SkeletonList } from '../components/ui' +import { GlyphLogo } from '../components/ProviderLogo' +import { Icon } from '../components/Icon' +import { PullIndicator, usePullToRefresh } from '../components/PullToRefresh' +import { store } from '../state/store' +import { fetchAgents, restoreAgentClient, type AgentClient } from '../api/gateway' +import { usePolling } from '../lib/usePolling' +import { t } from '../i18n' + +/** + * Agent 接管。 + * + * ── 手机上只读,这是有意的 ──────────────────────────────────── + * 这一页的作用是改写**本机 AI 客户端的配置文件**(`~/.claude/settings.json` + * 之类)。手机读不到那台机器的文件系统 —— 它连的是网关,而网关跑在另一台 + * 机器上。所以手机上: + * · 看得到有哪些客户端、各自的配置路径、是否已接管; + * · 可以「还原」(走网关的 `/api/agents/restore`,那是网关在替我们写); + * · **不能**改配置内容(那需要浏览本机文件、选 profile)。 + * + * 页内明说这一点,免得用户以为功能坏了。 + */ +export function AgentPage() { + const [clients, setClients] = useState(null) + const [error, setError] = useState('') + const [busy, setBusy] = useState('') + + const load = async () => { + try { + const data = await fetchAgents() + setClients(data.clients || []) + setError('') + } catch (failure) { + setError(failure instanceof Error ? failure.message : String(failure)) + } + } + + usePolling(load, 120_000) + + const pull = usePullToRefresh({ onRefresh: load }) + + const restore = async (client: AgentClient) => { + if (!client.id) return + if (!window.confirm(t('确定按最近一次备份还原「{name}」?', { name: client.name || client.id }))) return + setBusy(client.id) + try { + await restoreAgentClient(client.id) + store.toast(t('已还原「{name}」', { name: client.name || client.id })) + await load() + } catch (failure) { + store.toast(failure instanceof Error ? failure.message : String(failure), 'err') + } finally { + setBusy('') + } + } + + return ( +
+ + void load()} />} + /> + +
+
+
+ {t('手机上只能查看与还原接管。改写配置内容请回桌面端')} + {t('(手机读不到本机文件)')} + {t('。这里的开关与还原作用在网关所在的机器上。')} +
+
+
+ + {error ? : null} + {!clients ? : null} + + {clients && !clients.length ? ( +
+
+ +
+
+ ) : null} + + {(clients || []).map(client => ( +
+
+
+ +
+
{client.name || client.id}
+
+ {client.path || '—'} +
+
+ {client.applied ? t('已接管') : t('未接管')} +
+
+ {client.mode || t('单配置文件')} + {client.applied ? ( + + ) : null} +
+
+
+ ))} + +
+
+ {t('接管会把客户端的 base_url 指向本网关,并在写入前自动备份原文件。')} +
+
+
+ ) +} diff --git a/desktop-tauri/mobile/src/pages/AssistantPage.tsx b/desktop-tauri/mobile/src/pages/AssistantPage.tsx new file mode 100644 index 00000000..c1d0dbad --- /dev/null +++ b/desktop-tauri/mobile/src/pages/AssistantPage.tsx @@ -0,0 +1,641 @@ +import { useEffect, useRef, useState } from 'react' +import { Icon } from '../components/Icon' +import { MarkdownLite } from '../components/MarkdownLite' +import { Sheet, SheetContent, SheetHeader, SheetBody, SheetTitle } from '@ui' +import { store } from '../state/store' +import { fetchManage, type ManageModel } from '../api/gateway' +import { + fetchAssistantConfig, + streamAssistantChat, + type ChatMessage, + type ToolCall, +} from '../api/assistant' +import { toolsPayload, toolById } from '../lib/assistantTools' +import { ellipsisMiddle } from '../lib/format' +import { t } from '../i18n' + +/** + * AI 助手(手机端)。 + * + * ── 请求走网关,不在手机上直连模型 ──────────────────────────── + * 对话发到 `POST /api/assistant/chat`,由**网关**转发到上游:模型路由、账号 + * 选取、429 换号、协议翻译、用量记账全在服务器那一侧(见 `api::assistant` + * 的模块头)。手机端只做两件事:把消息发出去、把流回来的 delta 画出来。 + * 手机不持有任何上游凭证,也没有直连任何模型服务的代码。 + * + * ── 为什么不再有「快捷查询」那一套 ──────────────────────────── + * 上一版用四个本地写死的查询冒充对话(直接打管理 API,不经过模型),输入框 + * 是个点了只弹提示的假按钮。那套东西的问题是:用户以为在跟模型说话,实际上 + * 只能问那四件事。现在四个快捷入口保留为**提问模板** —— 点一下把话填进输入框 + * 并发出去,走的是真正的对话链路。 + * + * ── 模型从哪来 ──────────────────────────────────────────────── + * 默认用桌面端「助手设置」里选的那个(与桌面端共用一份配置)。手机端可以在 + * 顶部临时换一个已启用的模型,只影响本次会话,不写回配置 —— 手机上改全局 + * 设置容易误触,而换模型是个高频的临时动作。 + */ + +type Bubble = { + id: number + role: 'user' | 'assistant' + text: string + /** 正在流式输出:气泡上显示光标 / 停止按钮 */ + streaming?: boolean + /** 失败原因(assistant 气泡下方的红字) */ + error?: string + /** 这一轮里执行过的工具(显示成一行小字,让用户知道模型查了什么) */ + tools?: string[] +} + +type QuickAsk = { + label: string + icon: Parameters[0]['name'] +} + +const QUICK_ASKS: QuickAsk[] = [ + { label: t('各账号余额还剩多少?'), icon: 'wallet' }, + { label: t('哪些模型在多家重复了?'), icon: 'model' }, + { label: t('今天用了多少 Token?'), icon: 'chart' }, + { label: t('现在有哪些账号被限流?'), icon: 'alert' }, +] + +let nextId = 1 + +export function AssistantPage() { + const [bubbles, setBubbles] = useState([]) + const [draft, setDraft] = useState('') + const [busy, setBusy] = useState(false) + /** 默认模型(来自网关配置);null = 还没读到 */ + const [defaultModel, setDefaultModel] = useState(undefined) + /** 本次会话临时选的模型;空串 = 跟随配置 */ + const [model, setModel] = useState('') + const [models, setModels] = useState([]) + const [pickerOpen, setPickerOpen] = useState(false) + /** 网关里勾选过的工具表(空 = 纯聊天,后端也不会注入系统提示词) */ + const [tools, setTools] = useState>>([]) + const abortRef = useRef(null) + const scrollRef = useRef(null) + const inputRef = useRef(null) + + // 读默认模型与工具勾选表:工具表决定后端注不注入系统提示词,必须一起读 + useEffect(() => { + let cancelled = false + fetchAssistantConfig() + .then(config => { + if (cancelled) return + setDefaultModel(config.model || null) + const enabled = config.tools || {} + setTools(toolsPayload(enabled)) + }) + .catch(() => { + if (!cancelled) setDefaultModel(null) + }) + return () => { + cancelled = true + } + }, []) + + // 模型清单只在打开选择器时拉:不进页面就不该打这个请求 + useEffect(() => { + if (!pickerOpen || models.length) return + let cancelled = false + fetchManage() + .then(view => { + if (cancelled) return + setModels((view.models || []).filter(item => item.enabled !== false && item.id)) + }) + .catch(() => { + if (!cancelled) store.toast(t('读取模型清单失败'), 'err') + }) + return () => { + cancelled = true + } + }, [pickerOpen, models.length]) + + // 新内容到达时贴底(用户往上翻看历史时不打扰:只有本来就在底部才跟随) + useEffect(() => { + const node = scrollRef.current + if (!node) return + const nearBottom = node.scrollHeight - node.scrollTop - node.clientHeight < 120 + if (nearBottom) node.scrollTop = node.scrollHeight + }, [bubbles]) + + const effectiveModel = model || defaultModel || '' + + /** 发一条消息(`text` 为空时取输入框) */ + const send = async (text?: string) => { + const content = (text ?? draft).trim() + if (!content || busy) return + + // 历史 = 已完成的对话(失败 / 空回复的气泡不进上下文) + const history: ChatMessage[] = bubbles + .filter(item => item.text.trim() && !item.error) + .map(item => ({ role: item.role, content: item.text })) + history.push({ role: 'user', content }) + + const userId = nextId++ + const replyId = nextId++ + setBubbles(prev => [ + ...prev, + { id: userId, role: 'user', text: content }, + { id: replyId, role: 'assistant', text: '', streaming: true, tools: [] }, + ]) + setDraft('') + setBusy(true) + + const controller = new AbortController() + abortRef.current = controller + const patch = (next: Partial) => + setBubbles(prev => prev.map(item => (item.id === replyId ? { ...item, ...next } : item))) + + try { + /** + * 工具循环:模型可能连续调几轮工具,每轮都要把结果回灌再发一次。 + * + * 上限 6 轮 —— 与桌面端同一道闸:正常任务两三轮就收敛,跑到第六轮还在 + * 调工具,多半是模型在绕圈(或者某只工具一直失败),继续烧额度没有意义。 + */ + let messages = history + /** 本轮执行过的工具(显示在气泡下方,让用户知道模型查了什么) */ + const usedTools: string[] = [] + for (let round = 0; round < 6; round++) { + let pendingCalls: ToolCall[] = [] + let roundText = '' + + await streamAssistantChat({ + messages, + tools, + model: effectiveModel || null, + signal: controller.signal, + onEvent: event => { + if (event.type === 'delta') { + roundText += event.text + setBubbles(prev => + prev.map(item => + item.id === replyId ? { ...item, text: item.text + event.text } : item, + ), + ) + } else if (event.type === 'toolCalls') { + pendingCalls = event.calls + } + }, + }) + + if (!pendingCalls.length) break + + // 把这一轮的 assistant 消息(含工具调用)与工具结果一起回灌 + messages = [ + ...messages, + { role: 'assistant', content: roundText, toolCalls: pendingCalls }, + ] + for (const call of pendingCalls) { + const tool = toolById(call.name) + let result: string + try { + if (!tool) throw new Error(t('未知的工具:{name}', { name: call.name })) + const outcome = await tool.run(parseArgs(call.arguments)) + result = outcome.text + usedTools.push(tool.label) + patch({ tools: [...usedTools] }) + } catch (failure) { + // 工具失败不中断对话:把错误如实回给模型,让它自己决定怎么解释 + result = JSON.stringify({ + error: failure instanceof Error ? failure.message : String(failure), + }) + } + messages = [ + ...messages, + { role: 'tool', toolCallId: call.id, content: result }, + ] + } + if (controller.signal.aborted) break + } + + // 流正常结束但一个字都没有:多半是上游只回了 tool_calls 或空响应 + setBubbles(prev => + prev.map(item => { + if (item.id !== replyId) return item + if (item.text.trim()) return { ...item, streaming: false } + return { ...item, streaming: false, error: t('模型没有返回内容') } + }), + ) + } catch (failure) { + const aborted = controller.signal.aborted + patch({ + streaming: false, + error: aborted + ? undefined + : failure instanceof Error + ? failure.message + : String(failure), + }) + if (aborted) store.toast(t('已停止生成')) + } finally { + abortRef.current = null + setBusy(false) + inputRef.current?.focus() + } + } + + const stop = () => { + abortRef.current?.abort() + } + + const clear = () => { + if (busy) abortRef.current?.abort() + setBubbles([]) + setDraft('') + } + + return ( +
+ {/* + 页头必须自己吃下安全区:其他一级页用 `.m-head`,那里带了 + `calc(var(--m-safe-t) + …)`;这一页是手写的紧凑头(对话页要省纵向空间), + 漏了 safe-t 就会被刘海 / 状态栏压住。离线时顶部那条通知自己占安全区, + 这里再让一次会多出一段空白 —— 与 `.m-app[data-offline] .m-head` 同口径。 + */} +
+
+ {t('AI 助手')} + +
+ +
+ +
+ {bubbles.length === 0 ? ( +
+ +
+ {t('有什么可以帮你?可以问配置、查余额、让我改模型设置。')} +
+
+ ) : null} + + {bubbles.map(bubble => { + if (bubble.role === 'user') { + return ( +
+
+ {bubble.text} +
+
+ ) + } + return ( +
+ +
+
+ {bubble.text ? : bubble.streaming ? t('正在思考…') : null} + {bubble.streaming ? ( +
+ {bubble.error ? ( +
+ {bubble.error} +
+ ) : null} + {bubble.tools?.length ? ( +
+ {t('已调用')} + {bubble.tools.map((label, index) => ( + + {label} + + ))} +
+ ) : null} +
+
+ ) + })} + + {bubbles.length === 0 ? ( +
+ {QUICK_ASKS.map(item => ( + + ))} +
+ ) : null} +
+ +
+
+