Blog.

Translation APIs: Traducción y Detección en el Navegador

Translation APIs: Traducción y Detección en el Navegador
12 min read

La web es global, pero el contenido a menudo no lo es. Las APIs de traducción tradicionales (Cloud Translation API, DeepL, etc.) son caras, lentas, y requieren enviar todo el contenido a servidores externos. Para aplicaciones en tiempo real como chats, comentarios en vivo, o soporte multilingüe, esto genera latencia perceptible y levanta preocupaciones de privacidad.

Chrome introduce dos APIs que resuelven esto: Translator API y Language Detector API. Ambas ejecutan modelos de traducción y detección de idioma completamente en el dispositivo del usuario, sin conexión a internet y sin enviar datos a servidores.

¿Por Qué Importa?

La diferencia fundamental es privacidad: puedes traducir documentos legales, historiales médicos, o conversaciones privadas sin que un solo byte salga del dispositivo.

Casos de uso que antes eran imposibles:

  • Sectores regulados: Traducir historiales médicos sin violar HIPAA, documentos financieros sin incumplir compliance
  • Aplicaciones offline-first: Traducciones en dispositivos sin conexión
  • Chat en tiempo real: Traducción instantánea sin latencia de red

El trade-off: los modelos on-device son más pequeños que Google Translate o DeepL, por lo que la calidad puede ser inferior para textos complejos. Para la mayoría de casos (soporte, chat, contenido web), la calidad es suficiente.

Configuración del Entorno

Las Translation APIs comparten los mismos requisitos de hardware y configuración que las otras Built-in AI APIs. Si ya configuraste tu entorno, estás listo. Si no:

  1. Chrome Canary o Dev (versión 138+)
  2. Flags necesarios en chrome://flags:
    • Enables optimization guide on device: Enabled BypassPerfRequirement
    • Prompt API for Gemini Nano: Enabled
    • Translation API: Enabled
    • Language Detection API: Enabled
  3. Verificar componentes en chrome://components: Actualizar "Optimization Guide On Device Model"

El Flujo de Trabajo Completo

Imagina un chat de soporte donde el agente habla español y el usuario inglés. Queremos traducción instantánea, privada, y sin depender de servicios externos.

Paso 1: Detectar el Idioma

Primero, necesitamos saber qué idioma habla el usuario. No asumas nada del navigator.language (el usuario puede tener el navegador en inglés pero escribir en español).

// 1. Verificar soporte del navegador
if (!('LanguageDetector' in self)) {
  console.error('Language Detector API no soportada');
  return;
}

// 2. Verificar disponibilidad del modelo
const availability = await LanguageDetector.availability();

if (availability === 'unavailable') {
  // Hardware insuficiente, usar cloud API como fallback
  return;
}

// 3. Crear el detector
const detector = await LanguageDetector.create();

// 4. Detectar idioma
const results = await detector.detect('Hello, how can I help you today?');

// results es un array ordenado por confianza descendente
const bestMatch = results[0];
console.log(bestMatch.detectedLanguage); // "en"
console.log(bestMatch.confidence); // 0.98

// El último elemento siempre es "und" (undetermined), representando
// la probabilidad de que el texto no sea reconocido
console.log(results[results.length - 1]);
// { detectedLanguage: "und", confidence: 0.02 }

La API retorna múltiples candidatos porque textos pueden ser multilingües ("I love sushi" puede detectar inglés y japonés).

Paso 2: Traducir el Texto

Una vez sabemos el idioma origen (en) y destino (es), creamos el traductor.

// 1. Verificar soporte
if (!('Translator' in self)) {
  console.error('Translator API no soportada');
  return;
}

// 2. Verificar disponibilidad para el par de idiomas
const availability = await Translator.availability({
  sourceLanguage: 'en',
  targetLanguage: 'es',
});

if (availability === 'unavailable') {
  // Par de idiomas no soportado
  console.error('Traducción en-es no disponible en este dispositivo');
  return;
}

// 3. Crear traductor
const translator = await Translator.create({
  sourceLanguage: 'en',
  targetLanguage: 'es',
});

// 4. Traducir
const translated = await translator.translate(
  'Hello, how can I help you today?'
);
console.log(translated);
// Output: "Hola, ¿cómo puedo ayudarte hoy?"

// 5. Limpiar recursos
translator.destroy();

Gestión de Paquetes de Idiomas

