952 lines
47 KiB
Swift
952 lines
47 KiB
Swift
//
|
||
// I18n.swift
|
||
// SodaLive
|
||
//
|
||
// Created by Junie (AI) on 2025/12/17.
|
||
//
|
||
|
||
import Foundation
|
||
|
||
// MARK: - I18n 네임스페이스
|
||
// String Catalog를 사용하지 않는 컨텍스트에서 사용할 하드코딩 맵 기반 i18n.
|
||
// 기준 언어 선택은 LanguageHeaderProvider.current("ko"|"en"|"ja").
|
||
enum I18n {
|
||
// 채팅방(캐릭터 톡) 관련 문자열
|
||
enum ChatRoom {
|
||
// 잠금된 메시지 다이얼로그
|
||
static var lockedMessageTitle: String {
|
||
pick(ko: "잠금된 메시지", en: "Locked message", ja: "ロックされたメッセージ")
|
||
}
|
||
|
||
static func unlockMessageDescription(_ price: Int) -> String {
|
||
pick(
|
||
ko: "이 메시지를 \(price)캔으로 잠금해제 하시겠습니까?",
|
||
en: "Do you want to unlock this message for \(price) cans?",
|
||
ja: "このメッセージを\(price)缶で解除しますか?"
|
||
)
|
||
}
|
||
|
||
static var unlock: String {
|
||
pick(ko: "잠금해제", en: "Unlock", ja: "解除する")
|
||
}
|
||
|
||
// 대화 초기화 다이얼로그
|
||
static var resetTitle: String {
|
||
pick(ko: "대화 초기화", en: "Reset", ja: "会話をリセット")
|
||
}
|
||
|
||
static var resetDescription: String {
|
||
pick(
|
||
ko: "지금까지의 대화가 모두 초기화 되고 새롭게 대화를 시작합니다.",
|
||
en: "All previous messages will be cleared and a new conversation will begin.",
|
||
ja: "これまでの会話がすべて初期化され、新しい会話を開始します。"
|
||
)
|
||
}
|
||
|
||
static func resetWithCans(_ cans: Int) -> String {
|
||
pick(
|
||
ko: "\(cans)캔으로 초기화",
|
||
en: "Reset with \(cans) cans",
|
||
ja: "\(cans)缶でリセット"
|
||
)
|
||
}
|
||
}
|
||
// 검색 관련 문자열
|
||
enum Search {
|
||
// 검색 입력 플레이스홀더: 2글자 이상 안내
|
||
static var placeholderMin2: String {
|
||
pick(
|
||
ko: "검색어를 2글자 이상 입력하세요",
|
||
en: "Enter at least 2 characters",
|
||
ja: "2文字以上で入力してください"
|
||
)
|
||
}
|
||
}
|
||
enum ContentDetail {
|
||
static var creatorOtherContents: String {
|
||
pick(
|
||
ko: "크리에이터의 다른 콘텐츠",
|
||
en: "More from this creator",
|
||
ja: "このクリエイターの他のコンテンツ"
|
||
)
|
||
}
|
||
|
||
static var themeOtherContents: String {
|
||
pick(
|
||
ko: "테마의 다른 콘텐츠",
|
||
en: "More in this theme",
|
||
ja: "このテーマの他のコンテンツ"
|
||
)
|
||
}
|
||
|
||
static var pinLimitTitle: String {
|
||
pick(
|
||
ko: "고정 한도 도달",
|
||
en: "Pin limit reached",
|
||
ja: "固定上限に到達"
|
||
)
|
||
}
|
||
|
||
static var pinLimitDesc: String {
|
||
pick(
|
||
ko: "이 콘텐츠를 고정하시겠어요? 채널에 콘텐츠를 최대 3개까지 고정할 수 있습니다. 이 콘텐츠를 고정하면 가장 오래된 콘텐츠가 대체됩니다.",
|
||
en: "Do you want to pin this content? You can pin up to 3 contents in a channel. Pinning this will replace the oldest pinned content.",
|
||
ja: "このコンテンツを固定しますか?チャンネルには最大3件まで固定できます。このコンテンツを固定すると最も古い固定コンテンツが置き換えられます。"
|
||
)
|
||
}
|
||
}
|
||
enum CharacterDetailGallery {
|
||
static var purchaseConfirmTitle: String {
|
||
pick(ko: "구매 확인", en: "Confirmation", ja: "購入確認")
|
||
}
|
||
|
||
static var purchaseConfirmDescription: String {
|
||
pick(
|
||
ko: "선택한 이미지를 구매하시겠습니까?", en: "Buy the selected image?",
|
||
ja: "選択した画像を購入しますか?"
|
||
)
|
||
}
|
||
|
||
static func purchaseWithCans(_ cans: Int) -> String {
|
||
pick(
|
||
ko: "\(cans)캔으로 구매",
|
||
en: "Purchase for \(cans) cans",
|
||
ja: "\(cans)CANで購入"
|
||
)
|
||
}
|
||
}
|
||
enum Time {
|
||
static var justNow: String {
|
||
pick(ko: "방금 전", en: "Just now", ja: "たった今")
|
||
}
|
||
|
||
static func minutesAgo(_ minutes: Int) -> String {
|
||
pick(
|
||
ko: "\(minutes)분 전",
|
||
en: "\(minutes) minute\(minutes == 1 ? "" : "s") ago",
|
||
ja: "\(minutes)分前"
|
||
)
|
||
}
|
||
|
||
static func hoursAgo(_ hours: Int) -> String {
|
||
pick(
|
||
ko: "\(hours)시간 전",
|
||
en: "\(hours) hour\(hours == 1 ? "" : "s") ago",
|
||
ja: "\(hours)時間前"
|
||
)
|
||
}
|
||
|
||
static func daysAgo(_ days: Int) -> String {
|
||
pick(
|
||
ko: "\(days)일 전",
|
||
en: "\(days) day\(days == 1 ? "" : "s") ago",
|
||
ja: "\(days)日前"
|
||
)
|
||
}
|
||
|
||
static func monthsAgo(_ months: Int) -> String {
|
||
pick(
|
||
ko: "\(months)개월 전",
|
||
en: "\(months) month\(months == 1 ? "" : "s") ago",
|
||
ja: "\(months)か月前"
|
||
)
|
||
}
|
||
|
||
static func yearsAgo(_ years: Int) -> String {
|
||
pick(
|
||
ko: "\(years)년 전",
|
||
en: "\(years) year\(years == 1 ? "" : "s") ago",
|
||
ja: "\(years)年前"
|
||
)
|
||
}
|
||
}
|
||
enum Common {
|
||
static var viewAll: String { pick(ko: "전체보기", en: "View all", ja: "すべて見る") }
|
||
|
||
// 기본 샘플들
|
||
static var apply: String { pick(ko: "적용", en: "Save", ja: "適用") }
|
||
static var confirm: String { pick(ko: "확인", en: "Confirm", ja: "確認") }
|
||
static var cancel: String { pick(ko: "취소", en: "Cancel", ja: "キャンセル") }
|
||
static var yes: String { pick(ko: "예", en: "Yes", ja: "はい") }
|
||
static var no: String { pick(ko: "아니오", en: "No", ja: "いいえ") }
|
||
// 공통 액션/라벨
|
||
static var delete: String { pick(ko: "삭제", en: "Delete", ja: "削除") }
|
||
static var commentDeleteTitle: String { pick(ko: "댓글 삭제", en: "Delete comment", ja: "コメントを削除") }
|
||
static var postDeleteTitle: String { pick(ko: "게시물 삭제", en: "Delete post", ja: "投稿を削除") }
|
||
|
||
// 설정
|
||
static var settings: String { pick(ko: "설정", en: "Settings", ja: "設定") }
|
||
static var alertTitle: String { pick(ko: "알림", en: "Notice", ja: "お知らせ") }
|
||
static var noticeTitle: String { pick(ko: "안내", en: "Notice", ja: "お知らせ") }
|
||
static var pointGrantTitle: String { pick(ko: "포인트 지급", en: "Points granted", 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: "New", ja: "最新コンテンツ") }
|
||
static var openScheduled: String { pick(ko: "오픈예정", en: "Scheduled to open", ja: "オープン予定") }
|
||
static var points: String { pick(ko: "포인트", en: "Points", ja: "ポイント") }
|
||
}
|
||
|
||
enum Splash {
|
||
static var tagline: String {
|
||
pick(
|
||
ko: "목소리로 만나는 새로운 세계",
|
||
en: "A new world you meet by voice",
|
||
ja: "声で出会う新しい世界"
|
||
)
|
||
}
|
||
|
||
static var brandName: String {
|
||
pick(ko: "소다라이브", en: "SodaLive", ja: "ソーダライブ")
|
||
}
|
||
|
||
static var maintenanceTitle: String {
|
||
pick(ko: "안내", en: "Notice", ja: "お知らせ")
|
||
}
|
||
|
||
static var maintenanceDesc: String {
|
||
pick(
|
||
ko: "서비스 점검중입니다.", en: "Service Under Maintenance",
|
||
ja: "ただいまメンテナンス中です。"
|
||
)
|
||
}
|
||
|
||
static var updateTitle: String {
|
||
pick(ko: "업데이트", en: "Update", ja: "アップデート")
|
||
}
|
||
|
||
static var updateDesc: String {
|
||
pick(
|
||
ko: "최신 업데이트가 있습니다.\n업데이트 하시겠습니까?",
|
||
en: "A new update is available.\nWould you like to update now?",
|
||
ja: "最新のアップデートがあります。\nアップデートしますか?"
|
||
)
|
||
}
|
||
|
||
static var updateConfirm: String {
|
||
pick(ko: "업데이트", en: "Update", ja: "アップデート")
|
||
}
|
||
|
||
static var updateCancel: String {
|
||
pick(ko: "다음에", en: "Later", ja: "あとで")
|
||
}
|
||
|
||
static var forcedUpdateDesc: String {
|
||
pick(
|
||
ko: "필수 업데이트가 있습니다.\n업데이트 후 사용가능합니다.",
|
||
en: "A required update is available.\nPlease update to continue.",
|
||
ja: "必須アップデートがあります。\nアップデート後に利用できます。"
|
||
)
|
||
}
|
||
}
|
||
|
||
// 설정 화면 및 계정 관련 공통 문구는 아래 Settings 네임스페이스에 포함됨
|
||
|
||
// 설정 > 공통 및 회원탈퇴(SignOut)
|
||
enum Settings {
|
||
// 알림 다이얼로그 타이틀
|
||
static var alertTitle: String { pick(ko: "알림", en: "Notice", ja: "お知らせ") }
|
||
|
||
// 로그아웃 확인 질문
|
||
static var logoutQuestion: String {
|
||
pick(
|
||
ko: "로그아웃 하시겠어요?",
|
||
en: "Do you want to log out?",
|
||
ja: "ログアウトしますか?"
|
||
)
|
||
}
|
||
|
||
// 모든 기기 로그아웃 확인 질문
|
||
static var logoutAllQuestion: String {
|
||
pick(
|
||
ko: "모든 기기에서 로그아웃 하시겠어요?",
|
||
en: "Do you want to log out from all devices?",
|
||
ja: "すべてのデバイスからログアウトしますか?"
|
||
)
|
||
}
|
||
enum SignOut {
|
||
// 탈퇴 사유 목록 (UI에서 그대로 배열로 사용 가능)
|
||
static var reasons: [String] {
|
||
[
|
||
reasonChangeNickname,
|
||
reasonConflictWithUser,
|
||
reasonInconvenientManyErrors,
|
||
reasonDislikeOperation,
|
||
reasonOtherServiceBetter,
|
||
reasonWantToDeleteContent,
|
||
reasonLowFrequency,
|
||
reasonNoDesiredContentOrCreator,
|
||
reasonTooExpensive,
|
||
other
|
||
]
|
||
}
|
||
|
||
static var reasonChangeNickname: String {
|
||
pick(
|
||
ko: "닉네임을 변경하고 싶어서",
|
||
en: "I want to change my nickname",
|
||
ja: "ニックネームを変更したいから"
|
||
)
|
||
}
|
||
|
||
static var reasonConflictWithUser: String {
|
||
pick(
|
||
ko: "다른 사용자와의 다툼이 있어서",
|
||
en: "Conflict with another user",
|
||
ja: "他のユーザーとトラブルがあったから"
|
||
)
|
||
}
|
||
|
||
static var reasonInconvenientManyErrors: String {
|
||
pick(
|
||
ko: "이용이 불편하고 장애가 많아서",
|
||
en: "Inconvenient to use and many errors",
|
||
ja: "使いにくく障害が多いから"
|
||
)
|
||
}
|
||
|
||
static var reasonDislikeOperation: String {
|
||
pick(
|
||
ko: "서비스 운영이 마음에 들지 않아서",
|
||
en: "Dissatisfied with service operations",
|
||
ja: "サービス運営が気に入らないから"
|
||
)
|
||
}
|
||
|
||
static var reasonOtherServiceBetter: String {
|
||
pick(
|
||
ko: "다른 서비스가 더 좋아서",
|
||
en: "Another service is better",
|
||
ja: "他のサービスの方が良いから"
|
||
)
|
||
}
|
||
|
||
static var reasonWantToDeleteContent: String {
|
||
pick(
|
||
ko: "삭제하고 싶은 내용이 있어서",
|
||
en: "I want to delete some content",
|
||
ja: "削除したい内容があるから"
|
||
)
|
||
}
|
||
|
||
static var reasonLowFrequency: String {
|
||
pick(
|
||
ko: "이용빈도가 낮아서",
|
||
en: "I use the service infrequently",
|
||
ja: "利用頻度が低いから"
|
||
)
|
||
}
|
||
|
||
static var reasonNoDesiredContentOrCreator: String {
|
||
pick(
|
||
ko: "원하는 콘텐츠나 크리에이터가 없어서",
|
||
en: "I can't find desired content or creators",
|
||
ja: "望むコンテンツやクリエイターがいないから"
|
||
)
|
||
}
|
||
|
||
static var reasonTooExpensive: String {
|
||
pick(
|
||
ko: "이용요금이 비싸서",
|
||
en: "The price is too high",
|
||
ja: "利用料金が高いから"
|
||
)
|
||
}
|
||
|
||
static var other: String {
|
||
pick(
|
||
ko: "기타",
|
||
en: "Other",
|
||
ja: "その他"
|
||
)
|
||
}
|
||
|
||
// 검증 메시지
|
||
static var selectReasonRequired: String {
|
||
pick(
|
||
ko: "계정을 삭제하려는 이유를 선택해 주세요.", en: "select a reason for deleting your account.",
|
||
ja: "アカウント削除の理由を選択してください。"
|
||
)
|
||
}
|
||
|
||
// 비밀번호 확인 필드
|
||
static var passwordTitle: String {
|
||
pick(
|
||
ko: "비밀번호 확인",
|
||
en: "Password confirmation",
|
||
ja: "パスワード確認"
|
||
)
|
||
}
|
||
|
||
static var passwordPlaceholder: String {
|
||
pick(
|
||
ko: "비밀번호를 입력해주세요.",
|
||
en: "Please enter your password.",
|
||
ja: "パスワードを入力してください。"
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
enum Charge {
|
||
static var inAppPurchase: String {
|
||
pick(ko: "인 앱 결제", en: "In-app purchase", 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") }
|
||
static var menu3: String { pick(ko: "메뉴 3", en: "Menu 3", ja: "メニュー3") }
|
||
|
||
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: "Now", ja: "今すぐ開始") }
|
||
static var schedule: String { pick(ko: "예약 설정", en: "Booking", ja: "予約設定") }
|
||
|
||
// 공개 범위
|
||
static var publicRoom: String { pick(ko: "공개", en: "Open", 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: "成人") }
|
||
|
||
// 최근 데이터 관련 토스트/알림
|
||
static var recentDataLoaded: String { pick(ko: "최근데이터를 불러왔습니다.", en: "Recent data has been loaded.", ja: "最新データを読み込みました。") }
|
||
static var recentDataLoadFailed: String { pick(ko: "최근데이터를 불러오지 못했습니다.\n다시 시도해 주세요.", en: "Failed to load recent data.\nPlease try again.", ja: "最近のデータを読み込めませんでした。\n恐れ入りますが、もう一度お試しください。") }
|
||
static var createLiveFailedGeneric: String { pick(ko: "라이브를 만들지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.", en: "Could not create the live.\nPlease try again.\nIf the problem persists, please contact customer support.", ja: "ライブを作成できませんでした。\nもう一度お試しください。\n問題が続く場合はカスタマーサポートにお問い合わせください。") }
|
||
|
||
// 검증 에러 메시지
|
||
static var selectCoverImage: String { pick(ko: "커버이미지를 선택해주세요.", en: "Please select a cover image.", ja: "カバー画像を選択してください。") }
|
||
static var enterTitle: String { pick(ko: "제목을 입력해 주세요.", en: "Please enter a title.", ja: "タイトルを入力してください。") }
|
||
static var enterNoticeMin5: String { pick(ko: "공지를 5자 이상 입력해주세요.", en: "Please enter at least 5 characters for the notice.", ja: "お知らせは5文字以上で入力してください。") }
|
||
static var enterPeopleRange: String { pick(ko: "인원을 3~999명 사이로 입력해주세요.", en: "Please enter the number of people between 3 and 999.", ja: "参加人数は3〜999の範囲で入力してください。") }
|
||
static var enterPassword6: String { pick(ko: "방 입장 비밀번호 6자리를 입력해 주세요.", en: "Please enter a 6-digit room entry password.", ja: "入室パスワード(6桁)を入力してください。") }
|
||
}
|
||
|
||
enum LiveRoom {
|
||
static var ageRestrictionDesc: String {
|
||
pick(
|
||
ko: "지금 참여하던 라이브는 '19세 이상' 연령제한이 설정되어 정보통신망 이용촉진 및 정보 보호 등에 관한 법률 및 청소년 보호법의 규정에 의해 만 19세 미만의 청소년은 이용할 수 없습니다.\n마이페이지에서 본인인증 후 다시 이용하시기 바랍니다.",
|
||
en: "The live you were joining has been set to '19+' and, under applicable laws, minors under 19 cannot use it.\nPlease verify your identity in My Page and try again.",
|
||
ja: "参加中のライブは「19歳以上」に設定されており、関連法令により19歳未満の方はご利用いただけません。\nマイページで本人認証後、再度お試しください。"
|
||
)
|
||
}
|
||
|
||
static var likeHeartNoticeDesc: String {
|
||
pick(
|
||
ko: "'좋아해요'는 유료 후원입니다.\n클릭시 1캔이 소진됩니다.",
|
||
en: "'Like' is a paid donation.\nClicking consumes 1 can.",
|
||
ja: "「좋아요」は有料の支援です。\nクリックすると1缶消費されます。"
|
||
)
|
||
}
|
||
|
||
static var quitTitle: String { pick(ko: "라이브 나가기", en: "Leave live", ja: "ライブを退出") }
|
||
static var quitDesc: String { pick(ko: "라이브에서 나가시겠습니까?", en: "Do you want to leave the live?", ja: "ライブから退出しますか?") }
|
||
|
||
static var endTitle: String { pick(ko: "라이브 종료", en: "End live", ja: "ライブ終了") }
|
||
static var endDesc: String {
|
||
pick(
|
||
ko: "라이브를 종료하시겠습니까?\n라이브를 종료하면 대화내용은\n저장되지 않고 사라집니다.\n참여자들 또한 라이브가 종료되어\n강제퇴장 됩니다.",
|
||
en: "Do you want to end the live?\nEnding the live will erase the chat history.\nParticipants will also be removed.",
|
||
ja: "ライブを終了しますか?\nライブを終了すると会話内容は保存されず消去されます。\n参加者も終了に伴い強制退場となります。"
|
||
)
|
||
}
|
||
|
||
static var kickOutTitle: String { pick(ko: "내보내기", en: "Kick out", ja: "退場させる") }
|
||
static var kickOutConfirm: String { pick(ko: "내보내기", en: "Kick out", ja: "退場させる") }
|
||
}
|
||
|
||
enum LiveCancel {
|
||
static var reasonPlaceholder: String {
|
||
pick(
|
||
ko: "취소사유를 입력하세요",
|
||
en: "Enter a cancellation reason",
|
||
ja: "キャンセル理由を入力してください"
|
||
)
|
||
}
|
||
|
||
static var reservationCanceled: String {
|
||
pick(
|
||
ko: "예약이 취소되었습니다.",
|
||
en: "The reservation has been canceled.",
|
||
ja: "予約がキャンセルされました。"
|
||
)
|
||
}
|
||
}
|
||
|
||
enum CreateContent {
|
||
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 the details.", ja: "内容を入力してください") }
|
||
static var uploadTitle: String { pick(ko: "콘텐츠 업로드", en: "Content upload", ja: "コンテンツのアップロード") }
|
||
static var uploadDescription: String {
|
||
pick(
|
||
ko: "등록한 콘텐츠가 업로드 중입니다.\n콘텐츠 등록이 완료되면 알림을 보내드립니다.\n이 페이지를 나가도 콘텐츠는 자동으로 등록됩니다.",
|
||
en: "Your content is being uploaded.\nWe will notify you when the upload is complete.\nYou can leave this page and the content will still be registered.",
|
||
ja: "登録したコンテンツはアップロード中です。\nアップロードが完了すると通知します。\nこのページを離れても自動で登録されます。"
|
||
)
|
||
}
|
||
|
||
// 가격/구매 옵션
|
||
static var free: String { pick(ko: "무료", en: "Free", ja: "無料") }
|
||
static var paid: String { pick(ko: "유료", en: "Paid", ja: "有料") }
|
||
static var purchaseBoth: String { pick(ko: "소장/대여", en: "Buy/Rent", ja: "購入/レンタル") }
|
||
static var purchaseBuyOnly: String { pick(ko: "소장만", en: "Buy only", ja: "購入のみ") }
|
||
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: "成人") }
|
||
|
||
// 댓글 가능 여부
|
||
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 Playlist {
|
||
static var deleteTitle: String {
|
||
pick(ko: "재생 목록 삭제", en: "Delete playlist", ja: "プレイリストを削除")
|
||
}
|
||
|
||
static func deleteQuestion(_ title: String) -> String {
|
||
pick(
|
||
ko: "\(title)을 삭제하시겠습니까?",
|
||
en: "Do you want to delete \(title)?",
|
||
ja: "\(title)を削除しますか?"
|
||
)
|
||
}
|
||
}
|
||
|
||
enum Category {
|
||
static var all: String { pick(ko: "전체", en: "All", ja: "すべて") }
|
||
}
|
||
|
||
enum RankingSort {
|
||
// 분석/지표 등
|
||
static var revenue: String { pick(ko: "매출", en: "Revenue", ja: "売上高") }
|
||
static var salesVolume: String { pick(ko: "판매량", en: "Units", ja: "販売量") }
|
||
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: "キャラクター") }
|
||
static var work: String { pick(ko: "작품별", en: "By Original", ja: "作品別") }
|
||
static var talk: String { pick(ko: "톡", en: "Chat", ja: "トーク") }
|
||
static var workInfo: String { pick(ko: "작품정보", en: "Work info", ja: "作品情報") }
|
||
|
||
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: "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 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: "LIVE") }
|
||
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)話")
|
||
}
|
||
|
||
// 시리즈 전체보기(크리에이터 기준) 타이틀
|
||
static func viewAllByCreator(_ nickname: String) -> String {
|
||
pick(
|
||
ko: "\(nickname) 님의 시리즈 전체보기",
|
||
en: "All series by \(nickname)",
|
||
ja: "\(nickname) さんのシリーズ一覧"
|
||
)
|
||
}
|
||
}
|
||
|
||
// 시리즈 상세 화면 관련 문자열 모음
|
||
enum SeriesDetail {
|
||
// 탭
|
||
static var home: String { pick(ko: "홈", en: "Home", ja: "ホーム") }
|
||
static var introduction: String { pick(ko: "작품소개", en: "Introduction", ja: "作品紹介") }
|
||
|
||
// 섹션 타이틀
|
||
static var keywords: String { pick(ko: "키워드", en: "Keywords", ja: "キーワード") }
|
||
static var workIntro: String { pick(ko: "작품소개", en: "Introduction", ja: "作品紹介") }
|
||
static var details: String { pick(ko: "상세정보", en: "Details", ja: "詳細情報") }
|
||
static var price: String { pick(ko: "가격", en: "Price", ja: "価格") }
|
||
|
||
// 상세 라벨
|
||
static var genre: String { pick(ko: "장르", en: "Genre", ja: "ジャンル") }
|
||
static var ageLimit: String { pick(ko: "연령제한", en: "Age Restriction", ja: "年齢制限") }
|
||
static var writer: String { pick(ko: "작가", en: "Writer", ja: "作家") }
|
||
static var studio: String { pick(ko: "제작사", en: "Produced by", ja: "制作会社") }
|
||
static var schedule: String { pick(ko: "연재", en: "Ongoing", ja: "連載") }
|
||
static var releaseDate: String { pick(ko: "출시일", en: "Release date", ja: "リリース日") }
|
||
|
||
// 가격 라벨(명사형)
|
||
static var rentLabel: String { pick(ko: "대여", en: "Rent", ja: "レンタル") }
|
||
static var buyLabel: String { pick(ko: "소장", en: "Buy", ja: "購入") }
|
||
|
||
// 값 표기
|
||
static var ageAll: String { pick(ko: "전체연령가", en: "All", ja: "全年齢") }
|
||
static var age19Plus: String { pick(ko: "19세 이상", en: "19+", ja: "成人") }
|
||
static var random: String { pick(ko: "랜덤", en: "Irregular", ja: "ランダム") }
|
||
|
||
// 단위 및 포맷
|
||
static func cansUnit(_ value: Int) -> String {
|
||
pick(ko: "\(value)캔", en: "\(value) cans", ja: "\(value)缶")
|
||
}
|
||
|
||
static func daysSuffix(_ days: Int) -> String {
|
||
pick(ko: "(\(days)일)", en: "(\(days) day\(days == 1 ? "" : "s"))", ja: "(\(days)日)")
|
||
}
|
||
|
||
// 가격 범위 표기: min==max==0 -> 무료, min==max>0 -> N캔, 범위 -> (무료|min) ~ max캔
|
||
static func priceInfo(min: Int, max: Int) -> String {
|
||
if min == max {
|
||
if max == 0 {
|
||
return CreateContent.free
|
||
} else {
|
||
return cansUnit(max)
|
||
}
|
||
} else {
|
||
let left = (min == 0) ? CreateContent.free : "\(min)"
|
||
// 범위 구분자는 로케일과 무관하게 시각적 구분자 유지
|
||
return "\(left) ~ \(cansUnit(max))"
|
||
}
|
||
}
|
||
}
|
||
|
||
// 시리즈 메인 화면 내부 탭 라벨
|
||
enum SeriesMain {
|
||
// 기존 정의 재사용: 홈은 SeriesDetail.home 과 의미 동일
|
||
static var home: String { SeriesDetail.home }
|
||
static var dayOfWeek: String { pick(ko: "요일별", en: "By day", ja: "曜日別") }
|
||
static var byGenre: String { pick(ko: "장르별", en: "By genre", ja: "ジャンル別") }
|
||
}
|
||
|
||
// 콘텐츠 구매/대여 관련 공통 액션 라벨
|
||
enum Purchase {
|
||
static var rent: String { pick(ko: "대여하기", en: "Rent", ja: "レンタルする") }
|
||
static var buy: String { pick(ko: "소장하기", en: "Buy", ja: "購入する") }
|
||
static var purchase: String { pick(ko: "구매하기", en: "Purchase", ja: "購入する") }
|
||
}
|
||
|
||
// 마이페이지 > 프로필 수정 내 소셜 링크 섹션
|
||
enum ProfileUpdate {
|
||
// 라벨
|
||
static var instagram: String { pick(ko: "인스타그램", en: "Instagram", ja: "インスタグラム") }
|
||
static var youtube: String { pick(ko: "유튜브", en: "YouTube", ja: "YouTube") }
|
||
static var website: String { pick(ko: "웹사이트", en: "Website", ja: "ウェブサイト") }
|
||
static var blog: String { pick(ko: "블로그", en: "Blog", ja: "ブログ") }
|
||
|
||
// 플레이스홀더
|
||
static var instagramUrlPlaceholder: String {
|
||
pick(ko: "인스타그램 URL", en: "Instagram URL", ja: "インスタグラムURL")
|
||
}
|
||
static var youtubeUrlPlaceholder: String {
|
||
pick(ko: "유튜브 URL", en: "YouTube URL", ja: "YouTubeのURL")
|
||
}
|
||
static var websiteUrlPlaceholder: String {
|
||
pick(ko: "웹사이트 URL", en: "Website URL", ja: "ウェブサイトのURL")
|
||
}
|
||
static var blogUrlPlaceholder: String {
|
||
pick(ko: "블로그 URL", en: "Blog URL", ja: "ブログのURL")
|
||
}
|
||
|
||
// 소개글 입력 플레이스홀더
|
||
static var introductionPlaceholder: String {
|
||
pick(ko: "소개글을 입력하세요", en: "Enter your introduction", ja: "自己紹介を入力してください")
|
||
}
|
||
|
||
// 프로필/비밀번호 업데이트 관련 토스트/검증 메시지
|
||
static var profileUpdated: String {
|
||
pick(ko: "프로필이 변경되었습니다.", en: "Profile has been updated.", ja: "プロフィールが更新されました。")
|
||
}
|
||
|
||
static var passwordCurrentRequired: String {
|
||
pick(ko: "현재 비밀번호를 입력하세요.", en: "Please enter your current password.", ja: "現在のパスワードを入力してください。")
|
||
}
|
||
static var passwordNewRequired: String {
|
||
pick(ko: "변경할 비밀번호를 입력하세요.", en: "Please enter a new password.", ja: "新しいパスワードを入力してください。")
|
||
}
|
||
static var passwordNotMatch: String {
|
||
pick(ko: "비밀번호가 일치하지 않습니다.", en: "Passwords do not match.", ja: "パスワードが一致しません。")
|
||
}
|
||
static var passwordRuleHint: String {
|
||
pick(ko: "영문, 숫자 포함 8자 이상의 비밀번호를 입력해 주세요.", en: "Enter a password of at least 8 characters with letters and numbers.", ja: "英字と数字を含む8文字以上のパスワードを入力してください。")
|
||
}
|
||
static var passwordUpdated: String {
|
||
pick(ko: "비밀번호가 변경되었습니다.", en: "Password has been changed.", ja: "パスワードが変更されました。")
|
||
}
|
||
|
||
static var profileImageUpdateFailed: String {
|
||
pick(
|
||
ko: "프로필 이미지를 업데이트 하지 못했습니다.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.",
|
||
en: "Could not update the profile image.\nIf the problem persists, please contact customer support.",
|
||
ja: "プロフィール画像を更新できませんでした。\n問題が続く場合はカスタマーサポートにお問い合わせください。"
|
||
)
|
||
}
|
||
}
|
||
|
||
// 문자 메시지(Text Message) 관련 문자열
|
||
enum TextMessage {
|
||
// 전송 버튼 라벨
|
||
static var send: String {
|
||
pick(ko: "메시지 보내기", en: "Send", ja: "メッセージを送る")
|
||
}
|
||
|
||
// 입력창 플레이스홀더
|
||
static var placeholder: String {
|
||
pick(ko: "내용을 입력해 주세요.", en: "Please enter your message.", ja: "内容を入力してください。")
|
||
}
|
||
|
||
// 수신자 입력 플레이스홀더
|
||
static var recipientPlaceholder: String {
|
||
pick(ko: "받는 사람", en: "To.", ja: "受信者")
|
||
}
|
||
|
||
// 수신자 미선택 오류
|
||
static var selectRecipient: String {
|
||
pick(ko: "받는 사람을 선택해 주세요.", en: "Select a recipient.", ja: "受信者を選択してください。")
|
||
}
|
||
|
||
// 전송 성공 토스트/팝업 문구
|
||
static var sendSuccess: String {
|
||
pick(ko: "메시지 전송이 완료되었습니다.", en: "Your message has been sent.", ja: "メッセージの送信が完了しました。")
|
||
}
|
||
|
||
enum Validation {
|
||
// 최소 글자 수 안내
|
||
static func minLength(_ count: Int) -> String {
|
||
pick(
|
||
ko: "\(count)글자 이상 입력해 주세요.",
|
||
en: "Please enter at least \(count) characters.",
|
||
ja: "\(count)文字以上で入力してください。"
|
||
)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - 내부 헬퍼
|
||
@inline(__always)
|
||
private func pick(ko: String, en: String, ja: String) -> String {
|
||
switch LanguageHeaderProvider.current {
|
||
case "ko": return ko
|
||
case "ja": return ja
|
||
default: return en
|
||
}
|
||
}
|
||
|
||
/*
|
||
사용 예시 (ViewModel, Service 등 Text 이외 컨텍스트):
|
||
|
||
// ViewModel 내부
|
||
let title = I18n.Common.all
|
||
|
||
// View 내부(Text 대신 다른 UI 요소 라벨 등)
|
||
let menuLabel = I18n.Common.settings
|
||
|
||
주의: 기존에 Text("...")와 같은 String Catalog 사용부는 그대로 유지합니다.
|
||
*/
|