|
Documentação
Agende uma demoPlataforma
PlataformaMCPCLIAPI
Workflows
GuiasChangelog

Boas-vindas

  • Visão geral
  • Autenticação
  • Erros e códigos de status
  • Assinaturas de webhook

Localização

  • Visão geral
  • Criar jobs
  • Bloquear chaves não traduzíveis
  • Acompanhar um grupo de jobs
  • Buscar um job
  • Listar jobs
  • Entrega de webhook
  • Progresso em tempo real (WebSocket)

Pipeline

  • Visão geral
  • Edição por IA antes da localização
  • Revisão humana
  • avaliação por IA (pós-edição)
  • Reescreva para soar natural
  • Verificação por retradução
  • Configure o pipeline
  • Acompanhe execuções do pipeline

Provisionamento

  • Visão geral
  • Criar um job de provisionamento
  • Tipos de fonte
  • O que a IA extrai
  • Entrega de webhook
  • Progresso em tempo real (WebSocket)

Síncrono

  • Localize
  • Recognize

Gerenciamento do engine

  • Sugestões do engine

Progresso em tempo real via WebSocket

Você criou um grupo de jobs. Em algum lugar, um usuário está olhando para um spinner, e “traduzindo para 14 idiomas…” é verdade, mas não ajuda em nada — nunca sai do lugar. O que você quer é ver a contagem subir na frente dele: 3 prontos, depois 4, depois um idioma com falha, depois concluído.

Fazer polling do grupo de jobs resolve, mas gera tráfego demais, e cada poll traz um novo snapshot que você precisa comparar com o anterior para saber o que de fato mudou. O WebSocket vira esse jogo. Você conecta uma vez, e o servidor envia um evento sempre que um idioma é resolvido — e cada mensagem traz o estado completo do grupo, então você renderiza o snapshot, sem nunca reconciliar um delta. Se um frame se perder, se você reconectar ou reiniciar a aba, a próxima mensagem volta a trazer a verdade completa.

text
GET /jobs/localization/groups/:groupId/ws

Ainda é novo em localização assíncrona? Comece pela Visão geral. O groupId aqui é o mesmo que você recebeu quando criou os jobs.

Nesta página

  • Tipos de mensagem
  • Payloads de mensagem
  • Como integrar isso à sua UI
  • Mantenha sua chave de API no servidor

Tipos de mensagem#

Quatro tipos de mensagem passam pelo socket. Cada um mostra o que acabou de acontecer e traz junto o estado atual do grupo inteiro.

TipoQuandoCampos principais
snapshotNa conexão inicialEstado completo do grupo
job.completedUm idioma é concluído com sucessojobId, locale, mais o estado completo do grupo
job.failedUm idioma falhajobId, locale, error, mais o estado completo do grupo
group.completedTodos os jobs foram resolvidosgroupId, status, mais o estado completo do grupo. O servidor fecha a conexão depois dessa mensagem.

Cada mensagem contém um objeto snapshot com o estado atual do grupo: totalJobs, completedJobs, completedWithWarningsJobs, failedJobs e um mapa jobs indexado por ID de job, cada um com seu locale e status. Essas contagens são as mesmas reportadas pelo endpoint do grupo de jobs — então um snapshot vindo do socket e um poll no endpoint REST concordam sobre quanto o grupo já avançou.

renderize o snapshot, nunca reconcilie

Você nunca precisa rastrear quais eventos já viu, reproduzir mensagens perdidas nem mesclar uma atualização parcial ao estado local. Leia snapshot em cada mensagem e pinte sua UI a partir dele. Em uma reconexão, snapshot é reenviado primeiro, então um cliente que acabou de entrar e outro que está ouvindo desde o começo convergem para o mesmo estado.

Payloads de mensagem#

Estes são os frames exatos que o servidor envia. Os IDs têm formatos reais (ljg_ para o grupo, ljb_ para cada job); o snapshot aparece abreviado como "..." apenas onde repete a estrutura já mostrada.

Ao conectar, o servidor envia o estado atual:

json
{
  "type": "snapshot",
  "snapshot": {
    "groupId": "ljg_A1b2C3d4E5f6G7h8",
    "totalJobs": 3,
    "completedJobs": 1,
    "completedWithWarningsJobs": 0,
    "failedJobs": 0,
    "jobs": {
      "ljb_A1b2C3d4E5f6G7h8": { "locale": "de", "status": "completed" },
      "ljb_B2c3D4e5F6g7H8i9": { "locale": "fr", "status": "processing" },
      "ljb_C3d4E5f6G7h8I9j0": { "locale": "ja", "status": "queued" }
    }
  }
}

À medida que cada idioma é concluído, o evento identifica o idioma que mudou e inclui o snapshot atualizado:

json
{
  "type": "job.completed",
  "jobId": "ljb_B2c3D4e5F6g7H8i9",
  "locale": "fr",
  "snapshot": {
    "groupId": "ljg_A1b2C3d4E5f6G7h8",
    "totalJobs": 3,
    "completedJobs": 2,
    "completedWithWarningsJobs": 0,
    "failedJobs": 0,
    "jobs": {
      "ljb_A1b2C3d4E5f6G7h8": { "locale": "de", "status": "completed" },
      "ljb_B2c3D4e5F6g7H8i9": { "locale": "fr", "status": "completed" },
      "ljb_C3d4E5f6G7h8I9j0": { "locale": "ja", "status": "processing" }
    }
  }
}

