feat: Implementiere mehrsprachige Unterstützung (i18n)
- Füge Flask-Babel für professionelle i18n-Implementierung hinzu - Implementiere automatische Browser-Spracherkennung - Erstelle datenschutzfreundliche Sprachauswahl ohne Cookies - Verwende URL-Parameter und localStorage für Sprachauswahl - Füge vollständige Übersetzungen für Deutsch und Englisch hinzu - Implementiere responsive Dropdown-Sprachauswahl mit Landesflaggen - Verbessere Barrierefreiheit mit ARIA-Attributen und Screenreader-Support - Aktualisiere README mit i18n-Dokumentation - Version 1.4.0
This commit is contained in:
139
app.py
139
app.py
@@ -1,4 +1,5 @@
|
||||
from flask import Flask, render_template, request, redirect, url_for, session, abort, jsonify
|
||||
from flask import Flask, render_template, request, redirect, url_for, session, abort, jsonify, g
|
||||
from flask_babel import Babel, gettext, ngettext, get_locale
|
||||
from datetime import datetime, timedelta
|
||||
import numpy as np
|
||||
from dateutil.relativedelta import relativedelta
|
||||
@@ -11,12 +12,40 @@ app_start_time = time.time()
|
||||
app = Flask(__name__)
|
||||
app.secret_key = os.environ.get('SECRET_KEY', 'dev-key')
|
||||
|
||||
# Babel Konfiguration
|
||||
app.config['BABEL_DEFAULT_LOCALE'] = 'de'
|
||||
app.config['BABEL_SUPPORTED_LOCALES'] = ['de', 'en']
|
||||
app.config['BABEL_TRANSLATION_DIRECTORIES'] = 'translations'
|
||||
|
||||
babel = Babel()
|
||||
|
||||
# Version der App
|
||||
APP_VERSION = "1.3.13"
|
||||
|
||||
# HTML-Template wird jetzt aus templates/index.html geladen
|
||||
|
||||
WOCHENTAGE = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
|
||||
WOCHENTAGE_EN = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
||||
|
||||
def get_locale():
|
||||
# Prüfe URL-Parameter für Sprachauswahl (datenschutzfreundlich)
|
||||
if request.args.get('lang') in ['de', 'en']:
|
||||
return request.args.get('lang')
|
||||
# Prüfe Session für Sprachauswahl (Fallback für ältere Browser)
|
||||
if 'language' in session:
|
||||
return session['language']
|
||||
# Fallback auf Browser-Sprache
|
||||
return request.accept_languages.best_match(['de', 'en'], default='de')
|
||||
|
||||
# Registriere die Locale-Funktion mit Babel
|
||||
babel.init_app(app, locale_selector=get_locale)
|
||||
|
||||
def get_wochentage():
|
||||
"""Gibt die Wochentage in der aktuellen Sprache zurück"""
|
||||
locale = get_locale()
|
||||
if locale == 'en':
|
||||
return WOCHENTAGE_EN
|
||||
return WOCHENTAGE
|
||||
|
||||
def get_feiertage(year, bundesland):
|
||||
"""Holt die Feiertage für ein Jahr und Bundesland von feiertage-api.de."""
|
||||
@@ -30,6 +59,24 @@ def get_feiertage(year, bundesland):
|
||||
print(f"Fehler beim Abrufen der Feiertage: {e}")
|
||||
return []
|
||||
|
||||
@app.route('/set_language/<language>')
|
||||
def set_language(language):
|
||||
"""Setzt die Sprache über URL-Parameter (datenschutzfreundlich)"""
|
||||
if language in ['de', 'en']:
|
||||
# URL-Parameter verwenden statt Session
|
||||
referrer = request.referrer or url_for('index')
|
||||
if '?' in referrer:
|
||||
# URL hat bereits Parameter
|
||||
if 'lang=' in referrer:
|
||||
# Ersetze bestehenden lang-Parameter
|
||||
import re
|
||||
referrer = re.sub(r'[?&]lang=[^&]*', '', referrer)
|
||||
return redirect(f"{referrer}&lang={language}")
|
||||
else:
|
||||
# URL hat keine Parameter
|
||||
return redirect(f"{referrer}?lang={language}")
|
||||
return redirect(request.referrer or url_for('index'))
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
def index():
|
||||
# Rudimentäres Logging für Page Impressions
|
||||
@@ -94,24 +141,29 @@ def index():
|
||||
else:
|
||||
tage = abs((d2 - d1).days)
|
||||
except Exception:
|
||||
tage = 'Ungültige Eingabe'
|
||||
tage = gettext('Ungültige Eingabe')
|
||||
elif action == 'wochentag':
|
||||
active_idx = 1
|
||||
datum = request.form.get('datum3')
|
||||
try:
|
||||
d = datetime.strptime(datum, '%Y-%m-%d')
|
||||
wochentag = WOCHENTAGE[d.weekday()]
|
||||
wochentage = get_wochentage()
|
||||
wochentag = wochentage[d.weekday()]
|
||||
except Exception:
|
||||
wochentag = 'Ungültige Eingabe'
|
||||
wochentag = gettext('Ungültige Eingabe')
|
||||
elif action == 'kw_berechnen':
|
||||
active_idx = 2
|
||||
datum = request.form.get('datum6')
|
||||
try:
|
||||
d = datetime.strptime(datum, '%Y-%m-%d')
|
||||
kw = d.isocalendar().week
|
||||
kw_berechnen = f"KW {kw} ({d.year})"
|
||||
locale = get_locale()
|
||||
if locale == 'en':
|
||||
kw_berechnen = f"Week {kw} ({d.year})"
|
||||
else:
|
||||
kw_berechnen = f"KW {kw} ({d.year})"
|
||||
except Exception:
|
||||
kw_berechnen = 'Ungültige Eingabe'
|
||||
kw_berechnen = gettext('Ungültige Eingabe')
|
||||
elif action == 'kw_datum':
|
||||
active_idx = 3
|
||||
jahr = request.form.get('jahr7')
|
||||
@@ -122,9 +174,13 @@ def index():
|
||||
# Montag der KW
|
||||
start = datetime.fromisocalendar(jahr, kw, 1)
|
||||
end = datetime.fromisocalendar(jahr, kw, 7)
|
||||
kw_datum = f"{start.strftime('%d.%m.%Y')} bis {end.strftime('%d.%m.%Y')}"
|
||||
locale = get_locale()
|
||||
if locale == 'en':
|
||||
kw_datum = f"{start.strftime('%m/%d/%Y')} to {end.strftime('%m/%d/%Y')}"
|
||||
else:
|
||||
kw_datum = f"{start.strftime('%d.%m.%Y')} bis {end.strftime('%d.%m.%Y')}"
|
||||
except Exception:
|
||||
kw_datum = 'Ungültige Eingabe'
|
||||
kw_datum = gettext('Ungültige Eingabe')
|
||||
elif action == 'plusminus':
|
||||
active_idx = 4
|
||||
datum = request.form.get('datum_pm')
|
||||
@@ -137,31 +193,44 @@ def index():
|
||||
anzahl_int = int(anzahl)
|
||||
if richtung == 'sub':
|
||||
anzahl_int = -anzahl_int
|
||||
locale = get_locale()
|
||||
if einheit == 'tage':
|
||||
if is_werktage:
|
||||
# Werktage: numpy busday_offset
|
||||
result = np.busday_offset(d.date(), anzahl_int, roll='forward')
|
||||
result_dt = datetime.strptime(str(result), '%Y-%m-%d')
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Werktage: {result_dt.strftime('%d.%m.%Y')}"
|
||||
if locale == 'en':
|
||||
plusminus_result = f"Date {d.strftime('%m/%d/%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} workdays: {result_dt.strftime('%m/%d/%Y')}"
|
||||
else:
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Werktage: {result_dt.strftime('%d.%m.%Y')}"
|
||||
else:
|
||||
result = d + timedelta(days=anzahl_int)
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Tage: {result.strftime('%d.%m.%Y')}"
|
||||
if locale == 'en':
|
||||
plusminus_result = f"Date {d.strftime('%m/%d/%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} days: {result.strftime('%m/%d/%Y')}"
|
||||
else:
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Tage: {result.strftime('%d.%m.%Y')}"
|
||||
elif einheit == 'wochen':
|
||||
if is_werktage:
|
||||
plusminus_result = 'Nicht unterstützt: Werktage + Wochen.'
|
||||
plusminus_result = gettext('Nicht unterstützt: Werktage + Wochen.')
|
||||
else:
|
||||
result = d + timedelta(weeks=anzahl_int)
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Wochen: {result.strftime('%d.%m.%Y')}"
|
||||
if locale == 'en':
|
||||
plusminus_result = f"Date {d.strftime('%m/%d/%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} weeks: {result.strftime('%m/%d/%Y')}"
|
||||
else:
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Wochen: {result.strftime('%d.%m.%Y')}"
|
||||
elif einheit == 'monate':
|
||||
if is_werktage:
|
||||
plusminus_result = 'Nicht unterstützt: Werktage + Monate.'
|
||||
plusminus_result = gettext('Nicht unterstützt: Werktage + Monate.')
|
||||
else:
|
||||
result = d + relativedelta(months=anzahl_int)
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Monate: {result.strftime('%d.%m.%Y')}"
|
||||
if locale == 'en':
|
||||
plusminus_result = f"Date {d.strftime('%m/%d/%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} months: {result.strftime('%m/%d/%Y')}"
|
||||
else:
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Monate: {result.strftime('%d.%m.%Y')}"
|
||||
except Exception:
|
||||
plusminus_result = 'Ungültige Eingabe'
|
||||
plusminus_result = gettext('Ungültige Eingabe')
|
||||
return render_template('index.html', tage=tage, werktage=werktage, wochentag=wochentag, plusminus_result=plusminus_result, kw_berechnen=kw_berechnen, kw_datum=kw_datum, active_idx=active_idx
|
||||
, feiertage_anzahl=feiertage_anzahl, wochenendtage_anzahl=wochenendtage_anzahl, app_version=APP_VERSION
|
||||
, feiertage_anzahl=feiertage_anzahl, wochenendtage_anzahl=wochenendtage_anzahl, app_version=APP_VERSION, get_locale=get_locale
|
||||
)
|
||||
|
||||
|
||||
@@ -247,7 +316,8 @@ def api_wochentag():
|
||||
datum = data.get('datum')
|
||||
try:
|
||||
d = datetime.strptime(datum, '%Y-%m-%d')
|
||||
wochentag = WOCHENTAGE[d.weekday()]
|
||||
wochentage = get_wochentage()
|
||||
wochentag = wochentage[d.weekday()]
|
||||
return jsonify({'result': wochentag})
|
||||
except Exception as e:
|
||||
return jsonify({'error': 'Ungültige Eingabe', 'details': str(e)}), 400
|
||||
@@ -260,7 +330,12 @@ def api_kw_berechnen():
|
||||
try:
|
||||
d = datetime.strptime(datum, '%Y-%m-%d')
|
||||
kw = d.isocalendar().week
|
||||
return jsonify({'result': f"KW {kw} ({d.year})", 'kw': kw, 'jahr': d.year})
|
||||
locale = get_locale()
|
||||
if locale == 'en':
|
||||
kw_berechnen = f"Week {kw} ({d.year})"
|
||||
else:
|
||||
kw_berechnen = f"KW {kw} ({d.year})"
|
||||
return jsonify({'result': kw_berechnen, 'kw': kw, 'jahr': d.year})
|
||||
except Exception as e:
|
||||
return jsonify({'error': 'Ungültige Eingabe', 'details': str(e)}), 400
|
||||
|
||||
@@ -275,7 +350,12 @@ def api_kw_datum():
|
||||
kw = int(kw)
|
||||
start = datetime.fromisocalendar(jahr, kw, 1)
|
||||
end = datetime.fromisocalendar(jahr, kw, 7)
|
||||
return jsonify({'result': f"{start.strftime('%d.%m.%Y')} bis {end.strftime('%d.%m.%Y')}", 'start': start.strftime('%Y-%m-%d'), 'end': end.strftime('%Y-%m-%d')})
|
||||
locale = get_locale()
|
||||
if locale == 'en':
|
||||
kw_datum = f"{start.strftime('%m/%d/%Y')} to {end.strftime('%m/%d/%Y')}"
|
||||
else:
|
||||
kw_datum = f"{start.strftime('%d.%m.%Y')} bis {end.strftime('%d.%m.%Y')}"
|
||||
return jsonify({'result': kw_datum, 'start': start.strftime('%Y-%m-%d'), 'end': end.strftime('%Y-%m-%d')})
|
||||
except Exception as e:
|
||||
return jsonify({'error': 'Ungültige Eingabe', 'details': str(e)}), 400
|
||||
|
||||
@@ -293,26 +373,39 @@ def api_plusminus():
|
||||
anzahl_int = int(anzahl)
|
||||
if richtung == 'sub':
|
||||
anzahl_int = -anzahl_int
|
||||
locale = get_locale()
|
||||
if einheit == 'tage':
|
||||
if is_werktage:
|
||||
result = np.busday_offset(d.date(), anzahl_int, roll='forward')
|
||||
result_dt = datetime.strptime(str(result), '%Y-%m-%d')
|
||||
return jsonify({'result': result_dt.strftime('%Y-%m-%d')})
|
||||
if locale == 'en':
|
||||
plusminus_result = f"Date {d.strftime('%m/%d/%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} workdays: {result_dt.strftime('%m/%d/%Y')}"
|
||||
else:
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Werktage: {result_dt.strftime('%d.%m.%Y')}"
|
||||
else:
|
||||
result = d + timedelta(days=anzahl_int)
|
||||
return jsonify({'result': result.strftime('%Y-%m-%d')})
|
||||
if locale == 'en':
|
||||
plusminus_result = f"Date {d.strftime('%m/%d/%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} days: {result.strftime('%m/%d/%Y')}"
|
||||
else:
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Tage: {result.strftime('%d.%m.%Y')}"
|
||||
elif einheit == 'wochen':
|
||||
if is_werktage:
|
||||
return jsonify({'error': 'Nicht unterstützt: Werktage + Wochen.'}), 400
|
||||
else:
|
||||
result = d + timedelta(weeks=anzahl_int)
|
||||
return jsonify({'result': result.strftime('%Y-%m-%d')})
|
||||
if locale == 'en':
|
||||
plusminus_result = f"Date {d.strftime('%m/%d/%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} weeks: {result.strftime('%m/%d/%Y')}"
|
||||
else:
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Wochen: {result.strftime('%d.%m.%Y')}"
|
||||
elif einheit == 'monate':
|
||||
if is_werktage:
|
||||
return jsonify({'error': 'Nicht unterstützt: Werktage + Monate.'}), 400
|
||||
else:
|
||||
result = d + relativedelta(months=anzahl_int)
|
||||
return jsonify({'result': result.strftime('%Y-%m-%d')})
|
||||
if locale == 'en':
|
||||
plusminus_result = f"Date {d.strftime('%m/%d/%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} months: {result.strftime('%m/%d/%Y')}"
|
||||
else:
|
||||
plusminus_result = f"Datum {d.strftime('%d.%m.%Y')} {'plus' if anzahl_int>=0 else 'minus'} {abs(anzahl_int)} Monate: {result.strftime('%d.%m.%Y')}"
|
||||
else:
|
||||
return jsonify({'error': 'Ungültige Einheit'}), 400
|
||||
except Exception as e:
|
||||
|
Reference in New Issue
Block a user