refactor(i18n): drop the vestigial descriptor name field
Since entity.py started routing named descriptors through the catalog under desc.key, SamsungEntityDescription.name has been read for its value nowhere -- only twice as a flag, to decide whether an entity was translated at all. That left 148 English names duplicated between Python and translations/en.json with nothing keeping them honest: six had already drifted, invisibly, because editing the Python side changes nothing a user sees. So the field is gone, and translation_key defaults to desc.key. A descriptor now sets translation_key only to share one catalog entry across descriptors or to point at a differently-named one, and the catalog is the only place an entity name exists. Every descriptor resolves to exactly the translation key, icon, entity category, enabled-default and gating it did before -- with one deliberate exception: the hood fan, previously the sole descriptor with no key at all, now resolves to 'fan'. That is inert, because fan.py sets _attr_name = None so the entity presents as the device itself. The three helpers that forwarded a name into a descriptor (laundry.bool_option_switch, washer._bool_option_switch, air_purifier's sensor table) lose that parameter. test_translations.py now requires a catalog entry for every descriptor rather than only translated ones. Claude-Session: https://claude.ai/code/session_01GiibJZZLWVvyxq7mc7EDNp
This commit is contained in:
1 parent
6281b40549
commit
10c850f2b9
20 files changed
+241
-267
No files matched your search
@@ -20,9 +20,13 @@ def _make_entity(desc, href='/x/vs/0', key_override=None, instance='', instance_
|
||||
return LocalThingsEntity(_FakeCoordinator(), bound)
|
||||
|
||||
|
||||
def test_explicit_fallback_name_uses_translation_instead_of_attr_name():
|
||||
"""An _attr_name would take precedence over HA's translation catalog."""
|
||||
desc = BinarySensorDesc(key='enabled', name='Explicit name')
|
||||
def test_descriptor_key_is_the_default_translation_key():
|
||||
"""A descriptor names itself through the catalog, keyed by its own key.
|
||||
|
||||
Nothing sets _attr_name: that would take precedence over HA's
|
||||
translation catalog and make the entity untranslatable.
|
||||
"""
|
||||
desc = BinarySensorDesc(key='enabled')
|
||||
entity = _make_entity(desc, instance_name='Cubed Ice')
|
||||
assert entity.translation_key == 'enabled'
|
||||
assert not hasattr(entity, '_attr_name')
|
||||
|
||||
@@ -89,7 +89,7 @@ async def test_unknown_vendor_option_round_trips_to_exact_raw_value():
|
||||
self.writes.append(value)
|
||||
|
||||
desc = SelectDesc(
|
||||
key='mode', name='Mode', translation_key='door_alert',
|
||||
key='mode', translation_key='door_alert',
|
||||
options=('Known', 'FutureVendorMode'), write_fn=lambda *args: None,
|
||||
)
|
||||
capability = Capability(href='/x/vs/0', entities=(desc,))
|
||||
|
||||
+16
-26
@@ -125,21 +125,32 @@ def test_no_catalog_carries_unresolved_core_references():
|
||||
assert unresolved == [], language
|
||||
|
||||
|
||||
def test_every_translatable_descriptor_has_an_entity_catalog_entry():
|
||||
# The hood fan is its device's primary feature: fan.py sets _attr_name = None
|
||||
# so it presents as the device itself, and never reads a catalog name.
|
||||
UNNAMED_DESCRIPTORS = {("fan", "fan")}
|
||||
|
||||
|
||||
def test_every_descriptor_has_an_entity_catalog_entry():
|
||||
"""A descriptor's name comes from the catalog or nowhere.
|
||||
|
||||
``translation_key`` defaults to ``desc.key`` (entity.py), and there is no
|
||||
Python-side name to fall back on, so a descriptor with no catalog entry
|
||||
is an entity with no name.
|
||||
"""
|
||||
entity_strings = _load("en")["entity"]
|
||||
missing = []
|
||||
for desc in _all_descriptions():
|
||||
platform = PLATFORM_OF[type(desc)]
|
||||
if (platform, desc.key) in UNNAMED_DESCRIPTORS:
|
||||
continue
|
||||
translation_key = desc.translation_key
|
||||
if callable(translation_key):
|
||||
# Runtime table resolvers pick their key out of the catalog
|
||||
# itself (see laundry.cycle_select), so there's nothing static
|
||||
# to check here; the generic 'cycle' fallback is asserted below.
|
||||
continue
|
||||
if translation_key is None and desc.name is not None:
|
||||
translation_key = desc.key
|
||||
if translation_key is None:
|
||||
continue # Main fan entity: device name + HA fan translations.
|
||||
platform = PLATFORM_OF[type(desc)]
|
||||
translation_key = desc.key
|
||||
if translation_key not in entity_strings.get(platform, {}):
|
||||
missing.append((platform, desc.key, translation_key))
|
||||
assert missing == []
|
||||
@@ -151,27 +162,6 @@ def test_every_translatable_descriptor_has_an_entity_catalog_entry():
|
||||
assert key in select_strings
|
||||
|
||||
|
||||
def test_descriptor_names_match_the_english_catalog():
|
||||
"""``SamsungEntityDescription.name`` never reaches the UI.
|
||||
|
||||
A descriptor carrying a ``name`` is translated under ``desc.key``
|
||||
(entity.py), so the catalog wins and the Python string survives purely
|
||||
as documentation next to the field it describes -- which is only worth
|
||||
keeping if it still says what the UI says. Editing one side alone is
|
||||
otherwise invisible.
|
||||
"""
|
||||
entity_strings = _load("en")["entity"]
|
||||
drifted = []
|
||||
for desc in _all_descriptions():
|
||||
if desc.name is None or desc.translation_key is not None:
|
||||
continue
|
||||
platform = PLATFORM_OF[type(desc)]
|
||||
catalog_name = entity_strings.get(platform, {}).get(desc.key, {}).get("name")
|
||||
if catalog_name != desc.name:
|
||||
drifted.append((platform, desc.key, desc.name, catalog_name))
|
||||
assert drifted == []
|
||||
|
||||
|
||||
def test_all_entity_state_translation_keys_are_lowercase():
|
||||
entity_strings = _load("en")["entity"]
|
||||
for platform in entity_strings.values():
|
||||
|
||||
Reference in new issue
Block a user