A tradução simultânea com IA parece um único recurso, mas é um pequeno sistema: o microfone captura áudio, um modelo transforma fala em texto, outro reorganiza o sentido em outro idioma e um sintetizador devolve a resposta em voz. Vamos construir essa versão mínima e, depois, entender por que o intervalo entre essas etapas decide se a conversa parece natural ou um walkie-talkie quebrado.

O resultado deste roteiro será um fluxo executável em JavaScript que recebe uma entrada de texto representando a fala, envia essa informação para uma API compatível, traduz o conteúdo e devolve o resultado. A síntese de voz entra como a próxima etapa, porque a rota e os campos específicos dependem da modalidade de voz escolhida. A arquitetura, porém, já fica pronta para streaming.

A primeira versão precisa ser lenta

Comece sem tempo real. Parece contraditório, mas essa escolha elimina duas variáveis difíceis ao mesmo tempo: o transporte contínuo do áudio e o controle de interrupções. Se a transcrição não entende o arquivo inteiro, transmitir pedaços não conserta o problema; apenas torna o erro mais difícil de localizar.

O pipeline mínimo é:

áudio -> transcrição -> tradução -> texto traduzido

A tradução deve receber texto, não áudio bruto. Isso separa reconhecimento de fala e compreensão de linguagem. Se a saída ficar errada, você consegue perguntar se o defeito veio do ouvido do sistema ou da interpretação. Essa separação também permite trocar o idioma de destino sem enviar o áudio novamente.

Crie um arquivo JavaScript executável:

const apiKey = process.env.VOICE_API_KEY;
const baseUrl = process.env.VOICE_API_BASE_URL;
const transcriptionModel = process.env.TRANSCRIPTION_MODEL;
const languageModel = process.env.LANGUAGE_MODEL;

if (!apiKey || !baseUrl || !transcriptionModel || !languageModel) {
  throw new Error("Set the voice API configuration before running the script");
}

const headers = {
  Authorization: "Bearer " + apiKey,
  "Content-Type": "application/json",
};

async function transcribeAudio(audioText) {
  const response = await fetch(`${baseUrl}/chat/completions`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      model: transcriptionModel,
      messages: [
        {
          role: "user",
          content: `Transcribe this spoken content accurately. Keep names, numbers, and punctuation when possible:\n\n${audioText}`,
        },
      ],
    }),
  });

  if (!response.ok) throw new Error(await response.text());
  const data = await response.json();
  return data.choices[0].message.content;
}

async function translateText(text, targetLanguage) {
  const response = await fetch(`${baseUrl}/chat/completions`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      model: languageModel,
      messages: [
        {
          role: "user",
          content: `Translate the text below to ${targetLanguage}. Preserve the meaning, tone, names, and numbers. Return only the translation.\n\n${text}`,
        },
      ],
    }),
  });

  if (!response.ok) throw new Error(await response.text());
  const data = await response.json();
  return data.choices[0].message.content;
}

const sourceText = process.argv.slice(2).join(" ");

if (!sourceText) {
  throw new Error('Usage: run the script with a spoken sentence as input');
}

const transcript = await transcribeAudio(sourceText);
const translation = await translateText(transcript, "Brazilian Portuguese");

console.log("Transcript:");
console.log(transcript);
console.log("\nTranslation:");
console.log(translation);

Este primeiro arquivo usa texto no lugar do microfone para validar a lógica sem amarrar o exemplo a uma rota ou a um identificador de modelo não especificado. Para usar áudio real, conecte uma API de transcrição compatível e siga os campos definidos na documentação do serviço escolhido. A mesma regra vale para a autenticação e para a URL base, que devem vir do contrato da API usada no projeto.

Execute definindo as variáveis de ambiente correspondentes à sua API:

export VOICE_API_KEY="sua-chave"
export VOICE_API_BASE_URL="URL_BASE_DA_API"
export TRANSCRIPTION_MODEL="IDENTIFICADOR_DO_MODELO_DE_TRANSCRICAO"
export LANGUAGE_MODEL="IDENTIFICADOR_DO_MODELO_DE_TRADUCAO"

