mirror of
https://github.com/koloideal/Argenta.git
synced 2026-06-10 18:15:28 +03:00
docs
This commit is contained in:
@@ -1,9 +1,7 @@
|
|||||||
from argenta import App
|
from argenta import App
|
||||||
|
|
||||||
|
|
||||||
def empty_command_handler():
|
def empty_command_handler():
|
||||||
print("Empty command handler called")
|
print("Empty command handler called")
|
||||||
|
|
||||||
|
|
||||||
app: App = App()
|
app: App = App()
|
||||||
app.set_empty_command_handler(empty_command_handler)
|
app.set_empty_command_handler(empty_command_handler)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
from argenta import App
|
from argenta import App
|
||||||
|
|
||||||
|
|
||||||
def incorrect_input_syntax_handler(raw_command: str):
|
def incorrect_input_syntax_handler(raw_command: str):
|
||||||
print(f"Incorrect input syntax for command: {raw_command}")
|
print(f"Incorrect input syntax for command: {raw_command}")
|
||||||
|
|
||||||
|
|
||||||
app: App = App()
|
app: App = App()
|
||||||
app.set_incorrect_input_syntax_handler(incorrect_input_syntax_handler)
|
app.set_incorrect_input_syntax_handler(incorrect_input_syntax_handler)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
from argenta import App
|
from argenta import App
|
||||||
|
|
||||||
|
|
||||||
def repeated_input_flags_handler(raw_command: str):
|
def repeated_input_flags_handler(raw_command: str):
|
||||||
print(f"Repeated input flags: {raw_command}")
|
print(f"Repeated input flags: {raw_command}")
|
||||||
|
|
||||||
|
|
||||||
app: App = App()
|
app: App = App()
|
||||||
app.set_repeated_input_flags_handler(repeated_input_flags_handler)
|
app.set_repeated_input_flags_handler(repeated_input_flags_handler)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
from argenta import App
|
from argenta import App
|
||||||
|
|
||||||
|
|
||||||
def empty_command_handler():
|
def empty_command_handler():
|
||||||
print("Empty input command")
|
print("Empty input command")
|
||||||
|
|
||||||
|
|
||||||
app: App = App()
|
app: App = App()
|
||||||
app.set_empty_command_handler(empty_command_handler)
|
app.set_empty_command_handler(empty_command_handler)
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
from argenta import App
|
from argenta import App
|
||||||
from argenta.command import InputCommand
|
from argenta.command import InputCommand
|
||||||
|
|
||||||
|
|
||||||
def unknown_command_handler(command: InputCommand):
|
def unknown_command_handler(command: InputCommand):
|
||||||
print(f"Unknown input command with trigger: {command.trigger}")
|
print(f"Unknown input command with trigger: {command.trigger}")
|
||||||
|
|
||||||
|
|
||||||
app: App = App()
|
app: App = App()
|
||||||
app.set_unknown_command_handler(unknown_command_handler)
|
app.set_unknown_command_handler(unknown_command_handler)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
from argenta import App, Response
|
from argenta import App, Response
|
||||||
|
|
||||||
|
|
||||||
def exit_command_handler(response: Response):
|
def exit_command_handler(response: Response):
|
||||||
print("Exit command handler")
|
print("Exit command handler")
|
||||||
|
|
||||||
|
|
||||||
app: App = App()
|
app: App = App()
|
||||||
app.set_exit_command_handler(exit_command_handler)
|
app.set_exit_command_handler(exit_command_handler)
|
||||||
|
|||||||
@@ -12,12 +12,13 @@ orchestrator = Orchestrator()
|
|||||||
# 2. Создание роутера для группировки команд
|
# 2. Создание роутера для группировки команд
|
||||||
main_router = Router(title="Основные команды")
|
main_router = Router(title="Основные команды")
|
||||||
|
|
||||||
|
|
||||||
# 3. Определение команды и её обработчика
|
# 3. Определение команды и её обработчика
|
||||||
@main_router.command(Command(
|
@main_router.command(
|
||||||
"hello",
|
Command(
|
||||||
description="Печатает приветственное сообщение",
|
"hello", description="Печатает приветственное сообщение", flags=Flag("name")
|
||||||
flags=Flag("name")
|
)
|
||||||
))
|
)
|
||||||
def hello_handler(response: Response):
|
def hello_handler(response: Response):
|
||||||
"""Этот обработчик будет вызван для команды 'hello'."""
|
"""Этот обработчик будет вызван для команды 'hello'."""
|
||||||
name = response.input_flags.get_flag_by_name("name")
|
name = response.input_flags.get_flag_by_name("name")
|
||||||
@@ -26,6 +27,7 @@ def hello_handler(response: Response):
|
|||||||
else:
|
else:
|
||||||
print("Привет, мир!")
|
print("Привет, мир!")
|
||||||
|
|
||||||
|
|
||||||
# 4. Подключение роутера к приложению
|
# 4. Подключение роутера к приложению
|
||||||
app.include_router(main_router)
|
app.include_router(main_router)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
from argenta import Command, Response, Router
|
from argenta import Command, Response, Router
|
||||||
from argenta.command import Flag, Flags
|
from argenta.command.flag import ValidationStatus, Flag, Flags
|
||||||
from argenta.command.flag.models import ValidationStatus
|
|
||||||
from argenta.di import FromDishka
|
from argenta.di import FromDishka
|
||||||
|
|
||||||
from .repository import Priority, Task, TaskRepository
|
from .repository import Priority, Task, TaskRepository
|
||||||
@@ -24,14 +23,12 @@ router = Router(title="Task Manager")
|
|||||||
)
|
)
|
||||||
def add_task(response: Response, repo: FromDishka[TaskRepository]):
|
def add_task(response: Response, repo: FromDishka[TaskRepository]):
|
||||||
description_flag = response.input_flags.get_flag_by_name("description")
|
description_flag = response.input_flags.get_flag_by_name("description")
|
||||||
|
if not description_flag or not description_flag.status == ValidationStatus.VALID:
|
||||||
if not description_flag or not description_flag.input_value:
|
|
||||||
print("Error: --description flag is required.")
|
print("Error: --description flag is required.")
|
||||||
return
|
return
|
||||||
task_description = description_flag.input_value
|
task_description = description_flag.input_value or ""
|
||||||
|
|
||||||
priority_flag = response.input_flags.get_flag_by_name("priority")
|
priority_flag = response.input_flags.get_flag_by_name("priority")
|
||||||
|
|
||||||
if priority_flag and priority_flag.status == ValidationStatus.VALID:
|
if priority_flag and priority_flag.status == ValidationStatus.VALID:
|
||||||
priority_value = priority_flag.input_value
|
priority_value = priority_flag.input_value
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ from argenta import App, Orchestrator
|
|||||||
from .handlers import router
|
from .handlers import router
|
||||||
from .provider import TaskProvider
|
from .provider import TaskProvider
|
||||||
|
|
||||||
# 1. Создаем экземпляр приложения
|
# 1. Создаем экземпляр приложения и оркестратора
|
||||||
app = App(
|
app = App(
|
||||||
initial_message="Task Manager",
|
initial_message="Task Manager",
|
||||||
prompt="Enter a command: ",
|
prompt="Enter a command: ",
|
||||||
)
|
)
|
||||||
|
orchestrator = Orchestrator(custom_providers=[TaskProvider()])
|
||||||
|
|
||||||
# 2. Подключаем роутер с нашими командами
|
# 2. Подключаем роутер с нашими командами
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
|
|
||||||
# 3. Создаем и запускаем оркестратор
|
# 3. Запускаем поллинг через оркестратор
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
orchestrator = Orchestrator(custom_providers=[TaskProvider()])
|
|
||||||
orchestrator.start_polling(app)
|
orchestrator.start_polling(app)
|
||||||
|
|||||||
@@ -3,19 +3,23 @@ from argenta.command import Flag
|
|||||||
from argenta.di import FromDishka
|
from argenta.di import FromDishka
|
||||||
|
|
||||||
# 1. Создаём роутер
|
# 1. Создаём роутер
|
||||||
router = Router(title='Authentication')
|
router = Router(title="Authentication")
|
||||||
|
|
||||||
|
|
||||||
# 2. Определяем сервис и обработчики
|
# 2. Определяем сервис и обработчики
|
||||||
def authenticate_user(username: str) -> str:
|
def authenticate_user(username: str) -> str:
|
||||||
"""Возвращает фиктивный токен для пользователя."""
|
"""Возвращает фиктивный токен для пользователя."""
|
||||||
return f"token_for_{username}"
|
return f"token_for_{username}"
|
||||||
|
|
||||||
@router.command(Command('login', flags=Flag('username')))
|
|
||||||
|
@router.command(Command("login", flags=Flag("username")))
|
||||||
def login_handler(response: Response, data_bridge: FromDishka[DataBridge]):
|
def login_handler(response: Response, data_bridge: FromDishka[DataBridge]):
|
||||||
"""Обработчик для команды 'login'. Сохраняет токен в хранилище."""
|
"""Обработчик для команды 'login'. Сохраняет токен в хранилище."""
|
||||||
username_flag = response.input_flags.get_flag_by_name('username')
|
username_flag = response.input_flags.get_flag_by_name("username")
|
||||||
if not username_flag or not username_flag.input_value:
|
if not username_flag or not username_flag.input_value:
|
||||||
print("[red]Ошибка:[/red] необходимо указать имя пользователя с помощью флага --username.")
|
print(
|
||||||
|
"[red]Ошибка:[/red] необходимо указать имя пользователя с помощью флага --username."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
username = username_flag.input_value
|
username = username_flag.input_value
|
||||||
@@ -25,19 +29,23 @@ def login_handler(response: Response, data_bridge: FromDishka[DataBridge]):
|
|||||||
data_bridge.update({"auth_token": token})
|
data_bridge.update({"auth_token": token})
|
||||||
print(f"[green]Успешный вход![/green] Пользователь '{username}' аутентифицирован.")
|
print(f"[green]Успешный вход![/green] Пользователь '{username}' аутентифицирован.")
|
||||||
|
|
||||||
@router.command('get-profile')
|
|
||||||
|
@router.command("get-profile")
|
||||||
def get_profile_handler(response: Response, data_bridge: FromDishka[DataBridge]):
|
def get_profile_handler(response: Response, data_bridge: FromDishka[DataBridge]):
|
||||||
"""Обработчик для команды 'get-profile'. Использует токен из хранилища."""
|
"""Обработчик для команды 'get-profile'. Использует токен из хранилища."""
|
||||||
session_data = data_bridge.get_all()
|
session_data = data_bridge.get_all()
|
||||||
token = session_data.get("auth_token")
|
token = session_data.get("auth_token")
|
||||||
|
|
||||||
if not token:
|
if not token:
|
||||||
print("[red]Ошибка:[/red] вы не аутентифицированы. Сначала выполните команду 'login'.")
|
print(
|
||||||
|
"[red]Ошибка:[/red] вы не аутентифицированы. Сначала выполните команду 'login'."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
print(f"Загрузка профиля с использованием токена: [yellow]{token}[/yellow]")
|
print(f"Загрузка профиля с использованием токена: [yellow]{token}[/yellow]")
|
||||||
|
|
||||||
@router.command('logout')
|
|
||||||
|
@router.command("logout")
|
||||||
def logout_handler(response: Response, data_bridge: FromDishka[DataBridge]):
|
def logout_handler(response: Response, data_bridge: FromDishka[DataBridge]):
|
||||||
"""Обработчик для команды 'logout'. Очищает токен."""
|
"""Обработчик для команды 'logout'. Очищает токен."""
|
||||||
try:
|
try:
|
||||||
@@ -45,4 +53,3 @@ def logout_handler(response: Response, data_bridge: FromDishka[DataBridge]):
|
|||||||
print("[green]Выход выполнен.[/green] Данные сессии очищены.")
|
print("[green]Выход выполнен.[/green] Данные сессии очищены.")
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print("Вы и так не были аутентифицированы.")
|
print("Вы и так не были аутентифицированы.")
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class TestAppIntegration(unittest.TestCase):
|
|||||||
@self.router.command(Command("HELP", description="Show help"))
|
@self.router.command(Command("HELP", description="Show help"))
|
||||||
def help_cmd(response: Response):
|
def help_cmd(response: Response):
|
||||||
print("Available commands: HELP")
|
print("Available commands: HELP")
|
||||||
|
|
||||||
_ = help_cmd # appease linter: function is registered via decorator
|
_ = help_cmd # appease linter: function is registered via decorator
|
||||||
|
|
||||||
self.app.include_router(self.router)
|
self.app.include_router(self.router)
|
||||||
@@ -22,5 +23,3 @@ class TestAppIntegration(unittest.TestCase):
|
|||||||
with redirect_stdout(io.StringIO()) as stdout:
|
with redirect_stdout(io.StringIO()) as stdout:
|
||||||
self.router.finds_appropriate_handler(InputCommand.parse("HELP"))
|
self.router.finds_appropriate_handler(InputCommand.parse("HELP"))
|
||||||
self.assertIn("Available commands:", stdout.getvalue())
|
self.assertIn("Available commands:", stdout.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class Service:
|
|||||||
def hello(self) -> str:
|
def hello(self) -> str:
|
||||||
return "world"
|
return "world"
|
||||||
|
|
||||||
|
|
||||||
router = Router(title="DI")
|
router = Router(title="DI")
|
||||||
|
|
||||||
|
|
||||||
@@ -56,5 +57,3 @@ class TestDIHandler(unittest.TestCase):
|
|||||||
class _FakeApp:
|
class _FakeApp:
|
||||||
# Minimal stub for setup_dishka; app object is not used in unit tests
|
# Minimal stub for setup_dishka; app object is not used in unit tests
|
||||||
registered_routers = []
|
registered_routers = []
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,5 +20,3 @@ class TestSimpleHandler(unittest.TestCase):
|
|||||||
with redirect_stdout(io.StringIO()) as stdout:
|
with redirect_stdout(io.StringIO()) as stdout:
|
||||||
router.finds_appropriate_handler(InputCommand.parse("PING"))
|
router.finds_appropriate_handler(InputCommand.parse("PING"))
|
||||||
self.assertIn("PONG", stdout.getvalue())
|
self.assertIn("PONG", stdout.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+8
-3
@@ -6,13 +6,16 @@
|
|||||||
Argenta
|
Argenta
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
Что это и зачем?
|
||||||
|
----------------
|
||||||
|
|
||||||
**Библиотека для построения модульных CLI-приложений с простым и приятным API.**
|
**Библиотека для построения модульных CLI-приложений с простым и приятным API.**
|
||||||
|
|
||||||
Если у вас есть функциональность, которую вы хотите предоставить в виде CLI-приложения, Argenta поможет вам в этом.
|
Если у вас есть функциональность, которую вы хотите предоставить в виде CLI-приложения, Argenta поможет вам в этом.
|
||||||
Основная цель библиотеки — дать разработчикам возможность сосредоточиться на реализации своих идей, предоставляя для этого удобные абстракции.
|
Основная цель библиотеки — дать разработчикам возможность сосредоточиться на реализации своих идей, предоставляя для этого удобные абстракции.
|
||||||
|
|
||||||
.. image:: https://github.com/koloideal/Argenta/blob/main/imgs/mock_app_preview4.png?raw=True
|
.. image:: https://github.com/koloideal/Argenta/blob/main/imgs/mock_app_preview4.png?raw=True
|
||||||
:alt: Пример приложения
|
:alt: App example
|
||||||
|
|
||||||
Argenta предназначена для создания приложений, работающих в собственном контексте (scope). Это означает, что при запуске пользователь входит в интерактивную сессию, где ему доступна вся реализованная вами функциональность.
|
Argenta предназначена для создания приложений, работающих в собственном контексте (scope). Это означает, что при запуске пользователь входит в интерактивную сессию, где ему доступна вся реализованная вами функциональность.
|
||||||
|
|
||||||
@@ -22,9 +25,11 @@ Argenta предназначена для создания приложений,
|
|||||||
|
|
||||||
* **Интерактивные сессии**. В отличие от традиционных CLI-инструментов, Argenta создаёт циклические сессии, позволяя пользователю выполнять команды последовательно, не перезапуская приложение.
|
* **Интерактивные сессии**. В отличие от традиционных CLI-инструментов, Argenta создаёт циклические сессии, позволяя пользователю выполнять команды последовательно, не перезапуская приложение.
|
||||||
* **Декларативный синтаксис**. Команды и их обработчики объявляются с помощью простых декораторов, что делает код чистым и интуитивно понятным.
|
* **Декларативный синтаксис**. Команды и их обработчики объявляются с помощью простых декораторов, что делает код чистым и интуитивно понятным.
|
||||||
* **Встроенное внедрение зависимостей (DI)**. Благодаря интеграции с `dishka <https://dishka.readthedocs.io/en/stable/>`_, вы можете легко внедрять сервисы (например, подключения к БД) прямо в обработчики команд, что упрощает их тестирование и переиспользование.
|
* **Нативный DI**. Благодаря интеграции с `dishka <https://dishka.readthedocs.io/en/stable/>`_, вы можете легко внедрять зависимости прямо в обработчики команд, что упрощает их тестирование и переиспользование.
|
||||||
* **Автоматическая валидация и парсинг**. Библиотека берёт на себя обработку флагов и аргументов командной строки, включая их парсинг, валидацию и преобразование типов.
|
* **Автоматическая валидация и парсинг**. Библиотека берёт на себя обработку флагов и аргументов командной строки, включая их парсинг, валидацию и преобразование типов.
|
||||||
* **Гибкая настройка**. Вы можете легко кастомизировать системные сообщения, форматирование вывода и даже перенаправлять стандартный вывод (stdout) в свои обработчики.
|
* **Гибкая настройка**. Вы можете легко кастомизировать системные сообщения, форматирование вывода и т.д.
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
Архитектура и жизненный цикл
|
Архитектура и жизненный цикл
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|||||||
@@ -5,12 +5,18 @@
|
|||||||
|
|
||||||
В этом руководстве мы рассмотрим два примера создания CLI-приложения с помощью Argenta:
|
В этом руководстве мы рассмотрим два примера создания CLI-приложения с помощью Argenta:
|
||||||
|
|
||||||
* **Простой пример**: Быстрое знакомство с основными компонентами, такими как `App`, `Command` и `Router`.
|
* **Простой пример**: Минимальное приложение, быстрое знакомство с основными компонентами.
|
||||||
* **Более сложный пример**: Полнофункциональное приложение «Менеджер задач» с внедрением зависимостей и бизнес-логикой.
|
* **Более сложный пример**: Полнофункциональное приложение «Менеджер задач» с внедрением зависимостей и бизнес-логикой.
|
||||||
|
|
||||||
Простой пример
|
Простой пример
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
**Установка**
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
pip install argenta
|
||||||
|
|
||||||
Этот пример демонстрирует абсолютный минимум, необходимый для создания и запуска приложения. Вы можете скопировать этот код, запустить его и сразу увидеть результат.
|
Этот пример демонстрирует абсолютный минимум, необходимый для создания и запуска приложения. Вы можете скопировать этот код, запустить его и сразу увидеть результат.
|
||||||
|
|
||||||
.. literalinclude:: ../code_snippets/quickstart/simple_app.py
|
.. literalinclude:: ../code_snippets/quickstart/simple_app.py
|
||||||
@@ -22,6 +28,8 @@
|
|||||||
.. image:: https://i.ibb.co/JwK9Vv4j/2025-11-03-135118.png
|
.. image:: https://i.ibb.co/JwK9Vv4j/2025-11-03-135118.png
|
||||||
:alt: Simple App Example
|
:alt: Simple App Example
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
Более сложный пример: Менеджер задач
|
Более сложный пример: Менеджер задач
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
@@ -43,7 +51,7 @@
|
|||||||
|
|
||||||
3. **Создание провайдера для DI**
|
3. **Создание провайдера для DI**
|
||||||
|
|
||||||
Чтобы Argenta могла внедрять `TaskRepository` в наши обработчики, мы создадим провайдер для `dishka`.
|
Чтобы Argenta могла внедрять ``TaskRepository`` в наши обработчики, мы создадим провайдер для ``dishka``.
|
||||||
|
|
||||||
.. literalinclude:: ../code_snippets/quickstart/task_manager/provider.py
|
.. literalinclude:: ../code_snippets/quickstart/task_manager/provider.py
|
||||||
:language: python
|
:language: python
|
||||||
@@ -51,7 +59,7 @@
|
|||||||
|
|
||||||
4. **Создание обработчиков команд**
|
4. **Создание обработчиков команд**
|
||||||
|
|
||||||
Теперь создадим обработчики для команд `add-task` и `list-tasks`. Обратите внимание, как мы используем флаги и внедряем `TaskRepository`.
|
Теперь создадим обработчики для команд ``add-task`` и ``list-tasks``. Обратите внимание, как мы используем флаги и внедряем ``TaskRepository``.
|
||||||
|
|
||||||
.. literalinclude:: ../code_snippets/quickstart/task_manager/handlers.py
|
.. literalinclude:: ../code_snippets/quickstart/task_manager/handlers.py
|
||||||
:language: python
|
:language: python
|
||||||
@@ -59,7 +67,7 @@
|
|||||||
|
|
||||||
5. **Сборка и запуск приложения**
|
5. **Сборка и запуск приложения**
|
||||||
|
|
||||||
Наконец, соберем все вместе: создадим экземпляр `App`, подключим роутер и провайдер, а затем запустим приложение.
|
Наконец, соберем все вместе: создадим экземпляр ``App``, подключим роутер и провайдер, а затем запустим приложение.
|
||||||
|
|
||||||
.. literalinclude:: ../code_snippets/quickstart/task_manager/main.py
|
.. literalinclude:: ../code_snippets/quickstart/task_manager/main.py
|
||||||
:language: python
|
:language: python
|
||||||
@@ -67,7 +75,7 @@
|
|||||||
|
|
||||||
6. **Результат**
|
6. **Результат**
|
||||||
|
|
||||||
Теперь вы можете запустить `main.py` и взаимодействовать с вашим новым CLI-приложением.
|
Теперь вы можете запустить ``main.py`` и взаимодействовать с вашим новым CLI-приложением.
|
||||||
|
|
||||||
.. image:: https://i.ibb.co/bgsCLZhP/image.png
|
.. image:: https://i.ibb.co/bgsCLZhP/image.png
|
||||||
:alt: Task Manager Example
|
:alt: Task Manager Example
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import polib
|
||||||
|
import deepl
|
||||||
|
import getopt
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
DEEPL_API_TOKEN = 'ADD YOUR API KEY HERE!'
|
||||||
|
|
||||||
|
global argv
|
||||||
|
global opts
|
||||||
|
global args
|
||||||
|
|
||||||
|
argv = sys.argv[1:]
|
||||||
|
opts, args = getopt.getopt(argv, "f:l:")
|
||||||
|
|
||||||
|
def translate(text, lang):
|
||||||
|
# Define a dictionary to hold the mappings of tokens to placeholders
|
||||||
|
placeholders = {}
|
||||||
|
|
||||||
|
# Use a regular expression to find all the tokens
|
||||||
|
tokens = re.findall(r'%\((.*?)\)s', text)
|
||||||
|
|
||||||
|
# Replace each token with a unique placeholder
|
||||||
|
for i, token in enumerate(tokens):
|
||||||
|
placeholder = f'__PLACEHOLDER_{i}__'
|
||||||
|
placeholders[placeholder] = f'%({token})s'
|
||||||
|
text = text.replace(f'%({token})s', placeholder)
|
||||||
|
|
||||||
|
# Perform the translation
|
||||||
|
translator = deepl.Translator(DEEPL_API_TOKEN)
|
||||||
|
translated_text = str(translator.translate_text(text, target_lang=lang))
|
||||||
|
|
||||||
|
# Replace the placeholders back with the original tokens
|
||||||
|
for placeholder, token in placeholders.items():
|
||||||
|
translated_text = translated_text.replace(placeholder, token)
|
||||||
|
|
||||||
|
return translated_text
|
||||||
|
|
||||||
|
def get_filename():
|
||||||
|
# read arguments from command line
|
||||||
|
for opt, arg in opts:
|
||||||
|
if opt in ['-f']:
|
||||||
|
filename = arg
|
||||||
|
if not filename:
|
||||||
|
print('Please enter the filename of the PO file e.g. /directory/django.po:')
|
||||||
|
filename = input()
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def get_target_language():
|
||||||
|
# read arguments from command line
|
||||||
|
for opt, arg in opts:
|
||||||
|
if opt in ['-l']:
|
||||||
|
lang = arg
|
||||||
|
if not lang:
|
||||||
|
print('Please enter two letter ISO language code e.g. DE:')
|
||||||
|
lang = input()
|
||||||
|
return lang
|
||||||
|
|
||||||
|
def process_file(filename, lang):
|
||||||
|
po = polib.pofile(filename)
|
||||||
|
for entry in po.untranslated_entries():
|
||||||
|
if not entry.msgstr:
|
||||||
|
print(entry.msgid)
|
||||||
|
print('translating...')
|
||||||
|
entry.msgstr = translate(entry.msgid, lang)
|
||||||
|
print(entry.msgstr)
|
||||||
|
print('\n')
|
||||||
|
po.save(filename)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
process_file(get_filename(), get_target_language())
|
||||||
Reference in New Issue
Block a user