diff --git a/.gitignore b/.gitignore index 34cd63d..cdcc9c2 100644 --- a/.gitignore +++ b/.gitignore @@ -66,10 +66,6 @@ coverage.xml .pytest_cache/ cover/ -# Translations -*.mo -*.pot - # Django stuff: *.log local_settings.py diff --git a/babel.fish b/babel.fish new file mode 100755 index 0000000..06cf841 --- /dev/null +++ b/babel.fish @@ -0,0 +1,114 @@ +#!/bin/fish + +function display_help + set_color green + echo -n "1) " + set_color normal + echo "Extract translations into messages.pot file" + echo " and merge into existing languages" + + set_color green + echo -n "2) " + set_color normal + echo "Create translation folders for new language" + + set_color green + echo -n "3) " + set_color normal + echo "Compile *.po files for display" + + set_color red + echo -n "4) " + set_color normal + echo "Exit program" + + echo +end + +function compile_pot + set_color brgreen + echo "Compiling POT file..." + + set_color normal + set -l version "$(cat VERSION.md)" + command pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot --add-location full \ + --project Starfall --version "$version" \ + --copyright-holder "Flare Starfall" --msgid-bugs-address "flare@theflare.at" . + + if test -d translations + set_color brgreen + echo "Merging into existing languages..." + + set_color normal + command pybabel update -i messages.pot -d translations + end + + set_color brgreen + echo -n "Command complete " + set_color normal + echo "- returning to menu" + + echo +end + +function new_lang + read -p "set_color green; echo -n \"Please enter language >>> \"; set_color normal" -l lang + + if test -d "translations/$lang" + set_color brred + echo -n "ERROR " + set_color normal + echo "- Language already exists" + + echo + else + set_color brgreen + echo "Initializing language $lang..." + + set_color normal + command pybabel init -i messages.pot -d translations -l "$lang" + + set_color brgreen + echo -n "Command complete " + set_color normal + echo "- returning to menu" + + echo + end +end + +function compile_po + set_color brgreen + echo "Compiling PO files..." + + set_color normal + command pybabel compile -d translations + + set_color brgreen + echo -n "Command complete " + set_color normal + echo "- returning to menu" + + echo +end + +source .venv/bin/activate.fish +while true + display_help + read -p "set_color green; echo -n \"Please enter operation >>> \"; set_color normal" -n 1 -l confirm + or return 1 + + switch $confirm + case 1 + compile_pot + case 2 + new_lang + case 3 + compile_po + case 4 + return 0 + case '' '*' + echo "Invalid input" + continue + end +end diff --git a/messages.pot b/messages.pot new file mode 100644 index 0000000..7df3c42 --- /dev/null +++ b/messages.pot @@ -0,0 +1,111 @@ +# Translations template for Starfall. +# Copyright (C) 2026 Flare Starfall +# This file is distributed under the same license as the Starfall project. +# FIRST AUTHOR , 2026. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Starfall 4.5.0\n" +"Report-Msgid-Bugs-To: flare@theflare.at\n" +"POT-Creation-Date: 2026-03-08 14:48+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.18.0\n" + +#: web/templates/base.jinja:6 +msgid "base.meta.title.empty" +msgstr "" + +#: web/templates/base.jinja:6 +msgid "base.meta.title.site" +msgstr "" + +#: web/templates/base.jinja:11 +msgid "base.meta.description" +msgstr "" + +#: web/templates/base.jinja:21 +msgid "base.label.skip_to_content" +msgstr "" + +#: web/templates/base.jinja:27 +msgid "base.label.logo_alt" +msgstr "" + +#: web/templates/base.jinja:31 +msgid "base.label.toggle_nav" +msgstr "" + +#: web/templates/base.jinja:50 +msgid "base.label.theme.light" +msgstr "" + +#: web/templates/base.jinja:53 +msgid "base.label.theme.dark" +msgstr "" + +#: web/templates/base.jinja:56 +msgid "base.label.theme.auto" +msgstr "" + +#: web/templates/base.jinja:82 +msgid "base.label.copy" +msgstr "" + +#: web/templates/imprint.jinja:3 web/templates/imprint.jinja:8 +msgid "page.imprint.title" +msgstr "" + +#: web/templates/imprint.jinja:10 +msgid "page.imprint.card.title" +msgstr "" + +#: web/templates/imprint.jinja:12 +msgid "page.imprint.card.body" +msgstr "" + +#: web/templates/imprint.jinja:13 +msgid "page.imprint.card.lastmodified" +msgstr "" + +#: web/templates/imprint.jinja:23 +msgid "page.imprint.legal" +msgstr "" + +#: web/templates/imprint.jinja:31 +msgid "page.imprint.sec1.title" +msgstr "" + +#: web/templates/imprint.jinja:32 +msgid "page.imprint.sec2.title" +msgstr "" + +#: web/templates/imprint.jinja:33 +msgid "page.imprint.sec3.title" +msgstr "" + +#: web/templates/imprint.jinja:34 +msgid "page.imprint.sec4.title" +msgstr "" + +#: web/templates/imprint.jinja:35 +msgid "page.imprint.sec1.body" +msgstr "" + +#: web/templates/imprint.jinja:36 +msgid "page.imprint.sec2.body" +msgstr "" + +#: web/templates/imprint.jinja:37 +msgid "page.imprint.sec3.body" +msgstr "" + +#: web/templates/imprint.jinja:38 +msgid "page.imprint.sec4.body" +msgstr "" + diff --git a/starfall/web/__init__.py b/starfall/web/__init__.py index ee477de..71075ee 100644 --- a/starfall/web/__init__.py +++ b/starfall/web/__init__.py @@ -22,7 +22,7 @@ class WebUI: self.app: Flask | None = None self.assets: Environment | None = None self.babel: Babel | None = None - self.blueprint: Blueprint = Blueprint("main", __name__) + self.blueprint: Blueprint = Blueprint("starfall", __name__) self.config: Config | None = None self.queue: SnapshotQueue | None = None self.server: Server | None = None diff --git a/starfall/web/blueprints/admin.py b/starfall/web/blueprints/admin.py new file mode 100644 index 0000000..a05b8a4 --- /dev/null +++ b/starfall/web/blueprints/admin.py @@ -0,0 +1,13 @@ +from flask import Blueprint, Flask, render_template +from flask_assets import Environment + +from starfall.web.blueprints.base import BaseBlueprint + + +class AdminBlueprint(BaseBlueprint): + def __init__(self, blueprint: Blueprint, assets: Environment, app: Flask): + super().__init__(blueprint, assets, app) + blueprint.add_url_rule("/login", methods=["GET", "POST"], view_func=self.login) + + def login(self): + return render_template("admin/login.jinja", bp=self) diff --git a/starfall/web/blueprints/base.py b/starfall/web/blueprints/base.py index d6fcc55..0d1301c 100644 --- a/starfall/web/blueprints/base.py +++ b/starfall/web/blueprints/base.py @@ -3,6 +3,7 @@ import logging from abc import ABC from datetime import datetime from types import FrameType +from typing import Any from flask import Blueprint, Flask, request from flask.blueprints import BlueprintSetupState @@ -17,6 +18,7 @@ class BaseBlueprint(ABC): self.assets: Environment = assets self.date: datetime = datetime.now() self.app: Flask = app + self.data: dict[str, Any] = {} blueprint.record(self.on_blueprint_setup) if type(self) is not BaseBlueprint: diff --git a/starfall/web/blueprints/imprint.py b/starfall/web/blueprints/imprint.py new file mode 100644 index 0000000..1cd69aa --- /dev/null +++ b/starfall/web/blueprints/imprint.py @@ -0,0 +1,21 @@ +from flask import Blueprint, Flask, render_template +from flask_assets import Environment +from flask_babel import get_locale + +from starfall.web.blueprints.base import BaseBlueprint +from starfall.web.controllers.imprint import ImprintController + + +class ImprintBlueprint(BaseBlueprint): + def __init__(self, blueprint: Blueprint, assets: Environment, app: Flask) -> None: + super().__init__(blueprint, assets, app) + blueprint.add_url_rule("/imprint", view_func=self.imprint) + + def imprint(self): + self._log_access() + ImprintController.apply(self) + return render_template( + "imprint.jinja", + bp=self, + lang=get_locale(), + ) diff --git a/starfall/web/blueprints/main.py b/starfall/web/blueprints/main.py index 41345a8..fb58cab 100644 --- a/starfall/web/blueprints/main.py +++ b/starfall/web/blueprints/main.py @@ -1,8 +1,8 @@ -from datetime import datetime from typing import Any from flask import Blueprint, Flask, render_template from flask_assets import Environment +from flask_babel import get_locale from starfall.web.blueprints.base import BaseBlueprint @@ -11,35 +11,20 @@ class MainBlueprint(BaseBlueprint): def __init__(self, blueprint: Blueprint, assets: Environment, app: Flask) -> None: super().__init__(blueprint, assets, app) blueprint.add_url_rule("/", view_func=self.index) - blueprint.add_url_rule("/imprint", view_func=self.imprint) self.app.register_error_handler(404, self.not_found) def index(self): self._log_access() return render_template( "home.jinja", - assets=self.assets, - route=self._route(), - date=self.date, - ) - - def imprint(self): - self._log_access() - - lastmod = datetime.fromisoformat("2025-05-11T16:00+01:00") - return render_template( - "imprint.jinja", - assets=self.assets, - route=self._route(), - date=self.date, - lastmod=lastmod, + bp=self, + lang=get_locale(), ) def not_found(self, e: Any): + self.error: Any = e return render_template( "404.jinja", - assets=self.assets, - route=self._route(), - date=self.date, - error=e, + bp=self, + lang=get_locale(), ), 404 diff --git a/starfall/web/controllers/base.py b/starfall/web/controllers/base.py new file mode 100644 index 0000000..50acd5e --- /dev/null +++ b/starfall/web/controllers/base.py @@ -0,0 +1,2 @@ +class BaseController: + pass diff --git a/starfall/web/controllers/imprint.py b/starfall/web/controllers/imprint.py new file mode 100644 index 0000000..af95920 --- /dev/null +++ b/starfall/web/controllers/imprint.py @@ -0,0 +1,13 @@ +from datetime import datetime + +from starfall.web.blueprints.base import BaseBlueprint + + +class ImprintController: + @classmethod + def apply(cls, bp: BaseBlueprint): + bp.data["lastmod"] = cls.last_modified() + + @classmethod + def last_modified(cls): + return datetime.fromisoformat("2025-05-11") diff --git a/translations/en/LC_MESSAGES/messages.mo b/translations/en/LC_MESSAGES/messages.mo new file mode 100644 index 0000000..ec1b7c4 Binary files /dev/null and b/translations/en/LC_MESSAGES/messages.mo differ diff --git a/translations/en/LC_MESSAGES/messages.po b/translations/en/LC_MESSAGES/messages.po new file mode 100644 index 0000000..04b6e7a --- /dev/null +++ b/translations/en/LC_MESSAGES/messages.po @@ -0,0 +1,131 @@ +# English translations for Starfall. +# Copyright (C) 2026 Flare Starfall +# This file is distributed under the same license as the Starfall project. +# FIRST AUTHOR , 2026. +# +msgid "" +msgstr "" +"Project-Id-Version: Starfall 4.5.0\n" +"Report-Msgid-Bugs-To: flare@theflare.at\n" +"POT-Creation-Date: 2026-03-08 14:48+0100\n" +"PO-Revision-Date: 2026-03-08 15:04+0100\n" +"Last-Translator: \n" +"Language-Team: en \n" +"Language: en\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Generated-By: Babel 2.18.0\n" +"X-Generator: Poedit 3.8\n" + +#: web/templates/base.jinja:6 +msgid "base.meta.title.empty" +msgstr "Page not defined" + +#: web/templates/base.jinja:6 +msgid "base.meta.title.site" +msgstr "Starfall" + +#: web/templates/base.jinja:11 +msgid "base.meta.description" +msgstr "Home of Team Starfall" + +#: web/templates/base.jinja:21 +msgid "base.label.skip_to_content" +msgstr "Skip to content" + +#: web/templates/base.jinja:27 +msgid "base.label.logo_alt" +msgstr "Starfall - Logo" + +#: web/templates/base.jinja:31 +msgid "base.label.toggle_nav" +msgstr "Open menu" + +#: web/templates/base.jinja:50 +msgid "base.label.theme.light" +msgstr "Light" + +#: web/templates/base.jinja:53 +msgid "base.label.theme.dark" +msgstr "Dark" + +#: web/templates/base.jinja:56 +msgid "base.label.theme.auto" +msgstr "Auto" + +#: web/templates/base.jinja:82 +msgid "base.label.copy" +msgstr "© Team Starfall" + +#: web/templates/imprint.jinja:3 web/templates/imprint.jinja:8 +msgid "page.imprint.title" +msgstr "Imprint & Privacy" + +#: web/templates/imprint.jinja:10 +msgid "page.imprint.card.title" +msgstr "In short" + +#: web/templates/imprint.jinja:12 +msgid "page.imprint.card.body" +msgstr "The only thing this site processes is your site visit, stored for no longer than 7 days unless absolutely necessary. This data is only ever accessed by one person (this website's creator), and only in cases where reading the server logs is necessary (say, a website outage, or reading the logs while developing a new part of the website).
This disclaimer is not by itself legally binding. The full privacy statement is found below." + +#: web/templates/imprint.jinja:13 +msgid "page.imprint.card.lastmodified" +msgstr "Last modified:" + +#: web/templates/imprint.jinja:23 +msgid "page.imprint.legal" +msgstr "Based on the Model Data Protection Statement from the Anwaltskanzlei Weiß & Partner.
Edited to provide details of the data controller, and to detail exactly what server data is stored." + +#: web/templates/imprint.jinja:31 +msgid "page.imprint.sec1.title" +msgstr "Privacy Policy" + +#: web/templates/imprint.jinja:32 +msgid "page.imprint.sec2.title" +msgstr "I. Information about us as controllers of your data" + +#: web/templates/imprint.jinja:33 +msgid "page.imprint.sec3.title" +msgstr "II. The rights of users and data subjects" + +#: web/templates/imprint.jinja:34 +msgid "page.imprint.sec4.title" +msgstr "III. Information about the data processing" + +#: web/templates/imprint.jinja:35 +msgid "page.imprint.sec1.body" +msgstr "" +"

