1
0
Fork 0
forked from Fuji/Fuji

Add translation and language selection

This commit is contained in:
Maarceeli 2025-02-20 17:43:38 +01:00
parent bef2aadccb
commit f636f0cba9
17 changed files with 387 additions and 62 deletions

36
src/i18n.py Normal file
View file

@ -0,0 +1,36 @@
import gettext
import locale
import json
import os
# Determine the user's locale
try:
with open("config.json", "r") as file:
data = json.load(file)
langf = data.get("lang")
lang = langf
except FileNotFoundError:
lang = "pl"
print("Defaulting to Polish due to missing config file")
except AttributeError:
lang = "pl"
print("Defaulting to Polish due to missing language setting in config file")
except Exception as e:
lang = "pl"
print(f"Defaulting to Polish due to error: {e}")
finally:
print("Language set")
# Set up translation
localedir = os.path.join(os.path.dirname(__file__), "locales")
translation = gettext.translation("base", localedir, languages=[lang], fallback=True)
translation.install()
_ = translation.gettext # Shortcut for gettext
# Function to switch language dynamically
def set_language(new_lang):
global translation, _
translation = gettext.translation("base", localedir, languages=[new_lang], fallback=True)
translation.install()
_ = translation.gettext # Update global _

86
src/locales/base.pot Normal file
View file

@ -0,0 +1,86 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-16 12:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/main.py:115 src/pages/home.py:9
msgid "Home"
msgstr ""
#: src/main.py:116 src/pages/grades.py:8
msgid "Grades"
msgstr ""
#: src/main.py:117 src/pages/home.py:16 src/pages/timetable.py:8
msgid "Timetable"
msgstr ""
#: src/main.py:118 src/pages/homework.py:8
msgid "Homework"
msgstr ""
#: src/main.py:119 src/pages/exams.py:8
msgid "Exams"
msgstr ""
#: src/main.py:120 src/pages/attendance.py:8
msgid "Attendance"
msgstr ""
#: src/main.py:121 src/pages/behaviour.py:8
msgid "Behaviour"
msgstr ""
#: src/main.py:122 src/pages/settings.py:8
msgid "Settings"
msgstr ""
#: src/main.py:238
msgid "Welcome to Fuji!"
msgstr ""
#: src/main.py:240
msgid "Get Started"
msgstr ""
#: src/main.py:266 src/main.py:285
msgid "Log in"
msgstr ""
#: src/main.py:270
msgid "Username"
msgstr ""
#: src/main.py:277
msgid "Password"
msgstr ""
#: src/main.py:307
msgid "Select a student"
msgstr ""
#: src/main.py:331
msgid "Logged in!"
msgstr ""
#: src/main.py:334
msgid "Please restart the app to use it."
msgstr ""
#: src/pages/home.py:41
msgid "Recent Grades"
msgstr ""

View file

@ -0,0 +1,86 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-16 12:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/main.py:115 src/pages/home.py:9
msgid "Home"
msgstr "Home"
#: src/main.py:116 src/pages/grades.py:8
msgid "Grades"
msgstr "Grades"
#: src/main.py:117 src/pages/home.py:16 src/pages/timetable.py:8
msgid "Timetable"
msgstr "Timetable"
#: src/main.py:118 src/pages/homework.py:8
msgid "Homework"
msgstr "Homework"
#: src/main.py:119 src/pages/exams.py:8
msgid "Exams"
msgstr "Exams"
#: src/main.py:120 src/pages/attendance.py:8
msgid "Attendance"
msgstr "Attendance"
#: src/main.py:121 src/pages/behaviour.py:8
msgid "Behaviour"
msgstr "Behaviour"
#: src/main.py:122 src/pages/settings.py:8
msgid "Settings"
msgstr "Settings"
#: src/main.py:238
msgid "Welcome to Fuji!"
msgstr "Welcome to Fuji!"
#: src/main.py:240
msgid "Get Started"
msgstr "Get Started"
#: src/main.py:266 src/main.py:285
msgid "Log in"
msgstr "Log in"
#: src/main.py:270
msgid "Username"
msgstr "Username"
#: src/main.py:277
msgid "Password"
msgstr "Password"
#: src/main.py:307
msgid "Select a student"
msgstr "Select a student"
#: src/main.py:331
msgid "Logged in!"
msgstr "Logged in!"
#: src/main.py:334
msgid "Please restart the app to use it."
msgstr "Please restart the app to use it."
#: src/pages/home.py:41
msgid "Recent Grades"
msgstr "Recent Grades"

Binary file not shown.

View file

