Browse Source

Added translation support to all untranslated qubesmanager files

references QubesOS/qubes-issues#5410
Marta Marczykowska-Górecka 4 years ago
parent
commit
46952ebad2

+ 5 - 4
qubesmanager/appmenu_select.py

@@ -20,13 +20,12 @@
 #
 
 import subprocess
-import PyQt5.QtWidgets  # pylint: disable=import-error
-
+from PyQt5 import QtWidgets, QtCore  # pylint: disable=import-error
 
 # TODO description in tooltip
 # TODO icon
 # pylint: disable=too-few-public-methods
-class AppListWidgetItem(PyQt5.QtWidgets.QListWidgetItem):
+class AppListWidgetItem(QtWidgets.QListWidgetItem):
     def __init__(self, name, ident, tooltip=None, parent=None):
         super(AppListWidgetItem, self).__init__(name, parent)
         if tooltip:
@@ -85,6 +84,8 @@ class AppmenuSelectManager:
             stdin=subprocess.PIPE)
         p.communicate('\n'.join(new_whitelisted).encode())
         if p.returncode != 0:
-            raise RuntimeError('qvm-appmenus --set-whitelist failed')
+            exception_text = QtCore.QCoreApplication.translate(
+                "exception", 'qvm-appmenus --set-whitelist failed')
+            raise RuntimeError(exception_text)
 
         return True

+ 7 - 6
qubesmanager/backup.py

@@ -324,9 +324,9 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard):
 
     def backup_finished(self):
         if self.thread.msg:
-            self.progress_status.setText(self.tr("Backup error."))
+            self.progress_status.setText(self.tr("Backup error"))
             QtWidgets.QMessageBox.warning(
-                self, self.tr("Backup error!"),
+                self, self.tr("Backup error"),
                 self.tr("ERROR: {}").format(
                     self.thread.msg))
             self.button(self.CancelButton).setEnabled(False)
@@ -369,7 +369,7 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard):
             self.thread.wait()
             QtWidgets.QMessageBox.warning(
                 self, self.tr("Backup aborted!"),
-                self.tr("ERROR: {}").format("Aborted!"))
+                self.tr("ERROR: Aborted"))
 
         self.cleanup_temporary_files()
         self.done(0)
@@ -391,9 +391,10 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard):
 
 
 def main():
-    utils.run_asynchronous("Qubes Backup VMs",
-                           "qubes-manager",
-                           BackupVMsWindow)
+    utils.run_asynchronous(
+        QtCore.QCoreApplication.translate("appname", "Qubes Backup VMs"),
+        "qubes-manager",
+        BackupVMsWindow)
 
 
 if __name__ == "__main__":

+ 4 - 3
qubesmanager/bootfromdevice.py

@@ -21,7 +21,7 @@ import functools
 import subprocess
 from . import utils
 from . import ui_bootfromdevice  # pylint: disable=no-name-in-module
-from PyQt5 import QtWidgets  # pylint: disable=import-error
+from PyQt5 import QtWidgets, QtCore  # pylint: disable=import-error
 from qubesadmin import tools
 from qubesadmin.tools import qvm_start
 
@@ -148,8 +148,9 @@ def main(args=None):
     args = parser.parse_args(args)
     vm = args.domains.pop()
 
-    utils.run_synchronous("Boot Qube From Device",
-                          functools.partial(VMBootFromDeviceWindow, vm))
+    utils.run_synchronous(
+        QtCore.QCoreApplication.translate("appname", "Boot Qube From Device"),
+        functools.partial(VMBootFromDeviceWindow, vm))
 
 
 if __name__ == "__main__":

+ 24 - 11
qubesmanager/clipboard.py

@@ -27,6 +27,7 @@ from math import log
 
 # pylint: disable=import-error
 from PyQt5.QtWidgets import QApplication, QMessageBox
+from PyQt5.QtCore import QCoreApplication
 
 APPVIEWER_LOCK = "/var/run/qubes/appviewer.lock"
 CLIPBOARD_CONTENTS = "/var/run/qubes/qubes-clipboard.bin"
@@ -43,14 +44,20 @@ def copy_text_to_qubes_clipboard(text):
     try:
         file = os.open(APPVIEWER_LOCK, os.O_RDWR | os.O_CREAT, 0o0666)
     except Exception:  # pylint: disable=broad-except
-        QMessageBox.warning(None, "Warning!",
-                            "Error while accessing Qubes clipboard!")
+        QMessageBox.warning(
+            None,
+            QCoreApplication.translate("Clipboard", "Warning!"),
+            QCoreApplication.translate(
+                "Clipboard", "Error while accessing Qubes clipboard!"))
     else:
         try:
             fcntl.flock(file, fcntl.LOCK_EX)
         except Exception:  # pylint: disable=broad-except
-            QMessageBox.warning(None, "Warning!",
-                                "Error while locking Qubes clipboard!")
+            QMessageBox.warning(
+                None,
+                QCoreApplication.translate("Clipboard", "Warning!"),
+                QCoreApplication.translate(
+                    "Clipboard", "Error while locking Qubes clipboard!"))
         else:
             try:
                 with open(CLIPBOARD_CONTENTS, "w") as contents:
@@ -58,8 +65,11 @@ def copy_text_to_qubes_clipboard(text):
                 with open(CLIPBOARD_SOURCE, "w") as source:
                     source.write("dom0")
             except Exception:  # pylint: disable=broad-except
-                QMessageBox.warning(None, "Warning!",
-                                    "Error while writing to Qubes clipboard!")
+                QMessageBox.warning(
+                    None,
+                    QCoreApplication.translate("Clipboard", "Warning!"),
+                    QCoreApplication.translate(
+                        "Clipboard", "Error while writing to Qubes clipboard!"))
             fcntl.flock(file, fcntl.LOCK_UN)
         os.close(file)
 
@@ -71,11 +81,14 @@ def get_qubes_clipboard_formatted_size():
     try:
         file_size = os.path.getsize(CLIPBOARD_CONTENTS)
     except Exception:  # pylint: disable=broad-except
-        QMessageBox.warning(None, "Warning!",
-                            "Error while accessing Qubes clipboard!")
+        QMessageBox.warning(
+            None,
+            QCoreApplication.translate("Clipboard", "Warning!"),
+            QCoreApplication.translate(
+                "Clipboard", "Error while accessing Qubes clipboard!"))
     else:
-        formatted_bytes = '1 byte' if file_size == 1 \
-            else str(file_size) + ' bytes'
+        formatted_bytes = QCoreApplication.translate(
+            "Clipboard", "%n byte(s)", "", file_size)
         if file_size > 0:
             magnitude = min(int(log(file_size)/log(2)*0.1), len(units)-1)
             if magnitude > 0:
@@ -84,4 +97,4 @@ def get_qubes_clipboard_formatted_size():
                                          units[magnitude])
         return '%s' % formatted_bytes
 
-    return '? bytes'
+    return QCoreApplication.translate("Clipboard", '? bytes')

+ 4 - 3
qubesmanager/common_threads.py

@@ -50,7 +50,7 @@ class RemoveVMThread(QubesThread):
         try:
             del self.vm.app.domains[self.vm.name]
         except (exc.QubesException, KeyError) as ex:
-            self.msg = ("Error removing qube!", str(ex))
+            self.msg = (self.tr("Error removing qube!"), str(ex))
 
 
 # pylint: disable=too-few-public-methods
@@ -62,7 +62,8 @@ class CloneVMThread(QubesThread):
     def run(self):
         try:
             self.vm.app.clone_vm(self.vm, self.dst_name)
-            self.msg = ("Sucess", "The qube was cloned sucessfully.")
+            self.msg = (self.tr("Sucess"),
+                        self.tr("The qube was cloned sucessfully."))
             self.msg_is_success = True
         except exc.QubesException as ex:
-            self.msg = ("Error while cloning qube!", str(ex))
+            self.msg = (self.tr("Error while cloning qube!"), str(ex))

+ 14 - 3
qubesmanager/create_new_vm.py

@@ -21,6 +21,7 @@
 #
 #
 
+import os
 import sys
 import subprocess
 
@@ -204,7 +205,7 @@ class NewVmDlg(QtWidgets.QDialog, Ui_NewVMDlg):
         self.thread.start()
 
         self.progress = QtWidgets.QProgressDialog(
-            self.tr("Creating new qube <b>{}</b>...").format(name), "", 0, 0)
+            self.tr("Creating new qube <b>{0}</b>...").format(name), "", 0, 0)
         self.progress.setCancelButton(None)
         self.progress.setModal(True)
         self.progress.show()
@@ -216,7 +217,7 @@ class NewVmDlg(QtWidgets.QDialog, Ui_NewVMDlg):
             QtWidgets.QMessageBox.warning(
                 self,
                 self.tr("Error creating the qube!"),
-                self.tr("ERROR: {}").format(self.thread.msg))
+                self.tr("ERROR: {0}").format(self.thread.msg))
 
         self.done(0)
 
@@ -266,9 +267,19 @@ def main(args=None):
     args = parser.parse_args(args)
 
     qtapp = QtWidgets.QApplication(sys.argv)
+
+    translator = QtCore.QTranslator(qtapp)
+    locale = QtCore.QLocale.system().name()
+    i18n_dir = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)),
+        'i18n')
+    translator.load("qubesmanager_{!s}.qm".format(locale), i18n_dir)
+    qtapp.installTranslator(translator)
+
     qtapp.setOrganizationName('Invisible Things Lab')
     qtapp.setOrganizationDomain('https://www.qubes-os.org/')