A validação é simples: a primeira saída deve preservar o sentido da frase original, e a segunda deve estar em português brasileiro sem comentários adicionais. Se aparecer explicação junto da tradução, o prompt não está restritivo o bastante.

O áudio não deve esperar a frase inteira

Na versão real, o navegador captura pequenos blocos do microfone e os envia por uma conexão persistente, normalmente via WebSocket ou WebRTC. O servidor acumula esses blocos até detectar um ponto de estabilidade, chamado de segmento final. Antes disso, a transcrição é provisória.

Essa distinção é essencial. Um segmento parcial pode atualizar a legenda imediatamente, mas não deveria disparar a voz sintetizada a cada palavra. Caso contrário, o sistema falaria três versões incompletas da mesma frase. Use resultados parciais para exibição e resultados finalizados para tradução e áudio.

O fluxo fica assim:

bloco de áudio -> transcrição parcial -> legenda provisória

segmento final -> tradução -> síntese de voz -> reprodução

O modelo de linguagem também precisa de contexto. Traduza segmentos curtos demais e você perde pronomes, tempos verbais e referências. Espere demais e a conversa acumula atraso. O tamanho ideal depende do idioma, do ruído e do ritmo da fala. Essa parte ainda exige medição no seu cenário, não uma regra universal.

Onde a latência se multiplica

Meça separadamente o tempo de envio do áudio, o tempo de transcrição, o tempo de tradução e o tempo de síntese. Se essas etapas forem executadas em série, cada espera será somada à seguinte e a latência percebida crescerá.

O ganho vem de sobrepor trabalho. Enquanto o sistema sintetiza o segmento atual, o microfone já captura o próximo. Enquanto a pessoa continua falando, a transcrição atualiza a legenda. O pipeline deixa de ser uma fila única e vira uma linha de produção.

Registre três tempos: captura até texto parcial, fim da fala até texto final e texto final até áudio reproduzível. Registre também o tempo total percebido pelo usuário. Uma tradução tecnicamente correta pode perder utilidade quando cada resposta chega depois da próxima frase.

Como recolocar a voz no circuito

Depois de validar o texto, acrescente a síntese. Consulte a documentação da API de síntese de voz escolhida para descobrir a rota, os campos do corpo, o idioma, a voz e o formato de saída. Não invente esse contrato no código.

A reprodução precisa aceitar interrupções. Se a pessoa original parar de falar e retomar, descarte o áudio traduzido que ainda não foi reproduzido quando ele ficar obsoleto. Uma fila de áudio sem cancelamento transforma uma conversa em uma sequência atrasada de respostas antigas.

Também preserve os limites humanos. Nomes próprios, endereços, números e frases ambíguas merecem confirmação visual. Em ambientes barulhentos, mostre a transcrição original junto da tradução. O usuário precisa perceber quando o sistema ouviu algo errado.

O teste que separa demonstração de produto

Grave frases curtas, frases com números e trechos com interrupções. Para cada uma, anote a transcrição, a tradução, o tempo até a primeira legenda e o tempo até o primeiro áudio. Repita o teste com ruído e com dois falantes.

Se a API fornecer informações de uso, registre esses dados junto do identificador da sessão. Múltiplas chamadas por segmento aumentam o consumo, especialmente quando o pipeline separa transcrição, tradução e síntese. Compare o custo por minuto de áudio e o custo por conversa, não apenas o custo de uma chamada isolada.

Quando a versão mínima estiver estável, a evolução natural é combinar transcrição, tradução e síntese em streaming, adicionar detecção de troca de falante e criar uma política clara para segmentos incertos. Em muitos produtos, o desafio principal não está apenas na síntese em outro idioma, mas em decidir quando o sistema ouviu o bastante para falar sem transformar uma conversa viva em um eco atrasado.