Allow translation of an account bio/note (#1276)

The bio (note) of an account can now be translated via DeepL. If the user has
put in his own DeepL API key, that is used, otherwise, the standard one is
used. See #1267

Signed-off-by: Paul Schuetz <pa.schuetz@web.de>
This commit is contained in:
Paul Schuetz 2023-03-19 16:18:13 +01:00 committed by GitHub
parent a08587643d
commit da0b92e13d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 59 additions and 5 deletions

View file

@ -150,6 +150,17 @@ public struct AccountDetailContextMenu: View {
Divider()
}
if let lang = preferences.serverPreferences?.postLanguage ?? Locale.current.language.languageCode?.identifier
{
Button {
Task {
await viewModel.translate(userLang: lang)
}
} label: {
Label("status.action.translate", systemImage: "captions.bubble")
}
}
if viewModel.relationship?.following == true {
Button {
routerPath.presentedSheet = .listAddAccount(account: account)

View file

@ -202,12 +202,34 @@ struct AccountDetailHeaderView: View {
routerPath.handle(url: url)
})
if let translation = viewModel.translation, !viewModel.isLoadingTranslation {
GroupBox {
VStack(alignment: .leading, spacing: 4) {
Text(translation.content.asSafeMarkdownAttributedString)
.font(.scaledBody)
Text(getLocalizedStringLabel(langCode: translation.detectedSourceLanguage, provider: translation.provider))
.font(.footnote)
.foregroundColor(.gray)
}
}
.fixedSize(horizontal: false, vertical: true)
}
fieldsView
}
.padding(.horizontal, .layoutPadding)
.offset(y: -40)
}
private func getLocalizedStringLabel(langCode: String, provider: String) -> String {
if let localizedLanguage = Locale.current.localizedString(forLanguageCode: langCode) {
let format = NSLocalizedString("status.action.translated-label-from-%@-%@", comment: "")
return String.localizedStringWithFormat(format, localizedLanguage, provider)
} else {
return "status.action.translated-label-\(provider)"
}
}
private func makeCustomInfoLabel(title: LocalizedStringKey, count: Int, needsBadge: Bool = false) -> some View {
VStack {
Text(count, format: .number.notation(.compactName))

View file

@ -83,6 +83,9 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
}
}
@Published var translation: Translation?
@Published var isLoadingTranslation = false
private(set) var account: Account?
private var tabTask: Task<Void, Never>?
@ -263,4 +266,22 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
func statusDidAppear(status _: Models.Status) {}
func statusDidDisappear(status _: Status) {}
func translate(userLang: String) async {
guard let account else { return }
withAnimation {
isLoadingTranslation = true
}
let userAPIKey = DeepLUserAPIHandler.readIfAllowed()
let userAPIFree = UserPreferences.shared.userDeeplAPIFree
let deeplClient = DeepLClient(userAPIKey: userAPIKey, userAPIFree: userAPIFree)
let translation = try? await deeplClient.request(target: userLang, text: account.note.asRawText)
withAnimation {
self.translation = translation
isLoadingTranslation = false
}
}
}

View file

@ -1,6 +1,6 @@
import Foundation
public struct StatusTranslation: Decodable {
public struct Translation: Decodable {
public let content: HTMLString
public let detectedSourceLanguage: String
public let provider: String
@ -12,4 +12,4 @@ public struct StatusTranslation: Decodable {
}
}
extension StatusTranslation: Sendable {}
extension Translation: Sendable {}

View file

@ -48,7 +48,7 @@ public struct DeepLClient {
deeplUserAPIFree = userAPIFree
}
public func request(target: String, text: String) async throws -> StatusTranslation {
public func request(target: String, text: String) async throws -> Translation {
do {
var components = URLComponents(string: endpoint)!
var queryItems: [URLQueryItem] = []

View file

@ -21,7 +21,7 @@ public class StatusRowViewModel: ObservableObject {
@Published var isEmbedLoading: Bool = false
@Published var isFiltered: Bool = false
@Published var translation: StatusTranslation?
@Published var translation: Translation?
@Published var isLoadingTranslation: Bool = false
@Published var showDeleteAlert: Bool = false
@ -294,7 +294,7 @@ public class StatusRowViewModel: ObservableObject {
if !alwaysTranslateWithDeepl {
do {
// We first use instance translation API if available.
let translation: StatusTranslation = try await client.post(endpoint: Statuses.translate(id: finalStatus.id,
let translation: Translation = try await client.post(endpoint: Statuses.translate(id: finalStatus.id,
lang: userLang))
withAnimation {
self.translation = translation