BLOG

Amazon Translate을 사용하여 채팅 채널을 번역합니다.
작성일: 2018-04-25

매일 전 세계 수백만명의 사용자들이 이메일, 소셜 네트워크 및 채팅 플랫폼과 메시지 보드를 포함한 다른 온라인 커뮤니티를 통해 서로 의사 소통합니다. 대부분의 경우 사용자는 커뮤니티가 흥미 있고 채팅 대화에 참여하기를 원하지만 온라인 커뮤니티의 기본 언어는 사용자가 이해하는 언어와 다를 수 있습니다.

 

이러한 커뮤니티 중 하나가 게이머들을 위한 세계 최고의 소셜 비디오 서비스인 트위치입니다. 매일 수백만의 커뮤니티 멤버들이 공통의 관심사를 지켜보고, 이야기하고, 채팅하기 위해 모입니다. 모든 스트림에 채팅 기능이 내장되어 있어 수동적으로 스트림을 시청하는 대신 프로그램에 참여할 수 있습니다. 여러분은 여러분의 언어를 말하지 않아도 되는 트위치에 대한 많은 스트리머들을 발견할 것입니다. 하지만 여전히 그들의 스트림을 즐기고 채팅에 참여하고 싶어 할 것입니다.

 

이 통신을 가능하게 하기 위해, AWS는 빠르고, 고품질이며, 적당한 가격의 언어 변환을 제공하는 신경 기계 번역 서비스인 Amazon Translate을 제공합니다. Amazon Translate은 사용자 간의 모국어 간 통신을 가능하게 하기 위해 온디맨드 번역을 제공할 수 있습니다. 채팅, 이메일, 헬프데스크 및 티켓 응용 프로그램에 실시간 번역을 추가하여 영어권 에이전트 또는 직원이 여러 언어로 고객과 통신할 수 있습니다.

 

이 블로그 포스트는 트위치 채널에서 Amazon Translate을 사용하여 다양한 언어로 채팅하는 방법을 다룹니다. 여러분은 또한 실시간으로 수신되는 메시지를 음성으로 내는 것을 돕기 위해 텍스트 음성 변환 서비스인 Amazon Polly를 사용하는 방법을 배울 것입니다. 이 추가 기능은 추가적인 접근을 제공하고 사용자에게 가까운 곳에서 다른 작업에 집중하면서 메시지를 듣는 옵션을 제공합니다.

 

 

솔루션 개요

이 포스트에서는 CSS와 JavaScript로 HTML 페이지를 생성합니다. 트위치 채널에 연결하여 메시지를 수신하기 위해 JavaScript 라이브러리를 사용할 수 있습니다. 실시간 메시지가 들어오면 AWS SDK를 사용하여 Amazon Translate에 요청하여 해당 메시지를 변환하여 UI에 표시할 수 있습니다. 사용자가 사용하는 음성 옵션을 사용하도록 선택하면 AWS SDK를 사용하여 Amazon Polly에 요청 후, 이러한 메시지에 대한 합성된 음성을 사용자에게 재생할 수 있습니다.

 

 

트위치 계정 생성 및 구성

 

  1. 트위치 계정을 신청하려면 twitch.tv를 방문하세요.
  2. 트위치 계정에 성공적으로 가입한 후, https://twitchapps.com/tmi를 방문하여 트위치 OAuth 토큰을 만드세요.
  3. “트위치에 연결”버튼을 클릭하여 OAuth 토큰을 적어 두십시오.

 

 

 

 

IAM 사용자 생성

AWS Identity and Access Manager(IAM)를 사용하면 AWS 서비스 및 리소스에 대한 액세스를 안전하게 관리할 수 있습니다. IAM을 사용하여 AWS 사용자 및 그룹을 만들고 관리하고 사용 권한을 사용하여 AWS 리소스에 대한 액세스를 허용 및 거부할 수 있습니다. 시작하려면 이 예시를 실행하는 데 필요한 최소 권한을 가진 IAM 사용자를 생성합니다.

 

  1. https://console.aws.amazon.com/iam/home?region=us-east-1#/users으로 이동합니다.
  2. ‘사용자 추가’를 선택합니다.
  3. 사용자 이름을 입력하고 프로그래밍 액세스를 선택한 후, 다음 사용 권한을 선택하십시오.
  1. 직접적인 기존 정책 연결을 선택하고 변환을 검색하십시오.
  2. TranslateReadOnly 옆의 확인 란을 선택하고 다음 검토를 선택합니다.
  1. 사용자 생성’을 선택하고 액세스 키 ID및 비밀 액세스 키를 메모합니다.

