diff --git a/SodaLive/Resources/Localizable.xcstrings b/SodaLive/Resources/Localizable.xcstrings index 083d7fc..ff576ab 100644 --- a/SodaLive/Resources/Localizable.xcstrings +++ b/SodaLive/Resources/Localizable.xcstrings @@ -4049,22 +4049,6 @@ } } }, - "목" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Thu" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "木" - } - } - } - }, "모든 기기에서 로그아웃" : { "localizations" : { "en" : { @@ -4081,6 +4065,22 @@ } } }, + "목" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thu" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "木" + } + } + } + }, "모집완료" : { "localizations" : { "en" : { @@ -6801,6 +6801,22 @@ } } }, + "일" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sun" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "日" + } + } + } + }, "이미지" : { "localizations" : { "en" : { @@ -7025,22 +7041,6 @@ } } }, - "일" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sun" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "日" - } - } - } - }, "일간 랭킹" : { "localizations" : { "en" : { @@ -9201,6 +9201,22 @@ } } }, + "팬 Talk 전체보기" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "View all Fan Talk" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ファントークをすべて見る" + } + } + } + }, "펼쳐보기" : { "localizations" : { "en" : { diff --git a/SodaLive/Sources/Dialog/CreatorFollowNotifyDialog.swift b/SodaLive/Sources/Dialog/CreatorFollowNotifyDialog.swift index adf31d4..3f2cb94 100644 --- a/SodaLive/Sources/Dialog/CreatorFollowNotifyDialog.swift +++ b/SodaLive/Sources/Dialog/CreatorFollowNotifyDialog.swift @@ -34,7 +34,7 @@ struct CreatorFollowNotifyDialog: View { CreatorFollowNotifyItem( image: "ic_notify_all", - title: "전체", + title: I18n.MemberChannel.all, onTapGesture: { isShowing = false onClickNotifyAll() @@ -42,7 +42,7 @@ struct CreatorFollowNotifyDialog: View { ) CreatorFollowNotifyItem( image: "ic_notify_none", - title: "없음", + title: I18n.MemberChannel.none, onTapGesture: { isShowing = false onClickNotifyNone() @@ -50,7 +50,7 @@ struct CreatorFollowNotifyDialog: View { ) CreatorFollowNotifyItem( image: "ic_avatar_unfollow", - title: "팔로우 취소", + title: I18n.MemberChannel.unfollow, onTapGesture: { isShowing = false onClickUnFollow() diff --git a/SodaLive/Sources/Explorer/Profile/FanTalk/UserProfileFanTalkAllView.swift b/SodaLive/Sources/Explorer/Profile/FanTalk/UserProfileFanTalkAllView.swift index df16ea3..5ce0116 100644 --- a/SodaLive/Sources/Explorer/Profile/FanTalk/UserProfileFanTalkAllView.swift +++ b/SodaLive/Sources/Explorer/Profile/FanTalk/UserProfileFanTalkAllView.swift @@ -23,7 +23,7 @@ struct UserProfileFanTalkAllView: View { GeometryReader { proxy in BaseView(isLoading: $viewModel.isLoading) { VStack(spacing: 0) { - DetailNavigationBar(title: "팬 Talk 전체보기") + DetailNavigationBar(title: String(localized: "팬 Talk 전체보기")) VStack(alignment: .leading, spacing: 0) { HStack(spacing: 6.7) { diff --git a/SodaLive/Sources/Explorer/Profile/UserProfileView.swift b/SodaLive/Sources/Explorer/Profile/UserProfileView.swift index 06adce7..0bc6ffd 100644 --- a/SodaLive/Sources/Explorer/Profile/UserProfileView.swift +++ b/SodaLive/Sources/Explorer/Profile/UserProfileView.swift @@ -65,7 +65,7 @@ struct UserProfileView: View { } if creatorProfile.creator.creatorId == UserDefaults.int(forKey: .userId) { - Text("팔로워 리스트") + Text(I18n.MemberChannel.followersList) .font(.custom(Font.preBold.rawValue, size: 16)) .foregroundColor(Color.black) .padding(.vertical, 8) @@ -77,7 +77,7 @@ struct UserProfileView: View { } } else { VStack(alignment: .leading, spacing: 9.3) { - Text("팔로워 \(creatorProfile.creator.notificationRecipientCount)명") + Text(I18n.MemberChannel.followerCount(creatorProfile.creator.notificationRecipientCount)) .font(.custom(Font.preMedium.rawValue, size: 16)) .foregroundColor(Color.white) } @@ -98,7 +98,7 @@ struct UserProfileView: View { .cornerRadius(12) VStack(alignment: .leading, spacing: 8) { - Text("최신 콘텐츠") + Text(I18n.Common.latestContent) .font(.custom(Font.preMedium.rawValue, size: 12)) .foregroundColor(.button) .padding(.horizontal, 7) @@ -113,7 +113,7 @@ struct UserProfileView: View { HStack(spacing: 8) { if item.isScheduledToOpen { - Text("오픈예정") + Text(I18n.Common.openScheduled) .font(.custom(Font.preMedium.rawValue, size: 12)) .foregroundColor(Color(hex: "3bb9f1")) .padding(2.6) @@ -136,7 +136,7 @@ struct UserProfileView: View { .cornerRadius(2.6) if item.isPointAvailable { - Text("포인트") + Text(I18n.Common.points) .font(.custom(Font.preMedium.rawValue, size: 12)) .foregroundColor(.white) .padding(2.6) @@ -189,7 +189,7 @@ struct UserProfileView: View { if creatorProfile.creator.creatorId == UserDefaults.int(forKey: .userId) || creatorProfile.liveRoomList.count > 0 { VStack(alignment: .leading, spacing: 14) { HStack(spacing: 0) { - Text("라이브") + Text(I18n.MemberChannel.liveHeader) .font(.custom(Font.preBold.rawValue, size: 26)) .foregroundColor(Color.white) @@ -198,7 +198,7 @@ struct UserProfileView: View { if creatorProfile.creator.creatorId == UserDefaults.int(forKey: .userId) { HStack(spacing: 8) { - Text("룰렛 설정") + Text(I18n.MemberChannel.rouletteSettings) .font(.custom(Font.preBold.rawValue, size: 16)) .foregroundColor(Color.grayee) .padding(.vertical, 12) @@ -207,7 +207,7 @@ struct UserProfileView: View { .cornerRadius(12) .onTapGesture { isShowRouletteSettings = true } - Text("메뉴 설정") + Text(I18n.MemberChannel.menuSettings) .font(.custom(Font.preBold.rawValue, size: 16)) .foregroundColor(Color.grayee) .padding(.vertical, 12) @@ -224,7 +224,7 @@ struct UserProfileView: View { liveRoomList: creatorProfile.liveRoomList, onClickParticipant: { liveRoom in if creatorProfile.creator.creatorId == UserDefaults.int(forKey: .userId) { - viewModel.errorMessage = "현재 라이브 중입니다." + viewModel.errorMessage = I18n.MemberChannel.liveOnNow viewModel.isShowPopup = true } else { AppState.shared.isShowPlayer = false @@ -235,7 +235,7 @@ struct UserProfileView: View { }, onClickReservation: { liveRoom in if creatorProfile.creator.creatorId == UserDefaults.int(forKey: .userId) { - viewModel.errorMessage = "내가 만든 라이브는 예약할 수 없습니다." + viewModel.errorMessage = I18n.MemberChannel.cannotReserveOwnLive viewModel.isShowPopup = true } else { viewModel.reservationLiveRoom(roomId: liveRoom.roomId) @@ -259,7 +259,7 @@ struct UserProfileView: View { VStack(alignment: .leading, spacing: 14) { HStack(spacing: 0) { - Text("커뮤니티") + Text(I18n.MemberChannel.communityHeader) .font(.custom(Font.preBold.rawValue, size: 26)) .foregroundColor(Color.white) .padding(.horizontal, 24) @@ -430,14 +430,14 @@ struct UserProfileView: View { if viewModel.isShowCheersDeleteView { SodaDialog( - title: "응원글 삭제", - desc: "삭제하시겠습니까?", - confirmButtonTitle: "삭제", + title: I18n.MemberChannel.cheersDeleteTitle, + desc: I18n.Common.confirmDeleteQuestion, + confirmButtonTitle: I18n.Common.delete, confirmButtonAction: { viewModel.deleteCheers(creatorId: userId) viewModel.isShowCheersDeleteView = false }, - cancelButtonTitle: "취소", + cancelButtonTitle: I18n.Common.cancel, cancelButtonAction: { viewModel.isShowCheersDeleteView = false } ) } diff --git a/SodaLive/Sources/Explorer/Profile/UserProfileViewModel.swift b/SodaLive/Sources/Explorer/Profile/UserProfileViewModel.swift index f9a332a..da07f58 100644 --- a/SodaLive/Sources/Explorer/Profile/UserProfileViewModel.swift +++ b/SodaLive/Sources/Explorer/Profile/UserProfileViewModel.swift @@ -52,7 +52,7 @@ final class UserProfileViewModel: ObservableObject { @Published var liveStartDate: String? = nil @Published var nowDate: String? = nil - let paymentDialogCancelTitle = "취소" + let paymentDialogCancelTitle = I18n.Common.cancel func getCreatorProfile(userId: Int) { creatorProfile = nil @@ -75,7 +75,7 @@ final class UserProfileViewModel: ObservableObject { if let data = decoded.data, decoded.success { self.creatorProfile = data - self.navigationTitle = "\(data.creator.nickname)님의 채널" + self.navigationTitle = I18n.MemberChannel.channelTitle(data.creator.nickname) self.communityPostList.removeAll() self.communityPostList.append(contentsOf: data.communityPostList) @@ -113,7 +113,7 @@ final class UserProfileViewModel: ObservableObject { func reservationLiveRoom(roomId: Int) { getRoomDetail(roomId: roomId) { [unowned self] in if ($0.manager.id == UserDefaults.int(forKey: .userId)) { - self.errorMessage = "내가 만든 라이브는 예약할 수 없습니다." + self.errorMessage = I18n.MemberChannel.cannotReserveOwnLive self.isShowPopup = true } else { if $0.isPrivateRoom { @@ -125,9 +125,9 @@ final class UserProfileViewModel: ObservableObject { if ($0.price == 0 || $0.isPaid) { self.reservation(roomId: roomId) } else { - self.paymentDialogTitle = "\($0.price)캔으로 예약" - self.paymentDialogDesc = "'\($0.title)' 라이브에 참여하기 위해 결제합니다." - self.paymentDialogConfirmTitle = "결제 후 예약하기" + self.paymentDialogTitle = I18n.MemberChannel.reserveWithCansTitle($0.price) + self.paymentDialogDesc = I18n.MemberChannel.reservePaymentDesc($0.title) + self.paymentDialogConfirmTitle = I18n.MemberChannel.reservePaymentConfirmTitle self.paymentDialogConfirmAction = { [unowned self] in hidePaymentPopup() reservation(roomId: roomId) @@ -216,12 +216,12 @@ final class UserProfileViewModel: ObservableObject { if hours >= 1 { self.liveStartDate = beginDate.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.paymentDialogDesc = "\($0.price)캔을 차감하고\n라이브에 입장 하시겠습니까?" - self.paymentDialogConfirmTitle = "결제 후 참여하기" + self.paymentDialogTitle = I18n.MemberChannel.paidLiveEnterTitle + self.paymentDialogDesc = I18n.MemberChannel.paidLiveEnterDesc($0.price) + self.paymentDialogConfirmTitle = I18n.MemberChannel.paidLiveConfirmTitle self.paymentDialogConfirmAction = { [unowned self] in hidePaymentPopup() self.enterRoom(roomId: roomId) @@ -267,13 +267,13 @@ final class UserProfileViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "라이브에 입장하지 못했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.MemberChannel.enterLiveFailed } self.isShowPopup = true } } catch { - self.errorMessage = "라이브에 입장하지 못했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.MemberChannel.enterLiveFailed self.isShowPopup = true } } @@ -383,13 +383,13 @@ final class UserProfileViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "라이브 정보를 가져오지 못했습니다.\n다시 시도해 주세요." + self.errorMessage = I18n.MemberChannel.fetchLiveInfoFailed } self.isShowPopup = true } } catch { - self.errorMessage = "라이브 정보를 가져오지 못했습니다.\n다시 시도해 주세요." + self.errorMessage = I18n.MemberChannel.fetchLiveInfoFailed self.isShowPopup = true } @@ -408,10 +408,10 @@ final class UserProfileViewModel: ObservableObject { ] if let shareUrl = createOneLinkUrlWithURLComponents(params: params) { - self.shareMessage = "보이스온 \(nickname)님의 채널입니다.\n\(shareUrl)" + self.shareMessage = I18n.MemberChannel.shareChannelMessage(nickname) + "\n\(shareUrl)" self.isShowShareView = true } else { - self.errorMessage = "공유링크를 생성하지 못했습니다.\n다시 시도해 주세요." + self.errorMessage = I18n.MemberChannel.shareLinkCreateFailed self.isShowPopup = true } @@ -438,7 +438,7 @@ final class UserProfileViewModel: ObservableObject { if decoded.success { getCreatorProfile(userId: userId) - self.errorMessage = "차단하였습니다." + self.errorMessage = I18n.MemberChannel.userBlocked } else { if let message = decoded.message { self.errorMessage = message diff --git a/SodaLive/Sources/I18n/I18n.swift b/SodaLive/Sources/I18n/I18n.swift index e4d7895..fd394d6 100644 --- a/SodaLive/Sources/I18n/I18n.swift +++ b/SodaLive/Sources/I18n/I18n.swift @@ -13,20 +13,29 @@ import Foundation enum I18n { enum Common { static var viewAll: String { pick(ko: "전체보기", en: "View all", ja: "すべて見る") } - + // 기본 샘플들 static var apply: String { pick(ko: "적용", en: "Apply", ja: "適用") } static var confirm: String { pick(ko: "확인", en: "Confirm", ja: "確認") } static var cancel: String { pick(ko: "취소", en: "Cancel", ja: "キャンセル") } + // 공통 액션/라벨 + static var delete: String { pick(ko: "삭제", en: "Delete", ja: "削除") } // 설정 static var settings: String { pick(ko: "설정", en: "Settings", ja: "設定") } - - static var commonError: String { pick(ko: "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.", en: "Settings", ja: "設定") } - + + static var commonError: String { pick(ko: "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.", en: "Please try again.\nIf the problem persists, please contact customer support.", ja: "もう一度お試しください。\n問題が続く場合はカスタマーサポートにお問い合わせください。") } + static var roulette1: String { pick(ko: "룰렛 1", en: "Roulette 1", ja: "ルーレット1") } static var roulette2: String { pick(ko: "룰렛 2", en: "Roulette 2", ja: "ルーレット2") } static var roulette3: String { pick(ko: "룰렛 3", en: "Roulette 3", ja: "ルーレット3") } + + static var confirmDeleteQuestion: String { pick(ko: "삭제하시겠습니까?", en: "Are you sure you want to delete?", ja: "削除しますか?") } + + static var followers: String { pick(ko: "팔로워", en: "Followers", ja: "フォロワー") } + static var latestContent: String { pick(ko: "최신 콘텐츠", en: "Latest content", ja: "最新コンテンツ") } + static var openScheduled: String { pick(ko: "오픈예정", en: "Scheduled to open", ja: "オープン予定") } + static var points: String { pick(ko: "포인트", en: "Points", ja: "ポイント") } } enum MissionMenu { @@ -37,25 +46,25 @@ enum I18n { static var needMenu1First: String { pick(ko: "메뉴 1을 먼저 설정하세요", en: "Please set Menu 1 first", ja: "まずメニュー1を設定してください") } static var needMenu1And2First: String { pick(ko: "메뉴 1과 메뉴 2를 먼저 설정하세요", en: "Please set Menu 1 and Menu 2 first", ja: "まずメニュー1とメニュー2を設定してください") } } - + enum CreateLive { // 라이브 공지 입력 힌트 static var noticePlaceholder: String { pick(ko: "라이브 공지를 입력하세요", en: "Enter live notice", ja: "ライブのお知らせを入力してください") } - + // 시간 설정 static var startNow: String { pick(ko: "지금 즉시", en: "Start now", ja: "今すぐ開始") } static var schedule: String { pick(ko: "예약 설정", en: "Schedule", ja: "予約設定") } - + // 공개 범위 static var publicRoom: String { pick(ko: "공개", en: "Public", ja: "公開") } static var privateRoom: String { pick(ko: "비공개", en: "Private", ja: "非公開") } - + // 참여 가능 여부 static var joinAllowed: String { pick(ko: "가능", en: "Allowed", ja: "可能") } static var joinNotAllowed: String { pick(ko: "불가능", en: "Not allowed", ja: "不可") } - + // 연령 제한 static var allAges: String { pick(ko: "전체 연령", en: "All ages", ja: "全年齢") } static var over19: String { pick(ko: "19세 이상", en: "19+", ja: "19歳以上") } @@ -77,7 +86,7 @@ enum I18n { static var selectFile: String { pick(ko: "파일 선택", en: "Select file", ja: "ファイル選択") } static var selectTheme: String { pick(ko: "테마 선택", en: "Select theme", ja: "テーマを選択") } static var uploadContentDescriptionHint: String { pick(ko: "내용을 입력하세요", en: "Enter a description", ja: "内容を入力してください") } - + // 가격/구매 옵션 static var free: String { pick(ko: "무료", en: "Free", ja: "無料") } static var paid: String { pick(ko: "유료", en: "Paid", ja: "有料") } @@ -86,32 +95,32 @@ enum I18n { static var purchaseRentOnly: String { pick(ko: "대여만", en: "Rent only", ja: "レンタルのみ") } static var unlimited: String { pick(ko: "무제한", en: "Unlimited", ja: "無制限") } static var limitedEdition: String { pick(ko: "한정판", en: "Limited", ja: "限定版") } - + // 포인트 사용 static var available: String { pick(ko: "가능", en: "Available", ja: "可能") } static var unavailable: String { pick(ko: "불가능", en: "Unavailable", ja: "不可") } - + // 미리듣기 static var generate: String { pick(ko: "생성", en: "Generate", ja: "生成") } static var doNotGenerate: String { pick(ko: "생성 안 함", en: "Do not generate", ja: "生成しない") } - + // 연령 제한 static var allAges: String { pick(ko: "전체 연령", en: "All ages", ja: "全年齢") } static var over19: String { pick(ko: "19세 이상", en: "19+", ja: "19歳以上") } - + // 댓글 가능 여부 static var commentAllowed: String { pick(ko: "댓글 가능", en: "Comments allowed", ja: "コメント可") } static var commentNotAllowed: String { pick(ko: "댓글 불가", en: "Comments not allowed", ja: "コメント不可") } - + // 공개 설정 static var publishNow: String { pick(ko: "지금 공개", en: "Publish now", ja: "今すぐ公開") } static var publishReserved: String { pick(ko: "예약 공개", en: "Scheduled", ja: "予約公開") } } - + enum Category { static var all: String { pick(ko: "전체", en: "All", ja: "すべて") } } - + enum RankingSort { // 분석/지표 등 static var revenue: String { pick(ko: "매출", en: "Revenue", ja: "売上高") } @@ -119,7 +128,7 @@ enum I18n { static var comments: String { pick(ko: "댓글", en: "Comments", ja: "コメント") } static var likes: String { pick(ko: "좋아요", en: "Likes", ja: "いいね") } } - + enum Tab { // 탭/도메인 static var character: String { pick(ko: "캐릭터", en: "Character", ja: "キャラクター") } @@ -130,6 +139,73 @@ enum I18n { static var detail: String { pick(ko: "상세", en: "Details", ja: "詳細") } static var gallery: String { pick(ko: "갤러리", en: "Gallery", ja: "ギャラリー") } } + + enum MemberChannel { + // 팔로우 알림 설정 다이얼로그용 + static var all: String { pick(ko: "모두 알림", en: "All notifications", ja: "すべて通知") } + static var none: String { pick(ko: "알림 없음", en: "No notifications", ja: "通知なし") } + static var unfollow: String { pick(ko: "팔로우 취소", en: "Unfollow", ja: "フォロー解除") } + + static var liveOnNow: String { pick(ko: "현재 라이브 중입니다.", en: "Live is currently ongoing.", ja: "現在ライブ配信中です。") } + static var cannotReserveOwnLive: String { pick(ko: "내가 만든 라이브는 예약할 수 없습니다.", en: "You cannot reserve your own live.", 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 fetchLiveInfoFailed: String { pick(ko: "라이브 정보를 가져오지 못했습니다.\n다시 시도해 주세요.", en: "Failed to fetch live information.\nPlease try again.", ja: "ライブ情報を取得できませんでした。\nもう一度お試しください。") } + 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 shareLinkCreateFailed: String { pick(ko: "공유링크를 생성하지 못했습니다.\n다시 시도해 주세요.", en: "Failed to create a share link.\nPlease try again.", ja: "共有リンクを作成できませんでした。\nもう一度お試しください。") } + + static var cheersDeleteTitle: String { pick(ko: "응원글 삭제", en: "Delete Cheer", ja: "応援メッセージを削除") } + + static var liveHeader: String { pick(ko: "라이브", en: "Live", ja: "ライブ") } + static var rouletteSettings: String { pick(ko: "룰렛 설정", en: "Roulette settings", ja: "ルーレット設定") } + static var menuSettings: String { pick(ko: "메뉴 설정", en: "Menu settings", ja: "メニュー設定") } + static var communityHeader: String { pick(ko: "커뮤니티", en: "Community", ja: "コミュニティ") } + static var followersList: String { pick(ko: "팔로워 리스트", en: "Followers list", ja: "フォロワーリスト") } + static var followerCount: (Int) -> String = { count in + pick(ko: "팔로워 \(count)명", en: "\(count) followers", ja: "フォロワー\(count)人") + } + + static func channelTitle(_ nickname: String) -> String { + pick(ko: "\(nickname)님의 채널", en: "\(nickname)'s channel", ja: "\(nickname)のチャンネル") + } + + static func shareChannelMessage(_ nickname: String) -> String { + pick(ko: "보이스온 \(nickname)님의 채널입니다.", en: "This is \(nickname)'s channel on VoiceOn.", ja: "ボイスオンの\(nickname)さんのチャンネルです。") + } + + static func reserveWithCansTitle(_ price: Int) -> String { + pick(ko: "\(price)캔으로 예약", en: "Reserve with \(price) cans", ja: "\(price)缶で予約") + } + + static func reservePaymentDesc(_ title: String) -> String { + pick(ko: "'\(title)' 라이브에 참여하기 위해 결제합니다.", en: "Payment is required to join '\(title)' live.", ja: "『\(title)』ライブに参加するには決済が必要です。") + } + + static var reservePaymentConfirmTitle: String { pick(ko: "결제 후 예약하기", en: "Pay and reserve", ja: "決済して予約") } + + static var paidLiveEnterTitle: String { pick(ko: "유료 라이브 입장", en: "Enter paid live", ja: "有料ライブに入場") } + static func paidLiveEnterDesc(_ price: Int) -> String { + pick(ko: "\(price)캔을 차감하고\n라이브에 입장 하시겠습니까?", en: "\(price) cans will be used.\nDo you want to enter the live?", ja: "\(price)缶が消費されます。\nライブに入場しますか?") + } + static var paidLiveConfirmTitle: String { pick(ko: "결제 후 참여하기", en: "Pay and join", ja: "決済して参加") } + + static func elapsedLiveWarning(hours: Int, minutes: Int) -> String { + pick( + ko: "라이브를 시작한 지 \(hours)시간 \(minutes)분이 지났습니다. 라이브에 입장 후 30분 이내에 라이브가 종료될 수도 있습니다.", + en: "It has been \(hours) hour(s) and \(minutes) minute(s) since the live started. The live may end within 30 minutes after you enter.", + ja: "ライブ開始から\(hours)時間\(minutes)分が経過しています。入場後30分以内に終了する場合があります。" + ) + } + } + + enum Series { + static var new: String { pick(ko: "신작", en: "New", ja: "新作") } + static var complete: String { pick(ko: "완결", en: "Completed", ja: "完結") } + static var popular: String { pick(ko: "인기", en: "Popular", ja: "人気") } + static var totalEpisodes: (Int) -> String = { count in + pick(ko: "총 \(count)화", en: "Total \(count) episodes", ja: "全\(count)話") + } + } } // MARK: - 내부 헬퍼 diff --git a/SodaLive/Sources/UI/Component/SeriesListBigItemView.swift b/SodaLive/Sources/UI/Component/SeriesListBigItemView.swift index 1ead1db..0e8e334 100644 --- a/SodaLive/Sources/UI/Component/SeriesListBigItemView.swift +++ b/SodaLive/Sources/UI/Component/SeriesListBigItemView.swift @@ -40,15 +40,15 @@ struct SeriesListBigItemView: View { VStack(alignment: .leading, spacing: 0) { HStack(spacing: 3.3) { if !item.isComplete && item.isNew { - SeriesItemBadgeView(title: "신작", backgroundColor: .button) + SeriesItemBadgeView(title: I18n.Series.new, backgroundColor: .button) } if item.isComplete { - SeriesItemBadgeView(title: "완결", backgroundColor: Color(hex: "002abd")) + SeriesItemBadgeView(title: I18n.Series.complete, backgroundColor: Color(hex: "002abd")) } if item.isPopular { - SeriesItemBadgeView(title: "인기", backgroundColor: Color(hex: "ec6033")) + SeriesItemBadgeView(title: I18n.Series.popular, backgroundColor: Color(hex: "ec6033")) } Spacer() @@ -59,7 +59,7 @@ struct SeriesListBigItemView: View { HStack { Spacer() - SeriesItemBadgeView(title: "총 \(item.numberOfContent)화", backgroundColor: Color.gray33.opacity(0.7)) + SeriesItemBadgeView(title: I18n.Series.totalEpisodes(item.numberOfContent), backgroundColor: Color.gray33.opacity(0.7)) } } .padding(3.3)