feat(chat): 채팅 모듈 하드코딩 문구를 I18n 키로 통일한다
This commit is contained in:
@@ -33,7 +33,7 @@ struct CharacterItemView: View {
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
Text("N")
|
||||
Text(I18n.Chat.Character.newBadge)
|
||||
.appFont(size: 18, weight: .regular)
|
||||
.foregroundColor(.white)
|
||||
.frame(width: 30, height: 30)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import SwiftUI
|
||||
|
||||
struct CharacterSectionView: View {
|
||||
let title: LocalizedStringResource
|
||||
let title: String
|
||||
let items: [Character]
|
||||
let isShowRank: Bool
|
||||
var trailingTitle: String? = nil
|
||||
@@ -52,7 +52,7 @@ struct CharacterSectionView: View {
|
||||
|
||||
#Preview {
|
||||
CharacterSectionView(
|
||||
title: "신규 캐릭터",
|
||||
title: I18n.Chat.Character.newSectionTitle,
|
||||
items: [
|
||||
Character(characterId: 1, name: "찰리", description: "새로운 친구", imageUrl: "https://picsum.photos/300", isNew: true),
|
||||
Character(characterId: 2, name: "데이지", description: "", imageUrl: "https://picsum.photos/300", isNew: false)
|
||||
|
||||
@@ -39,7 +39,7 @@ struct CharacterView: View {
|
||||
// 인기 캐릭터 섹션
|
||||
if !viewModel.popularCharacters.isEmpty {
|
||||
CharacterSectionView(
|
||||
title: "인기 캐릭터",
|
||||
title: I18n.Chat.Character.popularSectionTitle,
|
||||
items: viewModel.popularCharacters,
|
||||
isShowRank: true,
|
||||
onTap: { ch in
|
||||
@@ -51,7 +51,7 @@ struct CharacterView: View {
|
||||
// 신규 캐릭터 섹션
|
||||
if !viewModel.newCharacters.isEmpty {
|
||||
CharacterSectionView(
|
||||
title: "신규 캐릭터",
|
||||
title: I18n.Chat.Character.newSectionTitle,
|
||||
items: viewModel.newCharacters,
|
||||
isShowRank: false,
|
||||
trailingTitle: I18n.Common.viewAll,
|
||||
@@ -67,7 +67,7 @@ struct CharacterView: View {
|
||||
if !viewModel.recommendCharacters.isEmpty {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
HStack {
|
||||
Text("추천 캐릭터")
|
||||
Text(I18n.Chat.Character.recommendSectionTitle)
|
||||
.appFont(size: 24, weight: .bold)
|
||||
.foregroundColor(.white)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ final class CharacterViewModel: ObservableObject {
|
||||
if let message = decoded.message {
|
||||
self.errorMessage = message
|
||||
} else {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.errorMessage = I18n.Common.commonError
|
||||
}
|
||||
|
||||
self.isShowPopup = true
|
||||
@@ -62,7 +62,7 @@ final class CharacterViewModel: ObservableObject {
|
||||
self.isLoading = false
|
||||
} catch {
|
||||
self.isLoading = false
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.errorMessage = I18n.Common.commonError
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ final class CharacterViewModel: ObservableObject {
|
||||
if let message = decoded.message {
|
||||
self.errorMessage = message
|
||||
} else {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.errorMessage = I18n.Common.commonError
|
||||
}
|
||||
|
||||
self.isShowPopup = true
|
||||
@@ -101,11 +101,10 @@ final class CharacterViewModel: ObservableObject {
|
||||
self.isLoading = false
|
||||
} catch {
|
||||
self.isLoading = false
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.errorMessage = I18n.Common.commonError
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ struct CharacterDetailView: View {
|
||||
var body: some View {
|
||||
BaseView(isLoading: $viewModel.isLoading) {
|
||||
VStack(spacing: 0) {
|
||||
DetailNavigationBar(title: String(localized: "캐릭터 정보")) {
|
||||
DetailNavigationBar(title: I18n.Chat.Character.detailTitle) {
|
||||
if presentationMode.wrappedValue.isPresented {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
} else {
|
||||
@@ -77,7 +77,7 @@ struct CharacterDetailView: View {
|
||||
if let others = viewModel.characterDetail?.others, !others.isEmpty {
|
||||
VStack(spacing: 16) {
|
||||
HStack {
|
||||
Text("장르의 다른 캐릭터")
|
||||
Text(I18n.Chat.Character.detailOtherCharactersTitle)
|
||||
.appFont(size: 26, weight: .bold)
|
||||
.foregroundColor(.white)
|
||||
|
||||
@@ -178,6 +178,13 @@ extension CharacterDetailView {
|
||||
|
||||
// MARK: - Profile Section
|
||||
extension CharacterDetailView {
|
||||
private func isMaleGender(_ gender: String) -> Bool {
|
||||
let normalizedGender = gender
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.lowercased()
|
||||
return normalizedGender == "남성" || normalizedGender == "male"
|
||||
}
|
||||
|
||||
private var profileSection: some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
if viewModel.characterDetail?.mbti != nil ||
|
||||
@@ -189,7 +196,7 @@ extension CharacterDetailView {
|
||||
Text(viewModel.characterDetail?.translated?.gender ?? gender)
|
||||
.appFont(size: 14, weight: .regular)
|
||||
.foregroundColor(
|
||||
gender == "남성" ?
|
||||
isMaleGender(gender) ?
|
||||
Color.button :
|
||||
Color.mainRed
|
||||
)
|
||||
@@ -201,7 +208,7 @@ extension CharacterDetailView {
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.stroke(lineWidth: 1)
|
||||
.foregroundColor(
|
||||
gender == "남성" ?
|
||||
isMaleGender(gender) ?
|
||||
Color.button :
|
||||
Color.mainRed
|
||||
)
|
||||
@@ -209,7 +216,7 @@ extension CharacterDetailView {
|
||||
}
|
||||
|
||||
if let age = viewModel.characterDetail?.age {
|
||||
Text("\(age)세")
|
||||
Text(I18n.Chat.Character.age(age))
|
||||
.appFont(size: 14, weight: .regular)
|
||||
.foregroundColor(Color(hex: "B0BEC5"))
|
||||
.padding(.horizontal, 7)
|
||||
@@ -252,7 +259,7 @@ extension CharacterDetailView {
|
||||
|
||||
if let characterType = viewModel.characterDetail?.characterType {
|
||||
HStack(spacing: 8) {
|
||||
Text(characterType.rawValue)
|
||||
Text(characterType == .Clone ? I18n.Chat.Character.typeClone : I18n.Chat.Character.typeCharacter)
|
||||
.appFont(size: 12, weight: .regular)
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 5)
|
||||
@@ -282,7 +289,7 @@ extension CharacterDetailView {
|
||||
private func worldViewSection(backgrounds: String) -> some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
HStack {
|
||||
Text("[세계관 및 작품 소개]")
|
||||
Text(I18n.Chat.Character.detailWorldViewTitle)
|
||||
.appFont(size: 18, weight: .bold)
|
||||
.foregroundColor(.white)
|
||||
|
||||
@@ -300,7 +307,7 @@ extension CharacterDetailView {
|
||||
private func originalWorkSection(title: String, link: String) -> some View {
|
||||
VStack(spacing: 8) {
|
||||
HStack {
|
||||
Text("원작")
|
||||
Text(I18n.Chat.Character.detailOriginalTitle)
|
||||
.appFont(size: 16, weight: .bold)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(.white)
|
||||
@@ -321,7 +328,7 @@ extension CharacterDetailView {
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}) {
|
||||
Text("원작 보러가기")
|
||||
Text(I18n.Chat.Character.detailOriginalLinkButton)
|
||||
.appFont(size: 16, weight: .bold)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(Color(hex: "3BB9F1"))
|
||||
@@ -342,7 +349,7 @@ extension CharacterDetailView {
|
||||
private func personalitySection(personalities: String) -> some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
HStack {
|
||||
Text("[성격 및 특징]")
|
||||
Text(I18n.Chat.Character.detailPersonalityTitle)
|
||||
.appFont(size: 18, weight: .bold)
|
||||
.foregroundColor(.white)
|
||||
|
||||
@@ -354,24 +361,19 @@ extension CharacterDetailView {
|
||||
// 캐릭터톡 대화 가이드
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
HStack {
|
||||
Text("⚠️ 캐릭터톡 대화 가이드")
|
||||
Text(I18n.Chat.Character.detailConversationGuideTitle)
|
||||
.appFont(size: 16, weight: .bold)
|
||||
.foregroundColor(Color(hex: "B0BEC5"))
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
Text("""
|
||||
보이스온의 오픈월드 캐릭터톡은 대화의 자유도가 높아 대화에 참여하는 당신은 누구든 될 수 있습니다. 세계관 속 연관 캐릭터가 되어 대화를 하거나 완전히 새로운 인물이 되어 캐릭터와 당신만의 스토리를 만들어 갈 수 있습니다.
|
||||
""")
|
||||
Text(I18n.Chat.Character.detailConversationGuideDescription1)
|
||||
.appFont(size: 16, weight: .regular)
|
||||
.foregroundColor(Color(hex: "AEAEB2"))
|
||||
.multilineTextAlignment(.leading)
|
||||
|
||||
Text("""
|
||||
오픈월드 캐릭터톡은 캐릭터를 정교하게 설계하였지만, 대화가 어색하거나 불완전할 수도 있습니다.
|
||||
대화 도중 캐릭터의 대화가 이상하거나 새로운 캐릭터로 대화를 나누고 싶다면 대화를 초기화 하고 새롭게 캐릭터와 대화를 나눠보세요.
|
||||
""")
|
||||
Text(I18n.Chat.Character.detailConversationGuideDescription2)
|
||||
.appFont(size: 16, weight: .regular)
|
||||
.foregroundColor(Color(hex: "AEAEB2"))
|
||||
.multilineTextAlignment(.leading)
|
||||
@@ -393,7 +395,7 @@ extension CharacterDetailView {
|
||||
// MARK: - Chat Button
|
||||
extension CharacterDetailView {
|
||||
private var chatButton: some View {
|
||||
Text("대화하기")
|
||||
Text(I18n.Chat.Character.detailChatButton)
|
||||
.appFont(size: 18, weight: .bold)
|
||||
.foregroundColor(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -450,7 +452,7 @@ struct CharacterExpandableTextView: View {
|
||||
.foregroundColor(Color(hex: "607D8B"))
|
||||
.rotationEffect(.degrees(isExpanded ? 180 : 0))
|
||||
|
||||
Text(isExpanded ? "간략히" : "더보기")
|
||||
Text(isExpanded ? I18n.Chat.Character.detailCollapse : I18n.Chat.Character.detailExpand)
|
||||
.appFont(size: 16, weight: .regular)
|
||||
.foregroundColor(Color(hex: "607D8B"))
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ struct CharacterDetailGalleryView: View {
|
||||
VStack(spacing: 8) {
|
||||
// 상단 정보 (계산된 % 보유중, 정보 아이콘, 개수)
|
||||
HStack {
|
||||
Text("\(viewModel.ownershipPercentage)% 보유중")
|
||||
Text(I18n.Chat.Character.DetailGallery.ownership(viewModel.ownershipPercentage))
|
||||
.appFont(size: 18, weight: .bold)
|
||||
.foregroundColor(.white)
|
||||
|
||||
@@ -91,7 +91,7 @@ struct CharacterDetailGalleryView: View {
|
||||
.appFont(size: 16, weight: .regular)
|
||||
.foregroundColor(.white)
|
||||
|
||||
Text("\(viewModel.totalCount)개")
|
||||
Text(I18n.Chat.Character.DetailGallery.totalCount(viewModel.totalCount))
|
||||
.appFont(size: 16, weight: .regular)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ final class NewCharacterListViewModel: ObservableObject {
|
||||
} else {
|
||||
self?.isLoading = false
|
||||
}
|
||||
self?.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self?.errorMessage = I18n.Common.commonError
|
||||
self?.isShowPopup = true
|
||||
}
|
||||
} receiveValue: { [weak self] response in
|
||||
@@ -93,7 +93,7 @@ final class NewCharacterListViewModel: ObservableObject {
|
||||
if let message = decoded.message {
|
||||
self.errorMessage = message
|
||||
} else {
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.errorMessage = I18n.Common.commonError
|
||||
}
|
||||
self.isShowPopup = true
|
||||
if isLoadMore {
|
||||
@@ -108,7 +108,7 @@ final class NewCharacterListViewModel: ObservableObject {
|
||||
} else {
|
||||
self.isLoading = false
|
||||
}
|
||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.errorMessage = I18n.Common.commonError
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,18 +17,18 @@ struct NewCharacterListView: View {
|
||||
Group { BaseView(isLoading: $viewModel.isLoading) {
|
||||
VStack(spacing: 8) {
|
||||
// Toolbar
|
||||
DetailNavigationBar(title: String(localized: "신규 캐릭터 전체보기"))
|
||||
DetailNavigationBar(title: I18n.Chat.Character.NewList.title)
|
||||
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
// 전체 n개
|
||||
HStack(spacing: 0) {
|
||||
Text("전체")
|
||||
Text(I18n.Chat.Character.NewList.totalPrefix)
|
||||
.appFont(size: 12, weight: .regular)
|
||||
.foregroundColor(Color(hex: "e2e2e2"))
|
||||
Text(" \(viewModel.totalCount)")
|
||||
.appFont(size: 12, weight: .regular)
|
||||
.foregroundColor(Color(hex: "ff5c49"))
|
||||
Text("개")
|
||||
Text(I18n.Chat.Character.NewList.countUnit)
|
||||
.appFont(size: 12, weight: .regular)
|
||||
.foregroundColor(Color(hex: "e2e2e2"))
|
||||
Spacer()
|
||||
|
||||
@@ -14,8 +14,8 @@ struct RecentCharacterSectionView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
HStack(spacing: 0) {
|
||||
Text("최근 대화한 캐릭터 ")
|
||||
HStack(spacing: 4) {
|
||||
Text(I18n.Chat.Character.recentSectionTitle)
|
||||
.appFont(size: 20, weight: .bold)
|
||||
.foregroundColor(.white)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user