Web interface to view, ban and unban fail2ban IPs on several hosts via SSH, generalised from the private f2b-web tool. - Web UI (Flask/Waitress): hosts.yaml, local/OIDC/proxy authentication, English/German UI, origin map, optional Loki/Grafana/GeoIP, CLI helpers - Agent f2b-webctl with install.sh/uninstall.sh (dedicated user + sudo, from= restriction, protected networks, manual jail) - Hardened Docker image, examples, documentation, smoke test, testbed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
46 lines
1.2 KiB
Python
46 lines
1.2 KiB
Python
"""Translations: one JSON file per language in webui/locales/ (e.g. en.json, de.json).
|
|
|
|
A new language only needs a new file; "_name" is the label shown in the dropdown.
|
|
Missing keys fall back to English.
|
|
"""
|
|
import json
|
|
import os
|
|
|
|
DIR = os.path.join(os.path.dirname(__file__), "locales")
|
|
FALLBACK = "en"
|
|
LANGS = {}
|
|
|
|
|
|
def load():
|
|
LANGS.clear()
|
|
for fname in sorted(os.listdir(DIR)):
|
|
if fname.endswith(".json"):
|
|
with open(os.path.join(DIR, fname), encoding="utf-8") as fh:
|
|
LANGS[fname[:-5]] = json.load(fh)
|
|
|
|
|
|
def pick(cookie_value, default):
|
|
if cookie_value in LANGS:
|
|
return cookie_value
|
|
return default if default in LANGS else FALLBACK
|
|
|
|
|
|
def catalog(lang):
|
|
"""Full dictionary for the browser, English as base."""
|
|
return {**LANGS.get(FALLBACK, {}), **LANGS.get(lang, {})}
|
|
|
|
|
|
def translate(lang, key, **params):
|
|
text = LANGS.get(lang, {}).get(key) or LANGS.get(FALLBACK, {}).get(key) or key
|
|
try:
|
|
return text.format(**params) if params else text
|
|
except (KeyError, IndexError, ValueError):
|
|
return text
|
|
|
|
|
|
def languages():
|
|
return [(code, data.get("_name", code)) for code, data in sorted(LANGS.items())]
|
|
|
|
|
|
load()
|