Skip to content

Instantly share code, notes, and snippets.

@blacklee
Created April 10, 2018 05:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save blacklee/44f78528b7258518ca0a917d4b2aa0f0 to your computer and use it in GitHub Desktop.
Save blacklee/44f78528b7258518ca0a917d4b2aa0f0 to your computer and use it in GitHub Desktop.
Get AVSpeechSynthesisVoice by [NSLocale preferredLanguages]
NSLocale AVSpeechSynthesisVoice.language
af en-US
af_NA en-US
af_ZA en-ZA
agq en-US
agq_CM en-US
ak en-US
ak_GH en-US
am en-US
am_ET en-US
ar ar-SA
ar_001 ar-SA
ar_AE ar-SA
ar_BH ar-SA
ar_DJ ar-SA
ar_DZ ar-SA
ar_EG ar-SA
ar_EH ar-SA
ar_ER ar-SA
ar_IL ar-SA
ar_IQ ar-SA
ar_JO ar-SA
ar_KM ar-SA
ar_KW ar-SA
ar_LB ar-SA
ar_LY ar-SA
ar_MA ar-SA
ar_MR ar-SA
ar_OM ar-SA
ar_PS ar-SA
ar_QA ar-SA
ar_SA ar-SA
ar_SD ar-SA
ar_SO ar-SA
ar_SS ar-SA
ar_SY ar-SA
ar_TD ar-SA
ar_TN ar-SA
ar_YE ar-SA
as en-US
as_IN hi-IN
asa en-US
asa_TZ en-US
ast en-US
ast_ES es-ES
az en-US
az_Cyrl en-US
az_Cyrl_AZ en-US
az_Latn en-US
az_Latn_AZ en-US
bas en-US
bas_CM en-US
be en-US
be_BY en-US
bem en-US
bem_ZM en-US
bez en-US
bez_TZ en-US
bg en-US
bg_BG en-US
bm en-US
bm_ML en-US
bn en-US
bn_BD en-US
bn_IN hi-IN
bo en-US
bo_CN zh-CN
bo_IN hi-IN
br en-US
br_FR fr-FR
brx en-US
brx_IN hi-IN
bs en-US
bs_Cyrl en-US
bs_Cyrl_BA en-US
bs_Latn en-US
bs_Latn_BA en-US
ca en-US
ca_AD en-US
ca_ES es-ES
ca_FR fr-FR
ca_IT it-IT
ce en-US
ce_RU ru-RU
cgg en-US
cgg_UG en-US
chr en-US
chr_US en-US
ckb en-US
ckb_IQ en-US
ckb_IR en-US
cs cs-CZ
cs_CZ cs-CZ
cy en-US
cy_GB en-GB
da da-DK
da_DK da-DK
da_GL da-DK
dav en-US
dav_KE en-US
de de-DE
de_AT de-DE
de_BE de-DE
de_CH de-DE
de_DE de-DE
de_IT de-DE
de_LI de-DE
de_LU de-DE
dje en-US
dje_NE en-US
dsb en-US
dsb_DE de-DE
dua en-US
dua_CM en-US
dyo en-US
dyo_SN en-US
dz en-US
dz_BT en-US
ebu en-US
ebu_KE en-US
ee en-US
ee_GH en-US
ee_TG en-US
el el-GR
el_CY el-GR
el_GR el-GR
en en-US
en_001 en-US
en_150 en-US
en_AD en-US
en_AG en-US
en_AI en-US
en_AL en-US
en_AR en-US
en_AS en-US
en_AT en-US
en_AU en-AU
en_BA en-US
en_BB en-US
en_BD en-US
en_BE en-US
en_BG en-US
en_BI en-US
en_BM en-US
en_BR en-US
en_BS en-US
en_BW en-US
en_BZ en-US
en_CA en-US
en_CC en-US
en_CH en-US
en_CK en-US
en_CM en-US
en_CN en-US
en_CX en-US
en_CY en-US
en_CZ en-US
en_DE en-US
en_DG en-US
en_DK en-US
en_DM en-US
en_EE en-US
en_ER en-US
en_ES en-US
en_FI en-US
en_FJ en-US
en_FK en-US
en_FM en-US
en_FR en-US
en_GB en-GB
en_GD en-US
en_GG en-US
en_GH en-US
en_GI en-US
en_GM en-US
en_GR en-US
en_GU en-US
en_GY en-US
en_HK en-US
en_HR en-US
en_HU en-US
en_ID en-US
en_IE en-IE
en_IL en-US
en_IM en-US
en_IN en-US
en_IO en-US
en_IS en-US
en_IT en-US
en_JE en-US
en_JM en-US
en_JP en-US
en_KE en-US
en_KI en-US
en_KN en-US
en_KR en-US
en_KY en-US
en_LC en-US
en_LR en-US
en_LS en-US
en_LT en-US
en_LU en-US
en_LV en-US
en_ME en-US
en_MG en-US
en_MH en-US
en_MO en-US
en_MP en-US
en_MS en-US
en_MT en-US
en_MU en-US
en_MV en-US
en_MW en-US
en_MY en-US
en_NA en-US
en_NF en-US
en_NG en-US
en_NL en-US
en_NO en-US
en_NR en-US
en_NU en-US
en_NZ en-US
en_PG en-US
en_PH en-US
en_PK en-US
en_PL en-US
en_PN en-US
en_PR en-US
en_PT en-US
en_PW en-US
en_RO en-US
en_RU en-US
en_RW en-US
en_SA en-US
en_SB en-US
en_SC en-US
en_SD en-US
en_SE en-US
en_SG en-US
en_SH en-US
en_SI en-US
en_SK en-US
en_SL en-US
en_SS en-US
en_SX en-US
en_SZ en-US
en_TC en-US
en_TH en-US
en_TK en-US
en_TO en-US
en_TR en-US
en_TT en-US
en_TV en-US
en_TW en-US
en_TZ en-US
en_UG en-US
en_UM en-US
en_US en-US
en_US_POSIX en-US
en_VC en-US
en_VG en-US
en_VI en-US
en_VU en-US
en_WS en-US
en_ZA en-ZA
en_ZM en-US
en_ZW en-US
eo en-US
es es-ES
es_419 es-ES
es_AG es-ES
es_AI es-ES
es_AR es-ES
es_AW es-ES
es_BB es-ES
es_BL es-ES
es_BM es-ES
es_BO es-ES
es_BQ es-ES
es_BR es-ES
es_BS es-ES
es_BZ es-ES
es_CL es-ES
es_CO es-ES
es_CR es-ES
es_CU es-ES
es_CW es-ES
es_DM es-ES
es_DO es-ES
es_EA es-ES
es_EC es-ES
es_ES es-ES
es_FK es-ES
es_GD es-ES
es_GF es-ES
es_GL es-ES
es_GP es-ES
es_GQ es-ES
es_GT es-ES
es_GY es-ES
es_HN es-ES
es_HT es-ES
es_IC es-ES
es_KN es-ES
es_KY es-ES
es_LC es-ES
es_MF es-ES
es_MQ es-ES
es_MS es-ES
es_MX es-MX
es_NI es-ES
es_PA es-ES
es_PE es-ES
es_PH es-ES
es_PM es-ES
es_PR es-ES
es_PY es-ES
es_SR es-ES
es_SV es-ES
es_SX es-ES
es_TC es-ES
es_TT es-ES
es_US es-ES
es_UY es-ES
es_VC es-ES
es_VE es-ES
es_VG es-ES
es_VI es-ES
et en-US
et_EE en-US
eu en-US
eu_ES es-ES
ewo en-US
ewo_CM en-US
fa en-US
fa_AF en-US
fa_IR en-US
ff en-US
ff_CM en-US
ff_GN en-US
ff_MR en-US
ff_SN en-US
fi fi-FI
fi_FI fi-FI
fil en-US
fil_PH en-US
fo en-US
fo_DK da-DK
fo_FO en-US
fr fr-FR
fr_BE fr-FR
fr_BF fr-FR
fr_BI fr-FR
fr_BJ fr-FR
fr_BL fr-FR
fr_CA fr-CA
fr_CD fr-FR
fr_CF fr-FR
fr_CG fr-FR
fr_CH fr-FR
fr_CI fr-FR
fr_CM fr-FR
fr_DJ fr-FR
fr_DZ fr-FR
fr_FR fr-FR
fr_GA fr-FR
fr_GF fr-FR
fr_GN fr-FR
fr_GP fr-FR
fr_GQ fr-FR
fr_HT fr-FR
fr_KM fr-FR
fr_LU fr-FR
fr_MA fr-FR
fr_MC fr-FR
fr_MF fr-FR
fr_MG fr-FR
fr_ML fr-FR
fr_MQ fr-FR
fr_MR fr-FR
fr_MU fr-FR
fr_NC fr-FR
fr_NE fr-FR
fr_PF fr-FR
fr_PM fr-FR
fr_RE fr-FR
fr_RW fr-FR
fr_SC fr-FR
fr_SN fr-FR
fr_SY fr-FR
fr_TD fr-FR
fr_TG fr-FR
fr_TN fr-FR
fr_VU fr-FR
fr_WF fr-FR
fr_YT fr-FR
fur en-US
fur_IT it-IT
fy en-US
fy_NL nl-NL
ga en-US
ga_IE en-IE
gd en-US
gd_GB en-GB
gl en-US
gl_ES es-ES
gsw en-US
gsw_CH en-US
gsw_FR fr-FR
gsw_LI en-US
gu en-US
gu_IN hi-IN
guz en-US
guz_KE en-US
gv en-US
gv_IM en-US
ha en-US
ha_GH en-US
ha_NE en-US
ha_NG en-US
haw en-US
haw_US en-US
he he-IL
he_IL he-IL
hi hi-IN
hi_IN hi-IN
hr en-US
hr_BA en-US
hr_HR en-US
hsb en-US
hsb_DE de-DE
hu hu-HU
hu_HU hu-HU
hy en-US
hy_AM en-US
id id-ID
id_ID id-ID
ig en-US
ig_NG en-US
ii en-US
ii_CN zh-CN
is en-US
is_IS en-US
it it-IT
it_CH it-IT
it_IT it-IT
it_SM it-IT
it_VA it-IT
iu en-US
iu_CA fr-CA
ja ja-JP
ja_JP ja-JP
jgo en-US
jgo_CM en-US
jmc en-US
jmc_TZ en-US
ka en-US
ka_GE en-US
kab en-US
kab_DZ en-US
kam en-US
kam_KE en-US
kde en-US
kde_TZ en-US
kea en-US
kea_CV en-US
khq en-US
khq_ML en-US
ki en-US
ki_KE en-US
kk en-US
kk_KZ en-US
kkj en-US
kkj_CM en-US
kl en-US
kl_GL en-US
kln en-US
kln_KE en-US
km en-US
km_KH en-US
kn en-US
kn_IN hi-IN
ko ko-KR
ko_KP ko-KR
ko_KR ko-KR
kok en-US
kok_IN hi-IN
ks en-US
ks_IN hi-IN
ksb en-US
ksb_TZ en-US
ksf en-US
ksf_CM en-US
ksh en-US
ksh_DE de-DE
kw en-US
kw_GB en-GB
ky en-US
ky_KG en-US
lag en-US
lag_TZ en-US
lb en-US
lb_LU en-US
lg en-US
lg_UG en-US
lkt en-US
lkt_US en-US
ln en-US
ln_AO en-US
ln_CD en-US
ln_CF en-US
ln_CG en-US
lo en-US
lo_LA en-US
lrc en-US
lrc_IQ en-US
lrc_IR en-US
lt en-US
lt_LT en-US
lu en-US
lu_CD en-US
luo en-US
luo_KE en-US
luy en-US
luy_KE en-US
lv en-US
lv_LV en-US
mas en-US
mas_KE en-US
mas_TZ en-US
mer en-US
mer_KE en-US
mfe en-US
mfe_MU en-US
mg en-US
mg_MG en-US
mgh en-US
mgh_MZ en-US
mgo en-US
mgo_CM en-US
mk en-US
mk_MK en-US
ml en-US
ml_IN hi-IN
mn en-US
mn_MN en-US
mr en-US
mr_IN hi-IN
ms en-US
ms_Arab en-US
ms_Arab_BN en-US
ms_Arab_MY en-US
ms_BN en-US
ms_MY en-US
ms_SG en-US
mt en-US
mt_MT en-US
mua en-US
mua_CM en-US
my en-US
my_MM en-US
mzn en-US
mzn_IR en-US
naq en-US
naq_NA en-US
nb en-US
nb_NO no-NO
nb_SJ en-US
nd en-US
nd_ZW en-US
nds en-US
nds_DE de-DE
nds_NL nl-NL
ne en-US
ne_IN hi-IN
ne_NP en-US
nl nl-NL
nl_AW nl-NL
nl_BE nl-BE
nl_BQ nl-NL
nl_CW nl-NL
nl_NL nl-NL
nl_SR nl-NL
nl_SX nl-NL
nmg en-US
nmg_CM en-US
nn en-US
nn_NO no-NO
nnh en-US
nnh_CM en-US
nus en-US
nus_SS en-US
nyn en-US
nyn_UG en-US
om en-US
om_ET en-US
om_KE en-US
or en-US
or_IN hi-IN
os en-US
os_GE en-US
os_RU ru-RU
pa en-US
pa_Arab en-US
pa_Arab_PK en-US
pa_Guru en-US
pa_Guru_IN hi-IN
pl pl-PL
pl_PL pl-PL
ps en-US
ps_AF en-US
pt pt-PT
pt_AO pt-PT
pt_BR pt-BR
pt_CH pt-PT
pt_CV pt-PT
pt_FR pt-PT
pt_GQ pt-PT
pt_GW pt-PT
pt_LU pt-PT
pt_MO pt-PT
pt_MZ pt-PT
pt_PT pt-PT
pt_ST pt-PT
pt_TL pt-PT
qu en-US
qu_BO en-US
qu_EC en-US
qu_PE en-US
rm en-US
rm_CH en-US
rn en-US
rn_BI en-US
ro ro-RO
ro_MD ro-RO
ro_RO ro-RO
rof en-US
rof_TZ en-US
ru ru-RU
ru_BY ru-RU
ru_KG ru-RU
ru_KZ ru-RU
ru_MD ru-RU
ru_RU ru-RU
ru_UA ru-RU
rw en-US
rw_RW en-US
rwk en-US
rwk_TZ en-US
sah en-US
sah_RU ru-RU
saq en-US
saq_KE en-US
sbp en-US
sbp_TZ en-US
se en-US
se_FI fi-FI
se_NO no-NO
se_SE sv-SE
seh en-US
seh_MZ en-US
ses en-US
ses_ML en-US
sg en-US
sg_CF en-US
shi en-US
shi_Latn en-US
shi_Latn_MA en-US
shi_Tfng en-US
shi_Tfng_MA en-US
si en-US
si_LK en-US
sk sk-SK
sk_SK sk-SK
sl en-US
sl_SI en-US
smn en-US
smn_FI fi-FI
sn en-US
sn_ZW en-US
so en-US
so_DJ en-US
so_ET en-US
so_KE en-US
so_SO en-US
sq en-US
sq_AL en-US
sq_MK en-US
sq_XK en-US
sr en-US
sr_Cyrl en-US
sr_Cyrl_BA en-US
sr_Cyrl_ME en-US
sr_Cyrl_RS en-US
sr_Cyrl_XK en-US
sr_Latn en-US
sr_Latn_BA en-US
sr_Latn_ME en-US
sr_Latn_RS en-US
sr_Latn_XK en-US
sv sv-SE
sv_AX sv-SE
sv_FI sv-SE
sv_SE sv-SE
sw en-US
sw_CD en-US
sw_KE en-US
sw_TZ en-US
sw_UG en-US
ta en-US
ta_IN hi-IN
ta_LK en-US
ta_MY en-US
ta_SG en-US
te en-US
te_IN hi-IN
teo en-US
teo_KE en-US
teo_UG en-US
tg en-US
tg_TJ en-US
th th-TH
th_TH th-TH
ti en-US
ti_ER en-US
ti_ET en-US
tk en-US
tk_TM en-US
to en-US
to_TO en-US
tr tr-TR
tr_CY tr-TR
tr_TR tr-TR
twq en-US
twq_NE en-US
tzm en-US
tzm_MA en-US
ug en-US
ug_CN zh-CN
uk en-US
uk_UA en-US
ur en-US
ur_IN hi-IN
ur_PK en-US
uz en-US
uz_Arab en-US
uz_Arab_AF en-US
uz_Cyrl en-US
uz_Cyrl_UZ en-US
uz_Latn en-US
uz_Latn_UZ en-US
vai en-US
vai_Latn en-US
vai_Latn_LR en-US
vai_Vaii en-US
vai_Vaii_LR en-US
vi en-US
vi_VN en-US
vun en-US
vun_TZ en-US
wae en-US
wae_CH en-US
xog en-US
xog_UG en-US
yav en-US
yav_CM en-US
yi en-US
yi_001 en-US
yo en-US
yo_BJ en-US
yo_NG en-US
yue en-US
yue_Hans en-US
yue_Hans_CN zh-CN
yue_Hant en-US
yue_Hant_HK zh-HK
zgh en-US
zgh_MA en-US
zh zh-CN
zh_Hans zh-CN
zh_Hans_CN zh-CN
zh_Hans_HK zh-HK
zh_Hans_MO zh-CN
zh_Hans_SG zh-CN
zh_Hant zh-CN
zh_Hant_HK zh-HK
zh_Hant_MO zh-CN
zh_Hant_TW zh-TW
zu en-US
zu_ZA en-ZA
AVSpeechSynthesisVoice *SpeechVoiceFromLocaleLanguageCode(NSString *langCode) {
NSArray<NSString*> *langCodeParts = [[langCode stringByReplacingOccurrencesOfString:@"-" withString:@"_"] componentsSeparatedByString:@"_"];
NSArray<AVSpeechSynthesisVoice*> *availTTSVoices = [AVSpeechSynthesisVoice speechVoices];
NSMutableArray<AVSpeechSynthesisVoice*> *filteredVoices = [NSMutableArray array];
AVSpeechSynthesisVoice *selectedVoice = nil, *defaultVoice = nil;
// first round filter, uses prefix.
for (AVSpeechSynthesisVoice *voice in availTTSVoices) {
if (!defaultVoice && [voice.language isEqualToString:@"en-US"]) {
defaultVoice = voice;
}
if ([voice.language hasPrefix:langCodeParts.firstObject]) {
[filteredVoices addObject:voice];
}
}
if (!filteredVoices.count) {
// second round filter, uses other parts
for (AVSpeechSynthesisVoice *voice in availTTSVoices) {
for (NSUInteger idx = 1; idx < langCodeParts.count; idx++) {
if ([voice.language rangeOfString:langCodeParts[idx]].location != NSNotFound) {
[filteredVoices addObject:voice];
break;
}
}
}
}
if (filteredVoices.count) { // now start pick
// first round, similar to equal
for (AVSpeechSynthesisVoice *voice in filteredVoices) {
for (NSUInteger idx = 1; idx < langCodeParts.count; idx++) {
if ([voice.language rangeOfString:langCodeParts[idx]].location != NSNotFound) {
selectedVoice = voice;
break;
}
}
}
if (!selectedVoice) {
NSDictionary *multiLangsDict = @{@"en": @"en-US", @"fr": @"fr-FR", @"es": @"es-ES", @"nl": @"nl-NL", @"zh": @"zh-CN", @"pt": @"pt-PT"};
// if the prefix is in the keys, then pick a voice by the relative value,
// do not pick by random, because it may pick [en-AU] instead of [en-US] for [en_XX]
if ([multiLangsDict.allKeys indexOfObject:langCodeParts.firstObject] != NSNotFound) {
NSString *voiceLang = multiLangsDict[langCodeParts.firstObject];
for (AVSpeechSynthesisVoice *voice in filteredVoices) {
if ([voice.language isEqualToString:voiceLang]) {
selectedVoice = voice;
break;
}
}
}
}
if (!selectedVoice) {
selectedVoice = filteredVoices.firstObject;
}
}
return selectedVoice ?: defaultVoice;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment