Building a Translation Web App with JavaScript: A Step-by-Step Guide

Building a Translation Web App with JavaScript: A Step-by-Step Guide

Language barriers should never hinder communication and collaboration in our increasingly interconnected world. Thanks to technology, we can bridge these gaps effortlessly. In this tutorial, we'll walk through the process of building a Translation Web App from scratch using JavaScript, HTML, and CSS. This app will empower users to translate text between different languages with ease.

Introduction

Language translation apps are essential tools for breaking down linguistic barriers, whether for travel, business, or personal use. Our Translation Web App aims to provide a seamless and intuitive platform for users to translate text between various languages. With features like document upload, character count tracking, and dark mode support, our app offers a comprehensive solution for language translation needs.

Technologies Used

  • HTML: Provides the structure and layout of the web page.

  • CSS: Styles the elements and enhances the visual appeal of the app.

  • JavaScript: Powers the functionality of the app, including text translation, document upload, and user interaction.

Step 1: Setting Up the HTML Structure

<body>

    <div class="container">
     <div class="card input-wrapper">
        <div class="from">
            <span class="heading"> From : </span>
            <div class="dropdown-contanier" id="input-language">
                <div class="dropdown-toggle">
                    <ion-icon name="globe-outline"></ion-icon>

                   <span class="selected" data-value="auto">Auto Detect</span>

                   <ion-icon name="chevron-down-outline"></ion-icon>
                </div>
                <ul class="dropdown-menu">
                 </ul>
            </div>
        </div>
        <div class="text-area">
            <textarea id="input-text" cols="30" rows="10" placeholder="Enter your text"></textarea>
            <div class="chars">
                <span id="input-chars">0</span> /5000
            </div>
        </div>
        <div class="card-bottom">
            <p>Or choose your document</p>
            <label for="upload-document">
                <span id="upload-title">Choose File</span>
                <ion-icon name="cloud-upload-outline"></ion-icon>
                <input type="file" id="upload-document" hidden>
            </label>
        </div>
     </div>
     <div class="center">
        <div class="swap-position">
            <ion-icon name="swap-horizontal-outline"></ion-icon>
        </div>
     </div>
     <div class="card output-wrapper">
        <div class="to">
            <span class="heading"> To : </span>
            <div class="dropdown-contanier" id="output-language">
                <div class="dropdown-toggle">
                    <ion-icon name="globe-outline"></ion-icon>
                   <span class="selected" data-value="en">English</span>
                   <ion-icon name="chevron-down-outline"></ion-icon>
                </div>
                <ul class="dropdown-menu">
                 </ul>
            </div>
        </div>
        <div class="text-area">
            <textarea id="output-text" cols="30" rows="10" placeholder="Transalate text appere here..."></textarea>
        </div>
        <div class="card-bottom">
            <p>Or download a document</p>
            <button id="download-document">
                <span>Download</span>
                <ion-icon name="cloud-download-outline"></ion-icon>
            </button>
        </div>
     </div>
    </div>

    <div class="mode">
        <label class="toggle" for="dark-mode-btn">
          <div class="toggle-track">
            <input type="checkbox" class="toggle-checkbox" id="dark-mode-btn" />
            <span class="toggle-thumb"></span>
            <img src="image/sun.png" alt="" />
            <img src="image/moon.png" alt="" />
          </div>
        </label>
    </div>
    <div class="copyright"><p>Made With <a href="#">❤️</a> By <a href="https://jaimindev.blogspot.com">Jaimin Patel</a></p></div>
    <script src="./languages.js"></script>
    <script src="./App.js"></script>
</body>

Step 2: Styling with CSS

:root{
--primary-color: #356aff;
--bg-color:#f5f5f5;
--light-bg-color:#fff;
--text-color:#111116;
--light-text-color:#cdccd1;
--light-secoundry-color:#000000;
--primary-text-color: #fff;
}
*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: "Poppins" , sans-serif;
}
body{
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: var(--bg-color);
}
body.dark{
    --bg-color:#111116;
    --light-bg-color:#1c1b20;
    --text-color:#fff;
    --light-text-color:#58575c;
    --light-secoundry-color:#58575c;
}
.container{
     position: relative;
     width: 1200px;
     padding: 0 20px;
     display: flex;
     gap: 10px;
}
.container .card{
    flex: 1;
    padding: 30px;
    border-radius: 20px;
    background-color: var(--light-bg-color);
}
.card .from, .card .to
{
    display: flex;  
    gap: 20px;
    align-items: center;
}
.card .heading{
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--light-text-color);
    white-space: nowrap;
}
.dropdown-contanier{
    position: relative;
    margin-bottom: 10px;
    width: 100%;
}
.dropdown-contanier .dropdown-toggle{
    display: flex;
    align-items: center;
    padding: 15px 20px;
    border-radius: 30px;
    background: var(--bg-color);
    color: var(--light-secoundry-color);
    cursor: pointer;
    transition: all 0.3s;
}
.dropdown-contanier .dropdown-toggle span{
    flex: 1;
    margin-left: 20px;
}
.dropdown-contanier .dropdown-toggle ion-icon{
  font-size: 20px;
  transition: transform 0.3s ease;
}
.dropdown-contanier.active .dropdown-toggle{
    border-radius: 20px 20px 0 0;
}
.dropdown-contanier .dropdown-menu{
    position: absolute;
    top: 100%;
    left: 0;
    width: 100%;
    height: 300px;
    overflow: auto;
    display: none;
    padding: 20px;
    z-index: 1;
    list-style: none;
    flex-direction: column;
    background-color: var(--bg-color);
    transition: all 0.3s;
    border-radius: 0 0 20px 20px;
}
.dropdown-contanier.active .dropdown-menu::-webkit-scrollbar{
    display: none;
}
.dropdown-contanier.active .dropdown-menu{
    display: flex;
}
.dropdown-contanier.active .dropdown-menu li{
    padding: 10px 20px;
    border-radius: 20px;
    cursor: pointer;
    margin-bottom: 5px;
    color: var(--primary-text-color);
    border-bottom:1px solid var(--light-bg-color);
    transition: all 0.3s;
}
.dropdown-contanier.active .dropdown-menu li.active{
    color: var(--primary-text-color);
    background-color: var(--primary-color);
}
.dropdown-contanier.active .dropdown-menu li:not(.active):hover{
    background-color: var(--light-bg-color);
}
.container .text-area{
    position: relative;
}
.container textarea{
    width: 100%;
    padding:20px;
    margin:10px 0;
    background-color: transparent;
    resize: none;
    outline: none;
    border:none;
    color: var(--text-color);
    font-size:18px;
}
.container .text-area .chars{
    position: absolute;
    bottom: 0;
    right: 0;
    padding:10px;
    font-size:0.8rem;
    color: var(--light-text-color);
}
.card-bottom{
    display: flex;
    align-items: center;
    flex-direction: column;
    justify-content: center;
    padding-top:20px;
    border-top:2px solid var(--bg-color);
}
.card-bottom p{
    margin-bottom: 20px;
    color: var(--light-text-color);
}
.card-bottom label{
    height: 50px;
    display: flex;
    align-items: center;
    gap: 20px;
    padding:0 20px;
    cursor: pointer;
    border-radius: 30px;
    background-color: var(--bg-color);
    color: var(--text-color);
}
.card-bottom label:hover{
    color: var(--primary-text-color);
    background-color: var(--primary-color);
}
.card-bottom span{
    font-size:12px;
    pointer-events: none;
}
.card-bottom ion-icon{
    font-size: 20px;
}
.card-bottom button{
    height: 50px;
    display: flex;
    align-items: center;
    gap: 20px;
    padding:0 20px;
    cursor: pointer;
    border-radius: 30px;
    border: none;
    outline: none;
    color: var(--text-color);
    cursor: pointer;
    background-color: var(--bg-color);
}
.card-bottom button:hover{
    color: var(--primary-text-color);
    background-color: var(--primary-color);
}
.container .canter{
    position: relative;
}
.swap-position{
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    transform: translate(-50%);
    top: 30px;
    left: 50%;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    cursor: pointer;
    border: 1px solid var(--bg-color);
    transition: all 0.3s;
    color: var(--primary-text-color);
    background-color: var(--primary-color);
}
.swap-position ion-icon{
    font-size: 25px;
}
.swap-position:hover{
    transform: translate(-50%) scale(1.1);
}
.mode{
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 1;
}
.toggle{
    position: relative;
    cursor: pointer;
}
.toggle-track{
    width: 24px;
    height: 50px;
    border-radius: 30px;
    display: flex;
    align-items: center;
    flex-direction: column;
    padding: 3px 0;
    justify-content: space-between;
    border: 1px solid var(--light-text-color);
    background-color: var(--light-bg-color);
    translate: all 0.3s ease;
}
.toggle-checkbox{
    display: none;
}
.toggle-thumb{
    position: absolute;
    top: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background-color: var(--primary-color);
    transition:all 0.5s;
}
.toggle input:checked ~ .toggle-thumb{
    transform: translateY(25px);
}
.toggle img{
    width: 20px;
    height: 20px;
}
.copyright {
    z-index: 1;
    position: absolute;
    color: var(--light-secoundry-color);
    bottom: 1rem;
    font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
    font-weight: 600;
}
.copyright a{
    text-decoration: none;
    color: var(--light-secoundry-color);
}

@media (max-width:800px){
    .container{
        width: 100%;
        margin-top: 20px;
        flex-direction: column;
    }
    .container .card{
        width: 100%;
    }
    .container .card .from{
        margin-right: 0;
    }
    .contanier .card .to{
        margin-left: 0;
    }
    .contanier .card .from, .contanier .card .to{
        flex-direction: column;
    }
    .swap-position{
        top:50%;
        left:50%;
        transform:translate(-50%, -50%);
    }
    .swap-position:hover{
        transform: translate(-50%, -50%) scale(1.1);
    }
    .swap-position ion-icon{
        transform: rotate(90deg);
    }
}

Step 3: Implementing Functionality with JavaScript

