DeepL 번역이 성능이 썩 괜찮고 가끔 써봄직 하여 봇에 추가해 봤다.
1. 번역 API 선정
사실 처음부터 DeepL을 사용하기로 한 건 아니었다.
후보로 여러 번역기들을 생각해 봤다.
- GPT
비싸다. 그냥 비싸다. 그리고 번역이 메인인 모델이 아니기 때문에 번역 품질의 신뢰성이 가장 높다고 보기 힘들다. - Papago
이미 kd()를 위해 사용하고 있다. 제한 토큰 수가 적기 때문에 패스. - Google Translate
번역 품질이 나쁜 것은 아니지만, 마냥 좋다고도 할 수 없고 돈을 내지 많으면 사실상 사용하는 것이 무의미한 수준이다. - DeepL
그나마 무료 플랜으로도 제한이 널널하고 번역 품질도 생각보다 괜찮아서 낙점했다.
그래서 DeepL의 홈페이지로 가서 등록하려니...
국내 카드를 안 먹는다.
해외 결제가 가능한 카드임에도 불구 대한민국에서 발행된 카드를 일절 받지 않는다.
이걸 어찌 우회해 보기 위해 구글링을 거듭하던 찰나...
2. Rapid API
Rapid API 이라는 사이트를 발견했다.
비단 DeepL API 뿐만 아니라 여러 유용한 API를 손쉽게 사용할 수 있게 제공하는 사이트이다.
DeepL API의 가격 정책을 확인해 보자.
사실 월 요청 수 제한이 100 인건 매우 매우 아쉽다.
1회에 요청하는 스트링의 길이가 짧을수록 손해를 보는 구조긴 하지만, 많이 사용할 것도 아니기 때문에 OK.
5000 캐릭터를 꽉 채워서 100번이나 요청할 수 있다는 게 오히려 괜찮을 수도 있다.
이 사이트의 좋은 점이라면 아래와 같이 즉시 사용할 수 있는 코드를 제공한다는 것이다.
아주 맘에 들었다. 응답의 구조도 제공하니 필요한 정보는 충분히 제공한다고 할 수 있겠다.
3. 가공
이미 사이트에서 다 줘버려서 할 게 없긴 하다만, 몇 가지 가공을 통해 아래의 형태로 만들었다.
import requests
import json
with open("./keys.json", 'r') as f:
cfg = json.load(f)
rapidapi_key = cfg["RapidAPI"]["RapidAPI_Key"]
f.close()
class deepl_translator:
async def dl_trans(src, dst, query):
url = "https://deepl-translator.p.rapidapi.com/translate"
payload = {
"text": query,
"source": src,
"target": dst
}
headers = {
"content-type": "application/json",
"X-RapidAPI-Key": rapidapi_key,
"X-RapidAPI-Host": "deepl-translator.p.rapidapi.com"
}
response = requests.post(url, json=payload, headers=headers)
result = response.json()['text']
return result
결과만 뽑아먹을 수 있게 ['text']만 리턴 시킨다.
봇에 사용할 명령어를 아래와 같이 작성한다.
@client.tree.command()
@app_commands.describe(src='출발 언어. 미입력시 자동으로 언어 감지.', dst='도착 언어. 미입력시 한국어로 간주. DE, EN, ES, FR, IT, JA 등 입력 가능', query='번역할 내용')
async def deepl(interaction: discord.Interaction, query: str, src: str=None, dst: str=None):
"""DeepL을 사용해 텍스트 번역. 자동으로 입력 언어를 감지합니다."""
# Discord message cannot contain over 2000 characters.
# We can send over 5000 characters as input of command,
# But cannot send result which is over 2000 characters.
# The following 3000 characters become meaningless.
# So, we need to slice the input query to 2000 characters.
if len(query) > 2000:
await interaction.response.send_message("번역할 내용이 너무 길어요. 2000자 이하로 입력해 주세요.", ephemeral=True)
lg.info(f"{interaction.user.display_name} requested deepl() with over 2000 characters.")
return
# DeepL support Auto-Detect
if src is None:
src = 'auto'
if dst is None:
dst = 'ko'
lg.info(f"{interaction.user.display_name} requested deepl().")
await interaction.response.defer(ephemeral=True)
tr_res_temp = await dl.dl_trans(src, dst, query)
result = f"DeepL Translation to {dst} \n\n{tr_res_temp}"
# Slice if over 2000 characters
if(len(result) >= 2000):
result = f"결과가 2000자를 초과하여 모든 결과를 표시할 수 없습니다. \n\n{result}"
result = result[:2000]
lg.info("Result message has exceeded 2000 characters; All of the message cannot be sent.")
await interaction.followup.send(result)
return
lg.info(f"The translation has completed successfully.")
await interaction.followup.send(result)
2000자가 넘으면 뱉어주고, 자동으로 입력 언어를 감지하게끔 했다.
디스코드 메세지는 2천자를 넘을 수 없다.
응답 레이턴시가 꽤 긴 관계로 defer()로 시간을 번 후, followup.send()로 결과를 보낸다.
defer()가 없으면 레이턴시로 인해 unknown interaction exception을 보게 될 것이다.
디스코드에서 생각하는 문자열의 길이와 len()이 생각하는 문자열의 길이의 차이는 고려하지 않았다.
둘이 차이가 없을 수도 있지 않을까?
4. 확인
기존 번역기로는 재미 보기 힘들었던 논문 번역으로 테스트해 보자.
대상 논문은 LLaMA: Open and Efficient Foundation Language Models로 정했다.
기존 번역기는 pdf에 있는 스트링을 그대로 복사해서 넣으면,
문장을 제대로 인식하지 못해 퀄리티가 많이 떨어지는 면이 있었다.
defer()를 통해 시간을 벌고 답도 잘 뱉어준다.
적용 자체는 성공적인 것 같다.
번역결과는 아래를 참고하라.
4.1 번역 결과
5. 마무리
이래저래 테스트한다고 요청을 많이 해서 달에 100번 밖에 안 되는 횟수의 많은 부분을 사용해 버렸다.
하지만 자주 사용할 기능은 아니니 문제는 없을 것이다.
'Study > Python' 카테고리의 다른 글
[Python] 봇 개선 및 커맨드 추가 (0) | 2023.10.16 |
---|---|
[Python] 웹 파싱 / 기존 기능 개선 (0) | 2023.08.11 |
[Python] 단위 변환 (0) | 2023.05.21 |
[Python / GPT] 봇 코드 리팩터링 (0) | 2023.05.17 |
[Python, AWS] csv에서 DynamoDB에로의 교체 (0) | 2023.05.10 |