From cb6dadbf94b90aa4ab6330052e29ffb952757ec0 Mon Sep 17 00:00:00 2001 From: Jens L Date: Mon, 2 Oct 2023 16:04:40 +0200 Subject: [PATCH] stages/email: rework email templates (#7029) rework email templates Signed-off-by: Jens Langhammer --- authentik/events/models.py | 39 ++-- .../email/static/stages/email/css/base.css | 205 ------------------ .../templates/email/account_confirmation.html | 53 ++--- .../stages/email/templates/email/base.html | 155 ++++++++++--- .../templates/email/event_notification.html | 52 +++++ .../stages/email/templates/email/generic.html | 45 ---- .../email/templates/email/password_reset.html | 75 +++---- authentik/stages/email/utils.py | 14 ++ locale/en/LC_MESSAGES/django.po | 81 ++++--- web/xliff/fr.xlf | 64 +++--- web/xliff/zh-Hans.xlf | 46 ++-- 11 files changed, 371 insertions(+), 458 deletions(-) delete mode 100644 authentik/stages/email/static/stages/email/css/base.css create mode 100644 authentik/stages/email/templates/email/event_notification.html delete mode 100644 authentik/stages/email/templates/email/generic.html diff --git a/authentik/events/models.py b/authentik/events/models.py index 82d215fbc..4b245e49f 100644 --- a/authentik/events/models.py +++ b/authentik/events/models.py @@ -436,32 +436,39 @@ class NotificationTransport(SerializerModel): def send_email(self, notification: "Notification") -> list[str]: """Send notification via global email configuration""" - subject = "authentik Notification: " - key_value = { - "user_email": notification.user.email, - "user_username": notification.user.username, + subject_prefix = "authentik Notification: " + context = { + "key_value": { + "user_email": notification.user.email, + "user_username": notification.user.username, + }, + "body": notification.body, + "title": "", } if notification.event and notification.event.user: - key_value["event_user_email"] = notification.event.user.get("email", None) - key_value["event_user_username"] = notification.event.user.get("username", None) + context["key_value"]["event_user_email"] = notification.event.user.get("email", None) + context["key_value"]["event_user_username"] = notification.event.user.get( + "username", None + ) if notification.event: - subject += notification.event.action + context["title"] += notification.event.action for key, value in notification.event.context.items(): if not isinstance(value, str): continue - key_value[key] = value + context["key_value"][key] = value else: - subject += notification.body[:75] + context["title"] += notification.body[:75] + # TODO: improve permission check + if notification.user.is_superuser: + context["source"] = { + "from": self.name, + } mail = TemplateEmailMessage( - subject=subject, + subject=subject_prefix + context["title"], to=[notification.user.email], language=notification.user.locale(), - template_name="email/generic.html", - template_context={ - "title": subject, - "body": notification.body, - "key_value": key_value, - }, + template_name="email/event_notification.html", + template_context=context, ) # Email is sent directly here, as the call to send() should have been from a task. try: diff --git a/authentik/stages/email/static/stages/email/css/base.css b/authentik/stages/email/static/stages/email/css/base.css deleted file mode 100644 index 2d46ea4f7..000000000 --- a/authentik/stages/email/static/stages/email/css/base.css +++ /dev/null @@ -1,205 +0,0 @@ -/* authentik Email CSS */ -* { - margin: 0; - font-family: Helvetica, Arial, sans-serif; - box-sizing: border-box; - font-size: 14px; -} -img { - max-width: 100%; -} -body { - -webkit-font-smoothing: antialiased; - -webkit-text-size-adjust: none; - width: 100% !important; - height: 100%; - line-height: 1.6em; -} -table td { - vertical-align: top; -} -body { - background-color: #f6f6f6; -} -.body-wrap { - background-color: #f6f6f6; - width: 100%; -} -.container { - display: block !important; - max-width: 600px !important; - margin: 0 auto !important; - clear: both !important; -} -.content { - max-width: 600px; - margin: 0 auto; - display: block; - padding: 20px; -} -.main { - background-color: #fff; - border: 1px solid #e9e9e9; -} -.content-wrap { - padding: 20px; -} -.content-block { - padding: 0 0 20px; -} -.header { - width: 100%; - margin-bottom: 20px; -} -.footer { - width: 100%; - clear: both; - color: #999; - padding: 20px; -} -.footer p, .footer a, .footer td { - color: #999; - font-size: 12px; -} -h1, h2, h3 { - font-family: Helvetica, Arial, sans-serif; - color: #000; - margin: 40px 0 0; - line-height: 1.2em; - font-weight: 400; -} -h1 { - font-size: 32px; - font-weight: 500; -} -h2 { - font-size: 24px; -} -h3 { - font-size: 18px; -} -h4 { - font-size: 14px; - font-weight: 600; -} -p, ul, ol { - margin-bottom: 10px; - font-weight: normal; -} -p li, ul li, ol li { - margin-left: 5px; - list-style-position: inside; -} -a { - color: #348eda; - text-decoration: underline; -} -.btn-primary { - text-decoration: none; - color: #FFF; - background-color: #348eda; - border: solid #348eda; - border-width: 10px 20px; - line-height: 2em; - font-weight: bold; - text-align: center; - cursor: pointer; - display: inline-block; - text-transform: capitalize; -} -.btn-primary a { - color: #fff; -} -.last { - margin-bottom: 0; -} -.first { - margin-top: 0; -} -.align-center { - text-align: center; -} -.align-right { - text-align: right; -} -.align-left { - text-align: left; -} -.clear { - clear: both; -} -.alert { - font-size: 16px; - color: #fff; - font-weight: 500; - padding: 20px; - text-align: center; -} -.alert a { - color: #fff; - text-decoration: none; - font-weight: 500; - font-size: 16px; -} -.alert-brand { - background-color: #fd4b2d; -} -.alert-warning { - background-color: #F0AB00; -} -.alert-danger { - background-color: #C9190B; -} -.alert-success { - background-color: #3E8635; -} -.body { - margin: 40px auto; - text-align: left; - width: 80%; -} -.body td { - padding: 5px 0; -} -.body .body-items { - width: 100%; -} -.body .body-items td { - border-top: #eee 1px solid; -} -.body .body-items .total td { - border-top: 2px solid #333; - border-bottom: 2px solid #333; - font-weight: 700; -} -@media only screen and (max-width: 640px) { - body { - padding: 0 !important; - } - h1, h2, h3, h4 { - font-weight: 800 !important; - margin: 20px 0 5px !important; - } - h1 { - font-size: 22px !important; - } - h2 { - font-size: 18px !important; - } - h3 { - font-size: 16px !important; - } - .container { - padding: 0 !important; - width: 100% !important; - } - .content { - padding: 0 !important; - } - .content-wrap { - padding: 10px !important; - } - .body { - width: 100% !important; - } -} diff --git a/authentik/stages/email/templates/email/account_confirmation.html b/authentik/stages/email/templates/email/account_confirmation.html index b6e1f7f62..404bc4639 100644 --- a/authentik/stages/email/templates/email/account_confirmation.html +++ b/authentik/stages/email/templates/email/account_confirmation.html @@ -4,35 +4,38 @@ {% load i18n %} {% block content %} - -

- {% trans 'Welcome!' %} -

-

- {% trans "We're excited to have you get started. First, you need to confirm your account. Just press the button below."%} -

- - + + + + + + + + + + + +{% endblock %} + +{% block sub_content %} + + {% blocktrans with url=url %} If that doesn't work, copy and paste the following link in your browser: {{ url }} {% endblocktrans %} -

-

- {% trans "If you have any questions, just reply to this email—we're always happy to help out." %} -

- + + {% endblock %} diff --git a/authentik/stages/email/templates/email/base.html b/authentik/stages/email/templates/email/base.html index e68cc7516..1b3dfc84b 100644 --- a/authentik/stages/email/templates/email/base.html +++ b/authentik/stages/email/templates/email/base.html @@ -1,34 +1,131 @@ {% load authentik_stages_email %} - - - - - - - - - - + + + + + + + + + +
+
+
+
- - - + -
-
- - {% block content %} - {% endblock %} -
- -
-
+ + + + + {% block content %} + {% endblock %} +
+ +
+
- + + + + + + +
+ + {% block sub_content %} + {% endblock %} +
+
+ + + + + Powered by authentik. + + + + + + + diff --git a/authentik/stages/email/templates/email/event_notification.html b/authentik/stages/email/templates/email/event_notification.html new file mode 100644 index 000000000..e34563404 --- /dev/null +++ b/authentik/stages/email/templates/email/event_notification.html @@ -0,0 +1,52 @@ +{% extends "email/base.html" %} + +{% load i18n %} + +{% block content %} + + +

+ {{ title }} +

+ + + + + + + + + {% if key_value %} + + + + {% endif %} +
+ {{ body }} +
+ + + {% for key, value in key_value.items %} + + + + + {% endfor %} + +
{{ key }}{{ value }}
+
+ + +{% endblock %} + +{% block sub_content %} +{% if source %} + + + {% blocktranslate with name=source.from %} + This email was sent from the notification transport {{name}}. + {% endblocktranslate %} + + +{% endif %} +{% endblock %} diff --git a/authentik/stages/email/templates/email/generic.html b/authentik/stages/email/templates/email/generic.html deleted file mode 100644 index abe848403..000000000 --- a/authentik/stages/email/templates/email/generic.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends "email/base.html" %} - -{% load i18n %} - -{% block content %} - - - {{ title }} - - - - - - - - - {% if key_value %} - - - - {% endif %} -
- {{ body }} -
- - - - - - - -
{% trans "Additional Information" %}
- - {% for key, value in key_value.items %} - - - - - {% endfor %} -
{{ key }}{{ value }}
-
-
- - -{% endblock %} diff --git a/authentik/stages/email/templates/email/password_reset.html b/authentik/stages/email/templates/email/password_reset.html index feda1b0e7..855f48c25 100644 --- a/authentik/stages/email/templates/email/password_reset.html +++ b/authentik/stages/email/templates/email/password_reset.html @@ -5,49 +5,40 @@ {% block content %} - - {% blocktrans with username=user.username %} - Hi {{ username }}, - {% endblocktrans %} - + +

