给你的启动器,接上中文资源介绍。
按资源 ID 和原文哈希查询共享译文。缓存未命中时,保留原文、异步生成;内容更新后,明确提示用户正在翻译新版本。
Modrinth / CurseForge · 模组 / 资源包 / 光影 / 整合包 · 简体中文 zh-Hans · 策略 minecraft-v1。标题保留原名;简介和正文翻译。游戏内文本、文件翻译、更新日志和账号计费尚未开放。
快速开始
API 根地址:https://translate.bugg.icu/v1。读取公开译文无需密钥;申请生成使用短时匿名会话。大模型凭据只存放在平台服务端。
先检查服务能力
curl https://translate.bugg.icu/v1/capabilities读取 generation_available、目标语言和批量上限;生成关闭时,已有译文仍可查询。示例使用已收录的 Sodium 项目 AANobbMI。
准备一份可核验的原文
curl https://translate.bugg.icu/v1/projects/modrinth/AANobbMI/source实际启动器通常已经从原站拿到 description / body,可直接计算哈希。此接口主要用于平台 Web 和接入调试;它不会在读取时访问原站。若返回 project_not_indexed,先申请匿名会话,再 POST /projects/{provider}/{project_id}/refresh,等待返回的任务完成。
把翻译与浏览解耦
搜索、滚动、详情和下载先正常完成。在后台读取本地译文缓存,再批量查询平台;任何网络错误都保留原文。以下命令行示例为便于理解会等待翻译,接入 GUI 时放到后台任务中。
稳定身份与原文哈希
项目主键是 provider + project_id。创建时间、作者、标签均为可变元数据,不能拼进项目主键。第一版实体固定为 project,entity_id 必须等于 project_id。
| 字段 | 输入来源 | format |
|---|---|---|
| title | 项目原名 | plain |
| summary | Modrinth description / CurseForge summary | plain |
| body | Modrinth 原始 body | markdown |
| body | CurseForge 原始 description HTML | html |
- 仅把 CRLF、CR 规范成 LF。
- 构造下列五个字符串字段,键按 ASCII 升序排列,不添加空白。
- 输出原生 Unicode 的 UTF-8 JSON 字节,计算 SHA-256,添加
sha256:前缀。
{"field":"summary","format":"plain","schema":"mc-text-v1","source_language":"und","text":"Original summary"}不要先剥除 HTML、裁剪长度、去首尾空白或做 Unicode NFC/NFD 转换。中文和 emoji 不应变成 \uXXXX 后再参与哈希。未知语言固定 und;不能自行换成 en。请跑通全部公开测试向量再接入。
先查译文,缺失时自动排队
3.1 批量查询 · POST /translations:resolve
{
"target_language": "zh-Hans", "profile": "minecraft-v1",
"items": [{
"request_id": "visible-row-1",
"resource": {"provider":"modrinth","project_id":"AANobbMI","entity_type":"project","entity_id":"AANobbMI"},
"fields": [{"field":"summary","source_hash":"sha256:这里替换为实际的64位小写十六进制值"}]
}]
}每批最多 50 个项目、每项最多 3 个字段。服务只读本地数据库,不在此接口调用原站或模型。仅在 state=ready 且 matches_request=true 时使用 translation;还需读取 is_current 和 update 判断版本提示。
3.2 创建短时会话
POST /v1/sessions/anonymous
Content-Type: application/json
{"client_id":"your-launcher"}
// 成功响应(access_token 仅示意)
{"access_token":"…","token_type":"Bearer","expires_in":3600,"scopes":["translations:ensure","jobs:read","feedback:write"]}只在需要生成时创建会话,内存复用到过期前;不要每次轮询申请。会话属于当前调用者,任务票据不能跨会话共享。
3.3 请求生成 · POST /translations:ensure
Authorization: Bearer <access_token>
Idempotency-Key: <本次逻辑请求唯一的 UUID>
Content-Type: application/json
// 在 resolve 请求体基础上添加:
"purpose": "visible_list"请求体仍是完整 JSON 对象;上面只展示新增字段。每批最多 20 项;详情使用 purpose=detail。服务从可信原站核验哈希,只翻译公开资源,不能提交任意文本。HTTP 202 表示接受排队,不表示译文完成。同一个逻辑请求的网络重试复用幂等键;更换请求体必须更换键,否则返回 409。
3.4 完成后替换,不打断操作
用 resolve 每 2–5 秒批量查询当前可见资源,或订阅任务事件。滚动离开的资源停止轮询;详情切换后丢弃旧页面返回的结果。后台生成仍会继续,其他启动器也能复用结果。
正文 artifact
简介直接位于 translation.text。正文通过 translation.artifact.artifact_sha256 下载:
GET /v1/artifacts/{artifact_sha256}用响应的原始字节校验 SHA-256(此处路径里的哈希不带 sha256:),再验证 resource、field、source_hash、target_language 和 profile。artifact 不可变,支持 ETag 与一年缓存;不要在校验前重新序列化 JSON。自己拼接可信服务源下的 artifact 路径,避免跟随不可信 URL。
原文更新时,告诉用户发生了什么
state 描述请求哈希的译文状态;update 描述它与平台当前原文的关系。两者一起使用,不能把“有历史译文”解释成“已经是最新版”。
| update.code | 含义 | 建议界面行为 |
|---|---|---|
| up_to_date | 请求与当前原文一致,译文就绪 | 显示译文 |
| translation_pending | 首次翻译未完成 | 显示原文和“正在准备译文” |
| source_updated | 客户端持有旧原文,新版译文可用 | 刷新原站内容,重算哈希 |
| update_pending | 原文已更新,新译文尚未排队 | 显示原文,自动 ensure |
| update_queued | 新版译文排队或延后重试 | 提示“原文已更新,新版本译文正在排队” |
| update_running | 新版翻译正在执行 | 提示“原文已更新,正在翻译新版本” |
| update_failed | 新版翻译暂时失败 | 保留原文并提示失败 |
{
"field": "summary",
"state": "running", "matches_request": false, "is_current": true,
"requested_source_hash": "sha256:…", "current_source_hash": "sha256:…",
"update": {
"code": "update_running", "source_changed": true,
"previous_translation_available": true,
"current_translation_state": "running",
"notice": "原文已更新,正在翻译新版本", "action": "ensure"
}
}action=refresh_source:刷新客户端原文再查询,不能继续用旧哈希提交新版翻译。action=ensure:按当前请求哈希申请生成,已有任务会去重。action=none:无需操作。可直接显示 notice,或按 code 自行本地化;不要用提示文案当程序判断条件。
旧译文必须明确标识
默认缺失时返回原文,不自动把旧译文放入 translation。只有请求 allow_stale=true 才可能返回 fallback_translation 与 fallback_source_hash;使用时持续显示“旧版译文,新版本翻译中”。即使 state=ready,若 is_current=false,它也只是与请求的历史哈希一致。
发现更新的时效
客户端从原站拿到新原文后,其新哈希会触发 ensure 的版本核验。平台后台默认按五分钟过期时间检查,每轮最多八个项目,拥塞或原站限流会延长实际延迟,不承诺五分钟内全库同步。freshness 只代表最近核验时间,不能作为原站绝未变化的保证。需要时可主动 refresh,受独立限额约束。
内容结构保留,原文与译文一致呈现
正文的 format 可能为 markdown 或 html,以实际响应为准。平台保留作者介绍中的标题层级、段落、列表、表格、链接和图片结构,翻译文字;代码、技术版本和 URL 使用占位符保护。不要直接展示原始标签文本,也不要把整个 HTML 文档当纯文本拼到页面上。
- Markdown 先解析为 HTML;HTML 使用内容片段。
- 原文与译文使用同一套 HTML 清洗规则,移除脚本、事件属性、iframe 和危险 URL。
- 再渲染到相同正文样式中;图片延迟加载,外链使用 noopener/noreferrer。
平台 Web 使用本地打包的 markdown-it 与 DOMPurify,不从第三方 CDN 加载脚本。保留介绍内容结构,不复制原站导航、广告或整站 CSS。客户端必须独立清洗,不能因为内容来自翻译平台就跳过防护。未经审查的原文与模型输出都不能直接注入 WebView。
项目资料、版本和依赖
GET /v1/projects/{provider}/{project_id}/source
GET /v1/projects/{provider}/{project_id}/manifest
GET /v1/projects/{provider}/{project_id}/historysource 返回完整原始字段与 metadata;manifest 返回字段哈希、译文状态、更新时间和项目资料;history 提供原文、元数据变动和译文发布记录。只读接口不请求原站。
| metadata 字段 | 用途 |
|---|---|
| authors / created_at / updated_at | 作者、创建及原站更新时间,时间采用 ISO 8601 |
| tags / game_versions / loaders | 分类、支持的 Minecraft 版本、加载器 |
| icon_url / downloads / license | 图标、下载量和许可证,原站未提供则为空 |
| releases | 最近 12 个原站版本,包含发布时间、渠道、游戏版本与依赖 |
| releases[].dependencies | project_id / version_id / name / relation / url |
| metadata_complete | false 表示部分资料拉取失败,应标为暂缺,不应推断“没有依赖” |
依赖属于具体发布文件,必须按所选版本展示。relation:required 必需、optional 可选、incompatible 不兼容、embedded 内置、tool 工具。列表的 dependency_count 仅对应最近版本;正文、标签和作者变化不会更换项目 ID。
搜索与分页
GET /v1/catalog/search?q=优化&kind=mod&limit=20
GET /v1/catalog/projects?limit=20
// 下一页:原样传回 next_cursor,同时保持 q / kind / limit 一致
GET /v1/catalog/search?q=优化&kind=mod&limit=20&cursor=<URL编码后的next_cursor>响应提供 projects、total_count 和 next_cursor;游标为空表示末页。limit 为 1–50,kind 为 mod / resourcepack / shader / modpack。q 检索已收录的标题、简介和中文译文,不会实时搜索全站资源。
使用索引和 keyset 分页,不执行深层 OFFSET。游标签名绑定筛选条件与每页数量,有效期 15 分钟;过期、篡改或筛选不一致时返回 409 cursor_expired,应清空分页栈重载首页。客户端保存访问过的游标实现上一页。资源库会持续新增,总数可能变化;不提供任意深页跳转或固定快照保证。
轮询与 SSE 事件
GET /v1/jobs/{job_id}
Authorization: Bearer <创建该任务票据的会话令牌>
GET /v1/events?job_ids=<job_id1,job_id2>
Authorization: Bearer <同一会话令牌>任务状态包括 queued → verifying → running / partial → ready;deferred 表示稍后重试;failed 和 source_mismatch 为终态。可读取分段完成量,不能把 partial 当成完整正文。
SSE 最多同时订阅 50 个票据,连接最长约 120 秒,断开后重连并重新读取任务状态。事件仅用于唤醒查询,最终结果以 resolve 为准。浏览器原生 EventSource 无法添加 Authorization,请使用 fetch 流或回退批量轮询,令牌不要放进 URL。
多语言完整示例
五种语言都演示:读取示例原文 → 计算哈希 → resolve → 缺失时创建会话和 ensure → 轮询 → 显示译文。示例只查询简介;正文 artifact 处理见第 03 节。可用环境变量 TRANSLATION_API 指向兼容平台,值应为 HTTPS 地址且包含 /v1。
正在读取示例…本地缓存键至少包含服务源、资源 ID、字段、原文哈希、语言和 profile。网络任务放在后台;401 重建会话、429 服从 Retry-After、5xx 指数退避。不要把模型密钥嵌进桌面程序。服务源切换后隔离缓存。
错误、限额与性能
| HTTP / code | 处理方式 |
|---|---|
| 400 invalid_request | 检查哈希、实体、批量上限和 JSON;不要原样高频重试 |
| 401 unauthorized | 会话过期,重新创建;管理接口使用独立管理员令牌 |
| 404 project_not_indexed | 申请 refresh 并等待任务,不要假设 source 会自动回源 |
| 409 idempotency_conflict | 同一键使用了不同请求体,修复幂等键管理 |
| 409 cursor_expired | 清空旧游标栈,重载首页 |
| 413 request_too_large | 请求 JSON 最大 64 KiB,拆分批次 |
| 429 rate_limited | 按 Retry-After 等待,不要并发重试风暴 |
| source_mismatch | 任务核验的哈希与原站不一致;刷新原文重算哈希 |
| budget_exhausted / deferred | 模型预算耗尽,后台延后,先保留原文 |
当前部署的默认限额
- resolve:每 IP 每分钟最多 1200 次;客户端应以可见区批量查询。
- 匿名会话:每 IP 每小时 10 个,令牌约 1 小时有效。
- ensure:每分钟 60 次;每 IP 和会话每小时最多 200 个字段。
- refresh:每 IP 每小时 10 次;反馈每小时 10 次。
- 模型另有全局日/月 token 和每日调用预算,队列和原站也有并发限制。
限额可能由部署方调整;以 capabilities 和错误响应为准。端到端耗时主要取决于原站、正文长度、队列和模型,不能把缓存查询延迟等同于首次翻译耗时。缓存命中不调用模型;分段缓存复用、WAL、批量 SQL、限量 LRU 和不可变 artifact 分别减少模型成本、锁竞争和网络开销。
接口索引
| 方法与路径(均位于 /v1) | 认证 | 用途 |
|---|---|---|
| GET /capabilities | 无 | 能力与上限 |
| POST /translations:resolve | 无 | 精确哈希查询 |
| POST /sessions/anonymous | 无 | 创建匿名会话 |
| POST /translations:ensure | 会话 + 幂等键 | 异步核验、生成 |
| GET /jobs/{job_id} · GET /events | 票据所属会话 | 进度与状态 |
| GET /artifacts/{artifact_sha256} | 无 | 不可变正文 |
| GET /projects/{provider}/{id}/source · manifest · history | 无 | 原文、资料、版本记录 |
| POST /projects/{provider}/{id}/refresh | 会话 | 核验原站更新 |
| GET /catalog/projects · /catalog/search | 无 | 本地译文库分页与搜索 |
| POST /feedback | 会话 | 提交译文问题 |
| GET /admin/overview · POST /admin/overview | 管理员令牌 | 预算、队列、反馈状态 |
精确请求与响应定义见 OpenAPI 3.1 文件;本页示例中的省略号和注释用于说明,不可直接作为有效 JSON 请求。项目正文和图片版权归原作者所有,界面保留原项目入口与 AI 译文标识。