diff --git a/qubesmanager/appmenu_select.py b/qubesmanager/appmenu_select.py index 67b55e0..86c9549 100755 --- a/qubesmanager/appmenu_select.py +++ b/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 diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index 1492fbd..39935ce 100644 --- a/qubesmanager/backup.py +++ b/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__": diff --git a/qubesmanager/bootfromdevice.py b/qubesmanager/bootfromdevice.py index 6377a78..66be230 100644 --- a/qubesmanager/bootfromdevice.py +++ b/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__": diff --git a/qubesmanager/clipboard.py b/qubesmanager/clipboard.py index 277e42f..a407d9c 100644 --- a/qubesmanager/clipboard.py +++ b/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') diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py index e2bf9e5..a04d28a 100644 --- a/qubesmanager/common_threads.py +++ b/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)) diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py index f239894..dde5eb0 100644 --- a/qubesmanager/create_new_vm.py +++ b/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 {}...").format(name), "", 0, 0) + self.tr("Creating new qube {0}...").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_() diff --git a/qubesmanager/firewall.py b/qubesmanager/firewall.py index c224d46..c27947e 100644 --- a/qubesmanager/firewall.py +++ b/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 diff --git a/qubesmanager/global_settings.py b/qubesmanager/global_settings.py index b8ce891..4b37b4a 100644 --- a/qubesmanager/global_settings.py +++ b/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__": diff --git a/qubesmanager/i18n/qubesmanager_en.ts b/qubesmanager/i18n/qubesmanager_en.ts index 3f25e2a..547fb24 100644 --- a/qubesmanager/i18n/qubesmanager_en.ts +++ b/qubesmanager/i18n/qubesmanager_en.ts @@ -206,25 +206,78 @@ p, li { white-space: pre-wrap; } + + Clipboard + + + Warning! + + + + + Error while accessing Qubes clipboard! + + + + + Error while locking Qubes clipboard! + + + + + Error while writing to Qubes clipboard! + + + + + %n byte(s) + + + + + + + ? bytes + + + + + CloneVMThread + + + Sucess + + + + + The qube was cloned sucessfully. + + + + + Error while cloning qube! + + + DevListWidgetItem - + Service already on the list! - + Error re-assigning device - + Refresh in progress... - + Refresh Applications @@ -394,34 +447,59 @@ p, li { white-space: pre-wrap; } Community template updates (testing) + + + qrexec call stderr was not empty + + + + + qrexec call exited with non-zero return code + + GlobalSettingsWindow - + Change state of all qubes - + Are you sure you want to set all qubes to check for updates? - + Are you sure you want to set all qubes to not check for updates? - + ERROR! - + Error managing {repo} repository settings: {msg} + + + Cannot detect enabled dom0 update repositories + + + + + Cannot detect enabled ITL template update repositories + + + + + qrexec call stdout did not contain "ok" as expected + + InformationNotesDialog @@ -478,6 +556,49 @@ p, li { white-space: pre-wrap; } + + ManagerUtils + + + default ({}) + + + + + (none) + + + + + (current) + + + + + Unexpected characters in path. + + + + + - Global property <b>{}</b> <br> + + + + + - <b>{0}</b> for qube <b>{1}</b> <br> + + + + + Houston, we have a problem... + + + + + Whoops. A critical error has occured. This is most likely a bug in Qubes Manager.<br><br><b><i>{0}</i></b><br/>at line <b>{1}</b><br/>of file {2}.<br/><br/> + + + MultiSelectWidget @@ -675,53 +796,139 @@ p, li { white-space: pre-wrap; } NewVmDlg - + No template available! - + Cannot create a qube when no template exists. - + Qube based on a template (AppVM) - + Standalone qube copied from a template - + Empty standalone qube (install your own OS) - + Incorrect qube name! - + A qube with the name <b>{}</b> already exists in the system! - - Creating new qube <b>{}</b>... - - - - + Error creating the qube! - - ERROR: {} + + Creating new qube <b>{0}</b>... + + + + + ERROR: {0} + + + + + QubeManager + + + n/a + + + + + Yes + + + + + default ({}) + + + + + Check updates + + + + + Updates pending! + + + + + Qube outdated + + + + + The qube must be restarted for its filesystem to reflect the template's recent committed changes. + + + + + Template running + + + + + The Template must be stopped before changes from its current session can be picked up by this qube. + + + + + QubesFirewallRulesModel + + + At least one rule must exist. + + + + + Last rule must be either drop all or accept all. + + + + + Rule type unknown! + + + + + No blacklist support. + + + + + it does not add up. + + + + + ICMP must be allowed. + + + + + DNS must be allowed @@ -732,6 +939,11 @@ p, li { white-space: pre-wrap; } Refresh in progress (refreshing applications from {}) + + + Refresh failed! + + ReleaseNotesDialog @@ -741,6 +953,14 @@ p, li { white-space: pre-wrap; } + + RemoveVMThread + + + Error removing qube! + + + RenameVMThread @@ -753,6 +973,11 @@ p, li { white-space: pre-wrap; } 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.<br> + + + Rename error! + + Restore @@ -902,6 +1127,14 @@ p, li { white-space: pre-wrap; } + + RunCommandThread + + + Error while running command! + + + SettingsDialog @@ -1307,18 +1540,72 @@ The qube must be running to disable seamless mode; this setting is not persisten + + StatusItem + + + Cannot change template on a running VM. + + + + + TemplateManager + + + qube is running + + + TemplateManagerWindow - + Errors encountered! - + Errors encountered on template change in the following qubes: <br> {}. + + + (select template) + + + + + Qube + + + + + Current + + + + + New + + + + + UpdateVMThread + + + Debian DSA-4371 fix installed in {} + + + + + Failed to apply DSA-4371 fix: {} + + + + + Error on qube update! + + VMBootFromDeviceWindow @@ -1456,50 +1743,81 @@ The qube must be running to disable seamless mode; this setting is not persisten - + Qube cannot be removed! - + This qube cannot be removed. It is used as: <br> {} <small>If you want to remove this qube, you should remove or change settings of each qube or setting that uses it.</small> - + Delete qube - + Are you absolutely sure you want to delete this qube? <br/> All qube settings and data will be irrevocably deleted. <br/> If you are sure, please enter this qube's name below. - + Removal cancelled - + The qube will not be removed. - + Clone qube - + Name for the cloned qube: - + Cloning Qube... + + + No finished thread found + + + + + Basic tab: + + + + + Advanced tab: + + + + + Devices tab: + + + + + Sevices tab: + + + + + Cannot change this setting while this qube is used as a NetVM by the following qubes: + + + VmListItem @@ -1528,18 +1846,8 @@ The qube must be running to disable seamless mode; this setting is not persisten Enter the same passphrase in both fields. - - - Backup error. - - - Backup error! - - - - ERROR: {} @@ -1558,22 +1866,32 @@ The qube must be running to disable seamless mode; this setting is not persisten Backup aborted! + + + Backup error + + + + + ERROR: Aborted + + VmManagerWindow - + Manager settings unreadable - + Qube Manager settings cannot be parsed. Previously saved display settings may not be restored correctly. Error: {} - + Loading Qube Manager... @@ -1583,142 +1901,142 @@ Error: {} - + Warning! - + This qube cannot be removed. It is used as: <br> {} <small>If you want to remove this qube, you should remove or change settings of each qube or setting that uses it.</small> - + Qube Removal Confirmation - + Are you sure you want to remove the Qube <b>'{0}'</b>?<br> All data on this Qube's private storage will be lost!<br><br>Type the name of the Qube (<b>{1}</b>) below to confirm: - + Qube removal confirmation failed - + Entered name did not match! Not removing {0}. - + Qubes clone Qube - + Enter name for Qube <b>{}</b> clone: - + Name already in use! - + There already exists a qube called '{}'. Cloning aborted. - + Cloning Qube... - + Error unpausing Qube! - + ERROR: {0} - + Error pausing Qube! - + Qube Shutdown Confirmation - + Are you sure you want to power down the Qube <b>'{0}'</b>?<br><small>This will shutdown all the running applications within this Qube.</small> - + Error shutting down Qube! - + Qube Restart Confirmation - + Are you sure you want to restart the Qube <b>'{0}'</b>?<br><small>This will shutdown all the running applications within this Qube.</small> - + Qube <b>'{0}'</b> is not running. Are you absolutely sure you want to try to kill it?<br><small>This will end <b>(not shutdown!)</b> all the running applications within this Qube.</small> - + Are you sure you want to kill the Qube <b>'{0}'</b>?<br><small>This will end <b>(not shutdown!)</b> all the running applications within this Qube.</small> - + Qube Kill Confirmation - + Error while killing Qube! - + <b>An exception ocurred while killing {0}.</b><br>ERROR: {1} - + Qube Update Confirmation - + <b>{0}</b><br>The Qube has to be running to be updated.<br>Do you want to start it?<br> - + Qubes command entry - + Run command in <b>{}</b>: @@ -2171,45 +2489,83 @@ Template <html><head/><body><p>DisposableVM Template</p><p><br/></p><p>Allows using this qube as a template for DisposableVMs. The DisposableVMs will inherit the VM's state (configuration, installed programs etc.), but their state will not persist between restarts. </p></body></html> + + + No finished thread found + + + + + Cloning qube... + + VmShutdownMonitor - + Qube Shutdown - + The Qube <b>'{0}'</b> hasn't shutdown within the last {1} seconds, do you want to kill it?<br> - + Kill it! - + Wait another {0} seconds... VmUpdateInfoItem + + + appname - - Updates pending! + + Qubes Backup VMs - - The qube must be restarted for its filesystem to reflect the template's recent committed changes. + + Boot Qube From Device - - The Template must be stopped before changes from its current session can be picked up by this qube. + + Create qube + + + + + Qubes Global Settings + + + + + Qube Manager + + + + + Qubes Restore VMs + + + + + Qube Settings + + + + + Template Manager @@ -2231,4 +2587,12 @@ Template + + exception + + + qvm-appmenus --set-whitelist failed + + + diff --git a/qubesmanager/i18n/qubesmanager_es.ts b/qubesmanager/i18n/qubesmanager_es.ts index 29997f9..629a846 100644 --- a/qubesmanager/i18n/qubesmanager_es.ts +++ b/qubesmanager/i18n/qubesmanager_es.ts @@ -247,10 +247,64 @@ p, li { white-space: pre-wrap; } ... + + Clipboard + + + Warning! + ¡Atencion! + + + + Error while accessing Qubes clipboard! + + + + + Error while locking Qubes clipboard! + + + + + Error while writing to Qubes clipboard! + + + + + %n byte(s) + + + + + + + + ? bytes + + + + + CloneVMThread + + + Sucess + + + + + The qube was cloned sucessfully. + + + + + Error while cloning qube! + + + DevListWidgetItem - + Service already on the list! ¡El servicio ya está en la lista! @@ -285,17 +339,17 @@ p, li { white-space: pre-wrap; } El servicio '{0}' es desconocido. - + Error re-assigning device - + Refresh in progress... - + Refresh Applications @@ -490,6 +544,16 @@ p, li { white-space: pre-wrap; } Community template updates (testing) + + + qrexec call stderr was not empty + + + + + qrexec call exited with non-zero return code + + GlobalSettingsWindow @@ -499,30 +563,45 @@ p, li { white-space: pre-wrap; } (actual) - + Change state of all qubes - + Are you sure you want to set all qubes to check for updates? - + Are you sure you want to set all qubes to not check for updates? - + ERROR! - + Error managing {repo} repository settings: {msg} + + + Cannot detect enabled dom0 update repositories + + + + + Cannot detect enabled ITL template update repositories + + + + + qrexec call stdout did not contain "ok" as expected + + InformationNotesDialog @@ -580,6 +659,49 @@ p, li { white-space: pre-wrap; } + + ManagerUtils + + + default ({}) + + + + + (none) + + + + + (current) + (actual) + + + + Unexpected characters in path. + + + + + - Global property <b>{}</b> <br> + + + + + - <b>{0}</b> for qube <b>{1}</b> <br> + + + + + Houston, we have a problem... + + + + + Whoops. A critical error has occured. This is most likely a bug in Qubes Manager.<br><br><b><i>{0}</i></b><br/>at line <b>{1}</b><br/>of file {2}.<br/><br/> + + + MultiSelectWidget @@ -852,7 +974,7 @@ p, li { white-space: pre-wrap; } Ya existe en el sistema una VM con el nombre <b>{0}</b> - + No template available! No hay una plantillas disponibles @@ -877,54 +999,140 @@ p, li { white-space: pre-wrap; } Creando nueva {0} <b>{1}</b>... - + ERROR: {0} - ERROR: {0} + ERROR: {0} - + Cannot create a qube when no template exists. - + Qube based on a template (AppVM) - + Standalone qube copied from a template - + Empty standalone qube (install your own OS) - + Incorrect qube name! - + A qube with the name <b>{}</b> already exists in the system! - - Creating new qube <b>{}</b>... - - - - + Error creating the qube! ERROR: {} - ERROR: {} + ERROR: {} + + + + Creating new qube <b>{0}</b>... + + + + + QubeManager + + + n/a + + + + + Yes + + + + + default ({}) + + + + + Check updates + + + + + Updates pending! + ¡Actualizaciones pendientes! + + + + Qube outdated + + + + + The qube must be restarted for its filesystem to reflect the template's recent committed changes. + + + + + Template running + + + + + The Template must be stopped before changes from its current session can be picked up by this qube. + + + + + QubesFirewallRulesModel + + + At least one rule must exist. + + + + + Last rule must be either drop all or accept all. + + + + + Rule type unknown! + + + + + No blacklist support. + + + + + it does not add up. + + + + + ICMP must be allowed. + + + + + DNS must be allowed + @@ -1019,6 +1227,11 @@ p, li { white-space: pre-wrap; } Refresh in progress (refreshing applications from {}) + + + Refresh failed! + + ReleaseNotesDialog @@ -1028,6 +1241,14 @@ p, li { white-space: pre-wrap; } Notas sobre esta versión de Qubes + + RemoveVMThread + + + Error removing qube! + + + RenameVMThread @@ -1040,6 +1261,11 @@ p, li { white-space: pre-wrap; } 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.<br> + + + Rename error! + + Restore @@ -1254,6 +1480,14 @@ p, li { white-space: pre-wrap; } + + RunCommandThread + + + Error while running command! + + + SettingsDialog @@ -1769,18 +2003,72 @@ The qube must be running to disable seamless mode; this setting is not persisten + + StatusItem + + + Cannot change template on a running VM. + + + + + TemplateManager + + + qube is running + + + TemplateManagerWindow - + Errors encountered! - + Errors encountered on template change in the following qubes: <br> {}. + + + (select template) + + + + + Qube + + + + + Current + + + + + New + + + + + UpdateVMThread + + + Debian DSA-4371 fix installed in {} + + + + + Failed to apply DSA-4371 fix: {} + + + + + Error on qube update! + + VMBootFromDeviceWindow @@ -1953,50 +2241,81 @@ The qube must be running to disable seamless mode; this setting is not persisten - + Qube cannot be removed! - + This qube cannot be removed. It is used as: <br> {} <small>If you want to remove this qube, you should remove or change settings of each qube or setting that uses it.</small> - + Delete qube - + Are you absolutely sure you want to delete this qube? <br/> All qube settings and data will be irrevocably deleted. <br/> If you are sure, please enter this qube's name below. - + Removal cancelled - + The qube will not be removed. - + Clone qube - + Name for the cloned qube: - + Cloning Qube... + + + No finished thread found + + + + + Basic tab: + + + + + Advanced tab: + + + + + Devices tab: + + + + + Sevices tab: + + + + + Cannot change this setting while this qube is used as a NetVM by the following qubes: + + + VmListItem @@ -2068,15 +2387,15 @@ The qube must be running to disable seamless mode; this setting is not persisten Backup error. - Error en la copia de respaldo. + Error en la copia de respaldo. Backup error! - Error en la copia de respaldo. + Error en la copia de respaldo. - + ERROR: {} ERROR: {} @@ -2095,11 +2414,21 @@ The qube must be running to disable seamless mode; this setting is not persisten Backup aborted! + + + Backup error + + + + + ERROR: Aborted + + VmManagerWindow - + Warning! ¡Cuidado! @@ -2124,7 +2453,7 @@ The qube must be running to disable seamless mode; this setting is not persisten La confirmación de borrado del VM falló. - + Entered name did not match! Not removing {0}. ¡El nombre ingresado no coincide! La VM {0} no será borrada. @@ -2144,7 +2473,7 @@ The qube must be running to disable seamless mode; this setting is not persisten Ocurrió un error al borrar la VM. - + ERROR: {0} ERROR: {0} @@ -2264,7 +2593,7 @@ The qube must be running to disable seamless mode; this setting is not persisten Ocurrió un error al terminar la VM - + <b>An exception ocurred while killing {0}.</b><br>ERROR: {1} <b>Ocurrió una excepción al terminar {0}.</b><br>ERROR: {1} @@ -2289,12 +2618,12 @@ The qube must be running to disable seamless mode; this setting is not persisten Ocurrió un error al actualizar la VM - + Qubes command entry Qubes: Ejecución de comandos - + Run command in <b>{}</b>: Ejecutar el comando en <b>{}</b>: @@ -2779,18 +3108,18 @@ The qube must be running to disable seamless mode; this setting is not persisten Ctrl+F - + Manager settings unreadable - + Qube Manager settings cannot be parsed. Previously saved display settings may not be restored correctly. Error: {} - + Loading Qube Manager... @@ -2800,112 +3129,112 @@ Error: {} - + This qube cannot be removed. It is used as: <br> {} <small>If you want to remove this qube, you should remove or change settings of each qube or setting that uses it.</small> - + Qube Removal Confirmation - + Are you sure you want to remove the Qube <b>'{0}'</b>?<br> All data on this Qube's private storage will be lost!<br><br>Type the name of the Qube (<b>{1}</b>) below to confirm: - + Qube removal confirmation failed - + Qubes clone Qube - + Enter name for Qube <b>{}</b> clone: - + Name already in use! - + There already exists a qube called '{}'. Cloning aborted. - + Cloning Qube... - + Error unpausing Qube! - + Error pausing Qube! - + Qube Shutdown Confirmation - + Are you sure you want to power down the Qube <b>'{0}'</b>?<br><small>This will shutdown all the running applications within this Qube.</small> - + Error shutting down Qube! - + Qube Restart Confirmation - + Are you sure you want to restart the Qube <b>'{0}'</b>?<br><small>This will shutdown all the running applications within this Qube.</small> - + Qube <b>'{0}'</b> is not running. Are you absolutely sure you want to try to kill it?<br><small>This will end <b>(not shutdown!)</b> all the running applications within this Qube.</small> - + Are you sure you want to kill the Qube <b>'{0}'</b>?<br><small>This will end <b>(not shutdown!)</b> all the running applications within this Qube.</small> - + Qube Kill Confirmation - + Error while killing Qube! - + Qube Update Confirmation - + <b>{0}</b><br>The Qube has to be running to be updated.<br>Do you want to start it?<br> @@ -3168,6 +3497,16 @@ Template <html><head/><body><p>DisposableVM Template</p><p><br/></p><p>Allows using this qube as a template for DisposableVMs. The DisposableVMs will inherit the VM's state (configuration, installed programs etc.), but their state will not persist between restarts. </p></body></html> + + + No finished thread found + + + + + Cloning qube... + + VmShutdownMonitor @@ -3182,22 +3521,22 @@ Template La VM <b>'{0}'</b> no fue apagada dentro de los últimos {1} segundos, ¿quieres terminarla?<br> - + Kill it! Terminar el VM - + Wait another {0} seconds... Esperar {0} segundos más... - + Qube Shutdown - + The Qube <b>'{0}'</b> hasn't shutdown within the last {1} seconds, do you want to kill it?<br> @@ -3207,7 +3546,7 @@ Template Updates pending! - ¡Actualizaciones pendientes! + ¡Actualizaciones pendientes! @@ -3219,14 +3558,47 @@ Template The TemplateVM must be stopped before changes from its current session can be picked up by this VM. El TemplateVM debe detenerse antes de que los cambios de su sesión actual puedan ser recogidos por esta VM. + + + appname - - The qube must be restarted for its filesystem to reflect the template's recent committed changes. + + Qubes Backup VMs + Qubes: Respaldo de VMs + + + + Boot Qube From Device - - The Template must be stopped before changes from its current session can be picked up by this qube. + + Create qube + + + + + Qubes Global Settings + Opciones de configuración global de Qubes OS + + + + Qube Manager + + + + + Qubes Restore VMs + Qubes: Recuperar VM desde copia de respaldo + + + + Qube Settings + + + + + Template Manager @@ -3248,4 +3620,12 @@ Template + + exception + + + qvm-appmenus --set-whitelist failed + + + diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index fc409b2..37c75c7 100644 --- a/qubesmanager/qube_manager.py +++ b/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) diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index edc4baa..b79e78c 100644 --- a/qubesmanager/restore.py +++ b/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__": diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index e17a9f6..d65a547 100644 --- a/qubesmanager/settings.py +++ b/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__": diff --git a/qubesmanager/table_widgets.py b/qubesmanager/table_widgets.py index 9ec9c1d..a5d4a97 100644 --- a/qubesmanager/table_widgets.py +++ b/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 = "Check updates" + label_text = "{}".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 = "Qube outdated" + label_text = "{}".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 = "Template running" + label_text = "{}".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) diff --git a/qubesmanager/template_manager.py b/qubesmanager/template_manager.py index 0cde1c8..d210e81 100644 --- a/qubesmanager/template_manager.py +++ b/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__": diff --git a/qubesmanager/utils.py b/qubesmanager/utils.py index 64d1f1c..94dc680 100644 --- a/qubesmanager/utils.py +++ b/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 {}
".format(prop) + list_text += QtCore.QCoreApplication.translate( + "ManagerUtils", "- Global property {}
").format(prop) else: - list_text += "- {} for qube {}
".format( + list_text += QtCore.QCoreApplication.translate( + "ManagerUtils", "- {0} for qube {1}
").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.

" - "%s" % error + - "
at line %d
of file %s.

" - % (line, filename)) + "{0}
at line {1}
of file " + "{2}.

").format(error, line, filename)) msg_box.exec_()