feat(i18n): 라이브 모듈 하드코딩 문구를 I18n 키로 통일한다
This commit is contained in:
@@ -1767,17 +1767,59 @@ enum I18n {
|
|||||||
static var chatDeleteTitle: String { pick(ko: "채팅 삭제", en: "Delete chat", ja: "チャット削除") }
|
static var chatDeleteTitle: String { pick(ko: "채팅 삭제", en: "Delete chat", ja: "チャット削除") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum LiveMain {
|
||||||
|
static var createLiveButton: String {
|
||||||
|
pick(ko: "라이브 만들기", en: "Create live", ja: "ライブを作成")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var replaySectionTitle: String {
|
||||||
|
pick(ko: "라이브 다시 듣기", en: "Live replay", ja: "ライブ再生")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum LiveNow {
|
enum LiveNow {
|
||||||
static var allTitle: String {
|
static var allTitle: String {
|
||||||
pick(ko: "지금 라이브 중 전체보기", en: "Live Now - All", ja: "ライブ配信中(全て)")
|
pick(ko: "지금 라이브 중 전체보기", en: "Live Now - All", ja: "ライブ配信中(全て)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static var sectionTitle: String {
|
||||||
|
pick(ko: "지금 라이브중", en: "Live now", ja: "ライブ配信中")
|
||||||
|
}
|
||||||
|
|
||||||
static var remaining: String {
|
static var remaining: String {
|
||||||
pick(ko: "잔여", en: "Remaining", ja: "残り")
|
pick(ko: "잔여", en: "Remaining", ja: "残り")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static var emptyStateMessage: String {
|
||||||
|
pick(
|
||||||
|
ko: "마이페이지에서 본인인증을 하거나\n라이브를 예약하고 참여해보세요.",
|
||||||
|
en: "Verify your identity in My Page\nor reserve and join a live.",
|
||||||
|
ja: "マイページで本人認証を行うか、\nライブを予約して参加してください。"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static var refreshButton: String {
|
||||||
|
pick(ko: "새로고침", en: "Refresh", ja: "更新")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var followingChannelsTitle: String {
|
||||||
|
pick(ko: "팔로잉 채널", en: "Following channels", ja: "フォロー中のチャンネル")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var liveBadge: String {
|
||||||
|
pick(ko: "Live", en: "Live", ja: "Live")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var moreButton: String {
|
||||||
|
pick(ko: "더보기", en: "More", ja: "もっと見る")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LiveCancel {
|
enum LiveCancel {
|
||||||
|
static var title: String {
|
||||||
|
pick(ko: "예약취소", en: "Cancel reservation", ja: "予約キャンセル")
|
||||||
|
}
|
||||||
|
|
||||||
static var reasonPlaceholder: String {
|
static var reasonPlaceholder: String {
|
||||||
pick(
|
pick(
|
||||||
ko: "취소사유를 입력하세요",
|
ko: "취소사유를 입력하세요",
|
||||||
@@ -1786,6 +1828,14 @@ enum I18n {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static var cancelButton: String {
|
||||||
|
pick(ko: "취소", en: "Cancel", ja: "キャンセル")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var confirmButton: String {
|
||||||
|
pick(ko: "확인", en: "Confirm", ja: "確認")
|
||||||
|
}
|
||||||
|
|
||||||
static var reservationCanceled: String {
|
static var reservationCanceled: String {
|
||||||
pick(
|
pick(
|
||||||
ko: "예약이 취소되었습니다.",
|
ko: "예약이 취소되었습니다.",
|
||||||
@@ -1795,6 +1845,154 @@ enum I18n {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum LiveReservation {
|
||||||
|
enum Section {
|
||||||
|
static var title: String {
|
||||||
|
pick(ko: "라이브 예약중", en: "Live reservations", ja: "ライブ予約中")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var emptyStateMessage: String {
|
||||||
|
pick(
|
||||||
|
ko: "지금 예약중인 라이브가 없습니다.\n채널을 팔로잉 하고 라이브 알림을 받아 보세요.",
|
||||||
|
en: "There are no live reservations right now.\nFollow channels and receive live notifications.",
|
||||||
|
ja: "現在予約中のライブはありません。\nチャンネルをフォローしてライブ通知を受け取りましょう。"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum All {
|
||||||
|
static var title: String {
|
||||||
|
pick(ko: "라이브, 예약 캘린더", en: "Live reservation calendar", ja: "ライブ予約カレンダー")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var emptyStateMessage: String {
|
||||||
|
pick(
|
||||||
|
ko: "지금 예약중인 라이브가 없습니다.\n다른 날짜의 라이브를 예약하고 참여해 보세요.",
|
||||||
|
en: "There are no live reservations right now.\nReserve a live on another date and join.",
|
||||||
|
ja: "現在予約中のライブはありません。\n別の日のライブを予約して参加してみてください。"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Item {
|
||||||
|
static var reservationCompleted: String {
|
||||||
|
pick(ko: "예약완료", en: "Reserved", ja: "予約完了")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var ownCreatedLive: String {
|
||||||
|
pick(ko: "내가 개설한 라이브", en: "Live I created", ja: "自分が開設したライブ")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var free: String {
|
||||||
|
CreateContent.free
|
||||||
|
}
|
||||||
|
|
||||||
|
static func month(_ value: String) -> String {
|
||||||
|
pick(
|
||||||
|
ko: "\(value)월",
|
||||||
|
en: "\(value)M",
|
||||||
|
ja: "\(value)月"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func priceWithCan(_ can: Int) -> String {
|
||||||
|
pick(
|
||||||
|
ko: "\(can)캔",
|
||||||
|
en: "\(can) cans",
|
||||||
|
ja: "\(can)can"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Complete {
|
||||||
|
static var title: String {
|
||||||
|
pick(ko: "라이브 예약 완료", en: "Live reservation complete", ja: "ライブ予約完了")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var completedMessage: String {
|
||||||
|
pick(ko: "예약이 완료되었습니다.", en: "Your reservation is complete.", ja: "予約が完了しました。")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var reservationInfoTitle: String {
|
||||||
|
pick(ko: "라이브 예약정보", en: "Reservation details", ja: "ライブ予約情報")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var channelLabel: String {
|
||||||
|
pick(ko: "채널", en: "Channel", ja: "チャンネル")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var purchaseDetailLabel: String {
|
||||||
|
pick(ko: "구매내역", en: "Purchase", ja: "購入内容")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var reservationDateLabel: String {
|
||||||
|
pick(ko: "예약일자", en: "Reservation date", ja: "予約日時")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var liveCostLabel: String {
|
||||||
|
pick(ko: "라이브 비용", en: "Live price", ja: "ライブ料金")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var paymentInfoTitle: String {
|
||||||
|
pick(ko: "결제정보", en: "Payment info", ja: "決済情報")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var ownedCanLabel: String {
|
||||||
|
pick(ko: "보유캔", en: "Owned cans", ja: "保有can")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var paymentCanLabel: String {
|
||||||
|
pick(ko: "결제캔", en: "Paid cans", ja: "決済can")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var remainingCanLabel: String {
|
||||||
|
pick(ko: "잔여캔", en: "Remaining cans", ja: "残りcan")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var canSuffix: String {
|
||||||
|
pick(ko: " 캔", en: " cans", ja: " can")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var goHome: String {
|
||||||
|
pick(ko: "홈으로 이동", en: "Go to Home", ja: "ホームへ移動")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var goReservationList: String {
|
||||||
|
pick(ko: "예약 내역 이동", en: "View reservations", ja: "予約履歴へ移動")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LiveChat {
|
||||||
|
static var staffBadge: String {
|
||||||
|
pick(ko: "스탭", en: "Staff", ja: "スタッフ")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var donationMemberSuffix: String {
|
||||||
|
pick(ko: "님이", en: "", ja: "さんが")
|
||||||
|
}
|
||||||
|
|
||||||
|
static func canWithUnit(_ can: Int) -> String {
|
||||||
|
pick(ko: "\(can)캔", en: "\(can) cans", ja: "\(can)can")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var secretMissionDonationSuffix: String {
|
||||||
|
pick(ko: "으로 비밀미션을 보냈습니다.🤫", en: " sent a secret mission.🤫", ja: "で秘密ミッションを送りました。🤫")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var donationSuffix: String {
|
||||||
|
pick(ko: "을 후원하셨습니다.💰🪙", en: " donated.💰🪙", ja: "を後援しました。💰🪙")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var heartDonationSuffix: String {
|
||||||
|
pick(ko: "'님이 마음을 전했습니다 : 💕", en: "' sent a heart : 💕", ja: "'さんがハートを送りました : 💕")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var joinSuffix: String {
|
||||||
|
pick(ko: "'님이 입장하셨습니다.", en: "' joined.", ja: "'さんが入場しました。")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum CreateContent {
|
enum CreateContent {
|
||||||
static var selectFile: String { pick(ko: "파일 선택", en: "Select file", ja: "ファイル選択") }
|
static var selectFile: String { pick(ko: "파일 선택", en: "Select file", ja: "ファイル選択") }
|
||||||
static var selectTheme: String { pick(ko: "테마 선택", en: "Select theme", ja: "テーマ選択") }
|
static var selectTheme: String { pick(ko: "테마 선택", en: "Select theme", ja: "テーマ選択") }
|
||||||
@@ -1899,6 +2097,7 @@ enum I18n {
|
|||||||
static var cannotReserveOwnLive: String { pick(ko: "내가 만든 라이브는 예약할 수 없습니다.", en: "reserve a live you created is required.", ja: "自分が作ったライブは予約できません。") }
|
static var cannotReserveOwnLive: String { pick(ko: "내가 만든 라이브는 예약할 수 없습니다.", en: "reserve a live you created is required.", ja: "自分が作ったライブは予約できません。") }
|
||||||
static var enterLiveFailed: String { pick(ko: "라이브에 입장하지 못했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.", en: "Could not enter the live.\nIf the problem persists, please contact customer support.", ja: "ライブに入室できませんでした。\n問題が続く場合はカスタマーサポートにお問い合わせください。") }
|
static var enterLiveFailed: String { pick(ko: "라이브에 입장하지 못했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.", en: "Could not enter the live.\nIf the problem persists, please contact customer support.", ja: "ライブに入室できませんでした。\n問題が続く場合はカスタマーサポートにお問い合わせください。") }
|
||||||
static var fetchLiveInfoFailed: String { pick(ko: "라이브 정보를 가져오지 못했습니다.\n다시 시도해 주세요.", en: "Failed to fetch live information.\nPlease try again.", ja: "ライブ情報を取得できませんでした。\nもう一度お試しください。") }
|
static var fetchLiveInfoFailed: String { pick(ko: "라이브 정보를 가져오지 못했습니다.\n다시 시도해 주세요.", en: "Failed to fetch live information.\nPlease try again.", ja: "ライブ情報を取得できませんでした。\nもう一度お試しください。") }
|
||||||
|
static var alreadyEndedLive: String { pick(ko: "이미 종료된 라이브 입니다.", en: "This live has already ended.", ja: "このライブはすでに終了しています。") }
|
||||||
static var userBlocked: String { pick(ko: "차단하였습니다.", en: "User has been blocked.", ja: "ブロックしました。") }
|
static var userBlocked: String { pick(ko: "차단하였습니다.", en: "User has been blocked.", ja: "ブロックしました。") }
|
||||||
static var userUnblocked: String { pick(ko: "차단이 해제 되었습니다.", en: "User has been unblocked.", ja: "ブロックを解除しました。") }
|
static var userUnblocked: String { pick(ko: "차단이 해제 되었습니다.", en: "User has been unblocked.", ja: "ブロックを解除しました。") }
|
||||||
static var blockDialogTitle: String { pick(ko: "사용자 차단", en: "Block User", ja: "ユーザーブロック") }
|
static var blockDialogTitle: String { pick(ko: "사용자 차단", en: "Block User", ja: "ユーザーブロック") }
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ struct LiveCancelDialog: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
Text("예약취소")
|
Text(I18n.LiveCancel.title)
|
||||||
.appFont(size: 18.3, weight: .bold)
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "bbbbbb"))
|
.foregroundColor(Color(hex: "bbbbbb"))
|
||||||
.padding(.top, 40)
|
.padding(.top, 40)
|
||||||
@@ -39,7 +39,7 @@ struct LiveCancelDialog: View {
|
|||||||
.padding(.top, 13.3)
|
.padding(.top, 13.3)
|
||||||
|
|
||||||
HStack(spacing: 13.3) {
|
HStack(spacing: 13.3) {
|
||||||
Text("취소")
|
Text(I18n.LiveCancel.cancelButton)
|
||||||
.appFont(size: 18.3, weight: .bold)
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "3bb9f1"))
|
.foregroundColor(Color(hex: "3bb9f1"))
|
||||||
.padding(.vertical, 16)
|
.padding(.vertical, 16)
|
||||||
@@ -54,7 +54,7 @@ struct LiveCancelDialog: View {
|
|||||||
isShowCancelPopup = false
|
isShowCancelPopup = false
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("확인")
|
Text(I18n.LiveCancel.confirmButton)
|
||||||
.appFont(size: 18.3, weight: .bold)
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(.vertical, 16)
|
.padding(.vertical, 16)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ struct LiveReplayListView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 14) {
|
VStack(spacing: 14) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("라이브 다시 듣기")
|
Text(I18n.LiveMain.replaySectionTitle)
|
||||||
.appFont(size: 24, weight: .bold)
|
.appFont(size: 24, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ struct LiveView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 20, height: 20)
|
.frame(width: 20, height: 20)
|
||||||
|
|
||||||
Text("라이브 만들기")
|
Text(I18n.LiveMain.createLiveButton)
|
||||||
.appFont(size: 13.3, weight: .bold)
|
.appFont(size: 13.3, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ final class LiveViewModel: ObservableObject {
|
|||||||
@Published var liveStartDate: String? = nil
|
@Published var liveStartDate: String? = nil
|
||||||
@Published var nowDate: String? = nil
|
@Published var nowDate: String? = nil
|
||||||
|
|
||||||
let paymentDialogCancelTitle = "취소"
|
let paymentDialogCancelTitle = I18n.Common.cancel
|
||||||
|
|
||||||
var page = 1
|
var page = 1
|
||||||
var isLast = false
|
var isLast = false
|
||||||
@@ -104,13 +104,13 @@ final class LiveViewModel: 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.MemberChannel.enterLiveFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = "라이브에 입장하지 못했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self.errorMessage = I18n.MemberChannel.enterLiveFailed
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,13 +151,13 @@ final class LiveViewModel: 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.MemberChannel.enterLiveFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = "라이브에 입장하지 못했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self.errorMessage = I18n.MemberChannel.enterLiveFailed
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,13 +189,13 @@ final class LiveViewModel: 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,13 +245,13 @@ final class LiveViewModel: 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,13 +297,13 @@ final class LiveViewModel: 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -314,7 +314,7 @@ final class LiveViewModel: ObservableObject {
|
|||||||
func reservationLiveRoom(roomId: Int) {
|
func reservationLiveRoom(roomId: Int) {
|
||||||
getRoomDetail(roomId: roomId) { [unowned self] in
|
getRoomDetail(roomId: roomId) { [unowned self] in
|
||||||
if ($0.manager.id == UserDefaults.int(forKey: .userId)) {
|
if ($0.manager.id == UserDefaults.int(forKey: .userId)) {
|
||||||
self.errorMessage = "내가 만든 라이브는 예약할 수 없습니다."
|
self.errorMessage = I18n.MemberChannel.cannotReserveOwnLive
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
} else {
|
} else {
|
||||||
if $0.isPrivateRoom {
|
if $0.isPrivateRoom {
|
||||||
@@ -326,9 +326,9 @@ final class LiveViewModel: ObservableObject {
|
|||||||
if ($0.price == 0 || $0.isPaid) {
|
if ($0.price == 0 || $0.isPaid) {
|
||||||
self.reservation(roomId: roomId)
|
self.reservation(roomId: roomId)
|
||||||
} else {
|
} else {
|
||||||
self.paymentDialogTitle = "\($0.price)캔으로 예약"
|
self.paymentDialogTitle = I18n.MemberChannel.reserveWithCansTitle($0.price)
|
||||||
self.paymentDialogDesc = "'\($0.title)' 라이브에 참여하기 위해 결제합니다."
|
self.paymentDialogDesc = I18n.MemberChannel.reservePaymentDesc($0.title)
|
||||||
self.paymentDialogConfirmTitle = "결제 후 예약하기"
|
self.paymentDialogConfirmTitle = I18n.MemberChannel.reservePaymentConfirmTitle
|
||||||
self.paymentDialogConfirmAction = { [unowned self] in
|
self.paymentDialogConfirmAction = { [unowned self] in
|
||||||
hidePopup()
|
hidePopup()
|
||||||
reservation(roomId: roomId)
|
reservation(roomId: roomId)
|
||||||
@@ -375,13 +375,13 @@ final class LiveViewModel: ObservableObject {
|
|||||||
if hours >= 1 {
|
if hours >= 1 {
|
||||||
self.liveStartDate = beginDate.convertDateFormat(dateFormat: "yyyy-MM-dd, HH:mm")
|
self.liveStartDate = beginDate.convertDateFormat(dateFormat: "yyyy-MM-dd, HH:mm")
|
||||||
self.nowDate = now.convertDateFormat(dateFormat: "yyyy-MM-dd, HH:mm")
|
self.nowDate = now.convertDateFormat(dateFormat: "yyyy-MM-dd, HH:mm")
|
||||||
self.paymentDialogDesc2 = "라이브를 시작한 지 \(hours)시간 \(minutes)분이 지났습니다. 라이브에 입장 후 30분 이내에 라이브가 종료될 수도 있습니다."
|
self.paymentDialogDesc2 = I18n.MemberChannel.elapsedLiveWarning(hours: hours, minutes: minutes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.paymentDialogTitle = "유료 라이브 입장"
|
self.paymentDialogTitle = I18n.MemberChannel.paidLiveEnterTitle
|
||||||
self.paymentDialogDesc = "\($0.price)캔을 차감하고\n라이브에 입장 하시겠습니까?"
|
self.paymentDialogDesc = I18n.MemberChannel.paidLiveEnterDesc($0.price)
|
||||||
self.paymentDialogConfirmTitle = "결제 후 참여하기"
|
self.paymentDialogConfirmTitle = I18n.MemberChannel.paidLiveConfirmTitle
|
||||||
self.paymentDialogConfirmAction = { [unowned self] in
|
self.paymentDialogConfirmAction = { [unowned self] in
|
||||||
hidePopup()
|
hidePopup()
|
||||||
self.enterRoom(roomId: roomId)
|
self.enterRoom(roomId: roomId)
|
||||||
@@ -425,18 +425,18 @@ final class LiveViewModel: ObservableObject {
|
|||||||
} else {
|
} else {
|
||||||
if let message = decoded.message {
|
if let message = decoded.message {
|
||||||
if message.contains("종료") {
|
if message.contains("종료") {
|
||||||
self.errorMessage = "이미 종료된 라이브 입니다."
|
self.errorMessage = I18n.MemberChannel.alreadyEndedLive
|
||||||
} else {
|
} else {
|
||||||
self.errorMessage = message
|
self.errorMessage = message
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.errorMessage = "라이브 정보를 가져오지 못했습니다.\n다시 시도해 주세요."
|
self.errorMessage = I18n.MemberChannel.fetchLiveInfoFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
self.errorMessage = "라이브 정보를 가져오지 못했습니다.\n다시 시도해 주세요."
|
self.errorMessage = I18n.MemberChannel.fetchLiveInfoFailed
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,13 +471,13 @@ final class LiveViewModel: 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,15 +105,14 @@ struct LiveNowAllView: View {
|
|||||||
|
|
||||||
if isShowAuthConfirmView {
|
if isShowAuthConfirmView {
|
||||||
SodaDialog(
|
SodaDialog(
|
||||||
title: "본인인증",
|
title: I18n.Main.Auth.dialogTitle,
|
||||||
desc: "청소년 보호를 위해\n본인인증을 완료한\n성인만 라이브 입장이 가능합니다.\n" +
|
desc: I18n.Main.Auth.liveEntryVerificationDescription,
|
||||||
"라이브 입장을 위해\n본인인증을 진행해 주세요.",
|
confirmButtonTitle: I18n.Main.Auth.goToVerification,
|
||||||
confirmButtonTitle: "본인인증 하러가기",
|
|
||||||
confirmButtonAction: {
|
confirmButtonAction: {
|
||||||
isShowAuthConfirmView = false
|
isShowAuthConfirmView = false
|
||||||
isShowAuthView = true
|
isShowAuthView = true
|
||||||
},
|
},
|
||||||
cancelButtonTitle: "취소",
|
cancelButtonTitle: I18n.Common.cancel,
|
||||||
cancelButtonAction: {
|
cancelButtonAction: {
|
||||||
isShowAuthConfirmView = false
|
isShowAuthConfirmView = false
|
||||||
pendingAction = nil
|
pendingAction = nil
|
||||||
@@ -142,7 +141,7 @@ struct LiveNowAllView: View {
|
|||||||
isShowAuthView = false
|
isShowAuthView = false
|
||||||
}
|
}
|
||||||
.onError { _ in
|
.onError { _ in
|
||||||
AppState.shared.errorMessage = "본인인증 중 오류가 발생했습니다."
|
AppState.shared.errorMessage = I18n.Main.Auth.authenticationError
|
||||||
AppState.shared.isShowErrorPopup = true
|
AppState.shared.isShowErrorPopup = true
|
||||||
isShowAuthView = false
|
isShowAuthView = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ struct LiveNowItemView: View {
|
|||||||
.padding(.horizontal, 2)
|
.padding(.horizontal, 2)
|
||||||
.padding(.bottom, 2)
|
.padding(.bottom, 2)
|
||||||
} else {
|
} else {
|
||||||
Text("무료")
|
Text(I18n.LiveReservation.Item.free)
|
||||||
.appFont(size: 14, weight: .regular)
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "#263238"))
|
.foregroundColor(Color(hex: "#263238"))
|
||||||
.padding(.vertical, 4)
|
.padding(.vertical, 4)
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ struct SectionLiveNowView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
LazyVStack(spacing: 13.3) {
|
LazyVStack(spacing: 13.3) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("지금 라이브중")
|
Text(I18n.LiveNow.sectionTitle)
|
||||||
.appFont(size: 24, weight: .bold)
|
.appFont(size: 24, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if items.count > 0 {
|
if items.count > 0 {
|
||||||
Text("전체보기")
|
Text(I18n.Common.viewAll)
|
||||||
.appFont(size: 14, weight: .regular)
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "78909C"))
|
.foregroundColor(Color(hex: "78909C"))
|
||||||
.onTapGesture { AppState.shared.setAppStep(step: .liveNowAll(onClickParticipant: onClickParticipant)) }
|
.onTapGesture { AppState.shared.setAppStep(step: .liveNowAll(onClickParticipant: onClickParticipant)) }
|
||||||
@@ -54,7 +54,7 @@ struct SectionLiveNowView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 60, height: 60)
|
.frame(width: 60, height: 60)
|
||||||
|
|
||||||
Text("마이페이지에서 본인인증을 하거나\n라이브를 예약하고 참여해보세요.")
|
Text(I18n.LiveNow.emptyStateMessage)
|
||||||
.appFont(size: 13, weight: .medium)
|
.appFont(size: 13, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "bbbbbb"))
|
.foregroundColor(Color(hex: "bbbbbb"))
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
@@ -72,7 +72,7 @@ struct SectionLiveNowView: View {
|
|||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
Image("ic_refresh")
|
Image("ic_refresh")
|
||||||
|
|
||||||
Text("새로고침")
|
Text(I18n.LiveNow.refreshButton)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color.grayd2)
|
.foregroundColor(Color.grayd2)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ struct SectionRecommendChannelView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("팔로잉 채널")
|
Text(I18n.LiveNow.followingChannelsTitle)
|
||||||
.appFont(size: 24, weight: .bold)
|
.appFont(size: 24, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ struct SectionRecommendChannelView: View {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if item.isOnAir {
|
if item.isOnAir {
|
||||||
Text("Live")
|
Text(I18n.LiveNow.liveBadge)
|
||||||
.appFont(size: 8.7, weight: .bold)
|
.appFont(size: 8.7, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(.vertical, 2.7)
|
.padding(.vertical, 2.7)
|
||||||
@@ -87,7 +87,7 @@ struct SectionRecommendChannelView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: screenSize().width * 0.18, height: screenSize().width * 0.18, alignment: .center)
|
.frame(width: screenSize().width * 0.18, height: screenSize().width * 0.18, alignment: .center)
|
||||||
|
|
||||||
Text("더보기")
|
Text(I18n.LiveNow.moreButton)
|
||||||
.appFont(size: 14, weight: .regular)
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.frame(width: screenSize().width * 0.18)
|
.frame(width: screenSize().width * 0.18)
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ struct LiveReservationAllItemView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if item.isReservation {
|
if item.isReservation {
|
||||||
Text("예약완료")
|
Text(I18n.LiveReservation.Item.reservationCompleted)
|
||||||
.appFont(size: 11.3, weight: .medium)
|
.appFont(size: 11.3, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "d2d2d2"))
|
.foregroundColor(Color(hex: "d2d2d2"))
|
||||||
.padding(.horizontal, 7)
|
.padding(.horizontal, 7)
|
||||||
@@ -63,7 +63,7 @@ struct LiveReservationAllItemView: View {
|
|||||||
.background(Color(hex: "533d89"))
|
.background(Color(hex: "533d89"))
|
||||||
.cornerRadius(10)
|
.cornerRadius(10)
|
||||||
} else {
|
} else {
|
||||||
Text(item.price > 0 ? "\(item.price)캔" : "무료")
|
Text(item.price > 0 ? I18n.LiveReservation.Item.priceWithCan(item.price) : I18n.LiveReservation.Item.free)
|
||||||
.appFont(size: 12, weight: .medium)
|
.appFont(size: 12, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "e2e2e2").opacity(0.49))
|
.foregroundColor(Color(hex: "e2e2e2").opacity(0.49))
|
||||||
.padding(.bottom, 6.7)
|
.padding(.bottom, 6.7)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ struct LiveReservationAllView: View {
|
|||||||
BaseView(isLoading: $viewModel.isLoading) {
|
BaseView(isLoading: $viewModel.isLoading) {
|
||||||
GeometryReader { proxy in
|
GeometryReader { proxy in
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
DetailNavigationBar(title: "라이브, 예약 캘린더")
|
DetailNavigationBar(title: I18n.LiveReservation.All.title)
|
||||||
|
|
||||||
WeekCalendarView { date in
|
WeekCalendarView { date in
|
||||||
viewModel.selectedDateString = date
|
viewModel.selectedDateString = date
|
||||||
@@ -57,7 +57,7 @@ struct LiveReservationAllView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 60, height: 60)
|
.frame(width: 60, height: 60)
|
||||||
|
|
||||||
Text("지금 예약중인 라이브가 없습니다.\n다른 날짜의 라이브를 예약하고 참여해 보세요.")
|
Text(I18n.LiveReservation.All.emptyStateMessage)
|
||||||
.appFont(size: 13, weight: .medium)
|
.appFont(size: 13, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "bbbbbb"))
|
.foregroundColor(Color(hex: "bbbbbb"))
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ struct LiveReservationCompleteView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
BaseView {
|
BaseView {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
DetailNavigationBar(title: "라이브 예약 완료") {
|
DetailNavigationBar(title: I18n.LiveReservation.Complete.title) {
|
||||||
AppState.shared.setAppStep(step: .main)
|
AppState.shared.setAppStep(step: .main)
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView(.vertical, showsIndicators: false) {
|
ScrollView(.vertical, showsIndicators: false) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
Text("예약이 완료되었습니다.")
|
Text(I18n.LiveReservation.Complete.completedMessage)
|
||||||
.appFont(size: 20, weight: .bold)
|
.appFont(size: 20, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "a285eb"))
|
.foregroundColor(Color(hex: "a285eb"))
|
||||||
.frame(width: screenSize().width - 26.7, alignment: .leading)
|
.frame(width: screenSize().width - 26.7, alignment: .leading)
|
||||||
@@ -33,14 +33,14 @@ struct LiveReservationCompleteView: View {
|
|||||||
.padding(.top, 16.7)
|
.padding(.top, 16.7)
|
||||||
.padding(.bottom, 26.7)
|
.padding(.bottom, 26.7)
|
||||||
|
|
||||||
Text("라이브 예약정보")
|
Text(I18n.LiveReservation.Complete.reservationInfoTitle)
|
||||||
.appFont(size: 16.7, weight: .bold)
|
.appFont(size: 16.7, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
||||||
|
|
||||||
VStack(spacing: 6.7) {
|
VStack(spacing: 6.7) {
|
||||||
HStack(spacing: 26.7) {
|
HStack(spacing: 26.7) {
|
||||||
Text("채널")
|
Text(I18n.LiveReservation.Complete.channelLabel)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "777777"))
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ struct LiveReservationCompleteView: View {
|
|||||||
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
||||||
|
|
||||||
HStack(spacing: 26.7) {
|
HStack(spacing: 26.7) {
|
||||||
Text("구매내역")
|
Text(I18n.LiveReservation.Complete.purchaseDetailLabel)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "777777"))
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ struct LiveReservationCompleteView: View {
|
|||||||
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
||||||
|
|
||||||
HStack(spacing: 26.7) {
|
HStack(spacing: 26.7) {
|
||||||
Text("예약일자")
|
Text(I18n.LiveReservation.Complete.reservationDateLabel)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "777777"))
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ struct LiveReservationCompleteView: View {
|
|||||||
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
||||||
|
|
||||||
HStack(spacing: 26.7) {
|
HStack(spacing: 26.7) {
|
||||||
Text("라이브 비용")
|
Text(I18n.LiveReservation.Complete.liveCostLabel)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "777777"))
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
|
||||||
@@ -94,14 +94,14 @@ struct LiveReservationCompleteView: View {
|
|||||||
.foregroundColor(Color(hex: "232323"))
|
.foregroundColor(Color(hex: "232323"))
|
||||||
.padding(.vertical, 20)
|
.padding(.vertical, 20)
|
||||||
|
|
||||||
Text("결제정보")
|
Text(I18n.LiveReservation.Complete.paymentInfoTitle)
|
||||||
.appFont(size: 16.7, weight: .bold)
|
.appFont(size: 16.7, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
||||||
|
|
||||||
VStack(spacing: 13.3) {
|
VStack(spacing: 13.3) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("보유캔")
|
Text(I18n.LiveReservation.Complete.ownedCanLabel)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "777777"))
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
|
||||||
@@ -111,14 +111,14 @@ struct LiveReservationCompleteView: View {
|
|||||||
.appFont(size: 15.3, weight: .bold)
|
.appFont(size: 15.3, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
|
||||||
Text(" 캔")
|
Text(I18n.LiveReservation.Complete.canSuffix)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
}
|
}
|
||||||
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("결제캔")
|
Text(I18n.LiveReservation.Complete.paymentCanLabel)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "777777"))
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
|
||||||
@@ -128,14 +128,14 @@ struct LiveReservationCompleteView: View {
|
|||||||
.appFont(size: 15.3, weight: .bold)
|
.appFont(size: 15.3, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
|
||||||
Text(" 캔")
|
Text(I18n.LiveReservation.Complete.canSuffix)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
}
|
}
|
||||||
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
.frame(width: screenSize().width - 53.4, alignment: .leading)
|
||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("잔여캔")
|
Text(I18n.LiveReservation.Complete.remainingCanLabel)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "777777"))
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ struct LiveReservationCompleteView: View {
|
|||||||
.appFont(size: 15.3, weight: .bold)
|
.appFont(size: 15.3, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
|
||||||
Text(" 캔")
|
Text(I18n.LiveReservation.Complete.canSuffix)
|
||||||
.appFont(size: 14.7, weight: .medium)
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ struct LiveReservationCompleteView: View {
|
|||||||
.padding(.top, 20)
|
.padding(.top, 20)
|
||||||
|
|
||||||
HStack(spacing: 13.3) {
|
HStack(spacing: 13.3) {
|
||||||
Text("홈으로 이동")
|
Text(I18n.LiveReservation.Complete.goHome)
|
||||||
.appFont(size: 18.3, weight: .bold)
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "9970ff"))
|
.foregroundColor(Color(hex: "9970ff"))
|
||||||
.padding(.vertical, 16)
|
.padding(.vertical, 16)
|
||||||
@@ -170,7 +170,7 @@ struct LiveReservationCompleteView: View {
|
|||||||
AppState.shared.setAppStep(step: .main)
|
AppState.shared.setAppStep(step: .main)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("예약 내역 이동")
|
Text(I18n.LiveReservation.Complete.goReservationList)
|
||||||
.appFont(size: 18.3, weight: .bold)
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(.vertical, 16)
|
.padding(.vertical, 16)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ struct LiveReservationItemView: View {
|
|||||||
|
|
||||||
VStack(alignment: .trailing, spacing: 8) {
|
VStack(alignment: .trailing, spacing: 8) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
Text("\(dateDic["month"] ?? "")월")
|
Text(I18n.LiveReservation.Item.month(dateDic["month"] ?? ""))
|
||||||
.appFont(size: 14, weight: .bold)
|
.appFont(size: 14, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(.vertical, 6)
|
.padding(.vertical, 6)
|
||||||
@@ -100,7 +100,7 @@ struct LiveReservationItemView: View {
|
|||||||
.background(Color(hex: "3b5ff1"))
|
.background(Color(hex: "3b5ff1"))
|
||||||
.cornerRadius(4)
|
.cornerRadius(4)
|
||||||
} else {
|
} else {
|
||||||
Text("무료")
|
Text(I18n.LiveReservation.Item.free)
|
||||||
.appFont(size: 14, weight: .regular)
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "#263238"))
|
.foregroundColor(Color(hex: "#263238"))
|
||||||
.padding(4)
|
.padding(4)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ struct MyLiveReservationItemView: View {
|
|||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
Image("ic_mic_colored")
|
Image("ic_mic_colored")
|
||||||
|
|
||||||
Text("내가 개설한 라이브")
|
Text(I18n.LiveReservation.Item.ownCreatedLive)
|
||||||
.appFont(size: 18, weight: .bold)
|
.appFont(size: 18, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "80D8FF"))
|
.foregroundColor(Color(hex: "80D8FF"))
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ struct MyLiveReservationItemView: View {
|
|||||||
|
|
||||||
VStack(alignment: .trailing, spacing: 8) {
|
VStack(alignment: .trailing, spacing: 8) {
|
||||||
VStack(spacing: 4) {
|
VStack(spacing: 4) {
|
||||||
Text("\(dateDic["month"] ?? "")월")
|
Text(I18n.LiveReservation.Item.month(dateDic["month"] ?? ""))
|
||||||
.appFont(size: 14, weight: .bold)
|
.appFont(size: 14, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(.vertical, 6)
|
.padding(.vertical, 6)
|
||||||
@@ -112,7 +112,7 @@ struct MyLiveReservationItemView: View {
|
|||||||
.background(Color(hex: "3b5ff1"))
|
.background(Color(hex: "3b5ff1"))
|
||||||
.cornerRadius(4)
|
.cornerRadius(4)
|
||||||
} else {
|
} else {
|
||||||
Text("무료")
|
Text(I18n.LiveReservation.Item.free)
|
||||||
.appFont(size: 14, weight: .regular)
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "#263238"))
|
.foregroundColor(Color(hex: "#263238"))
|
||||||
.padding(4)
|
.padding(4)
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ struct SectionLiveReservationView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 13.3) {
|
VStack(spacing: 13.3) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("라이브 예약중")
|
Text(I18n.LiveReservation.Section.title)
|
||||||
.appFont(size: 24, weight: .bold)
|
.appFont(size: 24, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if items.count > 0 {
|
if items.count > 0 {
|
||||||
Text("전체보기")
|
Text(I18n.Common.viewAll)
|
||||||
.appFont(size: 14, weight: .regular)
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "78909C"))
|
.foregroundColor(Color(hex: "78909C"))
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
@@ -101,7 +101,7 @@ struct SectionLiveReservationView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 60, height: 60)
|
.frame(width: 60, height: 60)
|
||||||
|
|
||||||
Text("지금 예약중인 라이브가 없습니다.\n채널을 팔로잉 하고 라이브 알림을 받아 보세요.")
|
Text(I18n.LiveReservation.Section.emptyStateMessage)
|
||||||
.appFont(size: 13, weight: .medium)
|
.appFont(size: 13, weight: .medium)
|
||||||
.foregroundColor(Color(hex: "bbbbbb"))
|
.foregroundColor(Color(hex: "bbbbbb"))
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ struct LiveRoomChatItemView: View {
|
|||||||
VStack(alignment: .leading, spacing: 6.7) {
|
VStack(alignment: .leading, spacing: 6.7) {
|
||||||
HStack(spacing: 5) {
|
HStack(spacing: 5) {
|
||||||
if chatMessage.rank == -3 {
|
if chatMessage.rank == -3 {
|
||||||
Text("스탭")
|
Text(I18n.LiveChat.staffBadge)
|
||||||
.appFont(size: 10)
|
.appFont(size: 10)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(2)
|
.padding(2)
|
||||||
|
|||||||
@@ -40,17 +40,17 @@ struct LiveRoomDonationChatItemView: View {
|
|||||||
.appFont(size: 12)
|
.appFont(size: 12)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Text("님이")
|
Text(I18n.LiveChat.donationMemberSuffix)
|
||||||
.appFont(size: 12, weight: .light)
|
.appFont(size: 12, weight: .light)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("\(chatMessage.can)캔")
|
Text(I18n.LiveChat.canWithUnit(chatMessage.can))
|
||||||
.appFont(size: 15)
|
.appFont(size: 15)
|
||||||
.foregroundColor(Color(hex: "fdca2f"))
|
.foregroundColor(Color(hex: "fdca2f"))
|
||||||
|
|
||||||
Text(chatMessage.chat.contains("비밀") ? "으로 비밀미션을 보냈습니다.🤫" : "을 후원하셨습니다.💰🪙")
|
Text(chatMessage.chat.contains("비밀") ? I18n.LiveChat.secretMissionDonationSuffix : I18n.LiveChat.donationSuffix)
|
||||||
.appFont(size: 15)
|
.appFont(size: 15)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ struct LiveRoomHeartDonationChatItemView: View {
|
|||||||
.appFont(size: 12, weight: .bold)
|
.appFont(size: 12, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "ec3aa6"))
|
.foregroundColor(Color(hex: "ec3aa6"))
|
||||||
|
|
||||||
Text("'님이 마음을 전했습니다 : 💕")
|
Text(I18n.LiveChat.heartDonationSuffix)
|
||||||
.appFont(size: 12)
|
.appFont(size: 12)
|
||||||
.foregroundColor(Color.gray11)
|
.foregroundColor(Color.gray11)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ struct LiveRoomJoinChatItemView: View {
|
|||||||
.appFont(size: 12, weight: .bold)
|
.appFont(size: 12, weight: .bold)
|
||||||
.foregroundColor(Color.mainYellow)
|
.foregroundColor(Color.mainYellow)
|
||||||
|
|
||||||
Text("'님이 입장하셨습니다.")
|
Text(I18n.LiveChat.joinSuffix)
|
||||||
.appFont(size: 12)
|
.appFont(size: 12)
|
||||||
.foregroundColor(Color.grayee)
|
.foregroundColor(Color.grayee)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -319,28 +319,28 @@
|
|||||||
|
|
||||||
### Live (56)
|
### Live (56)
|
||||||
#### Group 1 (1-10)
|
#### Group 1 (1-10)
|
||||||
- [ ] `SodaLive/Sources/Live/Cancel/LiveCancelDialog.swift`
|
- [x] `SodaLive/Sources/Live/Cancel/LiveCancelDialog.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/LatestFinishedLiveItemView.swift`
|
- [x] `SodaLive/Sources/Live/LatestFinishedLiveItemView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/LiveReplayListView.swift`
|
- [x] `SodaLive/Sources/Live/LiveReplayListView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/LiveView.swift`
|
- [x] `SodaLive/Sources/Live/LiveView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/LiveViewModel.swift`
|
- [x] `SodaLive/Sources/Live/LiveViewModel.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Now/All/LiveNowAllItemView.swift`
|
- [x] `SodaLive/Sources/Live/Now/All/LiveNowAllItemView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Now/All/LiveNowAllView.swift`
|
- [x] `SodaLive/Sources/Live/Now/All/LiveNowAllView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Now/LiveNowItemView.swift`
|
- [x] `SodaLive/Sources/Live/Now/LiveNowItemView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Now/SectionLiveNowView.swift`
|
- [x] `SodaLive/Sources/Live/Now/SectionLiveNowView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/RecommendChannel/SectionRecommendChannelView.swift`
|
- [x] `SodaLive/Sources/Live/RecommendChannel/SectionRecommendChannelView.swift`
|
||||||
|
|
||||||
#### Group 2 (11-20)
|
#### Group 2 (11-20)
|
||||||
- [ ] `SodaLive/Sources/Live/Reservation/All/LiveReservationAllItemView.swift`
|
- [x] `SodaLive/Sources/Live/Reservation/All/LiveReservationAllItemView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Reservation/All/LiveReservationAllView.swift`
|
- [x] `SodaLive/Sources/Live/Reservation/All/LiveReservationAllView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Reservation/Complete/LiveReservationCompleteView.swift`
|
- [x] `SodaLive/Sources/Live/Reservation/Complete/LiveReservationCompleteView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Reservation/LiveReservationItemView.swift`
|
- [x] `SodaLive/Sources/Live/Reservation/LiveReservationItemView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Reservation/MyLiveReservationItemView.swift`
|
- [x] `SodaLive/Sources/Live/Reservation/MyLiveReservationItemView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Reservation/SectionLiveReservationView.swift`
|
- [x] `SodaLive/Sources/Live/Reservation/SectionLiveReservationView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Room/Chat/LiveRoomChatItemView.swift`
|
- [x] `SodaLive/Sources/Live/Room/Chat/LiveRoomChatItemView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Room/Chat/LiveRoomDonationChatItemView.swift`
|
- [x] `SodaLive/Sources/Live/Room/Chat/LiveRoomDonationChatItemView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Room/Chat/LiveRoomHeartDonationChatItemView.swift`
|
- [x] `SodaLive/Sources/Live/Room/Chat/LiveRoomHeartDonationChatItemView.swift`
|
||||||
- [ ] `SodaLive/Sources/Live/Room/Chat/LiveRoomJoinChatItemView.swift`
|
- [x] `SodaLive/Sources/Live/Room/Chat/LiveRoomJoinChatItemView.swift`
|
||||||
|
|
||||||
#### Group 3 (21-30)
|
#### Group 3 (21-30)
|
||||||
- [ ] `SodaLive/Sources/Live/Room/Chat/LiveRoomRouletteDonationChatItemView.swift`
|
- [ ] `SodaLive/Sources/Live/Room/Chat/LiveRoomRouletteDonationChatItemView.swift`
|
||||||
@@ -892,3 +892,41 @@
|
|||||||
- 공통 키 재사용 정리: `I18n.Common.viewAll`, `I18n.Common.latestContent`, `I18n.Settings.companyInfo`, `I18n.Chat.Auth.*`, `I18n.LiveRoom.follow/following` 적용.
|
- 공통 키 재사용 정리: `I18n.Common.viewAll`, `I18n.Common.latestContent`, `I18n.Settings.companyInfo`, `I18n.Chat.Auth.*`, `I18n.LiveRoom.follow/following` 적용.
|
||||||
- Oracle 후속 보정: 홈 FAB 버튼 문구를 제목형 키(`uploadTitle`)에서 CTA 전용 키(`I18n.CreateContent.uploadAction`)로 분리해 영문/일문 의미를 버튼 행동과 일치시킴.
|
- Oracle 후속 보정: 홈 FAB 버튼 문구를 제목형 키(`uploadTitle`)에서 CTA 전용 키(`I18n.CreateContent.uploadAction`)로 분리해 영문/일문 의미를 버튼 행동과 일치시킴.
|
||||||
- Home Group 1 체크박스 9개 `- [x]` 완료 반영.
|
- Home Group 1 체크박스 9개 `- [x]` 완료 반영.
|
||||||
|
|
||||||
|
### 18차 구현 (Live 모듈 Group 1~2, 20개 파일 처리, 2026-04-01)
|
||||||
|
- 무엇/왜/어떻게:
|
||||||
|
- 무엇: `변경 대상 파일 전체 목록`의 `Live` Group 1~2(20개 파일)를 전수 점검하고, 런타임 사용자 노출 하드코딩 문구를 `I18n.*`로 전환했다.
|
||||||
|
- 왜: Live 메인/실시간 목록/예약/채팅 아이템에 하드코딩 문구가 남아 있어 모듈 간 i18n 접근이 불일치했고, 동일 의미 문구가 ViewModel에 중복되어 유지보수 비용이 높았기 때문이다.
|
||||||
|
- 어떻게: explore/librarian 병렬 탐색 + `grep`/`ast_grep_search` 직접 점검으로 대상 문자열을 분류한 뒤, `I18n.swift`에 Live 전용 키셋(`LiveMain`, `LiveNow`, `LiveReservation`, `LiveChat`)을 추가하고 호출부를 치환했다. 기존 공통 키(`I18n.Common`, `I18n.MemberChannel`, `I18n.Main.Auth`)는 재사용했다.
|
||||||
|
- 실행 명령/도구:
|
||||||
|
- `task(subagent_type="explore", ...)` x2 (`bg_d093725e`, `bg_d4acf3b2`)
|
||||||
|
- `task(subagent_type="librarian", ...)` x2 (`bg_cfe29077`, `bg_b4c29632`)
|
||||||
|
- `background_output(task_id=...)` x4 (위 4개 task 결과 수집)
|
||||||
|
- `grep("\"[^\"]*[가-힣][^\"]*\"", include=대상파일, path=SodaLive/Sources/Live/**)`
|
||||||
|
- `grep("String\\(localized:|LocalizedStringKey\\(|NSLocalizedString\\(", include=*.swift, path=SodaLive/Sources/Live)`
|
||||||
|
- `ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Live])`
|
||||||
|
- `bash: rg -n ...` (`command not found` 확인)
|
||||||
|
- `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.LiveMain`, `I18n.LiveReservation(Section/All/Item/Complete)`, `I18n.LiveChat`
|
||||||
|
- 확장: `I18n.LiveNow(sectionTitle/emptyStateMessage/refreshButton/followingChannelsTitle/liveBadge/moreButton)`, `I18n.LiveCancel(title/cancelButton/confirmButton)`, `I18n.MemberChannel.alreadyEndedLive`
|
||||||
|
- 치환 완료 파일(실치환 18개):
|
||||||
|
- `LiveCancelDialog.swift`, `LiveReplayListView.swift`, `LiveView.swift`, `LiveViewModel.swift`
|
||||||
|
- `LiveNowAllView.swift`, `LiveNowItemView.swift`, `SectionLiveNowView.swift`, `SectionRecommendChannelView.swift`
|
||||||
|
- `LiveReservationAllItemView.swift`, `LiveReservationAllView.swift`, `LiveReservationCompleteView.swift`, `LiveReservationItemView.swift`, `MyLiveReservationItemView.swift`, `SectionLiveReservationView.swift`
|
||||||
|
- `LiveRoomChatItemView.swift`, `LiveRoomDonationChatItemView.swift`, `LiveRoomHeartDonationChatItemView.swift`, `LiveRoomJoinChatItemView.swift`
|
||||||
|
- 점검만 수행(실치환 없음, 체크 완료 2개):
|
||||||
|
- `LatestFinishedLiveItemView.swift` (런타임 고정 문구 없음, 표시값은 API 기반)
|
||||||
|
- `LiveNowAllItemView.swift` (런타임 문구가 기존 `I18n` 참조 또는 데이터 바인딩)
|
||||||
|
- Group 1~2 체크박스 20개 `- [x]` 반영 완료.
|
||||||
|
- 대상 재탐지 결과, 잔여 한글 리터럴은 Preview 샘플/SDK 입력값(`payload.pg`, `payload.method`, `payload.orderName`)/서버 메시지 분기 비교(`message.contains("종료")`)만 존재.
|
||||||
|
- 빌드 검증:
|
||||||
|
- `SodaLive` Debug 빌드 성공(`** BUILD SUCCEEDED **`).
|
||||||
|
- `SodaLive-dev` Debug 빌드는 병렬 실행 시 `build.db` lock으로 1회 실패 후, 단독 재실행에서 성공(`** BUILD SUCCEEDED **`).
|
||||||
|
- 테스트 검증: 두 스킴 모두 `Scheme ... is not currently configured for the test action.`로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약).
|
||||||
|
- LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(`RefreshableScrollView`, `Kingfisher`, `AppState` 등)가 보고되나, 동일 변경셋은 `xcodebuild` 실컴파일 통과로 검증 완료.
|
||||||
|
|||||||
Reference in New Issue
Block a user