Translation issues

Hi there.

I’ve decided to write down another topic to ask for help because I’m banging my head for days now about the translation of my custom integration

I currently have 5 issues:

  1. Translating the name of my sensor. The name was initially set using the _attr_name. Based on the documentation, the name should be left alone and I should instead set the _attr_translation_key which I did (see code below) but the translation doesn’t get picked up. Any idea what I’m doing wrong?
  2. Translating state attribute names. This is documented as well, I’ve put the relevant section in my translations/en.json and translations/fr.json but the translations don’t get picked up and I have no idea why. Any idea what I’m doing wrong?
  3. Translating the name of my device. The sensor I create is attached to a device that is automatically created based on the DeviceInfo I set on the sensor (see code below) However it seems that I cannot pass a translation_key to that object so the question is: how can I translate its name?
  4. Translation placeholders: I have placeholders to reuse translation set by HA core, which is documented. However my config flow show the placeholder rather than interpolated value for some of them, mainly the error message. Any ideas why?
  5. Translation placeholders with parameters. I’m not sure how to use these. Does the parameters have to be fields on the current object to be interpolated correctly? I couldn’t find documentation on how it works exactly.

For good measure, here is my sensor class:

class WaterSensor(CoordinatorEntity[AgurDataUpdateCoordinator], SensorEntity):
    """Agur water Sensor class."""
    _attr_name = None
    _attr_translation_key = "last_water_index"
    _attr_icon = "mdi:counter"
    _attr_state_class = SensorStateClass.TOTAL_INCREASING
    _attr_device_class = SensorDeviceClass.WATER
    _attr_native_unit_of_measurement = UnitOfVolume.LITERS

    def __init__(self, coordinator: AgurDataUpdateCoordinator, unique_id: str, contract_id: str) -> None:
        """Pass coordinator to CoordinatorEntity."""
        super().__init__(coordinator=coordinator)
        self.entity_id = f"{SENSOR_PLATFORM}.{DOMAIN}_water_index_{contract_id}"
        self._attr_unique_id = unique_id
        self._contract_id = contract_id
        self._attr_extra_state_attributes = {
            "last_read": self.coordinator.data[contract_id].latest_data_point.date,
            "contract_id": contract_id,
            "contract_address": self.coordinator.data[contract_id].contract.address,
            "contract_owner": self.coordinator.data[contract_id].contract.owner,
            "meter_serial_number": self.coordinator.data[contract_id].contract.meter_serial_number,
        }
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, self.coordinator.data[contract_id].contract.meter_id)},
            default_manufacturer=DEFAULT_NAME,
            default_name="Water meter",
            # I've tried this but that doesn't work. DeviceInfo type hint doesn't have a parameter like this
            translation_key="last_water_index", 
            model=self.coordinator.data[contract_id].contract.meter_serial_number
        )

    @property
    def native_value(self) -> float:
        """Return the state of the sensor."""
        return self.coordinator.data[self._contract_id].latest_data_point.value

and my translations/en.json file:

{
  "title": "Agur",
  "config": {
    "step": {
      "user": {
        "title": "Agur account",
        "description": "Please enter your login credential for your Agur account.",
        "data": {
          "username": "Username",
          "password": "Password"
        }
      },
      "configuration": {
        "title": "Agur configuration",
        "data": {
          "contract_ids": "Available contract IDs",
          "import_statistics": "Import historical statistics for selected contracts?"
        },
        "data_description": {
          "contract_ids": "Please select the Agur contracts you wish to import in Home Assistant.",
          "import_statistics": "If this is checked, then the historical data will be imported along side the new data. Please be aware that if you uncheck this after you enabled it, the statistics data will be kept."
        }
      }
    },
    "error": {
      "auth": "Fail to log into your Agur account. Please check your username and password.",
      "config_entry": "Fail to save the configuration. Please try again later."
    },
    "abort": {
      "already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
      "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
    }
  },
  "options": {
    "step": {
      "init": {
        "title": "Agur configuration",
        "data": {
          "contract_ids": "Available contract IDs",
          "import_statistics": "Import historical statistics for selected contracts?"
        },
        "data_description": {
          "contract_ids": "Please select the Agur contracts you wish to import in Home Assistant.",
          "import_statistics": "If this is checked, then the historical data will be imported along side the new data. Please be aware that if you uncheck this after you enabled it, the statistics data will be kept."
        }
      }
    },
    "error": {
      "contact": "Fail to get the credentials for your Agur account. The configuration might be corrupted. Please remove this account and add it again.",
      "auth": "Fail to log into your Agur account. Please check your username and password.",
      "contracts": "Fail to fetch the contracts from your Agur account. Please try again later.",
      "unknown": "[%key:common::config_flow::error::unknown%]"
    }
  },
  "entity": {
    "sensor": {
      "last_water_index": {
        "name": "Last water index",
        "state_attributes": {
          "last_read": "Last read",
          "contract_id": "Contract ID",
          "contract_owner": "Contract owner",
          "contract_address": "Contract address",
          "meter_serial_number": "Meter serial number"
        }
      }
    }
  }
}

