Translate state name in responses, accounting for English fallback

This commit is contained in:
Michael Hansen 2025-02-04 14:27:36 -06:00
parent 94f6daa09c
commit 3c029fafdb
2 changed files with 84 additions and 0 deletions

View File

@ -810,6 +810,37 @@ class DefaultAgent(ConversationEntity):
elif intent_response.unmatched_states:
state1 = intent_response.unmatched_states[0]
state1_translated = ""
if state1 is not None:
device_class = state1.attributes.get("device_class", "_")
state_translations = await translation.async_get_translations(
self.hass, language, "state", {state1.domain}
)
entity_component_translations = await translation.async_get_translations(
self.hass, language, "entity_component", {state1.domain}
)
state_key = f"component.{state1.domain}.state.{device_class}.{state1.state}"
entity_component_key = f"component.{state1.domain}.entity_component.{device_class}.state.{state1.state}"
state_translation = state_translations.get(state_key)
entity_component_translation = entity_component_translations.get(
entity_component_key, state_translation
)
if (not state_translation) and entity_component_translation:
# Missing "state._" translation
state1_translated = entity_component_translation
elif state_translation and (not entity_component_translation):
# Missing "entity_component._.state" translation
state1_translated = state_translation
if state_translation and (
state_translation != entity_component_translation
):
# Prefer "state._" translation since English is the fallback
# language and doesn't necessarily have it.
state1_translated = state_translation
# Render response template
speech_slots = {
entity_name: entity_value.text or entity_value.value
@ -827,6 +858,7 @@ class DefaultAgent(ConversationEntity):
if state1 is not None
else None
),
"state_translated": state1_translated,
"query": {
# Entity states that matched the query (e.g, "on")
"matched": [

View File

@ -3178,3 +3178,55 @@ async def test_state_names_are_not_translated(
mock_async_render.call_args.args[0]["state"].state
== weather.ATTR_CONDITION_PARTLYCLOUDY
)
@pytest.mark.usefixtures("init_components")
async def test_state_translated_slot(
hass: HomeAssistant,
init_components: None,
) -> None:
"""Test the state_translated speech slot."""
await async_setup_component(hass, "weather", {})
hass.states.async_set("weather.test_weather", weather.ATTR_CONDITION_PARTLYCLOUDY)
expose_entity(hass, "weather.test_weather", True)
with patch(
"homeassistant.helpers.template.Template.async_render"
) as mock_async_render:
result = await conversation.async_converse(
hass, "what is the weather like?", None, Context(), None
)
assert result.response.response_type == intent.IntentResponseType.QUERY_ANSWER
mock_async_render.assert_called_once()
assert (
mock_async_render.call_args.args[0]["state_translated"] == "Partly cloudy"
)
@pytest.mark.usefixtures("init_components")
async def test_state_translated_slot_different_system_language(
hass: HomeAssistant,
init_components: None,
) -> None:
"""Test the state_translated speech slot when system language is different than pipeline language."""
assert hass.config.language == "en"
await async_setup_component(hass, "weather", {})
hass.states.async_set("weather.test_weather", weather.ATTR_CONDITION_PARTLYCLOUDY)
expose_entity(hass, "weather.test_weather", True)
with patch(
"homeassistant.helpers.template.Template.async_render"
) as mock_async_render:
result = await conversation.async_converse(
hass, "wat voor weer is het?", None, Context(), "nl"
)
assert result.response.response_type == intent.IntentResponseType.QUERY_ANSWER
mock_async_render.assert_called_once()
assert (
mock_async_render.call_args.args[0]["state_translated"]
== "Gedeeltelijk bewolkt"
)