Add support for placeholders in entity name translations (#104453)

* add placeholder support to entity name translation

* add negativ tests

* make property also available via description

* fix doc string in translation_placeholders()

* fix detection of placeholder

* validate placeholders for localized strings

* add test

* Cache translation_placeholders property

* Make translation_placeholders uncondotionally return dict

* Fall back to unsubstituted name in case of mismatch

* Only replace failing translations with English

* Update snapshots

* Blow up on non stable releases

* Fix test

* Update entity.py

---------

Co-authored-by: Erik <erik@montnemery.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
Michael 2024-01-03 17:34:47 +01:00 committed by GitHub
parent d071299233
commit eb01998395
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 425 additions and 24 deletions

View file

@ -1137,6 +1137,203 @@ async def test_friendly_name_description_device_class_name(
)
@pytest.mark.parametrize(
(
"has_entity_name",
"translation_key",
"translations",
"placeholders",
"expected_friendly_name",
),
(
(False, None, None, None, "Entity Blu"),
(True, None, None, None, "Device Bla Entity Blu"),
(
True,
"test_entity",
{
"en": {
"component.test.entity.test_domain.test_entity.name": "English ent"
},
},
None,
"Device Bla English ent",
),
(
True,
"test_entity",
{
"en": {
"component.test.entity.test_domain.test_entity.name": "{placeholder} English ent"
},
},
{"placeholder": "special"},
"Device Bla special English ent",
),
(
True,
"test_entity",
{
"en": {
"component.test.entity.test_domain.test_entity.name": "English ent {placeholder}"
},
},
{"placeholder": "special"},
"Device Bla English ent special",
),
),
)
async def test_entity_name_translation_placeholders(
hass: HomeAssistant,
has_entity_name: bool,
translation_key: str | None,
translations: dict[str, str] | None,
placeholders: dict[str, str] | None,
expected_friendly_name: str | None,
) -> None:
"""Test friendly name when the entity name translation has placeholders."""
async def async_get_translations(
hass: HomeAssistant,
language: str,
category: str,
integrations: Iterable[str] | None = None,
config_flow: bool | None = None,
) -> dict[str, Any]:
"""Return all backend translations."""
return translations[language]
ent = MockEntity(
unique_id="qwer",
device_info={
"identifiers": {("hue", "1234")},
"connections": {(dr.CONNECTION_NETWORK_MAC, "abcd")},
"name": "Device Bla",
},
)
ent.entity_description = entity.EntityDescription(
"test",
has_entity_name=has_entity_name,
translation_key=translation_key,
name="Entity Blu",
)
if placeholders is not None:
ent._attr_translation_placeholders = placeholders
with patch(
"homeassistant.helpers.entity_platform.translation.async_get_translations",
side_effect=async_get_translations,
):
await _test_friendly_name(hass, ent, expected_friendly_name)
@pytest.mark.parametrize(
(
"translation_key",
"translations",
"placeholders",
"release_channel",
"expected_error",
),
(
(
"test_entity",
{
"en": {
"component.test.entity.test_domain.test_entity.name": "{placeholder} English ent {2ndplaceholder}"
},
},
{"placeholder": "special"},
"stable",
(
"has translation placeholders '{'placeholder': 'special'}' which do "
"not match the name '{placeholder} English ent {2ndplaceholder}'"
),
),
(
"test_entity",
{
"en": {
"component.test.entity.test_domain.test_entity.name": "{placeholder} English ent {2ndplaceholder}"
},
},
{"placeholder": "special"},
"beta",
"HomeAssistantError: Missing placeholder '2ndplaceholder'",
),
(
"test_entity",
{
"en": {
"component.test.entity.test_domain.test_entity.name": "{placeholder} English ent"
},
},
None,
"stable",
(
"has translation placeholders '{}' which do "
"not match the name '{placeholder} English ent'"
),
),
),
)
async def test_entity_name_translation_placeholder_errors(
hass: HomeAssistant,
translation_key: str | None,
translations: dict[str, str] | None,
placeholders: dict[str, str] | None,
release_channel: str,
expected_error: str,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test entity name translation has placeholder issues."""
async def async_get_translations(
hass: HomeAssistant,
language: str,
category: str,
integrations: Iterable[str] | None = None,
config_flow: bool | None = None,
) -> dict[str, Any]:
"""Return all backend translations."""
return translations[language]
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Mock setup entry method."""
async_add_entities([ent])
return True
ent = MockEntity(
unique_id="qwer",
)
ent.entity_description = entity.EntityDescription(
"test",
has_entity_name=True,
translation_key=translation_key,
name="Entity Blu",
)
if placeholders is not None:
ent._attr_translation_placeholders = placeholders
platform = MockPlatform(async_setup_entry=async_setup_entry)
config_entry = MockConfigEntry(entry_id="super-mock-id")
config_entry.add_to_hass(hass)
entity_platform = MockEntityPlatform(
hass, platform_name=config_entry.domain, platform=platform
)
caplog.clear()
with patch(
"homeassistant.helpers.entity_platform.translation.async_get_translations",
side_effect=async_get_translations,
), patch(
"homeassistant.helpers.entity.get_release_channel", return_value=release_channel
):
await entity_platform.async_setup_entry(config_entry)
assert expected_error in caplog.text
@pytest.mark.parametrize(
("has_entity_name", "entity_name", "expected_friendly_name"),
(