Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Delta Chat iOS Changelog

# Unrealsed

- Translate text messages using Apple's system translation UI

## v2.55.0 Testflight
2026-07

Expand Down
40 changes: 40 additions & 0 deletions deltachat-ios/Chat/ChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import MCEmojiPicker
import QuickLook
import UIKit
import SwiftUI
import Translation
import AVFoundation
import DcCore
import SDWebImage
Expand All @@ -16,6 +17,7 @@ class ChatViewController: UIViewController, UITableViewDelegate, UITableViewData
private var messages: [(id: Int, timestamp: TimeInterval)] = []
private var isVisibleToUser: Bool = false
private var reactionMessageId: Int?
private var translationPresentationHost: UIViewController?
private var contextMenuVisible = false
private var isDraggingScrollView = false
private var quoteReturnMessageIds: [Int] = []
Expand Down Expand Up @@ -2095,6 +2097,15 @@ extension ChatViewController {
UIAction.menuAction(localizationKey: copyTitle, systemImageName: "doc.on.doc", with: messageId, action: copyTextToClipboard)
)
}
if #available(iOS 17.4, *),
message.type == DC_MSG_TEXT,
!message.isMarkerOrInfo,
let text = message.text,
!text.isEmpty {
children.append(
UIAction.menuAction(localizationKey: "Translate", systemImageName: "translate", with: text, action: presentTranslation)
)
}
if message.image != nil {
moreOptions.append(
UIAction.menuAction(localizationKey: "menu_copy_image_to_clipboard", systemImageName: "photo.on.rectangle", with: messageId, action: copyImageToClipboard)
Expand Down Expand Up @@ -2315,13 +2326,42 @@ extension ChatViewController {
UIPasteboard.general.string = stringsToCopy
}

@available(iOS 17.4, *)
private func presentTranslation(text: String) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this and the TranslationPresentationView to their own file eg Translation.swift

let host = UIHostingController(rootView: TranslationPresentationView(text: text) { [weak self] in

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicitly add the onDismiss label here so it is more readable what this closure does.

self?.translationPresentationHost?.willMove(toParent: nil)
self?.translationPresentationHost?.view.removeFromSuperview()
self?.translationPresentationHost?.removeFromParent()
self?.translationPresentationHost = nil
})
translationPresentationHost = host
addChild(host)
view.addSubview(host.view)
host.view.fillSuperview()
host.view.backgroundColor = .clear
host.didMove(toParent: self)
}

func copyImagesToClipboard(ids: [Int]) {
let images = ids.map(dcContext.getMessage).compactMap(\.image)
guard !images.isEmpty else { return }
UIPasteboard.general.images = images
}
}

@available(iOS 17.4, *)
private struct TranslationPresentationView: View {
let text: String
let onDismiss: () -> Void
@State private var isPresented = true

var body: some View {
Color.clear
.translationPresentation(isPresented: $isPresented, text: text)
.onChange(of: isPresented) { if !$0 { onDismiss() } }
}
}

// MARK: - BaseMessageCellDelegate
extension ChatViewController: BaseMessageCellDelegate {

Expand Down
1 change: 1 addition & 0 deletions scripts/untranslated.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<string name="connectivity_low_power_mode">Throttled by system's \"Low Power Mode\".</string>
<string name="backup_successful">Backup successful</string>
<string name="backup_successful_explain_ios">You can find the backup in the \"Delta Chat\" folder using the \"Files\" app.\n\nMove the backup out of this folder to keep it when deleting Delta Chat.</string>
<string name="Translate">Translate</string>
<string name="location_denied">Location access denied</string>
<string name="location_denied_explain_ios">In the system settings, enable \"Privacy/Location Services\" and set \"Delta Chat/Location\" to \"Always\" and \"Precise\".</string>
</resources>