feat(i18n): 콘텐츠 모듈 그룹1 하드코딩 문구를 I18n 키로 통일한다
This commit is contained in:
@@ -22,7 +22,7 @@ struct ContentAllByThemeView: View {
|
|||||||
HStack(spacing: 13.3) {
|
HStack(spacing: 13.3) {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("최신순")
|
Text(I18n.Content.Sort.newest)
|
||||||
.appFont(size: 13.3, weight: .medium)
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(
|
.foregroundColor(
|
||||||
Color(hex: "e2e2e2")
|
Color(hex: "e2e2e2")
|
||||||
@@ -34,7 +34,7 @@ struct ContentAllByThemeView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("높은 가격순")
|
Text(I18n.Content.Sort.priceHigh)
|
||||||
.appFont(size: 13.3, weight: .medium)
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(
|
.foregroundColor(
|
||||||
Color(hex: "e2e2e2")
|
Color(hex: "e2e2e2")
|
||||||
@@ -46,7 +46,7 @@ struct ContentAllByThemeView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("낮은 가격순")
|
Text(I18n.Content.Sort.priceLow)
|
||||||
.appFont(size: 13.3, weight: .medium)
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(
|
.foregroundColor(
|
||||||
Color(hex: "e2e2e2")
|
Color(hex: "e2e2e2")
|
||||||
@@ -64,7 +64,7 @@ struct ContentAllByThemeView: View {
|
|||||||
.padding(.top, 13.3)
|
.padding(.top, 13.3)
|
||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("전체")
|
Text(I18n.Content.Count.totalPrefix)
|
||||||
.appFont(size: 13.3, weight: .medium)
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "e2e2e2"))
|
.foregroundColor(Color(hex: "e2e2e2"))
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ struct ContentAllByThemeView: View {
|
|||||||
.foregroundColor(Color(hex: "ff5c49"))
|
.foregroundColor(Color(hex: "ff5c49"))
|
||||||
.padding(.leading, 8)
|
.padding(.leading, 8)
|
||||||
|
|
||||||
Text("개")
|
Text(I18n.Content.Count.countUnit)
|
||||||
.appFont(size: 13.3, weight: .medium)
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "e2e2e2"))
|
.foregroundColor(Color(hex: "e2e2e2"))
|
||||||
.padding(.leading, 2)
|
.padding(.leading, 2)
|
||||||
|
|||||||
@@ -78,13 +78,13 @@ final class ContentAllByThemeViewModel: ObservableObject {
|
|||||||
if let message = decoded.message {
|
if let message = decoded.message {
|
||||||
self.errorMessage = message
|
self.errorMessage = message
|
||||||
} else {
|
} else {
|
||||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self.errorMessage = I18n.Common.commonError
|
||||||
}
|
}
|
||||||
|
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self.errorMessage = I18n.Common.commonError
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ struct ContentAllView: View {
|
|||||||
Group {
|
Group {
|
||||||
BaseView(isLoading: $viewModel.isLoading) {
|
BaseView(isLoading: $viewModel.isLoading) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
DetailNavigationBar(title: isFree ? String(localized: "무료 콘텐츠 전체") : isPointAvailableOnly ? String(localized: "포인트 대여 전체") : String(localized: "콘텐츠 전체"))
|
DetailNavigationBar(
|
||||||
|
title: isFree ?
|
||||||
|
I18n.Content.All.freeTitle :
|
||||||
|
isPointAvailableOnly ? I18n.Content.All.pointRentalTitle : I18n.Content.All.title
|
||||||
|
)
|
||||||
|
|
||||||
if !viewModel.themeList.isEmpty {
|
if !viewModel.themeList.isEmpty {
|
||||||
ContentMainContentThemeView(
|
ContentMainContentThemeView(
|
||||||
@@ -32,7 +36,7 @@ struct ContentAllView: View {
|
|||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("최신순")
|
Text(I18n.Content.Sort.newest)
|
||||||
.appFont(size: 16, weight: .medium)
|
.appFont(size: 16, weight: .medium)
|
||||||
.foregroundColor(
|
.foregroundColor(
|
||||||
Color(hex: "e2e2e2")
|
Color(hex: "e2e2e2")
|
||||||
@@ -44,7 +48,7 @@ struct ContentAllView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("인기순")
|
Text(I18n.Content.Sort.popularity)
|
||||||
.appFont(size: 16, weight: .medium)
|
.appFont(size: 16, weight: .medium)
|
||||||
.foregroundColor(
|
.foregroundColor(
|
||||||
Color(hex: "e2e2e2")
|
Color(hex: "e2e2e2")
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ struct ContentNewAllItemView: View {
|
|||||||
.appFont(size: 8.5, weight: .medium)
|
.appFont(size: 8.5, weight: .medium)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
} else {
|
} else {
|
||||||
Text("무료")
|
Text(I18n.CreateContent.free)
|
||||||
.appFont(size: 8.5, weight: .medium)
|
.appFont(size: 8.5, weight: .medium)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ struct ContentNewAllView: View {
|
|||||||
Group {
|
Group {
|
||||||
BaseView(isLoading: $viewModel.isLoading) {
|
BaseView(isLoading: $viewModel.isLoading) {
|
||||||
VStack(alignment: .leading, spacing: 13.3) {
|
VStack(alignment: .leading, spacing: 13.3) {
|
||||||
DetailNavigationBar(title: isFree ? "최신 무료 콘텐츠" : "최신 콘텐츠")
|
DetailNavigationBar(title: isFree ? I18n.Content.New.freeTitle : I18n.Content.New.title)
|
||||||
|
|
||||||
Text("※ 최근 2주간 등록된 새로운 콘텐츠 입니다.")
|
Text(I18n.Content.New.recentTwoWeeksNotice)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(.graybb)
|
.foregroundColor(.graybb)
|
||||||
.padding(.horizontal, 13.3)
|
.padding(.horizontal, 13.3)
|
||||||
@@ -37,7 +37,7 @@ struct ContentNewAllView: View {
|
|||||||
)
|
)
|
||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("전체")
|
Text(I18n.Content.Count.totalPrefix)
|
||||||
.appFont(size: 13.3, weight: .medium)
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "e2e2e2"))
|
.foregroundColor(Color(hex: "e2e2e2"))
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ struct ContentNewAllView: View {
|
|||||||
.foregroundColor(Color(hex: "ff5c49"))
|
.foregroundColor(Color(hex: "ff5c49"))
|
||||||
.padding(.leading, 8)
|
.padding(.leading, 8)
|
||||||
|
|
||||||
Text("개")
|
Text(I18n.Content.Count.countUnit)
|
||||||
.appFont(size: 13.3, weight: .medium)
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "e2e2e2"))
|
.foregroundColor(Color(hex: "e2e2e2"))
|
||||||
.padding(.leading, 2)
|
.padding(.leading, 2)
|
||||||
|
|||||||
@@ -17,14 +17,14 @@ struct ContentRankingAllView: View {
|
|||||||
Group {
|
Group {
|
||||||
BaseView(isLoading: $viewModel.isLoading) {
|
BaseView(isLoading: $viewModel.isLoading) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
DetailNavigationBar(title: "인기 콘텐츠")
|
DetailNavigationBar(title: I18n.Content.Ranking.title)
|
||||||
|
|
||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
Text("\(viewModel.dateString)")
|
Text("\(viewModel.dateString)")
|
||||||
.appFont(size: 14.7, weight: .bold)
|
.appFont(size: 14.7, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
|
||||||
Text("※ 인기 콘텐츠의 순위는 매주 업데이트됩니다.")
|
Text(I18n.Content.Ranking.weeklyUpdateNotice)
|
||||||
.appFont(size: 13.3, weight: .light)
|
.appFont(size: 13.3, weight: .light)
|
||||||
.foregroundColor(Color(hex: "bbbbbb"))
|
.foregroundColor(Color(hex: "bbbbbb"))
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ struct ContentRankingAllView: View {
|
|||||||
.cornerRadius(2.6)
|
.cornerRadius(2.6)
|
||||||
|
|
||||||
if item.isPointAvailable {
|
if item.isPointAvailable {
|
||||||
Text("포인트")
|
Text(I18n.Common.points)
|
||||||
.appFont(size: 8, weight: .medium)
|
.appFont(size: 8, weight: .medium)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(2.6)
|
.padding(2.6)
|
||||||
@@ -116,7 +116,7 @@ struct ContentRankingAllView: View {
|
|||||||
.foregroundColor(Color(hex: "909090"))
|
.foregroundColor(Color(hex: "909090"))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Text("무료")
|
Text(I18n.CreateContent.free)
|
||||||
.appFont(size: 12, weight: .medium)
|
.appFont(size: 12, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "ffffff"))
|
.foregroundColor(Color(hex: "ffffff"))
|
||||||
.padding(.horizontal, 5.3)
|
.padding(.horizontal, 5.3)
|
||||||
|
|||||||
@@ -71,13 +71,13 @@ final class ContentRankingAllViewModel: ObservableObject {
|
|||||||
if let message = decoded.message {
|
if let message = decoded.message {
|
||||||
self.errorMessage = message
|
self.errorMessage = message
|
||||||
} else {
|
} else {
|
||||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self.errorMessage = I18n.Common.commonError
|
||||||
}
|
}
|
||||||
|
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self.errorMessage = I18n.Common.commonError
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
}
|
}
|
||||||
@@ -109,13 +109,13 @@ final class ContentRankingAllViewModel: ObservableObject {
|
|||||||
if let message = decoded.message {
|
if let message = decoded.message {
|
||||||
self.errorMessage = message
|
self.errorMessage = message
|
||||||
} else {
|
} else {
|
||||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self.errorMessage = I18n.Common.commonError
|
||||||
}
|
}
|
||||||
|
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self.errorMessage = I18n.Common.commonError
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,11 +49,11 @@ struct ContentListCategoryView: View {
|
|||||||
|
|
||||||
ContentListCategoryView(
|
ContentListCategoryView(
|
||||||
categoryList: [
|
categoryList: [
|
||||||
GetCategoryListResponse(categoryId: 0, category: "전체"),
|
GetCategoryListResponse(categoryId: 0, category: I18n.Category.all),
|
||||||
GetCategoryListResponse(categoryId: 1, category: "test"),
|
GetCategoryListResponse(categoryId: 1, category: "test"),
|
||||||
GetCategoryListResponse(categoryId: 0, category: "test2")
|
GetCategoryListResponse(categoryId: 0, category: "test2")
|
||||||
],
|
],
|
||||||
selectCategory: { _ in },
|
selectCategory: { _ in },
|
||||||
selectedCategory: .constant("전체")
|
selectedCategory: .constant(I18n.Category.all)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ struct ContentListItemView: View {
|
|||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
if item.isScheduledToOpen {
|
if item.isScheduledToOpen {
|
||||||
Text("오픈예정")
|
Text(I18n.Common.openScheduled)
|
||||||
.appFont(size: 11, weight: .medium)
|
.appFont(size: 11, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "3bb9f1"))
|
.foregroundColor(Color(hex: "3bb9f1"))
|
||||||
.padding(2.6)
|
.padding(2.6)
|
||||||
@@ -52,7 +52,7 @@ struct ContentListItemView: View {
|
|||||||
.cornerRadius(2.6)
|
.cornerRadius(2.6)
|
||||||
|
|
||||||
if item.isPointAvailable {
|
if item.isPointAvailable {
|
||||||
Text("포인트")
|
Text(I18n.Common.points)
|
||||||
.appFont(size: 11, weight: .medium)
|
.appFont(size: 11, weight: .medium)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(2.6)
|
.padding(2.6)
|
||||||
@@ -98,7 +98,7 @@ struct ContentListItemView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if item.isOwned {
|
if item.isOwned {
|
||||||
Text("소장중")
|
Text(I18n.Content.Status.owned)
|
||||||
.appFont(size: 14, weight: .medium)
|
.appFont(size: 14, weight: .medium)
|
||||||
.foregroundColor(Color.gray11)
|
.foregroundColor(Color.gray11)
|
||||||
.padding(.horizontal, 5.3)
|
.padding(.horizontal, 5.3)
|
||||||
@@ -106,7 +106,7 @@ struct ContentListItemView: View {
|
|||||||
.background(Color(hex: "b1ef2c"))
|
.background(Color(hex: "b1ef2c"))
|
||||||
.cornerRadius(2.6)
|
.cornerRadius(2.6)
|
||||||
} else if item.isRented {
|
} else if item.isRented {
|
||||||
Text("대여중")
|
Text(I18n.Content.Status.rented)
|
||||||
.appFont(size: 14, weight: .medium)
|
.appFont(size: 14, weight: .medium)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.padding(.horizontal, 5.3)
|
.padding(.horizontal, 5.3)
|
||||||
@@ -114,7 +114,7 @@ struct ContentListItemView: View {
|
|||||||
.background(Color(hex: "660fd4"))
|
.background(Color(hex: "660fd4"))
|
||||||
.cornerRadius(2.6)
|
.cornerRadius(2.6)
|
||||||
} else if item.isSoldOut {
|
} else if item.isSoldOut {
|
||||||
Text("Sold Out")
|
Text(I18n.Content.Status.soldOut)
|
||||||
.appFont(size: 14, weight: .medium)
|
.appFont(size: 14, weight: .medium)
|
||||||
.foregroundColor(Color.grayd2)
|
.foregroundColor(Color.grayd2)
|
||||||
.padding(.horizontal, 5.3)
|
.padding(.horizontal, 5.3)
|
||||||
@@ -135,7 +135,7 @@ struct ContentListItemView: View {
|
|||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Text("무료")
|
Text(I18n.CreateContent.free)
|
||||||
.appFont(size: 14, weight: .medium)
|
.appFont(size: 14, weight: .medium)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -434,6 +434,97 @@ enum I18n {
|
|||||||
pick(ko: "재생목록", en: "Playlists", ja: "プレイリスト")
|
pick(ko: "재생목록", en: "Playlists", ja: "プレイリスト")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Content {
|
||||||
|
enum All {
|
||||||
|
static var title: String {
|
||||||
|
pick(ko: "콘텐츠 전체", en: "All content", ja: "コンテンツ全体")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var freeTitle: String {
|
||||||
|
pick(ko: "무료 콘텐츠 전체", en: "All free content", ja: "無料コンテンツ全体")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var pointRentalTitle: String {
|
||||||
|
pick(ko: "포인트 대여 전체", en: "All point-rental content", ja: "ポイントレンタル全体")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum New {
|
||||||
|
static var title: String {
|
||||||
|
pick(ko: "최신 콘텐츠", en: "Latest content", ja: "最新コンテンツ")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var freeTitle: String {
|
||||||
|
pick(ko: "최신 무료 콘텐츠", en: "Latest free content", ja: "最新無料コンテンツ")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var recentTwoWeeksNotice: String {
|
||||||
|
pick(
|
||||||
|
ko: "※ 최근 2주간 등록된 새로운 콘텐츠 입니다.",
|
||||||
|
en: "※ New content registered in the last 2 weeks.",
|
||||||
|
ja: "※ 最近2週間で登録された新しいコンテンツです。"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Ranking {
|
||||||
|
static var title: String {
|
||||||
|
pick(ko: "인기 콘텐츠", en: "Popular content", ja: "人気コンテンツ")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var weeklyUpdateNotice: String {
|
||||||
|
pick(
|
||||||
|
ko: "※ 인기 콘텐츠의 순위는 매주 업데이트됩니다.",
|
||||||
|
en: "※ Popular content rankings are updated weekly.",
|
||||||
|
ja: "※ 人気コンテンツの順位は毎週更新されます。"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Sort {
|
||||||
|
static var newest: String {
|
||||||
|
pick(ko: "최신순", en: "Newest", ja: "新着順")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var popularity: String {
|
||||||
|
pick(ko: "인기순", en: "Most popular", ja: "人気順")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var priceHigh: String {
|
||||||
|
pick(ko: "높은 가격순", en: "Highest price", ja: "価格の高い順")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var priceLow: String {
|
||||||
|
pick(ko: "낮은 가격순", en: "Lowest price", ja: "価格の低い順")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Count {
|
||||||
|
static var totalPrefix: String {
|
||||||
|
pick(ko: "전체", en: "Total", ja: "全体")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var countUnit: String {
|
||||||
|
pick(ko: "개", en: "", ja: "件")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
static var owned: String {
|
||||||
|
pick(ko: "소장중", en: "Owned", ja: "所持中")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var rented: String {
|
||||||
|
pick(ko: "대여중", en: "Renting", ja: "レンタル中")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var soldOut: String {
|
||||||
|
pick(ko: "Sold Out", en: "Sold out", ja: "売り切れ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum CharacterDetailGallery {
|
enum CharacterDetailGallery {
|
||||||
static var purchaseConfirmTitle: String {
|
static var purchaseConfirmTitle: String {
|
||||||
pick(ko: "구매 확인", en: "Confirmation", ja: "購入確認")
|
pick(ko: "구매 확인", en: "Confirmation", ja: "購入確認")
|
||||||
|
|||||||
@@ -141,16 +141,16 @@
|
|||||||
|
|
||||||
### Content (78)
|
### Content (78)
|
||||||
#### Group 1 (1-10)
|
#### Group 1 (1-10)
|
||||||
- [ ] `SodaLive/Sources/Content/All/ByTheme/ContentAllByThemeView.swift`
|
- [x] `SodaLive/Sources/Content/All/ByTheme/ContentAllByThemeView.swift`
|
||||||
- [ ] `SodaLive/Sources/Content/All/ByTheme/ContentAllByThemeViewModel.swift`
|
- [x] `SodaLive/Sources/Content/All/ByTheme/ContentAllByThemeViewModel.swift`
|
||||||
- [ ] `SodaLive/Sources/Content/All/ContentAllView.swift`
|
- [x] `SodaLive/Sources/Content/All/ContentAllView.swift`
|
||||||
- [ ] `SodaLive/Sources/Content/All/ContentNewAllItemView.swift`
|
- [x] `SodaLive/Sources/Content/All/ContentNewAllItemView.swift`
|
||||||
- [ ] `SodaLive/Sources/Content/All/ContentNewAllView.swift`
|
- [x] `SodaLive/Sources/Content/All/ContentNewAllView.swift`
|
||||||
- [ ] `SodaLive/Sources/Content/All/ContentRankingAllView.swift`
|
- [x] `SodaLive/Sources/Content/All/ContentRankingAllView.swift`
|
||||||
- [ ] `SodaLive/Sources/Content/All/ContentRankingAllViewModel.swift`
|
- [x] `SodaLive/Sources/Content/All/ContentRankingAllViewModel.swift`
|
||||||
- [ ] `SodaLive/Sources/Content/Category/ContentListCategoryView.swift`
|
- [x] `SodaLive/Sources/Content/Category/ContentListCategoryView.swift`
|
||||||
- [ ] `SodaLive/Sources/Content/ContentItemView.swift`
|
- [x] `SodaLive/Sources/Content/ContentItemView.swift`
|
||||||
- [ ] `SodaLive/Sources/Content/ContentListItemView.swift`
|
- [x] `SodaLive/Sources/Content/ContentListItemView.swift`
|
||||||
|
|
||||||
#### Group 2 (11-20)
|
#### Group 2 (11-20)
|
||||||
- [ ] `SodaLive/Sources/Content/ContentListView.swift`
|
- [ ] `SodaLive/Sources/Content/ContentListView.swift`
|
||||||
@@ -975,3 +975,40 @@
|
|||||||
- 빌드 검증: `SodaLive`, `SodaLive-dev` Debug 빌드 모두 성공(`** BUILD SUCCEEDED **`).
|
- 빌드 검증: `SodaLive`, `SodaLive-dev` Debug 빌드 모두 성공(`** BUILD SUCCEEDED **`).
|
||||||
- 테스트 검증: 두 스킴 모두 `Scheme ... is not currently configured for the test action.`로 test action 미구성 확인(코드 실패 아님, 스킴 제약).
|
- 테스트 검증: 두 스킴 모두 `Scheme ... is not currently configured for the test action.`로 test action 미구성 확인(코드 실패 아님, 스킴 제약).
|
||||||
- LSP 진단 참고: SourceKit 단독 해석에서 외부 모듈/프로젝트 심볼(`Moya`, `Kingfisher`, `I18n` 등) 미해결 오류가 보고되었으나, 동일 변경셋은 `xcodebuild` 실컴파일 통과로 검증했다.
|
- LSP 진단 참고: SourceKit 단독 해석에서 외부 모듈/프로젝트 심볼(`Moya`, `Kingfisher`, `I18n` 등) 미해결 오류가 보고되었으나, 동일 변경셋은 `xcodebuild` 실컴파일 통과로 검증했다.
|
||||||
|
|
||||||
|
### 21차 구현 (Content 모듈 Group 1, 10개 파일 처리, 2026-04-01)
|
||||||
|
- 무엇/왜/어떻게:
|
||||||
|
- 무엇: `변경 대상 파일 전체 목록`의 `Content` Group 1(10개 파일)을 전수 점검하고, 런타임 사용자 노출 하드코딩 문구를 `I18n.*` 참조로 전환했다.
|
||||||
|
- 왜: 콘텐츠 전체/신규/랭킹/테마별 목록 구간에 하드코딩 문자열, `String(localized:)` 직접 참조, ViewModel 공통 오류 문구가 혼재되어 `I18n.swift` 단일 접근 원칙과 불일치했기 때문이다.
|
||||||
|
- 어떻게: explore/librarian 병렬 탐색 + `grep`/`ast_grep_search`/`lsp_symbols` 직접 점검으로 치환 대상을 확정하고, `I18n.swift`에 `I18n.Content` 네임스페이스를 추가한 뒤 Group 1 호출부를 교체했다.
|
||||||
|
- 실행 명령/도구:
|
||||||
|
- `task(subagent_type="explore", ...)` x2 (`bg_65648347`, `bg_d4b726f6`)
|
||||||
|
- `task(subagent_type="librarian", ...)` x2 (`bg_c8e277d6`, `bg_a66e0329`)
|
||||||
|
- `background_output(task_id=...)` x4 (위 4개 task 결과 수집)
|
||||||
|
- `grep("\"[^\"]*[가-힣][^\"]*\"", include=Group1 대상 파일)`
|
||||||
|
- `grep("String\\(localized:|NSLocalizedString\\(|LocalizedStringKey\\(", include=Group1 대상 파일)`
|
||||||
|
- `ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Content])`
|
||||||
|
- `lsp_symbols(filePath=I18n.swift, scope=document, query=Content)`
|
||||||
|
- `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.Content` 키셋 추가:
|
||||||
|
- `All(title/freeTitle/pointRentalTitle)`
|
||||||
|
- `New(title/freeTitle/recentTwoWeeksNotice)`
|
||||||
|
- `Ranking(title/weeklyUpdateNotice)`
|
||||||
|
- `Sort(newest/popularity/priceHigh/priceLow)`
|
||||||
|
- `Count(totalPrefix/countUnit)`
|
||||||
|
- `Status(owned/rented/soldOut)`
|
||||||
|
- 치환 완료 파일(실치환 9개):
|
||||||
|
- `ContentAllByThemeView.swift`, `ContentAllByThemeViewModel.swift`, `ContentAllView.swift`, `ContentNewAllItemView.swift`, `ContentNewAllView.swift`, `ContentRankingAllView.swift`, `ContentRankingAllViewModel.swift`, `ContentListCategoryView.swift`, `ContentListItemView.swift`
|
||||||
|
- 점검만 수행(실치환 없음, 체크 완료 1개):
|
||||||
|
- `ContentItemView.swift` (런타임 하드코딩 문구 없음, Preview 샘플 문자열만 존재)
|
||||||
|
- Group 1 체크박스 10개 `- [x]` 완료 반영.
|
||||||
|
- Group 1 재탐지 결과, 남은 한글 리터럴은 `ContentRankingAllViewModel.swift`의 API 정렬 파라미터 기본값(`"매출"`)과 Preview 샘플(`ContentItemView.swift`, `ContentListItemView.swift`)만 존재.
|
||||||
|
- `ContentAllView.swift`의 `String(localized:)` 직접 참조(내비게이션 타이틀)를 `I18n.Content.All.*`로 전환해 호출 경로를 통일.
|
||||||
|
- LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(`Kingfisher`, `BaseView`, `I18n` 등)가 보고되나, 동일 변경셋은 `xcodebuild` 실컴파일 통과로 검증 완료.
|
||||||
|
- 빌드 검증: `SodaLive`, `SodaLive-dev` Debug 빌드 모두 성공(`** BUILD SUCCEEDED **`).
|
||||||
|
- 테스트 검증: 두 스킴 모두 `Scheme ... is not currently configured for the test action.`로 test action 미구성 확인(코드 실패 아님, 스킴 제약).
|
||||||
|
|||||||
Reference in New Issue
Block a user