오디오 콘텐츠 테마 번역을 적용한다
오디오 콘텐츠 목록 응답에서 테마 문자열에 번역을 적용한다. 번역 데이터가 없을 때는 기존 원문을 유지한다.
This commit is contained in:
37
AGENTS.md
Normal file
37
AGENTS.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
> 이 문서는 본 저장소에서 **AI Coding Agent가 반드시 따라야 할 개발 헌법(운영 규칙)**이다.
|
||||||
|
> 모든 신규 코드는 본 문서를 최우선 기준으로 작성한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 전제
|
||||||
|
질문에 대한 답변과 설명은 한국어로 한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Commit Standards
|
||||||
|
|
||||||
|
1. 커밋 메시지는 **반드시 한국어로 작성한다.**
|
||||||
|
2. 제목(subject)은 **현재형**으로 작성한다. (예: “기능 추가”, “기능 추가함” 금지)
|
||||||
|
3. 제목은 **50자 이내**로 작성한다.
|
||||||
|
4. 제목과 본문 사이에는 **반드시 한 줄 공백**을 둔다.
|
||||||
|
5. 본문은 **한 줄당 72자 이내**로 작성한다.
|
||||||
|
6. 하나의 문단에서는 72자를 초과할 때만 줄바꿈한다.
|
||||||
|
7. **공개 API 변경 사항만 설명**하며, 패키지 프라이빗 구현 상세는 포함하지 않는다.
|
||||||
|
8. **테스트 코드에 대한 언급은 커밋 메시지에 포함하지 않는다.**
|
||||||
|
9. 제목에 `fix:`, `feat:`, `docs:` 등의 **접두어를 사용하지 않는다.**
|
||||||
|
10. 제목은 **첫 글자를 대문자로 시작**한다. 단, 함수명 등 소문자가 합당한 경우만 예외를 허용한다.
|
||||||
|
11. 도구 광고, 브랜딩, 홍보성 문구를 **절대 포함하지 않는다.**
|
||||||
|
12. 커밋 전에는 **반드시 파일을 개별 stage 한다.**
|
||||||
|
13. 커밋 전 **`work/scripts/check-commit-message-rules.sh` 검증을 통과해야 한다.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. AI 사용 규칙 (AI Interaction Rules)
|
||||||
|
|
||||||
|
- 매우 작은 단위의 변경만 수행한다.
|
||||||
|
- 대규모 리팩터링은 반드시 사전 승인을 요청한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
✅ 본 문서는 **AI와 사람이 동일한 개발 규칙을 공유하기 위한 최상위 기준 문서**이며,
|
||||||
|
✅ 모든 신규 코드는 본 문서를 기준으로 검토된다.
|
||||||
@@ -920,12 +920,34 @@ class AudioContentService(
|
|||||||
contentId = audioContent.id!!
|
contentId = audioContent.id!!
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* themeStr 번역 처리
|
||||||
|
*/
|
||||||
|
val themeStrTranslated = run {
|
||||||
|
val theme = audioContent.theme
|
||||||
|
if (theme?.id != null) {
|
||||||
|
val locale = langContext.lang.code
|
||||||
|
val translated = contentThemeTranslationRepository
|
||||||
|
.findByContentThemeIdAndLocale(theme.id!!, locale)
|
||||||
|
val text = translated?.theme
|
||||||
|
if (!text.isNullOrBlank()) text else theme.theme
|
||||||
|
} else {
|
||||||
|
audioContent.theme!!.theme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return GetAudioContentListItem(
|
return GetAudioContentListItem(
|
||||||
contentId = audioContent.id!!,
|
contentId = audioContent.id!!,
|
||||||
coverImageUrl = "$coverImageHost/${audioContent.coverImage}",
|
coverImageUrl = "$coverImageHost/${audioContent.coverImage}",
|
||||||
title = audioContent.title,
|
title = run {
|
||||||
|
val translatedTitle = contentTranslationRepository
|
||||||
|
.findByContentIdAndLocale(audioContent.id!!, langContext.lang.code)
|
||||||
|
?.renderedPayload
|
||||||
|
?.title
|
||||||
|
if (translatedTitle.isNullOrBlank()) audioContent.title else translatedTitle
|
||||||
|
},
|
||||||
price = audioContent.price,
|
price = audioContent.price,
|
||||||
themeStr = audioContent.theme!!.theme,
|
themeStr = themeStrTranslated,
|
||||||
duration = audioContent.duration,
|
duration = audioContent.duration,
|
||||||
likeCount = likeCount,
|
likeCount = likeCount,
|
||||||
commentCount = commentCount,
|
commentCount = commentCount,
|
||||||
@@ -1017,9 +1039,42 @@ class AudioContentService(
|
|||||||
items
|
items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// theme 번역 적용: 번역 데이터가 있으면 번역, 없으면 원문 유지
|
||||||
|
val themeTranslatedList = run {
|
||||||
|
if (translatedContentList.isEmpty()) {
|
||||||
|
translatedContentList
|
||||||
|
} else {
|
||||||
|
val locale = langContext.lang.code
|
||||||
|
|
||||||
|
// 활성 테마 목록에서 한글 원문 -> ID 매핑 구성
|
||||||
|
val themesWithIds = themeQueryRepository.getActiveThemeWithIdsOfContent(
|
||||||
|
isAdult = isAdult,
|
||||||
|
isFree = false,
|
||||||
|
isPointAvailableOnly = false,
|
||||||
|
contentType = contentType
|
||||||
|
)
|
||||||
|
val idByKorean = themesWithIds.associate { it.theme to it.id }
|
||||||
|
|
||||||
|
val themeIds = idByKorean.values.distinct()
|
||||||
|
val translatedById = if (themeIds.isNotEmpty()) {
|
||||||
|
contentThemeTranslationRepository
|
||||||
|
.findByContentThemeIdInAndLocale(themeIds, locale)
|
||||||
|
.associate { it.contentThemeId to it.theme }
|
||||||
|
} else {
|
||||||
|
emptyMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
translatedContentList.map { item ->
|
||||||
|
val themeId = idByKorean[item.themeStr]
|
||||||
|
val translated = if (themeId != null) translatedById[themeId] else null
|
||||||
|
if (!translated.isNullOrBlank()) item.copy(themeStr = translated) else item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return GetAudioContentListResponse(
|
return GetAudioContentListResponse(
|
||||||
totalCount = totalCount,
|
totalCount = totalCount,
|
||||||
items = translatedContentList
|
items = themeTranslatedList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user