Localize media-send system messages for mesh-only and failures.

Voice note and image send paths still injected hardcoded English system lines for mesh-only blocks, prepare failures, and oversized images.
This commit is contained in:
Ayush7614 2026-08-02 01:06:11 +05:30
parent 1f59e814f9
commit e6c668c8c3
4 changed files with 783 additions and 7 deletions

View File

@ -2083,6 +2083,754 @@
"zh-Hant" : { "stringUnit" : { "state" : "needs_review", "value" : "部分媒體無法刪除。請先嘗試刪除較舊的媒體。" } }
}
},
"media.system.voice_mesh_only" : {
"comment" : "System message when the user tries to send a voice note outside a mesh chat",
"extractionState" : "manual",
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"bn" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Sprachnachrichten sind nur in Mesh-Chats verfügbar."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "Las notas de voz solo están disponibles en chats mesh."
}
},
"fa" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"fil" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Les notes vocales ne sont disponibles que dans les chats mesh."
}
},
"he" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
"value" : "वॉइस नोट केवल mesh चैट में उपलब्ध हैं।"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "Le note vocali sono disponibili solo nelle chat mesh."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "ボイスノートはメッシュチャットでのみ利用できます。"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "음성 메모는 메시 채팅에서만 사용할 수 있습니다."
}
},
"ms" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"ne" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Spraakberichten zijn alleen beschikbaar in mesh-chats."
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Notatki głosowe są dostępne tylko w czatach mesh."
}
},
"pt" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Notas de voz só estão disponíveis em chats mesh."
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Голосовые заметки доступны только в mesh-чатах."
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
"value" : "Röstanteckningar är bara tillgängliga i mesh-chattar."
}
},
"ta" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ses notları yalnızca mesh sohbetlerinde kullanılabilir."
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"ur" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"vi" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voice notes are only available in mesh chats."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "语音便笺仅在 mesh 聊天中可用。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "語音備註僅在 mesh 聊天中可用。"
}
}
}
},
"media.system.image_mesh_only" : {
"comment" : "System message when the user tries to send an image outside a mesh chat",
"extractionState" : "manual",
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"bn" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bilder sind nur in Mesh-Chats verfügbar."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "Las imágenes solo están disponibles en chats mesh."
}
},
"fa" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"fil" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Les images ne sont disponibles que dans les chats mesh."
}
},
"he" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
"value" : "छवियाँ केवल mesh चैट में उपलब्ध हैं।"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "Le immagini sono disponibili solo nelle chat mesh."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "画像はメッシュチャットでのみ送信できます。"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "이미지는 메시 채팅에서만 사용할 수 있습니다."
}
},
"ms" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"ne" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Afbeeldingen zijn alleen beschikbaar in mesh-chats."
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Obrazy są dostępne tylko w czatach mesh."
}
},
"pt" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Imagens só estão disponíveis em chats mesh."
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Изображения доступны только в mesh-чатах."
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bilder är bara tillgängliga i mesh-chattar."
}
},
"ta" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Görseller yalnızca mesh sohbetlerinde kullanılabilir."
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"ur" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"vi" : {
"stringUnit" : {
"state" : "translated",
"value" : "Images are only available in mesh chats."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "图片仅在 mesh 聊天中可用。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "圖片僅在 mesh 聊天中可用。"
}
}
}
},
"media.system.image_prepare_failed" : {
"comment" : "System message when an image cannot be prepared for sending",
"extractionState" : "manual",
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"bn" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bild konnte nicht zum Senden vorbereitet werden."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "No se pudo preparar la imagen para enviarla."
}
},
"fa" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"fil" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Échec de la préparation de l'image à l'envoi."
}
},
"he" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
"value" : "भेजने के लिए छवि तैयार नहीं की जा सकी।"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "Preparazione dell'immagine per l'invio non riuscita."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "送信用の画像の準備に失敗しました。"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "전송할 이미지를 준비하지 못했습니다."
}
},
"ms" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"ne" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Afbeelding voorbereiden voor verzending mislukt."
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Nie udało się przygotować obrazu do wysłania."
}
},
"pt" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Falha ao preparar a imagem para envio."
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Не удалось подготовить изображение к отправке."
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
"value" : "Kunde inte förbereda bilden för sändning."
}
},
"ta" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Görsel gönderim için hazırlanamadı."
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"ur" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"vi" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed to prepare image for sending."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "无法准备要发送的图片。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "無法準備要傳送的圖片。"
}
}
}
},
"media.system.image_too_large" : {
"comment" : "System message when a selected image exceeds the send size limit",
"extractionState" : "manual",
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"bn" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bild ist zu groß zum Senden."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "La imagen es demasiado grande para enviarla."
}
},
"fa" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"fil" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "L'image est trop grande pour être envoyée."
}
},
"he" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
"value" : "छवि भेजने के लिए बहुत बड़ी है।"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "L'immagine è troppo grande per essere inviata."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "画像が大きすぎて送信できません。"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "이미지가 너무 커서 보낼 수 없습니다."
}
},
"ms" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"ne" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Afbeelding is te groot om te verzenden."
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Obraz jest za duży, by go wysłać."
}
},
"pt" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "A imagem é grande demais para enviar."
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Изображение слишком большое для отправки."
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bilden är för stor för att skicka."
}
},
"ta" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Görsel gönderilemeyecek kadar büyük."
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"ur" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"vi" : {
"stringUnit" : {
"state" : "translated",
"value" : "Image is too large to send."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "图片太大,无法发送。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "圖片太大,無法傳送。"
}
}
}
},
"notification.action.wave" : {
"comment" : "Title of the notification action button that sends a friendly wave back to a nearby person",
"extractionState" : "manual",

View File

@ -551,7 +551,11 @@ final class ChatMediaTransferCoordinator {
guard context.canSendMediaInCurrentContext else {
SecureLogger.info("Voice note blocked outside mesh/private context", category: .session)
try? FileManager.default.removeItem(at: url)
context.addSystemMessage("Voice notes are only available in mesh chats.")
context.addSystemMessage(String(
localized: "media.system.voice_mesh_only",
defaultValue: "Voice notes are only available in mesh chats.",
comment: "System message when the user tries to send a voice note outside a mesh chat"
))
return
}
@ -694,7 +698,11 @@ final class ChatMediaTransferCoordinator {
guard context.canSendMediaInCurrentContext else {
SecureLogger.info("Image send blocked outside mesh/private context", category: .session)
cleanup?()
context.addSystemMessage("Images are only available in mesh chats.")
context.addSystemMessage(String(
localized: "media.system.image_mesh_only",
defaultValue: "Images are only available in mesh chats.",
comment: "System message when the user tries to send an image outside a mesh chat"
))
return
}
@ -705,7 +713,11 @@ final class ChatMediaTransferCoordinator {
try ImageUtils.validateImageSource(at: sourceURL)
} catch {
SecureLogger.error("Image send preparation failed: \(error)", category: .session)
context.addSystemMessage("Failed to prepare image for sending.")
context.addSystemMessage(String(
localized: "media.system.image_prepare_failed",
defaultValue: "Failed to prepare image for sending.",
comment: "System message when an image cannot be prepared for sending"
))
return
}
@ -764,7 +776,11 @@ final class ChatMediaTransferCoordinator {
barrier.isCurrent(generation) else {
return
}
self.context.addSystemMessage("Image is too large to send.")
self.context.addSystemMessage(String(
localized: "media.system.image_too_large",
defaultValue: "Image is too large to send.",
comment: "System message when a selected image exceeds the send size limit"
))
}
} catch {
SecureLogger.error("Image send preparation failed: \(error)", category: .session)
@ -773,7 +789,11 @@ final class ChatMediaTransferCoordinator {
barrier.isCurrent(generation) else {
return
}
self.context.addSystemMessage("Failed to prepare image for sending.")
self.context.addSystemMessage(String(
localized: "media.system.image_prepare_failed",
defaultValue: "Failed to prepare image for sending.",
comment: "System message when an image cannot be prepared for sending"
))
}
}
}

View File

@ -760,7 +760,11 @@ struct ChatMediaTransferCoordinatorContextTests {
coordinator.sendVoiceNote(at: url)
#expect(!FileManager.default.fileExists(atPath: url.path))
#expect(context.systemMessages == ["Voice notes are only available in mesh chats."])
#expect(context.systemMessages == [String(
localized: "media.system.voice_mesh_only",
defaultValue: "Voice notes are only available in mesh chats.",
comment: "System message when the user tries to send a voice note outside a mesh chat"
)])
#expect(context.privateChats.isEmpty)
#expect(context.appendedPublicMessages.isEmpty)
#expect(coordinator.transferIdToMessageIDs.isEmpty)

View File

@ -1164,7 +1164,11 @@ struct ChatViewModelMediaTransferTests {
viewModel.sendImage(from: url)
let didNotify = await TestHelpers.waitUntil({
viewModel.messages.contains(where: { $0.sender == "system" && $0.content.contains("Failed to prepare image") })
viewModel.messages.contains(where: { $0.sender == "system" && $0.content.contains(String(
localized: "media.system.image_prepare_failed",
defaultValue: "Failed to prepare image for sending.",
comment: "System message when an image cannot be prepared for sending"
)) })
}, timeout: TestConstants.longTimeout)
#expect(didNotify)
#expect(transport.sentPrivateFiles.isEmpty)