From a92bfb992a4840dc12c2c2826c220963f0fbd2ca Mon Sep 17 00:00:00 2001 From: Magnus Larsson Date: Thu, 10 Oct 2024 12:58:53 +0200 Subject: [PATCH 1/4] Unit of measurement is translated based on hass language --- custom_components/birthdays/__init__.py | 43 ++++++++++++++++--- custom_components/birthdays/manifest.json | 2 +- .../birthdays/translations/en.json | 8 ++++ .../birthdays/translations/nl.json | 8 ++++ .../birthdays/translations/sv.json | 8 ++++ 5 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 custom_components/birthdays/translations/en.json create mode 100644 custom_components/birthdays/translations/nl.json create mode 100644 custom_components/birthdays/translations/sv.json diff --git a/custom_components/birthdays/__init__.py b/custom_components/birthdays/__init__.py index 23fe187..3b5ec3d 100644 --- a/custom_components/birthdays/__init__.py +++ b/custom_components/birthdays/__init__.py @@ -1,5 +1,6 @@ import asyncio import logging +from dataclasses import dataclass import voluptuous as vol @@ -8,6 +9,7 @@ from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.event import async_call_later from homeassistant.helpers.template import Template, is_template_string, render_complex +from homeassistant.helpers.translation import async_get_translations from homeassistant.util import dt as dt_util, slugify _LOGGER = logging.getLogger(__name__) @@ -54,11 +56,18 @@ ), extra=vol.ALLOW_EXTRA) +@dataclass +class Translation: + single_day_unit: str + multiple_days_unit: str + + async def async_setup(hass, config): devices = [] is_new_config = isinstance(config[DOMAIN], dict) and config[DOMAIN].get(CONF_BIRTHDAYS) is not None birthdays = config[DOMAIN][CONF_BIRTHDAYS] if is_new_config else config[DOMAIN] + translation = await _get_translation(hass) for birthday_data in birthdays: unique_id = birthday_data.get(CONF_UNIQUE_ID) @@ -69,13 +78,16 @@ async def async_setup(hass, config): if is_new_config: global_config = config[DOMAIN][CONF_GLOBAL_CONFIG] # Empty dict or has attributes global_attributes = global_config.get(CONF_ATTRIBUTES) or {} - attributes = dict(global_attributes, **attributes) # Add global_attributes but let local attributes be on top + attributes = dict(global_attributes, + **attributes) # Add global_attributes but let local attributes be on top - devices.append(BirthdayEntity(unique_id, name, date_of_birth, icon, attributes, hass)) + devices.append(BirthdayEntity(unique_id, name, date_of_birth, icon, attributes, translation, hass)) + # Set up component component = EntityComponent(_LOGGER, DOMAIN, hass) await component.async_add_entities(devices) + # Update state tasks = [asyncio.create_task(device.update_data()) for device in devices] await asyncio.wait(tasks) @@ -84,9 +96,25 @@ async def async_setup(hass, config): return True +async def _get_translation(hass) -> Translation: + """Fetch the translated units of measurement and update each sensor.""" + category = "config" + translations = await async_get_translations(hass, + language=hass.config.language, + category=category, + integrations=[DOMAIN]) + + base_key = f'component.{DOMAIN}.{category}.unit_of_measurement' + + single_day_unit = translations.get(f'{base_key}.single_day', 'day') + multiple_days_unit = translations.get(f'{base_key}.multiple_days', 'days') + + return Translation(single_day_unit=single_day_unit, multiple_days_unit=multiple_days_unit) + + class BirthdayEntity(Entity): - def __init__(self, unique_id, name, date_of_birth, icon, attributes, hass): + def __init__(self, unique_id, name, date_of_birth, icon, attributes, translation, hass): self._name = name if unique_id is not None: @@ -112,6 +140,8 @@ def __init__(self, unique_id, name, date_of_birth, icon, attributes, hass): else: self._extra_state_attributes[k] = v + self._translation: Translation = translation + @property def name(self): return self._name @@ -147,13 +177,16 @@ def date_of_birth(self): @property def unit_of_measurement(self): - return 'days' + return self._translation.single_day_unit \ + if self._state is not None and self._state == 1 \ + else self._translation.multiple_days_unit @property def hidden(self): return self._state is None - def _get_seconds_until_midnight(self): + @staticmethod + def _get_seconds_until_midnight(): one_day_in_seconds = 24 * 60 * 60 now = dt_util.now() diff --git a/custom_components/birthdays/manifest.json b/custom_components/birthdays/manifest.json index 7449d19..648a2d3 100644 --- a/custom_components/birthdays/manifest.json +++ b/custom_components/birthdays/manifest.json @@ -5,5 +5,5 @@ "dependencies": [], "codeowners": ["@Miicroo"], "requirements": [], - "version": "1.0.0" + "version": "1.2.0" } diff --git a/custom_components/birthdays/translations/en.json b/custom_components/birthdays/translations/en.json new file mode 100644 index 0000000..67fb2b4 --- /dev/null +++ b/custom_components/birthdays/translations/en.json @@ -0,0 +1,8 @@ +{ + "config": { + "unit_of_measurement": { + "single_day": "day", + "multiple_days": "days" + } + } +} \ No newline at end of file diff --git a/custom_components/birthdays/translations/nl.json b/custom_components/birthdays/translations/nl.json new file mode 100644 index 0000000..96a222f --- /dev/null +++ b/custom_components/birthdays/translations/nl.json @@ -0,0 +1,8 @@ +{ + "config": { + "unit_of_measurement": { + "single_day": "dag", + "multiple_days": "dagen" + } + } +} \ No newline at end of file diff --git a/custom_components/birthdays/translations/sv.json b/custom_components/birthdays/translations/sv.json new file mode 100644 index 0000000..1b55e30 --- /dev/null +++ b/custom_components/birthdays/translations/sv.json @@ -0,0 +1,8 @@ +{ + "config": { + "unit_of_measurement": { + "single_day": "dag", + "multiple_days": "dagar" + } + } +} \ No newline at end of file From c2c9941dfc0306f9d32045b49f701b5b06322254 Mon Sep 17 00:00:00 2001 From: Magnus Larsson Date: Thu, 10 Oct 2024 12:59:29 +0200 Subject: [PATCH 2/4] Fix import order --- custom_components/birthdays/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/birthdays/__init__.py b/custom_components/birthdays/__init__.py index 3b5ec3d..7948f73 100644 --- a/custom_components/birthdays/__init__.py +++ b/custom_components/birthdays/__init__.py @@ -1,6 +1,6 @@ import asyncio -import logging from dataclasses import dataclass +import logging import voluptuous as vol From 1f9701cdef7a5ad7a20cf42545cea702e4be3b08 Mon Sep 17 00:00:00 2001 From: Magnus Larsson Date: Thu, 10 Oct 2024 14:16:39 +0200 Subject: [PATCH 3/4] Fix tests --- tests/conftest.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 1d99892..f8345ff 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,19 @@ """pytest fixtures.""" +from unittest.mock import patch + import pytest +from custom_components.birthdays import Translation + @pytest.fixture(autouse=True) def auto_enable_custom_integrations(enable_custom_integrations): """Enable custom integrations defined in the test dir.""" yield + + +@pytest.fixture(autouse=True) +def mock_translations(): + translation = Translation(single_day_unit='day', multiple_days_unit='days') + with patch("custom_components.birthdays._get_translation", return_value=translation, autospec=True) as m: + yield m From 9b16871e8e2c26eeb1651598a19952d8a6d01dde Mon Sep 17 00:00:00 2001 From: Magnus Larsson Date: Thu, 10 Oct 2024 15:39:24 +0200 Subject: [PATCH 4/4] Fix pre-commit fails --- tests/conftest.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index f8345ff..f15a4ec 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,6 +14,11 @@ def auto_enable_custom_integrations(enable_custom_integrations): @pytest.fixture(autouse=True) def mock_translations(): - translation = Translation(single_day_unit='day', multiple_days_unit='days') - with patch("custom_components.birthdays._get_translation", return_value=translation, autospec=True) as m: + """Mock translations globally.""" + translation = Translation(single_day_unit="day", multiple_days_unit="days") + with patch( + "custom_components.birthdays._get_translation", + return_value=translation, + autospec=True, + ) as m: yield m