mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 17:27:10 +00:00
Take integration title from manifest if not translated (#34283)
This commit is contained in:
parent
3d9ae1b8bd
commit
94a3cec4bf
@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"title": "Philips Hue",
|
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"init": {
|
"init": {
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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",
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user