응용 프로그램 생성 및 실행

  1. HTML 페이지를 만들고 다음의 코드 블록에서 코드를 복사하십시오.

<!doctype html>

<html lang=”en”>

<head>

  <title>Amazon Translate</title>

  <meta charset=”utf-8″>

  <meta name=”viewport” content=”width=device-width, initial-scale=1, shrink-to-fit=no”>

 

  <!– Latest compiled and minified CSS for Bootstrap –>

  <link rel=”stylesheet” href=”https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css” integrity=”sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u” crossorigin=”anonymous”>

 

   <!– Custom CSS –>

   <style>

     .topHeader

     {

       background-color: #6441a4;

       padding: 10px;

       border-bottom: solid 1px #cacaca;

       color: white

     }

 

     .panelHeading

     {

       background-color: #6441a4 !important;

     }

 

     .panelBody

     {

      min-height: 450px;  max-height: 450px;overflow-y: scroll;

     }

 

     body{

        margin-left: 0px;

        margin-right: 0px;

        height: 100%;

      }

   </style>

</head>

<body>

  <div class=”container-fluid”>

    <!–Top Header–>

    <div class=”row topHeader”>

      <div class=”col-md-12″>

          <h4>Amazon Translate – Artificial Intelligence on AWS – Powerful machine learning for all Developers and Data Scientists</h4>

      </div>

    </div>

 

    <!–Status Label–>

    <div class=”row”>

      <div class=”col-md-12″>

        <p class=”bg-info”>

        <div id=”connecting-div”></div>

        </p>

      </div>

    </div>

 

    <div class=”row” style=”padding: 10px;”>

      <div class=”col-md-6″>

          <div class=”form-inline”>

            <div class=”form-group”>

              <input type=”text” id=”channel” class=”form-control” value=”” placeholder=”Channel”/>

            </div>

            <div class=”form-group”>

              <select id=”sourceLanguage” class=”form-control”>

                  <option value=”en”>en</option>

                  <option value=”ar”>ar</option>

                  <option value=”de” selected=”selected”>de</option>

                  <option value=”es”>es</option>

                  <option value=”fr”>fr</option>

                  <option value=”pt”>pt</option>

                  <option value=”zh”>zh</option>

                </select>

            </div>

            <div class=”form-group”>

              <select id=”targetLanguage” class=”form-control”>

                <option value=”en” selected=”selected”>en</option>

                <option value=”ar”>ar</option>

                <option value=”de”>de</option>

                <option value=”es”>es</option>

                <option value=”fr”>fr</option>

                <option value=”pt”>pt</option>

                <option value=”zh”>zh</option>

              </select>

            </div>

            <div class=”form-group”>

              <button type=”button” class=”form-control” id=”btn-go” onclick=”connect()”>Go</button>

              <button type=”button” class=”form-control” id=”btn-stop” onclick=”location.href=’index.html’;”>Stop</button>

              <span id=”status”></span>

            </div>

          </div>

        </div>

        <div class=”col-md-6″>

            <div class=”form-inline”>

              <div class=”form-group”>

                  <input type=”checkbox” id=”cbSpeak” value=”Speak”> Speak Live Translation

                  <input type=”text” id=”follow” class=”form-control” value=”” placeholder=”follow”/>

                </div>

            </div>

          </div>

      </div>

 

    <!–Chat Boxes–>

    <div class=”row”>

      <!–Live Chat–>

      <div class=”col-md-6″>

        <div class=”panel panel-primary”>

          <div class=”panel-heading panelHeading”>Live Chat</div>

            <div id=”livechatc” class=”panel-body panelBody”>

              <div class=”subscribe” id=”livechat”></div>

          </div>

        </div>

      </div>

      <!–Live Chat–>

      <!–Translated Chat–>

      <div class=”col-md-6″>

        <div class=”panel panel-primary”>

          <div class=”panel-heading panelHeading”>Live Translation</div>

            <div id=”livetranslationc” class=”panel-body panelBody”>

              <div class=”imageDetected” id=”livetranslation”></div>

          </div>

        </div>

      </div>

      <!–Translated Chat–>

    </div>

 

    <!–Send Message–>

    <div class=”row”>

        <div class=”col-md-11″>

            <input type=”text” id=”message” class=”form-control”/>

        </div>

        <div class=” col-md-1″>

          <button type=”button” class=”form-control btn btn-default” id=”btn-send” onclick=”sendMessage()”>Send</button>

        </div>

    </div>

  </div>

 

   <!– Latest compiled and minified JavaScript –>

   <!– jQuery first, then Bootstrap JS –>

   <script src=”https://code.jquery.com/jquery-3.2.1.slim.min.js” integrity=”sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN” crossorigin=”anonymous”></script>

   <script src=”https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js” integrity=”sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa” crossorigin=”anonymous”></script>

 

   <script src=”aws-js-sdk/dist/aws-sdk-all.js”></script>

   <script src=”http://cdn.tmijs.org/js/1.2.1/tmi.min.js” integrity=”sha384-eE0n7sm1W7DOUI2Xh5I4qSpZTe6hupAO0ovLfqEy0yVJtGRBNfssdmjbJhEYm6Bw” crossorigin=”anonymous”></script>

   <script>

     cred = {

          twitchUsername: “Twitch user name”,

          twitchOAuthToken: “Twitch OAuth token”,

          awsAccessKeyId: “access key”,

          awsSecretAccessKey: “secret key”

     };

 

     AWS.config.region = ‘region’;

     ep = new AWS.Endpoint(‘endpoint’);

 

     AWS.config.credentials = new AWS.Credentials(cred.awsAccessKeyId, cred.awsSecretAccessKey);

     window.translator = new AWS.Translate({endpoint: ep, region: AWS.config.region});

 

     /**************************Init and Connect to Chat****************************/

     function connect(){

       init();

 

       //Twitch Client

       var options = {

           options: {

             debug: false

           },

           connection: {

             cluster: “aws”,

             reconnect: true

           },

           identity: {

             username: cred.twitchUsername,

             password: cred.twitchOAuthToken

           },

           channels: [con.channel]

       };

 

       window.client = tmi.client(options);

 

       window.client.connect();

 

       //Attached Handlers

       window.client.on(“chat”, onChat);

       window.client.on(“connecting”, onConnecting);

       window.client.on(“connected”, onConnected);

 

       //Disable UI Elements

       document.getElementById(“sourceLanguage”).disabled = true;

       document.getElementById(“targetLanguage”).disabled = true;

       document.getElementById(“channel”).disabled = true;

       document.getElementById(“btn-go”).disabled = true;

     }

 

     function init(){

       //Get UI Controls

       var lc = document.getElementById(“livechat”);

       var lt = document.getElementById(“livetranslation”)

       var lcc = document.getElementById(“livechatc”);

       var ltc = document.getElementById(“livetranslationc”)

       var cbspeak = document.getElementById(“cbSpeak”)

       var follow = document.getElementById(“follow”);

       var sendMessage = document.getElementById(“message”);

 

       //Cache values

       con = {

         channel: document.getElementById(“channel”).value,

         sourceLanguage: document.getElementById(“sourceLanguage”).value,

         targetLanguage: document.getElementById(“targetLanguage”).value,

         liveChatUI: lc,

         liveTranslationUI: lt,

         liveChatUIContainer: lcc,

         liveTranslationUIContainer: ltc,

         cbSpeak: cbspeak,

         follow: follow,

         sendMessage: sendMessage

       }

 

         lc.innerHTML = ”;

         lt.innerHTML = ”;

 

         //Speaker

        var voiceId = “Joanna”;

        if(con.targetLanguage == “en”)

           voiceId = “Joanna”;

        else if(con.targetLanguage == “de”)

             voiceId = “Marlene”;

        else if(con.targetLanguage == “es”)

            voiceId = “Conchita”;

        else if(con.targetLanguage == “fr”)

           voiceId = “Celine”;

        else if(con.targetLanguage == “pt”)

           voiceId = “Ines”;

        else

           voiceId = “Joanna”;

         window.audioPlayer = AudioPlayer(voiceId);

     }

     /**************************Init and Connect to Chat****************************/

 

     /**************************Receive and Translate Chat****************************/

     function onChat (channel, userstate, message, self) {

         // Don’t listen to my own messages..

         if (self) return;

 

         //Translate

         if (message) {

           var username = userstate[‘username’];

 

           var params = {

               Text: message,

               SourceLanguageCode: con.sourceLanguage,

               TargetLanguageCode:  con.targetLanguage

           };

 

           window.translator.translateText(params, function onIncomingMessageTranslate(err, data) {

              if (err) {

                 console.log(“Error calling Translate. ” + err.message + err.stack);

             }

              if (data) {

                  console.log(“M: ” + message);

                  console.log(“T: ” + data.TranslatedText);

 

                  //Print original message in chat UI

                  con.liveChatUI.innerHTML += ‘<strong>’ + username + ‘</strong>: ‘ +  message + ‘<br>’;

 

                  //Print translation in translation UI

                  con.liveTranslationUI.innerHTML += ‘<strong>’ + username + ‘</strong>: ‘ + data.TranslatedText + ‘<br>’;

 

                  //If speak translation in enabled, speak translated message

                  if(con.cbSpeak.checked){

                    if(con.follow.value == “” || username == con.follow.value)

                      audioPlayer.Speak(username + ” says ” + data.TranslatedText);

                  }

 

                  //Scroll chat and translated UI to bottom to keep focus on latest messages

                  con.liveChatUIContainer.scrollTop = con.liveChatUIContainer.scrollHeight;

                  con.liveTranslationUIContainer.scrollTop = con.liveTranslationUIContainer.scrollHeight;

                }

           });

         }

     }

     /**************************Receive and Translate Chat****************************/

 

     /**************************Client Connecting****************************/

     function onConnecting (address, port) {

         document.getElementById(“status”).innerHTML = ” [ Connecting…]”

     }

 

     function onConnected (address, port) {

         document.getElementById(“status”).innerHTML = ” [ Connected ]”

         window.audioPlayer.Speak(“Connected to channel ” + con.channel + “. You should now be getting live chat messages.”);

     }

     /**************************Client Connecting****************************/

 

     /**************************Send Message****************************/

     function sendMessage(){

          if(con.sendMessage.value){

            message = con.sendMessage.value;

            var params = {

                Text: con.sendMessage.value,

                SourceLanguageCode: con.targetLanguage,

                TargetLanguageCode:  con.sourceLanguage

            };

 

            window.translator.translateText(params, function onSendMessageTranslate(err, data) {

                  if (err) {

                     console.log(“Error calling Translate. ” + err.message + err.stack);

                  }

                  if (data) {

                      console.log(“M: ” + message);

                      console.log(“T: ” + data.TranslatedText);

 

                      //Send message to chat

                      window.client.action(con.channel, data.TranslatedText);

 

                      //Clear send message UI

                      con.sendMessage.value = “”;

 

                      //Print original message in Translated UI

                      con.liveTranslationUI.innerHTML += ‘<strong> ME: </strong>: ‘ +  message + ‘<br>’;

 

                      //Print translated message in Chat UI

                      con.liveChatUI.innerHTML += ‘<strong> ME: </strong>: ‘ + data.TranslatedText + ‘<br>’;

 

                      //Scroll chat and translated UI to bottom to keep focus on latest messages

                      con.liveChatUIContainer.scrollTop = con.liveChatUIContainer.scrollHeight;

                      con.liveTranslationUIContainer.scrollTop = con.liveTranslationUIContainer.scrollHeight;

                  }

            });

          }

     }

     /**************************Send Message****************************/

 

     /**************************Audio player****************************/

     function AudioPlayer(voiceId) {

         var audioPlayer = document.createElement(‘audio’);

         audioPlayer.setAttribute(“id”, “audioPlayer”);

         document.body.appendChild(audioPlayer);

 

         var isSpeaking = false;

 

         var speaker = {

             self: this,

             playlist:[],

 

             Speak: function (text) {

                 //If currently speaking a message, add new message to the playlist

                 if (isSpeaking) {

                     this.playlist.push(text);

                 } else {

                     speakTextMessage(text).then(speakNextTextMessage)

                 }

             }

         }

 

         // Speak text message

         function speakTextMessage(text) {

             return new Promise(function (resolve, reject) {

                 isSpeaking = true;

                 getAudioStream(text).then(playAudioStream).then(resolve);

             });

         }

 

         // Speak next message in the list

         function speakNextTextMessage() {

             var pl = speaker.playlist;

             if (pl.length > 0) {

                 var txt = pl[0];

                 pl.splice(0, 1);

                 speakTextMessage(txt).then(speakNextTextMessage);

             }

         }

 

         // Get synthesized speech from Amazon polly

         function getAudioStream(textMessage) {

             return new Promise(function (resolve, reject) {

                 var polly = new AWS.Polly();

                 var params = {

                     OutputFormat: ‘mp3’,

                     Text: textMessage,

                     VoiceId: voiceId

                 }

                 polly.synthesizeSpeech(params, function (err, data) {

                     if (err)

                         reject(err);

                     else

                         resolve(data.AudioStream);

                 });

             });

         }

 

         // Play audio stream

         function playAudioStream(audioStream) {

             return new Promise(function (resolve, reject) {

                 var uInt8Array = new Uint8Array(audioStream);

                 var arrayBuffer = uInt8Array.buffer;

                 var blob = new Blob([arrayBuffer]);

 

                 var url = URL.createObjectURL(blob);

                 audioPlayer.src = url;

                 audioPlayer.addEventListener(“ended”, function () {

                     isSpeaking = false;

                     resolve();

                 });

                 audioPlayer.play();

             });

         }

 

         return speaker;

     }

     /**************************Audio player****************************/

   </script>

