21. Translating the content

 

21. Traducción del contenido con ChatGPT

¡Hola y bienvenido de nuevo!

En esta lección vamos a implementar la funcionalidad de traducción completa usando ChatGPT. ¡Es la cereza del pastel de nuestra aplicación! Ahora los usuarios podrán traducir sus archivos de audio/video a múltiples idiomas con solo un click.


🎯 ¿Qué vamos a implementar?

Vamos a crear el sistema completo de traducción que:

  1. Captura la solicitud del formulario POST

  2. Valida el idioma seleccionado

  3. Configura la clase Whisper para traducción

  4. Llama a ChatGPT a través de nuestra API

  5. Procesa la respuesta y extrae el texto traducido

  6. Guarda en la BD usando el método update()

  7. Muestra el resultado al usuario


🔍 Flujo completo de traducción

text

┌─────────────────────────────────────────────────────────────┐

│             FLUJO COMPLETO DE TRADUCCIÓN                     │

├─────────────────────────────────────────────────────────────┤

│ 1. USUARIO:                                                  │

│    • En view.php?file=25                                     │

│    • Selecciona "French" del dropdown                        │

│    • Click en "Translate"                                    │

│                                                              │

│ 2. NAVEGADOR:                                                │

│    • Envía POST a view.php?file=25                           │

│    • Datos: lang=French, translate=Submit                    │

│                                                              │

│ 3. PHP (view.php):                                           │

│    • Detecta $_POST['translate']                             │

│    • Valida $_POST['lang'] en array permitido                │

│    • Configura $whisperObj para traducción                   │

│    • Llama a $whisperObj->convert()                          │

│                                                              │

│ 4. ChatGPT API:                                              │

│    • Recibe: "Traduce 'Hola mundo' a French"                 │

│    • Retorna: JSON con "Bonjour le monde"                    │

│                                                              │

│ 5. PHP procesa respuesta:                                    │

│    • Extrae: $text['choices'][0]['message']['content']       │

│    • Llama: $whisperObj->update(25, "Bonjour...", "French")  │

│    • Redirige a: view.php?file=25 (para ver resultado)       │

│                                                              │

│ 6. USUARIO ve resultado:                                     │

│    • Texto original + Traducción en francés                  │

│    • ¡Traducción guardada para siempre!                      │

└─────────────────────────────────────────────────────────────┘


🛠️ Implementación en view.php

Parte 1: Manejo de solicitud POST

php

// Al inicio de view.php, después del manejo de GET:

if($_SERVER['REQUEST_METHOD'] === "POST"){

    

    // Verificar que se hizo click en "Translate"

    if(isset($_POST['translate'])){

        

        // Obtener el archivo actual usando el ID de la URL

        $file = $whisperObj->getFileById($_GET['file']);

        

        if($file){

            // Obtener el idioma seleccionado

            $lang = $_POST['lang'];

            

            // Lista de idiomas permitidos (seguridad)

            $allowedLang = [

                'English',

                'French', 

                'Spanish',

                'German',

            ];

            

            // Validar que el idioma esté en la lista permitida

            if(in_array($lang, $allowedLang)){

                

                // Configurar la clase Whisper para traducción

                $whisperObj->dataType = 'Translate';

                $whisperObj->content  = $file->content;  // Texto a traducir

                $whisperObj->lang     = $lang;           // Idioma destino

                

                // Verificar que no haya errores previos

                if(!$whisperObj->errors()){

                    

                    // 1. Llamar a ChatGPT para traducir

                    $text = $whisperObj->convert();

                    

                    // 2. Extraer el texto traducido del JSON de respuesta

                    $translated = $text['choices'][0]['message']['content'];

                    

                    // 3. Guardar traducción en la base de datos

                    $whisperObj->update($file->ID, $translated, $lang);

                    

                    // 4. Redirigir para mostrar el resultado

                    header('Location: view.php?file='.$file->ID);

                }

            } else {

                // Mostrar error si idioma no permitido

                $error = "Invalid Language Selected!";

            }

        }

    }

}


📝 Explicación detallada de cada paso

1. Validación del formulario POST

php

if($_SERVER['REQUEST_METHOD'] === "POST"){

    // $_SERVER['REQUEST_METHOD'] nos dice el tipo de solicitud:

    // GET  → Cuando cargamos la página normalmente

    // POST → Cuando enviamos un formulario

}

