fix(donation): 후원 문구에 다국어를 적용한다
This commit is contained in:
@@ -3567,6 +3567,14 @@ If you block this user, the following features will be restricted.
|
|||||||
ja: "まだサポートがありません。\n最初のサポートをしてみましょう!"
|
ja: "まだサポートがありません。\n最初のサポートをしてみましょう!"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func defaultMessage(_ can: Int) -> String {
|
||||||
|
pick(
|
||||||
|
ko: "\(can)캔을 후원하였습니다",
|
||||||
|
en: "Donated \(can) cans.",
|
||||||
|
ja: "\(can)canをサポートしました。"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum HomeRecommendation {
|
enum HomeRecommendation {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ struct CreatorChannelDonationCard: View {
|
|||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.frame(width: 18, height: 18)
|
.frame(width: 18, height: 18)
|
||||||
|
|
||||||
Text("\(donation.can)캔")
|
Text(I18n.LiveChat.canWithUnit(donation.can))
|
||||||
.appFont(size: 14, weight: .regular)
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
@@ -72,7 +72,7 @@ struct CreatorChannelDonationCard: View {
|
|||||||
extension CreatorChannelDonationResponse {
|
extension CreatorChannelDonationResponse {
|
||||||
var messageText: String {
|
var messageText: String {
|
||||||
let trimmedMessage = message.trimmingCharacters(in: .whitespacesAndNewlines)
|
let trimmedMessage = message.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
return trimmedMessage.isEmpty ? "\(can)캔을 후원하였습니다" : trimmedMessage
|
return trimmedMessage.isEmpty ? I18n.CreatorChannelDonation.defaultMessage(can) : trimmedMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
func relativeTimeText(now: Date = Date()) -> String {
|
func relativeTimeText(now: Date = Date()) -> String {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ struct CreatorChannelDonationRankingSection: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
if rankings.isEmpty == false {
|
if rankings.isEmpty == false {
|
||||||
VStack(alignment: .leading, spacing: SodaSpacing.s14) {
|
VStack(alignment: .leading, spacing: SodaSpacing.s14) {
|
||||||
Text("후원 랭킹")
|
Text(I18n.Explorer.donationRankingTitle)
|
||||||
.appFont(size: 20, weight: .bold)
|
.appFont(size: 20, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
@@ -19,7 +19,7 @@ struct CreatorChannelDonationRankingSection: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Button(action: onTapViewAll) {
|
Button(action: onTapViewAll) {
|
||||||
Text("전체보기")
|
Text(I18n.CreatorChannelHome.viewAll)
|
||||||
.appFont(size: 16, weight: .medium)
|
.appFont(size: 16, weight: .medium)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|||||||
@@ -263,3 +263,54 @@
|
|||||||
|
|
||||||
- 2026-08-14: 사용자 인터뷰로 후원 탭 empty 문구의 ko/en/ja 값을 확정하고 기존 후원 버튼 title 재사용을 Phase 6 미완료 후속 Task로 추가했다. 이번 작업은 문서만 보강했으며 Swift 구현과 빌드는 수행하지 않았다.
|
- 2026-08-14: 사용자 인터뷰로 후원 탭 empty 문구의 ko/en/ja 값을 확정하고 기존 후원 버튼 title 재사용을 Phase 6 미완료 후속 Task로 추가했다. 이번 작업은 문서만 보강했으며 Swift 구현과 빌드는 수행하지 않았다.
|
||||||
- 2026-08-14: Phase 6 구현을 완료했다. 후원 탭 empty 문구를 ko/en/ja I18n으로 교체하고 버튼은 기존 `I18n.ContentDetail.DonationDialog.title`을 재사용했으며 `Color.soda400` 배경과 white 아이콘·텍스트를 유지했다. 23개 정적 계약, `git diff --check`, iPhone 16 Pro 시뮬레이터 Debug 빌드가 통과했고, 세 언어 캡처와 두 독립 색상·CJK 리뷰에서 차단 항목 없이 PASS를 확인했다.
|
- 2026-08-14: Phase 6 구현을 완료했다. 후원 탭 empty 문구를 ko/en/ja I18n으로 교체하고 버튼은 기존 `I18n.ContentDetail.DonationDialog.title`을 재사용했으며 `Color.soda400` 배경과 white 아이콘·텍스트를 유지했다. 23개 정적 계약, `git diff --check`, iPhone 16 Pro 시뮬레이터 Debug 빌드가 통과했고, 세 언어 캡처와 두 독립 색상·CJK 리뷰에서 차단 항목 없이 PASS를 확인했다.
|
||||||
|
|
||||||
|
### Phase 7: 후원 랭킹·공용 카드 다국어 회귀 수정
|
||||||
|
|
||||||
|
**Phase 결과:** 후원 랭킹과 홈·후원 탭 공용 후원 카드의 고정 UI 문구가 ko/en/ja 앱 언어에 맞게 표시된다.
|
||||||
|
|
||||||
|
**선행조건:** `REV-P3-001`, `REV-P3-002` 확정과 PRD의 `DEC-DONATION-I18N-001`, `DEC-DONATION-I18N-002`.
|
||||||
|
|
||||||
|
#### Task 7.1: 랭킹·공용 카드의 런타임 하드코딩 제거
|
||||||
|
|
||||||
|
**Goal 실행 `P7-R1`:** 확정된 네 런타임 하드코딩을 기존 I18n 재사용과 하나의 카드 전용 fallback 문구로 교체한다.
|
||||||
|
|
||||||
|
- **시작 조건:** `docs/20260704_크리에이터_채널_후원_탭/reviews/phase3-donation-localization-audit.md`의 `REV-P3-001`, `REV-P3-002`.
|
||||||
|
- **완료 증거:** 정적 계약 검사, ko/en/ja 수동 확인, Debug 빌드, 검증 기록 누적.
|
||||||
|
- **범위 밖:** Preview fixture 번역, 신규 dependency·formatter abstraction, 호출 화면의 레이아웃·action 변경.
|
||||||
|
- **TDD 예외 사유:** 현재 프로젝트에 테스트 번들 타깃이 없고 변경 대상은 분기 없는 I18n 문구 연결이다.
|
||||||
|
- **대체 검증 방법:** 하드코딩 부재와 I18n 참조를 `rg`로 확인하고, ko/en/ja Simulator에서 두 호출 화면을 수동 확인한다.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
|
||||||
|
- Modify: `SodaLive/Sources/I18n/I18n.swift`
|
||||||
|
- Modify: `SodaLive/Sources/V2/CreatorChannel/Donation/Components/CreatorChannelDonationRankingSection.swift`
|
||||||
|
- Modify: `SodaLive/Sources/V2/CreatorChannel/Donation/Components/CreatorChannelDonationCard.swift`
|
||||||
|
- Verify: `SodaLive/Sources/V2/CreatorChannel/Donation/CreatorChannelDonationTabView.swift`
|
||||||
|
- Verify: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelDonationSection.swift`
|
||||||
|
|
||||||
|
- [x] 랭킹 title을 `I18n.Explorer.donationRankingTitle`로 교체한다.
|
||||||
|
- [x] 전체보기 action 문구를 `I18n.CreatorChannelHome.viewAll`로 교체한다.
|
||||||
|
- [x] can pill을 `I18n.LiveChat.canWithUnit(_:)`으로 교체한다.
|
||||||
|
- [x] 빈 메시지용 `I18n.CreatorChannelDonation.defaultMessage(_:)`를 ko/en/ja로 추가하고 공용 카드에서 사용한다.
|
||||||
|
- [x] 아래 Gate 결과를 검증 기록에 누적한다.
|
||||||
|
|
||||||
|
#### Phase 7 Gate
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rg -n "I18n\.Explorer\.donationRankingTitle|I18n\.CreatorChannelHome\.viewAll" SodaLive/Sources/V2/CreatorChannel/Donation/Components/CreatorChannelDonationRankingSection.swift
|
||||||
|
rg -n "I18n\.LiveChat\.canWithUnit|I18n\.CreatorChannelDonation\.defaultMessage" SodaLive/Sources/V2/CreatorChannel/Donation/Components/CreatorChannelDonationCard.swift
|
||||||
|
rg -n "defaultMessage|Donated .* cans|サポートしました" SodaLive/Sources/I18n/I18n.swift
|
||||||
|
git diff --check
|
||||||
|
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected:** 기존 I18n 3개와 신규 fallback 1개가 연결되고 런타임 한국어 하드코딩이 제거되며 `git diff --check`와 Debug 빌드가 성공한다.
|
||||||
|
|
||||||
|
수동 검증:
|
||||||
|
|
||||||
|
- [x] ko/en/ja에서 후원 탭 랭킹 title과 전체보기 action을 확인한다.
|
||||||
|
- [x] ko/en/ja에서 빈 메시지·메시지 있음 카드를 후원 탭과 홈 탭에서 각각 확인한다.
|
||||||
|
|
||||||
|
- 2026-08-26: `CreatorChannelDonationRankingSection`, `CreatorChannelDonationCard`와 직접 호출 경로를 점검해 `REV-P3-001`, `REV-P3-002`를 확정하고 `P7-R1`을 추가했다. 사용자 요청에 따라 이번 작업에서는 문서만 작성했으며 Swift·I18n 구현과 Gate 실행은 남겨 두었다.
|
||||||
|
- 2026-08-26: Phase 7 구현을 완료했다. 랭킹 title·전체보기·can pill은 기존 I18n을 재사용하고, 공용 카드의 빈 메시지 fallback만 ko/en/ja로 추가했다. 변경 전 정적 계약은 두 항목 모두 exit 1, 변경 후 Gate와 `git diff --check`는 exit 0, `SodaLive-dev` Debug 빌드는 `BUILD SUCCEEDED`, 독립 리뷰는 `APPROVED`였다. 사용자 요청에 따라 visual QA는 실행하지 않았으므로 실제 화면 ko/en/ja 수동 검증 두 항목은 미완료로 유지한다.
|
||||||
|
- 2026-08-26: 사용자 수동 테스트 완료 확인에 따라 Phase 7의 ko/en/ja 실제 화면 검증 두 항목을 완료 처리했다.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ Figma 참조:
|
|||||||
- 기존 홈 탭에는 채널 후원 요약 섹션과 후원하기 액션이 있지만, 후원 탭 전용 API, 랭킹 섹션, 스크롤 페이징이 없다.
|
- 기존 홈 탭에는 채널 후원 요약 섹션과 후원하기 액션이 있지만, 후원 탭 전용 API, 랭킹 섹션, 스크롤 페이징이 없다.
|
||||||
- 후원 후 홈 탭 후원 섹션은 갱신되지만, 후원 탭이 구현되면 후원 탭 목록도 함께 갱신되어야 한다.
|
- 후원 후 홈 탭 후원 섹션은 갱신되지만, 후원 탭이 구현되면 후원 탭 목록도 함께 갱신되어야 한다.
|
||||||
- 후원 탭 empty 문구와 `후원하기` 버튼이 한국어로 하드코딩되어 앱 언어 설정에 맞게 표시되지 않는다.
|
- 후원 탭 empty 문구와 `후원하기` 버튼이 한국어로 하드코딩되어 앱 언어 설정에 맞게 표시되지 않는다.
|
||||||
|
- 후원 랭킹 title·전체보기 action과 공용 후원 카드의 can 표기·빈 메시지 fallback이 한국어로 하드코딩되어 영어·일본어 설정에서도 한국어가 노출된다.
|
||||||
|
|
||||||
## 3. Goals
|
## 3. Goals
|
||||||
- `CreatorChannelTab.donation` 선택 시 후원 탭 API를 호출하고 응답 데이터로 화면을 구성한다.
|
- `CreatorChannelTab.donation` 선택 시 후원 탭 API를 호출하고 응답 데이터로 화면을 구성한다.
|
||||||
@@ -30,6 +31,7 @@ Figma 참조:
|
|||||||
- `donationCount == 0` empty 상태에서는 하단 고정 후원하기 버튼을 숨긴다.
|
- `donationCount == 0` empty 상태에서는 하단 고정 후원하기 버튼을 숨긴다.
|
||||||
- `donationCount != 0 || !rankings.isEmpty`이면 홈 탭 plus button 위치에 icon-only 후원하기 버튼을 표시한다.
|
- `donationCount != 0 || !rankings.isEmpty`이면 홈 탭 plus button 위치에 icon-only 후원하기 버튼을 표시한다.
|
||||||
- 후원 성공 후 후원 탭 데이터와 홈 탭 데이터가 갱신되어야 한다.
|
- 후원 성공 후 후원 탭 데이터와 홈 탭 데이터가 갱신되어야 한다.
|
||||||
|
- 후원 랭킹과 공용 후원 카드의 고정 UI 문구는 기존 I18n을 우선 재사용하고, 재사용할 수 없는 빈 메시지 fallback만 `I18n.CreatorChannelDonation`에 추가한다.
|
||||||
|
|
||||||
## 4. Non-Goals
|
## 4. Non-Goals
|
||||||
- 크리에이터 채널 공통 shell, header, sticky tab-bar 동작을 다시 설계하지 않는다.
|
- 크리에이터 채널 공통 shell, header, sticky tab-bar 동작을 다시 설계하지 않는다.
|
||||||
@@ -92,12 +94,12 @@ data class CreatorChannelDonationResponse(
|
|||||||
- `rankings`가 있으면 Figma `290:9097` 및 `665:19056` 기준의 카드로 표시한다.
|
- `rankings`가 있으면 Figma `290:9097` 및 `665:19056` 기준의 카드로 표시한다.
|
||||||
- 후원 랭킹은 최대 8명만 표시하고, 한 줄에 4명씩 표시한다.
|
- 후원 랭킹은 최대 8명만 표시하고, 한 줄에 4명씩 표시한다.
|
||||||
- 카드 배경은 `Color.gray900`, corner radius는 14pt, 내부 padding과 item 간격은 14pt 기준을 사용한다.
|
- 카드 배경은 `Color.gray900`, corner radius는 14pt, 내부 padding과 item 간격은 14pt 기준을 사용한다.
|
||||||
- title은 `후원 랭킹`을 표시한다.
|
- title은 기존 `I18n.Explorer.donationRankingTitle`을 재사용해 한국어 `후원랭킹`, 영어 `Donation ranking`, 일본어 `後援ランキング`을 표시한다.
|
||||||
- item은 75pt 원형 프로필, 중앙 정렬 닉네임, 프로필 하단에 rank number overlay를 표시한다.
|
- item은 75pt 원형 프로필, 중앙 정렬 닉네임, 프로필 하단에 rank number overlay를 표시한다.
|
||||||
- 랭킹 번호는 응답 배열 순서 기준으로 `index + 1`을 표시한다.
|
- 랭킹 번호는 응답 배열 순서 기준으로 `index + 1`을 표시한다.
|
||||||
- 닉네임은 1줄 말줄임 처리한다.
|
- 닉네임은 1줄 말줄임 처리한다.
|
||||||
- `profileImage`는 `DownsampledKFImage`로 표시하고, 비어 있거나 실패하면 기존 프로필 placeholder 관례를 따른다.
|
- `profileImage`는 `DownsampledKFImage`로 표시하고, 비어 있거나 실패하면 기존 프로필 placeholder 관례를 따른다.
|
||||||
- Figma의 `전체보기` 버튼을 표시한다.
|
- Figma의 `전체보기` 버튼은 기존 `I18n.CreatorChannelHome.viewAll`을 재사용한다.
|
||||||
- `전체보기` 터치 시 기존 `AppStep.userProfileDonationAll(userId:)`로 이동해 `UserProfileDonationAllView`를 연다.
|
- `전체보기` 터치 시 기존 `AppStep.userProfileDonationAll(userId:)`로 이동해 `UserProfileDonationAllView`를 연다.
|
||||||
- `userId`에는 현재 크리에이터 채널의 `creatorId`를 전달한다.
|
- `userId`에는 현재 크리에이터 채널의 `creatorId`를 전달한다.
|
||||||
|
|
||||||
@@ -109,7 +111,7 @@ data class CreatorChannelDonationResponse(
|
|||||||
- `createdAtUtc` 상대 시간
|
- `createdAtUtc` 상대 시간
|
||||||
- `can`
|
- `can`
|
||||||
- `message`
|
- `message`
|
||||||
- 후원 금액 pill은 기존 can icon과 `{can}캔` 형식을 사용한다.
|
- 후원 금액 pill은 기존 can icon과 `I18n.LiveChat.canWithUnit(_:)`을 재사용해 ko/en/ja 단위 표기를 적용한다.
|
||||||
- 카드 상단 배경색은 홈 탭 후원 카드의 `can` 구간 색상을 재사용한다.
|
- 카드 상단 배경색은 홈 탭 후원 카드의 `can` 구간 색상을 재사용한다.
|
||||||
- `1...50`: `#E2E2E2`
|
- `1...50`: `#E2E2E2`
|
||||||
- `51...100`: `#73EE01`
|
- `51...100`: `#73EE01`
|
||||||
@@ -117,7 +119,10 @@ data class CreatorChannelDonationResponse(
|
|||||||
- `501...`: `#FF4C3C`
|
- `501...`: `#FF4C3C`
|
||||||
- `can >= 501`인 후원 item은 상단 영역에서 `nickname`을 white, 상대 시간을 `gray100`으로 표시한다.
|
- `can >= 501`인 후원 item은 상단 영역에서 `nickname`을 white, 상대 시간을 `gray100`으로 표시한다.
|
||||||
- 그 외 후원 `can` 구간의 `nickname`과 상대 시간 글자색은 기존 표시 색상을 유지한다.
|
- 그 외 후원 `can` 구간의 `nickname`과 상대 시간 글자색은 기존 표시 색상을 유지한다.
|
||||||
- `message`가 빈 문자열이면 기존 홈 탭과 동일하게 `{can}캔을 후원하였습니다`를 표시한다.
|
- `message`가 빈 문자열이면 홈 탭과 후원 탭에서 공용 `I18n.CreatorChannelDonation.defaultMessage(_:)`를 사용한다.
|
||||||
|
- 한국어: `{can}캔을 후원하였습니다`
|
||||||
|
- 영어: `Donated {can} cans.`
|
||||||
|
- 일본어: `{can}canをサポートしました。`
|
||||||
- 서버 응답에 비밀 후원 여부가 없으므로, 클라이언트는 서버가 내려준 `nickname`, `profileImageUrl`, `message`를 그대로 표시한다.
|
- 서버 응답에 비밀 후원 여부가 없으므로, 클라이언트는 서버가 내려준 `nickname`, `profileImageUrl`, `message`를 그대로 표시한다.
|
||||||
|
|
||||||
### 5.5 Pagination
|
### 5.5 Pagination
|
||||||
@@ -162,6 +167,8 @@ data class CreatorChannelDonationResponse(
|
|||||||
- `rankings`가 있으면 `후원 랭킹` 카드에 프로필 그리드와 rank number가 표시된다.
|
- `rankings`가 있으면 `후원 랭킹` 카드에 프로필 그리드와 rank number가 표시된다.
|
||||||
- `후원 랭킹` 카드의 `전체보기`를 터치하면 `UserProfileDonationAllView`로 이동한다.
|
- `후원 랭킹` 카드의 `전체보기`를 터치하면 `UserProfileDonationAllView`로 이동한다.
|
||||||
- `donations` 목록은 기존 홈 탭 후원 카드와 같은 후원 금액별 상단 컬러, can pill, 상대 시간, 메시지 표시 규칙을 따른다.
|
- `donations` 목록은 기존 홈 탭 후원 카드와 같은 후원 금액별 상단 컬러, can pill, 상대 시간, 메시지 표시 규칙을 따른다.
|
||||||
|
- 앱 언어가 ko/en/ja일 때 후원 랭킹 title·전체보기 action·can pill·빈 메시지 fallback이 해당 언어로 표시된다.
|
||||||
|
- 공용 `CreatorChannelDonationCard`를 사용하는 홈 탭과 후원 탭에서 동일한 다국어 결과가 표시된다.
|
||||||
- 목록 하단 도달 시 `hasNext == true`이면 다음 페이지가 append된다.
|
- 목록 하단 도달 시 `hasNext == true`이면 다음 페이지가 append된다.
|
||||||
- `rankings.isEmpty && donationCount == 0`이면 Figma `290:9009` empty view가 표시되고, 하단 고정 후원하기 버튼은 숨겨진다.
|
- `rankings.isEmpty && donationCount == 0`이면 Figma `290:9009` empty view가 표시되고, 하단 고정 후원하기 버튼은 숨겨진다.
|
||||||
- Empty 문구가 지정된 ko/en/ja 값과 줄바꿈으로 표시되고, 버튼 문구는 기존 `I18n.ContentDetail.DonationDialog.title`을 사용한다.
|
- Empty 문구가 지정된 ko/en/ja 값과 줄바꿈으로 표시되고, 버튼 문구는 기존 `I18n.ContentDetail.DonationDialog.title`을 사용한다.
|
||||||
@@ -179,6 +186,8 @@ data class CreatorChannelDonationResponse(
|
|||||||
- 후원 item UI는 기존 홈 탭 구현과 중복을 줄이기 위해 공용 컴포넌트로 추출하거나, 홈/후원 탭 양쪽에서 같은 하위 컴포넌트를 사용하도록 최소 범위로 조정한다.
|
- 후원 item UI는 기존 홈 탭 구현과 중복을 줄이기 위해 공용 컴포넌트로 추출하거나, 홈/후원 탭 양쪽에서 같은 하위 컴포넌트를 사용하도록 최소 범위로 조정한다.
|
||||||
- 신규 문구가 필요하면 `SodaLive/Sources/I18n/I18n.swift`에 ko/en/ja를 추가한다.
|
- 신규 문구가 필요하면 `SodaLive/Sources/I18n/I18n.swift`에 ko/en/ja를 추가한다.
|
||||||
- Empty 문구는 `pick(ko:en:ja:)`로 관리하고, 후원 버튼은 기존 `I18n.ContentDetail.DonationDialog.title`을 재사용한다. 신규 후원 버튼 문구, route, API, dependency는 추가하지 않는다.
|
- Empty 문구는 `pick(ko:en:ja:)`로 관리하고, 후원 버튼은 기존 `I18n.ContentDetail.DonationDialog.title`을 재사용한다. 신규 후원 버튼 문구, route, API, dependency는 추가하지 않는다.
|
||||||
|
- 랭킹 title은 `I18n.Explorer.donationRankingTitle`, 전체보기는 `I18n.CreatorChannelHome.viewAll`, can pill은 `I18n.LiveChat.canWithUnit(_:)`을 재사용한다.
|
||||||
|
- 빈 후원 메시지 fallback만 `I18n.CreatorChannelDonation.defaultMessage(_:)`로 추가하며 신규 dependency나 공용 formatter abstraction은 만들지 않는다.
|
||||||
- 프로젝트 설정 변경은 신규 Swift 파일 target 등록이 필요한 경우에만 수행한다.
|
- 프로젝트 설정 변경은 신규 Swift 파일 target 등록이 필요한 경우에만 수행한다.
|
||||||
|
|
||||||
## 8. Open Questions
|
## 8. Open Questions
|
||||||
@@ -190,3 +199,13 @@ data class CreatorChannelDonationResponse(
|
|||||||
- 2026-07-04: Figma `get_design_context`와 `get_screenshot`으로 empty view `290:9009`를 확인했다.
|
- 2026-07-04: Figma `get_design_context`와 `get_screenshot`으로 empty view `290:9009`를 확인했다.
|
||||||
- 2026-07-04: `UserProfileDonationAllView`와 `AppStep.userProfileDonationAll(userId:)` 기존 라우팅을 확인해 랭킹 전체보기 재사용 경계를 정했다.
|
- 2026-07-04: `UserProfileDonationAllView`와 `AppStep.userProfileDonationAll(userId:)` 기존 라우팅을 확인해 랭킹 전체보기 재사용 경계를 정했다.
|
||||||
- 2026-08-14: 사용자 인터뷰로 후원 탭 empty 문구의 ko/en/ja 값을 확정했다. `후원하기`는 기존 `I18n.ContentDetail.DonationDialog.title`을 재사용하며 이번 작업에서는 Swift 구현을 수행하지 않는다.
|
- 2026-08-14: 사용자 인터뷰로 후원 탭 empty 문구의 ko/en/ja 값을 확정했다. `후원하기`는 기존 `I18n.ContentDetail.DonationDialog.title`을 재사용하며 이번 작업에서는 Swift 구현을 수행하지 않는다.
|
||||||
|
- 2026-08-26: `CreatorChannelDonationRankingSection`, 공용 `CreatorChannelDonationCard`, 두 컴포넌트의 호출 경로를 정적 점검해 런타임 하드코딩 4곳을 확정했다. Preview fixture의 샘플 문구는 런타임 제품 문구가 아니므로 제외했다.
|
||||||
|
- 2026-08-26: 사용자 인터뷰로 랭킹 title은 기존 `I18n.Explorer.donationRankingTitle`을 재사용하고, 빈 후원 메시지는 카드 전용 ko/en/ja 문구를 추가하기로 결정했다. 이번 작업에서는 문서만 갱신하고 Swift·I18n 구현과 빌드는 수행하지 않는다.
|
||||||
|
- 2026-08-26: Phase 7의 다국어 요구사항을 구현했다. `§5.3`, `§5.4`, `§6`, `§7`의 랭킹 title·전체보기·can pill·빈 메시지 fallback과 홈·후원 탭 공용 적용을 정적 계약, Debug 빌드, 독립 리뷰로 확인했다. 이 PRD에는 체크박스 항목이 없으며, 구현 체크 상태는 `plan-task.md` Phase 7에 기록했다. 사용자 요청에 따라 visual QA는 실행하지 않았다.
|
||||||
|
|
||||||
|
## 10. Decision Log
|
||||||
|
|
||||||
|
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 범위 |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| 2026-08-26 | DEC-DONATION-I18N-001 | 확정 | 랭킹 title은 기존 `I18n.Explorer.donationRankingTitle`을 재사용한다. | 사용자 인터뷰에서 기존 `후원랭킹` 표기를 선택했다. | Ranking section |
|
||||||
|
| 2026-08-26 | DEC-DONATION-I18N-002 | 확정 | 빈 후원 메시지는 `I18n.CreatorChannelDonation.defaultMessage(_:)` 전용 문구로 관리한다. | 공개 카드에서 `You donated`가 작성자를 오인하게 하므로 사용자 인터뷰에서 전용 문구를 선택했다. | Home·Donation 공용 card |
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
# 후원 랭킹·공용 카드 다국어 점검 보고서
|
||||||
|
|
||||||
|
## 1. 리뷰 정보
|
||||||
|
|
||||||
|
| 항목 | 내용 |
|
||||||
|
|---|---|
|
||||||
|
| 리뷰 대상 | Phase 3 / Task 3.2~3.3 및 직접 호출 경로 |
|
||||||
|
| 기준 commit 또는 working tree | `e46198f87a1b6a4357f5bb5b5358e8635383b3a6`; 사용자 소유 `project.pbxproj`, `CreatorChannelDonationRankingSection.swift` 변경은 리뷰 범위에서 보존 |
|
||||||
|
| 리뷰 일자 | 2026-08-26 |
|
||||||
|
| 리뷰어 | Codex |
|
||||||
|
| 기준 문서 | `docs/20260704_크리에이터_채널_후원_탭/prd.md`, `docs/20260704_크리에이터_채널_후원_탭/plan-task.md` |
|
||||||
|
| 리뷰 상태 | 판정 완료, 구현 대기 |
|
||||||
|
|
||||||
|
## 2. 리뷰 목적과 범위
|
||||||
|
|
||||||
|
### 목적
|
||||||
|
|
||||||
|
- `CreatorChannelDonationRankingSection`과 `CreatorChannelDonationCard`의 런타임 고정 문구가 앱 언어 설정을 따르는지 확인한다.
|
||||||
|
- 두 컴포넌트의 직접 호출 경로에 같은 유형의 미적용 문구가 있는지 확인한다.
|
||||||
|
|
||||||
|
### 포함 범위
|
||||||
|
|
||||||
|
- 코드: `CreatorChannelDonationRankingSection.swift`, `CreatorChannelDonationCard.swift`
|
||||||
|
- 호출 경로: `CreatorChannelDonationTabView.swift`, `CreatorChannelDonationCountBar.swift`, `CreatorChannelDonationEmptyView.swift`, `CreatorChannelDonationFloatingButton.swift`, `CreatorChannelDonationSection.swift`, `CreatorChannelView.swift`
|
||||||
|
- 공용 경계: `DateParser.relativeTimeText`, 관련 `I18n` 정의
|
||||||
|
|
||||||
|
### 제외 범위
|
||||||
|
|
||||||
|
- `PreviewProvider`의 닉네임·메시지 등 개발용 fixture
|
||||||
|
- 후원 기능 밖의 크리에이터 채널 섹션과 기존 전역 I18n 문구 품질
|
||||||
|
- Swift·I18n 구현, Simulator 실행, 빌드
|
||||||
|
|
||||||
|
## 3. 판정 기준
|
||||||
|
|
||||||
|
런타임에서 사용자가 보는 고정 문구가 `I18n`을 거치지 않아 ko/en/ja 설정에 따라 바뀌지 않으면 확정 문제로 판정한다. 서버 데이터, 숫자 순위, URL, asset name과 Preview fixture는 하드코딩 다국어 대상에서 제외한다.
|
||||||
|
|
||||||
|
## 4. 검토한 근거
|
||||||
|
|
||||||
|
### 문서와 코드
|
||||||
|
|
||||||
|
- 요구사항: PRD `§5.3`, `§5.4`, `§7`
|
||||||
|
- 계획: 기존 `Task 3.2`, `Task 3.3`, 신규 `P7-R1`
|
||||||
|
- 코드: Ranking section의 title·action, 공용 card의 can pill·빈 메시지 fallback
|
||||||
|
- 호출 관계: 공용 card는 후원 탭과 홈 탭에서 모두 사용한다.
|
||||||
|
|
||||||
|
### 실행 환경
|
||||||
|
|
||||||
|
```text
|
||||||
|
OS: macOS 26.0 (25A354)
|
||||||
|
검증 방식: rg·sed·git diff 기반 정적 점검
|
||||||
|
Simulator/빌드: 사용자 지시에 따라 미실행
|
||||||
|
```
|
||||||
|
|
||||||
|
### 실행한 검증
|
||||||
|
|
||||||
|
| 명령 또는 수동 검증 | 결과 | 핵심 증거 |
|
||||||
|
|---|---|---|
|
||||||
|
| `rg -n --glob '*.swift' 'Text\(|I18n\.|relativeTimeText' <대상 및 호출 경로>` | 성공 | 랭킹 2곳, 카드 2곳의 런타임 하드코딩 확인 |
|
||||||
|
| `rg -n 'CreatorChannelDonationRankingSection\|CreatorChannelDonationCard' SodaLive/Sources` | 성공 | 후원 탭·홈 탭 직접 호출 위치 확인 |
|
||||||
|
| `sed -n`으로 관련 View와 `DateParser.swift`, `I18n.swift` 확인 | 성공 | count bar·empty·dialog·상대 시간은 이미 I18n 사용 |
|
||||||
|
| ko/en/ja Simulator 확인 | 불가 | 이번 작업은 문서 전용이며 코드 구현을 금지함 |
|
||||||
|
|
||||||
|
## 5. 발견 사항 요약
|
||||||
|
|
||||||
|
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| `REV-P3-001` | Medium | 확정 | 랭킹 title과 전체보기 action이 한국어로 고정됨 | `Task 7.1` | `P7-R1` |
|
||||||
|
| `REV-P3-002` | Medium | 확정 | 공용 카드의 can pill과 빈 메시지가 한국어로 고정됨 | `Task 7.1` | `P7-R1` |
|
||||||
|
|
||||||
|
## 6. 발견 사항 상세
|
||||||
|
|
||||||
|
### REV-P3-001 — 랭킹 title과 전체보기 action이 한국어로 고정됨
|
||||||
|
|
||||||
|
- **심각도:** Medium
|
||||||
|
- **상태:** 확정
|
||||||
|
- **관련 요구사항:** PRD `§5.3`
|
||||||
|
- **관련 계약:** 없음
|
||||||
|
- **소유 Task:** `Task 7.1` / `P7-R1`
|
||||||
|
|
||||||
|
**관찰 내용**
|
||||||
|
|
||||||
|
`CreatorChannelDonationRankingSection`의 title과 action label이 문자열 literal을 사용해 영어·일본어 설정에서도 한국어로 표시된다.
|
||||||
|
|
||||||
|
**근거**
|
||||||
|
|
||||||
|
- 코드: `CreatorChannelDonationRankingSection.swift:10`, `:22`
|
||||||
|
- 기존 I18n: `I18n.Explorer.donationRankingTitle`, `I18n.CreatorChannelHome.viewAll`
|
||||||
|
- 인터뷰 결정: title은 기존 `후원랭킹` 키를 재사용한다.
|
||||||
|
|
||||||
|
**재현 또는 검증 절차**
|
||||||
|
|
||||||
|
1. 소스에서 `Text("후원랭킹")`, `Text("전체보기")`를 확인한다.
|
||||||
|
2. 두 값이 `LanguageHeaderProvider.current`를 읽는 `I18n`을 거치지 않는 것을 확인한다.
|
||||||
|
3. 앱 언어 변경과 무관하게 literal이 유지되는 구조임을 판정한다.
|
||||||
|
|
||||||
|
**영향**
|
||||||
|
|
||||||
|
후원 랭킹이 표시되는 채널에서 영어·일본어 사용자에게 한국어 title과 action이 노출된다.
|
||||||
|
|
||||||
|
**권장 조치**
|
||||||
|
|
||||||
|
기존 `I18n.Explorer.donationRankingTitle`과 `I18n.CreatorChannelHome.viewAll`을 직접 재사용한다. 신규 title·action 키는 추가하지 않는다.
|
||||||
|
|
||||||
|
**판정 기록**
|
||||||
|
|
||||||
|
- 2026-08-26 — 정적 코드와 기존 I18n 정의로 확정.
|
||||||
|
- 2026-08-26 — 사용자 인터뷰에서 기존 `후원랭킹` 키 재사용을 확정.
|
||||||
|
|
||||||
|
### REV-P3-002 — 공용 카드의 can pill과 빈 메시지가 한국어로 고정됨
|
||||||
|
|
||||||
|
- **심각도:** Medium
|
||||||
|
- **상태:** 확정
|
||||||
|
- **관련 요구사항:** PRD `§5.4`
|
||||||
|
- **관련 계약:** 없음
|
||||||
|
- **소유 Task:** `Task 7.1` / `P7-R1`
|
||||||
|
|
||||||
|
**관찰 내용**
|
||||||
|
|
||||||
|
`CreatorChannelDonationCard`의 can pill과 빈 `message` fallback이 한국어 literal을 조합한다. 이 카드는 후원 탭과 홈 탭에서 공용이므로 두 화면이 함께 영향을 받는다.
|
||||||
|
|
||||||
|
**근거**
|
||||||
|
|
||||||
|
- 코드: `CreatorChannelDonationCard.swift:60`, `:75`
|
||||||
|
- 호출: `CreatorChannelDonationTabView.swift:38`, `CreatorChannelDonationSection.swift:38`
|
||||||
|
- 기존 I18n: `I18n.LiveChat.canWithUnit(_:)`은 동일한 ko/en/ja can 형식을 제공한다.
|
||||||
|
- 인터뷰 결정: 공개 카드의 영어 fallback은 `Donated {can} cans.` 전용 문구를 사용한다.
|
||||||
|
|
||||||
|
**재현 또는 검증 절차**
|
||||||
|
|
||||||
|
1. 소스에서 `Text("\(donation.can)캔")`과 `"\(can)캔을 후원하였습니다"`를 확인한다.
|
||||||
|
2. `message`가 공백이면 fallback이 런타임에 표시되는 것을 코드 경로로 확인한다.
|
||||||
|
3. 공용 카드의 두 호출 위치를 확인한다.
|
||||||
|
|
||||||
|
**영향**
|
||||||
|
|
||||||
|
영어·일본어 사용자에게 can 단위와 빈 메시지가 한국어로 표시되며, 홈 탭과 후원 탭에 동시에 노출된다.
|
||||||
|
|
||||||
|
**권장 조치**
|
||||||
|
|
||||||
|
can pill은 `I18n.LiveChat.canWithUnit(_:)`을 재사용한다. 빈 메시지만 `I18n.CreatorChannelDonation.defaultMessage(_:)`로 추가하고 ko `{can}캔을 후원하였습니다`, en `Donated {can} cans.`, ja `{can}canをサポートしました。`를 제공한다.
|
||||||
|
|
||||||
|
**판정 기록**
|
||||||
|
|
||||||
|
- 2026-08-26 — 정적 코드와 두 직접 호출 위치로 확정.
|
||||||
|
- 2026-08-26 — 사용자 인터뷰에서 카드 전용 fallback을 확정.
|
||||||
|
|
||||||
|
### 연결 경로 추가 점검
|
||||||
|
|
||||||
|
- `CreatorChannelDonationCountBar`: `I18n.CreatorChannelCommunity.totalLabel` 사용.
|
||||||
|
- `CreatorChannelDonationEmptyView`: empty 문구와 후원 action 모두 I18n 사용.
|
||||||
|
- `CreatorChannelDonationFloatingButton`: 사용자 노출 텍스트가 없는 icon-only 컴포넌트.
|
||||||
|
- `CreatorChannelDonationSection`: section·empty·후원 action 문구 모두 I18n 사용.
|
||||||
|
- `CreatorChannelView`: 연결된 후원 dialog 문구는 I18n 사용.
|
||||||
|
- `DateParser.relativeTimeText`: `I18n.Time`을 사용.
|
||||||
|
|
||||||
|
추가 확정 발견 사항은 없다.
|
||||||
|
|
||||||
|
## 7. 확정 항목의 plan·goal 전환
|
||||||
|
|
||||||
|
`plan-task.md`에 `Phase 7`, `Task 7.1`, `P7-R1`을 추가했다. 사용자 지시에 따라 goal 생성과 구현은 수행하지 않았다.
|
||||||
|
|
||||||
|
## 8. 리뷰 종료 판정
|
||||||
|
|
||||||
|
| 판정 항목 | 결과 | 근거 |
|
||||||
|
|---|---|---|
|
||||||
|
| 리뷰 범위 전체 확인 | 충족 | 두 대상과 직접 호출·공용 시간 경계 확인 |
|
||||||
|
| 후보 항목 판정 완료 | 충족 | `REV-P3-001`, `REV-P3-002` 확정 |
|
||||||
|
| 확정 항목 plan 반영 | 충족 | `Phase 7 / Task 7.1 / P7-R1` |
|
||||||
|
| 보류 항목의 담당·재개 조건 기록 | 해당 없음 | 인터뷰 결정 완료 |
|
||||||
|
| 검증 명령과 결과 기록 | 충족 | 정적 점검 명령과 Simulator 미실행 사유 기록 |
|
||||||
|
|
||||||
|
**최종 결론:** 수정 goal 필요
|
||||||
|
|
||||||
|
**남은 항목:** `P7-R1`의 Swift·I18n 구현과 ko/en/ja Gate 검증. 이번 작업에서는 수행하지 않는다.
|
||||||
|
|
||||||
|
## 9. 수정 후 검증 기록
|
||||||
|
|
||||||
|
- 2026-08-26 (구현 전): 아직 구현하지 않아 기록 없음.
|
||||||
|
- 2026-08-26: `REV-P3-001`, `REV-P3-002`를 `Task 7.1`에서 수정했다. 랭킹 title·전체보기 action·can pill은 확정된 기존 I18n을 재사용하고, 공용 카드 빈 메시지 fallback만 `I18n.CreatorChannelDonation.defaultMessage(_:)`로 추가했다.
|
||||||
|
- 2026-08-26: 변경 전 정적 계약은 랭킹과 카드 모두 exit 1, 변경 후 Phase 7 Gate와 `git diff --check`는 exit 0이었다. `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`는 `BUILD SUCCEEDED`, 독립 리뷰는 `APPROVED`였다.
|
||||||
|
- 2026-08-26: 사용자 요청에 따라 visual QA를 실행하지 않았다. 따라서 ko/en/ja 실제 화면 수동 검증은 미실행 상태로 `plan-task.md`에 남겼다.
|
||||||
Reference in New Issue
Block a user