[improvement] Not breaking the application when a i18n (translation) key was not found

This commit is contained in:
Vinicius Moreira
2019-11-25 13:09:50 -03:00
parent 8c1bf684ed
commit b59cb347c4
25 changed files with 175 additions and 108 deletions

View File

@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.7.3]
### Improvements
- Not breaking the application when a i18n (translation) key was not found
## [0.7.2] 2019-11-01
### Improvements
- Snap

View File

@@ -20,3 +20,11 @@ The way to go here is to ask yourself if the improvement would be useful for mor
* Explain why you think these changes could be useful
* If it fixes a bug, be sure to link to the issue itself.
* Follow the [PEP 8](https://www.python.org/dev/peps/pep-0008/) code style to keep the code consistent.
# Adding a new translation
* To add a new translation, you will have to create a file in each directory listed below named as the first two letters in the ISO format (e.g: for 'english' would be 'en'):
- **bauh/view/resources/locale**
- **bauh/gems/appimage/resources/locale**
- **bauh/gems/arch/resources/locale**
- **bauh/gems/flatpak/resources/locale**
- **bauh/gems/snap/resources/locale**

View File

@@ -5,6 +5,8 @@ It has a **tray mode** (see **Settings** below) that attaches the application ic
This project has an official Twitter account ( **@bauh4linux** ) so people can stay on top of its news.
To contribute with this project, have a look at [CONTRIBUTING.md](https://github.com/vinifmor/bauh/blob/master/CONTRIBUTING.md)
![management panel](https://raw.githubusercontent.com/vinifmor/bauh/master/pictures/panel.png)
@@ -65,13 +67,13 @@ If you do not want to clone / download this repository, go to your **Home** fold
In order to autostart the application, use your Desktop Environment settings to register it as a startup application / script (**bauh --tray=1**).
### Gems ( package technology support )
#### Flatpak ( flatpak gem )
#### Flatpak ( flatpak )
- The user is able to search, install, uninstall, downgrade, launch and retrieve the applications history
#### Snap ( snap gem )
#### Snap ( snap )
- The user is able to search, install, uninstall, refresh, launch and downgrade applications
#### AUR ( arch gem )
#### AUR ( arch )
- It is **not enabled by default**
- The user is able to search, install, uninstall, downgrade, launch and retrieve the packages history
- It handles conflicts, and missing / optional packages installations ( including from your distro mirrors )
@@ -90,7 +92,7 @@ will be pre-downloaded faster ( it does **NOT** modify your **pacman** settings
- Arch package memory-indexer running every 20 minutes. This memory index is used when AUR Api cannot handle the amount of results found for a given search. It can be disabled via the environment variable **BAUH_ARCH_AUR_INDEX_UPDATER=0**.
- If some of your installed packages are not categorized, send an e-mail to **bauh4linux@gmail.com** informing their names and categories in the following format: ```name=category1[,category2,category3,...]```
#### AppImage ( appimage gem )
#### AppImage ( appimage )
- The user is able to search, install, uninstall, downgrade, launch and retrieve the applications history
- Supported sources: [AppImageHub](https://appimage.github.io) ( **applications with no releases published to GitHub are currently not available** )
- Faster downloads if **aria2c** is installed. Same behavior described in the **AUR support** section.

View File

@@ -1,4 +1,4 @@
__version__ = '0.7.2'
__version__ = '0.7.3'
__app_name__ = 'bauh'
import os

View File

@@ -1,16 +1,16 @@
import logging
import platform
import sys
from bauh.api.abstract.cache import MemoryCacheFactory
from bauh.api.abstract.disk import DiskCacheLoaderFactory
from bauh.api.abstract.download import FileDownloader
from bauh.api.http import HttpClient
from bauh.view.util.translation import I18n
class ApplicationContext:
def __init__(self, disk_cache: bool, download_icons: bool, http_client: HttpClient, app_root_dir: str, i18n: dict,
def __init__(self, disk_cache: bool, download_icons: bool, http_client: HttpClient, app_root_dir: str, i18n: I18n,
cache_factory: MemoryCacheFactory, disk_loader_factory: DiskCacheLoaderFactory,
logger: logging.Logger, file_downloader: FileDownloader, distro: str):
"""
@@ -18,7 +18,7 @@ class ApplicationContext:
:param download_icons: if packages icons should be downloaded
:param http_client: a shared instance of http client
:param app_root_dir: GUI root dir
:param i18n: the i18n dictionary keys
:param i18n: the translation keys
:param cache_factory:
:param disk_loader_factory:
:param logger: a logger instance

View File

@@ -12,9 +12,12 @@ from bauh.view.core.controller import GenericSoftwareManager
from bauh.view.core.downloader import AdaptableFileDownloader
from bauh.view.qt.systray import TrayIcon
from bauh.view.qt.window import ManageWindow
from bauh.view.util import util, logs, resource
from bauh.view.util import util, logs, resource, translation
from bauh.view.util.cache import DefaultMemoryCacheFactory, CacheCleaner
from bauh.view.util.disk import DefaultDiskCacheLoaderFactory
from bauh.view.util.translation import I18n
DEFAULT_I18N_KEY = 'en'
def main():
@@ -25,7 +28,9 @@ def main():
logger = logs.new_logger(__app_name__, bool(args.logs))
app_args.validate(args, logger)
i18n_key, i18n = util.get_locale_keys(args.locale)
i18n_key, current_i18n = translation.get_locale_keys(args.locale)
default_i18n = translation.get_locale_keys(DEFAULT_I18N_KEY)[1] if i18n_key != DEFAULT_I18N_KEY else {}
i18n = I18n(current_i18n, default_i18n)
cache_cleaner = CacheCleaner()
cache_factory = DefaultMemoryCacheFactory(expiration_time=args.cache_exp, cleaner=cache_cleaner)
@@ -57,7 +62,7 @@ def main():
if app.style().objectName().lower() not in {'fusion', 'breeze'}:
app.setStyle('Fusion')
managers = gems.load_managers(context=context, locale=i18n_key, config=user_config)
managers = gems.load_managers(context=context, locale=i18n_key, config=user_config, default_locale=DEFAULT_I18N_KEY)
manager = GenericSoftwareManager(managers, context=context, app_args=args)
manager.prepare()

View File

@@ -2,11 +2,11 @@ from typing import Set
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.view import MultipleSelectComponent, InputOption
from bauh.commons.html import bold
from bauh.view.util.translation import I18n
def request_optional_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatcher, i18n: dict) -> Set[str]:
def request_optional_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatcher, i18n: I18n) -> Set[str]:
opts = [InputOption('{}{} ( {} )'.format(p, ': ' + d['desc'] if d['desc'] else '', d['mirror'].upper()), p) for p, d in pkg_mirrors.items()]
view_opts = MultipleSelectComponent(label='',
options=opts,
@@ -21,7 +21,7 @@ def request_optional_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatch
return {o.value for o in view_opts.values}
def request_install_missing_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatcher, i18n: dict) -> bool:
def request_install_missing_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatcher, i18n: I18n) -> bool:
deps_str = ''.join(['<br/><span style="font-weight:bold"> - {} ( {} )</span>'.format(d, m.upper()) for d, m in pkg_mirrors.items()])
msg = '<p>{}</p>'.format(i18n['arch.missing_deps.body'].format(bold(pkgname)) + ':<br/>' + deps_str)
msg += i18n['ask.continue']

View File

@@ -1,21 +1,22 @@
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.view import MessageType
from bauh.commons.html import bold
from bauh.view.util.translation import I18n
def show_dep_not_installed(watcher: ProcessWatcher, pkgname: str, depname: str, i18n: dict):
def show_dep_not_installed(watcher: ProcessWatcher, pkgname: str, depname: str, i18n: I18n):
watcher.show_message(title=i18n['error'],
body=i18n['arch.install.dependency.install.error'].format(bold(depname), bold(pkgname)),
type_=MessageType.ERROR)
def show_dep_not_found(depname: str, i18n: dict, watcher: ProcessWatcher):
def show_dep_not_found(depname: str, i18n: I18n, watcher: ProcessWatcher):
watcher.show_message(title=i18n['arch.install.dep_not_found.title'],
body=i18n['arch.install.dep_not_found.body'].format(bold(depname)),
type_=MessageType.ERROR)
def show_optdep_not_installed(depname: str, watcher: ProcessWatcher, i18n: dict):
def show_optdep_not_installed(depname: str, watcher: ProcessWatcher, i18n: I18n):
watcher.show_message(title=i18n['error'],
body=i18n['arch.install.optdep.error'].format(bold(depname)),
type_=MessageType.ERROR)

View File

@@ -8,11 +8,12 @@ from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.http import HttpClient
from bauh.commons.html import bold
from bauh.commons.system import run_cmd, new_subprocess, ProcessHandler, SystemProcess, SimpleProcess
from bauh.view.util.translation import I18n
class AdaptableFileDownloader(FileDownloader):
def __init__(self, logger: logging.Logger, multithread_enabled: bool, i18n: dict, http_client: HttpClient):
def __init__(self, logger: logging.Logger, multithread_enabled: bool, i18n: I18n, http_client: HttpClient):
self.logger = logger
self.multithread_enabled = multithread_enabled
self.i18n = i18n

View File

@@ -6,7 +6,7 @@ from typing import List
from bauh import ROOT_DIR
from bauh.api.abstract.controller import SoftwareManager, ApplicationContext
from bauh.view.core.config import Configuration
from bauh.view.util import util
from bauh.view.util import util, translation
def find_manager(member):
@@ -20,7 +20,7 @@ def find_manager(member):
return manager_found
def load_managers(locale: str, context: ApplicationContext, config: Configuration) -> List[SoftwareManager]:
def load_managers(locale: str, context: ApplicationContext, config: Configuration, default_locale: str) -> List[SoftwareManager]:
managers = []
for f in os.scandir(ROOT_DIR + '/gems'):
@@ -37,7 +37,10 @@ def load_managers(locale: str, context: ApplicationContext, config: Configuratio
locale_path = '{}/resources/locale'.format(f.path)
if os.path.exists(locale_path):
context.i18n.update(util.get_locale_keys(locale, locale_path)[1])
context.i18n.current.update(translation.get_locale_keys(locale, locale_path)[1])
if default_locale and context.i18n.default:
context.i18n.default.update(translation.get_locale_keys(default_locale, locale_path)[1])
man = manager_class(context=context)

View File

@@ -1,11 +1,12 @@
from glob import glob
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap, QIcon
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QVBoxLayout, QDialog, QLabel, QWidget, QHBoxLayout
from bauh import __version__, __app_name__, ROOT_DIR
from bauh.view.util import resource
from bauh.view.util.translation import I18n
PROJECT_URL = 'https://github.com/vinifmor/' + __app_name__
LICENSE_URL = 'https://raw.githubusercontent.com/vinifmor/{}/master/LICENSE'.format(__app_name__)
@@ -13,9 +14,9 @@ LICENSE_URL = 'https://raw.githubusercontent.com/vinifmor/{}/master/LICENSE'.for
class AboutDialog(QDialog):
def __init__(self, locale_keys: dict):
def __init__(self, i18n: I18n):
super(AboutDialog, self).__init__()
self.setWindowTitle(locale_keys['tray.action.about'])
self.setWindowTitle(i18n['tray.action.about'])
layout = QVBoxLayout()
self.setLayout(layout)
@@ -33,7 +34,7 @@ class AboutDialog(QDialog):
line_desc = QLabel(self)
line_desc.setStyleSheet('font-size: 12px; font-weight: bold;')
line_desc.setText(locale_keys['about.info.desc'])
line_desc.setText(i18n['about.info.desc'])
line_desc.setAlignment(Qt.AlignCenter)
line_desc.setMinimumWidth(400)
layout.addWidget(line_desc)
@@ -55,21 +56,21 @@ class AboutDialog(QDialog):
layout.addWidget(gems_widget)
layout.addWidget(QLabel(''))
label_version = QLabel(locale_keys['version'].lower() + ' ' + __version__)
label_version = QLabel(i18n['version'].lower() + ' ' + __version__)
label_version.setStyleSheet('QLabel { font-size: 11px; font-weight: bold }')
label_version.setAlignment(Qt.AlignCenter)
layout.addWidget(label_version)
label_more_info = QLabel()
label_more_info.setStyleSheet('font-size: 11px;')
label_more_info.setText(locale_keys['about.info.link'] + ": <a href='{url}'>{url}</a>".format(url=PROJECT_URL))
label_more_info.setText(i18n['about.info.link'] + ": <a href='{url}'>{url}</a>".format(url=PROJECT_URL))
label_more_info.setOpenExternalLinks(True)
label_more_info.setAlignment(Qt.AlignCenter)
layout.addWidget(label_more_info)
label_license = QLabel()
label_license.setStyleSheet('font-size: 11px;')
label_license.setText("<a href='{}'>{}</a>".format(LICENSE_URL, locale_keys['about.info.license']))
label_license.setText("<a href='{}'>{}</a>".format(LICENSE_URL, i18n['about.info.license']))
label_license.setOpenExternalLinks(True)
label_license.setAlignment(Qt.AlignCenter)
layout.addWidget(label_license)
@@ -78,7 +79,7 @@ class AboutDialog(QDialog):
label_rate = QLabel()
label_rate.setStyleSheet('font-size: 11px; font-weight: bold;')
label_rate.setText(locale_keys['about.info.rate'] + ' :)')
label_rate.setText(i18n['about.info.rate'] + ' :)')
label_rate.setOpenExternalLinks(True)
label_rate.setAlignment(Qt.AlignCenter)
layout.addWidget(label_rate)

View File

@@ -15,6 +15,7 @@ from bauh.view.qt import dialog
from bauh.view.qt.components import IconButton
from bauh.view.qt.view_model import PackageView, PackageViewStatus
from bauh.view.util import resource
from bauh.view.util.translation import I18n
INSTALL_BT_STYLE = 'background: {back}; color: white; font-size: 10px; font-weight: bold'
@@ -25,7 +26,7 @@ PUBLISHER_MAX_SIZE = 25
class UpdateToggleButton(QWidget):
def __init__(self, app_view: PackageView, root: QWidget, i18n: dict, checked: bool = True, clickable: bool = True):
def __init__(self, app_view: PackageView, root: QWidget, i18n: I18n, checked: bool = True, clickable: bool = True):
super(UpdateToggleButton, self).__init__()
self.app_view = app_view
@@ -113,7 +114,7 @@ class AppsTable(QTableWidget):
if dialog.ask_confirmation(
title=self.i18n['manage_window.apps_table.row.actions.downgrade'],
body=self._parag(self.i18n['manage_window.apps_table.row.actions.downgrade.popup.body'].format(self._bold(str(pkg)))),
locale_keys=self.i18n):
i18n=self.i18n):
self.window.downgrade(pkg)
action_downgrade.triggered.connect(downgrade)
@@ -131,7 +132,7 @@ class AppsTable(QTableWidget):
if dialog.ask_confirmation(
title=self.i18n[action.i18_label_key],
body=self._parag('{} {} ?'.format(self.i18n[action.i18_label_key], self._bold(str(pkg)))),
locale_keys=self.i18n):
i18n=self.i18n):
self.window.execute_custom_action(pkg, action)
item.triggered.connect(custom_action)
@@ -162,7 +163,7 @@ class AppsTable(QTableWidget):
def _uninstall_app(self, app_v: PackageView):
if dialog.ask_confirmation(title=self.i18n['manage_window.apps_table.row.actions.uninstall.popup.title'],
body=self._parag(self.i18n['manage_window.apps_table.row.actions.uninstall.popup.body'].format(self._bold(str(app_v)))),
locale_keys=self.i18n):
i18n=self.i18n):
self.window.uninstall_app(app_v)
def _bold(self, text: str) -> str:
@@ -176,7 +177,7 @@ class AppsTable(QTableWidget):
if dialog.ask_confirmation(
title=self.i18n['manage_window.apps_table.row.actions.install.popup.title'],
body=self._parag(self.i18n['manage_window.apps_table.row.actions.install.popup.body'].format(self._bold(str(pkgv)))),
locale_keys=self.i18n):
i18n=self.i18n):
self.window.install(pkgv)

View File

@@ -1,22 +1,23 @@
from typing import List
from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget
from bauh.api.abstract.view import ViewComponent, SingleSelectComponent, MultipleSelectComponent
from bauh.view.qt import css
from bauh.view.qt.components import MultipleSelectQt, new_single_select
from bauh.view.util.translation import I18n
class ConfirmationDialog(QMessageBox):
def __init__(self, title: str, body: str, locale_keys: dict, components: List[ViewComponent] = None, confirmation_label: str = None, deny_label: str = None):
def __init__(self, title: str, body: str, i18n: I18n, components: List[ViewComponent] = None, confirmation_label: str = None, deny_label: str = None):
super(ConfirmationDialog, self).__init__()
self.setWindowTitle(title)
self.setStyleSheet('QLabel { margin-right: 25px; }')
self.bt_yes = self.addButton(locale_keys['popup.button.yes'] if not confirmation_label else confirmation_label.capitalize(), QMessageBox.YesRole)
self.bt_yes = self.addButton(i18n['popup.button.yes'] if not confirmation_label else confirmation_label.capitalize(), QMessageBox.YesRole)
self.bt_yes.setStyleSheet(css.OK_BUTTON)
self.addButton(locale_keys['popup.button.no'] if not deny_label else deny_label.capitalize(), QMessageBox.NoRole)
self.addButton(i18n['popup.button.no'] if not deny_label else deny_label.capitalize(), QMessageBox.NoRole)
if body:
if not components:

View File

@@ -6,6 +6,7 @@ from bauh.api.abstract.view import MessageType
from bauh.view.util import resource
from bauh.view.qt import css
from bauh.view.util.translation import I18n
MSG_TYPE_MAP = {
MessageType.ERROR: QMessageBox.Critical,
@@ -26,7 +27,7 @@ def show_message(title: str, body: str, type_: MessageType, icon: QIcon = QIcon(
popup.exec_()
def ask_confirmation(title: str, body: str, locale_keys: dict, icon: QIcon = QIcon(resource.get_path('img/logo.svg')), widgets: List[QWidget] = None):
def ask_confirmation(title: str, body: str, i18n: I18n, icon: QIcon = QIcon(resource.get_path('img/logo.svg')), widgets: List[QWidget] = None):
diag = QMessageBox()
diag.setIcon(QMessageBox.Question)
diag.setWindowTitle(title)
@@ -42,10 +43,10 @@ def ask_confirmation(title: str, body: str, locale_keys: dict, icon: QIcon = QIc
diag.layout().addWidget(wbody, 0, 1)
bt_yes = diag.addButton(locale_keys['popup.button.yes'], QMessageBox.YesRole)
bt_yes = diag.addButton(i18n['popup.button.yes'], QMessageBox.YesRole)
bt_yes.setStyleSheet(css.OK_BUTTON)
diag.addButton(locale_keys['popup.button.no'], QMessageBox.NoRole)
diag.addButton(i18n['popup.button.no'], QMessageBox.NoRole)
if icon:
diag.setWindowIcon(icon)

View File

@@ -9,11 +9,12 @@ from bauh.view.core.controller import GenericSoftwareManager
from bauh.view.util import resource
from bauh.view.qt import qt_utils, css
from bauh.view.qt.components import MultipleSelectQt, CheckboxQt, new_spacer
from bauh.view.util.translation import I18n
class GemSelectorPanel(QWidget):
def __init__(self, window: QWidget, manager: GenericSoftwareManager, i18n: dict, config: Configuration, show_panel_after_restart: bool = False):
def __init__(self, window: QWidget, manager: GenericSoftwareManager, i18n: I18n, config: Configuration, show_panel_after_restart: bool = False):
super(GemSelectorPanel, self).__init__()
self.window = window
self.manager = manager

View File

@@ -7,11 +7,12 @@ from PyQt5.QtWidgets import QDialog, QVBoxLayout, QTableWidget, QTableWidgetItem
from bauh.api.abstract.cache import MemoryCache
from bauh.api.abstract.model import PackageHistory
from bauh.view.util.translation import I18n
class HistoryDialog(QDialog):
def __init__(self, history: PackageHistory, icon_cache: MemoryCache, i18n: dict):
def __init__(self, history: PackageHistory, icon_cache: MemoryCache, i18n: I18n):
super(HistoryDialog, self).__init__()
self.setWindowFlags(self.windowFlags() | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint)

View File

@@ -4,13 +4,14 @@ from PyQt5.QtWidgets import QDialog, QVBoxLayout, QGroupBox, \
QLineEdit, QLabel, QGridLayout, QPushButton, QPlainTextEdit, QToolBar
from bauh.api.abstract.cache import MemoryCache
from bauh.view.util.translation import I18n
IGNORED_ATTRS = {'name', '__app__'}
class InfoDialog(QDialog):
def __init__(self, app: dict, icon_cache: MemoryCache, i18n: dict, screen_size: QSize()):
def __init__(self, app: dict, icon_cache: MemoryCache, i18n: I18n, screen_size: QSize()):
super(InfoDialog, self).__init__()
self.setWindowTitle(str(app['__app__']))
self.screen_size = screen_size

View File

@@ -7,13 +7,14 @@ from bauh.api.abstract.view import MessageType
from bauh.commons.system import new_subprocess
from bauh.view.qt.dialog import show_message
from bauh.view.util import resource
from bauh.view.util.translation import I18n
def is_root():
return os.getuid() == 0
def ask_root_password(i18n: dict):
def ask_root_password(i18n: I18n):
diag = QInputDialog()
diag.setStyleSheet("""QLineEdit { border-radius: 5px; font-size: 16px }""")
diag.setInputMode(QInputDialog.TextInput)

View File

@@ -12,6 +12,7 @@ from bauh.view.qt import qt_utils
from bauh.view.qt.components import new_spacer
from bauh.view.qt.thread import AnimateProgress
from bauh.view.qt.view_model import PackageView
from bauh.view.util.translation import I18n
class ScreenshotsDialog(QDialog):
@@ -19,7 +20,7 @@ class ScreenshotsDialog(QDialog):
MAX_HEIGHT = 600
MAX_WIDTH = 800
def __init__(self, pkg: PackageView, http_client: HttpClient, icon_cache: MemoryCache, i18n: dict, screenshots: List[QPixmap], logger: logging.Logger):
def __init__(self, pkg: PackageView, http_client: HttpClient, icon_cache: MemoryCache, i18n: I18n, screenshots: List[QPixmap], logger: logging.Logger):
super(ScreenshotsDialog, self).__init__()
self.setWindowTitle(str(pkg))
self.screenshots = screenshots

View File

@@ -5,11 +5,12 @@ from bauh.commons.html import bold
from bauh.view.core import config
from bauh.view.util import util
from bauh.view.qt import dialog
from bauh.view.util.translation import I18n
class StylesComboBox(QComboBox):
def __init__(self, parent: QWidget, i18n: dict, show_panel_after_restart: bool):
def __init__(self, parent: QWidget, i18n: I18n, show_panel_after_restart: bool):
super(StylesComboBox, self).__init__(parent=parent)
self.app = QApplication.instance()
self.styles = []

View File

@@ -14,6 +14,7 @@ from bauh.api.abstract.model import PackageUpdate
from bauh.view.util import util, resource
from bauh.view.qt.about import AboutDialog
from bauh.view.qt.window import ManageWindow
from bauh.view.util.translation import I18n
class UpdateCheck(QThread):
@@ -35,7 +36,7 @@ class UpdateCheck(QThread):
class TrayIcon(QSystemTrayIcon):
def __init__(self, i18n: dict, manager: SoftwareManager, manage_window: ManageWindow, check_interval: int = 60, update_notification: bool = True):
def __init__(self, i18n: I18n, manager: SoftwareManager, manage_window: ManageWindow, check_interval: int = 60, update_notification: bool = True):
super(TrayIcon, self).__init__()
self.i18n = i18n
self.manager = manager

View File

@@ -12,9 +12,9 @@ from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.model import PackageStatus, SoftwarePackage, PackageAction
from bauh.api.abstract.view import InputViewComponent, MessageType
from bauh.api.exception import NoInternetException
from bauh.api.http import HttpClient
from bauh.view.qt import commons
from bauh.view.qt.view_model import PackageView
from bauh.view.util.translation import I18n
RE_VERSION_IN_NAME = re.compile(r'\s+version\s+[\w\.]+\s*$')
@@ -76,12 +76,12 @@ class AsyncAction(QThread, ProcessWatcher):
class UpdateSelectedApps(AsyncAction):
def __init__(self, manager: SoftwareManager, locale_keys: dict, apps_to_update: List[PackageView] = None):
def __init__(self, manager: SoftwareManager, i18n: I18n, apps_to_update: List[PackageView] = None):
super(UpdateSelectedApps, self).__init__()
self.apps_to_update = apps_to_update
self.manager = manager
self.root_password = None
self.locale_keys = locale_keys
self.i18n = i18n
def run(self):
@@ -93,7 +93,7 @@ class UpdateSelectedApps(AsyncAction):
name = app.model.name if not RE_VERSION_IN_NAME.findall(app.model.name) else app.model.name.split('version')[0].strip()
self.change_status('{} {} {}...'.format(self.locale_keys['manage_window.status.upgrading'], name, app.model.version))
self.change_status('{} {} {}...'.format(self.i18n['manage_window.status.upgrading'], name, app.model.version))
success = bool(self.manager.update(app.model, self.root_password, self))
self.change_substatus('')
@@ -168,11 +168,11 @@ class UninstallApp(AsyncAction):
class DowngradeApp(AsyncAction):
def __init__(self, manager: SoftwareManager, locale_keys: dict, app: PackageView = None):
def __init__(self, manager: SoftwareManager, i18n: I18n, app: PackageView = None):
super(DowngradeApp, self).__init__()
self.manager = manager
self.app = app
self.locale_keys = locale_keys
self.i18n = i18n
self.root_password = None
def run(self):
@@ -182,7 +182,7 @@ class DowngradeApp(AsyncAction):
success = self.manager.downgrade(self.app.model, self.root_password, self)
except (requests.exceptions.ConnectionError, NoInternetException) as e:
success = False
self.print(self.locale_keys['internet.required'])
self.print(self.i18n['internet.required'])
finally:
self.notify_finished({'app': self.app, 'success': success})
self.app = None
@@ -206,18 +206,18 @@ class GetAppInfo(AsyncAction):
class GetAppHistory(AsyncAction):
def __init__(self, manager: SoftwareManager, locale_keys: dict, app: PackageView = None):
def __init__(self, manager: SoftwareManager, i18n: I18n, app: PackageView = None):
super(GetAppHistory, self).__init__()
self.app = app
self.manager = manager
self.locale_keys = locale_keys
self.i18n = i18n
def run(self):
if self.app:
try:
self.notify_finished({'history': self.manager.get_history(self.app.model)})
except (requests.exceptions.ConnectionError, NoInternetException) as e:
self.notify_finished({'error': self.locale_keys['internet.required']})
self.notify_finished({'error': self.i18n['internet.required']})
finally:
self.app = None
@@ -246,13 +246,13 @@ class SearchPackages(AsyncAction):
class InstallPackage(AsyncAction):
def __init__(self, manager: SoftwareManager, disk_cache: bool, icon_cache: MemoryCache, locale_keys: dict, pkg: PackageView = None):
def __init__(self, manager: SoftwareManager, disk_cache: bool, icon_cache: MemoryCache, i18n: I18n, pkg: PackageView = None):
super(InstallPackage, self).__init__()
self.pkg = pkg
self.manager = manager
self.icon_cache = icon_cache
self.disk_cache = disk_cache
self.locale_keys = locale_keys
self.i18n = i18n
self.root_password = None
def run(self):
@@ -272,7 +272,7 @@ class InstallPackage(AsyncAction):
only_icon=False)
except (requests.exceptions.ConnectionError, NoInternetException):
success = False
self.print(self.locale_keys['internet.required'])
self.print(self.i18n['internet.required'])
finally:
self.signal_finished.emit({'success': success, 'pkg': self.pkg})
self.pkg = None
@@ -403,9 +403,9 @@ class ListWarnings(QThread):
signal_warnings = pyqtSignal(list)
def __init__(self, man: SoftwareManager, locale_keys: dict):
def __init__(self, man: SoftwareManager, i18n: I18n):
super(QThread, self).__init__()
self.locale_keys = locale_keys
self.i18n = i18n
self.man = man
def run(self):
@@ -464,12 +464,13 @@ class ApplyFilters(AsyncAction):
class CustomAction(AsyncAction):
def __init__(self, manager: SoftwareManager, custom_action: PackageAction = None, pkg: PackageView = None, root_password: str = None):
def __init__(self, manager: SoftwareManager, i18n: I18n, custom_action: PackageAction = None, pkg: PackageView = None, root_password: str = None):
super(CustomAction, self).__init__()
self.manager = manager
self.pkg = pkg
self.custom_action = custom_action
self.root_password = root_password
self.i18n = i18n
def run(self):
success = True
@@ -481,7 +482,7 @@ class CustomAction(AsyncAction):
watcher=self)
except (requests.exceptions.ConnectionError, NoInternetException):
success = False
self.signal_output.emit(self.locale_keys['internet.required'])
self.signal_output.emit(self.i18n['internet.required'])
self.notify_finished({'success': success, 'pkg': self.pkg})
self.pkg = None

View File

@@ -36,6 +36,7 @@ from bauh.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, D
from bauh.view.qt.view_model import PackageView
from bauh.view.qt.view_utils import load_icon
from bauh.view.util import util, resource
from bauh.view.util.translation import I18n
DARK_ORANGE = '#FF4500'
@@ -50,7 +51,7 @@ class ManageWindow(QWidget):
signal_user_res = pyqtSignal(bool)
signal_table_update = pyqtSignal()
def __init__(self, i18n: dict, icon_cache: MemoryCache, manager: SoftwareManager, disk_cache: bool,
def __init__(self, i18n: I18n, icon_cache: MemoryCache, manager: SoftwareManager, disk_cache: bool,
download_icons: bool, screen_size, suggestions: bool, display_limit: int, config: Configuration,
context: ApplicationContext, notifications: bool, http_client: HttpClient, logger: logging.Logger,
tray_icon=None):
@@ -231,7 +232,7 @@ class ManageWindow(QWidget):
self.thread_downgrade = self._bind_async_action(DowngradeApp(self.manager, self.i18n), finished_call=self._finish_downgrade)
self.thread_suggestions = self._bind_async_action(FindSuggestions(man=self.manager), finished_call=self._finish_search, only_finished=True)
self.thread_run_app = self._bind_async_action(LaunchApp(self.manager), finished_call=self._finish_run_app, only_finished=False)
self.thread_custom_action = self._bind_async_action(CustomAction(manager=self.manager), finished_call=self._finish_custom_action)
self.thread_custom_action = self._bind_async_action(CustomAction(manager=self.manager, i18n=self.i18n), finished_call=self._finish_custom_action)
self.thread_screenshots = self._bind_async_action(GetScreenshots(self.manager), finished_call=self._finish_get_screenshots)
self.thread_apply_filters = ApplyFilters()
@@ -239,7 +240,7 @@ class ManageWindow(QWidget):
self.thread_apply_filters.signal_table.connect(self._update_table_and_upgrades)
self.signal_table_update.connect(self.thread_apply_filters.stop_waiting)
self.thread_install = InstallPackage(manager=self.manager, disk_cache=self.disk_cache, icon_cache=self.icon_cache, locale_keys=self.i18n)
self.thread_install = InstallPackage(manager=self.manager, disk_cache=self.disk_cache, icon_cache=self.icon_cache, i18n=self.i18n)
self._bind_async_action(self.thread_install, finished_call=self._finish_install)
self.thread_animate_progress = AnimateProgress()
@@ -289,7 +290,7 @@ class ManageWindow(QWidget):
self.dialog_about = None
self.first_refresh = suggestions
self.thread_warnings = ListWarnings(man=manager, locale_keys=i18n)
self.thread_warnings = ListWarnings(man=manager, i18n=i18n)
self.thread_warnings.signal_warnings.connect(self._show_warnings)
def set_tray_icon(self, tray_icon):
@@ -349,7 +350,7 @@ class ManageWindow(QWidget):
self.thread_animate_progress.pause()
diag = ConfirmationDialog(title=msg['title'],
body=msg['body'],
locale_keys=self.i18n,
i18n=self.i18n,
components=msg['components'],
confirmation_label=msg['confirmation_label'],
deny_label=msg['deny_label'])
@@ -792,7 +793,7 @@ class ManageWindow(QWidget):
if to_update and dialog.ask_confirmation(title=self.i18n['manage_window.upgrade_all.popup.title'],
body=self.i18n['manage_window.upgrade_all.popup.body'],
locale_keys=self.i18n,
i18n=self.i18n,
widgets=[UpdateToggleButton(None, self, self.i18n, clickable=False)]):
pwd = None

View File

@@ -0,0 +1,67 @@
import glob
import locale
from typing import Tuple
from bauh.view.util import resource
class I18n(dict):
def __init__(self, current_locale: dict, default_locale: dict):
super(I18n, self).__init__()
self.current = current_locale
self.default = default_locale
def __getitem__(self, item):
try:
return self.current.__getitem__(item)
except KeyError:
if self.default:
return self.default.__getitem__(item)
else:
raise
def get(self, *args, **kwargs):
res = self.current.get(args[0])
if res is None:
if self.default:
return self.default.get(*args, **kwargs)
else:
return self.current.get(*args, **kwargs)
return res
def get_locale_keys(key: str = None, locale_dir: str = resource.get_path('locale')) -> Tuple[str, dict]:
locale_path = None
if key is None:
current_locale = locale.getdefaultlocale()
else:
current_locale = [key.strip().lower()]
if current_locale:
current_locale = current_locale[0]
for locale_file in glob.glob(locale_dir + '/*'):
name = locale_file.split('/')[-1]
if current_locale == name or current_locale.startswith(name + '_'):
locale_path = locale_file
break
if not locale_path:
return key, {}
with open(locale_path, 'r') as f:
locale_keys = f.readlines()
locale_obj = {}
for line in locale_keys:
if line:
keyval = line.strip().split('=')
locale_obj[keyval[0].strip()] = keyval[1].strip()
return locale_path.split('/')[-1], locale_obj

View File

@@ -1,9 +1,6 @@
import glob
import locale
import os
import subprocess
import sys
from typing import Tuple
from PyQt5.QtCore import QCoreApplication
@@ -12,40 +9,6 @@ from bauh.commons.system import run_cmd
from bauh.view.util import resource
def get_locale_keys(key: str = None, locale_dir: str = resource.get_path('locale')) -> Tuple[str, dict]:
locale_path = None
if key is None:
current_locale = locale.getdefaultlocale()
else:
current_locale = [key.strip().lower()]
if current_locale:
current_locale = current_locale[0]
for locale_file in glob.glob(locale_dir + '/*'):
name = locale_file.split('/')[-1]
if current_locale == name or current_locale.startswith(name + '_'):
locale_path = locale_file
break
if not locale_path:
locale_path = resource.get_path('locale/en')
with open(locale_path, 'r') as f:
locale_keys = f.readlines()
locale_obj = {}
for line in locale_keys:
if line:
keyval = line.strip().split('=')
locale_obj[keyval[0].strip()] = keyval[1].strip()
return locale_path.split('/')[-1], locale_obj
def notify_user(msg: str, icon_path: str = resource.get_path('img/logo.svg')):
os.system("notify-send -a {} {} '{}'".format(__app_name__, "-i {}".format(icon_path) if icon_path else '', msg))