BeringAI for Word

시작하기 (translator.html & translator.js)

해당 문서는 MS Office Plugin에 대한 코드이기 때문에 통상적인 프론트 개발자 또는 백엔드 개발자가 보고 이해하는 데에 상당한 시간이 소요되는 문서입니다. 이 점 참조하여 해당 플러그인을 수정하여 재배포하고자 하는 경우 공수에 대해 적어도 3개월을 잡으실 것을 권장해드립니다.

Once you’ve created a BeringAI API account and located your authentication key, you’re ready to make your first request. Let’s start with text translation.

‘BeringAI Translator for Word’는 MS Word 상에서 구동되는 번역기 Add-in입니다.
본 문서는 해당 플러그인이 올바르게 유지보수되기를 바라는 마음에서 작성된 인수인계용 문서입니다.
운영되고 있는 플러그인을 다운로드 하시려면 우측 링크를 클릭해주세요.AppSource 다운로드 페이지 방문하기.
소스 코드는 Google Drive에 BeringAI Word.zip(테스트용), BeringAI Word Prod.zip(운영용) 으로 저장되어있습니다. 해당 압축파일을 풀고 VScode에서 확인해주시기 바랍니다.

시작하기

https://learn.microsoft.com/en-us/office/dev/add-ins/overview/learning-path-beginner
위 링크에 방문하시면 MS Office Add-ins를 만드는 방법에 대한 가이드라인을 확인하실 수 있습니다.

Node.js, npm 설치

플러그인은 위 환경에서 동작합니다.
관련하여 아래 커맨드라인을 통해 설치하실 수 있습니다.

Command

				
					npm -v
npm install npm -g
				
			

매우 강조하지만, 처음부터 다시 만드시는게 더 빠릅니다.

플러그인을 아예 처음부터 비어있는 상태로 만들고 싶다면 아래의 코드를 통해 새 Add-in을 만들어주세요.
아래 코드를 수행하면 Yo office라는 프로그램이 동작합니다.
해당 프로그램은 플러그인 데모 파일을 새로 생성해줍니다.

Command

				
					npm install -g yo generator-office
				
			

모든 것은 Taskpane으로부터 시작됩니다.

src>taskpane>taskpane.html, taskpane.js이 Add-in 실행시 기본적으로 실행되는 코드입니다.
만약 기본적으로 실행되는 파일을 변경하고 싶다면 manifest.xml에서 변경이 필요합니다.

taskpane.html, 시작을 책임지는 코드이지만, 껍데기 뿐입니다.

외부 스타일 및 폰트 라이브러리 등은 중요하지 않기 때문에 서술하지는 않겠습니다만, 굳이 언급하자면 Material Icon을 위해 font.googleapis로 시작하는 링크를 삽입해두었으니 그것만큼은 삭제하지 않으시는 것을 권장드립니다.

Body > Main에 기본적인 번역기의 틀이 다 들어있을 것으로 보이겠으나, 실제로는 삭선 처리된 코드만 남겨져있습니다.

Body> Main 파트는 taskpane.js에서 Inner Html을 사용하여 들어오는 코드입니다. 다음장인 taskpane.js에서 설명되겠지만, setting.html이나 translate.html코드가 InnerHtml로 들어오게 됩니다.

Body> Footer의 코드를 통해 공통 푸터를 제공하고 있습니다 연도가 변경된다면 카피라이트 문구를 변경해주시기 바랍니다.

taskpane.js

해당 코드는 처음 플러그인이 동작할 때, 토큰이 존재하는지, 토큰이 올바른지 등에 대해 파악해주는 코드입니다.

아래 Office.onReady로 시작하는 코드는 플러그인이 다 로드된 것이 확인된 후 동작해야할 것들이 기재되었습니다.

document.getElementById(“sideload-msg”).style.display=”none”;
document.getElementById(“app-body”).style.display=”flex”;
위 두개의 코드는 모든 플러그인에 기본적으로 필요한 것이므로 그냥 두시면 됩니다.
 
그 아래줄 부터의 코드는 토큰을 가져오고, jwtDecode를 하여 유효한지 확인하는 파트입니다.
 

토큰이 유효하다면 loadPage 펑션을 사용하여 translator.html, translator.js를 로드하여 taskpane.html에 InnerHtml로 보여줍니다.

토큰이 없거나 유효하지 않다면 loadPage 펑션을 사용하여 firstExperience.html, firstExperience.js를 로드하여 taskpane.html에 content Element에 삽입되어 InnerHtml로 보여줍니다.

taskpane.js 중 일부

				
					Office.onReady((info) => {
  if (info.host === Office.HostType.Word) {
    document.getElementById("sideload-msg").style.display = "none";
    document.getElementById("app-body").style.display = "flex";
    
    var token = getAccessToken();
    if (!token) {
      console.log('토큰이 없습니다.');
      loadPage("firstExperience.html", "firstExperience.js");
    } else {
      try {
          var decoded = jwtDecode(token);
          console.log('Decoded:', decoded);
          loadPage("translator.html", "translator.js");
      } catch (error) {
          console.error('유효하지 않은 토큰입니다:', error.message);
          loadPage("firstExperience.html", "firstExperience.js");
      }
    }
    
  }
});
				
			

taskpane.js 중 loadPage, loadScript 함수

				
					function loadPage(pageUrl, scriptUrl) {
  var contentDiv = document.getElementById("content");

  // Fetch the content of the page
  fetch(pageUrl)
    .then(response => response.text())
    .then(html => {
      // Set the HTML content of the content div
      contentDiv.innerHTML = html;

      // Load and execute the script
      loadScript(scriptUrl);
    })
    .catch(error => console.error("Error loading page:", error));
}

function loadScript(scriptUrl) {
  // Create a script element
  var script = document.createElement("script");

  // Set the script source URL
  script.src = scriptUrl;

  // Append the script element to the body
  document.body.appendChild(script);
}


				
			

firstExperience.html

해당 페이지는 유저애게 베링AI Plugin을 간략하게 소개하면서 로그인을 유도하는 페이지이다. 해당 페이지는 기획자의 의도로 만들어진 것이 절대 아니며, 마이크로소프트의 심사 대상에 ‘First experience’에 대한 내용이 포함되어 있기 때문에 넣은 것이다.

firstExperience.js

해당 JS코드는 단순히 ‘로그인’ 버튼을 누르면 loadPage 펑션을 통해 auth.js, auth.html을 여는 내용이다. 별다른 내용이 없으므로 그대로 두고 쓰되 참조만 하면 된다.

translator.html

번역기에 대한 틀이 있는 HTML이다.

수정할 사안이 있다면 통상적으로 ‘언어’ 및 ‘엔진’에 대한 업데이트가 있는 경우이다.

바로 아래 코드는 엔진을 선택하는 메뉴이다. legal, patent, business라는 id만 유지한다면 정상적으로 동작시킬 수 있다.

더 아래 코드는 소스언어 선택 드롭다운의 코드인데, 언어별로 3글자 언어 id를 가지고 있다는 것을 확인할 수 있다.

삭선 처리되어있는 코드처럼 새 언어가 필요할 경우 동일한 형태로 추가할 수 있다.

세글자 언어 코드만 개발진과 협의한대로 지정해주면 된다.

translator.html 중 엔진 선택 메뉴

				
					                    <div class="Bering-Menu">
                        <div class="Bering-Menu-Item Bering-Menu-Item-Left" id="legal">
                            <span class="material-icons-outlined md-18 border_color">
                                gavel
                                </span>
                            <span class="Bering-Menu-Item-Span">Legal<span>
                        </div>
                        <div class="Bering-Menu-Item Bering-Menu-Item-Center" id="patent">
                            <span class="material-icons-outlined md-18 insert_drive_file">
                                lightbulb
                                </span>
                            <span class="Bering-Menu-Item-Span">Patent<span>
                        </div>
                        <div class="Bering-Menu-Item Bering-Menu-Item-Right" id="business">
                            <span class="material-icons-outlined md-18 insert_drive_file">
                                business_center
                                </span>
                            <span class="Bering-Menu-Item-Span">Business<span>
                        </div>
                    </div>
				
			

translator.html 중 언어 선택 드롭다운

				
					 <div class="Source-Language-Select" id="source_language_select">
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="und">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">Detect language
                        </span>
                    </button>
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="eng">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">English</span>
                    </button>
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="kor">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">Korean</span>
                    </button>
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="jpn">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">Japanese</span>
                    </button>
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="cmn">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">Chinese (Simplified)</span>
                    </button>
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="ind">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">Indonesian</span>
                    </button>
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="spa">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">Spanish</span>
                    </button>
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="fra">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">French</span>
                    </button>
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="deu">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">German</span>
                    </button>
                    <!--
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="PT">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">Portuguese</span>
                    </button>
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="IT">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">Italian</span>
                    </button>
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="TH">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">Thai</span>
                    </button>
                    <button class="Selection-Button-Source Source-Language-Select-Button" id="VI">
                        <span class="material-icons md-18 done">
                            done
                        </span>
                        <span class="Selection-Button-Source Source-Language-Select-Button-Text">Vietnamese</span>
                    </button>
                    -->
                </div>

				
			

translator.js 중 Office.onReady파트

페이지 로드시에 동작해야하는 코드들에 대한 모음이다.

document.getElementById로 기재된 코드들은 ‘설정’, ‘번역 엔진’ 을 비롯한 각종 버튼을 읽어내기 위해 존재한다.

//document.getElementById(“review_selection”).onclick = () => tryCatch(requestReview);

//document.getElementById(“review_selection_sticky_footer”).onclick = () => tryCatch(requestReviewForLongText);

위 두 줄의 코드는 번역 결과물에 대해 평가하는 리뷰 기능에 대한 버튼 관련 코드인데 현재 삭선처리 해두었다. 장기적으로 해당 리뷰 기능이 Word 플러그인에 대한 유저 피드백을 접수하는 수단이 되리라 생각된다.

참조로 위의 코드들이 Office.onReady에 존재해야하는 이유는 그 외의 영역에 있을 경우 별도로 호출하지 않는 한,  MS Word내의 브라우저에서 아예 동작하지 않기 때문이다.

아래 코드 중 가장 중요한 것은 사실상 run()이다. 다른 코드들은 다른 프론트엔드 코드라고 생각하면 편하지만 run은 MS Word Plugin답게 만드는 코드이기 때문이다. 여기서 말하는 ‘MS Word Plugin 답게’란, 사이드 브라우저를 벗어나 MS Word Editor 창에 영향을 준다는 것이다.

 

translator.js 중 Office.onReady

				
					 Office.onReady((info) => {
  if (info.host === Office.HostType.Word) {
    
    
    // 페이지 로드 시에도 호출
    document.getElementById("setting").onclick = () => tryCatch(setting);
    document.getElementById("legal").onclick = () => tryCatch(selectLegalEngine)
    document.getElementById("patent").onclick = () => tryCatch(selectPatentEngine)
    document.getElementById("business").onclick = () => tryCatch(selectBusinessEngine)
    document.getElementById("modal_close").onclick = () => tryCatch(closeModal)
    document.getElementById("modal_close_patent").onclick = () => tryCatch(closeModal)
    document.getElementById("modal_close_free_tier").onclick = () => tryCatch(closeModal)
    document.getElementById("modal_close_same").onclick = () => tryCatch(closeModal)
    document.getElementById("sideload-msg").style.display = "none";
    document.getElementById("app-body").style.display = "flex";
    showSelectionContent();

    //Selection 영역
    document.getElementById("insert").onclick = () => tryCatch(replaceText);
    //document.getElementById("review_selection").onclick = () => tryCatch(requestReview);
    //document.getElementById("review_selection_sticky_footer").onclick = () => tryCatch(requestReviewForLongText);
    document.getElementById("insert_sticky_footer").onclick = () => tryCatch(replaceText);
    document.getElementById("clear").onclick = () => tryCatch(clearSourceTarget);
    document.getElementById("scroll").onclick = () => tryCatch(scrollDown);
    document.getElementById("detected_language").onclick = () => tryCatch(changeToDetectedLanguage);
    document.getElementById("target_copy").onclick = () => tryCatch(targetCopy);
    document.getElementById("target_copy_sticky_footer").onclick = () => tryCatch(targetCopy);

    document.getElementById("open_source_select").onclick = (event) => tryCatch(() => toggleSelectSource(event));
    document.getElementById("select_source_language").onclick = (event) => tryCatch(() => toggleSelectSourceOpened(event));
    document.getElementById("open_target_select").onclick = (event) => tryCatch(() => toggleSelectTarget(event));
    document.getElementById("select_target_language").onclick = (event) => tryCatch(() => toggleSelectTargetOpened(event));
    document.getElementById("switch").onclick = () => tryCatch(toggleSwitch);
    
    var sourceLanguageButtons = document.querySelectorAll(".Selection-Button-Source.Source-Language-Select-Button");
    // Use forEach to add click event listener to each element
    sourceLanguageButtons.forEach(button => {
      button.onclick = () => tryCatch(() => changeButtonTextSource(button));
    });
    var targetLanguageButtons = document.querySelectorAll(".Selection-Button-Target.Target-Language-Select-Button");
    // Use forEach to add click event listener to each element
    targetLanguageButtons.forEach(button => {
      button.onclick = () => tryCatch(() => changeButtonTextTarget(button));
    }); 
    run();
    applyStylesSource();
    applyStylesTarget();
  }
});
				
			

translator.js 중 run() 펑션

Office.context.document.addHandlerAsync는 document내에서의 이벤트 핸들러를 추가한다고 보면 된다.

Office.EventType.DocumentSelectionChanged는 document안에서의 Selection이 변경되는 이벤트를 감지하는 것이다. 여기서 말하는 Selection이란 워드 내에서의 드래그를 통한 영역 선택을 의미한다.

run()펑션을 통해 유저는 드래그한 텍스트를 번역기에 넣어 가져올 수 있다.

더 자세한 내용은 아래의 getSelect() 펑션을 확인해야한다.

translator.js 중 run() 펑션

				
					// Run a batch operation against the Word object model.
async function run() { 
  Office.context.document.addHandlerAsync(
    Office.EventType.DocumentSelectionChanged,
    selectionChangedHandler
  );
  await getSelect();
}
				
			

translator.js 중 getSelect() 펑션

아래 코드는, selected된 문자열을 가져와 소스 텍스트 창에 넣고 번역을 수행하는 것 까지 포함된다.

만약 선택된 문자가 공백일 경우, 번역을 수행하지 않는다.

만약 선택한 문자가 5000자 보다 길 경우, 5000자 까지만 잘라서 가지고 온다. 

선택한 문자의 길이가 길어 소스 텍스트 박스의 세로 길이가 화면의 길이보다 길 경우, 푸터처럼 아래 영역에 피쳐들을 띄워주는 바가 노출되게 해두었다. calculatedLengthStickyFooter 참조.

debounceMakeApiRequest();는 번역을 수행하는 펑션이다. 유저의 요금이 낭비되지 않도록 디바운스를 걸어두었는데, 후술되는 관련 파트를 참조해주기 바란다.

마지막으로 getSelection()은 MS Office Add-in의 일종의 내장 펑션으로 본 소스코드에는 존재하지 않는다. 단순히 긁어오는 펑션으로 보면 된다.

translator.js 중 getSelect() 펑션

				
					async function getSelect() {
    const selectedText = await runWithPromise(context => {
      const selection = context.document.getSelection();
      selection.load('text');
      return context.sync().then(() => {
        return selection.text;
      });
    });
  
  
    if(selectedText === ''){
    console.log('Blank Text:', selectedText);
    }
    else if(selectedText.length > 5000){
    const sourceElement = document.getElementById("source");
    sourceElement.textContent = selectedText.substring(0, 5000);
    var calculatedLength = document.getElementById("calculated_length")
    var calculatedLengthStickyFooter = document.getElementById("calculated_length_sticky_footer")
    calculatedLength.textContent = "5000";
    calculatedLengthStickyFooter.textContent = "5000";
    buttonSection.style.display = buttonGroup.style.display = 'inline-block';
    debounceMakeApiRequest();
      if (sourceHeight.clientHeight > document.body.clientHeight - 134){
        scrollDownButton.style.display = 'flex';
        stickyFooter.style.display = 'flex';
      }
      else{
        scrollDownButton.style.display = 'none';
        stickyFooter.style.display = 'none';
      }
    }
    else{
    var calculatedLength = document.getElementById("calculated_length")
    var calculatedLengthStickyFooter = document.getElementById("calculated_length_sticky_footer")
    const sourceElement = document.getElementById("source");
    sourceElement.textContent = selectedText;
    calculatedLength.textContent = selectedText.length;
    calculatedLengthStickyFooter.textContent = selectedText.length;
    buttonSection.style.display = buttonGroup.style.display = 'inline-block';
    debounceMakeApiRequest();
      if (sourceHeight.clientHeight > document.body.clientHeight){
        scrollDownButton.style.display = 'flex';
        stickyFooter.style.display = 'flex';
      }
      else{
        scrollDownButton.style.display = 'none';
        stickyFooter.style.display = 'none';
      }
   }
}

				
			

translator.js 중 debounceMakeApiRequest() 펑션

MakeApiRequest();는 번역을 수행하는 API를 수행시키는 펑션이다.

debounceMakeApiRequest()는 위 펑션에 디바운스를 걸어 수행시키는 펑션이라고 보면 된다. 

debounceMakeApiRequest()펑션은 일반적으로 소스 텍스트 박스에 번역하고자 하는 텍스트를 넣은 경우에 사용되지는 않는다.

debounceMakeApiRequest()펑션은 MS Word에디터 안에서 일어나는 셀렉션의 변경 이벤트에 의해 발생하는 getSelect()내에서만 호출이 된다. 이는 드래그를 하여 텍스트를 가져오는 것이 run with promise로 비동기적으로 수행되기 때문에 짧은 시간 내에 여러번 드래그하거나 셀렉션한 영역을 변경할 경우 번역이 여러번 수행될 수 있기 때문에 디바운스를 걸어두었다.

translator.js 중 debounceMakeApiRequest() 펑션

				
					function debounceMakeApiRequest() {
  clearTimeout(debounceTimeout);
  console.log("debounce!");
  debounceTimeout = setTimeout(() => {
      makeApiRequest();
  }, 1000); // 1초 지연
}
				
			

translator.js 중 makeApiRequest() 펑션

MakeApiRequest();는 번역을 수행하는 API를 수행시키는 펑션이다.

첫번째는 소스 텍스트 박스에 들어있는 텍스트를(직접 입력했거나, 셀렉션으로 가져왔거나) sourceText로 지정하는 것이다.

첫번째 if문은 소스텍스트가 비어있는 경우에 단순히 return함으로써 번역 API가 수행되지 않도록 하는 것이다.

requestedSourceLanguageId는 handleTranslation()펑션을 수행하는데, 해당 펑션은 자동감지되거나 선택되어있는 소스 언어의 ISO 3자리 값을 의미한다.

isSupportedPatent는 선택된 소스 및 타겟 언어쌍이 특허번역기에 포함되는지 확인하기 위해 존재한다.

중간에 try로 시작하여 각종 에러 모달을 띄우는 코드가 있는데 잘못된 언어쌍이거나 잘못된 엔진을 사용할 경우 띄우도록 설계되었다.

firstResponse는 번역 잡을 요청하는 과정으로, 번역 엔진, 텍스트, 소스언어, 타겟언어를 지정하여 잡 생성 콜을 한다.

api.post에서 api는 인터셉터로, 토큰이 유효한지 확인하며 axios를 사용해 베링랩 번역 api를 호출한다.

이후, eventSource를 통해 서버로 부터 번역 결과를 받게 된다. 번역 결과물을 받거나 실패한 경우, 유저의 컨트랙트 정보를 확인하고 사용량을 확인하는 코드가 수행되도록 되어있다. 

 

translator.js 중 makeApiRequest() 펑션

				
					
async function makeApiRequest() {
  const sourceText = Bering_TextField_Source.textContent.trim();
  if (!sourceText) {
    return;
  }
  const requestedSourceLanguageId = handleTranslation();
  const isSupportedPatent = supportedPairsPatent.some(pair =>
    (pair[0] === requestedSourceLanguageId && pair[1] === selectedTargetLanguageId) ||
    (pair[0] === selectedTargetLanguageId && pair[1] === requestedSourceLanguageId)
  );

  try {
    const abortController = new AbortController();
    if (requestedSourceLanguageId !== "eng" && selectedTargetLanguageId !== "eng") {
      document.getElementById('warningModal').classList.add('modal-show');
    }
    else if (engine === "patent" && isSupportedPatent==false) {
      document.getElementById('warningModalPatent').classList.add('modal-show');
    }
    else if (requestedSourceLanguageId == selectedTargetLanguageId) {
      document.getElementById('warningModalSame').classList.add('modal-show');
    }
    else{
      const firstResponse = await api.post(
        textTranslatorUrlPost, 
        {
          domain: engine,
          source_content: sourceText,
          source_language: requestedSourceLanguageId,
          target_language: selectedTargetLanguageId,
        },
        {
          headers: {
            'Content-Type': 'application/json',
          },
          signal: abortController.signal,
        }
      );

      console.log('firstResponse:', firstResponse);
      const translationJobId = firstResponse.data;
      document.querySelector('.loader').classList.add('visible');
      console.log('translationJobId:', translationJobId);

      const eventSource = initializeEventSource(token, translationJobId);
    
      eventSource.onmessage = (event) => {
        const eventData = JSON.parse(event.data);
        console.log('Received SSE message:', eventData);
        handleApiResponse(eventData);
      };

      eventSource.onerror = (event) => {
        document.querySelector('.loader').classList.remove('visible');
        console.error('SSE error:', event);
        eventSource.close();
        checkContract(token)
        .then(data => {
          console.log(data);
          free_quota = 999999999999;
          var quota = Math.floor(data.quota);
          if(quota > 5000) {
            free_quota = 999999999999;
            console.log('Not Free!');
            return;
          }
          })
        .catch(error => {
          checkUsage(token)
          .then(data => {
            console.log("????????????????????????????????");
            free_usage = data;
          })
          console.log(error);
          free_quota = 5000;
          console.log('Free!:', error);
          console.log('Free!U!:', free_usage);
          console.log('Free!Q!:', free_quota);
          document.getElementById('warningModalFreeTier').classList.add('modal-show');      
  });
      };
    }

  } catch (error) {
    handleApiError(error);
  }
}

				
			

