From 884e6070e80ce19e38382b1013b9375bd66f9049 Mon Sep 17 00:00:00 2001
From: Julian Rother <julianr@fsmpi.rwth-aachen.de>
Date: Fri, 30 Jul 2021 14:26:56 +0200
Subject: [PATCH] Completed German translation

---
 uffd/default_config.cfg                       |    2 +-
 uffd/invite/templates/invite/list.html        |   60 +-
 uffd/invite/templates/invite/new.html         |   26 +-
 uffd/invite/templates/invite/use.html         |   18 +-
 uffd/invite/views.py                          |   26 +-
 uffd/mail/templates/mail/list.html            |    8 +-
 uffd/mail/templates/mail/show.html            |   18 +-
 uffd/mail/views.py                            |    7 +-
 uffd/oauth2/templates/oauth2/logout.html      |   16 +-
 uffd/oauth2/views.py                          |    5 +-
 uffd/ratelimit.py                             |   13 +-
 uffd/role/templates/role/list.html            |   10 +-
 uffd/role/templates/role/show.html            |    6 +-
 uffd/rolemod/templates/rolemod/list.html      |    4 +-
 uffd/rolemod/templates/rolemod/show.html      |   22 +-
 uffd/rolemod/views.py                         |   13 +-
 .../templates/selfservice/self.html           |    6 +-
 uffd/selfservice/views.py                     |   26 +-
 .../session/templates/session/deviceauth.html |   24 +-
 .../templates/session/devicelogin.html        |   14 +-
 uffd/session/templates/session/login.html     |    8 +-
 uffd/session/views.py                         |   14 +-
 uffd/signup/templates/signup/confirm.html     |    6 +-
 uffd/signup/templates/signup/start.html       |   32 +-
 uffd/signup/templates/signup/submitted.html   |    6 +-
 uffd/signup/views.py                          |   24 +-
 uffd/templates/base.html                      |    6 +-
 uffd/translations/de/LC_MESSAGES/messages.mo  |  Bin 14883 -> 30649 bytes
 uffd/translations/de/LC_MESSAGES/messages.po  | 1347 +++++++++++++----
 uffd/user/templates/group/list.html           |    6 +-
 uffd/user/templates/group/show.html           |    4 +-
 uffd/user/templates/user/list.html            |   14 +-
 uffd/user/templates/user/show.html            |   30 +-
 33 files changed, 1297 insertions(+), 524 deletions(-)

diff --git a/uffd/default_config.cfg b/uffd/default_config.cfg
index 23b755b5..e56c564d 100644
--- a/uffd/default_config.cfg
+++ b/uffd/default_config.cfg
@@ -56,7 +56,7 @@ SESSION_COOKIE_SAMESITE='Strict'
 LANGUAGES={
 	# Language identifier (see Accept-Language HTTP header) -> Display Name
 	"en": "EN",
-	#"de": "DE", # Too incomplete right now to enable per default
+	"de": "DE", # Too incomplete right now to enable per default
 }
 
 ACL_ADMIN_GROUP="uffd_admin"
diff --git a/uffd/invite/templates/invite/list.html b/uffd/invite/templates/invite/list.html
index 00fd0ee4..6099b21d 100644
--- a/uffd/invite/templates/invite/list.html
+++ b/uffd/invite/templates/invite/list.html
@@ -3,17 +3,17 @@
 {% block body %}
 
 <div class="btn-toolbar mb-2">
-	<a class="btn btn-primary ml-auto" href="{{ url_for("invite.new") }}"><i class="fa fa-plus" aria-hidden="true"></i> New</a>
+	<a class="btn btn-primary ml-auto" href="{{ url_for("invite.new") }}"><i class="fa fa-plus" aria-hidden="true"></i> {{_('New')}}</a>
 </div>
 <div class="table-responsive">
 	<table class="table">
 		<thead>
 			<tr>
-				<th scope="col">Link</th>
-				<th scope="col">Created by</th>
-				<th scope="col">Permissions</th>
-				<th scope="col">Usages</th>
-				<th scope="col">Status</th>
+				<th scope="col">{{_('Link')}}</th>
+				<th scope="col">{{_('Created by')}}</th>
+				<th scope="col">{{_('Permissions')}}</th>
+				<th scope="col">{{_('Usages')}}</th>
+				<th scope="col">{{_('Status')}}</th>
 				<th scope="col"></th>
 			</tr>
 		</thead>
@@ -23,8 +23,8 @@
 				<td>
 					{% if invite.creator == request.user and invite.active %}
 					<a href="{{ url_for('invite.use', token=invite.token) }}"><code>{{ invite.short_token }}</code></a>
-					<button type="button" class="btn btn-link btn-sm p-0 copy-clipboard" data-copy="{{ url_for('invite.use', token=invite.token, _external=True) }}" title="Copy link to clipboard"><i class="fas fa-clipboard"></i></button>
-					<button type="button" class="btn btn-link btn-sm p-0" data-toggle="modal" data-target="#modal-{{ invite.id }}-qrcode" title="Show link as QR code"><i class="fas fa-qrcode"></i></button>
+					<button type="button" class="btn btn-link btn-sm p-0 copy-clipboard" data-copy="{{ url_for('invite.use', token=invite.token, _external=True) }}" title="{{_('Copy link to clipboard')}}"><i class="fas fa-clipboard"></i></button>
+					<button type="button" class="btn btn-link btn-sm p-0" data-toggle="modal" data-target="#modal-{{ invite.id }}-qrcode" title="{{_('Show link as QR code')}}"><i class="fas fa-qrcode"></i></button>
 					{% else %}
 					<code>{{ invite.short_token }}</code>
 					{% endif %}
@@ -39,7 +39,7 @@
 					{% endif %}
 				</td>
 				<td>
-					{{ 'Signup' if invite.allow_signup }}{{ ', ' if invite.allow_signup and invite.roles }}
+					{{ _('Signup') if invite.allow_signup }}{{ ', ' if invite.allow_signup and invite.roles }}
 					{% for role in invite.roles %}{{ ', ' if loop.index != 1 }}<i class="fas fa-key"></i>&thinsp;{{ role.name }}{% endfor %}
 				</td>
 				<td>
@@ -48,19 +48,19 @@
 				</td>
 				<td>
 					{% if invite.disabled %}
-						Disabled
+						{{_('Disabled')}}
 					{% elif invite.voided %}
-						Voided
+						{{_('Voided')}}
 					{% elif invite.expired %}
-						Expired
+						{{_('Expired')}}
 					{% elif not invite.permitted %}
-						Invalid, unpermitted creator
+						{{_('Invalid, unpermitted creator')}}
 					{% elif not invite.active %}
-						Invalid
+						{{_('Invalid')}}
 					{% elif invite.single_use %}
-						Valid once, expires {{ invite.valid_until.strftime('%Y-%m-%d') }}
+						{{ _('Valid once, expires %(expiry_date)s', expiry_date=invite.valid_until.strftime('%Y-%m-%d')) }}
 					{% else %}
-						Valid, expires {{ invite.valid_until.strftime('%Y-%m-%d') }}
+						{{ _('Valid, expires %(expiry_date)s',expiry_date=invite.valid_until.strftime('%Y-%m-%d')) }}
 					{% endif %}
 				</td>
 				<td class="text-right">
@@ -77,38 +77,38 @@
 	<div class="modal-dialog">
 		<div class="modal-content">
 			<div class="modal-header">
-				<h5 class="modal-title">Invite Link Details</h5>
+				<h5 class="modal-title">{{_('Invite Link Details')}}</h5>
 				<button type="button" class="close" data-dismiss="modal" aria-label="Close">
 					<span aria-hidden="true">&times;</span>
 				</button>
 			</div>
 			<div class="modal-body">
 				<ul class="list-unstyled">
-					<li><b>Type:</b> {% if invite.single_use %}Single-use{% else %}Multi-use{% endif %}</li>
-					<li><b>Created:</b> {{ invite.created.strftime('%Y-%m-%d %H:%M:%S') }}</li>
-					<li><b>Expires:</b> {{ invite.valid_until.strftime('%Y-%m-%d %H:%M:%S') }}</li>
-					<li><b>Permissions:</b>
+					<li><b>{{_('Type:')}}</b> {% if invite.single_use %}{{_('Single-use')}}{% else %}{{_('Multi-use')}}{% endif %}</li>
+					<li><b>{{_('Created:')}}</b> {{ invite.created.strftime('%Y-%m-%d %H:%M:%S') }}</li>
+					<li><b>{{_('Expires:')}}</b> {{ invite.valid_until.strftime('%Y-%m-%d %H:%M:%S') }}</li>
+					<li><b>{{_('Permissions:')}}</b>
 						<ul>
 							{% if invite.allow_signup %}
-							<li>Link allows account registration</li>
+							<li>{{_('Link allows account registration')}}</li>
 							{% else %}
-							<li>No account registration allowed</li>
+							<li>{{_('No account registration allowed')}}</li>
 							{% endif %}
 							{% for role in invite.roles %}
-							<li>Link grants users the role "{{ role.name }}"</li>
+							<li>{{_('Link grants users the role "%(name)s"', name=role.name)}}</li>
 							{% endfor %}
 						</ul>
 					</li>
 					<li><b>Usages:</b>
 						{% if not invite.signups and not invite.grants %}
-						Never used
+						{{_('Never used')}}
 						{% else %}
 						<ul>
 							{% for signup in invite.signups if signup.completed %}
-							<li>Registration of user <a href="{{ url_for('user.show', uid=signup.user.uid) }}">{{ signup.user.loginname }}</a></li>
+							<li>{{_('Registration of user <a href="%(user_url)s">%(user_name)s</a>', user_url=url_for('user.show', uid=signup.user.uid)|e, user_name=signup.user.loginname|e)|safe}}</li>
 							{% endfor %}
 							{% for grant in invite.grants if grant.user %}
-							<li>Roles granted to <a href="{{ url_for('user.show', uid=grant.user.uid) }}">{{ grant.user.loginname }}</a></li>
+							<li>{{_('Roles granted to <a href="%(user_url)s">%(user_name)s</a>', user_url=url_for('user.show', uid=grant.user.uid)|e, user_name=grant.user.loginname|e)}}|safe</li>
 							{% endfor %}
 						</ul>
 						{% endif %}
@@ -119,11 +119,11 @@
 				<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
 				{% if invite.active %}
 				<form action="{{ url_for('invite.disable', invite_id=invite.id) }}" method="POST">
-				<button type="submit" class="btn btn-primary">Disable Link</button>
+				<button type="submit" class="btn btn-primary">{{_('Disable Link')}}</button>
 				</form>
 				{% elif invite.creator == request.user and not invite.expired and invite.permitted %}
 				<form action="{{ url_for('invite.reset', invite_id=invite.id) }}" method="POST">
-				<button type="submit" class="btn btn-primary">Reenable Link</button>
+				<button type="submit" class="btn btn-primary">{{_('Reenable Link')}}</button>
 				</form>
 				{% endif %}
 			</div>
@@ -137,7 +137,7 @@
 	<div class="modal-dialog">
 		<div class="modal-content">
 			<div class="modal-header">
-				<h5 class="modal-title">Invite</h5>
+				<h5 class="modal-title">{{_('Invite')}}</h5>
 				<button type="button" class="close" data-dismiss="modal" aria-label="Close">
 					<span aria-hidden="true">&times;</span>
 				</button>
diff --git a/uffd/invite/templates/invite/new.html b/uffd/invite/templates/invite/new.html
index c6731bd8..5c69f52e 100644
--- a/uffd/invite/templates/invite/new.html
+++ b/uffd/invite/templates/invite/new.html
@@ -3,23 +3,23 @@
 {% block body %}
 <form action="{{ url_for("invite.new_submit") }}" method="POST" class="form">
 	<div class="form-group">
-		<label for="single-use">Link Type</label>
+		<label for="single-use">{{_('Link Type')}}</label>
 		<select class="form-control" id="single-use" name="single-use">
-			<option value="1">Valid for a single successful use</option>
-			<option value="0">Multi-use</option>
+			<option value="1">{{_('Valid for a single successful use')}}</option>
+			<option value="0">{{_('Multi-use')}}</option>
 		</select>
 	</div>
 	<div class="form-group">
-		<label for="valid-until">Valid Until</label>
+		<label for="valid-until">{{_('Valid Until')}}</label>
 		<input class="form-control" type="datetime-local" id="valid-until" name="valid-until" value="{{ (datetime.now() + timedelta(hours=36)).replace(hour=23, minute=59, second=59, microsecond=0).isoformat(timespec='minutes') }}">
-		<small class="text-muted">Must be within the next {{ config['INVITE_MAX_VALID_DAYS'] }} days</small>
+		<small class="text-muted">{{_('Must be within the next %(max_valid_days)d days', max_valid_days=config['INVITE_MAX_VALID_DAYS'])}}</small>
 	</div>
 	{% if allow_signup %}
 	<div class="form-group">
-		<label for="allow-signup">Account Registration</label>
+		<label for="allow-signup">{{_('Account Registration')}}</label>
 		<select class="form-control" id="allow-signup" name="allow-signup">
-			<option value="1">Link allows account registration</option>
-			<option value="0">No account registration allowed</option>
+			<option value="1">{{_('Link allows account registration')}}</option>
+			<option value="0">{{_('No account registration allowed')}}</option>
 		</select>
 	</div>
 	{% else %}
@@ -27,13 +27,13 @@
 	{% endif %}
 	{% if roles %}
 	<div class="form-group">
-		<label for="valid-until">Granted Roles</label>
+		<label for="valid-until">{{_('Granted Roles')}}</label>
 		<table class="table table-sm">
 			<thead>
 				<tr>
 					<th scope="col"></th>
-					<th scope="col">Name</th>
-					<th scope="col">Description</th>
+					<th scope="col">{{_('Name')}}</th>
+					<th scope="col">{{_('Description')}}</th>
 				</tr>
 			</thead>
 			<tbody>
@@ -52,7 +52,7 @@
 		</table>
 	</div>
 	{% endif %}
-	<button type="submit" class="btn btn-primary"><i class="fa fa-save" aria-hidden="true"></i> Create Link</button>
-	<a href="{{ url_for("invite.index") }}" class="btn btn-secondary">Cancel</a>
+	<button type="submit" class="btn btn-primary"><i class="fa fa-save" aria-hidden="true"></i> {{_('Create Link')}}</button>
+	<a href="{{ url_for("invite.index") }}" class="btn btn-secondary">{{_('Cancel')}}</a>
 </form>
 {% endblock %}
diff --git a/uffd/invite/templates/invite/use.html b/uffd/invite/templates/invite/use.html
index c852f3db..344b8b03 100644
--- a/uffd/invite/templates/invite/use.html
+++ b/uffd/invite/templates/invite/use.html
@@ -8,18 +8,18 @@
 			<img alt="branding logo" src="{{ config.get("BRANDING_LOGO_URL") }}" class="col-lg-8 col-md-12" >
 		</div>
 		<div class="col-12 mb-3">
-			<h2 class="text-center">Invite Link</h2>
+			<h2 class="text-center">{{_('Invite Link')}}</h2>
 		</div>
 		{% if not request.user %}
 		<p>Welcome to the CCCV Single-Sign-On!</p>
 		{% endif %}
 
 		{% if invite.roles and invite.allow_signup %}
-		<p>With this link you can register a new user account with the following roles or add the roles to an existing account:</p>
+		<p>{{_('With this link you can register a new user account with the following roles or add the roles to an existing account:')}}</p>
 		{% elif invite.roles %}
-		<p>With this link you can add the following roles to an existing account:</p>
+		<p>{{_('With this link you can add the following roles to an existing account:')}}</p>
 		{% elif invite.allow_signup %}
-		<p>With this link you can register a new user account.</p>
+		<p>{{_('With this link you can register a new user account.')}}</p>
 		{% endif %}
 		{% if invite.roles %}
 		<ul>
@@ -31,19 +31,19 @@
 		{% if request.user %}
 			{% if invite.roles %}
 				<form method="POST" action="{{ url_for("invite.grant", token=invite.token) }}" class="mb-2">
-					<button type="submit" class="btn btn-primary btn-block">Add the roles to your account now</button>
+					<button type="submit" class="btn btn-primary btn-block">{{_('Add the roles to your account now')}}</button>
 				</form>
-				<a href="{{ url_for("session.logout", ref=url_for("session.login", ref=request.full_path)) }}" class="btn btn-secondary btn-block">Logout and switch to a different account</a>
+				<a href="{{ url_for("session.logout", ref=url_for("session.login", ref=request.full_path)) }}" class="btn btn-secondary btn-block">{{_('Logout and switch to a different account')}}</a>
 			{% endif %}
 			{% if invite.allow_signup %}
-				<a href="{{ url_for("session.logout", ref=url_for("invite.signup_start", token=invite.token)) }}" class="btn btn-secondary btn-block">Logout to register a new account</a>
+				<a href="{{ url_for("session.logout", ref=url_for("invite.signup_start", token=invite.token)) }}" class="btn btn-secondary btn-block">{{_('Logout to register a new account')}}</a>
 			{% endif %}
 		{% else %}
 			{% if invite.allow_signup %}
-				<a href="{{ url_for("invite.signup_start", token=invite.token) }}" class="btn btn-primary btn-block">Register a new account</a>
+				<a href="{{ url_for("invite.signup_start", token=invite.token) }}" class="btn btn-primary btn-block">{{_('Register a new account')}}</a>
 			{% endif %}
 			{% if invite.roles %}
-				<a href="{{ url_for("session.login", ref=request.full_path) }}" class="btn btn-primary btn-block">Login and add the roles to your account</a>
+				<a href="{{ url_for("session.login", ref=request.full_path) }}" class="btn btn-primary btn-block">{{_('Login and add the roles to your account')}}</a>
 			{% endif %}
 		{% endif %}
 	</div>
diff --git a/uffd/invite/views.py b/uffd/invite/views.py
index ece81c64..7d1887be 100644
--- a/uffd/invite/views.py
+++ b/uffd/invite/views.py
@@ -2,6 +2,7 @@ import datetime
 import functools
 
 from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app, jsonify
+from flask_babel import gettext as _, lazy_gettext
 import sqlalchemy
 
 from uffd.csrf import csrf_protect
@@ -52,7 +53,7 @@ def reset_acl_filter(user):
 	return Invite.creator_dn == user.dn
 
 @bp.route('/')
-@register_navbar('Invites', icon='link', blueprint=bp, visible=invite_acl)
+@register_navbar(lazy_gettext('Invites'), icon='link', blueprint=bp, visible=invite_acl)
 @invite_acl_required
 def index():
 	invites = Invite.query.filter(view_acl_filter(request.user)).all()
@@ -81,13 +82,13 @@ def new_submit():
 		if key.startswith('role-') and value == '1':
 			invite.roles.append(Role.query.get(key[5:]))
 	if invite.valid_until > datetime.datetime.now() + datetime.timedelta(days=current_app.config['INVITE_MAX_VALID_DAYS']):
-		flash('The "Expires After" date is too far in the future')
+		flash(_('The "Expires After" date is too far in the future'))
 		return redirect(url_for('invite.new'))
 	if not invite.permitted:
-		flash('You are not allowed to create invite links with these permissions')
+		flash(_('You are not allowed to create invite links with these permissions'))
 		return redirect(url_for('invite.new'))
 	if not invite.allow_signup and not invite.roles:
-		flash('Invite link must either allow signup or grant at least one role')
+		flash(_('Invite link must either allow signup or grant at least one role'))
 		return redirect(url_for('invite.new'))
 	db.session.add(invite)
 	db.session.commit()
@@ -115,7 +116,7 @@ def reset(invite_id):
 def use(token):
 	invite = Invite.query.filter_by(token=token).first_or_404()
 	if not invite.active:
-		flash('Invalid invite link')
+		flash(_('Invalid invite link'))
 		return redirect('/')
 	return render_template('invite/use.html', invite=invite)
 
@@ -132,7 +133,7 @@ def grant(token):
 		return redirect(url_for('selfservice.index'))
 	ldap.session.commit()
 	db.session.commit()
-	flash('Roles successfully updated')
+	flash(_('Roles successfully updated'))
 	return redirect(url_for('selfservice.index'))
 
 @bp.url_defaults
@@ -144,10 +145,10 @@ def inject_invite_token(endpoint, values):
 def signup_start(token):
 	invite = Invite.query.filter_by(token=token).first_or_404()
 	if not invite.active:
-		flash('Invalid invite link')
+		flash(_('Invalid invite link'))
 		return redirect('/')
 	if not invite.allow_signup:
-		flash('Invite link does not allow signup')
+		flash(_('Invite link does not allow signup'))
 		return redirect('/')
 	return render_template('signup/start.html')
 
@@ -169,13 +170,14 @@ def signup_check(token):
 def signup_submit(token):
 	invite = Invite.query.filter_by(token=token).first_or_404()
 	if request.form['password1'] != request.form['password2']:
-		return render_template('signup/start.html', error='Passwords do not match')
+		return render_template('signup/start.html', error=_('Passwords do not match'))
 	signup_delay = signup_ratelimit.get_delay(request.form['mail'])
 	host_delay = host_ratelimit.get_delay()
 	if signup_delay and signup_delay > host_delay:
-		return render_template('signup/start.html', error='Too many signup requests with this mail address! Please wait %s.'%format_delay(signup_delay))
+		return render_template('signup/start.html', error=_('Too many signup requests with this mail address! Please wait %(delay)s.',
+		                                                    delay=format_delay(signup_delay)))
 	if host_delay:
-		return render_template('signup/start.html', error='Too many requests! Please wait %s.'%format_delay(host_delay))
+		return render_template('signup/start.html', error=_('Too many requests! Please wait %(delay)s.', delay=format_delay(host_delay)))
 	host_ratelimit.log()
 	signup = InviteSignup(invite=invite, loginname=request.form['loginname'],
 	                      displayname=request.form['displayname'],
@@ -188,6 +190,6 @@ def signup_submit(token):
 	db.session.commit()
 	sent = sendmail(signup.mail, 'Confirm your mail address', 'signup/mail.txt', signup=signup)
 	if not sent:
-		return render_template('signup/start.html', error='Cound not send mail')
+		return render_template('signup/start.html', error=_('Cound not send mail'))
 	signup_ratelimit.log(request.form['mail'])
 	return render_template('signup/submitted.html', signup=signup)
diff --git a/uffd/mail/templates/mail/list.html b/uffd/mail/templates/mail/list.html
index f994b140..c61bfda6 100644
--- a/uffd/mail/templates/mail/list.html
+++ b/uffd/mail/templates/mail/list.html
@@ -5,15 +5,15 @@
 	<div class="col">
 		<p class="text-right">
 			<a class="btn btn-primary" href="{{ url_for("mail.show") }}">
-				<i class="fa fa-plus" aria-hidden="true"></i> New
+				<i class="fa fa-plus" aria-hidden="true"></i> {{_('New')}}
 			</a>
 		</p>
 		<table class="table table-striped table-sm">
 			<thead>
 				<tr>
-					<th scope="col">name</th>
-					<th scope="col">receiving address</th>
-					<th scope="col">destinations</th>
+					<th scope="col">{{_('Name')}}</th>
+					<th scope="col">{{_('Receiving addresses')}}</th>
+					<th scope="col">{{_('Destinations')}}</th>
 				</tr>
 			</thead>
 			<tbody>
diff --git a/uffd/mail/templates/mail/show.html b/uffd/mail/templates/mail/show.html
index 44f74f47..dea92a2f 100644
--- a/uffd/mail/templates/mail/show.html
+++ b/uffd/mail/templates/mail/show.html
@@ -4,32 +4,32 @@
 <form action="{{ url_for("mail.update", uid=mail.uid) }}" method="POST">
 <div class="align-self-center">
 	<div class="form-group col">
-		<label for="mail-name">Name</label>
+		<label for="mail-name">{{_('Name')}}</label>
 		<input type="text" class="form-control" id="mail-name" name="mail-uid" {% if mail.uid %} value="{{ mail.uid }}" readonly {% else %} value=""{% endif %}>
 		<small class="form-text text-muted">
 		</small>
 	</div>
 	<div class="form-group col">
-		<label for="mail-receivers">Receiving addresses</label>
+		<label for="mail-receivers">{{_('Receiving addresses')}}</label>
 		<textarea rows="10" class="form-control" id="mail-receivers" name="mail-receivers">{{ mail.receivers|join('\n') }}</textarea>
 		<small class="form-text text-muted">
-			One address per line
+			{{_('One address per line')}}
 		</small>
 	</div>
 	<div class="form-group col">
-		<label for="mail-destinations">Destinations</label>
+		<label for="mail-destinations">{{_('Destinations')}}</label>
 		<textarea rows="10" class="form-control" id="mail-destinations" name="mail-destinations">{{ mail.destinations|join('\n') }}</textarea>
 		<small class="form-text text-muted">
-			One address per line
+			{{_('One address per line')}}
 		</small>
 	</div>
 	<div class="form-group col">
-		<button type="submit" class="btn btn-primary"><i class="fa fa-save" aria-hidden="true"></i> Save</button>
-		<a href="{{ url_for("mail.index") }}" class="btn btn-secondary">Cancel</a>
+		<button type="submit" class="btn btn-primary"><i class="fa fa-save" aria-hidden="true"></i> {{_('Save')}}</button>
+		<a href="{{ url_for("mail.index") }}" class="btn btn-secondary">{{_('Cancel')}}</a>
 		{% if mail.uid %}
-			<a href="{{ url_for("mail.delete", uid=mail.uid) }}" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i> Delete</a>
+			<a href="{{ url_for("mail.delete", uid=mail.uid) }}" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i> {{_('Delete')}}</a>
 		{% else %}
-			<a href="#" class="btn btn-danger disabled"><i class="fa fa-trash" aria-hidden="true"></i> Delete</a>
+			<a href="#" class="btn btn-danger disabled"><i class="fa fa-trash" aria-hidden="true"></i> {{_('Delete')}}</a>
 		{% endif %}
 	</div>
 </div>
diff --git a/uffd/mail/views.py b/uffd/mail/views.py
index 6ba83d10..0e5948d6 100644
--- a/uffd/mail/views.py
+++ b/uffd/mail/views.py
@@ -1,4 +1,5 @@
 from flask import Blueprint, render_template, request, url_for, redirect, flash, current_app
+from flask_babel import gettext as _, lazy_gettext
 
 from uffd.navbar import register_navbar
 from uffd.csrf import csrf_protect
@@ -19,7 +20,7 @@ def mail_acl_check():
 	return request.user and request.user.is_in_group(current_app.config['ACL_ADMIN_GROUP'])
 
 @bp.route("/")
-@register_navbar('Mail', icon='envelope', blueprint=bp, visible=mail_acl_check)
+@register_navbar(lazy_gettext('Forwardings'), icon='envelope', blueprint=bp, visible=mail_acl_check)
 def index():
 	return render_template('mail/list.html', mails=Mail.query.all())
 
@@ -43,7 +44,7 @@ def update(uid=None):
 	mail.destinations = request.form.get('mail-destinations', '').splitlines()
 	ldap.session.add(mail)
 	ldap.session.commit()
-	flash('Mail mapping updated.')
+	flash(_('Mail mapping updated.'))
 	return redirect(url_for('mail.show', uid=mail.uid))
 
 @bp.route("/<uid>/del")
@@ -52,5 +53,5 @@ def delete(uid):
 	mail = Mail.query.filter_by(uid=uid).first_or_404()
 	ldap.session.delete(mail)
 	ldap.session.commit()
-	flash('Deleted mail mapping.')
+	flash(_('Deleted mail mapping.'))
 	return redirect(url_for('mail.index'))
diff --git a/uffd/oauth2/templates/oauth2/logout.html b/uffd/oauth2/templates/oauth2/logout.html
index abddfe68..f048ccdf 100644
--- a/uffd/oauth2/templates/oauth2/logout.html
+++ b/uffd/oauth2/templates/oauth2/logout.html
@@ -7,14 +7,14 @@
 			<img alt="branding logo" src="{{ config.get("BRANDING_LOGO_URL") }}" class="col-lg-8 col-md-12" >
 		</div>
 		<div class="col-12">
-			<h2 class="text-center">Logout</h2>
+			<h2 class="text-center">{{_('Logout')}}</h2>
 		</div>
 
 		<div class="col-12">
 			<noscript>
-				<div class="alert alert-warning" role="alert">Javascript is required for automatic logout</div>
+				<div class="alert alert-warning" role="alert">{{_('Javascript is required for automatic logout')}}</div>
 			</noscript>
-			<p>While you successfully logged out of the Single-Sign-On service, you may still be logged in on these services:</p>
+			<p>{{_('While you successfully logged out of the Single-Sign-On service, you may still be logged in on these services:')}}</p>
 			<ul>
 			{% for client in clients if client.logout_urls %}
 				<li class="client" data-urls='{{ client.logout_urls|tojson }}'>
@@ -27,15 +27,15 @@
 			</ul>
 
 			<p>
-				Please wait until you have been automatically logged out of all services or make sure of this yourself.
+				{{_('Please wait until you have been automatically logged out of all services or make sure of this yourself.')}}
 			</p>
 
 			<button id="retry-button" class="btn btn-block btn-primary d-none" disabled>
-				<span id="cont-text">Logging you out on all services ...</span>
+				<span id="cont-text">{{_('Logging you out on all services ...')}}</span>
 			</button>
 
 			<a href="{{ request.values.get('ref') or '/' }}" class="btn btn-block btn-secondary">
-				<span>Skip this and continue</span>
+				<span>{{_('Skip this and continue')}}</span>
 			</a>
 
 		</div>
@@ -79,11 +79,11 @@ function logout_services() {
 			if (result.status == 'rejected')
 				throw result.reason;
 		}
-		$('#cont-text').text('Done, redirecting ...');
+		$('#cont-text').text({{_('Done, redirecting ...')|tojson}});
 		window.location = {{ (request.values.get('ref') or '/')|tojson }};
 	}).catch(function (err) {
 		$("#retry-button").prop('disabled', false);
-		$('#cont-text').text('Log out failed on some services. Retry?');
+		$('#cont-text').text({{_('Log out failed on some services. Retry?')|tojson}});
 	});
 }
 
diff --git a/uffd/oauth2/views.py b/uffd/oauth2/views.py
index ed39aecd..c3c33af0 100644
--- a/uffd/oauth2/views.py
+++ b/uffd/oauth2/views.py
@@ -4,6 +4,7 @@ import urllib.parse
 
 from flask import Blueprint, request, jsonify, render_template, session, redirect, url_for, flash
 from flask_oauthlib.provider import OAuth2Provider
+from flask_babel import gettext as _
 from sqlalchemy.exc import IntegrityError
 
 from uffd.ratelimit import host_ratelimit, format_delay
@@ -91,7 +92,7 @@ def authorize(*args, **kwargs): # pylint: disable=unused-argument
 		del session['devicelogin_started']
 		host_delay = host_ratelimit.get_delay()
 		if host_delay:
-			flash('We received too many requests from your ip address/network! Please wait at least %s.'%format_delay(host_delay))
+			flash(_('We received too many requests from your ip address/network! Please wait at least %(delay)s.', delay=format_delay(host_delay)))
 			return redirect(url_for('session.login', ref=request.full_path, devicelogin=True))
 		host_ratelimit.log()
 		initiation = OAuth2DeviceLoginInitiation(oauth2_client_id=client.client_id)
@@ -99,7 +100,7 @@ def authorize(*args, **kwargs): # pylint: disable=unused-argument
 		try:
 			db.session.commit()
 		except IntegrityError:
-			flash('Device login is currently not available. Try again later!')
+			flash(_('Device login is currently not available. Try again later!'))
 			return redirect(url_for('session.login', ref=request.values['ref'], devicelogin=True))
 		session['devicelogin_id'] = initiation.id
 		session['devicelogin_secret'] = initiation.secret
diff --git a/uffd/ratelimit.py b/uffd/ratelimit.py
index 86bc2f0b..46c97e42 100644
--- a/uffd/ratelimit.py
+++ b/uffd/ratelimit.py
@@ -3,6 +3,7 @@ import ipaddress
 import math
 
 from flask import request
+from flask_babel import gettext as _
 from sqlalchemy import Column, Integer, String, DateTime
 
 from uffd.database import db
@@ -66,16 +67,16 @@ class HostRatelimit(Ratelimit):
 
 def format_delay(seconds):
 	if seconds <= 15:
-		return 'a few seconds'
+		return _('a few seconds')
 	if seconds <= 30:
-		return '30 seconds'
+		return _('30 seconds')
 	if seconds <= 60:
-		return 'one minute'
+		return _('one minute')
 	if seconds < 3000:
-		return '%d minutes'%(math.ceil(seconds/60)+1)
+		return _('%(minutes)d minutes', minutes=(math.ceil(seconds/60)+1))
 	if seconds <= 3600:
-		return 'one hour'
-	return '%d hours'%math.ceil(seconds/3600)
+		return _('one hour')
+	return _('%(hours)d hours', hours=math.ceil(seconds/3600))
 
 # Global host-based ratelimit
 host_ratelimit = HostRatelimit('host', 1*60*60, 25)
diff --git a/uffd/role/templates/role/list.html b/uffd/role/templates/role/list.html
index 76e84301..c4825971 100644
--- a/uffd/role/templates/role/list.html
+++ b/uffd/role/templates/role/list.html
@@ -11,20 +11,16 @@
 		<table class="table table-striped table-sm">
 			<thead>
 				<tr>
-					<th scope="col">{{_("roleid")}}</th>
-					<th scope="col">{{_("name")}}</th>
-					<th scope="col">{{_("description")}}</th>
+					<th scope="col">{{_("Name")}}</th>
+					<th scope="col">{{_("Description")}}</th>
 				</tr>
 			</thead>
 			<tbody>
 				{% for role in roles|sort(attribute="name") %}
 				<tr id="role-{{ role.id }}">
-					<td>
-						{{ role.id }}
-					</td>
 					<th scope="row">
 						<a href="{{ url_for("role.show", roleid=role.id) }}">
-							{{ role.name or '<empty name>' }}
+							{{ role.name or _('<empty name>') }}
 						</a>
 					</th>
 					<td>
diff --git a/uffd/role/templates/role/show.html b/uffd/role/templates/role/show.html
index d46bb429..09315fd5 100644
--- a/uffd/role/templates/role/show.html
+++ b/uffd/role/templates/role/show.html
@@ -14,11 +14,11 @@
 		<a href="{{ url_for("role.index") }}" class="btn btn-secondary">{{_("Cancel")}}</a>
 		{% if role.id %}
 			{% if not role.is_default %}
-			<a href="{{ url_for("role.set_default", roleid=role.id) }}" onClick="return confirm('All non-service users will be removed as members from this role and get its permissions implicitly. Are you sure?');" class="btn btn-secondary">{{_("Set as default")}}</a>
+			<a href="{{ url_for("role.set_default", roleid=role.id) }}" onClick="return confirm({{_('All non-service users will be removed as members from this role and get its permissions implicitly. Are you sure?')|tojson}});" class="btn btn-secondary">{{_("Set as default")}}</a>
 			{% else %}
-			<a href="{{ url_for("role.unset_default", roleid=role.id) }}" onClick="return confirm('Are you sure?');" class="btn btn-secondary">{{_("Unset as default")}}</a>
+			<a href="{{ url_for("role.unset_default", roleid=role.id) }}" onClick="return confirm({{_('Are you sure?')|tojson}});" class="btn btn-secondary">{{_("Unset as default")}}</a>
 			{% endif %}
-			<a href="{{ url_for("role.delete", roleid=role.id) }}"  onClick="return confirm('Are you sure?');" class="btn btn-danger {{ 'disabled' if role.locked }}"><i class="fa fa-trash" aria-hidden="true"></i> {{_("Delete")}}</a>
+			<a href="{{ url_for("role.delete", roleid=role.id) }}"  onClick="return confirm({{_('Are you sure?')|tojson}});" class="btn btn-danger {{ 'disabled' if role.locked }}"><i class="fa fa-trash" aria-hidden="true"></i> {{_("Delete")}}</a>
 		{% else %}
 			<a href="#" class="btn btn-secondary disabled">{{_("Set as default")}}</a>
 			<a href="#" class="btn btn-danger disabled"><i class="fa fa-trash" aria-hidden="true"></i> {{_("Delete")}}</a>
diff --git a/uffd/rolemod/templates/rolemod/list.html b/uffd/rolemod/templates/rolemod/list.html
index f727ba6c..c43826aa 100644
--- a/uffd/rolemod/templates/rolemod/list.html
+++ b/uffd/rolemod/templates/rolemod/list.html
@@ -6,8 +6,8 @@
 		<table class="table table-striped table-sm">
 			<thead>
 				<tr>
-					<th scope="col">name</th>
-					<th scope="col">description</th>
+					<th scope="col">{{_('Name')}}</th>
+					<th scope="col">{{_('Description')}}</th>
 				</tr>
 			</thead>
 			<tbody>
diff --git a/uffd/rolemod/templates/rolemod/show.html b/uffd/rolemod/templates/rolemod/show.html
index 72863d91..fbb28c06 100644
--- a/uffd/rolemod/templates/rolemod/show.html
+++ b/uffd/rolemod/templates/rolemod/show.html
@@ -5,31 +5,31 @@
 <form method="POST" action="{{ url_for("rolemod.update", role_id=role.id) }}">
 	<div class="float-sm-right pb-2">
 		{% if config['ENABLE_INVITE'] %}
-		<a href="{{ url_for("invite.new", **{"role-%d"%role.id: 1}) }}" class="btn btn-primary mr-2"><i class="fa fa-link" aria-hidden="true"></i> Invite Members</a>
+		<a href="{{ url_for("invite.new", **{"role-%d"%role.id: 1}) }}" class="btn btn-primary mr-2"><i class="fa fa-link" aria-hidden="true"></i> {{_('Invite Members')}}</a>
 		{% endif %}
-		<button type="submit" class="btn btn-primary"><i class="fa fa-save" aria-hidden="true"></i> Save</button>
-		<a href="{{ url_for("rolemod.index") }}" class="btn btn-secondary">Cancel</a>
+		<button type="submit" class="btn btn-primary"><i class="fa fa-save" aria-hidden="true"></i> {{_('Save')}}</button>
+		<a href="{{ url_for("rolemod.index") }}" class="btn btn-secondary">{{_('Cancel')}}</a>
 	</div>
 	<ul class="nav nav-tabs pt-2 border-0" id="tablist" role="tablist">
 		<li class="nav-item">
-			<a class="nav-link active" id="overview-tab" data-toggle="tab" href="#overview" role="tab" aria-controls="overview" aria-selected="true">Overview</a>
+			<a class="nav-link active" id="overview-tab" data-toggle="tab" href="#overview" role="tab" aria-controls="overview" aria-selected="true">{{_('Overview')}}</a>
 		</li>
 		<li class="nav-item">
-			<a class="nav-link" id="members-tab" data-toggle="tab" href="#members" role="tab" aria-controls="members" aria-selected="false">Members <span class="badge badge-pill badge-secondary">{{ role.members|length }}</span></a>
+			<a class="nav-link" id="members-tab" data-toggle="tab" href="#members" role="tab" aria-controls="members" aria-selected="false">{{_('Members')}} <span class="badge badge-pill badge-secondary">{{ role.members|length }}</span></a>
 		</li>
 	</ul>
 	<div class="tab-content border mb-2 pt-2" id="tabcontent">
 		<div class="tab-pane fade show active" id="overview" role="tabpanel" aria-labelledby="overview-tab">
 			<div class="form-group col">
-				<label for="role-name">Role Name</label>
+				<label for="role-name">{{_('Role name')}}</label>
 				<input type="text" class="form-control" id="role-name" value="{{ role.name }}" readonly>
 			</div>
 			<div class="form-group col">
-				<label for="role-description">Description</label>
+				<label for="role-description">{{_('Description')}}</label>
 				<textarea class="form-control" id="role-description" rows="5" name="description">{{ role.description }}</textarea>
 			</div>
 			<div class="form-group col">
-				<label>Moderators:</label>
+				<label>{{_('Moderators:')}}</label>
 				<ul>
 					{% for moderator in role.moderator_group.members %}
 					<li>{{ moderator.displayname }} ({{ moderator.loginname }})</li>
@@ -39,11 +39,11 @@
 		</div>
 		<div class="tab-pane fade" id="members" role="tabpanel" aria-labelledby="members-tab">
 			<div class="col">
-				<span>Role members:</span>
+				<span>{{_('Role members:')}}</span>
 				<table class="table table-striped table-sm">
 					<thead>
 						<tr>
-							<th scope="col">Name</th>
+							<th scope="col">{{_('Name')}}</th>
 							<th scope="col"></th>
 						</tr>
 					</thead>
@@ -52,7 +52,7 @@
 						<tr>
 							<td>{{ member.displayname }} ({{ member.loginname }})</td>
 							<td class="text-right">
-								<a class="btn btn-danger py-0" href="{{ url_for('rolemod.delete_member', role_id=role.id, member_dn=member.dn) }}">Remove</a>
+								<a class="btn btn-danger py-0" href="{{ url_for('rolemod.delete_member', role_id=role.id, member_dn=member.dn) }}">{{_('Remove')}}</a>
 							</td>
 						</tr>
 						{% endfor %}
diff --git a/uffd/rolemod/views.py b/uffd/rolemod/views.py
index d9c5f834..8a9c6051 100644
--- a/uffd/rolemod/views.py
+++ b/uffd/rolemod/views.py
@@ -1,4 +1,5 @@
 from flask import Blueprint, render_template, request, url_for, redirect, flash
+from flask_babel import gettext as _, lazy_gettext
 
 from uffd.navbar import register_navbar
 from uffd.csrf import csrf_protect
