Cách làm ứng dụng dịch ngôn ngữ bằng React

Trình biên dịch là một công cụ phần mềm ứng dụng web, giúp bạn dịch văn bản sang nhiều ngôn ngữ khác nhau. Dưới đây là cách tạo ứng dụng dịch ngoại ngữ bằng React.

Ứng dụng biên dịch cho phép người dùng nhập nội dung và chọn định dạng ngôn ngữ nhập và xuất từ danh sách có sẵn và dùng Microsoft Translator API để triển khai nhiệm vụ. Người dùng cũng có thể đảo ngược ngôn ngữ nguồn và mục tiêu, đồng thời xóa text nhập vào. Thành phần này cung cấp giao diện thân thiện người dùng để biên dịch ngôn ngữ, với các bản cập nhật và xử lý lỗi linh động. Dưới đây là hướng dẫn chi tiết từng bước:

Điều kiện cần có:

  • ReactJS
  • CSS
  • Thành phần chức năng trong React
  • Rapid API

Dự án này về cơ bản triển khai các thành phần chức năng và quản lý trạng thái. Ứng dụng này dùng Microsoft Translator API để biên dịch, tạo truy vấn POST với văn bản nhập vào và tùy chọn ngôn ngữ. Sau khi nhận phản hồi, ứng dụng sẽ cập nhật nội dung được biên dịch vào trong thành phần trạng thái, các vòng quay tải được hiện trong cuộc gọi API. Giao diện của thành phần cho phép người dùng chọn ngôn ngữ, nội dung nhập vào, biên dịch và đảo ngược lựa chọn ngôn ngữ. Nếu gọi API bị từ chối, thì nó hiện thông báo lỗi trong quá trình biên dịch.

Các bước tạo ứng dụng biên dịch trong app React

Bước 1: Thiết lập dự án React bằng lệnh này:

npx create-react-app <<name of project>>

Bước 2: Điều hướng tới thư mục dự án bằng:

cd <<Name_of_project>>

Bước 3: Tạo thư mục “components” và thêm 3 file mới bên trong nó và đặt tên chúng là Translator.js, language.json và Translator.css.

Cấu trúc dự án:

Cấu trúc dự án

Các phần phụ thuộc được cập nhật trong package.json sẽ trông như thế này:

"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"
}                             

Ví dụ: Viết code sau trong file tương ứng:

  • App.js: File này nhập các thành phần Translator và xuất nó.
  • Translator.js: File này chứa logic cho phép người dùng chèn văn bản nhập vào và chọn định dạng ngôn ngữ đầu vào và đầu ra, đồng thời dùng API để lấy văn bản đã được biên dịch.
  • language.json: File này chứa một danh sách ngôn ngữ và các tên tương ứng của chúng.
  • Translator.css: File này chứa thiết kế của các thành phần Translator.

Code JavaScript:

// App.js
  
import './App.css';
import Translator from './components/Translator';
  
function App() {
  return (
    <div className="App">
      <Translator />
    </div>
  );
}
  
export default App;

Code JavaScript:

// language.json
  
