Original article: React Tutorial – How to Build a Text Translation PWA

En este artículo, te mostraré como crear una aplicación de traducción de texto con React.  Admitirá 17 idiomas, y también podrás hacer traducciones cruzadas.

Esto es que crearemos:

Screenshot-2021-09-12-123442-1

Así es como se verá nuestra aplicación después de que hayamos terminado de construirla. Tiene dos áreas de entrada de texto - una tendrá el código fuente, y la otra tendrá el texto resultante de la traducción. Tenemos también un campo seleccionable donde el usuario podrá elegir los idiomas que desee.

Entonces, entremos de lleno en ello.

Como construir la Interfaz de usuario

Para crear la interfaz fácilmente, usaremos una librería de Interfaces de Usuario llamado Semantic UI.

Por lo tanto, navegue al sitio web de Semantic UI en https://react.semantic-ui.com/.

A continuación, selecciona "Get Started" en el menú lateral:

Screenshot-2021-09-12-112404

Instalemos la librería usando uno de los siguientes comandos. Puedes utilizar yarn add o npm install.

$  yarn add semantic-ui-react semantic-ui-css
## O
$  npm install semantic-ui-react semantic-ui-css

Después de que finalizó la instalación, debemos importar el paquete dentro de nuestro archivo index.js de la siguiente manera:

import 'semantic-ui-css/semantic.min.css'

Ahora, ya podemos usar Semantic UI.

Como crear los componentes de la aplicación

Vamos a crear un componente llamado Traductor. Este tendrá todos los elementos que necesitamos.

Primero, necesitamos un título principal para la aplicación. Entonces, dentro del componente Traductor, crea un encabezado como este:

import React from 'react';

export default function Traductor() {
    return (
        <div>
            <div className="app-header">
                <h2 className="header">Traductor de texto</h2>
            </div>
        </div>
    )
}
Traductor.js

Ahora vamos a agregarle un poco de estilos con CSS. En el archivo App.css quitaremos los estilos que vienen por defecto, y agregaremos los nuestros:

@import url('https://fonts.googleapis.com/css2?family=Azeret+Mono&display=swap');

.app-header{
  text-align: center;
  padding: 20px;
}

.header{
  font-family: 'Azeret Mono', monospace;
  font-size: 30px;
}
App.css Styling

Acá, estamos usando una fuente llamada Azeret Mono de Google Fonts, y hemos alineado el encabezado (header) y le dimos un poco de relleno (padding).

Para lograr ver los cambios, en el archivo App.js debemos reemplazar todas las líneas de código  del return de la función App por un <div> con el componente Traductor. Quedando de esta manera:

import './App.css';
import Traductor from './componentes/Traductor';

function App() {
  return (
    <div>
      <Traductor />
    </div>
  );
}

export default App;
App.js

Al hacer npm start, así es como se verá nuestro encabezado en este punto:

Screenshot-2021-09-12-113234

También necesitamos otros cuatro elementos. En primer lugar, nuestra área de entrada de texto, segundo el menú desplegable para seleccionar el idioma, tercero es el área de texto de salida donde se reflejará nuestro texto traducido, y por último es un botón que traducirá nuestro texto.

Podemos importar los elementos Form, TextArea, Button e Icon directamente desde Semantic UI de esta manera:

import {
    Form,
    TextArea,
    Button,
    Icon
} from 'semantic-ui-react';
Traductor.js

A continuación, crearemos otro div después de  app-header llamado app-body con el siguiente código:

import React from 'react';
import {
    Form,
    TextArea,
    Button,
    Icon
} from 'semantic-ui-react';