¿Por qué verificamos POST?

  • GET: view.php?file=25 (cargar página)

  • POST: Datos del formulario (traducir)

2. Verificar botón "translate"

php

if(isset($_POST['translate'])){

    // El botón tiene: name="translate"

    // Cuando se hace click, $_POST['translate'] existe

    // Su valor es el texto del botón ("Translate")

}

3. Obtener el archivo actual

php

$file = $whisperObj->getFileById($_GET['file']);

// $_GET['file'] viene de la URL: view.php?file=25

// Importante: Aunque es POST, la URL sigue teniendo ?file=25

4. Validación de idioma (seguridad)

php

$allowedLang = ['English', 'French', 'Spanish', 'German'];

if(in_array($lang, $allowedLang)){

    // Solo procesar si el idioma está en la lista blanca

    // Previene que usuarios envíen idiomas no soportados

}

¿Por qué no confiar en el dropdown?

  • Usuarios podrían modificar el HTML con herramientas de desarrollo

  • Maliciosos podrían enviar idiomas no soportados

  • Siempre validar en el servidor (frontend es solo para UX)

5. Configurar la clase Whisper

php

$whisperObj->dataType = 'Translate';  // No 'ASR', para usar ChatGPT

$whisperObj->content  = $file->content; // Texto original a traducir

$whisperObj->lang     = $lang;        // Idioma destino

Recordemos cómo funciona getData() para traducción:

php

// Cuando dataType no es 'ASR', getData() genera:

{

  "model": "gpt-3.5-turbo",

  "messages": [

    {

      "role": "system",

      "content": "You will be provided with a text, and your task is to translate it into French"

    },

    {

      "role": "user", 

      "content": "Hola mundo"

    }

  ]

}

6. Procesar respuesta de ChatGPT

php

$text = $whisperObj->convert();

// $text contiene la respuesta JSON completa de OpenAI


$translated = $text['choices'][0]['message']['content'];

// Extraemos solo el texto traducido

Estructura de respuesta de ChatGPT API:

json

{

  "choices": [

    {

      "message": {

        "role": "assistant",

        "content": "Bonjour le monde"

      }

    }

  ]

}

Diagrama de acceso al dato:

text

$text

├── ['choices']           → Array de opciones

│   └── [0]              → Primera (y usualmente única) opción

│       └── ['message']  → Objeto mensaje

│           └── ['content'] → "Bonjour le monde" ← ¡ESTO QUEREMOS!

7. Guardar en BD y redirigir

php

$whisperObj->update($file->ID, $translated, $lang);

// Guarda en: translated='Bonjour le monde', lang='French'


header('Location: view.php?file='.$file->ID);

// Recarga la página para mostrar la traducción

// Usamos header() para redirección HTTP

¿Por qué redirigir?

  • Evita reenvío accidental si usuario refresca (F5)

  • Separación clara entre procesamiento y visualización

  • Mejor experiencia de usuario


🔧 Correcciones importantes en el código

Error común: dataType debe coincidir

php

// ERROR en el código original:

$whisperObj->dataType = 'Translate';  // ← Con 'T' mayúscula


// Pero en getApiUrl() y getHeader() verificamos:

if($this->dataType === "ASR"){  // ← Compara con "ASR"

    // Para ASR

} else {

    // Para TODO LO DEMÁS (incluye 'Translate')

}


// SOLUCIÓN: Puede ser 'Translate' o cualquier string que no sea 'ASR'

// O mejor, usar constante:

define('TYPE_TRANSLATION', 'translation');

$whisperObj->dataType = TYPE_TRANSLATION;

Manejo de errores mejorado:

php

// Después de $whisperObj->convert():

if(isset($text['error'])){

    $error = "Error de API: " . $text['error']['message'];

} else if(!isset($text['choices'][0]['message']['content'])){

    $error = "Formato de respuesta inesperado de ChatGPT";

} else {

    // Extraer y guardar traducción

    $translated = $text['choices'][0]['message']['content'];

    // ... resto del código

}


🎨 Visualización del formulario y resultados

Formulario de traducción:

html

<form id="form" method="POST">

    <div class="flex flex-col justify-center items-center">

        <div class="">

            <label for="lang">Select Language</label>

            <select id="lang" name="lang" class="px-4 py-3 mx-2 my-3">

                <option value="English">English</option>

                <option value="French">French</option>

                <option value="Spanish">Spanish</option>

                <option value="German">German</option>

            </select>

            

            <button id="translateBtn" name="translate" 

                    class="border border-gray-700 rounded px-6 py-2 cursor-pointer hover:bg-gray-100 font-normal">

                Translate

            </button>

        </div>

    </div>

</form>

Mostrar traducción existente:

php

<?php if($file->translated !== NULL): ?>

    <li id="translateContent" class="flex flex-col my-5 w-full">

        <h3 class="text-2xl"><?php echo $file->lang; ?></h3>

        <div>

            <?php echo $file->translated; ?>

        </div>

    </li>

<?php endif; ?>


💡 Cómo funciona internamente ChatGPT para traducción

Prompt enviado a ChatGPT:

text

System: You will be provided with a text, and your task is to translate it into French

User: Hola mundo, esto es una prueba de traducción automática.

Respuesta esperada:

text

Assistant: Bonjour le monde, ceci est un test de traduction automatique.

Ventajas de usar ChatGPT vs servicios de traducción tradicionales:

  1. Contexto → Entiende matices y frases idiomáticas

  2. Calidad → Traducciones más naturales

  3. Flexibilidad → Puedes personalizar instrucciones

  4. Unificar APIs → Usas el mismo proveedor (OpenAI) para todo


📊 Diagrama de tiempos de respuesta

text

┌─────────────────────────────────────────────────────────────┐

│             TIEMPOS ESTIMADOS DE TRADUCCIÓN                  │

├─────────────────────────────────────────────────────────────┤

│ 1. Validación PHP:               1-5 ms                      │

│ 2. Preparar datos ChatGPT:       5-10 ms                     │

│ 3. Llamada API OpenAI:           500-2000 ms (0.5-2 seg)     │

│ 4. Procesar respuesta JSON:      10-20 ms                    │

│ 5. Guardar en BD:                5-50 ms                     │

│ 6. Redirección:                  1-10 ms                     │

│                                                              │

│ TOTAL primera vez:              ~522-2095 ms (0.5-2.1 seg)   │

│                                                              │

│ Si traducción ya existe en BD:                               │

│ TOTAL subsecuente:              ~22-85 ms (0.02-0.09 seg)    │

│                                                              │

│ MEJORA: ¡Hasta 95 veces más rápido después del cache!        │

└─────────────────────────────────────────────────────────────┘


⚠️ Consideraciones y mejoras

1. Mostrar loader durante traducción:

javascript

// En view.php, agregar JavaScript:

$(document).ready(function(){

    $("#translateBtn").click(function(){

        $("#loader").removeClass('hidden');

    });

});

2. Manejo de errores en interfaz:

php

// Mostrar errores si existen:

<?php if(isset($error)): ?>

    <div class="flex bg-red-100 border border-red-400 text-red-700 px-4 py-2 rounded relative">

        <strong class="font-bold">Error:</strong>

        <span class="block sm:inline"><?php echo $error; ?></span>

    </div>

<?php endif; ?>

3. Traducciones múltiples (futuro):

php

// En lugar de sobreescribir, guardar historial:

