mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
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:
parent
f4a58c7dc7
commit
5533368171
306
homeassistant/components/google_translate/const.py
Normal file
306
homeassistant/components/google_translate/const.py
Normal 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"),
|
||||||
|
}
|
@ -7,112 +7,41 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider
|
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 = [
|
_LOGGER = logging.getLogger(__name__)
|
||||||
"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",
|
|
||||||
]
|
|
||||||
|
|
||||||
DEFAULT_LANG = "en"
|
DEFAULT_LANG = "en"
|
||||||
|
|
||||||
|
SUPPORT_OPTIONS = ["tld"]
|
||||||
|
|
||||||
|
DEFAULT_TLD = "com"
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
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):
|
async def async_get_engine(hass, config, discovery_info=None):
|
||||||
"""Set up Google speech component."""
|
"""Set up Google speech component."""
|
||||||
return GoogleProvider(hass, config[CONF_LANG])
|
return GoogleProvider(hass, config[CONF_LANG], config["tld"])
|
||||||
|
|
||||||
|
|
||||||
class GoogleProvider(Provider):
|
class GoogleProvider(Provider):
|
||||||
"""The Google speech API provider."""
|
"""The Google speech API provider."""
|
||||||
|
|
||||||
def __init__(self, hass, lang):
|
def __init__(self, hass, lang, tld):
|
||||||
"""Init Google TTS service."""
|
"""Init Google TTS service."""
|
||||||
self.hass = hass
|
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"
|
self.name = "Google"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -125,9 +54,20 @@ class GoogleProvider(Provider):
|
|||||||
"""Return list of supported languages."""
|
"""Return list of supported languages."""
|
||||||
return SUPPORT_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):
|
def get_tts_audio(self, message, language, options=None):
|
||||||
"""Load TTS from google."""
|
"""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()
|
mp3_data = BytesIO()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -84,6 +84,7 @@ async def test_service_say(hass, mock_gtts, calls):
|
|||||||
assert mock_gtts.mock_calls[0][2] == {
|
assert mock_gtts.mock_calls[0][2] == {
|
||||||
"text": "There is a person at the front door.",
|
"text": "There is a person at the front door.",
|
||||||
"lang": "en",
|
"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] == {
|
assert mock_gtts.mock_calls[0][2] == {
|
||||||
"text": "There is a person at the front door.",
|
"text": "There is a person at the front door.",
|
||||||
"lang": "de",
|
"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] == {
|
assert mock_gtts.mock_calls[0][2] == {
|
||||||
"text": "There is a person at the front door.",
|
"text": "There is a person at the front door.",
|
||||||
"lang": "de",
|
"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",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user