Serverless Challenge: Building An AI Translation Feature With AWS
Author image

Serverless Challenge: Building An AI Translation Feature With AWS

?? Hello there! Welcome to The Serverless Spotlight! I'm Uriel Bitton, a solutions architect on AWS and I'm here to teach you everything I know about serverless computing and optimizing for costs on the cloud.

I have built and architected over 15 serverless web applications with 12 years of experience in software development and cloud technologies.


In this week's Serverless Challenge Edition, I'll be guiding you through building a content translation feature.

I'll be using Amazon Translate - a translation service that uses machine learning to provide accurate translations in many different languages.

The aim is to build a landing page where visitors can input some text content and receive as a result the translated text in the language they choose.

Like before, we'll start by defining the tech stack, identifying and understanding the plan, and finally implementing the solution.

Tech Stack

Here's the serverless stack we'll be using:

  • AWS Lambda
  • Amazon Translate
  • React JS for our frontend app

The Plan

We'll create a simple landing page UI, where we'll have a textarea for user input and under it some select options to specify the languages to be translated.

Under it we'll have our returned translated content, which we can offer for the user to copy to the clipboard.

For the business logic, we'll spin up an AWS Lambda function that will run our serverless code to handle translation requests with the help of Amazon Translate.

For our front end, we'll invoke our Lambda function with the help of Lambda function URLs which will create endpoints for our client-side to call with fetch requests using JavaScript fetch functions.

Solutions Architecture

This is what the architecture will look like:

Implementation

Lambda

Let's start with writing our Lambda code.

Head over to the Lambda and create a new function. Choose the option Author from scratch, and enter the function name as translate-text.

Choose Node JS v20 as your runtime.

Below in the permissions section, you can create a new role for Lambda to access Amazon Translate with the permissions TranslateFullAccess.

If you are unfamiliar with how to give permissions to Lambda functions, you can follow this article I wrote here.

Once you create the function, you can scroll down to the Code section on the new page you are redirected to.

We'll add the code below and explain what it does.

const { TranslateClient, TranslateTextCommand } = require("@aws-sdk/client-translate");

const translateClient = new TranslateClient({ region: "us-east-1" });

exports.handler = async (event) => {
    const { text, sourceLanguage, targetLanguage } = event.queryStringParameters;
    const params = {
        SourceLanguageCode: sourceLanguage || 'auto',
        TargetLanguageCode: targetLanguage || 'en',
        Text: text
    };

    try {
        const command = new TranslateTextCommand(params);
        const data = await translateClient.send(command);
        console.log("Translated text:", data.TranslatedText);
        return data;
    } catch (err) {
        console.error("Error", err);
    }
};

        

First we import the aws-sdk's translate client library to invoke Amazon Translate's API.

In the main function we extract the text, sourceLanguage, and targetLanguage from the queryStringParameters which we will pass in from our fetch request on the frontend later on.

We then define the Params onbject which has a SourceLanguageCode, TargetLanguageCode and Text fields and we provide the values from the queryStringParameters as well as some default values.

Finally, in the try block, we execute the TranslateTextCommand and return the results in JSON format.

In the code above, we are telling Lambda to make a request to Amazon Translate, to translate the text we provide it and return us the translated string.

We can now deploy the function by clicking on the blue Deploy button at the top of the code editor.

Creating A Lambda Function URL

We now have to create a function URL to be able to invoke (call) our function code from our client side application.

On the tabs above the code editor you can click on the Configuration tab, then on the left sidebar click on the Function URL.

Click on the Create Function URL button to get started.

On this new page, you can select the none option for authentication if you want to test it out quickly or use IAM to manage access permissions for this function call.

Below, under Additional settings, check the cors checkbox and leave the rest of the defaults as they are, then proceed to create the function URL.

Once you create the function URL, you will be redirected to the function page and you will be able to see the function URL on the bottom right corner:

We'll note the url down for use in our frontend application later.

React JS App

In my IDE - I'll be using VSCode - I'll open up a new terminal and enter the following command:

npm create vite@latest translation-app        

This will create a new React project (with Vite as the bundler) called "translation-app".

Once you hit enter you should see the following answer from npm.

From the following questions, I've chosen React as the framework and TypeScript for type checking.

Once that's done, you can cd into the directory by running the "cd " command and install the dependencies:

cd translation-app        
npm install        

Once the dependencies have been installed, launch the app by running the following command:

npm run dev        

This will start the server on localhost port 5173. You should see the link in the terminal. Navigate to it to see your live server.

Let's write some code now to build out a basic UI and execute our serverless code on Lambda.

UI Code

Let's start by deleting the default template that is provided with Vite, and write our own code.

//App.tsx
//the jsx  for the UI