@ -0,0 +1,86 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-16 12:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/main.py:115 src/pages/home.py:9
msgid "Home"
msgstr "Strona główna"
#: src/main.py:116 src/pages/grades.py:8
msgid "Grades"
msgstr "Oceny"
#: src/main.py:117 src/pages/home.py:16 src/pages/timetable.py:8
msgid "Timetable"
msgstr "Plan lekcji"
#: src/main.py:118 src/pages/homework.py:8
msgid "Homework"
msgstr "Zadania domowe"
#: src/main.py:119 src/pages/exams.py:8
msgid "Exams"
msgstr "Sprawdziany"
#: src/main.py:120 src/pages/attendance.py:8
msgid "Attendance"
msgstr "Obecności"
#: src/main.py:121 src/pages/behaviour.py:8
msgid "Behaviour"
msgstr "Zachowanie"
#: src/main.py:122 src/pages/settings.py:8
msgid "Settings"
msgstr "Ustawienia"
#: src/main.py:238
msgid "Welcome to Fuji!"
msgstr "Witaj w Fuji!"
#: src/main.py:240
msgid "Get Started"
msgstr "Rozpocznij"
#: src/main.py:266 src/main.py:285
msgid "Log in"
msgstr "Zaloguj się"
#: src/main.py:270
msgid "Username"
msgstr "Nazwa użytkownika"
#: src/main.py:277
msgid "Password"
msgstr "Hasło"
#: src/main.py:307
msgid "Select a student"
msgstr "Wybierz ucznia"
#: src/main.py:331
msgid "Logged in!"
msgstr "Zalogowano!"
#: src/main.py:334
msgid "Please restart the app to use it."
msgstr "Proszę zrestartować aplikację, aby jej używać."
#: src/pages/home.py:41
msgid "Recent Grades"
msgstr "Ostatnie oceny"

View file