-    qtapp.setApplicationName('Create qube')
+    qtapp.setApplicationName(QtCore.QCoreApplication.translate(
+        "appname", 'Create qube'))
 
     dialog = NewVmDlg(qtapp, args.app)
     dialog.exec_()

+ 12 - 10
qubesmanager/firewall.py

@@ -280,8 +280,7 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
             return str(rule.proto)
         return "unknown"
 
-    @staticmethod
-    def get_firewall_conf(vm):
+    def get_firewall_conf(self, vm):
         conf = {
             'allow': None,
             'expire': 0,
@@ -296,14 +295,15 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
         last_rule = next(reversed_rules, None)
 
         if last_rule is None:
-            raise FirewallModifiedOutsideError('At least one rule must exist.')
+            raise FirewallModifiedOutsideError(
+                self.tr('At least one rule must exist.'))
 
         if last_rule == qubesadmin.firewall.Rule('action=accept') \
                 or last_rule == qubesadmin.firewall.Rule('action=drop'):
             common_action = last_rule.action
         else:
-            raise FirewallModifiedOutsideError('Last rule must be either '
-                                               'drop all or accept all.')
+            raise FirewallModifiedOutsideError(
+                self.tr('Last rule must be either drop all or accept all.'))
 
         dns_rule = qubesadmin.firewall.Rule(None,
                                         action='accept', specialtarget='dns')
@@ -319,29 +319,31 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
                 continue
 
             if rule.specialtarget is not None or rule.icmptype is not None:
-                raise FirewallModifiedOutsideError("Rule type unknown!")
+                raise FirewallModifiedOutsideError(
+                    self.tr("Rule type unknown!"))
 
             if (rule.dsthost is not None or rule.proto is not None) \
                     and rule.expire is None:
                 if rule.action == 'accept':
                     conf['rules'].insert(0, rule)
                     continue
-                raise FirewallModifiedOutsideError('No blacklist support.')
+                raise FirewallModifiedOutsideError(
+                    self.tr('No blacklist support.'))
 
             if rule.expire is not None and rule.dsthost is None \
                     and rule.proto is None:
                 conf['expire'] = int(str(rule.expire))
                 continue
 
-            raise FirewallModifiedOutsideError('it does not add up.')
+            raise FirewallModifiedOutsideError(self.tr('it does not add up.'))
 
         conf['allow'] = (common_action == 'accept')
 
         if not allow_icmp and not conf['allow']:
-            raise FirewallModifiedOutsideError('ICMP must be allowed.')
+            raise FirewallModifiedOutsideError(self.tr('ICMP must be allowed.'))
 
         if not allow_dns and not conf['allow']:
-            raise FirewallModifiedOutsideError('DNS must be allowed')
+            raise FirewallModifiedOutsideError(self.tr('DNS must be allowed'))
 
         return conf
 

+ 20 - 10
qubesmanager/global_settings.py

@@ -21,7 +21,7 @@
 #
 
 import subprocess
-from PyQt5 import QtWidgets  # pylint: disable=import-error
+from PyQt5 import QtWidgets, QtCore  # pylint: disable=import-error
 
 from qubesadmin.utils import parse_size
 
@@ -42,13 +42,19 @@ def _run_qrexec_repo(service, arg=''):
         check=False
     )
     if p.stderr:
-        raise RuntimeError('qrexec call stderr was not empty',
-                           {'stderr': p.stderr.decode('utf-8')})
+        raise RuntimeError(
+            QtCore.QCoreApplication.translate(
+                "GlobalSettings", 'qrexec call stderr was not empty'),
+            {'stderr': p.stderr.decode('utf-8')})
     if p.returncode != 0:
-        raise RuntimeError('qrexec call exited with non-zero return code',
-                           {'returncode': p.returncode})
+        raise RuntimeError(
+            QtCore.QCoreApplication.translate(
+                "GlobalSettings",
+                'qrexec call exited with non-zero return code'),
+            {'returncode': p.returncode})
     return p.stdout.decode('utf-8')
 
+
 # pylint: disable=too-many-instance-attributes
 class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
                            QtWidgets.QDialog):
@@ -272,15 +278,16 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
         elif repos['qubes-dom0-current']['enabled']:
             self.dom0_updates_repo.setCurrentIndex(0)
         else:
-            raise Exception('Cannot detect enabled dom0 update repositories')
+            raise Exception(
+                self.tr('Cannot detect enabled dom0 update repositories'))
 
         if repos['qubes-templates-itl-testing']['enabled']:
             self.itl_tmpl_updates_repo.setCurrentIndex(1)
         elif repos['qubes-templates-itl']['enabled']:
             self.itl_tmpl_updates_repo.setCurrentIndex(0)
         else:
-            raise Exception('Cannot detect enabled ITL template update '
-                            'repositories')
+            raise Exception(self.tr('Cannot detect enabled ITL template update '
+                            'repositories'))
 
         if repos['qubes-templates-community-testing']['enabled']:
             self.comm_tmpl_updates_repo.setCurrentIndex(2)
