Inspiración

En una conferencia como Nerdearla, un mismo escenario recibe charlas en español y en inglés, una tras otra, y miles de personas las siguen desde la sala y desde la transmisión. Quienes tienen una discapacidad auditiva, o no hablan el idioma de quien expone, quedan afuera. La interpretación profesional requiere una cabina por idioma y por sala, un costo que la mayoría de los eventos comunitarios no puede afrontar.

El nombre proviene de los lenguaraces, los intérpretes que mediaban entre lenguas en la historia del Río de la Plata. Mi objetivo fue construir un lenguaraz para cada escenario: de código abierto, económico y lo suficientemente robusto como para funcionar durante toda una jornada sin supervisión.

Qué hace

Lenguaraz convierte el audio de cada sala en subtítulos en vivo, en el idioma original y traducidos al español, inglés, portugués, francés, alemán o italiano, para varias salas en simultáneo.

El audio puede ingresar desde una consola web (micrófono, línea de la consola de sonido o un archivo) o directamente desde la transmisión del escenario (SRT, RTMP o HLS). El público escanea el código QR de la sala y los subtítulos se abren en su celular, en el idioma de su teléfono, sin instalar ninguna aplicación. Con el modo Escuchar, el teléfono lee cada línea traducida en voz alta: con auriculares, los subtítulos se convierten en interpretación simultánea, pensada especialmente para personas ciegas o con baja visión.

Además, ofrece un modo proyector y un overlay para OBS y vMix. También genera la transcripción de cada charla en VTT, SRT o TXT, lista para publicar junto a la grabación. Un panel de producción muestra el estado de cada sala, el nivel de audio, la latencia y los errores. Puede funcionar con Gemini o de manera completamente local, con Whisper y Gemma.

Cómo lo construí

Lenguaraz es un único programa en Go que incluye la interfaz web, desarrollada con HTML, CSS y JavaScript sin frameworks. En el navegador de quien opera la sala, el audio se captura, se convierte a 16 kHz y se envía al servidor. Allí cada sala funciona como una tubería independiente:

  • Gemini Live transcribe el audio en tiempo real, usando el glosario de la sala para reconocer mejor nombres propios y términos técnicos.
  • Gemini Flash-Lite traduce cada oración terminada.
  • Los subtítulos llegan a todos los dispositivos conectados mediante Server-Sent Events.

Elegí transcribir primero y traducir el texto después, en lugar de traducir el audio directamente, por una cuestión de costos. El audio se procesa una sola vez por sala y cada idioma adicional es apenas una llamada de texto:

$$ C_{\text{sala}} \approx c_{\text{audio}} \cdot T \;+\; L \cdot c_{\text{texto}} \cdot W $$

$$T$$ es la duración de la charla, $$L$$ la cantidad de idiomas de destino y $$W$$ las palabras pronunciadas. Con modelos que devuelven audio traducido, el primer término se pagaría $$L$$ veces.

Desarrollé el proyecto con Claude Code, a partir de un documento de diseño y un plan de implementación. Incluye pruebas unitarias con transcripciones reales grabadas y una batería de 49 verificaciones de punta a punta en el navegador con Playwright. El video de presentación también está construido con código, mediante Remotion.

Desafíos

Determinar cuándo termina una oración. Gemini Live reenvía la frase completa cada medio segundo y solo la da por cerrada cuando detecta un silencio. En un discurso continuo, eso puede demorar más de veinte segundos, y esperar ese cierre retrasaba las traducciones. La solución fue confirmar cada oración en cuanto dos palabras posteriores la validan, o tras una pausa de 0,8 segundos, y dividir las oraciones muy largas en una coma o un punto y coma. Así, los subtítulos llegan al público en aproximadamente dos segundos.

Sesiones más cortas que una charla. Las sesiones de Gemini Live tienen un límite de diez minutos. El sistema las renueva a los nueve, reemplaza las que dejan de transcribir y se reconecta de forma automática, conservando el audio recibido mientras tanto.

Traducciones lentas. Cada oración espera su traducción, por lo que una sola respuesta demorada afecta a toda la sala. Si una solicitud no responde en 1,5 segundos, se envía nuevamente y se utiliza la primera respuesta que llegue:

$$ t_{\text{traducción}} = \min\left(t_1,\; 1{,}5\,\text{s} + t_2\right) $$

Modelos locales sin streaming. Los servidores de Whisper reciben archivos, no transmisiones continuas. El motor local corta el audio en las pausas del orador, o como máximo cada diez segundos, y vuelve a transcribir la frase en curso cada 1,5 segundos para mostrar una línea en vivo.

Detalles del mundo real. Cada uno de estos casos necesitó su propia solución:

  • Los navegadores solo permiten capturar audio en conexiones seguras.
  • OBS carga sus fuentes antes de que el servidor esté disponible, por lo que un service worker mantiene el overlay accesible.
  • Un espectador con conexión lenta no puede demorar a toda la sala: se lo desconecta y, al volver, recupera las últimas 50 líneas.
  • Con el modo Escuchar activo, los lectores de pantalla anunciaban cada línea dos veces; ahora esos anuncios se silencian mientras el teléfono lee.

Qué aprendí

En el reconocimiento de voz en tiempo real, la dificultad principal no es transcribir, sino decidir cuándo un texto es definitivo.

La accesibilidad es un requisito de diseño y no una capa estética. En la práctica eso significa:

  • Contraste de 7:1 (WCAG AAA).
  • Una tipografía que distingue claramente la I, la l y el 1.
  • Botones de al menos 44 píxeles.
  • El idioma correcto marcado en cada subtítulo, para que los lectores de pantalla lo pronuncien bien.

Por último, separar la transcripción y la traducción detrás de dos interfaces simples hizo que el modo local, y los motores simulados que permiten correr todas las pruebas sin conexión, resultaran casi sin costo adicional.

Próximos pasos

  • Utilizarlo en vivo en un escenario real, con el glosario ajustado a cada charla.
  • Escalar la audiencia a más de un servidor mediante un bus compartido (Redis, NATS o Pub/Sub).
  • Incorporar un editor para corregir la transcripción de cada charla antes de publicarla.

Built With

Share this project:

Updates

Submission history