diff --git a/Localization/app.json b/Localization/app.json index f884d6004..1292d54d4 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -183,8 +183,9 @@ "direct": "Only mentioned user can see this post." }, "translation": { - "translated_from": "Translated from %s", + "translated_from": "Translated from %s using %s", "unknown_language": "Unknown", + "unknown_provider": "Unknown", "show_original": "Shown Original" } }, diff --git a/Mastodon/Diffable/Status/StatusSection.swift b/Mastodon/Diffable/Status/StatusSection.swift index 8ccb32c0c..9b45bae62 100644 --- a/Mastodon/Diffable/Status/StatusSection.swift +++ b/Mastodon/Diffable/Status/StatusSection.swift @@ -279,6 +279,7 @@ extension StatusSection { statusView: cell.statusView ) + cell.statusView.viewModel.context = configuration.context cell.statusView.viewModel.authContext = configuration.authContext cell.configure( diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift index cd4cec4b0..8ce9c2447 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift @@ -39,7 +39,7 @@ extension DataSourceFacade { } private extension DataSourceFacade { - static func translateStatus(provider: Provider, status: Status) async throws -> String? { + static func translateStatus(provider: Provider, status: Status) async throws -> Status.TranslatedContent? { do { let value = try await provider.context .apiService @@ -52,7 +52,7 @@ private extension DataSourceFacade { throw TranslationFailure.emptyOrInvalidResponse } - return content + return Status.TranslatedContent(content: content, provider: value.provider) } catch { throw TranslationFailure.emptyOrInvalidResponse } diff --git a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 7.xcdatamodel/contents b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 7.xcdatamodel/contents index 8fb3edc73..7198c75c6 100644 --- a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 7.xcdatamodel/contents +++ b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 7.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -218,7 +218,7 @@ - + diff --git a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift index c13b6febb..08f58ca3f 100644 --- a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift +++ b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift @@ -11,6 +11,16 @@ import Foundation public final class Status: NSManagedObject { public typealias ID = String + public class TranslatedContent: NSObject { + public let content: String + public let provider: String? + + public init(content: String, provider: String?) { + self.content = content + self.provider = provider + } + } + // sourcery: autoGenerateProperty @NSManaged public private(set) var identifier: ID // sourcery: autoGenerateProperty @@ -103,7 +113,7 @@ public final class Status: NSManagedObject { @NSManaged public private(set) var revealedAt: Date? // sourcery: autoUpdatableObject - @NSManaged public private(set) var translatedContent: String? + @NSManaged public private(set) var translatedContent: TranslatedContent? } extension Status { @@ -504,7 +514,7 @@ extension Status: AutoUpdatableObject { self.revealedAt = revealedAt } } - public func update(translatedContent: String?) { + public func update(translatedContent: TranslatedContent?) { if self.translatedContent != translatedContent { self.translatedContent = translatedContent } diff --git a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift index 40d847460..49bf2d7a1 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift +++ b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift @@ -381,12 +381,14 @@ public enum L10n { public enum Translation { /// Show Original public static let showOriginal = L10n.tr("Localizable", "Common.Controls.Status.Translation.ShowOriginal", fallback: "Show Original") - /// Translated from %@ - public static func translatedFrom(_ p1: Any) -> String { - return L10n.tr("Localizable", "Common.Controls.Status.Translation.TranslatedFrom", String(describing: p1), fallback: "Translated from %@") + /// Translated from %@ using %@ + public static func translatedFrom(_ p1: Any, _ p2: Any) -> String { + return L10n.tr("Localizable", "Common.Controls.Status.Translation.TranslatedFrom", String(describing: p1), String(describing: p2), fallback: "Translated from %@ using %@") } /// Unknown public static let unknownLanguage = L10n.tr("Localizable", "Common.Controls.Status.Translation.UnknownLanguage", fallback: "Unknown") + /// Unknown + public static let unknownProvider = L10n.tr("Localizable", "Common.Controls.Status.Translation.UnknownProvider", fallback: "Unknown") } public enum Visibility { /// Only mentioned user can see this post. diff --git a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings index a7c26c86e..95534fb6c 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings +++ b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings @@ -134,8 +134,9 @@ Please check your internet connection."; "Common.Controls.Status.Tag.Url" = "URL"; "Common.Controls.Status.TapToReveal" = "Tap to reveal"; "Common.Controls.Status.Translation.ShowOriginal" = "Show Original"; -"Common.Controls.Status.Translation.TranslatedFrom" = "Translated from %@"; +"Common.Controls.Status.Translation.TranslatedFrom" = "Translated from %@ using %@"; "Common.Controls.Status.Translation.UnknownLanguage" = "Unknown"; +"Common.Controls.Status.Translation.UnknownProvider" = "Unknown"; "Common.Controls.Status.UserReblogged" = "%@ reblogged"; "Common.Controls.Status.UserRepliedTo" = "Replied to %@"; "Common.Controls.Status.Visibility.Direct" = "Only mentioned user can see this post."; diff --git a/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings b/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings index 9afcd60d9..ea106ffd2 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings +++ b/MastodonSDK/Sources/MastodonLocalization/Resources/en.lproj/Localizable.strings @@ -62,8 +62,9 @@ Please check your internet connection."; "Common.Controls.Actions.SignUp" = "Create account"; "Common.Controls.Actions.Skip" = "Skip"; "Common.Controls.Actions.TakePhoto" = "Take Photo"; -"Common.Controls.Actions.TranslatePost.Title" = "Translate from %@"; +"Common.Controls.Actions.TranslatePost.Title" = "Translate from %@ using %@"; "Common.Controls.Actions.TranslatePost.UnknownLanguage" = "Unknown"; +"Common.Controls.Actions.TranslatePost.UnknownProvider" = "Unknown"; "Common.Controls.Actions.TryAgain" = "Try Again"; "Common.Controls.Actions.UnblockDomain" = "Unblock %@"; "Common.Controls.Friendship.Block" = "Block"; @@ -130,8 +131,9 @@ Please check your internet connection."; "Common.Controls.Status.Tag.Url" = "URL"; "Common.Controls.Status.TapToReveal" = "Tap to reveal"; "Common.Controls.Status.Translation.ShowOriginal" = "Show Original"; -"Common.Controls.Status.Translation.TranslatedFrom" = "Translated from %@"; +"Common.Controls.Status.Translation.TranslatedFrom" = "Translated from %@ using %@"; "Common.Controls.Status.Translation.UnknownLanguage" = "Unknown"; +"Common.Controls.Status.Translation.UnknownProvider" = "Unknown"; "Common.Controls.Status.UserReblogged" = "%@ reblogged"; "Common.Controls.Status.UserRepliedTo" = "Replied to %@"; "Common.Controls.Status.Visibility.Direct" = "Only mentioned user can see this post."; diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift index 42b6f406d..6e950bc95 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+Configuration.swift @@ -246,13 +246,14 @@ extension StatusView { func revertTranslation() { guard let originalStatus = viewModel.originalStatus else { return } viewModel.translatedFromLanguage = nil + viewModel.translatedUsingProvider = nil originalStatus.reblog?.update(translatedContent: nil) originalStatus.update(translatedContent: nil) configure(status: originalStatus) } func configureTranslated(status: Status) { - let translatedContent: String? = { + let translatedContent: Status.TranslatedContent? = { if let translatedContent = status.reblog?.translatedContent { return translatedContent } @@ -269,10 +270,11 @@ extension StatusView { // content do { - let content = MastodonContent(content: translatedContent, emojis: status.emojis.asDictionary) + let content = MastodonContent(content: translatedContent.content, emojis: status.emojis.asDictionary) let metaContent = try MastodonMetaContent.convert(document: content) viewModel.content = metaContent viewModel.translatedFromLanguage = status.reblog?.language ?? status.language + viewModel.translatedUsingProvider = status.reblog?.translatedContent?.provider ?? status.translatedContent?.provider viewModel.isCurrentlyTranslating = false } catch { assertionFailure(error.localizedDescription) diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift index 5ce4d6ec4..aae8e0b66 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView+ViewModel.swift @@ -49,7 +49,8 @@ extension StatusView { // Translation @Published public var isCurrentlyTranslating = false @Published public var translatedFromLanguage: String? - + @Published public var translatedUsingProvider: String? + @Published public var timestamp: Date? public var timestampFormatter: ((_ date: Date) -> String)? @Published public var timestampText = "" @@ -145,6 +146,7 @@ extension StatusView { isMediaSensitive = false isSensitiveToggled = false translatedFromLanguage = nil + translatedUsingProvider = nil isCurrentlyTranslating = false activeFilters = [] @@ -629,7 +631,9 @@ extension StatusView.ViewModel { guard let context = self.context, let authContext = self.authContext - else { return nil } + else { + return nil + } var configuration: Mastodon.Entity.V2.Instance.Configuration? = nil context.managedObjectContext.performAndWait { diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift index e73d00342..f64b0b225 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/StatusView.swift @@ -191,6 +191,7 @@ public final class StatusView: UIView { let label = UILabel() label.font = UIFontMetrics(forTextStyle: .footnote).scaledFont(for: .systemFont(ofSize: 13, weight: .regular)) label.textColor = Asset.Colors.Label.secondary.color + label.numberOfLines = 0 return label }() lazy var translatedInfoView: UIView = { @@ -212,10 +213,14 @@ public final class StatusView: UIView { containerView.addSubview($0) } + translatedInfoLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + revertButton.setContentHuggingPriority(.required, for: .horizontal) + NSLayoutConstraint.activate([ containerView.heightAnchor.constraint(equalToConstant: 24), translatedInfoLabel.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), translatedInfoLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16), + translatedInfoLabel.trailingAnchor.constraint(equalTo: revertButton.leadingAnchor, constant: -16), revertButton.topAnchor.constraint(equalTo: containerView.topAnchor), revertButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16), revertButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor) @@ -735,12 +740,18 @@ extension StatusView { } .store(in: &disposeBag) - viewModel.$translatedFromLanguage + Publishers.CombineLatest( + viewModel.$translatedFromLanguage, + viewModel.$translatedUsingProvider + ) .receive(on: DispatchQueue.main) - .sink { [weak self] translatedFromLanguage in + .sink { [weak self] translatedFromLanguage, translatedUsingProvider in guard let self = self else { return } if let translatedFromLanguage = translatedFromLanguage { - self.translatedInfoLabel.text = L10n.Common.Controls.Status.Translation.translatedFrom(Locale.current.localizedString(forIdentifier: translatedFromLanguage) ?? L10n.Common.Controls.Status.Translation.unknownLanguage) + self.translatedInfoLabel.text = L10n.Common.Controls.Status.Translation.translatedFrom( + Locale.current.localizedString(forIdentifier: translatedFromLanguage) ?? L10n.Common.Controls.Status.Translation.unknownLanguage, + translatedUsingProvider ?? L10n.Common.Controls.Status.Translation.unknownProvider + ) self.translatedInfoView.isHidden = false } else { self.translatedInfoView.isHidden = true