{
    "af": { "name": "Afrikaans" },
    "ak": { "name": "Akan" },
    "sq": { "name": "Albanian" },
    "am": { "name": "Amharic" },
    "ar": { "name": "Arabic" },
    "hy": { "name": "Armenian" },
    "as": { "name": "Assamese" },
    "ay": { "name": "Aymara" },
    "az": { "name": "Azerbaijani" },
    "bm": { "name": "Bambara" },
    "eu": { "name": "Basque" },
    "be": { "name": "Belarusian" },
    "bn": { "name": "Bengali" },
    "bs": { "name": "Bosnian" },
    "bg": { "name": "Bulgarian" },
    "my": { "name": "Burmese" },
    "ca": { "name": "Catalan; Valencian" },
    "ny": { "name": "Chichewa; Chewa; Nyanja" },
    "zh": { "name": "Chinese" },
    "co": { "name": "Corsican" },
    "hr": { "name": "Croatian" },
    "cs": { "name": "Czech" },
    "da": { "name": "Danish" },
    "dv": { "name": "Divehi; Dhivehi; Maldivian;" },
    "nl": { "name": "Dutch" },
    "en": { "name": "English" },
    "eo": { "name": "Esperanto" },
    "et": { "name": "Estonian" },
    "ee": { "name": "Ewe" },
    "fi": { "name": "Finnish" },
    "fr": { "name": "French" },
    "gl": { "name": "Galician" },
    "ka": { "name": "Georgian" },
    "de": { "name": "German" },
    "el": { "name": "Greek, Modern" },
    "gn": { "name": "Guaraní" },
    "gu": { "name": "Gujarati" },
    "ht": { "name": "Haitian; Haitian Creole" },
    "ha": { "name": "Hausa" },
    "he": { "name": "Hebrew (modern)" },
    "hi": { "name": "Hindi" },
    "hu": { "name": "Hungarian" },
    "id": { "name": "Indonesian" },
    "ga": { "name": "Irish" },
    "ig": { "name": "Igbo" },
    "is": { "name": "Icelandic" },
    "it": { "name": "Italian" },
    "ja": { "name": "Japanese" },
    "jv": { "name": "Javanese" },
    "kn": { "name": "Kannada" },
    "kk": { "name": "Kazakh" },
    "km": { "name": "Khmer" },
    "rw": { "name": "Kinyarwanda" },
    "ky": { "name": "Kirghiz, Kyrgyz" },
    "ko": { "name": "Korean" },
    "ku": { "name": "Kurdish" },
    "la": { "name": "Latin" },
    "lb": { "name": "Luxembourgish, Letzeburgesch" },
    "lg": { "name": "Luganda" },
    "ln": { "name": "Lingala" },
    "lo": { "name": "Lao" },
    "lt": { "name": "Lithuanian" },
    "lv": { "name": "Latvian" },
    "mk": { "name": "Macedonian" },
    "mg": { "name": "Malagasy" },
    "ms": { "name": "Malay" },
    "ml": { "name": "Malayalam" },
    "mt": { "name": "Maltese" },
    "mi": { "name": "Māori" },
    "mr": { "name": "Marathi (Marāṭhī)" },
    "mn": { "name": "Mongolian" },
    "ne": { "name": "Nepali" },
    "no": { "name": "Norwegian" },
    "om": { "name": "Oromo" },
    "or": { "name": "Oriya" },
    "pa": { "name": "Panjabi, Punjabi" },
    "fa": { "name": "Persian" },
    "pl": { "name": "Polish" },
    "ps": { "name": "Pashto, Pushto" },
    "pt": { "name": "Portuguese" },
    "qu": { "name": "Quechua" },
    "ro": { "name": "Romanian, Moldavian, Moldovan" },
    "ru": { "name": "Russian" },
    "sa": { "name": "Sanskrit (Saṁskṛta)" },
    "sd": { "name": "Sindhi" },
    "sm": { "name": "Samoan" },
    "sr": { "name": "Serbian" },
    "gd": { "name": "Scottish Gaelic; Gaelic" },
    "sn": { "name": "Shona" },
    "si": { "name": "Sinhala, Sinhalese" },
    "sk": { "name": "Slovak" },
    "sl": { "name": "Slovene" },
    "so": { "name": "Somali" },
    "st": { "name": "Southern Sotho" },
    "es": { "name": "Spanish; Castilian" },
    "su": { "name": "Sundanese" },
    "sw": { "name": "Swahili" },
    "sv": { "name": "Swedish" },
    "ta": { "name": "Tamil" },
    "te": { "name": "Telugu" },
    "tg": { "name": "Tajik" },
    "th": { "name": "Thai" },
    "ti": { "name": "Tigrinya" },
    "tk": { "name": "Turkmen" },
    "tl": { "name": "Tagalog" },
    "tr": { "name": "Turkish" },
    "ts": { "name": "Tsonga" },
    "tt": { "name": "Tatar" },
    "ug": { "name": "Uighur, Uyghur" },
    "uk": { "name": "Ukrainian" },
    "ur": { "name": "Urdu" },
    "uz": { "name": "Uzbek" },
    "vi": { "name": "Vietnamese" },
    "cy": { "name": "Welsh" },
    "fy": { "name": "Western Frisian" },
    "xh": { "name": "Xhosa" },
    "yi": { "name": "Yiddish" },
    "yo": { "name": "Yoruba" }
}

Code JavaScript:

// Translator.js
import React, { useState } from 'react'
import './Translator.css'
import languageList from './language.json';
  
