From 435f540f7906bf88a18d81861e213d96375e9628 Mon Sep 17 00:00:00 2001 From: Sean Budd Date: Fri, 14 Jul 2023 17:02:13 +1000 Subject: [PATCH] Add-on store: support localisation for installed add-ons (#15137) Part of #14973 Summary of the issue: Installed add-ons do not have their display name and description localised, even if localised manifests exist for the installed add-on. Description of user facing changes Reinstate localisation for installed add-ons of the display name and description Description of development approach Fetch translated strings from the localised manifests Testing strategy: Ensure add-on with localisations is correctly displayed in the installed add-ons tab of the store. Available and updatable add-ons rely on the strings to be translated when fetching from the store. --- source/_addonStore/models/addon.py | 31 ++++++++++++------- source/_addonStore/models/status.py | 4 +-- source/_addonStore/models/version.py | 3 +- source/addonHandler/__init__.py | 4 +-- .../_addonStoreGui/controls/messageDialogs.py | 10 +++--- .../_addonStoreGui/viewModels/addonList.py | 8 ++--- 6 files changed, 35 insertions(+), 25 deletions(-) diff --git a/source/_addonStore/models/addon.py b/source/_addonStore/models/addon.py index afa44c7edff..0353f29143a 100644 --- a/source/_addonStore/models/addon.py +++ b/source/_addonStore/models/addon.py @@ -39,6 +39,7 @@ from addonHandler import ( # noqa: F401 Addon as AddonHandlerModel, AddonBase as AddonHandlerBaseModel, + AddonManifest, ) AddonGUICollectionT = Dict[Channel, CaseInsensitiveDict["_AddonGUIModel"]] """ @@ -110,25 +111,35 @@ def asdict(self) -> Dict[str, Any]: @dataclasses.dataclass(frozen=True) -class AddonGUIModel(_AddonGUIModel): +class AddonManifestModel(_AddonGUIModel): """Can be displayed in the add-on store GUI. - May come from manifest or add-on store data. + Comes from add-on manifest. """ addonId: str - displayName: str - description: str - publisher: str addonVersionName: str channel: Channel homepage: Optional[str] minNVDAVersion: MajorMinorPatch lastTestedVersion: MajorMinorPatch + _manifest: "AddonManifest" legacy: bool = False """ Legacy add-ons contain invalid metadata and should not be accessible through the add-on store. """ + @property + def displayName(self) -> str: + return self._manifest["summary"] + + @property + def description(self) -> str: + return self._manifest["description"] + + @property + def publisher(self) -> str: + return self._manifest["author"] + @dataclasses.dataclass(frozen=True) # once created, it should not be modified. class AddonStoreModel(_AddonGUIModel): @@ -229,21 +240,19 @@ def _createStoreModelFromData(addon: Dict[str, Any]) -> AddonStoreModel: ) -def _createGUIModelFromManifest(addon: "AddonHandlerBaseModel") -> AddonGUIModel: - homepage = addon.manifest.get("url") +def _createGUIModelFromManifest(addon: "AddonHandlerBaseModel") -> AddonManifestModel: + homepage: Optional[str] = addon.manifest.get("url") if homepage == "None": # Manifest strings can be set to "None" homepage = None - return AddonGUIModel( + return AddonManifestModel( addonId=addon.name, - displayName=addon.manifest["summary"], - description=addon.manifest["description"], - publisher=addon.manifest["author"], channel=Channel.EXTERNAL, addonVersionName=addon.version, homepage=homepage, minNVDAVersion=MajorMinorPatch(*addon.minimumNVDAVersion), lastTestedVersion=MajorMinorPatch(*addon.lastTestedNVDAVersion), + _manifest=addon.manifest ) diff --git a/source/_addonStore/models/status.py b/source/_addonStore/models/status.py index 5bc88d2a05d..88d74f680e0 100644 --- a/source/_addonStore/models/status.py +++ b/source/_addonStore/models/status.py @@ -25,7 +25,7 @@ from .version import SupportsVersionCheck if TYPE_CHECKING: - from .addon import AddonGUIModel # noqa: F401 + from .addon import _AddonGUIModel # noqa: F401 from addonHandler import AddonsState # noqa: F401 @@ -144,7 +144,7 @@ class AddonStateCategory(str, enum.Enum): """Add-ons that are blocked from running because they are incompatible""" -def getStatus(model: "AddonGUIModel") -> Optional[AvailableAddonStatus]: +def getStatus(model: "_AddonGUIModel") -> Optional[AvailableAddonStatus]: from addonHandler import ( state as addonHandlerState, ) diff --git a/source/_addonStore/models/version.py b/source/_addonStore/models/version.py index 5b8a2ea606b..ffb9faa526f 100644 --- a/source/_addonStore/models/version.py +++ b/source/_addonStore/models/version.py @@ -37,7 +37,8 @@ class SupportsVersionCheck(Protocol): """ Examples implementing this protocol include: - addonHandler.Addon - addonHandler.AddonBundle - - _addonStore.models.AddonGUIModel + - _addonStore.models._AddonGUIModel + - _addonStore.models.AddonManifestModel - _addonStore.models.AddonStoreModel """ minimumNVDAVersion: addonAPIVersion.AddonApiVersionT diff --git a/source/addonHandler/__init__.py b/source/addonHandler/__init__.py index 6fe39667e6f..52aa8566d57 100644 --- a/source/addonHandler/__init__.py +++ b/source/addonHandler/__init__.py @@ -57,7 +57,7 @@ if TYPE_CHECKING: from _addonStore.models.addon import ( # noqa: F401 - AddonGUIModel, + AddonManifestModel, AddonHandlerModelGeneratorT, AddonStoreModel, ) @@ -392,7 +392,7 @@ def _addonStoreData(self) -> Optional["AddonStoreModel"]: return addonDataManager._getCachedInstalledAddonData(self.name) @property - def _addonGuiModel(self) -> "AddonGUIModel": + def _addonGuiModel(self) -> "AddonManifestModel": from _addonStore.models.addon import _createGUIModelFromManifest return _createGUIModelFromManifest(self) diff --git a/source/gui/_addonStoreGui/controls/messageDialogs.py b/source/gui/_addonStoreGui/controls/messageDialogs.py index ed06b2b64ce..4698141a83b 100644 --- a/source/gui/_addonStoreGui/controls/messageDialogs.py +++ b/source/gui/_addonStoreGui/controls/messageDialogs.py @@ -10,7 +10,7 @@ import wx import addonAPIVersion -from _addonStore.models.addon import AddonGUIModel +from _addonStore.models.addon import _AddonGUIModel from gui.addonGui import ErrorAddonInstallDialog from gui.message import messageBox @@ -49,7 +49,7 @@ def _addButtons(self, buttonHelper: "ButtonHelper") -> None: def _shouldProceedWhenInstalledAddonVersionUnknown( parent: wx.Window, - addon: AddonGUIModel + addon: _AddonGUIModel ) -> bool: # an installed add-on should have an addon Handler Model assert addon._addonHandlerModel @@ -97,7 +97,7 @@ def _shouldProceedToRemoveAddonDialog( def _shouldInstallWhenAddonTooOldDialog( parent: wx.Window, - addon: AddonGUIModel + addon: _AddonGUIModel ) -> bool: incompatibleMessage = pgettext( "addonStore", @@ -126,7 +126,7 @@ def _shouldInstallWhenAddonTooOldDialog( def _shouldEnableWhenAddonTooOldDialog( parent: wx.Window, - addon: AddonGUIModel + addon: _AddonGUIModel ) -> bool: incompatibleMessage = pgettext( "addonStore", @@ -153,7 +153,7 @@ def _shouldEnableWhenAddonTooOldDialog( ).ShowModal() == wx.YES -def _showAddonInfo(addon: AddonGUIModel) -> None: +def _showAddonInfo(addon: _AddonGUIModel) -> None: message = [ pgettext( "addonStore", diff --git a/source/gui/_addonStoreGui/viewModels/addonList.py b/source/gui/_addonStoreGui/viewModels/addonList.py index dd0cfa17c71..fe085e5bb88 100644 --- a/source/gui/_addonStoreGui/viewModels/addonList.py +++ b/source/gui/_addonStoreGui/viewModels/addonList.py @@ -20,7 +20,7 @@ from requests.structures import CaseInsensitiveDict from _addonStore.models.addon import ( - AddonGUIModel, + _AddonGUIModel, ) from _addonStore.models.status import ( _StatusFilterKey, @@ -86,15 +86,15 @@ class AddonListField(_AddonListFieldData, Enum): class AddonListItemVM: def __init__( self, - model: AddonGUIModel, + model: _AddonGUIModel, status: AvailableAddonStatus = AvailableAddonStatus.AVAILABLE ): - self._model: AddonGUIModel = model # read-only + self._model: _AddonGUIModel = model # read-only self._status: AvailableAddonStatus = status # modifications triggers L{updated.notify} self.updated = extensionPoints.Action() # Notify of changes to VM, argument: addonListItemVM @property - def model(self) -> AddonGUIModel: + def model(self) -> _AddonGUIModel: return self._model @property