From 6f0619e482c7a353fecb8a2d576fad061a53b301 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 13 Dec 2025 00:19:48 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=ED=85=8C?= =?UTF-8?q?=EB=A7=88=20=EC=A0=80=EC=9E=A5=EC=8B=9C=20=EB=B2=88=EC=97=AD=20?= =?UTF-8?q?API=EB=A1=9C=20=EC=9E=90=EB=8F=99=20=EB=B2=88=EC=97=AD=20?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/theme/AdminContentThemeService.kt | 14 +++++- .../content/theme/AudioContentTheme.kt | 2 +- .../translation/ContentThemeTranslation.kt | 11 +++++ .../ContentThemeTranslationRepository.kt | 7 +++ .../translation/LanguageTranslationEvent.kt | 48 ++++++++++++++++++- 5 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/content/theme/translation/ContentThemeTranslation.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/content/theme/translation/ContentThemeTranslationRepository.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/theme/AdminContentThemeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/theme/AdminContentThemeService.kt index 00e6237..896f636 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/theme/AdminContentThemeService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/theme/AdminContentThemeService.kt @@ -5,8 +5,11 @@ import kr.co.vividnext.sodalive.aws.s3.S3Uploader import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.content.theme.AudioContentTheme import kr.co.vividnext.sodalive.content.theme.GetAudioContentThemeResponse +import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationEvent +import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationTargetType import kr.co.vividnext.sodalive.utils.generateFileName import org.springframework.beans.factory.annotation.Value +import org.springframework.context.ApplicationEventPublisher import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -18,6 +21,8 @@ class AdminContentThemeService( private val objectMapper: ObjectMapper, private val repository: AdminContentThemeRepository, + private val applicationEventPublisher: ApplicationEventPublisher, + @Value("\${cloud.aws.s3.bucket}") private val bucket: String ) { @@ -37,7 +42,14 @@ class AdminContentThemeService( } fun createTheme(theme: String, imagePath: String) { - repository.save(AudioContentTheme(theme = theme, image = imagePath)) + val savedTheme = repository.save(AudioContentTheme(theme = theme, image = imagePath)) + + applicationEventPublisher.publishEvent( + LanguageTranslationEvent( + id = savedTheme.id!!, + targetType = LanguageTranslationTargetType.CONTENT_THEME + ) + ) } fun themeExistCheck(request: CreateContentThemeRequest) { diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentTheme.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentTheme.kt index 621b444..6b6b7c0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentTheme.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentTheme.kt @@ -7,7 +7,7 @@ import javax.persistence.Table @Entity @Table(name = "content_theme") -data class AudioContentTheme( +class AudioContentTheme( @Column(nullable = false) var theme: String, @Column(nullable = false) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/translation/ContentThemeTranslation.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/translation/ContentThemeTranslation.kt new file mode 100644 index 0000000..ec61103 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/translation/ContentThemeTranslation.kt @@ -0,0 +1,11 @@ +package kr.co.vividnext.sodalive.content.theme.translation + +import kr.co.vividnext.sodalive.common.BaseEntity +import javax.persistence.Entity + +@Entity +class ContentThemeTranslation( + val contentThemeId: Long, + val locale: String, + var theme: String +) : BaseEntity() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/translation/ContentThemeTranslationRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/translation/ContentThemeTranslationRepository.kt new file mode 100644 index 0000000..7bee3c0 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/translation/ContentThemeTranslationRepository.kt @@ -0,0 +1,7 @@ +package kr.co.vividnext.sodalive.content.theme.translation + +import org.springframework.data.jpa.repository.JpaRepository + +interface ContentThemeTranslationRepository : JpaRepository { + fun findByContentThemeIdAndLocale(contentThemeId: Long, locale: String): ContentThemeTranslation? +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageTranslationEvent.kt b/src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageTranslationEvent.kt index f0ba1cf..62db600 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageTranslationEvent.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageTranslationEvent.kt @@ -7,6 +7,9 @@ import kr.co.vividnext.sodalive.chat.character.translate.AiCharacterTranslationR import kr.co.vividnext.sodalive.chat.character.translate.TranslatedAiCharacterBackground import kr.co.vividnext.sodalive.chat.character.translate.TranslatedAiCharacterPersonality import kr.co.vividnext.sodalive.content.AudioContentRepository +import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository +import kr.co.vividnext.sodalive.content.theme.translation.ContentThemeTranslation +import kr.co.vividnext.sodalive.content.theme.translation.ContentThemeTranslationRepository import kr.co.vividnext.sodalive.content.translation.ContentTranslation import kr.co.vividnext.sodalive.content.translation.ContentTranslationPayload import kr.co.vividnext.sodalive.content.translation.ContentTranslationRepository @@ -21,7 +24,8 @@ import org.springframework.transaction.event.TransactionalEventListener enum class LanguageTranslationTargetType { CONTENT, - CHARACTER + CHARACTER, + CONTENT_THEME } class LanguageTranslationEvent( @@ -33,9 +37,11 @@ class LanguageTranslationEvent( class LanguageTranslationListener( private val audioContentRepository: AudioContentRepository, private val chatCharacterRepository: ChatCharacterRepository, + private val audioContentThemeRepository: AudioContentThemeQueryRepository, private val contentTranslationRepository: ContentTranslationRepository, private val aiCharacterTranslationRepository: AiCharacterTranslationRepository, + private val contentThemeTranslationRepository: ContentThemeTranslationRepository, private val translationService: PapagoTranslationService ) { @@ -46,6 +52,7 @@ class LanguageTranslationListener( when (event.targetType) { LanguageTranslationTargetType.CONTENT -> handleContentLanguageTranslation(event) LanguageTranslationTargetType.CHARACTER -> handleCharacterLanguageTranslation(event) + LanguageTranslationTargetType.CONTENT_THEME -> handleContentThemeLanguageTranslation(event) } } @@ -202,4 +209,43 @@ class LanguageTranslationListener( } } } + + private fun handleContentThemeLanguageTranslation(event: LanguageTranslationEvent) { + val contentTheme = audioContentThemeRepository.findThemeByIdAndActive(event.id) ?: return + + val sourceLanguage = "ko" + getTranslatableLanguageCodes(sourceLanguage).forEach { locale -> + val texts = mutableListOf() + texts.add(contentTheme.theme) + + val response = translationService.translate( + request = TranslateRequest( + texts = texts, + sourceLanguage = sourceLanguage, + targetLanguage = locale + ) + ) + + val translatedTexts = response.translatedText + if (translatedTexts.size == texts.size) { + val translatedTheme = translatedTexts[0] + + val existing = contentThemeTranslationRepository + .findByContentThemeIdAndLocale(contentTheme.id!!, locale) + + if (existing == null) { + contentThemeTranslationRepository.save( + ContentThemeTranslation( + contentThemeId = contentTheme.id!!, + locale = locale, + theme = translatedTheme + ) + ) + } else { + existing.theme = translatedTheme + contentThemeTranslationRepository.save(existing) + } + } + } + } }