Los modelos de traducción son modulares: Chrome descarga pares de idiomas bajo demanda (en-es, ja-en, fr-de, etc.). Implicaciones:

  1. Primera vez es lenta: create() puede tardar segundos/minutos descargando el paquete
  2. Requiere user activation: La descarga necesita un click del usuario
  3. Almacenamiento: Cada paquete ocupa cientos de MB

Monitoreo de Descarga

Implementa una UX clara para mostrar el progreso de descarga:

const availability = await Translator.availability({
  sourceLanguage: 'en',
  targetLanguage: 'es',
});

if (availability === 'unavailable') {
  showError('Este par de idiomas no está soportado en tu dispositivo');
  return;
}

if (availability === 'downloadable') {
  // Mostrar botón de descarga (requiere user activation)
  showDownloadButton(
    'Descargar modelo de traducción (una sola vez)',
    async () => {
      const translator = await Translator.create({
        sourceLanguage: 'en',
        targetLanguage: 'es',
        monitor(m) {
          m.addEventListener('downloadprogress', e => {
            // e.loaded: 0 a 1 (progreso normalizado)
            const percentage = Math.round(e.loaded * 100);
            updateProgressBar(percentage);
            console.log(`Descargando: ${percentage}%`);
          });
        },
      });

      hideDownloadButton();
      showTranslationUI();
    }
  );
  return;
}

if (availability === 'downloading') {
  showMessage('Descarga en progreso... espera un momento');
  return;
}

// availability === 'available'
// El modelo ya está descargado, crear directamente
const translator = await Translator.create({
  sourceLanguage: 'en',
  targetLanguage: 'es',
});

Nota: availability reporta "downloadable" para todos los pares de idiomas hasta que tu sitio descargue ese par (por privacidad).

Estados de Disponibilidad

EstadoSignificadoAcción Requerida
unavailableHardware insuficiente o idioma no soportadoUsar cloud API como fallback
downloadableModelo necesita descargarseMostrar botón de descarga (requiere user activation)
downloadingDescarga en progresoMostrar UI de progreso
availableModelo listo para usarCrear traductor directamente

Limitaciones de Input y Quota

Al igual que las Task APIs, Translator tiene límites de tamaño de entrada:

const translator = await Translator.create({
  sourceLanguage: 'en',
  targetLanguage: 'es',
});

console.log(translator.inputQuota);
// Ej: 4096 (valor específico de implementación)

// Medir uso ANTES de traducir
const longText = '...texto muy largo...';
const usage = await translator.measureInputUsage(longText);

if (usage > translator.inputQuota) {
  console.error(`Texto excede límite: ${usage} > ${translator.inputQuota}`);
  // Dividir texto en chunks o usar cloud API
}

Advertencia: measureInputUsage() requiere un round-trip al modelo. No lo uses en el evento input (sería demasiado frecuente). Úsalo al hacer clic en "Traducir" o con debouncing (~500ms).

Traducción en Streaming

Para textos largos (varios párrafos), la traducción puede tardar segundos. El streaming mejora la percepción de velocidad mostrando resultados progresivamente:

const translator = await Translator.create({
  sourceLanguage: 'en',
  targetLanguage: 'es',
});

const outputElement = document.querySelector('#translation-output');
outputElement.textContent = ''; // Limpiar

try {
  const stream = translator.translateStreaming(longText);

  for await (const chunk of stream) {
    // Cada chunk contiene el texto nuevo (delta), no acumulativo
    outputElement.textContent += chunk;
  }
} catch (error) {
  if (error.name === 'QuotaExceededError') {
    console.error(`Input demasiado largo: ${error.requested} > ${error.quota}`);
  } else {
    console.error('Error en traducción:', error);
  }
}

translator.destroy();

UX recomendada: Muestra un skeleton loader durante el streaming y reemplázalo progresivamente con el texto traducido.

Language Detector: Consideraciones Importantes

Precisión vs Longitud de Input

La precisión depende dramáticamente de la longitud del texto. Textos muy cortos (<10 palabras) dan resultados no confiables.

// ❌ MAL: Muy corto
await detector.detect('Hi');

// ✅ BIEN: Suficiente contexto
await detector.detect(
  'Hello! I would like to know more about your product. Can you help me?'
);

Para frases cortas, usa heurísticas simples o asume el idioma de navigator.language como fallback.

Idiomas Esperados

Si conocés los idiomas posibles de antemano, optimizá precisión y velocidad con expectedInputLanguages:

const detector = await LanguageDetector.create({
  expectedInputLanguages: ['en', 'es', 'fr'],
});

Trade-off: Idiomas no esperados pueden no detectarse correctamente o lanzar NotSupportedError.

Monitoreo de descarga

Al igual que otras Built-in AI APIs, LanguageDetector.create() acepta un callback monitor(m) para rastrear progreso de descarga. Ver ejemplo de Translator.

Caso de Uso Real: Chat Multilingüe

Combinar ambas APIs permite crear un chat con traducción automática en tiempo real:

  1. Detección automática: Identifica el idioma cuando hay suficiente contexto (>10 palabras)
  2. Cache de traductores: Reutiliza instancias para cada par de idiomas
  3. Traducción bidireccional: Usuario ↔ Agente
  4. Fallback a cloud: Para pares no disponibles

Ver Demo Interactiva →

La demo muestra un chat en tiempo real donde:

  • El usuario puede escribir en inglés, español, francés, alemán o portugués
  • El agente recibe los mensajes traducidos a su idioma configurado
  • Las respuestas del agente se traducen de vuelta al idioma del usuario
  • Ambos paneles muestran las traducciones con el texto original

Procesamiento Secuencial y UX

Advertencia: Las traducciones se procesan secuencialmente. Múltiples llamadas a translate() se bloquean hasta que las anteriores completen.

// ❌ Esto NO traduce en paralelo
const promises = messages.map(msg => translator.translate(msg));
const results = await Promise.all(promises);

Para múltiples mensajes, implementa indicadores de progreso acumulado.

Control de Acceso y Cancelación

Las Translation APIs siguen los mismos patrones de las otras Built-in AI APIs:

  • Permissions Policy: Para habilitar en cross-origin iframes, usa allow="translator; language-detector" en el elemento <iframe>
  • Cancelación con AbortController: Soporta signal en create() y translate() para cancelar descargas o traducciones en progreso
  • Web Workers: No soportado actualmente

Consulta la documentación de Prompt API para ejemplos detallados de estos patrones, que funcionan de manera idéntica en Translation APIs.

Manejo de Errores

Las Translation APIs lanzan errores estándar (QuotaExceededError, NotSupportedError, NetworkError, AbortError). El error más común es QuotaExceededError cuando el texto excede inputQuota:

try {
  const result = await translator.translate(text);
} catch (error) {
  if (error.name === 'QuotaExceededError') {
    // Dividir en chunks o usar cloud API
    const result = await cloudTranslate(text, from, to);
  } else if (error.name === 'NotSupportedError') {
    console.error('Par de idiomas no soportado, usar cloud API');
  }
}

Comparativa: On-Device vs Cloud Translation

AspectoTranslation APIs (On-Device)Cloud APIs (Google Translate, DeepL)
Privacidad✅ Total (sin salida de datos)❌ Contenido enviado a servidores
Latencia✅ <100ms (local)⚠️ 200-500ms (network round-trip)
Costo✅ Gratis (sin límites)❌ Pay-per-character
Offline✅ Funciona sin internet❌ Requiere conexión
Calidad⚠️ Buena (modelos pequeños)✅ Excelente (modelos grandes)
Pares de idiomas⚠️ Limitados por hardware✅ 100+ idiomas
Textos largos⚠️ Límite ~4K tokens✅ Sin límites prácticos
Soporte navegador⚠️ Solo Chrome 138+✅ Universal (API REST)

Estrategia: Translation APIs como default, fallback a cloud para textos largos, pares no soportados, o hardware insuficiente.

Conclusión

Las Translation APIs traen traducción y detección de idioma directamente al navegador, sin enviar datos a servidores. Esto abre casos de uso que antes eran imposibles: traducción de contenido sensible en sectores regulados, aplicaciones offline-first, y chat en tiempo real sin latencia de red.

El trade-off es claro: los modelos on-device son más pequeños, por lo que la calidad no alcanza a Google Translate o DeepL para textos complejos. Pero para la mayoría de casos (soporte, chat, contenido web), la calidad es suficiente y los beneficios superan las limitaciones.

Implementá estas APIs con progressive enhancement: detectá soporte, verificá disponibilidad, proporcioná fallbacks a cloud APIs, y diseñá UX claras para descargas de modelos. Con este enfoque, podés crear experiencias verdaderamente globales que respetan la privacidad del usuario y funcionan en cualquier condición de red.

Comments

Share your thoughts and join the discussion


Related Posts