export default function Traductor() {
    return (
        <div>
            <div className="app-header">
                <h2 className="header">Traductor de texto</h2>
            </div>

            <div className='app-body'>
                <div>
                    <Form>
                        <Form.Field
                            control={TextArea}
                            placeholder='Escribe el texto a traducir..'
                        />

                        <select className="select-idioma">
                            <option>Por favor selecciona un idioma..</option>
                        </select>

                        <Form.Field
                            control={TextArea}
                            placeholder='El resultado de la traducción..'
                        />

                        <Button
                            color="orange"
                            size="large"
                        >
                            <Icon name='traducir' />
                            Traducir</Button>
                    </Form>
                </div>
            </div>
        </div>
    )
}
Traductor.js

Y le agregaremos un poco de estilo con el siguiente CSS:

@import url('https://fonts.googleapis.com/css2?family=Azeret+Mono&display=swap');

.app-header{
  text-align: center;
  padding: 20px;
}

.header{
  font-family: 'Azeret Mono', monospace;
  font-size: 30px;
}

.app-body{
  padding: 20px;
  text-align: center;
}

.select-idioma{
  height: 40px !important;
  margin-bottom: 15px;
  outline: none !important;
}
App.css

Así es como se verá nuestra aplicación ahora. Puedes ver que tenemos las áreas de texto, seleccionar opciones y un botón para traducir.

Screenshot-2021-09-12-114039

Cómo configurar las APIs

Para habilitar la traducción, usaremos la API LibreTranslate. Por lo tanto, vaya a su sitio web para elegir tu API.

Screenshot-2021-09-12-114304

Como puedes ver en la imagen de arriba, tiene cuatro APIs.

Para empezar, necesitamos detectar nuestro idioma de entrada utilizando la API /detect que es de tipo POST.

Cómo instalar Axios

Primeramente, vamos a instalar Axios, ya que necesitaremos usarlo para realizar solicitudes a la API.

Para instalar Axios, simplemente escriba el siguiente comando:

yarn add axios

##O

npm i axios
Instalando Axios

Podemos usar yarn add axios o npm i axios, dependiendo de su administrador de paquetes que haya instalado.

Ahora, vamos a importarlo en nuestro componente Traductor.

import axios from 'axios';

También necesitamos los hooks useState y useEffect.

import React, { useState, useEffect } from 'react';

A continuación, cree un estado llamado inputText.

const [textoEntrante, setTextoEntrante] = useState('');

Y en el campo de área de texto de entrada, enlazarlo a un controlador de eventos onChange.

<Form.Field
 control={TextArea}
 placeholder='Escribe el texto a traducir..'
 onChange={(e) => setTextoEntrante(e.target.value)}
/>

Si introducimos algún texto, se almacenará en el estado textoEntrante.

Cómo llamar a la API de detección de idioma

Ahora, llamemos a la API de detección de idioma para detectar nuestro idioma de entrada.

Crea una función llamada obtenerFuenteDelIdioma() como esta:

const obtenerFuenteDelIdioma = () => {
        axios.post(`https://libretranslate.de/detect`, {
            q: textoEntrante
        })
        .then((respuesta) => {
            console.log(respuesta.data[0].language)
        })
    }

Aquí, estamos llamando a la API de detección, y estamos pasando nuestra entrada como el cuerpo del POST.

Estamos usando axios.post para enviar el texto de entrada como cuerpo, y estamos usando q como parámetro de encabezado.

Además, querremos llamar a esta función al hacer clic en el botón Traducir, así que vincule esta función al botón Traducir de esta manera:

<Button
                            color="orange"
                            size="large"
                            onClick={obtenerFuenteDelIdioma}
                        >
                            <Icon name='traducir' />
                            Traducir</Button>

Escribe algo en el primer cuadro de entrada de texto y, a continuación, pulse el botón Traducir. Verá un objeto, con la clave de idioma detectada en la consola, que necesitamos.

Screenshot-2021-09-12-115444

Ahora, necesitamos almacenar esta clave de idioma en un estado. Por lo tanto, cree un estado llamado claveIdiomaDetectada.

Luego, establezca el estado con la respuesta del llamado de la siguiente manera:

const obtenerFuenteDelIdioma = () => {
        axios.post(`https://libretranslate.de/detect`, {
            q: textoEntrante
        })
            .then((respuesta) => {
                setClaveIdiomaDetectada(respuesta.data[0].language)
            })
    }

Estamos estableciendo el índice cero a partir de los datos de respuesta, porque ahí es donde comienzan nuestros datos.

Aquí está todo el código hasta este punto:

import React, { useState, useEffect } from 'react';
import {
    Form,
    TextArea,
    Button,
    Icon
} from 'semantic-ui-react';
import axios from 'axios';

export default function Traducir() {
    const [textoEntrante, setTextoEntrante] = useState('');
    const [claveIdiomaDetectada, setClaveIdiomaDetectada] = useState('')
    const obtenerFuenteDelIdioma = () => {
        axios.post(`https://libretranslate.de/detect`, {
            q: textoEntrante
        })
            .then((respuesta) => {
                setClaveIdiomaDetectada(respuesta.data[0].language)
            })
    }

    return (
        <div>
            <div className="app-header">
                <h2 className="header">Traductor de texto</h2>
            </div>

            <div className='app-body'>
                <div>
                    <Form>
                        <Form.Field
                            control={TextArea}
                            placeholder='Escribe el texto a traducir..'
                            onChange={(e) => setTextoEntrante(e.target.value)}
                        />

                        <select className="select-idioma">
                            <option>Por favor seleccione un idioma..</option>
                        </select>

                        <Form.Field
                            control={TextArea}
                            placeholder='El resultado de su traducción..'
                        />

                        <Button
                            color="orange"
                            size="large"
                            onClick={obtenerFuenteDelIdioma}
                        >
                            <Icon name='traducir' />
                            Traducir</Button>
                    </Form>
                </div>
            </div>
        </div>
    )
}

Cómo llamar a la API de idiomas admitidos para el menú desplegable

Ahora, la segunda API obtiene los idiomas admitidos. Usaremos la lista en nuestro menú desplegable de selección.

Cree un hook useEffect para llamar a nuestra API de idiomas compatibles. useEffect es una función que se ejecutará cada vez que nuestro componente se renderice o cargue.

useEffect(() => {
        axios.get(`https://libretranslate.de/languages`)
            .then((respuesta) => {
                console.log(respuesta.data)
            })
    }, [])

Aquí estamos llamando a la API para idiomas compatibles utilizando el método axios.get. Luego estamos imprimiendo la respuesta en la consola.

Abra la consola para comprobar la lista de idiomas. Deberías ver algo como esto:

Screenshot-2021-09-12-120348

Establezcamos estos datos en un estado. Por lo tanto, cree un estado llamado listadoIdiomas. Será un arreglo vacío.

const [listadoIdiomas, setListadoIdiomas] = useState([])
useEffect(() => {
        axios.get(`https://libretranslate.de/languages`)
            .then((respuesta) => {
                setListadoIdiomas(respuesta.data)
            })
    }, [])

Luego, en el hook useEffect, debemos establecer la lista de idiomas usando setListadoIdiomas.

Necesitamos mostrar esta lista de idiomas en la opción de selección. Por lo tanto, asignemos el menú desplegable de selección utilizando el estado listadoIdiomas de la siguiente manera:

<select className="select-idioma">
                            <option>Por favor seleccione un idioma..</option>
                            {listadoIdiomas.map((idioma) => {
                                return (
                                    <option value={idioma.code}>
                                        {idioma.name}
                                    </option>
                                )
                            })}
                        </select>
Screenshot--605-

Ahora, podemos seleccionar nuestro idioma en el menú desplegable de selección.

Cómo obtener el código del idioma seleccionado

Ahora, si seleccionamos un idioma, por ejemplo, español, necesitamos obtener el código del idioma, ya que necesitamos ese código de idioma en nuestra API de traducción final.

Crea una función llamada claveIdioma() como esta:

const claveIdioma = () => {
     
}