@@ -335,7 +342,8 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
                 result = _run_qrexec_repo('qubes.repos.' + action, name)
                 if result != 'ok\n':
                     raise RuntimeError(
-                        'qrexec call stdout did not contain "ok" as expected',
+                        self.tr('qrexec call stdout did not contain "ok"'
+                                ' as expected'),
                         {'stdout': result})
             except RuntimeError as ex:
                 msg = '{desc}; {args}'.format(desc=ex.args[0], args=', '.join(
@@ -400,7 +408,9 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
 
 
 def main():
-    utils.run_synchronous("Qubes Global Settings", GlobalSettingsWindow)
+    utils.run_synchronous(
+        QtCore.QCoreApplication.translate("appname", "Qubes Global Settings"),
+        GlobalSettingsWindow)
 
 
 if __name__ == "__main__":

+ 448 - 84
qubesmanager/i18n/qubesmanager_en.ts

@@ -206,25 +206,78 @@ p, li { white-space: pre-wrap; }
         <translation type="unfinished"></translation>
     </message>
 </context>
+<context>
+    <name>Clipboard</name>
+    <message>
+        <location filename="../clipboard.py" line="84"/>
+        <source>Warning!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../clipboard.py" line="84"/>
+        <source>Error while accessing Qubes clipboard!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../clipboard.py" line="56"/>
+        <source>Error while locking Qubes clipboard!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../clipboard.py" line="68"/>
+        <source>Error while writing to Qubes clipboard!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location filename="../clipboard.py" line="90"/>
+        <source>%n byte(s)</source>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location filename="../clipboard.py" line="100"/>
+        <source>? bytes</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>CloneVMThread</name>
+    <message>
+        <location filename="../common_threads.py" line="65"/>
+        <source>Sucess</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../common_threads.py" line="65"/>
+        <source>The qube was cloned sucessfully.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../common_threads.py" line="69"/>
+        <source>Error while cloning qube!</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 <context>
     <name>DevListWidgetItem</name>
     <message>
-        <location filename="../settings.py" line="1111"/>
+        <location filename="../settings.py" line="1109"/>
         <source>Service already on the list!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="1005"/>
+        <location filename="../settings.py" line="1003"/>
         <source>Error re-assigning device </source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="1063"/>
+        <location filename="../settings.py" line="1061"/>
         <source>Refresh in progress...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="1074"/>
+        <location filename="../settings.py" line="1072"/>
         <source>Refresh Applications</source>
         <translation type="unfinished"></translation>
     </message>
@@ -394,34 +447,59 @@ p, li { white-space: pre-wrap; }
         <source>Community template updates (testing)</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../global_settings.py" line="47"/>
+        <source>qrexec call stderr was not empty</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../global_settings.py" line="52"/>
+        <source>qrexec call exited with non-zero return code</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>GlobalSettingsWindow</name>
     <message>
-        <location filename="../global_settings.py" line="310"/>
+        <location filename="../global_settings.py" line="317"/>
         <source>Change state of all qubes</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../global_settings.py" line="299"/>
+        <location filename="../global_settings.py" line="306"/>
         <source>Are you sure you want to set all qubes to check for updates?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../global_settings.py" line="310"/>
+        <location filename="../global_settings.py" line="317"/>
         <source>Are you sure you want to set all qubes to not check for updates?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../global_settings.py" line="353"/>
+        <location filename="../global_settings.py" line="361"/>
         <source>ERROR!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../global_settings.py" line="353"/>
+        <location filename="../global_settings.py" line="361"/>
         <source>Error managing {repo} repository settings: {msg}</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../global_settings.py" line="287"/>
+        <source>Cannot detect enabled dom0 update repositories</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../global_settings.py" line="295"/>
+        <source>Cannot detect enabled ITL template update repositories</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../global_settings.py" line="350"/>
+        <source>qrexec call stdout did not contain &quot;ok&quot; as expected</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>InformationNotesDialog</name>
@@ -478,6 +556,49 @@ p, li { white-space: pre-wrap; }
         <translation type="unfinished"></translation>
     </message>
 </context>
+<context>
+    <name>ManagerUtils</name>
+    <message>
+        <location filename="../utils.py" line="99"/>
+        <source>default ({})</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="103"/>
+        <source>(none)</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="111"/>
+        <source> (current)</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="211"/>
+        <source>Unexpected characters in path.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="222"/>
+        <source>- Global property &lt;b&gt;{}&lt;/b&gt; &lt;br&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="225"/>
+        <source>- &lt;b&gt;{0}&lt;/b&gt; for qube &lt;b&gt;{1}&lt;/b&gt; &lt;br&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="260"/>
+        <source>Houston, we have a problem...</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="262"/>
+        <source>Whoops. A critical error has occured. This is most likely a bug in Qubes Manager.&lt;br&gt;&lt;br&gt;&lt;b&gt;&lt;i&gt;{0}&lt;/i&gt;&lt;/b&gt;&lt;br/&gt;at line &lt;b&gt;{1}&lt;/b&gt;&lt;br/&gt;of file {2}.&lt;br/&gt;&lt;br/&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 <context>
     <name>MultiSelectWidget</name>
     <message>
@@ -675,53 +796,139 @@ p, li { white-space: pre-wrap; }
 <context>
     <name>NewVmDlg</name>
     <message>
-        <location filename="../create_new_vm.py" line="142"/>
+        <location filename="../create_new_vm.py" line="143"/>
         <source>No template available!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="142"/>
+        <location filename="../create_new_vm.py" line="143"/>
         <source>Cannot create a qube when no template exists.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="149"/>
+        <location filename="../create_new_vm.py" line="150"/>
         <source>Qube based on a template (AppVM)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="150"/>
+        <location filename="../create_new_vm.py" line="151"/>
         <source>Standalone qube copied from a template</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="151"/>
+        <location filename="../create_new_vm.py" line="152"/>
         <source>Empty standalone qube (install your own OS)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="169"/>
+        <location filename="../create_new_vm.py" line="170"/>
         <source>Incorrect qube name!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="169"/>
+        <location filename="../create_new_vm.py" line="170"/>
         <source>A qube with the name &lt;b&gt;{}&lt;/b&gt; already exists in the system!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="206"/>
-        <source>Creating new qube &lt;b&gt;{}&lt;/b&gt;...</source>
+        <location filename="../create_new_vm.py" line="217"/>
+        <source>Error creating the qube!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="216"/>
-        <source>Error creating the qube!</source>
+        <location filename="../create_new_vm.py" line="207"/>
+        <source>Creating new qube &lt;b&gt;{0}&lt;/b&gt;...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="216"/>
-        <source>ERROR: {}</source>
+        <location filename="../create_new_vm.py" line="217"/>
+        <source>ERROR: {0}</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>QubeManager</name>
+    <message>
+        <location filename="../table_widgets.py" line="480"/>
+        <source>n/a</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="342"/>
+        <source>Yes</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="306"/>
+        <source>default ({})</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="421"/>
+        <source>Check updates</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="425"/>
+        <source>Updates pending!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="428"/>
+        <source>Qube outdated</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="432"/>
+        <source>The qube must be restarted for its filesystem to reflect the template&apos;s recent committed changes.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="437"/>
+        <source>Template running</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="441"/>
+        <source>The Template must be stopped before changes from its current session can be picked up by this qube.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>QubesFirewallRulesModel</name>
+    <message>
+        <location filename="../firewall.py" line="298"/>
+        <source>At least one rule must exist.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="305"/>
+        <source>Last rule must be either drop all or accept all.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="322"/>
+        <source>Rule type unknown!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="330"/>
+        <source>No blacklist support.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="338"/>
+        <source>it does not add up.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="343"/>
+        <source>ICMP must be allowed.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="346"/>
+        <source>DNS must be allowed</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
@@ -732,6 +939,11 @@ p, li { white-space: pre-wrap; }
         <source>Refresh in progress (refreshing applications from {})</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../settings.py" line="115"/>
+        <source>Refresh failed!</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>ReleaseNotesDialog</name>
@@ -741,6 +953,14 @@ p, li { white-space: pre-wrap; }
         <translation type="unfinished"></translation>
     </message>
 </context>
+<context>
+    <name>RemoveVMThread</name>
+    <message>
+        <location filename="../common_threads.py" line="53"/>
+        <source>Error removing qube!</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 <context>
     <name>RenameVMThread</name>
     <message>
@@ -753,6 +973,11 @@ p, li { white-space: pre-wrap; }
         <source>Some properties could not be changed to the new name. The system has now both {} and {} qubes. To resolve this, please check and change the following properties and remove the qube {} manually.&lt;br&gt;</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../settings.py" line="84"/>
+        <source>Rename error!</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>Restore</name>
@@ -902,6 +1127,14 @@ p, li { white-space: pre-wrap; }
         <translation type="unfinished"></translation>
     </message>
 </context>
+<context>
+    <name>RunCommandThread</name>
+    <message>
+        <location filename="../qube_manager.py" line="326"/>
+        <source>Error while running command!</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 <context>
     <name>SettingsDialog</name>
     <message>
@@ -1307,18 +1540,72 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <translation type="unfinished"></translation>
     </message>
 </context>
+<context>
+    <name>StatusItem</name>
+    <message>
+        <location filename="../template_manager.py" line="262"/>
+        <source>Cannot change template on a running VM.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>TemplateManager</name>
+    <message>
+        <location filename="../template_manager.py" line="335"/>
+        <source>qube is running</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 <context>
     <name>TemplateManagerWindow</name>
     <message>
-        <location filename="../template_manager.py" line="230"/>
+        <location filename="../template_manager.py" line="231"/>
         <source>Errors encountered!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../template_manager.py" line="230"/>
+        <location filename="../template_manager.py" line="231"/>
         <source>Errors encountered on template change in the following qubes: &lt;br&gt; {}.</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../template_manager.py" line="69"/>
+        <source>(select template)</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../template_manager.py" line="87"/>
+        <source>Qube</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../template_manager.py" line="87"/>
+        <source>Current</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../template_manager.py" line="87"/>
+        <source>New</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>UpdateVMThread</name>
+    <message>
+        <location filename="../qube_manager.py" line="300"/>
+        <source>Debian DSA-4371 fix installed in {}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../qube_manager.py" line="307"/>
+        <source>Failed to apply DSA-4371 fix: {}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../qube_manager.py" line="313"/>
+        <source>Error on qube update!</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>VMBootFromDeviceWindow</name>
@@ -1456,50 +1743,81 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="621"/>
+        <location filename="../settings.py" line="620"/>
         <source>Qube cannot be removed!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="621"/>
+        <location filename="../settings.py" line="620"/>
         <source>This qube cannot be removed. It is used as: &lt;br&gt; {} &lt;small&gt;If you want to  remove this qube, you should remove or change settings of each qube or setting that uses it.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="631"/>
+        <location filename="../settings.py" line="630"/>
         <source>Delete qube</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="631"/>
+        <location filename="../settings.py" line="630"/>
         <source>Are you absolutely sure you want to delete this qube? &lt;br/&gt; All qube settings and data will be irrevocably deleted. &lt;br/&gt; If you are sure, please enter this qube&apos;s name below.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="645"/>
+        <location filename="../settings.py" line="644"/>
         <source>Removal cancelled</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="645"/>
+        <location filename="../settings.py" line="644"/>
         <source>The qube will not be removed.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="652"/>
+        <location filename="../settings.py" line="651"/>
         <source>Clone qube</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="652"/>
+        <location filename="../settings.py" line="651"/>
         <source>Name for the cloned qube:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="662"/>
+        <location filename="../settings.py" line="661"/>
         <source>Cloning Qube...</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../settings.py" line="246"/>
+        <source>No finished thread found</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="281"/>
+        <source>Basic tab:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="284"/>
+        <source>Advanced tab:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="287"/>
+        <source>Devices tab:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="290"/>
+        <source>Sevices tab:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="744"/>
+        <source>Cannot change this setting while this qube is used as a NetVM by the following qubes:
+</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>VmListItem</name>
@@ -1528,18 +1846,8 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <source>Enter the same passphrase in both fields.</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <location filename="../backup.py" line="327"/>
-        <source>Backup error.</source>
-        <translation type="unfinished"></translation>
-    </message>
     <message>
         <location filename="../backup.py" line="328"/>
-        <source>Backup error!</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="../backup.py" line="364"/>
         <source>ERROR: {}</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1558,22 +1866,32 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <source>Backup aborted!</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../backup.py" line="328"/>
+        <source>Backup error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../backup.py" line="364"/>
+        <source>ERROR: Aborted</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>VmManagerWindow</name>
     <message>
-        <location filename="../qube_manager.py" line="468"/>
+        <location filename="../qube_manager.py" line="469"/>
         <source>Manager settings unreadable</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="468"/>
+        <location filename="../qube_manager.py" line="469"/>
         <source>Qube Manager settings cannot be parsed. Previously saved display settings may not be restored correctly.
 Error: {}</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="677"/>
+        <location filename="../qube_manager.py" line="678"/>
         <source>Loading Qube Manager...</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1583,142 +1901,142 @@ Error: {}</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="821"/>
+        <location filename="../qube_manager.py" line="822"/>
         <source>Warning!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="822"/>
+        <location filename="../qube_manager.py" line="823"/>
         <source>This qube cannot be removed. It is used as: &lt;br&gt; {} &lt;small&gt;If you want to  remove this qube, you should remove or change settings of each qube or setting that uses it.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="832"/>
+        <location filename="../qube_manager.py" line="833"/>
         <source>Qube Removal Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="832"/>
+        <location filename="../qube_manager.py" line="833"/>
         <source>Are you sure you want to remove the Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt;?&lt;br&gt; All data on this Qube&apos;s private storage will be lost!&lt;br&gt;&lt;br&gt;Type the name of the Qube (&lt;b&gt;{1}&lt;/b&gt;) below to confirm:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="845"/>
+        <location filename="../qube_manager.py" line="846"/>
         <source>Qube removal confirmation failed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="845"/>
+        <location filename="../qube_manager.py" line="846"/>
         <source>Entered name did not match! Not removing {0}.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="870"/>
+        <location filename="../qube_manager.py" line="871"/>
         <source>Qubes clone Qube</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="870"/>
+        <location filename="../qube_manager.py" line="871"/>
         <source>Enter name for Qube &lt;b&gt;{}&lt;/b&gt; clone:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="880"/>
+        <location filename="../qube_manager.py" line="881"/>
         <source>Name already in use!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="880"/>
+        <location filename="../qube_manager.py" line="881"/>
         <source>There already exists a qube called &apos;{}&apos;. Cloning aborted.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="886"/>
+        <location filename="../qube_manager.py" line="887"/>
         <source>Cloning Qube...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="908"/>
+        <location filename="../qube_manager.py" line="909"/>
         <source>Error unpausing Qube!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="964"/>
+        <location filename="../qube_manager.py" line="965"/>
         <source>ERROR: {0}</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="937"/>
+        <location filename="../qube_manager.py" line="938"/>
         <source>Error pausing Qube!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="948"/>
+        <location filename="../qube_manager.py" line="949"/>
         <source>Qube Shutdown Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="948"/>
+        <location filename="../qube_manager.py" line="949"/>
         <source>Are you sure you want to power down the Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt;?&lt;br&gt;&lt;small&gt;This will shutdown all the running applications within this Qube.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="964"/>
+        <location filename="../qube_manager.py" line="965"/>
         <source>Error shutting down Qube!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="982"/>
+        <location filename="../qube_manager.py" line="983"/>
         <source>Qube Restart Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="982"/>
+        <location filename="../qube_manager.py" line="983"/>
         <source>Are you sure you want to restart the Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt;?&lt;br&gt;&lt;small&gt;This will shutdown all the running applications within this Qube.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1001"/>
+        <location filename="../qube_manager.py" line="1002"/>
         <source>Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt; is not running. Are you absolutely sure you want to try to kill it?&lt;br&gt;&lt;small&gt;This will end &lt;b&gt;(not shutdown!)&lt;/b&gt; all the running applications within this Qube.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1007"/>
+        <location filename="../qube_manager.py" line="1008"/>
         <source>Are you sure you want to kill the Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt;?&lt;br&gt;&lt;small&gt;This will end &lt;b&gt;(not shutdown!)&lt;/b&gt; all the running applications within this Qube.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1012"/>
+        <location filename="../qube_manager.py" line="1013"/>
         <source>Qube Kill Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1021"/>
+        <location filename="../qube_manager.py" line="1022"/>
         <source>Error while killing Qube!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1021"/>
+        <location filename="../qube_manager.py" line="1022"/>
         <source>&lt;b&gt;An exception ocurred while killing {0}.&lt;/b&gt;&lt;br&gt;ERROR: {1}</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1072"/>
+        <location filename="../qube_manager.py" line="1073"/>
         <source>Qube Update Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1072"/>
+        <location filename="../qube_manager.py" line="1073"/>
         <source>&lt;b&gt;{0}&lt;/b&gt;&lt;br&gt;The Qube has to be running to be updated.&lt;br&gt;Do you want to start it?&lt;br&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1092"/>
+        <location filename="../qube_manager.py" line="1093"/>
         <source>Qubes command entry</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1092"/>
+        <location filename="../qube_manager.py" line="1093"/>
         <source>Run command in &lt;b&gt;{}&lt;/b&gt;:</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2171,45 +2489,83 @@ Template</source>
         <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DisposableVM Template&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Allows using this qube as a template for DisposableVMs. The DisposableVMs will inherit the VM&apos;s state (configuration, installed programs etc.), but their state will not persist between restarts. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../qube_manager.py" line="551"/>
+        <source>No finished thread found</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../qube_manager.py" line="892"/>
+        <source>Cloning qube...</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>VmShutdownMonitor</name>
     <message>
-        <location filename="../qube_manager.py" line="232"/>
+        <location filename="../qube_manager.py" line="233"/>
         <source>Qube Shutdown</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="233"/>
+        <location filename="../qube_manager.py" line="234"/>
         <source>The Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt; hasn&apos;t shutdown within the last {1} seconds, do you want to kill it?&lt;br&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="237"/>
+        <location filename="../qube_manager.py" line="238"/>
         <source>Kill it!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="239"/>
+        <location filename="../qube_manager.py" line="240"/>
         <source>Wait another {0} seconds...</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>VmUpdateInfoItem</name>
+</context>
+<context>
+    <name>appname</name>
     <message>
-        <location filename="../table_widgets.py" line="421"/>
-        <source>Updates pending!</source>
+        <location filename="../backup.py" line="388"/>
+        <source>Qubes Backup VMs</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../table_widgets.py" line="425"/>
-        <source>The qube must be restarted for its filesystem to reflect the template&apos;s recent committed changes.</source>
+        <location filename="../bootfromdevice.py" line="151"/>
+        <source>Boot Qube From Device</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../table_widgets.py" line="431"/>
-        <source>The Template must be stopped before changes from its current session can be picked up by this qube.</source>
+        <location filename="../create_new_vm.py" line="281"/>
+        <source>Create qube</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../global_settings.py" line="417"/>
+        <source>Qubes Global Settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../qube_manager.py" line="1303"/>
+        <source>Qube Manager</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../restore.py" line="295"/>
+        <source>Qubes Restore VMs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="1235"/>
+        <source>Qube Settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../template_manager.py" line="384"/>
+        <source>Template Manager</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
@@ -2231,4 +2587,12 @@ Template</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
+<context>
+    <name>exception</name>
+    <message>
+        <location filename="../appmenu_select.py" line="87"/>
+        <source>qvm-appmenus --set-whitelist failed</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 </TS>

+ 458 - 78
qubesmanager/i18n/qubesmanager_es.ts

@@ -247,10 +247,64 @@ p, li { white-space: pre-wrap; }
         <translation type="unfinished">...</translation>
     </message>
 </context>
+<context>
+    <name>Clipboard</name>
+    <message>
+        <location filename="../clipboard.py" line="84"/>
+        <source>Warning!</source>
+        <translation type="unfinished">¡Atencion!</translation>
+    </message>
+    <message>
+        <location filename="../clipboard.py" line="84"/>
+        <source>Error while accessing Qubes clipboard!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../clipboard.py" line="56"/>
+        <source>Error while locking Qubes clipboard!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../clipboard.py" line="68"/>
+        <source>Error while writing to Qubes clipboard!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message numerus="yes">
+        <location filename="../clipboard.py" line="90"/>
+        <source>%n byte(s)</source>
+        <translation type="unfinished">
+            <numerusform></numerusform>
+            <numerusform></numerusform>
+        </translation>
+    </message>
+    <message>
+        <location filename="../clipboard.py" line="100"/>
+        <source>? bytes</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>CloneVMThread</name>
+    <message>
+        <location filename="../common_threads.py" line="65"/>
+        <source>Sucess</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../common_threads.py" line="65"/>
+        <source>The qube was cloned sucessfully.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../common_threads.py" line="69"/>
+        <source>Error while cloning qube!</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 <context>
     <name>DevListWidgetItem</name>
     <message>
-        <location filename="../settings.py" line="1111"/>
+        <location filename="../settings.py" line="1109"/>
         <source>Service already on the list!</source>
         <translation>¡El servicio ya está en la lista!</translation>
     </message>
@@ -285,17 +339,17 @@ p, li { white-space: pre-wrap; }
         <translation type="obsolete">El servicio &apos;{0}&apos; es desconocido.</translation>
     </message>
     <message>
-        <location filename="../settings.py" line="1005"/>
+        <location filename="../settings.py" line="1003"/>
         <source>Error re-assigning device </source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="1063"/>
+        <location filename="../settings.py" line="1061"/>
         <source>Refresh in progress...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="1074"/>
+        <location filename="../settings.py" line="1072"/>
         <source>Refresh Applications</source>
         <translation type="unfinished"></translation>
     </message>
@@ -490,6 +544,16 @@ p, li { white-space: pre-wrap; }
         <source>Community template updates (testing)</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../global_settings.py" line="47"/>
+        <source>qrexec call stderr was not empty</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../global_settings.py" line="52"/>
+        <source>qrexec call exited with non-zero return code</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>GlobalSettingsWindow</name>
@@ -499,30 +563,45 @@ p, li { white-space: pre-wrap; }
         <translation type="obsolete">(actual)</translation>
     </message>
     <message>
-        <location filename="../global_settings.py" line="310"/>
+        <location filename="../global_settings.py" line="317"/>
         <source>Change state of all qubes</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../global_settings.py" line="299"/>
+        <location filename="../global_settings.py" line="306"/>
         <source>Are you sure you want to set all qubes to check for updates?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../global_settings.py" line="310"/>
+        <location filename="../global_settings.py" line="317"/>
         <source>Are you sure you want to set all qubes to not check for updates?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../global_settings.py" line="353"/>
+        <location filename="../global_settings.py" line="361"/>
         <source>ERROR!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../global_settings.py" line="353"/>
+        <location filename="../global_settings.py" line="361"/>
         <source>Error managing {repo} repository settings: {msg}</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../global_settings.py" line="287"/>
+        <source>Cannot detect enabled dom0 update repositories</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../global_settings.py" line="295"/>
+        <source>Cannot detect enabled ITL template update repositories</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../global_settings.py" line="350"/>
+        <source>qrexec call stdout did not contain &quot;ok&quot; as expected</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>InformationNotesDialog</name>
@@ -580,6 +659,49 @@ p, li { white-space: pre-wrap; }
         <translation type="unfinished"></translation>
     </message>
 </context>
+<context>
+    <name>ManagerUtils</name>
+    <message>
+        <location filename="../utils.py" line="99"/>
+        <source>default ({})</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="103"/>
+        <source>(none)</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="111"/>
+        <source> (current)</source>
+        <translation type="unfinished">(actual)</translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="211"/>
+        <source>Unexpected characters in path.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="222"/>
+        <source>- Global property &lt;b&gt;{}&lt;/b&gt; &lt;br&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="225"/>
+        <source>- &lt;b&gt;{0}&lt;/b&gt; for qube &lt;b&gt;{1}&lt;/b&gt; &lt;br&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="260"/>
+        <source>Houston, we have a problem...</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utils.py" line="262"/>
+        <source>Whoops. A critical error has occured. This is most likely a bug in Qubes Manager.&lt;br&gt;&lt;br&gt;&lt;b&gt;&lt;i&gt;{0}&lt;/i&gt;&lt;/b&gt;&lt;br/&gt;at line &lt;b&gt;{1}&lt;/b&gt;&lt;br/&gt;of file {2}.&lt;br/&gt;&lt;br/&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 <context>
     <name>MultiSelectWidget</name>
     <message>
@@ -852,7 +974,7 @@ p, li { white-space: pre-wrap; }
         <translation type="obsolete">Ya existe en el sistema una VM con el nombre &lt;b&gt;{0}&lt;/b&gt;</translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="142"/>
+        <location filename="../create_new_vm.py" line="143"/>
         <source>No template available!</source>
         <translation>No hay una plantillas disponibles</translation>
     </message>
@@ -877,54 +999,140 @@ p, li { white-space: pre-wrap; }
         <translation type="obsolete">Creando nueva {0} &lt;b&gt;{1}&lt;/b&gt;...</translation>
     </message>
     <message>
-        <location filename="../qubesmanager/create_new_vm.py" line="258"/>
+        <location filename="../create_new_vm.py" line="217"/>
         <source>ERROR: {0}</source>
-        <translation type="obsolete">ERROR: {0}</translation>
+        <translation type="unfinished">ERROR: {0}</translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="142"/>
+        <location filename="../create_new_vm.py" line="143"/>
         <source>Cannot create a qube when no template exists.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="149"/>
+        <location filename="../create_new_vm.py" line="150"/>
         <source>Qube based on a template (AppVM)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="150"/>
+        <location filename="../create_new_vm.py" line="151"/>
         <source>Standalone qube copied from a template</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="151"/>
+        <location filename="../create_new_vm.py" line="152"/>
         <source>Empty standalone qube (install your own OS)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="169"/>
+        <location filename="../create_new_vm.py" line="170"/>
         <source>Incorrect qube name!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="169"/>
+        <location filename="../create_new_vm.py" line="170"/>
         <source>A qube with the name &lt;b&gt;{}&lt;/b&gt; already exists in the system!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../create_new_vm.py" line="206"/>
-        <source>Creating new qube &lt;b&gt;{}&lt;/b&gt;...</source>
+        <location filename="../create_new_vm.py" line="217"/>
+        <source>Error creating the qube!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="../create_new_vm.py" line="216"/>
-        <source>Error creating the qube!</source>
+        <source>ERROR: {}</source>
+        <translation type="obsolete">ERROR: {}</translation>
+    </message>
+    <message>
+        <location filename="../create_new_vm.py" line="207"/>
+        <source>Creating new qube &lt;b&gt;{0}&lt;/b&gt;...</source>
         <translation type="unfinished"></translation>
     </message>
+</context>
+<context>
+    <name>QubeManager</name>
     <message>
-        <location filename="../create_new_vm.py" line="216"/>
-        <source>ERROR: {}</source>
-        <translation type="unfinished">ERROR: {}</translation>
+        <location filename="../table_widgets.py" line="480"/>
+        <source>n/a</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="342"/>
+        <source>Yes</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="306"/>
+        <source>default ({})</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="421"/>
+        <source>Check updates</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="425"/>
+        <source>Updates pending!</source>
+        <translation type="unfinished">¡Actualizaciones pendientes!</translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="428"/>
+        <source>Qube outdated</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="432"/>
+        <source>The qube must be restarted for its filesystem to reflect the template&apos;s recent committed changes.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="437"/>
+        <source>Template running</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../table_widgets.py" line="441"/>
+        <source>The Template must be stopped before changes from its current session can be picked up by this qube.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>QubesFirewallRulesModel</name>
+    <message>
+        <location filename="../firewall.py" line="298"/>
+        <source>At least one rule must exist.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="305"/>
+        <source>Last rule must be either drop all or accept all.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="322"/>
+        <source>Rule type unknown!</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="330"/>
+        <source>No blacklist support.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="338"/>
+        <source>it does not add up.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="343"/>
+        <source>ICMP must be allowed.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../firewall.py" line="346"/>
+        <source>DNS must be allowed</source>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
@@ -1019,6 +1227,11 @@ p, li { white-space: pre-wrap; }
         <source>Refresh in progress (refreshing applications from {})</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../settings.py" line="115"/>
+        <source>Refresh failed!</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>ReleaseNotesDialog</name>
@@ -1028,6 +1241,14 @@ p, li { white-space: pre-wrap; }
         <translation>Notas sobre esta versión de Qubes</translation>
     </message>
 </context>
+<context>
+    <name>RemoveVMThread</name>
+    <message>
+        <location filename="../common_threads.py" line="53"/>
+        <source>Error removing qube!</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 <context>
     <name>RenameVMThread</name>
     <message>
@@ -1040,6 +1261,11 @@ p, li { white-space: pre-wrap; }
         <source>Some properties could not be changed to the new name. The system has now both {} and {} qubes. To resolve this, please check and change the following properties and remove the qube {} manually.&lt;br&gt;</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../settings.py" line="84"/>
+        <source>Rename error!</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>Restore</name>
@@ -1254,6 +1480,14 @@ p, li { white-space: pre-wrap; }
         <translation type="unfinished"></translation>
     </message>
 </context>
+<context>
+    <name>RunCommandThread</name>
+    <message>
+        <location filename="../qube_manager.py" line="326"/>
+        <source>Error while running command!</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 <context>
     <name>SettingsDialog</name>
     <message>
@@ -1769,18 +2003,72 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <translation type="unfinished"></translation>
     </message>
 </context>
+<context>
+    <name>StatusItem</name>
+    <message>
+        <location filename="../template_manager.py" line="262"/>
+        <source>Cannot change template on a running VM.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>TemplateManager</name>
+    <message>
+        <location filename="../template_manager.py" line="335"/>
+        <source>qube is running</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 <context>
     <name>TemplateManagerWindow</name>
     <message>
-        <location filename="../template_manager.py" line="230"/>
+        <location filename="../template_manager.py" line="231"/>
         <source>Errors encountered!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../template_manager.py" line="230"/>
+        <location filename="../template_manager.py" line="231"/>
         <source>Errors encountered on template change in the following qubes: &lt;br&gt; {}.</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../template_manager.py" line="69"/>
+        <source>(select template)</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../template_manager.py" line="87"/>
+        <source>Qube</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../template_manager.py" line="87"/>
+        <source>Current</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../template_manager.py" line="87"/>
+        <source>New</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>UpdateVMThread</name>
+    <message>
+        <location filename="../qube_manager.py" line="300"/>
+        <source>Debian DSA-4371 fix installed in {}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../qube_manager.py" line="307"/>
+        <source>Failed to apply DSA-4371 fix: {}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../qube_manager.py" line="313"/>
+        <source>Error on qube update!</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>VMBootFromDeviceWindow</name>
@@ -1953,50 +2241,81 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="621"/>
+        <location filename="../settings.py" line="620"/>
         <source>Qube cannot be removed!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="621"/>
+        <location filename="../settings.py" line="620"/>
         <source>This qube cannot be removed. It is used as: &lt;br&gt; {} &lt;small&gt;If you want to  remove this qube, you should remove or change settings of each qube or setting that uses it.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="631"/>
+        <location filename="../settings.py" line="630"/>
         <source>Delete qube</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="631"/>
+        <location filename="../settings.py" line="630"/>
         <source>Are you absolutely sure you want to delete this qube? &lt;br/&gt; All qube settings and data will be irrevocably deleted. &lt;br/&gt; If you are sure, please enter this qube&apos;s name below.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="645"/>
+        <location filename="../settings.py" line="644"/>
         <source>Removal cancelled</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="645"/>
+        <location filename="../settings.py" line="644"/>
         <source>The qube will not be removed.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="652"/>
+        <location filename="../settings.py" line="651"/>
         <source>Clone qube</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="652"/>
+        <location filename="../settings.py" line="651"/>
         <source>Name for the cloned qube:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../settings.py" line="662"/>
+        <location filename="../settings.py" line="661"/>
         <source>Cloning Qube...</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../settings.py" line="246"/>
+        <source>No finished thread found</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="281"/>
+        <source>Basic tab:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="284"/>
+        <source>Advanced tab:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="287"/>
+        <source>Devices tab:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="290"/>
+        <source>Sevices tab:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="744"/>
+        <source>Cannot change this setting while this qube is used as a NetVM by the following qubes:
+</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>VmListItem</name>
@@ -2068,15 +2387,15 @@ The qube must be running to disable seamless mode; this setting is not persisten
     <message>
         <location filename="../backup.py" line="327"/>
         <source>Backup error.</source>
-        <translation>Error en la copia de respaldo.</translation>
+        <translation type="obsolete">Error en la copia de respaldo.</translation>
     </message>
     <message>
         <location filename="../backup.py" line="328"/>
         <source>Backup error!</source>
-        <translation>Error en la copia de respaldo.</translation>
+        <translation type="obsolete">Error en la copia de respaldo.</translation>
     </message>
     <message>
-        <location filename="../backup.py" line="364"/>
+        <location filename="../backup.py" line="328"/>
         <source>ERROR: {}</source>
         <translation>ERROR: {}</translation>
     </message>
@@ -2095,11 +2414,21 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <source>Backup aborted!</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../backup.py" line="328"/>
+        <source>Backup error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../backup.py" line="364"/>
+        <source>ERROR: Aborted</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>VmManagerWindow</name>
     <message>
-        <location filename="../qube_manager.py" line="821"/>
+        <location filename="../qube_manager.py" line="822"/>
         <source>Warning!</source>
         <translation type="unfinished">¡Cuidado!</translation>
     </message>
@@ -2124,7 +2453,7 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <translation type="obsolete">La confirmación de borrado del VM falló.</translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="845"/>
+        <location filename="../qube_manager.py" line="846"/>
         <source>Entered name did not match! Not removing {0}.</source>
         <translation type="unfinished">¡El nombre ingresado no coincide! La VM {0} no será borrada.</translation>
     </message>
@@ -2144,7 +2473,7 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <translation type="obsolete">Ocurrió un error al borrar la VM.</translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="964"/>
+        <location filename="../qube_manager.py" line="965"/>
         <source>ERROR: {0}</source>
         <translation type="unfinished">ERROR: {0}</translation>
     </message>
@@ -2264,7 +2593,7 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <translation type="obsolete">Ocurrió un error al terminar la VM</translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1021"/>
+        <location filename="../qube_manager.py" line="1022"/>
         <source>&lt;b&gt;An exception ocurred while killing {0}.&lt;/b&gt;&lt;br&gt;ERROR: {1}</source>
         <translation type="unfinished">&lt;b&gt;Ocurrió una excepción al terminar {0}.&lt;/b&gt;&lt;br&gt;ERROR: {1}</translation>
     </message>
@@ -2289,12 +2618,12 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <translation type="obsolete">Ocurrió un error al actualizar la VM</translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1092"/>
+        <location filename="../qube_manager.py" line="1093"/>
         <source>Qubes command entry</source>
         <translation type="unfinished">Qubes: Ejecución de comandos</translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1092"/>
+        <location filename="../qube_manager.py" line="1093"/>
         <source>Run command in &lt;b&gt;{}&lt;/b&gt;:</source>
         <translation type="unfinished">Ejecutar el comando en &lt;b&gt;{}&lt;/b&gt;:</translation>
     </message>
@@ -2779,18 +3108,18 @@ The qube must be running to disable seamless mode; this setting is not persisten
         <translation type="unfinished">Ctrl+F</translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="468"/>
+        <location filename="../qube_manager.py" line="469"/>
         <source>Manager settings unreadable</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="468"/>
+        <location filename="../qube_manager.py" line="469"/>
         <source>Qube Manager settings cannot be parsed. Previously saved display settings may not be restored correctly.
 Error: {}</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="677"/>
+        <location filename="../qube_manager.py" line="678"/>
         <source>Loading Qube Manager...</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2800,112 +3129,112 @@ Error: {}</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="822"/>
+        <location filename="../qube_manager.py" line="823"/>
         <source>This qube cannot be removed. It is used as: &lt;br&gt; {} &lt;small&gt;If you want to  remove this qube, you should remove or change settings of each qube or setting that uses it.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="832"/>
+        <location filename="../qube_manager.py" line="833"/>
         <source>Qube Removal Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="832"/>
+        <location filename="../qube_manager.py" line="833"/>
         <source>Are you sure you want to remove the Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt;?&lt;br&gt; All data on this Qube&apos;s private storage will be lost!&lt;br&gt;&lt;br&gt;Type the name of the Qube (&lt;b&gt;{1}&lt;/b&gt;) below to confirm:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="845"/>
+        <location filename="../qube_manager.py" line="846"/>
         <source>Qube removal confirmation failed</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="870"/>
+        <location filename="../qube_manager.py" line="871"/>
         <source>Qubes clone Qube</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="870"/>
+        <location filename="../qube_manager.py" line="871"/>
         <source>Enter name for Qube &lt;b&gt;{}&lt;/b&gt; clone:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="880"/>
+        <location filename="../qube_manager.py" line="881"/>
         <source>Name already in use!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="880"/>
+        <location filename="../qube_manager.py" line="881"/>
         <source>There already exists a qube called &apos;{}&apos;. Cloning aborted.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="886"/>
+        <location filename="../qube_manager.py" line="887"/>
         <source>Cloning Qube...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="908"/>
+        <location filename="../qube_manager.py" line="909"/>
         <source>Error unpausing Qube!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="937"/>
+        <location filename="../qube_manager.py" line="938"/>
         <source>Error pausing Qube!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="948"/>
+        <location filename="../qube_manager.py" line="949"/>
         <source>Qube Shutdown Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="948"/>
+        <location filename="../qube_manager.py" line="949"/>
         <source>Are you sure you want to power down the Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt;?&lt;br&gt;&lt;small&gt;This will shutdown all the running applications within this Qube.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="964"/>
+        <location filename="../qube_manager.py" line="965"/>
         <source>Error shutting down Qube!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="982"/>
+        <location filename="../qube_manager.py" line="983"/>
         <source>Qube Restart Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="982"/>
+        <location filename="../qube_manager.py" line="983"/>
         <source>Are you sure you want to restart the Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt;?&lt;br&gt;&lt;small&gt;This will shutdown all the running applications within this Qube.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1001"/>
+        <location filename="../qube_manager.py" line="1002"/>
         <source>Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt; is not running. Are you absolutely sure you want to try to kill it?&lt;br&gt;&lt;small&gt;This will end &lt;b&gt;(not shutdown!)&lt;/b&gt; all the running applications within this Qube.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1007"/>
+        <location filename="../qube_manager.py" line="1008"/>
         <source>Are you sure you want to kill the Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt;?&lt;br&gt;&lt;small&gt;This will end &lt;b&gt;(not shutdown!)&lt;/b&gt; all the running applications within this Qube.&lt;/small&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1012"/>
+        <location filename="../qube_manager.py" line="1013"/>
         <source>Qube Kill Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1021"/>
+        <location filename="../qube_manager.py" line="1022"/>
         <source>Error while killing Qube!</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1072"/>
+        <location filename="../qube_manager.py" line="1073"/>
         <source>Qube Update Confirmation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="1072"/>
+        <location filename="../qube_manager.py" line="1073"/>
         <source>&lt;b&gt;{0}&lt;/b&gt;&lt;br&gt;The Qube has to be running to be updated.&lt;br&gt;Do you want to start it?&lt;br&gt;</source>
         <translation type="unfinished"></translation>
     </message>
@@ -3168,6 +3497,16 @@ Template</source>
         <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DisposableVM Template&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Allows using this qube as a template for DisposableVMs. The DisposableVMs will inherit the VM&apos;s state (configuration, installed programs etc.), but their state will not persist between restarts. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="../qube_manager.py" line="551"/>
+        <source>No finished thread found</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../qube_manager.py" line="892"/>
+        <source>Cloning qube...</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>VmShutdownMonitor</name>
@@ -3182,22 +3521,22 @@ Template</source>
         <translation type="obsolete">La VM &lt;b&gt;&apos;{0}&apos;&lt;/b&gt; no fue apagada dentro de los últimos {1} segundos, ¿quieres terminarla?&lt;br&gt;</translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="237"/>
+        <location filename="../qube_manager.py" line="238"/>
         <source>Kill it!</source>
         <translation type="unfinished">Terminar el VM</translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="239"/>
+        <location filename="../qube_manager.py" line="240"/>
         <source>Wait another {0} seconds...</source>
         <translation type="unfinished">Esperar {0} segundos más...</translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="232"/>
+        <location filename="../qube_manager.py" line="233"/>
         <source>Qube Shutdown</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../qube_manager.py" line="233"/>
+        <location filename="../qube_manager.py" line="234"/>
         <source>The Qube &lt;b&gt;&apos;{0}&apos;&lt;/b&gt; hasn&apos;t shutdown within the last {1} seconds, do you want to kill it?&lt;br&gt;</source>
         <translation type="unfinished"></translation>
     </message>
@@ -3207,7 +3546,7 @@ Template</source>
     <message>
         <location filename="../table_widgets.py" line="421"/>
         <source>Updates pending!</source>
-        <translation>¡Actualizaciones pendientes!</translation>
+        <translation type="obsolete">¡Actualizaciones pendientes!</translation>
     </message>
     <message>
         <location filename="../qubesmanager/table_widgets.py" line="612"/>
@@ -3219,14 +3558,47 @@ Template</source>
         <source>The TemplateVM must be stopped before changes from its current session can be picked up by this VM.</source>
         <translation type="obsolete">El TemplateVM debe detenerse antes de que los cambios de su sesión actual puedan ser recogidos por esta VM.</translation>
     </message>
+</context>
+<context>
+    <name>appname</name>
     <message>
-        <location filename="../table_widgets.py" line="425"/>
-        <source>The qube must be restarted for its filesystem to reflect the template&apos;s recent committed changes.</source>
+        <location filename="../backup.py" line="388"/>
+        <source>Qubes Backup VMs</source>
+        <translation type="unfinished">Qubes: Respaldo de VMs</translation>
+    </message>
+    <message>
+        <location filename="../bootfromdevice.py" line="151"/>
+        <source>Boot Qube From Device</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="../table_widgets.py" line="431"/>
-        <source>The Template must be stopped before changes from its current session can be picked up by this qube.</source>
+        <location filename="../create_new_vm.py" line="281"/>
+        <source>Create qube</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../global_settings.py" line="417"/>
+        <source>Qubes Global Settings</source>
+        <translation type="unfinished">Opciones de configuración global de Qubes OS</translation>
+    </message>
+    <message>
+        <location filename="../qube_manager.py" line="1303"/>
+        <source>Qube Manager</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../restore.py" line="295"/>
+        <source>Qubes Restore VMs</source>
+        <translation type="unfinished">Qubes: Recuperar VM desde copia de respaldo</translation>
+    </message>
+    <message>
+        <location filename="../settings.py" line="1235"/>
+        <source>Qube Settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../template_manager.py" line="384"/>
+        <source>Template Manager</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
@@ -3248,4 +3620,12 @@ Template</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
+<context>
+    <name>exception</name>
+    <message>
+        <location filename="../appmenu_select.py" line="87"/>
+        <source>qvm-appmenus --set-whitelist failed</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
 </TS>

+ 12 - 11
qubesmanager/qube_manager.py

@@ -159,6 +159,7 @@ class VmRowInTable:
             if not event or event.endswith(':netvm'):
                 self.netvm_widget.update()
             if not event or event.endswith(':internal'):
+                # this is a feature, not a property; TODO: fix event handling
                 self.internal_widget.update()
             if not event or event.endswith(':ip'):
                 self.ip_widget.update()
@@ -298,18 +299,18 @@ class UpdateVMThread(common_threads.QubesThread):
                 if stdout == b'changed=yes\n':
                     subprocess.call(
                         ['notify-send', '-i', 'dialog-information',
-                         'Debian DSA-4371 fix installed in {}'.format(
+                         self.tr('Debian DSA-4371 fix installed in {}').format(
                                 self.vm.name)])
                 elif stdout == b'changed=no\n':
                     pass
                 else:
                     raise exc.QubesException(
-                            "Failed to apply DSA-4371 fix: {}".format(
+                            self.tr("Failed to apply DSA-4371 fix: {}").format(
                                 stderr.decode('ascii')))
                 self.vm.run_service("qubes.InstallUpdatesGUI",
                                     user="root", wait=False)
         except (ChildProcessError, exc.QubesException) as ex:
-            self.msg = ("Error on qube update!", str(ex))
+            self.msg = (self.tr("Error on qube update!"), str(ex))
 
 
 # pylint: disable=too-few-public-methods
@@ -322,7 +323,7 @@ class RunCommandThread(common_threads.QubesThread):
         try:
             self.vm.run(self.command_to_run)
         except (ChildProcessError, exc.QubesException) as ex:
-            self.msg = ("Error while running command!", str(ex))
+            self.msg = (self.tr("Error while running command!"), str(ex))
 
 
 class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow):
@@ -536,18 +537,18 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow):
                     if thread.msg_is_success:
                         QtWidgets.QMessageBox.information(
                             self,
-                            self.tr(title),
-                            self.tr(msg))
+                            title,
+                            msg)
                     else:
                         QtWidgets.QMessageBox.warning(
                             self,
-                            self.tr(title),
-                            self.tr(msg))
+                            title,
+                            msg)
 
                 self.threads_list.remove(thread)
                 return
 
