mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-26 10:11:00 +00:00
Automatically detect language of posts, improve language detection when posting (#800)
* Use language detection to translate posts The source language of a post is now determined via Apples internal language detection, translation from the transmitted language is still possible. * Make language detection posting more accessible Language recognition is now always applied before posting, even if the user has explicitly selected a different language. However, the user is always asked in which of the two languages he wants to post. * Add localizations * Remove language detection in the timeline for now The language detection in the timeline is for now removed to increase timeline-performance. Signed-off-by: Paul Schuetz <pa.schuetz@web.de> * Show translate button even if no language is sent The translate-button is shown even if no language is sent with the post. Signed-off-by: Paul Schuetz <pa.schuetz@web.de> * Adjust to new commits on main Adjustments are made in regards to new developments on main. Signed-off-by: Paul Schuetz <pa.schuetz@web.de> --------- Signed-off-by: Paul Schuetz <pa.schuetz@web.de> Co-authored-by: Thomas Ricouard <ricouard77@gmail.com>
This commit is contained in:
parent
aab397f2bb
commit
cd3c50e151
22 changed files with 159 additions and 37 deletions
|
@ -374,6 +374,8 @@
|
||||||
"status.editor.error.upload" = "Error en la pujada";
|
"status.editor.error.upload" = "Error en la pujada";
|
||||||
"status.editor.language-select.navigation-title" = "Selecciona la llengua";
|
"status.editor.language-select.navigation-title" = "Selecciona la llengua";
|
||||||
"status.editor.language-select.recently-used" = "Recents";
|
"status.editor.language-select.recently-used" = "Recents";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Edita la imatge";
|
"status.editor.media.edit-image" = "Edita la imatge";
|
||||||
"status.editor.media.image-description" = "Descripció de la imatge";
|
"status.editor.media.image-description" = "Descripció de la imatge";
|
||||||
"status.editor.mode.edit" = "Edita la publicació";
|
"status.editor.mode.edit" = "Edita la publicació";
|
||||||
|
|
|
@ -375,6 +375,8 @@
|
||||||
"status.editor.error.upload" = "Fehler beim Hochladen";
|
"status.editor.error.upload" = "Fehler beim Hochladen";
|
||||||
"status.editor.language-select.navigation-title" = "Sprache auswählen";
|
"status.editor.language-select.navigation-title" = "Sprache auswählen";
|
||||||
"status.editor.language-select.recently-used" = "Kürzlich genutzt";
|
"status.editor.language-select.recently-used" = "Kürzlich genutzt";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Auf %@ posten (Erkannte Sprache)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Auf %@ posten (Ausgewählte Sprache)";
|
||||||
"status.editor.media.edit-image" = "Bild bearbeiten";
|
"status.editor.media.edit-image" = "Bild bearbeiten";
|
||||||
"status.editor.media.image-description" = "Bildbeschreibung";
|
"status.editor.media.image-description" = "Bildbeschreibung";
|
||||||
"status.editor.mode.edit" = "Deinen Beitrag bearbeiten";
|
"status.editor.mode.edit" = "Deinen Beitrag bearbeiten";
|
||||||
|
|
|
@ -375,6 +375,8 @@
|
||||||
"status.editor.error.upload" = "Error uploading";
|
"status.editor.error.upload" = "Error uploading";
|
||||||
"status.editor.language-select.navigation-title" = "Select Language";
|
"status.editor.language-select.navigation-title" = "Select Language";
|
||||||
"status.editor.language-select.recently-used" = "Recently Used";
|
"status.editor.language-select.recently-used" = "Recently Used";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Edit Image";
|
"status.editor.media.edit-image" = "Edit Image";
|
||||||
"status.editor.media.image-description" = "Image description";
|
"status.editor.media.image-description" = "Image description";
|
||||||
"status.editor.mode.edit" = "Editing your post";
|
"status.editor.mode.edit" = "Editing your post";
|
||||||
|
|
|
@ -376,6 +376,8 @@
|
||||||
"status.editor.error.upload" = "Error uploading";
|
"status.editor.error.upload" = "Error uploading";
|
||||||
"status.editor.language-select.navigation-title" = "Select Language";
|
"status.editor.language-select.navigation-title" = "Select Language";
|
||||||
"status.editor.language-select.recently-used" = "Recently Used";
|
"status.editor.language-select.recently-used" = "Recently Used";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Edit Image";
|
"status.editor.media.edit-image" = "Edit Image";
|
||||||
"status.editor.media.image-description" = "Image description";
|
"status.editor.media.image-description" = "Image description";
|
||||||
"status.editor.mode.edit" = "Editing your post";
|
"status.editor.mode.edit" = "Editing your post";
|
||||||
|
|
|
@ -376,6 +376,8 @@
|
||||||
"status.editor.error.upload" = "Error subiendo";
|
"status.editor.error.upload" = "Error subiendo";
|
||||||
"status.editor.language-select.navigation-title" = "Seleccionar idioma";
|
"status.editor.language-select.navigation-title" = "Seleccionar idioma";
|
||||||
"status.editor.language-select.recently-used" = "Usado recientemente";
|
"status.editor.language-select.recently-used" = "Usado recientemente";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Editar Imagen";
|
"status.editor.media.edit-image" = "Editar Imagen";
|
||||||
"status.editor.media.image-description" = "Descripción de la imagen";
|
"status.editor.media.image-description" = "Descripción de la imagen";
|
||||||
"status.editor.mode.edit" = "Editando tu publicación";
|
"status.editor.mode.edit" = "Editando tu publicación";
|
||||||
|
|
|
@ -375,6 +375,8 @@
|
||||||
"status.editor.error.upload" = "Errorea igotzerakoan";
|
"status.editor.error.upload" = "Errorea igotzerakoan";
|
||||||
"status.editor.language-select.navigation-title" = "Hautatu hizkuntza";
|
"status.editor.language-select.navigation-title" = "Hautatu hizkuntza";
|
||||||
"status.editor.language-select.recently-used" = "Duela gutxi erabilitakoak";
|
"status.editor.language-select.recently-used" = "Duela gutxi erabilitakoak";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Editatu irudiak";
|
"status.editor.media.edit-image" = "Editatu irudiak";
|
||||||
"status.editor.media.image-description" = "Irudiaren deskribapena";
|
"status.editor.media.image-description" = "Irudiaren deskribapena";
|
||||||
"status.editor.mode.edit" = "Bidalketa editatzen";
|
"status.editor.mode.edit" = "Bidalketa editatzen";
|
||||||
|
|
|
@ -371,6 +371,8 @@
|
||||||
"status.editor.error.upload" = "Erreur de téléchargement";
|
"status.editor.error.upload" = "Erreur de téléchargement";
|
||||||
"status.editor.language-select.navigation-title" = "Sélectionner la langue";
|
"status.editor.language-select.navigation-title" = "Sélectionner la langue";
|
||||||
"status.editor.language-select.recently-used" = "Utilisé récemment";
|
"status.editor.language-select.recently-used" = "Utilisé récemment";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Modifier l'image";
|
"status.editor.media.edit-image" = "Modifier l'image";
|
||||||
"status.editor.media.image-description" = "Description de l'image";
|
"status.editor.media.image-description" = "Description de l'image";
|
||||||
"status.editor.mode.edit" = "Modification de votre publication";
|
"status.editor.mode.edit" = "Modification de votre publication";
|
||||||
|
|
|
@ -376,6 +376,8 @@
|
||||||
"status.editor.error.upload" = "Errore durante il caricamento";
|
"status.editor.error.upload" = "Errore durante il caricamento";
|
||||||
"status.editor.language-select.navigation-title" = "Scegli la lingua";
|
"status.editor.language-select.navigation-title" = "Scegli la lingua";
|
||||||
"status.editor.language-select.recently-used" = "Usato recentemente";
|
"status.editor.language-select.recently-used" = "Usato recentemente";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Modifica l'immagine";
|
"status.editor.media.edit-image" = "Modifica l'immagine";
|
||||||
"status.editor.media.image-description" = "Descrizione dell'immagine";
|
"status.editor.media.image-description" = "Descrizione dell'immagine";
|
||||||
"status.editor.mode.edit" = "Modifica post";
|
"status.editor.mode.edit" = "Modifica post";
|
||||||
|
|
|
@ -375,6 +375,8 @@
|
||||||
"status.editor.error.upload" = "アップロードエラー";
|
"status.editor.error.upload" = "アップロードエラー";
|
||||||
"status.editor.language-select.navigation-title" = "言語設定";
|
"status.editor.language-select.navigation-title" = "言語設定";
|
||||||
"status.editor.language-select.recently-used" = "最近使用した";
|
"status.editor.language-select.recently-used" = "最近使用した";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "イメージの編集";
|
"status.editor.media.edit-image" = "イメージの編集";
|
||||||
"status.editor.media.image-description" = "イメージの説明文";
|
"status.editor.media.image-description" = "イメージの説明文";
|
||||||
"status.editor.mode.edit" = "投稿を編集する";
|
"status.editor.mode.edit" = "投稿を編集する";
|
||||||
|
|
|
@ -377,6 +377,8 @@
|
||||||
"status.editor.error.upload" = "전송 오류";
|
"status.editor.error.upload" = "전송 오류";
|
||||||
"status.editor.language-select.navigation-title" = "언어 선택";
|
"status.editor.language-select.navigation-title" = "언어 선택";
|
||||||
"status.editor.language-select.recently-used" = "최근 사용";
|
"status.editor.language-select.recently-used" = "최근 사용";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "이미지 편집";
|
"status.editor.media.edit-image" = "이미지 편집";
|
||||||
"status.editor.media.image-description" = "이미지 설명";
|
"status.editor.media.image-description" = "이미지 설명";
|
||||||
"status.editor.mode.edit" = "글 수정";
|
"status.editor.mode.edit" = "글 수정";
|
||||||
|
|
|
@ -375,6 +375,8 @@
|
||||||
"status.editor.error.upload" = "Feil ved opplasting";
|
"status.editor.error.upload" = "Feil ved opplasting";
|
||||||
"status.editor.language-select.navigation-title" = "Velg språk";
|
"status.editor.language-select.navigation-title" = "Velg språk";
|
||||||
"status.editor.language-select.recently-used" = "Nylig brukt";
|
"status.editor.language-select.recently-used" = "Nylig brukt";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Rediger bilde";
|
"status.editor.media.edit-image" = "Rediger bilde";
|
||||||
"status.editor.media.image-description" = "Bildebeskrivelse";
|
"status.editor.media.image-description" = "Bildebeskrivelse";
|
||||||
"status.editor.mode.edit" = "Redigerer innlegget ditt";
|
"status.editor.mode.edit" = "Redigerer innlegget ditt";
|
||||||
|
|
|
@ -369,6 +369,8 @@
|
||||||
"status.editor.error.upload" = "Fout tijdens uploaden";
|
"status.editor.error.upload" = "Fout tijdens uploaden";
|
||||||
"status.editor.language-select.navigation-title" = "Taal selecteren";
|
"status.editor.language-select.navigation-title" = "Taal selecteren";
|
||||||
"status.editor.language-select.recently-used" = "Onlangs gebruikt";
|
"status.editor.language-select.recently-used" = "Onlangs gebruikt";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Afbeelding bewerken";
|
"status.editor.media.edit-image" = "Afbeelding bewerken";
|
||||||
"status.editor.media.image-description" = "Omschrijving";
|
"status.editor.media.image-description" = "Omschrijving";
|
||||||
"status.editor.mode.edit" = "Post bewerken";
|
"status.editor.mode.edit" = "Post bewerken";
|
||||||
|
|
|
@ -371,6 +371,8 @@
|
||||||
"status.editor.error.upload" = "Błąd wysyłania";
|
"status.editor.error.upload" = "Błąd wysyłania";
|
||||||
"status.editor.language-select.navigation-title" = "Wybierz język";
|
"status.editor.language-select.navigation-title" = "Wybierz język";
|
||||||
"status.editor.language-select.recently-used" = "Ostatnio użyty";
|
"status.editor.language-select.recently-used" = "Ostatnio użyty";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Edytuj obrazek";
|
"status.editor.media.edit-image" = "Edytuj obrazek";
|
||||||
"status.editor.media.image-description" = "Opis obrazka";
|
"status.editor.media.image-description" = "Opis obrazka";
|
||||||
"status.editor.mode.edit" = "Edycja twojego postu";
|
"status.editor.mode.edit" = "Edycja twojego postu";
|
||||||
|
|
|
@ -375,6 +375,8 @@
|
||||||
"status.editor.error.upload" = "Erro ao fazer upload";
|
"status.editor.error.upload" = "Erro ao fazer upload";
|
||||||
"status.editor.language-select.navigation-title" = "Selecionar Idioma";
|
"status.editor.language-select.navigation-title" = "Selecionar Idioma";
|
||||||
"status.editor.language-select.recently-used" = "Usado recentemente";
|
"status.editor.language-select.recently-used" = "Usado recentemente";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Editar Imagem";
|
"status.editor.media.edit-image" = "Editar Imagem";
|
||||||
"status.editor.media.image-description" = "Descrição da imagem";
|
"status.editor.media.image-description" = "Descrição da imagem";
|
||||||
"status.editor.mode.edit" = "Editando sua postagem";
|
"status.editor.mode.edit" = "Editando sua postagem";
|
||||||
|
|
|
@ -371,6 +371,8 @@
|
||||||
"status.editor.error.upload" = "Yüklerken Hata Oluştu";
|
"status.editor.error.upload" = "Yüklerken Hata Oluştu";
|
||||||
"status.editor.language-select.navigation-title" = "Dil Seç";
|
"status.editor.language-select.navigation-title" = "Dil Seç";
|
||||||
"status.editor.language-select.recently-used" = "Son Kullanılanlar";
|
"status.editor.language-select.recently-used" = "Son Kullanılanlar";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "Görüntüyü Düzenle";
|
"status.editor.media.edit-image" = "Görüntüyü Düzenle";
|
||||||
"status.editor.media.image-description" = "Görüntü Açıklaması";
|
"status.editor.media.image-description" = "Görüntü Açıklaması";
|
||||||
"status.editor.mode.edit" = "Gönderin Düzenleniyor";
|
"status.editor.mode.edit" = "Gönderin Düzenleniyor";
|
||||||
|
|
|
@ -376,6 +376,8 @@
|
||||||
"status.editor.error.upload" = "上传错误";
|
"status.editor.error.upload" = "上传错误";
|
||||||
"status.editor.language-select.navigation-title" = "选择语言";
|
"status.editor.language-select.navigation-title" = "选择语言";
|
||||||
"status.editor.language-select.recently-used" = "最近使用";
|
"status.editor.language-select.recently-used" = "最近使用";
|
||||||
|
"status.editor.language-select.confirmation.detected-%@" = "Post in %@ (Detected language)";
|
||||||
|
"status.editor.language-select.confirmation.selected-%@" = "Post in %@ (Selected language)";
|
||||||
"status.editor.media.edit-image" = "编辑图片";
|
"status.editor.media.edit-image" = "编辑图片";
|
||||||
"status.editor.media.image-description" = "图片描述";
|
"status.editor.media.image-description" = "图片描述";
|
||||||
"status.editor.mode.edit" = "正在编辑你的嘟文";
|
"status.editor.mode.edit" = "正在编辑你的嘟文";
|
||||||
|
|
|
@ -21,6 +21,7 @@ public struct StatusEditorView: View {
|
||||||
@FocusState private var isSpoilerTextFocused: Bool
|
@FocusState private var isSpoilerTextFocused: Bool
|
||||||
|
|
||||||
@State private var isDismissAlertPresented: Bool = false
|
@State private var isDismissAlertPresented: Bool = false
|
||||||
|
@State private var isLanguageConfirmPresented = false
|
||||||
|
|
||||||
public init(mode: StatusEditorViewModel.Mode) {
|
public init(mode: StatusEditorViewModel.Mode) {
|
||||||
_viewModel = StateObject(wrappedValue: .init(mode: mode))
|
_viewModel = StateObject(wrappedValue: .init(mode: mode))
|
||||||
|
@ -104,12 +105,12 @@ public struct StatusEditorView: View {
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
Button {
|
Button {
|
||||||
Task {
|
Task {
|
||||||
let status = await viewModel.postStatus()
|
viewModel.evaluateLanguages()
|
||||||
if status != nil {
|
if let _ = viewModel.languageConfirmationDialogLanguages {
|
||||||
dismiss()
|
isLanguageConfirmPresented = true
|
||||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
} else {
|
||||||
object: nil)
|
await postStatus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
if viewModel.isPosting {
|
if viewModel.isPosting {
|
||||||
|
@ -120,6 +121,9 @@ public struct StatusEditorView: View {
|
||||||
}
|
}
|
||||||
.disabled(!viewModel.canPost)
|
.disabled(!viewModel.canPost)
|
||||||
.keyboardShortcut(.return, modifiers: .command)
|
.keyboardShortcut(.return, modifiers: .command)
|
||||||
|
.confirmationDialog("", isPresented: $isLanguageConfirmPresented, actions: {
|
||||||
|
languageConfirmationDialog
|
||||||
|
})
|
||||||
}
|
}
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
Button {
|
Button {
|
||||||
|
@ -156,6 +160,42 @@ public struct StatusEditorView: View {
|
||||||
.interactiveDismissDisabled(!viewModel.statusText.string.isEmpty)
|
.interactiveDismissDisabled(!viewModel.statusText.string.isEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var languageConfirmationDialog: some View {
|
||||||
|
if let dialogVals = viewModel.languageConfirmationDialogLanguages,
|
||||||
|
let detected = dialogVals["detected"],
|
||||||
|
let detectedLong = Locale.current.localizedString(forLanguageCode: detected),
|
||||||
|
let selected = dialogVals["selected"],
|
||||||
|
let selectedLong = Locale.current.localizedString(forLanguageCode: selected){
|
||||||
|
Button("status.editor.language-select.confirmation.detected-\(detectedLong)") {
|
||||||
|
viewModel.selectedLanguage = detected
|
||||||
|
Task {
|
||||||
|
await postStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button("status.editor.language-select.confirmation.selected-\(selectedLong)") {
|
||||||
|
viewModel.selectedLanguage = selected
|
||||||
|
Task {
|
||||||
|
await postStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button("action.cancel", role: .cancel) {
|
||||||
|
viewModel.languageConfirmationDialogLanguages = nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func postStatus() async {
|
||||||
|
let status = await viewModel.postStatus()
|
||||||
|
if status != nil {
|
||||||
|
dismiss()
|
||||||
|
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
||||||
|
object: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var spoilerTextView: some View {
|
private var spoilerTextView: some View {
|
||||||
if viewModel.spoilerOn {
|
if viewModel.spoilerOn {
|
||||||
|
|
|
@ -14,6 +14,7 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
||||||
var currentAccount: Account?
|
var currentAccount: Account?
|
||||||
var theme: Theme?
|
var theme: Theme?
|
||||||
var preferences: UserPreferences?
|
var preferences: UserPreferences?
|
||||||
|
var languageConfirmationDialogLanguages: [String: String]?
|
||||||
|
|
||||||
var textView: UITextView? {
|
var textView: UITextView? {
|
||||||
didSet {
|
didSet {
|
||||||
|
@ -141,6 +142,17 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
||||||
selectedLanguage = selectedLanguage ?? preference ?? currentAccount?.source?.language
|
selectedLanguage = selectedLanguage ?? preference ?? currentAccount?.source?.language
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evaluateLanguages(){
|
||||||
|
if let detectedLang = detectLanguage(text: statusText.string),
|
||||||
|
let selectedLanguage = selectedLanguage,
|
||||||
|
selectedLanguage != detectedLang {
|
||||||
|
languageConfirmationDialogLanguages = ["detected": detectedLang,
|
||||||
|
"selected": selectedLanguage]
|
||||||
|
} else {
|
||||||
|
languageConfirmationDialogLanguages = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func postStatus() async -> Status? {
|
func postStatus() async -> Status? {
|
||||||
guard let client else { return nil }
|
guard let client else { return nil }
|
||||||
do {
|
do {
|
||||||
|
@ -153,20 +165,6 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
||||||
expires_in: pollDuration.rawValue)
|
expires_in: pollDuration.rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hasExplicitlySelectedLanguage {
|
|
||||||
// Attempt language resolution using Natural Language
|
|
||||||
let recognizer = NLLanguageRecognizer()
|
|
||||||
recognizer.processString(statusText.string)
|
|
||||||
// Use languageHypotheses to get the probability with it
|
|
||||||
let hypotheses = recognizer.languageHypotheses(withMaximum: 1)
|
|
||||||
// Assert that 85% probability is enough :)
|
|
||||||
// A one word toot that is en/fr compatible is only ~50% confident, for instance
|
|
||||||
if let (language, probability) = hypotheses.first, probability > 0.85 {
|
|
||||||
// rawValue return the IETF BCP 47 language tag
|
|
||||||
selectedLanguage = language.rawValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = StatusData(status: statusText.string,
|
let data = StatusData(status: statusText.string,
|
||||||
visibility: visibility,
|
visibility: visibility,
|
||||||
inReplyToId: mode.replyToStatus?.id,
|
inReplyToId: mode.replyToStatus?.id,
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import Foundation
|
||||||
|
import NaturalLanguage
|
||||||
|
|
||||||
|
private func stripToPureLanguage(inText: String) -> String {
|
||||||
|
let hashtagRegex = try! Regex("#[\\w]*")
|
||||||
|
let emojiRegex = try! Regex(":\\w*:")
|
||||||
|
let atRegex = try! Regex("@\\w*")
|
||||||
|
|
||||||
|
var resultStr = inText
|
||||||
|
|
||||||
|
[hashtagRegex, emojiRegex, atRegex].forEach { regex in
|
||||||
|
let splitArray = resultStr.split(separator: regex, omittingEmptySubsequences: true)
|
||||||
|
resultStr = splitArray.joined() as String
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultStr.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectLanguage(text: String) -> String? {
|
||||||
|
let recognizer = NLLanguageRecognizer()
|
||||||
|
|
||||||
|
let strippedText = stripToPureLanguage(inText: text)
|
||||||
|
|
||||||
|
recognizer.processString(strippedText)
|
||||||
|
|
||||||
|
let hypotheses = recognizer.languageHypotheses(withMaximum: 1)
|
||||||
|
|
||||||
|
// Use the detected language only with >= 85 % confidence
|
||||||
|
if let (lang, confidence) = hypotheses.first, confidence >= 0.85 {
|
||||||
|
return lang.rawValue
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -82,9 +82,8 @@ struct StatusRowContextMenu: View {
|
||||||
await viewModel.translate(userLang: lang)
|
await viewModel.translate(userLang: lang)
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
if let statusLang = viewModel.status.language,
|
if let statusLang = viewModel.getStatusLang(),
|
||||||
let languageName = Locale.current.localizedString(forLanguageCode: statusLang)
|
let languageName = Locale.current.localizedString(forLanguageCode: statusLang) {
|
||||||
{
|
|
||||||
Label("status.action.translate-from-\(languageName)", systemImage: "captions.bubble")
|
Label("status.action.translate-from-\(languageName)", systemImage: "captions.bubble")
|
||||||
} else {
|
} else {
|
||||||
Label("status.action.translate", systemImage: "captions.bubble")
|
Label("status.action.translate", systemImage: "captions.bubble")
|
||||||
|
|
|
@ -345,14 +345,24 @@ public struct StatusRowView: View {
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func shouldShowTranslateButton(status: AnyStatus) -> Bool {
|
||||||
|
let statusLang = viewModel.getStatusLang()
|
||||||
|
|
||||||
|
if let userLang = preferences.serverPreferences?.postLanguage,
|
||||||
|
preferences.showTranslateButton,
|
||||||
|
!status.content.asRawText.isEmpty,
|
||||||
|
viewModel.translation == nil
|
||||||
|
{
|
||||||
|
return userLang != statusLang
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private func makeTranslateView(status: AnyStatus) -> some View {
|
private func makeTranslateView(status: AnyStatus) -> some View {
|
||||||
if let userLang = preferences.serverPreferences?.postLanguage,
|
if let userLang = preferences.serverPreferences?.postLanguage,
|
||||||
preferences.showTranslateButton,
|
shouldShowTranslateButton(status: status)
|
||||||
status.language != nil,
|
|
||||||
userLang != status.language,
|
|
||||||
!status.content.asRawText.isEmpty,
|
|
||||||
viewModel.translation == nil
|
|
||||||
{
|
{
|
||||||
Button {
|
Button {
|
||||||
Task {
|
Task {
|
||||||
|
@ -362,13 +372,13 @@ public struct StatusRowView: View {
|
||||||
if viewModel.isLoadingTranslation {
|
if viewModel.isLoadingTranslation {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
} else {
|
} else {
|
||||||
if let statusLanguage = status.language,
|
if let statusLanguage = viewModel.getStatusLang(),
|
||||||
let languageName = Locale.current.localizedString(forLanguageCode: statusLanguage)
|
let languageName = Locale.current.localizedString(forLanguageCode: statusLanguage)
|
||||||
{
|
{
|
||||||
Text("status.action.translate-from-\(languageName)")
|
Text("status.action.translate-from-\(languageName)")
|
||||||
} else {
|
} else {
|
||||||
Text("status.action.translate")
|
Text("status.action.translate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderless)
|
.buttonStyle(.borderless)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Env
|
||||||
import Models
|
import Models
|
||||||
import Network
|
import Network
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import NaturalLanguage
|
||||||
|
|
||||||
import DesignSystem
|
import DesignSystem
|
||||||
|
|
||||||
|
@ -289,8 +290,16 @@ public class StatusRowViewModel: ObservableObject {
|
||||||
reblogsCount = status.reblog?.reblogsCount ?? status.reblogsCount
|
reblogsCount = status.reblog?.reblogsCount ?? status.reblogsCount
|
||||||
repliesCount = status.reblog?.repliesCount ?? status.repliesCount
|
repliesCount = status.reblog?.repliesCount ?? status.repliesCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getStatusLang() -> String? {
|
||||||
|
status.language
|
||||||
|
}
|
||||||
|
|
||||||
func translate(userLang: String) async {
|
func translate(userLang: String) async {
|
||||||
|
await translate(userLang: userLang, sourceLang: getStatusLang())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func translate(userLang: String, sourceLang: String?) async {
|
||||||
guard let client else { return }
|
guard let client else { return }
|
||||||
do {
|
do {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
|
|
Loading…
Reference in a new issue