@@ -21,7 +22,7 @@ def acl_check(): #pylint: disable=inconsistent-return-statements
 		return redirect(url_for('index'))
 
 @bp.route("/")
-@register_navbar('Moderation', icon='user-lock', blueprint=bp, visible=user_is_rolemod)
+@register_navbar(lazy_gettext('Moderation'), icon='user-lock', blueprint=bp, visible=user_is_rolemod)
 def index():
 	roles = Role.query.filter(Role.moderator_group_dn.in_(request.user.group_dns)).all()
 	return render_template('rolemod/list.html', roles=roles)
@@ -32,7 +33,7 @@ def show(role_id):
 	User.query.all()
 	role = Role.query.get_or_404(role_id)
 	if role.moderator_group not in request.user.groups:
-		flash('Access denied')
+		flash(_('Access denied'))
 		return redirect(url_for('index'))
 	return render_template('rolemod/show.html', role=role)
 
@@ -41,11 +42,11 @@ def show(role_id):
 def update(role_id):
 	role = Role.query.get_or_404(role_id)
 	if role.moderator_group not in request.user.groups:
-		flash('Access denied')
+		flash(_('Access denied'))
 		return redirect(url_for('index'))
 	if request.form['description'] != role.description:
 		if len(request.form['description']) > 256:
-			flash('Description too long')
+			flash(_('Description too long'))
 			return redirect(url_for('.show', role_id=role.id))
 		role.description = request.form['description']
 	db.session.commit()
@@ -56,12 +57,12 @@ def update(role_id):
 def delete_member(role_id, member_dn):
 	role = Role.query.get_or_404(role_id)
 	if role.moderator_group not in request.user.groups:
-		flash('Access denied')
+		flash(_('Access denied'))
 		return redirect(url_for('index'))
 	member = User.query.get_or_404(member_dn)
 	role.members.discard(member)
 	member.update_groups()
 	ldap.session.commit()
 	db.session.commit()
-	flash('Member removed')
+	flash(_('Member removed'))
 	return redirect(url_for('.show', role_id=role.id))
diff --git a/uffd/selfservice/templates/selfservice/self.html b/uffd/selfservice/templates/selfservice/self.html
index 66c9d677..49d1c7a9 100644
--- a/uffd/selfservice/templates/selfservice/self.html
+++ b/uffd/selfservice/templates/selfservice/self.html
@@ -13,7 +13,7 @@
 	<div class="col-12 col-md-5">
 		<h5>{{_("Profile")}}</h5>
 		<p>{{_("Your profile information is used by all services that are integrated into the Single-Sign-On. Your e-mail address is also used for password recovery.")}}</p>
-		<p>{{_(" Changes may take serveral minutes to be visible in all services.")}}</p>
+		<p>{{_("Changes may take serveral minutes to be visible in all services.")}}</p>
 	</div>
 	<div class="col-12 col-md-7">
 		<form class="form" action="{{ url_for("selfservice.update_profile") }}" method="POST">
@@ -55,7 +55,7 @@
 			<div class="form-group">
 				<input type="password" class="form-control" id="user-password1" name="password1" placeholder="{{_("New Password")}}" required>
 				<small class="form-text text-muted">
-					At least 8 and at most 256 characters, no other special requirements.
+					{{_('At least 8 and at most 256 characters, no other special requirements.')}}
 				</small>
 			</div>
 			<div class="form-group">
@@ -120,7 +120,7 @@
 					<td>{{ role.description }}</td>
 					<td>
 						{% if config['ENABLE_ROLESELFSERVICE'] %}
-						<form action="{{ url_for("selfservice.leave_role", roleid=role.id) }}" method="POST" onsubmit="return confirm('Are you sure?');">
+						<form action="{{ url_for("selfservice.leave_role", roleid=role.id) }}" method="POST" onsubmit="return confirm({{_('Are you sure?')|tojson}});">
 							<button type="submit" class="btn btn-sm btn-danger float-right">{{_("Leave")}}</button>
 						</form>
 						{% endif %}
diff --git a/uffd/selfservice/views.py b/uffd/selfservice/views.py
index fadc8fea..de79999a 100644
--- a/uffd/selfservice/views.py
+++ b/uffd/selfservice/views.py
@@ -74,13 +74,13 @@ def forgot_password():
 	host_delay = host_ratelimit.get_delay()
 	if reset_delay or host_delay:
 		if reset_delay > host_delay:
-			flash('We received too many password reset requests for this user! Please wait at least %s.'%format_delay(reset_delay))
+			flash(_('We received too many password reset requests for this user! Please wait at least %(delay)s.', delay=format_delay(reset_delay)))
 		else:
-			flash('We received too many requests from your ip address/network! Please wait at least %s.'%format_delay(host_delay))
+			flash(_('We received too many requests from your ip address/network! Please wait at least %(delay)s.', delay=format_delay(host_delay)))
 		return redirect(url_for('.forgot_password'))
 	reset_ratelimit.log(loginname+'/'+mail)
 	host_ratelimit.log()
-	flash("We sent a mail to this users mail address if you entered the correct mail and login name combination")
+	flash(_("We sent a mail to this user's mail address if you entered the correct mail and login name combination"))
 	user = User.query.filter_by(loginname=loginname).one_or_none()
 	if user and user.mail == mail:
 		send_passwordreset(user)
@@ -90,7 +90,7 @@ def forgot_password():
 def token_password(token):
 	dbtoken = PasswordToken.query.get(token)
 	if not dbtoken or dbtoken.created < (datetime.datetime.now() - datetime.timedelta(days=2)):
-		flash('Token expired, please try again.')
+		flash(_('Token expired, please try again.'))
 		if dbtoken:
 			db.session.delete(dbtoken)
 			db.session.commit()
@@ -98,17 +98,17 @@ def token_password(token):
 	if request.method == 'GET':
 		return render_template('selfservice/set_password.html', token=token)
 	if not request.values['password1']:
-		flash('You need to set a password, please try again.')
+		flash(_('You need to set a password, please try again.'))
 		return render_template('selfservice/set_password.html', token=token)
 	if not request.values['password1'] == request.values['password2']:
-		flash('Passwords do not match, please try again.')
+		flash(_('Passwords do not match, please try again.'))
 		return render_template('selfservice/set_password.html', token=token)
 	user = User.query.filter_by(loginname=dbtoken.loginname).one()
 	if not user.set_password(request.values['password1']):
-		flash('Password ist not valid, please try again.')
+		flash(_('Password ist not valid, please try again.'))
 		return render_template('selfservice/set_password.html', token=token)
 	db.session.delete(dbtoken)
-	flash('New password set')
+	flash(_('New password set'))
 	ldap.session.commit()
 	db.session.commit()
 	return redirect(url_for('session.login'))
@@ -118,7 +118,7 @@ def token_password(token):
 def token_mail(token):
 	dbtoken = MailToken.query.get(token)
 	if not dbtoken or dbtoken.created < (datetime.datetime.now() - datetime.timedelta(days=2)):
-		flash('Token expired, please try again.')
+		flash(_('Token expired, please try again.'))
 		if dbtoken:
 			db.session.delete(dbtoken)
 			db.session.commit()
@@ -126,7 +126,7 @@ def token_mail(token):
 
 	user = User.query.filter_by(loginname=dbtoken.loginname).one()
 	user.set_mail(dbtoken.newmail)
-	flash('New mail set')
+	flash(_('New mail set'))
 	db.session.delete(dbtoken)
 	ldap.session.commit()
 	db.session.commit()
@@ -137,14 +137,14 @@ def token_mail(token):
 @login_required()
 def leave_role(roleid):
 	if not current_app.config['ENABLE_ROLESELFSERVICE']:
-		flash('Leaving roles is disabled')
+		flash(_('Leaving roles is disabled'))
 		return redirect(url_for('selfservice.index'))
 	role = Role.query.get_or_404(roleid)
 	role.members.discard(request.user)
 	request.user.update_groups()
 	ldap.session.commit()
 	db.session.commit()
-	flash('You left role "%s"'%role.name)
+	flash(_('You left role "%(role_name)s"', role_name=role.name))
 	return redirect(url_for('selfservice.index'))
 
 def send_mail_verification(loginname, newmail):
@@ -208,5 +208,5 @@ def send_mail(to_address, msg):
 			current_app.last_mail = msg
 		return True
 	except smtplib.SMTPException:
-		flash('Mail to "{}" could not be sent!'.format(to_address))
+		flash(_('Mail to "%(mail_address)s" could not be sent!', mail_address=to_address))
 		return False
diff --git a/uffd/session/templates/session/deviceauth.html b/uffd/session/templates/session/deviceauth.html
index 09eb76f2..382cf74a 100644
--- a/uffd/session/templates/session/deviceauth.html
+++ b/uffd/session/templates/session/deviceauth.html
@@ -14,13 +14,13 @@
 			<img alt="branding logo" src="{{ config.get("BRANDING_LOGO_URL") }}" class="col-lg-8 col-md-12" >
 		</div>
 		<div class="col-12">
-			<h2 class="text-center">Authorize Device Login</h2>
+			<h2 class="text-center">{{_('Authorize Device Login')}}</h2>
 		</div>
 		<div class="form-group col-12">
-			<p>Log into a service on another device without entering your password.</p>
+			<p>{{_('Log into a service on another device without entering your password.')}}</p>
 		</div>
 		<div class="form-group col-12">
-			<label for="initiation-code">Initiation Code</label>
+			<label for="initiation-code">{{_('Initiation Code')}}</label>
 			{% if not initiation %}
 				<input type="text" class="form-control" id="initiation-code" name="initiation-code" value="{{ initiation_code or '' }}" required="required" tabindex = "1" autofocus>
 			{% else %}
@@ -29,36 +29,36 @@
 		</div>
 		{% if confirmation %}
 			<div class="form-group col-12">
-				<label for="confirmation-code">Confirmation Code</label>
+				<label for="confirmation-code">{{_('Confirmation Code')}}</label>
 				<input type="text" class="form-control" id="confirmation-code" name="confirmation-code" value="{{ confirmation.code }}" readonly>
 			</div>
 		{% endif %}
 		{% if not initiation %}
 			<div class="form-group col-12">
-				<p>Start logging into a service on the other device and chose "Device Login" on the login page. Enter the displayed initiation code in the box above.</p>
+				<p>{{_('Start logging into a service on the other device and chose "Device Login" on the login page. Enter the displayed initiation code in the box above.')}}</p>
 			</div>
 			<div class="form-group col-12">
-				<button type="submit" class="btn btn-primary btn-block" tabindex = "2">Continue</button>
+				<button type="submit" class="btn btn-primary btn-block" tabindex = "2">{{_('Continue')}}</button>
 			</div>
 			<div class="form-group col-12">
-				<a href="{{ url_for('index') }}" class="btn btn-secondary btn-block" tabindex="0">Cancel</a>
+				<a href="{{ url_for('index') }}" class="btn btn-secondary btn-block" tabindex="0">{{_('Cancel')}}</a>
 			</div>
 		{% elif not confirmation %}
 			<div class="form-group col-12">
-				<p>Authorize the login for service <b>{{ initiation.description }}</b>?</p>
+				<p>{{_('Authorize the login for service <b>%(service_name)s</b>?', service_name=initiation.description|e)|safe}}</p>
 			</div>
 			<div class="form-group col-12">
-				<button type="submit" class="btn btn-primary btn-block" tabindex = "2">Authorize Login</button>
+				<button type="submit" class="btn btn-primary btn-block" tabindex = "2">{{_('Authorize Login')}}</button>
 			</div>
 			<div class="form-group col-12">
-				<a href="{{ url_for('index') }}" class="btn btn-secondary btn-block" tabindex="0">Cancel</a>
+				<a href="{{ url_for('index') }}" class="btn btn-secondary btn-block" tabindex="0">{{_('Cancel')}}</a>
 			</div>
 		{% else %}
 			<div class="form-group col-12">
-				<p>Enter the confirmation code on the other device and complete the login. Click <em>Finish</em> afterwards.</p>
+				<p>{{_('Enter the confirmation code on the other device and complete the login. Click <em>Finish</em> afterwards.')|safe}}</p>
 			</div>
 			<div class="form-group col-12">
-				<button type="submit" class="btn btn-primary btn-block" tabindex = "2">Finish</button>
+				<button type="submit" class="btn btn-primary btn-block" tabindex = "2">{{_('Finish')}}</button>
 			</div>
 		{% endif %}
 	</div>
diff --git a/uffd/session/templates/session/devicelogin.html b/uffd/session/templates/session/devicelogin.html
index 4bd92b15..62578d05 100644
--- a/uffd/session/templates/session/devicelogin.html
+++ b/uffd/session/templates/session/devicelogin.html
@@ -8,30 +8,30 @@
 			<img alt="branding logo" src="{{ config.get("BRANDING_LOGO_URL") }}" class="col-lg-8 col-md-12" >
 		</div>
 		<div class="col-12">
-			<h2 class="text-center">Device Login</h2>
+			<h2 class="text-center">{{_('Device Login')}}</h2>
 		</div>
 		<div class="form-group col-12">
-			<p>Use a login session on another device (e.g. your laptop) to log into a service without entering your password.</p>
+			<p>{{_('Use a login session on another device (e.g. your laptop) to log into a service without entering your password.')}}</p>
 		</div>
 		{% if initiation %}
 		<div class="form-group col-12">
-			<label for="initiation-code">Initiation Code</label>
+			<label for="initiation-code">{{_('Initiation Code')}}</label>
 			<input type="text" class="form-control" id="initiation-code" name="initiation-code" value="{{ initiation.code }}" readonly>
 		</div>
 		<input type="hidden" class="form-control" id="initiation-secret" name="initiation-secret" value="{{ initiation.secret }}">
 		<div class="form-group col-12">
-			<label for="confirmation-code">Confirmation Code</label>
+			<label for="confirmation-code">{{_('Confirmation Code')}}</label>
 			<input type="text" class="form-control" id="confirmation-code" name="confirmation-code" required="required" tabindex = "1" autofocus>
 		</div>
 		<div class="form-group col-12">
-			<p>Open <code><a href="{{ url_for('session.deviceauth') }}">{{ url_for('session.deviceauth', _external=True) }}</a></code> on the other device and enter the initiation code there. Then enter the confirmation code in the box above.</p>
+			<p>{{_('Open <code><a href="%(deviceauth_url)s">%(deviceauth_url)s</a></code> on the other device and enter the initiation code there. Then enter the confirmation code in the box above.', deviceauth_url=url_for('session.deviceauth', _external=True)|e)|safe}}</p>
 		</div>
 		<div class="form-group col-12">
-			<button type="submit" class="btn btn-primary btn-block" tabindex = "3">Continue</button>
+			<button type="submit" class="btn btn-primary btn-block" tabindex = "3">{{_('Continue')}}</button>
 		</div>
 		{% endif %}
 		<div class="form-group col-12">
-			<a href="{{ url_for('session.login', ref=ref, devicelogin=True) }}" class="btn btn-secondary btn-block" tabindex="0">Cancel</a>
+			<a href="{{ url_for('session.login', ref=ref, devicelogin=True) }}" class="btn btn-secondary btn-block" tabindex="0">{{_('Cancel')}}</a>
 		</div>
 	</div>
 </div>
diff --git a/uffd/session/templates/session/login.html b/uffd/session/templates/session/login.html
index 7f22b3db..f542802b 100644
--- a/uffd/session/templates/session/login.html
+++ b/uffd/session/templates/session/login.html
@@ -8,10 +8,10 @@
 			<img alt="branding logo" src="{{ config.get("BRANDING_LOGO_URL") }}" class="col-lg-8 col-md-12" >
 		</div>
 		<div class="col-12">
-			<h2 class="text-center">Login</h2>
+			<h2 class="text-center">{{_("Login")}}</h2>
 		</div>
 		<div class="form-group col-12">
-			<label for="user-loginname">{{ _("Login Name") }}</label>
+			<label for="user-loginname">{{_("Login Name")}}</label>
 			<input type="text" class="form-control" id="user-loginname" name="loginname" required="required" tabindex = "1" autofocus>
 		</div>
 		<div class="form-group col-12">
@@ -22,9 +22,9 @@
 			<button type="submit" class="btn btn-primary btn-block" tabindex = "3">{{_("Login")}}</button>
 		</div>
 		{% if request.values.get('devicelogin') %}
-		<div class="text-center text-muted mb-3">- or -</div>
+		<div class="text-center text-muted mb-3">{{_("- or -")}}</div>
 		<div class="form-group col-12">
-			<a href="{{ url_for('session.devicelogin_start', ref=ref) }}" class="btn btn-primary btn-block" tabindex="0">Device Login</a>
+			<a href="{{ url_for('session.devicelogin_start', ref=ref) }}" class="btn btn-primary btn-block" tabindex="0">{{_("Device Login")}}</a>
 		</div>
 		{% endif %}
 		<div class="clearfix col-12">
diff --git a/uffd/session/views.py b/uffd/session/views.py
index b558b274..cde2a3fd 100644
--- a/uffd/session/views.py
+++ b/uffd/session/views.py
@@ -88,9 +88,9 @@ def login():
 	host_delay = host_ratelimit.get_delay()
 	if login_delay or host_delay:
 		if login_delay > host_delay:
-			flash(_('We received too many invalid login attempts for this user! Please wait at least %s.')%format_delay(login_delay))
+			flash(_('We received too many invalid login attempts for this user! Please wait at least %(delay)s.', delay=format_delay(login_delay)))
 		else:
-			flash(_('We received too many requests from your ip address/network! Please wait at least %s.')%format_delay(host_delay))
+			flash(_('We received too many requests from your ip address/network! Please wait at least %(delay)s.', delay=format_delay(host_delay)))
 		return render_template('session/login.html', ref=request.values.get('ref'))
 	user = login_get_user(username, password)
 	if user is None:
@@ -144,7 +144,7 @@ def devicelogin():
 		return redirect(url_for('session.login', ref=request.values['ref'], devicelogin=True))
 	initiation = DeviceLoginInitiation.query.filter_by(id=session['devicelogin_id'], secret=session['devicelogin_secret']).one_or_none()
 	if not initiation or initiation.expired:
-		flash('Initiation code is no longer valid')
+		flash(_('Initiation code is no longer valid'))
 		return redirect(url_for('session.login', ref=request.values['ref'], devicelogin=True))
 	return render_template('session/devicelogin.html', ref=request.values.get('ref'), initiation=initiation)
 
@@ -154,11 +154,11 @@ def devicelogin_submit():
 		return redirect(url_for('session.login', ref=request.values['ref'], devicelogin=True))
 	initiation = DeviceLoginInitiation.query.filter_by(id=session['devicelogin_id'], secret=session['devicelogin_secret']).one_or_none()
 	if not initiation or initiation.expired:
-		flash('Initiation code is no longer valid')
+		flash(_('Initiation code is no longer valid'))
 		return redirect(url_for('session.login', ref=request.values['ref'], devicelogin=True))
 	confirmation = DeviceLoginConfirmation.query.filter_by(initiation=initiation, code=request.form['confirmation-code']).one_or_none()
 	if confirmation is None:
-		flash('Invalid confirmation code')
+		flash(_('Invalid confirmation code'))
 		return render_template('session/devicelogin.html', ref=request.values.get('ref'), initiation=initiation)
 	session['devicelogin_confirmation'] = confirmation.id
 	return secure_local_redirect(request.values['ref'])
@@ -170,7 +170,7 @@ def deviceauth():
 		return render_template('session/deviceauth.html')
 	initiation = DeviceLoginInitiation.query.filter_by(code=request.values['initiation-code']).one_or_none()
 	if initiation is None or initiation.expired:
-		flash('Invalid initiation code')
+		flash(_('Invalid initiation code'))
 		return redirect(url_for('session.deviceauth'))
 	return render_template('session/deviceauth.html', initiation=initiation)
 
@@ -181,7 +181,7 @@ def deviceauth_submit():
 	DeviceLoginConfirmation.query.filter_by(user_dn=request.user.dn).delete()
 	initiation = DeviceLoginInitiation.query.filter_by(code=request.form['initiation-code']).one_or_none()
 	if initiation is None or initiation.expired:
-		flash('Invalid initiation code')
+		flash(_('Invalid initiation code'))
 		return redirect(url_for('session.deviceauth'))
 	confirmation = DeviceLoginConfirmation(user=request.user, initiation=initiation)
 	db.session.add(confirmation)
diff --git a/uffd/signup/templates/signup/confirm.html b/uffd/signup/templates/signup/confirm.html
index 9e235523..dc81ba7f 100644
--- a/uffd/signup/templates/signup/confirm.html
+++ b/uffd/signup/templates/signup/confirm.html
@@ -8,17 +8,17 @@
 			<img alt="branding logo" src="{{ config.get("BRANDING_LOGO_URL") }}" class="col-lg-8 col-md-12" >
 		</div>
 		<div class="col-12">
-			<h2 class="text-center">Complete Registration</h2>
+			<h2 class="text-center">{{_('Complete Registration')}}</h2>
 		</div>
 		{% if error %}
 		<div class="alert alert-danger" role="alert">{{ error }}</div>
 		{% endif %}
 		<div class="form-group col-12">
