Add dialect support to google_translate (#81768)

* Add TLD option support to google_translate

* Fix tests for added TLD option in google_translate

* Add Language to TLD mapping, Make tld configurable in google_translate

* Move const to dedicated file in google_translate
This commit is contained in:
Todd Johnson 2022-12-01 06:31:19 -06:00 committed by GitHub
parent f4a58c7dc7
commit 5533368171
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 428 additions and 90 deletions

View file

@ -0,0 +1,306 @@
"""Constant for google_translate integration."""
from dataclasses import dataclass
SUPPORT_LANGUAGES = [
"af",
"ar",
"bg",
"bn",
"bs",
"ca",
"cs",
"cy",
"da",
"de",
"el",
"en",
"eo",
"es",
"et",
"fi",
"fr",
"gu",
"hi",
"hr",
"hu",
"hy",
"id",
"is",
"it",
"iw",
"ja",
"jw",
"km",
"kn",
"ko",
"la",
"lv",
"mk",
"ml",
"mr",
"my",
"ne",
"nl",
"no",
"pl",
"pt",
"ro",
"ru",
"si",
"sk",
"sq",
"sr",
"su",
"sv",
"sw",
"ta",
"te",
"th",
"tl",
"tr",
"uk",
"ur",
"vi",
# dialects
"zh-CN",
"zh-cn",
"zh-tw",
"en-us",
"en-ca",
"en-uk",
"en-gb",
"en-au",
"en-gh",
"en-in",
"en-ie",
"en-nz",
"en-ng",
"en-ph",
"en-za",
"en-tz",
"fr-ca",
"fr-fr",
"pt-br",
"pt-pt",
"es-es",
"es-us",
]
SUPPORT_TLD = [
"com",
"ad",
"ae",
"com.af",
"com.ag",
"com.ai",
"al",
"am",
"co.ao",
"com.ar",
"as",
"at",
"com.au",
"az",
"ba",
"com.bd",
"be",
"bf",
"bg",
"com.bh",
"bi",
"bj",
"com.bn",
"com.bo",
"com.br",
"bs",
"bt",
"co.bw",
"by",
"com.bz",
"ca",
"cd",
"cf",
"cg",
"ch",
"ci",
"co.ck",
"cl",
"cm",
"cn",
"com.co",
"co.cr",
"com.cu",
"cv",
"com.cy",
"cz",
"de",
"dj",
"dk",
"dm",
"com.do",
"dz",
"com.ec",
"ee",
"com.eg",
"es",
"com.et",
"fi",
"com.fj",
"fm",
"fr",
"ga",
"ge",
"gg",
"com.gh",
"com.gi",
"gl",
"gm",
"gr",
"com.gt",
"gy",
"com.hk",
"hn",
"hr",
"ht",
"hu",
"co.id",
"ie",
"co.il",
"im",
"co.in",
"iq",
"is",
"it",
"je",
"com.jm",
"jo",
"co.jp",
"co.ke",
"com.kh",
"ki",
"kg",
"co.kr",
"com.kw",
"kz",
"la",
"com.lb",
"li",
"lk",
"co.ls",
"lt",
"lu",
"lv",
"com.ly",
"co.ma",
"md",
"me",
"mg",
"mk",
"ml",
"com.mm",
"mn",
"ms",
"com.mt",
"mu",
"mv",
"mw",
"com.mx",
"com.my",
"co.mz",
"com.na",
"com.ng",
"com.ni",
"ne",
"nl",
"no",
"com.np",
"nr",
"nu",
"co.nz",
"com.om",
"com.pa",
"com.pe",
"com.pg",
"com.ph",
"com.pk",
"pl",
"pn",
"com.pr",
"ps",
"pt",
"com.py",
"com.qa",
"ro",
"ru",
"rw",
"com.sa",
"com.sb",
"sc",
"se",
"com.sg",
"sh",
"si",
"sk",
"com.sl",
"sn",
"so",
"sm",
"sr",
"st",
"com.sv",
"td",
"tg",
"co.th",
"com.tj",
"tl",
"tm",
"tn",
"to",
"com.tr",
"tt",
"com.tw",
"co.tz",
"com.ua",
"co.ug",
"co.uk",
"com.uy",
"co.uz",
"com.vc",
"co.ve",
"vg",
"co.vi",
"com.vn",
"vu",
"ws",
"rs",
"co.za",
"co.zm",
"co.zw",
"cat",
]
@dataclass
class Dialect:
"""Language and TLD for a dialect supported by Google Translate."""
lang: str
tld: str
MAP_LANG_TLD: dict[str, Dialect] = {
"en-us": Dialect("en", "com"),
"en-gb": Dialect("en", "co.uk"),
"en-uk": Dialect("en", "co.uk"),
"en-au": Dialect("en", "com.au"),
"en-ca": Dialect("en", "ca"),
"en-in": Dialect("en", "co.in"),
"en-ie": Dialect("en", "ie"),
"en-za": Dialect("en", "co.za"),
"fr-ca": Dialect("fr", "ca"),
"fr-fr": Dialect("fr", "fr"),
"pt-br": Dialect("pt", "com.br"),
"pt-pt": Dialect("pt", "pt"),
"es-es": Dialect("es", "es"),
"es-us": Dialect("es", "com"),
}

View file

@ -7,112 +7,41 @@ import voluptuous as vol
from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider
_LOGGER = logging.getLogger(__name__)
from .const import MAP_LANG_TLD, SUPPORT_LANGUAGES, SUPPORT_TLD
SUPPORT_LANGUAGES = [
"af",
"ar",
"bg",
"bn",
"bs",
"ca",
"cs",
"cy",
"da",
"de",
"el",
"en",
"eo",
"es",
"et",
"fi",
"fr",
"gu",
"hi",
"hr",
"hu",
"hy",
"id",
"is",
"it",
"iw",
"ja",
"jw",
"km",
"kn",
"ko",
"la",
"lv",
"mk",
"ml",
"mr",
"my",
"ne",
"nl",
"no",
"pl",
"pt",
"ro",
"ru",
"si",
"sk",
"sq",
"sr",
"su",
"sv",
"sw",
"ta",
"te",
"th",
"tl",
"tr",
"uk",
"ur",
"vi",
# dialects
"zh-CN",
"zh-cn",
"zh-tw",
"en-us",
"en-ca",
"en-uk",
"en-gb",
"en-au",
"en-gh",
"en-in",
"en-ie",
"en-nz",
"en-ng",
"en-ph",
"en-za",
"en-tz",
"fr-ca",
"fr-fr",
"pt-br",
"pt-pt",
"es-es",
"es-us",
]
_LOGGER = logging.getLogger(__name__)
DEFAULT_LANG = "en"
SUPPORT_OPTIONS = ["tld"]
DEFAULT_TLD = "com"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES)}
{
vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES),
vol.Optional("tld", default=DEFAULT_TLD): vol.In(SUPPORT_TLD),
}
)
async def async_get_engine(hass, config, discovery_info=None):
"""Set up Google speech component."""
return GoogleProvider(hass, config[CONF_LANG])
return GoogleProvider(hass, config[CONF_LANG], config["tld"])
class GoogleProvider(Provider):
"""The Google speech API provider."""
def __init__(self, hass, lang):
def __init__(self, hass, lang, tld):
"""Init Google TTS service."""
self.hass = hass
self._lang = lang
if lang in MAP_LANG_TLD:
self._lang = MAP_LANG_TLD[lang].lang
self._tld = MAP_LANG_TLD[lang].tld
else:
self._lang = lang
self._tld = tld
self.name = "Google"
@property
@ -125,9 +54,20 @@ class GoogleProvider(Provider):
"""Return list of supported languages."""
return SUPPORT_LANGUAGES
@property
def supported_options(self):
"""Return a list of supported options."""
return SUPPORT_OPTIONS
def get_tts_audio(self, message, language, options=None):
"""Load TTS from google."""
tts = gTTS(text=message, lang=language)
tld = self._tld
if language in MAP_LANG_TLD:
tld = MAP_LANG_TLD[language].tld
language = MAP_LANG_TLD[language].lang
if options is not None and "tld" in options.keys():
tld = options["tld"]
tts = gTTS(text=message, lang=language, tld=tld)
mp3_data = BytesIO()
try:

View file

@ -84,6 +84,7 @@ async def test_service_say(hass, mock_gtts, calls):
assert mock_gtts.mock_calls[0][2] == {
"text": "There is a person at the front door.",
"lang": "en",
"tld": "com",
}
@ -112,6 +113,7 @@ async def test_service_say_german_config(hass, mock_gtts, calls):
assert mock_gtts.mock_calls[0][2] == {
"text": "There is a person at the front door.",
"lang": "de",
"tld": "com",
}
@ -141,6 +143,96 @@ async def test_service_say_german_service(hass, mock_gtts, calls):
assert mock_gtts.mock_calls[0][2] == {
"text": "There is a person at the front door.",
"lang": "de",
"tld": "com",
}
async def test_service_say_en_uk_config(hass, mock_gtts, calls):
"""Test service call say with en-uk code in the config."""
await async_setup_component(
hass,
tts.DOMAIN,
{tts.DOMAIN: {"platform": "google_translate", "language": "en-uk"}},
)
await hass.services.async_call(
tts.DOMAIN,
"google_translate_say",
{
"entity_id": "media_player.something",
tts.ATTR_MESSAGE: "There is a person at the front door.",
},
blocking=True,
)
assert len(calls) == 1
await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID])
assert len(mock_gtts.mock_calls) == 2
assert mock_gtts.mock_calls[0][2] == {
"text": "There is a person at the front door.",
"lang": "en",
"tld": "co.uk",
}
async def test_service_say_en_uk_service(hass, mock_gtts, calls):
"""Test service call say with en-uk code in the config."""
await async_setup_component(
hass,
tts.DOMAIN,
{tts.DOMAIN: {"platform": "google_translate"}},
)
await hass.services.async_call(
tts.DOMAIN,
"google_translate_say",
{
"entity_id": "media_player.something",
tts.ATTR_MESSAGE: "There is a person at the front door.",
tts.ATTR_LANGUAGE: "en-uk",
},
blocking=True,
)
assert len(calls) == 1
await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID])
assert len(mock_gtts.mock_calls) == 2
assert mock_gtts.mock_calls[0][2] == {
"text": "There is a person at the front door.",
"lang": "en",
"tld": "co.uk",
}
async def test_service_say_en_couk(hass, mock_gtts, calls):
"""Test service call say in co.uk tld accent."""
await async_setup_component(
hass, tts.DOMAIN, {tts.DOMAIN: {"platform": "google_translate"}}
)
await hass.services.async_call(
tts.DOMAIN,
"google_translate_say",
{
"entity_id": "media_player.something",
tts.ATTR_MESSAGE: "There is a person at the front door.",
tts.ATTR_OPTIONS: {"tld": "co.uk"},
},
blocking=True,
)
assert len(calls) == 1
url = await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID])
assert len(mock_gtts.mock_calls) == 2
assert url.endswith(".mp3")
assert mock_gtts.mock_calls[0][2] == {
"text": "There is a person at the front door.",
"lang": "en",
"tld": "co.uk",
}