Implement a first version of Apple's Translation

The user can now choose between his instance's server, DeepL (with API
key) and Apple's Translation framework. A translation is cleared if
the translation type is changed. The strings aren't yet written, but
the translations settings view's inconsistent background is now fixed.
This commit is contained in:
Paul Schuetz 2024-05-07 01:08:20 +02:00
parent e21ec0bd1f
commit 022f2f9a49
No known key found for this signature in database
GPG key ID: 4FDE1FB89050039E
7 changed files with 73 additions and 18 deletions

View file

@ -11,8 +11,8 @@ struct TranslationSettingsView: View {
var body: some View {
Form {
deepLToggle
if preferences.alwaysUseDeepl {
translationSelector
if preferences.preferredTranslationType == .useDeepl {
Section("settings.translation.user-api-key") {
deepLPicker
SecureField("settings.translation.user-api-key", text: $apiKey)
@ -51,10 +51,12 @@ struct TranslationSettingsView: View {
}
@ViewBuilder
private var deepLToggle: some View {
private var translationSelector: some View {
@Bindable var preferences = preferences
Toggle(isOn: $preferences.alwaysUseDeepl) {
Text("settings.translation.always-deepl")
Picker("settings.translation.preferred-translation-type", selection: $preferences.preferredTranslationType) {
ForEach(TranslationType.allCases, id: \.self) { type in
Text(type.rawValue).tag(type)
}
}
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
@ -80,6 +82,9 @@ struct TranslationSettingsView: View {
} footer: {
Text("settings.translation.auto-detect-post-language-footer")
}
#if !os(visionOS)
.listRowBackground(theme.primaryBackgroundColor)
#endif
}
private func writeNewValue() {

View file

@ -59867,6 +59867,7 @@
}
},
"settings.translation.always-deepl" : {
"extractionState" : "stale",
"localizations" : {
"be" : {
"stringUnit" : {
@ -60573,6 +60574,9 @@
}
}
}
},
"settings.translation.preferred-translation-type" : {
},
"settings.translation.user-api-key" : {
"localizations" : {

View file

@ -24,7 +24,7 @@ public enum DeepLUserAPIHandler {
}
public static func readIfAllowed() -> String? {
guard UserPreferences.shared.alwaysUseDeepl else { return nil }
guard UserPreferences.shared.preferredTranslationType == .useDeepl else { return nil }
return readValue()
}
@ -35,7 +35,11 @@ public enum DeepLUserAPIHandler {
}
public static func deactivateToggleIfNoKey() {
UserPreferences.shared.alwaysUseDeepl = shouldAlwaysUseDeepl
if UserPreferences.shared.preferredTranslationType == .useDeepl {
if readValue() == nil {
UserPreferences.shared.preferredTranslationType = .useServerIfPossible
}
}
}
public static var shouldAlwaysUseDeepl: Bool {

View file

@ -0,0 +1,5 @@
public enum TranslationType: String, CaseIterable {
case useServerIfPossible
case useDeepl
case useApple
}

View file

@ -185,11 +185,7 @@ import SwiftUI
}
}
public var alwaysUseDeepl: Bool {
didSet {
storage.alwaysUseDeepl = alwaysUseDeepl
}
}
public var preferredTranslationType: TranslationType
public var userDeeplAPIFree: Bool {
didSet {
@ -482,7 +478,7 @@ import SwiftUI
appDefaultPostsSensitive = storage.appDefaultPostsSensitive
appRequireAltText = storage.appRequireAltText
autoPlayVideo = storage.autoPlayVideo
alwaysUseDeepl = storage.alwaysUseDeepl
preferredTranslationType = .useServerIfPossible
userDeeplAPIFree = storage.userDeeplAPIFree
autoDetectPostLanguage = storage.autoDetectPostLanguage
inAppBrowserReaderView = storage.inAppBrowserReaderView

View file

@ -31,6 +31,15 @@ import SwiftUI
var translation: Translation?
var isLoadingTranslation: Bool = false
var showDeleteAlert: Bool = false
var showAppleTranslation = false
var preferredTranslationType = TranslationType.useServerIfPossible {
didSet {
if oldValue != preferredTranslationType {
translation = nil
showAppleTranslation = false
}
}
}
private(set) var actionsAccountsFetched: Bool = false
var favoriters: [Account] = []
@ -297,10 +306,16 @@ import SwiftUI
}
func translate(userLang: String) async {
updatePreferredTranslation()
if preferredTranslationType == .useApple {
showAppleTranslation = true
return
}
withAnimation {
isLoadingTranslation = true
}
if !alwaysTranslateWithDeepl {
if preferredTranslationType != .useDeepl {
do {
// We first use instance translation API if available.
let translation: Translation = try await client.post(endpoint: Statuses.translate(id: finalStatus.id,
@ -342,8 +357,14 @@ import SwiftUI
DeepLUserAPIHandler.readIfAllowed()
}
var alwaysTranslateWithDeepl: Bool {
DeepLUserAPIHandler.shouldAlwaysUseDeepl
func updatePreferredTranslation() {
if DeepLUserAPIHandler.shouldAlwaysUseDeepl {
preferredTranslationType = .useDeepl
} else if UserPreferences.shared.preferredTranslationType == .useApple {
preferredTranslationType = .useApple
} else {
preferredTranslationType = .useServerIfPossible
}
}
func fetchRemoteStatus() async -> Bool {

View file

@ -2,6 +2,7 @@ import DesignSystem
import Env
import Models
import SwiftUI
import Translation
@MainActor
struct StatusRowTranslateView: View {
@ -35,7 +36,8 @@ struct StatusRowTranslateView: View {
}
}
var body: some View {
@ViewBuilder
var translateButton: some View {
if !isInCaptureMode,
!isCompact,
let userLang = preferences.serverPreferences?.postLanguage,
@ -54,8 +56,26 @@ struct StatusRowTranslateView: View {
}
.buttonStyle(.borderless)
}
}
if let translation = viewModel.translation, !viewModel.isLoadingTranslation {
@ViewBuilder
var generalTranslateButton: some View {
if #available(iOS 17.4, *) {
@Bindable var viewModel = viewModel
translateButton
.translationPresentation(isPresented: $viewModel.showAppleTranslation, text: viewModel.finalStatus.content.asRawText)
} else {
translateButton
}
}
var body: some View {
generalTranslateButton
.onChange(of: preferences.preferredTranslationType) { _, _ in
_ = viewModel.updatePreferredTranslation()
}
if let translation = viewModel.translation, !viewModel.isLoadingTranslation, preferences.preferredTranslationType != .useApple {
GroupBox {
VStack(alignment: .leading, spacing: 4) {
Text(translation.content.asSafeMarkdownAttributedString)