const dropdowns = document.querySelectorAll('.dropdown-contanier'),
      inputLanguageDropdown = document.querySelector('#input-language'),
      outputLanguageDropdown = document.querySelector('#output-language');

function populateDropdown(dropdown, options){
      dropdown.querySelector('ul').innerHtml = '';
      options.forEach((option) => {
        const li = document.createElement('li');
        const title = option.name + " (" + option.native + ")";
        li.innerHTML = title;
        li.dataset.value = option.code;
        li.classList.add('option');
        dropdown.querySelector('ul').appendChild(li);
      });
}      
populateDropdown(inputLanguageDropdown, languages);
populateDropdown(outputLanguageDropdown, languages);


dropdowns.forEach((dropdown)=>{
    dropdown.addEventListener('click', (e) => {
      dropdown.classList.toggle('active');
    })

    dropdown.querySelectorAll('.option').forEach((item)=>{
      item.addEventListener('click', (e) => {
        dropdown.querySelectorAll('.option').forEach((item)=>{
          item.classList.remove('active');
        });

        item.classList.add('active');
        const selected = dropdown.querySelector('.selected');
        selected.innerHTML = item.innerHTML;
        selected.dataset.value = item.dataset.value
        translate();
      });
    })


});

document.addEventListener('click', (e) => {
  dropdowns.forEach((dropdown) => {
    if(!dropdown.contains(e.target)){
      dropdown.classList.remove('active');
    }
  })
});


//function to translate text
const inputTextElem = document.querySelector('#input-text');
const outputTextElem = document.querySelector('#output-text');
const inputLanguage = inputLanguageDropdown.querySelector('.selected');
const outputLanguage = outputLanguageDropdown.querySelector('.selected');

function translate(){
  const inputText = document.querySelector('#input-text').value;
  const inputLanguage = inputLanguageDropdown.querySelector('.selected').dataset.value;
  const outputLanguage = outputLanguageDropdown.querySelector('.selected').dataset.value;

  const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${inputLanguage}&tl=${outputLanguage}&dt=t&q=${encodeURI(
    inputText
  )}`;

  fetch(url).then((response) => response.json()).then((json) => {
    outputTextElem.value = json[0].map((item) => item[0]).join("");
  }).catch((error) =>{
     console.error(error);
  });
};

inputTextElem.addEventListener('input', (e) => {
   if(inputTextElem.value.lengh > 5000){
    inputTextElem.value = inputTextElem.value.slice(0, 5000);
   }
   translate();
})

const swapBtn = document.querySelector('.swap-position');

swapBtn.addEventListener('click', (e) => {
    const temp = inputLanguage.innerHTML;
    inputLanguage.innerHTML = outputLanguage.innerHTML;
    outputLanguage.innerHTML = temp;

    const tempValue = inputLanguage.dataset.value;
    inputLanguage.dataset.value = outputLanguage.dataset.value;
    outputLanguage.dataset.value = tempValue;

    const inputInputText = inputTextElem.value;
    inputTextElem.value = outputTextElem.value;
    outputLanguage.value = inputInputText;

    translate();
});

const uploadDocument = document.querySelector('#upload-document'),
      uploadTitle = document.querySelector('#upload-title');

      uploadDocument.addEventListener('change', (e) => {
            const file = e.target.files[0];
            if(
              file.type === "application/pdf" ||
              file.type === "application/msword" ||
              file.type === "text/plain"
            ){
               uploadTitle.innerHTML = file.name;
               const reader = new FileReader();
               reader.readAsText(file);
               reader.onload = (e) => {
                  inputTextElem.value = e.target.result;
                  translate();
               }
            }else{
              alert('Please select a valid file');
            }
      })  


const downloadDocument = document.querySelector('#download-document');

downloadDocument.addEventListener('click', () => {
    const outputText = outputTextElem.value;
    const outputLanguage = outputLanguageDropdown.querySelector('.selected').innerHTML;

    if(outputText){
      const blob = new Blob([outputText], {type: "text/plain"});
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.download = `transaled-to-${outputLanguage}.text`;
      a.href = url;
      a.click();
    }
});


const inputCharts = document.querySelector('#input-chars');

inputTextElem.addEventListener('input', (e)=> {
  inputCharts.innerHTML = e.target.value.length;
})

const darkModeCheckbox = document.getElementById("dark-mode-btn");

darkModeCheckbox.addEventListener("change", () => {
  document.body.classList.toggle("dark");
});

link: google-translate-clone

Conclusion

By following this step-by-step guide, you've learned how to build a Translation Web App using JavaScript, HTML, and CSS. This app empowers users to overcome language barriers effortlessly by providing a simple and intuitive platform for text translation. With features like document upload, character count tracking, and dark mode support, our app offers a comprehensive solution for language translation needs. Feel free to customize and expand upon this project to add more features and functionalities. Happy coding!


Feel free to adjust the content and structure of the article to better fit your preferences and target audience. Additionally, you can include screenshots or GIFs demonstrating the app's functionality to enhance the visual appeal of the article. Happy sharing!