카테고리 목록 조회에 언어별 번역 적용
LangContext에 따라 카테고리명을 번역해 반환한다. 번역본이 없으면 Papago API로 번역 후 CategoryTranslation에 저장하고 즉시 결과를 반환한다. 공개 API의 getCategoryList 응답이 요청 로케일을 반영한다.
This commit is contained in:
@@ -4,8 +4,11 @@ import kr.co.vividnext.sodalive.common.SodaException
|
|||||||
import kr.co.vividnext.sodalive.content.AudioContentRepository
|
import kr.co.vividnext.sodalive.content.AudioContentRepository
|
||||||
import kr.co.vividnext.sodalive.content.LanguageDetectEvent
|
import kr.co.vividnext.sodalive.content.LanguageDetectEvent
|
||||||
import kr.co.vividnext.sodalive.content.LanguageDetectTargetType
|
import kr.co.vividnext.sodalive.content.LanguageDetectTargetType
|
||||||
|
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||||
import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationEvent
|
import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationEvent
|
||||||
import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationTargetType
|
import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationTargetType
|
||||||
|
import kr.co.vividnext.sodalive.i18n.translation.PapagoTranslationService
|
||||||
|
import kr.co.vividnext.sodalive.i18n.translation.TranslateRequest
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
|
import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
|
||||||
import org.springframework.context.ApplicationEventPublisher
|
import org.springframework.context.ApplicationEventPublisher
|
||||||
@@ -18,8 +21,11 @@ class CategoryService(
|
|||||||
private val contentRepository: AudioContentRepository,
|
private val contentRepository: AudioContentRepository,
|
||||||
private val blockMemberRepository: BlockMemberRepository,
|
private val blockMemberRepository: BlockMemberRepository,
|
||||||
private val categoryContentRepository: CategoryContentRepository,
|
private val categoryContentRepository: CategoryContentRepository,
|
||||||
|
private val categoryTranslationRepository: CategoryTranslationRepository,
|
||||||
|
|
||||||
private val applicationEventPublisher: ApplicationEventPublisher
|
private val langContext: LangContext,
|
||||||
|
private val applicationEventPublisher: ApplicationEventPublisher,
|
||||||
|
private val translationService: PapagoTranslationService
|
||||||
) {
|
) {
|
||||||
@Transactional
|
@Transactional
|
||||||
fun createCategory(request: CreateCategoryRequest, member: Member) {
|
fun createCategory(request: CreateCategoryRequest, member: Member) {
|
||||||
@@ -119,11 +125,83 @@ class CategoryService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
fun getCategoryList(creatorId: Long, memberId: Long): List<GetCategoryListResponse> {
|
fun getCategoryList(creatorId: Long, memberId: Long): List<GetCategoryListResponse> {
|
||||||
val isBlocked = blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = creatorId)
|
val isBlocked = blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = creatorId)
|
||||||
if (isBlocked) throw SodaException("잘못된 접근입니다.")
|
if (isBlocked) throw SodaException("잘못된 접근입니다.")
|
||||||
|
|
||||||
return repository.findByCreatorId(creatorId = creatorId)
|
// 기본 카테고리 목록 조회 (원본 언어 기준)
|
||||||
|
val baseList = repository.findByCreatorId(creatorId = creatorId)
|
||||||
|
if (baseList.isEmpty()) return baseList
|
||||||
|
|
||||||
|
val locale = langContext.lang.code
|
||||||
|
|
||||||
|
// 원본 엔티티를 조회하여 languageCode 파악
|
||||||
|
val categoryIds = baseList.map { it.categoryId }
|
||||||
|
val entities = repository.findAllById(categoryIds)
|
||||||
|
val entityMap = entities.associateBy { it.id!! }
|
||||||
|
|
||||||
|
// 요청 로케일로 이미 저장된 번역 일괄 조회
|
||||||
|
val translations = categoryTranslationRepository
|
||||||
|
.findByCategoryIdInAndLocale(categoryIds, locale)
|
||||||
|
.associateBy { it.categoryId }
|
||||||
|
|
||||||
|
// 각 항목에 대해 번역 적용. 없으면 Papago로 번역 저장 후 적용
|
||||||
|
val result = mutableListOf<GetCategoryListResponse>()
|
||||||
|
for (item in baseList) {
|
||||||
|
val entity = entityMap[item.categoryId]
|
||||||
|
if (entity == null) {
|
||||||
|
result.add(item)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val sourceLang = entity.languageCode
|
||||||
|
if (!sourceLang.isNullOrBlank() && sourceLang != locale) {
|
||||||
|
val existing = translations[item.categoryId]
|
||||||
|
if (existing != null && !existing.category.isNullOrBlank()) {
|
||||||
|
result.add(GetCategoryListResponse(categoryId = item.categoryId, category = existing.category))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 번역본이 없으면 Papago 번역 후 저장
|
||||||
|
val texts = listOf(entity.title)
|
||||||
|
val response = translationService.translate(
|
||||||
|
request = TranslateRequest(
|
||||||
|
texts = texts,
|
||||||
|
sourceLanguage = sourceLang,
|
||||||
|
targetLanguage = locale
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val translatedTexts = response.translatedText
|
||||||
|
if (translatedTexts.size == texts.size) {
|
||||||
|
val translatedCategory = translatedTexts[0]
|
||||||
|
|
||||||
|
val existingOne = categoryTranslationRepository
|
||||||
|
.findByCategoryIdAndLocale(entity.id!!, locale)
|
||||||
|
if (existingOne == null) {
|
||||||
|
categoryTranslationRepository.save(
|
||||||
|
CategoryTranslation(
|
||||||
|
categoryId = entity.id!!,
|
||||||
|
locale = locale,
|
||||||
|
category = translatedCategory
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
existingOne.category = translatedCategory
|
||||||
|
categoryTranslationRepository.save(existingOne)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(GetCategoryListResponse(categoryId = item.categoryId, category = translatedCategory))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 번역이 필요 없거나 실패한 경우 원본 사용
|
||||||
|
result.add(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateTitle(title: String) {
|
private fun validateTitle(title: String) {
|
||||||
|
|||||||
@@ -4,4 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository
|
|||||||
|
|
||||||
interface CategoryTranslationRepository : JpaRepository<CategoryTranslation, Long> {
|
interface CategoryTranslationRepository : JpaRepository<CategoryTranslation, Long> {
|
||||||
fun findByCategoryIdAndLocale(categoryId: Long, locale: String): CategoryTranslation?
|
fun findByCategoryIdAndLocale(categoryId: Long, locale: String): CategoryTranslation?
|
||||||
|
|
||||||
|
fun findByCategoryIdInAndLocale(categoryIds: Collection<Long>, locale: String): List<CategoryTranslation>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user