+ {% blocktrans with username=user.username %} + Hi {{ username }}, + {% endblocktrans %} +

+ - - - - - - - - - - - -
- {% blocktrans %} - You recently requested to change your password for your authentik account. Use the button below to set a new password. - {% endblocktrans %} -
- - - - - - - -
- {% blocktrans with expires=expires|naturaltime %} - If you did not request a password change, please ignore this Email. The link above is valid for {{ expires }}. - {% endblocktrans %} -
- + + + + + + + + +
+ {% blocktrans %} + You recently requested to change your password for your authentik account. Use the button below to set a new password. + {% endblocktrans %} +
+ {% trans 'Reset Password' %} +
+ + +{% endblock %} + +{% block sub_content %} + + + {% blocktrans with expires=expires|naturaltime %} + If you did not request a password change, please ignore this Email. The link above is valid for {{ expires }}. + {% endblocktrans %} + {% endblock %} diff --git a/authentik/stages/email/utils.py b/authentik/stages/email/utils.py index 13650ec4b..5422ec43a 100644 --- a/authentik/stages/email/utils.py +++ b/authentik/stages/email/utils.py @@ -1,9 +1,21 @@ """email utils""" +from email.mime.image import MIMEImage +from functools import lru_cache + from django.core.mail import EmailMultiAlternatives from django.template.loader import render_to_string from django.utils import translation +@lru_cache() +def logo_data(): + """Get logo as MIME Image for emails""" + with open("web/icons/icon_left_brand.png", "rb") as _logo_file: + logo = MIMEImage(_logo_file.read()) + logo.add_header("Content-ID", "logo.png") + return logo + + class TemplateEmailMessage(EmailMultiAlternatives): """Wrapper around EmailMultiAlternatives with integrated template rendering""" @@ -12,4 +24,6 @@ class TemplateEmailMessage(EmailMultiAlternatives): html_content = render_to_string(template_name, template_context) super().__init__(**kwargs) self.content_subtype = "html" + self.mixed_subtype = "related" + self.attach(logo_data()) self.attach_alternative(html_content, "text/html") diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index db2b66eb5..b3f311e51 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-15 09:51+0000\n" +"POT-Creation-Date: 2023-10-02 12:46+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -375,63 +375,63 @@ msgstr "" msgid "Event user" msgstr "" -#: authentik/events/models.py:484 +#: authentik/events/models.py:491 msgid "Notification Transport" msgstr "" -#: authentik/events/models.py:485 +#: authentik/events/models.py:492 msgid "Notification Transports" msgstr "" -#: authentik/events/models.py:491 +#: authentik/events/models.py:498 msgid "Notice" msgstr "" -#: authentik/events/models.py:492 +#: authentik/events/models.py:499 msgid "Warning" msgstr "" -#: authentik/events/models.py:493 +#: authentik/events/models.py:500 msgid "Alert" msgstr "" -#: authentik/events/models.py:518 +#: authentik/events/models.py:525 msgid "Notification" msgstr "" -#: authentik/events/models.py:519 +#: authentik/events/models.py:526 msgid "Notifications" msgstr "" -#: authentik/events/models.py:529 +#: authentik/events/models.py:536 msgid "" "Select which transports should be used to notify the user. If none are " "selected, the notification will only be shown in the authentik UI." msgstr "" -#: authentik/events/models.py:537 +#: authentik/events/models.py:544 msgid "Controls which severity level the created notifications will have." msgstr "" -#: authentik/events/models.py:542 +#: authentik/events/models.py:549 msgid "" "Define which group of users this notification should be sent and shown to. " "If left empty, Notification won't ben sent." msgstr "" -#: authentik/events/models.py:560 +#: authentik/events/models.py:567 msgid "Notification Rule" msgstr "" -#: authentik/events/models.py:561 +#: authentik/events/models.py:568 msgid "Notification Rules" msgstr "" -#: authentik/events/models.py:581 +#: authentik/events/models.py:588 msgid "Webhook Mapping" msgstr "" -#: authentik/events/models.py:582 +#: authentik/events/models.py:589 msgid "Webhook Mappings" msgstr "" @@ -2099,21 +2099,21 @@ msgstr "" msgid "Email sent." msgstr "" -#: authentik/stages/email/templates/email/account_confirmation.html:9 +#: authentik/stages/email/templates/email/account_confirmation.html:10 msgid "Welcome!" msgstr "" -#: authentik/stages/email/templates/email/account_confirmation.html:12 +#: authentik/stages/email/templates/email/account_confirmation.html:19 msgid "" "We're excited to have you get started. First, you need to confirm your " "account. Just press the button below." msgstr "" -#: authentik/stages/email/templates/email/account_confirmation.html:21 +#: authentik/stages/email/templates/email/account_confirmation.html:24 msgid "Confirm Account" msgstr "" -#: authentik/stages/email/templates/email/account_confirmation.html:30 +#: authentik/stages/email/templates/email/account_confirmation.html:36 #, python-format msgid "" "\n" @@ -2122,43 +2122,42 @@ msgid "" " " msgstr "" -#: authentik/stages/email/templates/email/account_confirmation.html:35 -msgid "" -"If you have any questions, just reply to this email—we're always happy to " -"help out." -msgstr "" - -#: authentik/stages/email/templates/email/generic.html:24 -msgid "Additional Information" -msgstr "" - -#: authentik/stages/email/templates/email/password_reset.html:9 +#: authentik/stages/email/templates/email/event_notification.html:46 #, python-format msgid "" "\n" -" Hi %(username)s,\n" -" " +" This email was sent from the notification transport %(name)s.\n" +" " msgstr "" -#: authentik/stages/email/templates/email/password_reset.html:19 +#: authentik/stages/email/templates/email/password_reset.html:10 +#, python-format msgid "" "\n" -" You recently requested to change your password for your " -"authentik account. Use the button below to set a new password.\n" -" " +" Hi %(username)s,\n" +" " msgstr "" -#: authentik/stages/email/templates/email/password_reset.html:33 +#: authentik/stages/email/templates/email/password_reset.html:21 +msgid "" +"\n" +" You recently requested to change your password for your authentik " +"account. Use the button below to set a new password.\n" +" " +msgstr "" + +#: authentik/stages/email/templates/email/password_reset.html:28 msgid "Reset Password" msgstr "" -#: authentik/stages/email/templates/email/password_reset.html:45 +#: authentik/stages/email/templates/email/password_reset.html:39 #, python-format msgid "" "\n" -" If you did not request a password change, please ignore " -"this Email. The link above is valid for %(expires)s.\n" -" " +" If you did not request a password change, please ignore this Email. The " +"link above is valid for %(expires)s.\n" +" " msgstr "" #: authentik/stages/email/templates/email/setup.html:9 diff --git a/web/xliff/fr.xlf b/web/xliff/fr.xlf index e16d3ef3e..00909b58a 100644 --- a/web/xliff/fr.xlf +++ b/web/xliff/fr.xlf @@ -1,4 +1,4 @@ - + @@ -613,9 +613,9 @@ Il y a jour(s) - The URL "" was not found. - L'URL " - " n'a pas été trouvée. + The URL "" was not found. + L'URL " + " n'a pas été trouvée. @@ -1067,8 +1067,8 @@ Il y a jour(s) - To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. - Pour permettre n'importe quelle URI de redirection, définissez cette valeur sur ".*". Soyez conscient des possibles implications de sécurité que cela peut avoir. + To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. + Pour permettre n'importe quelle URI de redirection, définissez cette valeur sur ".*". Soyez conscient des possibles implications de sécurité que cela peut avoir. @@ -1298,7 +1298,7 @@ Il y a jour(s) Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you). - Utilisez ce fournisseur avec l'option "auth_request" de Nginx ou "forwardAuth" de Traefik. Chaque application/domaine a besoin de son propre fournisseur. De plus, sur chaque domaine, "/outpost.goauthentik.io" doit être routé vers le poste avancé (lorsque vous utilisez un poste avancé géré, cela est fait pour vous). + Utilisez ce fournisseur avec l'option "auth_request" de Nginx ou "forwardAuth" de Traefik. Chaque application/domaine a besoin de son propre fournisseur. De plus, sur chaque domaine, "/outpost.goauthentik.io" doit être routé vers le poste avancé (lorsque vous utilisez un poste avancé géré, cela est fait pour vous). @@ -1645,7 +1645,7 @@ Il y a jour(s) Token to authenticate with. Currently only bearer authentication is supported. - Jeton d'authentification à utiliser. Actuellement, seule l'authentification "bearer authentication" est prise en charge. + Jeton d'authentification à utiliser. Actuellement, seule l'authentification "bearer authentication" est prise en charge. @@ -1813,8 +1813,8 @@ Il y a jour(s) - Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". - Entrez une URL complète, un chemin relatif ou utilisez 'fa://fa-test' pour utiliser l'icône Font Awesome "fa-test". + Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". + Entrez une URL complète, un chemin relatif ou utilisez 'fa://fa-test' pour utiliser l'icône Font Awesome "fa-test". @@ -3147,7 +3147,7 @@ doesn't pass when either or both of the selected options are equal or above the To use SSL instead, use 'ldaps://' and disable this option. - Pour utiliser SSL à la base, utilisez "ldaps://" et désactviez cette option. + Pour utiliser SSL à la base, utilisez "ldaps://" et désactviez cette option. @@ -3236,8 +3236,8 @@ doesn't pass when either or both of the selected options are equal or above the - Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' - Champ qui contient les membres d'un groupe. Si vous utilisez le champ "memberUid", la valeur est censée contenir un nom distinctif relatif, par exemple 'memberUid=un-utilisateur' au lieu de 'memberUid=cn=un-utilisateur,ou=groups,...' + Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' + Champ qui contient les membres d'un groupe. Si vous utilisez le champ "memberUid", la valeur est censée contenir un nom distinctif relatif, par exemple 'memberUid=un-utilisateur' au lieu de 'memberUid=cn=un-utilisateur,ou=groups,...' @@ -3532,7 +3532,7 @@ doesn't pass when either or both of the selected options are equal or above the Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. - Moment où les utilisateurs temporaires doivent être supprimés. Cela ne s'applique que si votre IDP utilise le format NameID "transient" et que l'utilisateur ne se déconnecte pas manuellement. + Moment où les utilisateurs temporaires doivent être supprimés. Cela ne s'applique que si votre IDP utilise le format NameID "transient" et que l'utilisateur ne se déconnecte pas manuellement. @@ -3700,7 +3700,7 @@ doesn't pass when either or both of the selected options are equal or above the Optionally set the 'FriendlyName' value of the Assertion attribute. - Indiquer la valeur "FriendlyName" de l'attribut d'assertion (optionnel) + Indiquer la valeur "FriendlyName" de l'attribut d'assertion (optionnel) @@ -4029,8 +4029,8 @@ doesn't pass when either or both of the selected options are equal or above the - When using an external logging solution for archiving, this can be set to "minutes=5". - En cas d'utilisation d'une solution de journalisation externe pour l'archivage, cette valeur peut être fixée à "minutes=5". + When using an external logging solution for archiving, this can be set to "minutes=5". + En cas d'utilisation d'une solution de journalisation externe pour l'archivage, cette valeur peut être fixée à "minutes=5". @@ -4039,8 +4039,8 @@ doesn't pass when either or both of the selected options are equal or above the - Format: "weeks=3;days=2;hours=3,seconds=2". - Format : "weeks=3;days=2;hours=3,seconds=2". + Format: "weeks=3;days=2;hours=3,seconds=2". + Format : "weeks=3;days=2;hours=3,seconds=2". @@ -4236,10 +4236,10 @@ doesn't pass when either or both of the selected options are equal or above the - Are you sure you want to update ""? + Are you sure you want to update ""? Êtes-vous sûr de vouloir mettre à jour - " - " ? + " + " ? @@ -5340,8 +5340,8 @@ doesn't pass when either or both of the selected options are equal or above the - A "roaming" authenticator, like a YubiKey - Un authentificateur "itinérant", comme une YubiKey + A "roaming" authenticator, like a YubiKey + Un authentificateur "itinérant", comme une YubiKey @@ -5666,7 +5666,7 @@ doesn't pass when either or both of the selected options are equal or above the Show arbitrary input fields to the user, for example during enrollment. Data is saved in the flow context under the 'prompt_data' variable. - Afficher des champs de saisie arbitraires à l'utilisateur, par exemple pendant l'inscription. Les données sont enregistrées dans le contexte du flux sous la variable "prompt_data". + Afficher des champs de saisie arbitraires à l'utilisateur, par exemple pendant l'inscription. Les données sont enregistrées dans le contexte du flux sous la variable "prompt_data". @@ -5675,10 +5675,10 @@ doesn't pass when either or both of the selected options are equal or above the - ("", of type ) + ("", of type ) - (" - ", de type + (" + ", de type ) @@ -5727,8 +5727,8 @@ doesn't pass when either or both of the selected options are equal or above the - If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. - Si défini à une durée supérieure à 0, l'utilisateur aura la possibilité de choisir de "rester connecté", ce qui prolongera sa session jusqu'à la durée spécifiée ici. + If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + Si défini à une durée supérieure à 0, l'utilisateur aura la possibilité de choisir de "rester connecté", ce qui prolongera sa session jusqu'à la durée spécifiée ici. @@ -6512,7 +6512,7 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti Can be in the format of 'unix://' when connecting to a local docker daemon, using 'ssh://' to connect via SSH, or 'https://:2376' when connecting to a remote system. - Peut être au format "unix://" pour une connexion à un service docker local, "ssh://" pour une connexion via SSH, ou "https://:2376" pour une connexion à un système distant. + Peut être au format "unix://" pour une connexion à un service docker local, "ssh://" pour une connexion via SSH, ou "https://:2376" pour une connexion à un système distant. @@ -7819,4 +7819,4 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti - \ No newline at end of file + diff --git a/web/xliff/zh-Hans.xlf b/web/xliff/zh-Hans.xlf index 1d2ff7720..a760ca993 100644 --- a/web/xliff/zh-Hans.xlf +++ b/web/xliff/zh-Hans.xlf @@ -1,4 +1,4 @@ - + @@ -613,9 +613,9 @@ - The URL "" was not found. - 未找到 URL " - "。 + The URL "" was not found. + 未找到 URL " + "。 @@ -1067,8 +1067,8 @@ - To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. - 要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。 + To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. + 要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。 @@ -1814,8 +1814,8 @@ - Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". - 输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。 + Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". + 输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。 @@ -3238,8 +3238,8 @@ doesn't pass when either or both of the selected options are equal or above the - Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' - 包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...' + Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' + 包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...' @@ -4031,8 +4031,8 @@ doesn't pass when either or both of the selected options are equal or above the - When using an external logging solution for archiving, this can be set to "minutes=5". - 使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。 + When using an external logging solution for archiving, this can be set to "minutes=5". + 使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。 @@ -4041,8 +4041,8 @@ doesn't pass when either or both of the selected options are equal or above the - Format: "weeks=3;days=2;hours=3,seconds=2". - 格式:"weeks=3;days=2;hours=3,seconds=2"。 + Format: "weeks=3;days=2;hours=3,seconds=2". + 格式:"weeks=3;days=2;hours=3,seconds=2"。 @@ -4238,10 +4238,10 @@ doesn't pass when either or both of the selected options are equal or above the - Are you sure you want to update ""? + Are you sure you want to update ""? 您确定要更新 - " - " 吗? + " + " 吗? @@ -5342,7 +5342,7 @@ doesn't pass when either or both of the selected options are equal or above the - A "roaming" authenticator, like a YubiKey + A "roaming" authenticator, like a YubiKey 像 YubiKey 这样的“漫游”身份验证器 @@ -5677,10 +5677,10 @@ doesn't pass when either or both of the selected options are equal or above the - ("", of type ) + ("", of type ) - (" - ",类型为 + (" + ",类型为 @@ -5729,7 +5729,7 @@ doesn't pass when either or both of the selected options are equal or above the - If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. 如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。 @@ -7821,4 +7821,4 @@ Bindings to groups/users are checked against the user of the event. - \ No newline at end of file +