mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-04-27 02:14:45 +00:00
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:
parent
a08587643d
commit
da0b92e13d
6 changed files with 59 additions and 5 deletions
|
@ -150,6 +150,17 @@ public struct AccountDetailContextMenu: View {
|
||||||
Divider()
|
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 {
|
if viewModel.relationship?.following == true {
|
||||||
Button {
|
Button {
|
||||||
routerPath.presentedSheet = .listAddAccount(account: account)
|
routerPath.presentedSheet = .listAddAccount(account: account)
|
||||||
|
|
|
@ -202,12 +202,34 @@ struct AccountDetailHeaderView: View {
|
||||||
routerPath.handle(url: url)
|
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
|
fieldsView
|
||||||
}
|
}
|
||||||
.padding(.horizontal, .layoutPadding)
|
.padding(.horizontal, .layoutPadding)
|
||||||
.offset(y: -40)
|
.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 {
|
private func makeCustomInfoLabel(title: LocalizedStringKey, count: Int, needsBadge: Bool = false) -> some View {
|
||||||
VStack {
|
VStack {
|
||||||
Text(count, format: .number.notation(.compactName))
|
Text(count, format: .number.notation(.compactName))
|
||||||
|
|
|
@ -83,6 +83,9 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Published var translation: Translation?
|
||||||
|
@Published var isLoadingTranslation = false
|
||||||
|
|
||||||
private(set) var account: Account?
|
private(set) var account: Account?
|
||||||
private var tabTask: Task<Void, Never>?
|
private var tabTask: Task<Void, Never>?
|
||||||
|
|
||||||
|
@ -263,4 +266,22 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
func statusDidAppear(status _: Models.Status) {}
|
func statusDidAppear(status _: Models.Status) {}
|
||||||
|
|
||||||
func statusDidDisappear(status _: 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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct StatusTranslation: Decodable {
|
public struct Translation: Decodable {
|
||||||
public let content: HTMLString
|
public let content: HTMLString
|
||||||
public let detectedSourceLanguage: String
|
public let detectedSourceLanguage: String
|
||||||
public let provider: String
|
public let provider: String
|
||||||
|
@ -12,4 +12,4 @@ public struct StatusTranslation: Decodable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension StatusTranslation: Sendable {}
|
extension Translation: Sendable {}
|
|
@ -48,7 +48,7 @@ public struct DeepLClient {
|
||||||
deeplUserAPIFree = userAPIFree
|
deeplUserAPIFree = userAPIFree
|
||||||
}
|
}
|
||||||
|
|
||||||
public func request(target: String, text: String) async throws -> StatusTranslation {
|
public func request(target: String, text: String) async throws -> Translation {
|
||||||
do {
|
do {
|
||||||
var components = URLComponents(string: endpoint)!
|
var components = URLComponents(string: endpoint)!
|
||||||
var queryItems: [URLQueryItem] = []
|
var queryItems: [URLQueryItem] = []
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class StatusRowViewModel: ObservableObject {
|
||||||
@Published var isEmbedLoading: Bool = false
|
@Published var isEmbedLoading: Bool = false
|
||||||
@Published var isFiltered: Bool = false
|
@Published var isFiltered: Bool = false
|
||||||
|
|
||||||
@Published var translation: StatusTranslation?
|
@Published var translation: Translation?
|
||||||
@Published var isLoadingTranslation: Bool = false
|
@Published var isLoadingTranslation: Bool = false
|
||||||
@Published var showDeleteAlert: Bool = false
|
@Published var showDeleteAlert: Bool = false
|
||||||
|
|
||||||
|
@ -294,7 +294,7 @@ public class StatusRowViewModel: ObservableObject {
|
||||||
if !alwaysTranslateWithDeepl {
|
if !alwaysTranslateWithDeepl {
|
||||||
do {
|
do {
|
||||||
// We first use instance translation API if available.
|
// 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))
|
lang: userLang))
|
||||||
withAnimation {
|
withAnimation {
|
||||||
self.translation = translation
|
self.translation = translation
|
||||||
|
|
Loading…
Reference in a new issue