Remove unused legacy state translations (#112023)

* Remove unused state translations

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

* nothing does merging anymore

* useless dispatch

* remove

* remove platform code from hassfest

* preen

* Update homeassistant/helpers/translation.py

* ruff

* fix merge

* check is impossible now since we already know if translations exist or not

* keep the function for now

* remove unreachable code since we filter out `.` before now

* reduce

* reduce

* fix merge conflict (again)
This commit is contained in:
J. Nick Koston 2024-04-14 06:13:17 -05:00 committed by GitHub
parent 0200d1aa66
commit 33412dd9f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 139 additions and 514 deletions

View File

@ -6,6 +6,7 @@ import asyncio
from collections.abc import Iterable, Mapping
from contextlib import suppress
import logging
import pathlib
import string
from typing import Any
@ -41,40 +42,18 @@ def recursive_flatten(prefix: Any, data: dict[str, Any]) -> dict[str, Any]:
@callback
def component_translation_path(
component: str, language: str, integration: Integration
) -> str | None:
def component_translation_path(language: str, integration: Integration) -> pathlib.Path:
"""Return the translation json file location for a component.
For component:
- components/hue/translations/nl.json
For platform:
- components/hue/translations/light.nl.json
If component is just a single file, will return None.
"""
parts = component.split(".")
domain = parts[0]
is_platform = len(parts) == 2
# If it's a component that is just one file, we don't support translations
# Example custom_components/my_component.py
if integration.file_path.name != domain:
return None
if is_platform:
filename = f"{parts[1]}.{language}.json"
else:
filename = f"{language}.json"
translation_path = integration.file_path / "translations"
return str(translation_path / filename)
return integration.file_path / "translations" / f"{language}.json"
def _load_translations_files_by_language(
translation_files: dict[str, dict[str, str]],
translation_files: dict[str, dict[str, pathlib.Path]],
) -> dict[str, dict[str, Any]]:
"""Load and parse translation.json files."""
loaded: dict[str, dict[str, Any]] = {}
@ -98,47 +77,6 @@ def _load_translations_files_by_language(
return loaded
def _merge_resources(
translation_strings: dict[str, dict[str, Any]],
components: set[str],
category: str,
) -> dict[str, dict[str, Any]]:
"""Build and merge the resources response for the given components and platforms."""
# Build response
resources: dict[str, dict[str, Any]] = {}
for component in components:
domain = component.rpartition(".")[-1]
domain_resources = resources.setdefault(domain, {})
# Integrations are able to provide translations for their entities under other
# integrations if they don't have an existing device class. This is done by
# using a custom device class prefixed with their domain and two underscores.
# These files are in platform specific files in the integration folder with
# names like `strings.sensor.json`.
# We are going to merge the translations for the custom device classes into
# the translations of sensor.
new_value = translation_strings.get(component, {}).get(category)
if new_value is None:
continue
if isinstance(new_value, dict):
domain_resources.update(new_value)
else:
_LOGGER.error(
(
"An integration providing translations for %s provided invalid"
" data: %s"
),
domain,
new_value,
)
return resources
def build_resources(
translation_strings: dict[str, dict[str, dict[str, Any] | str]],
components: set[str],
@ -163,32 +101,20 @@ async def _async_get_component_strings(
"""Load translations."""
translations_by_language: dict[str, dict[str, Any]] = {}
# Determine paths of missing components/platforms
files_to_load_by_language: dict[str, dict[str, str]] = {}
files_to_load_by_language: dict[str, dict[str, pathlib.Path]] = {}
loaded_translations_by_language: dict[str, dict[str, Any]] = {}
has_files_to_load = False
for language in languages:
files_to_load: dict[str, str] = {}
files_to_load_by_language[language] = files_to_load
translations_by_language[language] = {}
for comp in components:
domain, _, platform = comp.partition(".")
files_to_load: dict[str, pathlib.Path] = {
domain: component_translation_path(language, integration)
for domain in components
if (
not (integration := integrations.get(domain))
or not integration.has_translations
):
continue
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(comp, language, integration):
files_to_load[comp] = path
has_files_to_load = True
(integration := integrations.get(domain))
and integration.has_translations
)
}
files_to_load_by_language[language] = files_to_load
has_files_to_load |= bool(files_to_load)
if has_files_to_load:
loaded_translations_by_language = await hass.async_add_executor_job(
@ -197,18 +123,15 @@ async def _async_get_component_strings(
for language in languages:
loaded_translations = loaded_translations_by_language.setdefault(language, {})
for comp in components:
if "." in comp:
continue
for domain in components:
# Translations that miss "title" will get integration put in.
component_translations = loaded_translations.setdefault(comp, {})
component_translations = loaded_translations.setdefault(domain, {})
if "title" not in component_translations and (
integration := integrations.get(comp)
integration := integrations.get(domain)
):
component_translations["title"] = integration.name
translations_by_language[language].update(loaded_translations)
translations_by_language.setdefault(language, {}).update(loaded_translations)
return translations_by_language
@ -355,10 +278,12 @@ class _TranslationCache:
_LOGGER.error(
(
"Validation of translation placeholders for localized (%s) string "
"%s failed"
"%s failed: (%s != %s)"
),
language,
key,
updated_placeholders,
cached_placeholders,
)
mismatches.add(key)
@ -382,17 +307,7 @@ class _TranslationCache:
categories.update(resource)
for category in categories:
new_resources: Mapping[str, dict[str, Any] | str]
if category in ("state", "entity_component"):
new_resources = _merge_resources(
translation_strings, components, category
)
else:
new_resources = build_resources(
translation_strings, components, category
)
new_resources = build_resources(translation_strings, components, category)
category_cache = cached.setdefault(category, {})
for component, resource in new_resources.items():
@ -430,7 +345,7 @@ async def async_get_translations(
elif integrations is not None:
components = set(integrations)
else:
components = _async_get_components(hass, category)
components = {comp for comp in hass.config.components if "." not in comp}
return await _async_get_translations_cache(hass).async_fetch(
language, category, components
@ -452,7 +367,7 @@ def async_get_cached_translations(
if integration is not None:
components = {integration}
else:
components = _async_get_components(hass, category)
components = {comp for comp in hass.config.components if "." not in comp}
return _async_get_translations_cache(hass).get_cached(
language, category, components
@ -466,21 +381,6 @@ def _async_get_translations_cache(hass: HomeAssistant) -> _TranslationCache:
return cache
_DIRECT_MAPPED_CATEGORIES = {"state", "entity_component", "services"}
@callback
def _async_get_components(
hass: HomeAssistant,
category: str,
) -> set[str]:
"""Return a set of components for which translations should be loaded."""
if category in _DIRECT_MAPPED_CATEGORIES:
return hass.config.components
# Only 'state' supports merging, so remove platforms from selection
return {component for component in hass.config.components if "." not in component}
@callback
def async_setup(hass: HomeAssistant) -> None:
"""Create translation cache and register listeners for translation loaders.
@ -590,13 +490,4 @@ def async_translate_state(
if localize_key in translations:
return translations[localize_key]
translations = async_get_cached_translations(hass, language, "state", domain)
if device_class is not None:
localize_key = f"component.{domain}.state.{device_class}.{state}"
if localize_key in translations:
return translations[localize_key]
localize_key = f"component.{domain}.state._.{state}"
if localize_key in translations:
return translations[localize_key]
return state

View File

@ -3,7 +3,6 @@
from __future__ import annotations
from functools import partial
from itertools import chain
import json
import re
from typing import Any
@ -12,7 +11,6 @@ import voluptuous as vol
from voluptuous.humanize import humanize_error
import homeassistant.helpers.config_validation as cv
from homeassistant.util import slugify
from script.translations import upload
from .model import Config, Integration
@ -414,49 +412,6 @@ def gen_ha_hardware_schema(config: Config, integration: Integration):
)
def gen_platform_strings_schema(config: Config, integration: Integration) -> vol.Schema:
"""Generate platform strings schema like strings.sensor.json.
Example of valid data:
{
"state": {
"moon__phase": {
"full": "Full"
}
}
}
"""
def device_class_validator(value: str) -> str:
"""Key validator for platform states.
Platform states are only allowed to provide states for device classes they prefix.
"""
if not value.startswith(f"{integration.domain}__"):
raise vol.Invalid(
f"Device class need to start with '{integration.domain}__'. Key {value} is invalid. See https://developers.home-assistant.io/docs/internationalization/core#stringssensorjson"
)
slug_friendly = value.replace("__", "_", 1)
slugged = slugify(slug_friendly)
if slug_friendly != slugged:
raise vol.Invalid(
f"invalid device class {value}. After domain__, needs to be all lowercase, no spaces."
)
return value
return vol.Schema(
{
vol.Optional("state"): cv.schema_with_slug_keys(
cv.schema_with_slug_keys(str, slug_validator=translation_key_validator),
slug_validator=device_class_validator,
)
}
)
ONBOARDING_SCHEMA = vol.Schema(
{
vol.Required("area"): {str: translation_value_validator},
@ -525,32 +480,6 @@ def validate_translation_file( # noqa: C901
"name or add exception to ALLOW_NAME_TRANSLATION",
)
platform_string_schema = gen_platform_strings_schema(config, integration)
platform_strings = [integration.path.glob("strings.*.json")]
if config.specific_integrations:
platform_strings.append(integration.path.glob("translations/*.en.json"))
for path in chain(*platform_strings):
name = str(path.relative_to(integration.path))
try:
strings = json.loads(path.read_text())
except ValueError as err:
integration.add_error("translations", f"Invalid JSON in {name}: {err}")
continue
try:
platform_string_schema(strings)
except vol.Invalid as err:
msg = f"Invalid {path.name}: {humanize_error(strings, err)}"
if config.specific_integrations:
integration.add_warning("translations", msg)
else:
integration.add_error("translations", msg)
else:
find_references(strings, path.name, references)
if config.specific_integrations:
return

View File

@ -2050,12 +2050,7 @@ async def test_state_translated(
):
if category == "entity":
return {
"component.hue.entity.light.translation_key.state.on": "state_is_on"
}
if category == "state":
return {
"component.some_domain.state.some_device_class.off": "state_is_off",
"component.some_domain.state._.foo": "state_is_foo",
"component.hue.entity.light.translation_key.state.on": "state_is_on",
}
return {}
@ -2066,16 +2061,6 @@ async def test_state_translated(
tpl8 = template.Template('{{ state_translated("light.hue_5678") }}', hass)
assert tpl8.async_render() == "state_is_on"
tpl9 = template.Template(
'{{ state_translated("some_domain.with_device_class_1") }}', hass
)
assert tpl9.async_render() == "state_is_off"
tpl10 = template.Template(
'{{ state_translated("some_domain.with_device_class_2") }}', hass
)
assert tpl10.async_render() == "state_is_foo"
tpl11 = template.Template('{{ state_translated("domain.is_unavailable") }}', hass)
assert tpl11.async_render() == "unavailable"

View File

@ -47,35 +47,10 @@ async def test_component_translation_path(
{"switch": [{"platform": "test"}, {"platform": "test_embedded"}]},
)
assert await async_setup_component(hass, "test_package", {"test_package": None})
(
int_test,
int_test_embedded,
int_test_package,
) = await asyncio.gather(
async_get_integration(hass, "test"),
async_get_integration(hass, "test_embedded"),
async_get_integration(hass, "test_package"),
)
int_test_package = await async_get_integration(hass, "test_package")
assert path.normpath(
translation.component_translation_path("test.switch", "en", int_test)
) == path.normpath(
hass.config.path("custom_components", "test", "translations", "switch.en.json")
)
assert path.normpath(
translation.component_translation_path(
"test_embedded.switch", "en", int_test_embedded
)
) == path.normpath(
hass.config.path(
"custom_components", "test_embedded", "translations", "switch.en.json"
)
)
assert path.normpath(
translation.component_translation_path("test_package", "en", int_test_package)
translation.component_translation_path("en", int_test_package)
) == path.normpath(
hass.config.path("custom_components", "test_package", "translations", "en.json")
)
@ -86,28 +61,39 @@ def test__load_translations_files_by_language(
) -> None:
"""Test the load translation files function."""
# Test one valid and one invalid file
file1 = hass.config.path(
"custom_components", "test", "translations", "switch.en.json"
)
file2 = hass.config.path(
en_file = hass.config.path("custom_components", "test", "translations", "en.json")
invalid_file = hass.config.path(
"custom_components", "test", "translations", "invalid.json"
)
file3 = hass.config.path(
"custom_components", "test", "translations", "_broken.en.json"
broken_file = hass.config.path(
"custom_components", "test", "translations", "_broken.json"
)
assert translation._load_translations_files_by_language(
{"en": {"switch.test": file1, "invalid": file2, "broken": file3}}
) == {
"en": {
"switch.test": {
"state": {"string1": "Value 1", "string2": "Value 2"},
"something": "else",
},
"invalid": {},
{
"en": {"test": en_file},
"invalid": {"test": invalid_file},
"broken": {"test": broken_file},
}
) == {
"broken": {},
"en": {
"test": {
"entity": {
"switch": {
"other1": {"name": "Other 1"},
"other2": {"name": "Other 2"},
"other3": {"name": "Other 3"},
"other4": {"name": "Other 4"},
"outlet": {"name": "Outlet " "{placeholder}"},
}
},
"something": "else",
}
},
"invalid": {"test": {}},
}
assert "Translation file is unexpected type" in caplog.text
assert "_broken.en.json" in caplog.text
assert "_broken.json" in caplog.text
@pytest.mark.parametrize(
@ -185,33 +171,61 @@ async def test_get_translations(
hass: HomeAssistant, mock_config_flows, enable_custom_integrations: None
) -> None:
"""Test the get translations helper."""
translations = await translation.async_get_translations(hass, "en", "state")
translations = await translation.async_get_translations(hass, "en", "entity")
assert translations == {}
assert await async_setup_component(hass, "switch", {"switch": {"platform": "test"}})
await hass.async_block_till_done()
translations = await translation.async_get_translations(hass, "en", "state")
translations = await translation.async_get_translations(
hass, "en", "entity", {"test"}
)
assert translations["component.switch.state.string1"] == "Value 1"
assert translations["component.switch.state.string2"] == "Value 2"
assert translations == {
"component.test.entity.switch.other1.name": "Other 1",
"component.test.entity.switch.other2.name": "Other 2",
"component.test.entity.switch.other3.name": "Other 3",
"component.test.entity.switch.other4.name": "Other 4",
"component.test.entity.switch.outlet.name": "Outlet {placeholder}",
}
translations = await translation.async_get_translations(hass, "de", "state")
assert "component.switch.something" not in translations
assert translations["component.switch.state.string1"] == "German Value 1"
assert translations["component.switch.state.string2"] == "German Value 2"
translations = await translation.async_get_translations(
hass, "de", "entity", {"test"}
)
assert translations == {
"component.test.entity.switch.other1.name": "Anderes 1",
"component.test.entity.switch.other2.name": "Other 2",
"component.test.entity.switch.other3.name": "",
"component.test.entity.switch.other4.name": "Other 4",
"component.test.entity.switch.outlet.name": "Outlet {placeholder}",
}
# Test a partial translation
translations = await translation.async_get_translations(hass, "es", "state")
assert translations["component.switch.state.string1"] == "Spanish Value 1"
assert translations["component.switch.state.string2"] == "Value 2"
translations = await translation.async_get_translations(
hass, "es", "entity", {"test"}
)
assert translations == {
"component.test.entity.switch.other1.name": "Otra 1",
"component.test.entity.switch.other2.name": "Otra 2",
"component.test.entity.switch.other3.name": "Otra 3",
"component.test.entity.switch.other4.name": "Otra 4",
"component.test.entity.switch.outlet.name": "Enchufe {placeholder}",
}
# Test that an untranslated language falls back to English.
translations = await translation.async_get_translations(
hass, "invalid-language", "state"
hass, "invalid-language", "entity", {"test"}
)
assert translations["component.switch.state.string1"] == "Value 1"
assert translations["component.switch.state.string2"] == "Value 2"
assert translations == {
"component.test.entity.switch.other1.name": "Other 1",
"component.test.entity.switch.other2.name": "Other 2",
"component.test.entity.switch.other3.name": "Other 3",
"component.test.entity.switch.other4.name": "Other 4",
"component.test.entity.switch.outlet.name": "Outlet {placeholder}",
}
async def test_get_translations_loads_config_flows(
@ -348,162 +362,6 @@ async def test_get_translation_categories(hass: HomeAssistant) -> None:
assert "component.light.device_automation.action_type.turn_on" in translations
async def test_legacy_platform_translations_not_used_built_in_integrations(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""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"]["test_legacy_state_translations.sensor"] = {
"state": {
"test_legacy_state_translations__phase": {
"first_quarter": "First Quarter"
}
}
}
return result
with patch(
"homeassistant.helpers.translation._load_translations_files_by_language",
side_effect=mock_load_translations_files,
):
translations = await translation.async_get_translations(hass, "en", "state")
assert (
"component.sensor.state.test_legacy_state_translations__phase.first_quarter"
in translations
)
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"]["test_legacy_state_translations_bad_data.sensor"] = {
"state": "bad data"
}
return result
with patch(
"homeassistant.helpers.translation._load_translations_files_by_language",
side_effect=mock_load_bad_translations_files,
):
translations = await translation.async_get_translations(hass, "en", "state")
assert (
"component.sensor.state.test_legacy_state_translations__phase.first_quarter"
in translations
)
assert (
"An integration providing translations for sensor provided invalid data:"
" bad data"
) in caplog.text
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.
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"]["test_legacy_state_translations.sensor"] = {
"state": {
"test_legacy_state_translations__phase": {
"first_quarter": "First Quarter"
}
}
}
return result
hass.config.components.add("sensor")
with patch(
"homeassistant.helpers.translation._load_translations_files_by_language",
side_effect=mock_load_translations_files,
):
translations = await translation.async_get_translations(hass, "en", "state")
assert (
"component.sensor.state.test_legacy_state_translations__phase.first_quarter"
not in translations
)
hass.config.components.add("test_legacy_state_translations.sensor")
with patch(
"homeassistant.helpers.translation._load_translations_files_by_language",
side_effect=mock_load_translations_files,
):
translations = await translation.async_get_translations(hass, "en", "state")
assert (
"component.sensor.state.test_legacy_state_translations__phase.first_quarter"
in translations
)
with patch(
"homeassistant.helpers.translation._load_translations_files_by_language",
side_effect=mock_load_translations_files,
):
translations = await translation.async_get_translations(
hass, "en", "state", integrations={"sensor"}
)
assert (
"component.sensor.state.test_legacy_state_translations__phase.first_quarter"
in translations
)
async def test_translation_merging_loaded_together(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
@ -592,14 +450,14 @@ async def test_caching(hass: HomeAssistant) -> None:
# Patch with same method so we can count invocations
with patch(
"homeassistant.helpers.translation._merge_resources",
side_effect=translation._merge_resources,
) as mock_merge:
"homeassistant.helpers.translation.build_resources",
side_effect=translation.build_resources,
) as mock_build_resources:
load1 = await translation.async_get_translations(hass, "en", "entity_component")
assert len(mock_merge.mock_calls) == 1
assert len(mock_build_resources.mock_calls) == 5
load2 = await translation.async_get_translations(hass, "en", "entity_component")
assert len(mock_merge.mock_calls) == 1
assert len(mock_build_resources.mock_calls) == 5
assert load1 == load2
@ -665,47 +523,58 @@ async def test_custom_component_translations(
async def test_get_cached_translations(
hass: HomeAssistant, mock_config_flows, enable_custom_integrations: None
):
) -> None:
"""Test the get cached translations helper."""
translations = translation.async_get_cached_translations(hass, "en", "state")
translations = await translation.async_get_translations(hass, "en", "entity")
assert translations == {}
assert await async_setup_component(hass, "switch", {"switch": {"platform": "test"}})
await hass.async_block_till_done()
await translation._async_get_translations_cache(hass).async_load(
"en", hass.config.components
)
translations = translation.async_get_cached_translations(hass, "en", "state")
await translation._async_get_translations_cache(hass).async_load("en", {"test"})
assert translations["component.switch.state.string1"] == "Value 1"
assert translations["component.switch.state.string2"] == "Value 2"
await translation._async_get_translations_cache(hass).async_load(
"de", hass.config.components
translations = translation.async_get_cached_translations(
hass, "en", "entity", "test"
)
translations = translation.async_get_cached_translations(hass, "de", "state")
assert "component.switch.something" not in translations
assert translations["component.switch.state.string1"] == "German Value 1"
assert translations["component.switch.state.string2"] == "German Value 2"
assert translations == {
"component.test.entity.switch.other1.name": "Other 1",
"component.test.entity.switch.other2.name": "Other 2",
"component.test.entity.switch.other3.name": "Other 3",
"component.test.entity.switch.other4.name": "Other 4",
"component.test.entity.switch.outlet.name": "Outlet {placeholder}",
}
await translation._async_get_translations_cache(hass).async_load("es", {"test"})
# Test a partial translation
await translation._async_get_translations_cache(hass).async_load(
"es", hass.config.components
translations = translation.async_get_cached_translations(
hass, "es", "entity", "test"
)
assert translations == {
"component.test.entity.switch.other1.name": "Otra 1",
"component.test.entity.switch.other2.name": "Otra 2",
"component.test.entity.switch.other3.name": "Otra 3",
"component.test.entity.switch.other4.name": "Otra 4",
"component.test.entity.switch.outlet.name": "Enchufe {placeholder}",
}
await translation._async_get_translations_cache(hass).async_load(
"invalid-language", {"test"}
)
translations = translation.async_get_cached_translations(hass, "es", "state")
assert translations["component.switch.state.string1"] == "Spanish Value 1"
assert translations["component.switch.state.string2"] == "Value 2"
# Test that an untranslated language falls back to English.
await translation._async_get_translations_cache(hass).async_load(
"invalid-language", hass.config.components
)
translations = translation.async_get_cached_translations(
hass, "invalid-language", "state"
hass, "invalid-language", "entity", "test"
)
assert translations["component.switch.state.string1"] == "Value 1"
assert translations["component.switch.state.string2"] == "Value 2"
assert translations == {
"component.test.entity.switch.other1.name": "Other 1",
"component.test.entity.switch.other2.name": "Other 2",
"component.test.entity.switch.other3.name": "Other 3",
"component.test.entity.switch.other4.name": "Other 4",
"component.test.entity.switch.outlet.name": "Outlet {placeholder}",
}
async def test_setup(hass: HomeAssistant):
@ -784,36 +653,6 @@ async def test_translate_state(hass: HomeAssistant):
mock.assert_called_once_with(hass, hass.config.language, "entity_component")
assert result == "TRANSLATED"
with patch(
"homeassistant.helpers.translation.async_get_cached_translations",
return_value={"component.binary_sensor.state.device_class.on": "TRANSLATED"},
) as mock:
result = translation.async_translate_state(
hass, "on", "binary_sensor", "platform", None, "device_class"
)
mock.assert_has_calls(
[
call(hass, hass.config.language, "entity_component"),
call(hass, hass.config.language, "state", "binary_sensor"),
]
)
assert result == "TRANSLATED"
with patch(
"homeassistant.helpers.translation.async_get_cached_translations",
return_value={"component.binary_sensor.state._.on": "TRANSLATED"},
) as mock:
result = translation.async_translate_state(
hass, "on", "binary_sensor", "platform", None, None
)
mock.assert_has_calls(
[
call(hass, hass.config.language, "entity_component"),
call(hass, hass.config.language, "state", "binary_sensor"),
]
)
assert result == "TRANSLATED"
with patch(
"homeassistant.helpers.translation.async_get_cached_translations",
return_value={},
@ -824,7 +663,6 @@ async def test_translate_state(hass: HomeAssistant):
mock.assert_has_calls(
[
call(hass, hass.config.language, "entity_component"),
call(hass, hass.config.language, "state", "binary_sensor"),
]
)
assert result == "on"
@ -840,7 +678,6 @@ async def test_translate_state(hass: HomeAssistant):
[
call(hass, hass.config.language, "entity"),
call(hass, hass.config.language, "entity_component"),
call(hass, hass.config.language, "state", "binary_sensor"),
]
)
assert result == "on"

View File

@ -7,5 +7,6 @@
"other4": { "name": "Other 4" },
"outlet": { "name": "Outlet {placeholder}" }
}
}
},
"something": "else"
}

View File

@ -1,6 +0,0 @@
{
"state": {
"string1": "German Value 1",
"string2": "German Value 2"
}
}

View File

@ -1,7 +0,0 @@
{
"state": {
"string1": "Value 1",
"string2": "Value 2"
},
"something": "else"
}

View File

@ -1,5 +0,0 @@
{
"state": {
"string1": "Spanish Value 1"
}
}