Добавлена локализация, теперь приложение также переведено на английский

This commit is contained in:
kirill 2025-05-29 13:38:15 +03:00
parent 83f4cac86b
commit 7ecdf5ea39
7 changed files with 115 additions and 73 deletions

View File

@ -10,6 +10,6 @@ API_HASH=b18441a1ff607e10a989891a5462e627
# Конфиг
CURRENT_USER = "user"
DO_NOTIFY = False
DO_NOTIFY = 0 # Linux-only for now
UTC_OFFSET = 0
LANGUAGE = "en"
LANGUAGE = "en" # en, ru

View File

@ -31,7 +31,7 @@ pip install -r requirements.txt
4. Настройте переменные окружения:
```bash
cp .env.example .env
# Отредактируйте .env файл, добавив свои API ключи
# Настройте .env файл и добавьте свои API ключи
# Получите ключи на https://my.telegram.org/apps
```

View File

@ -4,30 +4,60 @@ from os import getenv
from dotenv import load_dotenv
from telethon import TelegramClient
from textual.app import App
from textual.keys import Keys, _character_to_key
from datetime import timezone, timedelta
from src.screens import AuthScreen, ChatScreen
import src.locales
load_dotenv()
API_ID = getenv("API_ID")
API_HASH = getenv("API_HASH")
LANGUAGE = getenv("LANGUAGE")
UTC_OFFSET = getenv("UTC_OFFSET")
if not API_ID or not API_HASH:
if "" in [API_ID, API_HASH, LANGUAGE, UTC_OFFSET]:
raise ValueError(
"API_ID и API_HASH не найдены в .env файле. "
"Пожалуйста, скопируйте .env.example в .env и заполните свои ключи."
"Недостаточно параметров в .env файле."
"Скопируйте .env.example в .env и заполните свои API-ключи."
"Not enough settings in .env file."
"Copy .env.example into .env and fill your API-keys."
)
API_ID = int(API_ID)
#locale = locales.
locale = dict(zip(
getattr(src.locales, "codes"), getattr(src.locales, LANGUAGE)
))
class Talc(App):
"""Класс приложения"""
CSS_PATH = "style.tcss"
BINDINGS = [
(Keys.ControlE, "notify(\"Нажата кнопка профиля\")", locale["you"]),
(Keys.ControlF, "notify(\"Нажата кнопка папок\")", locale["folders"]),
(Keys.Tab, "notify(\"Нажат таб\")", locale["switch_focus"]),
(Keys.Enter, "notify(\"Нажат энтер\")", locale["enter"]),
(Keys.Escape, "notify(\"Нажат эскейп\")", locale["back"]),
(_character_to_key("/"), "notify(\"Нажат слэш\")", locale["search"])
]
def __init__(
self,
driver_class = None,
css_path = None,
watch_css = False,
ansi_color = False
):
super().__init__(driver_class, css_path, watch_css, ansi_color)
self.locale = locale
self.timezone = timezone(timedelta(hours=int(UTC_OFFSET)))
async def on_mount(self) -> None:
self.telegram_client = TelegramClient(getenv("CURRENT_USER"), API_ID, API_HASH)
self.telegram_client = TelegramClient(
getenv("CURRENT_USER"),
API_ID,
API_HASH
)
await self.telegram_client.connect()
chat_screen = ChatScreen(telegram_client=self.telegram_client)

View File

@ -1,2 +1,3 @@
ru = {"greeting_auth": "Добро пожаловать в Тальк", "phone_number": "Номер телефона", "code": "Код", "password": "Пароль", "you": "Вы", "mention": "Вас упомянули"}
en = {"greeting_auth": "Welcome to Talc", "phone_number": "Phone number", "code": "Code", "password": "Password", "you": "You", "mention": "You got mentioned"}
codes = ["auth_greeting", "phone_number", "code", "password", "you", "mention", "media", "message", "folders", "switch_focus", "enter", "back", "search", "start_converse"]
ru = ["Добро пожаловать в Тальк", "Номер телефона", "Код", "Пароль", "Вы", "Вас упомянули", "Медиа", "Сообщение", "Папки", "Переключение фокуса", "Открыть", "Назад", "Поиск", "Нажмите на чат в панели слева, чтобы начать общаться"]
en = ["Welcome to Talc", "Phone number", "Code", "Password", "You", "You got mentioned", "Media", "Message", "Folders", "Switch focus", "Enter", "Back", "Search", "Click on the chat to start conversation"]

View File

@ -22,17 +22,18 @@ class AuthScreen(Screen):
) -> None:
super().__init__(name, id, classes)
self.client = telegram_client
self.locale = self.app.locale
def on_mount(self) -> None:
self.ac = self.query_one("#auth_container")
def compose(self) -> ComposeResult:
with Vertical(id="auth_container"):
yield Label("Добро пожаловать в Talc")
yield Input(placeholder="Номер телефона", id="phone")
yield Input(placeholder="Код", id="code", disabled=True)
yield Label(self.locale["auth_greeting"])
yield Input(placeholder=self.locale["phone_number"], id="phone")
yield Input(placeholder=self.locale["code"], id="code", disabled=True)
yield Input(
placeholder="Пароль",
placeholder=self.locale["password"],
id="password",
password=True,
disabled=True
@ -75,6 +76,7 @@ class ChatScreen(Screen):
super().__init__(name, id, classes)
self.telegram_client = telegram_client
self.DO_NOTIFY = getenv("DO_NOTIFY")
self.locale = self.app.locale
async def on_mount(self) -> None:
self.limit = 100
@ -152,6 +154,7 @@ class ChatScreen(Screen):
chat.peername = str(dialogs[i].name)
chat.is_group = dialogs[i].is_group
chat.is_channel = dialogs[i].is_channel
chat.peer_id = dialogs[i].id
try:
@ -161,7 +164,7 @@ class ChatScreen(Screen):
is_my_msg = dialogs[i].id == self.me_id
if dialogs[i].is_group and is_my_msg:
chat.username = "Вы"
chat.username = self.locale["you"]
chat.msg = str(dialogs[i].message.message)
elif dialogs[i].is_group:
chat.username = str(
@ -169,7 +172,7 @@ class ChatScreen(Screen):
)
chat.msg = str(dialogs[i].message.message)
elif is_my_msg:
chat.msg = "Вы: " * is_my_msg + str(
chat.msg = f"{self.locale["you"]}: " * is_my_msg + str(
dialogs[i].message.message
)
else:
@ -183,8 +186,8 @@ class ChatScreen(Screen):
def notify_send(self, event) -> None:
if not event:
return None
if bool(self.DO_NOTIFY) and event.mentioned and not self.app.focused:
system(f"notify-send \"Вас упомянули\" Talc")
if int(self.DO_NOTIFY) and not self.app.focused and event.mentioned:
system(f"notify-send \"{self.locale["mention"]}\" Talc")
def compose(self) -> ComposeResult:
yield Footer() # Нижняя панель с подсказками
@ -196,6 +199,6 @@ class ChatScreen(Screen):
with ContentSwitcher(id="dialog_switcher"):
# ↑ Внутри него как раз крутятся диалоги
yield Label(
"Нажмите на чат в панели слева, чтобы начать общаться",
id="begin_talk_label"
self.locale["start_converse"],
id="start_converse_label"
) #TODO: не показывается надпись, надо будет исправить

View File

@ -74,7 +74,7 @@ ContentSwitcher {
height: 100%;
}
.begin_talk_label {
.start_converse_label {
width: 100%;
height: 100%;
text-align: center;

View File

@ -6,6 +6,7 @@ from textual.reactive import Reactive
from textual.widgets import Input, Button, Label, Static, ContentSwitcher
from textual.app import ComposeResult, RenderResult
from textual.content import Content
from textual.style import Style
from telethon import TelegramClient, events, utils, types
import datetime
from os import getenv
@ -17,6 +18,7 @@ class Chat(Widget):
peername: Reactive[str] = Reactive(" ", recompose=True)
msg: Reactive[str] = Reactive(" ", recompose=True)
is_group: Reactive[bool] = Reactive(False, recompose=True)
is_channel: Reactive[bool] = Reactive(False, recompose=True)
peer_id: Reactive[int] = Reactive(0)
def __init__(
@ -46,7 +48,8 @@ class Chat(Widget):
self.switcher.mount(Dialog(
telegram_client=self.app.telegram_client,
chat_id=self.peer_id,
id=dialog_id
id=dialog_id,
is_channel=self.is_channel and not self.is_group
))
except:
# Диалог уже есть: ничего не делаем
@ -59,10 +62,10 @@ class Chat(Widget):
with Horizontal():
yield Label(f"┌───┐\n{self.peername[:1]:1}\n└───┘")
with Vertical():
yield Label(self.peername, id="peername")
yield Label(self.peername, id="peername", markup=False)
if self.is_group:
yield Label(self.username, id="name")
yield Label(self.msg, id="last_msg")
yield Label(self.username, id="name", markup=False)
yield Label(self.msg, id="last_msg", markup=False)
class Dialog(Widget):
"""Класс окна диалога"""
@ -73,19 +76,20 @@ class Dialog(Widget):
classes=None,
disabled=None,
telegram_client: TelegramClient | None = None,
chat_id = None
chat_id: int | None = None,
is_channel: bool | None = None
) -> None:
super().__init__(id=id, classes=classes, disabled=disabled)
self.telegram_client = telegram_client
self.chat_id = chat_id
self.is_msg_update_blocked = False
self.timezone = datetime.timezone(
datetime.timedelta(hours=int(getenv("UTC_OFFSET")))
)
self.timezone = self.app.timezone
self.is_channel = is_channel
async def on_mount(self) -> None:
self.limit = 10
if not self.is_channel:
self.msg_input = self.query_one("#msg_input")
self.dialog = self.query_one(Vertical).query_one("#dialog")
@ -102,7 +106,7 @@ class Dialog(Widget):
event(chats=(self.chat_id))
)(self.update_dialog)
self.dialog.scroll_down(animate=False, immediate=True)
#self.dialog.scroll_down(animate=False, immediate=True)
def mount_messages(self, limit: int) -> None:
print("Загрузка виджетов сообщений...")
@ -135,15 +139,8 @@ class Dialog(Widget):
for i in range(limit):
msg = self.dialog.query_one(f"#msg-{i + 1}")
message = Content(str(messages[i].message))
if str(messages[i].message):
message = Content(
"[Медиа] " * \
bool(messages[i].media) + \
str(messages[i].message)
)
else:
message = Content("[Медиа]" * bool(messages[i].media) + "")
entities = messages[i].entities
if entities:
for entity in entities:
@ -173,6 +170,13 @@ class Dialog(Widget):
entity.offset + entity.length
)
if messages[i].media and str(message):
msg.message = Content(f"[{self.app.locale["media"]}] ")\
.stylize("link", 0, 6) + message
elif messages[i].media:
msg.message = Content(f"[{self.app.locale["media"]}]")\
.stylize("link", 0, 6)
else:
msg.message = message
try:
@ -195,8 +199,12 @@ class Dialog(Widget):
def compose(self) -> ComposeResult:
with Vertical():
yield VerticalScroll(id="dialog")
if not self.is_channel:
with Horizontal(id="input_place"):
yield Input(placeholder="Сообщение", id="msg_input")
yield Input(
placeholder=self.app.locale["message"],
id="msg_input"
)
yield Button(label="", id="send", variant="primary")
async def on_button_pressed(self, event = None) -> None:
@ -212,7 +220,7 @@ class Dialog(Widget):
str(self.msg_input.value)
)
except ValueError:
self.app.notify("Ошибка отправки")
print("Ошибка отправки")
self.msg_input.value = ""
await self.update_dialog()