From a406935e4f6a382780a6147e96073114d8318736 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Fri, 19 Dec 2025 19:20:28 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BF=A0=ED=8F=B0=20=EB=B0=8F=20=EA=B3=B5?= =?UTF-8?q?=EC=A7=80=20=ED=99=94=EB=A9=B4=20=EB=8B=A4=EA=B5=AD=EC=96=B4=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SodaLive/Resources/Localizable.xcstrings | 142 ++++++++++++------ .../Profile/UserProfileDonationAllView.swift | 2 +- SodaLive/Sources/I18n/I18n.swift | 110 ++++++++++++++ .../MyPage/Can/Coupon/CanCouponView.swift | 28 ++-- .../Can/Coupon/CanCouponViewModel.swift | 6 +- .../Settings/Notice/NoticeDetailView.swift | 2 +- 6 files changed, 222 insertions(+), 68 deletions(-) diff --git a/SodaLive/Resources/Localizable.xcstrings b/SodaLive/Resources/Localizable.xcstrings index 069ab87..6bf0044 100644 --- a/SodaLive/Resources/Localizable.xcstrings +++ b/SodaLive/Resources/Localizable.xcstrings @@ -756,6 +756,18 @@ }, "%@ %@" : { "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@" + } + }, "ko" : { "stringUnit" : { "state" : "new", @@ -2572,6 +2584,22 @@ } } }, + "공지사항 상세" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notice details" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "お知らせ詳細" + } + } + } + }, "관심사" : { "localizations" : { "en" : { @@ -6780,54 +6808,6 @@ } } }, - "이벤트" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Events" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "イベント" - } - } - } - }, - "인기 캐릭터" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Popular Characters" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "人気キャラクター" - } - } - } - }, - "일" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sun" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "日" - } - } - } - }, "이메일" : { "localizations" : { "en" : { @@ -6892,6 +6872,22 @@ } } }, + "이벤트" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Events" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "イベント" + } + } + } + }, "이벤트 참여하기" : { "localizations" : { "en" : { @@ -6972,6 +6968,22 @@ } } }, + "인기 캐릭터" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Popular Characters" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "人気キャラクター" + } + } + } + }, "인기 캐릭터 채팅" : { "localizations" : { "en" : { @@ -7052,6 +7064,22 @@ } } }, + "일" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sun" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "日" + } + } + } + }, "일간 랭킹" : { "localizations" : { "en" : { @@ -9772,6 +9800,22 @@ } } }, + "후원랭킹 전체보기" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "View all support rankings" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "後援ランキングをすべて見る" + } + } + } + }, "후원하기" : { "localizations" : { "en" : { @@ -9806,4 +9850,4 @@ } }, "version" : "1.1" -} \ No newline at end of file +} diff --git a/SodaLive/Sources/Explorer/Profile/UserProfileDonationAllView.swift b/SodaLive/Sources/Explorer/Profile/UserProfileDonationAllView.swift index 090535f..ae9cf0c 100644 --- a/SodaLive/Sources/Explorer/Profile/UserProfileDonationAllView.swift +++ b/SodaLive/Sources/Explorer/Profile/UserProfileDonationAllView.swift @@ -16,7 +16,7 @@ struct UserProfileDonationAllView: View { var body: some View { BaseView(isLoading: $viewModel.isLoading) { VStack(spacing: 0) { - DetailNavigationBar(title: "후원랭킹 전체보기") + DetailNavigationBar(title: String(localized: "후원랭킹 전체보기")) if userId == UserDefaults.int(forKey: .userId) { VStack(spacing: 10.7) { diff --git a/SodaLive/Sources/I18n/I18n.swift b/SodaLive/Sources/I18n/I18n.swift index 2707a46..4f715c7 100644 --- a/SodaLive/Sources/I18n/I18n.swift +++ b/SodaLive/Sources/I18n/I18n.swift @@ -111,6 +111,116 @@ enum I18n { static var points: String { pick(ko: "포인트", en: "Points", ja: "ポイント") } } + // 쿠폰 등록/사용 화면 관련 텍스트 + enum CanCoupon { + // 화면 타이틀 + static var title: String { + pick(ko: "쿠폰등록", en: "Register coupon", ja: "クーポン登録") + } + + // 입력 섹션 + static var inputTitle: String { + pick(ko: "쿠폰번호 입력", en: "Enter coupon code", ja: "クーポン番号入力") + } + + static var inputPlaceholder: String { + pick( + ko: "쿠폰번호를 입력하세요", + en: "Enter coupon code", + ja: "クーポン番号を入力してください" + ) + } + + // 액션 버튼 + static var submit: String { + pick(ko: "등록하기", en: "Register", ja: "登録する") + } + + // 섹션 헤더 + static var guideHeader: String { + pick(ko: "[등록 및 사용안내]", en: "[Registration and usage]", ja: "[登録および利用案内]") + } + + static var cautionHeader: String { + pick(ko: "[주의사항]", en: "[Notes]", ja: "[注意事項]") + } + + // 안내 항목 + static var guide1: String { + pick( + ko: "공백없이 쿠폰번호 12자리를 입력해주세요.", + en: "Enter 12 characters without spaces.", + ja: "スペースなしで12桁のクーポン番号を入力してください。" + ) + } + + static var guide2: String { + pick( + ko: "충전된 캔 또는 포인트는 해당 충전현황에서 확인할 수 있습니다", + en: "You can check charged cans or points in the charge status.", + ja: "チャージされた缶またはポイントはチャージ状況で確認できます。" + ) + } + + static var guide3: String { + pick( + ko: "쿠폰으로 충전된 캔은 사용 전까지 소멸되지 않으며, 포인트는 충전 후 72시간 이내에 사용하지 않으면 자동 소멸됩니다.", + en: "Cans charged by coupon do not expire before use. Points expire automatically if not used within 72 hours after charging.", + ja: "クーポンでチャージした缶は使用前に失効しません。ポイントはチャージ後72時間以内に使用しないと自動的に失効します。" + ) + } + + // 주의사항 항목 + static var caution1: String { + pick( + ko: "이벤트 쿠폰을 통해 충전한 캔이나 포인트는 환불되지 않습니다.", + en: "Cans or points charged via event coupons are non-refundable.", + ja: "イベントクーポンでチャージした缶やポイントは返金できません。" + ) + } + + static var caution2: String { + pick( + ko: "쿠폰은 상업적 용도로 사용하거나 매매할 수 없습니다.", + en: "Coupons may not be used for commercial purposes or traded.", + ja: "クーポンを商用目的で使用したり売買したりすることはできません。" + ) + } + + static var caution3: String { + pick( + ko: "한번 등록한 쿠폰은 재사용이 불가합니다.", + en: "A coupon can’t be used again once registered.", + ja: "一度登録したクーポンは再使用できません。" + ) + } + + static var caution4: String { + pick( + ko: "연령 제한 정책에 따라 쿠폰이용은 본인인증한 회원만 이용 가능합니다.", + en: "Due to age policy, only verified members can use coupons.", + ja: "年齢制限ポリシーにより、本人確認済みの会員のみクーポンを利用できます。" + ) + } + + static var contactHelp: String { + pick( + ko: "※ 쿠폰 등록 및 이용에 문제가 발생한 경우 '고객센터'로 문의주시기 바랍니다.", + en: "※ If you have issues with coupon registration or use, please contact Customer Service.", + ja: "※ クーポンの登録・利用に問題が発生した場合は『カスタマーセンター』までお問い合わせください。" + ) + } + + // 성공/토스트 + static var useCompleted: String { + pick( + ko: "쿠폰 사용이 완료되었습니다.", + en: "Coupon use completed.", + ja: "クーポンの使用が完了しました。" + ) + } + } + enum MissionMenu { static var menu1: String { pick(ko: "메뉴 1", en: "Menu 1", ja: "メニュー1") } static var menu2: String { pick(ko: "메뉴 2", en: "Menu 2", ja: "メニュー2") } diff --git a/SodaLive/Sources/MyPage/Can/Coupon/CanCouponView.swift b/SodaLive/Sources/MyPage/Can/Coupon/CanCouponView.swift index 1711152..d7d97ba 100644 --- a/SodaLive/Sources/MyPage/Can/Coupon/CanCouponView.swift +++ b/SodaLive/Sources/MyPage/Can/Coupon/CanCouponView.swift @@ -17,16 +17,16 @@ struct CanCouponView: View { BaseView(isLoading: $viewModel.isLoading) { GeometryReader { proxy in VStack(spacing: 0) { - DetailNavigationBar(title: "쿠폰등록") + DetailNavigationBar(title: I18n.CanCoupon.title) ScrollView(.vertical, showsIndicators: false) { VStack(spacing: 0) { - Text("쿠폰번호 입력") + Text(I18n.CanCoupon.inputTitle) .font(.custom(Font.bold.rawValue, size: 16.7)) .foregroundColor(Color(hex: "eeeeee")) .frame(maxWidth: .infinity, alignment: .leading) - TextField("쿠폰번호를 입력하세요", text: $viewModel.couponNumber) + TextField(I18n.CanCoupon.inputPlaceholder, text: $viewModel.couponNumber) .autocapitalization(.allCharacters) // Force uppercase keyboard .textContentType(.none) .disableAutocorrection(true) @@ -45,7 +45,7 @@ struct CanCouponView: View { } } - Text("등록하기") + Text(I18n.CanCoupon.submit) .font(.custom(Font.bold.rawValue, size: 18.3)) .foregroundColor(.white) .padding(.vertical, 16) @@ -58,17 +58,17 @@ struct CanCouponView: View { } VStack(alignment: .leading, spacing: 0) { - Text("[등록 및 사용안내]") + Text(I18n.CanCoupon.guideHeader) .font(.custom(Font.bold.rawValue, size: 13.3)) .foregroundColor(.grayee) - CanCouponNoticeItemView(notice: "공백없이 쿠폰번호 12자리를 입력해주세요.") + CanCouponNoticeItemView(notice: I18n.CanCoupon.guide1) .padding(.top, 8) - CanCouponNoticeItemView(notice: "충전된 캔 또는 포인트는 해당 충전현황에서 확인할 수 있습니다") + CanCouponNoticeItemView(notice: I18n.CanCoupon.guide2) .padding(.top, 4) - CanCouponNoticeItemView(notice: "쿠폰으로 충전된 캔은 사용 전까지 소멸되지 않으며, 포인트는 충전 후 72시간 이내에 사용하지 않으면 자동 소멸됩니다.") + CanCouponNoticeItemView(notice: I18n.CanCoupon.guide3) .padding(.top, 4) Rectangle() @@ -77,23 +77,23 @@ struct CanCouponView: View { .background(Color.gray55) .padding(.vertical, 26.7) - Text("[주의사항]") + Text(I18n.CanCoupon.cautionHeader) .font(.custom(Font.bold.rawValue, size: 13.3)) .foregroundColor(.grayee) - CanCouponNoticeItemView(notice: "이벤트 쿠폰을 통해 충전한 캔이나 포인트는 환불되지 않습니다.") + CanCouponNoticeItemView(notice: I18n.CanCoupon.caution1) .padding(.top, 8) - CanCouponNoticeItemView(notice: "쿠폰은 상업적 용도로 사용하거나 매매할 수 없습니다.") + CanCouponNoticeItemView(notice: I18n.CanCoupon.caution2) .padding(.top, 4) - CanCouponNoticeItemView(notice: "한번 등록한 쿠폰은 재사용이 불가합니다.") + CanCouponNoticeItemView(notice: I18n.CanCoupon.caution3) .padding(.top, 4) - CanCouponNoticeItemView(notice: "연령 제한 정책에 따라 쿠폰이용은 본인인증한 회원만 이용 가능합니다.") + CanCouponNoticeItemView(notice: I18n.CanCoupon.caution4) .padding(.top, 4) - Text("※ 쿠폰 등록 및 이용에 문제가 발생한 경우 '고객센터'로 문의주시기 바랍니다.") + Text(I18n.CanCoupon.contactHelp) .font(.custom(Font.medium.rawValue, size: 13.3)) .foregroundColor(.grayee) .fixedSize(horizontal: false, vertical: true) diff --git a/SodaLive/Sources/MyPage/Can/Coupon/CanCouponViewModel.swift b/SodaLive/Sources/MyPage/Can/Coupon/CanCouponViewModel.swift index 42d1904..8a53747 100644 --- a/SodaLive/Sources/MyPage/Can/Coupon/CanCouponViewModel.swift +++ b/SodaLive/Sources/MyPage/Can/Coupon/CanCouponViewModel.swift @@ -42,7 +42,7 @@ final class CanCouponViewModel: ObservableObject { if let message = decoded.message { self.errorMessage = message } else { - self.errorMessage = "쿠폰 사용이 완료되었습니다." + self.errorMessage = I18n.CanCoupon.useCompleted } DispatchQueue.main.asyncAfter(deadline: .now() + 1) { @@ -53,13 +53,13 @@ final class CanCouponViewModel: 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/Settings/Notice/NoticeDetailView.swift b/SodaLive/Sources/Settings/Notice/NoticeDetailView.swift index b8131b6..82f8cb8 100644 --- a/SodaLive/Sources/Settings/Notice/NoticeDetailView.swift +++ b/SodaLive/Sources/Settings/Notice/NoticeDetailView.swift @@ -15,7 +15,7 @@ struct NoticeDetailView: View { var body: some View { BaseView { VStack(spacing: 0) { - DetailNavigationBar(title: "공지사항 상세") + DetailNavigationBar(title: String(localized: "공지사항 상세")) VStack(alignment: .leading, spacing: 6.7) { Text(notice.title)