浏览代码

Expand AI Companion speech transcription to global locale coverage.

Default locale fallback now prioritizes the user's current and preferred languages, then scans all Apple-supported recognizer locales for better multilingual transcript generation.

Co-authored-by: Cursor <cursoragent@cursor.com>
huzaifahayat12 1 月之前
父节点
当前提交
2542f621e1
共有 1 个文件被更改,包括 44 次插入2 次删除
  1. 44 2
      meetings_app/Transcription/MeetingTranscriptionService.swift

+ 44 - 2
meetings_app/Transcription/MeetingTranscriptionService.swift

@@ -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