feat(i18n): 콘텐츠 상세/댓글 하드코딩 문구를 I18n 키로 통일한다

This commit is contained in:
Yu Sung
2026-04-01 16:50:56 +09:00
parent a90996603b
commit c039931f34
30 changed files with 503 additions and 202 deletions

View File

@@ -165,40 +165,40 @@
- [x] `SodaLive/Sources/Content/Curation/ContentCurationView.swift`
#### Group 3 (21-30)
- [ ] `SodaLive/Sources/Content/Curation/ContentCurationViewModel.swift`
- [ ] `SodaLive/Sources/Content/Detail/AudioContentDeleteDialogView.swift`
- [ ] `SodaLive/Sources/Content/Detail/AudioContentReportDialogView.swift`
- [ ] `SodaLive/Sources/Content/Detail/Comment/AudioContentCommentItemView.swift`
- [ ] `SodaLive/Sources/Content/Detail/Comment/AudioContentCommentListView.swift`
- [ ] `SodaLive/Sources/Content/Detail/Comment/AudioContentCommentListViewModel.swift`
- [ ] `SodaLive/Sources/Content/Detail/Comment/AudioContentListReplyView.swift`
- [ ] `SodaLive/Sources/Content/Detail/Comment/AudioContentListReplyViewModel.swift`
- [ ] `SodaLive/Sources/Content/Detail/Comment/ContentDetailCommentView.swift`
- [ ] `SodaLive/Sources/Content/Detail/ContentDetailInfoLimitedEditionView.swift`
- [x] `SodaLive/Sources/Content/Curation/ContentCurationViewModel.swift`
- [x] `SodaLive/Sources/Content/Detail/AudioContentDeleteDialogView.swift`
- [x] `SodaLive/Sources/Content/Detail/AudioContentReportDialogView.swift`
- [x] `SodaLive/Sources/Content/Detail/Comment/AudioContentCommentItemView.swift`
- [x] `SodaLive/Sources/Content/Detail/Comment/AudioContentCommentListView.swift`
- [x] `SodaLive/Sources/Content/Detail/Comment/AudioContentCommentListViewModel.swift`
- [x] `SodaLive/Sources/Content/Detail/Comment/AudioContentListReplyView.swift`
- [x] `SodaLive/Sources/Content/Detail/Comment/AudioContentListReplyViewModel.swift`
- [x] `SodaLive/Sources/Content/Detail/Comment/ContentDetailCommentView.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentDetailInfoLimitedEditionView.swift`
#### Group 4 (31-40)
- [ ] `SodaLive/Sources/Content/Detail/ContentDetailInfoView.swift`
- [ ] `SodaLive/Sources/Content/Detail/ContentDetailMenuView.swift`
- [ ] `SodaLive/Sources/Content/Detail/ContentDetailMosaicView.swift`
- [ ] `SodaLive/Sources/Content/Detail/ContentDetailOtherContentView.swift`
- [ ] `SodaLive/Sources/Content/Detail/ContentDetailPlayView.swift`
- [ ] `SodaLive/Sources/Content/Detail/ContentDetailPreviousNextContentButtonView.swift`
- [ ] `SodaLive/Sources/Content/Detail/ContentDetailPurchaseButton.swift`
- [ ] `SodaLive/Sources/Content/Detail/ContentDetailView.swift`
- [ ] `SodaLive/Sources/Content/Detail/ContentDetailViewModel.swift`
- [ ] `SodaLive/Sources/Content/Detail/ContentOrderConfirmDialogView.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentDetailInfoView.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentDetailMenuView.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentDetailMosaicView.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentDetailOtherContentView.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentDetailPlayView.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentDetailPreviousNextContentButtonView.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentDetailPurchaseButton.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentDetailView.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentDetailViewModel.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentOrderConfirmDialogView.swift`
#### Group 5 (41-50)
- [ ] `SodaLive/Sources/Content/Detail/ContentOrderDialogView.swift`
- [ ] `SodaLive/Sources/Content/Detail/LiveRoomDonationDialogView.swift`
- [ ] `SodaLive/Sources/Content/Main/Banner/ContentMainBannerViewModel.swift`
- [ ] `SodaLive/Sources/Content/Main/V2/ContentMainContentThemeView.swift`
- [ ] `SodaLive/Sources/Content/Modify/ContentModifyView.swift`
- [ ] `SodaLive/Sources/Content/Modify/ContentModifyViewModel.swift`
- [ ] `SodaLive/Sources/Content/Player/ContentPlayerView.swift`
- [ ] `SodaLive/Sources/Content/Playlist/ContentPlaylistItemView.swift`
- [ ] `SodaLive/Sources/Content/Playlist/ContentPlaylistListView.swift`
- [ ] `SodaLive/Sources/Content/Playlist/ContentPlaylistListViewModel.swift`
- [x] `SodaLive/Sources/Content/Detail/ContentOrderDialogView.swift`
- [x] `SodaLive/Sources/Content/Detail/LiveRoomDonationDialogView.swift`
- [x] `SodaLive/Sources/Content/Main/Banner/ContentMainBannerViewModel.swift`
- [x] `SodaLive/Sources/Content/Main/V2/ContentMainContentThemeView.swift`
- [x] `SodaLive/Sources/Content/Modify/ContentModifyView.swift`
- [x] `SodaLive/Sources/Content/Modify/ContentModifyViewModel.swift`
- [x] `SodaLive/Sources/Content/Player/ContentPlayerView.swift`
- [x] `SodaLive/Sources/Content/Playlist/ContentPlaylistItemView.swift`
- [x] `SodaLive/Sources/Content/Playlist/ContentPlaylistListView.swift`
- [x] `SodaLive/Sources/Content/Playlist/ContentPlaylistListViewModel.swift`
#### Group 6 (51-60)
- [ ] `SodaLive/Sources/Content/Playlist/Create/ContentPlaylistCreateView.swift`
@@ -1050,3 +1050,40 @@
- 테스트 검증: 두 스킴 모두 `Scheme ... is not currently configured for the test action.`로 test action 미구성 확인(코드 실패 아님, 스킴 제약).
- 수동 QA(문구 경로 수동 점검): Group 2 변경 파일에서 사용자 노출 텍스트가 `I18n.*` 경유인지 라인 단위 검토 완료. 비노출 문자열(디버그 로그/API 파라미터)은 예외로 문서화했다.
- LSP 진단 참고: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼(`Kingfisher`, `ObjectBox`, `I18n`, `AppState` 등) 미해결 오류가 보고되나, 동일 변경셋은 `xcodebuild` 실컴파일 통과로 검증했다.
### 23차 구현 (Content 모듈 Group 3~5, 30개 파일 처리, 2026-04-01)
- 무엇/왜/어떻게:
- 무엇: `변경 대상 파일 전체 목록``Content` Group 3~5(30개 파일)를 전수 점검해 사용자 노출 하드코딩 문구를 `I18n.*` 참조로 전환했다.
- 왜: 댓글/상세/구매/후원/수정/플레이리스트 구간에서 뷰 리터럴·ViewModel 에러문구·다이얼로그 문구가 혼재되어 `I18n.swift` 단일 접근 원칙이 깨져 있었기 때문이다.
- 어떻게: 병렬 컨텍스트 수집(`explore`/`librarian`/`oracle` 백그라운드 + `grep`/`read`/`lsp_diagnostics` 직접 점검) 후, Group 3~5 범위 파일만 치환했다. 공통 오류는 `I18n.Common.commonError`로 통합하고, 도메인 전용 문구는 `I18n.ContentDetail.*`, `I18n.Content.*`, `I18n.CreateContent.*`로 정리했다.
- 실행 명령/도구:
- `task(subagent_type="explore", ...)` x2 (`bg_91507310`, `bg_db55ce36`)
- `task(subagent_type="librarian", ...)` x2 (`bg_a4a5eff9`, `bg_b2fde7b7`)
- `task(subagent_type="oracle", ...)` x1 (`bg_3ab5a233`)
- `grep("\"[^\"]*[가-힣][^\"]*\"", include=Group3~5 대상 파일)`
- `grep("String\\(localized:|NSLocalizedString\\(|LocalizedStringKey\\(", include=Group3~5 대상 파일)`
- `lsp_diagnostics(filePath=변경 파일 전체)`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test`
- 결과:
- `I18n.swift` 확장:
- `I18n.ContentDetail.*` 대규모 추가(상세 헤더/메뉴/삭제·신고 다이얼로그/댓글/한정판/모자이크/주변 콘텐츠/이전화·다음화/구매·대여/후원 다이얼로그/완료 토스트 등)
- `I18n.Content.Banner.loadFailed`
- `I18n.Content.Playlist.*`(생성 버튼/빈 상태/개수 표기)
- `I18n.CreateContent.modifyTitle`, `modifyAction`, `modifySuccess`
- 치환 완료 파일(실치환 28개):
- Group 3: 10개 전부 실치환 완료
- Group 4: 10개 전부 실치환 완료
- Group 5: 8개 실치환 완료 (`ContentOrderDialogView`, `LiveRoomDonationDialogView`, `ContentMainBannerViewModel`, `ContentModifyView`, `ContentModifyViewModel`, `ContentPlaylistItemView`, `ContentPlaylistListView`, `ContentPlaylistListViewModel`)
- 점검만 수행(실치환 없음, 체크 완료 2개):
- `ContentMainContentThemeView.swift`, `ContentPlayerView.swift` (런타임 노출 하드코딩 없음, Preview 샘플 문자열만 존재)
- Group 3~5 체크박스 30개 `- [x]` 완료 반영.
- Group 3~5 재탐지 결과, 남은 한글 리터럴은 Preview 샘플 데이터 및 비노출 문자열 분기(기본 파라미터 `reason: "프로필 신고"`)만 확인.
- Oracle 후속 반영: `ContentDetailViewModel``isInsufficientCanError(message:errorProperty:)` 헬퍼를 추가해 부족 캔 분기를 `errorProperty` 우선 + 메시지 보조 판별로 정리했다(기존 단일 텍스트 포함 비교 의존 완화).
- 직접 로컬라이제이션 API(`String(localized:)`, `NSLocalizedString`, `LocalizedStringKey`)는 Group 3~5 대상 파일에서 0건.
- 빌드 검증: `SodaLive`, `SodaLive-dev` Debug 빌드 모두 성공(`** BUILD SUCCEEDED **`).
- 테스트 검증: 두 스킴 모두 `Scheme ... is not currently configured for the test action.`로 test action 미구성 확인(코드 실패 아님, 스킴 제약).
- 수동 QA(문구 경로 수동 점검): Group 3~5 대상 파일 재스캔에서 사용자 노출 문자열의 `I18n.*` 경유를 확인했고, 예외(Preview/비노출 비교 문자열)만 잔존함을 확인했다.
- LSP 진단 참고: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼(`Kingfisher`, `Moya`, `I18n`, `AppState` 등) 미해결 오류가 보고되나, 동일 변경셋은 `xcodebuild` 실컴파일 통과로 검증했다.