Personal data (usually referred to just as „data“ below) will only be processed by us to the extent necessary and for the purpose of providing a functional and user-friendly website, including its contents, and the services offered there.

\n" +"

Per Art. 4 No. 1 of Regulation (EU) 2016/679, i.e. the General Data Protection Regulation (hereinafter referred to as the „GDPR“), „processing“ refers to any operation or set of operations such as collection, recording, organization, structuring, storage, adaptation, alteration, retrieval, consultation, use, disclosure by transmission, dissemination, or otherwise making available, alignment, or combination, restriction, erasure, or destruction performed on personal data, whether by automated means or not.

\n" +"

The following privacy policy is intended to inform you in particular about the type, scope, purpose, duration, and legal basis for the processing of such data either under our own control or in conjunction with others. We also inform you below about the third-party components we use to optimize our website and improve the user experience which may result in said third parties also processing data they collect and control.

\n" +"

Our privacy policy is structured as follows:

\n" +"

I. Information about us as controllers of your data
II. The rights of users and data subjects
III. Information about the data processing

" + +#: web/templates/imprint.jinja:36 +msgid "page.imprint.sec2.body" +msgstr "

The party responsible for this website (the „controller“) for purposes of data protection law is:
Robert Bäs-Fischlmair a.k.a. Flare Starfall
Phone: +43 (0) 677 62890651
Email: flare@theflare.at

