Limit legacy state translations to custom components (#112295)

* Limit legacy state translations to custom components

We were trying to load **thousands** of `*.light.json`, `*.switch.json` files at run time that did not exist.

There have been replaced with entity translations: https://github.com/home-assistant/developers.home-assistant/pull/1557 https://github.com/home-assistant/core/pull/82701

https://github.com/home-assistant/core/pull/112023 will completely remove them, but
for now we will only load them for custom components to reduce the number
of files having to be examined

* reduce

* reduce

* reduce

* reduce

* comment

* coverage

* try to remove empty dict in loaded_translations fallback when missing
This commit is contained in:
J. Nick Koston 2024-03-05 12:27:45 -10:00 committed by GitHub
parent d34e2c1f12
commit fbabbc8f92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 106 additions and 26 deletions

View File

@ -165,20 +165,21 @@ async def _async_get_component_strings(
for language in languages:
files_to_load: dict[str, str] = {}
files_to_load_by_language[language] = files_to_load
loaded_translations: dict[str, Any] = {}
translations_by_language[language] = loaded_translations
translations_by_language[language] = {}
for loaded in components:
domain = loaded.partition(".")[0]
domain, _, platform = loaded.partition(".")
if not (integration := integrations.get(domain)):
continue
path = component_translation_path(loaded, language, integration)
# No translation available
if path is None:
loaded_translations[loaded] = {}
else:
if platform and integration.is_built_in:
# Legacy state translations are no longer used for built-in integrations
# and we avoid trying to load them. This is a temporary measure to allow
# them to keep working for custom integrations until we can fully remove
# them.
continue
if path := component_translation_path(loaded, language, integration):
files_to_load[loaded] = path
if not files_to_load:

View File

@ -335,20 +335,57 @@ async def test_get_translation_categories(hass: HomeAssistant) -> None:
assert "component.light.device_automation.action_type.turn_on" in translations
async def test_translation_merging(
async def test_legacy_platform_translations_not_used_built_in_integrations(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test we merge translations of two integrations."""
"""Test legacy platform translations are not used for built-in integrations."""
hass.config.components.add("moon.sensor")
hass.config.components.add("sensor")
load_requests = []
def mock_load_translations_files_by_language(files):
load_requests.append(files)
return {}
with patch(
"homeassistant.helpers.translation._load_translations_files_by_language",
mock_load_translations_files_by_language,
):
await translation.async_get_translations(hass, "en", "state")
assert len(load_requests) == 1
to_load = load_requests[0]
assert len(to_load) == 1
en_load = to_load["en"]
assert len(en_load) == 1
assert "sensor" in en_load
assert "moon.sensor" not in en_load
async def test_translation_merging_custom_components(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
enable_custom_integrations: None,
) -> None:
"""Test we merge translations of two integrations.
Legacy state translations only used for custom integrations.
"""
hass.config.components.add("test_legacy_state_translations.sensor")
hass.config.components.add("sensor")
orig_load_translations = translation._load_translations_files_by_language
def mock_load_translations_files(files):
"""Mock loading."""
result = orig_load_translations(files)
result["en"]["moon.sensor"] = {
"state": {"moon__phase": {"first_quarter": "First Quarter"}}
result["en"]["test_legacy_state_translations.sensor"] = {
"state": {
"test_legacy_state_translations__phase": {
"first_quarter": "First Quarter"
}
}
}
return result
@ -358,15 +395,20 @@ async def test_translation_merging(
):
translations = await translation.async_get_translations(hass, "en", "state")
assert "component.sensor.state.moon__phase.first_quarter" in translations
assert (
"component.sensor.state.test_legacy_state_translations__phase.first_quarter"
in translations
)
hass.config.components.add("season.sensor")
hass.config.components.add("test_legacy_state_translations_bad_data.sensor")
# Patch in some bad translation data
def mock_load_bad_translations_files(files):
"""Mock loading."""
result = orig_load_translations(files)
result["en"]["season.sensor"] = {"state": "bad data"}
result["en"]["test_legacy_state_translations_bad_data.sensor"] = {
"state": "bad data"
}
return result
with patch(
@ -375,7 +417,10 @@ async def test_translation_merging(
):
translations = await translation.async_get_translations(hass, "en", "state")
assert "component.sensor.state.moon__phase.first_quarter" in translations
assert (
"component.sensor.state.test_legacy_state_translations__phase.first_quarter"
in translations
)
assert (
"An integration providing translations for sensor provided invalid data:"
@ -383,17 +428,26 @@ async def test_translation_merging(
) in caplog.text
async def test_translation_merging_loaded_apart(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
async def test_translation_merging_loaded_apart_custom_integrations(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
enable_custom_integrations: None,
) -> None:
"""Test we merge translations of two integrations when they are not loaded at the same time."""
"""Test we merge translations of two integrations when they are not loaded at the same time.
Legacy state translations only used for custom integrations.
"""
orig_load_translations = translation._load_translations_files_by_language
def mock_load_translations_files(files):
"""Mock loading."""
result = orig_load_translations(files)
result["en"]["moon.sensor"] = {
"state": {"moon__phase": {"first_quarter": "First Quarter"}}
result["en"]["test_legacy_state_translations.sensor"] = {
"state": {
"test_legacy_state_translations__phase": {
"first_quarter": "First Quarter"
}
}
}
return result
@ -405,9 +459,12 @@ async def test_translation_merging_loaded_apart(
):
translations = await translation.async_get_translations(hass, "en", "state")
assert "component.sensor.state.moon__phase.first_quarter" not in translations
assert (
"component.sensor.state.test_legacy_state_translations__phase.first_quarter"
not in translations
)
hass.config.components.add("moon.sensor")
hass.config.components.add("test_legacy_state_translations.sensor")
with patch(
"homeassistant.helpers.translation._load_translations_files_by_language",
@ -415,7 +472,10 @@ async def test_translation_merging_loaded_apart(
):
translations = await translation.async_get_translations(hass, "en", "state")
assert "component.sensor.state.moon__phase.first_quarter" in translations
assert (
"component.sensor.state.test_legacy_state_translations__phase.first_quarter"
in translations
)
with patch(
"homeassistant.helpers.translation._load_translations_files_by_language",
@ -425,7 +485,10 @@ async def test_translation_merging_loaded_apart(
hass, "en", "state", integrations={"sensor"}
)
assert "component.sensor.state.moon__phase.first_quarter" in translations
assert (
"component.sensor.state.test_legacy_state_translations__phase.first_quarter"
in translations
)
async def test_translation_merging_loaded_together(

View File

@ -0,0 +1 @@
"""Provide a mock package component."""

View File

@ -0,0 +1,7 @@
{
"domain": "test_legacy_state_translations",
"name": "Test package for legacy state translations",
"documentation": "http://test-package.io",
"config_flow": true,
"version": "1.2.3"
}

View File

@ -0,0 +1 @@
"""Provide a mock package component."""

View File

@ -0,0 +1,7 @@
{
"domain": "test_legacy_state_translations_bad_data",
"name": "Test package for legacy state translations",
"documentation": "http://test-package.io",
"config_flow": true,
"version": "1.2.3"
}