Open In App

Text Translation Tool using MERN Stack

Last Updated : 07 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we’ll walk through the step-by-step process of creating a text translation application with MongoDB, React, ExpressJS, and NodeJS. This application will provide users with a user-friendly interface for translating text between different languages.

Output Preview: Let us have a look at how the final output will look like.

Screenshot-2024-03-06-112357

Output

Prerequisites:

Approach to Create Text Translation Tool using MERN Stack:

Backend:

  • Set up a new NodeJS project with npm or yarn and initialize a Git repository.
  • Create a folder called “server” within the project directory.
  • Within the “server” folder, create the following file:
    • server.js: This file contains the server-side code using Express to create a RESTful API for translation. It listens on port 5000 and handles requests for translations.
  • Inside the “server” folder, run the command npm install express cors to install the dependencies.

Frontend:

  • Set up a new React project with create-react-app. Initialize a Git repository. Define the project structure.
  • Create a folder called “client” within the project directory. The “client” folder will contain the front-end code.
  • Within the “client” folder, create the following files and directories:
    • src/index.js: This file contains the client-side code to render the Translate component to the root HTML element.
    • src/styles/App.css: This file contains the CSS styles for the Translate component.
    • src/components/App.js: This file contains the main component that renders the Translate component.
    • src/components/data.js: This file contains an array of country codes and their corresponding languages.
    • src/components/Translate.js: This file contains the Translate component that handles text input and translation requests.
  • Inside the “client” folder, run the command npm install react react-dom to install the React library.

Steps to Create the Backend Server:

Step 1: Create a directory for the project.

mkdir server
cd server

Step 2: Initialized the Express app and installing the required packages

npm init -y

Step 3: Install the necessary package in your server using the following command.

npm install express cors

Project Structure(Backend):

Screenshot-2024-03-04-051503

Backend Folder Structure

The updated dependencies in package.json file of backend will look like:

"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.3"
},

Example: Create the required files and write the following code.

Javascript




// server.js
 
import express from 'express';
import cors from 'cors';
const app = express();
app.use(cors());
 
app.get('/', async (req, res) => {
    try {
        const { text, source, target } = req.query;
        const url = `https://api.mymemory.translated.net/get?q=${text}&langpair=${source}|${target}`;
        const response = await fetch(url);
        const json = await response.json();
        const matches = await json.matches;
        const translatedText = matches[matches.length - 1].translation || 'No translation found';
        res.send(translatedText);
    } catch (error) {
        console.log(error);
        res.send('Something went wrong!');
    }
});
 
app.listen(5000, () => {
    console.log('Server is running on port 5000');
});


Start your server using the following command.

node server.js

Steps to Setup Frontend with React:

Step 1: Create React App

npx create-react-app client

Step 2: Switch to the project directory

cd client

Step 3: Installing the required packages:

npm install react react-dom

Project Structure(Frontend):

Screenshot-2024-03-04-051359

Fronend Folder Structure

The updated Dependencies in package.json file of frontend will look like:

"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},

Example: Create the required files and write the following code.

CSS




/* App.css */
 
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: sans-serif;
}
 
body {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 10px;
    min-height: 100vh;
    background: #222222;
}
 
.container {
    min-width: 700px;
    width: 100%;
    padding: 30px;
    background: #fff;
    border-radius: 7px;
    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.01);
}
 
.wrapper {
    border-radius: 5px;
    border: 1px solid #ccc;
}
 
.wrapper .text-input {
    display: flex;
    border-bottom: 1px solid #ccc;
}
 
.text-input .to-text {
    border-radius: 0px;
    border-left: 1px solid #ccc;
}
 
.text-input textarea {
    height: 250px;
    width: 100%;
    border: none;
    outline: none;
    resize: none;
    background: none;
    font-size: 18px;
    padding: 10px 15px;
    border-radius: 5px;
}
 
.text-input textarea::placeholder {
    color: #b7b6b6;
}
 
.controls,
li,
.icons,
.icons i {
    display: flex;
    align-items: center;
    justify-content: space-between;
}
 
.controls {
    list-style: none;
    padding: 12px 15px;
}
 
.controls .row .icons {
    width: 38%;
}
 
.controls .row .icons i {
    width: 50px;
    color: #adadad;
    font-size: 14px;
    cursor: pointer;
    transition: transform 0.2s ease;
    justify-content: center;
}
 
.controls .row.from .icons {
    padding-right: 15px;
    border-right: 1px solid #ccc;
}
 
.controls .row.to .icons {
    padding-left: 15px;
    border-left: 1px solid #ccc;
}
 
.controls .row select {
    color: #333;
    border: none;
    outline: none;
    font-size: 18px;
    background: none;
    padding-left: 5px;
}
 
