diff --git a/admin/Dockerfile b/admin/Dockerfile
index 4299285b..8c3f98cd 100644
--- a/admin/Dockerfile
+++ b/admin/Dockerfile
@@ -10,5 +10,6 @@ COPY requirements.txt .
COPY start.sh /start.sh
RUN pip install -r requirements.txt
+RUN pybabel compile -d freeposte/translations
CMD ["/start.sh"]
diff --git a/admin/babel.cfg b/admin/babel.cfg
new file mode 100644
index 00000000..f0234b32
--- /dev/null
+++ b/admin/babel.cfg
@@ -0,0 +1,3 @@
+[python: **.py]
+[jinja2: **/templates/**.html]
+extensions=jinja2.ext.autoescape,jinja2.ext.with_
diff --git a/admin/freeposte/__init__.py b/admin/freeposte/__init__.py
index facf4d0a..8d44fa65 100644
--- a/admin/freeposte/__init__.py
+++ b/admin/freeposte/__init__.py
@@ -4,6 +4,7 @@ import flask_bootstrap
import flask_login
import flask_script
import flask_migrate
+import flask_babel
import os
import docker
@@ -23,7 +24,9 @@ default_config = {
'DEBUG': False,
'BOOTSTRAP_SERVE_LOCAL': True,
'DKIM_PATH': '/dkim/{domain}.{selector}.key',
- 'DKIM_SELECTOR': 'dkim'
+ 'DKIM_SELECTOR': 'dkim',
+ 'BABEL_DEFAULT_LOCALE': 'en',
+ 'BABEL_DEFAULT_TIMEZONE': 'UTC'
}
# Load configuration from the environment if available
@@ -36,6 +39,7 @@ db = flask_sqlalchemy.SQLAlchemy(app)
migrate = flask_migrate.Migrate(app, db)
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
+babel = flask_babel.Babel(app)
# Manager commnad
manager = flask_script.Manager(app)
diff --git a/admin/freeposte/admin/forms.py b/admin/freeposte/admin/forms.py
index 0aa0ec1f..9e79eb13 100644
--- a/admin/freeposte/admin/forms.py
+++ b/admin/freeposte/admin/forms.py
@@ -1,5 +1,6 @@
from wtforms import validators, fields, widgets
from wtforms_components import fields as fields_
+from flask_babel import lazy_gettext as _
import flask_login
import flask_wtf
@@ -28,92 +29,93 @@ class DestinationField(fields.SelectMultipleField):
def pre_validate(self, form):
for item in self.data:
if not self.validator.match(item):
- raise validators.ValidationError("Invalid email address.")
+ raise validators.ValidationError(_('Invalid email address.'))
-class Confirmation(flask_wtf.FlaskForm):
- submit = fields.SubmitField('Confirm')
+class ConfirmationForm(flask_wtf.FlaskForm):
+ submit = fields.SubmitField(_('Confirm'))
class LoginForm(flask_wtf.FlaskForm):
- email = fields.StringField('E-mail', [validators.Email()])
- pw = fields.PasswordField('Password', [validators.DataRequired()])
- submit = fields.SubmitField('Sign in')
+ email = fields.StringField(_('E-mail'), [validators.Email()])
+ pw = fields.PasswordField(_('Password'), [validators.DataRequired()])
+ submit = fields.SubmitField(_('Sign in'))
class DomainForm(flask_wtf.FlaskForm):
- name = fields.StringField('Domain name', [validators.DataRequired()])
- max_users = fields_.IntegerField('Maximum user count', default=10)
- max_aliases = fields_.IntegerField('Maximum alias count', default=10)
- comment = fields.StringField('Comment')
- submit = fields.SubmitField('Create')
+ name = fields.StringField(_('Domain name'), [validators.DataRequired()])
+ max_users = fields_.IntegerField(_('Maximum user count'), default=10)
+ max_aliases = fields_.IntegerField(_('Maximum alias count'), default=10)
+ comment = fields.StringField(_('Comment'))
+ submit = fields.SubmitField(_('Create'))
class UserForm(flask_wtf.FlaskForm):
- localpart = fields.StringField('E-mail', [validators.DataRequired()])
- pw = fields.PasswordField('Password', [validators.DataRequired()])
- pw2 = fields.PasswordField('Confirm password', [validators.EqualTo('pw')])
- quota_bytes = fields_.IntegerSliderField('Quota', default=1000000000)
- enable_imap = fields.BooleanField('Allow IMAP access', default=True)
- enable_pop = fields.BooleanField('Allow POP3 access', default=True)
- comment = fields.StringField('Comment')
- submit = fields.SubmitField('Save')
+ localpart = fields.StringField(_('E-mail'), [validators.DataRequired()])
+ pw = fields.PasswordField(_('Password'), [validators.DataRequired()])
+ pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])
+ quota_bytes = fields_.IntegerSliderField(_('Quota'), default=1000000000)
+ enable_imap = fields.BooleanField(_('Allow IMAP access'), default=True)
+ enable_pop = fields.BooleanField(_('Allow POP3 access'), default=True)
+ comment = fields.StringField(_('Comment'))
+ submit = fields.SubmitField(_('Save'))
class UserSettingsForm(flask_wtf.FlaskForm):
- displayed_name = fields.StringField('Displayed name')
- spam_enabled = fields.BooleanField('Enable spam filter')
- spam_threshold = fields_.IntegerSliderField('Spam filter threshold')
- submit = fields.SubmitField('Save settings')
+ displayed_name = fields.StringField(_('Displayed name'))
+ spam_enabled = fields.BooleanField(_('Enable spam filter'))
+ spam_threshold = fields_.IntegerSliderField(_('Spam filter threshold'))
+ submit = fields.SubmitField(_('Save settings'))
class UserPasswordForm(flask_wtf.FlaskForm):
- pw = fields.PasswordField('Password', [validators.DataRequired()])
- pw2 = fields.PasswordField('Password check', [validators.DataRequired()])
- submit = fields.SubmitField('Update password')
+ pw = fields.PasswordField(_('Password'), [validators.DataRequired()])
+ pw2 = fields.PasswordField(_('Password check'), [validators.DataRequired()])
+ submit = fields.SubmitField(_('Update password'))
class UserForwardForm(flask_wtf.FlaskForm):
- forward_enabled = fields.BooleanField('Enable forwarding')
+ forward_enabled = fields.BooleanField(_('Enable forwarding'))
forward_destination = fields.StringField(
- 'Destination', [validators.Optional(), validators.Email()]
+ _('Destination'), [validators.Optional(), validators.Email()]
)
- submit = fields.SubmitField('Update')
+ submit = fields.SubmitField(_('Update'))
class UserReplyForm(flask_wtf.FlaskForm):
- reply_enabled = fields.BooleanField('Enable automatic reply')
- reply_subject = fields.StringField('Reply subject')
- reply_body = fields.StringField('Reply body', widget=widgets.TextArea())
- submit = fields.SubmitField('Update')
+ reply_enabled = fields.BooleanField(_('Enable automatic reply'))
+ reply_subject = fields.StringField(_('Reply subject'))
+ reply_body = fields.StringField(_('Reply body'),
+ widget=widgets.TextArea())
+ submit = fields.SubmitField(_('Update'))
class AliasForm(flask_wtf.FlaskForm):
- localpart = fields.StringField('Alias', [validators.DataRequired()])
+ localpart = fields.StringField(_('Alias'), [validators.DataRequired()])
wildcard = fields.BooleanField(
- 'Use SQL Like Syntax (e.g. for catch-all aliases, admin-%@domain.com)')
- destination = DestinationField('Destination')
- comment = fields.StringField('Comment')
- submit = fields.SubmitField('Create')
+ _('Use SQL LIKE Syntax (e.g. for catch-all aliases)'))
+ destination = DestinationField(_('Destination'))
+ comment = fields.StringField(_('Comment'))
+ submit = fields.SubmitField(_('Create'))
class AdminForm(flask_wtf.FlaskForm):
- admin = fields.SelectField('Admin email', choices=[])
- submit = fields.SubmitField('Submit')
+ admin = fields.SelectField(_('Admin email'), choices=[])
+ submit = fields.SubmitField(_('Submit'))
class ManagerForm(flask_wtf.FlaskForm):
- manager = fields.SelectField('Manager email')
- submit = fields.SubmitField('Submit')
+ manager = fields.SelectField(_('Manager email'))
+ submit = fields.SubmitField(_('Submit'))
class FetchForm(flask_wtf.FlaskForm):
- protocol = fields.SelectField('Protocol', choices=[
+ protocol = fields.SelectField(_('Protocol'), choices=[
('imap', 'IMAP'), ('pop3', 'POP3')
])
- host = fields.StringField('Hostname or IP')
- port = fields.IntegerField('TCP port')
- tls = fields.BooleanField('Enable TLS')
- username = fields.StringField('Username')
- password = fields.StringField('Password')
- submit = fields.SubmitField('Submit')
+ host = fields.StringField(_('Hostname or IP'))
+ port = fields.IntegerField(_('TCP port'))
+ tls = fields.BooleanField(_('Enable TLS'))
+ username = fields.StringField(_('Username'))
+ password = fields.StringField(_('Password'))
+ submit = fields.SubmitField(_('Submit'))
diff --git a/admin/freeposte/admin/templates/admin/create.html b/admin/freeposte/admin/templates/admin/create.html
index 2d9024b5..6fe7e67b 100644
--- a/admin/freeposte/admin/templates/admin/create.html
+++ b/admin/freeposte/admin/templates/admin/create.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}
-Add a global administrator
+{% trans %}Add a global administrator{% endtrans %}
{% endblock %}
{% block box_content %}
diff --git a/admin/freeposte/admin/templates/admin/list.html b/admin/freeposte/admin/templates/admin/list.html
index 01054991..d286fb3f 100644
--- a/admin/freeposte/admin/templates/admin/list.html
+++ b/admin/freeposte/admin/templates/admin/list.html
@@ -1,24 +1,26 @@
{% extends "base.html" %}
{% block title %}
-Global administrators
+{% trans %}Global administrators{% endtrans %}
{% endblock %}
{% block main_action %}
-Add administrator
+
+ {% trans %}Add administrator{% endtrans %}
+
{% endblock %}
{% block box %}
- Actions |
- Email |
+ {% trans %}Actions{% endtrans %} |
+ {% trans %}Email{% endtrans %} |
{% for admin in admins %}
-
+
|
{{ admin }} |
diff --git a/admin/freeposte/admin/templates/alias/create.html b/admin/freeposte/admin/templates/alias/create.html
index 30377984..c848464b 100644
--- a/admin/freeposte/admin/templates/alias/create.html
+++ b/admin/freeposte/admin/templates/alias/create.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}
-Create alias
+{% trans %}Create alias{% endtrans %}
{% endblock %}
{% block subtitle %}
diff --git a/admin/freeposte/admin/templates/alias/edit.html b/admin/freeposte/admin/templates/alias/edit.html
index e446dbf7..b28ea170 100644
--- a/admin/freeposte/admin/templates/alias/edit.html
+++ b/admin/freeposte/admin/templates/alias/edit.html
@@ -1,7 +1,7 @@
{% extends "alias/create.html" %}
{% block title %}
-Edit alias
+{% trans %}Edit alias{% endtrans %}
{% endblock %}
{% block subtitle %}
diff --git a/admin/freeposte/admin/templates/alias/list.html b/admin/freeposte/admin/templates/alias/list.html
index 3d752329..d7380ef5 100644
--- a/admin/freeposte/admin/templates/alias/list.html
+++ b/admin/freeposte/admin/templates/alias/list.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}
-Alias list
+{% trans %}Alias list{% endtrans %}
{% endblock %}
{% block subtitle %}
@@ -9,25 +9,25 @@ Alias list
{% endblock %}
{% block main_action %}
-Add alias
+{% trans %}Add alias{% endtrans %}
{% endblock %}
{% block box %}
- Actions |
- Email |
- Destination |
- Comment |
- Created |
- Last edit |
+ {% trans %}Actions{% endtrans %} |
+ {% trans %}Email{% endtrans %} |
+ {% trans %}Destination{% endtrans %} |
+ {% trans %}Comment{% endtrans %} |
+ {% trans %}Created{% endtrans %} |
+ {% trans %}Last edit{% endtrans %} |
{% for alias in domain.aliases %}
-
-
+
+
|
{{ alias }} |
{{ alias.destination|join(', ') or '-' }} |
diff --git a/admin/freeposte/admin/templates/confirm.html b/admin/freeposte/admin/templates/confirm.html
index c95db696..36ecd7f5 100644
--- a/admin/freeposte/admin/templates/confirm.html
+++ b/admin/freeposte/admin/templates/confirm.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}
-Confirm action
+{% trans %}Confirm action{% endtrans %}
{% endblock %}
{% block subtitle %}
@@ -9,6 +9,6 @@ Confirm action
{% endblock %}
{% block box_content %}
-You are about to {{ action }}. Please confirm your action.
+{% trans action %}You are about to {{ action }}. Please confirm your action.{% endtrans %}
{{ macros.form(form) }}
{% endblock %}
diff --git a/admin/freeposte/admin/templates/domain/create.html b/admin/freeposte/admin/templates/domain/create.html
index c6bb5455..29a4f431 100644
--- a/admin/freeposte/admin/templates/domain/create.html
+++ b/admin/freeposte/admin/templates/domain/create.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}
-New domain
+{% trans %}New domain{% endtrans %}
{% endblock %}
{% block box_content %}
diff --git a/admin/freeposte/admin/templates/domain/details.html b/admin/freeposte/admin/templates/domain/details.html
index c70120fd..9a98f944 100644
--- a/admin/freeposte/admin/templates/domain/details.html
+++ b/admin/freeposte/admin/templates/domain/details.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}
-Domain details
+{% trans %}Domain details{% endtrans %}
{% endblock %}
{% block subtitle %}
@@ -10,7 +10,7 @@ Domain details
{% block main_action %}
{% if current_user.global_admin %}
-Regenerate keys
+{% trans %}Regenerate keys{% endtrans %}
{% endif %}
{% endblock %}
@@ -18,30 +18,30 @@ Domain details
- Domain name |
+ {% trans %}Domain name{% endtrans %} |
{{ domain.name }} |
- DNS MX entry |
+ {% trans %}DNS MX entry{% endtrans %} |
{{ domain.name }}. 600 IN MX 10 {{ config["HOSTNAME"] }}. |
- DNS SPF entries |
+ {% trans %}DNS SPF entries{% endtrans %} |
{{ domain.name }}. 600 IN TXT "v=spf1 mx a:{{ config["HOSTNAME"] }} -all"
{{ domain.name }}. 600 IN SPF "v=spf1 mx a:{{ config["HOSTNAME"] }} -all" |
{% if domain.dkim_publickey %}
- DKIM public key |
+ {% trans %}DKIM public key{% endtrans %} |
{{ domain.dkim_publickey }} |
- DNS DKIM entry |
+ {% trans %}DNS DKIM entry{% endtrans %} |
{{ config["DKIM_SELECTOR"] }}._domainkey.{{ domain.name }}. IN 600 TXT "v=DKIM1; k=rsa; p={{ domain.dkim_publickey }}" |
- DNS DMARC entry |
+ {% trans %}DNS DMARC entry{% endtrans %} |
_dmarc.{{ domain.name }}. 600 IN TXT "v=DMARC1; p=reject; rua=mailto:{{ config["POSTMASTER"] }}@{{ config["DOMAIN"] }}; adkim=s; aspf=s" |
{% endif %}
diff --git a/admin/freeposte/admin/templates/domain/edit.html b/admin/freeposte/admin/templates/domain/edit.html
index f1be7741..13af23a5 100644
--- a/admin/freeposte/admin/templates/domain/edit.html
+++ b/admin/freeposte/admin/templates/domain/edit.html
@@ -1,7 +1,7 @@
{% extends "domain/create.html" %}
{% block title %}
-Edit domain
+{% trans %}Edit domain{% endtrans %}
{% endblock %}
{% block subtitle %}
diff --git a/admin/freeposte/admin/templates/domain/list.html b/admin/freeposte/admin/templates/domain/list.html
index 4aa150e4..ea81e4c3 100644
--- a/admin/freeposte/admin/templates/domain/list.html
+++ b/admin/freeposte/admin/templates/domain/list.html
@@ -1,12 +1,12 @@
{% extends "base.html" %}
{% block title %}
-Domain list
+{% trans %}Domain list{% endtrans %}
{% endblock %}
{% block main_action %}
{% if current_user.global_admin %}
-New domain
+{% trans %}New domain{% endtrans %}
{% endif %}
{% endblock %}
@@ -14,28 +14,28 @@ Domain list
- Actions |
- Manage |
- Domain name |
- Mailbox count |
- Alias count |
- Comment |
- Created |
- Last edit |
+ {% trans %}Actions{% endtrans %} |
+ {% trans %}Manage{% endtrans %} |
+ {% trans %}Domain name{% endtrans %} |
+ {% trans %}Mailbox count{% endtrans %} |
+ {% trans %}Alias count{% endtrans %} |
+ {% trans %}Comment{% endtrans %} |
+ {% trans %}Created{% endtrans %} |
+ {% trans %}Last edit{% endtrans %} |
{% for domain in current_user.get_managed_domains() %}
-
+
{% if current_user.global_admin %}
-
-
+
+
{% endif %}
|
-
-
-
+
+
+
|
{{ domain.name }} |
{{ domain.users | count }} / {{ domain.max_users or '∞' }} |
diff --git a/admin/freeposte/admin/templates/fetch/create.html b/admin/freeposte/admin/templates/fetch/create.html
index f0b470e3..f5d8618a 100644
--- a/admin/freeposte/admin/templates/fetch/create.html
+++ b/admin/freeposte/admin/templates/fetch/create.html
@@ -1,7 +1,7 @@
{% extends "form.html" %}
{% block title %}
-Add a fetched account
+{% trans %}Add a fetched account{% endtrans %}
{% endblock %}
{% block subtitle %}
diff --git a/admin/freeposte/admin/templates/fetch/edit.html b/admin/freeposte/admin/templates/fetch/edit.html
index bac05427..749df5b7 100644
--- a/admin/freeposte/admin/templates/fetch/edit.html
+++ b/admin/freeposte/admin/templates/fetch/edit.html
@@ -1,7 +1,7 @@
{% extends "form.html" %}
{% block title %}
-Update a fetched account
+{% trans %}Update a fetched account{% endtrans %}
{% endblock %}
{% block subtitle %}
diff --git a/admin/freeposte/admin/templates/fetch/list.html b/admin/freeposte/admin/templates/fetch/list.html
index b4782c3d..1e82cb78 100644
--- a/admin/freeposte/admin/templates/fetch/list.html
+++ b/admin/freeposte/admin/templates/fetch/list.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}
-Fetched accounts
+{% trans %}Fetched accounts{% endtrans %}
{% endblock %}
{% block subtitle %}
@@ -9,26 +9,26 @@ Fetched accounts
{% endblock %}
{% block main_action %}
-Add an account
+{% trans %}Add an account{% endtrans %}
{% endblock %}
{% block box %}
- Actions |
- Endpoint |
- Username |
- Last check |
- Status |
- Created |
- Last edit |
+ {% trans %}Actions{% endtrans %} |
+ {% trans %}Endpoint{% endtrans %} |
+ {% trans %}Username{% endtrans %} |
+ {% trans %}Last check{% endtrans %} |
+ {% trans %}Status{% endtrans %} |
+ {% trans %}Created{% endtrans %} |
+ {% trans %}Last edit{% endtrans %} |
{% for fetch in user.fetches %}
-
-
+
+
|
{{ fetch.protocol }}{{ 's' if fetch.tls else '' }}://{{ fetch.host }}:{{ fetch.port }} |
{{ fetch.username }} |
diff --git a/admin/freeposte/admin/templates/login.html b/admin/freeposte/admin/templates/login.html
index 3a217046..27b63d43 100644
--- a/admin/freeposte/admin/templates/login.html
+++ b/admin/freeposte/admin/templates/login.html
@@ -3,10 +3,10 @@
{% block sidebar %}