src/eric7/Project/ProjectTranslationsBrowser.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Project/ProjectTranslationsBrowser.py	Thu Jul 07 11:23:56 2022 +0200
@@ -0,0 +1,1382 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class used to display the translations part of the
+project.
+"""
+
+import os
+import shutil
+import fnmatch
+import functools
+import contextlib
+
+from PyQt6.QtCore import pyqtSignal, Qt, QProcess, QEventLoop
+from PyQt6.QtGui import QGuiApplication, QCursor
+from PyQt6.QtWidgets import QDialog, QMenu
+
+from EricGui.EricOverrideCursor import EricOverridenCursor
+
+from EricWidgets import EricMessageBox
+from EricWidgets.EricApplication import ericApp
+
+from .ProjectBrowserModel import (
+    ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem,
+    ProjectBrowserDirectoryItem, ProjectBrowserTranslationType
+)
+from .ProjectBaseBrowser import ProjectBaseBrowser
+
+import UI.PixmapCache
+from UI.NotificationWidget import NotificationTypes
+
+import Preferences
+import Utilities
+
+
+class ProjectTranslationsBrowser(ProjectBaseBrowser):
+    """
+    A class used to display the translations part of the project.
+    
+    @signal appendStdout(str) emitted after something was received from
+        a QProcess on stdout
+    @signal appendStderr(str) emitted after something was received from
+        a QProcess on stderr
+    @signal showMenu(str, QMenu) emitted when a menu is about to be shown.
+        The name of the menu and a reference to the menu are given.
+    """
+    appendStdout = pyqtSignal(str)
+    appendStderr = pyqtSignal(str)
+    showMenu = pyqtSignal(str, QMenu)
+    
+    def __init__(self, project, parent=None):
+        """
+        Constructor
+        
+        @param project reference to the project object
+        @param parent parent widget of this browser (QWidget)
+        """
+        ProjectBaseBrowser.__init__(self, project,
+                                    ProjectBrowserTranslationType, parent)
+        self.isTranslationsBrowser = True
+        
+        self.selectedItemsFilter = [ProjectBrowserFileItem,
+                                    ProjectBrowserSimpleDirectoryItem]
+        
+        self.setWindowTitle(self.tr('Translations'))
+
+        self.setWhatsThis(self.tr(
+            """<b>Project Translations Browser</b>"""
+            """<p>This allows to easily see all translations contained in"""
+            """ the current project. Several actions can be executed via"""
+            """ the context menu.</p>"""
+        ))
+        
+        self.__lreleaseProcesses = []
+        self.__pylupdateProcesses = []
+        self.lreleaseProcRunning = False
+        self.pylupdateProcRunning = False
+        self.__tmpProjects = []
+        
+    def _createPopupMenus(self):
+        """
+        Protected overloaded method to generate the popup menu.
+        """
+        self.menuActions = []
+        self.multiMenuActions = []
+        self.dirMenuActions = []
+        self.dirMultiMenuActions = []
+        
+        self.tsMenuActions = []
+        self.qmMenuActions = []
+        self.tsprocMenuActions = []
+        self.qmprocMenuActions = []
+        
+        self.tsMultiMenuActions = []
+        self.qmMultiMenuActions = []
+        self.tsprocMultiMenuActions = []
+        self.qmprocMultiMenuActions = []
+        
+        self.tsprocDirMenuActions = []
+        self.qmprocDirMenuActions = []
+        
+        self.tsprocBackMenuActions = []
+        self.qmprocBackMenuActions = []
+        
+        self.menu = QMenu(self)
+        if self.project.getProjectType() in [
+            "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E7Plugin",
+            "PySide2", "PySide2C", "PySide6", "PySide6C"
+        ]:
+            act = self.menu.addAction(
+                self.tr('Generate translation'), self.__generateSelected)
+            self.tsMenuActions.append(act)
+            self.tsprocMenuActions.append(act)
+            act = self.menu.addAction(
+                self.tr('Generate translation (with obsolete)'),
+                self.__generateObsoleteSelected)
+            self.tsMenuActions.append(act)
+            self.tsprocMenuActions.append(act)
+            act = self.menu.addAction(
+                self.tr('Generate all translations'), self.__generateAll)
+            self.tsprocMenuActions.append(act)
+            act = self.menu.addAction(
+                self.tr('Generate all translations (with obsolete)'),
+                self.__generateObsoleteAll)
+            self.tsprocMenuActions.append(act)
+            self.menu.addSeparator()
+            act = self.menu.addAction(
+                self.tr('Open in Qt-Linguist'), self._openItem)
+            self.tsMenuActions.append(act)
+            act = self.menu.addAction(
+                self.tr('Open in Editor'), self.__openFileInEditor)
+            self.tsMenuActions.append(act)
+            self.menu.addSeparator()
+            act = self.menu.addAction(
+                self.tr('Release translation'), self.__releaseSelected)
+            self.tsMenuActions.append(act)
+            self.qmprocMenuActions.append(act)
+            act = self.menu.addAction(
+                self.tr('Release all translations'), self.__releaseAll)
+            self.qmprocMenuActions.append(act)
+            self.menu.addSeparator()
+            act = self.menu.addAction(
+                self.tr('Preview translation'), self.__TRPreview)
+            self.qmMenuActions.append(act)
+            act = self.menu.addAction(
+                self.tr('Preview all translations'), self.__TRPreviewAll)
+            self.menu.addSeparator()
+        else:
+            if self.hooks["extractMessages"] is not None:
+                act = self.menu.addAction(
+                    self.hooksMenuEntries.get(
+                        "extractMessages",
+                        self.tr('Extract messages')),
+                    self.__extractMessages)
+                self.menuActions.append(act)
+                self.menu.addSeparator()
+            if self.hooks["generateSelected"] is not None:
+                act = self.menu.addAction(
+                    self.hooksMenuEntries.get(
+                        "generateSelected",
+                        self.tr('Generate translation')),
+                    self.__generateSelected)
+                self.tsMenuActions.append(act)
+                self.tsprocMenuActions.append(act)
+            if self.hooks["generateSelectedWithObsolete"] is not None:
+                act = self.menu.addAction(
+                    self.hooksMenuEntries.get(
+                        "generateSelectedWithObsolete",
+                        self.tr('Generate translation (with obsolete)')),
+                    self.__generateObsoleteSelected)
+                self.tsMenuActions.append(act)
+                self.tsprocMenuActions.append(act)
+            if self.hooks["generateAll"] is not None:
+                act = self.menu.addAction(
+                    self.hooksMenuEntries.get(
+                        "generateAll",
+                        self.tr('Generate all translations')),
+                    self.__generateAll)
+                self.tsprocMenuActions.append(act)
+            if self.hooks["generateAllWithObsolete"] is not None:
+                act = self.menu.addAction(
+                    self.hooksMenuEntries.get(
+                        "generateAllWithObsolete",
+                        self.tr(
+                            'Generate all translations (with obsolete)')),
+                    self.__generateObsoleteAll)
+                self.tsprocMenuActions.append(act)
+            self.menu.addSeparator()
+            if self.hooks["open"] is not None:
+                act = self.menu.addAction(
+                    self.hooksMenuEntries.get(
+                        "open", self.tr('Open')),
+                    self._openItem)
+                self.tsMenuActions.append(act)
+            act = self.menu.addAction(
+                self.tr('Open in Editor'), self.__openFileInEditor)
+            self.tsMenuActions.append(act)
+            self.menu.addSeparator()
+            if self.hooks["releaseSelected"] is not None:
+                act = self.menu.addAction(
+                    self.hooksMenuEntries.get(
+                        "releaseSelected",
+                        self.tr('Release translation')),
+                    self.__releaseSelected)
+                self.tsMenuActions.append(act)
+                self.qmprocMenuActions.append(act)
+            if self.hooks["releaseAll"] is not None:
+                act = self.menu.addAction(
+                    self.hooksMenuEntries.get(
+                        "releaseAll",
+                        self.tr('Release all translations')),
+                    self.__releaseAll)
+                self.qmprocMenuActions.append(act)
+            self.menu.addSeparator()
+        act = self.menu.addAction(
+            self.tr('Remove from project'), self.__removeLanguageFile)
+        self.menuActions.append(act)
+        act = self.menu.addAction(
+            self.tr('Delete'), self.__deleteLanguageFile)
+        self.menuActions.append(act)
+        self.menu.addSeparator()
+        self.__addTranslationAct = self.menu.addAction(
+            self.tr('Add translation...'), self.project.addLanguage)
+        self.menu.addAction(
+            self.tr('Add translation files...'),
+            self.__addTranslationFiles)
+        self.menu.addSeparator()
+        self.menu.addAction(
+            self.tr('Copy Path to Clipboard'), self._copyToClipboard)
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr('Configure...'), self._configure)
+        
+        self.backMenu = QMenu(self)
+        if self.project.getProjectType() in [
+            "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E7Plugin",
+            "PySide2", "PySide2C", "PySide6", "PySide6C"
+        ]:
+            act = self.backMenu.addAction(
+                self.tr('Generate all translations'),
+                self.__generateAll)
+            self.tsprocBackMenuActions.append(act)
+            act = self.backMenu.addAction(
+                self.tr('Generate all translations (with obsolete)'),
+                self.__generateObsoleteAll)
+            self.tsprocBackMenuActions.append(act)
+            act = self.backMenu.addAction(
+                self.tr('Release all translations'),
+                self.__releaseAll)
+            self.qmprocBackMenuActions.append(act)
+            self.backMenu.addSeparator()
+            act = self.backMenu.addAction(
+                self.tr('Preview all translations'),
+                self.__TRPreview)
+        else:
+            if self.hooks["extractMessages"] is not None:
+                act = self.backMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "extractMessages",
+                        self.tr('Extract messages')),
+                    self.__extractMessages)
+                self.backMenu.addSeparator()
+            if self.hooks["generateAll"] is not None:
+                act = self.backMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "generateAll",
+                        self.tr('Generate all translations')),
+                    self.__generateAll)
+                self.tsprocBackMenuActions.append(act)
+            if self.hooks["generateAllWithObsolete"] is not None:
+                act = self.backMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "generateAllWithObsolete",
+                        self.tr(
+                            'Generate all translations (with obsolete)')),
+                    self.__generateObsoleteAll)
+                self.tsprocBackMenuActions.append(act)
+            if self.hooks["releaseAll"] is not None:
+                act = self.backMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "releaseAll",
+                        self.tr('Release all translations')),
+                    self.__releaseAll)
+                self.qmprocBackMenuActions.append(act)
+        self.backMenu.addSeparator()
+        self.__addTranslationBackAct = self.backMenu.addAction(
+            self.tr('Add translation...'), self.project.addLanguage)
+        self.backMenu.addAction(
+            self.tr('Add translation files...'),
+            self.__addTranslationFiles)
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(self.tr('Configure...'), self._configure)
+        self.backMenu.setEnabled(False)
+
+        # create the menu for multiple selected files
+        self.multiMenu = QMenu(self)
+        if self.project.getProjectType() in [
+            "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E7Plugin",
+            "PySide2", "PySide2C", "PySide6", "PySide6C"
+        ]:
+            act = self.multiMenu.addAction(
+                self.tr('Generate translations'),
+                self.__generateSelected)
+            self.tsMultiMenuActions.append(act)
+            self.tsprocMultiMenuActions.append(act)
+            act = self.multiMenu.addAction(
+                self.tr('Generate translations (with obsolete)'),
+                self.__generateObsoleteSelected)
+            self.tsMultiMenuActions.append(act)
+            self.tsprocMultiMenuActions.append(act)
+            self.multiMenu.addSeparator()
+            act = self.multiMenu.addAction(
+                self.tr('Open in Qt-Linguist'), self._openItem)
+            self.tsMultiMenuActions.append(act)
+            act = self.multiMenu.addAction(
+                self.tr('Open in Editor'), self.__openFileInEditor)
+            self.tsMultiMenuActions.append(act)
+            self.multiMenu.addSeparator()
+            act = self.multiMenu.addAction(
+                self.tr('Release translations'), self.__releaseSelected)
+            self.tsMultiMenuActions.append(act)
+            self.qmprocMultiMenuActions.append(act)
+            self.multiMenu.addSeparator()
+            act = self.multiMenu.addAction(
+                self.tr('Preview translations'), self.__TRPreview)
+            self.qmMultiMenuActions.append(act)
+        else:
+            if self.hooks["extractMessages"] is not None:
+                act = self.multiMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "extractMessages",
+                        self.tr('Extract messages')),
+                    self.__extractMessages)
+                self.multiMenuActions.append(act)
+                self.multiMenu.addSeparator()
+            if self.hooks["generateSelected"] is not None:
+                act = self.multiMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "generateSelected",
+                        self.tr('Generate translations')),
+                    self.__generateSelected)
+                self.tsMultiMenuActions.append(act)
+                self.tsprocMultiMenuActions.append(act)
+            if self.hooks["generateSelectedWithObsolete"] is not None:
+                act = self.multiMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "generateSelectedWithObsolete",
+                        self.tr('Generate translations (with obsolete)')),
+                    self.__generateObsoleteSelected)
+                self.tsMultiMenuActions.append(act)
+                self.tsprocMultiMenuActions.append(act)
+            self.multiMenu.addSeparator()
+            if self.hooks["open"] is not None:
+                act = self.multiMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "open", self.tr('Open')),
+                    self._openItem)
+                self.tsMultiMenuActions.append(act)
+            act = self.multiMenu.addAction(
+                self.tr('Open in Editor'), self.__openFileInEditor)
+            self.tsMultiMenuActions.append(act)
+            self.multiMenu.addSeparator()
+            if self.hooks["releaseSelected"] is not None:
+                act = self.multiMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "releaseSelected",
+                        self.tr('Release translations')),
+                    self.__releaseSelected)
+                self.tsMultiMenuActions.append(act)
+                self.qmprocMultiMenuActions.append(act)
+        self.multiMenu.addSeparator()
+        act = self.multiMenu.addAction(
+            self.tr('Remove from project'), self.__removeLanguageFile)
+        self.multiMenuActions.append(act)
+        act = self.multiMenu.addAction(
+            self.tr('Delete'), self.__deleteLanguageFile)
+        self.multiMenuActions.append(act)
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(self.tr('Configure...'), self._configure)
+
+        self.dirMenu = QMenu(self)
+        if self.project.getProjectType() in [
+            "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E7Plugin",
+            "PySide2", "PySide2C", "PySide6", "PySide6C"
+        ]:
+            act = self.dirMenu.addAction(
+                self.tr('Generate all translations'),
+                self.__generateAll)
+            self.tsprocDirMenuActions.append(act)
+            act = self.dirMenu.addAction(
+                self.tr('Generate all translations (with obsolete)'),
+                self.__generateObsoleteAll)
+            self.tsprocDirMenuActions.append(act)
+            act = self.dirMenu.addAction(
+                self.tr('Release all translations'),
+                self.__releaseAll)
+            self.qmprocDirMenuActions.append(act)
+            self.dirMenu.addSeparator()
+            act = self.dirMenu.addAction(
+                self.tr('Preview all translations'),
+                self.__TRPreview)
+        else:
+            if self.hooks["extractMessages"] is not None:
+                act = self.dirMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "extractMessages",
+                        self.tr('Extract messages')),
+                    self.__extractMessages)
+                self.dirMenuActions.append(act)
+                self.dirMenu.addSeparator()
+            if self.hooks["generateAll"] is not None:
+                act = self.dirMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "generateAll",
+                        self.tr('Generate all translations')),
+                    self.__generateAll)
+                self.tsprocDirMenuActions.append(act)
+            if self.hooks["generateAllWithObsolete"] is not None:
+                act = self.dirMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "generateAllWithObsolete",
+                        self.tr(
+                            'Generate all translations (with obsolete)')),
+                    self.__generateObsoleteAll)
+                self.tsprocDirMenuActions.append(act)
+            if self.hooks["releaseAll"] is not None:
+                act = self.dirMenu.addAction(
+                    self.hooksMenuEntries.get(
+                        "releaseAll",
+                        self.tr('Release all translations')),
+                    self.__releaseAll)
+                self.qmprocDirMenuActions.append(act)
+        self.dirMenu.addSeparator()
+        act = self.dirMenu.addAction(
+            self.tr('Delete'), self._deleteDirectory)
+        self.dirMenuActions.append(act)
+        self.dirMenu.addSeparator()
+        self.__addTranslationDirAct = self.dirMenu.addAction(
+            self.tr('Add translation...'), self.project.addLanguage)
+        self.dirMenu.addAction(
+            self.tr('Add translation files...'),
+            self.__addTranslationFiles)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(
+            self.tr('Copy Path to Clipboard'), self._copyToClipboard)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr('Configure...'), self._configure)
+        
+        self.dirMultiMenu = None
+        
+        self.menu.aboutToShow.connect(self.__showContextMenu)
+        self.multiMenu.aboutToShow.connect(self.__showContextMenuMulti)
+        self.dirMenu.aboutToShow.connect(self.__showContextMenuDir)
+        self.backMenu.aboutToShow.connect(self.__showContextMenuBack)
+        self.mainMenu = self.menu
+        
+    def _contextMenuRequested(self, coord):
+        """
+        Protected slot to show the context menu.
+        
+        @param coord the position of the mouse pointer (QPoint)
+        """
+        if not self.project.isOpen():
+            return
+        
+        with contextlib.suppress(Exception):
+            categories = self.getSelectedItemsCountCategorized(
+                [ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem])
+            cnt = categories["sum"]
+            if cnt <= 1:
+                index = self.indexAt(coord)
+                if index.isValid():
+                    self._selectSingleItem(index)
+                    categories = self.getSelectedItemsCountCategorized(
+                        [ProjectBrowserFileItem,
+                         ProjectBrowserSimpleDirectoryItem])
+                    cnt = categories["sum"]
+                        
+            bfcnt = categories[str(ProjectBrowserFileItem)]
+            sdcnt = categories[str(ProjectBrowserSimpleDirectoryItem)]
+            if cnt > 1 and cnt == bfcnt:
+                self.multiMenu.popup(self.mapToGlobal(coord))
+            else:
+                index = self.indexAt(coord)
+                if cnt == 1 and index.isValid():
+                    if bfcnt == 1:
+                        self.menu.popup(self.mapToGlobal(coord))
+                    elif sdcnt == 1:
+                        self.dirMenu.popup(self.mapToGlobal(coord))
+                    else:
+                        self.backMenu.popup(self.mapToGlobal(coord))
+                else:
+                    self.backMenu.popup(self.mapToGlobal(coord))
+        
+    def __showContextMenu(self):
+        """
+        Private slot called by the menu aboutToShow signal.
+        """
+        if self.project.getProjectType() in [
+            "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E7Plugin",
+            "PySide2", "PySide2C", "PySide6", "PySide6C"
+        ]:
+            tsFiles = 0
+            qmFiles = 0
+            itmList = self.getSelectedItems()
+            for itm in itmList[:]:
+                if itm.fileName().endswith('.ts'):
+                    tsFiles += 1
+                elif itm.fileName().endswith('.qm'):
+                    qmFiles += 1
+            if (
+                (tsFiles > 0 and qmFiles > 0) or
+                (tsFiles == 0 and qmFiles == 0)
+            ):
+                for act in self.tsMenuActions + self.qmMenuActions:
+                    act.setEnabled(False)
+            elif tsFiles > 0:
+                for act in self.tsMenuActions:
+                    act.setEnabled(True)
+                for act in self.qmMenuActions:
+                    act.setEnabled(False)
+            elif qmFiles > 0:
+                for act in self.tsMenuActions:
+                    act.setEnabled(False)
+                for act in self.qmMenuActions:
+                    act.setEnabled(True)
+            if self.pylupdateProcRunning:
+                for act in self.tsprocMenuActions:
+                    act.setEnabled(False)
+            if self.lreleaseProcRunning:
+                for act in self.qmprocMenuActions:
+                    act.setEnabled(True)
+        self.__addTranslationAct.setEnabled(
+            self.project.getTranslationPattern() != "")
+        
+        ProjectBaseBrowser._showContextMenu(self, self.menu)
+        
+        self.showMenu.emit("Main", self.menu)
+        
+    def __showContextMenuMulti(self):
+        """
+        Private slot called by the multiMenu aboutToShow signal.
+        """
+        if self.project.getProjectType() in [
+            "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E7Plugin",
+            "PySide2", "PySide2C", "PySide6", "PySide6C"
+        ]:
+            tsFiles = 0
+            qmFiles = 0
+            itmList = self.getSelectedItems()
+            for itm in itmList[:]:
+                if itm.fileName().endswith('.ts'):
+                    tsFiles += 1
+                elif itm.fileName().endswith('.qm'):
+                    qmFiles += 1
+            if (
+                (tsFiles > 0 and qmFiles > 0) or
+                (tsFiles == 0 and qmFiles == 0)
+            ):
+                for act in self.tsMultiMenuActions + self.qmMultiMenuActions:
+                    act.setEnabled(False)
+            elif tsFiles > 0:
+                for act in self.tsMultiMenuActions:
+                    act.setEnabled(True)
+                for act in self.qmMultiMenuActions:
+                    act.setEnabled(False)
+            elif qmFiles > 0:
+                for act in self.tsMultiMenuActions:
+                    act.setEnabled(False)
+                for act in self.qmMultiMenuActions:
+                    act.setEnabled(True)
+            if self.pylupdateProcRunning:
+                for act in self.tsprocMultiMenuActions:
+                    act.setEnabled(False)
+            if self.lreleaseProcRunning:
+                for act in self.qmprocMultiMenuActions:
+                    act.setEnabled(True)
+        
+        ProjectBaseBrowser._showContextMenuMulti(self, self.multiMenu)
+        
+        self.showMenu.emit("MainMulti", self.multiMenu)
+        
+    def __showContextMenuDir(self):
+        """
+        Private slot called by the dirMenu aboutToShow signal.
+        """
+        if self.project.getProjectType() in [
+            "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E7Plugin",
+            "PySide2", "PySide2C", "PySide6", "PySide6C"
+        ]:
+            if self.pylupdateProcRunning:
+                for act in self.tsprocDirMenuActions:
+                    act.setEnabled(False)
+            if self.lreleaseProcRunning:
+                for act in self.qmprocDirMenuActions:
+                    act.setEnabled(True)
+        self.__addTranslationDirAct.setEnabled(
+            self.project.getTranslationPattern() != "")
+        
+        ProjectBaseBrowser._showContextMenuDir(self, self.dirMenu)
+        
+        self.showMenu.emit("MainDir", self.dirMenu)
+        
+    def __showContextMenuBack(self):
+        """
+        Private slot called by the backMenu aboutToShow signal.
+        """
+        if self.project.getProjectType() in [
+            "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E7Plugin",
+            "PySide2", "PySide2C", "PySide6", "PySide6C"
+        ]:
+            if self.pylupdateProcRunning:
+                for act in self.tsprocBackMenuActions:
+                    act.setEnabled(False)
+            if self.lreleaseProcRunning:
+                for act in self.qmprocBackMenuActions:
+                    act.setEnabled(True)
+        self.__addTranslationBackAct.setEnabled(
+            self.project.getTranslationPattern() != "")
+        
+        self.showMenu.emit("MainBack", self.backMenu)
+        
+    def __addTranslationFiles(self):
+        """
+        Private method to add translation files to the project.
+        """
+        itm = self.model().item(self.currentIndex())
+        if isinstance(itm, ProjectBrowserFileItem):
+            dn = os.path.dirname(itm.fileName())
+        elif isinstance(
+            itm,
+            (ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem)
+        ):
+            dn = itm.dirName()
+        else:
+            dn = None
+        self.project.addFiles('translation', dn)
+        
+    def _openItem(self):
+        """
+        Protected slot to handle the open popup menu entry.
+        """
+        itmList = self.getSelectedItems()
+        for itm in itmList:
+            if isinstance(itm, ProjectBrowserFileItem):
+                # hook support
+                if self.hooks["open"] is not None:
+                    self.hooks["open"](itm.fileName())
+                elif itm.isLinguistFile():
+                    if itm.fileExt() == '.ts':
+                        self.linguistFile.emit(itm.fileName())
+                    else:
+                        self.trpreview.emit([itm.fileName()])
+                else:
+                    self.sourceFile.emit(itm.fileName())
+        
+    def __openFileInEditor(self):
+        """
+        Private slot to handle the Open in Editor menu action.
+        """
+        itmList = self.getSelectedItems()
+        for itm in itmList[:]:
+            self.sourceFile.emit(itm.fileName())
+        
+    def __removeLanguageFile(self):
+        """
+        Private method to remove a translation from the project.
+        """
+        itmList = self.getSelectedItems()
+        
+        for itm in itmList[:]:
+            fn = itm.fileName()
+            self.closeSourceWindow.emit(fn)
+            self.project.removeLanguageFile(fn)
+        
+    def __deleteLanguageFile(self):
+        """
+        Private method to delete a translation file from the project.
+        """
+        itmList = self.getSelectedItems()
+        
+        translationFiles = [itm.fileName() for itm in itmList]
+        
+        from UI.DeleteFilesConfirmationDialog import (
+            DeleteFilesConfirmationDialog
+        )
+        dlg = DeleteFilesConfirmationDialog(
+            self.parent(),
+            self.tr("Delete translation files"),
+            self.tr("Do you really want to delete these translation files"
+                    " from the project?"),
+            translationFiles)
+        
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            for fn in translationFiles:
+                self.closeSourceWindow.emit(fn)
+                self.project.deleteLanguageFile(fn)
+        
+    def __TRPreview(self, previewAll=False):
+        """
+        Private slot to handle the Preview translations action.
+        
+        @param previewAll flag indicating, that all translations
+            should be previewed (boolean)
+        """
+        fileNames = []
+        itmList = self.getSelectedItems()
+        if itmList and not previewAll:
+            for itm in itmList:
+                if isinstance(itm, ProjectBrowserSimpleDirectoryItem):
+                    dname = self.project.getRelativePath(itm.dirName())
+                    trfiles = sorted(self.project.pdata["TRANSLATIONS"][:])
+                    for trfile in trfiles:
+                        if (
+                            trfile.startswith(dname) and
+                            trfile not in fileNames
+                        ):
+                            fileNames.append(
+                                os.path.join(self.project.ppath, trfile))
+                else:
+                    fn = itm.fileName()
+                    if fn not in fileNames:
+                        fileNames.append(os.path.join(self.project.ppath, fn))
+        else:
+            trfiles = sorted(self.project.pdata["TRANSLATIONS"][:])
+            fileNames.extend([os.path.join(self.project.ppath, trfile)
+                              for trfile in trfiles
+                              if trfile.endswith('.qm')])
+        self.trpreview[list, bool].emit(fileNames, True)
+        
+    def __TRPreviewAll(self):
+        """
+        Private slot to handle the Preview all translations action.
+        """
+        self.__TRPreview(True)
+    
+    ###########################################################################
+    ##  Methods to support the generation and release commands
+    ###########################################################################
+    
+    def __writeTempProjectFile(self, langs, filterList):
+        """
+        Private method to write a temporary project file suitable for
+        pylupdate and lrelease.
+        
+        @param langs list of languages to include in the process. An empty
+            list (default) means that all translations should be included.
+            (list of ProjectBrowserFileItem)
+        @param filterList list of source file extension that should be
+            considered (list of strings)
+        @return flag indicating success
+        """
+        path, ext = os.path.splitext(self.project.pfile)
+        pfile = '{0}_e4x.pro'.format(path)
+        
+        # only consider files satisfying the filter criteria
+        _sources = [s for s in self.project.pdata["SOURCES"]
+                    if os.path.splitext(s)[1] in filterList]
+        sources = []
+        for s in _sources:
+            addIt = True
+            for transExcept in self.project.pdata["TRANSLATIONEXCEPTIONS"]:
+                if s.startswith(transExcept):
+                    addIt = False
+                    break
+            if addIt:
+                sources.append(s)
+        
+        _forms = [f for f in self.project.pdata["FORMS"] if f.endswith('.ui')]
+        forms = []
+        for f in _forms:
+            addIt = True
+            for transExcept in self.project.pdata["TRANSLATIONEXCEPTIONS"]:
+                if f.startswith(transExcept):
+                    addIt = False
+                    break
+            if addIt:
+                forms.append(f)
+        
+        if langs:
+            langs = [self.project.getRelativePath(lang.fileName())
+                     for lang in langs if lang.fileName().endswith('.ts')]
+        else:
+            try:
+                pattern = self.project.pdata["TRANSLATIONPATTERN"].replace(
+                    "%language%", "*")
+                langs = [lang for lang in self.project.pdata["TRANSLATIONS"]
+                         if fnmatch.fnmatch(lang, pattern)]
+            except IndexError:
+                langs = []
+        if not langs:
+            EricMessageBox.warning(
+                self,
+                self.tr("Write temporary project file"),
+                self.tr("""No translation files (*.ts) selected."""))
+            return False
+        
+        # create a prefix relative from the *.ts down to the project path
+        langLevel = {}
+        for lang in langs:
+            level = lang.count(os.sep)
+            lst = langLevel.get(level, [])
+            lst.append(lang)
+            langLevel[level] = lst
+
+        for level, langs in langLevel.items():
+            prefix = '../' * level
+            sections = [
+                ("SOURCES",
+                 [prefix + src for src in sources])]
+            sections.append(
+                ("FORMS",
+                 [prefix + form for form in forms]))
+            sections.append(
+                ("TRANSLATIONS",
+                 [prefix + lang for lang in langs]))
+            
+            directory, name = os.path.split(pfile)
+            outFile = os.path.join(directory, os.path.dirname(langs[0]), name)
+            outDir = os.path.dirname(outFile)
+            if not os.path.exists(outDir):
+                os.makedirs(outDir)
+            try:
+                with open(outFile, "w", encoding="utf-8") as pf:
+                    for key, fileList in sections:
+                        if len(fileList) > 0:
+                            pf.write('{0} = '.format(key))
+                            pf.write(' \\\n\t'.join(
+                                [f.replace(os.sep, '/') for f in fileList]))
+                            pf.write('\n\n')
+                
+                self.__tmpProjects.append(outFile)
+            except OSError:
+                EricMessageBox.critical(
+                    self,
+                    self.tr("Write temporary project file"),
+                    self.tr(
+                        "<p>The temporary project file <b>{0}</b> could not"
+                        " be written.</p>").format(outFile))
+        
+        if len(self.__tmpProjects) == 0:
+            return False
+            
+        return True
+    
+    def __readStdoutLupdate(self, proc):
+        """
+        Private slot to handle the readyReadStandardOutput signal of the
+        pylupdate process.
+        
+        @param proc reference to the process
+        @type QProcess
+        """
+        self.__readStdout(proc, '{0}: '.format(self.pylupdate))
+        
+    def __readStdoutLrelease(self, proc):
+        """
+        Private slot to handle the readyReadStandardOutput signal of the
+        lrelease process.
+        
+        @param proc reference to the process
+        @type QProcess
+        """
+        self.__readStdout(proc, 'lrelease: ')
+        
+    def __readStdout(self, proc, ps):
+        """
+        Private method to read from a process' stdout channel.
+        
+        @param proc process to read from (QProcess)
+        @param ps prompt string (string)
+        """
+        ioEncoding = Preferences.getSystem("IOEncoding")
+        
+        proc.setReadChannel(QProcess.ProcessChannel.StandardOutput)
+        while proc and proc.canReadLine():
+            s = ps
+            output = str(proc.readLine(), ioEncoding, 'replace')
+            s += output
+            self.appendStdout.emit(s)
+        
+    def __readStderrLupdate(self, proc):
+        """
+        Private slot to handle the readyReadStandardError signal of the
+        pylupdate5 / pylupdate6 / pyside2-lupdate / pyside6-lupdate process.
+        
+        @param proc reference to the process
+        @type QProcess
+        """
+        self.__readStderr(proc, '{0}: '.format(self.pylupdate))
+        
+    def __readStderrLrelease(self, proc):
+        """
+        Private slot to handle the readyReadStandardError signal of the
+        lrelease process.
+        
+        @param proc reference to the process
+        @type QProcess
+        """
+        self.__readStderr(proc, 'lrelease: ')
+        
+    def __readStderr(self, proc, ps):
+        """
+        Private method to read from a process' stderr channel.
+        
+        @param proc process to read from (QProcess)
+        @param ps propmt string (string)
+        """
+        ioEncoding = Preferences.getSystem("IOEncoding")
+        
+        proc.setReadChannel(QProcess.ProcessChannel.StandardError)
+        while proc and proc.canReadLine():
+            s = ps
+            error = str(proc.readLine(), ioEncoding, 'replace')
+            s += error
+            self.appendStderr.emit(s)
+    
+    ###########################################################################
+    ##  Methods for the generation commands
+    ###########################################################################
+    
+    def __extractMessages(self):
+        """
+        Private slot to extract the messages to form a messages template file.
+        """
+        if self.hooks["extractMessages"] is not None:
+            self.hooks["extractMessages"]()
+        
+    def __generateTSFileDone(self, proc, exitCode, exitStatus):
+        """
+        Private slot to handle the finished signal of the pylupdate process.
+        
+        @param proc reference to the process
+        @type QProcess
+        @param exitCode exit code of the process
+        @type int
+        @param exitStatus exit status of the process
+        @type QProcess.ExitStatus
+        """
+        ui = ericApp().getObject("UserInterface")
+        if exitStatus == QProcess.ExitStatus.NormalExit and exitCode == 0:
+            ui.showNotification(
+                UI.PixmapCache.getPixmap("linguist48"),
+                self.tr("Translation file generation"),
+                self.tr(
+                    "The generation of the translation files (*.ts)"
+                    " was successful."))
+        else:
+            if exitStatus == QProcess.ExitStatus.CrashExit:
+                info = self.tr(" The process has crashed.")
+            else:
+                info = ""
+            ui.showNotification(
+                UI.PixmapCache.getPixmap("linguist48"),
+                self.tr("Translation file generation"),
+                self.tr(
+                    "The generation of the translation files (*.ts) has"
+                    " failed.{0}").format(info),
+                kind=NotificationTypes.CRITICAL,
+                timeout=0)
+        
+        for index in range(len(self.__pylupdateProcesses)):
+            if proc == self.__pylupdateProcesses[index][0]:
+                tmpProjectFile = self.__pylupdateProcesses[index][1]
+                if tmpProjectFile:
+                    with contextlib.suppress(OSError):
+                        self.__tmpProjects.remove(tmpProjectFile)
+                        os.remove(tmpProjectFile)
+                del self.__pylupdateProcesses[index]
+                break
+        
+        if not self.__pylupdateProcesses:
+            # all done
+            self.pylupdateProcRunning = False
+            
+            QGuiApplication.restoreOverrideCursor()
+            QGuiApplication.processEvents(
+                QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
+        
+    def __generateTSFile(self, noobsolete=False, generateAll=True):
+        """
+        Private method used to run pylupdate5 / pylupdate6 / pyside2-lupdate /
+        pyside6-lupdate to generate the .ts files.
+        
+        @param noobsolete flag indicating whether obsolete entries should be
+            kept (boolean)
+        @param generateAll flag indicating whether all translations should be
+            generated (boolean)
+        """
+        langs = [] if generateAll else self.getSelectedItems()
+        
+        # Hook support
+        if generateAll:
+            if noobsolete:
+                if self.hooks["generateAll"] is not None:
+                    self.hooks["generateAll"](
+                        self.project.pdata["TRANSLATIONS"])
+                    return
+            else:
+                if self.hooks["generateAllWithObsolete"] is not None:
+                    self.hooks["generateAllWithObsolete"](
+                        self.project.pdata["TRANSLATIONS"])
+                    return
+        else:
+            if noobsolete:
+                if self.hooks["generateSelected"] is not None:
+                    li = [self.project.getRelativePath(lang.fileName())
+                          for lang in langs]
+                    self.hooks["generateSelected"](li)
+                    return
+            else:
+                if self.hooks["generateSelectedWithObsolete"] is not None:
+                    li = [self.project.getRelativePath(lang.fileName())
+                          for lang in langs]
+                    self.hooks["generateSelectedWithObsolete"](li)
+                    return
+        
+        # generate a minimal temporary project file suitable for pylupdate
+        self.__tmpProjects = []
+        if self.project.getProjectLanguage() in [
+            "Python", "Python3"
+        ]:
+            if self.project.getProjectType() not in [
+                "PyQt6", "PyQt6C", "E7Plugin"
+            ]:
+                ok = self.__writeTempProjectFile(langs, [".py"])
+                if not ok:
+                    return
+        else:
+            return
+        
+        if self.project.getProjectType() in ["PyQt5", "PyQt5C"]:
+            self.pylupdate = Utilities.generatePyQtToolPath('pylupdate5')
+        elif self.project.getProjectType() in ["PyQt6", "PyQt6C", "E7Plugin"]:
+            self.pylupdate = Utilities.generatePyQtToolPath('pylupdate6')
+        elif self.project.getProjectType() in ["PySide2", "PySide2C"]:
+            self.pylupdate = Utilities.generatePySideToolPath(
+                'pyside2-lupdate', variant=2)
+        elif self.project.getProjectType() in ["PySide6", "PySide6C"]:
+            self.pylupdate = Utilities.generatePySideToolPath(
+                'pyside6-lupdate', variant=6)
+        else:
+            return
+
+        self.__pylupdateProcesses = []
+        if self.project.getProjectType() in ["PyQt6", "PyQt6C", "E7Plugin"]:
+            if langs:
+                langs = [self.project.getRelativePath(lang.fileName())
+                         for lang in langs if lang.fileName().endswith('.ts')]
+            else:
+                try:
+                    pattern = self.project.pdata["TRANSLATIONPATTERN"].replace(
+                        "%language%", "*")
+                    langs = [
+                        lang for lang in self.project.pdata["TRANSLATIONS"]
+                        if fnmatch.fnmatch(lang, pattern)
+                    ]
+                except IndexError:
+                    langs = []
+            if not langs:
+                EricMessageBox.warning(
+                    self,
+                    self.tr("Translation file generation"),
+                    self.tr("""No translation files (*.ts) selected."""))
+                return
+            
+            excludePatterns = [
+                pat for pat in self.project.getIgnorePatterns()
+                if pat.endswith((".py", ".ui"))
+            ]
+            
+            QGuiApplication.setOverrideCursor(
+                QCursor(Qt.CursorShape.WaitCursor))
+            QGuiApplication.processEvents(
+                QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
+            
+            for lang in langs:
+                proc = QProcess()
+                args = []
+                
+                for pattern in excludePatterns:
+                    args += ["--exclude", pattern]
+                
+                if noobsolete:
+                    args.append('--no-obsolete')
+                
+                args += ["--ts", lang]
+                args.append(".")
+                
+                proc.setWorkingDirectory(self.project.ppath)
+                proc.finished.connect(
+                    functools.partial(self.__generateTSFileDone, proc)
+                )
+                proc.readyReadStandardOutput.connect(
+                    functools.partial(self.__readStdoutLupdate, proc)
+                )
+                proc.readyReadStandardError.connect(
+                    functools.partial(self.__readStderrLupdate, proc)
+                )
+                
+                proc.start(self.pylupdate, args)
+                procStarted = proc.waitForStarted()
+                if procStarted:
+                    self.pylupdateProcRunning = True
+                    self.__pylupdateProcesses.append((proc, ""))
+                else:
+                    with EricOverridenCursor():
+                        EricMessageBox.critical(
+                            self,
+                            self.tr('Process Generation Error'),
+                            self.tr(
+                                'Could not start {0}.<br>'
+                                'Ensure that it is in the search path.'
+                            ).format(self.pylupdate))
+        else:
+            QGuiApplication.setOverrideCursor(
+                QCursor(Qt.CursorShape.WaitCursor))
+            QGuiApplication.processEvents(
+                QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
+            
+            for tempProjectFile in self.__tmpProjects[:]:
+                proc = QProcess()
+                args = []
+
+                if noobsolete:
+                    args.append('-noobsolete')
+                
+                args.append('-verbose')
+                path, filename = os.path.split(tempProjectFile)
+                args.append(filename)
+                proc.setWorkingDirectory(
+                    os.path.join(self.project.ppath, path))
+                proc.finished.connect(
+                    functools.partial(self.__generateTSFileDone, proc)
+                )
+                proc.readyReadStandardOutput.connect(
+                    functools.partial(self.__readStdoutLupdate, proc)
+                )
+                proc.readyReadStandardError.connect(
+                    functools.partial(self.__readStderrLupdate, proc)
+                )
+                
+                proc.start(self.pylupdate, args)
+                procStarted = proc.waitForStarted()
+                if procStarted:
+                    self.pylupdateProcRunning = True
+                    self.__pylupdateProcesses.append((proc, tempProjectFile))
+                else:
+                    with EricOverridenCursor():
+                        EricMessageBox.critical(
+                            self,
+                            self.tr('Process Generation Error'),
+                            self.tr(
+                                'Could not start {0}.<br>'
+                                'Ensure that it is in the search path.'
+                            ).format(self.pylupdate))
+                    # cleanup
+                    with contextlib.suppress(OSError):
+                        self.__tmpProjects.remove(tempProjectFile)
+                        os.remove(tempProjectFile)
+        
+        if not self.__pylupdateProcesses:
+            # no processes could be started, revert override cursor
+            QGuiApplication.restoreOverrideCursor()
+            QGuiApplication.processEvents(
+                QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
+        
+    def __generateAll(self):
+        """
+        Private method to generate all translation files (.ts) for Qt Linguist.
+        
+        All obsolete strings are removed from the .ts file.
+        """
+        self.__generateTSFile(noobsolete=True, generateAll=True)
+        
+    def __generateObsoleteAll(self):
+        """
+        Private method to generate all translation files (.ts) for Qt Linguist.
+        
+        Obsolete strings are kept.
+        """
+        self.__generateTSFile(noobsolete=False, generateAll=True)
+        
+    def __generateSelected(self):
+        """
+        Private method to generate selected translation files (.ts) for
+        Qt Linguist.
+        
+        All obsolete strings are removed from the .ts file.
+        """
+        self.__generateTSFile(noobsolete=True, generateAll=False)
+        
+    def __generateObsoleteSelected(self):
+        """
+        Private method to generate selected translation files (.ts) for
+        Qt Linguist.
+        
+        Obsolete strings are kept.
+        """
+        self.__generateTSFile(noobsolete=False, generateAll=False)
+    
+    ###########################################################################
+    ##  Methods for the release commands
+    ###########################################################################
+    
+    def __releaseTSFileDone(self, proc, exitCode, exitStatus):
+        """
+        Private slot to handle the finished signal of the lrelease process.
+        
+        @param proc reference to the process
+        @type QProcess
+        @param exitCode exit code of the process
+        @type int
+        @param exitStatus exit status of the process
+        @type QProcess.ExitStatus
+        """
+        ui = ericApp().getObject("UserInterface")
+        if exitStatus == QProcess.ExitStatus.NormalExit and exitCode == 0:
+            ui.showNotification(
+                UI.PixmapCache.getPixmap("linguist48"),
+                self.tr("Translation file release"),
+                self.tr("The release of the translation files (*.qm)"
+                        " was successful."))
+            if self.project.pdata["TRANSLATIONSBINPATH"]:
+                target = os.path.join(
+                    self.project.ppath,
+                    self.project.pdata["TRANSLATIONSBINPATH"])
+                for langFile in self.project.pdata["TRANSLATIONS"][:]:
+                    if langFile.endswith('.ts'):
+                        qmFile = os.path.join(self.project.ppath,
+                                              langFile.replace('.ts', '.qm'))
+                        if os.path.exists(qmFile):
+                            shutil.move(qmFile, target)
+        else:
+            ui.showNotification(
+                UI.PixmapCache.getPixmap("linguist48"),
+                self.tr("Translation file release"),
+                self.tr(
+                    "The release of the translation files (*.qm) has failed."),
+                kind=NotificationTypes.CRITICAL,
+                timeout=0)
+        
+        for index in range(len(self.__lreleaseProcesses)):
+            if proc == self.__lreleaseProcesses[index]:
+                del self.__lreleaseProcesses[index]
+                break
+        if not self.__lreleaseProcesses:
+            # all done
+            self.lreleaseProcRunning = False
+            self.project.checkLanguageFiles()
+        
+    def __releaseTSFile(self, generateAll=False):
+        """
+        Private method to run lrelease to release the translation files (.qm).
+        
+        @param generateAll flag indicating whether all translations should be
+            released (boolean)
+        """
+        langs = [] if generateAll else self.getSelectedItems()
+        
+        # Hooks support
+        if generateAll:
+            if self.hooks["releaseAll"] is not None:
+                self.hooks["releaseAll"](self.project.pdata["TRANSLATIONS"])
+                return
+        else:
+            if self.hooks["releaseSelected"] is not None:
+                li = [self.project.getRelativePath(lang.fileName())
+                      for lang in langs]
+                self.hooks["releaseSelected"](li)
+                return
+        
+        if self.project.getProjectType() in [
+            "PyQt5", "PyQt5C", "PyQt6", "PyQt6C", "E7Plugin",
+            "PySide2", "PySide2C", "PySide6", "PySide6C"
+        ]:
+            lrelease = os.path.join(
+                Utilities.getQtBinariesPath(),
+                Utilities.generateQtToolName("lrelease"))
+        else:
+            return
+        if Utilities.isWindowsPlatform():
+            lrelease += '.exe'
+        
+        if langs:
+            langs = [self.project.getRelativePath(lang.fileName())
+                     for lang in langs if lang.fileName().endswith('.ts')]
+        else:
+            try:
+                pattern = self.project.pdata["TRANSLATIONPATTERN"].replace(
+                    "%language%", "*")
+                langs = [lang for lang in self.project.pdata["TRANSLATIONS"]
+                         if fnmatch.fnmatch(lang, pattern)]
+            except IndexError:
+                langs = []
+        if not langs:
+            EricMessageBox.warning(
+                self,
+                self.tr("Write temporary project file"),
+                self.tr("""No translation files (*.ts) selected."""))
+            return
+        
+        self.__lreleaseProcesses = []
+        args = []
+        args.append('-verbose')
+        for langFile in langs:
+            path, filename = os.path.split(langFile)
+            args.append(filename)
+        
+        proc = QProcess()
+        proc.setWorkingDirectory(os.path.join(self.project.ppath, path))
+        proc.finished.connect(
+            functools.partial(self.__releaseTSFileDone, proc)
+        )
+        proc.readyReadStandardOutput.connect(
+            functools.partial(self.__readStdoutLrelease, proc)
+        )
+        proc.readyReadStandardError.connect(
+            functools.partial(self.__readStderrLrelease, proc)
+        )
+        
+        proc.start(lrelease, args)
+        procStarted = proc.waitForStarted()
+        if procStarted:
+            self.lreleaseProcRunning = True
+            self.__lreleaseProcesses.append(proc)
+        else:
+            EricMessageBox.critical(
+                self,
+                self.tr('Process Generation Error'),
+                self.tr(
+                    '<p>Could not start lrelease.<br>'
+                    'Ensure that it is available as <b>{0}</b>.</p>'
+                ).format(lrelease))
+        
+    def __releaseSelected(self):
+        """
+        Private method to release the translation files (.qm).
+        """
+        self.__releaseTSFile(generateAll=False)
+        
+    def __releaseAll(self):
+        """
+        Private method to release the translation files (.qm).
+        """
+        self.__releaseTSFile(generateAll=True)
+    
+    ###########################################################################
+    ## Support for hooks below
+    ###########################################################################
+    
+    def _initHookMethods(self):
+        """
+        Protected method to initialize the hooks dictionary.
+        
+        Supported hook methods are:
+        <ul>
+        <li>extractMessages: takes no parameters</li>
+        <li>generateAll: takes list of filenames as parameter</li>
+        <li>generateAllWithObsolete: takes list of filenames as parameter</li>
+        <li>generateSelected: takes list of filenames as parameter</li>
+        <li>generateSelectedWithObsolete: takes list of filenames as
+            parameter</li>
+        <li>releaseAll: takes list of filenames as parameter</li>
+        <li>releaseSelected: takes list of filenames as parameter</li>
+        <li>open: takes a filename as parameter</li>
+        </ul>
+        
+        <b>Note</b>: Filenames are relative to the project directory.
+        """
+        self.hooks = {
+            "extractMessages": None,
+            "generateAll": None,
+            "generateAllWithObsolete": None,
+            "generateSelected": None,
+            "generateSelectedWithObsolete": None,
+            "releaseAll": None,
+            "releaseSelected": None,
+            "open": None,
+        }

eric ide

mercurial