Repository: AlexDev505/AudioBookPlayer
Pull request #14: first round of translation done
Author: login: Koladweep
Created: 2025-03-20T12:06:22Z | Merged: 2025-03-26T14:02:34Z



Diff:
diff --git a/ABPlayer/config.py b/ABPlayer/config.py
index 789a70d..9f6b392 100644
--- a/ABPlayer/config.py
+++ b/ABPlayer/config.py
@@ -31,7 +31,7 @@ def init() -> None:
 
 def _create_config() -> None:
     """
-    Создает файл конфигурации со значениями по умолчанию.
+    Creates a configuration file with default values.
     """
     with open(os.environ["CONFIG_PATH"], "wb") as file:
         file.write(orjson.dumps(FIELDS))
@@ -39,7 +39,7 @@ def _create_config() -> None:
 
 def _load_config() -> dict:
     """
-    Загружает данные из файла конфигурации.
+    Loads data from the configuration file.
     """
     try:
         with open(os.environ["CONFIG_PATH"], "rb") as file:
@@ -51,9 +51,9 @@ def _load_config() -> dict:
 
 def update_config(*, update_env=True, **fields: [str, ty.Any]) -> None:
     """
-    Обновляет поля в файле конфигурации.
-    :param update_env: True - произойдет обновление переменных окружения.
-    :param fields: Поля для обновления.
+    Updates fields in the configuration file.
+    :param update_env: True - environment variables will be updated.
+    :param fields: Fields to update.
     """
     logger.opt(colors=True).debug(
         "Configuration update. "
@@ -68,9 +68,9 @@ def update_config(*, update_env=True, **fields: [str, ty.Any]) -> None:
 
 def _validate_config(config: dict) -> dict:
     """
-    Проверяет валидность данных конфигурации.
-    Исправляет в случае ошибок.
-    :returns: Данные конфигурации.
+    Validates the configuration data.
+    Fixes in case of errors.
+    :returns: Configuration data.
     """
     if any(key not in FIELDS for key in config):
         config = {field: config.get(field, FIELDS[field]) for field in FIELDS}
@@ -100,7 +100,7 @@ def _validate_config(config: dict) -> dict:
 
 def _add_to_env(config: dict) -> None:
     """
-    Добавляет данные конфигурации в переменные окружения.
+    Adds configuration data to environment variables.
     """
     logger.trace("adding configuration to the virtual environment")
     for field in FIELDS:
diff --git a/ABPlayer/drivers/akniga.py b/ABPlayer/drivers/akniga.py
index 5c7321f..d5126d6 100644
--- a/ABPlayer/drivers/akniga.py
+++ b/ABPlayer/drivers/akniga.py
@@ -15,8 +15,8 @@
 
 
 """
-На сайте AKniga информация о книге загружается динамически через JavaScript.
-Поэтому для получения полных данных о книге нам необходима среда исполнения JS.
+On the AKniga website, book information is loaded dynamically via JavaScript.
+Therefore, to get complete book data, we need a JavaScript execution environment.
 """
 
 
@@ -24,25 +24,25 @@ class BaseJsApi(ABC):
     @abstractmethod
     def get_book_data(self, url: str) -> dict:
         """
-        Метод, возвращающий динамически загружаемые данные о книге.
+        Method that returns dynamically loaded book data.
         {
             "author": <str>,
-            "titleonly": <str>,  # название книги в чистом виде
+            "titleonly": <str>,  # clean book title
             "items": [{
-                "file": <int>,  # номер файла
-                "title": <str>,  # название главы
-                "time_from_start": <str>,  # точка начала
-                "time_finish": <str>,  # точка окончания
+                "file": <int>,  # file number
+                "title": <str>,  # chapter title
+                "time_from_start": <str>,  # start time
+                "time_finish": <str>,  # end time
             }, ...],
-            "m3u8": <str>,  # ссылка на файл m3u8
+            "m3u8": <str>,  # link to m3u8 file
         }
         """
 
 
 class PyWebViewJsApi(BaseJsApi):
     """
-    Среда исполнения JS, использующая Pywebview.
-    Создаёт скрытое окно, загружает там страницу книги и берет с нее данные.
+    JavaScript execution environment using Pywebview.
+    Creates a hidden window, loads the book page there, and retrieves data from it.
     """
 
     def __init__(self):
@@ -52,10 +52,10 @@ def __init__(self):
 
     def get_book_data(self, url: str) -> dict:
         self._window = webview.create_window("", url=url, hidden=True)
-        # Выполнение `self._get_book_data` после загрузки страницы
+        # Execute `self._get_book_data` after the page loads
         self._window.events.loaded += self._get_book_data
         self._active = True
-        if len(webview.windows) == 1:  # На данный момент нет запущенных окон pywebview
+        if len(webview.windows) == 1:  # Currently, there are no running pywebview windows
             webview.start()
         else:
             while self._active:
diff --git a/ABPlayer/drivers/base.py b/ABPlayer/drivers/base.py
index 59d813a..136c113 100644
--- a/ABPlayer/drivers/base.py
+++ b/ABPlayer/drivers/base.py
@@ -20,7 +20,7 @@
 
 class DownloadProcessStatus(Enum):
     """
-    Статусы прогресса скачивания.
+    Download progress statuses.
     """
 
     WAITING = "waiting"
@@ -34,8 +34,8 @@ class DownloadProcessStatus(Enum):
 
 class BaseDownloadProcessHandler(ABC):
     """
-    Обработчик процесса скачивания.
-    Визуализирует процесс скачивания книги.
+    Download process handler.
+    Visualizes the book download process.
     >>> N = 10
     >>> process_handler = BaseDownloadProcessHandler()
     >>> process_handler.init(N, status=DownloadProcessStatus.DOWNLOADING)
@@ -72,7 +72,7 @@ def finish(self) -> None:
     @abstractmethod
     def show_progress(self) -> None:
         """
-        Отображает прогресс.
+        Displays the progress.
         """
 
     def __repr__(self):
@@ -86,7 +86,7 @@ def __format__(self, format_spec):
 
 class BaseDownloader(ABC):
     """
-    Загрузчик файлов.
+    File downloader.
     """
 
     def __init__(
@@ -95,13 +95,13 @@ def __init__(
         self.book = book
         self.process_handler = process_handler
 
-        # Общий размер файлов(в байтах)
-        # Определяется, только если передан `process_handler`
+        # Total size of files (in bytes)
+        # Determined only if `process_handler` is passed
         self.total_size: int | None = None
 
-        # Файл открытый для записи
+        # File opened for writing
         self._file: ty.TextIO | None = None
-        # Поток скачивания файла
+        # File download thread
         self._file_stream: requests.Response | None = None
         self._terminated: bool = False
 
@@ -112,20 +112,20 @@ def __init__(
     @abstractmethod
     def _prepare(self) -> None:
         """
-        Подготавливает загрузчик к скачиванию.
-        Метод должен быть реализован в наследуемом классе.
+        Prepares the downloader for downloading.
+        This method must be implemented in the subclass.
         """
 
     @abstractmethod
     def _download_book(self) -> None:
         """
-        Скачивает файлы.
-        Метод должен быть реализован в наследуемом классе.
+        Downloads files.
+        This method must be implemented in the subclass.
         """
 
     def save_preview(self) -> None:
         """
-        Скачивает и сохраняет обложку книги.
+        Downloads and saves the book cover.
         """
         if not self.book.preview:
             return
@@ -146,8 +146,8 @@ def save_preview(self) -> None:
 
     def download_book(self) -> bool:
         """
-        Скачивает файлы книги.
-        :returns: Список с путями к скачанным файлам.
+        Downloads book files.
+        :returns: List of paths to downloaded files.
         """
         logger.debug("preparing downloading")
         self._prepare()
@@ -167,7 +167,7 @@ def download_book(self) -> bool:
                 self.process_handler.status = DownloadProcessStatus.TERMINATED
             logger.debug("terminated")
 
-        return not self._terminated  # True - при успешном скачивании
+        return not self._terminated  # True - if the download was successful
 
     def terminate(self) -> None:
         """
@@ -198,7 +198,7 @@ def __repr__(self):
 
 
 class Driver(ABC):
-    drivers: list[ty.Type[Driver]] = []  # Все доступные драйверы
+    drivers: list[ty.Type[Driver]] = []  # All available drivers
 
     site_url = NotImplementedVariable()
     downloader_factory = NotImplementedVariable()
@@ -218,48 +218,48 @@ def get_suitable_driver(cls, url: str) -> ty.Type[Driver] | None:
     @staticmethod
     def get_page(url: str) -> requests.Response:
         """
-        :param url: Ссылка на книгу.
-        :returns: Результат GET запроса.
+        :param url: URL of the book.
+        :returns: Result of the GET request.
         """
         return requests.get(url)
 
     @abstractmethod
     def get_book(self, url: str) -> Book:
         """
-        Метод, получающий информацию о книге.
-        Должен быть реализован для каждого драйвера отдельно.
-        :param url: Ссылка на книгу.
-        :returns: Экземпляр книги.
+        Method that retrieves information about a book.
+        Must be implemented for each driver separately.
+        :param url: URL of the book.
+        :returns: Instance of the book.
         """
 
     @abstractmethod
     def get_book_series(self, url: str) -> list[Book]:
         """
-        Метод, получающий информацию о книгах из серии.
-        Должен быть реализован для каждого драйвера отдельно.
-        :param url: Ссылка на книгу.
-        :returns: Список неполных экземпляров книг.
+        Method that retrieves information about books in a series.
+        Must be implemented for each driver separately.
+        :param url: URL of the book.
+        :returns: List of incomplete book instances.
         """
 
     @abstractmethod
     def search_books(self, query: str, limit: int = 10, offset: int = 0) -> list[Book]:
         """
-        Метод, выполняющий поиск книг по запросу.
-        Должен быть реализован для каждого драйвера отдельно.
-        :param query: Поисковый запрос.
-        :param limit: Кол-во книг, которое нужно вернуть.
-        :param offset: Будет пропущено `offset` первых книг.
-        :returns: Список неполных экземпляров книг.
+        Method that performs a search for books by query.
+        Must be implemented for each driver separately.
+        :param query: Search query.
+        :param limit: Number of books to return.
+        :param offset: Number of books to skip from the start.
+        :returns: List of incomplete book instances.
         """
 
     def download_book(
         self, book: Book, process_handler: BaseDownloadProcessHandler | None = None
     ) -> bool:
         """
-        Метод, скачивающий аудио файлы книги.
-        :param book: Экземпляр книги.
-        :param process_handler: Обработчик процесса скачивания.
-        :return: Список путей к файлам.
+        Method that downloads the book's audio files.
+        :param book: Instance of the book.
+        :param process_handler: Handler for the download process.
+        :return: List of file paths.
         """
         self.downloader = self.downloader_factory(book, process_handler)
         return self.downloader.download_book()
diff --git a/ABPlayer/drivers/tools.py b/ABPlayer/drivers/tools.py
index 057bf9a..cb866c5 100644
--- a/ABPlayer/drivers/tools.py
+++ b/ABPlayer/drivers/tools.py
@@ -38,11 +38,11 @@ def prepare_file_metadata(
     item_index: int,
 ) -> None:
     """
-    Изменяет метаданные аудио файла.
-    :param file_path: Путь к аудио файлу.
-    :param author: Автор книги.
-    :param title: Название главы.
-    :param item_index: Порядковый номер файла.
+    Modifies the metadata of the audio file.
+    :param file_path: Path to the audio file.
+    :param author: Author of the book.
+    :param title: Title of the chapter.
+    :param item_index: Sequential number of the file.
     """
     file = eyed3.load(file_path)
     file.initTag()
@@ -54,8 +54,8 @@ def prepare_file_metadata(
 
 def get_audio_file_duration(file_path: Path) -> float:
     """
-    :param file_path: Путь к аудио файлу.
-    :returns: Длительность аудио файла в секундах.
+    :param file_path: Path to the audio file.
+    :returns: Duration of the audio file in seconds.
     """
     result = subprocess.check_output(
         rf'{os.environ["FFPROBE_PATH"]} -v quiet -show_streams -of json "{file_path}"',
@@ -67,7 +67,7 @@ def get_audio_file_duration(file_path: Path) -> float:
 
 def safe_name(text: str) -> str:
     """
-    Убирает или заменяет символы не допустимые для имени файла Windows.
+    Removes or replaces characters not allowed in Windows file names.
     """
     while text.count('"') >= 2:
         text = re.sub(r'"(.*?)"', r"«\g<1>»", text)
@@ -76,7 +76,7 @@ def safe_name(text: str) -> str:
 
 def create_instance_id(obj: ty.Any) -> int:
     """
-    Создает идентификатор экземпляра.
+    Creates an instance identifier.
     >>> class A:
     ...     def __init__(self):
     ...         create_instance_id(self)
@@ -96,7 +96,7 @@ def create_instance_id(obj: ty.Any) -> int:
 
 def instance_id(obj: ty.Any) -> int | None:
     """
-    :returns: Идентификатор экземпляра.
+    :returns: Instance identifier.
     """
     return getattr(obj, "_instance_id", None)
 
diff --git a/ABPlayer/js_api/js_api.py b/ABPlayer/js_api/js_api.py
index 8a96571..e00de5a 100644
--- a/ABPlayer/js_api/js_api.py
+++ b/ABPlayer/js_api/js_api.py
@@ -16,7 +16,7 @@ class JSApi:
 
     def init(self, window: webview.Window):
         """
-        Регистрирует методы для дальнейшего вызова из среды JS.
+        Registers methods for further invocation from the JS environment.
         """
         for section in self.sections:
             section = section()
diff --git a/ABPlayer/js_api/tools.py b/ABPlayer/js_api/tools.py
index 223e600..b920842 100644
--- a/ABPlayer/js_api/tools.py
+++ b/ABPlayer/js_api/tools.py
@@ -4,9 +4,9 @@
 
 def ttl_cache(max_age: int, maxsize: int = 128, typed: bool = False):
     """
-    Декоратор кэширующий результат вызова функции
-    в течении определенного промежутка времени.
-    max_age: Продолжительность сохранения кэша(в секундах).
+    A decorator that caches the result of a function call
+    for a specified period of time.
+    max_age: Duration to keep the cache (in seconds).
     """
 
     def _decorator(fn):
diff --git a/ABPlayer/locales/en/LC_MESSAGES/base.mo b/ABPlayer/locales/en/LC_MESSAGES/base.mo
index 89301e1..3498af7 100644
Binary files a/ABPlayer/locales/en/LC_MESSAGES/base.mo and b/ABPlayer/locales/en/LC_MESSAGES/base.mo differ
diff --git a/ABPlayer/locales/en/LC_MESSAGES/base.po b/ABPlayer/locales/en/LC_MESSAGES/base.po
index dbc7ff8..7014409 100644
--- a/ABPlayer/locales/en/LC_MESSAGES/base.po
+++ b/ABPlayer/locales/en/LC_MESSAGES/base.po
@@ -24,177 +24,179 @@ msgstr ""
 #: ../../abplayer/drivers/knigavuhe.py:204
 #: ../../abplayer/drivers/librivox.py:72 ../../abplayer/drivers/librivox.py:158
 msgid "unknown_author"
-msgstr ""
+msgstr "Unknown author"
 
 #: ../../abplayer/js_api/books.py:497
 msgid "no_suitable_driver"
-msgstr ""
+msgstr "No suitable driver"
 
 #: ../../abplayer/js_api/books.py:502
 msgid "book.already_exists"
-msgstr ""
+msgstr "The book is already in the library"
 
 #: ../../abplayer/js_api/books.py:507
 msgid "book.not_found"
-msgstr ""
+msgstr "Book not found"
 
 #: ../../abplayer/js_api/books.py:512
 msgid "book.already_downloaded"
-msgstr ""
+msgstr "The book being downloaded or has already been downloaded"
 
 #: ../../abplayer/js_api/books.py:517
 msgid "book.not_downloaded"
-msgstr ""
+msgstr "The book is not downloaded"
 
 #: ../../abplayer/js_api/js_api.py:70
 msgid "connection_issues"
-msgstr ""
+msgstr "Connection issues"
 
 #: ../../abplayer/js_api/settings.py:283
 msgid "canceled"
-msgstr ""
+msgstr "Operation cancelled"
 
 #: ../../abplayer/js_api/settings.py:288
 msgid "update.file_not_found"
-msgstr ""
+msgstr "Update file not found"
 
 #: ../../abplayer/web/templates/base.html:38
 msgid "base.hide_menu"
-msgstr ""
+msgstr "Hide menu"
 
 #: ../../abplayer/web/templates/base.html:40
 msgid "base.library"
-msgstr ""
+msgstr "My books"
 
 #: ../../abplayer/web/templates/base.html:41
 msgid "base.favorite"
-msgstr ""
+msgstr "Favourites"
 
 #: ../../abplayer/web/templates/base.html:42
 msgid "base.search_book"
-msgstr ""
+msgstr "New book"
 
 #: ../../abplayer/web/templates/base.html:43
 msgid "base.downloads"
-msgstr ""
+msgstr "Downloads"
 
 #: ../../abplayer/web/templates/base.html:46
 msgid "base.books_folder"
-msgstr ""
+msgstr "Books folder"
 
 #: ../../abplayer/web/templates/base.html:47
 msgid "base.settings"
-msgstr ""
+msgstr "Settings"
 
 #: ../../abplayer/web/templates/book.html:30
 #: ../../abplayer/web/templates/book.html:32
 msgid "s"
-msgstr ""
+msgstr "s"
 
 #: ../../abplayer/web/templates/book.html:36
 msgid "wait_for_downloading_end"
-msgstr ""
+msgstr "Wait for the download to finish"
 
 #: ../../abplayer/web/templates/book.html:38
 msgid "online_listening_not_provided"
 msgstr ""
+"Online listening is currently unavailable\n"
+"Download the book to start listening"
 
 #: ../../abplayer/web/templates/book.html:39
 msgid "book.download"
-msgstr ""
+msgstr "Download"
 
 #: ../../abplayer/web/templates/downloads.html:5
 msgid "downloads"
-msgstr ""
+msgstr "Downloads"
 
 #: ../../abplayer/web/templates/downloads.html:8
 msgid "no_downloads"
-msgstr ""
+msgstr "No active downloads"
 
 #: ../../abplayer/web/templates/library.html:9
 msgid "filters.query"
-msgstr ""
+msgstr "Search in library"
 
 #: ../../abplayer/web/templates/library.html:12
 msgid "filters.reverse_order"
-msgstr ""
+msgstr "In reverse order"
 
 #: ../../abplayer/web/templates/library.html:14
 msgid "filters.authors"
-msgstr ""
+msgstr "Authors"
 
 #: ../../abplayer/web/templates/library.html:16
 msgid "filters.series"
-msgstr ""
+msgstr "Series"
 
 #: ../../abplayer/web/templates/js/library.js:151
 #: ../../abplayer/web/templates/library.html:23
 msgid "library"
-msgstr ""
+msgstr "Library"
 
 #: ../../abplayer/web/templates/library.html:26
 msgid "library.all"
-msgstr ""
+msgstr "All"
 
 #: ../../abplayer/web/templates/library.html:27
 msgid "library.new"
-msgstr ""
+msgstr "New"
 
 #: ../../abplayer/web/templates/library.html:28
 msgid "library.in_process"
-msgstr ""
+msgstr "In process"
 
 #: ../../abplayer/web/templates/library.html:29
 msgid "library.listened"
-msgstr ""
+msgstr "Listened"
 
 #: ../../abplayer/web/templates/search.html:7
 msgid "search_for_book"
-msgstr ""
+msgstr "Search for book"
 
 #: ../../abplayer/web/templates/search.html:10
 msgid "begin_typing"
-msgstr ""
+msgstr "Start typing or paste a link"
 
 #: ../../abplayer/web/templates/settings.html:7
 msgid "interface"
-msgstr ""
+msgstr "Interface"
 
 #: ../../abplayer/web/templates/settings.html:10
 msgid "dark_theme"
-msgstr ""
+msgstr "Dark theme"
 
 #: ../../abplayer/web/templates/settings.html:15
 msgid "language"
-msgstr ""
+msgstr "Language 🌐"
 
 #: ../../abplayer/web/templates/settings.html:24
 msgid "storage"
-msgstr ""
+msgstr "Storage"
 
 #: ../../abplayer/web/templates/settings.html:27
 msgid "books_folder"
-msgstr ""
+msgstr "Books folder"
 
 #: ../../abplayer/web/templates/settings.html:28
 msgid "open"
-msgstr ""
+msgstr "Open"
 
 #: ../../abplayer/web/templates/settings.html:29
 msgid "change"
-msgstr ""
+msgstr "Change"
 
 #: ../../abplayer/web/templates/settings.html:41
 msgid "version"
-msgstr ""
+msgstr "Version"
 
 #: ../../abplayer/web/templates/settings.html:44
 msgid "developer"
-msgstr ""
+msgstr "Developer"
 
 #: ../../abplayer/web/templates/settings.html:45
 msgid "project_page"
-msgstr ""
+msgstr "Project page"
 
 #: ../../abplayer/web/templates/js/book.js:21
 #: ../../abplayer/web/templates/js/book.js:122
@@ -202,60 +204,60 @@ msgstr ""
 #: ../../abplayer/web/templates/js/book.js:145
 #: ../../abplayer/web/templates/js/library.js:231
 msgid "book.listening_progress"
-msgstr ""
+msgstr "Listening progress"
 
 #: ../../abplayer/web/templates/js/book.js:22
 #: ../../abplayer/web/templates/js/library.js:232
 msgid "book.added"
-msgstr ""
+msgstr "Added"
 
 #: ../../abplayer/web/templates/js/downloads.js:38
 msgid "downloading.initializing"
-msgstr ""
+msgstr "Initializing"
 
 #: ../../abplayer/web/templates/js/downloads.js:56
 msgid "downloading.waiting"
-msgstr ""
+msgstr "Waiting"
 
 #: ../../abplayer/web/templates/js/downloads.js:57
 msgid "downloading.preparing"
-msgstr ""
+msgstr "Preparing"
 
 #: ../../abplayer/web/templates/js/downloads.js:58
 msgid "downloading.downloading"
-msgstr ""
+msgstr "Downloading"
 
 #: ../../abplayer/web/templates/js/downloads.js:59
 msgid "downloading.ending"
-msgstr ""
+msgstr "Ending"
 
 #: ../../abplayer/web/templates/js/downloads.js:60
 msgid "downloading.finished"
-msgstr ""
+msgstr "Download finished"
 
 #: ../../abplayer/web/templates/js/downloads.js:61
 msgid "downloading.terminating"
-msgstr ""
+msgstr "Stopping"
 
 #: ../../abplayer/web/templates/js/downloads.js:91
 msgid "book.downloaded"
-msgstr ""
+msgstr "Downloaded"
 
 #: ../../abplayer/web/templates/js/library.js:149
 msgid "library.favorite"
-msgstr ""
+msgstr "Library: Favourites"
 
 #: ../../abplayer/web/templates/js/library.js:297
 msgid "book.book_deleted_but_files_already_exists"
-msgstr ""
+msgstr "Deleted from library, but downloaded files remain"
 
 #: ../../abplayer/web/templates/js/library.js:298
 msgid "book.delete_files"
-msgstr ""
+msgstr "Delete remaining files"
 
 #: ../../abplayer/web/templates/js/library.js:301
 msgid "book.deleted_from_library"
-msgstr ""
+msgstr "Deleted from library"
 
 #: ../../abplayer/web/templates/js/library.js:320
 msgid "book.files_deleted"
@@ -263,85 +265,84 @@ msgstr "%s book files deleted"
 
 #: ../../abplayer/web/templates/js/search.js:104
 msgid "book.add_to_library"
-msgstr ""
+msgstr "Add to library"
 
 #: ../../abplayer/web/templates/js/search.js:107
 msgid "search.empty"
-msgstr ""
+msgstr "Nothing found"
 
 #: ../../abplayer/web/templates/js/settings.js:34
 msgid "books_folder.changed"
-msgstr ""
+msgstr "Books folder changed"
 
 #: ../../abplayer/web/templates/js/settings.js:34
 msgid "books_folder.books_added"
-msgstr ""
+msgstr "Books discovered"
 
 #: ../../abplayer/web/templates/js/settings.js:38
 msgid "books_folder.downloaded_books_in_old_folder"
-msgstr ""
+msgstr "Book files are in previous location"
 
 #: ../../abplayer/web/templates/js/settings.js:39
 msgid "books_folder.move_to_new_folder"
-msgstr ""
+msgstr "Move everything to new location"
 
 #: ../../abplayer/web/templates/js/settings.js:40
 msgid "books_folder.delete_old_books"
-msgstr ""
+msgstr "Delete files in previous location"
 
 #: ../../abplayer/web/templates/js/settings.js:46
 msgid "books_folder.moving_files"
-msgstr ""
+msgstr "Moving files to new location"
 
 #: ../../abplayer/web/templates/js/settings.js:46
 #: ../../abplayer/web/templates/js/settings.js:54
 #: ../../abplayer/web/templates/js/settings.js:85
 msgid "it_can_take_some_time"
-msgstr ""
+msgstr "This may take some time"
 
 #: ../../abplayer/web/templates/js/settings.js:50
 msgid "books_folder.moving_finished"
-msgstr ""
+msgstr "Transfer complete"
 
 #: ../../abplayer/web/templates/js/settings.js:50
 msgid "books_folder.books_moved"
-msgstr ""
+msgstr "Book Files moved"
 
 #: ../../abplayer/web/templates/js/settings.js:54
 msgid "books_folder.deleting_files"
-msgstr ""
+msgstr "Delete files"
 
 #: ../../abplayer/web/templates/js/settings.js:58
 msgid "books_folder.deleting_finished"
-msgstr ""
+msgstr "Deletion complete"
 
 #: ../../abplayer/web/templates/js/settings.js:58
 msgid "books_folder.books_deleted"
-msgstr ""
+msgstr "Book files deleted"
 
 #: ../../abplayer/web/templates/js/settings.js:70
 msgid "update.available"
-msgstr ""
+msgstr "Update available"
 
 #: ../../abplayer/web/templates/js/settings.js:71
 #: ../../abplayer/web/templates/js/settings.js:77
 msgid "update.changelog"
-msgstr ""
+msgstr "Changelog"
 
 #: ../../abplayer/web/templates/js/settings.js:72
 #: ../../abplayer/web/templates/js/settings.js:78
 msgid "update.install"
-msgstr ""
+msgstr "Install"
 
 #: ../../abplayer/web/templates/js/settings.js:76
 msgid "update.new_testing_version"
-msgstr ""
+msgstr "New testing version available"
 
 #: ../../abplayer/web/templates/js/settings.js:79
 msgid "update.no_suggest_tests"
-msgstr ""
+msgstr "Do not suggest participation in testing"
 
 #: ../../abplayer/web/templates/js/settings.js:85
 msgid "update.downloading"
-msgstr ""
-
+msgstr "Downloading update"
diff --git a/ABPlayer/models/book.py b/ABPlayer/models/book.py
index e4ee86f..9ee9e40 100644
--- a/ABPlayer/models/book.py
+++ b/ABPlayer/models/book.py
@@ -20,27 +20,27 @@
 @dataclass
 class BookItem:
     """
-    Глава книги.
+    Chapter of the book.
     """
 
-    file_url: str  # Ссылка на файл, для скачивания
-    file_index: int  # Номер файла(Нумерация с единицы)
-    title: str  # Название главы
-    start_time: int  # Время (в секундах), когда начинается глава
-    end_time: int  # Время (в секундах), когда заканчивается глава
+    file_url: str  # URL of the file for downloading
+    file_index: int  # File number (Numbering starts from one)
+    title: str  # Chapter title
+    start_time: int  # Time (in seconds) when the chapter starts
+    end_time: int  # Time (in seconds) when the chapter ends
 
     @property
     def duration(self) -> int:
         """
-        Длительность главы (в секундах).
+        Duration of the chapter (in seconds).
         """
         return self.end_time - self.start_time
 
 
 class BookItems(list):
     """
-    Список глав.
-    Представлен списком словарей.
+    List of chapters.
+    Represented as a list of dictionaries.
     """
 
     def __init__(self, items: list[BookItem | dict[str, str | int]] = ()):
@@ -57,36 +57,36 @@ def to_dump(self) -> list[dict]:
 
 class Status(Enum):
     """
-    Статус книги.
+    Book status.
     """
 
-    NEW = "new"  # Новая книга
-    STARTED = "started"  # Начал слушать
-    FINISHED = "finished"  # Закончил слушать
+    NEW = "new"  # New book
+    STARTED = "started"  # Started listening
+    FINISHED = "finished"  # Finished listening
 
 
 @dataclass
 class StopFlag:
     """
-    Отметка, на которой пользователь остановил прослушивание.
-    В базе данных храниться как словарь.
+    The mark where the user stopped listening.
+    Stored in the database as a dictionary.
     """
 
-    item: int = 0  # Глава(Индекс)
-    time: int = 0  # Секунда
+    item: int = 0  # Chapter (Index)
+    time: int = 0  # Second
 
 
 class BookFiles(dict):
     """
-    Аудио файлы книги. Словарь: dict[str, str] {<имя файла>: <хеш>}
+    Audio files of the book. Dictionary: dict[str, str] {<file name>: <hash>}
     """
 
 
 @dataclass
 class Book:
     """
-    Класс, описывающий, как книги, хранятся в базе данных,
-    а так же какие данные драйвера парсят с сайтов.
+    Class describing how books are stored in the database,
+    as well as what data drivers parse from websites.
     """
 
     id: int | None = None
@@ -94,13 +94,13 @@ class Book:
     name: str = ""
     series_name: str = ""
     number_in_series: str = ""
-    description: str = ""  # Описание
-    reader: str = ""  # Чтец
-    duration: str = ""  # Длительность
-    url: str = ""  # Ссылка на книгу
-    preview: str = ""  # Ссылка на превью(обложку) книги
-    driver: str = ""  # Драйвер, с которым работает сайт
-    items: BookItems = field(default_factory=BookItems)  # Список глав
+    description: str = ""  # Description
+    reader: str = ""  # Reader
+    duration: str = ""  # Duration
+    url: str = ""  # Book URL
+    preview: str = ""  # Preview (cover) URL
+    driver: str = ""  # Driver used for the website
+    items: BookItems = field(default_factory=BookItems)  # List of chapters
     status: Status = Status.NEW
     stop_flag: StopFlag = field(default_factory=StopFlag)
     favorite: bool = False
@@ -110,7 +110,7 @@ class Book:
     @property
     def book_path(self) -> str:
         """
-        :returns: Относительный путь к книге в библиотеке.
+        :returns: Relative path to the book in the library.
         """
         if self.series_name:
             return os.path.join(
@@ -124,21 +124,21 @@ def book_path(self) -> str:
     @property
     def dir_path(self) -> str:
         """
-        :returns: Абсолютный путь к директории, в которой храниться книга.
+        :returns: Absolute path to the directory where the book is stored.
         """
         return os.path.abspath(os.path.join(os.environ["books_folder"], self.book_path))
 
     @property
     def preview_path(self) -> str:
         """
-        :returns: Абсолютный путь к файлу обложки книги.
+        :returns: Absolute path to the book cover file.
         """
         return os.path.join(self.dir_path, "cover.jpg")
 
     @property
     def listening_progress(self) -> str:
         """
-        :returns: Прогресс прослушивания. (В процентах)
+        :returns: Listening progress. (In percentage)
         """
         total = sum([item.duration for item in self.items])
         if not total:
@@ -158,8 +158,8 @@ def listening_progress(self) -> str:
     @classmethod
     def scan_dir(cls, dir_path: str) -> ty.Generator[Book, ty.Any, None]:
         """
-        Сканирует директорию на наличие `.abp` файлов.
-        :returns: Генератор экземпляров книг, загруженных из найденных файлов.
+        Scans the directory for `.abp` files.
+        :returns: Generator of book instances loaded from found files.
         """
         logger.opt(colors=True).debug(f"scanning <y>{dir_path}</y> for <r>.abp</r>")
         books_found = 0
@@ -167,7 +167,7 @@ def scan_dir(cls, dir_path: str) -> ty.Generator[Book, ty.Any, None]:
             if ".abp" in file_names:
                 abp_path = os.path.join(root, ".abp")
                 if not (book := Book.load_from_storage(abp_path)):
-                    # Удаляем файл, если не получается загрузить книгу
+                    # Remove the file if the book cannot be loaded
                     try:
                         os.remove(abp_path)
                     except IOError:
@@ -181,8 +181,8 @@ def scan_dir(cls, dir_path: str) -> ty.Generator[Book, ty.Any, None]:
     @classmethod
     def load_from_storage(cls, file_path: str) -> Book | None:
         """
-        Создаёт экземпляр книги из `.abp` файла.
-        :returns: Экземпляр книги или None.
+        Creates a book instance from a `.abp` file.
+        :returns: Book instance or None.
         """
         logger.opt(colors=True).trace(
             f"loading data from <r>.abp</r> <y>{file_path}</y>"
@@ -244,7 +244,7 @@ def load_from_storage(cls, file_path: str) -> Book | None:
 
     def save_to_storage(self) -> None:
         """
-        Сохраняет книгу в `.abp` файл.
+        Saves the book to a `.abp` file.
         """
         logger.opt(colors=True).debug(
             f"{self:styled} saved to <r>.abp</r>: <y>{self.abp_file_path}</y>"


Reviews:
user: login: AlexDev505
body: Some typos, but overall it's good.
It would be cool if you moved the translation of the comments in the code
state: CHANGES_REQUESTED
author_association: OWNER
submitted_at: 2025-03-20T13:55:25Z
user: login: AlexDev505
state: COMMENTED
author_association: OWNER
submitted_at: 2025-03-24T09:26:36Z

Review comments:
diff_hunk: @@ -24,324 +24,329 @@ msgstr ""
 #: ../../abplayer/drivers/knigavuhe.py:204
 #: ../../abplayer/drivers/librivox.py:72 ../../abplayer/drivers/librivox.py:158
 msgid "unknown_author"
-msgstr ""
+msgstr "Unknown author"
 
 #: ../../abplayer/js_api/books.py:497
 msgid "no_suitable_driver"
-msgstr ""
+msgstr "No sutiable driver"
path: ABPlayer/locales/en/LC_MESSAGES/base.po
user: login: AlexDev505
body: typo: suitable
created_at: 2025-03-20T13:42:09Z
author_association: OWNER
diff_hunk: @@ -24,324 +24,329 @@ msgstr ""
 #: ../../abplayer/drivers/knigavuhe.py:204
 #: ../../abplayer/drivers/librivox.py:72 ../../abplayer/drivers/librivox.py:158
 msgid "unknown_author"
-msgstr ""
+msgstr "Unknown author"
 
 #: ../../abplayer/js_api/books.py:497
 msgid "no_suitable_driver"
-msgstr ""
+msgstr "No sutiable driver"
 
 #: ../../abplayer/js_api/books.py:502
 msgid "book.already_exists"
-msgstr ""
+msgstr "The book is already in the library"
 
 #: ../../abplayer/js_api/books.py:507
 msgid "book.not_found"
-msgstr ""
+msgstr "Book not found"
 
 #: ../../abplayer/js_api/books.py:512
 msgid "book.already_downloaded"
-msgstr ""
+msgstr "The book being downloaded or has already been downloaded"
 
 #: ../../abplayer/js_api/books.py:517
 msgid "book.not_downloaded"
-msgstr ""
+msgstr "The book is not downloaded"
 
 #: ../../abplayer/js_api/js_api.py:70
 msgid "connection_issues"
-msgstr ""
+msgstr "Connection issues"
 
 #: ../../abplayer/js_api/settings.py:283
 msgid "canceled"
-msgstr ""
+msgstr "Operation cancelled"
 
 #: ../../abplayer/js_api/settings.py:288
 msgid "update.file_not_found"
-msgstr ""
+msgstr "Update file not found"
 
 #: ../../abplayer/web/templates/base.html:38
 msgid "base.hide_menu"
-msgstr ""
+msgstr "Hide menu"
 
 #: ../../abplayer/web/templates/base.html:40
 msgid "base.library"
-msgstr ""
+msgstr "My books"
 
 #: ../../abplayer/web/templates/base.html:41
 msgid "base.favorite"
-msgstr ""
+msgstr "Favourites"
 
 #: ../../abplayer/web/templates/base.html:42
 msgid "base.search_book"
-msgstr ""
+msgstr "New book"
 
 #: ../../abplayer/web/templates/base.html:43
 msgid "base.downloads"
-msgstr ""
+msgstr "Downloads"
 
 #: ../../abplayer/web/templates/base.html:46
 msgid "base.books_folder"
-msgstr ""
+msgstr "Books folder"
 
 #: ../../abplayer/web/templates/base.html:47
 msgid "base.settings"
-msgstr ""
+msgstr "Settings"
 
 #: ../../abplayer/web/templates/book.html:30
 #: ../../abplayer/web/templates/book.html:32
 msgid "s"
-msgstr ""
+msgstr "s"
 
 #: ../../abplayer/web/templates/book.html:36
 msgid "wait_for_downloading_end"
-msgstr ""
+msgstr "Wait for the download to finish"
 
 #: ../../abplayer/web/templates/book.html:38
 msgid "online_listening_not_provided"
 msgstr ""
+"Online listening is currently unavailable\n"
+"Download the book to start listening"
 
 #: ../../abplayer/web/templates/book.html:39
 msgid "book.download"
-msgstr ""
+msgstr "Download"
 
 #: ../../abplayer/web/templates/downloads.html:5
 msgid "downloads"
-msgstr ""
+msgstr "Downloads"
 
 #: ../../abplayer/web/templates/downloads.html:8
 msgid "no_downloads"
-msgstr ""
+msgstr "No active downloads"
 
 #: ../../abplayer/web/templates/library.html:9
 msgid "filters.query"
-msgstr ""
+msgstr "Search in library"
 
 #: ../../abplayer/web/templates/library.html:12
 msgid "filters.reverse_order"
-msgstr ""
+msgstr "In reverse order"
 
 #: ../../abplayer/web/templates/library.html:14
 msgid "filters.authors"
-msgstr ""
+msgstr "Authors"
 
 #: ../../abplayer/web/templates/library.html:16
 msgid "filters.series"
-msgstr ""
+msgstr "Series"
 
 #: ../../abplayer/web/templates/js/library.js:151
 #: ../../abplayer/web/templates/library.html:23
 msgid "library"
-msgstr ""
+msgstr "Library"
 
 #: ../../abplayer/web/templates/library.html:26
 msgid "library.all"
-msgstr ""
+msgstr "All"
 
 #: ../../abplayer/web/templates/library.html:27
 msgid "library.new"
-msgstr ""
+msgstr "New"
 
 #: ../../abplayer/web/templates/library.html:28
 msgid "library.in_process"
-msgstr ""
+msgstr "In process"
 
 #: ../../abplayer/web/templates/library.html:29
 msgid "library.listened"
-msgstr ""
+msgstr "Listened"
 
 #: ../../abplayer/web/templates/search.html:7
 msgid "search_for_book"
-msgstr ""
+msgstr "Search for book"
 
 #: ../../abplayer/web/templates/search.html:10
 msgid "begin_typing"
-msgstr ""
+msgstr "Start typing or paste a link"
 
 #: ../../abplayer/web/templates/settings.html:7
 msgid "interface"
-msgstr ""
+msgstr "Interface"
 
 #: ../../abplayer/web/templates/settings.html:10
 msgid "dark_theme"
-msgstr ""
+msgstr "Dark theme"
 
 #: ../../abplayer/web/templates/settings.html:15
 msgid "language"
-msgstr ""
+msgstr "Language 🌐"
 
 #: ../../abplayer/web/templates/settings.html:24
 msgid "storage"
-msgstr ""
+msgstr "Storage"
 
 #: ../../abplayer/web/templates/settings.html:27
 msgid "books_folder"
-msgstr ""
+msgstr "Books folder"
 
 #: ../../abplayer/web/templates/settings.html:28
 msgid "open"
-msgstr ""
+msgstr "Open"
 
 #: ../../abplayer/web/templates/settings.html:29
 msgid "change"
-msgstr ""
+msgstr "Change"
 
 #: ../../abplayer/web/templates/settings.html:41
 msgid "version"
-msgstr ""
+msgstr "Version"
 
 #: ../../abplayer/web/templates/settings.html:44
 msgid "developer"
-msgstr ""
+msgstr "Developer"
 
 #: ../../abplayer/web/templates/settings.html:45
 msgid "project_page"
-msgstr ""
+msgstr "Project page"
 
 #: ../../abplayer/web/templates/js/book.js:21
 #: ../../abplayer/web/templates/js/book.js:122
 #: ../../abplayer/web/templates/js/book.js:124
 #: ../../abplayer/web/templates/js/book.js:145
 #: ../../abplayer/web/templates/js/library.js:231
 msgid "book.listening_progress"
-msgstr ""
+msgstr "Listening progress"
 
 #: ../../abplayer/web/templates/js/book.js:22
 #: ../../abplayer/web/templates/js/library.js:232
 msgid "book.added"
-msgstr ""
+msgstr "Added"
 
 #: ../../abplayer/web/templates/js/downloads.js:38
 msgid "downloading.initializing"
-msgstr ""
+msgstr "Initializing"
 
 #: ../../abplayer/web/templates/js/downloads.js:56
 msgid "downloading.waiting"
-msgstr ""
+msgstr "Waiting"
 
 #: ../../abplayer/web/templates/js/downloads.js:57
 msgid "downloading.preparing"
-msgstr ""
+msgstr "Preparing"
 
 #: ../../abplayer/web/templates/js/downloads.js:58
 msgid "downloading.downloading"
-msgstr ""
+msgstr "Downloading"
 
 #: ../../abplayer/web/templates/js/downloads.js:59
 msgid "downloading.ending"
-msgstr ""
+msgstr "Ending"
 
 #: ../../abplayer/web/templates/js/downloads.js:60
 msgid "downloading.finished"
-msgstr ""
+msgstr "Download finished"
 
 #: ../../abplayer/web/templates/js/downloads.js:61
 msgid "downloading.terminating"
-msgstr ""
+msgstr "Stopping"
 
 #: ../../abplayer/web/templates/js/downloads.js:91
 msgid "book.downloaded"
-msgstr ""
+msgstr "Downloaded"
 
 #: ../../abplayer/web/templates/js/library.js:149
 msgid "library.favorite"
-msgstr ""
+msgstr "Library: Favourites"
 
 #: ../../abplayer/web/templates/js/library.js:297
 msgid "book.book_deleted_but_files_already_exists"
-msgstr ""
+msgstr "Deleted from library, but downloaded files remain"
 
 #: ../../abplayer/web/templates/js/library.js:298
 msgid "book.delete_files"
-msgstr ""
+msgstr "Delete remaining files"
 
 #: ../../abplayer/web/templates/js/library.js:301
 msgid "book.deleted_from_library"
-msgstr ""
+msgstr "Deleted from library"
 
 #: ../../abplayer/web/templates/js/library.js:320
 msgid "book.files_deleted"
 msgstr "%s book files deleted"
 
 #: ../../abplayer/web/templates/js/search.js:104
 msgid "book.add_to_library"
-msgstr ""
+msgstr "Add to library"
 
 #: ../../abplayer/web/templates/js/search.js:107
 msgid "search.empty"
-msgstr ""
+msgstr "Nothing found"
 
 #: ../../abplayer/web/templates/js/settings.js:34
 msgid "books_folder.changed"
-msgstr ""
+msgstr "Books folder changed"
 
 #: ../../abplayer/web/templates/js/settings.js:34
 msgid "books_folder.books_added"
-msgstr ""
+#,fuzzy
+msgstr "Books moved to new folder"
 
 #: ../../abplayer/web/templates/js/settings.js:38
 msgid "books_folder.downloaded_books_in_old_folder"
-msgstr ""
+msgstr "Book files are in previous location"
 
 #: ../../abplayer/web/templates/js/settings.js:39
 msgid "books_folder.move_to_new_folder"
-msgstr ""
+msgstr "Move everything to new location"
 
 #: ../../abplayer/web/templates/js/settings.js:40
 msgid "books_folder.delete_old_books"
-msgstr ""
+msgstr "Delete files in previous location"
 
 #: ../../abplayer/web/templates/js/settings.js:46
 msgid "books_folder.moving_files"
-msgstr ""
+msgstr "Moving files to new location"
 
 #: ../../abplayer/web/templates/js/settings.js:46
 #: ../../abplayer/web/templates/js/settings.js:54
 #: ../../abplayer/web/templates/js/settings.js:85
 msgid "it_can_take_some_time"
-msgstr ""
+msgstr "This may take some time"
 
 #: ../../abplayer/web/templates/js/settings.js:50
 msgid "books_folder.moving_finished"
-msgstr ""
+#,fuzzy
+msgstr "Transfer complete"
 
 #: ../../abplayer/web/templates/js/settings.js:50
 msgid "books_folder.books_moved"
-msgstr ""
+#,fuzzy
+msgstr "Book Files moved"
 
 #: ../../abplayer/web/templates/js/settings.js:54
 msgid "books_folder.deleting_files"
-msgstr ""
+msgstr "Delete files"
 
 #: ../../abplayer/web/templates/js/settings.js:58
 msgid "books_folder.deleting_finished"
-msgstr ""
+msgstr "Deletion complete"
 
 #: ../../abplayer/web/templates/js/settings.js:58
 msgid "books_folder.books_deleted"
-msgstr ""
+#,fuzzy
+msgstr "Book files deleted"
 
 #: ../../abplayer/web/templates/js/settings.js:70
 msgid "update.available"
-msgstr ""
+msgstr "Update available"
 
 #: ../../abplayer/web/templates/js/settings.js:71
 #: ../../abplayer/web/templates/js/settings.js:77
 msgid "update.changelog"
-msgstr ""
+msgstr "Changelog"
 
 #: ../../abplayer/web/templates/js/settings.js:72
 #: ../../abplayer/web/templates/js/settings.js:78
 msgid "update.install"
-msgstr ""
+msgstr "Install"
 
 #: ../../abplayer/web/templates/js/settings.js:76
 msgid "update.new_testing_version"
-msgstr ""
+msgstr "New testing version available"
 
 #: ../../abplayer/web/templates/js/settings.js:79
 msgid "update.no_suggest_tests"
-msgstr ""
+msgstr "Do not suggest participation in testing"
 
 #: ../../abplayer/web/templates/js/settings.js:85
 msgid "update.downloading"
-msgstr ""
-
+msgstr "Downloading upate"
path: ABPlayer/locales/en/LC_MESSAGES/base.po
user: login: AlexDev505
body: typo: update
created_at: 2025-03-20T13:49:31Z
author_association: OWNER
diff_hunk: @@ -8,5 +8,6 @@
     ROOT_DIR = "."
 
 
-def set_language(lang: str) -> None:
+def set_language(lang: str='en') -> None:
+    print(os.path.abspath(os.path.join(ROOT_DIR, "locales")))
path: ABPlayer/locales/__init__.py
user: login: AlexDev505
body: remove this `print`
created_at: 2025-03-24T09:26:36Z
author_association: OWNER

PR comments:
user: login: Koladweep
created_at: 2025-03-21T03:31:23Z
body: > Some typos, but overall it's good. It would be cool if you moved the translation of the comments in the code

I'll correct the translation. Could you clarify what you mean by moving the translation of comments in the code? Does that mean fixing the typo?
author_association: CONTRIBUTOR
user: login: AlexDev505
created_at: 2025-03-21T03:33:25Z
body: In the last pull request, you translated docstrings in the code. It would be nice if you did it in this pool request as well.
author_association: OWNER
user: login: Koladweep
created_at: 2025-03-21T03:46:50Z
body: > In the last pull request, you translated docstrings in the code. It would be nice if you did it in this pool request as well.

Okay but I want to do it clean, avoiding typos. I can do, say, a file or two every day. 
author_association: CONTRIBUTOR
user: login: Koladweep
created_at: 2025-03-26T07:42:16Z
body: Lines 68 and 87 in audiobookplayer/ABPlayer/starting_window.py- 
hardcoded language.
author_association: CONTRIBUTOR
user: login: AlexDev505
created_at: 2025-03-26T07:47:08Z
body: can you fix this?


author_association: OWNER
user: login: Koladweep
created_at: 2025-03-26T16:04:29Z
body: > can you fix this?

I tried a simple approach:

in starting_window.html line 41
  <div id="status">{{ starting_text }}</div>

in ABPlayer/web/app.py line 51
    return render_template("starting_window.html", starting_text=_("status.starting"))



ABPlayer/locales/en/LC_MESSAGES/base.po

#:../../abplayer/web/templates/starting_window.html:41
msgid "status.starting"
msgstr "launching..."

#:../../abplayer/web/templates/starting_window.html:41
msgid "status.loading_library"
msgstr "loading library..."


ABPlayer/locales/ru/LC_MESSAGES/base.po
#:../../abplayer/web/templates/starting_window.html:41
msgid "status.starting"
msgstr "запуск..."

#:../../abplayer/web/templates/starting_window.html:41
msgid "status.loading_library"
msgstr "загрузка библиотеки..."

compiled these.

In theory this should work, but I guess it won't because it will get executed after time.sleep()

here's what happens if I try this

![image](https://github.com/user-attachments/assets/3c474c97-7fda-45a0-8521-ef53d00066ba)


Application launches successfuly but this part won't work. So, I guess you're doing fine by keeping it hard coded. in a language of choice.

author_association: CONTRIBUTOR
user: login: AlexDev505
created_at: 2025-03-26T16:37:50Z
body: Okay, I guess there's no need to worry about that.
author_association: OWNER