From 9fd21d20aec2c8a4dc8710808a89f8e739c40e69 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 15 Jan 2019 16:06:04 -0800 Subject: [PATCH] Fix loading translations for embedded platforms (#20122) * Fix loading translations for embedded platforms * Update doc string * Lint --- homeassistant/helpers/translation.py | 57 ++++++++++++++----- tests/helpers/test_translation.py | 10 +++- .../test_embedded/__init__.py | 6 ++ .../custom_components/test_embedded/switch.py | 7 +++ 4 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 tests/testing_config/custom_components/test_embedded/__init__.py create mode 100644 tests/testing_config/custom_components/test_embedded/switch.py diff --git a/homeassistant/helpers/translation.py b/homeassistant/helpers/translation.py index 2ca621154a1..e1c5895b89a 100644 --- a/homeassistant/helpers/translation.py +++ b/homeassistant/helpers/translation.py @@ -1,10 +1,10 @@ """Translation string lookup helpers.""" import logging -from os import path +import pathlib from typing import Any, Dict, Iterable from homeassistant import config_entries -from homeassistant.loader import get_component, bind_hass +from homeassistant.loader import get_component, get_platform, bind_hass from homeassistant.util.json import load_json from .typing import HomeAssistantType @@ -32,24 +32,51 @@ def flatten(data: Dict) -> Dict[str, Any]: def component_translation_file(hass: HomeAssistantType, component: str, language: str) -> str: - """Return the translation json file location for a component.""" - if '.' in component: - name = component.split('.', 1)[1] - else: - name = component + """Return the translation json file location for a component. - module = get_component(hass, component) + For component one of: + - components/light/.translations/nl.json + - components/.translations/group.nl.json + + For platform one of: + - components/light/.translations/hue.nl.json + - components/hue/.translations/light.nl.json + """ + is_platform = '.' in component + + if not is_platform: + module = get_component(hass, component) + assert module is not None + + module_path = pathlib.Path(module.__file__) + + if module.__name__ == module.__package__: + # light/__init__.py + filename = '{}.json'.format(language) + else: + # group.py + filename = '{}.{}.json'.format(component, language) + + return str(module_path.parent / '.translations' / filename) + + # It's a platform + parts = component.split('.', 1) + module = get_platform(hass, *parts) assert module is not None - component_path = path.dirname(module.__file__) - # If loading translations for the package root, (__init__.py), the - # prefix should be skipped. - if module.__name__ == module.__package__: - filename = '{}.json'.format(language) + # Either within HA or custom_components + # Either light/hue.py or hue/light.py + module_path = pathlib.Path(module.__file__) + + # Compare to parent so we don't have to strip off `.py` + if module_path.parent.name == parts[0]: + # this is light/hue.py + filename = "{}.{}.json".format(parts[1], language) else: - filename = '{}.{}.json'.format(name, language) + # this is hue/light.py + filename = "{}.{}.json".format(parts[0], language) - return path.join(component_path, '.translations', filename) + return str(module_path.parent / '.translations' / filename) def load_translations_files(translation_files: Dict[str, str]) \ diff --git a/tests/helpers/test_translation.py b/tests/helpers/test_translation.py index 99c6f7dddf1..9d62f204dcd 100644 --- a/tests/helpers/test_translation.py +++ b/tests/helpers/test_translation.py @@ -40,7 +40,10 @@ def test_flatten(): async def test_component_translation_file(hass): """Test the component translation file function.""" assert await async_setup_component(hass, 'switch', { - 'switch': {'platform': 'test'} + 'switch': [ + {'platform': 'test'}, + {'platform': 'test_embedded'} + ] }) assert await async_setup_component(hass, 'test_standalone', { 'test_standalone' @@ -53,6 +56,11 @@ async def test_component_translation_file(hass): hass, 'switch.test', 'en')) == path.normpath(hass.config.path( 'custom_components', 'switch', '.translations', 'test.en.json')) + assert path.normpath(translation.component_translation_file( + hass, 'switch.test_embedded', 'en')) == path.normpath(hass.config.path( + 'custom_components', 'test_embedded', '.translations', + 'switch.en.json')) + assert path.normpath(translation.component_translation_file( hass, 'test_standalone', 'en')) == path.normpath(hass.config.path( 'custom_components', '.translations', 'test_standalone.en.json')) diff --git a/tests/testing_config/custom_components/test_embedded/__init__.py b/tests/testing_config/custom_components/test_embedded/__init__.py new file mode 100644 index 00000000000..2206054356e --- /dev/null +++ b/tests/testing_config/custom_components/test_embedded/__init__.py @@ -0,0 +1,6 @@ +"""Component with embedded platforms.""" + + +async def async_setup(hass, config): + """Mock config.""" + return True diff --git a/tests/testing_config/custom_components/test_embedded/switch.py b/tests/testing_config/custom_components/test_embedded/switch.py new file mode 100644 index 00000000000..e4e0f5fcd39 --- /dev/null +++ b/tests/testing_config/custom_components/test_embedded/switch.py @@ -0,0 +1,7 @@ +"""Switch platform for the embedded component.""" + + +async def async_setup_platform(hass, config, async_add_entities_callback, + discovery_info=None): + """Find and return test switches.""" + pass