-        raise RuntimeError('No finished thread found')
+        raise RuntimeError(self.tr('No finished thread found'))
 
     def closeEvent(self, event):
         # pylint: disable=invalid-name
@@ -888,7 +889,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow):
                 "Cloning Qube..."), "", 0, 0)
         self.progress.setCancelButton(None)
         self.progress.setModal(True)
-        self.progress.setWindowTitle("Cloning qube...")
+        self.progress.setWindowTitle(self.tr("Cloning qube..."))
         self.progress.show()
 
         thread = common_threads.CloneVMThread(vm, clone_name)
@@ -1300,7 +1301,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow):
 
 def main():
     manager_utils.run_asynchronous(
-        "Qube Manager",
+        QtCore.QCoreApplication.translate("appname", "Qube Manager"),
         "qubes-manager",
         VmManagerWindow)
 

+ 3 - 1
qubesmanager/restore.py

@@ -290,7 +290,9 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtWidgets.QWizard):
 
 
 def main():
-    utils.run_synchronous("Qubes Restore VMs", RestoreVMsWindow)
+    utils.run_synchronous(
+        QtCore.QCoreApplication.translate("appname", "Qubes Restore VMs"),
+        RestoreVMsWindow)
 
 
 if __name__ == "__main__":

+ 17 - 18
qubesmanager/settings.py

@@ -79,9 +79,9 @@ class RenameVMThread(common_threads.QubesThread):
                                              self.vm.name) + list_text)
 
         except qubesadmin.exc.QubesException as ex:
-            self.msg = ("Rename error!", str(ex))
+            self.msg = (self.tr("Rename error!"), str(ex))
         except Exception as ex:  # pylint: disable=broad-except
-            self.msg = ("Rename error!", repr(ex))
+            self.msg = (self.tr("Rename error!"), repr(ex))
 
 
 # pylint: disable=too-few-public-methods
@@ -112,7 +112,7 @@ class RefreshAppsVMThread(common_threads.QubesThread):
                 if not_running:
                     vm.shutdown()
             except Exception as ex:  # pylint: disable=broad-except
-                self.msg = ("Refresh failed!", str(ex))
+                self.msg = (self.tr("Refresh failed!"), str(ex))
 
 
 # pylint: disable=too-many-instance-attributes
@@ -233,8 +233,8 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
                     (title, msg) = thread.msg
                     QtWidgets.QMessageBox.warning(
                         self,
-                        self.tr(title),
-                        self.tr(msg))
+                        title,
+                        msg)
 
                 self.threads_list.remove(thread)
 
@@ -243,7 +243,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
 
                 return
 
-        raise RuntimeError('No finished thread found')
+        raise RuntimeError(self.tr('No finished thread found'))
 
     def keyPressEvent(self, event):  # pylint: disable=invalid-name
         if event.key() == QtCore.Qt.Key_Enter \
@@ -278,16 +278,16 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
         try:
             ret_tmp = self.__apply_basic_tab__()
             if ret_tmp:
-                ret += ["Basic tab:"] + ret_tmp
+                ret += [self.tr("Basic tab:")] + ret_tmp
             ret_tmp = self.__apply_advanced_tab__()
             if ret_tmp:
-                ret += ["Advanced tab:"] + ret_tmp
+                ret += [self.tr("Advanced tab:")] + ret_tmp
             ret_tmp = self.__apply_devices_tab__()
             if ret_tmp:
-                ret += ["Devices tab:"] + ret_tmp
+                ret += [self.tr("Devices tab:")] + ret_tmp
             ret_tmp = self.__apply_services_tab__()
             if ret_tmp:
-                ret += ["Sevices tab:"] + ret_tmp
+                ret += [self.tr("Sevices tab:")] + ret_tmp
         except qubesadmin.exc.QubesException as qex:
             ret.append(self.tr('Error while saving changes: ') + str(qex))
         except Exception as ex:  # pylint: disable=broad-except
@@ -582,8 +582,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
             thread.finished.connect(self.clear_threads)
 
             self.progress = QtWidgets.QProgressDialog(
-                self.tr(
-                    "Renaming Qube..."), "", 0, 0)
+                self.tr("Renaming Qube..."), "", 0, 0)
             self.progress.setCancelButton(None)
             self.progress.setModal(True)
             self.thread_closes = True