export default function Translator() {
    const [inputFormat, setInputFormat] = useState('en');
    const [outputFormat, setOutputFormat] = useState('hi');
    const [translatedText, setTranslatedText] = useState('Translation');
    const [inputText, setInputText] = useState('');
  
    const handleReverseLanguage = () => {
        const value = inputFormat;
        setInputFormat(outputFormat);
        setOutputFormat(value);
        setInputText('');
        setTranslatedText('Translation');
    }
  
    const handleRemoveInputText = () => {
        setInputText('');
        setTranslatedText('Translation');
    }
  
    const handleTranslate = async () => {
        if (!inputText || !inputFormat || !outputFormat) return;
        document.querySelector('.fa.fa-spinner.fa-spin').style.display = "block";
        document.querySelector('.translate').style.display = 'none';
  
        const url = 
`https://microsoft-translator-text.p.rapidapi.com/translate?to%5B0%5D=${outputFormat}&api-version=3.0&profanityAction=NoAction&textType=plain`;
        const options = {
            method: 'POST',
            headers: {
                'content-type': 'application/json',
                'X-RapidAPI-Key': {"YOUR_API_KEY"},
                'X-RapidAPI-Host': 'microsoft-translator-text.p.rapidapi.com'
            },
            body: JSON.stringify([
                {
                    Text: inputText
                }
            ])
        };
        try {
            const response = await fetch(url, options);
            const result = await response.text();
            const responseObject = JSON.parse(result);
            const translation = responseObject[0].translations[0].text;
            setTranslatedText(translation);
        } catch (error) {
            console.log(error);
            alert("Please Try Again! Some Error Occurred at your side");
        }
        document.querySelector('.fa.fa-spinner.fa-spin').style.display = "none";
        document.querySelector('.translate').style.display = 'block';
    }
    return (
        <div className="container">
            <div className="row1">
                <select value={inputFormat} 
                        onChange={(e) => setInputFormat(e.target.value)}>
                    {Object.keys(languageList).map((key, index) => {
                        const language = languageList[key];
                        return (
                            <option key={index} value={key}>{language.name}</option>
                        );
                    })}
                </select>
                <svg className='reversesvg' 
                     onClick={handleReverseLanguage} 
                     focusable="false" 
                     xmlns="http://www.w3.org/2000/svg" 
                     viewBox="0 0 24 24">
                <path d=
"M6.99 11L3 15l3.99 4v-3H14v-2H6.99v-3zM21 9l-3.99-4v3H10v2h7.01v3L21 9z">
                </path>
                </svg>
                <select value={outputFormat} onChange={(e) => {
                    setOutputFormat(e.target.value);
                    setTranslatedText('Translation');
                }}>
                    {Object.keys(languageList).map((key, index) => {
                        const language = languageList[key];
                        return (
                            <option key={index + 118} value={key}>{language.name}</option>
                        );
                    })}
                </select>
            </div>
            <div className="row2">
                <div className="inputText">
                    <svg className='removeinput' 
                         style={{ display: (inputText.length) ? "block" : "none" }} 
                         onClick={handleRemoveInputText} 
                         focusable="false" 
                         xmlns="http://www.w3.org/2000/svg" 
                         viewBox="0 0 24 24">
                         <path d=
"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z">
                        </path>
                    </svg>
                    <textarea type="text" 
                              value={inputText} 
                              placeholder='Enter Text' 
                              onChange={(e) => setInputText(e.target.value)} />
                </div>
                <div className="outputText">{translatedText}</div>
            </div>
            <div className="row3">
                <button className='btn' 
                        onClick={handleTranslate}>
                        <i className="fa fa-spinner fa-spin"></i>
                        <span className='translate'>Translate</span>
                </button>
            </div>
        </div>
    )
}

Code CSS:

/* Translator.css */

body {
padding: 0;
margin: 0;
box-sizing: border-box;
}

.container {
display: flex;
align-items: center;
min-height: 100vh;
flex-direction: column;
box-sizing: border-box;
padding-top: 50px;
}

.row1 {
width: calc(100% - 10px);
max-width: 650px;
}

select {
width: calc(50% - 20px);
line-height: 40px;
font-size: 14px;
border: 1px solid #dadce0;
border-radius: 8px;
height: 40px;
padding-left: 18px;
position: relative;
color: #1a0dab;
cursor: pointer;
outline: none;
user-select: none;
}

.reversesvg {
width: 20px;
color: rgba(0, 0, 0, 0.26);
line-height: 40px;
opacity: 0.5;
vertical-align: middle;
cursor: pointer;
margin: 0px 10px;
}

option {
color: #202124;
}

.row2 {
display: flex;
justify-content: space-between;
align-items: center;
width: calc(100% - 10px);
max-width: 650px;
margin-top: 10px;
position: relative;
}

.inputText {
width: calc(50% - 20px);
position: relative;
}

.outputText {
width: calc(50% - 20px);
height: 169px;
font-family: monospace;
box-sizing: border-box;
padding: 15px;
font-size: 20px;
border-radius: 8px;
overflow: auto;
background: #edeff0;
text-align: left;
}

.row2 textarea {
width: 100%;
height: 150px;
resize: none;
box-sizing: border-box;
padding: 0px 35px 10px 10px;
margin-top: 15px;
border: none;
font-size: 20px;
overflow: auto;
color: #202124;
outline: none;
}

.removeinput {
cursor: pointer;
width: 20px;
position: absolute;
opacity: 0.5;
top: 5px;
right: 3px;
}

.row3 {
width: calc(100% - 10px);
max-width: 650px;
margin-top: 15px;
margin-bottom: 20px;
}

.btn {
width: 100%;
position: relative;
padding: 10px;
outline: none;
background-color: #6d9eed;
color: #fff;
border: 2px solid #6d9eed;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
letter-spacing: 0.5px;
}

.btn i {
display: none;
}

@media screen and (max-width: 500px) {
.row2 {
	flex-direction: column;
}
.inputText {
	width: 100%;
}
.outputText {
	width: 100%;
	margin-top: 20px;
}
}

::-webkit-scrollbar {
width: 4px;
}
::-webkit-scrollbar-track {
display: none;
}
::-webkit-scrollbar-thumb {
background: rgb(173, 172, 172);
border-radius: 4px;
}

Các bước chạy ứng dụng biên dịch

Bước 1: Nhập lệnh sau vào terminal.

npm start

Bước 2: Mở trình duyệt web và nhập URL sau:

http://localhost:3000/

Kết quả:

App biên dịch

Chúc các bạn thành công!

Thứ Tư, 04/10/2023 17:01
51 👨 187
0 Bình luận
Sắp xếp theo