Add Apple offline language downloads in settings

This commit is contained in:
2024-09-28 00:28:04 -04:00
parent 2a19d5d831
commit e2cf6ffab2
4 changed files with 163 additions and 2 deletions

View File

@@ -33,6 +33,10 @@
3ACB685F297633BC00C46468 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685D297633BC00C46468 /* Localizable.strings */; };
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; };
3AE45AF6297BB2E700C1D842 /* LibreTranslateServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */; };
3AF9B5A82CA0B5CF0021A08E /* LanguageSortComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF9B5A72CA0B5CF0021A08E /* LanguageSortComparator.swift */; };
3AF9B5A92CA0B5CF0021A08E /* LanguageSortComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF9B5A72CA0B5CF0021A08E /* LanguageSortComparator.swift */; };
3AF9B5AB2CA0D2D90021A08E /* AppleTranslationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF9B5AA2CA0D2D90021A08E /* AppleTranslationSettingsView.swift */; };
3AF9B5AC2CA0D2D90021A08E /* AppleTranslationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF9B5AA2CA0D2D90021A08E /* AppleTranslationSettingsView.swift */; };
3CCD1E6A2A874C4E0099A953 /* Nip98HTTPAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCD1E692A874C4E0099A953 /* Nip98HTTPAuth.swift */; };
4C011B5E2BD0A56A002F2F9B /* ChatEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C011B5C2BD0A56A002F2F9B /* ChatEventView.swift */; };
4C011B5F2BD0A56A002F2F9B /* ChatroomThreadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C011B5D2BD0A56A002F2F9B /* ChatroomThreadView.swift */; };
@@ -1332,6 +1336,8 @@
3AF6336829884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
3AF6336929884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
3AF6336A29884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-PT"; path = "pt-PT.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
3AF9B5A72CA0B5CF0021A08E /* LanguageSortComparator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageSortComparator.swift; sourceTree = "<group>"; };
3AF9B5AA2CA0D2D90021A08E /* AppleTranslationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleTranslationSettingsView.swift; sourceTree = "<group>"; };
3CCD1E692A874C4E0099A953 /* Nip98HTTPAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Nip98HTTPAuth.swift; sourceTree = "<group>"; };
4C011B5C2BD0A56A002F2F9B /* ChatEventView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatEventView.swift; sourceTree = "<group>"; };
4C011B5D2BD0A56A002F2F9B /* ChatroomThreadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatroomThreadView.swift; sourceTree = "<group>"; };
@@ -2276,6 +2282,7 @@
4C1A9A2029DDD3E100516EAC /* KeySettingsView.swift */,
4C1A9A2429DDDF2600516EAC /* ZapSettingsView.swift */,
4C1A9A2629DDE31900516EAC /* TranslationSettingsView.swift */,
3AF9B5AA2CA0D2D90021A08E /* AppleTranslationSettingsView.swift */,
E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */,
5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */,
D7FD12252BD345A700CF195B /* FirstAidSettingsView.swift */,
@@ -2743,6 +2750,7 @@
D74AAFCE2B155D8C006CF0F4 /* ZapDataModel.swift */,
D74AAFD32B155ECB006CF0F4 /* Zaps+.swift */,
D74AAFD52B155F0C006CF0F4 /* WalletConnect+.swift */,
3AF9B5A72CA0B5CF0021A08E /* LanguageSortComparator.swift */,
);
path = Util;
sourceTree = "<group>";
@@ -3774,6 +3782,7 @@
5C8711DE2C460C06007879C2 /* PostingTimelineView.swift in Sources */,
4CA9275D2A28FF630098A105 /* LongformView.swift in Sources */,
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */,
3AF9B5AC2CA0D2D90021A08E /* AppleTranslationSettingsView.swift in Sources */,
D7EDED1E2B11797D0018B19C /* LongformEvent.swift in Sources */,
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */,
5C4D9EA72C042FA5005EA0F7 /* HighlightDraftContentView.swift in Sources */,
@@ -3971,6 +3980,7 @@
4C7D09622A098D0E00943473 /* WalletConnect.swift in Sources */,
4C3EA67928FF7ABF00C48A62 /* list.c in Sources */,
4C64987E286D082C00EAE2B3 /* DirectMessagesModel.swift in Sources */,
3AF9B5A92CA0B5CF0021A08E /* LanguageSortComparator.swift in Sources */,
4C12535E2A76CA870004F4B8 /* SwitchedTimelineNotify.swift in Sources */,
D74F430A2B23F0BE00425B75 /* DamusPurple.swift in Sources */,
9CA876E229A00CEA0003B9A3 /* AttachMediaUtility.swift in Sources */,
@@ -4355,6 +4365,7 @@
D73E5E7E2C6A97F4007EB227 /* Hashtags.swift in Sources */,
D73E5E7F2C6A97F4007EB227 /* LocalNotification.swift in Sources */,
D73E5E802C6A97F4007EB227 /* CredentialHandler.swift in Sources */,
3AF9B5AB2CA0D2D90021A08E /* AppleTranslationSettingsView.swift in Sources */,
D73E5E812C6A97F4007EB227 /* KeyboardVisible.swift in Sources */,
D73E5E832C6A97F4007EB227 /* AVPlayer+Additions.swift in Sources */,
D73E5E842C6A97F4007EB227 /* Zaps+.swift in Sources */,
@@ -4449,6 +4460,7 @@
D73E5EDE2C6A97F4007EB227 /* AppearanceSettingsView.swift in Sources */,
D73E5EDF2C6A97F4007EB227 /* KeySettingsView.swift in Sources */,
D73E5EE02C6A97F4007EB227 /* ZapSettingsView.swift in Sources */,
3AF9B5A82CA0B5CF0021A08E /* LanguageSortComparator.swift in Sources */,
D73E5F792C6A9C4C007EB227 /* HomeModel.swift in Sources */,
D73E5EE12C6A97F4007EB227 /* TranslationSettingsView.swift in Sources */,
D73E5EE22C6A97F4007EB227 /* SearchSettingsView.swift in Sources */,

View File

@@ -0,0 +1,51 @@
//
// LanguageSortComparator.swift
// damus
//
// Created by Terry Yiu on 9/22/24.
//
import Foundation
struct LanguageSortComparator: SortComparator {
var order: SortOrder
func compare(_ lhs: Locale.Language, _ rhs: Locale.Language) -> ComparisonResult {
let comparisonResult = compareForward(lhs, rhs)
switch order {
case .forward:
return comparisonResult
case .reverse:
switch comparisonResult {
case .orderedAscending:
return .orderedDescending
case .orderedDescending:
return .orderedAscending
case .orderedSame:
return .orderedSame
}
}
}
private func compareForward(_ lhs: Locale.Language, _ rhs: Locale.Language) -> ComparisonResult {
let currentLocale = Locale.current
let localizedLhs = currentLocale.localizedString(forLanguage: lhs)
let localizedRhs = currentLocale.localizedString(forLanguage: rhs)
return localizedLhs.localizedCompare(localizedRhs)
}
}
extension Locale {
func localizedString(forLanguage language: Locale.Language) -> String {
guard let languageCode = language.languageCode, let localizedLanguageCode = localizedString(forLanguageCode: languageCode.identifier) else {
return language.languageCode?.identifier ?? language.minimalIdentifier
}
if let region = language.region, let localizedRegion = localizedString(forRegionCode: region.identifier) {
return "\(localizedLanguageCode) (\(localizedRegion))"
} else {
return localizedLanguageCode
}
}
}

View File

@@ -0,0 +1,90 @@
//
// AppleTranslationSettingsView.swift
// damus
//
// Created by Terry Yiu on 9/22/24.
//
import SwiftUI
import Translation
@available(iOS 18.0, *)
struct AppleTranslationSettingsView: View {
@ObservedObject var settings: UserSettingsStore
@State private var supportedLanguages: [Locale.Language] = []
@State private var installedLanguages = Set<Locale.Language>()
@State private var installingLanguages = Set<Locale.Language>()
@State private var languageTranslationConfigurations = [Locale.Language: TranslationSession.Configuration]()
var body: some View {
if settings.translation_service == .none {
if !installedLanguages.isEmpty {
Section(NSLocalizedString("Available Offline", comment: "Section for downloaded languages for Apple offline translations.")) {
ForEach(installedLanguages.sorted(using: LanguageSortComparator(order: .forward)), id: \.self) { language in
Text(Locale.current.localizedString(forLanguage: language))
}
}
}
Section(NSLocalizedString("Languages Available for Download", comment: "Section for downloadable languages for Apple offline translations.")) {
ForEach(supportedLanguages.filter { !installedLanguages.contains($0) }, id: \.self) { language in
HStack {
Text(Locale.current.localizedString(forLanguage: language))
Button(
action: {
installingLanguages.insert(language)
languageTranslationConfigurations[language]?.invalidate()
},
label: {
Image(systemName: "arrow.down.circle")
}
)
}
.translationTask(languageTranslationConfigurations[language]) { session in
if installingLanguages.contains(language) {
do {
// Display a sheet asking the user's permission
// to start downloading the language pairing by
// translating a dummy string.
//
// We do not use `session.prepareTranslation()` because
// it does not throw errors as loudly as `session.translate` does,
// which helps us indicate when language download is complete.
_ = try await session.translate("A")
installedLanguages.insert(language)
installingLanguages.remove(language)
} catch {
// Handle any errors.
print("Error downloading language \(language): \(error)")
installingLanguages.remove(language)
}
}
}
}
}
.onAppear {
Task {
let languageAvailability = LanguageAvailability()
supportedLanguages = await languageAvailability.supportedLanguages
supportedLanguages.sort(using: LanguageSortComparator(order: .forward))
installedLanguages.removeAll()
for supportedLanguage in supportedLanguages {
let status = await languageAvailability.status(from: supportedLanguage, to: nil)
switch status {
case .installed:
installedLanguages.insert(supportedLanguage)
case .supported:
languageTranslationConfigurations[supportedLanguage] = TranslationSession.Configuration(
source: supportedLanguage
)
default:
break
}
}
}
}
}
}
}

View File

@@ -6,13 +6,14 @@
//
import SwiftUI
import Translation
struct TranslationSettingsView: View {
@ObservedObject var settings: UserSettingsStore
var damus_state: DamusState
@Environment(\.dismiss) var dismiss
@Environment(\.dismiss) var dismiss
var body: some View {
Form {
Section(NSLocalizedString("Translations", comment: "Section title for selecting the translation service.")) {
@@ -100,8 +101,15 @@ struct TranslationSettingsView: View {
Toggle(NSLocalizedString("Translate DMs", comment: "Toggle to translate direct messages."), isOn: $settings.translate_dms)
.toggleStyle(.switch)
*/
} else if #available(iOS 18.0, *) {
Toggle(NSLocalizedString("Automatically translate notes", comment: "Toggle to automatically translate notes."), isOn: $settings.auto_translate)
.toggleStyle(.switch)
}
}
if #available(iOS 18.0, *) {
AppleTranslationSettingsView(settings: settings)
}
}
.navigationTitle(NSLocalizedString("Translation", comment: "Navigation title for translation settings."))
.onReceive(handle_notify(.switched_timeline)) { _ in