Uma falha é uma mensagem normal, não uma conexão encerrada. job.failed traz o idioma e um error, além do mesmo snapshot completo — o idioma com falha aparece como status: "failed" no mapa jobs, todos os outros idiomas continuam chegando por streaming, e o socket segue até group.completed:

json
{
  "type": "job.failed",
  "jobId": "ljb_C3d4E5f6G7h8I9j0",
  "locale": "ja",
  "error": "Model timeout after 30 seconds",
  "snapshot": { "...": "..." }
}

Quando todos os jobs forem resolvidos, o servidor envia um evento final e fecha a conexão:

json
{
  "type": "group.completed",
  "groupId": "ljg_A1b2C3d4E5f6G7h8",
  "status": "completed",
  "snapshot": { "...": "..." }
}

O status terminal é completed quando todos os idiomas são concluídos com sucesso, completed_with_warnings quando todos os idiomas geram saída, mas uma ou mais etapas opcionais do pipeline falham em pelo menos um deles, partial quando alguns idiomas têm sucesso e outros falham, e failed quando todos falham. Para entender o que cada um deles significa para o grupo como um todo, veja Track a job group.

Renderize a partir do snapshot sempre que houver algo que você não reconheça

Faça switch nos tipos de mensagem que você conhece e, para qualquer coisa que não reconhecer, volte a renderizar a partir de snapshot. Toda mensagem traz um snapshot completo, então um cliente que, por padrão, pinta a UI a partir dele continua correto mesmo ao receber um frame para o qual não tem um branch específico.

Como integrar isso à sua UI#

O grupo é o seu modelo de progresso. Quando você criou os jobs, o 202 devolveu um groupId e um array jobs — uma entrada por idioma. Inicialize seu registro de progresso com essa resposta e você já terá o formato que o socket vai preencher: o total a acompanhar e um contador começando em zero.

javascript
const { groupId, jobs } = await response.json();

await db.translationProgress.create({
  contentId: content.id,
  groupId,
  totalLanguages: jobs.length,
  completedLanguages: 0,
});

Depois, abra o socket para esse groupId e, em cada mensagem, leia snapshot e renderize de novo. Veja o contador subir à medida que os idiomas forem chegando e pare quando group.completed chegar:

javascript
import WebSocket from "ws";

const groupId = "ljg_A1b2C3d4E5f6G7h8";
const ws = new WebSocket(
  `wss://api.lingo.dev/jobs/localization/groups/${groupId}/ws`,
  { headers: { "X-API-Key": process.env.LINGO_API_KEY } }
);

ws.on("message", (raw) => {
  const event = JSON.parse(raw);
  const { snapshot } = event;

  switch (event.type) {
    case "snapshot":
      console.log(`${snapshot.completedJobs}/${snapshot.totalJobs} complete`);
      break;
    case "job.completed":
      console.log(`${event.locale} ready (${snapshot.completedJobs}/${snapshot.totalJobs})`);
      break;
    case "job.failed":
      console.error(`${event.locale} failed: ${event.error}`);
      break;
    case "group.completed":
      console.log(`All translations done: ${event.status}`);
      ws.close();
      break;
  }
});

Executando em um grupo de três idiomas, isso imprime a execução em tempo real:

text
1/3 complete
fr ready (2/3)
ja failed: Model timeout after 30 seconds
All translations done: partial

O contador avançou sozinho, um idioma falhou sem derrubar o stream, e partial mostrou onde a execução terminou — exatamente o que seu spinner precisa para virar uma barra de progresso de verdade. Repare que o loop nunca acumula estado: cada branch lê o snapshot da mensagem em mãos, então o mesmo código funciona corretamente na primeira conexão, em cada atualização e na reconexão.

Mantenha sua chave de API no servidor#

O socket se autentica com sua chave de API, a mesma chave com escopo de organização usada pelos endpoints REST. Isso significa que o navegador é o lugar errado para abri-lo — uma chave de API no JavaScript do cliente dá acesso a toda engine da sua organização para qualquer pessoa que visualizar o código-fonte.

Conecte pelo seu backend, não pelo navegador

Abra o WebSocket a partir do seu servidor, onde a chave já está, e então distribua os eventos ao navegador pelo seu próprio canal — um WebSocket ou stream de server-sent events sob seu controle. Seu frontend recebe progresso em tempo real; sua chave nunca sai da sua infraestrutura.

Isso espelha o modelo de webhook: a conexão que toca o Lingo.dev fica no servidor, e o que chega ao usuário é o que o seu próprio app decidir encaminhar.

Onde isso se encaixa#

O WebSocket é a visualização em tempo real — fica vinculado a um grupo e se fecha quando esse grupo termina. Para uma entrega durável, de servidor para servidor, que sobreviva ao fechamento de uma aba ou a um deploy, combine-o com webhooks: o socket move a UI enquanto a execução está na tela, e o webhook registra cada resultado no momento em que ele chega. Conecte os dois a partir da mesma chamada de criação e seus usuários verão o progresso conforme ele acontece, enquanto seu backend preserva a saída independentemente de quem estiver acompanhando.

Entrega via webhook
Entrega durável, de servidor para servidor, de cada idioma à medida que é concluído
Criar jobs
Envie conteúdo para tradução e receba o groupId ao qual você vai se conectar aqui
Acompanhar um grupo de jobs
Status do grupo e o que a conclusão parcial significa para o grupo

Esta página foi útil?

Max PrilutskiyMax Prilutskiy·Atualizado há 12 dias·6 min de leitura