.text-input textarea::-webkit-scrollbar {
    width: 4px;
}
 
.controls .row select::-webkit-scrollbar {
    width: 8px;
}
 
.text-input textarea::-webkit-scrollbar-track,
.controls .row select::-webkit-scrollbar-track {
    background: #fff;
}
 
.text-input textarea::-webkit-scrollbar-thumb {
    background: #ddd;
    border-radius: 8px;
}
 
.controls .row select::-webkit-scrollbar-thumb {
    background: #999;
    border-radius: 8px;
    border-right: 2px solid #ffffff;
}
 
.controls .exchange {
    color: #adadad;
    cursor: pointer;
    font-size: 16px;
    transition: transform 0.2s ease;
}
 
.controls i:active {
    transform: scale(0.9);
}
 
.container button {
    width: 100%;
    padding: 14px;
    outline: none;
    border: none;
    color: #fff;
    cursor: pointer;
    margin-top: 20px;
    font-size: 17px;
    border-radius: 5px;
    background: #2f8d46;
}
 
.above-container {
    display: flex;
    align-items: center;
    justify-content: center;
}
 
.above-container-content {
    font-size: xx-large;
    font-weight: 100;
    display: inline;
    margin-right: 1%;
    margin-bottom: 1%;
    color: #ffffff;
}
 
.above-container img {
    width: 40px;
}


Javascript




// index.js
 
import React from 'react';
import ReactDOM from 'react-dom/client';
import './styles/index.css';
import App from './components/App';
 
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>
);


Javascript




// App.js
 
import "../styles/App.css";
import Translate from "./Translate.js";
function App() {
    return (
        <>
            <Translate />
        </>
    );
}
 
export default App;


Javascript




// data.js
 
const countries = {
    "am-ET": "Amharic",
    "ar-SA": "Arabic",
    "be-BY": "Bielarus",
    "bem-ZM": "Bemba",
    "bi-VU": "Bislama",
    "bjs-BB": "Bajan",
    "bn-IN": "Bengali",
    "bo-CN": "Tibetan",
    "br-FR": "Breton",
    "bs-BA": "Bosnian",
    "ca-ES": "Catalan",
    "cop-EG": "Coptic",
    "cs-CZ": "Czech",
    "cy-GB": "Welsh",
    "da-DK": "Danish",
    "dz-BT": "Dzongkha",
    "de-DE": "German",
    "dv-MV": "Maldivian",
    "el-GR": "Greek",
    "en-GB": "English",
    "es-ES": "Spanish",
    "et-EE": "Estonian",
    "eu-ES": "Basque",
    "fa-IR": "Persian",
    "fi-FI": "Finnish",
    "fn-FNG": "Fanagalo",
    "fo-FO": "Faroese",
    "fr-FR": "French",
    "gl-ES": "Galician",
    "gu-IN": "Gujarati",
    "ha-NE": "Hausa",
    "he-IL": "Hebrew",
    "hi-IN": "Hindi",
    "hr-HR": "Croatian",
    "hu-HU": "Hungarian",
    "id-ID": "Indonesian",
    "is-IS": "Icelandic",
    "it-IT": "Italian",
    "ja-JP": "Japanese",
    "kk-KZ": "Kazakh",
    "km-KM": "Khmer",
    "kn-IN": "Kannada",
    "ko-KR": "Korean",
    "ku-TR": "Kurdish",
    "ky-KG": "Kyrgyz",
    "la-VA": "Latin",
    "lo-LA": "Lao",
    "lv-LV": "Latvian",
    "men-SL": "Mende",
    "mg-MG": "Malagasy",
    "mi-NZ": "Maori",
    "ms-MY": "Malay",
    "mt-MT": "Maltese",
    "my-MM": "Burmese",
    "ne-NP": "Nepali",
    "niu-NU": "Niuean",
    "nl-NL": "Dutch",
    "no-NO": "Norwegian",
    "ny-MW": "Nyanja",
    "ur-PK": "Pakistani",
    "pau-PW": "Palauan",
    "pa-IN": "Panjabi",
    "ps-PK": "Pashto",
    "pis-SB": "Pijin",
    "pl-PL": "Polish",
    "pt-PT": "Portuguese",
    "rn-BI": "Kirundi",
    "ro-RO": "Romanian",
    "ru-RU": "Russian",
    "sg-CF": "Sango",
    "si-LK": "Sinhala",
    "sk-SK": "Slovak",
    "sm-WS": "Samoan",
    "sn-ZW": "Shona",
    "so-SO": "Somali",
    "sq-AL": "Albanian",
    "sr-RS": "Serbian",
    "sv-SE": "Swedish",
    "sw-SZ": "Swahili",
    "ta-LK": "Tamil",
    "te-IN": "Telugu",
    "tet-TL": "Tetum",
    "tg-TJ": "Tajik",
    "th-TH": "Thai",
    "ti-TI": "Tigrinya",
    "tk-TM": "Turkmen",
    "tl-PH": "Tagalog",
    "tn-BW": "Tswana",
    "to-TO": "Tongan",
    "tr-TR": "Turkish",
    "uk-UA": "Ukrainian",
    "uz-UZ": "Uzbek",
    "vi-VN": "Vietnamese",
    "wo-SN": "Wolof",
    "xh-ZA": "Xhosa",
    "yi-YD": "Yiddish",
    "zu-ZA": "Zulu",
};
 
