feat(i18n): AWS 언어 감지와 분할 번역 공급자를 추가한다

This commit is contained in:
2026-11-10 10:23:32 +09:00
parent a8e4cbac28
commit 7cac09e39c
8 changed files with 585 additions and 2 deletions
@@ -0,0 +1,27 @@
package kr.co.vividnext.sodalive.i18n.translation
import org.springframework.stereotype.Component
import software.amazon.awssdk.services.comprehend.ComprehendClient
import software.amazon.awssdk.services.comprehend.model.DetectDominantLanguageRequest
@Component
class AwsLanguageDetectionProvider(private val client: ComprehendClient) : LanguageDetectionProvider {
override val providerId = LanguageProviderId.AWS
override fun detect(query: String): String? {
if (query.isBlank()) return null
return try {
// shortcut: 감지는 앞 100000 UTF-8 바이트만 판정하므로 전체 본문과 결과가 다를 수 있음, 전체 본문 기준 품질이 요구되면 별도 감지 정책 승인 후 업그레이드
val text = takeTextPrefixByCodePoint(query, MAX_REQUEST_TEXT_BYTES, ::utf8CodePointSize)
val request = DetectDominantLanguageRequest.builder().text(text).build()
client.detectDominantLanguage(request).languages().firstOrNull()?.languageCode()?.takeIf { it.isNotBlank() }
} catch (_: Exception) {
null
}
}
companion object {
private const val MAX_REQUEST_TEXT_BYTES = 100000
}
}
@@ -0,0 +1,32 @@
package kr.co.vividnext.sodalive.i18n.translation
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.comprehend.ComprehendClient
import software.amazon.awssdk.services.translate.TranslateClient
@Configuration
class AwsLanguageProviderConfiguration(
@Value("\${cloud.aws.credentials.access-key}") accessKey: String,
@Value("\${cloud.aws.credentials.secret-key}") secretKey: String,
@Value("\${cloud.aws.region.static}") region: String
) {
private val credentialsProvider = StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey))
private val region = Region.of(region)
@Bean(destroyMethod = "close")
fun comprehendClient(): ComprehendClient = ComprehendClient.builder()
.credentialsProvider(credentialsProvider)
.region(region)
.build()
@Bean(destroyMethod = "close")
fun translateClient(): TranslateClient = TranslateClient.builder()
.credentialsProvider(credentialsProvider)
.region(region)
.build()
}
@@ -0,0 +1,48 @@
package kr.co.vividnext.sodalive.i18n.translation
import org.springframework.stereotype.Service
import software.amazon.awssdk.services.translate.TranslateClient
import software.amazon.awssdk.services.translate.model.TranslateTextRequest
@Service
class AwsTranslationService(private val client: TranslateClient) : TranslationProvider {
override val providerId = LanguageProviderId.AWS
override val providerVersion = "translate-2017-07-01"
override fun translate(request: TranslateRequest): TranslateResult {
if (request.texts.isEmpty() || request.sourceLanguage == request.targetLanguage) {
return TranslateResult(emptyList())
}
if (
!TranslationLanguages.isSupported(request.sourceLanguage) ||
!TranslationLanguages.isSupported(request.targetLanguage)
) {
return TranslateResult(emptyList())
}
val translatedTexts = mutableListOf<String>()
try {
for (text in request.texts) {
val translatedText = StringBuilder()
for (chunk in splitTextByCodePoint(text, MAX_REQUEST_TEXT_BYTES, ::utf8CodePointSize)) {
val sdkRequest = TranslateTextRequest.builder()
.sourceLanguageCode(request.sourceLanguage)
.targetLanguageCode(request.targetLanguage)
.text(chunk)
.build()
val translated = client.translateText(sdkRequest)?.translatedText()
if (translated.isNullOrBlank()) return TranslateResult(emptyList())
translatedText.append(translated)
}
translatedTexts.add(translatedText.toString())
}
} catch (_: Exception) {
return TranslateResult(emptyList())
}
return TranslateResult(translatedTexts)
}
companion object {
private const val MAX_REQUEST_TEXT_BYTES = 10000
}
}