From 5144f65cf7f742a12b225222efaf30de029a5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=AD=20Climent?= Date: Fri, 7 Oct 2022 22:05:06 +0200 Subject: [PATCH 1/7] Implement the basic engine + replace strings in __init__ and mainWindow modules (#67) --- wingetui/__init__.py | 61 ++++++++-------- .../__pycache__/lang_tools.cpython-310.pyc | Bin 0 -> 1486 bytes wingetui/lang/lang_ca.json | 3 + wingetui/lang/lang_en.json | 3 + wingetui/lang/lang_tools.py | 66 ++++++++++++++++++ wingetui/lang/translated_percentage.py | 7 ++ wingetui/languages.py | 13 ++++ wingetui/mainWindow.py | 51 +++++--------- wingetui/tools.py | 34 +++++++-- 9 files changed, 166 insertions(+), 72 deletions(-) create mode 100644 wingetui/lang/__pycache__/lang_tools.cpython-310.pyc create mode 100644 wingetui/lang/lang_ca.json create mode 100644 wingetui/lang/lang_en.json create mode 100644 wingetui/lang/lang_tools.py create mode 100644 wingetui/lang/translated_percentage.py create mode 100644 wingetui/languages.py diff --git a/wingetui/__init__.py b/wingetui/__init__.py index 0dd25cc404..153f48e095 100644 --- a/wingetui/__init__.py +++ b/wingetui/__init__.py @@ -9,12 +9,11 @@ import wingetHelpers, scoopHelpers from mainWindow import * from tools import * + from tools import _ import globals from blurwindow import GlobalBlur, ExtendFrameIntoClientArea - debugging = True - if hasattr(Qt, 'AA_EnableHighDpiScaling'): QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) if hasattr(Qt, 'AA_UseHighDpiPixmaps'): @@ -51,7 +50,7 @@ def __init__(self): titlewidget.addStretch() self.popup.layout().addLayout(titlewidget) self.popup.layout().addStretch() - self.loadingText = QLabel("Loading WingetUI...") + self.loadingText = QLabel(_("Loading WingetUI...")) self.loadingText.setStyleSheet(f"font-family: \"Segoe UI Variable Text\"; color: {'white' if isDark() else 'black'};font-size: 12px;") self.popup.layout().addWidget(self.loadingText) ApplyMenuBlur(self.popup.winId().__int__(), self.popup) @@ -104,7 +103,7 @@ def __init__(self): self.kill.connect(lambda: (self.popup.hide(), sys.exit(0))) self.callInMain.connect(lambda f: f()) Thread(target=self.loadStuffThread, daemon=True).start() - self.loadingText.setText("Checking for other running instances...") + self.loadingText.setText(_("Checking for other running instances...")) except Exception as e: raise e @@ -137,7 +136,7 @@ def loadStuffThread(self): except Exception as e: print(e) finally: - self.callInMain.emit(lambda: self.loadingText.setText(f"Loading UI components...")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Loading UI components..."))) self.callInMain.emit(lambda: self.loadingText.repaint()) self.callInMain.emit(self.loadMainUI) print(globals.componentStatus) @@ -150,22 +149,22 @@ def checkForRunningInstances(self): try: timestamps = [float(file.replace(os.path.join(os.path.join(os.path.expanduser("~"), ".wingetui"), "WingetUI_"), "")) for file in glob.glob(os.path.join(os.path.join(os.path.expanduser("~"), ".wingetui"), "WingetUI_*"))] # get a list with the timestamps validTimestamps = [timestamp for timestamp in timestamps if timestamp < self.nowTime] - self.callInMain.emit(lambda: self.loadingText.setText(f"Evaluating found instace(s)...")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Checking found instace(s)..."))) print("Found lock file(s), reactivating...") for tst in validTimestamps: setSettings("RaiseWindow_"+str(tst), True) if validTimestamps != [] and timestamps != [self.nowTime]: for i in range(16): time.sleep(0.1) - self.callInMain.emit(lambda: self.loadingText.setText(f"Sent handshake. Waiting for instance listener's answer... ({int(i/15*100)}%)")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Sent handshake. Waiting for instance listener's answer... ({0}%)").format(int(i/15*100)))) for tst in validTimestamps: if not getSettings("RaiseWindow_"+str(tst), cache = False): print(f"Instance {tst} responded, quitting...") - self.callInMain.emit(lambda: self.loadingText.setText(f"Instance {tst} responded, quitting...")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Instance {0} responded, quitting...").format(tst))) setSettings(self.lockFileName, False) self.kill.emit() sys.exit(0) - self.callInMain.emit(lambda: self.loadingText.setText(f"Starting daemons...")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Starting daemons..."))) print("Reactivation signal ignored: RaiseWindow_"+str(validTimestamps)) for tst in validTimestamps: setSettings("RaiseWindow_"+str(tst), False) @@ -176,41 +175,41 @@ def checkForRunningInstances(self): def detectWinget(self): try: - self.callInMain.emit(lambda: self.loadingText.setText(f"Locating winget...")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Locating winget..."))) o = subprocess.run(f"{wingetHelpers.winget} -v", shell=True, stdout=subprocess.PIPE) print(o.stdout) print(o.stderr) globals.componentStatus["wingetFound"] = o.returncode == 0 globals.componentStatus["wingetVersion"] = o.stdout.decode('utf-8').replace("\n", "") - self.callInMain.emit(lambda: self.loadingText.setText(f"Winget found: {globals.componentStatus['wingetFound']}")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Winget found: {0}").format(globals.componentStatus['wingetFound']))) except Exception as e: print(e) self.loadStatus += 1 print("updating winget") try: if not getSettings("DisableUpdateIndexes"): - self.callInMain.emit(lambda: self.loadingText.setText(f"Updating winget sources...")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Updating winget sources..."))) o = subprocess.run(f"{wingetHelpers.winget} source update --name winget", shell=True, stdout=subprocess.PIPE) - self.callInMain.emit(lambda: self.loadingText.setText(f"Updated winget sources")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Updated winget sources"))) except Exception as e: print(e) self.loadStatus += 1 def detectScoop(self): try: - self.callInMain.emit(lambda: self.loadingText.setText(f"Locating scoop...")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Locating scoop..."))) o = subprocess.run(f"powershell -Command scoop -v", shell=True, stdout=subprocess.PIPE) print(o.stdout) print(o.stderr) globals.componentStatus["scoopFound"] = o.returncode == 0 globals.componentStatus["scoopVersion"] = o.stdout.decode('utf-8').split("\n")[1] - self.callInMain.emit(lambda: self.loadingText.setText(f"Scoop found: {globals.componentStatus['scoopFound']}")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Scoop found: {0}").format(globals.componentStatus['scoopFound']))) except Exception as e: print(e) self.loadStatus += 1 try: if not getSettings("DisableUpdateIndexes"): - self.callInMain.emit(lambda: self.loadingText.setText(f"Clearing scoop cache...")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Clearing scoop cache..."))) p = subprocess.Popen(f"powershell -Command scoop cache rm *", shell=True, stdout=subprocess.PIPE) if(getSettings("EnableScoopCleanup")): p2 = subprocess.Popen(f"powershell -Command scoop cleanup --all", shell=True, stdout=subprocess.PIPE) @@ -220,9 +219,9 @@ def detectScoop(self): print(e) try: if not getSettings("DisableUpdateIndexes"): - self.callInMain.emit(lambda: self.loadingText.setText(f"Updating scoop sources...")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Updating scoop sources..."))) o = subprocess.run(f"powershell -Command scoop update", shell=True, stdout=subprocess.PIPE) - self.callInMain.emit(lambda: self.loadingText.setText(f"Updated scoop sources")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Updated scoop sources"))) except Exception as e: print(e) self.loadStatus += 1 @@ -230,11 +229,11 @@ def detectScoop(self): def detectSudo(self): global sudoLocation try: - self.callInMain.emit(lambda: self.loadingText.setText(f"Locating sudo...")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Locating sudo..."))) o = subprocess.run(f"{sudoPath} -v", shell=True, stdout=subprocess.PIPE) globals.componentStatus["sudoFound"] = o.returncode == 0 globals.componentStatus["sudoVersion"] = o.stdout.decode('utf-8').split("\n")[0] - self.callInMain.emit(lambda: self.loadingText.setText(f"Sudo found: {globals.componentStatus['sudoFound']}")) + self.callInMain.emit(lambda: self.loadingText.setText(_("Sudo found: {0}").format(globals.componentStatus['sudoFound']))) except Exception as e: print(e) self.loadStatus += 1 @@ -251,58 +250,58 @@ def loadMainUI(self): menu = QMenu("WingetUI") globals.trayMenu = menu - self.infoAction = QAction(f"WingetUI version {versionName}",menu) + self.infoAction = QAction(_("WingetUI version {0}").format(versionName), menu) self.infoAction.setIcon(QIcon(getMedia("info"))) self.infoAction.setEnabled(False) menu.addAction(self.infoAction) - self.showAction = QAction("Show WingetUI",menu) + self.showAction = QAction(_("Show WingetUI"), menu) self.showAction.setIcon(QIcon(getMedia("menu_show"))) menu.addAction(self.showAction) self.trayIcon.setContextMenu(menu) menu.addSeparator() - self.dAction = QAction("Available updates",menu) + self.dAction = QAction(_("Available updates"), menu) self.dAction.setIcon(QIcon(getMedia("menu_updates"))) self.dAction.setEnabled(False) menu.addAction(self.dAction) - self.updatesMenu = menu.addMenu("0 updates found") + self.updatesMenu = menu.addMenu(_("0 updates found")) self.updatesMenu.menuAction().setIcon(QIcon(getMedia("list"))) self.updatesMenu.setParent(menu) globals.trayMenuUpdatesList = self.updatesMenu menu.addMenu(self.updatesMenu) - globals.updatesHeader = QAction("App Name \tInstalled Version \t → \t New version", menu) + globals.updatesHeader = QAction(_("App Name \tInstalled Version \t → \t New version"), menu) globals.updatesHeader.setEnabled(False) globals.updatesHeader.setIcon(QIcon(getMedia("version"))) self.updatesMenu.addAction(globals.updatesHeader) - self.uaAction = QAction("Update all", menu) + self.uaAction = QAction(_("Update all"), menu) self.uaAction.setIcon(QIcon(getMedia("menu_installall"))) menu.addAction(self.uaAction) menu.addSeparator() - self.iAction = QAction("Installed packages",menu) + self.iAction = QAction(_("Installed packages"),menu) self.iAction.setIcon(QIcon(getMedia("menu_uninstall"))) self.iAction.setEnabled(False) menu.addAction(self.iAction) - self.installedMenu = menu.addMenu("0 packages found") + self.installedMenu = menu.addMenu(_("0 packages found")) self.installedMenu.menuAction().setIcon(QIcon(getMedia("list"))) self.installedMenu.setParent(menu) globals.trayMenuInstalledList = self.installedMenu menu.addMenu(self.installedMenu) menu.addSeparator() - globals.installedHeader = QAction("App Name\tInstalled Version", menu) + globals.installedHeader = QAction(_("App Name\tInstalled Version"), menu) globals.installedHeader.setIcon(QIcon(getMedia("version"))) globals.installedHeader.setEnabled(False) self.installedMenu.addAction(globals.installedHeader) self.quitAction = QAction(menu) self.quitAction.setIcon(QIcon(getMedia("menu_close"))) - self.quitAction.setText("Quit") + self.quitAction.setText(_("Quit")) self.quitAction.triggered.connect(lambda: (self.quit(), sys.exit(0))) menu.addAction(self.quitAction) @@ -334,7 +333,7 @@ def applyMenuStyle(): self.showAction.triggered.connect(self.window.showWindow) self.uaAction.triggered.connect(self.window.updates.upgradeAllAction.trigger) showWindow = self.showAction.trigger - self.loadingText.setText(f"Latest details...") + self.loadingText.setText(_("Latest details...")) if not self.isDaemon: self.window.show() if(self.window.isAdmin()): diff --git a/wingetui/lang/__pycache__/lang_tools.cpython-310.pyc b/wingetui/lang/__pycache__/lang_tools.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da73a4f5c42e3a000238cc22d60e16ecb2e494d7 GIT binary patch literal 1486 zcmYjRTW{P%6!sjCy_?M@Y0?|L04}tYrQIaG@le{TG!%h=C`y7NSwSmz&t%tGuf5F7 zCe(6XDF1**S}A$pfxo~b{EK4bKst$0knW49?<(``(u<^zkBI`pgL{czMZbT=|~ncN)z2;*(fuMH*j-bT^+^ zx~sJ42T7E6hEYchCn;|((h)g=W>9M1Zs(J?(@qkrF1em+4cAmqtVP2_8)!!cL~6W& zZY?aA;?`ma!^n7fx3#=din;lQms3V_VYw8y77Yx0c`ffY--%M)TwdAJRda6MKqL() z>U&4gj-*F(krU}Ka

;7=?g6Nw3Wbp>u~ONJvtZpoxSquto_n*Kg)N#1rDomb1%~4s0YlCAM%Zm@3tP<_xT0e zuHi_*sdKIH2uBN_u>PR3L!72QHa=!`9N!)-*bXD^Ow3D0HzCR#8KzoBsY)U(a6!nn zNHuXLKT!nlKjlwQ1k3v=WEAp z2y2m&*YQSYNK;D*o;wkuV0+K5fsKTtk$T9 zhyNe5HQ1k1o}p_PS7|9-t5sXAwKx&2mhlIXq`R2Oz0Y7#tO@e8h4EtgJd^}mFtrzc zo5oO!q8|;69}NZ~MZ?##IBitQGs00RBJ2zMP?T|zNXTtzShYAG^@ZVfEW_=6zH;Pc z>i)2K_v-3OoOXo%@ZIK;&ZJn~EX`rOYU@g9=`2gs^x&>JN{JstvWMAbx-=XNGO5e+ zW~rAtNl<6t0^?7{y#wqBJH%?V53zt`4dm;TFyE+}N=D7-N3eUridB>NPWgP~Im!vg z?9ukmP+nIy$yXMaGd}}YC}67B`WX%ral;ZuB?NUl1_uVre+*SCOZ^elID>yVz!UDm zW6t3p?!hDO!$V$yzj+n@;v?`UufZRD6n^J*_>GUjuY4SS;S=yPKL9`RN%#?__BvL7 Hs2~3i7%-oW literal 0 HcmV?d00001 diff --git a/wingetui/lang/lang_ca.json b/wingetui/lang/lang_ca.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/wingetui/lang/lang_ca.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/wingetui/lang/lang_en.json b/wingetui/lang/lang_en.json new file mode 100644 index 0000000000..544b7b4ddd --- /dev/null +++ b/wingetui/lang/lang_en.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/wingetui/lang/lang_tools.py b/wingetui/lang/lang_tools.py new file mode 100644 index 0000000000..8c8b5e3839 --- /dev/null +++ b/wingetui/lang/lang_tools.py @@ -0,0 +1,66 @@ +from os.path import exists +from pathlib import Path + + +languageReference = { + "default": "System language", + "ca" : "Catalan - Català", + "en" : "English - English", +} + + +languageRemap = { + "pt-PT": "pt_PT", + "pt-BR": "pt_BR", + "uk": "ua", + "zh-Hant-TW": "zh_TW", + "zh-Hans-CN": "zh_CN", +} + + +# ISO 3166-1 +languageFlagsRemap = { + "ar": "sa", + "bs": "ba", + "ca": "ad", + "cs": "cz", + "da": "dk", + "en": "gb", + "el": "gr", + "et": "ee", + "fa": "ir", + "he": "il", + "ja": "jp", + "ko": "kr", + "nb": "no", + "nn": "no", + "pt_BR": "br", + "pt_PT": "pt", + "si": "lk", + "zh_CN": "cn", + "zh_TW": "tw", + "vi": "vn", + "sr": "rs", + "sv": "se", +} + + +def getMarkdownSupportLangs(): + from translated_percentage import untranslatedPercentage + + readmeLangs = [ + "| Language | Translated | |", + "| :-- | :-- | --- |", + ] + + dir = str(Path(__file__).parent) + for lang, langName in languageReference.items(): + if (not exists(f"{dir}/lang_{lang}.json")): continue + perc = untranslatedPercentage[lang] if (lang in untranslatedPercentage) else "100%" + if (perc == "0%"): continue + langName = languageReference[lang] if (lang in languageReference) else lang + flag = languageFlagsRemap[lang] if (lang in languageFlagsRemap) else lang + readmeLangs.append(f"| {langName} | {perc} | |") + readmeLangs.append("") + + return "\n".join(readmeLangs) diff --git a/wingetui/lang/translated_percentage.py b/wingetui/lang/translated_percentage.py new file mode 100644 index 0000000000..d7448aaae7 --- /dev/null +++ b/wingetui/lang/translated_percentage.py @@ -0,0 +1,7 @@ +# Autogenerated file, do not modify it!!! +# The following list includes ONLY non-full translated files. + +untranslatedPercentage = { + "en": "107.28%", + "ca": "42069%", +} \ No newline at end of file diff --git a/wingetui/languages.py b/wingetui/languages.py new file mode 100644 index 0000000000..aa10e3d99a --- /dev/null +++ b/wingetui/languages.py @@ -0,0 +1,13 @@ +from lang.lang_tools import languageReference + +lang = {} +englang = {} +languages = {} # will be auto-generated + +## auto-generate map of files +for key in languageReference.keys(): + if (key != "default"): + languages[key] = f"lang_{key}.json" + +debugLang = False + diff --git a/wingetui/mainWindow.py b/wingetui/mainWindow.py index 6a9b732fac..d72fb82688 100644 --- a/wingetui/mainWindow.py +++ b/wingetui/mainWindow.py @@ -6,6 +6,7 @@ import globals +from tools import _ from uiSections import * from storeEngine import * @@ -23,7 +24,7 @@ def __init__(self): super().__init__() self.isWinDark = isDark() self.callInMain.connect(lambda f: f()) - self.setWindowTitle("WingetUI: A Graphical User interface to manage Winget and Scoop packages") + self.setWindowTitle("WingetUI") self.setMinimumSize(700, 560) self.setObjectName("micawin") self.setWindowIcon(QIcon(realpath+"/resources/icon.png")) @@ -73,40 +74,22 @@ def loadWidgets(self) -> None: self.discover = DiscoverSoftwareSection() self.discover.setStyleSheet("QGroupBox{border-radius: 5px;}") globals.discover = self.discover - self.widgets[self.discover] = self.addTab(self.discover, "Discover packages") + self.widgets[self.discover] = self.addTab(self.discover, _("Discover packages")) self.updates = UpdateSoftwareSection() self.updates.setStyleSheet("QGroupBox{border-radius: 5px;}") globals.updates = self.updates - self.widgets[self.updates] = self.addTab(self.updates, "Software updates") + self.widgets[self.updates] = self.addTab(self.updates, _("Software updates")) self.uninstall = UninstallSoftwareSection() self.uninstall.setStyleSheet("QGroupBox{border-radius: 5px;}") globals.uninstall = self.uninstall - self.widgets[self.uninstall] = self.addTab(self.uninstall, "Installed packages") + self.widgets[self.uninstall] = self.addTab(self.uninstall, _("Installed packages")) self.settingsSection = SettingsSection() - self.widgets[self.settingsSection] = self.addTab(self.settingsSection, "WingetUI Settings", addToMenu=True, actionIcon="settings") + self.widgets[self.settingsSection] = self.addTab(self.settingsSection, _("WingetUI Settings"), addToMenu=True, actionIcon="settings") self.aboutSection = AboutSection() - self.widgets[self.aboutSection] = self.addTab(self.aboutSection, "About WingetUI", addToMenu=True, actionIcon="info") + self.widgets[self.aboutSection] = self.addTab(self.aboutSection, _("About WingetUI"), addToMenu=True, actionIcon="info") self.aboutSection = DebuggingSection() - self.widgets[self.aboutSection] = self.addTab(self.aboutSection, "WingetUI log", addToMenu=True, actionIcon="buggy") + self.widgets[self.aboutSection] = self.addTab(self.aboutSection, _("WingetUI log"), addToMenu=True, actionIcon="buggy") - class Text(QPlainTextEdit): - def __init__(self): - super().__init__() - self.setPlainText("click to show log") - - def mousePressEvent(self, e: QMouseEvent) -> None: - self.setPlainText(buffer.getvalue()) - self.appendPlainText(errbuffer.getvalue()) - return super().mousePressEvent(e) - - def showEvent(self, e: QShowEvent) -> None: - self.setPlainText(buffer.getvalue()) - self.appendPlainText(errbuffer.getvalue()) - return super().showEvent(e) - - p = Text() - p.setReadOnly(True) - #self.addTab(p, "Debugging log", addToMenu=True) self.buttonLayout.addWidget(QWidget(), stretch=1) vl = QVBoxLayout() hl = QHBoxLayout() @@ -198,11 +181,11 @@ def addTab(self, widget: QWidget, label: str, addToMenu: bool = False, actionIco def warnAboutAdmin(self): self.err = ErrorMessage(self) errorData = { - "titlebarTitle": f"WingetUI", - "mainTitle": f"Administrator privileges", - "mainText": f"It looks like you ran WingetUI as administrator, which is not recommended. You can still use the program, but we hightly recommend not running WingetUI with administrator privileges. Click on \"Show details\" to see why.", - "buttonTitle": "Ok", - "errorDetails": "There are two main reasons to not run WingetUI as administrator:\n The first one is that the scoop package manager might cause problems with some commands when ran with administrator rights.\n The second one is that running WingetUI as administrator means that any package that you download will be ran as administrator (and this is not safe).\n Remeber that if you need to install a specific package as administrator, you can always right-click tyhe item -> Install/Update/Uninstall as administrator.", + "titlebarTitle": _("WingetUI"), + "mainTitle": _("Administrator privileges"), + "mainText": _("It looks like you ran WingetUI as administrator, which is not recommended. You can still use the program, but we hightly recommend not running WingetUI with administrator privileges. Click on \"Show details\" to see why."), + "buttonTitle": _("Ok"), + "errorDetails": _("There are two main reasons to not run WingetUI as administrator:\n The first one is that the scoop package manager might cause problems with some commands when ran with administrator rights.\n The second one is that running WingetUI as administrator means that any package that you download will be ran as administrator (and this is not safe).\n Remeber that if you need to install a specific package as administrator, you can always right-click tyhe item -> Install/Update/Uninstall as administrator."), "icon": QIcon(getMedia("infocolor")), } self.err.showErrorMessage(errorData, showNotification=False) @@ -220,11 +203,11 @@ def closeEvent(self, event): event.accept() if(globals.pending_programs != []): if getSettings("DisablesystemTray"): - if(tools.MessageBox.question(self, "Warning", "There is an installation in progress. If you close WingetUI, the installation may fail and have unexpected results. Do you still want to close the application?", tools.MessageBox.No | tools.MessageBox.Yes, tools.MessageBox.No) == tools.MessageBox.Yes): + if(tools.MessageBox.question(self, _("Warning"), _("There is an installation in progress. If you close WingetUI, the installation may fail and have unexpected results. Do you still want to quit WingetUI?"), tools.MessageBox.No | tools.MessageBox.Yes, tools.MessageBox.No) == tools.MessageBox.Yes): if globals.updatesAvailable: self.hide() globals.canUpdate = True - globals.trayIcon.showMessage("Updating WingetUI", "WingetUI is being updated. When finished, WingetUI will restart itself", QIcon(getMedia("notif_info"))) + globals.trayIcon.showMessage(_("Updating WingetUI"), _("WingetUI is being updated. When finished, WingetUI will restart itself"), QIcon(getMedia("notif_info"))) event.ignore() else: event.accept() @@ -236,7 +219,7 @@ def closeEvent(self, event): if globals.updatesAvailable: self.hide() globals.canUpdate = True - globals.trayIcon.showMessage("Updating WingetUI", "WingetUI is being updated. When finished, WingetUI will restart itself", QIcon(getMedia("notif_info"))) + globals.trayIcon.showMessage(_("Updating WingetUI"), _("WingetUI is being updated. When finished, WingetUI will restart itself"), QIcon(getMedia("notif_info"))) event.ignore() else: self.hide() @@ -246,7 +229,7 @@ def closeEvent(self, event): if globals.updatesAvailable: self.hide() globals.canUpdate = True - globals.trayIcon.showMessage("Updating WingetUI", "WingetUI is being updated. When finished, WingetUI will restart itself", QIcon(getMedia("notif_info"))) + globals.trayIcon.showMessage(_("Updating WingetUI"), _("WingetUI is being updated. When finished, WingetUI will restart itself"), QIcon(getMedia("notif_info"))) event.ignore() else: if getSettings("DisablesystemTray"): diff --git a/wingetui/tools.py b/wingetui/tools.py index 711df70565..0a1d21bba7 100644 --- a/wingetui/tools.py +++ b/wingetui/tools.py @@ -10,6 +10,7 @@ from win32mica import ApplyMica, MICAMODE from urllib.request import urlopen from versions import * +from languages import * import globals @@ -19,6 +20,10 @@ old_stderr = sys.stderr buffer = io.StringIO() errbuffer = io.StringIO() +settingsCache = {} +installersWidget = None +updatesAvailable = False + if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): sys.stdout = buffer = io.StringIO() sys.stderr = errbuffer = io.StringIO() @@ -28,6 +33,12 @@ else: realpath = '/'.join(sys.argv[0].replace("\\", "/").split("/")[:-1]) +if not os.path.isdir(os.path.join(os.path.expanduser("~"), ".wingetui")): + try: + os.makedirs(os.path.join(os.path.expanduser("~"), ".wingetui")) + except: + pass + def cprint(*args) -> None: print(*args, file=old_stdout) @@ -38,15 +49,24 @@ def report(exception) -> None: # Exception reporter cprint("🔴 "+line) print(f"🔴 Note this traceback was caught by reporter and has been added to the log ({exception})") -settingsCache = {} -installersWidget = None -updatesAvailable = False +def _(s): # Translate function + global lang + return "-"*len(s) + try: + t = lang[s] + return (t+"✅[Found]✅" if debugLang else t) if t else f"{s}⚠️[UntranslatedString]⚠️" if debugLang else eng_(s) + except KeyError: + if debugLang: print(s) + return f"{eng_(s)}🔴[MissingString]🔴" if debugLang else eng_(s) -if not os.path.isdir(os.path.join(os.path.expanduser("~"), ".wingetui")): +def eng_(s): # English translate function try: - os.makedirs(os.path.join(os.path.expanduser("~"), ".wingetui")) - except: - pass + t = englang[s] + return t if t else s + except KeyError: + if debugLang: + print(s) + return s def getSettings(s: str, cache = True): global settingsCache From ded643d6ed58d600ee68d4aad4220cf17df0a86f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=AD=20Climent?= Date: Fri, 7 Oct 2022 22:15:09 +0200 Subject: [PATCH 2/7] add localisation functions to the discover software section --- wingetui/uiSections.py | 45 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/wingetui/uiSections.py b/wingetui/uiSections.py index f99e7be1b0..d032535d90 100644 --- a/wingetui/uiSections.py +++ b/wingetui/uiSections.py @@ -8,6 +8,7 @@ from storeEngine import * import globals +from tools import _ class DiscoverSoftwareSection(QWidget): @@ -49,7 +50,7 @@ def __init__(self, parent = None): hLayout = QHBoxLayout() hLayout.setContentsMargins(25, 0, 25, 0) - self.forceCheckBox = QCheckBox("Instant search") + self.forceCheckBox = QCheckBox(_("Instant search")) self.forceCheckBox.setFixedHeight(30) self.forceCheckBox.setLayoutDirection(Qt.RightToLeft) self.forceCheckBox.setFixedWidth(98) @@ -59,7 +60,7 @@ def __init__(self, parent = None): self.forceCheckBox.clicked.connect(lambda v: setSettings("DisableInstantSearchOnInstall", bool(not v))) self.query = CustomLineEdit() - self.query.setPlaceholderText(" Search something on Winget or Scoop") + self.query.setPlaceholderText(" "+_("Search for something on Winget or Scoop")) self.query.returnPressed.connect(self.filter) self.query.textChanged.connect(lambda: self.filter() if self.forceCheckBox.isChecked() else print()) self.query.setFixedHeight(30) @@ -84,7 +85,7 @@ def __init__(self, parent = None): hLayout.addWidget(img) v = QVBoxLayout() - self.discoverLabel = QLabel("Discover packages") + self.discoverLabel = QLabel(_("Discover packages")) self.discoverLabel.setStyleSheet(f"font-size: 30pt;font-family: \"Segoe UI Variable Display\";font-weight: bold;") v.addWidget(self.discoverLabel) @@ -120,19 +121,19 @@ def showMenu(pos: QPoint): contextMenu.setParent(self) contextMenu.setStyleSheet("* {background: red;color: black}") ApplyMenuBlur(contextMenu.winId().__int__(), contextMenu) - inf = QAction("Show info") + inf = QAction(_("Show info")) inf.triggered.connect(lambda: self.openInfo(self.packageList.currentItem().text(0), self.packageList.currentItem().text(1), self.packageList.currentItem().text(3), packageItem=self.packageList.currentItem())) inf.setIcon(QIcon(getMedia("info"))) - ins1 = QAction("Install") + ins1 = QAction(_("Install")) ins1.setIcon(QIcon(getMedia("newversion"))) ins1.triggered.connect(lambda: self.fastinstall(self.packageList.currentItem().text(0), self.packageList.currentItem().text(1), self.packageList.currentItem().text(3), packageItem=self.packageList.currentItem())) - ins2 = QAction("Run as administrator") + ins2 = QAction(_("Run as administrator")) ins2.setIcon(QIcon(getMedia("runasadmin"))) ins2.triggered.connect(lambda: self.fastinstall(self.packageList.currentItem().text(0), self.packageList.currentItem().text(1), self.packageList.currentItem().text(3), admin=True, packageItem=self.packageList.currentItem())) - ins3 = QAction("Skip hash check") + ins3 = QAction(_("Skip hash check")) ins3.setIcon(QIcon(getMedia("checksum"))) ins3.triggered.connect(lambda: self.fastinstall(self.packageList.currentItem().text(0), self.packageList.currentItem().text(1), self.packageList.currentItem().text(3), skiphash=True, packageItem=self.packageList.currentItem())) - ins4 = QAction("Interactive installation") + ins4 = QAction(_("Interactive installation")) ins4.setIcon(QIcon(getMedia("interactive"))) ins4.triggered.connect(lambda: self.fastinstall(self.packageList.currentItem().text(0), self.packageList.currentItem().text(1), self.packageList.currentItem().text(3), interactive=True, packageItem=self.packageList.currentItem())) contextMenu.addAction(ins1) @@ -202,11 +203,11 @@ def showMenu(pos: QPoint): tooltips = { - self.upgradeSelected: "Install selected package", - inf: "Show package info", - ins2: "Run the installer with administrator privileges", - ins3: "Skip the hash check", - ins4: "Interactive installation", + self.upgradeSelected: _("Install selected package"), + inf: _("Show package info"), + ins2: _("Run the installer with administrator privileges"), + ins3: _("Skip the hash check"), + ins4: _("Interactive installation"), } for action in [self.upgradeSelected, inf, ins2, ins3, ins4]: @@ -216,7 +217,7 @@ def showMenu(pos: QPoint): self.toolbar.addSeparator() - self.importAction = QAction("Import packages from a file", self.toolbar) + self.importAction = QAction(_("Import packages from a file"), self.toolbar) self.importAction.setIcon(QIcon(getMedia("import"))) self.importAction.triggered.connect(lambda: self.importPackages()) self.toolbar.addAction(self.importAction) @@ -225,7 +226,7 @@ def showMenu(pos: QPoint): self.toolbar.addWidget(TenPxSpacer()) self.toolbar.addWidget(TenPxSpacer()) - self.countLabel = QLabel("Searching for packages...") + self.countLabel = QLabel(_("Searching for packages...")) self.packageList.label.setText(self.countLabel.text()) self.countLabel.setObjectName("greyLabel") v.addWidget(self.countLabel) @@ -303,7 +304,7 @@ def showMenu(pos: QPoint): def importPackages(self): try: packageList = [] - file = QFileDialog.getOpenFileName(self, "Select package file", filter="JSON (*.json)")[0] + file = QFileDialog.getOpenFileName(self, _("Select package file"), filter="JSON (*.json)")[0] if file != "": f = open(file, "r") contents = json.load(f) @@ -331,7 +332,7 @@ def importPackages(self): def finishLoadingIfNeeded(self, store: str) -> None: if(store == "winget"): - self.countLabel.setText("Found packages: "+str(self.packageList.topLevelItemCount())+", not finished yet...") + self.countLabel.setText(_("Found packages: {0}, not finished yet...").format(str(self.packageList.topLevelItemCount()))) if self.packageList.topLevelItemCount() == 0: self.packageList.label.setText(self.countLabel.text()) else: @@ -342,7 +343,7 @@ def finishLoadingIfNeeded(self, store: str) -> None: self.searchButton.setEnabled(True) self.query.setEnabled(True) elif(store == "scoop"): - self.countLabel.setText("Found packages: "+str(self.packageList.topLevelItemCount())+", not finished yet...") + self.countLabel.setText(_("Found packages: {0}, not finished yet...").format(str(self.packageList.topLevelItemCount()))) if self.packageList.topLevelItemCount() == 0: self.packageList.label.setText(self.countLabel.text()) else: @@ -355,7 +356,7 @@ def finishLoadingIfNeeded(self, store: str) -> None: if(self.wingetLoaded and self.scoopLoaded): self.filter() self.loadingProgressBar.hide() - self.countLabel.setText("Found packages: "+str(self.packageList.topLevelItemCount())) + self.countLabel.setText(_("Found packages: {0}").format(str(self.packageList.topLevelItemCount()))) self.packageList.label.setText("") print("🟢 Total packages: "+str(self.packageList.topLevelItemCount())) @@ -390,9 +391,9 @@ def filter(self) -> None: if found == 0: if self.packageList.label.text() == "": self.packageList.label.show() - self.packageList.label.setText("No packages found matching the input criteria") + self.packageList.label.setText(_("No packages found matching the input criteria")) else: - if self.packageList.label.text() == "No packages found matching the input criteria": + if self.packageList.label.text() == _("No packages found matching the input criteria"): self.packageList.label.hide() self.packageList.label.setText("") self.packageList.scrollToItem(self.packageList.currentItem()) @@ -422,7 +423,7 @@ def reload(self) -> None: self.query.setEnabled(False) self.packageList.clear() self.query.setText("") - self.countLabel.setText("Searching for packages...") + self.countLabel.setText(_("Searching for packages...")) self.packageList.label.setText(self.countLabel.text()) if not getSettings("DisableWinget"): Thread(target=wingetHelpers.searchForPackage, args=(self.addProgram, self.finishLoading), daemon=True).start() From b8c6de30d920ad68d726ddbca0c6e2a491416996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=AD=20Climent?= Date: Fri, 7 Oct 2022 22:29:57 +0200 Subject: [PATCH 3/7] Localize updates section --- wingetui/uiSections.py | 77 +++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/wingetui/uiSections.py b/wingetui/uiSections.py index d032535d90..71d33e1d2e 100644 --- a/wingetui/uiSections.py +++ b/wingetui/uiSections.py @@ -102,7 +102,7 @@ def __init__(self, parent = None): self.packageListScrollBar.setOrientation(Qt.Vertical) self.packageList: TreeWidget = TreeWidget("a") - self.packageList.setHeaderLabels(["Package name", "Package ID", "Version", "Origin"]) + self.packageList.setHeaderLabels([_("Package name"), _("Package ID"), _("Version"), _("Origin")]) self.packageList.setColumnCount(4) self.packageList.sortByColumn(0, Qt.AscendingOrder) self.packageList.setSortingEnabled(True) @@ -492,7 +492,7 @@ def __init__(self, parent = None): hLayout.setContentsMargins(25, 0, 25, 0) self.query = CustomLineEdit() - self.query.setPlaceholderText(" Search available updates") + self.query.setPlaceholderText(" "+_("Search for available updates")) self.query.returnPressed.connect(self.filter) self.query.textChanged.connect(lambda: self.filter() if self.forceCheckBox.isChecked() else print()) self.query.setFixedHeight(30) @@ -511,7 +511,7 @@ def __init__(self, parent = None): sct = QShortcut(QKeySequence("Esc"), self) sct.activated.connect(self.query.clear) - self.forceCheckBox = QCheckBox("Instant search") + self.forceCheckBox = QCheckBox(_("Instant search")) self.forceCheckBox.setFixedHeight(30) self.forceCheckBox.setLayoutDirection(Qt.RightToLeft) self.forceCheckBox.setFixedWidth(98) @@ -526,7 +526,7 @@ def __init__(self, parent = None): hLayout.addWidget(self.img) v = QVBoxLayout() - self.discoverLabel = QLabel("Software updates") + self.discoverLabel = QLabel(_("Software updates")) self.discoverLabel.setStyleSheet(f"font-size: 30pt;font-family: \"Segoe UI Variable Display\";font-weight: bold;") v.addWidget(self.discoverLabel) @@ -542,7 +542,7 @@ def __init__(self, parent = None): self.packageList = TreeWidget("ª") self.packageList.setIconSize(QSize(24, 24)) self.packageList.setColumnCount(6) - self.packageList.setHeaderLabels(["", "Package name", "Package ID", "Installed Version", "New Version", "Installation source"]) + self.packageList.setHeaderLabels(["", _("Package name"), _("Package ID"), _("Installed Version"), _("New Version"), _("Installation source")]) self.packageList.setColumnWidth(0, 50) self.packageList.setColumnWidth(1, 350) self.packageList.setColumnWidth(2, 200) @@ -565,22 +565,22 @@ def showMenu(pos: QPoint): contextMenu.setParent(self) contextMenu.setStyleSheet("* {background: red;color: black}") ApplyMenuBlur(contextMenu.winId().__int__(), contextMenu) - inf = QAction("Show info") + inf = QAction(_("Show info")) inf.triggered.connect(lambda: self.openInfo(self.packageList.currentItem().text(1), self.packageList.currentItem().text(2), self.packageList.currentItem().text(5).lower(), self.packageList.currentItem())) inf.setIcon(QIcon(getMedia("info"))) - ins1 = QAction("Update") + ins1 = QAction(_("Update")) ins1.setIcon(QIcon(getMedia("newversion"))) ins1.triggered.connect(lambda: self.update(self.packageList.currentItem().text(1), self.packageList.currentItem().text(2), self.packageList.currentItem().text(5).lower(), packageItem=self.packageList.currentItem())) - ins2 = QAction("Run as administrator") + ins2 = QAction(_("Run as administrator")) ins2.setIcon(QIcon(getMedia("runasadmin"))) ins2.triggered.connect(lambda: self.update(self.packageList.currentItem().text(1), self.packageList.currentItem().text(2), self.packageList.currentItem().text(5).lower(), packageItem=self.packageList.currentItem(), admin=True)) - ins3 = QAction("Skip hash check") + ins3 = QAction(_("Skip hash check")) ins3.setIcon(QIcon(getMedia("checksum"))) ins3.triggered.connect(lambda: self.update(self.packageList.currentItem().text(1), self.packageList.currentItem().text(2), self.packageList.currentItem().text(5).lower(), packageItem=self.packageList.currentItem(), skiphash=True)) - ins4 = QAction("Interactive update") + ins4 = QAction(_("Interactive update")) ins4.setIcon(QIcon(getMedia("interactive"))) ins4.triggered.connect(lambda: self.update(self.packageList.currentItem().text(1), self.packageList.currentItem().text(2), self.packageList.currentItem().text(5).lower(), packageItem=self.packageList.currentItem(), interactive=True)) - ins5 = QAction("Uninstall package") + ins5 = QAction(_("Uninstall package")) ins5.setIcon(QIcon(getMedia("menu_uninstall"))) ins5.triggered.connect(lambda: globals.uninstall.uninstall(self.packageList.currentItem().text(1), self.packageList.currentItem().text(2), self.packageList.currentItem().text(5), packageItem=self.packageList.currentItem())) contextMenu.addAction(ins1) @@ -590,7 +590,7 @@ def showMenu(pos: QPoint): contextMenu.addAction(ins4) contextMenu.addAction(ins3) contextMenu.addSeparator() - ins6 = QAction("Ignore updates for this package") + ins6 = QAction(_("Ignore updates for this package")) ins6.setIcon(QIcon(getMedia("blacklist"))) ins6.triggered.connect(lambda: (setSettingsValue("BlacklistedUpdates", getSettingsValue("BlacklistedUpdates")+self.packageList.currentItem().text(2)+","), self.packageList.currentItem().setHidden(True))) contextMenu.addAction(ins6) @@ -688,11 +688,11 @@ def setAllSelected(checked: bool) -> None: tooltips = { - self.upgradeSelected: "Install selected package", - inf: "Show package info", - ins2: "Run the installer with administrator privileges", - ins3: "Skip the hash check", - ins4: "Interactive installation", + self.upgradeSelected: _("Install selected package"), + inf: _("Show package info"), + ins2: _("Run the installer with administrator privileges"), + ins3: _("Skip the hash check"), + ins4: _("Interactive installation"), } for action in [self.upgradeSelected, inf, ins2, ins3, ins4]: @@ -703,10 +703,10 @@ def setAllSelected(checked: bool) -> None: self.toolbar.addSeparator() - self.upgradeAllAction = QAction(QIcon(getMedia("installall")), "Upgrade all", self.toolbar) + self.upgradeAllAction = QAction(QIcon(getMedia("installall")), _("Upgrade all"), self.toolbar) self.upgradeAllAction.triggered.connect(lambda: self.updateAll()) self.toolbar.addAction(self.upgradeAllAction) - self.upgradeSelectedAction = QAction(QIcon(getMedia("list")), "Upgrade selected", self.toolbar) + self.upgradeSelectedAction = QAction(QIcon(getMedia("list")), _("Upgrade selected"), self.toolbar) self.upgradeSelectedAction.triggered.connect(lambda: self.updateSelected()) self.toolbar.addAction(self.upgradeSelectedAction) @@ -720,19 +720,19 @@ def setAllSelected(checked: bool) -> None: self.selectNoneAction.triggered.connect(lambda: setAllSelected(False)) self.toolbar.addAction(self.selectNoneAction) self.toolbar.widgetForAction(self.selectNoneAction).setFixedSize(40, 45) - self.toolbar.widgetForAction(self.selectNoneAction).setToolTip("Select none") - self.toolbar.widgetForAction(self.selectAllAction).setToolTip("Select all") + self.toolbar.widgetForAction(self.selectNoneAction).setToolTip(_("Select none")) + self.toolbar.widgetForAction(self.selectAllAction).setToolTip(_("Select all")) self.toolbar.addSeparator() - self.selectAllAction = QAction(QIcon(getMedia("blacklist")), "Blacklist apps", self.toolbar) + self.selectAllAction = QAction(QIcon(getMedia("blacklist")), _("Blacklist apps"), self.toolbar) self.selectAllAction.triggered.connect(lambda: blacklistSelectedPackages()) self.toolbar.addAction(self.selectAllAction) - self.selectAllAction = QAction(QIcon(getMedia("undelete")), "Reset blacklist", self.toolbar) + self.selectAllAction = QAction(QIcon(getMedia("undelete")), _("Reset blacklist"), self.toolbar) self.selectAllAction.triggered.connect(lambda: (setSettingsValue("BlacklistedUpdates", ""), self.reload())) self.toolbar.addAction(self.selectAllAction) - self.showUnknownSection = QCheckBox("Show unknown versions") + self.showUnknownSection = QCheckBox(_("Show unknown versions")) self.showUnknownSection.setFixedHeight(30) self.showUnknownSection.setLayoutDirection(Qt.RightToLeft) self.showUnknownSection.setFixedWidth(190) @@ -765,7 +765,7 @@ def updatelist(selff = None): #h2Layout.addStretch() #h2Layout.addWidget(self.showUnknownVersions) - self.countLabel = QLabel("Checking for updates...") + self.countLabel = QLabel(_("Checking for updates...")) self.packageList.label.setText(self.countLabel.text()) self.countLabel.setObjectName("greyLabel") layout.addLayout(hLayout) @@ -842,20 +842,20 @@ def updatelist(selff = None): def finishLoadingIfNeeded(self, store: str) -> None: if(store == "winget"): - self.countLabel.setText("Available updates: "+str(self.packageList.topLevelItemCount())+", not finished yet...") + self.countLabel.setText(_("Available updates: {0}, not finished yet...").format(str(self.packageList.topLevelItemCount()))) if self.packageList.topLevelItemCount() == 0: self.packageList.label.setText(self.countLabel.text()) else: self.packageList.label.setText("") - globals.trayMenuUpdatesList.menuAction().setText(f"{self.packageList.topLevelItemCount()} updates found") + globals.trayMenuUpdatesList.menuAction().setText(_("Available updates: {0}, not finished yet...").format(str(self.packageList.topLevelItemCount()))) self.wingetLoaded = True self.reloadButton.setEnabled(True) self.filter() self.searchButton.setEnabled(True) self.query.setEnabled(True) elif(store == "scoop"): - self.countLabel.setText("Available updates: "+str(self.packageList.topLevelItemCount())+", not finished yet...") - globals.trayMenuUpdatesList.menuAction().setText(f"{self.packageList.topLevelItemCount()} updates found") + self.countLabel.setText(_("Available updates: {0}, not finished yet...").format(str(self.packageList.topLevelItemCount()))) + globals.trayMenuUpdatesList.menuAction().setText(_("Available updates: {0}, not finished yet...").format(str(self.packageList.topLevelItemCount()))) if self.packageList.topLevelItemCount() == 0: self.packageList.label.setText(self.countLabel.text()) else: @@ -868,6 +868,7 @@ def finishLoadingIfNeeded(self, store: str) -> None: if(self.wingetLoaded and self.scoopLoaded): self.loadingProgressBar.hide() self.loadingProgressBar.hide() + globals.trayMenuUpdatesList.menuAction().setText(_("Available updates: {0}").format(str(self.packageList.topLevelItemCount()))) count = 0 lastVisibleItem = None for i in range(self.packageList.topLevelItemCount()): @@ -877,9 +878,9 @@ def finishLoadingIfNeeded(self, store: str) -> None: self.packageList.label.setText(str(count)) if not getSettings("DisableUpdatesNotifications"): if count > 1: - notify("Updates found!", f"{count} apps can be updated") + notify(_("Updates found!"), _("{0} apps can be updated").format(count)) elif count == 1: - notify("Update found!", f"{lastVisibleItem.text(1)} can be updated") + notify(_("Update found!"), _("{0} can be updated").format(lastVisibleItem.text(1))) if count > 0: globals.trayIcon.setIcon(QIcon(getMedia("greenicon"))) else: @@ -948,9 +949,9 @@ def filter(self) -> None: if found == 0: if self.packageList.label.text() == "": self.packageList.label.show() - self.packageList.label.setText("No packages found matching the input criteria") + self.packageList.label.setText(_("No packages found matching the input criteria")) else: - if self.packageList.label.text() == "No packages found matching the input criteria": + if self.packageList.label.text() == _("No packages found matching the input criteria"): self.packageList.label.hide() self.packageList.label.setText("") self.packageList.scrollToItem(self.packageList.currentItem()) @@ -960,15 +961,15 @@ def updatePackageNumber(self, showQueried: bool = False, foundResults: int = 0): for item in self.packageList.findItems('', Qt.MatchContains, 1): if not item.isHidden(): self.availableUpdates += 1 - self.countLabel.setText(f"Available updates: {self.availableUpdates}") - globals.trayIcon.setToolTip("WingetUI" if self.availableUpdates == 0 else (f"WingetUI - {self.availableUpdates} update is available" if self.availableUpdates == 1 else f"WingetUI - {self.availableUpdates} updates are available") ) - globals.trayMenuUpdatesList.menuAction().setText(f"{self.availableUpdates} updates found") + self.countLabel.setText(_("Available updates: {0}").format(self.availableUpdates)) + globals.trayIcon.setToolTip("WingetUI" if self.availableUpdates == 0 else (_("WingetUI - 1 update is available") if self.availableUpdates == 1 else _("WingetUI - {0} updates are available").format(self.availableUpdates)) ) + globals.trayMenuUpdatesList.menuAction().setText(_("{0} updates found").format(self.availableUpdates)) if self.availableUpdates > 0: self.packageList.label.hide() self.packageList.label.setText("") self.img.setPixmap(QIcon(getMedia("alert_laptop")).pixmap(QSize(64, 64))) else: - self.packageList.label.setText("Hooray! No updates were found!") + self.packageList.label.setText(_("Hooray! No updates were found!")) self.packageList.label.show() self.img.setPixmap(QIcon(getMedia("checked_laptop")).pixmap(QSize(64, 64))) @@ -1030,7 +1031,7 @@ def reload(self) -> None: for action in globals.trayMenuUpdatesList.actions(): globals.trayMenuUpdatesList.removeAction(action) globals.trayMenuUpdatesList.addAction(globals.updatesHeader) - self.countLabel.setText("Checking for updates...") + self.countLabel.setText(_("Checking for updates...")) self.packageList.label.setText(self.countLabel.text()) self.blacklist = getSettingsValue("BlacklistedUpdates") if not getSettings("DisableWinget"): From 7d7bd675fcedb59011cc9868eda21642aaa05810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=AD=20Climent?= Date: Sat, 8 Oct 2022 12:53:54 +0200 Subject: [PATCH 4/7] Localise uninstall, about, settings and log page --- wingetui/uiSections.py | 212 +++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 114 deletions(-) diff --git a/wingetui/uiSections.py b/wingetui/uiSections.py index 71d33e1d2e..9e4ba75c48 100644 --- a/wingetui/uiSections.py +++ b/wingetui/uiSections.py @@ -1100,7 +1100,7 @@ def __init__(self, parent = None): #hLayout.setContentsMargins(25, 0, 25, 0) self.query = CustomLineEdit() - self.query.setPlaceholderText(" Search on your software") + self.query.setPlaceholderText(" "+_("Search on your software")) self.query.returnPressed.connect(self.filter) self.query.textChanged.connect(lambda: self.filter() if self.forceCheckBox.isChecked() else print()) self.query.setFixedHeight(30) @@ -1120,7 +1120,7 @@ def __init__(self, parent = None): sct.activated.connect(self.query.clear) - self.forceCheckBox = QCheckBox("Instant search") + self.forceCheckBox = QCheckBox(_("Instant search")) self.forceCheckBox.setFixedHeight(30) self.forceCheckBox.setLayoutDirection(Qt.RightToLeft) self.forceCheckBox.setFixedWidth(98) @@ -1136,7 +1136,7 @@ def __init__(self, parent = None): hLayout.addWidget(img) v = QVBoxLayout() - self.discoverLabel = QLabel("Installed packages") + self.discoverLabel = QLabel(_("Installed packages")) self.discoverLabel.setStyleSheet(f"font-size: 30pt;font-family: \"Segoe UI Variable Display\";font-weight: bold;") v.addWidget(self.discoverLabel) @@ -1155,9 +1155,9 @@ def __init__(self, parent = None): self.packageListScrollBar = CustomScrollBar() self.packageListScrollBar.setOrientation(Qt.Vertical) - self.packageList = TreeWidget("Found 0 Packages") + self.packageList = TreeWidget(_("Found 0 Packages")) self.packageList.setIconSize(QSize(24, 24)) - self.headers = ["", "Package name", "Package ID", "Installed Version", "Installation source"] # empty header added for checkbox + self.headers = ["", _("Package name"), _("Package ID"), _("Installed Version"), _("Installation source")] # empty header added for checkbox self.packageList.setColumnCount(len(self.headers)) self.packageList.setHeaderLabels(self.headers) self.packageList.setColumnWidth(0, 46) @@ -1197,19 +1197,19 @@ def showMenu(pos: QPoint): contextMenu.setParent(self) contextMenu.setStyleSheet("* {background: red;color: black}") ApplyMenuBlur(contextMenu.winId().__int__(), contextMenu) - ins1 = QAction("Uninstall") + ins1 = QAction(_("Uninstall")) ins1.setIcon(QIcon(getMedia("menu_uninstall"))) ins1.triggered.connect(lambda: self.uninstall(self.packageList.currentItem().text(1), self.packageList.currentItem().text(2), self.packageList.currentItem().text(4), packageItem=self.packageList.currentItem())) - ins2 = QAction("Run as administrator") + ins2 = QAction(_("Run as administrator")) ins2.setIcon(QIcon(getMedia("runasadmin"))) ins2.triggered.connect(lambda: self.uninstall(self.packageList.currentItem().text(1), self.packageList.currentItem().text(2), self.packageList.currentItem().text(4), packageItem=self.packageList.currentItem(), admin=True)) - ins3 = QAction("Remove permanent data") + ins3 = QAction(_("Remove permanent data")) ins3.setIcon(QIcon(getMedia("menu_close"))) ins3.triggered.connect(lambda: self.uninstall(self.packageList.currentItem().text(1), self.packageList.currentItem().text(2), self.packageList.currentItem().text(4), packageItem=self.packageList.currentItem(), removeData=True)) - ins5 = QAction("Interactive uninstall") + ins5 = QAction(_("Interactive uninstall")) ins5.setIcon(QIcon(getMedia("interactive"))) ins5.triggered.connect(lambda: self.uninstall(self.packageList.currentItem().text(1), self.packageList.currentItem().text(2), self.packageList.currentItem().text(4), interactive=True)) - ins4 = QAction("Show package info") + ins4 = QAction(_("Show package info")) ins4.setIcon(QIcon(getMedia("info"))) ins4.triggered.connect(lambda: self.openInfo(self.packageList.currentItem().text(1), self.packageList.currentItem().text(2), self.packageList.currentItem().text(4), self.packageList.currentItem())) contextMenu.addAction(ins1) @@ -1234,15 +1234,12 @@ def showMenu(pos: QPoint): self.loadingProgressBar.setFixedHeight(4) self.loadingProgressBar.setTextVisible(False) self.loadingProgressBar.setStyleSheet("margin: 0px; margin-left: 15px;margin-right: 15px;") - layout = QVBoxLayout() w = QWidget() w.setLayout(layout) w.setMaximumWidth(1300) - - self.bodyWidget = QWidget() l = QHBoxLayout() l.addWidget(ScrollWidget(self.packageList), stretch=0) @@ -1252,19 +1249,6 @@ def showMenu(pos: QPoint): l.addWidget(self.packageListScrollBar) self.bodyWidget.setLayout(l) - #self.selectAllPkgsCheckBox = QCheckBox("Select all") - #self.selectAllPkgsCheckBox.setFixedHeight(30) - #self.selectAllPkgsCheckBox.setLayoutDirection(Qt.RightToLeft) - ##self.selectAllPkgsCheckBox.setFixedWidth(140) - #self.selectAllPkgsCheckBox.setStyleSheet("margin-top: 0px;") - #self.selectAllPkgsCheckBox.setChecked(self.allPkgSelected) - #self.selectAllPkgsCheckBox.clicked.connect(lambda v: self.selectAllInstalled()) - - #self.exportSelectionButton = QPushButton("Export selection (beta)") - #self.exportSelectionButton.setFixedWidth(300) - #self.exportSelectionButton.setStyleSheet("margin-top: 0px;") - #self.exportSelectionButton.clicked.connect(self.exportSelection) - self.toolbar = QToolBar(self) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) @@ -1283,9 +1267,9 @@ def showMenu(pos: QPoint): tooltips = { - self.upgradeSelected: "Uninstall selected package", - ins2: "Uninstall with administrator privileges", - ins5: "Interactive uninstall", + self.upgradeSelected: _("Uninstall selected package"), + ins2: _("Uninstall with administrator privileges"), + ins5: _("Interactive uninstall"), } for action in [self.upgradeSelected, ins2, ins5]: @@ -1296,7 +1280,7 @@ def showMenu(pos: QPoint): self.toolbar.addSeparator() - self.upgradeSelectedAction = QAction(QIcon(getMedia("list")), "Uninstall selected packages", self.toolbar) + self.upgradeSelectedAction = QAction(QIcon(getMedia("list")), _("Uninstall selected packages"), self.toolbar) self.upgradeSelectedAction.triggered.connect(lambda: self.uninstallSelected()) self.toolbar.addAction(self.upgradeSelectedAction) @@ -1316,16 +1300,16 @@ def setAllSelected(checked: bool) -> None: self.selectNoneAction.triggered.connect(lambda: setAllSelected(False)) self.toolbar.addAction(self.selectNoneAction) self.toolbar.widgetForAction(self.selectNoneAction).setFixedSize(40, 45) - self.toolbar.widgetForAction(self.selectNoneAction).setToolTip("Select none") - self.toolbar.widgetForAction(self.selectAllAction).setToolTip("Select all") + self.toolbar.widgetForAction(self.selectNoneAction).setToolTip(_("Select none")) + self.toolbar.widgetForAction(self.selectAllAction).setToolTip(_("Select all")) self.toolbar.addSeparator() - self.exportAction = QAction(QIcon(getMedia("export")), "Export selected packages to a file", self.toolbar) + self.exportAction = QAction(QIcon(getMedia("export")), _("Export selected packages to a file"), self.toolbar) self.exportAction.triggered.connect(lambda: self.exportSelection()) self.toolbar.addAction(self.exportAction) - self.exportAction = QAction(QIcon(getMedia("export")), "Export all", self.toolbar) + self.exportAction = QAction(QIcon(getMedia("export")), _("Export all"), self.toolbar) self.exportAction.triggered.connect(lambda: self.exportSelection(all=True)) self.toolbar.addAction(self.exportAction) @@ -1337,7 +1321,7 @@ def setAllSelected(checked: bool) -> None: - self.countLabel = QLabel("Searching for installed packages...") + self.countLabel = QLabel(_("Searching for installed packages...")) self.packageList.label.setText(self.countLabel.text()) self.countLabel.setObjectName("greyLabel") layout.addLayout(hLayout) @@ -1426,9 +1410,9 @@ def uninstallSelected(self) -> None: pass conf = False if len(toUninstall) == 1: - conf = MessageBox.question(self, "Are you sure?", f"Do you really want to uninstall {toUninstall[0].text(1)}?", MessageBox.No | MessageBox.Yes, MessageBox.Yes) == MessageBox.Yes + conf = MessageBox.question(self, _("Are you sure?"), _("Do you really want to uninstall {0}?").format(toUninstall[0].text(1)), MessageBox.No | MessageBox.Yes, MessageBox.Yes) == MessageBox.Yes elif len(toUninstall) > 1: - conf = MessageBox.question(self, "Are you sure?", f"Do you really want to uninstall {len(toUninstall)} packages?", MessageBox.No | MessageBox.Yes, MessageBox.Yes) == MessageBox.Yes + conf = MessageBox.question(self, _("Are you sure?"), _("Do you really want to uninstall {0} packages?").format(len(toUninstall)), MessageBox.No | MessageBox.Yes, MessageBox.Yes) == MessageBox.Yes if conf: for program in toUninstall: self.uninstall(program.text(1), program.text(2), program.text(4), packageItem=program, avoidConfirm=True) @@ -1441,24 +1425,24 @@ def openInfo(self, title: str, id: str, store: str, packageItem: TreeWidgetItemW def finishLoadingIfNeeded(self, store: str) -> None: if(store == "winget"): - self.countLabel.setText("Found packages: "+str(self.packageList.topLevelItemCount())+", not finished yet...") + self.countLabel.setText(_("Found packages: {0}, not finished yet...").format(str(self.packageList.topLevelItemCount()))) if self.packageList.topLevelItemCount() == 0: self.packageList.label.setText(self.countLabel.text()) else: self.packageList.label.setText("") - globals.trayMenuInstalledList.setTitle(f"{self.packageList.topLevelItemCount()} packages found") + globals.trayMenuInstalledList.setTitle(_("{0} packages found").format(str(self.packageList.topLevelItemCount()))) self.wingetLoaded = True self.reloadButton.setEnabled(True) self.searchButton.setEnabled(True) self.filter() self.query.setEnabled(True) elif(store == "scoop"): - self.countLabel.setText("Found packages: "+str(self.packageList.topLevelItemCount())+", not finished yet...") + self.countLabel.setText(_("Found packages: {0}, not finished yet...").format(str(self.packageList.topLevelItemCount()))) if self.packageList.topLevelItemCount() == 0: self.packageList.label.setText(self.countLabel.text()) else: self.packageList.label.setText("") - globals.trayMenuInstalledList.setTitle(f"{self.packageList.topLevelItemCount()} packages found") + globals.trayMenuInstalledList.setTitle(_("{0} packages found").format(str(self.packageList.topLevelItemCount()))) self.scoopLoaded = True self.reloadButton.setEnabled(True) self.filter() @@ -1467,8 +1451,8 @@ def finishLoadingIfNeeded(self, store: str) -> None: if(self.wingetLoaded and self.scoopLoaded): self.filter() self.loadingProgressBar.hide() - globals.trayMenuInstalledList.setTitle(f"{self.packageList.topLevelItemCount()} packages found") - self.countLabel.setText("Found packages: "+str(self.packageList.topLevelItemCount())) + globals.trayMenuInstalledList.setTitle(_("{0} packages found").format(str(self.packageList.topLevelItemCount()))) + self.countLabel.setText(_("Found packages: {0}").format(str(self.packageList.topLevelItemCount()))) self.packageList.label.setText("") print("🟢 Total packages: "+str(self.packageList.topLevelItemCount())) @@ -1518,9 +1502,9 @@ def filter(self) -> None: if found == 0: if self.packageList.label.text() == "": self.packageList.label.show() - self.packageList.label.setText("No packages found matching the input criteria") + self.packageList.label.setText(_("No packages found matching the input criteria")) else: - if self.packageList.label.text() == "No packages found matching the input criteria": + if self.packageList.label.text() == _("No packages found matching the input criteria"): self.packageList.label.hide() self.packageList.label.setText("") self.packageList.scrollToItem(self.packageList.currentItem()) @@ -1533,7 +1517,7 @@ def uninstall(self, title: str, id: str, store: str, packageItem: TreeWidgetItem if avoidConfirm: answer = True else: - answer = MessageBox.question(self, "Are you sure?", f"Do you really want to uninstall {title}?", MessageBox.No | MessageBox.Yes, MessageBox.Yes) == MessageBox.Yes + answer = MessageBox.question(self, _("Are you sure?"), _("Do you really want to uninstall {0}?").format(title), MessageBox.No | MessageBox.Yes, MessageBox.Yes) == MessageBox.Yes if answer: print("🔵 Uninstalling", id) if not "scoop" in store.lower(): @@ -1550,7 +1534,7 @@ def reload(self) -> None: self.query.setEnabled(False) self.packageList.clear() self.query.setText("") - self.countLabel.setText("Searching for installed packages...") + self.countLabel.setText(_("Searching for installed packages...")) self.packageList.label.setText(self.countLabel.text()) if not getSettings("DisableWinget"): Thread(target=wingetHelpers.searchForInstalledPackage, args=(self.addProgram, self.finishLoading), daemon=True).start() @@ -1622,7 +1606,7 @@ def exportSelection(self, all: bool = False) -> None: "scoop": scoopExportSchema } - filename = QFileDialog.getSaveFileName(self, "Save File", "wingetui exported packages", filter='JSON (*.json)') + filename = QFileDialog.getSaveFileName(self, _("Save File"), _("wingetui exported packages"), filter='JSON (*.json)') if filename[0] != "": with open(filename[0], 'w') as f: f.write(json.dumps(overAllSchema, indent=4)) @@ -1661,7 +1645,7 @@ def __init__(self, parent = None): self.setWidget(self.widget) self.announcements = QAnnouncements() self.layout.addWidget(self.announcements) - title = QLabel("Component information") + title = QLabel(_("Component information")) title.setStyleSheet(f"font-size: 40px;font-family: \"Segoe UI Variable Display\";font-weight: bold;") self.layout.addWidget(title) @@ -1675,49 +1659,49 @@ def __init__(self, parent = None): table.setEnabled(False) table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) table.setShowGrid(False) - table.setHorizontalHeaderLabels(["Status", "Version"]) + table.setHorizontalHeaderLabels([_("Status"), _("Version")]) table.setColumnWidth(1, 500) table.verticalHeader().setFixedWidth(100) table.setVerticalHeaderLabels(["Winget", " Scoop", " GSudo"]) - table.setItem(0, 0, QTableWidgetItem(str("Found" if globals.componentStatus["wingetFound"] else "Not found"))) + table.setItem(0, 0, QTableWidgetItem(_("Found") if globals.componentStatus["wingetFound"] else _("Not found"))) table.setItem(0, 1, QTableWidgetItem(str(globals.componentStatus["wingetVersion"]))) - table.setItem(1, 0, QTableWidgetItem(str("Found" if globals.componentStatus["scoopFound"] else "Not found"))) + table.setItem(1, 0, QTableWidgetItem(_("Found") if globals.componentStatus["scoopFound"] else _("Not found"))) table.setItem(1, 1, QTableWidgetItem(str(globals.componentStatus["scoopVersion"]))) - table.setItem(2, 0, QTableWidgetItem(str("Found" if globals.componentStatus["sudoFound"] else "Not found"))) + table.setItem(2, 0, QTableWidgetItem(_("Found") if globals.componentStatus["sudoFound"] else _("Not found"))) table.setItem(2, 1, QTableWidgetItem(str(globals.componentStatus["sudoVersion"]))) table.setCornerWidget(QLabel("")) table.setCornerButtonEnabled(True) table.cornerWidget().setStyleSheet("background: transparent;") self.layout.addWidget(table) - title = QLabel("About WingetUI "+str(versionName)+"") + title = QLabel(_("About WingetUI version {0}").format(versionName)) title.setStyleSheet(f"font-size: 40px;font-family: \"Segoe UI Variable Display\";font-weight: bold;") self.layout.addWidget(title) self.layout.addWidget(QLabel()) - description = QLabel("The main goal of this project is to give a GUI Store to the most common CLI Package Managers for windows, such as Winget and Scoop.\nThis project has no connection with the winget-cli official project, and it's totally unofficial.") + description = QLabel(_("The main goal of this project is to give a GUI Store to the most common CLI Package Managers for windows, such as Winget and Scoop.")+"\n"+_("This project has no connection with the winget-cli official project, and it's totally unofficial.")) self.layout.addWidget(description) self.layout.addSpacing(5) - self.layout.addWidget(QLinkLabel(f"Project homepage: https://github.com/martinet101/WinGetUI")) + self.layout.addWidget(QLinkLabel(f"{_('Project homepage')}: https://github.com/martinet101/WingetUI")) self.layout.addSpacing(30) - self.layout.addWidget(QLinkLabel("Licenses:", "font-size: 22pt;font-family: \"Segoe UI Variable Display\";font-weight: bold;")) + self.layout.addWidget(QLinkLabel(f"{_('Licenses')}:", "font-size: 22pt;font-family: \"Segoe UI Variable Display\";font-weight: bold;")) self.layout.addWidget(QLabel()) self.layout.addWidget(QLinkLabel(f"WingetUI:    LGPL v2.1:           https://github.com/martinet101/WinGetUI/blob/main/LICENSE")) self.layout.addWidget(QLabel()) self.layout.addWidget(QLinkLabel(f"PySide6:      LGPLv3:               https://www.gnu.org/licenses/lgpl-3.0.html")) - self.layout.addWidget(QLinkLabel(f"Python3:     PSF License:       https://docs.python.org/3/license.html#psf-license")) + self.layout.addWidget(QLinkLabel(f"Python3:     {_('PSF License')}:       https://docs.python.org/3/license.html#psf-license")) self.layout.addWidget(QLinkLabel()) - self.layout.addWidget(QLinkLabel(f"Winget:       MIT License:       https://github.com/microsoft/winget-cli/blob/master/LICENSE")) + self.layout.addWidget(QLinkLabel(f"Winget:       {_('MIT License')}:       https://github.com/microsoft/winget-cli/blob/master/LICENSE")) self.layout.addWidget(QLinkLabel(f"Scoop:         Unlicense:           https://github.com/lukesampson/scoop/blob/master/LICENSE")) - self.layout.addWidget(QLinkLabel(f"GSudo:        MIT License:       https://github.com/gerardog/gsudo/blob/master/LICENSE.txt")) + self.layout.addWidget(QLinkLabel(f"GSudo:        {_('MIT License')}:       https://github.com/gerardog/gsudo/blob/master/LICENSE.txt")) self.layout.addWidget(QLinkLabel()) - self.layout.addWidget(QLinkLabel(f"Icons:          By Icons8:           https://icons8.com")) + self.layout.addWidget(QLinkLabel(f"{_('Icons')}:          {_('By Icons8')}:           https://icons8.com")) self.layout.addWidget(QLinkLabel()) self.layout.addWidget(QLinkLabel()) - button = QPushButton("About Qt") + button = QPushButton(_("About Qt6")) button.setFixedWidth(710) button.setFixedHeight(25) - button.clicked.connect(lambda: MessageBox.aboutQt(self, "WingetUI: About Qt")) + button.clicked.connect(lambda: MessageBox.aboutQt(self, _("WingetUI - About Qt6"))) self.layout.addWidget(button) self.layout.addWidget(QLinkLabel()) self.layout.addStretch() @@ -1749,26 +1733,26 @@ def __init__(self, parent = None): self.announcements = QAnnouncements() self.announcements.setMinimumWidth(800) self.layout.addWidget(self.announcements) - title = QLabel("General Settings") + title = QLabel(_("General Settings")) title.setStyleSheet(f"font-size: 40px;font-family: \"Segoe UI Variable Display\";font-weight: bold;") self.layout.addWidget(title) - subtitle = QLabel("General preferences") + subtitle = QLabel(_("General preferences")) subtitle.setStyleSheet(f"font-size: 25px;font-family: \"Segoe UI Variable Display\";font-weight: bold;") self.layout.addWidget(subtitle) self.layout.addWidget(QLabel()) - themeTextLabel = QLabel("Application theme:") + themeTextLabel = QLabel(_("Application theme:")) themes = { - "Light": "light", - "Dark": "dark", - "Follow system color scheme": "auto" + _("Light"): "light", + _("Dark"): "dark", + _("Follow system color scheme"): "auto" } invertedThemes = { - "light" : "Light", - "dark" : "Dark", - "auto" : "Follow system color scheme" + "light" : _("Light"), + "dark" : _("Dark"), + "auto" : _("Follow system color scheme") } themeText = CustomComboBox() @@ -1778,7 +1762,7 @@ def __init__(self, parent = None): try: themeText.setCurrentText(invertedThemes[currentValue]) except KeyError: - themeText.setCurrentText("Follow system color scheme") + themeText.setCurrentText(_("Follow system color scheme")) except Exception as e: report(e) @@ -1794,70 +1778,70 @@ def __init__(self, parent = None): self.layout.addLayout(hl) - updateCheckBox = QCheckBox("Update WingetUI automatically") + updateCheckBox = QCheckBox(_("Update WingetUI automatically")) updateCheckBox.setChecked(not getSettings("DisableAutoUpdateWingetUI")) updateCheckBox.clicked.connect(lambda v: setSettings("DisableAutoUpdateWingetUI", not bool(v))) self.layout.addWidget(updateCheckBox) - changeDefaultInstallAction = QCheckBox("Directly install when double-clicking an item on the Discover Software tab (instead of showing the package info)") + changeDefaultInstallAction = QCheckBox(_("Directly install when double-clicking an item on the Discover Software tab (instead of showing the package info)")) changeDefaultInstallAction.setChecked(getSettings("InstallOnDoubleClick")) changeDefaultInstallAction.clicked.connect(lambda v: setSettings("InstallOnDoubleClick", bool(v))) self.layout.addWidget(changeDefaultInstallAction) - changeDefaultUpdateAction = QCheckBox("Show info about the package on the Updates tab") + changeDefaultUpdateAction = QCheckBox(_("Show info about the package on the Updates tab")) changeDefaultUpdateAction.setChecked(not getSettings("DoNotUpdateOnDoubleClick")) changeDefaultUpdateAction.clicked.connect(lambda v: setSettings("DoNotUpdateOnDoubleClick", bool(not v))) self.layout.addWidget(changeDefaultUpdateAction) - dontUseBuiltInGsudo = QCheckBox("Use installed GSudo instead of the bundled one (requires app restart)") + dontUseBuiltInGsudo = QCheckBox(_("Use installed GSudo instead of the bundled one (requires app restart)")) dontUseBuiltInGsudo.setChecked(getSettings("UseUserGSudo")) dontUseBuiltInGsudo.clicked.connect(lambda v: setSettings("UseUserGSudo", bool(v))) self.layout.addWidget(dontUseBuiltInGsudo) self.layout.addWidget(QLabel()) - subtitle = QLabel("Startup options") + subtitle = QLabel(_("Startup options")) subtitle.setStyleSheet(f"font-size: 25px;font-family: \"Segoe UI Variable Display\";font-weight: bold;") self.layout.addWidget(subtitle) - doCloseWingetUI = QCheckBox("Close WingetUI to the notification area") + doCloseWingetUI = QCheckBox(_("Close WingetUI to the notification area")) doCloseWingetUI.setChecked(not getSettings("DisablesystemTray")) doCloseWingetUI.clicked.connect(lambda v: setSettings("DisablesystemTray", not bool(v))) self.layout.addWidget(doCloseWingetUI) - doCloseWingetUI = QCheckBox("Autostart wingetUI in the notifications area") + doCloseWingetUI = QCheckBox(_("Autostart wingetUI in the notifications area")) doCloseWingetUI.setChecked(not getSettings("DisableAutostart")) doCloseWingetUI.clicked.connect(lambda v: setSettings("DisableAutostart", not bool(v))) self.layout.addWidget(doCloseWingetUI) - disableUpdateIndexes = QCheckBox("Do not update package indexes on launch") + disableUpdateIndexes = QCheckBox(_("Do not update package indexes on launch")) disableUpdateIndexes.setChecked(getSettings("DisableUpdateIndexes")) disableUpdateIndexes.clicked.connect(lambda v: setSettings("DisableUpdateIndexes", bool(v))) self.layout.addWidget(disableUpdateIndexes) - enableScoopCleanup = QCheckBox("Enable scoop cleanup on launch") + enableScoopCleanup = QCheckBox(_("Enable scoop cleanup on launch")) enableScoopCleanup.setChecked(getSettings("EnableScoopCleanup")) enableScoopCleanup.clicked.connect(lambda v: setSettings("EnableScoopCleanup", bool(v))) self.layout.addWidget(enableScoopCleanup) self.layout.addWidget(QLabel()) - subtitle = QLabel("Notification tray options") + subtitle = QLabel(_("Notification tray options")) subtitle.setStyleSheet(f"font-size: 25px;font-family: \"Segoe UI Variable Display\";font-weight: bold;") self.layout.addWidget(subtitle) - checkForUpdates = QCheckBox("Check for updates periodically") + checkForUpdates = QCheckBox(_("Check for updates periodically")) checkForUpdates.setChecked(not getSettings("DisableAutoCheckforUpdates")) checkForUpdates.clicked.connect(lambda v: setSettings("DisableAutoCheckforUpdates", not bool(v))) self.layout.addWidget(checkForUpdates) - updatesFrequencyText = QLabel("Check for updates every:") + updatesFrequencyText = QLabel(_("Check for updates every:")) times = { - "30 minutes": "1800", - "1 hour": "3600", - "2 hours": "7200", - "4 hours": "14400", - "8 hours": "28800", + _("30 minutes"): "1800", + _("1 hour"): "3600", + _("2 hours"): "7200", + _("4 hours"): "14400", + _("8 hours"): "28800", } invertedTimes = { - "1800" : "30 minutes", - "3600" : "1 hour", - "7200" : "2 hours", - "14400": "4 hours", - "28800": "8 hours", + "1800" : _("30 minutes"), + "3600" : _("1 hour"), + "7200" : _("2 hours"), + "14400": _("4 hours"), + "28800": _("8 hours"), } updatesFrequency = CustomComboBox() @@ -1866,7 +1850,7 @@ def __init__(self, parent = None): try: updatesFrequency.setCurrentText(invertedTimes[currentValue]) except KeyError: - updatesFrequency.setCurrentText("1 hour") + updatesFrequency.setCurrentText(_("1 hour")) except Exception as e: report(e) @@ -1881,30 +1865,30 @@ def __init__(self, parent = None): self.layout.addLayout(hl) - notifyAboutUpdates = QCheckBox("Show a notification when there are available updates") + notifyAboutUpdates = QCheckBox(_("Show a notification when there are available updates")) notifyAboutUpdates.setChecked(not getSettings("DisableUpdatesNotifications")) notifyAboutUpdates.clicked.connect(lambda v: setSettings("DisableUpdatesNotifications", not bool(v))) self.layout.addWidget(notifyAboutUpdates) self.layout.addWidget(QLabel()) - subtitle = QLabel("Package manager preferences") + subtitle = QLabel(_("Package managers preferences")) subtitle.setStyleSheet(f"font-size: 25px;font-family: \"Segoe UI Variable Display\";font-weight: bold;") self.layout.addWidget(subtitle) - parallelInstalls = QCheckBox("Allow parallel installs (NOT RECOMMENDED)") + parallelInstalls = QCheckBox(_("Allow parallel installs (NOT RECOMMENDED)")) parallelInstalls.setChecked(getSettings("AllowParallelInstalls")) parallelInstalls.clicked.connect(lambda v: setSettings("AllowParallelInstalls", bool(v))) self.layout.addWidget(parallelInstalls) - disableWinget = QCheckBox("Disable Winget") + disableWinget = QCheckBox(_("Disable Winget")) disableWinget.setChecked(getSettings("DisableWinget")) disableWinget.clicked.connect(lambda v: setSettings("DisableWinget", bool(v))) self.layout.addWidget(disableWinget) - disableScoop = QCheckBox("Disable Scoop") + disableScoop = QCheckBox(_("Disable Scoop")) disableScoop.setChecked(getSettings("DisableScoop")) disableScoop.clicked.connect(lambda v: setSettings("DisableScoop", bool(v))) self.layout.addWidget(disableScoop) - scoopPreventCaps = QCheckBox("Show scoop apps as lowercase") + scoopPreventCaps = QCheckBox(_("Show scoop apps as lowercase")) scoopPreventCaps.setChecked(getSettings("LowercaseScoopApps")) scoopPreventCaps.clicked.connect(lambda v: setSettings("LowercaseScoopApps", bool(v))) self.layout.addWidget(scoopPreventCaps) @@ -1914,17 +1898,17 @@ def __init__(self, parent = None): self.layout.addWidget(QLabel()) l = QHBoxLayout() - button = QPushButton("Add a bucket to scoop") + button = QPushButton(_("Add a bucket to scoop")) #button.setFixedWidth(350) button.setFixedHeight(30) button.clicked.connect(lambda: self.scoopAddExtraBucket()) l.addWidget(button) - button = QPushButton("Remove a bucket from scoop") + button = QPushButton(_("Remove a bucket from scoop")) #button.setFixedWidth(350) button.setFixedHeight(30) button.clicked.connect(lambda: self.scoopRemoveExtraBucket()) l.addWidget(button) - button = QPushButton("Install scoop") + button = QPushButton(_("Install scoop")) #button.setFixedWidth(350) button.setFixedHeight(30) button.clicked.connect(lambda: (setSettings("DisableScoop", False), disableScoop.setChecked(False), os.startfile(os.path.join(realpath, "resources/install_scoop.cmd")))) @@ -1936,13 +1920,13 @@ def __init__(self, parent = None): print("🟢 Settings tab loaded!") def scoopAddExtraBucket(self) -> None: - r = QInputDialog.getItem(self, "Scoop bucket manager", "What bucket do you want to add", ["main", "extras", "versions", "nirsoft", "php", "nerd-fonts", "nonportable", "java", "games"], 1, editable=False) + r = QInputDialog.getItem(self, _("Scoop bucket manager"), _("What bucket do you want to add"), ["main", "extras", "versions", "nirsoft", "php", "nerd-fonts", "nonportable", "java", "games"], 1, editable=False) if r[1]: print(r[0]) globals.installersWidget.addItem(PackageInstallerWidget(f"{r[0]} scoop bucket", "custom", customCommand=f"scoop bucket add {r[0]}")) def scoopRemoveExtraBucket(self) -> None: - r = QInputDialog.getItem(self, "Scoop bucket manager", "What bucket do you want to remove", ["main", "extras", "versions", "nirsoft", "php", "nerd-fonts", "nonportable", "java", "games"], 1, editable=False) + r = QInputDialog.getItem(self, _("Scoop bucket manager"), _("What bucket do you want to remove"), ["main", "extras", "versions", "nirsoft", "php", "nerd-fonts", "nonportable", "java", "games"], 1, editable=False) if r[1]: print(r[0]) globals.installersWidget.addItem(PackageInstallerWidget(f"{r[0]} scoop bucket", "custom", customCommand=f"scoop bucket rm {r[0]}")) @@ -1963,17 +1947,17 @@ def contextMenuEvent(self, e: QContextMenuEvent) -> None: menu.addSeparator() a = QAction() - a.setText(("Reload log")) + a.setText(_("Reload log")) a.triggered.connect(lambda: self.textEdit.setPlainText(buffer.getvalue())) menu.addAction(a) a2 = QAction() - a2.setText(("Export log as a file")) + a2.setText(_("Export log as a file")) a2.triggered.connect(lambda: saveLog()) menu.addAction(a2) a3 = QAction() - a3.setText(("Copy log to clipboard")) + a3.setText(_("Copy log to clipboard")) a3.triggered.connect(lambda: copyLog()) menu.addAction(a3) @@ -1994,14 +1978,14 @@ def contextMenuEvent(self, e: QContextMenuEvent) -> None: self.textEdit.setPlainText(buffer.getvalue()) - reloadButton = QPushButton(("Reload log")) + reloadButton = QPushButton(_("Reload log")) reloadButton.setFixedWidth(200) reloadButton.clicked.connect(lambda: self.textEdit.setPlainText(buffer.getvalue())) def saveLog(): try: print("🔵 Saving log...") - f = QFileDialog.getSaveFileName(self, "Save log", os.path.expanduser("~"), "Text file (.txt)") + f = QFileDialog.getSaveFileName(self, _("Export log"), os.path.expanduser("~"), f"{_('Text file')} (.txt)") if f[0]: fpath = f[0] if not ".txt" in fpath.lower(): @@ -2019,7 +2003,7 @@ def saveLog(): report(e) self.textEdit.setPlainText(buffer.getvalue()) - exportButtom = QPushButton(("Export log as a file")) + exportButtom = QPushButton(_("Export log as a file")) exportButtom.setFixedWidth(200) exportButtom.clicked.connect(lambda: saveLog()) @@ -2033,7 +2017,7 @@ def copyLog(): report(e) self.textEdit.setPlainText(buffer.getvalue()) - copyButton = QPushButton(("Copy log to clipboard")) + copyButton = QPushButton(_("Copy log to clipboard")) copyButton.setFixedWidth(200) copyButton.clicked.connect(lambda: copyLog()) From ca3f6d9909342841901027db05de3ef22b867285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=AD=20Climent?= Date: Sat, 8 Oct 2022 14:22:33 +0200 Subject: [PATCH 5/7] Localise installer widgets --- wingetui/storeEngine.py | 217 ++++++++++++++++++---------------------- wingetui/tools.py | 5 +- 2 files changed, 101 insertions(+), 121 deletions(-) diff --git a/wingetui/storeEngine.py b/wingetui/storeEngine.py index 7257356be0..501b7a9ba7 100644 --- a/wingetui/storeEngine.py +++ b/wingetui/storeEngine.py @@ -5,6 +5,7 @@ from PySide6.QtGui import * from PySide6.QtWidgets import * from tools import * +from tools import _ import globals @@ -19,10 +20,10 @@ class PackageInstallerWidget(QGroupBox): def __init__(self, title: str, store: str, version: list = [], parent=None, customCommand: str = "", args: list = [], packageId="", admin: bool = False, useId: bool = False, packageItem: TreeWidgetItemWithQAction = None): super().__init__(parent=parent) self.packageItem = packageItem - self.actionDone = "installed" - self.actionDoing = "installing" - self.actionName = "installation" - self.actionVerb = "install" + self.actionDone = _("installed") + self.actionDoing = _("installing") + self.actionName = _("installation") + self.actionVerb = _("install") self.runAsAdmin = admin self.useId = useId self.adminstr = [sudoPath] if self.runAsAdmin else [] @@ -39,7 +40,7 @@ def __init__(self, title: str, store: str, version: list = [], parent=None, cust self.cmdline_args = args self.layout = QHBoxLayout() self.layout.setContentsMargins(30, 10, 10, 10) - self.label = QLabel(title+" Installation") + self.label = QLabel(_("{0} installation").format(title)) self.layout.addWidget(self.label) self.layout.addSpacing(5) self.progressbar = QProgressBar() @@ -51,13 +52,13 @@ def __init__(self, title: str, store: str, version: list = [], parent=None, cust self.layout.addWidget(self.progressbar, stretch=1) self.info = QLineEdit() self.info.setStyleSheet("color: grey; border-bottom: inherit;") - self.info.setText("Waiting for other installations to finish...") + self.info.setText(_("Waiting for other installations to finish...")) self.info.setReadOnly(True) self.addInfoLine.connect(lambda text: self.info.setText(text)) self.finishInstallation.connect(self.finish) self.layout.addWidget(self.info) self.counterSignal.connect(self.counter) - self.cancelButton = QPushButton(QIcon(realpath+"/resources/cancel.png"), "Cancel") + self.cancelButton = QPushButton(QIcon(realpath+"/resources/cancel.png"), _("Cancel")) self.cancelButton.clicked.connect(self.cancel) self.cancelButton.setFixedHeight(30) self.info.setFixedHeight(30) @@ -113,7 +114,7 @@ def runInstallation(self) -> None: self.leftFast.stop() self.rightSlow.stop() self.rightFast.stop() - self.addInfoLine.emit("Starting installation...") + self.addInfoLine.emit(_("Starting installation...")) self.progressbar.setValue(0) self.packageId = self.packageId.replace("…", "") self.programName = self.programName.replace("…", "") @@ -153,9 +154,9 @@ def cancel(self): if not self.finishedInstallation: subprocess.Popen("taskkill /im winget.exe /f", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, shell=True, cwd=os.getcwd(), env=os.environ).wait() self.finishedInstallation = True - self.info.setText("Installation canceled by user!") + self.info.setText(_("Installation canceled by user!")) self.cancelButton.setEnabled(True) - self.cancelButton.setText("Close") + self.cancelButton.setText(_("Close")) self.cancelButton.setIcon(QIcon(realpath+"/resources/warn.png")) self.cancelButton.clicked.connect(self.close) self.onCancel.emit() @@ -181,11 +182,11 @@ def finish(self, returncode: int, output: str = "") -> None: except: pass if not(self.canceled): if(returncode == 0): - self.callInMain.emit(lambda: globals.trayIcon.showMessage(f"{self.actionName.capitalize()} succeeded", f"{self.programName} was {self.actionDone} successfully!", QIcon(getMedia("notif_info")))) + self.callInMain.emit(lambda: globals.trayIcon.showMessage(_("{0} succeeded").format(self.actionName.capitalize()), _("{0} was {1} successfully!").format(self.programName, self.actionDone), QIcon(getMedia("notif_info")))) self.cancelButton.setText("OK") self.cancelButton.setIcon(QIcon(realpath+"/resources/tick.png")) self.cancelButton.clicked.connect(self.close) - self.info.setText(f"{self.programName} was {self.actionDone} successfully!") + self.info.setText(_("{0} was {1} successfully!").format(self.programName, self.actionDone)) self.progressbar.setValue(1000) if type(self) == PackageInstallerWidget: globals.uninstall.addItem(self.packageItem.text(0), self.packageItem.text(1), self.packageItem.text(2), self.packageItem.text(3)) # Add the package on the uninstaller @@ -194,14 +195,14 @@ def finish(self, returncode: int, output: str = "") -> None: msgBox = MessageBox(self) msgBox.setWindowTitle("WingetUI") msgBox.setText(f"{self.programName} was {self.actionDone} successfully.") - msgBox.setInformativeText(f"You will need to restart the application in order to get the {self.programName} new packages") + msgBox.setInformativeText("You will need to restart the application in order to get the {self.programName} new packages") msgBox.setStandardButtons(MessageBox.Ok) msgBox.setDefaultButton(MessageBox.Ok) msgBox.setIcon(MessageBox.Information) msgBox.exec_() else: globals.trayIcon.setIcon(QIcon(getMedia("yellowicon"))) - self.cancelButton.setText("OK") + self.cancelButton.setText(_("OK")) self.cancelButton.setIcon(QIcon(realpath+"/resources/warn.png")) self.cancelButton.clicked.connect(self.close) self.progressbar.setValue(1000) @@ -209,25 +210,25 @@ def finish(self, returncode: int, output: str = "") -> None: if(returncode == 2): # if the installer's hash does not coincide errorData = { "titlebarTitle": f"WingetUI - {self.programName} {self.actionName}", - "mainTitle": f"{self.actionName.capitalize()} aborted", - "mainText": f"The checksum of the installer does not coincide with the expected value, and the authenticity of the installer can't be verified. If you trust the publisher, {self.actionVerb} the package again skipping the hash check.", - "buttonTitle": "Close", + "mainTitle": _("{0} aborted").format(self.actionName.capitalize()), + "mainText": _("The checksum of the installer does not coincide with the expected value, and the authenticity of the installer can't be verified. If you trust the publisher, {0} the package again skipping the hash check.").format(self.actionVerb), + "buttonTitle": _("Close"), "errorDetails": output.replace("-\|/", "").replace("▒", "").replace("█", ""), "icon": QIcon(getMedia("warn")), - "notifTitle": f"Can't {self.actionVerb} {self.programName}", - "notifText": f"The installer has an invalid checksum", + "notifTitle": _("Can't {0} {1}").format(self.actionVerb, self.programName), + "notifText": _("The installer has an invalid checksum"), "notifIcon": QIcon(getMedia("notif_warn")), } else: # if there's a generic error errorData = { - "titlebarTitle": f"WingetUI - {self.programName} {self.actionName}", - "mainTitle": f"{self.actionName.capitalize()} failed", - "mainText": f"We could not {self.actionVerb} {self.programName}. Please try again later. Click on \"Show details\" to get the logs from the installer.", - "buttonTitle": "Close", + "titlebarTitle": _("WingetUI - {0} {1}").format(self.programName, self.actionName), + "mainTitle": _("{0} failed").format(self.actionName.capitalize()), + "mainText": _("We could not {0} {1}. Please try again later. Click on \"Show details\" to get the logs from the installer.").format(self.actionVerb, self.programName), + "buttonTitle": _("Close"), "errorDetails": output.replace("-\|/", "").replace("▒", "").replace("█", ""), "icon": QIcon(getMedia("warn")), - "notifTitle": f"Can't {self.actionVerb} {self.programName}", - "notifText": f"{self.programName} {self.actionName} failed", + "notifTitle": _("Can't {0} {1}").format(self.actionVerb, self.programName), + "notifText": _("{0} {1} failed").format(self.programName.capitalize(), self.actionName), "notifIcon": QIcon(getMedia("notif_warn")), } self.err.showErrorMessage(errorData) @@ -277,10 +278,10 @@ class PackageUpdaterWidget(PackageInstallerWidget): def __init__(self, title: str, store: str, version: list = [], parent=None, customCommand: str = "", args: list = [], packageId="", packageItem: TreeWidgetItemWithQAction = None, admin: bool = False, useId: bool = False): super().__init__(title, store, version, parent, customCommand, args, packageId, admin, useId) self.packageItem = packageItem - self.actionDone = "updated" - self.actionDoing = "updating" - self.actionName = "update" - self.actionVerb = "update" + self.actionDone = _("updated") + self.actionDoing = _("updating") + self.actionName = _("update") + self.actionVerb = _("update") def startInstallation(self) -> None: while self.installId != globals.current_program and not getSettings("AllowParallelInstalls"): @@ -293,7 +294,7 @@ def runInstallation(self) -> None: self.leftSlow.stop() self.leftFast.stop() self.rightSlow.stop() - self.addInfoLine.emit("Applying update...") + self.addInfoLine.emit(_("Applying update...")) self.rightFast.stop() self.progressbar.setValue(0) self.packageId = self.packageId.replace("…", "") @@ -348,18 +349,18 @@ def __init__(self, title: str, store: str, useId=False, packageId = "", packageI self.programName = title self.packageId = packageId super().__init__(parent=None, title=title, store=store, packageId=packageId, admin=admin, args=args, packageItem=packageItem) - self.actionDone = "uninstalled" + self.actionDone = _("uninstalled") self.removeData = removeData - self.actionDoing = "uninstalling" - self.actionName = "uninstallation" - self.actionVerb = "uninstall" + self.actionDoing = _("uninstalling") + self.actionName = _("uninstallation") + self.actionVerb = _("uninstall") self.finishedInstallation = True self.runAsAdmin = admin self.adminstr = [sudoPath] if self.runAsAdmin else [] self.store = store.lower() self.setStyleSheet("QGroupBox{padding-top:15px; margin-top:-15px; border: none}") self.setFixedHeight(50) - self.label.setText(title+" Uninstallation") + self.label.setText(_("{} Uninstallation").format(title)) def startInstallation(self) -> None: while self.installId != globals.current_program and not getSettings("AllowParallelInstalls"): @@ -403,12 +404,12 @@ def cancel(self): self.leftFast.stop() self.rightSlow.stop() self.rightFast.stop() - self.info.setText("Installation canceled by user!") + self.info.setText(_("Uninstall canceled by user!")) if not self.finishedInstallation: subprocess.Popen("taskkill /im winget.exe /f", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, shell=True, cwd=os.getcwd(), env=os.environ).wait() self.finishedInstallation = True self.cancelButton.setEnabled(True) - self.cancelButton.setText("Close") + self.cancelButton.setText(_("Close")) self.cancelButton.setIcon(QIcon(realpath+"/resources/warn.png")) self.cancelButton.clicked.connect(self.close) self.onCancel.emit() @@ -443,51 +444,29 @@ def finish(self, returncode: int, output: str = "") -> None: if not(self.canceled): if(returncode == 0): self.callInMain.emit(lambda: globals.trayIcon.showMessage(f"{self.actionName.capitalize()} succeeded", f"{self.programName} was {self.actionDone} successfully!", QIcon(getMedia("notif_info")))) - self.cancelButton.setText("OK") + self.cancelButton.setText(_("OK")) self.cancelButton.setIcon(QIcon(realpath+"/resources/tick.png")) self.cancelButton.clicked.connect(self.close) - self.info.setText(f"{self.programName} was {self.actionDone} successfully!") + self.info.setText(f"{self.programName} was uninstalled successfully!") self.progressbar.setValue(1000) self.startCoolDown() - if(self.store == "powershell"): - msgBox = MessageBox(self) - msgBox.setWindowTitle("WingetUI") - msgBox.setText(f"{self.programName} was uninstalled successfully.") - msgBox.setInformativeText(f"You will need to restart the application in order to get the {self.programName} new packages") - msgBox.setStandardButtons(MessageBox.Ok) - msgBox.setDefaultButton(MessageBox.Ok) - msgBox.setIcon(MessageBox.Information) - msgBox.exec_() else: globals.trayIcon.setIcon(QIcon(getMedia("yellowicon"))) - self.cancelButton.setText("OK") + self.cancelButton.setText(_("OK")) self.cancelButton.setIcon(QIcon(realpath+"/resources/warn.png")) self.cancelButton.clicked.connect(self.close) self.progressbar.setValue(1000) self.err = ErrorMessage(self.window()) - if(returncode == 2): # if the installer's hash does not coincide - errorData = { - "titlebarTitle": f"WingetUI - {self.programName} {self.actionName}", - "mainTitle": f"{self.actionName.capitalize()} aborted", - "mainText": f"The checksum of the installer does not coincide with the expected value, and the authenticity of the installer can't be verified. If you trust the publisher, {self.actionVerb} the package again skipping the hash check.", - "buttonTitle": "Close", - "errorDetails": output.replace("-\|/", "").replace("▒", "").replace("█", ""), - "icon": QIcon(getMedia("warn")), - "notifTitle": f"Can't {self.actionVerb} {self.programName}", - "notifText": f"The installer has an invalid checksum", - "notifIcon": QIcon(getMedia("notif_warn")), - } - else: # if there's a generic error - errorData = { - "titlebarTitle": f"WingetUI - {self.programName} {self.actionName}", - "mainTitle": f"{self.actionName.capitalize()} failed", - "mainText": f"We could not {self.actionVerb} {self.programName}. Please try again later. Click on \"Show details\" to get the logs from the installer.", - "buttonTitle": "Close", - "errorDetails": output.replace("-\|/", "").replace("▒", "").replace("█", ""), - "icon": QIcon(getMedia("warn")), - "notifTitle": f"Can't {self.actionVerb} {self.programName}", - "notifText": f"{self.programName} {self.actionName} failed", - "notifIcon": QIcon(getMedia("notif_warn")), + errorData = { + "titlebarTitle": _("WingetUI - {0} {1}").format(self.programName, self.actionName), + "mainTitle": _("{0} failed").format(self.actionName.capitalize()), + "mainText": _("We could not {0} {1}. Please try again later. Click on \"Show details\" to get the logs from the uninstaller.").format(self.actionVerb, self.programName), + "buttonTitle": _("Close"), + "errorDetails": output.replace("-\|/", "").replace("▒", "").replace("█", ""), + "icon": QIcon(getMedia("warn")), + "notifTitle": _("Can't {0} {1}").format(self.actionVerb, self.programName), + "notifText": _("{0} {1} failed").format(self.programName.capitalize(), self.actionName), + "notifIcon": QIcon(getMedia("notif_warn")), } self.err.showErrorMessage(errorData) @@ -541,7 +520,7 @@ def __init__(self, parent = None): self.layout = QVBoxLayout() self.title = QLinkLabel() self.title.setStyleSheet("font-size: 30pt;font-family: \"Segoe UI Variable Display\";font-weight: bold;") - self.title.setText("Loading...") + self.title.setText(_("Loading...")) fortyWidget = QWidget() fortyWidget.setFixedWidth(120) @@ -557,35 +536,35 @@ def __init__(self, parent = None): self.hLayout = QHBoxLayout() self.oLayout = QHBoxLayout() - self.description = QLinkLabel("Description: Unknown") + self.description = QLinkLabel(_('Description:')+" "+_('Unknown')) self.description.setWordWrap(True) self.layout.addWidget(self.description) - self.homepage = QLinkLabel("Homepage URL: Unknown") + self.homepage = QLinkLabel(_('Homepage URL:')+" "+_('Unknown')) self.homepage.setWordWrap(True) self.layout.addWidget(self.homepage) - self.publisher = QLinkLabel("Publisher: Unknown") + self.publisher = QLinkLabel(_('Publisher:')+" "+_('Unknown')) self.publisher.setWordWrap(True) self.layout.addWidget(self.publisher) - self.author = QLinkLabel("Author: Unknown") + self.author = QLinkLabel(_('Author:')+" "+_('Unknown')) self.author.setWordWrap(True) self.layout.addWidget(self.author) self.layout.addStretch() - self.license = QLinkLabel("License: Unknown") + self.license = QLinkLabel(_('License:')+" "+_('Unknown')) self.license.setWordWrap(True) self.layout.addWidget(self.license) self.layout.addStretch() hLayout = QHBoxLayout() - self.versionLabel = QLinkLabel("Version: ") + self.versionLabel = QLinkLabel(_("Version:")) self.versionCombo = CustomComboBox() @@ -593,7 +572,7 @@ def __init__(self, parent = None): self.versionCombo.setIconSize(QSize(24, 24)) self.versionCombo.setFixedHeight(35) self.installButton = QPushButton() - self.installButton.setText("Install") + self.installButton.setText(_("Install")) self.installButton.setObjectName("AccentButton") self.installButton.setIconSize(QSize(24, 24)) self.installButton.clicked.connect(self.install) @@ -605,15 +584,15 @@ def __init__(self, parent = None): optionsGroupBox = QGroupBox() self.forceCheckbox = QCheckBox() - self.forceCheckbox.setText("Skip hash check") + self.forceCheckbox.setText(_("Skip hash check")) self.forceCheckbox.setChecked(False) self.interactiveCheckbox = QCheckBox() - self.interactiveCheckbox.setText("Interactive installation") + self.interactiveCheckbox.setText(_("Interactive installation")) self.interactiveCheckbox.setChecked(False) self.adminCheckbox = QCheckBox() - self.adminCheckbox.setText("Run as admin") + self.adminCheckbox.setText(_("Run as admin")) self.adminCheckbox.setChecked(False) self.oLayout.addStretch() @@ -638,29 +617,29 @@ def __init__(self, parent = None): self.layout.addStretch() - self.packageId = QLinkLabel("Program ID: Unknown") + self.packageId = QLinkLabel(_('Program ID:')+" "+_('Unknown')) self.packageId.setWordWrap(True) self.layout.addWidget(self.packageId) - self.manifest = QLinkLabel("Manifest: Unknown") + self.manifest = QLinkLabel(_('Manifest:')+" "+_('Unknown')) self.manifest.setWordWrap(True) self.layout.addWidget(self.manifest) - self.lastver = QLinkLabel("Latest version: Unknown") + self.lastver = QLinkLabel(_('Latest version:')+" "+_('Unknown')) self.lastver.setWordWrap(True) self.layout.addWidget(self.lastver) - self.sha = QLinkLabel("Installer SHA256 (Latest version): Unknown") + self.sha = QLinkLabel(_('Installer SHA256 (Latest version):')+" "+_('Unknown')) self.sha.setWordWrap(True) self.layout.addWidget(self.sha) - self.link = QLinkLabel("Installer URL (Latest version): Unknown") + self.link = QLinkLabel(_('Installer URL (Latest version):')+" "+_('Unknown')) self.link.setWordWrap(True) self.layout.addWidget(self.link) - self.type = QLinkLabel("Installer type (Latest version): Unknown") + self.type = QLinkLabel(_('Installer type (Latest version):')+" "+_('Unknown')) self.type.setWordWrap(True) self.layout.addWidget(self.type) self.storeLabel = QLinkLabel(f"Source: {self.store}") self.storeLabel.setWordWrap(True) self.layout.addWidget(self.storeLabel) self.layout.addStretch() - self.advert = QLinkLabel("ALERT: NEITHER MICROSOFT NOR THE CREATORS OF WINGET UI STORE ARE RESPONSIBLE FOR THE DOWNLOADED SOFTWARE. PROCEED WITH CAUTION") + self.advert = QLinkLabel(_("DISCLAIMER: NEITHER MICROSOFT NOR THE CREATORS OF WINGET UI STORE ARE RESPONSIBLE FOR THE DOWNLOADED SOFTWARE. PROCEED WITH CAUTION")) self.advert.setWordWrap(True) self.layout.addWidget(self.advert) @@ -737,7 +716,7 @@ def loadProgram(self, title: str, id: str, useId: bool, store: str, update: bool self.installButton.setEnabled(False) self.versionCombo.setEnabled(False) self.isAnUpdate = update - self.installButton.setText("Please wait...") + self.installButton.setText(_("Please wait...")) store = store.lower() self.title.setText(title) @@ -748,19 +727,19 @@ def loadProgram(self, title: str, id: str, useId: bool, store: str, update: bool self.interactiveCheckbox.setEnabled(False) self.adminCheckbox.setChecked(False) self.adminCheckbox.setEnabled(False) - self.description.setText("Loading...") - self.author.setText("Author: "+"Loading...") - self.publisher.setText("Publisher: "+"Loading...") - self.homepage.setText(f"Homepage: {'Loading...'}") - self.license.setText(f"License: {'Loading...'} ({'Loading...'})") - self.lastver.setText("Latest version: Loading...") - self.sha.setText(f"Installer SHA256 (Latest version): {'Loading...'}") - self.link.setText(f"Installer URL (Latest version): {'Loading...'}") - self.type.setText(f"Installer type (Latest version): {'Loading...'}") - self.packageId.setText(f"Package ID: {'Loading...'}") - self.manifest.setText(f"Manifest: {'Loading...'}") - self.storeLabel.setText(f"Source: {self.store.capitalize()}") - self.versionCombo.addItems(["Loading..."]) + self.description.setText(_("Loading...")) + self.author.setText(_("Author")+": "+_("Loading...")) + self.publisher.setText(f"{_('Publisher')}: "+_("Loading...")) + self.homepage.setText(f"{_('Homepage')}: {_('Loading...')}") + self.license.setText(f"{_('License')}: {_('Loading...')} ({_('Loading...')})") + self.lastver.setText(f"{_('Latest version')}: {_('Loading...')}") + self.sha.setText(f"{_('Installer SHA256')} ({_('Latest version')}): {_('Loading...')}") + self.link.setText(f"{_('Installer URL')} ({_('Latest version')}): {_('Loading...')}") + self.type.setText(f"{_('Installer type')} ({_('Latest version')}): {_('Loading...')}") + self.packageId.setText(f"{_('Package ID')}: {_('Loading...')}") + self.manifest.setText(f"{_('Manifest')}: {_('Loading...')}") + self.storeLabel.setText(f"{_('Source')}: {self.store.capitalize()}") + self.versionCombo.addItems([_("Loading...")]) self.finishedCount = 0 if(store.lower()=="winget"): @@ -773,9 +752,9 @@ def printData(self, appInfo: dict) -> None: if not("scoop" in self.store.lower()) or self.finishedCount > 1: self.loadingProgressBar.hide() if self.isAnUpdate: - self.installButton.setText("Update") + self.installButton.setText(_("Update")) else: - self.installButton.setText("Install") + self.installButton.setText(_("Install")) self.installButton.setEnabled(True) self.versionCombo.setEnabled(True) self.adminCheckbox.setEnabled(True) @@ -784,19 +763,19 @@ def printData(self, appInfo: dict) -> None: self.interactiveCheckbox.setEnabled(True) self.title.setText(appInfo["title"]) self.description.setText(appInfo["description"]) - self.author.setText("Author: "+appInfo["author"]) - self.publisher.setText("Publisher: "+appInfo["publisher"]) - self.homepage.setText(f"Homepage: {appInfo['homepage']}") - self.license.setText(f"License: {appInfo['license']} ({appInfo['license-url']})") + self.author.setText(f"{_('Author')}: "+appInfo["author"]) + self.publisher.setText(f"{_('Publisher')}: "+appInfo["publisher"]) + self.homepage.setText(f"{_('Homepage')}: {appInfo['homepage']}") + self.license.setText(f"{_('License')}: {appInfo['license']} ({appInfo['license-url']})") try: - self.lastver.setText(f"Latest version: {appInfo['versions'][0]}") + self.lastver.setText(f"{_('Latest version')}: {appInfo['versions'][0]}") except IndexError: - self.lastver.setText(f"Latest version: Unknown") - self.sha.setText(f"Installer SHA256 (Latest version): {appInfo['installer-sha256']}") - self.link.setText(f"Installer URL (Latest version): {appInfo['installer-url']}") - self.type.setText(f"Installer type (Latest version): {appInfo['installer-type']}") - self.packageId.setText(f"Package ID: {appInfo['id']}") - self.manifest.setText(f"Manifest: {appInfo['manifest']}") + self.lastver.setText(_('Latest version:')+" "+_('Unknown')) + self.sha.setText(f"{_('Installer SHA256')} ({_('Latest version')}): {appInfo['installer-sha256']}") + self.link.setText(f"{_('Installer URL')} ({_('Latest version')}): {appInfo['installer-url']}") + self.type.setText(f"{_('Installer type')} ({_('Latest version')}): {appInfo['installer-type']}") + self.packageId.setText(f"{_('Package ID')}: {appInfo['id']}") + self.manifest.setText(f"{_('Manifest')}: {appInfo['manifest']}") while self.versionCombo.count()>0: self.versionCombo.removeItem(0) try: @@ -806,7 +785,7 @@ def printData(self, appInfo: dict) -> None: def install(self): title = self.title.text() - packageId = self.packageId.text().replace('Package ID:', '').strip() + packageId = self.packageId.text().replace(_('Package ID')+":", '').strip() print(f"🟢 Starting installation of package {title} with id {packageId}") cmdline_args = [] if(self.forceCheckbox.isChecked()): @@ -819,7 +798,7 @@ def install(self): else: if not "scoop" in self.store.lower(): cmdline_args.append("--silent") - if(self.versionCombo.currentText()=="Latest"): + if(self.versionCombo.currentText()==_("Latest") or self.versionCombo.currentText() == "Latest"): version = [] else: version = ["--version", self.versionCombo.currentText()] diff --git a/wingetui/tools.py b/wingetui/tools.py index 0a1d21bba7..34e47c483a 100644 --- a/wingetui/tools.py +++ b/wingetui/tools.py @@ -301,6 +301,7 @@ def __init__(self, emptystr: str = "") -> None: op=QGraphicsOpacityEffect(self.label) op.setOpacity(0.5) self.label.setGraphicsEffect(op) + self.label.setAttribute(Qt.WA_TransparentForMouseEvents) self.label.setAutoFillBackground(True) font = self.label.font() font.setBold(True) @@ -468,7 +469,7 @@ def __init__(self, parent): self.okButton = QPushButton() self.okButton.setFixedHeight(30) self.okButton.clicked.connect(self.delete) - self.moreInfoButton = QPushButton("Show details") + self.moreInfoButton = QPushButton(_("Show details")) self.moreInfoButton.setFixedHeight(30) self.moreInfoButton.clicked.connect(self.moreInfo) #hl.addStretch() @@ -489,7 +490,7 @@ def delete(self): def moreInfo(self): spacingAdded = False self.moreInfoTextArea.setVisible(not self.moreInfoTextArea.isVisible()) - self.moreInfoButton.setText("Hide details" if self.moreInfoTextArea.isVisible() else "Show details") + self.moreInfoButton.setText(_("Hide details") if self.moreInfoTextArea.isVisible() else _("Show details")) if self.moreInfoTextArea.isVisible(): # show textedit s = self.size() From 38fd721010e5c70a3820a7a7f808de13f70b6d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=AD=20Climent?= Date: Wed, 12 Oct 2022 17:20:10 +0200 Subject: [PATCH 6/7] Add catalan + finish adding keys --- .gitignore | 3 +- media/socialicon.png | Bin 0 -> 225532 bytes wingetui/__init__.py | 4 +- wingetui/lang/download_translations.py | 171 +++++++++++++++++++++ wingetui/lang/lang_ca.json | 195 +++++++++++++++++++++++- wingetui/lang/lang_en-US-POSIX.json | 196 +++++++++++++++++++++++++ wingetui/lang/lang_en.json | 195 +++++++++++++++++++++++- wingetui/lang/translated_percentage.py | 3 +- wingetui/languages.py | 1 - wingetui/tools.py | 60 +++++++- wingetui/uiSections.py | 31 +++- 11 files changed, 841 insertions(+), 18 deletions(-) create mode 100644 media/socialicon.png create mode 100644 wingetui/lang/download_translations.py create mode 100644 wingetui/lang/lang_en-US-POSIX.json diff --git a/.gitignore b/.gitignore index 5d8195163a..d075b96dc0 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ Output/ vcredist.exe wingetuiBin/ wingetui_bin/ -wingetuiBin.zip \ No newline at end of file +wingetuiBin.zip +APIKEY.txt \ No newline at end of file diff --git a/media/socialicon.png b/media/socialicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1718e1b0f32e9011eb73eed698756323ea147764 GIT binary patch literal 225532 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