@ -3,6 +3,9 @@ import json
import keyring
import pickle
import base64
import gettext
import threading
from i18n import *
from pages.home import *
from pages.grades import *
from pages.timetable import *
@ -14,6 +17,42 @@ from pages.settings import *
from sdk.src.interfaces.prometheus.context import *
from sdk.src.interfaces.prometheus.interface import *
def saveauth(service, username, data, chunk_size=1000):
chunks = [data[i:i+chunk_size] for i in range(0, len(data), chunk_size)]
keyring.set_password(service, f"{username}_count", str(len(chunks)))
for i, chunk in enumerate(chunks):
keyring.set_password(service, f"{username}_{i}", chunk)
def sync():
auth_context_raw = loadauth("Fuji", "Auth Context")
auth_context = PrometheusAuthContext.model_validate_json(auth_context_raw)
interface = PrometheusInterface(
auth_context=auth_context,
student_context=None,
)
try:
interface.login()
except NoLoggedInException:
print("nologgedinexception")
students = interface.get_students()
student = int(keyring.get_password("Fuji", "Student_Index"))
interface.select_student(students[student].context)
auth_context = interface.get_auth_context()
jsoncontext = auth_context.model_dump_json()
saveauth("Fuji", "Auth Context", jsoncontext)
def loadauth(service, username):
try:
count = int(keyring.get_password(service, f"{username}_count"))
@ -23,7 +62,7 @@ def loadauth(service, username):
def main(page: ft.Page):
# Page settings
#run Page settings
page.title = "Fuji"
page.theme = ft.Theme(
@ -35,28 +74,10 @@ def main(page: ft.Page):
windows=ft.PageTransitionTheme.NONE
)
)
# Eduvulcan login
# Sync
auth_context_raw = loadauth("Fuji", "Auth Context")
auth_context = PrometheusAuthContext.model_validate_json(auth_context_raw)
interface = PrometheusInterface(
auth_context=auth_context,
student_context=None,
)
r = interface.login()
if r:
print(r)
if not r:
pass
students = interface.get_students()
student = int(keyring.get_password("Fuji", "Student_Index"))
interface.select_student(students[student].context)
s = threading.Thread(target=sync)
s.start()
# Page routing
def change_page(route):
@ -90,14 +111,14 @@ def main(page: ft.Page):
min_extended_width=400,
group_alignment=0,
destinations=[
ft.NavigationRailDestination(icon=ft.Icons.HOME_OUTLINED, selected_icon=ft.Icons.HOME, label="Home"),
ft.NavigationRailDestination(icon=ft.Icons.LOOKS_6_OUTLINED, selected_icon=ft.Icons.LOOKS_6, label="Grades"),
ft.NavigationRailDestination(icon=ft.Icons.BACKPACK_OUTLINED, selected_icon=ft.Icons.BACKPACK, label="Timetable"),
ft.NavigationRailDestination(icon=ft.Icons.BOOK_OUTLINED, selected_icon=ft.Icons.BOOK, label="Homework"),
ft.NavigationRailDestination(icon=ft.Icons.CALENDAR_TODAY_OUTLINED, selected_icon=ft.Icons.CALENDAR_TODAY, label="Exams"),
ft.NavigationRailDestination(icon=ft.Icons.EVENT_NOTE_OUTLINED, selected_icon=ft.Icons.EVENT_NOTE, label="Attendance"),
ft.NavigationRailDestination(icon=ft.Icons.STICKY_NOTE_2_OUTLINED, selected_icon=ft.Icons.STICKY_NOTE_2, label="Behaviour"),
ft.NavigationRailDestination(icon=ft.Icons.SETTINGS_OUTLINED, selected_icon=ft.Icons.SETTINGS_ROUNDED, label="Settings"),
ft.NavigationRailDestination(icon=ft.Icons.HOME_OUTLINED, selected_icon=ft.Icons.HOME, label=(_("Home"))),
ft.NavigationRailDestination(icon=ft.Icons.LOOKS_6_OUTLINED, selected_icon=ft.Icons.LOOKS_6, label=(_("Grades"))),
ft.NavigationRailDestination(icon=ft.Icons.BACKPACK_OUTLINED, selected_icon=ft.Icons.BACKPACK, label=(_("Timetable"))),
ft.NavigationRailDestination(icon=ft.Icons.BOOK_OUTLINED, selected_icon=ft.Icons.BOOK, label=(_("Homework"))),
ft.NavigationRailDestination(icon=ft.Icons.CALENDAR_TODAY_OUTLINED, selected_icon=ft.Icons.CALENDAR_TODAY, label=(_("Exams"))),
ft.NavigationRailDestination(icon=ft.Icons.EVENT_NOTE_OUTLINED, selected_icon=ft.Icons.EVENT_NOTE, label=(_("Attendance"))),
ft.NavigationRailDestination(icon=ft.Icons.STICKY_NOTE_2_OUTLINED, selected_icon=ft.Icons.STICKY_NOTE_2, label=(_("Behaviour"))),
ft.NavigationRailDestination(icon=ft.Icons.SETTINGS_OUTLINED, selected_icon=ft.Icons.SETTINGS_ROUNDED, label=(_("Settings"))),
],
on_change=lambda e: page.go([
"/",
@ -162,15 +183,6 @@ def login(page: ft.Page):
students = interface.get_students()
def saveauth(service, username, data, chunk_size=1000):
chunks = [data[i:i+chunk_size] for i in range(0, len(data), chunk_size)]
keyring.set_password(service, f"{username}_count", str(len(chunks)))
for i, chunk in enumerate(chunks):
keyring.set_password(service, f"{username}_{i}", chunk)
def on_change(e):
selected_index = next((i for i, student in enumerate(students) if student.full_name == e.control.value), -1)
@ -183,7 +195,7 @@ def login(page: ft.Page):
saveauth("Fuji", "Auth Context", jsoncontext)
config = {"isLoggedIn": True}
config = {"isLoggedIn": True, "lang": "pl"}
with open("config.json", "w") as file:
json.dump(config, file)
@ -222,9 +234,9 @@ def login(page: ft.Page):
fit=ft.ImageFit.CONTAIN,
border_radius=ft.border_radius.all(100000)
),
ft.Text(value="Welcome to Fuji!", size=64),
ft.Text(value=(_("Welcome to Fuji!")), size=64),
ft.Button(
"Get Started",
(_("Get Started")),
scale=2.0,
width=230,
on_click=lambda e: page.go("/login/eduvulcan")
@ -250,18 +262,18 @@ def login(page: ft.Page):
ft.Container(
content=ft.Column(
[
ft.Text(value="Log in", size=32, weight="bold",
ft.Text(value=(_("Log in")), size=32, weight="bold",
text_align=ft.TextAlign.CENTER),
ft.TextField(
label="Username",
label=(_("Username")),
autofill_hints=[ft.AutofillHint.USERNAME],
width=300,
on_change=changeusr
),
ft.TextField(
label="Password",
label=(_("Password")),
password=True,
can_reveal_password=True,
autofill_hints=[ft.AutofillHint.PASSWORD],
@ -269,7 +281,7 @@ def login(page: ft.Page):
on_change=changepasswd
),
ft.Button("Log in", scale=1.25, width=250, on_click=loginev),
ft.Button((_("Log in")), scale=1.25, width=250, on_click=loginev),
],
alignment=ft.MainAxisAlignment.CENTER,
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
@ -291,7 +303,7 @@ def login(page: ft.Page):
ft.Container(
content=ft.Column(
[
ft.Text(value="Select a student", size=32, weight="bold",
ft.Text(value=(_("Select a student")), size=32, weight="bold",
text_align=ft.TextAlign.CENTER),
students(),
],
@ -307,6 +319,7 @@ def login(page: ft.Page):
)
page.views.append(students_view)
# Start view
elif page.route == "/start":
start_view = ft.View(
route="/start",
@ -314,10 +327,10 @@ def login(page: ft.Page):
ft.Container(
content=ft.Column(
[
ft.Text(value="Logged in!", size=32, weight="bold",
ft.Text(value=(_("Logged in!")), size=32, weight="bold",
text_align=ft.TextAlign.CENTER),
ft.Text(value="Please restart the app to use it.", size=16, weight="normal",
ft.Text(value=(_("Please restart the app to use it.")), size=16, weight="normal",
text_align=ft.TextAlign.CENTER),
],
alignment=ft.MainAxisAlignment.CENTER,
@ -353,7 +366,7 @@ if __name__ == "__main__":
else:
ft.app(target=login)
except FileNotFoundError:
config = {"isLoggedIn": False}
config = {"isLoggedIn": False, "lang": "pl"}
with open("config.json", "w") as file:
json.dump(config, file)
ft.app(target=login)

View file

@ -1,8 +1,9 @@
import flet as ft
from i18n import _
def AttendancePage():
return ft.Column([
ft.Text(" Attendance", size=30, weight="bold"),
ft.Text((_("Attendance")), size=30, weight="bold"),
ft.Placeholder()
])

View file

@ -1,8 +1,10 @@
import flet as ft
from i18n import _
def BehaviourPage():
return ft.Column([
ft.Text(" Behaviour", size=30, weight="bold"),
ft.Text((_("Behaviour")), size=30, weight="bold"),
ft.Placeholder()
])

View file

@ -1,8 +1,10 @@
import flet as ft
from i18n import _
def ExamsPage():
return ft.Column([
ft.Text(" Exams", size=30, weight="bold"),
ft.Text((_("Exams")), size=30, weight="bold"),
ft.Placeholder()
])

View file

@ -1,8 +1,10 @@
import flet as ft
from i18n import _
def GradesPage():
return ft.Column([
ft.Text(" Grades", size=30, weight="bold"),
ft.Text((_("Grades")), size=30, weight="bold"),
ft.Placeholder()
])

View file

@ -1,16 +1,19 @@
import flet as ft
from i18n import *
from vars import *
set_language("pl")
def HomePage():
return ft.Column([
ft.Text(" Home", size=30, weight="bold"),
ft.Text((_("Home")), size=30, weight="bold"),
ft.Text("\n", size=30, weight="bold"),
ft.Row([
ft.Container( # Timetable Card
content=ft.Column([
ft.Row([
ft.Icon(ft.Icons.BACKPACK_OUTLINED, size=32, color="#FFFFFF"),
ft.Text("Timetable", size=24, font_family="Roboto", weight="bold")
ft.Text((_("Timetable")), size=24, font_family="Roboto", weight="bold")
]),
ft.ListView(
controls=[
@ -35,7 +38,7 @@ def HomePage():
content=ft.Column([
ft.Row([
ft.Icon(ft.Icons.LOOKS_6_OUTLINED, size=32, color="#FFFFFF"),
ft.Text("Recent Grades", size=24, font_family="Roboto", weight="bold")
ft.Text((_("Recent Grades")), size=24, font_family="Roboto", weight="bold")
]),
ft.Placeholder()

View file

@ -1,8 +1,10 @@
import flet as ft
from i18n import _
def HomeworkPage():
return ft.Column([
ft.Text(" Homework", size=30, weight="bold"),
ft.Text((_("Homework")), size=30, weight="bold"),
ft.Placeholder()
])

View file

@ -1,8 +1,10 @@
import flet as ft
from i18n import _
def SettingsPage():
return ft.Column([
ft.Text(" Settings", size=30, weight="bold"),
ft.Text((_("Settings")), size=30, weight="bold"),
ft.Placeholder()
])

View file

@ -1,8 +1,10 @@
import flet as ft
from i18n import _
def TimetablePage():
return ft.Column([
ft.Text(" Timetable", size=30, weight="bold"),
ft.Text((_("Timetable")), size=30, weight="bold"),
ft.Placeholder()
])

View file

@ -21,7 +21,8 @@ class EfebClient:
self._session.cookies.update(cookies)
self._symbol = symbol
self._is_ce = is_ce
self._session.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36'
def get_cookies(self):
return self._session.cookies.get_dict()

View file

@ -6,4 +6,4 @@ BASE_MESSAGES = "https://dziennik-wiadomosci.vulcan.net.pl"
ENDPOINT_LOGIN_FS_LS = "fs/ls"
ENDPOINT_STUDENT_APP = "App"
ENDPOINT_MESSAGES_APP = "App"
ENDPOINT_MESSAGES_APP = "App"

View file

@ -98,6 +98,7 @@ class PrometheusWebClient:
response = self._session.get(
f"{PROMETHEUS_WEB_BASE}/{ENDPOINT_FS_LS}",
params=dict(query),
headers=HEADERS,
)
if "Logowanie" in response.text: