|
文档
预约演示平台
平台MCPCLIAPI
工作流
指南更新日志

欢迎

  • 概览
  • 身份验证
  • 错误与状态码
  • Webhook 签名

本地化

  • 概览
  • 创建任务
  • 锁定不可翻译的键
  • 查看任务组状态
  • 获取单个作业
  • 列出作业
  • Webhook 结果投递
  • 实时进度(WebSocket)

流水线

  • 概览
  • 本地化前 AI 预编辑
  • 人工审核
  • AI 审校(后编辑)
  • 改写成更自然的文案
  • 回译检查
  • 配置 Pipeline
  • 查看流水线运行记录

预配

  • 概览
  • 创建预配作业
  • 来源类型
  • AI 会提取哪些内容
  • Webhook 投递
  • 实时进度(WebSocket)

同步

  • 本地化
  • Recognize

引擎管理

  • Engine Suggestions

Webhook 结果投递

你已经创建了一个任务组,并在几毫秒内收到了 202 响应。翻译现在正在后台运行,每个语言区域对应一个独立任务。你当然可以轮询每个任务直到它完成——但你没必要只为了确认德语是否就绪,就专门维护一套轮询逻辑。你真正需要的,是每个语言区域一完成,服务器就立刻收到通知。

这正是 webhook 的作用。创建任务时传入 callbackUrl 后,Lingo.dev 会在每个任务进入终态时,将结果 POST 到这个 URL——每个语言区域一条 POST,完成即送达。 翻译顺利完成的语言区域会以 translation.completed 的形式携带数据送达;翻译失败的语言区域则会以 translation.failed 的形式携带错误送达。不管成功还是失败,你都会按语言逐一收到通知,无需主动查询。

本页会介绍这两种负载,以及该如何处理它们。投递的签名与重试机制和预配共用,相关细节都在 Webhook 签名验证 页面里;在你会用到的地方,我们也都附上了链接。

本页内容

  • 投递如何工作
  • 成功完成时的负载
  • 失败时的负载
  • 如何处理 webhook
  • 什么时候不该用投递

投递如何工作#

组里的每个语言区域都是一个独立任务。只要其中任意一个进入终态,结果就会单独投递到你的 callbackUrl——Lingo.dev 不会等最慢的那个语言区域,也不会把整个任务组打包成一次调用。目标语言区域有十四个,就最多会收到十四条 POST;每种语言各自在完成时送达,先后顺序取决于它们实际完成的时间。

你可以在创建任务组时,通过请求级的 callbackUrl 指定投递地址;也可以在控制台中设置组织级默认值,让所有任务组继承。某个任务组上单独传入的 callbackUrl 会覆盖组织默认值。

仅支持 HTTPS

callbackUrl 必须使用 HTTPS。若你传入的是 HTTP URL,创建任务时会直接返回 400——webhook 本身带签名,而在明文链路上传送签名负载就失去意义了。

线上传输的负载有两种格式,通过它们的 type 字段区分:translation.completed 和 translation.failed。两者都会标明所属任务、任务组以及对应的语言区域,因此同一个处理器只需根据 type 做路由,就能更新正确的记录。

优雅处理未知事件类型

当前线上会发送 translation.completed 和 translation.failed。请把这个集合视为开放的——对已知类型按分支处理,其他类型直接忽略即可。这样即使未来新增事件类型,也不会把已部署的处理器搞挂。

成功完成时的负载#

任务成功完成时,负载里会带上翻译后的 data——结构与你通过获取任务拿到的结果完全一致,只不过这里不是你去轮询,而是系统主动推送给你。data 会完整映射你提交时的结构:所有字符串都会被翻译,所有非字符串值(数字、布尔值、null)都会原样保留,嵌套结构也不会变。

json
{
  "type": "translation.completed",
  "jobId": "ljb_A1b2C3d4E5f6G7h8",
  "groupId": "ljg_A1b2C3d4E5f6G7h8",
  "sourceLocale": "en",
  "targetLocale": "de",
  "data": {
    "id": "course_101",
    "title": "Einführung in maschinelles Lernen",
    "steps": [
      { "heading": "Was ist ML?", "body": "Maschinelles Lernen ist ein Teilbereich der künstlichen Intelligenz." },
      { "heading": "Überwachtes Lernen", "body": "Trainieren eines Modells mit gelabelten Daten." }
    ],
    "metadata": { "author": "Dr. Smith", "difficulty": "beginner" }
  }
}
字段说明
typetranslation.completed
jobId已完成的任务(前缀为 ljb_)
groupId所属任务组(前缀为 ljg_)
sourceLocale你提交的源语言区域
targetLocale该负载所对应的目标语言区域
data翻译后的内容,结构与你提交的 data 保持一致

只要任务产出了结果,就不算失败——所以即使某个任务最终状态是 completed_with_warnings(已经产出结果,但某个可选的 pipeline 阶段发生了回退),它仍会以 translation.completed 的形式投递,并附带可用的 data。Webhook 只负责告诉你这个语言区域已经就绪;至于解释回退原因的逐步警告,则记录在 单个任务 里,你需要时可以通过 jobId 获取。

失败时的负载#

单个语言区域也可能失败——比如模型超时,或者所有已配置模型都不可用。当任务进入 failed 时,你依然会收到通知。此时负载类型为 translation.failed,并且会用一个 error 字符串来替代 data:

json
{
  "type": "translation.failed",
  "jobId": "ljb_C3d4E5f6G7h8I9j0",
  "groupId": "ljg_A1b2C3d4E5f6G7h8",
  "sourceLocale": "en",
  "targetLocale": "ja",
  "error": "Model timeout after 30 seconds"
}
字段说明
typetranslation.failed
jobId失败的任务
groupId所属任务组
sourceLocale你提交的源语言区域
targetLocale失败的目标语言区域
error便于人工阅读的失败描述

失败只影响单个语言区域。比如你提交了 de、fr 和 ja,其中某个 ja 失败了,它会作为一条独立的 translation.failed POST 送达;而 de 和 fr 仍会以 translation.completed 的形式送达——德语和法语翻译照常交付。任务组的部分失败状态会反映这种混合结果。若要恢复失败的语言区域,请仅针对该语言区域重新提交一个新任务,并使用新的幂等键。

如何处理 webhook#

一个谨慎的读者看到这里,第一反应往往是对的:我的处理器要做真正的业务操作——写数据库、刷新缓存、给已连接客户端做扇出——那岂不是会让连接一直挂着,最后把 webhook 拖到超时?

会,所以别让 Lingo.dev 等。先返回 200,再处理。 先立即确认已收到请求,等响应发出后再做真正的业务处理。处理器返回得快,投递就更稳;如果一直阻塞在下游操作上,就会引来原本完全没必要的重试。

javascript
app.post("/webhooks/translations", verifyWebhook, async (req, res) => {
  // Acknowledge first - one POST per locale, the moment it lands.
  res.status(200).send("ok");

  const { type, jobId, groupId, targetLocale, data } = req.body;

  if (type === "translation.completed") {
    await db.content.update({
      where: { groupId },
      data: { [`content_${targetLocale}`]: data },
    });

    // Advance your own progress model - your UI can poll this or receive it over SSE.
    await db.translationProgress.increment({
      where: { groupId },
      data: { completedLanguages: { increment: 1 } },
    });
  }

  if (type === "translation.failed") {
    console.error(`Translation failed: ${jobId} (${targetLocale})`, req.body.error);
  }
});

这页唯一没有展开定义的是 verifyWebhook 中间件。每次投递都会遵循 Standard Webhooks 规范进行签名,所以你不需要自己去反向摸索协议。如何校验签名,以及非 2xx 响应对应的重试节奏,都已在 Webhook 签名验证 页面完整说明,并与预配共用。在你信任任何负载之前,务必先把这层中间件接上:未经验证的请求体,本质上就是未经认证的请求体。

先验证,再信任请求体

你的端点是公开 URL;任何人都可以向它发起 POST。在根据任何负载采取动作之前,先基于原始请求体完成签名校验。具体做法——请求头、HMAC,以及 whsec_ 密钥——都在 签名验证 页面里。

什么时候不该用投递#

Webhook 是一种推送上的便利,不是你的记录事实来源。遇到下面两种情况,更合适的工具另有其人,而且都只差点开一个链接。

如果结果投递时你的端点正好不可用,平台会自动重试——即使所有重试都用尽,结果也不会丢。它仍然可以通过 jobId 获取;任务的 callbackStatus 会记录这次推送最终是否成功。至于具体的重试节奏,见 签名与投递 页面。Webhook 在大多数情况下帮你省掉轮询;而在少数异常情况下,底层始终都有任务记录可查。

如果你真正想要的是 UI 里的实时进度——比如随着语言区域陆续完成,计数器从 14 个里的 3 个跳到 4 个——而不是按语言区域向你的服务器发送回调,那你需要的是任务组 WebSocket,而不是 webhook。

实时进度(WebSocket)
把任务组进度以完整状态快照的形式流式推送到 UI,而不是按语言区域回调你的服务器。
Webhook 签名验证
验证签名、读取请求头并处理重试节奏——适用于所有 webhook 投递。
获取单个任务
通过 jobId 获取任意结果,包括警告信息——这是每次投递背后的事实来源。

这个页面对你有帮助吗?

Max PrilutskiyMax Prilutskiy·已更新 12 天前·2 分钟阅读