" + +#: web/templates/imprint.jinja:37 +msgid "page.imprint.sec3.body" +msgstr "" +"

With regard to the data processing to be described in more detail below, users and data subjects have the right

\n" +"
    \n" +"
  • to confirmation of whether data concerning them is being processed, information about the data being processed, further information about the nature of the data processing, and copies of the data (cf. also Art. 15 GDPR);
  • \n" +"
  • to correct or complete incorrect or incomplete data (cf. also Art. 16 GDPR);
  • \n" +"
  • to the immediate deletion of data concerning them (cf. also Art. 17 DSGVO), or, alternatively, if further processing is necessary as stipulated in Art. 17 Para. 3 GDPR, to restrict said processing per Art. 18 GDPR;
  • to receive copies of the data concerning them and/or provided by them and to have the same transmitted to other providers/controllers (cf. also Art. 20 GDPR);
  • to file complaints with the supervisory authority if they believe that data concerning them is being processed by the controller in breach of data protection provisions (see also Art. 77 GDPR).
  • \n" +"
\n" +"

In addition, the controller is obliged to inform all recipients to whom it discloses data of any such corrections, deletions, or restrictions placed on processing the same per Art. 16, 17 Para. 1, 18 GDPR. However, this obligation does not apply if such notification is impossible or involves a disproportionate effort. Nevertheless, users have a right to information about these recipients.