</body>

</html>

  1. AWS JavaScript SDK로 경로를 지정하여 다음 코드 조각을 업데이트합니다.

<script src=”aws-js-sdk/dist/aws-sdk-all.js”></script>

  1. AWS 리전 및 엔드포인트를 사용하여 다음 코드 조각을 업데이트합니다.

AWS.config.region = ‘region’;

ep = new AWS.Endpoint(‘endpoint’);

  1. 트위치 사용자 이름, OAuth 토큰, AWS 액세스 키 ID 및 AWS 보안 액세스 키를 사용하여 다음 코드 조각을 업데이트합니다.

cred = {

          twitchUsername: “Twitch user name”,

          twitchOAuthToken: “Twitch OAuth token”,

          awsAccessKeyId: “access key”,

          awsSecretAccessKey: “secret key”

     };

  1. 브라우저에서 웹 페이지를 엽니다.
  2. 트위치 채널 이름을 입력합니다.
  3. 소스 및 대상 언어를 선택하고 이동을 클릭합니다.

이제 화면에 해당 번역과 함께 실시간 메시지가 표시됩니다. 트위치 채팅을 이 예시 어플리케이션과 비교하면, Amazon Translate의 성능과 매우 낮은 지연 시간을 보여주는 트위치 채팅에 나타나는 메시지를 동시에 볼 수 있습니다.

 

 