handleTranslation() 펑션

해당 펑션은, 자동 감지 된 소스 언어의 코드값 또는 지정된 소스 언어 코드값을 확인하고 그에 따라 소스 언어 드롭다운의 스타일을 변경하고 코드 값을 makeApiRequest()에 리턴한다.

translator.js 중 handleTranslation() 펑션

				
					function handleTranslation() {
  const francLanguage = franc(Bering_TextField_Source.textContent, options);
  const translateFrom = document.getElementById('translate_from');

  if (selectedSourceLanguageId == undefined) {
    translateFrom.style.display = 'none';
    handleDetectedLanguage(francLanguage);
    return francLanguage;
  } else {
    //const languageTwoName = languageTwoCodes[francLanguage] || "Unknown Language";
    const languageName = languageCodes[francLanguage] || "Unknown Language";
    if (languageName !== "Unknown Language"){
    if (selectedSourceLanguageId != francLanguage) {
      var sourceDetectedLanguage = document.getElementById('source_detected_language');
      sourceDetectedLanguage.textContent = languageName;
      translateFrom.style.display = 'flex';
    }
    }

    var detected_language = document.getElementById("language_detected");
    detected_language.textContent = detected_language.dataset.originalText = "";
    return selectedSourceLanguageId;
  }
}

				
			

handleDetectedLanguage() 펑션

해당 펑션은, 자동감지 상태의 경우 소스언어 드롭다운 텍스트 영역에 {감지된 언어의 이름 + “- detected”}로 노출되게 하는 코드이다.

translator.js 중 handleDetectedLanguage() 펑션

				
					function handleTranslation() {
  const francLanguage = franc(Bering_TextField_Source.textContent, options);
  const translateFrom = document.getElementById('translate_from');

  if (selectedSourceLanguageId == undefined) {
    translateFrom.style.display = 'none';
    handleDetectedLanguage(francLanguage);
    return francLanguage;
  } else {
    //const languageTwoName = languageTwoCodes[francLanguage] || "Unknown Language";
    const languageName = languageCodes[francLanguage] || "Unknown Language";
    if (languageName !== "Unknown Language"){
    if (selectedSourceLanguageId != francLanguage) {
      var sourceDetectedLanguage = document.getElementById('source_detected_language');
      sourceDetectedLanguage.textContent = languageName;
      translateFrom.style.display = 'flex';
    }
    }

    var detected_language = document.getElementById("language_detected");
    detected_language.textContent = detected_language.dataset.originalText = "";
    return selectedSourceLanguageId;
  }
}

				
			

handleApiResponse() 펑션

해당 펑션은 결과적으로 Bering_TextField_Target.innerHTML=combinedSegments; 를 통해서 API를 통해 받은 결과 텍스트를 화면애 박아주는 것이라고 보면 된다.

translator.js 중 handleApiRequest() 펑션

				
					function handleApiResponse(response) {
  console.log('Received SSE message:', response);

  if (response.segments) {
    // 모든 target_segment 값을 줄바꿈으로 구분된 하나의 문자열로 만듦
    const combinedSegments = response.segments
      .map(segment => segment.target_segment.replace(/\n/g, '<br>'))
      .join('');

    // 줄바꿈이 포함된 텍스트를 innerHTML을 통해 설정
    Bering_TextField_Target.innerHTML = combinedSegments;
  } else {
    console.error('Invalid response structure:', response);
  }
  
}
				
			

Published