public function addTranslation($fileID, $content, $lang){

    $stmt = $this->DB->prepare("

        INSERT INTO translations (file_id, content, lang, created_at)

        VALUES (:fileID, :content, :lang, NOW())

    ");

    // Permite múltiples traducciones del mismo archivo

}

4. Validación de límite de caracteres:

php

// ChatGPT tiene límites (4096 tokens ~ 3000 palabras)

if(strlen($file->content) > 3000){

    $error = "El texto es muy largo para traducir. Máximo 3000 palabras.";

}


🔍 Ejemplo práctico completo

Caso: Archivo ID 25 con contenido español

php

// 1. Usuario selecciona "French" y hace click

// 2. PHP recibe: $_POST['lang'] = "French", $_GET['file'] = "25"


// 3. Configuración para ChatGPT:

$whisperObj->dataType = 'Translate';

$whisperObj->content = "Hola mundo, ¿cómo estás hoy?";

$whisperObj->lang = "French";


// 4. Llamada a convert() envía a ChatGPT:

// Request: Traduce "Hola mundo..." a francés

// Response: {"choices":[{"message":{"content":"Bonjour le monde, comment vas-tu aujourd'hui ?"}}]}


// 5. Extracción:

$translated = "Bonjour le monde, comment vas-tu aujourd'hui ?";


// 6. Guardado:

$whisperObj->update(25, $translated, "French");


// 7. Redirección a: view.php?file=25

// 8. Usuario ve:

//    Text: Hola mundo, ¿cómo estás hoy?

//    French: Bonjour le monde, comment vas-tu aujourd'hui ?


🎯 Puntos Clave para Recordar

  1. GET vs POST → GET para cargar, POST para enviar datos

  2. Validación servidor → Siempre validar aunque el frontend restrinja

  3. Configurar propiedades → dataType, content, lang antes de convert()

  4. Estructura respuesta ChatGPT → $text['choices'][0]['message']['content']

  5. Redirección después de POST → Evita reenvíos accidentales (F5)

  6. Cache en BD → Traducciones se guardan para uso futuro

  7. Seguridad → Validar inputs, usar in_array() para idiomas permitidos


📋 Cuestionario de Repaso

Pregunta 1:

¿Por qué usamos header('Location: ...') después de procesar la traducción?
a) Para enviar un email de confirmación
b) Para evitar que el usuario reenvíe el formulario al refrescar (F5)
c) Para cerrar la sesión del usuario
d) Para redirigir a la página de pago

Pregunta 2:

¿Qué estructura tiene la respuesta JSON de ChatGPT que necesitamos?
a) $text['result']['translation']
b) $text['data']['content']
c) $text['choices'][0]['message']['content']
d) $text['response']['text']

Pregunta 3:

¿Por qué validamos el idioma con in_array($lang, $allowedLang) si ya tenemos un dropdown?
a) Porque PHP requiere validar todos los arrays
b) Porque usuarios malintencionados pueden modificar el HTML y enviar idiomas no soportados
c) Porque el dropdown no funciona en todos los navegadores
d) Porque así se ejecuta más rápido

Pregunta 4:

¿Qué tres propiedades debemos configurar en $whisperObj antes de llamar a convert() para traducción?
a) file, type, size
b) dataType, content, lang
c) apiKey, model, temperature
d) id, name, created_at

Pregunta 5:

¿Qué ventaja tiene guardar la traducción en la base de datos?
a) Evita llamadas repetidas y costosas a la API de ChatGPT
b) Hace que la traducción sea más precisa
c) Permite traducir a más idiomas de los que soporta ChatGPT
d) Mejora la seguridad de la aplicación


📝 Respuestas del Cuestionario

  1. b) La redirección después de un POST previene el "repost" cuando el usuario refresca la página (F5). Esto evita que se procese la misma traducción múltiples veces.

  2. c) La API de ChatGPT retorna un array choices que contiene objetos message, y dentro de cada message está el content con la respuesta del asistente. [0] accede al primer elemento del array.

  3. b) Nunca confíes en la validación del frontend. Usuarios pueden usar herramientas de desarrollo para modificar HTML y enviar datos no válidos. La validación del servidor es obligatoria para seguridad.

  4. b) Para traducción necesitamos: dataType (para usar ChatGPT en lugar de Whisper), content (texto a traducir) y lang (idioma destino). Estas propiedades son usadas por getData() para construir el prompt.

  5. a) La principal ventaja es económica y de rendimiento. La primera traducción paga el costo de API y toma ~2 segundos. Las siguientes lecturas desde BD son casi instantáneas y gratuitas.


🚀 Lo que viene después

¡Increíble! Ahora tenemos:
✅ Traducción funcional con ChatGPT
✅ Guardado persistente en base de datos
✅ Interfaz completa para seleccionar idiomas
✅ Validación de seguridad para inputs

En la próxima lección vamos a:

  1. Implementar un loader visual durante la traducción

  2. Mejorar el manejo de errores con mensajes amigables

  3. Agregar feedback visual (spinner, mensajes de éxito/error)

  4. Optimizar la experiencia de usuario completa

✨ ¡Felicidades! Has completado la funcionalidad principal de la aplicación. Ahora los usuarios pueden subir archivos, transcribirlos a texto y traducirlos a múltiples idiomas. ¡Una aplicación completa de procesamiento de audio con IA



Comentarios

Entradas más populares de este blog

1-7. Transforma tu audio a texto

10. Haz que tu asistente hable

8. NUEVO - Solución si tu micrófono no está captando tu audio