
Traducir contenido estructurado desde PHP con el SDK de PolyLingo
By Robert M
Traducir contenido estructurado desde PHP con el SDK de PolyLingo
El SDK de PolyLingo para PHP ya está disponible en Packagist. Instálalo con Composer, pásale una cadena de texto plano, Markdown, JSON o HTML, y obtén traducciones en todos los idiomas que necesites — sin escribir HTTP en crudo ni preocuparte de que tu estructura se estropee en el tránsito.
Esta publicación cubre la instalación, autenticación y toda la superficie del SDK: traducción síncrona, solicitudes por lotes, trabajos asíncronos y manejo de errores.
Instalación
Requiere PHP 7.4 o superior. Instálalo vía Composer:
composer require usepolylingo/polylingo
El SDK depende de guzzlehttp/guzzle ^7.8 y psr/http-client ^1.0. Ambos se instalan automáticamente.
Configuración del cliente
Crea una única instancia de PolyLingo y reutilízala en toda tu aplicación. La única opción requerida es tu clave API:
<?php
use PolyLingo\PolyLingo;
$client = new PolyLingo([
'apiKey' => getenv('POLYLINGO_API_KEY'),
]);
Guarda tu clave API en una variable de entorno. Nunca la codifiques directamente ni la subas al control de versiones.
Dos configuraciones opcionales que vale la pena conocer:
$client = new PolyLingo([
'apiKey' => getenv('POLYLINGO_API_KEY'),
'baseURL' => 'https://api.usepolylingo.com/v1', // por defecto, sobrescribe para instancias autoalojadas
'timeout' => 120_000, // milisegundos, por defecto 120000 (2 minutos)
]);
Traducción de contenido
Texto plano, Markdown, JSON o HTML
Pasa tu contenido y un array de códigos de idiomas destino a translate(). El campo format indica al SDK qué tipo de contenido está manejando:
$result = $client->translate([
'content' => '# Hello',
'targets' => ['es', 'fr', 'de'],
'format' => 'markdown',
]);
$es = $result['translations']['es'];
$tokens = $result['usage']['total_tokens'];
La opción format acepta plain, markdown, json o html. Si la omites, la API detecta automáticamente el formato a partir del contenido. También puedes pasar un indicio de idioma source y un valor model de standard (por defecto) o advanced.
La preservación del formato es el comportamiento clave aquí. Para contenido json, solo se traducen los valores de cadena. Las claves, anidamientos, arrays y tipos no cadena vuelven exactamente como los enviaste. Para markdown, los encabezados permanecen encabezados, los bloques de código se dejan tal cual y las URLs de los enlaces no se tocan. Para html, las etiquetas y atributos se preservan y solo se traducen los nodos de texto.
Traducción de un archivo de localización JSON
Un caso de uso común es traducir un archivo de localización. Envía todo el objeto como una cadena JSON:
$source = json_decode(file_get_contents('messages/en.json'), true);
$result = $client->translate([
'content' => json_encode($source),
'format' => 'json',
'targets' => ['de', 'fr', 'ja'],
]);
foreach (['de', 'fr', 'ja'] as $locale) {
$translated = json_decode($result['translations'][$locale], true);
file_put_contents(
"messages/{$locale}.json",
json_encode($translated, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n"
);
}
Una sola solicitud maneja los tres locales. Las claves permanecen intactas en cada archivo de salida.
Solicitudes por lotes
Usa batch() cuando tengas múltiples elementos de contenido separados para traducir. Puedes enviar hasta 100 elementos en una sola solicitud, cada uno con su propio id y format opcional:
$batch = $client->batch([
'items' => [
['id' => 'hero_title', 'content' => 'Welcome back', 'format' => 'plain'],
['id' => 'hero_subtitle', 'content' => 'Here is what is new today', 'format' => 'plain'],
['id' => 'cta', 'content' => 'Get started', 'format' => 'plain'],
],
'targets' => ['es', 'fr'],
]);
foreach ($batch['results'] as $row) {
echo $row['id'] . ': ' . $row['translations']['es'] . "\n";
}
Todos los elementos comparten el mismo array targets. La respuesta preserva el id que pasaste para cada elemento, para que puedas mapear los resultados a tus datos originales sin depender del orden.
Trabajos asíncronos
Para traducciones de larga duración (documentos grandes, muchos destinos o ambos) la API de trabajos acepta una solicitud, devuelve inmediatamente un job_id y te permite hacer polling para el resultado. El SDK maneja esto de dos maneras.
Encolar y hacer polling manualmente
$accepted = $client->jobs->create([
'content' => file_get_contents('long-article.md'),
'targets' => ['es', 'fr', 'de', 'ja', 'zh'],
'format' => 'markdown',
]);
$jobId = $accepted['job_id'];
// Hacer polling hasta que el trabajo alcance un estado terminal
do {
sleep(5);
$state = $client->jobs->get($jobId);
} while ($state['status'] === 'pending' || $state['status'] === 'processing');
if ($state['status'] === 'complete') {
$translations = $state['translations'];
}
Una llamada que hace polling hasta que termina
Si no necesitas control manual, jobs->translate() maneja el bucle de polling por ti:
$done = $client->jobs->translate([
'content' => file_get_contents('long-article.md'),
'targets' => ['es', 'fr', 'de'],
'format' => 'markdown',
// Sobrescrituras opcionales (se muestran valores por defecto):
// 'pollInterval' => 5000, // ms entre polls, por defecto 5000
// 'timeout' => 1_200_000, // presupuesto total de espera, por defecto 20 minutos
// 'onProgress' => function (?int $queuePosition) {
// echo "Queue position: {$queuePosition}\n";
// },
]);
$translations = $done['translations'];
$usage = $done['usage'];
El callback onProgress se dispara en cada poll con la posición actual en la cola si la API la devuelve, o null si no está disponible. Úsalo para registrar el progreso o actualizar una interfaz de usuario.
Endpoints de utilidad
Tres endpoints ligeros no necesitan parámetros más allá de la autenticación:
$health = $client->health();
// ['status' => 'ok', 'timestamp' => '...']
$langs = $client->languages();
// ['languages' => [['code' => 'en', 'name' => 'English', 'rtl' => false], ...]]
$usage = $client->usage();
// ['usage' => ['tokens_used' => 12000, 'tokens_remaining' => 88000, ...]]
GET /health y GET /languages no requieren clave API. GET /usage devuelve el consumo de tokens para el mes calendario actual para la cuenta autenticada.
Manejo de errores
Todos los errores del SDK extienden PolyLingo\Errors\PolyLingoException. Captura los subtipos específicos que quieras manejar de forma diferente:
use PolyLingo\Errors\AuthException;
use PolyLingo\Errors\JobFailedException;
use PolyLingo\Errors\PolyLingoException;
use PolyLingo\Errors\RateLimitException;
try {
$result = $client->translate([
'content' => '# Hello',
'targets' => ['es'],
'format' => 'markdown',
]);
} catch (AuthException $e) {
// HTTP 401 — clave API inválida, ausente o revocada
} catch (RateLimitException $e) {
// HTTP 429 — límite por minuto alcanzado
$retryAfter = $e->getRetryAfter(); // int|null segundos
} catch (JobFailedException $e) {
// Trabajo asíncrono alcanzó un estado terminal fallido
$jobId = $e->getJobId();
} catch (PolyLingoException $e) {
// Todos los demás errores de API
$httpStatus = $e->getHttpStatus();
$errorCode = $e->getErrorCode(); // ej. "invalid_request", "translation_error"
}
RateLimitException::getRetryAfter() devuelve el número de segundos que debes esperar antes de reintentar si la API incluye ese encabezado, o null si no está presente. JobFailedException::getJobId() te da el ID del trabajo fallido para que puedas registrarlo o mostrarlo al usuario.
Referencia rápida
| Método | Endpoint | Requiere autenticación |
|---|---|---|
$client->health() | GET /health | No |
$client->languages() | GET /languages | No |
$client->translate() | POST /translate | Sí |
$client->batch() | POST /translate/batch | Sí |
$client->usage() | GET /usage | Sí |
$client->jobs->create() | POST /jobs | Sí |
$client->jobs->get($id) | GET /jobs/:id | Sí |
$client->jobs->translate() | POST /jobs + polling | Sí |
Comenzar
El SDK está en Packagist en usepolylingo/polylingo. La documentación completa de la API está en usepolylingo.com/docs.
El nivel gratuito incluye 50,000 tokens por mes. No se requiere tarjeta de crédito.
composer require usepolylingo/polylingo