5 Commits

Author SHA1 Message Date
d7ab7dc7ce chore(changelog): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-01 15:56:01 +05:00
82a93abb7d chore(localization): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-01 15:55:01 +05:00
25439889f7 feat(context_menu_manager): add quick game launch via context menu
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-01 15:52:39 +05:00
4e5ccfc374 feat(file-explorer): add gamepad support for selecting mounted drives
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-01 12:07:43 +05:00
3d87718072 feat(file-explorer): add file selection on double-click
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-07-01 11:48:28 +05:00
13 changed files with 155 additions and 58 deletions

View File

@@ -10,6 +10,7 @@
- Начальная поддержка EGS (Без EOS, скачивания игр и запуска игр из сторонних магазинов) - Начальная поддержка EGS (Без EOS, скачивания игр и запуска игр из сторонних магазинов)
- Автодополнение bash для комманды portprotonqt - Автодополнение bash для комманды portprotonqt
- Поддержка геймпадов в диалоге выбора игры - Поддержка геймпадов в диалоге выбора игры
- Быстрый запуск игры через контекстное меню
### Changed ### Changed
- Удалены сборки для Fedora 40 - Удалены сборки для Fedora 40

View File

@@ -20,9 +20,9 @@ Current translation status:
| Locale | Progress | Translated | | Locale | Progress | Translated |
| :----- | -------: | ---------: | | :----- | -------: | ---------: |
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 182 | | [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 183 |
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 182 | | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 183 |
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 182 of 182 | | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 183 of 183 |
--- ---

View File

@@ -20,9 +20,9 @@
| Локаль | Прогресс | Переведено | | Локаль | Прогресс | Переведено |
| :----- | -------: | ---------: | | :----- | -------: | ---------: |
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 182 | | [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 183 |
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 182 | | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 183 |
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 182 из 182 | | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 183 из 183 |
--- ---

View File

@@ -129,6 +129,11 @@ class ContextMenuManager:
menu = QMenu(self.parent) menu = QMenu(self.parent)
menu.setStyleSheet(self.theme.CONTEXT_MENU_STYLE) menu.setStyleSheet(self.theme.CONTEXT_MENU_STYLE)
launch_action = menu.addAction(_("Launch Game"))
launch_action.triggered.connect(
lambda: self._launch_game(game_card)
)
favorites = read_favorites() favorites = read_favorites()
is_favorite = game_card.name in favorites is_favorite = game_card.name in favorites
favorite_action = menu.addAction( favorite_action = menu.addAction(
@@ -219,6 +224,40 @@ class ContextMenuManager:
menu.exec(game_card.mapToGlobal(pos)) menu.exec(game_card.mapToGlobal(pos))
def _launch_game(self, game_card):
"""
Launch a game using a validated exec_line, handling EGS games specifically.
Args:
game_card: The GameCard instance containing game data.
"""
if not self._check_portproton():
return
if game_card.game_source == "epic":
if not os.path.exists(self.legendary_path):
self.signals.show_warning_dialog.emit(
_("Error"),
_("Legendary executable not found at {path}").format(path=self.legendary_path)
)
return
# Construct EGS launch command
wrapper = "flatpak run ru.linux_gaming.PortProton"
start_sh_path = os.path.join(self.portproton_location, "data", "scripts", "start.sh")
if self.portproton_location and ".var" not in self.portproton_location:
wrapper = start_sh_path
if not os.path.exists(start_sh_path):
self.signals.show_warning_dialog.emit(
_("Error"),
_("start.sh not found at {path}").format(path=start_sh_path)
)
return
exec_line = f'"{self.legendary_path}" launch {game_card.appid} --no-wine --wrapper "env START_FROM_STEAM=1 {wrapper}"'
else:
exec_line = self._get_exec_line(game_card.name, game_card.exec_line)
if not exec_line:
return
self.parent.toggleGame(exec_line)
def add_egs_to_steam(self, game_name: str, app_name: str): def add_egs_to_steam(self, game_name: str, app_name: str):
""" """
Adds an EGS game to Steam using the egs_api. Adds an EGS game to Steam using the egs_api.

View File

@@ -3,7 +3,7 @@ import tempfile
from typing import cast, TYPE_CHECKING from typing import cast, TYPE_CHECKING
from PySide6.QtGui import QPixmap, QIcon from PySide6.QtGui import QPixmap, QIcon
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QDialog, QLineEdit, QFormLayout, QHBoxLayout, QLabel, QVBoxLayout, QListWidget, QScrollArea, QWidget, QListWidgetItem, QSizePolicy QDialog, QLineEdit, QFormLayout, QHBoxLayout, QLabel, QVBoxLayout, QListWidget, QScrollArea, QWidget, QListWidgetItem, QSizePolicy, QApplication
) )
from PySide6.QtCore import Qt, QObject, Signal, QMimeDatabase, QTimer from PySide6.QtCore import Qt, QObject, Signal, QMimeDatabase, QTimer
from icoextract import IconExtractor, IconExtractorError from icoextract import IconExtractor, IconExtractorError
@@ -167,6 +167,7 @@ class FileExplorer(QDialog):
self.drives_scroll.setFixedHeight(70) self.drives_scroll.setFixedHeight(70)
self.main_layout.addWidget(self.drives_scroll) self.main_layout.addWidget(self.drives_scroll)
self.drives_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.drives_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.drives_scroll.setFocusPolicy(Qt.FocusPolicy.StrongFocus) # Allow focus on scroll area
# Путь # Путь
self.path_label = QLabel() self.path_label = QLabel()
@@ -177,7 +178,7 @@ class FileExplorer(QDialog):
self.file_list = QListWidget() self.file_list = QListWidget()
self.file_list.setStyleSheet(self.theme.FILE_EXPLORER_STYLE) self.file_list.setStyleSheet(self.theme.FILE_EXPLORER_STYLE)
self.file_list.itemClicked.connect(self.handle_item_click) self.file_list.itemClicked.connect(self.handle_item_click)
self.file_list.itemDoubleClicked.connect(self.handle_item_double_click) # Подключение двойного клика self.file_list.itemDoubleClicked.connect(self.handle_item_double_click)
self.main_layout.addWidget(self.file_list) self.main_layout.addWidget(self.file_list)
# Кнопки # Кнопки
@@ -227,6 +228,10 @@ class FileExplorer(QDialog):
# Открываем директорию # Открываем директорию
self.current_path = os.path.normpath(full_path) self.current_path = os.path.normpath(full_path)
self.update_file_list() self.update_file_list()
elif not self.directory_only:
# Выбираем файл, если directory_only=False
self.file_signal.file_selected.emit(os.path.normpath(full_path))
self.accept()
else: else:
logger.debug("Double-clicked item is not a directory, ignoring: %s", full_path) logger.debug("Double-clicked item is not a directory, ignoring: %s", full_path)
except Exception as e: except Exception as e:
@@ -287,14 +292,37 @@ class FileExplorer(QDialog):
widget.deleteLater() widget.deleteLater()
drives = self.get_mounted_drives() drives = self.get_mounted_drives()
self.drive_buttons = [] # Store buttons for navigation
for drive in drives: for drive in drives:
drive_name = os.path.basename(drive) or drive.split('/')[-1] or drive drive_name = os.path.basename(drive) or drive.split('/')[-1] or drive
button = AutoSizeButton(drive_name, icon=self.theme_manager.get_icon("mount_point")) button = AutoSizeButton(drive_name, icon=self.theme_manager.get_icon("mount_point"))
button.setStyleSheet(self.theme.ACTION_BUTTON_STYLE) button.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
button.setFocusPolicy(Qt.FocusPolicy.StrongFocus) # Make button focusable
button.clicked.connect(lambda checked, path=drive: self.change_drive(path)) button.clicked.connect(lambda checked, path=drive: self.change_drive(path))
self.drives_layout.addWidget(button) self.drives_layout.addWidget(button)
self.drive_buttons.append(button)
self.drives_layout.addStretch() self.drives_layout.addStretch()
# Set focus to first drive button if available
if self.drive_buttons:
self.drive_buttons[0].setFocus()
def select_drive(self):
"""Handle drive selection via gamepad"""
focused_widget = QApplication.focusWidget()
if isinstance(focused_widget, AutoSizeButton) and focused_widget in self.drive_buttons:
drive_path = None
for drive in self.get_mounted_drives():
drive_name = os.path.basename(drive) or drive.split('/')[-1] or drive
if drive_name == focused_widget.text():
drive_path = drive
break
if drive_path and os.path.isdir(drive_path) and os.access(drive_path, os.R_OK):
self.current_path = os.path.normpath(drive_path)
self.update_file_list()
else:
logger.warning(f"Путь диска недоступен: {drive_path}")
def change_drive(self, drive_path): def change_drive(self, drive_path):
"""Переход к выбранному диску""" """Переход к выбранному диску"""
if os.path.isdir(drive_path) and os.access(drive_path, os.R_OK): if os.path.isdir(drive_path) and os.access(drive_path, os.R_OK):

View File

@@ -9,7 +9,7 @@ from PySide6.QtCore import Qt, QObject, QEvent, QPoint, Signal, Slot, QTimer
from PySide6.QtGui import QKeyEvent from PySide6.QtGui import QKeyEvent
from portprotonqt.logger import get_logger from portprotonqt.logger import get_logger
from portprotonqt.image_utils import FullscreenDialog from portprotonqt.image_utils import FullscreenDialog
from portprotonqt.custom_widgets import NavLabel from portprotonqt.custom_widgets import NavLabel, AutoSizeButton
from portprotonqt.game_card import GameCard from portprotonqt.game_card import GameCard
from portprotonqt.config_utils import read_fullscreen_config, read_window_geometry, save_window_geometry, read_auto_fullscreen_gamepad, read_rumble_config from portprotonqt.config_utils import read_fullscreen_config, read_window_geometry, save_window_geometry, read_auto_fullscreen_gamepad, read_rumble_config
from portprotonqt.dialogs import AddGameDialog from portprotonqt.dialogs import AddGameDialog
@@ -162,7 +162,26 @@ class InputManager(QObject):
if not self.file_explorer or not hasattr(self.file_explorer, 'file_list'): if not self.file_explorer or not hasattr(self.file_explorer, 'file_list'):
return return
if button_code in BUTTONS['add_game']: focused_widget = QApplication.focusWidget()
if button_code in BUTTONS['confirm']: # A button (BTN_SOUTH)
if isinstance(focused_widget, AutoSizeButton) and hasattr(self.file_explorer, 'drive_buttons') and focused_widget in self.file_explorer.drive_buttons:
self.file_explorer.select_drive() # Select the focused drive
elif self.file_explorer.file_list.count() == 0:
return
else:
selected = self.file_explorer.file_list.currentItem().text()
full_path = os.path.join(self.file_explorer.current_path, selected)
if os.path.isdir(full_path):
# Открываем директорию
self.file_explorer.current_path = os.path.normpath(full_path)
self.file_explorer.update_file_list()
elif not self.file_explorer.directory_only:
# Выбираем файл, если directory_only=False
self.file_explorer.file_signal.file_selected.emit(os.path.normpath(full_path))
self.file_explorer.accept()
else:
logger.debug("Selected item is not a directory, cannot select: %s", full_path)
elif button_code in BUTTONS['add_game']: # X button
if self.file_explorer.file_list.count() == 0: if self.file_explorer.file_list.count() == 0:
return return
selected = self.file_explorer.file_list.currentItem().text() selected = self.file_explorer.file_list.currentItem().text()
@@ -173,24 +192,9 @@ class InputManager(QObject):
self.file_explorer.accept() self.file_explorer.accept()
else: else:
logger.debug("Selected item is not a directory: %s", full_path) logger.debug("Selected item is not a directory: %s", full_path)
elif button_code in BUTTONS['confirm']: elif button_code in BUTTONS['back']: # B button
if self.file_explorer.file_list.count() == 0:
return
selected = self.file_explorer.file_list.currentItem().text()
full_path = os.path.join(self.file_explorer.current_path, selected)
if os.path.isdir(full_path):
# Открываем директорию
self.file_explorer.current_path = os.path.normpath(full_path)
self.file_explorer.update_file_list()
elif not self.file_explorer.directory_only:
# Выбираем файл, если directory_only=False
self.file_explorer.file_signal.file_selected.emit(os.path.normpath(full_path))
self.file_explorer.accept()
else:
logger.debug("Selected item is not a directory, cannot select: %s", full_path)
elif button_code in BUTTONS['back']:
self.file_explorer.close() self.file_explorer.close()
elif button_code in BUTTONS['prev_dir']: elif button_code in BUTTONS['prev_dir']: # Y button
self.file_explorer.previous_dir() self.file_explorer.previous_dir()
else: else:
if self.original_button_handler: if self.original_button_handler:
@@ -204,15 +208,33 @@ class InputManager(QObject):
if not self.file_explorer or not hasattr(self.file_explorer, 'file_list') or not self.file_explorer.file_list: if not self.file_explorer or not hasattr(self.file_explorer, 'file_list') or not self.file_explorer.file_list:
return return
if not self.file_explorer.file_list.count(): focused_widget = QApplication.focusWidget()
return if code in (ecodes.ABS_HAT0X, ecodes.ABS_X) and hasattr(self.file_explorer, 'drive_buttons') and self.file_explorer.drive_buttons:
# Navigate drive buttons horizontally
if code in (ecodes.ABS_HAT0Y, ecodes.ABS_Y): if not isinstance(focused_widget, AutoSizeButton) or focused_widget not in self.file_explorer.drive_buttons:
# If not focused on a drive button, focus the first one
self.file_explorer.drive_buttons[0].setFocus()
return
current_idx = self.file_explorer.drive_buttons.index(focused_widget)
if value < 0: # Left
next_idx = max(current_idx - 1, 0)
self.file_explorer.drive_buttons[next_idx].setFocus()
elif value > 0: # Right
next_idx = min(current_idx + 1, len(self.file_explorer.drive_buttons) - 1)
self.file_explorer.drive_buttons[next_idx].setFocus()
elif code in (ecodes.ABS_HAT0Y, ecodes.ABS_Y):
if isinstance(focused_widget, AutoSizeButton) and focused_widget in self.file_explorer.drive_buttons:
# Move focus to file list if navigating down from drive buttons
if value > 0 and self.file_explorer.file_list.count() > 0:
self.file_explorer.file_list.setFocus()
self.file_explorer.file_list.setCurrentRow(0)
self.file_explorer.file_list.scrollToItem(self.file_explorer.file_list.currentItem())
return
# Для D-pad - реакция с фиксированной скоростью # Для D-pad - реакция с фиксированной скоростью
if code == ecodes.ABS_HAT0Y: if code == ecodes.ABS_HAT0Y:
if value != 0: if value != 0:
self.current_direction = value self.current_direction = value
self.stick_value = 1.0 # Максимальная скорость для D-pad, чтобы скачков не было self.stick_value = 1.0 # Максимальная скорость для D-pad
if not self.nav_timer.isActive(): if not self.nav_timer.isActive():
self.file_explorer.move_selection(self.current_direction) self.file_explorer.move_selection(self.current_direction)
self.last_nav_time = current_time self.last_nav_time = current_time
@@ -220,7 +242,6 @@ class InputManager(QObject):
else: else:
self.current_direction = 0 self.current_direction = 0
self.nav_timer.stop() self.nav_timer.stop()
# Для стика - плавное управление с учетом степени отклонения # Для стика - плавное управление с учетом степени отклонения
elif code == ecodes.ABS_Y: elif code == ecodes.ABS_Y:
if abs(value) < self.dead_zone: if abs(value) < self.dead_zone:
@@ -229,19 +250,15 @@ class InputManager(QObject):
self.nav_timer.stop() self.nav_timer.stop()
self.stick_activated = False self.stick_activated = False
return return
# Рассчитываем "силу" отклонения (0.3 - 1.0)
normalized_value = (abs(value) - self.dead_zone) / (32768 - self.dead_zone) normalized_value = (abs(value) - self.dead_zone) / (32768 - self.dead_zone)
speed_factor = 0.3 + (normalized_value * 0.7) # От 30% до 100% скорости speed_factor = 0.3 + (normalized_value * 0.7) # От 30% до 100% скорости
self.current_direction = -1 if value < 0 else 1 self.current_direction = -1 if value < 0 else 1
self.stick_value = speed_factor self.stick_value = speed_factor
self.stick_activated = True self.stick_activated = True
if not self.nav_timer.isActive(): if not self.nav_timer.isActive():
self.file_explorer.move_selection(self.current_direction) self.file_explorer.move_selection(self.current_direction)
self.last_nav_time = current_time self.last_nav_time = current_time
self.nav_timer.start(int(self.initial_nav_delay * 1000)) self.nav_timer.start(int(self.initial_nav_delay * 1000))
elif self.original_dpad_handler: elif self.original_dpad_handler:
self.original_dpad_handler(code, value, current_time) self.original_dpad_handler(code, value, current_time)
except Exception as e: except Exception as e:

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-07-01 00:15+0500\n" "POT-Creation-Date: 2025-07-01 15:54+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de_DE\n" "Language: de_DE\n"
@@ -26,6 +26,9 @@ msgstr ""
msgid "PortProton is not found" msgid "PortProton is not found"
msgstr "" msgstr ""
msgid "Launch Game"
msgstr ""
msgid "Remove from Favorites" msgid "Remove from Favorites"
msgstr "" msgstr ""
@@ -66,6 +69,10 @@ msgstr ""
msgid "Legendary executable not found at {path}" msgid "Legendary executable not found at {path}"
msgstr "" msgstr ""
#, python-brace-format
msgid "start.sh not found at {path}"
msgstr ""
msgid "Success" msgid "Success"
msgstr "" msgstr ""
@@ -110,10 +117,6 @@ msgstr ""
msgid "Removed '{game_name}' from favorites" msgid "Removed '{game_name}' from favorites"
msgstr "" msgstr ""
#, python-brace-format
msgid "start.sh not found at {path}"
msgstr ""
#, python-brace-format #, python-brace-format
msgid "Launch game \"{name}\" with PortProton" msgid "Launch game \"{name}\" with PortProton"
msgstr "" msgstr ""

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-07-01 00:15+0500\n" "POT-Creation-Date: 2025-07-01 15:54+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: es_ES\n" "Language: es_ES\n"
@@ -26,6 +26,9 @@ msgstr ""
msgid "PortProton is not found" msgid "PortProton is not found"
msgstr "" msgstr ""
msgid "Launch Game"
msgstr ""
msgid "Remove from Favorites" msgid "Remove from Favorites"
msgstr "" msgstr ""
@@ -66,6 +69,10 @@ msgstr ""
msgid "Legendary executable not found at {path}" msgid "Legendary executable not found at {path}"
msgstr "" msgstr ""
#, python-brace-format
msgid "start.sh not found at {path}"
msgstr ""
msgid "Success" msgid "Success"
msgstr "" msgstr ""
@@ -110,10 +117,6 @@ msgstr ""
msgid "Removed '{game_name}' from favorites" msgid "Removed '{game_name}' from favorites"
msgstr "" msgstr ""
#, python-brace-format
msgid "start.sh not found at {path}"
msgstr ""
#, python-brace-format #, python-brace-format
msgid "Launch game \"{name}\" with PortProton" msgid "Launch game \"{name}\" with PortProton"
msgstr "" msgstr ""

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PortProtonQt 0.1.1\n" "Project-Id-Version: PortProtonQt 0.1.1\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-07-01 00:15+0500\n" "POT-Creation-Date: 2025-07-01 15:54+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -24,6 +24,9 @@ msgstr ""
msgid "PortProton is not found" msgid "PortProton is not found"
msgstr "" msgstr ""
msgid "Launch Game"
msgstr ""
msgid "Remove from Favorites" msgid "Remove from Favorites"
msgstr "" msgstr ""
@@ -64,6 +67,10 @@ msgstr ""
msgid "Legendary executable not found at {path}" msgid "Legendary executable not found at {path}"
msgstr "" msgstr ""
#, python-brace-format
msgid "start.sh not found at {path}"
msgstr ""
msgid "Success" msgid "Success"
msgstr "" msgstr ""
@@ -108,10 +115,6 @@ msgstr ""
msgid "Removed '{game_name}' from favorites" msgid "Removed '{game_name}' from favorites"
msgstr "" msgstr ""
#, python-brace-format
msgid "start.sh not found at {path}"
msgstr ""
#, python-brace-format #, python-brace-format
msgid "Launch game \"{name}\" with PortProton" msgid "Launch game \"{name}\" with PortProton"
msgstr "" msgstr ""

View File

@@ -9,8 +9,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-07-01 00:15+0500\n" "POT-Creation-Date: 2025-07-01 15:54+0500\n"
"PO-Revision-Date: 2025-07-01 00:15+0500\n" "PO-Revision-Date: 2025-07-01 15:54+0500\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language: ru_RU\n" "Language: ru_RU\n"
"Language-Team: ru_RU <LL@li.org>\n" "Language-Team: ru_RU <LL@li.org>\n"
@@ -27,6 +27,9 @@ msgstr "Ошибка"
msgid "PortProton is not found" msgid "PortProton is not found"
msgstr "PortProton не найден" msgstr "PortProton не найден"
msgid "Launch Game"
msgstr "Запустить игру"
msgid "Remove from Favorites" msgid "Remove from Favorites"
msgstr "Удалить из Избранного" msgstr "Удалить из Избранного"
@@ -67,6 +70,10 @@ msgstr "Удалить из PortProton"
msgid "Legendary executable not found at {path}" msgid "Legendary executable not found at {path}"
msgstr "Legendary не найден по пути {path}" msgstr "Legendary не найден по пути {path}"
#, python-brace-format
msgid "start.sh not found at {path}"
msgstr "start.sh не найден по адресу {path}"
msgid "Success" msgid "Success"
msgstr "Успешно" msgstr "Успешно"
@@ -113,10 +120,6 @@ msgstr "'{game_name}' был(а) добавлен(а) в избранное"
msgid "Removed '{game_name}' from favorites" msgid "Removed '{game_name}' from favorites"
msgstr "'{game_name}' был(а) удалён(а) из избранного" msgstr "'{game_name}' был(а) удалён(а) из избранного"
#, python-brace-format
msgid "start.sh not found at {path}"
msgstr "start.sh не найден по адресу {path}"
#, python-brace-format #, python-brace-format
msgid "Launch game \"{name}\" with PortProton" msgid "Launch game \"{name}\" with PortProton"
msgstr "Запустить игру \"{name}\" с помощью PortProton" msgstr "Запустить игру \"{name}\" с помощью PortProton"