Y en la opción de selección (<select>), debemos unir esta función usando el evento onChange:

<select className="select-idioma" onChange={claveIdioma}>
                            <option>Por favor seleccione un idioma..</option>
                            {listadoIdiomas.map((idioma) => {
                                return (
                                    <option value={idioma.code}>
                                        {idioma.name}
                                    </option>
                                )
                            })}
                        </select>

Además, necesitamos almacenar el código del idioma en un estado, así que vamos a crearlo.

Cree un estado llamado claveIdiomaSeleccionada, que tendrá nuestra clave del idioma seleccionado de la entrada de selección.

const [claveIdiomaSeleccionada, setClaveIdiomaSeleccionada] = useState('')

Esta función claveIdioma aceptará un parámetro denominado idiomaSeleccionado. Y almacenaremos estos datos en el estado claveIdiomaSeleccionada, que obtendremos de la entrada de selección.

const claveIdioma = (idiomaSeleccionado) => {
        setClaveIdiomaSeleccionada(idiomaSeleccionado.target.value)
}

Ahora, si nos fijamos en la documentación de LibreTranslate, necesitamos tres entradas de datos:

  1. El texto a traducir.
  2. El código del idioma fuente. (source)
  3. El código del idioma de destino. (target)
Screenshot-2021-09-12-120659

No necesitamos la clave de la API (api_key) porque este servicio es gratuito.

Tenemos los tres inputs que necesitamos enviar en el cuerpo contenido en estos estados a continuación:

const [textoEntrante, setTextoEntrante] = useState('');
const [claveIdiomaDetectada, setClaveIdiomaDetectada] = useState('');
const [claveIdiomaSeleccionada, setClaveIdiomaSeleccionada] = useState('')

Ahora, llamemos a nuestra API final, que es /translate.

Cómo llamar a la API de traducción para traducir nuestro texto

Cree un estado final denominado textoResultante. Este estado contendrá nuestro texto traducido de salida.

const [textoResultante, setTextoResultante] = useState('');

Cree una función que llame a la API de traducción:

const traducirTexto = () => {
       obtenerFuenteDelIdioma();

        let data = {
            q : textoEntrante,
            source: claveIdiomaDetectada,
            target: claveIdiomaSeleccionada
        }
        axios.post(`https://libretranslate.de/translate`, data)
        .then((respuesta) => {
            setTextoResultante(respuesta.data.translatedText)
        })
    }

Como puedes ver, estamos configurando textoEntrante en el estado textoResultante, y estamos llamando a la función obtenerFuenteDelIdioma dentro de la función traducirTexto. Por lo tanto, cada vez que se ejecute esta función, obtenerFuenteDelIdioma los activará automáticamente para obtener la fuente del idioma.

En otras palabras, al hacer clic (evento onClick) en el botón Traducir, la función traducirTexto() establecerá la fuente del idioma a través de obtenerFuenteDelIdioma(), y luego llamará a la API de traducción.

Entonces, en el botón Traducir, conecta esta función:

<Button
                            color="orange"
                            size="large"
                            onClick={traducirTexto}
                        >
                            <Icon name='traducir' />
                            Traducir</Button>

A continuación, vamos a crear un objeto llamado data. Enviaremos dentro de él toda la información que obtuvimos previamente, como textoEntrante, claveIdiomaDetectada, y la clave de claveIdiomaSeleccionada como q, source, y target respectivamente.

let data = {
            q : textoEntrante,
            source: claveIdiomaDetectada,
            target: claveIdiomaSeleccionada
        }

Luego, llamamos a la API de traducción usando axios.post y enviamos el objeto data como un parámetro de cuerpo de la petición.

let data = {
            q : textoEntrante,
            source: claveIdiomaDetectada,
            target: claveIdiomaSeleccionada
        }
        axios.post(`https://libretranslate.de/translate`, data)

Por último, establecemos la respuesta devuelta en el estado textoResultante.

