-
-
Notifications
You must be signed in to change notification settings - Fork 37.4k
Allow fetching translations by categories #34329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
58ea102
9a8f914
c573ecf
663b2a8
8eaefb1
dd9ffb8
02f81af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| """Translation string lookup helpers.""" | ||
| import asyncio | ||
| import logging | ||
| from typing import Any, Dict, Iterable, Optional | ||
| from typing import Any, Dict, Optional, Set | ||
|
|
||
| from homeassistant.core import callback | ||
| from homeassistant.loader import ( | ||
|
|
@@ -16,6 +16,7 @@ | |
|
|
||
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
| TRANSLATION_LOAD_LOCK = "translation_load_lock" | ||
| TRANSLATION_STRING_CACHE = "translation_string_cache" | ||
|
|
||
|
|
||
|
|
@@ -36,7 +37,7 @@ def flatten(data: Dict) -> Dict[str, Any]: | |
|
|
||
|
|
||
| @callback | ||
| def component_translation_file( | ||
| def component_translation_path( | ||
| component: str, language: str, integration: Integration | ||
| ) -> Optional[str]: | ||
| """Return the translation json file location for a component. | ||
|
|
@@ -80,7 +81,9 @@ def load_translations_files( | |
|
|
||
|
|
||
| def build_resources( | ||
| translation_cache: Dict[str, Dict[str, Any]], components: Iterable[str] | ||
| translation_cache: Dict[str, Dict[str, Any]], | ||
| components: Set[str], | ||
| category: Optional[str], | ||
| ) -> Dict[str, Dict[str, Any]]: | ||
| """Build the resources response for the given components.""" | ||
| # Build response | ||
|
|
@@ -91,40 +94,43 @@ def build_resources( | |
| else: | ||
| domain = component.split(".", 1)[0] | ||
|
|
||
| if domain not in resources: | ||
| resources[domain] = {} | ||
| domain_resources = resources.setdefault(domain, {}) | ||
|
|
||
| # Add the translations for this component to the domain resources. | ||
| # Since clients cannot determine which platform an entity belongs to, | ||
| # all translations for a domain will be returned together. | ||
| resources[domain].update(translation_cache[component]) | ||
|
|
||
| return resources | ||
| if category is None: | ||
| domain_resources.update(translation_cache[component]) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the translation cache contain keys that are either languages or domains?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. categories are the top level entries in So the dict is |
||
| continue | ||
|
|
||
| if category not in translation_cache[component]: | ||
| continue | ||
|
|
||
| @bind_hass | ||
| async def async_get_component_resources( | ||
| hass: HomeAssistantType, language: str | ||
| ) -> Dict[str, Any]: | ||
| """Return translation resources for all components. | ||
| domain_resources.setdefault(category, {}).update( | ||
| translation_cache[component][category] | ||
| ) | ||
|
|
||
| We go through all loaded components and platforms: | ||
| - see if they have already been loaded (exist in translation_cache) | ||
| - load them if they have not been loaded yet | ||
| - write them to cache | ||
| - flatten the cache and return | ||
| """ | ||
| # Get cache for this language | ||
| cache = hass.data.setdefault(TRANSLATION_STRING_CACHE, {}) | ||
| translation_cache = cache.setdefault(language, {}) | ||
| return {"component": resources} | ||
|
|
||
| # Get the set of components to check | ||
| components = hass.config.components | await async_get_config_flows(hass) | ||
|
|
||
| async def async_get_component_cache( | ||
| hass: HomeAssistantType, language: str, components: Set[str] | ||
| ) -> Dict[str, Any]: | ||
| """Return translation cache that includes all specified components.""" | ||
| # Get cache for this language | ||
| cache: Dict[str, Dict[str, Any]] = hass.data.setdefault( | ||
| TRANSLATION_STRING_CACHE, {} | ||
| ) | ||
| translation_cache: Dict[str, Any] = cache.setdefault(language, {}) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here we seem to set a language as top level key in the translation cache. How does that match with your description in the comment?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I got confused with all the nested dicts. The translation cache here and what we return is one level below the language key.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We return You're right that |
||
|
|
||
| # Calculate the missing components and platforms | ||
| missing_loaded = components - set(translation_cache) | ||
| missing_domains = {loaded.split(".")[-1] for loaded in missing_loaded} | ||
|
|
||
| if not missing_loaded: | ||
| return translation_cache | ||
|
|
||
| missing_domains = list({loaded.split(".")[-1] for loaded in missing_loaded}) | ||
| missing_integrations = dict( | ||
| zip( | ||
| missing_domains, | ||
|
|
@@ -141,7 +147,7 @@ async def async_get_component_resources( | |
| domain = parts[-1] | ||
| integration = missing_integrations[domain] | ||
|
|
||
| path = component_translation_file(loaded, language, integration) | ||
| path = component_translation_path(loaded, language, integration) | ||
| # No translation available | ||
| if path is None: | ||
| translation_cache[loaded] = {} | ||
|
|
@@ -167,22 +173,47 @@ async def async_get_component_resources( | |
| # Update cache | ||
| translation_cache.update(loaded_translations) | ||
|
|
||
| resources = build_resources(translation_cache, components) | ||
|
|
||
| # Return the component translations resources under the 'component' | ||
| # translation namespace | ||
| return flatten({"component": resources}) | ||
| return translation_cache | ||
|
|
||
|
|
||
| @bind_hass | ||
| async def async_get_translations( | ||
| hass: HomeAssistantType, language: str | ||
| hass: HomeAssistantType, | ||
| language: str, | ||
| category: Optional[str] = None, | ||
| integration: Optional[str] = None, | ||
| config_flow: Optional[bool] = None, | ||
| ) -> Dict[str, Any]: | ||
| """Return all backend translations.""" | ||
| resources = await async_get_component_resources(hass, language) | ||
| """Return all backend translations. | ||
|
|
||
| If integration specified, load it for that one. | ||
| Otherwise default to loaded intgrations combined with config flow | ||
| integrations if config_flow is true. | ||
| """ | ||
| if integration is not None: | ||
| components = {integration} | ||
| elif config_flow: | ||
| components = hass.config.components | await async_get_config_flows(hass) | ||
| else: | ||
| components = set(hass.config.components) | ||
|
|
||
| lock = hass.data.get(TRANSLATION_LOAD_LOCK) | ||
| if lock is None: | ||
| lock = hass.data[TRANSLATION_LOAD_LOCK] = asyncio.Lock() | ||
|
|
||
| tasks = [async_get_component_cache(hass, language, components)] | ||
|
|
||
| # Fetch the English resources, as a fallback for missing keys | ||
| if language != "en": | ||
| tasks.append(async_get_component_cache(hass, "en", components)) | ||
|
|
||
| async with lock: | ||
| results = await asyncio.gather(*tasks) | ||
|
|
||
| resources = flatten(build_resources(results[0], components, category)) | ||
|
|
||
| if language != "en": | ||
| # Fetch the English resources, as a fallback for missing keys | ||
| base_resources = await async_get_component_resources(hass, "en") | ||
| base_resources = flatten(build_resources(results[1], components, category)) | ||
| resources = {**base_resources, **resources} | ||
|
|
||
| return resources | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,5 +2,6 @@ | |
| "state": { | ||
| "string1": "Value 1", | ||
| "string2": "Value 2" | ||
| } | ||
| }, | ||
| "something": "else" | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the other categories than the languages?