Restructure web templates for translations, add translations to source control

This commit is contained in:
Flare Starfall 2026-03-08 23:49:52 +01:00
parent 787e26e5b3
commit 77dbf8fb06
18 changed files with 430 additions and 134 deletions

4
.gitignore vendored
View File

@ -66,10 +66,6 @@ coverage.xml
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py

114
babel.fish Executable file
View File

@ -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

111
messages.pot Normal file
View File

@ -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 <EMAIL@ADDRESS>, 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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 ""

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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(),
)

View File

@ -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

View File

@ -0,0 +1,2 @@
class BaseController:
pass

View File

@ -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")

Binary file not shown.

View File

@ -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 <EMAIL@ADDRESS>, 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 <LL@li.org>\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 "&copy; Team Starfall"
#: web/templates/imprint.jinja:3 web/templates/imprint.jinja:8
msgid "page.imprint.title"
msgstr "Imprint &amp; 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).<br />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 <a href=\"https://www.generator-datenschutzerklärung.de\" target=\"_blank\" rel=\"noreferrer noopener\">Model Data Protection Statement</a> from the <a href=\"https://www.ratgeberrecht.eu/\" target=\"_blank\" rel=\"noreferrer noopener\">Anwaltskanzlei Weiß &amp; Partner</a>.<br>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 ""
"<p>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.</p>\n"
"<p>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.</p>\n"
"<p>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.</p>\n"
"<p>Our privacy policy is structured as follows:</p>\n"
"<p>I. Information about us as controllers of your data<br>II. The rights of users and data subjects<br>III. Information about the data processing</p>"
#: web/templates/imprint.jinja:36
msgid "page.imprint.sec2.body"
msgstr "<p>The party responsible for this website (the „controller“) for purposes of data protection law is:<br>Robert Bäs-Fischlmair a.k.a. Flare Starfall<br>Phone: +43 (0) 677 62890651<br>Email: <a href=\"mailto:flare@theflare.at\">flare@theflare.at</a></p>"
#: web/templates/imprint.jinja:37
msgid "page.imprint.sec3.body"
msgstr ""
"<p>With regard to the data processing to be described in more detail below, users and data subjects have the right</p>\n"
"<ul>\n"
"<li>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);</li>\n"
"<li>to correct or complete incorrect or incomplete data (cf. also Art. 16 GDPR);</li>\n"
"<li>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; </li> <li>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);</li> <li>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).</li>\n"
"</ul>\n"
"<p>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.</p>\n"
"<p><strong>Likewise, under Art. 21 GDPR, users and data subjects have the right to object to the controller&apos;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.</strong></p>"
#: web/templates/imprint.jinja:38
msgid "page.imprint.sec4.body"
msgstr ""
"<p>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.</p>\n"
"<h3>Server data</h3>\n"
"<p>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.</p>\n"
"<p>The data thus collected will be temporarily stored, but not in association with any other of your data.</p>\n"
"<p>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.</p>\n"
"<p>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.</p>"

View File

