mirror of
https://github.com/kaaninchen/Gleiswechsel.git
synced 2026-09-17 16:52:47 +00:00
transitous rewrite: multiple languages support
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
"token": "",
|
"token": "",
|
||||||
"server": ,
|
"server": ,
|
||||||
"vc": ,
|
"vc": ,
|
||||||
|
"lang": "de"
|
||||||
"formatting": "┇",
|
"formatting": "┇",
|
||||||
"emojis": true
|
"emojis": true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ numpy==2.5.1
|
|||||||
pandas==3.0.3
|
pandas==3.0.3
|
||||||
propcache==0.5.2
|
propcache==0.5.2
|
||||||
py-cord==2.8.0
|
py-cord==2.8.0
|
||||||
|
pyyaml==6.0.3
|
||||||
pycparser==3.0
|
pycparser==3.0
|
||||||
pynacl==1.6.2
|
pynacl==1.6.2
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ from datetime import datetime, timezone
|
|||||||
|
|
||||||
from src.utils import logger, get_train_name, validate_connection, parse_iso
|
from src.utils import logger, get_train_name, validate_connection, parse_iso
|
||||||
from src.config import config
|
from src.config import config
|
||||||
|
from src.lang.locales import lang
|
||||||
|
|
||||||
|
|
||||||
stations = config.connections.stations
|
stations = config.connections.stations
|
||||||
blacklist = config.connections.blacklist
|
blacklist = config.connections.blacklist
|
||||||
|
long_name_lang = lang.channel.long_name
|
||||||
user_agent = config.http.user_agent
|
user_agent = config.http.user_agent
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
@@ -220,9 +223,9 @@ def get_trip_details(random_connection: dict | None) -> dict | None:
|
|||||||
|
|
||||||
train_name = get_train_name(display_name, mode)
|
train_name = get_train_name(display_name, mode)
|
||||||
if train_from == from_station:
|
if train_from == from_station:
|
||||||
long_name = f"{train_name} nach {goes_to} von {train_from}"
|
long_name = long_name_lang.train_from()
|
||||||
else:
|
else:
|
||||||
long_name = f"{train_name} nach {goes_to} über {from_station}"
|
long_name = long_name_lang.train_via()
|
||||||
|
|
||||||
trip_details = {
|
trip_details = {
|
||||||
"long_name": long_name,
|
"long_name": long_name,
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,4 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
@@ -8,6 +7,7 @@ class DiscordConfig:
|
|||||||
token: str
|
token: str
|
||||||
server: int
|
server: int
|
||||||
vc: int
|
vc: int
|
||||||
|
lang: str
|
||||||
formatting: str
|
formatting: str
|
||||||
emojis: bool
|
emojis: bool
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
channel:
|
||||||
|
status: "Ankunft um {arrival}"
|
||||||
|
long_name:
|
||||||
|
train_from: "{train_name} nach {goes_to} von {train_from}"
|
||||||
|
train_via: "{train_name} nach {goes_to} über {from_station}"
|
||||||
|
embeds:
|
||||||
|
footer:
|
||||||
|
notice: "Daten großzügig bereitgestellt von https://transitous.org"
|
||||||
|
info:
|
||||||
|
description: "Abfahrt von {station} um {departure}. Ankunft um {arrival}"
|
||||||
|
via: "Über"
|
||||||
|
"via_and": "und"
|
||||||
|
"next_stop": "Nächster Halt"
|
||||||
|
announcement:
|
||||||
|
title: "Informationen zu ihrer Fahrt"
|
||||||
|
"end_of_connection":
|
||||||
|
message: "Sehr geehrte Fahrgäste,\nIn wenigen Minuten erreichen wir {destination}. Unsere Reise endet dort\n\nWir wünschen Ihnen eine angenehme Weiterreise.\n\nVielen Dank für ihr Vertrauen und auf Wiedersehen."
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
channel:
|
||||||
|
status: "Ankunft um {arrival}"
|
||||||
|
long_name:
|
||||||
|
train_from: "{train_name} to {goes_to} from {train_from}"
|
||||||
|
train_via: "{train_name} to {goes_to} via {from_station}"
|
||||||
|
embeds:
|
||||||
|
footer:
|
||||||
|
notice: "Data provided by https://transitous.org"
|
||||||
|
info:
|
||||||
|
description: "Departure from {station} at {departure}. Arrival by {arrival}"
|
||||||
|
via: "via"
|
||||||
|
"via_and": "and"
|
||||||
|
"next_stop": "Next stop"
|
||||||
|
announcement:
|
||||||
|
title: "Information about your trip"
|
||||||
|
"end_of_connection":
|
||||||
|
message: "Dear Passengers,\nwe will be arriving at {destination} in a few minutes. Our journey ends there.\n\nWe wish you a pleasant onward journey.\n\nThank you for your patronage, and goodbye."
|
||||||
+10
-6
@@ -3,9 +3,12 @@ import random
|
|||||||
|
|
||||||
from src.utils import get_operator_metadata, get_next_station, format_via_list, format_stop_list
|
from src.utils import get_operator_metadata, get_next_station, format_via_list, format_stop_list
|
||||||
from src.dc.helpers import format_timestamp_to_dc
|
from src.dc.helpers import format_timestamp_to_dc
|
||||||
|
from src.lang.locales import lang
|
||||||
|
|
||||||
|
lang_embed = lang.embeds
|
||||||
|
|
||||||
def build_embed_footer(mode: str, slogans):
|
def build_embed_footer(mode: str, slogans):
|
||||||
footer_notice = f"Data provided by https://transitous.org • mode: {mode}"
|
footer_notice = f"{lang.embeds.footer.notice()} • mode: {mode}"
|
||||||
icon = "https://raw.githubusercontent.com/kaaninchen/Gleiswechsel/refs/heads/main/src/data/assets/transitous-logo.png"
|
icon = "https://raw.githubusercontent.com/kaaninchen/Gleiswechsel/refs/heads/main/src/data/assets/transitous-logo.png"
|
||||||
|
|
||||||
if slogans is not None:
|
if slogans is not None:
|
||||||
@@ -22,6 +25,7 @@ def build_embed_footer(mode: str, slogans):
|
|||||||
def build_info_embed() -> discord.Embed:
|
def build_info_embed() -> discord.Embed:
|
||||||
from src.dc.handlers import trip
|
from src.dc.handlers import trip
|
||||||
|
|
||||||
|
station = trip["station"]
|
||||||
agency = trip["agency"]
|
agency = trip["agency"]
|
||||||
metadata = get_operator_metadata(agency, trip["route_color"], trip["mode"])
|
metadata = get_operator_metadata(agency, trip["route_color"], trip["mode"])
|
||||||
departure = format_timestamp_to_dc(trip["departure"])
|
departure = format_timestamp_to_dc(trip["departure"])
|
||||||
@@ -29,7 +33,7 @@ def build_info_embed() -> discord.Embed:
|
|||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title = trip["long_name"],
|
title = trip["long_name"],
|
||||||
description=f"Abfahrt von {trip["station"]} um {departure}. Ankunft um {arrival}",
|
description=lang_embed.info.description(),
|
||||||
color = metadata["color"]
|
color = metadata["color"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,12 +44,12 @@ def build_info_embed() -> discord.Embed:
|
|||||||
if next_stop:
|
if next_stop:
|
||||||
next_stop_station = next_stop.get("name")
|
next_stop_station = next_stop.get("name")
|
||||||
|
|
||||||
via = format_via_list(stops)
|
via = format_via_list(stops, lang_embed.info.via_and())
|
||||||
|
|
||||||
if via:
|
if via:
|
||||||
embed.add_field(name="Über", value=via, inline=False)
|
embed.add_field(name=lang_embed.info.via(), value=via, inline=False)
|
||||||
if next_stop:
|
if next_stop:
|
||||||
embed.add_field(name="Nächster Halt", value=f"__{next_stop_station}__", inline=False)
|
embed.add_field(name=lang_embed.info.next_stop(), value=f"__{next_stop_station}__", inline=False)
|
||||||
|
|
||||||
route_fields = format_stop_list(stops, next_stop_station)
|
route_fields = format_stop_list(stops, next_stop_station)
|
||||||
for field_name, field_value in route_fields:
|
for field_name, field_value in route_fields:
|
||||||
@@ -65,7 +69,7 @@ def build_announcement_embed(msg):
|
|||||||
metadata = get_operator_metadata(agency, trip["route_color"], trip["mode"])
|
metadata = get_operator_metadata(agency, trip["route_color"], trip["mode"])
|
||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title = "Informationen zu ihrer Fahrt",
|
title = lang_embed.announcement.title(),
|
||||||
description=msg,
|
description=msg,
|
||||||
color=metadata["color"]
|
color=metadata["color"]
|
||||||
)
|
)
|
||||||
|
|||||||
+6
-5
@@ -1,12 +1,14 @@
|
|||||||
import discord
|
import discord
|
||||||
import asyncio
|
import asyncio
|
||||||
import random
|
import random
|
||||||
from datetime import datetime, timedelta, date
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from src.utils import logger, channel_formatting, choose_connection, get_sound_path, LOCAL_TZ, get_next_station
|
from src.utils import logger, channel_formatting, choose_connection, get_sound_path, LOCAL_TZ, get_next_station
|
||||||
from src.config import config
|
from src.config import config
|
||||||
|
from src.lang.locales import lang
|
||||||
|
|
||||||
_scheduled_task: asyncio.Task | None = None
|
_scheduled_task: asyncio.Task | None = None
|
||||||
|
channel_lang = lang.channel
|
||||||
|
|
||||||
async def rename_vc(bot: discord.Bot, voice_channel, from_scheduler: bool = False):
|
async def rename_vc(bot: discord.Bot, voice_channel, from_scheduler: bool = False):
|
||||||
global trip, _scheduled_task
|
global trip, _scheduled_task
|
||||||
@@ -35,7 +37,7 @@ async def rename_vc(bot: discord.Bot, voice_channel, from_scheduler: bool = Fals
|
|||||||
|
|
||||||
formatting = channel_formatting(mode)
|
formatting = channel_formatting(mode)
|
||||||
await voice_channel.edit(name=f"{formatting}{long_name}")
|
await voice_channel.edit(name=f"{formatting}{long_name}")
|
||||||
await voice_channel.set_status(f"Ankunft um {arrival}")
|
await voice_channel.set_status(channel_lang.status())
|
||||||
|
|
||||||
logger(f"Updated channel name!")
|
logger(f"Updated channel name!")
|
||||||
|
|
||||||
@@ -56,8 +58,7 @@ async def announcer(announcement: str, voice_channel: discord.VoiceChannel, dest
|
|||||||
announcement_status = await voice_announcer(destination, voice_channel)
|
announcement_status = await voice_announcer(destination, voice_channel)
|
||||||
if announcement_status:
|
if announcement_status:
|
||||||
return
|
return
|
||||||
embed = build_announcement_embed(
|
embed = build_announcement_embed(lang.embeds.announcement.end_of_connection.message())
|
||||||
f'Sehr geehrte Fahrgäste,\nIn wenigen Minuten erreichen wir {destination}. Dieser Zug endet dort.\n\nWir wünschen Ihnen eine angenehme Weiterreise.\n\nVielen Dank für ihr Vertrauen und auf Wiedersehen.')
|
|
||||||
case "transfer":
|
case "transfer":
|
||||||
embed = build_info_embed()
|
embed = build_info_embed()
|
||||||
case _:
|
case _:
|
||||||
@@ -96,7 +97,7 @@ async def _schedule_next_transfer(bot: discord.Bot, arrival_dt: datetime, voice_
|
|||||||
|
|
||||||
if wait_seconds > 0:
|
if wait_seconds > 0:
|
||||||
remaining = str(timedelta(seconds=wait_seconds))
|
remaining = str(timedelta(seconds=wait_seconds))
|
||||||
logger(f"Nexxt transfer in {remaining.split('.')[0]} ({arrival_dt.strftime('%H:%M')} Uhr)")
|
logger(f"Next transfer in {remaining.split('.')[0]} ({arrival_dt.strftime('%H:%M')} Uhr)")
|
||||||
|
|
||||||
if wait_seconds > announcement_countdown:
|
if wait_seconds > announcement_countdown:
|
||||||
wait_until_end_announcement = wait_seconds - announcement_countdown
|
wait_until_end_announcement = wait_seconds - announcement_countdown
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# generated by datamodel-codegen:
|
||||||
|
# filename: de.yaml
|
||||||
|
# timestamp: 2026-08-17T18:44:37+00:00
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LongName:
|
||||||
|
train_from: str
|
||||||
|
train_via: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Channel:
|
||||||
|
status: str
|
||||||
|
long_name: LongName
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Footer:
|
||||||
|
notice: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Info:
|
||||||
|
description: str
|
||||||
|
via: str
|
||||||
|
via_and: str
|
||||||
|
next_stop: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EndOfConnection:
|
||||||
|
message: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Announcement:
|
||||||
|
title: str
|
||||||
|
end_of_connection: EndOfConnection
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Embeds:
|
||||||
|
footer: Footer
|
||||||
|
info: Info
|
||||||
|
announcement: Announcement
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Model:
|
||||||
|
channel: Channel
|
||||||
|
embeds: Embeds
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import yaml
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from src.utils import logger
|
||||||
|
from src.config import config
|
||||||
|
from src.lang.language_models import Model
|
||||||
|
|
||||||
|
class AutoFormatStr(str):
|
||||||
|
def __call__(self):
|
||||||
|
frame = sys._getframe(1)
|
||||||
|
context = {**frame.f_globals, **frame.f_locals}
|
||||||
|
return self.format(**context)
|
||||||
|
|
||||||
|
class LanguageObject:
|
||||||
|
def __init__(self, dictionary: dict):
|
||||||
|
for key, value in dictionary.items():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
setattr(self, key, LanguageObject(value))
|
||||||
|
elif isinstance(value, str):
|
||||||
|
setattr(self, key, AutoFormatStr(value))
|
||||||
|
else:
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def load_language(lang_code):
|
||||||
|
path = f"src/data/locales/{lang_code}.yaml"
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(path, "r", encoding="utf-8") as file:
|
||||||
|
data = yaml.safe_load(file)
|
||||||
|
return LanguageObject(data)
|
||||||
|
except Exception as e:
|
||||||
|
logger(f"Failed loading language file: {e}", "FATAL")
|
||||||
|
|
||||||
|
lang: Model = load_language(config.discord.lang)
|
||||||
+2
-2
@@ -160,7 +160,7 @@ def get_next_station(stops: dict, train_from: str) -> dict | None:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def format_via_list(stops: dict) -> str:
|
def format_via_list(stops: dict, via_and: str) -> str:
|
||||||
if len(stops) > 2:
|
if len(stops) > 2:
|
||||||
stations = list(stops.keys())
|
stations = list(stops.keys())
|
||||||
trip_from = stations[0]
|
trip_from = stations[0]
|
||||||
@@ -173,7 +173,7 @@ def format_via_list(stops: dict) -> str:
|
|||||||
important_stops.remove(trip_to)
|
important_stops.remove(trip_to)
|
||||||
|
|
||||||
if len(important_stops) > 1:
|
if len(important_stops) > 1:
|
||||||
via = f"{', '.join(important_stops[:-1])} und {important_stops[-1]}"
|
via = f"{', '.join(important_stops[:-1])} {via_and} {important_stops[-1]}"
|
||||||
else:
|
else:
|
||||||
via = important_stops[0]
|
via = important_stops[0]
|
||||||
return via
|
return via
|
||||||
|
|||||||
Reference in New Issue
Block a user