Take integration title from manifest if not translated (#34283)

This commit is contained in:
Paulus Schoutsen 2020-04-16 08:38:54 -07:00 committed by GitHub
parent 3d9ae1b8bd
commit 94a3cec4bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 26 deletions

View File

@ -1,5 +1,4 @@
{ {
"title": "Philips Hue",
"config": { "config": {
"step": { "step": {
"init": { "init": {

View File

@ -1,8 +1,11 @@
"""Translation string lookup helpers.""" """Translation string lookup helpers."""
import asyncio
import logging import logging
from typing import Any, Dict, Iterable, Optional from typing import Any, Dict, Iterable, Optional
from homeassistant.core import callback
from homeassistant.loader import ( from homeassistant.loader import (
Integration,
async_get_config_flows, async_get_config_flows,
async_get_integration, async_get_integration,
bind_hass, bind_hass,
@ -32,8 +35,9 @@ def flatten(data: Dict) -> Dict[str, Any]:
return recursive_flatten("", data) return recursive_flatten("", data)
async def component_translation_file( @callback
hass: HomeAssistantType, component: str, language: str def component_translation_file(
component: str, language: str, integration: Integration
) -> Optional[str]: ) -> Optional[str]:
"""Return the translation json file location for a component. """Return the translation json file location for a component.
@ -49,9 +53,6 @@ async def component_translation_file(
domain = parts[-1] domain = parts[-1]
is_platform = len(parts) == 2 is_platform = len(parts) == 2
integration = await async_get_integration(hass, domain)
assert integration is not None, domain
if is_platform: if is_platform:
filename = f"{parts[0]}.{language}.json" filename = f"{parts[0]}.{language}.json"
return str(integration.file_path / ".translations" / filename) return str(integration.file_path / ".translations" / filename)
@ -105,26 +106,47 @@ def build_resources(
async def async_get_component_resources( async def async_get_component_resources(
hass: HomeAssistantType, language: str hass: HomeAssistantType, language: str
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Return translation resources for all components.""" """Return translation resources for all components.
if TRANSLATION_STRING_CACHE not in hass.data:
hass.data[TRANSLATION_STRING_CACHE] = {}
if language not in hass.data[TRANSLATION_STRING_CACHE]:
hass.data[TRANSLATION_STRING_CACHE][language] = {}
translation_cache = hass.data[TRANSLATION_STRING_CACHE][language]
# Get the set of components We go through all loaded components and platforms:
- see if they have already been loaded (exist in translation_cache)
- load them if they have not been loaded yet
- write them to cache
- flatten the cache and return
"""
# Get cache for this language
cache = hass.data.setdefault(TRANSLATION_STRING_CACHE, {})
translation_cache = cache.setdefault(language, {})
# Get the set of components to check
components = hass.config.components | await async_get_config_flows(hass) components = hass.config.components | await async_get_config_flows(hass)
# Calculate the missing components # Calculate the missing components and platforms
missing_components = components - set(translation_cache) missing_loaded = components - set(translation_cache)
missing_domains = {loaded.split(".")[-1] for loaded in missing_loaded}
missing_integrations = dict(
zip(
missing_domains,
await asyncio.gather(
*[async_get_integration(hass, domain) for domain in missing_domains]
),
)
)
# Determine paths of missing components/platforms
missing_files = {} missing_files = {}
for component in missing_components: for loaded in missing_loaded:
path = await component_translation_file(hass, component, language) parts = loaded.split(".")
domain = parts[-1]
integration = missing_integrations[domain]
path = component_translation_file(loaded, language, integration)
# No translation available # No translation available
if path is None: if path is None:
translation_cache[component] = {} translation_cache[loaded] = {}
else: else:
missing_files[component] = path missing_files[loaded] = path
# Load missing files # Load missing files
if missing_files: if missing_files:
@ -134,6 +156,14 @@ async def async_get_component_resources(
assert load_translations_job is not None assert load_translations_job is not None
loaded_translations = await load_translations_job loaded_translations = await load_translations_job
# Translations that miss "title" will get integration put in.
for loaded, translations in loaded_translations.items():
if "." in loaded:
continue
if "title" not in translations:
translations["title"] = missing_integrations[loaded].name
# Update cache # Update cache
translation_cache.update(loaded_translations) translation_cache.update(loaded_translations)

View File

@ -1,12 +1,14 @@
"""Test the translation helper.""" """Test the translation helper."""
# pylint: disable=protected-access import asyncio
from os import path from os import path
from unittest.mock import patch import pathlib
from asynctest import Mock, patch
import pytest import pytest
from homeassistant.generated import config_flows from homeassistant.generated import config_flows
import homeassistant.helpers.translation as translation import homeassistant.helpers.translation as translation
from homeassistant.loader import async_get_integration
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import mock_coro from tests.common import mock_coro
@ -43,14 +45,28 @@ async def test_component_translation_file(hass):
assert await async_setup_component(hass, "test_standalone", {"test_standalone"}) assert await async_setup_component(hass, "test_standalone", {"test_standalone"})
assert await async_setup_component(hass, "test_package", {"test_package"}) assert await async_setup_component(hass, "test_package", {"test_package"})
(
int_test,
int_test_embedded,
int_test_standalone,
int_test_package,
) = await asyncio.gather(
async_get_integration(hass, "test"),
async_get_integration(hass, "test_embedded"),
async_get_integration(hass, "test_standalone"),
async_get_integration(hass, "test_package"),
)
assert path.normpath( assert path.normpath(
await translation.component_translation_file(hass, "switch.test", "en") translation.component_translation_file("switch.test", "en", int_test)
) == path.normpath( ) == path.normpath(
hass.config.path("custom_components", "test", ".translations", "switch.en.json") hass.config.path("custom_components", "test", ".translations", "switch.en.json")
) )
assert path.normpath( assert path.normpath(
await translation.component_translation_file(hass, "switch.test_embedded", "en") translation.component_translation_file(
"switch.test_embedded", "en", int_test_embedded
)
) == path.normpath( ) == path.normpath(
hass.config.path( hass.config.path(
"custom_components", "test_embedded", ".translations", "switch.en.json" "custom_components", "test_embedded", ".translations", "switch.en.json"
@ -58,12 +74,14 @@ async def test_component_translation_file(hass):
) )
assert ( assert (
await translation.component_translation_file(hass, "test_standalone", "en") translation.component_translation_file(
"test_standalone", "en", int_test_standalone
)
is None is None
) )
assert path.normpath( assert path.normpath(
await translation.component_translation_file(hass, "test_package", "en") translation.component_translation_file("test_package", "en", int_test_package)
) == path.normpath( ) == path.normpath(
hass.config.path( hass.config.path(
"custom_components", "test_package", ".translations", "en.json" "custom_components", "test_package", ".translations", "en.json"
@ -118,6 +136,8 @@ async def test_get_translations(hass, mock_config_flows):
async def test_get_translations_loads_config_flows(hass, mock_config_flows): async def test_get_translations_loads_config_flows(hass, mock_config_flows):
"""Test the get translations helper loads config flow translations.""" """Test the get translations helper loads config flow translations."""
mock_config_flows.append("component1") mock_config_flows.append("component1")
integration = Mock(file_path=pathlib.Path(__file__))
integration.name = "Component 1"
with patch.object( with patch.object(
translation, "component_translation_file", return_value=mock_coro("bla.json") translation, "component_translation_file", return_value=mock_coro("bla.json")
@ -125,6 +145,12 @@ async def test_get_translations_loads_config_flows(hass, mock_config_flows):
translation, translation,
"load_translations_files", "load_translations_files",
return_value={"component1": {"hello": "world"}}, return_value={"component1": {"hello": "world"}},
), patch(
"homeassistant.helpers.translation.async_get_integration",
return_value=integration,
): ):
translations = await translation.async_get_translations(hass, "en") translations = await translation.async_get_translations(hass, "en")
assert translations == {"component.component1.hello": "world"} assert translations == {
"component.component1.title": "Component 1",
"component.component1.hello": "world",
}