또한, re:Invent 2017에 트위치 채팅의 실시간 번역을 보여 주는 비디오 데모를 보실 수 있습니다.

 

 

 

메시지 수신 및 번역

다음 코드 블록은 실시간으로 수신되는 메시지를 받는 방법을 보여 주며 AWS SDK를 사용하여 Amazon Translate을 호출하여 UI에 번역된 메시지를 표시합니다. 다음과 같이 메시지를 번역하는 코드는 몇 줄에 불과하지만 입력 및 UI 처리에 대한 대부분의 경우에만 발생합니다.

 

/**************************Receive and Translate Chat****************************/

     function onChat (channel, userstate, message, self) {

         // Don’t listen to my own messages..

         if (self) return;

 

         //Translate

         if (message) {

           var username = userstate[‘username’];

 

           var params = {

               Text: message,

               SourceLanguageCode: con.sourceLanguage,

               TargetLanguageCode:  con.targetLanguage

           };

 

           window.translator.translateText(params, function onIncomingMessageTranslate(err, data) {

              if (err) {

                 console.log(“Error calling Translate. ” + err.message + err.stack);

             }

              if (data) {

                  console.log(“M: ” + message);

                  console.log(“T: ” + data.TranslatedText);

 

                  //Print original message in chat UI

                  con.liveChatUI.innerHTML += ‘<strong>’ + username + ‘</strong>: ‘ +  message + ‘<br>’;

 

                  //Print translation in translation UI

                  con.liveTranslationUI.innerHTML += ‘<strong>’ + username + ‘</strong>: ‘ + data.TranslatedText + ‘<br>’;

 

                  //If speak translation in enabled, speak translated message

                  if(con.cbSpeak.checked){

                    if(con.follow.value == “” || username == con.follow.value)

                      audioPlayer.Speak(username + ” says ” + data.TranslatedText);

                  }

 

                  //Scroll chat and translated UI to bottom to keep focus on latest messages

                  con.liveChatUIContainer.scrollTop = con.liveChatUIContainer.scrollHeight;

                  con.liveTranslationUIContainer.scrollTop = con.liveTranslationUIContainer.scrollHeight;

                }

           });

         }

     }

     /**************************Receive and Translate Chat****************************/

 

 

번역된 메시지 전송

다음 코드 블록은 발신 메시지를 변환하여 트위치 채널로 전송하는 방법을 보여 줍니다.

 

/**************************Send Message****************************/

     function sendMessage(){

          if(con.sendMessage.value){

            message = con.sendMessage.value;

            var params = {

                Text: con.sendMessage.value,

                SourceLanguageCode: con.targetLanguage,

                TargetLanguageCode:  con.sourceLanguage

            };

 

            window.translator.translateText(params, function onSendMessageTranslate(err, data) {

                  if (err) {

                     console.log(“Error calling Translate. ” + err.message + err.stack);

                  }

                  if (data) {

                      console.log(“M: ” + message);

                      console.log(“T: ” + data.TranslatedText);

 

                      //Send message to chat

                      window.client.action(con.channel, data.TranslatedText);

 

                      //Clear send message UI

                      con.sendMessage.value = “”;

 

                      //Print original message in Translated UI

                      con.liveTranslationUI.innerHTML += ‘<strong> ME: </strong>: ‘ +  message + ‘<br>’;

 

                      //Print translated message in Chat UI

                      con.liveChatUI.innerHTML += ‘<strong> ME: </strong>: ‘ + data.TranslatedText + ‘<br>’;

 

                      //Scroll chat and translated UI to bottom to keep focus on latest messages

                      con.liveChatUIContainer.scrollTop = con.liveChatUIContainer.scrollHeight;

                      con.liveTranslationUIContainer.scrollTop = con.liveTranslationUIContainer.scrollHeight;

                  }

            });

          }

     }

     /**************************Send Message****************************/

 

 

요약

이 포스트에서는 채팅 메시지의 실시간 번역을 위해 Amazon Translate을 사용하는 방법에 대해 배웠습니다. 트위치 채널을 예시로 들어 보았지만, 다른 채팅 플랫폼이나 고객 서비스 상호 작용, 메시지 보드 등 실시간 스트리밍을 위한 출발점으로 사용할 수 있습니다.

 

원문 URL: https://aws.amazon.com/ko/blogs/machine-learning/translate-a-chat-channel-using-amazon-translate/

** 메가존 TechBlog는 AWS BLOG 영문 게재글중에서 한국 사용자들에게 유용한 정보 및 콘텐츠를 우선적으로 번역하여 내부 엔지니어 검수를 받아서, 정기적으로 게재하고 있습니다. 추가로 번역및 게재를 희망하는 글에 대해서 관리자에게 메일 또는 SNS페이지에 댓글을 남겨주시면, 우선적으로 번역해서 전달해드리도록 하겠습니다.