diff --git a/SodaLive/Resources/Localizable.xcstrings b/SodaLive/Resources/Localizable.xcstrings index e6779f9..600c3ab 100644 --- a/SodaLive/Resources/Localizable.xcstrings +++ b/SodaLive/Resources/Localizable.xcstrings @@ -80,6 +80,9 @@ } } } + }, + " %@" : { + }, " %lld" : { "localizations" : { @@ -4143,6 +4146,25 @@ } } }, + "모서리 원을 드래그해서 크롭 영역 크기를 조정하세요" : { + + }, + "목" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thu" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "木" + } + } + } + }, "모든 기기에서 로그아웃" : { "localizations" : { "en" : { @@ -4158,9 +4180,6 @@ } } } - }, - "모서리 원을 드래그해서 크롭 영역 크기를 조정하세요" : { - }, "모집완료" : { "localizations" : { @@ -4194,22 +4213,6 @@ } } }, - "목" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Thu" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "木" - } - } - } - }, "무료" : { "localizations" : { "en" : { @@ -8679,22 +8682,6 @@ } } }, - "캐릭터 정보" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Character info" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "キャラクター情報" - } - } - } - }, "캔" : { "localizations" : { "en" : { @@ -8711,6 +8698,22 @@ } } }, + "캐릭터 정보" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Character info" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "キャラクター情報" + } + } + } + }, "캔 충전" : { "localizations" : { "en" : { diff --git a/SodaLive/Sources/I18n/I18n.swift b/SodaLive/Sources/I18n/I18n.swift index 9317c4b..8a0ac0b 100644 --- a/SodaLive/Sources/I18n/I18n.swift +++ b/SodaLive/Sources/I18n/I18n.swift @@ -2687,6 +2687,290 @@ If you block this user, the following features will be restricted. } } + enum MyPage { + enum Common { + static var totalPrefix: String { + pick(ko: "총", en: "Total", ja: "合計") + } + + static var personUnit: String { + pick(ko: "명", en: "people", ja: "人") + } + + static var countUnit: String { + pick(ko: "개", en: "items", ja: "件") + } + } + + enum Auth { + static var verified: String { + pick(ko: "인증완료", en: "Verified", ja: "認証完了") + } + + static var verifyRequiredBeforeCoupon: String { + pick( + ko: "본인인증 후 등록합니다.", + en: "Please complete identity verification before registering.", + ja: "本人認証後に登録できます。" + ) + } + + static var verificationErrorWithSupport: String { + pick( + ko: "본인인증 중 오류가 발생했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.", + en: "An error occurred during identity verification.\nIf the issue persists, contact customer support.", + ja: "本人認証中にエラーが発生しました。\n問題が続く場合はカスタマーセンターにお問い合わせください。" + ) + } + } + + enum Block { + static var listTitle: String { + pick(ko: "차단 리스트", en: "Blocked users", ja: "ブロックリスト") + } + + static var emptyBlockedUsers: String { + pick(ko: "차단한 유저가 없습니다.", en: "No blocked users.", ja: "ブロックしたユーザーがいません。") + } + + static var unblockAction: String { + pick(ko: "차단해제", en: "Unblock", ja: "ブロック解除") + } + + static var blockAction: String { + pick(ko: "차단", en: "Block", ja: "ブロック") + } + } + + enum Can { + static var chargeTitle: String { + pick(ko: "충전하기", en: "Charge", ja: "チャージ") + } + + static var chargeAction: String { + pick(ko: "충전하기", en: "Charge", ja: "チャージする") + } + + static var chargeShort: String { + pick(ko: "충전", en: "Charge", ja: "チャージ") + } + + static var chargeCansAction: String { + pick(ko: "캔 충전", en: "Charge cans", ja: "canチャージ") + } + + static var couponRegisterButton: String { + pick(ko: "쿠폰 등록", en: "Register coupon", ja: "クーポン登録") + } + + static var statusTitle: String { + pick(ko: "캔내역", en: "Can history", ja: "can履歴") + } + + static var paidCan: String { + pick(ko: "결제 캔", en: "Paid cans", ja: "決済can") + } + + static var rewardCan: String { + pick(ko: "리워드 캔", en: "Reward cans", ja: "リワードcan") + } + + static var chargeHistory: String { + pick(ko: "충전내역", en: "Charge history", ja: "チャージ履歴") + } + + static var useHistory: String { + pick(ko: "사용내역", en: "Usage history", ja: "使用履歴") + } + + static var localizedUnit: String { + pick(ko: "캔", en: "cans", ja: "can") + } + + static var koreanUnitToken: String { + "캔" + } + + enum Payment { + static var title: String { + pick(ko: "결제하기", en: "Payment", ja: "決済") + } + + static var selectMethod: String { + pick(ko: "결제 수단 선택", en: "Select payment method", ja: "決済手段を選択") + } + + static var unifiedMethod: String { + pick(ko: "통합 결제", en: "Unified payment", ja: "統合決済") + } + + static var phoneMethod: String { + pick(ko: "휴대폰 결제", en: "Mobile payment", ja: "携帯決済") + } + + static var cardMethod: String { + pick(ko: "카드", en: "Card", ja: "カード") + } + + static var bankTransferMethod: String { + pick(ko: "계좌이체", en: "Bank transfer", ja: "口座振替") + } + + static var termsAgreement: String { + pick( + ko: "구매조건 확인 및 결제 진행 동의", + en: "Agree to purchase terms and proceed with payment", + ja: "購入条件を確認し決済に同意" + ) + } + + static var noticeCanExpiry: String { + pick( + ko: "충전된 캔의 유효기간은 충전 후 5년 입니다.", + en: "Charged cans are valid for 5 years after charging.", + ja: "チャージしたcanの有効期限はチャージ後5年間です。" + ) + } + + static var noticeCancellationPolicy: String { + pick( + ko: "결제 취소는 결제 후 7일 이내에만 할 수 있습니다.\n단, 캔의 일부를 사용하면 결제 취소를 할 수 없습니다.", + en: "Payment cancellation is available only within 7 days after payment.\nHowever, cancellation is not available if part of the cans has been used.", + ja: "決済のキャンセルは決済後7日以内のみ可能です。\nただし、canを一部使用した場合はキャンセルできません。" + ) + } + + static var noticeEventPointNoRefund: String { + pick( + ko: "광고성 이벤트 등 회사가 무료로 지급한 포인트는 환불되지 않습니다.", + en: "Points provided for free by the company, such as promotional events, are non-refundable.", + ja: "広告イベントなど会社が無償で付与したポイントは返金されません。" + ) + } + + static var noticeTermsReference: String { + pick( + ko: "자세한 내용은 보이스온 이용약관에서 확인할 수 있습니다.", + en: "For details, please check the VoiceOn Terms of Service.", + ja: "詳細はVoiceOn利用規約をご確認ください。" + ) + } + + static var amountTitle: String { + pick(ko: "결제금액", en: "Payment amount", ja: "決済金額") + } + + static var payAction: String { + pick(ko: "결제하기", en: "Pay", ja: "決済する") + } + + static var methodRequired: String { + pick(ko: "결제수단을 선택해 주세요.", en: "Please select a payment method.", ja: "決済手段を選択してください。") + } + + static var agreementRequired: String { + pick( + ko: "결제진행에 동의하셔야 결제가 가능합니다.", + en: "You must agree to proceed with payment.", + ja: "決済を進めるには同意が必要です。" + ) + } + + static var inProgressError: String { + pick(ko: "결제 중 오류가 발생했습니다.", en: "An error occurred during payment.", ja: "決済中にエラーが発生しました。") + } + + static var closeAction: String { + pick(ko: "닫기", en: "Close", ja: "閉じる") + } + + static var exitConfirmTitle: String { + pick(ko: "결제를 종료할까요?", en: "Cancel this payment?", ja: "決済を終了しますか?") + } + + static var continueAction: String { + pick(ko: "계속", en: "Continue", ja: "続ける") + } + + static var exitAction: String { + pick(ko: "종료", en: "Exit", ja: "終了") + } + + static var exitConfirmMessage: String { + pick( + ko: "진행 중인 결제를 중단하고 이전 화면으로 돌아갑니다.", + en: "Stop the ongoing payment and return to the previous screen.", + ja: "進行中の決済を中断して前の画面に戻ります。" + ) + } + + static var failedWithSupport: String { + pick( + ko: "결제도중 오류가 발생했습니다.\n고객센터로 문의주시기 바랍니다.", + en: "An error occurred during payment.\nPlease contact customer support.", + ja: "決済中にエラーが発生しました。\nカスタマーセンターまでお問い合わせください。" + ) + } + + static var chargeCompleted: String { + pick(ko: "캔이 충전되었습니다", en: "Cans have been charged.", ja: "canがチャージされました。") + } + + static func wonAmount(_ amount: Int) -> String { + pick(ko: "\(amount) 원", en: "₩\(amount)", ja: "₩\(amount)") + } + } + } + + enum Main { + static var login: String { + pick(ko: "LOGIN", en: "LOGIN", ja: "ログイン") + } + + static var viewMyChannel: String { + pick(ko: "내 채널 보기", en: "View my channel", ja: "自分のチャンネルを見る") + } + + static var detail: String { + pick(ko: "자세히", en: "Details", ja: "詳細") + } + + static var editProfile: String { + pick(ko: "프로필 수정", en: "Edit profile", ja: "プロフィール編集") + } + + static var recentlyListenedPrefix: String { + pick(ko: "최근 들은 ", en: "Recently listened ", ja: "最近聞いた ") + } + } + + enum Category { + static var storage: String { + pick(ko: "보관함", en: "Library", ja: "保管庫") + } + + static var blockList: String { + pick(ko: "차단목록", en: "Blocked list", ja: "ブロックリスト") + } + + static var couponRegister: String { + pick(ko: "쿠폰등록", en: "Register coupon", ja: "クーポン登録") + } + + static var notice: String { + pick(ko: "공지사항", en: "Notices", ja: "お知らせ") + } + + static var event: String { + pick(ko: "이벤트", en: "Events", ja: "イベント") + } + + static var customerCenter: String { + pick(ko: "고객센터", en: "Customer center", ja: "カスタマーセンター") + } + } + } + enum User { static var emailTitle: String { pick(ko: "이메일", en: "Email", ja: "メール") diff --git a/SodaLive/Sources/MyPage/Auth/AuthButtonView.swift b/SodaLive/Sources/MyPage/Auth/AuthButtonView.swift index a7bf674..932892f 100644 --- a/SodaLive/Sources/MyPage/Auth/AuthButtonView.swift +++ b/SodaLive/Sources/MyPage/Auth/AuthButtonView.swift @@ -9,7 +9,7 @@ import SwiftUI struct AuthButtonView: View { var body: some View { - Text("본인인증") + Text(I18n.Main.Auth.dialogTitle) .appFont(size: 15.3, weight: .bold) .foregroundColor(Color(hex: "eeeeee")) .padding(.horizontal, 13.3) diff --git a/SodaLive/Sources/MyPage/Block/BlockMemberListView.swift b/SodaLive/Sources/MyPage/Block/BlockMemberListView.swift index 7e227a2..8a66d67 100644 --- a/SodaLive/Sources/MyPage/Block/BlockMemberListView.swift +++ b/SodaLive/Sources/MyPage/Block/BlockMemberListView.swift @@ -14,10 +14,10 @@ struct BlockMemberListView: View { var body: some View { BaseView(isLoading: $viewModel.isLoading) { VStack(spacing: 0) { - DetailNavigationBar(title: String(localized: "차단 리스트")) + DetailNavigationBar(title: I18n.MyPage.Block.listTitle) HStack(spacing: 0) { - Text("총") + Text(I18n.MyPage.Common.totalPrefix) .appFont(size: 13.3, weight: .medium) .foregroundColor(Color.grayee) @@ -25,7 +25,7 @@ struct BlockMemberListView: View { .appFont(size: 13.3, weight: .medium) .foregroundColor(Color.mainRed3) - Text("명") + Text(I18n.MyPage.Common.personUnit) .appFont(size: 13.3, weight: .medium) .foregroundColor(Color.grayee) @@ -56,7 +56,7 @@ struct BlockMemberListView: View { .padding(.top, 26.7) } } else { - Text("차단한 유저가 없습니다.") + Text(I18n.MyPage.Block.emptyBlockedUsers) .appFont(size: 13.3, weight: .medium) .foregroundColor(Color.grayee) .frame(maxHeight: .infinity) diff --git a/SodaLive/Sources/MyPage/Block/BlockedMemberListItemView.swift b/SodaLive/Sources/MyPage/Block/BlockedMemberListItemView.swift index 5182255..dcdc9cb 100644 --- a/SodaLive/Sources/MyPage/Block/BlockedMemberListItemView.swift +++ b/SodaLive/Sources/MyPage/Block/BlockedMemberListItemView.swift @@ -38,7 +38,7 @@ struct BlockedMemberListItemView: View { Spacer() - Text(isBlocked ? "차단해제" : "차단") + Text(isBlocked ? I18n.MyPage.Block.unblockAction : I18n.MyPage.Block.blockAction) .appFont(size: 12, weight: .medium) .foregroundColor(Color.button) .frame(minWidth: 83) diff --git a/SodaLive/Sources/MyPage/Can/Charge/CanChargeView.swift b/SodaLive/Sources/MyPage/Can/Charge/CanChargeView.swift index 2ded721..54199e4 100644 --- a/SodaLive/Sources/MyPage/Can/Charge/CanChargeView.swift +++ b/SodaLive/Sources/MyPage/Can/Charge/CanChargeView.swift @@ -25,7 +25,7 @@ struct CanChargeView: View { var body: some View { BaseView(isLoading: $storeManager.isLoading) { VStack(spacing: 13.3) { - DetailNavigationBar(title: "충전하기") + DetailNavigationBar(title: I18n.MyPage.Can.chargeTitle) if UserDefaults.bool(forKey: .auth) { CanChargeTabView(currentTab: $currentTab) @@ -121,16 +121,7 @@ struct CanPgItemView: View { // MARK: - Localize "캔" unit inside arbitrary text based on current app language fileprivate func localizeCanWord(in text: String) -> String { - let unit: String - switch LanguageHeaderProvider.current { - case "ko": - unit = "캔" - case "ja": - unit = "can" - default: - unit = "cans" - } - return text.replacingOccurrences(of: "캔", with: unit) + text.replacingOccurrences(of: I18n.MyPage.Can.koreanUnitToken, with: I18n.MyPage.Can.localizedUnit) } struct CanChargeTabView: View { diff --git a/SodaLive/Sources/MyPage/Can/Charge/CanChargeViewModel.swift b/SodaLive/Sources/MyPage/Can/Charge/CanChargeViewModel.swift index b5e5228..0bcf6ce 100644 --- a/SodaLive/Sources/MyPage/Can/Charge/CanChargeViewModel.swift +++ b/SodaLive/Sources/MyPage/Can/Charge/CanChargeViewModel.swift @@ -43,13 +43,13 @@ final class CanChargeViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } diff --git a/SodaLive/Sources/MyPage/Can/Coupon/CanChargeCouponButtonView.swift b/SodaLive/Sources/MyPage/Can/Coupon/CanChargeCouponButtonView.swift index 44313f5..151d2b8 100644 --- a/SodaLive/Sources/MyPage/Can/Coupon/CanChargeCouponButtonView.swift +++ b/SodaLive/Sources/MyPage/Can/Coupon/CanChargeCouponButtonView.swift @@ -10,7 +10,7 @@ import SwiftUI struct CanChargeCouponButtonView: View { var body: some View { HStack(spacing: 5.3) { - Text("쿠폰 등록") + Text(I18n.MyPage.Can.couponRegisterButton) .appFont(size: 16, weight: .bold) .foregroundColor(Color(hex: "eeeeee")) diff --git a/SodaLive/Sources/MyPage/Can/Payment/CanPaymentView.swift b/SodaLive/Sources/MyPage/Can/Payment/CanPaymentView.swift index eb98091..2c5da96 100644 --- a/SodaLive/Sources/MyPage/Can/Payment/CanPaymentView.swift +++ b/SodaLive/Sources/MyPage/Can/Payment/CanPaymentView.swift @@ -29,7 +29,7 @@ struct CanPaymentView: View { GeometryReader { proxy in VStack(spacing: 0) { - DetailNavigationBar(title: "결제하기") + DetailNavigationBar(title: I18n.MyPage.Can.Payment.title) ScrollView(.vertical, showsIndicators: false) { VStack(spacing: 0) { @@ -63,7 +63,7 @@ struct CanPaymentView: View { .resizable() .frame(width: 20, height: 20) - Text("구매조건 확인 및 결제 진행 동의") + Text(I18n.MyPage.Can.Payment.termsAgreement) .appFont(size: 14.7, weight: .medium) .foregroundColor(Color(hex: "eeeeee")) } @@ -79,7 +79,7 @@ struct CanPaymentView: View { .appFont(size: 12, weight: .medium) .foregroundColor(Color(hex: "777777")) - Text("결제 취소는 결제 후 7일 이내에만 할 수 있습니다.\n단, 캔의 일부를 사용하면 결제 취소를 할 수 없습니다.") + Text(I18n.MyPage.Can.Payment.noticeCancellationPolicy) .appFont(size: 12, weight: .medium) .foregroundColor(Color(hex: "777777")) .fixedSize(horizontal: false, vertical: true) @@ -91,7 +91,7 @@ struct CanPaymentView: View { .appFont(size: 12, weight: .medium) .foregroundColor(Color(hex: "777777")) - Text("광고성 이벤트 등 회사가 무료로 지급한 포인트는 환불되지 않습니다.") + Text(I18n.MyPage.Can.Payment.noticeEventPointNoRefund) .appFont(size: 12, weight: .medium) .foregroundColor(Color(hex: "777777")) .fixedSize(horizontal: false, vertical: true) @@ -103,7 +103,7 @@ struct CanPaymentView: View { .appFont(size: 12, weight: .medium) .foregroundColor(Color(hex: "777777")) - Text("자세한 내용은 보이스온 이용약관에서 확인할 수 있습니다.") + Text(I18n.MyPage.Can.Payment.noticeTermsReference) .appFont(size: 12, weight: .medium) .foregroundColor(Color(hex: "777777")) .fixedSize(horizontal: false, vertical: true) @@ -118,7 +118,7 @@ struct CanPaymentView: View { HStack(spacing: 0) { VStack(alignment: .leading, spacing: 5) { - Text("결제금액") + Text(I18n.MyPage.Can.Payment.amountTitle) .appFont(size: 13.3, weight: .medium) .foregroundColor(Color(hex: "eeeeee")) @@ -131,7 +131,7 @@ struct CanPaymentView: View { Spacer() - Text("결제하기") + Text(I18n.MyPage.Can.Payment.payAction) .appFont(size: 18.3, weight: .bold) .foregroundColor(.white) .padding(.vertical, 16) @@ -144,7 +144,7 @@ struct CanPaymentView: View { storeManager.payment(product: product, chargeId: chargeId) } } else { - viewModel.errorMessage = "결제진행에 동의하셔야 결제가 가능합니다." + viewModel.errorMessage = I18n.MyPage.Can.Payment.agreementRequired viewModel.isShowPopup = true } } diff --git a/SodaLive/Sources/MyPage/Can/Payment/CanPaymentViewModel.swift b/SodaLive/Sources/MyPage/Can/Payment/CanPaymentViewModel.swift index ef96b6f..ce9d55f 100644 --- a/SodaLive/Sources/MyPage/Can/Payment/CanPaymentViewModel.swift +++ b/SodaLive/Sources/MyPage/Can/Payment/CanPaymentViewModel.swift @@ -49,13 +49,13 @@ final class CanPaymentViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } @@ -86,13 +86,13 @@ final class CanPaymentViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } diff --git a/SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentView.swift b/SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentView.swift index 4248b7b..70c5d00 100644 --- a/SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentView.swift +++ b/SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentView.swift @@ -41,7 +41,7 @@ struct CanPgPaymentView: View { .onError { DEBUG_LOG("onError: \($0)") viewModel.isShowPaymentView = false - viewModel.errorMessage = "결제 중 오류가 발생했습니다." + viewModel.errorMessage = I18n.MyPage.Can.Payment.inProgressError viewModel.isShowPopup = true } .onDone { @@ -72,7 +72,7 @@ struct CanPgPaymentView: View { .ignoresSafeArea(edges: .bottom) HStack(spacing: 8) { Button(action: { showExitConfirm = true }) { - Text("닫기") + Text(I18n.MyPage.Can.Payment.closeAction) .appFont(size: 14, weight: .bold) .foregroundColor(.white) .padding(.horizontal, 12) @@ -86,21 +86,21 @@ struct CanPgPaymentView: View { } } .background(Color.black.ignoresSafeArea()) - .alert("결제를 종료할까요?", isPresented: $showExitConfirm) { - Button("계속", role: .cancel) { } - Button("종료", role: .destructive) { + .alert(I18n.MyPage.Can.Payment.exitConfirmTitle, isPresented: $showExitConfirm) { + Button(I18n.MyPage.Can.Payment.continueAction, role: .cancel) { } + Button(I18n.MyPage.Can.Payment.exitAction, role: .destructive) { DEBUG_LOG("Payverse: user requested to exit") viewModel.isShowPayversePaymentView = false // 필요 시 상위로 복귀 AppState.shared.back() } } message: { - Text("진행 중인 결제를 중단하고 이전 화면으로 돌아갑니다.") + Text(I18n.MyPage.Can.Payment.exitConfirmMessage) } } else { GeometryReader { proxy in VStack(spacing: 0) { - DetailNavigationBar(title: "결제하기") + DetailNavigationBar(title: I18n.MyPage.Can.Payment.title) ScrollView(.vertical, showsIndicators: false) { VStack(spacing: 0) { @@ -129,14 +129,14 @@ struct CanPgPaymentView: View { .frame(width: screenSize().width) .padding(.top, 13.3) - Text("결제 수단 선택") + Text(I18n.MyPage.Can.Payment.selectMethod) .appFont(size: 16.7, weight: .bold) .foregroundColor(Color.grayee) .frame(width: screenSize().width - 26.7, alignment: .leading) .padding(.top, 26.7) HStack(spacing: 16.7) { - Text("통합 결제") + Text(I18n.MyPage.Can.Payment.unifiedMethod) .appFont(size: 16.7, weight: viewModel.paymentMethod == .unified ? .bold : .medium) .foregroundColor(viewModel.paymentMethod == .unified ? Color.button : Color.grayee) .frame(maxWidth: .infinity) @@ -179,7 +179,7 @@ struct CanPgPaymentView: View { } } - Text("휴대폰 결제") + Text(I18n.MyPage.Can.Payment.phoneMethod) .appFont(size: 16.7, weight: viewModel.paymentMethod == .phone ? .bold : .medium) .foregroundColor(viewModel.paymentMethod == .phone ? Color.button : Color.grayee) .frame(maxWidth: .infinity) @@ -209,7 +209,7 @@ struct CanPgPaymentView: View { .resizable() .frame(width: 20, height: 20) - Text("구매조건 확인 및 결제 진행 동의") + Text(I18n.MyPage.Can.Payment.termsAgreement) .appFont(size: 14.7, weight: .medium) .foregroundColor(Color.grayee) } @@ -225,7 +225,7 @@ struct CanPgPaymentView: View { .appFont(size: 12, weight: .medium) .foregroundColor(Color.gray77) - Text("충전된 캔의 유효기간은 충전 후 5년 입니다.") + Text(I18n.MyPage.Can.Payment.noticeCanExpiry) .appFont(size: 12, weight: .medium) .foregroundColor(Color.gray77) .fixedSize(horizontal: false, vertical: true) @@ -238,7 +238,7 @@ struct CanPgPaymentView: View { .appFont(size: 12, weight: .medium) .foregroundColor(Color.gray77) - Text("결제 취소는 결제 후 7일 이내에만 할 수 있습니다.\n단, 캔의 일부를 사용하면 결제 취소를 할 수 없습니다.") + Text(I18n.MyPage.Can.Payment.noticeCancellationPolicy) .appFont(size: 12, weight: .medium) .foregroundColor(Color.gray77) .fixedSize(horizontal: false, vertical: true) @@ -250,7 +250,7 @@ struct CanPgPaymentView: View { .appFont(size: 12, weight: .medium) .foregroundColor(Color.gray77) - Text("광고성 이벤트 등 회사가 무료로 지급한 포인트는 환불되지 않습니다.") + Text(I18n.MyPage.Can.Payment.noticeEventPointNoRefund) .appFont(size: 12, weight: .medium) .foregroundColor(Color.gray77) .fixedSize(horizontal: false, vertical: true) @@ -262,7 +262,7 @@ struct CanPgPaymentView: View { .appFont(size: 12, weight: .medium) .foregroundColor(Color.gray77) - Text("자세한 내용은 보이스온 이용약관에서 확인할 수 있습니다.") + Text(I18n.MyPage.Can.Payment.noticeTermsReference) .appFont(size: 12, weight: .medium) .foregroundColor(Color.gray77) .fixedSize(horizontal: false, vertical: true) @@ -276,7 +276,7 @@ struct CanPgPaymentView: View { HStack(spacing: 0) { VStack(alignment: .leading, spacing: 5) { - Text("결제금액") + Text(I18n.MyPage.Can.Payment.amountTitle) .appFont(size: 13.3, weight: .medium) .foregroundColor(Color.grayee) @@ -289,7 +289,7 @@ struct CanPgPaymentView: View { Spacer() - Text("결제하기") + Text(I18n.MyPage.Can.Payment.payAction) .appFont(size: 18.3, weight: .bold) .foregroundColor(.white) .padding(.vertical, 16) @@ -298,10 +298,10 @@ struct CanPgPaymentView: View { .cornerRadius(10) .onTapGesture { if viewModel.paymentMethod == nil { - viewModel.errorMessage = "결제수단을 선택해 주세요." + viewModel.errorMessage = I18n.MyPage.Can.Payment.methodRequired viewModel.isShowPopup = true } else if !viewModel.isTermsAgree { - viewModel.errorMessage = "결제진행에 동의하셔야 결제가 가능합니다." + viewModel.errorMessage = I18n.MyPage.Can.Payment.agreementRequired viewModel.isShowPopup = true } else { if viewModel.paymentMethod == .unified { @@ -317,7 +317,7 @@ struct CanPgPaymentView: View { viewModel.isShowPaymentView = true } else { - viewModel.errorMessage = "결제도중 오류가 발생했습니다.\n고객센터로 문의주시기 바랍니다." + viewModel.errorMessage = I18n.MyPage.Can.Payment.failedWithSupport viewModel.isShowPopup = true } } diff --git a/SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentViewModel.swift b/SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentViewModel.swift index 152f3f8..d6fddc8 100644 --- a/SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentViewModel.swift +++ b/SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentViewModel.swift @@ -78,13 +78,13 @@ final class CanPgPaymentViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } @@ -117,7 +117,7 @@ final class CanPgPaymentViewModel: ObservableObject { let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData) if decoded.success { - self.errorMessage = "캔이 충전되었습니다" + self.errorMessage = I18n.MyPage.Can.Payment.chargeCompleted self.isShowPopup = true onSuccess() @@ -125,20 +125,20 @@ final class CanPgPaymentViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } .store(in: &subscription) } else { isLoading = false - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError isShowPopup = true } } @@ -171,7 +171,7 @@ final class CanPgPaymentViewModel: ObservableObject { self.payversePayloadJson = String(data: merged, encoding: .utf8)! self.isShowPayversePaymentView = true } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true return } @@ -179,13 +179,13 @@ final class CanPgPaymentViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } @@ -194,7 +194,7 @@ final class CanPgPaymentViewModel: ObservableObject { func handleVerifyOpenURL(_ url: URL) { guard let comps = URLComponents(url: url, resolvingAgainstBaseURL: false) else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true return } @@ -208,7 +208,7 @@ final class CanPgPaymentViewModel: ObservableObject { resultStatus == "DECLINE" || orderId == nil || orderId?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true || tid == nil || tid?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true DispatchQueue.main.asyncAfter(deadline: .now() + 1) { AppState.shared.back() @@ -265,13 +265,13 @@ final class CanPgPaymentViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } diff --git a/SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempView.swift b/SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempView.swift index d3cc1ba..f3c18bf 100644 --- a/SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempView.swift +++ b/SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempView.swift @@ -41,7 +41,7 @@ struct CanPaymentTempView: View { .onError { DEBUG_LOG("onError: \($0)") viewModel.isShowPaymentView = false - viewModel.errorMessage = "결제 중 오류가 발생했습니다." + viewModel.errorMessage = I18n.MyPage.Can.Payment.inProgressError viewModel.isShowPopup = true } .onDone { @@ -65,7 +65,7 @@ struct CanPaymentTempView: View { } else { GeometryReader { proxy in VStack(spacing: 0) { - DetailNavigationBar(title: "결제하기") + DetailNavigationBar(title: I18n.MyPage.Can.Payment.title) ScrollView(.vertical, showsIndicators: false) { VStack(spacing: 0) { @@ -77,7 +77,7 @@ struct CanPaymentTempView: View { Spacer() - Text("\(self.can * 110) 원") + Text(I18n.MyPage.Can.Payment.wonAmount(self.can * 110)) .appFont(size: 15.3, weight: .bold) .foregroundColor(Color.grayee) } @@ -89,7 +89,7 @@ struct CanPaymentTempView: View { .frame(width: screenSize().width) .padding(.top, 13.3) - Text("결제 수단 선택") + Text(I18n.MyPage.Can.Payment.selectMethod) .appFont(size: 16.7, weight: .bold) .foregroundColor(Color.grayee) .frame(width: screenSize().width - 26.7, alignment: .leading) @@ -97,7 +97,7 @@ struct CanPaymentTempView: View { HStack(spacing: 13.3) { - Text("카드") + Text(I18n.MyPage.Can.Payment.cardMethod) .appFont(size: 16.7, weight: viewModel.paymentMethod == .card ? .bold : .medium) .foregroundColor(viewModel.paymentMethod == .card ? Color.button : Color.grayee) .frame(width: (screenSize().width - 40) / 2) @@ -120,7 +120,7 @@ struct CanPaymentTempView: View { } - Text("계좌이체") + Text(I18n.MyPage.Can.Payment.bankTransferMethod) .appFont(size: 16.7, weight: viewModel.paymentMethod == .bank ? .bold : .medium) .foregroundColor(viewModel.paymentMethod == .bank ? Color.button : Color.grayee) .frame(width: (screenSize().width - 40) / 2) @@ -146,7 +146,7 @@ struct CanPaymentTempView: View { .padding(.top, 16.7) HStack(spacing: 13.3) { - Text("휴대폰 결제") + Text(I18n.MyPage.Can.Payment.phoneMethod) .appFont(size: 16.7, weight: viewModel.paymentMethod == .phone ? .bold : .medium) .foregroundColor(viewModel.paymentMethod == .phone ? Color.button : Color.grayee) .frame(width: (screenSize().width - 40) / 2) @@ -178,7 +178,7 @@ struct CanPaymentTempView: View { .resizable() .frame(width: 20, height: 20) - Text("구매조건 확인 및 결제 진행 동의") + Text(I18n.MyPage.Can.Payment.termsAgreement) .appFont(size: 14.7, weight: .medium) .foregroundColor(Color.grayee) } @@ -194,12 +194,12 @@ struct CanPaymentTempView: View { HStack(spacing: 0) { VStack(alignment: .leading, spacing: 5) { - Text("결제금액") + Text(I18n.MyPage.Can.Payment.amountTitle) .appFont(size: 13.3, weight: .medium) .foregroundColor(Color.grayee) HStack(spacing: 0) { - Text("\(self.can * 110) 원") + Text(I18n.MyPage.Can.Payment.wonAmount(self.can * 110)) .appFont(size: 23.3, weight: .bold) .foregroundColor(Color.grayee) } @@ -207,7 +207,7 @@ struct CanPaymentTempView: View { Spacer() - Text("결제하기") + Text(I18n.MyPage.Can.Payment.payAction) .appFont(size: 18.3, weight: .bold) .foregroundColor(.white) .padding(.vertical, 16) @@ -216,10 +216,10 @@ struct CanPaymentTempView: View { .cornerRadius(10) .onTapGesture { if viewModel.paymentMethod == nil { - viewModel.errorMessage = "결제수단을 선택해 주세요." + viewModel.errorMessage = I18n.MyPage.Can.Payment.methodRequired viewModel.isShowPopup = true } else if !viewModel.isTermsAgree { - viewModel.errorMessage = "결제진행에 동의하셔야 결제가 가능합니다." + viewModel.errorMessage = I18n.MyPage.Can.Payment.agreementRequired viewModel.isShowPopup = true } else { viewModel.chargeCan(can: can, paymentGateway: .PG){ diff --git a/SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempViewModel.swift b/SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempViewModel.swift index ada9836..ce89543 100644 --- a/SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempViewModel.swift +++ b/SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempViewModel.swift @@ -65,13 +65,13 @@ final class CanPaymentTempViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } @@ -109,20 +109,20 @@ final class CanPaymentTempViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } .store(in: &subscription) } else { isLoading = false - errorMessage = "본인인증 중 오류가 발생했습니다." + errorMessage = I18n.Main.Auth.authenticationError isShowPopup = true } } diff --git a/SodaLive/Sources/MyPage/Can/Status/CanStatusView.swift b/SodaLive/Sources/MyPage/Can/Status/CanStatusView.swift index 99f73cd..e7f5d61 100644 --- a/SodaLive/Sources/MyPage/Can/Status/CanStatusView.swift +++ b/SodaLive/Sources/MyPage/Can/Status/CanStatusView.swift @@ -17,7 +17,7 @@ struct CanStatusView: View { BaseView(isLoading: $viewModel.isLoading) { GeometryReader { proxy in VStack(spacing: 0) { - DetailNavigationBar(title: String(localized: "캔내역")) { + DetailNavigationBar(title: I18n.MyPage.Can.statusTitle) { AppState.shared.setAppStep(step: .main) } @@ -35,7 +35,7 @@ struct CanStatusView: View { HStack(spacing: 26.7) { VStack(spacing: 10) { - Text("결제 캔") + Text(I18n.MyPage.Can.paidCan) .appFont(size: 12, weight: .light) .foregroundColor(Color(hex: "777777")) @@ -44,7 +44,7 @@ struct CanStatusView: View { .appFont(size: 16.7, weight: .bold) .foregroundColor(Color(hex: "eeeeee")) - Text(" 캔") + Text(" \(I18n.MyPage.Can.localizedUnit)") .appFont(size: 10.7, weight: .medium) .foregroundColor(Color(hex: "bbbbbb")) } @@ -56,7 +56,7 @@ struct CanStatusView: View { .foregroundColor(Color(hex: "909090").opacity(0.5)) VStack(spacing: 10) { - Text("리워드 캔") + Text(I18n.MyPage.Can.rewardCan) .appFont(size: 12, weight: .light) .foregroundColor(Color(hex: "777777")) @@ -65,7 +65,7 @@ struct CanStatusView: View { .appFont(size: 16.7, weight: .bold) .foregroundColor(Color(hex: "eeeeee")) - Text(" 캔") + Text(" \(I18n.MyPage.Can.localizedUnit)") .appFont(size: 10.7, weight: .medium) .foregroundColor(Color(hex: "bbbbbb")) } @@ -82,7 +82,7 @@ struct CanStatusView: View { HStack(spacing: 0) { VStack(spacing: 0) { Spacer() - Text("충전내역") + Text(I18n.MyPage.Can.chargeHistory) .appFont(size: 13.3, weight: .medium) .foregroundColor( Color(hex: viewModel.currentTab == .charge ? "eeeeee" : "777777") @@ -104,7 +104,7 @@ struct CanStatusView: View { VStack(spacing: 0) { Spacer() - Text("사용내역") + Text(I18n.MyPage.Can.useHistory) .appFont(size: 13.3, weight: .medium) .foregroundColor( Color(hex: viewModel.currentTab == .use ? "eeeeee" : "777777") @@ -142,7 +142,7 @@ struct CanStatusView: View { .resizable() .frame(width: 26.7, height: 26.7) - Text("충전하기") + Text(I18n.MyPage.Can.chargeAction) .appFont(size: 18.3, weight: .bold) .foregroundColor(Color(hex: "1313bc")) } diff --git a/SodaLive/Sources/MyPage/Can/Status/CanStatusViewModel.swift b/SodaLive/Sources/MyPage/Can/Status/CanStatusViewModel.swift index 6bd6462..afa765a 100644 --- a/SodaLive/Sources/MyPage/Can/Status/CanStatusViewModel.swift +++ b/SodaLive/Sources/MyPage/Can/Status/CanStatusViewModel.swift @@ -52,13 +52,13 @@ final class CanStatusViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } @@ -90,13 +90,13 @@ final class CanStatusViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } @@ -128,13 +128,13 @@ final class CanStatusViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } diff --git a/SodaLive/Sources/MyPage/CanCardView.swift b/SodaLive/Sources/MyPage/CanCardView.swift index 5f7f796..71abc24 100644 --- a/SodaLive/Sources/MyPage/CanCardView.swift +++ b/SodaLive/Sources/MyPage/CanCardView.swift @@ -37,7 +37,7 @@ struct CanCardView: View { .resizable() .frame(width: 26.7, height: 26.7) - Text("충전") + Text(I18n.MyPage.Can.chargeShort) .appFont(size: 12, weight: .bold) .foregroundColor(Color(hex: "b38fff")) } diff --git a/SodaLive/Sources/MyPage/MyPageView.swift b/SodaLive/Sources/MyPage/MyPageView.swift index 3ed20c8..dd2fd36 100644 --- a/SodaLive/Sources/MyPage/MyPageView.swift +++ b/SodaLive/Sources/MyPage/MyPageView.swift @@ -40,7 +40,7 @@ struct MyPageView: View { } .onError { DEBUG_LOG("onError: \($0)") - viewModel.errorMessage = "본인인증 중 오류가 발생했습니다." + viewModel.errorMessage = I18n.Main.Auth.authenticationError viewModel.isShowPopup = true viewModel.isShowAuthView = false } @@ -77,7 +77,7 @@ struct MyPageView: View { .padding(.horizontal, 24) } else { HStack { - Text("LOGIN") + Text(I18n.MyPage.Main.login) .appFont(size: 32, weight: .bold) .foregroundColor(Color.gray77) } @@ -93,7 +93,7 @@ struct MyPageView: View { } if UserDefaults.string(forKey: .role) == MemberRole.CREATOR.rawValue { - Text("내 채널 보기") + Text(I18n.MyPage.Main.viewMyChannel) .appFont(size: 16, weight: .bold) .foregroundColor(Color.white) .padding(.vertical, 12) @@ -229,7 +229,7 @@ struct UpdateBannerView: View { Spacer() HStack(spacing: 2) { - Text("자세히") + Text(I18n.MyPage.Main.detail) .appFont(size: 16) .foregroundColor(Color(hex: "B0BEC5")) @@ -273,7 +273,7 @@ struct ProfileSectionView: View { Spacer() - Button("프로필 수정") { + Button(I18n.MyPage.Main.editProfile) { if AppState.shared.roomId <= 0 { AppState.shared.setAppStep(step: .profileUpdate(refresh: refresh)) } @@ -325,7 +325,7 @@ struct CanPointCardsView: View { Spacer() if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - Button("캔 충전") { + Button(I18n.MyPage.Can.chargeCansAction) { AppState.shared.setAppStep(step: .canCharge(refresh: refresh)) } .padding(.horizontal, 16) @@ -394,44 +394,44 @@ struct CategoryButtonsView: View { var body: some View { LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 4), spacing: 16) { - CategoryButtonItem(icon: "ic_my_storage", title: "보관함") { + CategoryButtonItem(icon: "ic_my_storage", title: I18n.MyPage.Category.storage) { AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist)) } - CategoryButtonItem(icon: "ic_my_block", title: "차단목록") { + CategoryButtonItem(icon: "ic_my_block", title: I18n.MyPage.Category.blockList) { AppState.shared.setAppStep(step: .blockList) } CategoryButtonItem( icon: "ic_my_coupon", - title: "쿠폰등록" + title: I18n.MyPage.Category.couponRegister ) { if isAuthenticated { AppState.shared.setAppStep(step: .canCoupon(refresh: refresh)) } else { - showMessage("본인인증 후 등록합니다.") + showMessage(I18n.MyPage.Auth.verifyRequiredBeforeCoupon) DispatchQueue.main.asyncAfter(deadline: .now() + 1) { isShowAuthView = true } } } - CategoryButtonItem(icon: "ic_my_notice", title: "공지사항") { + CategoryButtonItem(icon: "ic_my_notice", title: I18n.MyPage.Category.notice) { AppState.shared.setAppStep(step: .notices) } - CategoryButtonItem(icon: "ic_my_event", title: "이벤트") { + CategoryButtonItem(icon: "ic_my_event", title: I18n.MyPage.Category.event) { AppState.shared.setAppStep(step: .events) } - CategoryButtonItem(icon: "ic_my_service_center", title: "고객센터") { + CategoryButtonItem(icon: "ic_my_service_center", title: I18n.MyPage.Category.customerCenter) { AppState.shared.setAppStep(step: .serviceCenter) } if isKoreanCountry { CategoryButtonItem( icon: "ic_my_auth", - title: isAuthenticated ? "인증완료" : "본인인증" + title: isAuthenticated ? I18n.MyPage.Auth.verified : I18n.Main.Auth.dialogTitle ) { if !isAuthenticated { isShowAuthView = true @@ -444,7 +444,7 @@ struct CategoryButtonsView: View { struct CategoryButtonItem: View { let icon: String - let title: LocalizedStringResource + let title: String let onClick: () -> Void var body: some View { @@ -475,7 +475,7 @@ struct RecentContentSection: View { var body: some View { VStack(alignment: .leading, spacing: 14) { HStack(spacing: 0) { - Text("최근 들은 ") + Text(I18n.MyPage.Main.recentlyListenedPrefix) .appFont(size: 16, weight: .bold) .foregroundColor(Color(hex: "B0BEC5")) diff --git a/SodaLive/Sources/MyPage/MyPageViewModel.swift b/SodaLive/Sources/MyPage/MyPageViewModel.swift index 5292fa3..ea58126 100644 --- a/SodaLive/Sources/MyPage/MyPageViewModel.swift +++ b/SodaLive/Sources/MyPage/MyPageViewModel.swift @@ -59,13 +59,13 @@ final class MyPageViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError } self.isShowPopup = true } } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.Common.commonError self.isShowPopup = true } } @@ -113,19 +113,19 @@ final class MyPageViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "본인인증 중 오류가 발생했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.MyPage.Auth.verificationErrorWithSupport } self.isShowPopup = true } } catch { - self.errorMessage = "본인인증 중 오류가 발생했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.errorMessage = I18n.MyPage.Auth.verificationErrorWithSupport self.isShowPopup = true } } .store(in: &subscription) } else { isLoading = false - errorMessage = "본인인증 중 오류가 발생했습니다." + errorMessage = I18n.Main.Auth.authenticationError isShowPopup = true } } @@ -151,6 +151,7 @@ final class MyPageViewModel: ObservableObject { self.latestNotice = data } } catch { + ERROR_LOG(error.localizedDescription) } } .store(in: &subscription) diff --git a/SodaLive/Sources/MyPage/OrderList/OrderListAllInnerView.swift b/SodaLive/Sources/MyPage/OrderList/OrderListAllInnerView.swift index cbd4752..261b901 100644 --- a/SodaLive/Sources/MyPage/OrderList/OrderListAllInnerView.swift +++ b/SodaLive/Sources/MyPage/OrderList/OrderListAllInnerView.swift @@ -15,7 +15,7 @@ struct OrderListAllInnerView: View { BaseView(isLoading: $viewModel.isLoading) { VStack(spacing: 0) { HStack(spacing: 0) { - Text("총") + Text(I18n.MyPage.Common.totalPrefix) .appFont(size: 13.3, weight: .medium) .foregroundColor(Color(hex: "eeeeee")) @@ -23,7 +23,7 @@ struct OrderListAllInnerView: View { .appFont(size: 13.3, weight: .medium) .foregroundColor(Color(hex: "dd4500")) - Text("개") + Text(I18n.MyPage.Common.countUnit) .appFont(size: 13.3, weight: .medium) .foregroundColor(Color(hex: "eeeeee")) diff --git a/docs/20260331_하드코딩텍스트_I18n통일계획.md b/docs/20260331_하드코딩텍스트_I18n통일계획.md index 0513965..9da1291 100644 --- a/docs/20260331_하드코딩텍스트_I18n통일계획.md +++ b/docs/20260331_하드코딩텍스트_I18n통일계획.md @@ -411,28 +411,28 @@ ### MyPage (41) #### Group 1 (1-10) -- [ ] `SodaLive/Sources/MyPage/Auth/AuthButtonView.swift` -- [ ] `SodaLive/Sources/MyPage/Block/BlockMemberListView.swift` -- [ ] `SodaLive/Sources/MyPage/Block/BlockedMemberListItemView.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Charge/CanChargeView.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Charge/CanChargeViewModel.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Coupon/CanChargeCouponButtonView.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Coupon/CanCouponNoticeItemView.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Payment/CanPaymentView.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Payment/CanPaymentViewModel.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentView.swift` +- [x] `SodaLive/Sources/MyPage/Auth/AuthButtonView.swift` +- [x] `SodaLive/Sources/MyPage/Block/BlockMemberListView.swift` +- [x] `SodaLive/Sources/MyPage/Block/BlockedMemberListItemView.swift` +- [x] `SodaLive/Sources/MyPage/Can/Charge/CanChargeView.swift` +- [x] `SodaLive/Sources/MyPage/Can/Charge/CanChargeViewModel.swift` +- [x] `SodaLive/Sources/MyPage/Can/Coupon/CanChargeCouponButtonView.swift` +- [x] `SodaLive/Sources/MyPage/Can/Coupon/CanCouponNoticeItemView.swift` +- [x] `SodaLive/Sources/MyPage/Can/Payment/CanPaymentView.swift` +- [x] `SodaLive/Sources/MyPage/Can/Payment/CanPaymentViewModel.swift` +- [x] `SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentView.swift` #### Group 2 (11-20) -- [ ] `SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentViewModel.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempView.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempViewModel.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Status/CanStatusView.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Status/CanStatusViewModel.swift` -- [ ] `SodaLive/Sources/MyPage/Can/Status/CanUseStatusView.swift` -- [ ] `SodaLive/Sources/MyPage/CanCardView.swift` -- [ ] `SodaLive/Sources/MyPage/MyPageView.swift` -- [ ] `SodaLive/Sources/MyPage/MyPageViewModel.swift` -- [ ] `SodaLive/Sources/MyPage/OrderList/OrderListAllInnerView.swift` +- [x] `SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentViewModel.swift` +- [x] `SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempView.swift` +- [x] `SodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempViewModel.swift` +- [x] `SodaLive/Sources/MyPage/Can/Status/CanStatusView.swift` +- [x] `SodaLive/Sources/MyPage/Can/Status/CanStatusViewModel.swift` +- [x] `SodaLive/Sources/MyPage/Can/Status/CanUseStatusView.swift` +- [x] `SodaLive/Sources/MyPage/CanCardView.swift` +- [x] `SodaLive/Sources/MyPage/MyPageView.swift` +- [x] `SodaLive/Sources/MyPage/MyPageViewModel.swift` +- [x] `SodaLive/Sources/MyPage/OrderList/OrderListAllInnerView.swift` #### Group 3 (21-30) - [ ] `SodaLive/Sources/MyPage/OrderList/OrderListAllView.swift` @@ -802,3 +802,31 @@ - 빌드 검증: `SodaLive`, `SodaLive-dev` Debug 빌드 모두 성공(`** BUILD SUCCEEDED **`). - 테스트 검증: 두 스킴 모두 `Scheme ... is not currently configured for the test action.`로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약). - LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(`Moya`, `I18n`, `LoadingView` 등)가 보고되나, 동일 변경셋은 `xcodebuild` 실컴파일 통과로 검증 완료. + +### 15차 구현 (MyPage 모듈 Group 1~2, 20개 파일 처리, 2026-03-31) +- 무엇/왜/어떻게: + - 무엇: `변경 대상 파일 전체 목록`의 `MyPage` Group 1~2(20개 파일)에서 사용자 노출 하드코딩 문구를 `I18n.*` 참조로 전환하고 체크박스를 완료 처리. + - 왜: MyPage 영역에 `String(localized:)` 직접 참조, 뷰 리터럴, ViewModel 반복 에러 문구가 혼재되어 모듈 단위 i18n 일관성이 깨져 있었기 때문. + - 어떻게: explore/librarian 병렬 탐색 + `grep`/`ast_grep_search`로 런타임 노출 문자열을 분류한 뒤, `I18n.swift`에 `I18n.MyPage` 네임스페이스를 추가하고 Group 1~2 호출부를 일괄 치환. +- 실행 명령/도구: + - `task(subagent_type="explore", ...)` x2 (`bg_2804258c`, `bg_56679c82`) + - `task(subagent_type="librarian", ...)` x2 (`bg_82e0b3b7`, `bg_a708658e`) + - `grep("\b(String\(localized:|NSLocalizedString\(|LocalizedStringKey\()", include=*.swift, path=SodaLive/Sources/MyPage)` + - `grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/MyPage)` + 대상 파일별 개별 재검증 + - `ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/MyPage])` + - `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.MyPage` 키셋 추가(`Common`, `Auth`, `Block`, `Can`, `Can.Payment`, `Main`, `Category`). + - 치환 완료 파일: `AuthButtonView`, `BlockMemberListView`, `BlockedMemberListItemView`, `CanChargeView`, `CanChargeViewModel`, `CanChargeCouponButtonView`, `CanPaymentView`, `CanPaymentViewModel`, `CanPgPaymentView`, `CanPgPaymentViewModel`, `CanPaymentTempView`, `CanPaymentTempViewModel`, `CanStatusView`, `CanStatusViewModel`, `CanCardView`, `MyPageView`, `MyPageViewModel`, `OrderListAllInnerView`. + - 점검만 수행(실치환 없음): `CanCouponNoticeItemView.swift`, `CanUseStatusView.swift` (런타임 노출 하드코딩 없음, Preview/불릿/데이터 바인딩만 존재). + - `MyPageView`의 `CategoryButtonItem`은 `LocalizedStringResource` → `String`으로 조정해 `I18n.*` 문자열 접근을 통일. + - 반복 실패 문구는 `I18n.Common.commonError`로 통합했고, 본인인증 장문 오류는 `I18n.MyPage.Auth.verificationErrorWithSupport`로 분리. + - Group 1~2 체크박스 20개 `- [x]` 반영 완료. + - 대상 파일 재탐지 결과, 남은 한글 리터럴은 Preview 샘플/SDK 전달 상수(`payload.pg`, `payload.method`, `payload.orderName`, PG method rawValue)만 존재. + - LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(`Bootpay`, `Kingfisher`, `I18n` 등)가 보고되나, 동일 변경셋은 `xcodebuild` 실컴파일 통과로 검증 완료. + - 빌드 검증: `SodaLive`, `SodaLive-dev` Debug 빌드 모두 성공(`** BUILD SUCCEEDED **`). + - 테스트 검증: 두 스킴 모두 `Scheme ... is not currently configured for the test action.`로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약).