feat(i18n): 음성 메시지 하드코딩 문구를 I18n 키로 통일한다
This commit is contained in:
@@ -2400,6 +2400,72 @@ If you block this user, the following features will be restricted.
|
||||
}
|
||||
|
||||
enum Voice {
|
||||
enum SavePopup {
|
||||
static var title: String {
|
||||
pick(ko: "메시지 보관", en: "Archive message", ja: "メッセージを保管")
|
||||
}
|
||||
|
||||
static func description(_ canCount: Int) -> String {
|
||||
pick(
|
||||
ko: "메시지를 보관하는데\n\(canCount)캔이 필요합니다.\n메시지를 보관하시겠습니까?",
|
||||
en: "Archiving this message requires\n\(canCount) cans.\nDo you want to archive this message?",
|
||||
ja: "メッセージを保管するには\n\(canCount)can が必要です。\nこのメッセージを保管しますか?"
|
||||
)
|
||||
}
|
||||
|
||||
static var notice: String {
|
||||
pick(
|
||||
ko: "※ 메시지 보관시, 본인이 삭제하기 전까지 영구보관됩니다.",
|
||||
en: "※ Archived messages are kept permanently until you delete them.",
|
||||
ja: "※ 保管したメッセージは、ご自身で削除するまで永久に保管されます。"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum Write {
|
||||
static var title: String {
|
||||
pick(ko: "음성메시지", en: "Voice message", ja: "音声メッセージ")
|
||||
}
|
||||
|
||||
static var recipientLabel: String {
|
||||
pick(ko: "TO.", en: "TO.", ja: "宛先")
|
||||
}
|
||||
|
||||
static var sendButton: String {
|
||||
pick(ko: "메시지 보내기", en: "Send message", 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: "メッセージの送信が完了しました。")
|
||||
}
|
||||
|
||||
static var sendFailed: String {
|
||||
pick(
|
||||
ko: "음성메시지를 전송하지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.",
|
||||
en: "Could not send the voice message.\nPlease try again.\nIf the issue persists, contact customer support.",
|
||||
ja: "音声メッセージを送信できませんでした。\nもう一度お試しください。\n問題が続く場合はカスタマーサポートにお問い合わせください。"
|
||||
)
|
||||
}
|
||||
|
||||
static var reRecord: String {
|
||||
pick(ko: "다시 녹음", en: "Record again", ja: "再録音")
|
||||
}
|
||||
}
|
||||
|
||||
enum Toast {
|
||||
static var saveFailed: String {
|
||||
pick(
|
||||
ko: "메시지를 저장하지 못했습니다\n잠시 후 다시 시도해 주세요.",
|
||||
en: "Could not save the message.\nPlease try again later.",
|
||||
ja: "メッセージを保存できませんでした。\nしばらくしてからもう一度お試しください。"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum Sound {
|
||||
static var permissionDenied: String {
|
||||
pick(
|
||||
|
||||
@@ -37,7 +37,7 @@ struct VoiceMessageView: View {
|
||||
viewModel.selectedMessageId = item.messageId
|
||||
soundManager.stopAudio()
|
||||
if item.isKept {
|
||||
viewModel.errorMessage = "이미 보관된 메시지 입니다"
|
||||
viewModel.errorMessage = I18n.Message.Text.Detail.alreadyKept
|
||||
viewModel.isShowPopup = true
|
||||
return
|
||||
} else {
|
||||
@@ -76,7 +76,7 @@ struct VoiceMessageView: View {
|
||||
.resizable()
|
||||
.frame(width: 60, height: 60)
|
||||
|
||||
Text("메시지가 없습니다.\n친구들과 소통해보세요!")
|
||||
Text(I18n.Message.Text.emptyState)
|
||||
.multilineTextAlignment(.center)
|
||||
.appFont(size: 10.7, weight: .medium)
|
||||
.foregroundColor(Color(hex: "bbbbbb"))
|
||||
@@ -114,18 +114,18 @@ struct VoiceMessageView: View {
|
||||
.ignoresSafeArea()
|
||||
|
||||
VStack(spacing: 0) {
|
||||
Text("메시지 보관")
|
||||
Text(I18n.Message.Voice.SavePopup.title)
|
||||
.appFont(size: 18.3, weight: .bold)
|
||||
.foregroundColor(Color(hex: "bbbbbb"))
|
||||
.padding(.top, 40)
|
||||
|
||||
Text("메시지를 보관하는데\n\(viewModel.saveMessagePrice)캔이 필요합니다.\n메시지를 보관하시겠습니까?")
|
||||
Text(I18n.Message.Voice.SavePopup.description(viewModel.saveMessagePrice))
|
||||
.appFont(size: 15, weight: .medium)
|
||||
.foregroundColor(Color(hex: "bbbbbb"))
|
||||
.padding(.top, 13.3)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
Text("※ 메시지 보관시, 본인이 삭제하기 전까지 영구보관됩니다.")
|
||||
Text(I18n.Message.Voice.SavePopup.notice)
|
||||
.appFont(size: 12, weight: .medium)
|
||||
.foregroundColor(Color(hex: "bbbbbb"))
|
||||
.padding(.top, 13.3)
|
||||
@@ -133,7 +133,7 @@ struct VoiceMessageView: View {
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
HStack(spacing: 13.3) {
|
||||
Text("취소")
|
||||
Text(I18n.Common.cancel)
|
||||
.appFont(size: 18.3, weight: .bold)
|
||||
.foregroundColor(Color(hex: "3bb9f1"))
|
||||
.padding(.vertical, 16)
|
||||
@@ -151,7 +151,7 @@ struct VoiceMessageView: View {
|
||||
viewModel.isShowSavePopup = false
|
||||
}
|
||||
|
||||
Text("확인")
|
||||
Text(I18n.Common.confirm)
|
||||
.appFont(size: 18.3, weight: .bold)
|
||||
.foregroundColor(.white)
|
||||
.padding(.vertical, 16)
|
||||
|
||||
@@ -32,7 +32,7 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
@Published var recipientNickname: String = ""
|
||||
@Published var recipientId = 0
|
||||
|
||||
@Published var sendText = "메시지 보내기"
|
||||
@Published var sendText = I18n.Message.Voice.Write.sendButton
|
||||
|
||||
@Published var selectedMessageId = -1
|
||||
@Published var openPlayerItemIndex = -1
|
||||
@@ -73,7 +73,7 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
|
||||
func deleteMessage() {
|
||||
if selectedMessageId <= 0 {
|
||||
errorMessage = "메시지를 삭제하지 못했습니다\n잠시 후 다시 시도해 주세요."
|
||||
errorMessage = I18n.Message.Text.Detail.deleteFailed
|
||||
isShowPopup = true
|
||||
return
|
||||
}
|
||||
@@ -97,7 +97,7 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
|
||||
|
||||
if decoded.success {
|
||||
self.errorMessage = "삭제되었습니다."
|
||||
self.errorMessage = I18n.Message.Text.Detail.deleteSuccess
|
||||
self.isShowPopup = true
|
||||
self.refresh()
|
||||
} else {
|
||||
@@ -105,13 +105,13 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
self.errorMessage = message
|
||||
self.isShowPopup = true
|
||||
} else {
|
||||
self.errorMessage = "메시지를 보관하지 못했습니다.\n잠시 후 다시 시도해 주세요."
|
||||
self.errorMessage = I18n.Message.Text.Detail.keepFailed
|
||||
}
|
||||
|
||||
self.isShowPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "메시지를 보관하지 못했습니다.\n잠시 후 다시 시도해 주세요."
|
||||
self.errorMessage = I18n.Message.Text.Detail.keepFailed
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
|
||||
func keepVoiceMessage() {
|
||||
if selectedMessageId <= 0 {
|
||||
errorMessage = "메시지를 저장하지 못했습니다\n잠시 후 다시 시도해 주세요."
|
||||
errorMessage = I18n.Message.Voice.Toast.saveFailed
|
||||
isShowPopup = true
|
||||
return
|
||||
}
|
||||
@@ -143,7 +143,7 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
|
||||
|
||||
if decoded.success {
|
||||
self.errorMessage = "보관되었습니다."
|
||||
self.errorMessage = I18n.Message.Text.Detail.keepSuccess
|
||||
self.isShowPopup = true
|
||||
self.refresh()
|
||||
} else {
|
||||
@@ -151,13 +151,13 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
self.errorMessage = message
|
||||
self.isShowPopup = true
|
||||
} else {
|
||||
self.errorMessage = "메시지를 보관하지 못했습니다.\n잠시 후 다시 시도해 주세요."
|
||||
self.errorMessage = I18n.Message.Text.Detail.keepFailed
|
||||
}
|
||||
|
||||
self.isShowPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "메시지를 보관하지 못했습니다.\n잠시 후 다시 시도해 주세요."
|
||||
self.errorMessage = I18n.Message.Text.Detail.keepFailed
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
@@ -166,7 +166,7 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
|
||||
func keepTextMessage() {
|
||||
if selectedMessageId <= 0 {
|
||||
errorMessage = "메시지를 저장하지 못했습니다\n잠시 후 다시 시도해 주세요."
|
||||
errorMessage = I18n.Message.Voice.Toast.saveFailed
|
||||
isShowPopup = true
|
||||
return
|
||||
}
|
||||
@@ -189,7 +189,7 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
|
||||
|
||||
if decoded.success {
|
||||
self.errorMessage = "보관되었습니다."
|
||||
self.errorMessage = I18n.Message.Text.Detail.keepSuccess
|
||||
self.isShowPopup = true
|
||||
self.refresh()
|
||||
} else {
|
||||
@@ -197,13 +197,13 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
self.errorMessage = message
|
||||
self.isShowPopup = true
|
||||
} else {
|
||||
self.errorMessage = "메시지를 보관하지 못했습니다.\n잠시 후 다시 시도해 주세요."
|
||||
self.errorMessage = I18n.Message.Text.Detail.keepFailed
|
||||
}
|
||||
|
||||
self.isShowPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "메시지를 보관하지 못했습니다.\n잠시 후 다시 시도해 주세요."
|
||||
self.errorMessage = I18n.Message.Text.Detail.keepFailed
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,7 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
|
||||
func write(soundData: Data, onSuccess: @escaping () -> Void) {
|
||||
if recipientId <= 0 {
|
||||
errorMessage = "받는 사람을 선택해 주세요."
|
||||
errorMessage = I18n.Message.Voice.Write.selectRecipient
|
||||
isShowPopup = true
|
||||
return
|
||||
}
|
||||
@@ -254,7 +254,7 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
|
||||
if decoded.success {
|
||||
onSuccess()
|
||||
self.errorMessage = "메시지 전송이 완료되었습니다."
|
||||
self.errorMessage = I18n.Message.Voice.Write.sendSuccess
|
||||
self.isShowPopup = true
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
AppState.shared.back()
|
||||
@@ -263,19 +263,19 @@ final class VoiceMessageViewModel: ObservableObject {
|
||||
if let message = decoded.message {
|
||||
self.errorMessage = message
|
||||
} else {
|
||||
self.errorMessage = "음성메시지를 전송하지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.errorMessage = I18n.Message.Voice.Write.sendFailed
|
||||
}
|
||||
|
||||
self.isShowPopup = true
|
||||
}
|
||||
} catch {
|
||||
self.errorMessage = "음성메시지를 전송하지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.errorMessage = I18n.Message.Voice.Write.sendFailed
|
||||
self.isShowPopup = true
|
||||
}
|
||||
}
|
||||
.store(in: &subscription)
|
||||
} else {
|
||||
self.errorMessage = "음성메시지를 전송하지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
self.errorMessage = I18n.Message.Voice.Write.sendFailed
|
||||
self.isShowPopup = true
|
||||
self.isLoading = false
|
||||
}
|
||||
@@ -317,13 +317,13 @@ final class VoiceMessageViewModel: 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
|
||||
}
|
||||
}
|
||||
@@ -365,13 +365,13 @@ final class VoiceMessageViewModel: 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
|
||||
}
|
||||
}
|
||||
@@ -413,13 +413,13 @@ final class VoiceMessageViewModel: 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ struct VoiceMessageWriteView: View {
|
||||
Spacer()
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
Text("음성메시지")
|
||||
Text(I18n.Message.Voice.Write.title)
|
||||
.appFont(size: 18.3, weight: .bold)
|
||||
.foregroundColor(.white)
|
||||
|
||||
@@ -57,14 +57,14 @@ struct VoiceMessageWriteView: View {
|
||||
.frame(width: 46.7, height: 46.7)
|
||||
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Text("TO.")
|
||||
Text(I18n.Message.Voice.Write.recipientLabel)
|
||||
.appFont(size: 13.3, weight: .medium)
|
||||
.foregroundColor(Color(hex: "eeeeee"))
|
||||
|
||||
Text(
|
||||
viewModel.recipientNickname.count > 0 ?
|
||||
viewModel.recipientNickname :
|
||||
I18n.TextMessage.recipientPlaceholder
|
||||
I18n.Message.Text.Write.recipientLabel
|
||||
)
|
||||
.appFont(size: 16.7, weight: viewModel.recipientNickname.count > 0 ? .bold : .light)
|
||||
.foregroundColor(
|
||||
@@ -111,7 +111,7 @@ struct VoiceMessageWriteView: View {
|
||||
.padding(.vertical, 52.3)
|
||||
.onTapGesture {
|
||||
if viewModel.recipientId <= 0 {
|
||||
viewModel.errorMessage = "받는 사람을 선택해 주세요."
|
||||
viewModel.errorMessage = I18n.Message.Voice.Write.selectRecipient
|
||||
viewModel.isShowPopup = true
|
||||
} else {
|
||||
progress = 0
|
||||
@@ -129,7 +129,7 @@ struct VoiceMessageWriteView: View {
|
||||
HStack(spacing: 0) {
|
||||
Spacer()
|
||||
|
||||
Text("삭제")
|
||||
Text(I18n.Common.delete)
|
||||
.appFont(size: 15.3, weight: .medium)
|
||||
.foregroundColor(Color(hex: "bbbbbb").opacity(0))
|
||||
|
||||
@@ -151,7 +151,7 @@ struct VoiceMessageWriteView: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("삭제")
|
||||
Text(I18n.Common.delete)
|
||||
.appFont(size: 15.3, weight: .medium)
|
||||
.foregroundColor(Color(hex: "bbbbbb"))
|
||||
.onTapGesture {
|
||||
@@ -165,7 +165,7 @@ struct VoiceMessageWriteView: View {
|
||||
.padding(.top, 90)
|
||||
|
||||
HStack(spacing: 13.3) {
|
||||
Text("다시 녹음")
|
||||
Text(I18n.Message.Voice.Write.reRecord)
|
||||
.appFont(size: 18.3, weight: .bold)
|
||||
.foregroundColor(Color(hex: "9970ff"))
|
||||
.frame(width: (proxy.size.width - 40) / 3, height: 50)
|
||||
@@ -195,7 +195,7 @@ struct VoiceMessageWriteView: View {
|
||||
onRefresh()
|
||||
}
|
||||
} catch {
|
||||
viewModel.errorMessage = "음성메시지를 전송하지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||
viewModel.errorMessage = I18n.Message.Voice.Write.sendFailed
|
||||
viewModel.isShowPopup = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,9 +405,9 @@
|
||||
- [x] `SodaLive/Sources/Message/Voice/VoiceMessageItemView.swift`
|
||||
|
||||
#### Group 2 (11-13)
|
||||
- [ ] `SodaLive/Sources/Message/Voice/VoiceMessageView.swift`
|
||||
- [ ] `SodaLive/Sources/Message/Voice/VoiceMessageViewModel.swift`
|
||||
- [ ] `SodaLive/Sources/Message/Voice/Write/VoiceMessageWriteView.swift`
|
||||
- [x] `SodaLive/Sources/Message/Voice/VoiceMessageView.swift`
|
||||
- [x] `SodaLive/Sources/Message/Voice/VoiceMessageViewModel.swift`
|
||||
- [x] `SodaLive/Sources/Message/Voice/Write/VoiceMessageWriteView.swift`
|
||||
|
||||
### MyPage (41)
|
||||
#### Group 1 (1-10)
|
||||
@@ -775,3 +775,30 @@
|
||||
- 빌드 검증: `SodaLive`, `SodaLive-dev` Debug 빌드 모두 성공(`** BUILD SUCCEEDED **`).
|
||||
- 테스트 검증: 두 스킴 모두 `Scheme ... is not currently configured for the test action.`로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약).
|
||||
- LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(`Kingfisher`, `MessageRepository` 등)가 보고되나, 동일 변경셋은 `xcodebuild` 실컴파일 통과로 검증 완료.
|
||||
|
||||
### 14차 구현 (Message 모듈 Group 2, 3개 파일 처리, 2026-03-31)
|
||||
- 무엇/왜/어떻게:
|
||||
- 무엇: `변경 대상 파일 전체 목록`의 `Message` Group 2(3개 파일)에서 사용자 노출 하드코딩 문구를 `I18n.*` 참조로 전환하고 체크박스를 완료 처리.
|
||||
- 왜: Voice 메시지 목록/보관 팝업/작성 화면/ViewModel 토스트 문구가 하드코딩 상태라 Message 모듈의 i18n 접근이 Group 1과 불일치했기 때문.
|
||||
- 어떻게: explore/librarian 병렬 탐색 + `grep`/`ast_grep_search`/`rg` 직접 점검으로 대상 문자열을 확정하고, `I18n.swift`의 `I18n.Message.Voice` 네임스페이스를 확장한 뒤 호출부를 치환.
|
||||
- 실행 명령/도구:
|
||||
- `task(subagent_type="explore", ...)` x2 (`bg_4384335d`, `bg_fe76ec47`)
|
||||
- `task(subagent_type="librarian", ...)` x2 (`bg_da2d810f`, `bg_2416c47e`)
|
||||
- `grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/Message/Voice)`
|
||||
- `grep("I18n\\.Message\\.", include=*.swift, path=SodaLive/Sources/Message)`
|
||||
- `ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Message/Voice])`
|
||||
- `bash: rg -n ...` (`command not found` 확인)
|
||||
- `lsp_diagnostics(filePath=변경 파일 4개)`
|
||||
- `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.Message.Voice.SavePopup`, `I18n.Message.Voice.Write`, `I18n.Message.Voice.Toast` 키셋 추가.
|
||||
- 치환 완료 파일: `VoiceMessageView.swift`, `VoiceMessageViewModel.swift`, `VoiceMessageWriteView.swift`.
|
||||
- Voice 보관 팝업(제목/본문/안내/버튼), 작성 화면(타이틀/수신자 라벨/다시 녹음/삭제), ViewModel 토스트/성공·실패 문구를 `I18n.*` 참조로 교체.
|
||||
- 대상 3개 파일 재탐지 결과 한글 하드코딩 리터럴 0건 확인.
|
||||
- `Message` Group 2 체크박스 3개 `- [x]` 완료 반영.
|
||||
- 빌드 검증: `SodaLive`, `SodaLive-dev` Debug 빌드 모두 성공(`** BUILD SUCCEEDED **`).
|
||||
- 테스트 검증: 두 스킴 모두 `Scheme ... is not currently configured for the test action.`로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약).
|
||||
- LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(`Moya`, `I18n`, `LoadingView` 등)가 보고되나, 동일 변경셋은 `xcodebuild` 실컴파일 통과로 검증 완료.
|
||||
|
||||
Reference in New Issue
Block a user