.then((respuesta) => {
            setTextoResultante(respuesta.data.translatedText)
        })

Por lo tanto, ahora escriba algo en el cuadro de entrada, seleccione el idioma y haga clic en Traducir. Obtendrá su texto traducido.

Aquí está todo el código hasta este punto, para su referencia:

import React, { useState, useEffect } from 'react';
import {
    Form,
    TextArea,
    Button,
    Icon
} from 'semantic-ui-react';
import axios from 'axios';

export default function Traductor() {
    const [textoEntrante, setTextoEntrante] = useState('');
    const [claveIdiomaDetectada, setClaveIdiomaDetectada] = useState('');
    const [claveIdiomaSeleccionada, setClaveIdiomaSeleccionada] = useState('')
    const [listadoIdiomas, setListadoIdiomas] = useState([])
    const [textoResultante, setTextoResultante] = useState('');
    const obtenerFuenteDelIdioma = () => {
        axios.post(`https://libretranslate.de/detect`, {
            q: textoEntrante
        })
            .then((respuesta) => {
                setClaveIdiomaDetectada(respuesta.data[0].language)
            })
    }
    useEffect(() => {
        axios.get(`https://libretranslate.de/languages`)
            .then((respuesta) => {
                setListadoIdiomas(respuesta.data)
            })
    }, [])

    const claveIdioma = (idiomaSeleccionado) => {
        setClaveIdioma(idiomaSeleccionado.target.value)
    }

    const traducirTexto = () => {
        obtenerFuenteDelIdioma();

        let data = {
            q : textoEntrante,
            source: claveIdiomaDetectada,
            target: claveIdiomaSeleccionada
        }
        axios.post(`https://libretranslate.de/translate`, data)
        .then((respuesta) => {
            setTextoResultante(respuesta.data.translatedText)
        })
    }

    return (
        <div>
            <div className="app-header">
                <h2 className="header">Traductor de texto</h2>
            </div>

            <div className='app-body'>
                <div>
                    <Form>
                        <Form.Field
                            control={TextArea}
                            placeholder='Escribe el texto a traducir..'
                            onChange={(e) => setTextoEntrante(e.target.value)}
                        />

                        <select className="select-idioma" onChange={claveIdioma}>
                            <option>Por favor seleccione un idioma..</option>
                            {listadoIdiomas.map((idioma) => {
                                return (
                                    <option value={idioma.code}>
                                        {idioma.name}
                                    </option>
                                )
                            })}
                        </select>

                        <Form.Field
                            control={TextArea}
                            placeholder='El resultado de su traducción..'
                            value={textoResultante}
                        />

                        <Button
                            color="orange"
                            size="large"
                            onClick={traducirTexto}
                        >
                            <Icon name='traducir' />
                            Traducir</Button>
                    </Form>
                </div>
            </div>
        </div>
    )
}
Traductor.js

Ahora, el último paso. En el hook useEffect, llama a la función obtenerFuenteDelIdioma(), y establece textoEntrante como el arreglo de dependencias del useEffect. Esto significa que siempre y cuando nuestro textoEntrada cambie su texto, o este estado se actualice, se ejecutará la función useEffect, y llamará a la función obtenerFuenteDelIdioma() cada vez que se ejecute.

useEffect(() => {
       axios.get(`https://libretranslate.de/languages`)
       .then((respuesta) => {
        setListadoIdiomas(respuesta.data)
       })

       obtenerFuenteDelIdioma()
    }, [textoEntrante])

Ahora, veamos nuestra salida:

Screenshot-2021-09-12-123442

Escriba algo de texto como entrada y seleccione el idioma. Presione Traducir y verá sus datos traducidos en el espacio de salida.

Conclusión

Ahora ya sabes cómo crear un traductor de texto con React. Puedes crear tu propia interfaz de usuario si lo deseas.

Así que adelante, construye y experimenta un poco con él. Hay toneladas de cosas que puedes hacer.

Buen aprendizaje!