diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/LanguageDetectEvent.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/LanguageDetectEvent.kt index 9a95002..6d125a2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/LanguageDetectEvent.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/LanguageDetectEvent.kt @@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.content import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentRepository import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterRepository import kr.co.vividnext.sodalive.chat.original.OriginalWorkRepository +import kr.co.vividnext.sodalive.content.category.CategoryRepository import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository import kr.co.vividnext.sodalive.content.series.ContentSeriesRepository import kr.co.vividnext.sodalive.explorer.profile.CreatorCheersRepository @@ -34,7 +35,9 @@ enum class LanguageDetectTargetType { CHARACTER_COMMENT, CREATOR_CHEERS, SERIES, - ORIGINAL_WORK + ORIGINAL_WORK, + + CREATOR_CONTENT_CATEGORY } class LanguageDetectEvent( @@ -56,6 +59,7 @@ class LanguageDetectListener( private val creatorCheersRepository: CreatorCheersRepository, private val seriesRepository: ContentSeriesRepository, private val originalWorkRepository: OriginalWorkRepository, + private val categoryRepository: CategoryRepository, private val applicationEventPublisher: ApplicationEventPublisher, @@ -89,6 +93,7 @@ class LanguageDetectListener( LanguageDetectTargetType.CREATOR_CHEERS -> handleCreatorCheersLanguageDetect(event) LanguageDetectTargetType.SERIES -> handleSeriesLanguageDetect(event) LanguageDetectTargetType.ORIGINAL_WORK -> handleOriginalWorkLanguageDetect(event) + LanguageDetectTargetType.CREATOR_CONTENT_CATEGORY -> handleCreatorContentCategoryLanguageDetect(event) } } @@ -341,6 +346,25 @@ class LanguageDetectListener( ) } + private fun handleCreatorContentCategoryLanguageDetect(event: LanguageDetectEvent) { + val categoryId = event.id + + val category = categoryRepository.findByIdOrNull(categoryId) ?: return + if (!category.languageCode.isNullOrBlank()) return + + val langCode = requestPapagoLanguageCode(event.query, categoryId) ?: return + + category.languageCode = langCode + categoryRepository.save(category) + + applicationEventPublisher.publishEvent( + LanguageTranslationEvent( + id = categoryId, + targetType = LanguageTranslationTargetType.CREATOR_CONTENT_CATEGORY + ) + ) + } + private fun requestPapagoLanguageCode(query: String, targetIdForLog: Long): String? { return try { val headers = HttpHeaders().apply { diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/Category.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/Category.kt index 6125131..248c86d 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/Category.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/Category.kt @@ -8,9 +8,10 @@ import javax.persistence.JoinColumn import javax.persistence.ManyToOne @Entity -data class Category( +class Category( var title: String, var orders: Int = 1, + var languageCode: String? = null, var isActive: Boolean = true ) : BaseEntity() { @ManyToOne(fetch = FetchType.LAZY) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryService.kt index 00c6c9a..5c91b2a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryService.kt @@ -2,8 +2,13 @@ package kr.co.vividnext.sodalive.content.category import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.content.AudioContentRepository +import kr.co.vividnext.sodalive.content.LanguageDetectEvent +import kr.co.vividnext.sodalive.content.LanguageDetectTargetType +import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationEvent +import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationTargetType import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.block.BlockMemberRepository +import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -12,7 +17,9 @@ class CategoryService( private val repository: CategoryRepository, private val contentRepository: AudioContentRepository, private val blockMemberRepository: BlockMemberRepository, - private val categoryContentRepository: CategoryContentRepository + private val categoryContentRepository: CategoryContentRepository, + + private val applicationEventPublisher: ApplicationEventPublisher ) { @Transactional fun createCategory(request: CreateCategoryRequest, member: Member) { @@ -40,6 +47,14 @@ class CategoryService( ) categoryContent.isActive = true } + + applicationEventPublisher.publishEvent( + LanguageDetectEvent( + id = category.id!!, + query = request.title, + targetType = LanguageDetectTargetType.CREATOR_CONTENT_CATEGORY + ) + ) } @Transactional @@ -50,6 +65,13 @@ class CategoryService( if (!request.title.isNullOrBlank()) { validateTitle(title = request.title) category.title = request.title + + applicationEventPublisher.publishEvent( + LanguageTranslationEvent( + id = request.categoryId, + targetType = LanguageTranslationTargetType.CREATOR_CONTENT_CATEGORY + ) + ) } for (contentId in request.addContentIdList) { diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryTranslation.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryTranslation.kt new file mode 100644 index 0000000..e743c6d --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryTranslation.kt @@ -0,0 +1,18 @@ +package kr.co.vividnext.sodalive.content.category + +import kr.co.vividnext.sodalive.common.BaseEntity +import javax.persistence.Entity +import javax.persistence.Table +import javax.persistence.UniqueConstraint + +@Entity +@Table( + uniqueConstraints = [ + UniqueConstraint(columnNames = ["categoryId", "locale"]) + ] +) +class CategoryTranslation( + val categoryId: Long, + val locale: String, + var category: String +) : BaseEntity() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryTranslationRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryTranslationRepository.kt new file mode 100644 index 0000000..92b8839 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/category/CategoryTranslationRepository.kt @@ -0,0 +1,7 @@ +package kr.co.vividnext.sodalive.content.category + +import org.springframework.data.jpa.repository.JpaRepository + +interface CategoryTranslationRepository : JpaRepository { + fun findByCategoryIdAndLocale(categoryId: Long, locale: String): CategoryTranslation? +} 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 30796a0..99e241e 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 @@ -13,6 +13,9 @@ import kr.co.vividnext.sodalive.chat.original.translation.OriginalWorkTranslatio import kr.co.vividnext.sodalive.chat.original.translation.OriginalWorkTranslationPayload import kr.co.vividnext.sodalive.chat.original.translation.OriginalWorkTranslationRepository import kr.co.vividnext.sodalive.content.AudioContentRepository +import kr.co.vividnext.sodalive.content.category.CategoryRepository +import kr.co.vividnext.sodalive.content.category.CategoryTranslation +import kr.co.vividnext.sodalive.content.category.CategoryTranslationRepository import kr.co.vividnext.sodalive.content.series.translation.SeriesGenreTranslation import kr.co.vividnext.sodalive.content.series.translation.SeriesGenreTranslationRepository import kr.co.vividnext.sodalive.content.series.translation.SeriesTranslation @@ -41,7 +44,9 @@ enum class LanguageTranslationTargetType { SERIES, SERIES_GENRE, - ORIGINAL_WORK + ORIGINAL_WORK, + + CREATOR_CONTENT_CATEGORY } class LanguageTranslationEvent( @@ -65,6 +70,9 @@ class LanguageTranslationListener( private val seriesGenreTranslationRepository: SeriesGenreTranslationRepository, private val originalWorkTranslationRepository: OriginalWorkTranslationRepository, + private val categoryRepository: CategoryRepository, + private val categoryTranslationRepository: CategoryTranslationRepository, + private val translationService: PapagoTranslationService ) { @Async @@ -78,6 +86,7 @@ class LanguageTranslationListener( LanguageTranslationTargetType.SERIES -> handleSeriesLanguageTranslation(event) LanguageTranslationTargetType.SERIES_GENRE -> handleSeriesGenreLanguageTranslation(event) LanguageTranslationTargetType.ORIGINAL_WORK -> handleOriginalWorkLanguageTranslation(event) + LanguageTranslationTargetType.CREATOR_CONTENT_CATEGORY -> handleCreatorContentCategoryLanguageTranslation(event) } } @@ -453,4 +462,45 @@ class LanguageTranslationListener( } } } + + private fun handleCreatorContentCategoryLanguageTranslation(event: LanguageTranslationEvent) { + val category = categoryRepository.findByIdOrNull(event.id) + + if (category == null || !category.isActive || category.languageCode.isNullOrBlank()) return + + val sourceLanguage = category.languageCode ?: "ko" + getTranslatableLanguageCodes(sourceLanguage).forEach { locale -> + val texts = mutableListOf() + texts.add(category.title) + + val response = translationService.translate( + request = TranslateRequest( + texts = texts, + sourceLanguage = sourceLanguage, + targetLanguage = locale + ) + ) + + val translatedTexts = response.translatedText + if (translatedTexts.size == texts.size) { + val translatedCategory = translatedTexts[0] + + val existing = categoryTranslationRepository + .findByCategoryIdAndLocale(category.id!!, locale) + + if (existing == null) { + categoryTranslationRepository.save( + CategoryTranslation( + categoryId = category.id!!, + locale = locale, + category = translatedCategory + ) + ) + } else { + existing.category = translatedCategory + categoryTranslationRepository.save(existing) + } + } + } + } }