diff --git a/modoboa/locale/en/LC_MESSAGES/django.po b/modoboa/locale/en/LC_MESSAGES/django.po
index 6fb2f1d25..96f737f68 100644
--- a/modoboa/locale/en/LC_MESSAGES/django.po
+++ b/modoboa/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: 2020-05-06 13:15+0200\n"
+"POT-Creation-Date: 2020-11-05 15:36+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -520,46 +520,50 @@ msgstr ""
msgid "Dummy"
msgstr ""
-#: core/forms.py:21
+#: core/forms.py:23
msgid "Username"
msgstr ""
-#: core/forms.py:25
+#: core/forms.py:27
msgid "Password"
msgstr ""
-#: core/forms.py:38
+#: core/forms.py:40
msgid "Old password"
msgstr ""
-#: core/forms.py:42
+#: core/forms.py:44
msgid "New password"
msgstr ""
-#: core/forms.py:46
+#: core/forms.py:48
msgid "Confirmation"
msgstr ""
-#: core/forms.py:74
+#: core/forms.py:76
msgid "Old password mismatchs"
msgstr ""
-#: core/forms.py:83
+#: core/forms.py:85
msgid "Passwords mismatch"
msgstr ""
-#: core/forms.py:103
+#: core/forms.py:105
msgid "Enable API access"
msgstr ""
-#: core/forms.py:137
+#: core/forms.py:139
msgid "Verification code"
msgstr ""
-#: core/forms.py:148
+#: core/forms.py:150
msgid "Invalid code"
msgstr ""
+#: core/forms.py:163
+msgid "This code is invalid"
+msgstr ""
+
#: core/handlers.py:36
msgid "added"
msgstr ""
@@ -602,35 +606,35 @@ msgstr ""
msgid "[modoboa] Update(s) available"
msgstr ""
-#: core/models.py:55
+#: core/models.py:52
msgid "Allow mailboxes access"
msgstr ""
-#: core/models.py:57
+#: core/models.py:54
msgid "Allow this administrator to access user mailboxes"
msgstr ""
-#: core/models.py:60
+#: core/models.py:57
msgid "password"
msgstr ""
-#: core/models.py:63
+#: core/models.py:60
msgid "language"
msgstr ""
-#: core/models.py:66
+#: core/models.py:63
msgid "Prefered language to display pages."
msgstr ""
-#: core/models.py:70
+#: core/models.py:67
msgid "Phone number"
msgstr ""
-#: core/models.py:72
+#: core/models.py:69
msgid "Secondary email"
msgstr ""
-#: core/models.py:75
+#: core/models.py:72
msgid "An alternative e-mail address, can be used for recovery needs."
msgstr ""
@@ -692,6 +696,14 @@ msgstr[1] ""
msgid "Your password must contain a combination of different character types."
msgstr ""
+#: core/serializers.py:15
+msgid "Invalid PIN code"
+msgstr ""
+
+#: core/serializers.py:21
+msgid "2FA is already enabled"
+msgstr ""
+
#: core/sms_backends/ovh.py:19
msgid "API endpoint"
msgstr ""
@@ -920,6 +932,79 @@ msgstr ""
msgid "Click here to update your password"
msgstr ""
+#: core/templates/core/user_security.html:2 core/templatetags/core_tags.py:124
+msgid "Security"
+msgstr ""
+
+#: core/templates/core/user_security.html:6
+msgid "Two-Factor authentication"
+msgstr ""
+
+#: core/templates/core/user_security.html:17
+msgid ""
+"Install a soft token authenticator like FreeOTP or Google Authenticator from "
+"your application repository and use that app to scan this QR code."
+msgstr ""
+
+#: core/templates/core/user_security.html:25
+msgid "Pin code"
+msgstr ""
+
+#: core/templates/core/user_security.html:28
+msgid "Register"
+msgstr ""
+
+#: core/templates/core/user_security.html:33
+msgid ""
+"Congratulations! Two-Factor Authentication is now enabled for your account."
+msgstr ""
+
+#: core/templates/core/user_security.html:38
+#: core/templates/core/user_security.html:89
+msgid ""
+"The following recovery codes can be used one time each to let you regain "
+"access to your account, in case you lose your phone for example. Make sure "
+"to save them in a safe place, otherwise you won't be able to access your "
+"account anymore."
+msgstr ""
+
+#: core/templates/core/user_security.html:53
+msgid "Two-Factor Authentication is enabled for your account."
+msgstr ""
+
+#: core/templates/core/user_security.html:58
+msgid "Disable 2FA"
+msgstr ""
+
+#: core/templates/core/user_security.html:61
+msgid "Reset recovery codes"
+msgstr ""
+
+#: core/templates/core/user_security.html:65
+msgid ""
+"Two-Factor Authentication (2FA) is not yet activated for your account. "
+"Enabling this feature will increase your account's security."
+msgstr ""
+
+#: core/templates/core/user_security.html:73
+msgid "Enable 2FA"
+msgstr ""
+
+#: core/templates/core/user_security.html:83
+#: core/templates/core/user_security.html:99
+#: templates/common/generic_modal.html:6 templates/common/generic_modal.html:13
+#: templates/common/wizard_forms.html:6 templates/common/wizard_forms.html:27
+msgid "Close"
+msgstr ""
+
+#: core/templates/core/user_security.html:84
+msgid "New recovery codes"
+msgstr ""
+
+#: core/templates/core/user_security.html:88
+msgid "Here are your new recovery codes. "
+msgstr ""
+
#: core/templatetags/core_tags.py:34
msgid "Modoboa"
msgstr ""
@@ -928,7 +1013,7 @@ msgstr ""
msgid "Logout"
msgstr ""
-#: core/templatetags/core_tags.py:127
+#: core/templatetags/core_tags.py:131
msgid "API"
msgstr ""
@@ -940,51 +1025,55 @@ msgstr ""
msgid "Parameters saved"
msgstr ""
-#: core/views/auth.py:59
+#: core/views/auth.py:61
#, python-format
msgid "Password scheme mismatch. Updating %s password"
msgstr ""
-#: core/views/auth.py:66
+#: core/views/auth.py:68
#, python-format
msgid "Password hash parameter missmatch. Updating %s password"
msgstr ""
-#: core/views/auth.py:82
+#: core/views/auth.py:84
#, python-format
msgid "User '%s' successfully logged in"
msgstr ""
-#: core/views/auth.py:90
+#: core/views/auth.py:92
msgid "Your username and password didn't match. Please try again."
msgstr ""
-#: core/views/auth.py:125
+#: core/views/auth.py:127
msgid "User '{}' successfully logged out"
msgstr ""
-#: core/views/auth.py:170 core/views/auth.py:225
+#: core/views/auth.py:172 core/views/auth.py:227
msgid "Please use the following code to recover your Modoboa password: {}"
msgstr ""
-#: core/views/user.py:53
+#: core/views/user.py:60
msgid "Profile updated"
msgstr ""
-#: core/views/user.py:81
+#: core/views/user.py:88
msgid "Preferences saved"
msgstr ""
-#: core/views/user.py:103
+#: core/views/user.py:110
msgid "Access updated"
msgstr ""
+#: core/viewsets.py:52
+msgid "2FA is not enabled"
+msgstr ""
+
#: lib/db_utils.py:23
#, python-format
msgid "Connection to database %s not configured"
msgstr ""
-#: lib/email_utils.py:141 lib/email_utils.py:360
+#: lib/email_utils.py:143 lib/email_utils.py:362
msgid "unable to determine encoding of string"
msgstr ""
@@ -1068,11 +1157,6 @@ msgstr ""
msgid "Help:"
msgstr ""
-#: templates/common/generic_modal.html:6 templates/common/generic_modal.html:13
-#: templates/common/wizard_forms.html:6 templates/common/wizard_forms.html:27
-msgid "Close"
-msgstr ""
-
#: templates/common/wizard_forms.html:31
msgid "Previous"
msgstr ""
diff --git a/modoboa/locale/en/LC_MESSAGES/djangojs.po b/modoboa/locale/en/LC_MESSAGES/djangojs.po
index d4f7a4f01..263146b0a 100644
--- a/modoboa/locale/en/LC_MESSAGES/djangojs.po
+++ b/modoboa/locale/en/LC_MESSAGES/djangojs.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-03-09 16:48+0100\n"
+"POT-Creation-Date: 2020-11-05 15:47+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -37,7 +37,7 @@ msgstr ""
msgid "Cancel"
msgstr ""
-#: static/js/global.js:342
+#: static/js/global.js:347
msgid "Internal error"
msgstr ""
@@ -52,3 +52,11 @@ msgstr ""
#: static/js/searchbar.js:28
msgid "Search..."
msgstr ""
+
+#: static/js/twocols_nav.js:170
+msgid "Are you sure?"
+msgstr ""
+
+#: static/js/twocols_nav.js:171
+msgid "This will invalidate your registered application."
+msgstr ""
diff --git a/modoboa/locale/fr/LC_MESSAGES/django.po b/modoboa/locale/fr/LC_MESSAGES/django.po
index b361b4306..745856a20 100644
--- a/modoboa/locale/fr/LC_MESSAGES/django.po
+++ b/modoboa/locale/fr/LC_MESSAGES/django.po
@@ -1,7 +1,7 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
-#
+#
# Translators:
# Antoine Nguyen , 2016-2017,2020
# Christophe CHAUVET , 2018
@@ -10,14 +10,14 @@ msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-05-06 13:15+0200\n"
-"PO-Revision-Date: 2020-05-06 11:19+0000\n"
+"POT-Creation-Date: 2020-11-05 15:36+0100\n"
+"PO-Revision-Date: 2020-11-05 15:49+0100\n"
"Last-Translator: Antoine Nguyen \n"
"Language-Team: French (http://www.transifex.com/tonio/modoboa/language/fr/)\n"
+"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: core/app_settings.py:26
@@ -46,7 +46,8 @@ msgstr "Méthode de chiffrement par défaut"
#: core/app_settings.py:56
msgid "Scheme used to crypt mailbox passwords"
-msgstr "Méthode utilisée pour chiffrer les mots de passe des boites aux lettres"
+msgstr ""
+"Méthode utilisée pour chiffrer les mots de passe des boites aux lettres"
#: core/app_settings.py:60
msgid "Rounds"
@@ -56,7 +57,9 @@ msgstr "Tours"
msgid ""
"Number of rounds to use (only used by sha256crypt and sha512crypt). Must be "
"between 1000 and 999999999, inclusive."
-msgstr "Nombre de tours à utiliser (sha256crypt et sha512crypt uniquement). Doit être compris entre 1000 et 999999999 inclue."
+msgstr ""
+"Nombre de tours à utiliser (sha256crypt et sha512crypt uniquement). Doit "
+"être compris entre 1000 et 999999999 inclue."
#: core/app_settings.py:69
msgid "Update password scheme at login"
@@ -64,7 +67,9 @@ msgstr "Mettre à jour la méthode de chiffrement du mot de passe à la connexio
#: core/app_settings.py:72
msgid "Update user password at login to use the default password scheme"
-msgstr "Mise à jour du mot de passe utilisateur lors d'une connexion pour utiliser la méthode de chiffrement par défaut"
+msgstr ""
+"Mise à jour du mot de passe utilisateur lors d'une connexion pour utiliser "
+"la méthode de chiffrement par défaut"
#: core/app_settings.py:77
msgid "Default password"
@@ -91,7 +96,10 @@ msgid ""
"The URL of an external page where users will be able to update their "
"password. It applies only to non local users, ie. those automatically "
"created after a successful external authentication (LDAP, SMTP)."
-msgstr "L'URL d'une page externe où les utilisateurs pourront mettre à jour leur mot de passe. Elle s'applique uniquement aux utilisateurs non locaux, c'est à dire ceux créés après une authentification externe réussie (LDAP, SMTP)."
+msgstr ""
+"L'URL d'une page externe où les utilisateurs pourront mettre à jour leur mot "
+"de passe. Elle s'applique uniquement aux utilisateurs non locaux, c'est à "
+"dire ceux créés après une authentification externe réussie (LDAP, SMTP)."
#: core/app_settings.py:104
msgid "Password recovery announcement"
@@ -99,7 +107,9 @@ msgstr "Annonce pour la récupération de mot de passe"
#: core/app_settings.py:109
msgid "A temporary message that will be displayed on the reset password page."
-msgstr "Un message temporaire qui sera affiché sur la page de récupération de mot de passe"
+msgstr ""
+"Un message temporaire qui sera affiché sur la page de récupération de mot de "
+"passe"
#: core/app_settings.py:115
msgid "Enable password recovery by SMS"
@@ -107,7 +117,9 @@ msgstr "Activer la récupération de mot de passe par SMS"
#: core/app_settings.py:118
msgid "Enable password recovery by SMS for users who filled a phone number."
-msgstr "Activer la récupération de mot de passe par SMS pour les utilisateurs ayant renseigné un numéro de téléphone"
+msgstr ""
+"Activer la récupération de mot de passe par SMS pour les utilisateurs ayant "
+"renseigné un numéro de téléphone"
#: core/app_settings.py:124
msgid "SMS provider"
@@ -115,7 +127,8 @@ msgstr "Fournisseur SMS"
#: core/app_settings.py:127
msgid "Choose a provider to send password recovery SMS"
-msgstr "Choisir un fournisseur pour envoyer les SMS de récupération de mot de passe"
+msgstr ""
+"Choisir un fournisseur pour envoyer les SMS de récupération de mot de passe"
#: core/app_settings.py:133
msgid "LDAP settings"
@@ -144,7 +157,9 @@ msgstr "Activer le serveur secondaire (secours)"
#: core/app_settings.py:152
msgid ""
"Enable a secondary LDAP server which will be used if the primary one fails"
-msgstr "Activer un serveur LDAP secondaire qui sera utilisé si le principal est en échec"
+msgstr ""
+"Activer un serveur LDAP secondaire qui sera utilisé si le principal est en "
+"échec"
#: core/app_settings.py:158
msgid "Secondary server address"
@@ -184,9 +199,12 @@ msgstr "Groupes administrateurs"
#: core/app_settings.py:190
msgid ""
-"Members of those LDAP Posix groups will be created as domain administrators."
-" Use ';' characters to separate groups."
-msgstr "Les membres de ces groupes LDAP (Posix) seront créés en tant qu'administrateurs de domaine. Utiliser des caractères ';' pour séparer les groupes."
+"Members of those LDAP Posix groups will be created as domain administrators. "
+"Use ';' characters to separate groups."
+msgstr ""
+"Les membres de ces groupes LDAP (Posix) seront créés en tant "
+"qu'administrateurs de domaine. Utiliser des caractères ';' pour séparer les "
+"groupes."
#: core/app_settings.py:197
msgid "Group type"
@@ -202,7 +220,9 @@ msgstr "Base de recherche des groupes"
#: core/app_settings.py:209
msgid "The distinguished name of the search base used to find groups"
-msgstr "Le nom distinctif de la base de recherche utilisée pour rechercher les groupes"
+msgstr ""
+"Le nom distinctif de la base de recherche utilisée pour rechercher les "
+"groupes"
#: core/app_settings.py:215
msgid "Password attribute"
@@ -240,7 +260,9 @@ msgstr "ND de connexion"
msgid ""
"The distinguished name to use when binding to the LDAP server. Leave empty "
"for an anonymous bind"
-msgstr "Le nom distinctif à utiliser pour les connections au serveur LDAP. Laisser vide pour une connexion anonyme"
+msgstr ""
+"Le nom distinctif à utiliser pour les connections au serveur LDAP. Laisser "
+"vide pour une connexion anonyme"
#: core/app_settings.py:243 core/app_settings.py:297
msgid "Bind password"
@@ -248,7 +270,9 @@ msgstr "Mot de passe de connexion"
#: core/app_settings.py:246 core/app_settings.py:300
msgid "The password to use when binding to the LDAP server (with 'Bind DN')"
-msgstr "Le mot de passe à utiliser pour les connections au serveur LDAP (avec 'ND de connexion')"
+msgstr ""
+"Le mot de passe à utiliser pour les connections au serveur LDAP (avec 'ND de "
+"connexion')"
#: core/app_settings.py:254 core/app_settings.py:346
msgid "Users search base"
@@ -256,7 +280,9 @@ msgstr "Base de recherche des utilisateurs"
#: core/app_settings.py:257 core/app_settings.py:349
msgid "The distinguished name of the search base used to find users"
-msgstr "Le nom distinctif de la base de recherche utilisée pour rechercher les utilisateurs"
+msgstr ""
+"Le nom distinctif de la base de recherche utilisée pour rechercher les "
+"utilisateurs"
#: core/app_settings.py:263 core/app_settings.py:355
msgid "Search filter"
@@ -266,7 +292,9 @@ msgstr "Filtre de recherche"
msgid ""
"An optional filter string (e.g. '(objectClass=person)'). In order to be "
"valid, it must be enclosed in parentheses."
-msgstr "Un filtre de recherche optionel (ex. '(objectClass=person)'). Pour être valide, il doit être entouré de parenthèses."
+msgstr ""
+"Un filtre de recherche optionel (ex. '(objectClass=person)'). Pour être "
+"valide, il doit être entouré de parenthèses."
#: core/app_settings.py:273
msgid "User DN template"
@@ -277,7 +305,9 @@ msgstr "Modèle de nom distinctif utilisateur"
msgid ""
"The template used to construct a user's DN. It should contain one "
"placeholder (ie. %(user)s)"
-msgstr "Le modèle utilisé pour construire le nom distinctif d'un utilisateur. Il doit contenir un espace réservé (ex. %(user)s)"
+msgstr ""
+"Le modèle utilisé pour construire le nom distinctif d'un utilisateur. Il "
+"doit contenir un espace réservé (ex. %(user)s)"
#: core/app_settings.py:284
msgid "LDAP synchronization settings"
@@ -290,7 +320,9 @@ msgstr "Activer l'export LDAP"
#: core/app_settings.py:311
msgid ""
"Enable automatic synchronization between local database and LDAP directory"
-msgstr "Activer la synchronisation automatique entre la base de données locale et l'annuaire LDAP"
+msgstr ""
+"Activer la synchronisation automatique entre la base de données locale et "
+"l'annuaire LDAP"
#: core/app_settings.py:317
msgid "Delete remote LDAP account when local account is deleted"
@@ -300,7 +332,9 @@ msgstr "Supprimer un compte LDAP distant lorsqu'un compte local est supprimé"
msgid ""
"Delete remote LDAP account when local account is deleted, otherwise it will "
"be disabled."
-msgstr "Supprimer un compte LDAP distant lorsqu'un compte local supprimé, autrement il sera désactivé."
+msgstr ""
+"Supprimer un compte LDAP distant lorsqu'un compte local supprimé, autrement "
+"il sera désactivé."
#: core/app_settings.py:327
msgid "Account DN template"
@@ -311,7 +345,9 @@ msgstr "Modèle des DN de compte"
msgid ""
"The template used to construct an account's DN. It should contain one "
"placeholder (ie. %(user)s)"
-msgstr "Le modèle utilisé pour construite un DN de compte (nom unique). Il doit contenir un emplacement (c'est à dire 1%(user)s)"
+msgstr ""
+"Le modèle utilisé pour construite un DN de compte (nom unique). Il doit "
+"contenir un emplacement (c'est à dire 1%(user)s)"
#: core/app_settings.py:337
msgid "Enable import from LDAP"
@@ -319,7 +355,9 @@ msgstr "Activer l'import LDAP"
#: core/app_settings.py:340
msgid "Enable account synchronization from LDAP directory to local database"
-msgstr "Activer la synchronisation des comptes entre un annuaire LDAP et la base de données locale"
+msgstr ""
+"Activer la synchronisation des comptes entre un annuaire LDAP et la base de "
+"données locale"
#: core/app_settings.py:365
msgid "Username attribute"
@@ -327,7 +365,8 @@ msgstr "Attribut nom d'utilisateur"
#: core/app_settings.py:368
msgid "The name of the LDAP attribute where the username can be found."
-msgstr "Le nom de l'attribut LDAP dans lequel le nom d'utilisateur peut être trouvé."
+msgstr ""
+"Le nom de l'attribut LDAP dans lequel le nom d'utilisateur peut être trouvé."
#: core/app_settings.py:373
msgid "Enable Dovecot LDAP sync"
@@ -335,7 +374,9 @@ msgstr "Activer la synchronisation LDAP pour Dovecot"
#: core/app_settings.py:376
msgid "LDAP authentication settings will be applied to Dovecot configuration."
-msgstr "Les paramètres d'authentification LDAP seront appliqués à la configuration de Dovecot."
+msgstr ""
+"Les paramètres d'authentification LDAP seront appliqués à la configuration "
+"de Dovecot."
#: core/app_settings.py:382
msgid "Dovecot LDAP config file"
@@ -344,7 +385,9 @@ msgstr "Fichier de configuration Dovecot pour LDAP"
#: core/app_settings.py:386
msgid ""
"Location of the configuration file which contains Dovecot LDAP settings."
-msgstr "Emplacement du fichier de configuration qui contient les paramètres LDAP de Dovecot"
+msgstr ""
+"Emplacement du fichier de configuration qui contient les paramètres LDAP de "
+"Dovecot"
#: core/app_settings.py:391 core/templates/core/dashboard.html:5
msgid "Dashboard"
@@ -356,7 +399,9 @@ msgstr "Flux RSS personnalisé"
#: core/app_settings.py:397
msgid "Display custom RSS feed to resellers and domain administrators"
-msgstr "Afficher un flux RSS personnalisé aux revendeurs et administrateurs de domaine"
+msgstr ""
+"Afficher un flux RSS personnalisé aux revendeurs et administrateurs de "
+"domaine"
#: core/app_settings.py:402
msgid "Hide features widget"
@@ -364,7 +409,9 @@ msgstr "Masquer le widget des fonctionnalités"
#: core/app_settings.py:405
msgid "Hide features widget for resellers and domain administrators"
-msgstr "Masquer le widget des fonctionnalités pour les revendeurs et les administrateurs de domaine"
+msgstr ""
+"Masquer le widget des fonctionnalités pour les revendeurs et les "
+"administrateurs de domaine"
#: core/app_settings.py:409
msgid "Notifications"
@@ -404,7 +451,9 @@ msgstr "Envoyer un email quand de nouvelles versions sont trouvées"
#: core/app_settings.py:439
msgid "Send an email to notify admins about new versions"
-msgstr "Envoyer un email pour notifier les administrateurs à propos des nouvelles versions"
+msgstr ""
+"Envoyer un email pour notifier les administrateurs à propos des nouvelles "
+"versions"
#: core/app_settings.py:443
msgid "Recipient"
@@ -420,7 +469,9 @@ msgstr "Envoyer des statistiques"
#: core/app_settings.py:454
msgid "Send statistics to Modoboa public API (counters and used extensions)"
-msgstr "Envoyer des statistiques à l'API publique de Modoboa (compteurs et extensions utilisées)"
+msgstr ""
+"Envoyer des statistiques à l'API publique de Modoboa (compteurs et "
+"extensions utilisées)"
#: core/app_settings.py:458
msgid "Miscellaneous"
@@ -432,9 +483,11 @@ msgstr "Seuil des comptes inactifs"
#: core/app_settings.py:464
msgid ""
-"An account with a last login date greater than this threshold (in days) will"
-" be considered as inactive"
-msgstr "Un compte avec une date de dernière connexion antérieure à ce seuil sera considéré comme inactif"
+"An account with a last login date greater than this threshold (in days) will "
+"be considered as inactive"
+msgstr ""
+"Un compte avec une date de dernière connexion antérieure à ce seuil sera "
+"considéré comme inactif"
#: core/app_settings.py:470
msgid "Top notifications check interval"
@@ -466,7 +519,8 @@ msgstr "Redirection par défaut"
#: core/app_settings.py:494
msgid "The default redirection used when no application is specified"
-msgstr "La redirection appliquée par défaut quand aucune application n'est spécifiée"
+msgstr ""
+"La redirection appliquée par défaut quand aucune application n'est spécifiée"
#: core/app_settings.py:548 core/app_settings.py:556 core/app_settings.py:564
msgid "Invalid syntax"
@@ -489,7 +543,10 @@ msgid ""
"You have USE_TZ set to False, this may result in issues during transitions "
"between summer/winter time (ie the same local time occuring twice due to "
"clock change)."
-msgstr "Vous avez défini USE_TZ à False, ce qui peut entraîner des problèmes lors des transitions entre l'heure d'été et l'heure d'hiver (c.-à-d. La même heure locale se produisant deux fois en raison d'un changement d'horloge)."
+msgstr ""
+"Vous avez défini USE_TZ à False, ce qui peut entraîner des problèmes lors "
+"des transitions entre l'heure d'été et l'heure d'hiver (c.-à-d. La même "
+"heure locale se produisant deux fois en raison d'un changement d'horloge)."
#: core/checks/settings_checks.py:9
msgid "Set `USE_TZ = True` in settings.py"
@@ -523,46 +580,50 @@ msgstr "Choisir un fournisseur"
msgid "Dummy"
msgstr "Test"
-#: core/forms.py:21
+#: core/forms.py:23
msgid "Username"
msgstr "Nom d'utilisateur"
-#: core/forms.py:25
+#: core/forms.py:27
msgid "Password"
msgstr "Mot de passe"
-#: core/forms.py:38
+#: core/forms.py:40
msgid "Old password"
msgstr "Ancien mot de passe"
-#: core/forms.py:42
+#: core/forms.py:44
msgid "New password"
msgstr "Nouveau mot de passe"
-#: core/forms.py:46
+#: core/forms.py:48
msgid "Confirmation"
msgstr "Confirmation"
-#: core/forms.py:74
+#: core/forms.py:76
msgid "Old password mismatchs"
msgstr "L'ancien mot de passe est incorrect"
-#: core/forms.py:83
+#: core/forms.py:85
msgid "Passwords mismatch"
msgstr "Les mots de passe diffèrent"
-#: core/forms.py:103
+#: core/forms.py:105
msgid "Enable API access"
msgstr "Activer l'accès API"
-#: core/forms.py:137
+#: core/forms.py:139
msgid "Verification code"
msgstr "Code de vérification"
-#: core/forms.py:148
+#: core/forms.py:150
msgid "Invalid code"
msgstr "Code incorrect"
+#: core/forms.py:163
+msgid "This code is invalid"
+msgstr "Ce code est incorrect"
+
#: core/handlers.py:36
msgid "added"
msgstr "ajouté"
@@ -605,41 +666,42 @@ msgstr "Une ou plusieurs mises à jour sont disponibles!"
msgid "[modoboa] Update(s) available"
msgstr "[modoboa] Mise(s) à jour disponible(s)"
-#: core/models.py:55
+#: core/models.py:52
msgid "Allow mailboxes access"
msgstr "Autoriser l'accès aux boites"
-#: core/models.py:57
+#: core/models.py:54
msgid "Allow this administrator to access user mailboxes"
msgstr "Autoriser cet administrateur à accéder aux boites des utilisateurs"
-#: core/models.py:60
+#: core/models.py:57
msgid "password"
msgstr "mot de passe"
-#: core/models.py:63
+#: core/models.py:60
msgid "language"
msgstr "langue"
-#: core/models.py:66
+#: core/models.py:63
msgid "Prefered language to display pages."
msgstr "Langage préféré pour afficher les pages"
-#: core/models.py:70
+#: core/models.py:67
msgid "Phone number"
msgstr "Numéro de téléphone"
-#: core/models.py:72
+#: core/models.py:69
msgid "Secondary email"
msgstr "Email secondaire"
-#: core/models.py:75
+#: core/models.py:72
msgid "An alternative e-mail address, can be used for recovery needs."
msgstr "Une adresse e-mail alternative ou de secours."
#: core/models.py:126
msgid "Failed to update password: LDAP module not installed"
-msgstr "Echec de la mise à jour du mot de passe : le module LDAP n'est pas installé"
+msgstr ""
+"Echec de la mise à jour du mot de passe : le module LDAP n'est pas installé"
#: core/models.py:155
msgid "account"
@@ -693,7 +755,17 @@ msgstr[1] "Le mot de passe doit contenir au moins {} caractères spéciaux."
#: core/password_validation.py:65
msgid "Your password must contain a combination of different character types."
-msgstr "Votre mot de passe doit contenir une combinaison de différents types de caractère."
+msgstr ""
+"Votre mot de passe doit contenir une combinaison de différents types de "
+"caractère."
+
+#: core/serializers.py:15
+msgid "Invalid PIN code"
+msgstr "Code PIN incorrect"
+
+#: core/serializers.py:21
+msgid "2FA is already enabled"
+msgstr "La 2FA est déjà activée"
#: core/sms_backends/ovh.py:19
msgid "API endpoint"
@@ -763,7 +835,9 @@ msgstr "Dernières nouvelles"
msgid ""
"Visit the official "
"weblog for more information."
-msgstr "Visitez le blog officiel pour plus d'information."
+msgstr ""
+"Visitez le blog "
+"officiel pour plus d'information."
#: core/templates/core/api_access.html:3
msgid "API access"
@@ -776,9 +850,11 @@ msgstr "Contrôlez votre accès à l'API de Modoboa"
#: core/templates/core/api_access.html:7
#, python-format
msgid ""
-"A documentation of the API is available here."
-msgstr "Une documentation de l'API est disponible ici."
+"A documentation of the API is available here."
+msgstr ""
+"Une documentation de l'API est disponible ici."
#: core/templates/core/api_access.html:14
msgid "API access token"
@@ -808,7 +884,8 @@ msgstr "Une ou plusieurs mises à jour sont disponibles!"
#: core/templates/core/information.html:6
msgid "Check the following list to find the component(s) that can be updated."
-msgstr "Consulter la liste suivante pour trouver le(s) composant(s) concerné(s)."
+msgstr ""
+"Consulter la liste suivante pour trouver le(s) composant(s) concerné(s)."
#: core/templates/core/information.html:10
msgid "Applications"
@@ -874,8 +951,12 @@ msgid ""
msgid_plural ""
"\n"
" The following updates are available:\n"
-msgstr[0] "\nLa mise à jour suivante est disponible :\n"
-msgstr[1] "\nLes mises à jour suivantes sont disponibles :\n"
+msgstr[0] ""
+"\n"
+"La mise à jour suivante est disponible :\n"
+msgstr[1] ""
+"\n"
+"Les mises à jour suivantes sont disponibles :\n"
#: core/templates/core/parameters.html:2 core/templatetags/core_tags.py:71
msgid "Parameters"
@@ -923,6 +1004,79 @@ msgstr "Mettez à jour vos informations personnelles"
msgid "Click here to update your password"
msgstr "Cliquez ici pour mettre à jour votre mot de passe"
+#: core/templates/core/user_security.html:2 core/templatetags/core_tags.py:124
+msgid "Security"
+msgstr "Sécurité"
+
+#: core/templates/core/user_security.html:6
+msgid "Two-Factor authentication"
+msgstr "Authentification à double facteur"
+
+#: core/templates/core/user_security.html:17
+msgid ""
+"Install a soft token authenticator like FreeOTP or Google Authenticator from "
+"your application repository and use that app to scan this QR code."
+msgstr "Installer une application de génération de mots de passe à usage unique comme FreeOTP ou Google Athenticator depuis votre magasin d'applications et utiliser le pour scanner ce code QR."
+
+#: core/templates/core/user_security.html:25
+msgid "Pin code"
+msgstr "Code PIN"
+
+#: core/templates/core/user_security.html:28
+msgid "Register"
+msgstr "Enregistrer"
+
+#: core/templates/core/user_security.html:33
+msgid ""
+"Congratulations! Two-Factor Authentication is now enabled for your account."
+msgstr "Félicitations ! L'authentification à double facteur est activée pour votre compte."
+
+#: core/templates/core/user_security.html:38
+#: core/templates/core/user_security.html:89
+msgid ""
+"The following recovery codes can be used one time each to let you regain "
+"access to your account, in case you lose your phone for example. Make sure "
+"to save them in a safe place, otherwise you won't be able to access your "
+"account anymore."
+msgstr "Les codes de récuparation suivants peuvent être utilisés une fois chacun pour récupérer l'accès à votre compte, par exemple si vous perdez votre téléphone. Assurez vous de les sauvegarder dans un endroit sûr, autrement vous ne pourrez plus accéder à votre compte."
+
+#: core/templates/core/user_security.html:53
+msgid "Two-Factor Authentication is enabled for your account."
+msgstr "L'authentication à double facteur est activée pour votre compte."
+
+#: core/templates/core/user_security.html:58
+msgid "Disable 2FA"
+msgstr "Désactiver la 2FA"
+
+#: core/templates/core/user_security.html:61
+msgid "Reset recovery codes"
+msgstr "Réinitialiser les codes de récupération"
+
+#: core/templates/core/user_security.html:65
+msgid ""
+"Two-Factor Authentication (2FA) is not yet activated for your account. "
+"Enabling this feature will increase your account's security."
+msgstr "L'authentification à double facteur (2FA) n'est pas encore activée pour votre compte. Activer cette fonctionnalité améliorera la sécurité de votre compte."
+
+#: core/templates/core/user_security.html:73
+msgid "Enable 2FA"
+msgstr "Activer la 2FA"
+
+#: core/templates/core/user_security.html:83
+#: core/templates/core/user_security.html:99
+#: templates/common/generic_modal.html:6 templates/common/generic_modal.html:13
+#: templates/common/wizard_forms.html:6 templates/common/wizard_forms.html:27
+msgid "Close"
+msgstr "Fermer"
+
+#: core/templates/core/user_security.html:84
+msgid "New recovery codes"
+msgstr "Nouveaux codes de récupération"
+
+#: core/templates/core/user_security.html:88
+msgid "Here are your new recovery codes. "
+msgstr "Voici vos nouveaux codes de récupération."
+
#: core/templatetags/core_tags.py:34
msgid "Modoboa"
msgstr "Modoboa"
@@ -931,7 +1085,7 @@ msgstr "Modoboa"
msgid "Logout"
msgstr "Déconnexion"
-#: core/templatetags/core_tags.py:127
+#: core/templatetags/core_tags.py:131
msgid "API"
msgstr "API"
@@ -943,51 +1097,61 @@ msgstr "Le coeur de Modoboa"
msgid "Parameters saved"
msgstr "Paramètres enregistrés"
-#: core/views/auth.py:59
+#: core/views/auth.py:61
#, python-format
msgid "Password scheme mismatch. Updating %s password"
-msgstr "La méthode de chiffrement du mot de passe diffère. Mise à jour du mot de passe de %s"
+msgstr ""
+"La méthode de chiffrement du mot de passe diffère. Mise à jour du mot de "
+"passe de %s"
-#: core/views/auth.py:66
+#: core/views/auth.py:68
#, python-format
msgid "Password hash parameter missmatch. Updating %s password"
msgstr ""
-#: core/views/auth.py:82
+#: core/views/auth.py:84
#, python-format
msgid "User '%s' successfully logged in"
msgstr "Utiliseur '%s' connecté avec succès"
-#: core/views/auth.py:90
+#: core/views/auth.py:92
msgid "Your username and password didn't match. Please try again."
-msgstr "Votre nom d'utilisateur et mot de passe ne correspondent pas. Veuillez réessayer."
+msgstr ""
+"Votre nom d'utilisateur et mot de passe ne correspondent pas. Veuillez "
+"réessayer."
-#: core/views/auth.py:125
+#: core/views/auth.py:127
msgid "User '{}' successfully logged out"
msgstr "Utilisateur '{}' déconnecté avec succès"
-#: core/views/auth.py:170 core/views/auth.py:225
+#: core/views/auth.py:172 core/views/auth.py:227
msgid "Please use the following code to recover your Modoboa password: {}"
-msgstr "Veuillez utiliser le code suivant pour récupérer votre mot de passe Modoboa : {}"
+msgstr ""
+"Veuillez utiliser le code suivant pour récupérer votre mot de passe "
+"Modoboa : {}"
-#: core/views/user.py:53
+#: core/views/user.py:60
msgid "Profile updated"
msgstr "Profil mis à jour"
-#: core/views/user.py:81
+#: core/views/user.py:88
msgid "Preferences saved"
msgstr "Préférences enregistrées"
-#: core/views/user.py:103
+#: core/views/user.py:110
msgid "Access updated"
msgstr "Accès mis à jour"
+#: core/viewsets.py:52
+msgid "2FA is not enabled"
+msgstr "La 2FA n'est pas activée"
+
#: lib/db_utils.py:23
#, python-format
msgid "Connection to database %s not configured"
msgstr "Connexion à la base de données %s non configurée"
-#: lib/email_utils.py:141 lib/email_utils.py:360
+#: lib/email_utils.py:143 lib/email_utils.py:362
msgid "unable to determine encoding of string"
msgstr "Impossible de déterminer l'encodage de la chaîne de caractères"
@@ -1071,12 +1235,6 @@ msgstr "Erreur :"
msgid "Help:"
msgstr "Aide :"
-#: templates/common/generic_modal.html:6
-#: templates/common/generic_modal.html:13 templates/common/wizard_forms.html:6
-#: templates/common/wizard_forms.html:27
-msgid "Close"
-msgstr "Fermer"
-
#: templates/common/wizard_forms.html:31
msgid "Previous"
msgstr "Précédent"
@@ -1107,7 +1265,9 @@ msgstr "Mot de passe oublié ?"
msgid ""
"Your password has been set. You may go ahead and sign "
"in now."
-msgstr "Votre mot de passe a été défini. Vous pouvez continuer et vous connecter dès maintenant."
+msgstr ""
+"Votre mot de passe a été défini. Vous pouvez continuer et vous connecter dès maintenant."
#: templates/registration/password_reset_confirm.html:7
msgid "Change password"
@@ -1117,7 +1277,10 @@ msgstr "Changer le mot de passe"
msgid ""
"The password reset link was invalid, possibly because it has already been "
"used. Please request a new password reset."
-msgstr "Le lien de réinitialisation du mot de passe était invalide, probablement parce qu'il a déjà été utilisé. Merci de demander une nouvelle réinitialisation de mot de passe."
+msgstr ""
+"Le lien de réinitialisation du mot de passe était invalide, probablement "
+"parce qu'il a déjà été utilisé. Merci de demander une nouvelle "
+"réinitialisation de mot de passe."
#: templates/registration/password_reset_confirm_code.html:20
msgid "Enter the code you've just received by SMS"
@@ -1133,20 +1296,29 @@ msgid ""
"exists with the email you entered. You should receive them shortly.
"
"If you don't receive an email, please make sure you've entered your "
"primary address, and check your spam folder.
"
-msgstr "Nous vous avons envoyé par email les instructions pour définir votre mot de passe, pour peu qu'un compte avec l'email que vous avez renseigné existe. Vous devriez les recevoir rapidement.
Si vous ne recevez pas d'email, merci de vérifier que vous avez renseigné votre adresse principale et de vérifier votre dossier spam.
"
+msgstr ""
+"Nous vous avons envoyé par email les instructions pour définir votre mot "
+"de passe, pour peu qu'un compte avec l'email que vous avez renseigné existe. "
+"Vous devriez les recevoir rapidement.
Si vous ne recevez pas d'email, "
+"merci de vérifier que vous avez renseigné votre adresse principale et de "
+"vérifier votre dossier spam.
"
#: templates/registration/password_reset_email.html:4
#, python-format
msgid ""
"To initiate the password reset process for your %(username)s Modoboa "
"account, click the link below:"
-msgstr "Pour initier la procédure de réinitialisation du mot de passe de votre compte Modoboa %(username)s, cliquez sur le lien ci-dessous :"
+msgstr ""
+"Pour initier la procédure de réinitialisation du mot de passe de votre "
+"compte Modoboa %(username)s, cliquez sur le lien ci-dessous :"
#: templates/registration/password_reset_email.html:10
msgid ""
"If clicking the link above doesn't work, please copy and paste the URL in a "
"new browser window instead."
-msgstr "Si cliquer sur lien ci-dessous ne fonctionne pas, merci de copier / coller cette URL dans une nouvelle fenêtre navigateur à la place."
+msgstr ""
+"Si cliquer sur lien ci-dessous ne fonctionne pas, merci de copier / coller "
+"cette URL dans une nouvelle fenêtre navigateur à la place."
#: templates/registration/password_reset_email.html:14
msgid "Sincerely, The Modoboa Team."
diff --git a/modoboa/locale/fr/LC_MESSAGES/djangojs.po b/modoboa/locale/fr/LC_MESSAGES/djangojs.po
index 2574ef4e2..76b21d816 100644
--- a/modoboa/locale/fr/LC_MESSAGES/djangojs.po
+++ b/modoboa/locale/fr/LC_MESSAGES/djangojs.po
@@ -2,23 +2,22 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
-#
+#
# Translators:
# Antoine Nguyen , 2020
-#
-#, fuzzy
+#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-03-09 16:48+0100\n"
-"PO-Revision-Date: 2020-05-11 18:45+0000\n"
+"POT-Creation-Date: 2020-11-05 15:47+0100\n"
+"PO-Revision-Date: 2020-11-05 15:48+0100\n"
"Last-Translator: Antoine Nguyen , 2020\n"
"Language-Team: French (https://www.transifex.com/tonio/teams/13749/fr/)\n"
+"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: bower_components/jquery-ui/jquery-ui.min.js:8
@@ -41,7 +40,7 @@ msgstr "Avertissement"
msgid "Cancel"
msgstr "Annuler"
-#: static/js/global.js:342
+#: static/js/global.js:347
msgid "Internal error"
msgstr "Erreur interne"
@@ -56,3 +55,11 @@ msgstr "Chercher"
#: static/js/searchbar.js:28
msgid "Search..."
msgstr "Chercher..."
+
+#: static/js/twocols_nav.js:170
+msgid "Are you sure?"
+msgstr "Etes vous certain ?"
+
+#: static/js/twocols_nav.js:171
+msgid "This will invalidate your registered application."
+msgstr "Cela annulera votre application enregistrée."