export default countries;


Javascript




// Translate.js
 
import React, { useEffect } from "react";
import countries from "./data.js";
 
 
const Translate = () => {
    useEffect(() => {
        const fromText = document.querySelector(".from-text");
        const toText = document.querySelector(".to-text");
        const exchageIcon = document.querySelector(".exchange");
        const selectTag = document.querySelectorAll("select");
        const icons = document.querySelectorAll(".row i");
        const translateBtn = document.querySelector("button");
        selectTag.forEach((tag, id) => {
            for (let country_code in countries) {
                let selected =
                    id === 0
                        ? country_code === "en-GB"
                            ? "selected"
                            : ""
                        : country_code === "hi-IN"
                            ? "selected"
                            : "";
                let option = `<option ${selected} value="${country_code}">${countries[country_code]}</option>`;
                tag.insertAdjacentHTML("beforeend", option);
            }
        });
 
        exchageIcon.addEventListener("click", () => {
            console.log("helo");
            let tempText = fromText.value;
            let tempLang = selectTag[0].value;
            console.log(tempText);
            console.log(tempLang);
            fromText.value = toText.value;
            toText.value = tempText;
            selectTag[0].value = selectTag[1].value;
            selectTag[1].value = tempLang;
        });
 
        fromText.addEventListener("keyup", () => {
            if (!fromText.value) {
                toText.value = "";
            }
        });
 
        translateBtn.addEventListener("click", async () => {
            let text = fromText.value.trim();
            let translateFrom = selectTag[0].value;
            let translateTo = selectTag[1].value;
            if (!text) return;
            toText.setAttribute("placeholder", "Translating...");
            try {
                const response = await
                    fetch(`http://localhost:5000/?text=${text}&source=${translateFrom}&target=${translateTo}`);
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                const translatedText = await response.text();
                toText.value = translatedText;
                toText.setAttribute("placeholder", "Translation");
            } catch (error) {
                console.error("Fetch error:", error);
                toText.setAttribute("placeholder", "Error in translation");
            }
        });
 
 
        icons.forEach((icon) => {
            icon.addEventListener("click", ({ target }) => {
                if (!fromText.value || !toText.value) return;
                if (target.classList.contains("fa-copy")) {
                    if (target.id === "from") {
                        navigator.clipboard.writeText(fromText.value);
                    } else {
                        navigator.clipboard.writeText(toText.value);
                    }
                } else {
                    let utterance;
                    if (target.id === "from") {
                        utterance = new SpeechSynthesisUtterance(fromText.value);
                        utterance.lang = selectTag[0].value;
                    } else {
                        utterance = new SpeechSynthesisUtterance(toText.value);
                        utterance.lang = selectTag[1].value;
                    }
                    speechSynthesis.speak(utterance);
                }
            });
        });
    }, []);
    return (
        <>
            <div className="above-container ">
                <img
                    className="above-container-content"
                    alt="GeeksforGeeks Logo"
                />
                <h1 className="above-container-content">Translate</h1>
            </div>
 
            <div className="container">
                <div className="wrapper">
                    <div className="text-input">
                        <textarea
                            spellCheck="false"
                            className="from-text"
                            placeholder="Enter text"
                        ></textarea>
                        <textarea
                            spellCheck="false"
                            readOnly
                            disabled
                            className="to-text"
                            placeholder="Translation"
                        ></textarea>
                    </div>
                    <ul className="controls">
                        <li className="row from">
                            <div className="icons">
                                <i id="from" className="fas fa-volume-up"></i>
                                <i id="from" className="fas fa-copy"></i>
                            </div>
                            <select></select>
                        </li>
                        <li className="exchange">
                            <i className="fas fa-exchange-alt"></i>
                        </li>
                        <li className="row to">
                            <select></select>
                            <div className="icons">
                                <i id="to" className="fas fa-volume-up"></i>
                                <i id="to" className="fas fa-copy"></i>
                            </div>
                        </li>
                    </ul>
                </div>
                <button>Translate Text</button>
            </div>
        </>
    );
};
 
export default Translate;


Start your application using the following command.

npm start

Output:

gfg67

Output



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads