import SwiftUI /// Settings screen for choosing which languages voice notes can be transcribed /// in. Enabling a language reserves it and downloads its on-device model; the /// system caps how many can be reserved at once, so the list disables further /// toggles at capacity and explains the limit in the footer. struct TranscriptionLanguagesView: View { @Environment(TranscriptionSettings.self) private var settings @State private var errorMessage: String? var body: some View { List { if let errorMessage { Section { Label(errorMessage, systemImage: "exclamationmark.triangle") .font(.footnote) .foregroundStyle(.red) } } if !enabledLocales.isEmpty { Section("Enabled") { ForEach(enabledLocales, id: \.identifier) { row($0) } } } Section { ForEach(otherLocales, id: \.identifier) { row($0) } } header: { Text("All Languages") } footer: { Text(footerText) } } .navigationTitle("Languages") .overlay { if settings.supportedLocales.isEmpty { ProgressView() } } .task { await settings.prepare() } } private var enabledLocales: [Locale] { settings.supportedLocales.filter { settings.isEnabled($0) } } private var otherLocales: [Locale] { settings.supportedLocales.filter { !settings.isEnabled($0) } } private var footerText: String { guard settings.maximumReserved > 0 else { return "Each new note is transcribed on device in your enabled languages." } return "Enable up to \(settings.maximumReserved) languages. Each new voice note is sampled in your enabled languages and transcribed in whichever fits best." } private func row(_ locale: Locale) -> some View { let enabled = settings.isEnabled(locale) return HStack { VStack(alignment: .leading, spacing: 2) { Text(TranscriptionSettings.displayName(for: locale)) stateSubtitle(for: locale) } Spacer() Toggle("", isOn: Binding( get: { settings.isEnabled(locale) }, set: { toggle(locale, on: $0) } )) .labelsHidden() .disabled(!enabled && settings.isAtCapacity) } } @ViewBuilder private func stateSubtitle(for locale: Locale) -> some View { switch settings.assetStates[TranscriptionSettings.canonicalID(locale)] { case .installed: Label("Installed", systemImage: "checkmark.circle.fill") .font(.caption) .foregroundStyle(.green) case .downloading(let fraction): HStack(spacing: 6) { ProgressView().controlSize(.small) if let fraction { Text("Downloading \(Int(fraction * 100))%") } else { Text("Downloading…") } } .font(.caption) .foregroundStyle(.secondary) case .notInstalled: Label("Available to download", systemImage: "icloud.and.arrow.down") .font(.caption) .foregroundStyle(.secondary) case .unsupported, .none: EmptyView() } } private func toggle(_ locale: Locale, on: Bool) { errorMessage = nil Task { if on { do { try await settings.enable(locale) } catch TranscriptionSettings.SettingsError.atCapacity { errorMessage = "You can enable up to \(settings.maximumReserved) languages. Turn one off first." } catch { errorMessage = "Couldn't enable \(TranscriptionSettings.displayName(for: locale)): \(error.localizedDescription)" } } else { await settings.disable(locale) } } } }