DeepL 번역
대표적인 번역 API로는 Google Translate API, Microsoft Azure Translator, 그리고 최근 많이 사용되는 DeepL API가 있다.
솔직히, 아직까지는 DeepL이 다른 번역 API들보다 더 우수한 번역 결과를 보여주는 것 같다. 이번 시간에는 DeepL API를 스프링 부트에 적용하여 번역 기능을 추가해보자.
DeepL API Key 발급
우선 아래 사이트로 접속한다.
회원가입을 한 후에, 위 화면에서 DeepL API Free 플랜을 신청한다.
위와 같은 화면이 나오는데, 요금이 발생하지는 않는다. 무료 플랜의 경우 500,000자까지 이용할 수 있다.
무료 플랜 구독을 완료한 후에, 내 계정에 들어가서 위의 API키를 복사한다.
Deepl-mock
Deepl-mock은 DeepL API를 모방하여 애플리케이션 테스트를 간소화하기 위한 모의 서버이다. 이를 통해 실제 API 호출을 하지 않고도 DeepL API와의 상호작용을 테스트할 수 있으며, 프록시 서버도 포함되어 있어 프록시 사용 테스트도 가능하게 해준다.
위의 리포지토리를 클론 받은 후에 도커로 띄우거나 npm으로 실행하면 된다. 도커로 띄어보자.
$ docker image build -t deepl/deepl-mock .
$ docker run -d --rm --name deepl-mock -p3000:3000 -p3001:3001 deepl/deepl-mock
프로젝트 구성
build.gradle.kts
gradle 파일에 deepL라이브러리를 추가해준다.
implementation("com.deepl.api:deepl-java:1.6.0")
TranslateController.kt
@RestController
@RequestMapping("/api/v1/translate")
class TranslateController(
private val translateService: TranslateService
) {
@PostMapping
fun translate(@RequestBody translateRequest: TranslateRequest): String {
return translateService.translate(translateRequest)
}
}
TranslateRequest.kt
data class TranslateRequest(
val text: String,
val targetLang: LanguageCode
)
TranslateService.kt
@Service
class TranslateService(
private val translator: Translator = Translator(
"여기에 발급 받은 DeepL API Key 값을 입력. 절대 외부(깃허브 등)에 노출 금지!!",
TranslatorOptions().setServerUrl("http://localhost:3000") // deepL-mock을 연결하기 위한 로컬주소. 제거 예정
.setSendPlatformInfo(false) // DeepL로 내 어플리케이션 정보를 보내지 않음
),
) {
fun translate(text: String, targetLang: LanguageCode): String {
return translator.translateText(
text, null, targetLang.code, TextTranslationOptions().setFormality(Formality.PreferMore)
).text
}
}
enum class LanguageCode(val code: String) {
ENGLISH("en-US"),
KOREAN("ko"),
}
테스트 결과
이제 서버를 실행 후, 호출을 해보면 아래와 같이 `양성자 빔`값(테스트용 고정 값)을 리턴한다.
DeepL API 호출이 정상적으로 동작하는 것을 확인했으므로, 이제 실제 API를 호출할 수 있도록 `TranslateService.kt` 코드에서 `.setServerUrl("http://localhost:3000")`를 제거한다.
결과
번역이 잘 된것을 확인 할 수 있다. 그러나, 첫 번째 단어인 `Hooks`는 `후크`로 번역하기 보다는 `Hooks`그대로 두거나, `훅`으로 번역되기를 원한다면. DeepL의 glossary 기능을 사용하여 특정 단어를 사용자 정의할 수 있다.
Glossary 기능 추가
Glossary기능을 통해 커스텀 용어집을 만들어보자. 우선 이전 코드를 수정해야한다.
TranslateRequest.kt
data class TranslateRequest(
val text: String,
val targetLang: Lang,
val sourceLang: Lang,
val glossaryId: String?
)
TranslateService.kt
@Service
class TranslateService(
private val translator: Translator = Translator(
"여기에 발급 받은 DeepL API Key 값을 입력. 절대 외부(깃허브 등)에 노출 금지!!",
TranslatorOptions().setSendPlatformInfo(false) // DeepL로 내 어플리케이션 정보를 보내지 않음
),
) {
fun translate(translateRequest: TranslateRequest): String {
val translationOptions = TextTranslationOptions().setFormality(Formality.PreferMore)
// glossaryId가 있으면 glossary를 사용하겠다는 의미
translateRequest.glossaryId?.let { translationOptions.setGlossary(it) }
val text = translator.translateText(
translateRequest.text,
// Glossary 기능을 설정하면 sourceLang을 꼭 지정을 해야한다.
translateRequest.sourceLang.sourceCode,
translateRequest.targetLang.targetCode,
translationOptions
).text
return text
}
fun createGlossary(createGlossaryRequest: CreateGlossaryRequest): CreateGlossaryResponse {
val createGlossary = translator.createGlossary(
createGlossaryRequest.glossaryName,
createGlossaryRequest.sourceLang.sourceCode,
createGlossaryRequest.targetLang.targetCode,
GlossaryEntries(createGlossaryRequest.entries)
)
return CreateGlossaryResponse(
glossaryId = createGlossary.glossaryId,
ready = createGlossary.isReady,
name = createGlossary.name,
sourceLang = createGlossary.sourceLang,
targetLang = createGlossary.targetLang,
creationTime = createGlossary.creationTime,
entryCount = createGlossary.entryCount
)
}
fun getGlossary(glossaryId: String): GlossaryInfo {
return translator.getGlossary(glossaryId)
}
}
// 영어의 경우 번역시 sourceCode일때와 targetCode일때 코드가 다르다.
// targetCode일때는 EN-US, EN-GB로 구분해서 써야한다.
enum class Lang(val sourceCode: String, val targetCode: String) {
ENGLISH("EN", "EN-US"),
KOREAN( "KO","KO"),
}
Glossary 적용 후, 테스트 결과
`Hook : 훅`으로 저장한 후 다시 번역을 시도해보니, 성공적으로 `Hooks`가 `훅`으로 번역되었다.
그런데...
Glossary를 사용할 때는 한계가 있다. Glossary 기능을 이용하려면 `sourceLang`이 `null`일 수 없어, 언어 감지 기능을 사용할 수 없다.
또한, `sourceLang`을 `ENGLISH`로 지정한 상태에서 번역을 진행하면, 아래와 같이 자연스럽지 않은 번역이 나올 수 있다.
`text`가 영어, 일본어, 중국어, 프랑스어 등 다양한 언어로 무작위로 입력되는 환경에서, 그 언어에 맞게 `sourceLang`을 자동으로 변경하고 싶지만, 이를 자동화하기가 쉽지 않다. 결국 둘 중에 하나를 포기해야한다.
- glossary 기능을 포기
- `sourceLang`값 `null` (언어감지 기능)을 포기
glossary 기능을 포기하는 것이 좋을 것 같다.
REFERENCES
DeepL API 문서 - https://developers.deepl.com/docs/v/ko