Skip to content

Commit 2e5fbd1

Browse files
committed
Fix setting voice and language
1 parent 6d85d81 commit 2e5fbd1

File tree

5 files changed

+45
-12
lines changed

5 files changed

+45
-12
lines changed

Sources/Navigator/TTS/AVTTSEngine.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public class AVTTSEngine: NSObject, TTSEngine, AVSpeechSynthesizerDelegate, Logg
7979

8080
private func voice(for utterance: TTSUtterance) -> AVSpeechSynthesisVoice? {
8181
let language = utterance.language ?? config.defaultLanguage
82-
if let voice = config.voice, voice.language == language {
82+
if let voice = config.voice, voice.language.removingRegion() == language.removingRegion() {
8383
return AVSpeechSynthesisVoice(identifier: voice.identifier)
8484
} else {
8585
return AVSpeechSynthesisVoice(language: language)

Sources/Navigator/TTS/TTSEngine.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ public protocol TTSEngineDelegate: AnyObject {
2424

2525
public struct TTSConfiguration {
2626
public var defaultLanguage: Language {
27-
didSet { voice = nil }
27+
didSet {
28+
defaultLanguage = defaultLanguage.removingRegion()
29+
voice = nil
30+
}
2831
}
2932
public var rate: Double
3033
public var pitch: Double
@@ -38,7 +41,7 @@ public struct TTSConfiguration {
3841
voice: TTSVoice? = nil,
3942
delay: TimeInterval = 0
4043
) {
41-
self.defaultLanguage = defaultLanguage
44+
self.defaultLanguage = defaultLanguage.removingRegion()
4245
self.rate = rate
4346
self.pitch = pitch
4447
self.voice = voice

Sources/Shared/Toolkit/Language.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,40 @@ public struct Language: Hashable {
2121
return code
2222
}
2323
}
24+
25+
public func removingRegion() -> Code {
26+
.bcp47(String(bcp47.prefix { $0 != "-" && $0 != "_" }))
27+
}
2428
}
2529

2630
public let code: Code
2731

2832
public var locale: Locale { Locale(identifier: code.bcp47) }
2933

30-
public var localizedName: String {
31-
Locale.current.localizedString(forIdentifier: code.bcp47)
34+
public func localizedDescription(in locale: Locale = Locale.current) -> String {
35+
locale.localizedString(forIdentifier: code.bcp47)
3236
?? code.bcp47
3337
}
3438

39+
public func localizedLanguage(in targetLocale: Locale = Locale.current) -> String? {
40+
locale.languageCode.flatMap { targetLocale.localizedString(forLanguageCode: $0) }
41+
}
42+
43+
public func localizedRegion(in targetLocale: Locale = Locale.current) -> String? {
44+
locale.regionCode.flatMap { targetLocale.localizedString(forRegionCode: $0) }
45+
}
46+
3547
public init(code: Code) {
3648
self.code = code
3749
}
3850

3951
public init(locale: Locale) {
4052
self.init(code: .bcp47(locale.identifier))
4153
}
54+
55+
public func removingRegion() -> Language {
56+
Language(code: code.removingRegion())
57+
}
4258
}
4359

4460
extension Language: CustomStringConvertible {

TestApp/Sources/Reader/Common/TTS/TTSView.swift

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,14 @@ struct TTSSettings: View {
8787
caption: "Language",
8888
for: \.defaultLanguage,
8989
choices: viewModel.availableLanguages,
90-
choiceLabel: { $0.localizedName }
90+
choiceLabel: { $0.localizedDescription() }
9191
)
9292

9393
ConfigPicker(
9494
caption: "Voice",
9595
for: \.voice,
96-
choices: viewModel.availableVoices(for: viewModel.config.defaultLanguage),
97-
choiceLabel: { $0?.name ?? "Default" }
96+
choices: viewModel.availableVoices,
97+
choiceLabel: { $0.localizedDescription() }
9898
)
9999
}
100100
}
@@ -143,3 +143,16 @@ struct TTSSettings: View {
143143
}
144144
}
145145
}
146+
147+
private extension Optional where Wrapped == TTSVoice {
148+
func localizedDescription() -> String {
149+
guard case let .some(voice) = self else {
150+
return "Default"
151+
}
152+
var desc = voice.name
153+
if let region = voice.language.localizedRegion() {
154+
desc += " (\(region))"
155+
}
156+
return desc
157+
}
158+
}

TestApp/Sources/Reader/Common/TTS/TTSViewModel.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,16 @@ final class TTSViewModel: ObservableObject, Loggable {
5858

5959
var defaultConfig: TTSConfiguration { tts.defaultConfig }
6060

61-
func availableVoices(for language: Language) -> [TTSVoice?] {
62-
[nil] + tts.availableVoices.filter { $0.language == language }
61+
var availableVoices: [TTSVoice?] {
62+
[nil] + tts.availableVoices
63+
.filter { $0.language.removingRegion() == config.defaultLanguage }
6364
}
6465

6566
lazy var availableLanguages: [Language] =
6667
tts.availableVoices
67-
.map { $0.language }
68+
.map { $0.language.removingRegion() }
6869
.removingDuplicates()
69-
.sorted { $0.localizedName < $1.localizedName }
70+
.sorted { $0.localizedDescription() < $1.localizedDescription() }
7071

7172
@objc func play() {
7273
navigator.findLocationOfFirstVisibleContent { [self] locator in

0 commit comments

Comments
 (0)