<div className="translation-app">
      <h1>Welcome to the translation app</h1>
      <label>
        <h4>Enter text to translate</h4>
        <textarea
          style={{ width: 600, height: 120, padding: 10 }}
          value={text}
          onChange={(e) => setText(e.target.value)}
        />
      </label>
      <label>
        <h4>Select language</h4>
        <select
          style={{ padding: "10px 15px", background: "#fafaf" }}
          value={targetLanguage}
          onChange={(e) => setTargetLanguage(e.target.value)}
        >
          <option value="en">English</option>
          <option value="es">Spanish</option>
          <option value="fr">French</option>
          <option value="ag">German</option>
        </select>
      </label>

      <div>
        <p>Translation will appear here</p>
        <small>{loading && "Loading translations..."}</small>
        <div style={{ width: 600, height: 120, border: "2px solid #428df5" }}>
          {translatedText}
        </div>
      </div>
      <div style={{ display: "flex", gap: 10, marginTop: 10 }}>
        <button
          style={{ background: "#428df5", color: "#fff" }}
          onClick={handleTranslate}
        >
          Translate
        </button>
        <button>Clear</button>
      </div>
    </div>        

In the HTML code above, we add an input for the original text, a select to choose the target language and a button to run the translation function.

Below I define the handleTranslate function:

const handleTranslate = async () => {
    if (!text) {
      alert("Please enter text to translate");
      return;
    }
    setLoading(true);
    try {
      const response = await fetch(
        https://demo-function-url.lambda-url.us-east-1.on.aws?text=${text}&targetLanguage=${targetLanguage} //replace with your Lambda function url here
      );
      const data = await response.json();
      console.log(data);
      setTranslatedText(data.TranslatedText);
    } catch (error) {
      console.error("Error translating text", error);
    } finally {
      setLoading(false);
    }
  };        

In this function, I define a fetch function that makes a GET request to the Lambda function URL we created earlier. We then output the results on the UI to show the translated text.

All together with the useState variables, our code looks like this:

import { useState } from "react";

function App() {
  const [translatedText, setTranslatedText] = useState<string>("");
  const [targetLanguage, setTargetLanguage] = useState<string>("en");
  const [text, setText] = useState<string>("");
  const [loading, setLoading] = useState<boolean>(false);
 
  const handleTranslate = async () => {
    if (!text) {
      alert("Please enter text to translate");
      return;
    }
    setLoading(true);
    try {
      const response = await fetch(
        https://demo-function-url.lambda-url.us-east-1.on.aws?text=${text}&targetLanguage=${targetLanguage} //replace with your Lambda function url here
      );
      const data = await response.json();
      console.log(data);
      setTranslatedText(data.TranslatedText);
    } catch (error) {
      console.error("Error translating text", error);
    } finally {
      setLoading(false);
    }
  };
   
   return (
       <div className="translation-app">
      <h1>Welcome to the translation app</h1>
      <label>
        <h4>Enter text to translate</h4>
        <textarea
          style={{ width: 600, height: 120, padding: 10 }}
          value={text}
          onChange={(e) => setText(e.target.value)}
        />
      </label>
      <label>
        <h4>Select language</h4>
        <select
          style={{ padding: "10px 15px", background: "#fafaf" }}
          value={targetLanguage}
          onChange={(e) => setTargetLanguage(e.target.value)}
        >
          <option value="en">English</option>
          <option value="es">Spanish</option>
          <option value="fr">French</option>
          <option value="ag">German</option>
        </select>
      </label>

      <div>
        <p>Translation will appear here</p>
        <small>{loading && "Loading translations..."}</small>
        <div style={{ width: 600, height: 120, border: "2px solid #428df5" }}>
          {translatedText}
        </div>
      </div>
      <div style={{ display: "flex", gap: 10, marginTop: 10 }}>
        <button
          style={{ background: "#428df5", color: "#fff" }}
          onClick={handleTranslate}
        >
          Translate
        </button>
        <button>Clear</button>
      </div>
    </div>
   )    
}

export default App;        

Let's run our code by clicking the Translate button.

I inputted a text in German in the textarea field, then chose the language to translate to in English, and hit translate button.

I can then see the translated string in the UI correctly.

We now have a functioning Translation-on-demand feature, which we can integrate into an application!

Conclusion

In this blog post, we take on yet another challenge of building a content translation feature using Amazon Translate.

The process involves creating a landing page where users can input text and select the target language for translation, with the translation result displayed on the page.

The implementation utilizes an AWS Lambda function to handle translation requests via a serverless architecture, exposing endpoints with Lambda function URLs which makes a request to Amazon Translate.

With the results, we then display the translated text into the UI, effectively creating a scalable, serverless translation feature.


?? My name is Uriel Bitton and I hope you learned something of value in this edition of The Serverless Spotlight.

?? Please consider sharing it with your network to help others learn as well.

?? You can also explore my social media links here:

?? *my blog website is coming soon, so stay tuned for that.

?? I hope to see you in the next week's edition!

Uriel

要查看或添加评论,请登录

Uriel Bitton的更多文章

社区洞察

其他会员也浏览了