@ -11,7 +11,7 @@
<meta name="description" content="{{_('base.meta.description')}}" />
<link rel="icon" href="{{url_for('static', filename='img/favicon.png')}}" />
<link rel="stylesheet" type="text/css" href="{{assets['scss'].urls()|first}}" />
<link rel="stylesheet" type="text/css" href="{{bp.assets['scss'].urls()|first}}" />
{# %block head: Additional stylesheets/scripts for page. #}
{%block head%}{%endblock%}
@ -79,7 +79,7 @@
<footer class="fixed-bottom">
<nav class="navbar">
<div class="container-fluid">
<small>{{_('base.label.copy')}} {{date.year}}</small>
<small>{{_('base.label.copy')}} {{bp.date.year}}</small>
<ul class="navbar-nav">
{%include 'components/nav_foot.jinja'%}
</ul>

View File

@ -1 +1 @@
<a class="nav-link {%if route == '/imprint'%}active{%endif%}" href="/imprint">Imprint & Privacy</a>
<a class="nav-link {%if bp._route() == '/imprint'%}active{%endif%}" href="/imprint">Imprint & Privacy</a>

View File

@ -1,3 +1,3 @@
<li class="nav-item">
<a class="nav-link {%if route == '/'%}active{%endif%}" href="/">Home</a>
<a class="nav-link {%if bp._route() == '/'%}active{%endif%}" href="/">Home</a>
</li>

View File

@ -1 +1,9 @@
{%extends 'base.jinja'%}
{%extends 'base.jinja'%}
{%block body%}
<div class="container" id="content">
<div class="d-flex flex-column">
<h1>Starfall</h1>
</div>
</div>
{%endblock%}

View File

@ -9,8 +9,8 @@
<section class="card mb-2">
<h2 class="card-header">{{_('page.imprint.card.title')}}</h2>
<div class="card-body">
<p>{{_('page.imprint.card.body')}}</p>
<p><strong>{{_('page.imprint.card.lastmodified')}} {{lastmod|dateformat}}</strong></p>
<p>{{_('page.imprint.card.body')|safe}}</p>
<p><strong>{{_('page.imprint.card.lastmodified')}} {{bp.data["lastmod"]|dateformat}}</strong></p>
</div>
</section>
{%for i in range(1,4)%}
@ -20,7 +20,7 @@
</section>
{%endfor%}
<section>
<p><small>{{_('page.imprint.legal')}}</small></p>
<p><small>{{_('page.imprint.legal')|safe}}</small></p>
</section>
</div>
</div>

View File

@ -1,100 +0,0 @@
{%extends 'base.jinja'%}
{%block title%}Imprint &amp; Privacy{%endblock%}
{%block body%}
<div class="container" id="content">
<div class="d-flex flex-column">
<h1 class="mb-4">Imprint &amp; Privacy</h1>
<section class="card mb-2">
<h2 class="card-header">In short</h2>
<div class="card-body">
<p>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).</p>
<p>This disclaimer is not by itself legally binding. The full privacy statement is found below.</p>
<p><strong>Last modified: May 11, 2025</strong></p>
</div>
</section>
<section class="border rounded p-2 mb-2">
<h2>Privacy Policy</h2>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>Our privacy policy is structured as follows:</p>
<p>I. Information about us as controllers of your data<br>II. The rights of users and data subjects<br>III.
Information about the data processing</p>
</section>
<section class="border rounded p-2 mb-2">
<h2>I. Information about us as controllers of your data</h2>
<p>The party responsible for this website (the „controller“) for purposes of data protection law
is:<br>Robert Bäs-Fischlmair a.k.a. Flare Starfall<br>Phone: +43 (0) 677 62890651<br>Email: <a
href="mailto:flare@theflare.at">flare@theflare.at</a></p>
</section>
<section class="border rounded p-2 mb-2">
<h2>II. The rights of users and data subjects</h2>
<p>With regard to the data processing to be described in more detail below, users and data subjects have the
right</p>
<ul>
<li>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);</li>
<li>to correct or complete incorrect or incomplete data (cf. also Art. 16 GDPR);</li>
<li>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; </li>
<li>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);</li>
<li>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).</li>
</ul>
<p>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.</p>
<p><strong>Likewise, under Art. 21 GDPR, users and data subjects have the right to object to the
controller&apos;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.</strong></p>
</section>
<section class="border rounded p-2 mb-2">
<h2>III. Information about the data processing</h2>
<p>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.</p>
<h3>Server data</h3>
<p>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.</p>
<p>The data thus collected will be temporarily stored, but not in association with any other of your data.
</p>
<p>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.</p>
<p>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.</p>
</section>
<section>
<p><small>Based on the <a href="https://www.generator-datenschutzerklärung.de" target="_blank"
rel="noreferrer noopener">Model Data Protection Statement</a> from the <a
href="https://www.ratgeberrecht.eu/" target="_blank" rel="noreferrer noopener">Anwaltskanzlei
Weiß &amp; Partner</a>.<br>Edited to provide details of the data controller, and to detail
exactly what server data is stored.</small></p>
</section>
</div>
</div>
{%endblock%}