In general, I find it hard to work with this translation system. Am I the only one? I’m probably doing something stupid so any help would be appreciated :slight_smile:

To your P2:

The “state_attributes” structure in your en.json is IMO not correct.

"entity": {
    "sensor": {
      "last_water_index": {
        "name": "Last water index",
        "state_attributes": {
          "last_read": { "name": "Last read" }
           }
        }
      }
    }
  }

Hey, sorry I missed your reply until now. I’ll try your suggestion although, the weird thing is that I have installed my extension on my production HA, through HACS and the translations now appear without any other modification!

So could be that the translations are not properly loaded on my dev HA, or something else? I’m not sure, I’m a bit confused

Hi Thomas,
I also have a custom component and I’m having similar issues, did you get any answers or interest from the core team?

I can get it to load and use my en.json file and that seems to work, when I set the language to anything else it still uses the en.json file. I have flushed caches etc etc but nothing.

I also tried calling async_translate_state directly from translation.py and I still get the English language strings, when I log the language using hass.config.language it is correct i.e. fr, it for the language I select in the frontend so it’s nothing to do with the frontend and caching.

EDIT: I made a mistake in the previous paragraph, it doesn’t get the English language strings from en.json, it is returning the state key that I use in the call to async_translate_state

It seems that the core team are interested in other things rather than completing the language functionality.

Have you made translation files for any other language? If not it’s automatically using the en.json file as fallback.

Hello,
Thanks for replying.
Yes I have made test translation files for 3 other languages with a few differences so I can check if it’s working. I haven’t got the proper language translations in yet, just test strings as I need to make sure it works first.

I have my custom component like this
/config/custom_components/visonic

with a translations subdirectory
/config/custom_components/visonic/translations

In the translations subdirectory I have 4 files: en.json, it.json, ge.json and fr.json

Note that I do not have a strings.json in the visonic directory

When I set my language in Home Assistant to English it works and uses the en.json file:

  • When I use async_translate_state directly from translation.py it returns the correct translations.
  • All other settings are using the English translations from en.json

When I set my language in Home Assistant to any of the other 3 languages (let’s say French) then:

  • When I use async_translate_state directly from translation.py it returns the ‘state’ key i.e. the key that I pass in to retrieve the translation I get returned.
  • All other settings are using the English translation en.json instead of the fr.json file I have.

I have tried clearing all the caches, history the whole lot, tried Firefox, Edge and Chrome. I keep restarting Home Assistant itself.

But the fact that calling async_translate_state directly doesn’t work indicates to me that it’s not a browser problem but it’s a core translate problem not picking up the other language translation files.

I have also put a deliberate error in the fr.json file and Home Assistant detects it on startup so it is reading and parsing the fr.json file.

Any help would be appreciated

I’ve resolved both problems in this issue so I’ll document it in case others have the same problem

The language settings in Home Assistant
There are 2 language settings in Home Assistant and I can’t really work out why I need both. One on the Settings / System / General settings (which is what I was changing) and the other seems to be on a per user basis. The General settings sets the hass.config.language variable in Home Assistant, I’m not sure where the other setting is visible in the backend code. This thread gave me the information that I eventually found so I assume it’s a frontend thing and not a backend:
Change my language - Configuration / Frontend - Home Assistant Community (home-assistant.io)

Using async_translate_state directly from translation.py
I was calling this function too early in the start up of the custom integration, I had it in async_setup and for some reason that I don’t understand Home Assistant isn’t ready at that point in time. So I moved my code to async_setup_entry and it works.

EDIT: So I can do the original points 1, 2 and 3. But 4 and 5 still do not work, I cannot seem to get placeholders working to only include common strings once and then refer to them.

Thanks
Dave

Hi,

I also struggled with translation not working – mine suddenly started working perfectly the moment I set the attribute _attr_has_entity_name = True. Not sure if that triggered another change, but suddenly my translations started working correctly.

Regards,
Johan du Plessis