|
|
@@ -101,7 +101,7 @@ final class MeetingTranscriptionService {
|
|
101
|
101
|
systemURL: URL?,
|
|
102
|
102
|
chunkSeconds: TimeInterval = 30,
|
|
103
|
103
|
overlapSeconds: TimeInterval = 0,
|
|
104
|
|
- locales: [Locale] = [Locale(identifier: "en-US")],
|
|
|
104
|
+ locales: [Locale] = [],
|
|
105
|
105
|
onProgress: (@Sendable (MeetingTranscriptionProgress) -> Void)? = nil
|
|
106
|
106
|
) async throws -> [TranscriptSegment] {
|
|
107
|
107
|
try await requestAuthorization()
|
|
|
@@ -125,7 +125,7 @@ final class MeetingTranscriptionService {
|
|
125
|
125
|
let counter = ProgressCounter(total: totalChunks, onProgress: onProgress)
|
|
126
|
126
|
await counter.emitInitial()
|
|
127
|
127
|
|
|
128
|
|
- let effectiveLocales = locales.isEmpty ? [Locale(identifier: "en-US")] : locales
|
|
|
128
|
+ let effectiveLocales = locales.isEmpty ? preferredLocalesForGlobalRecognition() : locales
|
|
129
|
129
|
|
|
130
|
130
|
async let micSegments: [TranscriptSegment] = {
|
|
131
|
131
|
guard let plan = micPlan else { return [] }
|
|
|
@@ -248,6 +248,48 @@ final class MeetingTranscriptionService {
|
|
248
|
248
|
return ""
|
|
249
|
249
|
}
|
|
250
|
250
|
|
|
|
251
|
+ private func preferredLocalesForGlobalRecognition() -> [Locale] {
|
|
|
252
|
+ let supported = SFSpeechRecognizer.supportedLocales()
|
|
|
253
|
+ guard supported.isEmpty == false else {
|
|
|
254
|
+ return [Locale.current, Locale(identifier: "en-US")]
|
|
|
255
|
+ }
|
|
|
256
|
+
|
|
|
257
|
+ let normalizedSupportedByIdentifier: [String: Locale] = Dictionary(
|
|
|
258
|
+ uniqueKeysWithValues: supported.map { locale in
|
|
|
259
|
+ (normalizeLocaleIdentifier(locale.identifier), locale)
|
|
|
260
|
+ }
|
|
|
261
|
+ )
|
|
|
262
|
+
|
|
|
263
|
+ var ordered: [Locale] = []
|
|
|
264
|
+ var seen = Set<String>()
|
|
|
265
|
+
|
|
|
266
|
+ func appendIfSupportedIdentifier(_ identifier: String) {
|
|
|
267
|
+ let normalized = normalizeLocaleIdentifier(identifier)
|
|
|
268
|
+ guard seen.contains(normalized) == false else { return }
|
|
|
269
|
+ guard let supportedLocale = normalizedSupportedByIdentifier[normalized] else { return }
|
|
|
270
|
+ seen.insert(normalized)
|
|
|
271
|
+ ordered.append(supportedLocale)
|
|
|
272
|
+ }
|
|
|
273
|
+
|
|
|
274
|
+ appendIfSupportedIdentifier(Locale.current.identifier)
|
|
|
275
|
+ for identifier in Locale.preferredLanguages {
|
|
|
276
|
+ appendIfSupportedIdentifier(identifier)
|
|
|
277
|
+ }
|
|
|
278
|
+
|
|
|
279
|
+ for locale in supported.sorted(by: { $0.identifier < $1.identifier }) {
|
|
|
280
|
+ appendIfSupportedIdentifier(locale.identifier)
|
|
|
281
|
+ }
|
|
|
282
|
+
|
|
|
283
|
+ if ordered.isEmpty {
|
|
|
284
|
+ return [Locale(identifier: "en-US")]
|
|
|
285
|
+ }
|
|
|
286
|
+ return ordered
|
|
|
287
|
+ }
|
|
|
288
|
+
|
|
|
289
|
+ private func normalizeLocaleIdentifier(_ identifier: String) -> String {
|
|
|
290
|
+ identifier.replacingOccurrences(of: "_", with: "-").lowercased()
|
|
|
291
|
+ }
|
|
|
292
|
+
|
|
251
|
293
|
private func transcribeBuffer(buffer: AVAudioPCMBuffer, recognizer: SFSpeechRecognizer) async throws -> String {
|
|
252
|
294
|
let request = SFSpeechAudioBufferRecognitionRequest()
|
|
253
|
295
|
request.shouldReportPartialResults = false
|