iOS 13 で AVSpeechSynthesisVoice.currentLanguageCode() の挙動が変更に(多分バグ)
Xcode 11, iOS 12.2, iOS 13.1, iOS 13.1.3 で動作確認
デフォルトの読み上げ音声の言語の確認
AVSpeechSynthesisVoice.currentLanguageCode()
参考に読み上げ確認の実装
(AVSpeechSynthesisVoice
を設定せずデフォルト状態での読み上げ)
let synthesize = AVSpeechSynthesizer() let utterance = AVSpeechUtterance(string: "1 2 3") // utterance.voice = AVSpeechSynthesisVoice() synthesize.speak(utterance)
iOS 12
デバイスの言語設定の優先順序に基づいて LanguageCode が返される。
英語にしか対応していない未ローカライズのアプリでもデバイスの言語設定が 日本語(日本)
になっていれば AVSpeechSynthesisVoice.currentLanguageCode()
は ja-JP
を返しデフォルトで日本語で読み上げが行われる。
iOS 13
デバイスの言語設定ではなくアプリの表示言語が反映される様に挙動が変わっている。
例えば英語にしか対応していない未ローカライズではデバイスの言語設定が 日本語(日本)
になっていても AVSpeechSynthesisVoice.currentLanguageCode()
は en-US
を返す様になってしまっており、デバイスの設定言語で読み上げされないという現象が発生してしまっている。
日本語設定のデバイスが手元にあれば以下で簡単に挙動を確認できる
- Xcode のテンプレートからアプリを作成して(ローカライズなど何もせず)
AVSpeechSynthesisVoice.currentLanguageCode()
を出力する。 - iOS 12 では
ja-JP
、iOS 13 ではen-US
になる。 - iOS 12 では
AVSpeechSynthesizer
はデフォルトでデバイス設定の日本語で読み上げする。 - iOS 13 ではデバイス設定が日本語でもアプリが日本語ローカライズされていないと
AVSpeechSynthesizer
はデフォルトで英語で読み上げする。
iOS 12 | iOS 13 | |
---|---|---|
Settings>General>Language & Region>iPhone Language | 日本語(日本) | 日本語(日本) |
Locale.preferredLanguages.first | ja-JP | ja-JP |
AVSpeechSynthesisVoice.currentLanguageCode() | ja-JP | en-US |
仕様変更かバグか
公式ドキュメント
currentLanguageCode() - AVSpeechSynthesisVoice | Apple Developer Documentation
Return Value An NSString object containing the BCP 47 language and locale code for the user’s current locale. Discussion This code reflects the user’s language and region preferences as selected in the Settings app.
設定アプリで選択した言語と地域が反映されると書いてある。
よって iOS 13 での挙動は意図しないものなので多分バグ。*1*2
Workaround
iOS 13 では iOS 12 の処理を真似た独自実装で currentLanguageCode
を生成して返す様に試みる。
Workaround for that AVSpeechSynthesisVoice current ...
実装内容1
AVSpeechSynthesisVoice
の言語コードについては Objective-C ヘッダに以下のコメントの記述がある
Use a BCP-47 language tag to specify the desired language and region.
システムの優先言語設定を取得できる Locale.preferredLanguages
の値は zh-Hant-HK
の形式でこの場合
zh
がlanguageCode
Hant
がscriptCode
HK
がregionCode
となる。
AVSpeechSynthesisVoice.speechVoices()
で取得できる利用可能な音声の language
の形式は zh-HK
となっており scriptCode
は存在しない。
このためわざわざ preferredLanguages
から一度 Locale
を作成して languageCode
と regionCode
を取り出して再構築するという手順を踏んでいる。
実装内容2
オリジナルの currentLanguageCode
の挙動を確認したところ、speechVoices
のリストに存在しない言語と地域がデバイスに設定されている時は en-US
を返す様になっていたのでそこを踏襲しつつ、現在のデバイス設定の言語コードと地域コードから AVSpeechSynthesisVoice
用の言語コードを作成する様にしている。
例えば uk
ウクライナ語、vi
ベトナム語などOSで選択可能な言語で且つ読み上げ不可能な言語(speechVoices
のリストに存在していない言語)を選択した場合、currentLanguageCode
は en-US
を返す。
また ja-US
など地域が不一致のパターンにおいても en-US
を返す。*3
macOS
追試で macOS でも調べてみたところ、Catalina では
languageCode
はアプリの言語regionCode
はシステム環境設定の地域設定
となる様で日本語環境では AVSpeechSynthesisVoice.currentLanguageCode
が en-JP
を返した。
iOS の様に利用可能な音声リストが無い場合は en-US
を返す様な処理が入っていないっぽい。
そして en-JP
で読み上げ可能な言語が無いのでデフォルト設定のままでは読み上げされないという現象が起きていた。
よって en-US
なり ja-JP
なりの利用可能な AVSpeechSynthesisVoice
を自分で設定してやる必要がある。*4
ちなみに Mojave ではそもそも AVSpeechSynthesizer
AVSpeechSynthesisVoice
AVSpeechUtterance
あたりは import できてコンパイルもできるけど一切機能しないという問題があるらしいので挙動の検証以前の問題だった。
Mojave では旧来の NSSpeechSynthesizer
を使うのが正解っぽい。
更新
Workaround を修正
Workaround for that AVSpeechSynthesisVoice current ...
Locale
の identifier
には regionCode
が含まれていない場合があるので対応した。
具体的には en-US
ja-JP
以外に en
ja
のケースがある。
また、前述のデバイス言語から読み上げ音声への変換の例外(zh-Hans
から zh-CN
といったケース)に対応するために、一旦 AVSpeechSynthesisVoice
を作成して取得される language
を戻り値として使用する様に変更した。