-			<label for="user-password1">Please enter your password to complete the account registration</label>
+			<label for="user-password1">{{_('Please enter your password to complete the account registration')}}</label>
 			<input type="password" class="form-control" id="user-password1" name="password" required="required">
 		</div>
 		<div class="form-group col-12">
-			<button type="submit" class="btn btn-primary btn-block">Complete Account Registration</button>
+			<button type="submit" class="btn btn-primary btn-block">{{_('Complete Account Registration')}}</button>
 		</div>
 	</div>
 </div>
diff --git a/uffd/signup/templates/signup/start.html b/uffd/signup/templates/signup/start.html
index abb225e9..fd96d80f 100644
--- a/uffd/signup/templates/signup/start.html
+++ b/uffd/signup/templates/signup/start.html
@@ -8,7 +8,7 @@
 			<img alt="branding logo" src="{{ config.get("BRANDING_LOGO_URL") }}" class="col-lg-8 col-md-12" >
 		</div>
 		<div class="col-12">
-			<h2 class="text-center">Account Registration</h2>
+			<h2 class="text-center">{{_('Account Registration')}}</h2>
 		</div>
 		{% if error %}
 		<div class="form-group col-12">
@@ -16,45 +16,45 @@
 		</div>
 		{% endif %}
 		<div class="form-group col-12">
-			<label for="user-loginname">Login Name</label>
+			<label for="user-loginname">{{_('Login Name')}}</label>
 			<div class="js-only-input-group">
 				<input type="text" class="form-control" id="user-loginname" name="loginname" aria-describedby="loginname-feedback" value="{{ request.form.loginname }}" minlength=1 maxlength=32 pattern="[a-z0-9_-]*" required>
 				<div class="js-only-input-group-append d-none">
-					<button class="btn btn-outline-secondary rounded-right" type="button" id="check-loginname">Check</button>
+					<button class="btn btn-outline-secondary rounded-right" type="button" id="check-loginname">{{_('Check')}}</button>
 				</div>
-				<div id="loginname-feedback" class="invalid-feedback">foobar</div>
+				<div id="loginname-feedback" class="invalid-feedback"></div>
 			</div>
 			<small class="form-text text-muted">
-				At least one and at most 32 lower-case characters, digits, dashes ("-") or underscores ("_"). <b>Cannot be changed later!</b>
+				{{_('At least one and at most 32 lower-case characters, digits, dashes ("-") or underscores ("_"). <b>Cannot be changed later!</b>')|safe}}
 			</small>
 		</div>
 		<div class="form-group col-12">
-			<label for="user-displayname">Display Name</label>
+			<label for="user-displayname">{{_('Display Name')}}</label>
 			<input type="text" class="form-control" id="user-displayname" name="displayname" value="{{ request.form.displayname }}" minlength=1 maxlength=128 required>
 			<small class="form-text text-muted">
-				At least one and at most 128 characters, no other special requirements.
+				{{_('At least one and at most 128 characters, no other special requirements.')}}
 			</small>
 		</div>
 		<div class="form-group col-12">
-			<label for="user-mail">E-Mail Address</label>
+			<label for="user-mail">{{_('E-Mail Address')}}</label>
 			<input type="email" class="form-control" id="user-mail" name="mail" value="{{ request.form.mail }}" required>
 			<small class="form-text text-muted">
-				We will send a confirmation mail to this address that you need to complete the registration.
+				{{_('We will send a confirmation mail to this address that you need to complete the registration.')}}
 			</small>
 		</div>
 		<div class="form-group col-12">
-			<label for="user-password1">Password</label>
+			<label for="user-password1">{{_('Password')}}</label>
 			<input type="password" class="form-control" id="user-password1" name="password1" minlength=8 maxlength=256 required>
 			<small class="form-text text-muted">
-				At least 8 and at most 256 characters, no other special requirements. But please don't be stupid, do use a password manager.
+				{{_("At least 8 and at most 256 characters, no other special requirements. But please don't be stupid, do use a password manager.")}}
 			</small>
 		</div>
 		<div class="form-group col-12">
-			<label for="user-password2">Repeat Password</label>
+			<label for="user-password2">{{_('Repeat Password')}}</label>
 			<input type="password" class="form-control" id="user-password2" name="password2" required>
 		</div>
 		<div class="form-group col-12">
-			<button type="submit" class="btn btn-primary btn-block">Create Account</button>
+			<button type="submit" class="btn btn-primary btn-block">{{_('Create Account')}}</button>
 		</div>
 	</div>
 </div>
@@ -83,13 +83,13 @@ $("#check-loginname").on("click", function () {
 				$("#user-loginname").addClass("is-valid");
 				$("#loginname-feedback").text("");
 			} else if (resp.status == 'exists') {
-				$("#loginname-feedback").text("The name is already taken");
+				$("#loginname-feedback").text({{_("The name is already taken")|tojson}});
 				$("#user-loginname").addClass("is-invalid");
 			} else if (resp.status == 'ratelimited') {
-				$("#loginname-feedback").text("Too many requests! Please wait a bit before trying again!");
+				$("#loginname-feedback").text({{_("Too many requests! Please wait a bit before trying again!")|tojson}});
 				$("#user-loginname").addClass("is-invalid");
 			} else {
-				$("#loginname-feedback").text("The name is invalid");
+				$("#loginname-feedback").text({{_("The name is invalid")|tojson}});
 				$("#user-loginname").addClass("is-invalid");
 			}
 		}
diff --git a/uffd/signup/templates/signup/submitted.html b/uffd/signup/templates/signup/submitted.html
index 685c1a01..6c85e910 100644
--- a/uffd/signup/templates/signup/submitted.html
+++ b/uffd/signup/templates/signup/submitted.html
@@ -8,10 +8,10 @@
 			<img alt="branding logo" src="{{ config.get("BRANDING_LOGO_URL") }}" class="col-lg-8 col-md-12" >
 		</div>
 		<div class="col-12 mb-3">
-			<h2 class="text-center">Confirm your E-Mail Address</h2>
+			<h2 class="text-center">{{_('Confirm your E-Mail Address')}}</h2>
 		</div>
-		<p>We sent a confirmation mail to <b>{{ signup.mail }}</b>. You need to confirm your mail address within 48 hours to complete the account registration.</p>
-		<p>If you mistyped your mail address or don't receive the confirmation mail for another reason, retry the registration procedure from the beginning.</p>
+		<p>{{_('We sent a confirmation mail to <b>%(signup_mail)s</b>. You need to confirm your mail address within 48 hours to complete the account registration.', signup_mail=signup.mail|e)|safe}}</p>
+		<p>{{_("If you mistyped your mail address or don't receive the confirmation mail for another reason, retry the registration procedure from the beginning.")}}</p>
 	</div>
 </div>
 {% endblock %}
diff --git a/uffd/signup/views.py b/uffd/signup/views.py
index ba0f58c6..b15b14c6 100644
--- a/uffd/signup/views.py
+++ b/uffd/signup/views.py
@@ -1,6 +1,7 @@
 import functools
 
 from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app, jsonify
+from flask_babel import gettext as _
 
 from uffd.database import db
 from uffd.ldap import ldap
@@ -19,7 +20,7 @@ def signup_enabled(func):
 	@functools.wraps(func)
 	def decorator(*args, **kwargs):
 		if not current_app.config['SELF_SIGNUP']:
-			flash('Singup not enabled')
+			flash(_('Singup not enabled'))
 			return redirect(url_for('index'))
 		return func(*args, **kwargs)
 	return decorator
@@ -45,13 +46,14 @@ def signup_check():
 @signup_enabled
 def signup_submit():
 	if request.form['password1'] != request.form['password2']:
-		return render_template('signup/start.html', error='Passwords do not match')
+		return render_template('signup/start.html', error=_('Passwords do not match'))
 	signup_delay = signup_ratelimit.get_delay(request.form['mail'])
 	host_delay = host_ratelimit.get_delay()
 	if signup_delay and signup_delay > host_delay:
-		return render_template('signup/start.html', error='Too many signup requests with this mail address! Please wait %s.'%format_delay(signup_delay))
+		return render_template('signup/start.html', error=_('Too many signup requests with this mail address! Please wait %(delay)s.',
+		                                                    delay=format_delay(signup_delay)))
 	if host_delay:
-		return render_template('signup/start.html', error='Too many requests! Please wait %s.'%format_delay(host_delay))
+		return render_template('signup/start.html', error=_('Too many requests! Please wait %(delay)s.', delay=format_delay(host_delay)))
 	host_ratelimit.log()
 	signup = Signup(loginname=request.form['loginname'],
 	                displayname=request.form['displayname'],
@@ -63,7 +65,7 @@ def signup_submit():
 	db.session.commit()
 	sent = sendmail(signup.mail, 'Confirm your mail address', 'signup/mail.txt', signup=signup)
 	if not sent:
-		return render_template('signup/start.html', error='Cound not send mail')
+		return render_template('signup/start.html', error=_('Cound not send mail'))
 	signup_ratelimit.log(request.form['mail'])
 	return render_template('signup/submitted.html', signup=signup)
 
@@ -72,7 +74,7 @@ def signup_submit():
 def signup_confirm(token):
 	signup = Signup.query.get(token)
 	if not signup or signup.expired or signup.completed:
-		flash('Invalid signup link')
+		flash(_('Invalid signup link'))
 		return redirect(url_for('index'))
 	return render_template('signup/confirm.html', signup=signup)
 
@@ -80,23 +82,23 @@ def signup_confirm(token):
 def signup_confirm_submit(token):
 	signup = Signup.query.get(token)
 	if not signup or signup.expired or signup.completed:
-		flash('Invalid signup link')
+		flash(_('Invalid signup link'))
 		return redirect(url_for('index'))
 	confirm_delay = confirm_ratelimit.get_delay(token)
 	host_delay = host_ratelimit.get_delay()
 	if confirm_delay and confirm_delay > host_delay:
-		return render_template('signup/confirm.html', signup=signup, error='Too many failed attempts! Please wait %s.'%format_delay(confirm_delay))
+		return render_template('signup/confirm.html', signup=signup, error=_('Too many failed attempts! Please wait %(delay)s.', delay=format_delay(confirm_delay)))
 	if host_delay:
-		return render_template('signup/confirm.html', signup=signup, error='Too many requests! Please wait %s.'%format_delay(host_delay))
+		return render_template('signup/confirm.html', signup=signup, error=_('Too many requests! Please wait %(delay)s.', delay=format_delay(host_delay)))
 	if not signup.check_password(request.form['password']):
 		host_ratelimit.log()
 		confirm_ratelimit.log(token)
-		return render_template('signup/confirm.html', signup=signup, error='Wrong password')
+		return render_template('signup/confirm.html', signup=signup, error=_('Wrong password'))
 	user, msg = signup.finish(request.form['password'])
 	if user is None:
 		return render_template('signup/confirm.html', signup=signup, error=msg)
 	db.session.commit()
 	ldap.session.commit()
 	set_session(user, password=request.form['password'], skip_mfa=True)
-	flash('Your account was successfully created')
+	flash(_('Your account was successfully created'))
 	return redirect(url_for('selfservice.index'))
diff --git a/uffd/templates/base.html b/uffd/templates/base.html
index 58ef8d72..63406e58 100644
--- a/uffd/templates/base.html
+++ b/uffd/templates/base.html
@@ -81,7 +81,7 @@
 								{% endfor %}
 							</select>
 							<noscript>
-								<button type="submit" class="bg-dark py-0 pl-0" style="border: 0px; color: rgba(255, 255, 255, 0.5);">Change</button>
+								<button type="submit" class="bg-dark py-0 pl-0" style="border: 0px; color: rgba(255, 255, 255, 0.5);">{{_('Change')}}</button>
 							</noscript>
 						</form>
 					</li>
@@ -89,8 +89,8 @@
 					{% if request.user %}
 					<li class="nav-item">
 						<a class="nav-link" href="{{ url_for("session.deviceauth") }}">
-							<span aria-hidden="true" class="fas fa-mobile-alt" title="Authorize Device Login"></span>
-							<span class="d-inline d-md-none">Device Login</span>
+							<span aria-hidden="true" class="fas fa-mobile-alt" title="{{_('Authorize Device Login')}}"></span>
+							<span class="d-inline d-md-none">{{_('Device Login')}}</span>
 						</a>
 					</li>
 					<li class="nav-item">
diff --git a/uffd/translations/de/LC_MESSAGES/messages.mo b/uffd/translations/de/LC_MESSAGES/messages.mo
index 952c7b0b0f71194d8092cfe0910cde1f4d2dfdc7..e275eb91a683da1156ad7f935e44542a327d4a36 100644
GIT binary patch
literal 30649
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

delta 4094
zcmdn_o^f$W{XHR;sSFGZ4E+oY3^EK13{zM@JOt+9WMJ@NU|`_oWMJT7U|@*iWMJTB
zU|>k+WME)tU|=ZaWMHUaU|^`_WMEKYU|{&g$-v;oz`!8O#lRrRz`&5g#lXPDz`#()
z#lXPEz`)SS#lXPIz`!tpi-EzAfuWvZ8W#ftKLZ2938;b_To8-yb1^V*GcYi`fr|g&
zVqj2YU|{$Ul~?3uU=U?sV9?`cU{GgZU~u7PV31;9V94QSU`SzLU})!NVBiDk=Vo9~
zU|?YQ&CS4I4e}%p1A{#S149H41A_oVJp;oG9*6^0@-Q$+GcYjh<Y8ctV_;yo$^+5x
zg$Lqt173)M=DZ9HR~Z->+;|xnDnJJELFA|MF)%1IFfh#FgP3=K52F489|OZ?1_p-v
zQ1Qw95cP}s85rd185kH=@k4^-BtImm&hs-cs4_4x+~Q|okO#$t07Qd=00RRkcGLwR
zA*3S!vB**Y;zLgXNR(tj#ft<W<~2d}^$IXBgfcKNOcG#VU}j)ocp<>RP!EdJHv$X{
zLJSNHKLsE@;1OhC;ACK6Fc4&5P+?$Tuo7foXk}nvh!<pF;9y{2cq$07_?;jFgCGL~
z!#_bt&<hALFo-cQFlY-wqRvSO;vhdENJxYUF)-vaFfgPFF)*}&LRg5Q9&BKYFeFi>
z3PTJo7KS*WSs0?RR~Ql{bA%Zfv=|r|c0=W#2}45egD?Yw1_J}bH(^K!$%{ZjOhtr&
zK?39;5e5bkP#O_|m=`Gm3Be4JdWeroMIa8S69FX(28JFH28IX*28OvJkRbdf0to>|
zQ3i&^pu{B#N!{9Fkf3)JV_<M(U|>iSV_<M#U|?7w1_{ZRVh{)MibE2skT?UwTLuOO
z4RHnrSq26Mv3d!Jg(eaV3?2*&46YIk3|<Tj41E#|3^oi53^yepE)|o6_*7XE;xkQ2
zhyx5HA*tC*l7S(Zfq|h*l7S(Ffq~(kBm;vP0|SGx6huBtih<!6D6L6B99Ad|3DNqg
z(hLl03=9lgq!}1o85kIDOG6T$q6{QWxXUmwl!EfT3<HBW0|Uba83u+(P~wqcU`S+O
zU~rO!&`V??4q}so#Jz$X#Nj$}kPx(#gXAt3IY@{k$T2W5f%1Q<9K=T%au6Tp%Rv%N
zsT>1CFCzm(tsDbG3<CoLw*n-jG87;Vt5krf?@?f2XaJ>Z1qKFv1_lOUMMy|ED?%)s
zt_X3!W<^Mv+6U#IPy~fcJp;pKMMx?aR)VAj3nc~yVFm^UM<s|4LX{v6N>XBAFkoO{
z$Wns%aJCX8wJ%p<V9;Y=VA!VwiPN`AAa5}+a4SR1=~iZ7&|zR;n6C^;BWIK$4&+l|
zU=Rl7|0ETNkIGaaQPHWwz@W>(z_39D8kZ^%2N<hDLcm&;f#DSc1A~t$qyREkgE(k|
z8UuqB0|UbzHHdlN)galEOPzt?5U8M1hvcsN>Jats)fpJ-K|%Ig9pZCs4Tyw^2E<1W
z8W5M~Ye3}ZXh5>hQVmGRY|(%O@j<BiyBd(7=Fx<tc5zLJg}R!MIJebgU`PO^8BIt5
zv{;j&9$ZE**Mu0fO%sxjFKI$7_^k;E2@Wj=hC&7g1~V;)Lsn=(^7BS5NDv>>Vqnk&
zm6%$P5M<GYgp{^6B;;(gA&J*Tn}MN+fq@}Ln}J~+0|UcL?RtnsGjyOn(}5VgMF$de
zCv_lkd|QWsL7Rbr;gb#|TgvG|d>X9_NxW&g3=Ad=3=FxtkVLi!O0Us{<fd)9kb>xr
zE+lHc>Ow+>qh1eE@JQ%EELPKlD74mNV8~`*U<lEJ7<fRBfnf#%1H&mjNL(iCLlT>s
z0VIlC3>X-^85kG}4Hy_a85kJ$8Za;zF)%Q&8$yEG$`BGIc7_ZLB8&_SA%>8UuFo`r
z7&OU<fx!-x=!_r+yf<QC$YNk%ur!8*z!GCfNbQEwCyXH_-&JD<26s@=Ys|oq#K6E{
zX9Cf;&;-&B;4x)j2xMSj2sUM4_{6}#u+$V{-wZQw<yOzI%?uKE*UTUWF_=T*+`}9a
zw`JxG3^PF`n>i$ns8}#C6frO`I9M<+R5CCyY=-i6Eg_ZA0!yevEg^~d1eCsF2`TX&
zSV9uxe=rRyjjSL+%xMLQOJOTWB9pRWV3_<|C|cApIXShsSRo}fFEcfT!7({GzcjC8
zvXyB4<a)8@$w}hElP`<Av$>?^q?V*k{w*QRnwg$iQapL0q}t>IlBc-K^K$YNQxuZ(
zQ&K1IlA1sHwzT489+}F`lVwsECoh%ro~$f?n6<dLG_`2*UPa}}g-Rlm*DASBwo_JN
z%S%j8RVbOf(NIE(!8bKGDYdAW!8bo8wJ5P9zevHoD8IA-!Jq7=63<u8;Fnsi5Rh10
zT%KQ)GWm{*+hiwIWu=@-g`CuqlGLJN9fiD7ut5rmc_|8|c`2zy#mV_asgqZ$Cb_`$
zF~ArKL8%3)i6sbraz<iadTNRul1P4OPKrWaeu+X-szPyU$z(2dsmc5rVw){BSQ$mq
zGmA@7ix`4ZA&k@%1^>Lsdo-IEizlzqvY70tEkC(bn}2ezw&G-W9i_<)y2+E*>vc~4
zuD@vVbi-$xn~jti`SXfXOB5185s;Qxno}~_&Sci)2PQs~=b9=^jyDsXeABdMGNYNq
z<O^oDOvQSW`7K;0Z?upW2kTPEOD$K(P0Y+uNK8p7N-ZwdoBYz^#N<S4B`H0I$ox`;
z<kF&|)Vz|MN`;KXvQ&kVjMU=P$+gz=_@La(VuhmooYcuCHuE;w@-t4JXRjunl384k
zlUS*cmzbN%kdvRDnFnT0K5VZnR>S~Oo|yusiW!Pilk@XZiYH4uq;MFTDi|4A8Ch&@
zcTi@We9JL!a*~te<SLiO$y;3)2ssCbDR|}<<QJ7NI3^_(r6y-gHg+!-@;SV%I5{IV
zkHN3Bq$;(@Dl>2LdJhTKsMO5ljL8e))hAbb@=l)OS)dV6ba+o%szOR;s)B1~UT$Je
zW_osNW{Dn%XO*ge(C*|FA?%!=l3J{gky=!mSCU$klbDj4H~Eg&k;y?mLX!i0L?>7J
ztlDhqtHH?bn3tQHlae|))lYu%#0cTZGyG&GH~U+0!;Q;JoqW_^eX@AKWp0nuqQuhT
z^wgx(qRGL*hSE^Ic?>WHD7YZ($$LFDxm{9=6d>Hx$@fFF`CWB=6Ekyk9l`OG$}l-Q
zRGLpAFEcr#M4>D(Co?6LVX{GqT#Sa1o1>;eQfgvRQfg*NY951cW=VQZW@<`m5h62K
zW#;9j=A{>v78InSi!%6u90DSA6yUiDqG9r*FrUee;liTcshN4H7_t+?ZMmH?i!)M-
z6tYt@^CmCM7gs6*C+(ul)S?mwztSQFr_$t%;*!Lq)I1%9sKkt%)V#?BuHuu6BMfDH
zON)yYQlXKfP@I~X2hT?(lkH>0)S=m~L?J!(@DfngEYV|dNi2rt=MshN{Jgx9)X4=Y
zVv~8J)F*34OHFQc7u%c@ZO14L$zvdgq~<|09Eek*keEB!K6dkD&N$x5IdRUDHRDYu
z=f!hPZirW&?3ZA}@0yvHnVzZ;oLW?tnVecYSvJX4B?at#sE|TcsX}pTMrxiOLttrY
zP7bIfV{plwte7P>xhC0a^7dp8uFTY8h2qS-6or(@jYZOvU#85RY)~RQ`AXXF%~#Sx
z7$@sw%1yqR*;x-yHwvjm#U-gZIVE}u;h9AW8K3}DNJ%ZyPf5*1Bvw#pr7F0B6NW-=
zW(k;|iwKXX(xStAlCz6bOR7@y6nrxCvK7)(i&OJbQbCac%`ggy*(IfkIhn<osr=wb
zf{N#*=1oq>)}Q<+TY2){?Ci<2bEGEU&la<E%u6duOi#^ID9g_SIZ44YKmnR4ixu=0
z5=%ivQDz><ov9^N<*7y43Q4Ki`MJ51^>bt<-_Pcq9GvH|SuEd*(bT0>AtSN41nh#;
zoE&hfNmU5S&&dIWCc11|eolI7Udm+GqLsohxs=S*VyKSE2_?Fdt%|!gzbj5)oUB@|
sA?}!0m71BJ3NDABISIs`>{G5P52_{@AT}v@x<FYB!KvAxGCz+200)n82LJ#7

diff --git a/uffd/translations/de/LC_MESSAGES/messages.po b/uffd/translations/de/LC_MESSAGES/messages.po
index 77a75923..6fb606da 100644
--- a/uffd/translations/de/LC_MESSAGES/messages.po
+++ b/uffd/translations/de/LC_MESSAGES/messages.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PROJECT VERSION\n"
 "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2021-07-15 22:28+0200\n"
+"POT-Creation-Date: 2021-07-30 19:49+0200\n"
 "PO-Revision-Date: 2021-05-25 21:18+0200\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language: de\n"
@@ -18,19 +18,379 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Generated-By: Babel 2.9.1\n"
 
-#: mfa/views.py:53
+#: uffd/ratelimit.py:70
+msgid "a few seconds"
+msgstr "ein paar Sekunden"
+
+#: uffd/ratelimit.py:72
+msgid "30 seconds"
+msgstr "30 Sekunden"
+
+#: uffd/ratelimit.py:74
+msgid "one minute"
+msgstr "eine Minute"
+
+#: uffd/ratelimit.py:76
+#, python-format
+msgid "%(minutes)d minutes"
+msgstr "%(minutes)d Minuten"
+
+#: uffd/ratelimit.py:78
+msgid "one hour"
+msgstr "eine Stunde"
+
+#: uffd/ratelimit.py:79
+#, python-format
+msgid "%(hours)d hours"
+msgstr "%(hours)d Stunden\""
+
+#: uffd/invite/views.py:56
+msgid "Invites"
+msgstr "Einladungslinks"
+
+#: uffd/invite/views.py:85
+msgid "The \"Expires After\" date is too far in the future"
+msgstr "Das Ablaufdatum liegt zu weit in der Zukunft"
+
+#: uffd/invite/views.py:88
+msgid "You are not allowed to create invite links with these permissions"
+msgstr "Dir fehlen Berechtigungen um diesen Einladungslink zu erstellen"
+
+#: uffd/invite/views.py:91
+msgid "Invite link must either allow signup or grant at least one role"
+msgstr ""
+"Einladungslink must entweder Account-Registrierung erlauben oder Rollen "
+"vergeben"
+
+#: uffd/invite/views.py:119 uffd/invite/views.py:148
+msgid "Invalid invite link"
+msgstr "Ungültiger Einladungslink"
+
+#: uffd/invite/views.py:136
+msgid "Roles successfully updated"
+msgstr "Rollen erfolgreich geändert"
+
+#: uffd/invite/views.py:151
+msgid "Invite link does not allow signup"
+msgstr "Einladungslink erlaubt keine Account-Registrierung"
+
+#: uffd/invite/views.py:173 uffd/selfservice/views.py:53
+#: uffd/signup/views.py:49
+msgid "Passwords do not match"
+msgstr "Die Passwörter stimmen nicht überein"
+
+#: uffd/invite/views.py:178 uffd/signup/views.py:54
+#, python-format
+msgid "Too many signup requests with this mail address! Please wait %(delay)s."
+msgstr ""
+"Zu viele Account-Registrierungen mit dieser E-Mail-Adresse! Bitte warte "
+"%(delay)s."
+
+#: uffd/invite/views.py:180 uffd/signup/views.py:56 uffd/signup/views.py:92
+#, python-format
+msgid "Too many requests! Please wait %(delay)s."
+msgstr "Zu viele Anfragen! Bitte warte %(delay)s."
+
+#: uffd/invite/views.py:193 uffd/signup/views.py:68
+msgid "Cound not send mail"
+msgstr "Mailversand fehlgeschlagen"
+
+#: uffd/invite/templates/invite/list.html:6
+#: uffd/mail/templates/mail/list.html:8 uffd/role/templates/role/list.html:8
+#: uffd/user/templates/user/list.html:8
+msgid "New"
+msgstr "Neu"
+
+#: uffd/invite/templates/invite/list.html:12
+msgid "Link"
+msgstr "Link"
+
+#: uffd/invite/templates/invite/list.html:13
+msgid "Created by"
+msgstr "Ersteller"
+
+#: uffd/invite/templates/invite/list.html:14
+msgid "Permissions"
+msgstr "Berechtigungen"
+
+#: uffd/invite/templates/invite/list.html:15
+msgid "Usages"
+msgstr "Verwendungen"
+
+#: uffd/invite/templates/invite/list.html:16
+msgid "Status"
+msgstr "Status"
+
+#: uffd/invite/templates/invite/list.html:26
+msgid "Copy link to clipboard"
+msgstr "Link kopieren"
+
+#: uffd/invite/templates/invite/list.html:27
+msgid "Show link as QR code"
+msgstr "Link als QR-Code anzeigen"
+
+#: uffd/invite/templates/invite/list.html:42
+msgid "Signup"
+msgstr "Account-Registrierung"
+
+#: uffd/invite/templates/invite/list.html:51
+msgid "Disabled"
+msgstr "Deaktiviert"
+
+#: uffd/invite/templates/invite/list.html:53
+msgid "Voided"
+msgstr "Verbraucht"
+
+#: uffd/invite/templates/invite/list.html:55
+msgid "Expired"
+msgstr "Abgelaufen"
+
+#: uffd/invite/templates/invite/list.html:57
+msgid "Invalid, unpermitted creator"
+msgstr "Ungültig, unberechtigter Ersteller"
+
+#: uffd/invite/templates/invite/list.html:59
+msgid "Invalid"
+msgstr "Ungültig"
+
+#: uffd/invite/templates/invite/list.html:61
+#, python-format
+msgid "Valid once, expires %(expiry_date)s"
+msgstr "Einmal verwendbar, gültig bis %(expiry_date)s"
+
+#: uffd/invite/templates/invite/list.html:63
+#, python-format
+msgid "Valid, expires %(expiry_date)s"
+msgstr "Gültig bis %(expiry_date)s"
+
+#: uffd/invite/templates/invite/list.html:80
+msgid "Invite Link Details"
+msgstr "Details zum Einladungslink"
+
+#: uffd/invite/templates/invite/list.html:87
+msgid "Type:"
+msgstr "Typ:"
+
+#: uffd/invite/templates/invite/list.html:87
+msgid "Single-use"
+msgstr "Einmal verwendbar"
+
+#: uffd/invite/templates/invite/list.html:87
+#: uffd/invite/templates/invite/new.html:9
+msgid "Multi-use"
+msgstr "Mehrfach verwendbar"
+
+#: uffd/invite/templates/invite/list.html:88
+msgid "Created:"
+msgstr "Erstellt:"
+
+#: uffd/invite/templates/invite/list.html:89
+msgid "Expires:"
+msgstr "Ablaufdatum:"
+
+#: uffd/invite/templates/invite/list.html:90
+msgid "Permissions:"
+msgstr "Berechtigungen:"
+
+#: uffd/invite/templates/invite/list.html:93
+#: uffd/invite/templates/invite/new.html:21
+msgid "Link allows account registration"
+msgstr "Link erlaubt Account-Registrierung"
+
+#: uffd/invite/templates/invite/list.html:95
+#: uffd/invite/templates/invite/new.html:22
+msgid "No account registration allowed"
+msgstr "Keine Account-Registrierung möglich"
+
+#: uffd/invite/templates/invite/list.html:98
+#, python-format
+msgid "Link grants users the role \"%(name)s\""
+msgstr "Link gibt Nutzern die Rolle \"%(name)s\""
+
+#: uffd/invite/templates/invite/list.html:104
+msgid "Never used"
+msgstr "Keine Verwendungen"
+
+#: uffd/invite/templates/invite/list.html:108
+#, python-format
+msgid "Registration of user <a href=\"%(user_url)s\">%(user_name)s</a>"
+msgstr "Account-Registrierung von <a href=\"%(user_url)s\">%(user_name)s</a>"
+
+#: uffd/invite/templates/invite/list.html:111
+#, python-format
+msgid "Roles granted to <a href=\"%(user_url)s\">%(user_name)s</a>"
+msgstr "Rollen an href=\"%(user_url)s\">%(user_name)s</a> vergeben"
+
+#: uffd/invite/templates/invite/list.html:122
+msgid "Disable Link"
+msgstr "Link deaktivieren"
+
+#: uffd/invite/templates/invite/list.html:126
+msgid "Reenable Link"
+msgstr "Link reaktivieren"
+
+#: uffd/invite/templates/invite/list.html:140
+msgid "Invite"
+msgstr "Einladungslink"
+
+#: uffd/invite/templates/invite/new.html:6
+msgid "Link Type"
+msgstr "Link Typ"
+
+#: uffd/invite/templates/invite/new.html:8
+msgid "Valid for a single successful use"
+msgstr "Für eine erfolgreiche Verwendung gültig"
+
+#: uffd/invite/templates/invite/new.html:13
+msgid "Valid Until"
+msgstr "Ablaufdatum"
+
+#: uffd/invite/templates/invite/new.html:15
+#, python-format
+msgid "Must be within the next %(max_valid_days)d days"
+msgstr "Muss innerhalb der nächsten %(max_valid_days)d Tage liegen"
+
+#: uffd/invite/templates/invite/new.html:19
+#: uffd/signup/templates/signup/start.html:11
+msgid "Account Registration"
+msgstr "Account-Registrierung"
+
+#: uffd/invite/templates/invite/new.html:30
+msgid "Granted Roles"
+msgstr "Enthaltene Rollen"
+
+#: uffd/invite/templates/invite/new.html:35
+#: uffd/mail/templates/mail/list.html:14 uffd/mail/templates/mail/show.html:7
+#: uffd/mfa/templates/mfa/setup.html:98 uffd/mfa/templates/mfa/setup.html:99
+#: uffd/mfa/templates/mfa/setup.html:107 uffd/mfa/templates/mfa/setup.html:157
+#: uffd/mfa/templates/mfa/setup.html:158 uffd/mfa/templates/mfa/setup.html:169
+#: uffd/role/templates/role/list.html:14
+#: uffd/rolemod/templates/rolemod/list.html:9
+#: uffd/rolemod/templates/rolemod/show.html:46
+#: uffd/selfservice/templates/selfservice/self.html:107
+#: uffd/user/templates/group/list.html:10
+#: uffd/user/templates/group/show.html:11 uffd/user/templates/user/show.html:95
+#: uffd/user/templates/user/show.html:127
+msgid "Name"
+msgstr "Name"
+
+#: uffd/invite/templates/invite/new.html:36
+#: uffd/role/templates/role/list.html:15 uffd/role/templates/role/show.html:48
+#: uffd/rolemod/templates/rolemod/list.html:10
+#: uffd/rolemod/templates/rolemod/show.html:28
+#: uffd/selfservice/templates/selfservice/self.html:108
+#: uffd/user/templates/group/list.html:11 uffd/user/templates/user/show.html:96
+#: uffd/user/templates/user/show.html:128
+msgid "Description"
+msgstr "Beschreibung"
+
+#: uffd/invite/templates/invite/new.html:55
+msgid "Create Link"
+msgstr "Link erstellen"
+
+#: uffd/invite/templates/invite/new.html:56
+#: uffd/mail/templates/mail/show.html:28 uffd/mfa/templates/mfa/auth.html:39
+#: uffd/role/templates/role/show.html:14
+#: uffd/rolemod/templates/rolemod/show.html:11
+#: uffd/session/templates/session/deviceauth.html:44
+#: uffd/session/templates/session/deviceauth.html:54
+#: uffd/session/templates/session/devicelogin.html:34
+#: uffd/user/templates/user/show.html:8
+msgid "Cancel"
+msgstr "Abbrechen"
+
+#: uffd/invite/templates/invite/use.html:11
+msgid "Invite Link"
+msgstr "Einladungslink"
+
+#: uffd/invite/templates/invite/use.html:18
+msgid ""
+"With this link you can register a new user account with the following "
+"roles or add the roles to an existing account:"
+msgstr ""
+"Mit diesem Link kannst du einen Account mit den folgenden Rollen "
+"erstellen oder diese Rollen zu einem existierenden Account hinzufügen:"
+
+#: uffd/invite/templates/invite/use.html:20
+msgid "With this link you can add the following roles to an existing account:"
+msgstr ""
+"Mit diesem Link kannst du die folgenden Rollen zu einem existierenden "
+"Account hinzufügen:"
+
+#: uffd/invite/templates/invite/use.html:22
+msgid "With this link you can register a new user account."
+msgstr "Mit diesem Link kannst du einen Account registieren."
+
+#: uffd/invite/templates/invite/use.html:34
+msgid "Add the roles to your account now"
+msgstr "Rollen jetzt zu deinem Account hinzufügen"
+
+#: uffd/invite/templates/invite/use.html:36
+msgid "Logout and switch to a different account"
+msgstr "Abmelden und zu einem anderen Account wechseln"
+
+#: uffd/invite/templates/invite/use.html:39
+msgid "Logout to register a new account"
+msgstr "Abmelden um einen neuen Account zu registrieren"
+
+#: uffd/invite/templates/invite/use.html:43
+msgid "Register a new account"
+msgstr "Neuen Account registrieren"
+
+#: uffd/invite/templates/invite/use.html:46
+msgid "Login and add the roles to your account"
+msgstr "Anmelden und die Rollen zu deinem Account hinzufügen"
+
+#: uffd/mail/views.py:23
+msgid "Forwardings"
+msgstr "Weiterleitungen"
+
+#: uffd/mail/views.py:47
+msgid "Mail mapping updated."
+msgstr "Mailweiterleitung geändert."
+
+#: uffd/mail/views.py:56
+msgid "Deleted mail mapping."
+msgstr "Mailweiterleitung gelöscht."
+
+#: uffd/mail/templates/mail/list.html:15 uffd/mail/templates/mail/show.html:13
+msgid "Receiving addresses"
+msgstr "Empfangsadressen"
+
+#: uffd/mail/templates/mail/list.html:16 uffd/mail/templates/mail/show.html:20
+msgid "Destinations"
+msgstr "Zieladressen"
+
+#: uffd/mail/templates/mail/show.html:16 uffd/mail/templates/mail/show.html:23
+msgid "One address per line"
+msgstr "Eine Adresse pro Zeile"
+
+#: uffd/mail/templates/mail/show.html:27 uffd/role/templates/role/show.html:13
+#: uffd/rolemod/templates/rolemod/show.html:10
+#: uffd/user/templates/user/show.html:7
+msgid "Save"
+msgstr "Speichern"
+
+#: uffd/mail/templates/mail/show.html:30 uffd/mail/templates/mail/show.html:32
+#: uffd/mfa/templates/mfa/setup.html:117 uffd/mfa/templates/mfa/setup.html:179
+#: uffd/role/templates/role/show.html:21 uffd/role/templates/role/show.html:24
+#: uffd/user/templates/user/show.html:11 uffd/user/templates/user/show.html:13
+msgid "Delete"
+msgstr "Löschen"
+
+#: uffd/mfa/views.py:54
 msgid "Two-factor authentication was reset"
 msgstr "Zwei-Faktor-Authentifizierung wurde zurückgesetzt"
 
-#: mfa/views.py:82
+#: uffd/mfa/views.py:83
 msgid "Generate recovery codes first!"
 msgstr "Generiere zuerst die Wiederherstellungscodes!"
 
-#: mfa/views.py:91
+#: uffd/mfa/views.py:92
 msgid "Code is invalid"
 msgstr "Wiederherstellungscode ist ungültig"
 
-#: mfa/views.py:115
+#: uffd/mfa/views.py:116
 #, python-format
 msgid ""
 "2FA WebAuthn support disabled because import of the fido2 module failed "
@@ -39,16 +399,16 @@ msgstr ""
 "2FA WebAuthn Unterstützung deaktiviert, da das fido2 Modul nicht geladen "
 "werden konnte (%s)"
 
-#: mfa/views.py:224
+#: uffd/mfa/views.py:225
 #, python-format
 msgid "We received too many invalid attempts! Please wait at least %s."
 msgstr "Wir haben zu viele fehlgeschlagene Versuche! Bitte warte mindestens %s."
 
-#: mfa/views.py:238
+#: uffd/mfa/views.py:239
 msgid "You have exhausted your recovery codes. Please generate new ones now!"
 msgstr "Du hast keine Wiederherstellungscode mehr. Bitte generiere diese jetzt!"
 
-#: mfa/views.py:241
+#: uffd/mfa/views.py:242
 msgid ""
 "You only have a few recovery codes remaining. Make sure to generate new "
 "ones before they run out."
@@ -56,46 +416,42 @@ msgstr ""
 "Du hast nur noch wenige Wiederherstellungscodes übrig. Bitte generiere "
 "diese erneut bevor keine mehr übrig sind."
 
-#: mfa/views.py:245
+#: uffd/mfa/views.py:246
 msgid "Two-factor authentication failed"
 msgstr "Zwei-Faktor-Authentifizierung fehlgeschlagen"
 
-#: mfa/templates/mfa/auth.html:12
+#: uffd/mfa/templates/mfa/auth.html:12
+#: uffd/selfservice/templates/selfservice/self.html:73
 msgid "Two-Factor Authentication"
 msgstr "Zwei-Faktor-Authentifizierung"
 
-#: mfa/templates/mfa/auth.html:17
+#: uffd/mfa/templates/mfa/auth.html:17
 msgid "Enable javascript for authentication with U2F/FIDO2 devices"
 msgstr "Aktiviere Javascript zur Authentifizierung mit U2F/FIDO2 Geräten"
 
-#: mfa/templates/mfa/auth.html:21
+#: uffd/mfa/templates/mfa/auth.html:21
 msgid "Authentication with U2F/FIDO2 devices is not supported by your browser"
 msgstr ""
 "Authentifizierung mit U2F/FIDO2 Geräten wird von deinem Browser nicht "
 "unterstützt"
 
-#: mfa/templates/mfa/auth.html:27
+#: uffd/mfa/templates/mfa/auth.html:27
 msgid "Authenticate with U2F/FIDO2 device"
 msgstr "Authentifiziere dich mit einem U2F/FIDO2 Gerät"
 
-#: mfa/templates/mfa/auth.html:30
+#: uffd/mfa/templates/mfa/auth.html:30
 msgid "or"
 msgstr "oder"
 
-#: mfa/templates/mfa/auth.html:33
+#: uffd/mfa/templates/mfa/auth.html:33
 msgid "Code from your authenticator app or recovery code"
 msgstr "Code aus deiner Authentifikator-App oder Wiederherstellungscode"
 
-#: mfa/templates/mfa/auth.html:36
+#: uffd/mfa/templates/mfa/auth.html:36
 msgid "Verify"
 msgstr "Verifizieren"
 
-#: mfa/templates/mfa/auth.html:39 role/templates/role/show.html:14
-#: user/templates/user/show.html:8
-msgid "Cancel"
-msgstr "Abbrechen"
-
-#: mfa/templates/mfa/disable.html:6
+#: uffd/mfa/templates/mfa/disable.html:6
 msgid ""
 "When you proceed, all recovery codes, registered authenticator "
 "applications and devices will be invalidated.\n"
@@ -107,19 +463,23 @@ msgstr ""
 "Wiederherstellungscodes generieren und das Setup der Anwendungen und "
 "Geräte erneut durchführen."
 
-#: mfa/templates/mfa/disable.html:11 mfa/templates/mfa/setup.html:32
+#: uffd/mfa/templates/mfa/disable.html:11 uffd/mfa/templates/mfa/setup.html:32
 msgid "Disable two-factor authentication"
 msgstr "Zwei-Faktor-Authentifizierung (2FA) deaktivieren"
 
-#: mfa/templates/mfa/setup.html:18
+#: uffd/mfa/templates/mfa/setup.html:18
+#: uffd/selfservice/templates/selfservice/self.html:79
 msgid "Two-factor authentication is currently <strong>enabled</strong>."
 msgstr "Die Zwei-Faktor-Authentifizierung ist derzeit <strong>aktiviert</strong>."
 
-#: mfa/templates/mfa/setup.html:20
+#: uffd/mfa/templates/mfa/setup.html:20
+#: uffd/selfservice/templates/selfservice/self.html:81
 msgid "Two-factor authentication is currently <strong>disabled</strong>."
-msgstr "Die Zwei-Faktor-Authentifizierung ist derzeit <strong>deaktiviert</strong>."
+msgstr ""
+"Die Zwei-Faktor-Authentifizierung ist derzeit "
+"<strong>deaktiviert</strong>."
 
-#: mfa/templates/mfa/setup.html:23
+#: uffd/mfa/templates/mfa/setup.html:23
 msgid ""
 "You need to generate recovery codes and setup at least one authentication"
 " method to enable two-factor authentication."
@@ -128,7 +488,7 @@ msgstr ""
 "Authentifizierungsmethode hinzufügen um Zwei-Faktor-Authentifizierung "
 "nutzen zu können."
 
-#: mfa/templates/mfa/setup.html:25
+#: uffd/mfa/templates/mfa/setup.html:25
 msgid ""
 "You need to setup at least one authentication method to enable two-factor"
 " authentication."
@@ -136,15 +496,16 @@ msgstr ""
 "Du musst mindestens eine Authentifizierungsmethode hinzufügen um Zwei-"
 "Faktor-Authentifizierung nutzen zu können."
 
-#: mfa/templates/mfa/setup.html:36
+#: uffd/mfa/templates/mfa/setup.html:36
 msgid "Reset two-factor configuration"
 msgstr "Zwei-Faktor-Authentifizierung zurücksetzen"
 
-#: mfa/templates/mfa/setup.html:46 mfa/templates/mfa/setup_recovery.html:5
+#: uffd/mfa/templates/mfa/setup.html:46
+#: uffd/mfa/templates/mfa/setup_recovery.html:5
 msgid "Recovery Codes"
 msgstr "Wiederherstellungscodes"
 
-#: mfa/templates/mfa/setup.html:48
+#: uffd/mfa/templates/mfa/setup.html:48
 msgid ""
 "Recovery codes allow you to login and setup new two-factor methods when "
 "you lost your registered second factor."
@@ -152,7 +513,7 @@ msgstr ""
 "Wiederherstellungscodes erlauben die Anmeldung und das erneute Hinzufügen"
 " einer Zwei-Faktor-Methode, falls der Zweite Faktor verloren geht."
 
-#: mfa/templates/mfa/setup.html:52
+#: uffd/mfa/templates/mfa/setup.html:52
 msgid ""
 "You need to setup recovery codes before you can setup up authenticator "
 "apps or U2F/FIDO2 devices."
@@ -160,75 +521,63 @@ msgstr ""
 "Du musst Wiederherstellungscodes generieren bevor du einen "
 "Authentifikator-App oder ein U2F/FIDO2 Gerät hinzufen kannst."
 
-#: mfa/templates/mfa/setup.html:54
+#: uffd/mfa/templates/mfa/setup.html:54
 msgid "Each code can only be used once."
 msgstr "Jeder Code kann nur einmal verwendet werden."
 
-#: mfa/templates/mfa/setup.html:62
+#: uffd/mfa/templates/mfa/setup.html:62
 msgid "Generate recovery codes to enable two-factor authentication"
 msgstr ""
 "Generiere Wiederherstellungscodes um die Zwei-Faktor-Authentifizierung zu"
 " aktivieren"
 
-#: mfa/templates/mfa/setup.html:66
+#: uffd/mfa/templates/mfa/setup.html:66
 msgid "Generate new recovery codes"
 msgstr "Generiere neue Wiederherstellungscodes"
 
-#: mfa/templates/mfa/setup.html:75
+#: uffd/mfa/templates/mfa/setup.html:75
 msgid "You have no remaining recovery codes."
 msgstr "Du hast keine Wiederherstellungscodes übrig."
 
-#: mfa/templates/mfa/setup.html:85
+#: uffd/mfa/templates/mfa/setup.html:85
 msgid "Authenticator Apps (TOTP)"
 msgstr "Authentifikator-Apps (TOTP)"
 
-#: mfa/templates/mfa/setup.html:87
+#: uffd/mfa/templates/mfa/setup.html:87
 msgid "Use an authenticator application on your mobile device as a second factor."
 msgstr "Nutze eine Authentifikator-App auf deinem Mobilgerät als zweiten Faktor."
 
-#: mfa/templates/mfa/setup.html:90
+#: uffd/mfa/templates/mfa/setup.html:90
 msgid ""
 "The authenticator app generates a 6-digit one-time code each time you "
 "login.\n"
 "\t\t\tCompatible apps are freely available for most phones."
 msgstr ""
 "Die Authentifikator-App generiert ein 6-stelliges Einmalpasswort für "
-"jeden Login.Passende Apps sind kostenlos verfügbar für die meisten "
+"jeden Login. Passende Apps sind kostenlos verfügbar für die meisten "
 "Mobilgeräte."
 
-#: mfa/templates/mfa/setup.html:98 mfa/templates/mfa/setup.html:99
-#: mfa/templates/mfa/setup.html:107 mfa/templates/mfa/setup.html:157
-#: mfa/templates/mfa/setup.html:158 mfa/templates/mfa/setup.html:169
-msgid "Name"
-msgstr "Name"
-
-#: mfa/templates/mfa/setup.html:100
+#: uffd/mfa/templates/mfa/setup.html:100
 msgid "Setup new app"
 msgstr "Neue App hinzufügen"
 
-#: mfa/templates/mfa/setup.html:108 mfa/templates/mfa/setup.html:170
+#: uffd/mfa/templates/mfa/setup.html:108 uffd/mfa/templates/mfa/setup.html:170
 msgid "Registered On"
 msgstr "Registriert am"
 
-#: mfa/templates/mfa/setup.html:117 mfa/templates/mfa/setup.html:179
-#: role/templates/role/show.html:21 role/templates/role/show.html:24
-#: user/templates/user/show.html:11 user/templates/user/show.html:13
-msgid "Delete"
-msgstr "Löschen"
-
-#: mfa/templates/mfa/setup.html:122
+#: uffd/mfa/templates/mfa/setup.html:122
 msgid "No authenticator apps registered yet"
 msgstr "Bisher keine Authentifikator-Apps registriert"
 
-#: mfa/templates/mfa/setup.html:134
+#: uffd/mfa/templates/mfa/setup.html:134
 msgid "U2F and FIDO2 Devices"
 msgstr "U2F und FIDO2 Geräte"
 
-#: mfa/templates/mfa/setup.html:136
+#: uffd/mfa/templates/mfa/setup.html:136
 msgid "Use an U2F or FIDO2 compatible hardware security key as a second factor."
 msgstr "Nutze einen U2F oder FIDO2 kompatiblen Key als zweiten Faktor."
 
-#: mfa/templates/mfa/setup.html:139
+#: uffd/mfa/templates/mfa/setup.html:139
 msgid ""
 "U2F and FIDO2 devices are not supported by all browsers and can be "
 "particularly difficult to use on mobile\n"
@@ -241,25 +590,25 @@ msgstr ""
 "wird dringend empfohlen ebenfalls eine Authentifikator-App "
 "hinzuzufügen</strong> um einen Login mit allen Browsern zu ermöglichen."
 
-#: mfa/templates/mfa/setup.html:147
+#: uffd/mfa/templates/mfa/setup.html:147
 msgid "U2F/FIDO2 support not enabled"
 msgstr "U2F/FIDO2 Unterstützung nicht aktiviert"
 
-#: mfa/templates/mfa/setup.html:151
+#: uffd/mfa/templates/mfa/setup.html:151
 msgid "Enable javascript in your browser to use U2F and FIDO2 devices!"
 msgstr ""
 "Aktiviere Javascript in deinem Browser, um U2F und FIDO2 Geräte nutzen zu"
 " können!"
 
-#: mfa/templates/mfa/setup.html:161
+#: uffd/mfa/templates/mfa/setup.html:161
 msgid "Setup new device"
 msgstr "Neues Gerät hinzufügen"
 
-#: mfa/templates/mfa/setup.html:184
+#: uffd/mfa/templates/mfa/setup.html:184
 msgid "No U2F/FIDO2 devices registered yet"
 msgstr "Bisher kein U2F/FIDO2 Gerät registriert"
 
-#: mfa/templates/mfa/setup_recovery.html:8
+#: uffd/mfa/templates/mfa/setup_recovery.html:8
 msgid ""
 "Recovery codes allow you to login when you lose access to your "
 "authenticator app or U2F/FIDO device. Each code can\n"
@@ -269,7 +618,7 @@ msgstr ""
 "Authentifikator-App oder das U2F/FIDO2 Gerät verloren geht. Jeder Code "
 "kann nur einmal verwendet werden."
 
-#: mfa/templates/mfa/setup_recovery.html:21
+#: uffd/mfa/templates/mfa/setup_recovery.html:21
 msgid ""
 "These are your new recovery codes. Make sure to store them in a safe "
 "place or you risk losing access to your\n"
@@ -279,15 +628,15 @@ msgstr ""
 "Ort, sonst könntest du den Zugriff auf dein Konto verlieren. Alle "
 "vorherigen Wiederherstellungscodes sind nun ungültig."
 
-#: mfa/templates/mfa/setup_recovery.html:28
+#: uffd/mfa/templates/mfa/setup_recovery.html:28
 msgid "Download codes"
 msgstr "Codes herunterladen"
 
-#: mfa/templates/mfa/setup_recovery.html:30
+#: uffd/mfa/templates/mfa/setup_recovery.html:30
 msgid "Print codes"
 msgstr "Codes ausdrucken"
 
-#: mfa/templates/mfa/setup_totp.html:6
+#: uffd/mfa/templates/mfa/setup_totp.html:6
 msgid ""
 "Install an authenticator application on your mobile device like FreeOTP "
 "or Google Authenticator and scan this QR\n"
@@ -297,7 +646,7 @@ msgstr ""
 "oder Google Authenticator and scanne diesen QR Code. Auf Geräten von "
 "Apple kann die App \"Authenticator\" verwendet werden."
 
-#: mfa/templates/mfa/setup_totp.html:18
+#: uffd/mfa/templates/mfa/setup_totp.html:18
 msgid ""
 "If you are on your mobile device and cannot scan the code, you can click "
 "on it to open it with your\n"
@@ -310,452 +659,882 @@ msgstr ""
 "das nicht funktioniert, gib die folgenden Angaben manuell in die "
 "Authentifikator-App ein:"
 
-#: mfa/templates/mfa/setup_totp.html:23
+#: uffd/mfa/templates/mfa/setup_totp.html:23
 msgid "Issuer"
 msgstr "Herausgeber"
 
-#: mfa/templates/mfa/setup_totp.html:24
+#: uffd/mfa/templates/mfa/setup_totp.html:24
 msgid "Account"
 msgstr "Konto"
 
-#: mfa/templates/mfa/setup_totp.html:25
+#: uffd/mfa/templates/mfa/setup_totp.html:25
 msgid "Secret"
 msgstr "Geheimnis"
 
-#: mfa/templates/mfa/setup_totp.html:26
+#: uffd/mfa/templates/mfa/setup_totp.html:26
 msgid "Type"
 msgstr "Typ"
 
-#: mfa/templates/mfa/setup_totp.html:27
+#: uffd/mfa/templates/mfa/setup_totp.html:27
 msgid "Digits"
 msgstr "Zeichen"
 
-#: mfa/templates/mfa/setup_totp.html:28
+#: uffd/mfa/templates/mfa/setup_totp.html:28
 msgid "Hash algorithm"
 msgstr "Hash-Algorithmus"
 
-#: mfa/templates/mfa/setup_totp.html:29
+#: uffd/mfa/templates/mfa/setup_totp.html:29
 msgid "Interval/period"
 msgstr "Intervall/Dauer"
 
-#: mfa/templates/mfa/setup_totp.html:29
+#: uffd/mfa/templates/mfa/setup_totp.html:29
 msgid "seconds"
 msgstr "Sekunden"
 
-#: mfa/templates/mfa/setup_totp.html:38
+#: uffd/mfa/templates/mfa/setup_totp.html:38
 msgid "Verify and complete setup"
 msgstr "Verifiziere und beende das Setup"
 
-#: role/views.py:44 session/views.py:126 user/views_group.py:14
-#: user/views_user.py:22
+#: uffd/oauth2/views.py:95 uffd/selfservice/views.py:79
+#: uffd/session/views.py:93
+#, python-format
+msgid ""
+"We received too many requests from your ip address/network! Please wait "
+"at least %(delay)s."
+msgstr ""
+"Wir haben zu viele Anfragen von deiner IP-Adresses bzw. aus deinem "
+"Netzwerk empfangen! Bitte warte mindestens %(delay)s."
+
+#: uffd/oauth2/views.py:103
+msgid "Device login is currently not available. Try again later!"
+msgstr "Geräte-Login ist gerade nicht verfügbar. Versuche es später nochmal!"
+
+#: uffd/oauth2/templates/oauth2/logout.html:10 uffd/templates/base.html:99
+msgid "Logout"
+msgstr "Abmelden"
+
+#: uffd/oauth2/templates/oauth2/logout.html:15
+msgid "Javascript is required for automatic logout"
+msgstr "Für das automatische Abmelden muss Javascript aktiviert sein"
+
+#: uffd/oauth2/templates/oauth2/logout.html:17
+msgid ""
+"While you successfully logged out of the Single-Sign-On service, you may "
+"still be logged in on these services:"
+msgstr ""
+"Während du nun aus dem Single-Sign-On abgemeldet bist, bist du eventuell "
+"weiterhin in folgenden Diensten angemeldet:"
+
+#: uffd/oauth2/templates/oauth2/logout.html:30
+msgid ""
+"Please wait until you have been automatically logged out of all services "
+"or make sure of this yourself."
+msgstr ""
+"Bitte warte, bis das automatische Abmelden bei allen Diensten "
+"abgeschlossen ist oder melde dich überall manuell ab."
+
+#: uffd/oauth2/templates/oauth2/logout.html:34
+msgid "Logging you out on all services ..."
+msgstr "Melde dich bei allen Diensten ab ..."
+
+#: uffd/oauth2/templates/oauth2/logout.html:38
+msgid "Skip this and continue"
+msgstr "Automatisches Abmelden überspringen"
+
+#: uffd/oauth2/templates/oauth2/logout.html:82
+msgid "Done, redirecting ..."
+msgstr "Abgeschlossen, leite weiter ..."
+
+#: uffd/oauth2/templates/oauth2/logout.html:86
+msgid "Log out failed on some services. Retry?"
+msgstr ""
+"Automatisches Abmelden bei einigen Diensten fehlgeschlagen. Nochmal "
+"versuchen?"
+
+#: uffd/role/views.py:44 uffd/rolemod/views.py:36 uffd/rolemod/views.py:45
+#: uffd/rolemod/views.py:60 uffd/session/views.py:130
+#: uffd/user/views_group.py:14 uffd/user/views_user.py:22
 msgid "Access denied"
 msgstr "Zugriff verweigert"
 
-#: role/views.py:51 user/templates/user/show.html:21
-#: user/templates/user/show.html:93
+#: uffd/role/views.py:51 uffd/selfservice/templates/selfservice/self.html:92
+#: uffd/user/templates/user/list.html:20 uffd/user/templates/user/show.html:21
+#: uffd/user/templates/user/show.html:90
 msgid "Roles"
 msgstr "Rollen"
 
-#: role/views.py:101
+#: uffd/role/views.py:101
 msgid "Locked roles cannot be deleted"
 msgstr "Gesperrte Rollen können nicht gelöscht werden"
 
-#: role/templates/role/list.html:8 user/templates/user/list.html:8
-msgid "New"
-msgstr "Neu"
-
-#: role/templates/role/list.html:14
-msgid "roleid"
-msgstr "Rollen ID"
-
-#: role/templates/role/list.html:15 role/templates/role/show.html:86
-#: role/templates/role/show.html:127 user/templates/group/list.html:10
-#: user/templates/group/show.html:11 user/templates/user/show.html:98
-#: user/templates/user/show.html:130
-msgid "name"
-msgstr "Name"
-
-#: role/templates/role/list.html:16 role/templates/role/show.html:87
-#: role/templates/role/show.html:128 user/templates/group/list.html:11
-#: user/templates/user/show.html:99 user/templates/user/show.html:131
-msgid "description"
-msgstr "Beschreibung"
+#: uffd/role/templates/role/list.html:23
+msgid "<empty name>"
+msgstr "<leerer Name>"
 
-#: role/templates/role/show.html:6
+#: uffd/role/templates/role/show.html:6
 msgid ""
 "Name, moderator group, included roles and groups of this role are managed"
 " externally."
 msgstr ""
-"Name, Moderator:innengruppe, enthaltene Rollen und Gruppen dieser Rolle "
+"Name, Moderationsgruppe, enthaltene Rollen und Gruppen dieser Rolle "
 "werden extern verwaltet."
 
-#: role/templates/role/show.html:13
-#: selfservice/templates/selfservice/self.html:67
-#: user/templates/user/show.html:7
-msgid "Save"
-msgstr "Speichern"
+#: uffd/role/templates/role/show.html:17
+msgid ""
+"All non-service users will be removed as members from this role and get "
+"its permissions implicitly. Are you sure?"
+msgstr ""
+"Alle Benutzer-Accounts, die keine Service-Accounts sind, verlieren diese "
+"Rolle und erhalten dessen Berechtigungen implizit."
 
-#: role/templates/role/show.html:17 role/templates/role/show.html:23
+#: uffd/role/templates/role/show.html:17 uffd/role/templates/role/show.html:23
 msgid "Set as default"
 msgstr "Als Default setzen"
 
-#: role/templates/role/show.html:19
+#: uffd/role/templates/role/show.html:19 uffd/role/templates/role/show.html:21
+#: uffd/selfservice/templates/selfservice/self.html:123
+#: uffd/user/templates/user/show.html:11
+msgid "Are you sure?"
+msgstr "Wirklich fortfahren?"
+
+#: uffd/role/templates/role/show.html:19
 msgid "Unset as default"
 msgstr "Nicht mehr als Default setzen"
 
-#: role/templates/role/show.html:29
+#: uffd/role/templates/role/show.html:29
 msgid "Settings"
 msgstr "Einstellungen"
 
-#: role/templates/role/show.html:32
+#: uffd/role/templates/role/show.html:32
 msgid "Included roles"
 msgstr "Enthaltene Rollen"
 
-#: role/templates/role/show.html:35 role/templates/role/show.html:122
+#: uffd/role/templates/role/show.html:35 uffd/role/templates/role/show.html:122
 msgid "Included groups"
 msgstr "Enthaltene Gruppen"
 
-#: role/templates/role/show.html:42
+#: uffd/role/templates/role/show.html:42
 msgid "Role Name"
 msgstr "Rollenname"
 
-#: role/templates/role/show.html:48
-msgid "Description"
-msgstr "Beschreibung"
-
-#: role/templates/role/show.html:54
+#: uffd/role/templates/role/show.html:54
 msgid "Moderator Group"
-msgstr "Moderator:innengruppe"
+msgstr "Moderationsgruppe"
 
-#: role/templates/role/show.html:56
+#: uffd/role/templates/role/show.html:56
 msgid "No Moderator Group"
-msgstr "Keine Moderator:innengruppe"
+msgstr "Keine Moderationsgruppe"
 
-#: role/templates/role/show.html:63
+#: uffd/role/templates/role/show.html:63
 msgid "Moderators"
-msgstr "Moderator:innen"
+msgstr "Moderatoren"
 
-#: role/templates/role/show.html:71 user/templates/group/show.html:15
+#: uffd/role/templates/role/show.html:71
+#: uffd/rolemod/templates/rolemod/show.html:18
+#: uffd/user/templates/group/show.html:15
 msgid "Members"
 msgstr "Mitglieder"
 
-#: role/templates/role/show.html:81
+#: uffd/role/templates/role/show.html:81
 msgid "Roles to include groups from recursively"
 msgstr "Rollen, deren Gruppen rekursiv enthalten sein sollen"
 
-#: role/templates/role/show.html:88
+#: uffd/role/templates/role/show.html:86 uffd/role/templates/role/show.html:127
+msgid "name"
+msgstr "Name"
+
+#: uffd/role/templates/role/show.html:87 uffd/role/templates/role/show.html:128
+msgid "description"
+msgstr "Beschreibung"
+
+#: uffd/role/templates/role/show.html:88
 msgid "currently includes groups"
 msgstr "derzeit enthaltene Gruppen"
 
-#: role/templates/role/show.html:129
+#: uffd/role/templates/role/show.html:129
 msgid "2FA required"
 msgstr "2FA erforderlich"
 
-#: selfservice/views.py:24
+#: uffd/rolemod/views.py:25
+msgid "Moderation"
+msgstr "Moderation"
+
+#: uffd/rolemod/views.py:49
+msgid "Description too long"
+msgstr "Beschreibung zu lang"
+
+#: uffd/rolemod/views.py:67
+msgid "Member removed"
+msgstr "Mitglied entfernt"
+
+#: uffd/rolemod/templates/rolemod/show.html:8
+msgid "Invite Members"
+msgstr "Mitglieder einladen"
+
+#: uffd/rolemod/templates/rolemod/show.html:15
+msgid "Overview"
+msgstr "Übersicht"
+
+#: uffd/rolemod/templates/rolemod/show.html:24
+msgid "Role name"
+msgstr "Rollenname"
+
+#: uffd/rolemod/templates/rolemod/show.html:32
+msgid "Moderators:"
+msgstr "Moderatoren:"
+
+#: uffd/rolemod/templates/rolemod/show.html:42
+msgid "Role members:"
+msgstr "Mitglieder:"
+
+#: uffd/rolemod/templates/rolemod/show.html:55
+msgid "Remove"
+msgstr "Entfernen"
+
+#: uffd/selfservice/views.py:25
 msgid "Selfservice"
-msgstr ""
+msgstr "Selfservice"
 
-#: selfservice/views.py:37
+#: uffd/selfservice/views.py:37
 msgid "Display name changed."
 msgstr "Anzeigename geändert."
 
-#: selfservice/views.py:39
+#: uffd/selfservice/views.py:39
 msgid "Display name is not valid."
 msgstr "Anzeigename ist nicht valide."
 
-#: selfservice/views.py:42
-msgid "Passwords do not match"
-msgstr "Die Passwörter stimmen nicht überein"
+#: uffd/selfservice/views.py:42
+msgid "We sent you an email, please verify your mail address."
+msgstr "Wir haben dir eine E-Mail gesendet, bitte prüfe deine E-Mail-Adresse."
 
-#: selfservice/views.py:45
-msgid "Password changed."
-msgstr "Passwort geändert."
+#: uffd/selfservice/views.py:56
+msgid "Password changed"
+msgstr "Passwort geändert"
 
-#: selfservice/views.py:48
-msgid "Password could not be set."
-msgstr "Das Passwort konnte nicht gesetzt werden."
+#: uffd/selfservice/views.py:59
+msgid "Invalid password"
+msgstr "Passwort ungültig"
 
-#: selfservice/views.py:51
-msgid "We sent you an email, please verify your mail address."
-msgstr "Wir haben dir eine E-Mail gesendet, bitte prüfe deine E-Mail-Adresse."
+#: uffd/selfservice/views.py:77
+#, python-format
+msgid ""
+"We received too many password reset requests for this user! Please wait "
+"at least %(delay)s."
+msgstr ""
+"Wir haben zu viele fehlgeschlagene Anmeldeversuche für diesen Account! "
+"Bitte warte mindestens %(delay)s."
+
+#: uffd/selfservice/views.py:83
+msgid ""
+"We sent a mail to this user's mail address if you entered the correct "
+"mail and login name combination"
+msgstr ""
+"Falls E-Mail-Adresse und Benutzername richtig waren, wurde eine E-Mail an"
+" die Adresse gesendet."
+
+#: uffd/selfservice/views.py:93 uffd/selfservice/views.py:121
+msgid "Token expired, please try again."
+msgstr "Link abgelaufen, bitte versuche es erneut."
+
+#: uffd/selfservice/views.py:101
+msgid "You need to set a password, please try again."
+msgstr "Password fehlt, bitte versuche es erneut."
+
+#: uffd/selfservice/views.py:104
+msgid "Passwords do not match, please try again."
+msgstr "Die Passwörter stimmen nicht überein, bitte versuche es erneut"
+
+#: uffd/selfservice/views.py:108
+msgid "Password ist not valid, please try again."
+msgstr "Ungültiges Passwort, bitte versuche es erneut"
+
+#: uffd/selfservice/views.py:111
+msgid "New password set"
+msgstr "Passwort geändert"
+
+#: uffd/selfservice/views.py:129
+msgid "New mail set"
+msgstr "E-Mail-Adresse geändert"
+
+#: uffd/selfservice/views.py:140
+msgid "Leaving roles is disabled"
+msgstr "Verlassen von Rollen ist deaktiviert"
+
+#: uffd/selfservice/views.py:147
+#, python-format
+msgid "You left role \"%(role_name)s\""
+msgstr "Rolle \"%(role_name)s\" verlassen"
+
+#: uffd/selfservice/views.py:211
+#, python-format
+msgid "Mail to \"%(mail_address)s\" could not be sent!"
+msgstr "E-Mail an \"%(mail_address)s\" konnte nicht gesendet werden!"
 
-#: selfservice/templates/selfservice/forgot_password.html:11
+#: uffd/selfservice/templates/selfservice/forgot_password.html:11
 msgid "Forgot password"
 msgstr "Passwort vergessen"
 
-#: selfservice/templates/selfservice/forgot_password.html:14
-#: selfservice/templates/selfservice/self.html:25
-#: session/templates/session/login.html:14 user/templates/user/show.html:51
+#: uffd/selfservice/templates/selfservice/forgot_password.html:14
+#: uffd/selfservice/templates/selfservice/self.html:22
+#: uffd/session/templates/session/login.html:14
+#: uffd/signup/templates/signup/start.html:19
+#: uffd/user/templates/user/list.html:18 uffd/user/templates/user/show.html:48
 msgid "Login Name"
-msgstr "Anmeldename"
+msgstr "Benutzername"
 
-#: selfservice/templates/selfservice/forgot_password.html:18
+#: uffd/selfservice/templates/selfservice/forgot_password.html:18
 msgid "Mail Address"
-msgstr "Mail-Adresse"
+msgstr "E-Mail-Adresse"
 
-#: selfservice/templates/selfservice/forgot_password.html:22
+#: uffd/selfservice/templates/selfservice/forgot_password.html:22
 msgid "Send password reset mail"
 msgstr "Passwort-Zurücksetzen-Mail versenden"
 
-#: selfservice/templates/selfservice/self.html:7
+#: uffd/selfservice/templates/selfservice/self.html:7
 msgid ""
 "Some permissions require you to setup two-factor authentication.\n"
 "\tThese permissions are not in effect until you do that."
 msgstr ""
+"Einige deiner Berechtigungen erfordern das Einrichten von Zwei-Faktor-"
+"Authentifizierung.\n"
+"\tDiese Berechtigungen werden erst aktiv, wenn du dies getan hast."
 
-#: selfservice/templates/selfservice/self.html:13
-msgid "Manage two-factor authentication"
-msgstr "Zwei-Faktor-Authentifizierung (2FA) bearbeiten"
+#: uffd/selfservice/templates/selfservice/self.html:14
+#: uffd/user/templates/user/show.html:18
+msgid "Profile"
+msgstr "Profile"
 
-#: selfservice/templates/selfservice/self.html:21
-msgid "Uid"
+#: uffd/selfservice/templates/selfservice/self.html:15
+msgid ""
+"Your profile information is used by all services that are integrated into"
+" the Single-Sign-On. Your e-mail address is also used for password "
+"recovery."
 msgstr ""
-
-#: selfservice/templates/selfservice/self.html:29
-#: user/templates/user/show.html:66
+"Deine Profilangaben werden von allen Diensten verwendet, die an das "
+"Single-Sign-On angeschlossen sind. Die E-Mail-Adresse wird außerdem für "
+"die „Passwort vergessen“ genutzt."
+
+#: uffd/selfservice/templates/selfservice/self.html:16
+msgid "Changes may take serveral minutes to be visible in all services."
+msgstr "Änderungen sind erst nach einigen Minuten in allen Diensten sichtbar."
+
+#: uffd/selfservice/templates/selfservice/self.html:26
+#: uffd/user/templates/user/show.html:28
+msgid "User ID"
+msgstr "Benutzer ID"
+
+#: uffd/selfservice/templates/selfservice/self.html:31
+#: uffd/signup/templates/signup/start.html:32
+#: uffd/user/templates/user/list.html:19 uffd/user/templates/user/show.html:63
 msgid "Display Name"
 msgstr "Anzeigename"
 
-#: selfservice/templates/selfservice/self.html:33
-#: user/templates/user/show.html:73
-msgid "Mail"
+#: uffd/selfservice/templates/selfservice/self.html:35
+#: uffd/signup/templates/signup/start.html:39
+msgid "E-Mail Address"
 msgstr "E-Mail-Adresse"
 
-#: selfservice/templates/selfservice/self.html:36
-msgid "We will send you a confirmation mail to set a new mail address."
+#: uffd/selfservice/templates/selfservice/self.html:38
+msgid "We will send you a confirmation mail to this address if you change it"
 msgstr ""
 "Wir werden dir eine Bestätigungsmail zum Setzen der neuen E-Mail-Adresse "
 "senden."
 
-#: selfservice/templates/selfservice/self.html:40
-#: session/templates/session/login.html:18 user/templates/user/show.html:80
+#: uffd/selfservice/templates/selfservice/self.html:41
+msgid "Update Profile"
+msgstr "Änderungen speichern"
+
+#: uffd/selfservice/templates/selfservice/self.html:50
+#: uffd/session/templates/session/login.html:18
+#: uffd/signup/templates/signup/start.html:46
+#: uffd/user/templates/user/show.html:77
 msgid "Password"
 msgstr "Passwort"
 
-#: selfservice/templates/selfservice/self.html:43
-#: selfservice/templates/selfservice/set_password.html:17
-#: user/templates/user/show.html:87
+#: uffd/selfservice/templates/selfservice/self.html:51
 msgid ""
-"At least 8 and at most 256 characters, no other special requirements. But"
-" please don't be stupid, do use a password manager."
+"Your login password for the Single-Sign-On. Only enter it on the Single-"
+"Sign-On login page! No other legit websites will ask you for this "
+"password. We do not ever need your password to assist you."
 msgstr ""
-"Mindestens 8 und maximal 256 Zeichen, keine weiteren Einschränkungen. "
-"Bitte sei nicht dumm und verwende einen Passwort-Manager."
+"Dein Passwort zur Anmeldung im Single-Sign-On. Gib dieses Passwort "
+"ausschließlich auf der Anmeldeseite des Single-Sign-Ons ein! Keine andere"
+" Webseite wird dich nach diesem Passwort fragen. Es wird auch niemals für"
+" Support-Anfragen benötigt."
 
-#: selfservice/templates/selfservice/self.html:47
-msgid "Password Repeat"
+#: uffd/selfservice/templates/selfservice/self.html:56
+#: uffd/selfservice/templates/selfservice/set_password.html:14
+msgid "New Password"
+msgstr "Neues Passwort"
+
+#: uffd/selfservice/templates/selfservice/self.html:58
+#: uffd/user/templates/user/show.html:84
+msgid "At least 8 and at most 256 characters, no other special requirements."
+msgstr "Mindestens 8 und maximal 256 Zeichen, keine weiteren Einschränkungen."
+
+#: uffd/selfservice/templates/selfservice/self.html:62
+#: uffd/selfservice/templates/selfservice/set_password.html:21
+#: uffd/signup/templates/signup/start.html:53
+msgid "Repeat Password"
 msgstr "Passwort wiederholen"
 
-#: selfservice/templates/selfservice/self.html:53
-msgid "You have this role"
-msgstr "Du hast diese Rolle"
+#: uffd/selfservice/templates/selfservice/self.html:64
+msgid "Change Password"
+msgstr "Passwort ändern"
+
+#: uffd/selfservice/templates/selfservice/self.html:74
+msgid ""
+"Setting up Two-Factor Authentication (2FA) adds an additional step to the"
+" Single-Sign-On login and increases the security of your account "
+"significantly."
+msgstr ""
+"Zwei-Faktor-Authentifizierung (2FA) fügt einen zusätzlichen Schritt zur "
+"Anmeldung im Single-Sign-On hinzu und verbessert damit die Sicherheit "
+"deines Accounts erheblich."
+
+#: uffd/selfservice/templates/selfservice/self.html:84
+msgid "Manage two-factor authentication"
+msgstr "Zwei-Faktor-Authentifizierung (2FA) verwalten"
+
+#: uffd/selfservice/templates/selfservice/self.html:93
+msgid ""
+"Aside from a set of base permissions, your roles determine the "
+"permissions of your account."
+msgstr ""
+"Deine Berechtigungen werden, von einigen Basis-Berechtigungen abgesehen, "
+"von deinen Rollen bestimmt"
+
+#: uffd/selfservice/templates/selfservice/self.html:95
+#, python-format
+msgid ""
+"See <a href=\"%(services_url)s\">Services</a> for an overview of your "
+"current permissions."
+msgstr ""
+"Auf <a href=\"%(services_url)s\">Dienste</a> erhälst du einen Überblick "
+"über deine aktuellen Berechtigungen."
+
+#: uffd/selfservice/templates/selfservice/self.html:100
+msgid "Administrators and role moderators can invite you to new roles."
+msgstr "Administratoren und Rollen-Moderatoren können dich zu Rollen einladen."
+
+#: uffd/selfservice/templates/selfservice/self.html:102
+msgid "Administrators can add new roles to your account."
+msgstr "Administratoren können dich zu neuen Rollen hinzufügen."
+
+#: uffd/selfservice/templates/selfservice/self.html:117
+msgid ""
+"Some permissions in this role require you to setup two-factor "
+"authentication"
+msgstr ""
+"Einige Berechtigungen dieser Rolle erfordern das Einrichten von Zwei-"
+"Faktor-Authentifikation"
 
-#: selfservice/templates/selfservice/self.html:55
-msgid "You currently have these roles"
-msgstr "Du hast aktuell folgende Rollen"
+#: uffd/selfservice/templates/selfservice/self.html:124
+msgid "Leave"
+msgstr "Verlassen"
 
-#: selfservice/templates/selfservice/self.html:63
-msgid "You currently don't have any roles."
-msgstr "Du hast aktuell keine Rollen."
+#: uffd/selfservice/templates/selfservice/self.html:132
+msgid "You currently don't have any roles"
+msgstr "Du hast derzeit keine Rollen"
 
-#: selfservice/templates/selfservice/set_password.html:11
+#: uffd/selfservice/templates/selfservice/set_password.html:11
 msgid "Reset password"
 msgstr "Passwort zurücksetzen"
 
-#: selfservice/templates/selfservice/set_password.html:14
-msgid "New Password"
-msgstr "Neues Passwort"
-
-#: selfservice/templates/selfservice/set_password.html:21
-msgid "Repeat Password"
-msgstr "Passwort wiederholen"
+#: uffd/selfservice/templates/selfservice/set_password.html:17
+#: uffd/signup/templates/signup/start.html:49
+msgid ""
+"At least 8 and at most 256 characters, no other special requirements. But"
+" please don't be stupid, do use a password manager."
+msgstr ""
+"Mindestens 8 und maximal 256 Zeichen, keine weiteren Einschränkungen. "
+"Bitte sei nicht dumm und verwende einen Passwort-Manager."
 
-#: selfservice/templates/selfservice/set_password.html:25
+#: uffd/selfservice/templates/selfservice/set_password.html:25
 msgid "Set password"
 msgstr "Passwort setzen"
 
-#: services/views.py:83
+#: uffd/services/views.py:83
 msgid "Services"
-msgstr ""
+msgstr "Dienste"
 
-#: services/templates/services/overview.html:8
+#: uffd/services/templates/services/overview.html:8
 msgid ""
 "Some services may not be publicly listed! Log in to see all services you "
 "have access to."
 msgstr ""
-"Einige Services sind eventuell nicht öffentlich aufgelistet! Melde dich "
-"an um alle deine Service zu sehen."
+"Einige Dienste sind eventuell nicht öffentlich aufgelistet! Melde dich an"
+" um alle Dienste zu sehen, auf die du Zugriff hast."
 
-#: services/templates/services/overview.html:44
+#: uffd/services/templates/services/overview.html:44
 msgid "No access"
 msgstr "Kein Zugriff"
 
-#: services/templates/services/overview.html:78
-#: user/templates/user/list.html:58 user/templates/user/list.html:81
+#: uffd/services/templates/services/overview.html:78
+#: uffd/user/templates/user/list.html:58 uffd/user/templates/user/list.html:79
 msgid "Close"
 msgstr "Schließen"
 
-#: session/views.py:87
+#: uffd/session/views.py:91
 #, python-format
 msgid ""
 "We received too many invalid login attempts for this user! Please wait at"
-" least %s."
-msgstr ""
-"Wir haben zu viele fehlgeschlagene Anmeldeversuche für diesen Account! "
-"Bitte warte mindestens %s."
-
-#: session/views.py:89
-#, python-format
-msgid ""
-"We received too many requests from your ip address/network! Please wait "
-"at least %s."
+" least %(delay)s."
 msgstr ""
-"Wir haben zu viele Anfragen von der IP Adresses / aus deinem Netzwerk "
-"bekommen! Bitte warte mindestens %s."
+"Wir haben zu viele fehlgeschlagene Anmeldeversuche für diesen Account "
+"erhalten! Bitte warte mindestens %(delay)s."
 
-#: session/views.py:95
+#: uffd/session/views.py:99
 msgid "Login name or password is wrong"
-msgstr "Der Anmeldename oder das Passwort ist falsch"
+msgstr "Der Benutzername oder das Passwort ist falsch"
 
-#: session/views.py:98
+#: uffd/session/views.py:102
 msgid "You do not have access to this service"
 msgstr "Du hast keinen Zugriff auf diesen Service"
 
-#: session/views.py:110 session/views.py:121
+#: uffd/session/views.py:114 uffd/session/views.py:125
 msgid "You need to login first"
 msgstr "Du musst dich erst anmelden"
 
-#: session/templates/session/login.html:22
+#: uffd/session/views.py:147 uffd/session/views.py:157
+msgid "Initiation code is no longer valid"
+msgstr "Startcode ist nicht mehr gültig"
+
+#: uffd/session/views.py:161
+msgid "Invalid confirmation code"
+msgstr "Ungültiger Bestätigungscode"
+
+#: uffd/session/views.py:173 uffd/session/views.py:184
+msgid "Invalid initiation code"
+msgstr "Ungültiger Startcode"
+
+#: uffd/session/templates/session/deviceauth.html:17
+#: uffd/templates/base.html:92
+msgid "Authorize Device Login"
+msgstr "Gerätelogin erlauben"
+
+#: uffd/session/templates/session/deviceauth.html:20
+msgid "Log into a service on another device without entering your password."
+msgstr ""
+"Melde dich an einem Dienst auf einem anderen Gerät an ohne dein Password "
+"eingeben zu müssen."
+
+#: uffd/session/templates/session/deviceauth.html:23
+#: uffd/session/templates/session/devicelogin.html:18
+msgid "Initiation Code"
+msgstr "Startcode"
+
+#: uffd/session/templates/session/deviceauth.html:32
+#: uffd/session/templates/session/devicelogin.html:23
+msgid "Confirmation Code"
+msgstr "Bestätigungscode"
+
+#: uffd/session/templates/session/deviceauth.html:38
+msgid ""
+"Start logging into a service on the other device and chose \"Device "
+"Login\" on the login page. Enter the displayed initiation code in the box"
+" above."
+msgstr ""
+"Beginne die Anmeldung an einem Dienst auf dem anderen Gerät und wähle "
+"\"Gerätelogin\" auf der Anmeldeseite aus. Gib den angezeigten Startcode "
+"oben ein."
+
+#: uffd/session/templates/session/deviceauth.html:41
+#: uffd/session/templates/session/devicelogin.html:30
+msgid "Continue"
+msgstr "Weiter"
+
+#: uffd/session/templates/session/deviceauth.html:48
+#, python-format
+msgid "Authorize the login for service <b>%(service_name)s</b>?"
+msgstr "Anmeldung an Dienst <b>%(service_name)s</b> erlauben?"
+
+#: uffd/session/templates/session/deviceauth.html:51
+msgid "Authorize Login"
+msgstr "Anmeldung erlauben"
+
+#: uffd/session/templates/session/deviceauth.html:58
+msgid ""
+"Enter the confirmation code on the other device and complete the login. "
+"Click <em>Finish</em> afterwards."
+msgstr ""
+"Gib den Bestätigungscode auf dem anderen Gerät ein und schließe die "
+"Anmeldung ab. Clicke danach auf <em>Abschließen</em>."
+
+#: uffd/session/templates/session/deviceauth.html:61
+msgid "Finish"
+msgstr "Beenden"
+
+#: uffd/session/templates/session/devicelogin.html:11
+#: uffd/session/templates/session/login.html:27 uffd/templates/base.html:93
+msgid "Device Login"
+msgstr "Geratelogin"
+
+#: uffd/session/templates/session/devicelogin.html:14
+msgid ""
+"Use a login session on another device (e.g. your laptop) to log into a "
+"service without entering your password."
+msgstr ""
+"Nutze eine Login-Sitzung auf einem anderen Gerät (z.B. deinem Laptop) um "
+"dich bei einem Dienst anzumelden."
+
+#: uffd/session/templates/session/devicelogin.html:27
+#, python-format
+msgid ""
+"Open <code><a href=\"%(deviceauth_url)s\">%(deviceauth_url)s</a></code> "
+"on the other device and enter the initiation code there. Then enter the "
+"confirmation code in the box above."
+msgstr ""
+"Öffne <code><a href=\"%(deviceauth_url)s\">%(deviceauth_url)s</a></code> "
+"auf dem anderen Gerät und gib dort den obenstehenden Startcode ein. Geben"
+" anschließend den Bestätigungscode hier ein."
+
+#: uffd/session/templates/session/login.html:11
+#: uffd/session/templates/session/login.html:22
 msgid "Login"
 msgstr "Anmelden"
 
-#: session/templates/session/login.html:26
+#: uffd/session/templates/session/login.html:25
+msgid "- or -"
+msgstr "- oder -"
+
+#: uffd/session/templates/session/login.html:32
 msgid "Register"
 msgstr "Registrieren"
 
-#: session/templates/session/login.html:29
+#: uffd/session/templates/session/login.html:35
 msgid "Forgot Password?"
 msgstr "Passwort vergessen?"
 
-#: templates/base.html:75
-msgid "Logout"
-msgstr "Abmelden"
+#: uffd/signup/views.py:23
+msgid "Singup not enabled"
+msgstr "Account-Registrierung ist deaktiviert"
+
+#: uffd/signup/views.py:77 uffd/signup/views.py:85
+msgid "Invalid signup link"
+msgstr "Ungültiger Account-Registrierungs-Link"
+
+#: uffd/signup/views.py:90
+#, python-format
+msgid "Too many failed attempts! Please wait %(delay)s."
+msgstr "Zu viele fehlgeschlagene Versuche! Bitte warte mindestens %(delay)s."
+
+#: uffd/signup/views.py:96
+msgid "Wrong password"
+msgstr "Falsches Passwort"
+
+#: uffd/signup/views.py:103
+msgid "Your account was successfully created"
+msgstr "Account erfolgreich erstellt"
 
-#: templates/base.html:108
+#: uffd/signup/templates/signup/confirm.html:11
+msgid "Complete Registration"
+msgstr "Account-Registrierung abschließen"
+
+#: uffd/signup/templates/signup/confirm.html:17
+msgid "Please enter your password to complete the account registration"
+msgstr "Bitte gib dein Passwort ein, um die Account-Registrierung abzuschließen"
+
+#: uffd/signup/templates/signup/confirm.html:21
+msgid "Complete Account Registration"
+msgstr "Account-Registrierung abschließen"
+
+#: uffd/signup/templates/signup/start.html:23
+msgid "Check"
+msgstr "Überprüfen"
+
+#: uffd/signup/templates/signup/start.html:28
+msgid ""
+"At least one and at most 32 lower-case characters, digits, dashes (\"-\")"
+" or underscores (\"_\"). <b>Cannot be changed later!</b>"
+msgstr ""
+"1 bis 32 Kleinbuchstaben, Zahlen, Binde- (\"-\") und Unterstriche "
+"(\"_\"). <b>Kann später nicht geändert werden!</b>"
+
+#: uffd/signup/templates/signup/start.html:35
+msgid "At least one and at most 128 characters, no other special requirements."
+msgstr "Mindestens 1 und maximal 128 Zeichen, keine weiteren Einschränkungen."
+
+#: uffd/signup/templates/signup/start.html:42
+msgid ""
+"We will send a confirmation mail to this address that you need to "
+"complete the registration."
+msgstr ""
+"Wir werden eine Bestätigungsmail an diese Adresse senden. Du benötigst "
+"sie, um die Account-Registrierung abzuschließen."
+
+#: uffd/signup/templates/signup/start.html:57
+msgid "Create Account"
+msgstr "Account registrieren"
+
+#: uffd/signup/templates/signup/start.html:86
+msgid "The name is already taken"
+msgstr "Dieser Name wird bereits verwendet"
+
+#: uffd/signup/templates/signup/start.html:89
+msgid "Too many requests! Please wait a bit before trying again!"
+msgstr "Zu viele Anfragen! Bitte warte etwas, bevor du es erneut versuchst!"
+
+#: uffd/signup/templates/signup/start.html:92
+msgid "The name is invalid"
+msgstr "Name ungültig"
+
+#: uffd/signup/templates/signup/submitted.html:11
+msgid "Confirm your E-Mail Address"
+msgstr "E-Mail-Adresse bestätigen"
+
+#: uffd/signup/templates/signup/submitted.html:13
+#, python-format
+msgid ""
+"We sent a confirmation mail to <b>%(signup_mail)s</b>. You need to "
+"confirm your mail address within 48 hours to complete the account "
+"registration."
+msgstr ""
+"Eine Bestätigungsmail wurde an <b>%(signup_mail)s</b> gesendet. Du must "
+"deine E-Mail-Adresse innerhalb von 48 Stunden bestätigen, um die Account-"
+"Registrierung abzuschließen."
+
+#: uffd/signup/templates/signup/submitted.html:14
+msgid ""
+"If you mistyped your mail address or don't receive the confirmation mail "
+"for another reason, retry the registration procedure from the beginning."
+msgstr ""
+"Falls du dich bei deiner E-Mail-Adresse vertippt hast oder du aus anderen"
+" Gründen keine Bestätigungsmail erhalten hast, kannst du den Prozess "
+"einfach von Vorne beginnen."
+
+#: uffd/templates/base.html:84
+msgid "Change"
+msgstr "Ändern"
+
+#: uffd/templates/base.html:133
 msgid "Sourcecode"
 msgstr "Quellcode"
 
-#: user/views_group.py:21
+#: uffd/user/views_group.py:21
 msgid "Groups"
 msgstr "Gruppen"
 
-#: user/views_user.py:29
+#: uffd/user/views_user.py:29
 msgid "Users"
-msgstr "Nutzer:innen"
+msgstr "Benutzer"
 
-#: user/views_user.py:49
+#: uffd/user/views_user.py:49
 msgid "Login name does not meet requirements"
-msgstr "Anmeldename entspricht nicht den Anforderungen"
+msgstr "Benutzername entspricht nicht den Anforderungen"
 
-#: user/views_user.py:54
+#: uffd/user/views_user.py:54
 msgid "Mail is invalid"
 msgstr "E-Mail-Adresse nicht valide"
 
-#: user/views_user.py:58
+#: uffd/user/views_user.py:58
 msgid "Display name does not meet requirements"
 msgstr "Anzeigename entspricht nicht den Anforderungen"
 
-#: user/views_user.py:75
+#: uffd/user/views_user.py:75
 msgid "Service user created"
 msgstr "Service-Account erstellt"
 
-#: user/views_user.py:78
+#: uffd/user/views_user.py:78
 msgid "User created. We sent the user a password reset link by mail"
 msgstr ""
-"Nutzer:in erstellt. Wir haben der/dem Nutzer:in eine E-Mail mit einem "
-"Passwort Zurücksetzen Link gesendet"
+"Benutzer erstellt. E-Mail mit einem Link zum Setzen des Passworts wurde "
+"versendet."
 
-#: user/views_user.py:80
+#: uffd/user/views_user.py:80
 msgid "User updated"
-msgstr "Nutzer:in aktualisiert"
+msgstr "Benutzer aktualisiert"
 
-#: user/views_user.py:91
+#: uffd/user/views_user.py:91
 msgid "Deleted user"
-msgstr "Nutzer:in gelöscht"
+msgstr "Benutzer gelöscht"
 
-#: user/templates/group/list.html:9 user/templates/group/show.html:7
-msgid "gid"
-msgstr ""
+#: uffd/user/templates/group/list.html:9 uffd/user/templates/group/show.html:7
+msgid "GID"
+msgstr "GID"
 
-#: user/templates/user/list.html:11
+#: uffd/user/templates/user/list.html:11
 msgid "CSV import"
-msgstr "CSV Import"
+msgstr "CSV-Import"
 
-#: user/templates/user/list.html:17
-msgid "uid"
-msgstr ""
-
-#: user/templates/user/list.html:18
-msgid "login name"
-msgstr "Anmeldename"
+#: uffd/user/templates/user/list.html:17
+msgid "UID"
+msgstr "UID"
 
-#: user/templates/user/list.html:19
-msgid "display name"
-msgstr "Anzeigename"
-
-#: user/templates/user/list.html:20
-msgid "roles"
-msgstr "Rollen"
+#: uffd/user/templates/user/list.html:34 uffd/user/templates/user/show.html:30
+msgid "service"
+msgstr "service"
 
-#: user/templates/user/list.html:57
+#: uffd/user/templates/user/list.html:57
 msgid "Import a csv formated list of users"
-msgstr "Importiere eine als CSV formatierte Liste von Nutzer:innen"
+msgstr "Importiere eine als CSV formatierte Liste von Benutzern"
 
-#: user/templates/user/list.html:77
-msgid "Ignore login name blacklist"
+#: uffd/user/templates/user/list.html:64
+msgid ""
+"The format should be \"loginname,mailaddres,roleid1;roleid2\". Neither "
+"setting the display name nor setting passwords is supported (yet). "
+"Example:"
 msgstr ""
+"Das Format sollte \"loginname,mailaddres,roleid1;roleid2\" sein. Der "
+"Anzeigename oder das Password können (derzeit) nicht gesetzt werden. "
+"Beispiel:"
 
-#: user/templates/user/list.html:82
+#: uffd/user/templates/user/list.html:75 uffd/user/templates/user/show.html:58
+msgid "Ignore login name blacklist"
+msgstr "Benutzernamen-Blacklist ignorieren"
+
+#: uffd/user/templates/user/list.html:80
 msgid "Import"
 msgstr "Importieren"
 
-#: user/templates/user/show.html:10
+#: uffd/user/templates/user/show.html:10
 msgid "Reset 2FA"
 msgstr "2FA zurücksetzen"
 
-#: user/templates/user/show.html:18
-msgid "Profile"
-msgstr "Profile"
+#: uffd/user/templates/user/show.html:36
+msgid "will be choosen"
+msgstr "wird automatisch bestimmt"
+
+#: uffd/user/templates/user/show.html:43
+msgid "Service User"
+msgstr "Service-Account"
 
-#: user/templates/user/show.html:54
+#: uffd/user/templates/user/show.html:51
 msgid ""
-"Only letters, numbers and underscore (\"_\") are allowed. At most 32, at "
-"least 2 characters. There is a word blacklist. Must be unique."
+"Only letters, numbers, dashes (\"-\") and underscores (\"_\") are "
+"allowed. At most 32, at least 2 characters. There is a word blacklist. "
+"Must be unique."
 msgstr ""
-"Nur Buchstaben, Zahlen und Unterstriche (\"_\") sind erlaubt. Maximal 32,"
-" mindestens 2 Zeichen. Muss einmalig sein."
+"Nur Buchstaben, Zahlen, Binde- (\"-\") und Unterstriche (\"_\") sind "
+"erlaubt. Maximal 32, mindestens 2 Zeichen. Es gibt eine Blacklist. Muss "
+"einmalig sein."
 
-#: user/templates/user/show.html:69
+#: uffd/user/templates/user/show.html:66
 msgid ""
 "If you leave this empty it will be set to the login name. At most 128, at"
 " least 2 characters. No character restrictions."
 msgstr ""
-"Wenn das Feld leer bleibt, wird der Anmeldename verwendet. Maximal 128, "
-"midestens 2 Zeichen. Keine Zeichenbeschränkung."
+"Wenn das Feld leer bleibt, wird der Benutzername verwendet. Maximal 128, "
+"mindestens 2 Zeichen. Keine Zeichenbeschränkung."
+
+#: uffd/user/templates/user/show.html:70
+msgid "Mail"
+msgstr "E-Mail-Adresse"
 
-#: user/templates/user/show.html:76
+#: uffd/user/templates/user/show.html:73
 msgid ""
 "Do a sanity check here. A user can take over another account if both have"
 " the same mail address set."
 msgstr ""
-"Prüfe die Einmaligkeit. Ein:e Nutzer:in kann einen anderen Account "
-"übernehmen, wenn beide die selbe E-Mail-Adresse verwenden."
+"Überprüfe, ob die E-Mail-Adresse plausibel ist! Ein Benutzer kann einen "
+"anderen Account übernehmen, wenn beide die selbe E-Mail-Adresse "
+"verwenden."
 
-#: user/templates/user/show.html:84
+#: uffd/user/templates/user/show.html:81
 msgid "mail to set it will be sent"
 msgstr "Mail zum Setzen wird versendet"
 
-#: user/templates/user/show.html:126
+#: uffd/user/templates/user/show.html:123
 msgid "Resulting groups (only updated after save)"
 msgstr "Resultierende Gruppen (wird nur aktualisiert beim Speichern)"
 
diff --git a/uffd/user/templates/group/list.html b/uffd/user/templates/group/list.html
index 89abf1ad..e393485d 100644
--- a/uffd/user/templates/group/list.html
+++ b/uffd/user/templates/group/list.html
@@ -6,9 +6,9 @@
 		<table class="table table-striped table-sm">
 			<thead>
 				<tr>
-					<th scope="col">{{_("gid")}}</th>
-					<th scope="col">{{_("name")}}</th>
-					<th scope="col">{{_("description")}}</th>
+					<th scope="col">{{_("GID")}}</th>
+					<th scope="col">{{_("Name")}}</th>
+					<th scope="col">{{_("Description")}}</th>
 				</tr>
 			</thead>
 			<tbody>
diff --git a/uffd/user/templates/group/show.html b/uffd/user/templates/group/show.html
index 71ab805a..d43d7406 100644
--- a/uffd/user/templates/group/show.html
+++ b/uffd/user/templates/group/show.html
@@ -4,11 +4,11 @@
 <form action="{{ url_for("group.show", gid=group.gid) }}" method="POST">
 <div class="align-self-center">
 	<div class="form-group col">
-		<label for="group-gid">{{_("gid")}}</label>
+		<label for="group-gid">{{_("GID")}}</label>
 		<input type="number" class="form-control" id="group-gid" name="gid" value="{{ group.gid }}" readonly>
 	</div>
 	<div class="form-group col">
-		<label for="group-loginname">{{_("name")}}</label>
+		<label for="group-loginname">{{_("Name")}}</label>
 		<input type="text" class="form-control" id="group-loginname" name="loginname" value="{{ group.name }}" readonly>
 	</div>
 	<div class="col"> 
diff --git a/uffd/user/templates/user/list.html b/uffd/user/templates/user/list.html
index 7f5edc7e..013f6fb3 100644
--- a/uffd/user/templates/user/list.html
+++ b/uffd/user/templates/user/list.html
@@ -14,10 +14,10 @@
 		<table class="table table-striped table-sm">
 			<thead>
 				<tr>
-					<th scope="col">{{_("uid")}}</th>
-					<th scope="col">{{_("login name")}}</th>
-					<th scope="col">{{_("display name")}}</th>
-					<th scope="col">{{_("roles")}}</th>
+					<th scope="col">{{_("UID")}}</th>
+					<th scope="col">{{_("Login Name")}}</th>
+					<th scope="col">{{_("Display Name")}}</th>
+					<th scope="col">{{_("Roles")}}</th>
 				</tr>
 			</thead>
 			<tbody>
@@ -31,7 +31,7 @@
 							{{ user.loginname }}
 						</a>
 						{% if user.is_service_user %}
-						<span class="badge badge-secondary">service</span>
+						<span class="badge badge-secondary">{{_('service')}}</span>
 						{% endif %}
 					</td>
 					<td>
@@ -61,9 +61,7 @@
 			</div>
 			<div class="modal-body">
 				<p>
-					The format should be "loginname,mailaddres,roleid1;roleid2".
-					Neither setting the display name nor setting passwords is supported (yet).
-					Example:
+					{{_('The format should be "loginname,mailaddres,roleid1;roleid2". Neither setting the display name nor setting passwords is supported (yet). Example:')}}
 				</p>
 				<pre>
 testuser1,foobar@example.com,5;2;6
diff --git a/uffd/user/templates/user/show.html b/uffd/user/templates/user/show.html
index dff8eb97..9bf051d2 100644
--- a/uffd/user/templates/user/show.html
+++ b/uffd/user/templates/user/show.html
@@ -8,7 +8,7 @@
 		<a href="{{ url_for("user.index") }}" class="btn btn-secondary">{{_("Cancel")}}</a>
 		{% if user.uid %}
 			<a href="{{ url_for("mfa.admin_disable", uid=user.uid) }}" class="btn btn-secondary">{{_("Reset 2FA")}}</a>
-			<a href="{{ url_for("user.delete", uid=user.uid) }}" onClick="return confirm('Are you sure?');" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i> {{_("Delete")}}</a>
+			<a href="{{ url_for("user.delete", uid=user.uid) }}" onClick="return confirm({{_('Are you sure?')|tojson}});" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i> {{_("Delete")}}</a>
 		{% else %}
 			<a href="#" class="btn btn-danger disabled"><i class="fa fa-trash" aria-hidden="true"></i> {{_("Delete")}}</a>
 		{% endif %}
@@ -20,30 +20,27 @@
 		<li class="nav-item">
 			<a class="nav-link" id="roles-tab" data-toggle="tab" href="#roles" role="tab" aria-controls="roles" aria-selected="false">{{_("Roles")}}</a>
 		</li>
-		<li class="nav-item">
-			<a class="nav-link" id="ldif-tab" data-toggle="tab" href="#ldif" role="tab" aria-controls="ldif" aria-selected="false">LDIF</a>
-		</li>
 	</ul>
 	<div class="tab-content border mb-2 pt-2" id="tabcontent">
 		<div class="tab-pane fade show active" id="profile" role="tabpanel" aria-labelledby="roles-tab">
 			<div class="form-group col">
 				<label for="user-uid">
-					uid
+					{{_('User ID')}}
 					{% if user.is_service_user %}
-					<span class="badge badge-secondary">service</span>
+					<span class="badge badge-secondary">{{_('service')}}</span>
 					{% endif %}
 				</label>
 				{% if user.uid %}
 				<input type="number" class="form-control" id="user-uid" name="uid" value="{{ user.uid or '' }}" readonly>
 				{% else %}
-				<input type="text" class="form-control" id="user-uid" name="uid" placeholder="will be choosen" readonly>
+				<input type="text" class="form-control" id="user-uid" name="uid" placeholder="{{_('will be choosen')}}" readonly>
 				{% endif %}
 			</div>
 			{% if not user.uid %}
 			<div class="form-group col">
 				<div class="form-check">
 					<input class="form-check-input" type="checkbox" id="user-serviceaccount" name="serviceaccount" value="1" aria-label="enabled">
-					<label class="form-check-label" for="user-serviceaccount">Service User</label>
+					<label class="form-check-label" for="user-serviceaccount">{{_('Service User')}}</label>
 				</div>
 			</div>
 			{% endif %}
@@ -58,7 +55,7 @@
 			<div class="form-group col">
 				<div class="form-check">
 					<input class="form-check-input" type="checkbox" id="ignore-loginname-blacklist" name="ignore-loginname-blacklist" value="1" aria-label="enabled">
-					<label class="form-check-label" for="ignore-loginname-blacklist">Ignore login name blacklist</label>
+					<label class="form-check-label" for="ignore-loginname-blacklist">{{_('Ignore login name blacklist')}}</label>
 				</div>
 			</div>
 			{% endif %}
@@ -84,7 +81,7 @@
 				<input type="password" class="form-control" id="user-password" name="password" placeholder="{{_("mail to set it will be sent")}}" readonly>
 				{% endif %}
 				<small class="form-text text-muted">
-					{{_("At least 8 and at most 256 characters, no other special requirements. But please don't be stupid, do use a password manager.")}}
+					{{_("At least 8 and at most 256 characters, no other special requirements.")}}
 				</small>
 			</div>
 		</div>
@@ -95,8 +92,8 @@
 					<thead>
 						<tr>
 							<th scope="col"></th>
-							<th scope="col">{{_("name")}}</th>
-							<th scope="col">{{_("description")}}</th>
+							<th scope="col">{{_("Name")}}</th>
+							<th scope="col">{{_("Description")}}</th>
 						</tr>
 					</thead>
 					<tbody>
@@ -127,8 +124,8 @@
 				<table class="table table-striped table-sm">
 					<thead>
 						<tr>
-							<th scope="col">{{_("name")}}</th>
-							<th scope="col">{{_("description")}}</th>
+							<th scope="col">{{_("Name")}}</th>
+							<th scope="col">{{_("Description")}}</th>
 						</tr>
 					</thead>
 					<tbody>
@@ -148,11 +145,6 @@
 				</table>
 			</div>
 		</div>
-		<div class="tab-pane fade" id="ldif" role="tabpanel" aria-labelledby="ldif-tab">
-			<div class="form-group col">
-				<pre>{{ '<not available>'|e }}</pre>
-			</div>
-		</div>
 	</div>
 </div>
 </form>
-- 
GitLab