\n" +"

Likewise, under Art. 21 GDPR, users and data subjects have the right to object to the controller's future processing of their data pursuant to Art. 6 Para. 1 lit. f) GDPR. In particular, an objection to data processing for the purpose of direct advertising is permissible.

" + +#: web/templates/imprint.jinja:38 +msgid "page.imprint.sec4.body" +msgstr "" +"

Your data processed when using our website will be deleted or blocked as soon as the purpose for its storage ceases to apply, provided the deletion of the same is not in breach of any statutory storage obligations or unless otherwise stipulated below.

\n" +"

Server data

\n" +"

For technical reasons, the following data sent by your internet browser to us or to our server provider will be collected, especially to ensure a secure and stable website: These server log files record the type and version of your browser, operating system, the webpages on our site visited, and the date and time of your visit. The IP address from which you visited our site is additionally recorded if an error occurs.

\n" +"

The data thus collected will be temporarily stored, but not in association with any other of your data.

\n" +"

The basis for this storage is Art. 6 Para. 1 lit. f) GDPR. Our legitimate interest lies in the improvement, stability, functionality, and security of our website.

\n" +"

The data will be deleted within no more than seven days, unless continued storage is required for evidentiary purposes. In which case, all or part of the data will be excluded from deletion until the investigation of the relevant incident is finally resolved.

" diff --git a/web/templates/base.jinja b/web/templates/base.jinja index 24fb161..af93d46 100644 --- a/web/templates/base.jinja +++ b/web/templates/base.jinja @@ -11,7 +11,7 @@ - + {# %block head: Additional stylesheets/scripts for page. #} {%block head%}{%endblock%} @@ -79,7 +79,7 @@