관리자 채팅 메시지 다국어 처리

This commit is contained in:
2025-12-22 22:30:05 +09:00
parent 14d0ae9851
commit 280b21c3cb
10 changed files with 231 additions and 50 deletions

View File

@@ -13,6 +13,8 @@ import kr.co.vividnext.sodalive.aws.s3.S3Uploader
import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterBannerService
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
import kr.co.vividnext.sodalive.utils.generateFileName
import org.springframework.beans.factory.annotation.Value
import org.springframework.security.access.prepost.PreAuthorize
@@ -35,6 +37,8 @@ class AdminChatBannerController(
private val bannerService: ChatCharacterBannerService,
private val adminCharacterService: AdminChatCharacterService,
private val s3Uploader: S3Uploader,
private val langContext: LangContext,
private val messageSource: SodaMessageSource,
@Value("\${cloud.aws.s3.bucket}")
private val s3Bucket: String,
@@ -158,8 +162,8 @@ class AdminChatBannerController(
filePath = "characters/banners/$bannerId/$fileName",
metadata = metadata
)
} catch (e: Exception) {
throw SodaException("이미지 저장에 실패했습니다: ${e.message}")
} catch (_: Exception) {
throw SodaException(messageKey = "admin.chat.banner.image_save_failed")
}
}
@@ -208,7 +212,8 @@ class AdminChatBannerController(
fun deleteBanner(@PathVariable bannerId: Long) = run {
bannerService.deleteBanner(bannerId)
ApiResponse.ok("배너가 성공적으로 삭제되었습니다.")
val message = messageSource.getMessage("admin.chat.banner.delete_success", langContext.lang)
ApiResponse.ok(message)
}
/**
@@ -224,6 +229,7 @@ class AdminChatBannerController(
) = run {
bannerService.updateBannerOrders(request.ids)
ApiResponse.ok(null, "배너 정렬 순서가 성공적으로 변경되었습니다.")
val message = messageSource.getMessage("admin.chat.banner.reorder_success", langContext.lang)
ApiResponse.ok(null, message)
}
}

View File

@@ -29,13 +29,13 @@ class AdminChatCalculateService(
val todayKst = LocalDate.now(kstZone)
if (endDate.isAfter(todayKst)) {
throw SodaException("끝 날짜는 오늘 날짜까지만 입력 가능합니다.")
throw SodaException(messageKey = "admin.chat.calculate.end_date_max_today")
}
if (startDate.isAfter(endDate)) {
throw SodaException("시작 날짜는 끝 날짜보다 이후일 수 없습니다.")
throw SodaException(messageKey = "admin.chat.calculate.start_date_after_end")
}
if (endDate.isAfter(startDate.plusMonths(6))) {
throw SodaException("조회 가능 기간은 최대 6개월입니다.")
throw SodaException(messageKey = "admin.chat.calculate.max_period_6_months")
}
val startUtc = startDateStr.convertLocalDateTime()

View File

@@ -124,7 +124,7 @@ class AdminChatCharacterController(
// 외부 API 호출 전 DB에 동일한 이름이 있는지 조회
val existingCharacter = service.findByName(request.name)
if (existingCharacter != null) {
throw SodaException("동일한 이름은 등록이 불가능합니다: ${request.name}")
throw SodaException(messageKey = "admin.chat.character.duplicate_name")
}
// 1. 외부 API 호출
@@ -233,14 +233,18 @@ class AdminChatCharacterController(
// success가 false이면 throw
if (!apiResponse.success) {
throw SodaException(apiResponse.message ?: "등록에 실패했습니다. 다시 시도해 주세요.")
val apiMessage = apiResponse.message
if (apiMessage.isNullOrBlank()) {
throw SodaException(messageKey = "admin.chat.character.register_failed_retry")
}
throw SodaException(apiMessage)
}
// success가 true이면 data.id 반환
return apiResponse.data?.id ?: throw SodaException("등록에 실패했습니다. 응답에 ID가 없습니다.")
return apiResponse.data?.id ?: throw SodaException(messageKey = "admin.chat.character.register_failed_no_id")
} catch (e: Exception) {
e.printStackTrace()
throw SodaException("${e.message}, 등록에 실패했습니다. 다시 시도해 주세요.")
throw SodaException(messageKey = "admin.chat.character.register_failed_retry")
}
}
@@ -257,7 +261,7 @@ class AdminChatCharacterController(
metadata = metadata
)
} catch (e: Exception) {
throw SodaException("이미지 저장에 실패했습니다: ${e.message}")
throw SodaException(messageKey = "admin.chat.character.image_save_failed")
}
}
@@ -297,19 +301,19 @@ class AdminChatCharacterController(
request.originalWorkId != null
if (!hasChangedData && !hasImage && !hasDbOnlyChanges) {
throw SodaException("변경된 데이터가 없습니다.")
throw SodaException(messageKey = "admin.chat.character.no_changes")
}
// 외부 API로 전달할 변경이 있을 때만 외부 API 호출(3가지 필드만 변경된 경우는 호출하지 않음)
if (hasChangedData) {
val chatCharacter = service.findById(request.id)
?: throw SodaException("해당 ID의 캐릭터를 찾을 수 없습니다: ${request.id}")
?: throw SodaException(messageKey = "admin.chat.character.not_found")
// 이름이 수정된 경우 DB에 동일한 이름이 있는지 확인
if (request.name != null && request.name != chatCharacter.name) {
val existingCharacter = service.findByName(request.name)
if (existingCharacter != null) {
throw SodaException("동일한 이름은 등록이 불가능합니다: ${request.name}")
throw SodaException(messageKey = "admin.chat.character.duplicate_name")
}
}
@@ -438,11 +442,15 @@ class AdminChatCharacterController(
// success가 false이면 throw
if (!apiResponse.success) {
throw SodaException(apiResponse.message ?: "수정에 실패했습니다. 다시 시도해 주세요.")
val apiMessage = apiResponse.message
if (apiMessage.isNullOrBlank()) {
throw SodaException(messageKey = "admin.chat.character.update_failed_retry")
}
throw SodaException(apiMessage)
}
} catch (e: Exception) {
e.printStackTrace()
throw SodaException("${e.message} 수정에 실패했습니다. 다시 시도해 주세요.")
throw SodaException(messageKey = "admin.chat.character.update_failed_retry")
}
}
}

View File

@@ -63,7 +63,7 @@ class CharacterCurationAdminController(
@RequestBody request: CharacterCurationAddCharacterRequest
): ApiResponse<Boolean> {
val ids = request.characterIds.filter { it > 0 }.distinct()
if (ids.isEmpty()) throw SodaException("등록할 캐릭터 ID 리스트가 비어있습니다")
if (ids.isEmpty()) throw SodaException(messageKey = "admin.chat.curation.character_ids_empty")
service.addCharacters(curationId, ids)
return ApiResponse.ok(true)
}

View File

@@ -32,7 +32,7 @@ class CharacterCurationAdminService(
@Transactional
fun update(request: CharacterCurationUpdateRequest): CharacterCuration {
val curation = curationRepository.findById(request.id)
.orElseThrow { SodaException("큐레이션을 찾을 수 없습니다: ${request.id}") }
.orElseThrow { SodaException(messageKey = "admin.chat.curation.not_found") }
request.title?.let { curation.title = it }
request.isAdult?.let { curation.isAdult = it }
@@ -44,7 +44,7 @@ class CharacterCurationAdminService(
@Transactional
fun softDelete(curationId: Long) {
val curation = curationRepository.findById(curationId)
.orElseThrow { SodaException("큐레이션을 찾을 수 없습니다: $curationId") }
.orElseThrow { SodaException(messageKey = "admin.chat.curation.not_found") }
curation.isActive = false
curationRepository.save(curation)
}
@@ -53,7 +53,7 @@ class CharacterCurationAdminService(
fun reorder(ids: List<Long>) {
ids.forEachIndexed { index, id ->
val curation = curationRepository.findById(id)
.orElseThrow { SodaException("큐레이션을 찾을 수 없습니다: $id") }
.orElseThrow { SodaException(messageKey = "admin.chat.curation.not_found") }
curation.sortOrder = index + 1
curationRepository.save(curation)
}
@@ -61,14 +61,14 @@ class CharacterCurationAdminService(
@Transactional
fun addCharacters(curationId: Long, characterIds: List<Long>) {
if (characterIds.isEmpty()) throw SodaException("등록할 캐릭터 ID 리스트가 비어있습니다")
if (characterIds.isEmpty()) throw SodaException(messageKey = "admin.chat.curation.character_ids_empty")
val curation = curationRepository.findById(curationId)
.orElseThrow { SodaException("큐레이션을 찾을 수 없습니다: $curationId") }
if (!curation.isActive) throw SodaException("비활성화된 큐레이션입니다: $curationId")
.orElseThrow { SodaException(messageKey = "admin.chat.curation.not_found") }
if (!curation.isActive) throw SodaException(messageKey = "admin.chat.curation.inactive")
val uniqueIds = characterIds.filter { it > 0 }.distinct()
if (uniqueIds.isEmpty()) throw SodaException("유효한 캐릭터 ID가 없습니다")
if (uniqueIds.isEmpty()) throw SodaException(messageKey = "admin.chat.curation.invalid_character_ids")
// 활성 캐릭터만 조회 (조회 단계에서 검증 포함)
val characters = characterRepository.findByIdInAndIsActiveTrue(uniqueIds)
@@ -101,23 +101,23 @@ class CharacterCurationAdminService(
@Transactional
fun removeCharacter(curationId: Long, characterId: Long) {
val curation = curationRepository.findById(curationId)
.orElseThrow { SodaException("큐레이션을 찾을 수 없습니다: $curationId") }
.orElseThrow { SodaException(messageKey = "admin.chat.curation.not_found") }
val mappings = mappingRepository.findByCuration(curation)
val target = mappings.firstOrNull { it.chatCharacter.id == characterId }
?: throw SodaException("매핑을 찾을 수 없습니다: curation=$curationId, character=$characterId")
?: throw SodaException(messageKey = "admin.chat.curation.mapping_not_found")
mappingRepository.delete(target)
}
@Transactional
fun reorderCharacters(curationId: Long, characterIds: List<Long>) {
val curation = curationRepository.findById(curationId)
.orElseThrow { SodaException("큐레이션을 찾을 수 없습니다: $curationId") }
.orElseThrow { SodaException(messageKey = "admin.chat.curation.not_found") }
val mappings = mappingRepository.findByCuration(curation)
val mappingByCharacterId = mappings.associateBy { it.chatCharacter.id }
characterIds.forEachIndexed { index, cid ->
val mapping = mappingByCharacterId[cid]
?: throw SodaException("큐레이션에 포함되지 않은 캐릭터입니다: $cid")
?: throw SodaException(messageKey = "admin.chat.curation.character_not_in_curation")
mapping.sortOrder = index + 1
mappingRepository.save(mapping)
}
@@ -146,7 +146,7 @@ class CharacterCurationAdminService(
@Transactional(readOnly = true)
fun listCharacters(curationId: Long): List<ChatCharacter> {
val curation = curationRepository.findById(curationId)
.orElseThrow { SodaException("큐레이션을 찾을 수 없습니다: $curationId") }
.orElseThrow { SodaException(messageKey = "admin.chat.curation.not_found") }
val mappings = mappingRepository.findByCurationWithCharacterOrderBySortOrderAsc(curation)
return mappings.map { it.chatCharacter }
}

View File

@@ -11,6 +11,8 @@ import kr.co.vividnext.sodalive.aws.s3.S3Uploader
import kr.co.vividnext.sodalive.chat.character.image.CharacterImageService
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
import kr.co.vividnext.sodalive.utils.ImageBlurUtil
import kr.co.vividnext.sodalive.utils.generateFileName
import org.springframework.beans.factory.annotation.Value
@@ -34,6 +36,8 @@ class AdminCharacterImageController(
private val imageService: CharacterImageService,
private val s3Uploader: S3Uploader,
private val imageCloudFront: ImageContentCloudFront,
private val langContext: LangContext,
private val messageSource: SodaMessageSource,
@Value("\${cloud.aws.s3.content-bucket}")
private val s3Bucket: String,
@@ -106,14 +110,18 @@ class AdminCharacterImageController(
@DeleteMapping("/{imageId}")
fun delete(@PathVariable imageId: Long) = run {
imageService.deleteImage(imageId)
ApiResponse.ok(null, "이미지가 삭제되었습니다.")
val message = messageSource.getMessage("admin.chat.character.image_deleted", langContext.lang)
ApiResponse.ok(null, message)
}
@PutMapping("/orders")
fun updateOrders(@RequestBody request: UpdateCharacterImageOrdersRequest) = run {
if (request.characterId == null) throw SodaException("characterId는 필수입니다")
if (request.characterId == null) {
throw SodaException(messageKey = "admin.chat.character.character_id_required")
}
imageService.updateOrders(request.characterId, request.ids)
ApiResponse.ok(null, "정렬 순서가 변경되었습니다.")
val message = messageSource.getMessage("admin.chat.character.order_updated", langContext.lang)
ApiResponse.ok(null, message)
}
private fun buildS3Key(characterId: Long): String {
@@ -132,7 +140,7 @@ class AdminCharacterImageController(
metadata = metadata
)
} catch (e: Exception) {
throw SodaException("이미지 저장에 실패했습니다: ${e.message}")
throw SodaException(messageKey = "admin.chat.character.image_save_failed")
}
}
@@ -141,7 +149,7 @@ class AdminCharacterImageController(
// 멀티파트를 BufferedImage로 읽기
val bytes = image.bytes
val bimg = javax.imageio.ImageIO.read(java.io.ByteArrayInputStream(bytes))
?: throw SodaException("이미지 포맷을 인식할 수 없습니다.")
?: throw SodaException(messageKey = "admin.chat.character.image_format_invalid")
val blurred = ImageBlurUtil.blurFast(bimg)
// PNG로 저장(알파 유지), JPEG 업로드가 필요하면 포맷 변경 가능
@@ -164,7 +172,7 @@ class AdminCharacterImageController(
metadata = metadata
)
} catch (e: Exception) {
throw SodaException("블러 이미지 저장에 실패했습니다: ${e.message}")
throw SodaException(messageKey = "admin.chat.character.blur_image_save_failed")
}
}
}

View File

@@ -58,7 +58,7 @@ class AdminChatCharacterService(
@Transactional(readOnly = true)
fun getChatCharacterDetail(characterId: Long, imageHost: String = ""): ChatCharacterDetailResponse {
val chatCharacter = chatCharacterRepository.findById(characterId)
.orElseThrow { SodaException("해당 ID의 캐릭터를 찾을 수 없습니다: $characterId") }
.orElseThrow { SodaException(messageKey = "admin.chat.character.not_found") }
return ChatCharacterDetailResponse.from(chatCharacter, imageHost)
}

View File

@@ -192,8 +192,8 @@ class AdminOriginalWorkController(
filePath = "originals/$originalWorkId/${generateFileName(prefix = "original")}",
metadata = metadata
)
} catch (e: Exception) {
throw SodaException("이미지 저장에 실패했습니다: ${e.message}")
} catch (_: Exception) {
throw SodaException(messageKey = "admin.chat.original.image_save_failed")
}
}
}

View File

@@ -38,7 +38,7 @@ class AdminOriginalWorkService(
@Transactional
fun createOriginalWork(request: OriginalWorkRegisterRequest): OriginalWork {
originalWorkRepository.findByTitleAndIsDeletedFalse(request.title)?.let {
throw SodaException("동일한 제목의 원작이 이미 존재합니다: ${request.title}")
throw SodaException(messageKey = "admin.chat.original.duplicate_title")
}
val entity = OriginalWork(
title = request.title,
@@ -107,7 +107,7 @@ class AdminOriginalWorkService(
@Transactional
fun updateOriginalWork(request: OriginalWorkUpdateRequest, imagePath: String? = null): OriginalWork {
val ow = originalWorkRepository.findByIdAndIsDeletedFalse(request.id)
.orElseThrow { SodaException("해당 원작을 찾을 수 없습니다") }
.orElseThrow { SodaException(messageKey = "admin.chat.original.not_found") }
request.title?.let { ow.title = it }
request.contentType?.let { ow.contentType = it }
@@ -177,7 +177,7 @@ class AdminOriginalWorkService(
@Transactional
fun updateOriginalWorkImage(originalWorkId: Long, imagePath: String): OriginalWork {
val ow = originalWorkRepository.findByIdAndIsDeletedFalse(originalWorkId)
.orElseThrow { SodaException("해당 원작을 찾을 수 없습니다") }
.orElseThrow { SodaException(messageKey = "admin.chat.original.not_found") }
ow.imagePath = imagePath
return originalWorkRepository.save(ow)
}
@@ -186,7 +186,7 @@ class AdminOriginalWorkService(
@Transactional
fun deleteOriginalWork(id: Long) {
val ow = originalWorkRepository.findByIdAndIsDeletedFalse(id)
.orElseThrow { SodaException("해당 원작을 찾을 수 없습니다: $id") }
.orElseThrow { SodaException(messageKey = "admin.chat.original.not_found") }
ow.isDeleted = true
originalWorkRepository.save(ow)
}
@@ -195,7 +195,7 @@ class AdminOriginalWorkService(
@Transactional(readOnly = true)
fun getOriginalWork(id: Long): OriginalWork {
return originalWorkRepository.findByIdAndIsDeletedFalse(id)
.orElseThrow { SodaException("해당 원작을 찾을 수 없습니다") }
.orElseThrow { SodaException(messageKey = "admin.chat.original.not_found") }
}
/** 원작 페이징 조회 */
@@ -216,7 +216,7 @@ class AdminOriginalWorkService(
fun getCharactersOfOriginalWorkPage(originalWorkId: Long, page: Int, size: Int): Page<ChatCharacter> {
// 원작 존재 및 소프트 삭제 여부 확인
originalWorkRepository.findByIdAndIsDeletedFalse(originalWorkId)
.orElseThrow { SodaException("해당 원작을 찾을 수 없습니다") }
.orElseThrow { SodaException(messageKey = "admin.chat.original.not_found") }
val safePage = if (page < 0) 0 else page
val safeSize = when {
@@ -238,7 +238,7 @@ class AdminOriginalWorkService(
@Transactional
fun assignCharacters(originalWorkId: Long, characterIds: List<Long>) {
val ow = originalWorkRepository.findByIdAndIsDeletedFalse(originalWorkId)
.orElseThrow { SodaException("해당 원작을 찾을 수 없습니다") }
.orElseThrow { SodaException(messageKey = "admin.chat.original.not_found") }
if (characterIds.isEmpty()) return
val characters = chatCharacterRepository.findByIdInAndIsActiveTrue(characterIds)
characters.forEach { it.originalWork = ow }
@@ -250,7 +250,7 @@ class AdminOriginalWorkService(
fun unassignCharacters(originalWorkId: Long, characterIds: List<Long>) {
// 원작 존재 확인 (소프트 삭제 제외)
originalWorkRepository.findByIdAndIsDeletedFalse(originalWorkId)
.orElseThrow { SodaException("해당 원작을 찾을 수 없습니다") }
.orElseThrow { SodaException(messageKey = "admin.chat.original.not_found") }
if (characterIds.isEmpty()) return
val characters = chatCharacterRepository.findByIdInAndIsActiveTrue(characterIds)
characters.forEach { it.originalWork = null }
@@ -261,13 +261,13 @@ class AdminOriginalWorkService(
@Transactional
fun assignOneCharacter(originalWorkId: Long, characterId: Long) {
val character = chatCharacterRepository.findById(characterId)
.orElseThrow { SodaException("해당 캐릭터를 찾을 수 없습니다") }
.orElseThrow { SodaException(messageKey = "admin.chat.character.not_found") }
if (originalWorkId == 0L) {
character.originalWork = null
} else {
val ow = originalWorkRepository.findByIdAndIsDeletedFalse(originalWorkId)
.orElseThrow { SodaException("해당 원작을 찾을 수 없습니다") }
.orElseThrow { SodaException(messageKey = "admin.chat.original.not_found") }
character.originalWork = ow
}

View File

@@ -150,6 +150,159 @@ class SodaMessageSource {
)
)
private val adminChatBannerMessages = mapOf(
"admin.chat.banner.image_save_failed" to mapOf(
Lang.KO to "이미지 저장에 실패했습니다.",
Lang.EN to "Failed to save image.",
Lang.JA to "画像の保存に失敗しました。"
),
"admin.chat.banner.delete_success" to mapOf(
Lang.KO to "배너가 성공적으로 삭제되었습니다.",
Lang.EN to "Banner deleted successfully.",
Lang.JA to "バナーが削除されました。"
),
"admin.chat.banner.reorder_success" to mapOf(
Lang.KO to "배너 정렬 순서가 성공적으로 변경되었습니다.",
Lang.EN to "Banner order updated successfully.",
Lang.JA to "バナーの並び順が変更されました。"
)
)
private val adminChatCalculateMessages = mapOf(
"admin.chat.calculate.end_date_max_today" to mapOf(
Lang.KO to "끝 날짜는 오늘 날짜까지만 입력 가능합니다.",
Lang.EN to "End date can be at most today.",
Lang.JA to "終了日は本日まで指定できます。"
),
"admin.chat.calculate.start_date_after_end" to mapOf(
Lang.KO to "시작 날짜는 끝 날짜보다 이후일 수 없습니다.",
Lang.EN to "Start date cannot be after end date.",
Lang.JA to "開始日は終了日より後にできません。"
),
"admin.chat.calculate.max_period_6_months" to mapOf(
Lang.KO to "조회 가능 기간은 최대 6개월입니다.",
Lang.EN to "Maximum query period is 6 months.",
Lang.JA to "照会期間は最大6ヶ月です。"
)
)
private val adminChatCharacterMessages = mapOf(
"admin.chat.character.duplicate_name" to mapOf(
Lang.KO to "동일한 이름은 등록이 불가능합니다.",
Lang.EN to "A character with the same name already exists.",
Lang.JA to "同じ名前は登録できません。"
),
"admin.chat.character.register_failed_retry" to mapOf(
Lang.KO to "등록에 실패했습니다. 다시 시도해 주세요.",
Lang.EN to "Registration failed. Please try again.",
Lang.JA to "登録に失敗しました。もう一度お試しください。"
),
"admin.chat.character.register_failed_no_id" to mapOf(
Lang.KO to "등록에 실패했습니다. 응답에 ID가 없습니다.",
Lang.EN to "Registration failed. No ID in response.",
Lang.JA to "登録に失敗しました。応答にIDがありません。"
),
"admin.chat.character.image_save_failed" to mapOf(
Lang.KO to "이미지 저장에 실패했습니다.",
Lang.EN to "Failed to save image.",
Lang.JA to "画像の保存に失敗しました。"
),
"admin.chat.character.no_changes" to mapOf(
Lang.KO to "변경된 데이터가 없습니다.",
Lang.EN to "No changes detected.",
Lang.JA to "変更されたデータがありません。"
),
"admin.chat.character.not_found" to mapOf(
Lang.KO to "해당 캐릭터를 찾을 수 없습니다.",
Lang.EN to "Character not found.",
Lang.JA to "該当キャラクターが見つかりません。"
),
"admin.chat.character.update_failed_retry" to mapOf(
Lang.KO to "수정에 실패했습니다. 다시 시도해 주세요.",
Lang.EN to "Update failed. Please try again.",
Lang.JA to "更新に失敗しました。もう一度お試しください。"
)
)
private val adminChatCurationMessages = mapOf(
"admin.chat.curation.not_found" to mapOf(
Lang.KO to "큐레이션을 찾을 수 없습니다.",
Lang.EN to "Curation not found.",
Lang.JA to "キュレーションが見つかりません。"
),
"admin.chat.curation.character_ids_empty" to mapOf(
Lang.KO to "등록할 캐릭터 ID 리스트가 비어있습니다",
Lang.EN to "Character ID list to register is empty.",
Lang.JA to "登録するキャラクターIDリストが空です。"
),
"admin.chat.curation.inactive" to mapOf(
Lang.KO to "비활성화된 큐레이션입니다.",
Lang.EN to "Curation is inactive.",
Lang.JA to "無効化されたキュレーションです。"
),
"admin.chat.curation.invalid_character_ids" to mapOf(
Lang.KO to "유효한 캐릭터 ID가 없습니다",
Lang.EN to "No valid character IDs.",
Lang.JA to "有効なキャラクターIDがありません。"
),
"admin.chat.curation.mapping_not_found" to mapOf(
Lang.KO to "매핑을 찾을 수 없습니다.",
Lang.EN to "Mapping not found.",
Lang.JA to "マッピングが見つかりません。"
),
"admin.chat.curation.character_not_in_curation" to mapOf(
Lang.KO to "큐레이션에 포함되지 않은 캐릭터입니다.",
Lang.EN to "Character not included in this curation.",
Lang.JA to "このキュレーションに含まれていないキャラクターです。"
)
)
private val adminChatCharacterImageMessages = mapOf(
"admin.chat.character.image_deleted" to mapOf(
Lang.KO to "이미지가 삭제되었습니다.",
Lang.EN to "Image deleted.",
Lang.JA to "画像が削除されました。"
),
"admin.chat.character.character_id_required" to mapOf(
Lang.KO to "characterId는 필수입니다",
Lang.EN to "characterId is required.",
Lang.JA to "characterIdは必須です。"
),
"admin.chat.character.order_updated" to mapOf(
Lang.KO to "정렬 순서가 변경되었습니다.",
Lang.EN to "Order updated.",
Lang.JA to "並び順が変更されました。"
),
"admin.chat.character.image_format_invalid" to mapOf(
Lang.KO to "이미지 포맷을 인식할 수 없습니다.",
Lang.EN to "Unsupported image format.",
Lang.JA to "画像形式を認識できません。"
),
"admin.chat.character.blur_image_save_failed" to mapOf(
Lang.KO to "블러 이미지 저장에 실패했습니다.",
Lang.EN to "Failed to save blurred image.",
Lang.JA to "ぼかし画像の保存に失敗しました。"
)
)
private val adminChatOriginalWorkMessages = mapOf(
"admin.chat.original.image_save_failed" to mapOf(
Lang.KO to "이미지 저장에 실패했습니다.",
Lang.EN to "Failed to save image.",
Lang.JA to "画像の保存に失敗しました。"
),
"admin.chat.original.duplicate_title" to mapOf(
Lang.KO to "동일한 제목의 원작이 이미 존재합니다.",
Lang.EN to "An original work with the same title already exists.",
Lang.JA to "同じタイトルの原作が既に存在します。"
),
"admin.chat.original.not_found" to mapOf(
Lang.KO to "해당 원작을 찾을 수 없습니다.",
Lang.EN to "Original work not found.",
Lang.JA to "該当の原作が見つかりません。"
)
)
fun getMessage(key: String, lang: Lang): String? {
val messageGroups = listOf(
commonMessages,
@@ -158,7 +311,13 @@ class SodaMessageSource {
auditionNotificationMessages,
auditionRoleMessages,
settlementRatioMessages,
adminCanMessages
adminCanMessages,
adminChatBannerMessages,
adminChatCalculateMessages,
adminChatCharacterMessages,
adminChatCurationMessages,
adminChatCharacterImageMessages,
adminChatOriginalWorkMessages
)
for (messages in messageGroups) {
val translations = messages[key] ?: continue