Voltar ao blog
Terminal window showing a Gemfile entry for the PolyLingo gem alongside a short Ruby translate call and its output hash.

Traduzir conteúdo estruturado do Ruby com a gem PolyLingo

By Robert M

Traduzir conteúdo estruturado do Ruby com a gem PolyLingo

A gem PolyLingo para Ruby está agora disponível no RubyGems. Ela cobre toda a superfície da API PolyLingo: tradução síncrona, requisições em lote, trabalhos assíncronos com polling e todos os endpoints utilitários. Não possui dependências em tempo de execução e requer Ruby 2.7 ou superior.

Este post guia pela instalação, configuração do cliente e todos os métodos disponíveis na gem.


Instalação

Adicione ao seu Gemfile:

gem "polylingo"

Então execute:

bundle install

Ou instale diretamente:

gem install polylingo

Sem dependências em tempo de execução. A gem usa apenas a biblioteca padrão do Ruby para HTTP.


Configurando o cliente

require "polylingo"

client = PolyLingo.new(api_key: ENV.fetch("POLYLINGO_API_KEY"))

Duas configurações opcionais estão disponíveis:

client = PolyLingo.new(
  api_key:  ENV.fetch("POLYLINGO_API_KEY"),
  base_url: "https://api.usepolylingo.com/v1", # padrão, sobrescreva para instâncias self-hosted
  timeout:  120,                               # segundos (abertura + leitura), padrão 120
)

Mantenha sua chave API em uma variável de ambiente. Nunca a codifique diretamente e nunca a comite no controle de versão.


Traduzindo conteúdo

Requisição única

Passe seu conteúdo, um array de códigos de idiomas alvo e um formato opcional:

result = client.translate(
  content: "# Olá\n\nEste é um conteúdo **estruturado**.",
  targets: %w[es fr de],
  format:  "markdown"
)

puts result["translations"]["es"]
puts result["usage"]["total_tokens"]

O parâmetro format aceita plain, markdown, json ou html. Se omitido, a API detecta o formato automaticamente. Você também pode passar source como dica de idioma e model como "standard" (padrão) ou "advanced".

A preservação do formato funciona da mesma forma que o restante da API PolyLingo. Para json, apenas valores string são traduzidos e chaves, aninhamentos e tipos não-string permanecem intactos. Para markdown, títulos permanecem títulos e blocos de código são mantidos verbatim. Para html, tags e atributos são preservados e apenas nós de texto são traduzidos.

Traduzindo um arquivo JSON de localidade

require "json"

source = JSON.parse(File.read("config/locales/en.yml"))

result = client.translate(
  content: source.to_json,
  format:  "json",
  targets: %w[fr de ja]
)

%w[fr de ja].each do |locale|
  translated = JSON.parse(result["translations"][locale])
  File.write(
    "config/locales/#{locale}.json",
    JSON.pretty_generate(translated)
  )
  puts "Escreveu config/locales/#{locale}.json"
end

Uma requisição trata todos os três locais. As chaves são idênticas à fonte em cada arquivo de saída.


Requisições em lote

Envie até 100 itens de conteúdo em uma única requisição, cada um com seu próprio id e format opcional:

result = client.batch(
  items: [
    { id: "hero_title",    content: "Bem-vindo de volta",   format: "plain" },
    { id: "hero_subtitle", content: "Aqui está o que mudou hoje", format: "plain" },
    { id: "cta",           content: "Comece agora",    format: "plain" },
  ],
  targets: %w[es fr de]
)

result["results"].each do |row|
  puts "#{row["id"]}: #{row["translations"]["es"]}"
end

Todos os itens compartilham o mesmo array targets. O id que você passa é preservado na resposta para que você possa mapear os resultados de volta aos seus dados fonte sem depender da ordem.


Trabalhos assíncronos

Para traduções de longa duração, a API de trabalhos aceita a requisição, retorna um job_id imediatamente e permite que você faça polling pelo resultado. A gem trata isso de duas formas.

Enfileirar e fazer polling manualmente

accepted = client.jobs.create(
  content: File.read("content/long-article.md"),
  targets: %w[es fr de ja zh],
  format:  "markdown"
)

job_id = accepted["job_id"]

loop do
  sleep 5
  state = client.jobs.get(job_id)
  break if %w[complete failed].include?(state["status"])
end

if state["status"] == "complete"
  translations = state["translations"]
end

Uma chamada que faz polling até terminar

done = client.jobs.translate(
  content:       File.read("content/long-article.md"),
  targets:       %w[es fr de],
  format:        "markdown",

  # Sobrescritas opcionais (padrões mostrados):
  poll_interval: 5,    # segundos entre polls, padrão 5
  timeout:       1200, # orçamento total de espera em segundos, padrão 1200 (20 minutos)
  on_progress:   ->(queue_position) { puts "Posição na fila: #{queue_position.inspect}" }
)

translations = done["translations"]
usage        = done["usage"]

Todos os valores de tempo na gem Ruby usam segundos, consistente com as convenções Ruby. A lambda on_progress é disparada a cada poll e recebe a posição atual na fila como um inteiro, ou nil se a API não retornar uma.


Endpoints utilitários

health    = client.health
# => { "status" => "ok", "timestamp" => "..." }

languages = client.languages
# => { "languages" => [{ "code" => "en", "name" => "English", "rtl" => false }, ...] }

usage     = client.usage
# => { "usage" => { "tokens_used" => 12000, "tokens_remaining" => 38000, ... } }

health e languages não requerem chave API. usage retorna o consumo de tokens para o mês corrente do calendário para a conta autenticada.


Tratamento de erros

Todos os erros herdam de PolyLingo::PolyLingoError. Capture as subclasses específicas que deseja tratar:

begin
  result = client.translate(
    content: "# Olá",
    targets: %w[es],
    format:  "markdown"
  )
rescue PolyLingo::AuthError => e
  # HTTP 401 — chave API inválida, ausente ou revogada
  puts "Falha na autenticação: #{e.message}"
rescue PolyLingo::RateLimitError => e
  # HTTP 429 — limite por minuto atingido
  retry_after = e.retry_after # segundos inteiros ou nil
  sleep retry_after if retry_after
  retry
rescue PolyLingo::JobFailedError => e
  # Trabalho assíncrono atingiu status terminal falhado, ou polling expirou
  puts "Trabalho falhou: #{e.job_id}"
rescue PolyLingo::PolyLingoError => e
  # Todos os outros erros da API
  puts "#{e.status}: #{e.error} — #{e.message}"
end

RateLimitError#retry_after retorna um inteiro do corpo JSON da resposta ou do cabeçalho Retry-After, o que estiver presente, ou nil se nenhum estiver incluído. JobFailedError#job_id fornece o ID do trabalho falhado quando conhecido.


Referência rápida

MétodoEndpointRequer autenticação
client.healthGET /healthNão
client.languagesGET /languagesNão
client.translate(...)POST /translateSim
client.batch(...)POST /translate/batchSim
client.usageGET /usageSim
client.jobs.create(...)POST /jobsSim
client.jobs.get(job_id)GET /jobs/:idSim
client.jobs.translate(...)POST /jobs + pollingSim

Comece agora

A gem está no RubyGems em rubygems.org/gems/polylingo. O código-fonte está em github.com/UsePolyLingo/polylingo-ruby. A documentação completa da API está em usepolylingo.com/docs.

O plano gratuito inclui 50.000 tokens por mês. Não é necessário cartão de crédito.

gem install polylingo

Obtenha sua chave API