@@ -639,8 +638,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
             self.threads_list.append(thread)
 
             self.progress = QtWidgets.QProgressDialog(
-                self.tr(
-                    "Cloning Qube..."), "", 0, 0)
+                self.tr("Cloning Qube..."), "", 0, 0)
             self.progress.setCancelButton(None)
             self.progress.setModal(True)
             self.thread_closes = True
@@ -722,9 +720,9 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
             domains_using = [vm.name for vm in self.vm.connected_vms]
             if domains_using:
                 self.provides_network_checkbox.setEnabled(False)
-                self.provides_network_checkbox.setToolTip(
+                self.provides_network_checkbox.setToolTip(self.tr(
                     "Cannot change this setting while this qube is used as a "
-                    "NetVM by the following qubes:\n" +
+                    "NetVM by the following qubes:\n") +
                     "\n".join(domains_using))
 
     def enable_seamless(self):
@@ -1213,8 +1211,9 @@ def main(args=None):
     args = parser.parse_args(args)
     vm = args.domains.pop()
 
-    utils.run_synchronous("Qube Settings",
-                          functools.partial(VMSettingsWindow, vm, args.tab))
+    utils.run_synchronous(
+        QtCore.QCoreApplication.translate("appname", "Qube Settings"),
+        functools.partial(VMSettingsWindow, vm, args.tab))
 
 
 if __name__ == "__main__":

+ 23 - 11
qubesmanager/table_widgets.py

@@ -295,15 +295,16 @@ class VMPropertyItem(QtWidgets.QTableWidgetItem):
         if self.empty_function(val):
             text = ""
         elif val is None:
-            text = "n/a"
+            text = QtCore.QCoreApplication.translate("QubeManager", "n/a")
         elif val is True:
-            text = "Yes"
+            text = QtCore.QCoreApplication.translate("QubeManager", "Yes")
         else:
             text = str(val)
 
         if self.check_default and hasattr(self.vm, self.property_name) and \
                 self.vm.property_is_default(self.property_name):
-            text = 'default (' + text + ')'
+            text = QtCore.QCoreApplication.translate(
+                "QubeManager", 'default ({})').format(text)
         self.setText(text)
 
     def __lt__(self, other):
@@ -338,7 +339,8 @@ class VmInternalItem(VMPropertyItem):
 
     def update(self):
         internal = self.vm.features.get('internal', False)
-        self.setText("Yes" if internal else "")
+        self.setText(QtCore.QCoreApplication.translate(
+            "QubeManager", "Yes") if internal else "")
 
 
 # features man qvm-features
@@ -416,19 +418,28 @@ class VmUpdateInfoWidget(QtWidgets.QWidget):
         self.value = state
         self.table_item.set_value(state)
         if state == "update":
-            label_text = "<font color=\"#CCCC00\">Check updates</font>"
+            label_text = "<font color=\"#CCCC00\">{}</font>".format(
+                QtCore.QCoreApplication.translate("QubeManager",
+                                                  "Check updates"))
             icon_path = ":/update-recommended.png"
-            tooltip_text = self.tr("Updates pending!")
+            tooltip_text = QtCore.QCoreApplication.translate("QubeManager",
+                                                             "Updates pending!")
         elif state == "outdated":
-            label_text = "<font color=\"red\">Qube outdated</font>"
+            label_text = "<font color=\"red\">{}</font>".format(
+                QtCore.QCoreApplication.translate("QubeManager",
+                                                  "Qube outdated"))
             icon_path = ":/outdated.png"
-            tooltip_text = self.tr(
+            tooltip_text = QtCore.QCoreApplication.translate(
+                "QubeManager",
                 "The qube must be restarted for its filesystem to reflect the "
                 "template's recent committed changes.")
         elif state == "to-be-outdated":
-            label_text = "<font color=\"#800000\">Template running</font>"
+            label_text = "<font color=\"#800000\">{}</font>".format(
+                QtCore.QCoreApplication.translate("QubeManager",
+                                                  "Template running"))
             icon_path = ":/to-be-outdated.png"
-            tooltip_text = self.tr(
+            tooltip_text = QtCore.QCoreApplication.translate(
+                "QubeManager",
                 "The Template must be stopped before changes from its "
                 "current session can be picked up by this qube.")
         else:
@@ -466,7 +477,8 @@ class VmSizeOnDiskItem(QtWidgets.QTableWidgetItem):
 
     def update(self):
         if self.vm.qid == 0:
-            self.setText("n/a")
+            self.setText(QtCore.QCoreApplication.translate("QubeManager",
+                                                           "n/a"))
         else:
             self.value = 10
             self.value = round(self.vm.get_disk_utilization()/(1024*1024), 2)

+ 11 - 7
qubesmanager/template_manager.py

@@ -66,7 +66,7 @@ class TemplateManagerWindow(
         self.templates = [vm.name for vm in self.qubes_app.domains
                           if vm.klass == 'TemplateVM']
 
-        self.change_all_combobox.addItem('(select template)')
+        self.change_all_combobox.addItem(self.tr('(select template)'))
         for template in self.templates:
             self.change_all_combobox.addItem(template)
 
@@ -84,7 +84,8 @@ class TemplateManagerWindow(
             self.rows_in_table[vm.name] = row
             row_count += 1
 
-        self.vm_list.setHorizontalHeaderLabels(['', 'Qube', 'Current', 'New'])
+        self.vm_list.setHorizontalHeaderLabels(
+            ['', self.tr('Qube'), self.tr('Current'), self.tr('New')])
         self.vm_list.resizeColumnsToContents()
 
     def initialize_table_events(self):
@@ -258,7 +259,7 @@ class StatusItem(QtWidgets.QTableWidgetItem):
 
         if self.state:
             self.setIcon(QtGui.QIcon.fromTheme('dialog-warning'))
-            self.setToolTip("Cannot change template on a running VM.")
+            self.setToolTip(self.tr("Cannot change template on a running VM."))
         else:
             self.setIcon(QtGui.QIcon())
             self.setToolTip("")
@@ -331,7 +332,9 @@ class VMRow:
                              self.current_item)
 
         # new template
-        self.dummy_new_item = QtWidgets.QTableWidgetItem("qube is running")
+        self.dummy_new_item = QtWidgets.QTableWidgetItem(
+            QtCore.QCoreApplication.translate("TemplateManager",
+                                              "qube is running"))
         self.new_item = NewTemplateItem(self.vm, templates, table_widget)
 
         table_widget.setItem(row_no, columns.index('New template'),
@@ -378,9 +381,10 @@ class VMRow:
 
 
 def main():
-    utils.run_asynchronous("Template Manager",
-                           "qubes-manager",
-                           TemplateManagerWindow)
+    utils.run_asynchronous(
+        QtCore.QCoreApplication.translate("appname", "Template Manager"),
+        "qubes-manager",
+        TemplateManagerWindow)
 
 
 if __name__ == "__main__":

+ 17 - 11
qubesmanager/utils.py

@@ -96,10 +96,11 @@ def prepare_choice(widget, holder, propname, choice, default,
             default_string = str(default) if default is not None else 'none'
             if transform is not None:
                 default_string = transform(default_string)
-            text = 'default ({})'.format(default_string)
+            text = QtCore.QCoreApplication.translate(
+                "ManagerUtils", 'default ({})').format(default_string)
         # N+1: explicit None
         elif item is None:
-            text = '(none)'
+            text = QtCore.QCoreApplication.translate("ManagerUtils", '(none)')
         # 1..N: choices
         else:
             text = str(item)
@@ -107,7 +108,8 @@ def prepare_choice(widget, holder, propname, choice, default,
                 text = transform(text)
 
         if item == oldvalue:
-            text += ' (current)'
+            text += QtCore.QCoreApplication.translate(
+                "ManagerUtils", ' (current)')
             idx = i
 
         widget.insertItem(i, text)
@@ -206,7 +208,8 @@ def get_path_from_vm(vm, service_name):
         assert '../' not in untrusted_path
         assert '\0' not in untrusted_path
         return untrusted_path.strip()
-    raise ValueError('Unexpected characters in path.')
+    raise ValueError(QtCore.QCoreApplication.translate(
+        "ManagerUtils", 'Unexpected characters in path.'))
 
 
 def format_dependencies_list(dependencies):
@@ -216,9 +219,11 @@ def format_dependencies_list(dependencies):
     list_text = ""
     for (holder, prop) in dependencies:
         if holder is None:
-            list_text += "- Global property <b>{}</b> <br>".format(prop)
+            list_text += QtCore.QCoreApplication.translate(
+                "ManagerUtils", "- Global property <b>{}</b> <br>").format(prop)
         else:
-            list_text += "- <b>{}</b> for qube <b>{}</b> <br>".format(
+            list_text += QtCore.QCoreApplication.translate(
+                "ManagerUtils", "- <b>{0}</b> for qube <b>{1}</b> <br>").format(
                 prop, holder.name)
 
     return list_text
@@ -252,12 +257,13 @@ def handle_exception(exc_type, exc_value, exc_traceback):
     msg_box = QtWidgets.QMessageBox()
     msg_box.setDetailedText(strace)
     msg_box.setIcon(QtWidgets.QMessageBox.Critical)
-    msg_box.setWindowTitle("Houston, we have a problem...")
-    msg_box.setText("Whoops. A critical error has occured. "
+    msg_box.setWindowTitle(QtCore.QCoreApplication.translate(
+        "ManagerUtils", "Houston, we have a problem..."))
+    msg_box.setText(QtCore.QCoreApplication.translate(
+        "ManagerUtils", "Whoops. A critical error has occured. "
                     "This is most likely a bug in Qubes Manager.<br><br>"
-                    "<b><i>%s</i></b>" % error +
-                    "<br/>at line <b>%d</b><br/>of file %s.<br/><br/>"
-                    % (line, filename))
+                    "<b><i>{0}</i></b><br/>at line <b>{1}</b><br/>of file "
+                        "{2}.<br/><br/>").format(error, line, filename))
 
     msg_box.exec_()