From 2203fc9089c3cd03b0500be07596f2a5abf15eb4 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Fri, 14 Aug 2026 18:40:47 +0900 Subject: [PATCH] =?UTF-8?q?fix(i18n):=20=ED=8C=AC=EB=A0=88=ED=84=B0=20?= =?UTF-8?q?=ED=83=AD=20=EB=B9=88=20=EC=83=81=ED=83=9C=EB=A5=BC=20=EB=8B=A4?= =?UTF-8?q?=EA=B5=AD=EC=96=B4=ED=99=94=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SodaLive/Sources/I18n/I18n.swift | 8 ++++ .../CreatorChannelFanTalkEmptyView.swift | 6 +-- .../plan-task.md | 44 ++++++++++++++++++- .../20260704_크리에이터_채널_팬Talk_탭/prd.md | 32 +++++++++----- 4 files changed, 75 insertions(+), 15 deletions(-) diff --git a/SodaLive/Sources/I18n/I18n.swift b/SodaLive/Sources/I18n/I18n.swift index ef96fd66..fe27f7fc 100644 --- a/SodaLive/Sources/I18n/I18n.swift +++ b/SodaLive/Sources/I18n/I18n.swift @@ -3519,6 +3519,14 @@ If you block this user, the following features will be restricted. } enum CreatorChannelFanTalk { + static var emptyMessage: String { + pick( + ko: "아직 응원이 없습니다.\n처음으로 크리에이터를 응원해 보세요!", + en: "No fan letters yet.\nBe the first to send one!", + ja: "まだファンレターがありません。\n最初のファンレターを送ってみましょう!" + ) + } + static var writeAction: String { pick(ko: "응원남기기", en: "Send a fan letter", ja: "ファンレターを送る") } static var send: String { pick(ko: "보내기", en: "Send", ja: "送信") } static var exitModalTitle: String { pick(ko: "입력 종료", en: "Discard input", ja: "入力終了") } static var exitModalMessage: String { pick(ko: "입력을 종료할까요?\n지금 나가면 입력한 내용이 저장되지 않아요.", en: "Discard your input?\nIf you leave now, your input will not be saved.", ja: "入力を終了しますか?\n終了すると、入力内容は保存されません。") } diff --git a/SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift b/SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift index 9a274f15..66a68dfe 100644 --- a/SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift +++ b/SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift @@ -6,7 +6,7 @@ struct CreatorChannelFanTalkEmptyView: View { var body: some View { VStack(spacing: SodaSpacing.s20) { - Text("아직 응원이 없습니다.\n처음으로 크리에이터를 응원해 보세요!") + Text(I18n.CreatorChannelFanTalk.emptyMessage) .appFont(size: 16, weight: .medium) .foregroundColor(Color.gray500) .multilineTextAlignment(.center) @@ -22,14 +22,14 @@ struct CreatorChannelFanTalkEmptyView: View { .scaledToFit() .frame(width: 20, height: 20) - Text("응원 남기기") + Text(I18n.CreatorChannelFanTalk.writeAction) .appFont(size: 16, weight: .medium) .foregroundColor(.white) .lineLimit(1) } .padding(.horizontal, SodaSpacing.s20) .frame(height: 44) - .background(Color.soda400) + .background(Color.gray800) .clipShape(Capsule()) } .buttonStyle(.plain) diff --git a/docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md b/docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md index efe8992c..84bd091f 100644 --- a/docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md +++ b/docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md @@ -33,9 +33,9 @@ - 글쓰기와 수정하기 action은 이번에 작성하는 팬Talk 쓰기 페이지로 연결한다. - 작성 API는 기존 팬Talk API인 `ExplorerRepository.writeCheers(parentCheersId:creatorId:content:)`를 사용한다. - 수정 API는 기존 팬Talk API인 `ExplorerRepository.modifyCheers(cheersId:content:isActive:)`를 사용하고 `isActive`는 `nil`로 전달한다. -- 내 채널인 경우 우측하단 글쓰기 floating button과 empty view 내부 `응원 남기기` 버튼을 모두 표시하지 않는다. +- 내 채널인 경우 우측하단 글쓰기 floating button과 empty view 내부 `응원남기기` 버튼을 모두 표시하지 않는다. - 우측하단 plus button은 `CreatorChannelFloatingActionMenu`의 private `floatingButton`과 동일한 UI이므로, 팬Talk 전용으로 별도 구현하지 않고 `CreatorChannel/Components` 하위 공용 icon floating button으로 추출해 재사용한다. -- 팬Talk 쓰기 화면은 크리에이터 채널 팬Talk 탭의 우측하단 plus button, empty state의 `응원 남기기` 버튼, 내가 쓴 글의 `수정하기`에서 진입한다. +- 팬Talk 쓰기 화면은 크리에이터 채널 팬Talk 탭의 우측하단 plus button, empty state의 `응원남기기` 버튼, 내가 쓴 글의 `수정하기`에서 진입한다. - 팬Talk 쓰기 화면 진입 시 키보드가 올라온 상태를 기본으로 하며, 입력 값 유무에 따라 보내기 버튼 disabled/enabled 상태를 분리한다. - 입력 값이 있는 상태에서 취소 버튼을 터치하면 입력 종료 안내 모달을 표시한다. 입력 값이 없으면 별도 모달 없이 이전 화면으로 닫는다. - 공용 모달은 요청 명칭상 `V2/Components` 대상이지만, 현재 저장소 경로 관례에 맞춰 `SodaLive/Sources/V2/Component/Modal/**` 아래에 작성한다. @@ -449,3 +449,43 @@ - 2026-07-07: Reviewer gate 지적에 따라 PRD 본문 요구사항/성공 기준/제약 조건에 삭제 확인 모달의 상위 `CreatorChannelView` 표시, `SodaV2ActionModal` 사용, list/detail anchored context popup 공용화, `CheersReportDialogView` 제외 범위를 명시했다. - 2026-07-07: 사용자 후속 요청에 따라 팬Talk 목록 row anchored context popup 문구를 `수정`/`삭제`에서 `수정하기`/`삭제하기`로 변경했다. 상세 popup 기본 문구는 기존 공용 기본값을 유지하고, 팬Talk row 호출부에서 `I18n.Explorer.editAction`, `I18n.Explorer.deleteAction`을 주입한다. + +### Phase 11: 팬레터 empty state 다국어 보강 + +- [x] **Task 11.1: 팬레터 empty state 문구 정의** + - 대상 파일: + - 수정: `SodaLive/Sources/I18n/I18n.swift` + - 작업 내용: + - Empty 문구를 한국어 `아직 응원이 없습니다.\n처음으로 크리에이터를 응원해 보세요!`, 영어 `No fan letters yet.\nBe the first to send one!`, 일본어 `まだファンレターがありません。\n最初のファンレターを送ってみましょう!`로 정의한다. + - 작성 버튼을 한국어 `응원남기기`, 영어 `Send a fan letter`, 일본어 `ファンレターを送る`로 정의한다. + - 지정된 `\n` 줄바꿈을 모든 언어에서 유지한다. + - 검증 기준: + - 실행 명령: `rg -n "아직 응원이 없습니다|No fan letters yet|まだファンレターがありません|응원남기기|Send a fan letter|ファンレターを送る" SodaLive/Sources/I18n/I18n.swift` + - 기대 결과: empty 문구와 작성 버튼의 ko/en/ja 값이 `pick(ko:en:ja:)`에서 확인된다. + +- [x] **Task 11.2: 팬레터 empty View에 I18n 적용** + - 대상 파일: + - 수정: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift` + - 확인: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift` + - 확인: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift` + - 작업 내용: + - 하드코딩된 empty 문구와 `응원 남기기` 버튼을 Task 11.1의 I18n 값으로 교체한다. + - 기존 팬레터 작성 route 연결과 내 채널의 empty 버튼·floating button 숨김 규칙을 유지한다. + - empty 작성 버튼은 `Color.gray800` 배경과 white 아이콘·텍스트를 사용한다. + - 신규 route나 API를 추가하지 않는다. + - 검증 기준: + - 실행 명령: `rg -n "CreatorChannelFanTalkEmptyView|I18n\.|showsWriteButton|onTapWrite|creatorChannelFanTalkWrite|Color\.gray800|foregroundColor\(\.white\)|응원 남기기" SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift` + - 기대 결과: View는 I18n 값을 사용하고 기존 작성 route와 내 채널 숨김 조건 및 gray800/white 버튼 색상을 유지하며 하드코딩된 `응원 남기기`가 없다. + +- [x] **Task 11.3: 정적 검사와 Debug 빌드 검증** + - 대상 파일: + - 확인: `SodaLive/Sources/I18n/I18n.swift` + - 확인: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift` + - 검증 기준: + - 실행 명령: `git diff --check` + - 기대 결과: 출력 없이 exit code 0이다. + - 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` + - 기대 결과: `** BUILD SUCCEEDED **`. + +- 2026-08-14: 사용자 인터뷰로 팬레터 탭 empty 문구와 작성 버튼의 ko/en/ja 값을 확정하고 Phase 11에 미완료 후속 Task로 추가했다. 기존 작성 route와 내 채널 버튼 숨김 규칙은 유지하며 이번 작업은 문서만 보강했다. +- 2026-08-14: Phase 11 구현을 완료했다. 팬레터 탭 empty 문구와 작성 버튼을 ko/en/ja I18n으로 교체하고 기존 작성 route 및 내 채널 버튼 숨김 규칙을 유지했다. 사용자 후속 요청에 따라 작성 버튼은 `Color.gray800` 배경과 white 아이콘·텍스트로 변경했다. 23개 정적 계약, `git diff --check`, iPhone 16 Pro 시뮬레이터 Debug 빌드가 통과했고, 세 언어 재캡처와 두 독립 색상·CJK 리뷰에서 차단 항목 없이 PASS를 확인했다. diff --git a/docs/20260704_크리에이터_채널_팬Talk_탭/prd.md b/docs/20260704_크리에이터_채널_팬Talk_탭/prd.md index 9ed8ef0d..446a14d1 100644 --- a/docs/20260704_크리에이터_채널_팬Talk_탭/prd.md +++ b/docs/20260704_크리에이터_채널_팬Talk_탭/prd.md @@ -24,6 +24,7 @@ Figma 참조: - 기존 홈 탭에는 최신 팬Talk 요약만 있고, 팬Talk 탭 전용 목록 API, 페이징, 답글 표시, 신고/삭제 액션이 없다. - 기존 프로필 FanTalk 구현은 구 API와 구 UI 구조를 사용하므로 V2 크리에이터 채널 탭에 그대로 붙이기 어렵다. - 팬Talk 탭의 글쓰기 floating button과 empty state `응원 남기기` 버튼이 실제 글 입력 화면으로 연결되지 않는다. +- 팬레터 탭 empty 문구와 작성 버튼이 한국어로 하드코딩되어 앱 언어 설정에 맞게 표시되지 않는다. - 내가 쓴 팬Talk의 `수정하기` 액션이 실제 수정 화면으로 연결되지 않는다. - V2 화면에서 재사용할 수 있는 Figma 기준 액션 모달이 없어 입력 종료 안내 모달을 일관되게 표시하기 어렵다. @@ -38,9 +39,9 @@ Figma 참조: - 내가 쓴 글의 더보기 메뉴에는 공용 `I18n` 문구 기준의 `수정`, `삭제` 액션을 표시한다. - 내가 쓴 글이 아니지만 내 채널인 경우 더보기 메뉴에는 공용 `I18n` 문구 기준의 `삭제` 액션만 표시한다. - `삭제` 액션은 기존 FanTalk에서 사용하던 삭제 API와 동일하게 `ExplorerRepository.modifyCheers(..., isActive: false)`를 사용한다. -- `fanTalkCount == 0`이면 Figma empty view(`290:9001`)를 표시한다. -- 내 채널인 경우 우측하단 글쓰기 floating button과 empty view 내부 `응원 남기기` 버튼을 모두 표시하지 않는다. -- 크리에이터 채널 팬Talk 탭의 우측하단 plus button과 empty state `응원 남기기` 버튼에서 팬Talk 글 입력 화면으로 진입한다. +- `fanTalkCount == 0`이면 Figma empty view(`290:9001`)를 표시하고 지정된 ko/en/ja 문구를 사용한다. +- 내 채널인 경우 우측하단 글쓰기 floating button과 empty view 내부 `응원남기기` 버튼을 모두 표시하지 않는다. +- 크리에이터 채널 팬Talk 탭의 우측하단 plus button과 empty state `응원남기기` 버튼에서 팬Talk 글 입력 화면으로 진입한다. - 팬Talk 글 입력 화면은 입력 값 없음, 입력 값 있음, 키보드 없음 상태를 Figma 기준으로 표시한다. - 입력 값이 없으면 보내기 버튼을 비활성화하고, 입력 값이 있으면 보내기 버튼을 활성화한다. - 입력 값이 있는 상태에서 취소 버튼을 터치하면 입력 종료 안내 모달을 표시한다. @@ -152,9 +153,16 @@ data class CreatorChannelFanTalkReplyResponse( ### 5.7 Empty and writing button - `fanTalkCount == 0`이면 Figma `290:9001` 기준 empty view를 표시한다. -- Empty 문구는 `아직 응원이 없습니다.\n처음으로 크리에이터를 응원해 보세요!`를 표시한다. -- Empty view에는 `+` 아이콘과 `응원 남기기` 텍스트가 있는 capsule button을 표시한다. -- 단, 내 채널인 경우 empty view 내부 `응원 남기기` 버튼을 표시하지 않는다. +- Empty 문구는 아래 언어별 값을 사용한다. `\n`은 명시적 줄바꿈이다. + - 한국어: `아직 응원이 없습니다.\n처음으로 크리에이터를 응원해 보세요!` + - 영어: `No fan letters yet.\nBe the first to send one!` + - 일본어: `まだファンレターがありません。\n最初のファンレターを送ってみましょう!` +- Empty view에는 `+` 아이콘과 아래 언어별 텍스트가 있는 capsule button을 표시한다. + - 한국어: `응원남기기` + - 영어: `Send a fan letter` + - 일본어: `ファンレターを送る` +- Empty view의 작성 버튼은 `Color.gray800` 배경과 white 아이콘·텍스트를 사용한다. +- 단, 내 채널인 경우 empty view 내부 `응원남기기` 버튼을 표시하지 않는다. - `fanTalkCount > 0`이면 홈 탭 plus button 위치에 글쓰기 floating button을 표시한다. - 단, 내 채널인 경우 글쓰기 floating button을 표시하지 않는다. - 글쓰기 floating button은 `CreatorChannelFloatingActionMenu` 내부 `floatingButton`과 동일한 66pt 원형 버튼, 38pt 아이콘, `Color.soda400` 배경을 사용한다. @@ -171,10 +179,10 @@ data class CreatorChannelFanTalkReplyResponse( ### 5.9 FanTalk write/modify entry - 쓰기 페이지는 크리에이터 채널 팬Talk 탭에서 진입한다. -- 진입 경로는 `fanTalkCount > 0`일 때 표시되는 우측하단 plus button과 `fanTalkCount == 0`일 때 empty state에 표시되는 `응원 남기기` 버튼이다. +- 진입 경로는 `fanTalkCount > 0`일 때 표시되는 우측하단 plus button과 `fanTalkCount == 0`일 때 empty state에 표시되는 `응원남기기` 버튼이다. - 내가 쓴 글의 더보기 메뉴에서 `수정하기` 액션을 터치하면 같은 쓰기 페이지를 수정 모드로 연다. - 수정 모드로 진입할 때는 수정 대상 `fanTalkId`와 기존 글 content를 반드시 함께 전달한다. -- 내 채널인 경우 기존 규칙대로 plus button과 empty state `응원 남기기` 버튼을 모두 표시하지 않으므로 작성 모드로 진입할 수 없다. +- 내 채널인 경우 기존 규칙대로 plus button과 empty state `응원남기기` 버튼을 모두 표시하지 않으므로 작성 모드로 진입할 수 없다. - 쓰기 route는 `AppStep`에 추가하고 `creatorId`, 작성/수정 mode, 수정 대상 `fanTalkId`, 초기 content, `onSuccess`를 전달할 수 있게 한다. - 작성 모드에서는 `fanTalkId`와 초기 content를 전달하지 않거나 비어 있는 값으로 전달한다. - 수정 모드에서는 `fanTalkId`가 0보다 커야 하고 초기 content가 기존 글 content와 같아야 한다. @@ -224,10 +232,11 @@ data class CreatorChannelFanTalkReplyResponse( - 팬Talk 탭 목록의 삭제 confirm dialog는 `CreatorChannelView` 상위 wrapping `ZStack`에서 표시되어 긴 목록 스크롤 위치와 무관하게 화면 기준으로 중앙 표시된다. - 삭제 confirm 시 기존 FanTalk 삭제 API가 호출되고 성공 후 첫 페이지가 갱신된다. - `fanTalkCount == 0`이면 Figma `290:9001` empty view가 표시된다. -- 내 채널인 경우 empty view 내부 `응원 남기기` 버튼과 우측하단 글쓰기 floating button이 모두 표시되지 않는다. +- Empty 문구와 작성 버튼이 지정된 ko/en/ja 값 및 줄바꿈으로 표시된다. +- 내 채널인 경우 empty view 내부 `응원남기기` 버튼과 우측하단 글쓰기 floating button이 모두 표시되지 않는다. - `fanTalkCount > 0`이고 내 채널이 아니면 우측하단 글쓰기 floating button이 표시된다. - `fanTalkCount > 0`이고 내 채널이 아니면 우측하단 글쓰기 floating button tap 시 팬Talk 글 입력 화면으로 이동한다. -- `fanTalkCount == 0`이고 내 채널이 아니면 empty state `응원 남기기` 버튼 tap 시 팬Talk 글 입력 화면으로 이동한다. +- `fanTalkCount == 0`이고 내 채널이 아니면 empty state `응원남기기` 버튼 tap 시 팬Talk 글 입력 화면으로 이동한다. - 팬Talk 글 입력 화면은 입력 값이 없을 때 보내기 버튼을 비활성화한다. - 팬Talk 글 입력 화면은 입력 값이 있을 때 보내기 버튼을 활성화한다. - 입력 값이 있는 상태에서 취소 버튼을 터치하면 입력 종료 안내 모달이 표시된다. @@ -250,6 +259,7 @@ data class CreatorChannelFanTalkReplyResponse( - 신고 기능 재사용을 위해 기존 `ReportRepository`, `ReportRequest`, `CheersReportDialogView`를 사용한다. - `CheersReportDialogView`는 신고 사유 선택 전용 UI이므로 이번 확인형 V2 popup dialog 전환 범위에서 제외한다. - 신규 문구가 필요하면 `SodaLive/Sources/I18n/I18n.swift`에 ko/en/ja를 추가한다. +- Empty 문구와 `응원남기기` 버튼은 View에 직접 작성하지 않고 기존 크리에이터 채널 I18n 구조의 `pick(ko:en:ja:)` 값으로 관리한다. - 프로젝트 설정 변경은 신규 Swift 파일 target 등록이 필요한 경우에만 수행한다. ## 8. Assumptions @@ -272,3 +282,5 @@ data class CreatorChannelFanTalkReplyResponse( - 2026-07-06: `rg '수정 모드.*fanTalkId|fanTalkId.*기존 글 content|fanTalkId.*초기 content|fanTalkId.*initialContent|기존 content.*content 초기값|수정 대상.*fanTalkId.*기존 글 content|fanTalkId.*0보다 커야' docs/20260704_크리에이터_채널_팬Talk_탭/prd.md docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md`로 수정 모드 route에 `fanTalkId`와 기존 글 content를 함께 전달한다는 조건을 확인했다. - 2026-07-07: 사용자 후속 요청에 따라 팬Talk row 더보기의 삭제 확인 dialog는 팬Talk 탭 내부가 아니라 `CreatorChannelView` 상위 wrapping `ZStack`에서 표시한다. 삭제 확인 UI는 이번에 추가된 V2 공용 모달 `SodaV2ActionModal`을 사용하며, 팬Talk 리스트와 상세보기의 anchored context popup은 동일한 공용 컴포넌트와 동일한 `I18n` 문구를 사용한다. 신고 사유 선택 UI인 `CheersReportDialogView`는 기존 신고 플로우 재사용 범위로 유지하고 이번 확인형 V2 popup dialog 전환 범위에서 제외한다. +- 2026-08-14: 사용자 인터뷰로 팬레터 탭 empty 문구와 `응원남기기` 버튼의 ko/en/ja 값을 확정했다. 기존 작성 route와 내 채널 버튼 숨김 규칙은 유지하며 이번 작업에서는 Swift 구현을 수행하지 않는다. +- 2026-08-14: 사용자 요청에 따라 팬레터 탭 empty 작성 버튼은 `Color.gray800` 배경과 white 아이콘·텍스트를 사용하도록 확정했다. 홈 후원·팬레터 empty 버튼은 white/black, 후원 탭 empty 버튼은 기존 `Color.soda400`/white를 유지한다.