User locale #557

This commit is contained in:
Daniel Grams 2023-11-14 16:24:03 +01:00
parent 705bb4bf0b
commit 069160c75d
34 changed files with 828 additions and 607 deletions

View File

@ -62,7 +62,7 @@ flask db upgrade
### Init ### Init
```sh ```sh
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel init -i messages.pot -d project/translations -l de pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -k dummy_gettext -o messages.pot . && pybabel init -i messages.pot -d project/translations -l de
``` ```
### Add locale ### Add locale
@ -74,7 +74,7 @@ pybabel init -i messages.pot -d project/translations -l en
### Extract new msgid's and merge into \*.po files ### Extract new msgid's and merge into \*.po files
```sh ```sh
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel update -N -i messages.pot -d project/translations pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -k dummy_gettext -o messages.pot . && pybabel update -N -i messages.pot -d project/translations
``` ```
#### Compile after translation is done #### Compile after translation is done

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-09-30 11:43+0200\n" "POT-Creation-Date: 2023-11-13 17:25+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,183 +17,183 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.12.1\n" "Generated-By: Babel 2.12.1\n"
#: project/i10n.py:15 #: project/i10n.py:32
msgid "Event_Art" msgid "Event_Art"
msgstr "" msgstr ""
#: project/i10n.py:16 #: project/i10n.py:33
msgid "Event_Book" msgid "Event_Book"
msgstr "" msgstr ""
#: project/i10n.py:17 #: project/i10n.py:34
msgid "Event_Movie" msgid "Event_Movie"
msgstr "" msgstr ""
#: project/i10n.py:18 #: project/i10n.py:35
msgid "Event_Family" msgid "Event_Family"
msgstr "" msgstr ""
#: project/i10n.py:19 #: project/i10n.py:36
msgid "Event_Festival" msgid "Event_Festival"
msgstr "" msgstr ""
#: project/i10n.py:20 #: project/i10n.py:37
msgid "Event_Religious" msgid "Event_Religious"
msgstr "" msgstr ""
#: project/i10n.py:21 #: project/i10n.py:38
msgid "Event_Shopping" msgid "Event_Shopping"
msgstr "" msgstr ""
#: project/i10n.py:22 #: project/i10n.py:39
msgid "Event_Comedy" msgid "Event_Comedy"
msgstr "" msgstr ""
#: project/i10n.py:23 #: project/i10n.py:40
msgid "Event_Music" msgid "Event_Music"
msgstr "" msgstr ""
#: project/i10n.py:24 #: project/i10n.py:41
msgid "Event_Dance" msgid "Event_Dance"
msgstr "" msgstr ""
#: project/i10n.py:25 #: project/i10n.py:42
msgid "Event_Nightlife" msgid "Event_Nightlife"
msgstr "" msgstr ""
#: project/i10n.py:26 #: project/i10n.py:43
msgid "Event_Theater" msgid "Event_Theater"
msgstr "" msgstr ""
#: project/i10n.py:27 #: project/i10n.py:44
msgid "Event_Dining" msgid "Event_Dining"
msgstr "" msgstr ""
#: project/i10n.py:28 #: project/i10n.py:45
msgid "Event_Conference" msgid "Event_Conference"
msgstr "" msgstr ""
#: project/i10n.py:29 #: project/i10n.py:46
msgid "Event_Meetup" msgid "Event_Meetup"
msgstr "" msgstr ""
#: project/i10n.py:30 #: project/i10n.py:47
msgid "Event_Fitness" msgid "Event_Fitness"
msgstr "" msgstr ""
#: project/i10n.py:31 #: project/i10n.py:48
msgid "Event_Sports" msgid "Event_Sports"
msgstr "" msgstr ""
#: project/i10n.py:32 #: project/i10n.py:49
msgid "Event_Other" msgid "Event_Other"
msgstr "" msgstr ""
#: project/i10n.py:33 #: project/i10n.py:50
msgid "Event_Exhibition" msgid "Event_Exhibition"
msgstr "" msgstr ""
#: project/i10n.py:34 #: project/i10n.py:51
msgid "Event_Culture" msgid "Event_Culture"
msgstr "" msgstr ""
#: project/i10n.py:35 #: project/i10n.py:52
msgid "Event_Tour" msgid "Event_Tour"
msgstr "" msgstr ""
#: project/i10n.py:36 #: project/i10n.py:53
msgid "Event_OpenAir" msgid "Event_OpenAir"
msgstr "" msgstr ""
#: project/i10n.py:37 #: project/i10n.py:54
msgid "Event_Stage" msgid "Event_Stage"
msgstr "" msgstr ""
#: project/i10n.py:38 #: project/i10n.py:55
msgid "Event_Lecture" msgid "Event_Lecture"
msgstr "" msgstr ""
#: project/i10n.py:39 #: project/i10n.py:56
msgid "Typical Age range" msgid "Typical Age range"
msgstr "" msgstr ""
#: project/i10n.py:40 #: project/i10n.py:57
msgid "Administrator" msgid "Administrator"
msgstr "" msgstr ""
#: project/i10n.py:41 #: project/i10n.py:58
msgid "Event expert" msgid "Event expert"
msgstr "" msgstr ""
#: project/i10n.py:42 #: project/i10n.py:59
msgid "EventReviewStatus.inbox" msgid "EventReviewStatus.inbox"
msgstr "" msgstr ""
#: project/i10n.py:43 #: project/i10n.py:60
msgid "EventReviewStatus.verified" msgid "EventReviewStatus.verified"
msgstr "" msgstr ""
#: project/i10n.py:44 #: project/i10n.py:61
msgid "EventReviewStatus.rejected" msgid "EventReviewStatus.rejected"
msgstr "" msgstr ""
#: project/i10n.py:45 #: project/i10n.py:62
msgid "Scope_openid" msgid "Scope_openid"
msgstr "" msgstr ""
#: project/i10n.py:46 #: project/i10n.py:63
msgid "Scope_profile" msgid "Scope_profile"
msgstr "" msgstr ""
#: project/i10n.py:47 #: project/i10n.py:64
msgid "Scope_user:read" msgid "Scope_user:read"
msgstr "" msgstr ""
#: project/i10n.py:48 #: project/i10n.py:65
msgid "Scope_user:write" msgid "Scope_user:write"
msgstr "" msgstr ""
#: project/i10n.py:49 #: project/i10n.py:66
msgid "Scope_organizer:write" msgid "Scope_organizer:write"
msgstr "" msgstr ""
#: project/i10n.py:50 #: project/i10n.py:67
msgid "Scope_place:write" msgid "Scope_place:write"
msgstr "" msgstr ""
#: project/i10n.py:51 #: project/i10n.py:68
msgid "Scope_event:write" msgid "Scope_event:write"
msgstr "" msgstr ""
#: project/i10n.py:52 #: project/i10n.py:69
msgid "Scope_eventlist:write" msgid "Scope_eventlist:write"
msgstr "" msgstr ""
#: project/i10n.py:53 #: project/i10n.py:70
msgid "Scope_eventreference:write" msgid "Scope_eventreference:write"
msgstr "" msgstr ""
#: project/i10n.py:54 #: project/i10n.py:71
msgid "Scope_organization:read" msgid "Scope_organization:read"
msgstr "" msgstr ""
#: project/i10n.py:55 #: project/i10n.py:72
msgid "Scope_organization:write" msgid "Scope_organization:write"
msgstr "" msgstr ""
#: project/i10n.py:56 #: project/i10n.py:73
msgid "Scope_customwidget:write" msgid "Scope_customwidget:write"
msgstr "" msgstr ""
#: project/i10n.py:57 #: project/i10n.py:74
msgid "There must be no self-reference." msgid "There must be no self-reference."
msgstr "" msgstr ""
#: project/utils.py:15 #: project/utils.py:19
msgid "Event_" msgid "Event_"
msgstr "" msgstr ""
#: project/utils.py:19 #: project/utils.py:23
msgid "." msgid "."
msgstr "" msgstr ""
@ -201,11 +201,6 @@ msgstr ""
msgid "message" msgid "message"
msgstr "" msgstr ""
#: project/api/organization/resources.py:647
#: project/views/admin_unit_member_invitation.py:89
msgid "You have received an invitation"
msgstr ""
#: project/forms/admin.py:11 project/templates/layout.html:344 #: project/forms/admin.py:11 project/templates/layout.html:344
#: project/views/root.py:55 #: project/views/root.py:55
msgid "Terms of service" msgid "Terms of service"
@ -237,7 +232,7 @@ msgid "Announcement"
msgstr "" msgstr ""
#: project/forms/admin.py:18 project/forms/oauth2_client.py:24 #: project/forms/admin.py:18 project/forms/oauth2_client.py:24
#: project/forms/user.py:13 #: project/forms/user.py:18 project/forms/user.py:32
msgid "Save" msgid "Save"
msgstr "" msgstr ""
@ -267,7 +262,7 @@ msgstr ""
#: project/forms/admin_unit_member.py:12 project/forms/admin_unit_member.py:25 #: project/forms/admin_unit_member.py:12 project/forms/admin_unit_member.py:25
#: project/forms/admin_unit_member.py:30 project/forms/event.py:112 #: project/forms/admin_unit_member.py:30 project/forms/event.py:112
#: project/forms/event_suggestion.py:38 project/forms/organizer.py:33 #: project/forms/event_suggestion.py:38 project/forms/organizer.py:33
#: project/forms/user.py:18 project/forms/user.py:23 #: project/forms/user.py:37 project/forms/user.py:42
#: project/templates/_macros.html:246 project/templates/_macros.html:1586 #: project/templates/_macros.html:246 project/templates/_macros.html:1586
#: project/templates/admin/admin.html:27 project/templates/admin/email.html:4 #: project/templates/admin/admin.html:27 project/templates/admin/email.html:4
#: project/templates/admin/email.html:66 project/templates/admin/users.html:19 #: project/templates/admin/email.html:66 project/templates/admin/users.html:19
@ -475,7 +470,7 @@ msgid "Verification requests postal codes"
msgstr "" msgstr ""
#: project/forms/admin_unit.py:93 #: project/forms/admin_unit.py:93
msgid "Accept verification requests to organizations with these postal codes." msgid "Limit verification requests to organizations with these postal codes."
msgstr "" msgstr ""
#: project/forms/admin_unit.py:109 #: project/forms/admin_unit.py:109
@ -523,11 +518,11 @@ msgstr ""
msgid "Link Color" msgid "Link Color"
msgstr "" msgstr ""
#: project/forms/admin_unit.py:165 project/forms/user.py:17 #: project/forms/admin_unit.py:165 project/forms/user.py:36
msgid "Request deletion" msgid "Request deletion"
msgstr "" msgstr ""
#: project/forms/admin_unit.py:170 project/forms/user.py:22 #: project/forms/admin_unit.py:170 project/forms/user.py:41
#: project/templates/admin_unit/cancel_deletion.html:6 #: project/templates/admin_unit/cancel_deletion.html:6
#: project/templates/admin_unit/update.html:44 #: project/templates/admin_unit/update.html:44
#: project/templates/manage/events.html:49 project/templates/profile.html:13 #: project/templates/manage/events.html:49 project/templates/profile.html:13
@ -1356,13 +1351,21 @@ msgstr ""
msgid "Confirm" msgid "Confirm"
msgstr "" msgstr ""
#: project/forms/user.py:9 project/templates/admin/admin.html:31 #: project/forms/user.py:9
msgid "Language"
msgstr ""
#: project/forms/user.py:11
msgid "Default"
msgstr ""
#: project/forms/user.py:28 project/templates/admin/admin.html:31
#: project/templates/admin/newsletter.html:4 #: project/templates/admin/newsletter.html:4
#: project/templates/admin/newsletter.html:93 #: project/templates/admin/newsletter.html:93
msgid "Newsletter" msgid "Newsletter"
msgstr "" msgstr ""
#: project/forms/user.py:10 #: project/forms/user.py:29
msgid "Information about new features and improvements." msgid "Information about new features and improvements."
msgstr "" msgstr ""
@ -1850,7 +1853,7 @@ msgstr ""
#: project/templates/developer/read.html:4 #: project/templates/developer/read.html:4
#: project/templates/developer/read.html:8 project/templates/layout.html:361 #: project/templates/developer/read.html:8 project/templates/layout.html:361
#: project/templates/profile.html:45 #: project/templates/profile.html:49
msgid "Developer" msgid "Developer"
msgstr "" msgstr ""
@ -1864,20 +1867,25 @@ msgstr ""
msgid "Delete account" msgid "Delete account"
msgstr "" msgstr ""
#: project/templates/profile.html:36 #: project/templates/profile.html:36 project/templates/user/general.html:4
#: project/templates/user/general.html:8
msgid "General"
msgstr ""
#: project/templates/profile.html:40
#: project/templates/user/notifications.html:4 #: project/templates/user/notifications.html:4
#: project/templates/user/notifications.html:8 #: project/templates/user/notifications.html:8
msgid "Notifications" msgid "Notifications"
msgstr "" msgstr ""
#: project/templates/profile.html:40 #: project/templates/profile.html:44
msgid "Applications" msgid "Applications"
msgstr "" msgstr ""
#: project/templates/oauth2_client/list.html:4 #: project/templates/oauth2_client/list.html:4
#: project/templates/oauth2_client/list.html:11 #: project/templates/oauth2_client/list.html:11
#: project/templates/oauth2_client/read.html:11 #: project/templates/oauth2_client/read.html:11
#: project/templates/profile.html:49 #: project/templates/profile.html:53
msgid "OAuth2 clients" msgid "OAuth2 clients"
msgstr "" msgstr ""
@ -1912,7 +1920,7 @@ msgstr ""
msgid "User" msgid "User"
msgstr "" msgstr ""
#: project/templates/admin/email.html:47 project/views/admin.py:165 #: project/templates/admin/email.html:47 project/views/admin.py:163
msgid "Mail sent successfully" msgid "Mail sent successfully"
msgstr "" msgstr ""
@ -2297,7 +2305,7 @@ msgstr ""
#: project/templates/manage/reference_requests_incoming.html:20 #: project/templates/manage/reference_requests_incoming.html:20
#: project/templates/reference/read.html:22 #: project/templates/reference/read.html:22
#: project/templates/reference_request/review_status.html:17 #: project/templates/reference_request/review_status.html:17
#: project/views/reference_request_review.py:42 #: project/views/reference_request_review.py:38
msgid "View event" msgid "View event"
msgstr "" msgstr ""
@ -2408,7 +2416,7 @@ msgid "Reviews"
msgstr "" msgstr ""
#: project/templates/manage/verification_requests_incoming.html:19 #: project/templates/manage/verification_requests_incoming.html:19
#: project/views/verification_request_review.py:36 #: project/views/verification_request_review.py:32
msgid "View organization" msgid "View organization"
msgstr "" msgstr ""
@ -2537,7 +2545,7 @@ msgstr ""
msgid "You do not have an account yet? Not a problem!" msgid "You do not have an account yet? Not a problem!"
msgstr "" msgstr ""
#: project/templates/security/login_user.html:44 project/views/widget.py:122 #: project/templates/security/login_user.html:44 project/views/widget.py:119
msgid "Register for free" msgid "Register for free"
msgstr "" msgstr ""
@ -2614,29 +2622,19 @@ msgid "Organization successfully deleted"
msgstr "" msgstr ""
#: project/views/admin.py:133 project/views/manage.py:485 #: project/views/admin.py:133 project/views/manage.py:485
#: project/views/user.py:63 #: project/views/user.py:64 project/views/user.py:83
msgid "Settings successfully updated" msgid "Settings successfully updated"
msgstr "" msgstr ""
#: project/views/admin.py:153 #: project/views/admin.py:233
#, python-format
msgid "Test mail from %(site_name)s"
msgstr ""
#: project/views/admin.py:183
#, python-format
msgid "Newsletter from %(site_name)s"
msgstr ""
#: project/views/admin.py:232
msgid "User successfully updated" msgid "User successfully updated"
msgstr "" msgstr ""
#: project/views/admin.py:252 #: project/views/admin.py:253
msgid "Entered email does not match user email" msgid "Entered email does not match user email"
msgstr "" msgstr ""
#: project/views/admin.py:256 #: project/views/admin.py:257
msgid "User successfully deleted" msgid "User successfully deleted"
msgstr "" msgstr ""
@ -2654,14 +2652,6 @@ msgstr ""
msgid "AdminUnit successfully updated" msgid "AdminUnit successfully updated"
msgstr "" msgstr ""
#: project/views/admin_unit.py:271
msgid "Organization invitation accepted"
msgstr ""
#: project/views/admin_unit.py:285
msgid "Organization deletion requested"
msgstr ""
#: project/views/admin_unit_member.py:46 #: project/views/admin_unit_member.py:46
msgid "Member successfully updated" msgid "Member successfully updated"
msgstr "" msgstr ""
@ -2682,46 +2672,38 @@ msgstr ""
msgid "Invitation successfully declined" msgid "Invitation successfully declined"
msgstr "" msgstr ""
#: project/views/admin_unit_member_invitation.py:94 #: project/views/admin_unit_member_invitation.py:93
msgid "Invitation successfully sent" msgid "Invitation successfully sent"
msgstr "" msgstr ""
#: project/views/admin_unit_member_invitation.py:117 #: project/views/admin_unit_member_invitation.py:116
msgid "Entered email does not match invitation email" msgid "Entered email does not match invitation email"
msgstr "" msgstr ""
#: project/views/admin_unit_member_invitation.py:122 #: project/views/admin_unit_member_invitation.py:121
msgid "Invitation successfully deleted" msgid "Invitation successfully deleted"
msgstr "" msgstr ""
#: project/views/event.py:208 #: project/views/event.py:209
msgid "Event successfully published" msgid "Event successfully published"
msgstr "" msgstr ""
#: project/views/event.py:210 #: project/views/event.py:211
msgid "Draft successfully saved" msgid "Draft successfully saved"
msgstr "" msgstr ""
#: project/views/event.py:212 #: project/views/event.py:213
msgid "Event successfully planned" msgid "Event successfully planned"
msgstr "" msgstr ""
#: project/views/event.py:272 #: project/views/event.py:273
msgid "Event successfully updated" msgid "Event successfully updated"
msgstr "" msgstr ""
#: project/views/event.py:298 #: project/views/event.py:299
msgid "Event successfully deleted" msgid "Event successfully deleted"
msgstr "" msgstr ""
#: project/views/event.py:463
msgid "Referenced event changed"
msgstr ""
#: project/views/event.py:486
msgid "New event report"
msgstr ""
#: project/views/event_place.py:34 #: project/views/event_place.py:34
msgid "Place successfully created" msgid "Place successfully created"
msgstr "" msgstr ""
@ -2742,11 +2724,6 @@ msgstr ""
msgid "Event suggestion successfully rejected" msgid "Event suggestion successfully rejected"
msgstr "" msgstr ""
#: project/views/event_suggestion.py:87
#: project/views/reference_request_review.py:139
msgid "Event review status updated"
msgstr ""
#: project/views/js.py:33 #: project/views/js.py:33
msgid "Short name is already taken" msgid "Short name is already taken"
msgstr "" msgstr ""
@ -2847,68 +2824,114 @@ msgid ""
"notified after the other organization reviews the event." "notified after the other organization reviews the event."
msgstr "" msgstr ""
#: project/views/reference_request.py:172 #: project/views/reference_request_review.py:36
msgid "New reference request"
msgstr ""
#: project/views/reference_request.py:181
msgid "New reference automatically verified"
msgstr ""
#: project/views/reference_request_review.py:40
msgid "Request already verified" msgid "Request already verified"
msgstr "" msgstr ""
#: project/views/reference_request_review.py:61 #: project/views/reference_request_review.py:57
msgid "Reference successfully created" msgid "Reference successfully created"
msgstr "" msgstr ""
#: project/views/reference_request_review.py:69 #: project/views/reference_request_review.py:65
msgid "Request successfully updated" msgid "Request successfully updated"
msgstr "" msgstr ""
#: project/views/reference_request_review.py:92 #: project/views/reference_request_review.py:88
#: project/views/verification_request_review.py:83 #: project/views/verification_request_review.py:79
#, python-format #, python-format
msgid "" msgid ""
"If all upcoming reference requests of %(admin_unit_name)s should be " "If all upcoming reference requests of %(admin_unit_name)s should be "
"verified automatically." "verified automatically."
msgstr "" msgstr ""
#: project/views/user.py:107 #: project/views/user.py:127
msgid "" msgid ""
"You are administrator of at least one organization. Cancel your " "You are administrator of at least one organization. Cancel your "
"membership to delete your account." "membership to delete your account."
msgstr "" msgstr ""
#: project/views/user.py:114 project/views/user.py:141 #: project/views/user.py:134 project/views/user.py:161
msgid "Entered email does not match your email" msgid "Entered email does not match your email"
msgstr "" msgstr ""
#: project/views/user.py:160 #: project/views/utils.py:27
msgid "New event report"
msgstr ""
#: project/views/utils.py:28 project/views/utils.py:36
msgid "You have received an invitation"
msgstr ""
#: project/views/utils.py:29
#, python-format
msgid "Newsletter from %(site_name)s"
msgstr ""
#: project/views/utils.py:30
msgid "Organization deletion requested"
msgstr ""
#: project/views/utils.py:33
msgid "Organization invitation accepted"
msgstr ""
#: project/views/utils.py:37
msgid "New reference automatically verified"
msgstr ""
#: project/views/utils.py:40
msgid "New reference request"
msgstr ""
#: project/views/utils.py:41 project/views/utils.py:46
msgid "Event review status updated"
msgstr ""
#: project/views/utils.py:44
msgid "Referenced event changed"
msgstr ""
#: project/views/utils.py:45
msgid "New event review"
msgstr ""
#: project/views/utils.py:47
msgid "User deletion requested" msgid "User deletion requested"
msgstr "" msgstr ""
#: project/views/utils.py:91 #: project/views/utils.py:48
#, python-format
msgid "Test mail from %(site_name)s"
msgstr ""
#: project/views/utils.py:49
msgid "New verification request"
msgstr ""
#: project/views/utils.py:50
msgid "Verification request review status updated"
msgstr ""
#: project/views/utils.py:122
msgid "" msgid ""
"An entry with the entered values already exists. Duplicate entries are " "An entry with the entered values already exists. Duplicate entries are "
"not allowed." "not allowed."
msgstr "" msgstr ""
#: project/views/utils.py:142 #: project/views/utils.py:173
#, python-format #, python-format
msgid "Error in the %s field - %s" msgid "Error in the %s field - %s"
msgstr "" msgstr ""
#: project/views/utils.py:149 #: project/views/utils.py:180
msgid "Show" msgid "Show"
msgstr "" msgstr ""
#: project/views/utils.py:157 #: project/views/utils.py:188
msgid "You do not have permission for this action" msgid "You do not have permission for this action"
msgstr "" msgstr ""
#: project/views/utils.py:306 #: project/views/utils.py:382
msgid "" msgid ""
"The invitation was issued to another user. Sign in with the email address" "The invitation was issued to another user. Sign in with the email address"
" the invitation was sent to." " the invitation was sent to."
@ -2924,37 +2947,25 @@ msgstr ""
msgid "Verification request successfully deleted" msgid "Verification request successfully deleted"
msgstr "" msgstr ""
#: project/views/verification_request.py:207 #: project/views/verification_request_review.py:30
msgid "New verification request"
msgstr ""
#: project/views/verification_request_review.py:34
msgid "Verification request already verified" msgid "Verification request already verified"
msgstr "" msgstr ""
#: project/views/verification_request_review.py:64 #: project/views/verification_request_review.py:60
msgid "Organization successfully verified" msgid "Organization successfully verified"
msgstr "" msgstr ""
#: project/views/verification_request_review.py:66 #: project/views/verification_request_review.py:62
msgid "Verification request successfully updated" msgid "Verification request successfully updated"
msgstr "" msgstr ""
#: project/views/verification_request_review.py:119 #: project/views/widget.py:111
msgid "Verification request review status updated"
msgstr ""
#: project/views/widget.py:114
msgid "Thank you so much! The event is being verified." msgid "Thank you so much! The event is being verified."
msgstr "" msgstr ""
#: project/views/widget.py:118 #: project/views/widget.py:115
msgid "" msgid ""
"For more options and your own calendar of events, you can register for " "For more options and your own calendar of events, you can register for "
"free." "free."
msgstr "" msgstr ""
#: project/views/widget.py:184
msgid "New event review"
msgstr ""

View File

@ -0,0 +1,26 @@
"""empty message
Revision ID: 182a83a49c1f
Revises: c20d6d7575be
Create Date: 2023-11-13 15:50:05.343979
"""
import sqlalchemy as sa
import sqlalchemy_utils
from alembic import op
from project import dbtypes
# revision identifiers, used by Alembic.
revision = "182a83a49c1f"
down_revision = "c20d6d7575be"
branch_labels = None
depends_on = None
def upgrade():
op.add_column("user", sa.Column("locale", sa.String(length=255), nullable=True))
def downgrade():
op.drop_column("user", "locale")

View File

@ -142,7 +142,7 @@ sitemap_path = os.path.join(cache_path, sitemap_file)
robots_txt_path = os.path.join(cache_path, robots_txt_file) robots_txt_path = os.path.join(cache_path, robots_txt_file)
# i18n # i18n
from project.i10n import get_locale from project.i10n import get_locale, get_locale_from_request
app.config["BABEL_DEFAULT_LOCALE"] = "de" app.config["BABEL_DEFAULT_LOCALE"] = "de"
app.config["BABEL_DEFAULT_TIMEZONE"] = "Europe/Berlin" app.config["BABEL_DEFAULT_TIMEZONE"] = "Europe/Berlin"
@ -238,6 +238,10 @@ def user_registered_sighandler(app, user, confirm_token, confirmation_token, for
set_user_accepted_tos(user) set_user_accepted_tos(user)
db.session.commit() db.session.commit()
if not user.locale:
user.locale = get_locale_from_request()
db.session.commit()
# OAuth2 # OAuth2
from project.oauth2 import config_oauth from project.oauth2 import config_oauth

View File

@ -1,6 +1,5 @@
from flask import abort, request from flask import abort, request
from flask_apispec import doc, marshal_with, use_kwargs from flask_apispec import doc, marshal_with, use_kwargs
from flask_babel import gettext
from sqlalchemy import and_ from sqlalchemy import and_
from project import db from project import db
@ -131,7 +130,7 @@ from project.views.reference_request import (
handle_request_according_to_relation, handle_request_according_to_relation,
send_reference_request_mails, send_reference_request_mails,
) )
from project.views.utils import get_current_admin_unit_for_api, send_mail_async from project.views.utils import get_current_admin_unit_for_api, send_template_mail_async
from project.views.verification_request import send_verification_request_inbox_mails from project.views.verification_request import send_verification_request_inbox_mails
@ -642,9 +641,8 @@ class OrganizationOrganizationInvitationListResource(BaseResource):
db.session.add(invitation) db.session.add(invitation)
db.session.commit() db.session.commit()
send_mail_async( send_template_mail_async(
invitation.email, invitation.email,
gettext("You have received an invitation"),
"organization_invitation_notice", "organization_invitation_notice",
invitation=invitation, invitation=invitation,
) )

View File

@ -1,9 +1,28 @@
from flask_babel import lazy_gettext from flask_babel import lazy_gettext
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import BooleanField, EmailField, SubmitField from wtforms import BooleanField, EmailField, SelectField, SubmitField
from wtforms.validators import DataRequired, Optional from wtforms.validators import DataRequired, Optional
class GeneralForm(FlaskForm):
locale = SelectField(
lazy_gettext("Language"),
choices=[
("None", lazy_gettext("Default")),
("de", "Deutsch"),
("en", "English"),
],
default="None",
validators=[Optional()],
)
submit = SubmitField(lazy_gettext("Save"))
def populate_obj(self, obj):
super().populate_obj(obj)
if obj.locale == "None":
obj.locale = None
class NotificationForm(FlaskForm): class NotificationForm(FlaskForm):
newsletter_enabled = BooleanField( newsletter_enabled = BooleanField(
lazy_gettext("Newsletter"), lazy_gettext("Newsletter"),

View File

@ -1,14 +1,35 @@
from flask import request from flask import request
from flask_babel import gettext from flask_babel import Locale, gettext
from flask_security import current_user
from project import app from project import app
def get_locale(): def get_locale():
if not request: try:
return app.config["LANGUAGES"][0] if (
current_user
and current_user.is_authenticated
and current_user.locale
and Locale.parse(current_user.locale)
):
return current_user.locale
except Exception:
pass
return request.accept_languages.best_match(app.config["LANGUAGES"]) if not request:
return app.config["BABEL_DEFAULT_LOCALE"]
return get_locale_from_request()
def get_locale_from_request():
if not request: # pragma: no cover
return None
return request.accept_languages.best_match(
app.config["LANGUAGES"], app.config["BABEL_DEFAULT_LOCALE"]
)
def print_dynamic_texts(): def print_dynamic_texts():

View File

@ -65,6 +65,7 @@ class User(db.Model, UserMixin):
) )
created_at = deferred(Column(DateTime, default=datetime.datetime.utcnow)) created_at = deferred(Column(DateTime, default=datetime.datetime.utcnow))
deletion_requested_at = deferred(Column(DateTime, nullable=True)) deletion_requested_at = deferred(Column(DateTime, nullable=True))
locale = Column(String(255), nullable=True)
@property @property
def is_member_of_verified_admin_unit(self): def is_member_of_verified_admin_unit(self):

View File

@ -32,6 +32,10 @@
<h2>{{ _('Settings') }}</h2> <h2>{{ _('Settings') }}</h2>
<div class="list-group"> <div class="list-group">
<a href="{{ url_for('user_general') }}" class="list-group-item">
{{ _('General') }}
<i class="fa fa-caret-right"></i>
</a>
<a href="{{ url_for('user_notifications') }}" class="list-group-item"> <a href="{{ url_for('user_notifications') }}" class="list-group-item">
{{ _('Notifications') }} {{ _('Notifications') }}
<i class="fa fa-caret-right"></i> <i class="fa fa-caret-right"></i>

View File

@ -0,0 +1,21 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_field_with_errors, render_field %}
{%- block title -%}
{{ _('General') }}
{%- endblock -%}
{% block content %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ url_for('profile') }}">{{ _('Profile') }}</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ _('General') }}</li>
</ol>
</nav>
<form action="" method="POST">
{{ form.hidden_tag() }}
{{ render_field_with_errors(form.locale) }}
{{ render_field(form.submit) }}
</form>
{% endblock %}

View File

@ -5,7 +5,12 @@
{%- endblock -%} {%- endblock -%}
{% block content %} {% block content %}
<h1>{{ _('Notifications') }}</h1> <nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ url_for('profile') }}">{{ _('Profile') }}</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ _('Notifications') }}</li>
</ol>
</nav>
<form action="" method="POST"> <form action="" method="POST">
{{ form.hidden_tag() }} {{ form.hidden_tag() }}

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-09-30 11:43+0200\n" "POT-Creation-Date: 2023-11-13 17:25+0100\n"
"PO-Revision-Date: 2020-06-07 18:51+0200\n" "PO-Revision-Date: 2020-06-07 18:51+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n" "Language: de\n"
@ -18,183 +18,183 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.12.1\n" "Generated-By: Babel 2.12.1\n"
#: project/i10n.py:15 #: project/i10n.py:32
msgid "Event_Art" msgid "Event_Art"
msgstr "Kunst" msgstr "Kunst"
#: project/i10n.py:16 #: project/i10n.py:33
msgid "Event_Book" msgid "Event_Book"
msgstr "Literatur" msgstr "Literatur"
#: project/i10n.py:17 #: project/i10n.py:34
msgid "Event_Movie" msgid "Event_Movie"
msgstr "Film" msgstr "Film"
#: project/i10n.py:18 #: project/i10n.py:35
msgid "Event_Family" msgid "Event_Family"
msgstr "Familie" msgstr "Familie"
#: project/i10n.py:19 #: project/i10n.py:36
msgid "Event_Festival" msgid "Event_Festival"
msgstr "Festival" msgstr "Festival"
#: project/i10n.py:20 #: project/i10n.py:37
msgid "Event_Religious" msgid "Event_Religious"
msgstr "Religion" msgstr "Religion"
#: project/i10n.py:21 #: project/i10n.py:38
msgid "Event_Shopping" msgid "Event_Shopping"
msgstr "Shopping" msgstr "Shopping"
#: project/i10n.py:22 #: project/i10n.py:39
msgid "Event_Comedy" msgid "Event_Comedy"
msgstr "Comedy" msgstr "Comedy"
#: project/i10n.py:23 #: project/i10n.py:40
msgid "Event_Music" msgid "Event_Music"
msgstr "Musik" msgstr "Musik"
#: project/i10n.py:24 #: project/i10n.py:41
msgid "Event_Dance" msgid "Event_Dance"
msgstr "Tanz" msgstr "Tanz"
#: project/i10n.py:25 #: project/i10n.py:42
msgid "Event_Nightlife" msgid "Event_Nightlife"
msgstr "Party" msgstr "Party"
#: project/i10n.py:26 #: project/i10n.py:43
msgid "Event_Theater" msgid "Event_Theater"
msgstr "Theater" msgstr "Theater"
#: project/i10n.py:27 #: project/i10n.py:44
msgid "Event_Dining" msgid "Event_Dining"
msgstr "Essen" msgstr "Essen"
#: project/i10n.py:28 #: project/i10n.py:45
msgid "Event_Conference" msgid "Event_Conference"
msgstr "Konferenz" msgstr "Konferenz"
#: project/i10n.py:29 #: project/i10n.py:46
msgid "Event_Meetup" msgid "Event_Meetup"
msgstr "Networking" msgstr "Networking"
#: project/i10n.py:30 #: project/i10n.py:47
msgid "Event_Fitness" msgid "Event_Fitness"
msgstr "Fitness" msgstr "Fitness"
#: project/i10n.py:31 #: project/i10n.py:48
msgid "Event_Sports" msgid "Event_Sports"
msgstr "Sport" msgstr "Sport"
#: project/i10n.py:32 #: project/i10n.py:49
msgid "Event_Other" msgid "Event_Other"
msgstr "Sonstiges" msgstr "Sonstiges"
#: project/i10n.py:33 #: project/i10n.py:50
msgid "Event_Exhibition" msgid "Event_Exhibition"
msgstr "Ausstellung" msgstr "Ausstellung"
#: project/i10n.py:34 #: project/i10n.py:51
msgid "Event_Culture" msgid "Event_Culture"
msgstr "Kultur" msgstr "Kultur"
#: project/i10n.py:35 #: project/i10n.py:52
msgid "Event_Tour" msgid "Event_Tour"
msgstr "Führung" msgstr "Führung"
#: project/i10n.py:36 #: project/i10n.py:53
msgid "Event_OpenAir" msgid "Event_OpenAir"
msgstr "Open Air" msgstr "Open Air"
#: project/i10n.py:37 #: project/i10n.py:54
msgid "Event_Stage" msgid "Event_Stage"
msgstr "Bühne" msgstr "Bühne"
#: project/i10n.py:38 #: project/i10n.py:55
msgid "Event_Lecture" msgid "Event_Lecture"
msgstr "Vortrag" msgstr "Vortrag"
#: project/i10n.py:39 #: project/i10n.py:56
msgid "Typical Age range" msgid "Typical Age range"
msgstr "Typische Altersspanne" msgstr "Typische Altersspanne"
#: project/i10n.py:40 #: project/i10n.py:57
msgid "Administrator" msgid "Administrator"
msgstr "Administrator:in" msgstr "Administrator:in"
#: project/i10n.py:41 #: project/i10n.py:58
msgid "Event expert" msgid "Event expert"
msgstr "Veranstaltungsexpert:in" msgstr "Veranstaltungsexpert:in"
#: project/i10n.py:42 #: project/i10n.py:59
msgid "EventReviewStatus.inbox" msgid "EventReviewStatus.inbox"
msgstr "Ungeprüft" msgstr "Ungeprüft"
#: project/i10n.py:43 #: project/i10n.py:60
msgid "EventReviewStatus.verified" msgid "EventReviewStatus.verified"
msgstr "Verifiziert" msgstr "Verifiziert"
#: project/i10n.py:44 #: project/i10n.py:61
msgid "EventReviewStatus.rejected" msgid "EventReviewStatus.rejected"
msgstr "Abgelehnt" msgstr "Abgelehnt"
#: project/i10n.py:45 #: project/i10n.py:62
msgid "Scope_openid" msgid "Scope_openid"
msgstr "Identität verifizieren" msgstr "Identität verifizieren"
#: project/i10n.py:46 #: project/i10n.py:63
msgid "Scope_profile" msgid "Scope_profile"
msgstr "Profil-Informationen" msgstr "Profil-Informationen"
#: project/i10n.py:47 #: project/i10n.py:64
msgid "Scope_user:read" msgid "Scope_user:read"
msgstr "Nutzer-Einstellungen lesen" msgstr "Nutzer-Einstellungen lesen"
#: project/i10n.py:48 #: project/i10n.py:65
msgid "Scope_user:write" msgid "Scope_user:write"
msgstr "Nutzer-Einstellungen anlegen, ändern und löschen" msgstr "Nutzer-Einstellungen anlegen, ändern und löschen"
#: project/i10n.py:49 #: project/i10n.py:66
msgid "Scope_organizer:write" msgid "Scope_organizer:write"
msgstr "Veranstalter anlegen, ändern und löschen" msgstr "Veranstalter anlegen, ändern und löschen"
#: project/i10n.py:50 #: project/i10n.py:67
msgid "Scope_place:write" msgid "Scope_place:write"
msgstr "Orte anlegen, ändern und löschen" msgstr "Orte anlegen, ändern und löschen"
#: project/i10n.py:51 #: project/i10n.py:68
msgid "Scope_event:write" msgid "Scope_event:write"
msgstr "Veranstaltungen anlegen, ändern und löschen" msgstr "Veranstaltungen anlegen, ändern und löschen"
#: project/i10n.py:52 #: project/i10n.py:69
msgid "Scope_eventlist:write" msgid "Scope_eventlist:write"
msgstr "Veranstaltungslisten anlegen, ändern und löschen" msgstr "Veranstaltungslisten anlegen, ändern und löschen"
#: project/i10n.py:53 #: project/i10n.py:70
msgid "Scope_eventreference:write" msgid "Scope_eventreference:write"
msgstr "Empfehlungen anlegen, ändern und löschen" msgstr "Empfehlungen anlegen, ändern und löschen"
#: project/i10n.py:54 #: project/i10n.py:71
msgid "Scope_organization:read" msgid "Scope_organization:read"
msgstr "Organisationen lesen" msgstr "Organisationen lesen"
#: project/i10n.py:55 #: project/i10n.py:72
msgid "Scope_organization:write" msgid "Scope_organization:write"
msgstr "Organisationen anlegen, ändern und löschen" msgstr "Organisationen anlegen, ändern und löschen"
#: project/i10n.py:56 #: project/i10n.py:73
msgid "Scope_customwidget:write" msgid "Scope_customwidget:write"
msgstr "Widgets anlegen, ändern und löschen" msgstr "Widgets anlegen, ändern und löschen"
#: project/i10n.py:57 #: project/i10n.py:74
msgid "There must be no self-reference." msgid "There must be no self-reference."
msgstr "Es darf keine Selbstreferenz geben." msgstr "Es darf keine Selbstreferenz geben."
#: project/utils.py:15 #: project/utils.py:19
msgid "Event_" msgid "Event_"
msgstr "Event_" msgstr "Event_"
#: project/utils.py:19 #: project/utils.py:23
msgid "." msgid "."
msgstr "." msgstr "."
@ -202,11 +202,6 @@ msgstr "."
msgid "message" msgid "message"
msgstr "message" msgstr "message"
#: project/api/organization/resources.py:647
#: project/views/admin_unit_member_invitation.py:89
msgid "You have received an invitation"
msgstr "Du hast eine Einladung erhalten"
#: project/forms/admin.py:11 project/templates/layout.html:344 #: project/forms/admin.py:11 project/templates/layout.html:344
#: project/views/root.py:55 #: project/views/root.py:55
msgid "Terms of service" msgid "Terms of service"
@ -238,7 +233,7 @@ msgid "Announcement"
msgstr "Ankündigung" msgstr "Ankündigung"
#: project/forms/admin.py:18 project/forms/oauth2_client.py:24 #: project/forms/admin.py:18 project/forms/oauth2_client.py:24
#: project/forms/user.py:13 #: project/forms/user.py:18 project/forms/user.py:32
msgid "Save" msgid "Save"
msgstr "Speichern" msgstr "Speichern"
@ -268,7 +263,7 @@ msgstr "Nutzer löschen"
#: project/forms/admin_unit_member.py:12 project/forms/admin_unit_member.py:25 #: project/forms/admin_unit_member.py:12 project/forms/admin_unit_member.py:25
#: project/forms/admin_unit_member.py:30 project/forms/event.py:112 #: project/forms/admin_unit_member.py:30 project/forms/event.py:112
#: project/forms/event_suggestion.py:38 project/forms/organizer.py:33 #: project/forms/event_suggestion.py:38 project/forms/organizer.py:33
#: project/forms/user.py:18 project/forms/user.py:23 #: project/forms/user.py:37 project/forms/user.py:42
#: project/templates/_macros.html:246 project/templates/_macros.html:1586 #: project/templates/_macros.html:246 project/templates/_macros.html:1586
#: project/templates/admin/admin.html:27 project/templates/admin/email.html:4 #: project/templates/admin/admin.html:27 project/templates/admin/email.html:4
#: project/templates/admin/email.html:66 project/templates/admin/users.html:19 #: project/templates/admin/email.html:66 project/templates/admin/users.html:19
@ -492,7 +487,9 @@ msgstr "Postleitzahlen für Verifizierungsanfragen"
#: project/forms/admin_unit.py:93 #: project/forms/admin_unit.py:93
msgid "Limit verification requests to organizations with these postal codes." msgid "Limit verification requests to organizations with these postal codes."
msgstr "Limitiere Verifizierungsanfragen auf Oranisationen mit diesen Postleitzahlen" msgstr ""
"Limitiere Verifizierungsanfragen auf Oranisationen mit diesen "
"Postleitzahlen"
#: project/forms/admin_unit.py:109 #: project/forms/admin_unit.py:109
msgid "Verify new organization" msgid "Verify new organization"
@ -543,11 +540,11 @@ msgstr "Hauptfarbe"
msgid "Link Color" msgid "Link Color"
msgstr "Linkfarbe" msgstr "Linkfarbe"
#: project/forms/admin_unit.py:165 project/forms/user.py:17 #: project/forms/admin_unit.py:165 project/forms/user.py:36
msgid "Request deletion" msgid "Request deletion"
msgstr "Löschung beantragen" msgstr "Löschung beantragen"
#: project/forms/admin_unit.py:170 project/forms/user.py:22 #: project/forms/admin_unit.py:170 project/forms/user.py:41
#: project/templates/admin_unit/cancel_deletion.html:6 #: project/templates/admin_unit/cancel_deletion.html:6
#: project/templates/admin_unit/update.html:44 #: project/templates/admin_unit/update.html:44
#: project/templates/manage/events.html:49 project/templates/profile.html:13 #: project/templates/manage/events.html:49 project/templates/profile.html:13
@ -1415,13 +1412,21 @@ msgstr "Ablehnen"
msgid "Confirm" msgid "Confirm"
msgstr "Bestätigen" msgstr "Bestätigen"
#: project/forms/user.py:9 project/templates/admin/admin.html:31 #: project/forms/user.py:9
msgid "Language"
msgstr "Sprache"
#: project/forms/user.py:11
msgid "Default"
msgstr "Standard"
#: project/forms/user.py:28 project/templates/admin/admin.html:31
#: project/templates/admin/newsletter.html:4 #: project/templates/admin/newsletter.html:4
#: project/templates/admin/newsletter.html:93 #: project/templates/admin/newsletter.html:93
msgid "Newsletter" msgid "Newsletter"
msgstr "Newsletter" msgstr "Newsletter"
#: project/forms/user.py:10 #: project/forms/user.py:29
msgid "Information about new features and improvements." msgid "Information about new features and improvements."
msgstr "Informationen über neue Features und Verbesserungen." msgstr "Informationen über neue Features und Verbesserungen."
@ -1911,7 +1916,7 @@ msgstr "Organisation wechseln"
#: project/templates/developer/read.html:4 #: project/templates/developer/read.html:4
#: project/templates/developer/read.html:8 project/templates/layout.html:361 #: project/templates/developer/read.html:8 project/templates/layout.html:361
#: project/templates/profile.html:45 #: project/templates/profile.html:49
msgid "Developer" msgid "Developer"
msgstr "Entwickler" msgstr "Entwickler"
@ -1925,20 +1930,25 @@ msgstr "Dein Account ist zur Löschung vorgesehen."
msgid "Delete account" msgid "Delete account"
msgstr "Account löschen" msgstr "Account löschen"
#: project/templates/profile.html:36 #: project/templates/profile.html:36 project/templates/user/general.html:4
#: project/templates/user/general.html:8
msgid "General"
msgstr "Allgemein"
#: project/templates/profile.html:40
#: project/templates/user/notifications.html:4 #: project/templates/user/notifications.html:4
#: project/templates/user/notifications.html:8 #: project/templates/user/notifications.html:8
msgid "Notifications" msgid "Notifications"
msgstr "Benachrichtigungen" msgstr "Benachrichtigungen"
#: project/templates/profile.html:40 #: project/templates/profile.html:44
msgid "Applications" msgid "Applications"
msgstr "Apps" msgstr "Apps"
#: project/templates/oauth2_client/list.html:4 #: project/templates/oauth2_client/list.html:4
#: project/templates/oauth2_client/list.html:11 #: project/templates/oauth2_client/list.html:11
#: project/templates/oauth2_client/read.html:11 #: project/templates/oauth2_client/read.html:11
#: project/templates/profile.html:49 #: project/templates/profile.html:53
msgid "OAuth2 clients" msgid "OAuth2 clients"
msgstr "OAuth2 Clients" msgstr "OAuth2 Clients"
@ -1973,7 +1983,7 @@ msgstr "Löschen"
msgid "User" msgid "User"
msgstr "Nutzer" msgstr "Nutzer"
#: project/templates/admin/email.html:47 project/views/admin.py:165 #: project/templates/admin/email.html:47 project/views/admin.py:163
msgid "Mail sent successfully" msgid "Mail sent successfully"
msgstr "Mail erfolgreich gesendet" msgstr "Mail erfolgreich gesendet"
@ -2369,7 +2379,7 @@ msgstr "Veranstaltungsvorschlag prüfen"
#: project/templates/manage/reference_requests_incoming.html:20 #: project/templates/manage/reference_requests_incoming.html:20
#: project/templates/reference/read.html:22 #: project/templates/reference/read.html:22
#: project/templates/reference_request/review_status.html:17 #: project/templates/reference_request/review_status.html:17
#: project/views/reference_request_review.py:42 #: project/views/reference_request_review.py:38
msgid "View event" msgid "View event"
msgstr "Veranstaltung anzeigen" msgstr "Veranstaltung anzeigen"
@ -2482,7 +2492,7 @@ msgid "Reviews"
msgstr "Prüfungen" msgstr "Prüfungen"
#: project/templates/manage/verification_requests_incoming.html:19 #: project/templates/manage/verification_requests_incoming.html:19
#: project/views/verification_request_review.py:36 #: project/views/verification_request_review.py:32
msgid "View organization" msgid "View organization"
msgstr "Organisation anzeigen" msgstr "Organisation anzeigen"
@ -2508,7 +2518,9 @@ msgstr ""
msgid "" msgid ""
"No organizations were found that can verify your organization with postal" "No organizations were found that can verify your organization with postal"
" code %(postal_code)s." " code %(postal_code)s."
msgstr "Es wurden keine Organisationen gefunden, die deine Organisation mit der Postleitzahl %(postal_code)s verifizieren können." msgstr ""
"Es wurden keine Organisationen gefunden, die deine Organisation mit der "
"Postleitzahl %(postal_code)s verifizieren können."
#: project/templates/manage/widgets.html:87 #: project/templates/manage/widgets.html:87
msgid "Link, um Veranstaltungen vorzuschlagen" msgid "Link, um Veranstaltungen vorzuschlagen"
@ -2613,7 +2625,7 @@ msgstr "Angemeldet bleiben"
msgid "You do not have an account yet? Not a problem!" msgid "You do not have an account yet? Not a problem!"
msgstr "Du hast noch keinen Account? Kein Problem!" msgstr "Du hast noch keinen Account? Kein Problem!"
#: project/templates/security/login_user.html:44 project/views/widget.py:122 #: project/templates/security/login_user.html:44 project/views/widget.py:119
msgid "Register for free" msgid "Register for free"
msgstr "Kostenlos registrieren" msgstr "Kostenlos registrieren"
@ -2692,29 +2704,19 @@ msgid "Organization successfully deleted"
msgstr "Organisation erfolgreich gelöscht" msgstr "Organisation erfolgreich gelöscht"
#: project/views/admin.py:133 project/views/manage.py:485 #: project/views/admin.py:133 project/views/manage.py:485
#: project/views/user.py:63 #: project/views/user.py:64 project/views/user.py:83
msgid "Settings successfully updated" msgid "Settings successfully updated"
msgstr "Einstellungen erfolgreich aktualisiert" msgstr "Einstellungen erfolgreich aktualisiert"
#: project/views/admin.py:153 #: project/views/admin.py:233
#, python-format
msgid "Test mail from %(site_name)s"
msgstr "Test-Mail von %(site_name)s"
#: project/views/admin.py:183
#, python-format
msgid "Newsletter from %(site_name)s"
msgstr "Newsletter von %(site_name)s"
#: project/views/admin.py:232
msgid "User successfully updated" msgid "User successfully updated"
msgstr "Nutzer erfolgreich aktualisiert" msgstr "Nutzer erfolgreich aktualisiert"
#: project/views/admin.py:252 #: project/views/admin.py:253
msgid "Entered email does not match user email" msgid "Entered email does not match user email"
msgstr "Die eingegebene Email passt nicht zur Email des Nutzers" msgstr "Die eingegebene Email passt nicht zur Email des Nutzers"
#: project/views/admin.py:256 #: project/views/admin.py:257
msgid "User successfully deleted" msgid "User successfully deleted"
msgstr "Nutzer erfolgreich gelöscht" msgstr "Nutzer erfolgreich gelöscht"
@ -2735,14 +2737,6 @@ msgstr "Organisation erfolgreich erstellt"
msgid "AdminUnit successfully updated" msgid "AdminUnit successfully updated"
msgstr "Organisation erfolgreich aktualisiert" msgstr "Organisation erfolgreich aktualisiert"
#: project/views/admin_unit.py:271
msgid "Organization invitation accepted"
msgstr "Organisationseinladung akzeptiert"
#: project/views/admin_unit.py:285
msgid "Organization deletion requested"
msgstr "Löschung der Organisation beantragt"
#: project/views/admin_unit_member.py:46 #: project/views/admin_unit_member.py:46
msgid "Member successfully updated" msgid "Member successfully updated"
msgstr "Mitglied erfolgreich aktualisiert" msgstr "Mitglied erfolgreich aktualisiert"
@ -2763,46 +2757,38 @@ msgstr "Einladung erfolgreich akzeptiert"
msgid "Invitation successfully declined" msgid "Invitation successfully declined"
msgstr "Einladung erfolgreich abgelehnt" msgstr "Einladung erfolgreich abgelehnt"
#: project/views/admin_unit_member_invitation.py:94 #: project/views/admin_unit_member_invitation.py:93
msgid "Invitation successfully sent" msgid "Invitation successfully sent"
msgstr "Einladung erfolgreich gesendet" msgstr "Einladung erfolgreich gesendet"
#: project/views/admin_unit_member_invitation.py:117 #: project/views/admin_unit_member_invitation.py:116
msgid "Entered email does not match invitation email" msgid "Entered email does not match invitation email"
msgstr "Die eingegebene Email passt nicht zur Email der Einladung" msgstr "Die eingegebene Email passt nicht zur Email der Einladung"
#: project/views/admin_unit_member_invitation.py:122 #: project/views/admin_unit_member_invitation.py:121
msgid "Invitation successfully deleted" msgid "Invitation successfully deleted"
msgstr "Einladung erfolgreich gelöscht" msgstr "Einladung erfolgreich gelöscht"
#: project/views/event.py:208 #: project/views/event.py:209
msgid "Event successfully published" msgid "Event successfully published"
msgstr "Veranstaltung erfolgreich veröffentlicht" msgstr "Veranstaltung erfolgreich veröffentlicht"
#: project/views/event.py:210 #: project/views/event.py:211
msgid "Draft successfully saved" msgid "Draft successfully saved"
msgstr "Entwurf erfolgreich gespeichert" msgstr "Entwurf erfolgreich gespeichert"
#: project/views/event.py:212 #: project/views/event.py:213
msgid "Event successfully planned" msgid "Event successfully planned"
msgstr "Veranstaltung erfolgreich geplant" msgstr "Veranstaltung erfolgreich geplant"
#: project/views/event.py:272 #: project/views/event.py:273
msgid "Event successfully updated" msgid "Event successfully updated"
msgstr "Veranstaltung erfolgreich aktualisiert" msgstr "Veranstaltung erfolgreich aktualisiert"
#: project/views/event.py:298 #: project/views/event.py:299
msgid "Event successfully deleted" msgid "Event successfully deleted"
msgstr "Veranstaltung erfolgreich gelöscht" msgstr "Veranstaltung erfolgreich gelöscht"
#: project/views/event.py:463
msgid "Referenced event changed"
msgstr "Empfohlene Veranstaltung wurde geändert"
#: project/views/event.py:486
msgid "New event report"
msgstr "Neue Meldung zu einer Veranstaltung"
#: project/views/event_place.py:34 #: project/views/event_place.py:34
msgid "Place successfully created" msgid "Place successfully created"
msgstr "Ort erfolgreich erstellt" msgstr "Ort erfolgreich erstellt"
@ -2823,11 +2809,6 @@ msgstr "Ort erfolgreich gelöscht"
msgid "Event suggestion successfully rejected" msgid "Event suggestion successfully rejected"
msgstr "Veranstaltungsvorschlag erfolgreich abgelehnt" msgstr "Veranstaltungsvorschlag erfolgreich abgelehnt"
#: project/views/event_suggestion.py:87
#: project/views/reference_request_review.py:139
msgid "Event review status updated"
msgstr "Prüfungsstatus aktualisiert"
#: project/views/js.py:33 #: project/views/js.py:33
msgid "Short name is already taken" msgid "Short name is already taken"
msgstr "Der Kurzname ist bereits vergeben" msgstr "Der Kurzname ist bereits vergeben"
@ -2933,28 +2914,20 @@ msgstr ""
"benachrichtigt, nachdem die andere Organisation die Veranstaltung geprüft" "benachrichtigt, nachdem die andere Organisation die Veranstaltung geprüft"
" hat." " hat."
#: project/views/reference_request.py:172 #: project/views/reference_request_review.py:36
msgid "New reference request"
msgstr "Neue Empfehlungsanfrage"
#: project/views/reference_request.py:181
msgid "New reference automatically verified"
msgstr "Neue automatisch verifizierte Empfehlung"
#: project/views/reference_request_review.py:40
msgid "Request already verified" msgid "Request already verified"
msgstr "Empfehlungsanfrage ist bereits verifiziert" msgstr "Empfehlungsanfrage ist bereits verifiziert"
#: project/views/reference_request_review.py:61 #: project/views/reference_request_review.py:57
msgid "Reference successfully created" msgid "Reference successfully created"
msgstr "Empfehlung erfolgreich erstellt" msgstr "Empfehlung erfolgreich erstellt"
#: project/views/reference_request_review.py:69 #: project/views/reference_request_review.py:65
msgid "Request successfully updated" msgid "Request successfully updated"
msgstr "Empfehlungsanfrage erfolgreich aktualisiert" msgstr "Empfehlungsanfrage erfolgreich aktualisiert"
#: project/views/reference_request_review.py:92 #: project/views/reference_request_review.py:88
#: project/views/verification_request_review.py:83 #: project/views/verification_request_review.py:79
#, python-format #, python-format
msgid "" msgid ""
"If all upcoming reference requests of %(admin_unit_name)s should be " "If all upcoming reference requests of %(admin_unit_name)s should be "
@ -2963,7 +2936,7 @@ msgstr ""
"Ob alle zukünftigen Empfehlungsanfragen von %(admin_unit_name)s " "Ob alle zukünftigen Empfehlungsanfragen von %(admin_unit_name)s "
"automatisch verifiziert werden sollen." "automatisch verifiziert werden sollen."
#: project/views/user.py:107 #: project/views/user.py:127
msgid "" msgid ""
"You are administrator of at least one organization. Cancel your " "You are administrator of at least one organization. Cancel your "
"membership to delete your account." "membership to delete your account."
@ -2971,15 +2944,69 @@ msgstr ""
"Du bist Administrator von mindestens einer Organisation. Beende deine " "Du bist Administrator von mindestens einer Organisation. Beende deine "
"Mitgliedschaft, um deinen Account zu löschen." "Mitgliedschaft, um deinen Account zu löschen."
#: project/views/user.py:114 project/views/user.py:141 #: project/views/user.py:134 project/views/user.py:161
msgid "Entered email does not match your email" msgid "Entered email does not match your email"
msgstr "Die eingegebene Email entspricht nicht deiner Email" msgstr "Die eingegebene Email entspricht nicht deiner Email"
#: project/views/user.py:160 #: project/views/utils.py:27
msgid "New event report"
msgstr "Neue Meldung zu einer Veranstaltung"
#: project/views/utils.py:28 project/views/utils.py:36
msgid "You have received an invitation"
msgstr "Du hast eine Einladung erhalten"
#: project/views/utils.py:29
#, python-format
msgid "Newsletter from %(site_name)s"
msgstr "Newsletter von %(site_name)s"
#: project/views/utils.py:30
msgid "Organization deletion requested"
msgstr "Löschung der Organisation beantragt"
#: project/views/utils.py:33
msgid "Organization invitation accepted"
msgstr "Organisationseinladung akzeptiert"
#: project/views/utils.py:37
msgid "New reference automatically verified"
msgstr "Neue automatisch verifizierte Empfehlung"
#: project/views/utils.py:40
msgid "New reference request"
msgstr "Neue Empfehlungsanfrage"
#: project/views/utils.py:41 project/views/utils.py:46
msgid "Event review status updated"
msgstr "Prüfungsstatus aktualisiert"
#: project/views/utils.py:44
msgid "Referenced event changed"
msgstr "Empfohlene Veranstaltung wurde geändert"
#: project/views/utils.py:45
msgid "New event review"
msgstr "Neue Veranstaltung zu prüfen"
#: project/views/utils.py:47
msgid "User deletion requested" msgid "User deletion requested"
msgstr "Löschung des Nutzers beantragt" msgstr "Löschung des Nutzers beantragt"
#: project/views/utils.py:91 #: project/views/utils.py:48
#, python-format
msgid "Test mail from %(site_name)s"
msgstr "Test-Mail von %(site_name)s"
#: project/views/utils.py:49
msgid "New verification request"
msgstr "Neue Verifizierungsanfrage"
#: project/views/utils.py:50
msgid "Verification request review status updated"
msgstr "Prüfungsstatus der Verifizierungsanfrage aktualisiert"
#: project/views/utils.py:122
msgid "" msgid ""
"An entry with the entered values already exists. Duplicate entries are " "An entry with the entered values already exists. Duplicate entries are "
"not allowed." "not allowed."
@ -2987,20 +3014,20 @@ msgstr ""
"Ein Eintrag mit den eingegebenen Werten existiert bereits. Doppelte " "Ein Eintrag mit den eingegebenen Werten existiert bereits. Doppelte "
"Einträge sind nicht erlaubt." "Einträge sind nicht erlaubt."
#: project/views/utils.py:142 #: project/views/utils.py:173
#, python-format #, python-format
msgid "Error in the %s field - %s" msgid "Error in the %s field - %s"
msgstr "Fehler im Feld %s: %s" msgstr "Fehler im Feld %s: %s"
#: project/views/utils.py:149 #: project/views/utils.py:180
msgid "Show" msgid "Show"
msgstr "Anzeigen" msgstr "Anzeigen"
#: project/views/utils.py:157 #: project/views/utils.py:188
msgid "You do not have permission for this action" msgid "You do not have permission for this action"
msgstr "Du hast keine Berechtigung für diese Aktion" msgstr "Du hast keine Berechtigung für diese Aktion"
#: project/views/utils.py:306 #: project/views/utils.py:382
msgid "" msgid ""
"The invitation was issued to another user. Sign in with the email address" "The invitation was issued to another user. Sign in with the email address"
" the invitation was sent to." " the invitation was sent to."
@ -3020,31 +3047,23 @@ msgstr ""
msgid "Verification request successfully deleted" msgid "Verification request successfully deleted"
msgstr "Verifizierungsanfrage erfolgreich gelöscht" msgstr "Verifizierungsanfrage erfolgreich gelöscht"
#: project/views/verification_request.py:207 #: project/views/verification_request_review.py:30
msgid "New verification request"
msgstr "Neue Verifizierungsanfrage"
#: project/views/verification_request_review.py:34
msgid "Verification request already verified" msgid "Verification request already verified"
msgstr "Verifizierungsanfrage ist bereits verifiziert" msgstr "Verifizierungsanfrage ist bereits verifiziert"
#: project/views/verification_request_review.py:64 #: project/views/verification_request_review.py:60
msgid "Organization successfully verified" msgid "Organization successfully verified"
msgstr "Organisation erfolgreich verifiziert" msgstr "Organisation erfolgreich verifiziert"
#: project/views/verification_request_review.py:66 #: project/views/verification_request_review.py:62
msgid "Verification request successfully updated" msgid "Verification request successfully updated"
msgstr "Verifizierungsanfrage erfolgreich aktualisiert" msgstr "Verifizierungsanfrage erfolgreich aktualisiert"
#: project/views/verification_request_review.py:119 #: project/views/widget.py:111
msgid "Verification request review status updated"
msgstr "Prüfungsstatus der Verifizierungsanfrage aktualisiert"
#: project/views/widget.py:114
msgid "Thank you so much! The event is being verified." msgid "Thank you so much! The event is being verified."
msgstr "Vielen Dank! Die Veranstaltung wird geprüft." msgstr "Vielen Dank! Die Veranstaltung wird geprüft."
#: project/views/widget.py:118 #: project/views/widget.py:115
msgid "" msgid ""
"For more options and your own calendar of events, you can register for " "For more options and your own calendar of events, you can register for "
"free." "free."
@ -3052,10 +3071,6 @@ msgstr ""
"Für mehr Optionen und einen eigenen Veranstaltungskalender, kannst du " "Für mehr Optionen und einen eigenen Veranstaltungskalender, kannst du "
"dich kostenlos registrieren." "dich kostenlos registrieren."
#: project/views/widget.py:184
msgid "New event review"
msgstr "Neue Veranstaltung zu prüfen"
#~ msgid "" #~ msgid ""
#~ "Indicate when the event will end. " #~ "Indicate when the event will end. "
#~ "An event can last a maximum of " #~ "An event can last a maximum of "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-09-30 11:43+0200\n" "POT-Creation-Date: 2023-11-13 17:25+0100\n"
"PO-Revision-Date: 2021-04-30 15:04+0200\n" "PO-Revision-Date: 2021-04-30 15:04+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n" "Language: en\n"
@ -18,183 +18,183 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.12.1\n" "Generated-By: Babel 2.12.1\n"
#: project/i10n.py:15 #: project/i10n.py:32
msgid "Event_Art" msgid "Event_Art"
msgstr "Art" msgstr "Art"
#: project/i10n.py:16 #: project/i10n.py:33
msgid "Event_Book" msgid "Event_Book"
msgstr "Book" msgstr "Book"
#: project/i10n.py:17 #: project/i10n.py:34
msgid "Event_Movie" msgid "Event_Movie"
msgstr "Movie" msgstr "Movie"
#: project/i10n.py:18 #: project/i10n.py:35
msgid "Event_Family" msgid "Event_Family"
msgstr "Family" msgstr "Family"
#: project/i10n.py:19 #: project/i10n.py:36
msgid "Event_Festival" msgid "Event_Festival"
msgstr "Festival" msgstr "Festival"
#: project/i10n.py:20 #: project/i10n.py:37
msgid "Event_Religious" msgid "Event_Religious"
msgstr "Religious" msgstr "Religious"
#: project/i10n.py:21 #: project/i10n.py:38
msgid "Event_Shopping" msgid "Event_Shopping"
msgstr "Shopping" msgstr "Shopping"
#: project/i10n.py:22 #: project/i10n.py:39
msgid "Event_Comedy" msgid "Event_Comedy"
msgstr "Comedy" msgstr "Comedy"
#: project/i10n.py:23 #: project/i10n.py:40
msgid "Event_Music" msgid "Event_Music"
msgstr "Music" msgstr "Music"
#: project/i10n.py:24 #: project/i10n.py:41
msgid "Event_Dance" msgid "Event_Dance"
msgstr "Dance" msgstr "Dance"
#: project/i10n.py:25 #: project/i10n.py:42
msgid "Event_Nightlife" msgid "Event_Nightlife"
msgstr "Nightlife" msgstr "Nightlife"
#: project/i10n.py:26 #: project/i10n.py:43
msgid "Event_Theater" msgid "Event_Theater"
msgstr "Theater" msgstr "Theater"
#: project/i10n.py:27 #: project/i10n.py:44
msgid "Event_Dining" msgid "Event_Dining"
msgstr "Dining" msgstr "Dining"
#: project/i10n.py:28 #: project/i10n.py:45
msgid "Event_Conference" msgid "Event_Conference"
msgstr "Conference" msgstr "Conference"
#: project/i10n.py:29 #: project/i10n.py:46
msgid "Event_Meetup" msgid "Event_Meetup"
msgstr "Meetup" msgstr "Meetup"
#: project/i10n.py:30 #: project/i10n.py:47
msgid "Event_Fitness" msgid "Event_Fitness"
msgstr "Fitness" msgstr "Fitness"
#: project/i10n.py:31 #: project/i10n.py:48
msgid "Event_Sports" msgid "Event_Sports"
msgstr "Sports" msgstr "Sports"
#: project/i10n.py:32 #: project/i10n.py:49
msgid "Event_Other" msgid "Event_Other"
msgstr "Other" msgstr "Other"
#: project/i10n.py:33 #: project/i10n.py:50
msgid "Event_Exhibition" msgid "Event_Exhibition"
msgstr "Exhibition" msgstr "Exhibition"
#: project/i10n.py:34 #: project/i10n.py:51
msgid "Event_Culture" msgid "Event_Culture"
msgstr "Culture" msgstr "Culture"
#: project/i10n.py:35 #: project/i10n.py:52
msgid "Event_Tour" msgid "Event_Tour"
msgstr "Tour" msgstr "Tour"
#: project/i10n.py:36 #: project/i10n.py:53
msgid "Event_OpenAir" msgid "Event_OpenAir"
msgstr "Open Air" msgstr "Open Air"
#: project/i10n.py:37 #: project/i10n.py:54
msgid "Event_Stage" msgid "Event_Stage"
msgstr "Stage" msgstr "Stage"
#: project/i10n.py:38 #: project/i10n.py:55
msgid "Event_Lecture" msgid "Event_Lecture"
msgstr "Lecture" msgstr "Lecture"
#: project/i10n.py:39 #: project/i10n.py:56
msgid "Typical Age range" msgid "Typical Age range"
msgstr "" msgstr ""
#: project/i10n.py:40 #: project/i10n.py:57
msgid "Administrator" msgid "Administrator"
msgstr "" msgstr ""
#: project/i10n.py:41 #: project/i10n.py:58
msgid "Event expert" msgid "Event expert"
msgstr "" msgstr ""
#: project/i10n.py:42 #: project/i10n.py:59
msgid "EventReviewStatus.inbox" msgid "EventReviewStatus.inbox"
msgstr "Inbox" msgstr "Inbox"
#: project/i10n.py:43 #: project/i10n.py:60
msgid "EventReviewStatus.verified" msgid "EventReviewStatus.verified"
msgstr "Verified" msgstr "Verified"
#: project/i10n.py:44 #: project/i10n.py:61
msgid "EventReviewStatus.rejected" msgid "EventReviewStatus.rejected"
msgstr "Rejected" msgstr "Rejected"
#: project/i10n.py:45 #: project/i10n.py:62
msgid "Scope_openid" msgid "Scope_openid"
msgstr "Verify identity" msgstr "Verify identity"
#: project/i10n.py:46 #: project/i10n.py:63
msgid "Scope_profile" msgid "Scope_profile"
msgstr "Profile information" msgstr "Profile information"
#: project/i10n.py:47 #: project/i10n.py:64
msgid "Scope_user:read" msgid "Scope_user:read"
msgstr "Read user settings" msgstr "Read user settings"
#: project/i10n.py:48 #: project/i10n.py:65
msgid "Scope_user:write" msgid "Scope_user:write"
msgstr "Create, update and delete user settings" msgstr "Create, update and delete user settings"
#: project/i10n.py:49 #: project/i10n.py:66
msgid "Scope_organizer:write" msgid "Scope_organizer:write"
msgstr "Create, update and delete organizers" msgstr "Create, update and delete organizers"
#: project/i10n.py:50 #: project/i10n.py:67
msgid "Scope_place:write" msgid "Scope_place:write"
msgstr "Create, update and delete places" msgstr "Create, update and delete places"
#: project/i10n.py:51 #: project/i10n.py:68
msgid "Scope_event:write" msgid "Scope_event:write"
msgstr "Create, update and delete events" msgstr "Create, update and delete events"
#: project/i10n.py:52 #: project/i10n.py:69
msgid "Scope_eventlist:write" msgid "Scope_eventlist:write"
msgstr "Create, update and delete event lists" msgstr "Create, update and delete event lists"
#: project/i10n.py:53 #: project/i10n.py:70
msgid "Scope_eventreference:write" msgid "Scope_eventreference:write"
msgstr "" msgstr ""
#: project/i10n.py:54 #: project/i10n.py:71
msgid "Scope_organization:read" msgid "Scope_organization:read"
msgstr "Read organizations" msgstr "Read organizations"
#: project/i10n.py:55 #: project/i10n.py:72
msgid "Scope_organization:write" msgid "Scope_organization:write"
msgstr "Create, update and delete organizations" msgstr "Create, update and delete organizations"
#: project/i10n.py:56 #: project/i10n.py:73
msgid "Scope_customwidget:write" msgid "Scope_customwidget:write"
msgstr "" msgstr ""
#: project/i10n.py:57 #: project/i10n.py:74
msgid "There must be no self-reference." msgid "There must be no self-reference."
msgstr "" msgstr ""
#: project/utils.py:15 #: project/utils.py:19
msgid "Event_" msgid "Event_"
msgstr "" msgstr ""
#: project/utils.py:19 #: project/utils.py:23
msgid "." msgid "."
msgstr "" msgstr ""
@ -202,11 +202,6 @@ msgstr ""
msgid "message" msgid "message"
msgstr "" msgstr ""
#: project/api/organization/resources.py:647
#: project/views/admin_unit_member_invitation.py:89
msgid "You have received an invitation"
msgstr ""
#: project/forms/admin.py:11 project/templates/layout.html:344 #: project/forms/admin.py:11 project/templates/layout.html:344
#: project/views/root.py:55 #: project/views/root.py:55
msgid "Terms of service" msgid "Terms of service"
@ -238,7 +233,7 @@ msgid "Announcement"
msgstr "" msgstr ""
#: project/forms/admin.py:18 project/forms/oauth2_client.py:24 #: project/forms/admin.py:18 project/forms/oauth2_client.py:24
#: project/forms/user.py:13 #: project/forms/user.py:18 project/forms/user.py:32
msgid "Save" msgid "Save"
msgstr "" msgstr ""
@ -268,7 +263,7 @@ msgstr ""
#: project/forms/admin_unit_member.py:12 project/forms/admin_unit_member.py:25 #: project/forms/admin_unit_member.py:12 project/forms/admin_unit_member.py:25
#: project/forms/admin_unit_member.py:30 project/forms/event.py:112 #: project/forms/admin_unit_member.py:30 project/forms/event.py:112
#: project/forms/event_suggestion.py:38 project/forms/organizer.py:33 #: project/forms/event_suggestion.py:38 project/forms/organizer.py:33
#: project/forms/user.py:18 project/forms/user.py:23 #: project/forms/user.py:37 project/forms/user.py:42
#: project/templates/_macros.html:246 project/templates/_macros.html:1586 #: project/templates/_macros.html:246 project/templates/_macros.html:1586
#: project/templates/admin/admin.html:27 project/templates/admin/email.html:4 #: project/templates/admin/admin.html:27 project/templates/admin/email.html:4
#: project/templates/admin/email.html:66 project/templates/admin/users.html:19 #: project/templates/admin/email.html:66 project/templates/admin/users.html:19
@ -476,7 +471,7 @@ msgid "Verification requests postal codes"
msgstr "" msgstr ""
#: project/forms/admin_unit.py:93 #: project/forms/admin_unit.py:93
msgid "Accept verification requests to organizations with these postal codes." msgid "Limit verification requests to organizations with these postal codes."
msgstr "" msgstr ""
#: project/forms/admin_unit.py:109 #: project/forms/admin_unit.py:109
@ -524,11 +519,11 @@ msgstr ""
msgid "Link Color" msgid "Link Color"
msgstr "" msgstr ""
#: project/forms/admin_unit.py:165 project/forms/user.py:17 #: project/forms/admin_unit.py:165 project/forms/user.py:36
msgid "Request deletion" msgid "Request deletion"
msgstr "" msgstr ""
#: project/forms/admin_unit.py:170 project/forms/user.py:22 #: project/forms/admin_unit.py:170 project/forms/user.py:41
#: project/templates/admin_unit/cancel_deletion.html:6 #: project/templates/admin_unit/cancel_deletion.html:6
#: project/templates/admin_unit/update.html:44 #: project/templates/admin_unit/update.html:44
#: project/templates/manage/events.html:49 project/templates/profile.html:13 #: project/templates/manage/events.html:49 project/templates/profile.html:13
@ -1364,13 +1359,21 @@ msgstr ""
msgid "Confirm" msgid "Confirm"
msgstr "" msgstr ""
#: project/forms/user.py:9 project/templates/admin/admin.html:31 #: project/forms/user.py:9
msgid "Language"
msgstr ""
#: project/forms/user.py:11
msgid "Default"
msgstr ""
#: project/forms/user.py:28 project/templates/admin/admin.html:31
#: project/templates/admin/newsletter.html:4 #: project/templates/admin/newsletter.html:4
#: project/templates/admin/newsletter.html:93 #: project/templates/admin/newsletter.html:93
msgid "Newsletter" msgid "Newsletter"
msgstr "" msgstr ""
#: project/forms/user.py:10 #: project/forms/user.py:29
msgid "Information about new features and improvements." msgid "Information about new features and improvements."
msgstr "" msgstr ""
@ -1858,7 +1861,7 @@ msgstr ""
#: project/templates/developer/read.html:4 #: project/templates/developer/read.html:4
#: project/templates/developer/read.html:8 project/templates/layout.html:361 #: project/templates/developer/read.html:8 project/templates/layout.html:361
#: project/templates/profile.html:45 #: project/templates/profile.html:49
msgid "Developer" msgid "Developer"
msgstr "" msgstr ""
@ -1872,20 +1875,25 @@ msgstr ""
msgid "Delete account" msgid "Delete account"
msgstr "" msgstr ""
#: project/templates/profile.html:36 #: project/templates/profile.html:36 project/templates/user/general.html:4
#: project/templates/user/general.html:8
msgid "General"
msgstr ""
#: project/templates/profile.html:40
#: project/templates/user/notifications.html:4 #: project/templates/user/notifications.html:4
#: project/templates/user/notifications.html:8 #: project/templates/user/notifications.html:8
msgid "Notifications" msgid "Notifications"
msgstr "" msgstr ""
#: project/templates/profile.html:40 #: project/templates/profile.html:44
msgid "Applications" msgid "Applications"
msgstr "" msgstr ""
#: project/templates/oauth2_client/list.html:4 #: project/templates/oauth2_client/list.html:4
#: project/templates/oauth2_client/list.html:11 #: project/templates/oauth2_client/list.html:11
#: project/templates/oauth2_client/read.html:11 #: project/templates/oauth2_client/read.html:11
#: project/templates/profile.html:49 #: project/templates/profile.html:53
msgid "OAuth2 clients" msgid "OAuth2 clients"
msgstr "" msgstr ""
@ -1920,7 +1928,7 @@ msgstr ""
msgid "User" msgid "User"
msgstr "" msgstr ""
#: project/templates/admin/email.html:47 project/views/admin.py:165 #: project/templates/admin/email.html:47 project/views/admin.py:163
msgid "Mail sent successfully" msgid "Mail sent successfully"
msgstr "" msgstr ""
@ -2305,7 +2313,7 @@ msgstr ""
#: project/templates/manage/reference_requests_incoming.html:20 #: project/templates/manage/reference_requests_incoming.html:20
#: project/templates/reference/read.html:22 #: project/templates/reference/read.html:22
#: project/templates/reference_request/review_status.html:17 #: project/templates/reference_request/review_status.html:17
#: project/views/reference_request_review.py:42 #: project/views/reference_request_review.py:38
msgid "View event" msgid "View event"
msgstr "" msgstr ""
@ -2416,7 +2424,7 @@ msgid "Reviews"
msgstr "" msgstr ""
#: project/templates/manage/verification_requests_incoming.html:19 #: project/templates/manage/verification_requests_incoming.html:19
#: project/views/verification_request_review.py:36 #: project/views/verification_request_review.py:32
msgid "View organization" msgid "View organization"
msgstr "" msgstr ""
@ -2545,7 +2553,7 @@ msgstr ""
msgid "You do not have an account yet? Not a problem!" msgid "You do not have an account yet? Not a problem!"
msgstr "" msgstr ""
#: project/templates/security/login_user.html:44 project/views/widget.py:122 #: project/templates/security/login_user.html:44 project/views/widget.py:119
msgid "Register for free" msgid "Register for free"
msgstr "" msgstr ""
@ -2622,29 +2630,19 @@ msgid "Organization successfully deleted"
msgstr "" msgstr ""
#: project/views/admin.py:133 project/views/manage.py:485 #: project/views/admin.py:133 project/views/manage.py:485
#: project/views/user.py:63 #: project/views/user.py:64 project/views/user.py:83
msgid "Settings successfully updated" msgid "Settings successfully updated"
msgstr "" msgstr ""
#: project/views/admin.py:153 #: project/views/admin.py:233
#, python-format
msgid "Test mail from %(site_name)s"
msgstr ""
#: project/views/admin.py:183
#, python-format
msgid "Newsletter from %(site_name)s"
msgstr ""
#: project/views/admin.py:232
msgid "User successfully updated" msgid "User successfully updated"
msgstr "" msgstr ""
#: project/views/admin.py:252 #: project/views/admin.py:253
msgid "Entered email does not match user email" msgid "Entered email does not match user email"
msgstr "" msgstr ""
#: project/views/admin.py:256 #: project/views/admin.py:257
msgid "User successfully deleted" msgid "User successfully deleted"
msgstr "" msgstr ""
@ -2662,14 +2660,6 @@ msgstr ""
msgid "AdminUnit successfully updated" msgid "AdminUnit successfully updated"
msgstr "" msgstr ""
#: project/views/admin_unit.py:271
msgid "Organization invitation accepted"
msgstr ""
#: project/views/admin_unit.py:285
msgid "Organization deletion requested"
msgstr ""
#: project/views/admin_unit_member.py:46 #: project/views/admin_unit_member.py:46
msgid "Member successfully updated" msgid "Member successfully updated"
msgstr "" msgstr ""
@ -2690,46 +2680,38 @@ msgstr ""
msgid "Invitation successfully declined" msgid "Invitation successfully declined"
msgstr "" msgstr ""
#: project/views/admin_unit_member_invitation.py:94 #: project/views/admin_unit_member_invitation.py:93
msgid "Invitation successfully sent" msgid "Invitation successfully sent"
msgstr "" msgstr ""
#: project/views/admin_unit_member_invitation.py:117 #: project/views/admin_unit_member_invitation.py:116
msgid "Entered email does not match invitation email" msgid "Entered email does not match invitation email"
msgstr "" msgstr ""
#: project/views/admin_unit_member_invitation.py:122 #: project/views/admin_unit_member_invitation.py:121
msgid "Invitation successfully deleted" msgid "Invitation successfully deleted"
msgstr "" msgstr ""
#: project/views/event.py:208 #: project/views/event.py:209
msgid "Event successfully published" msgid "Event successfully published"
msgstr "" msgstr ""
#: project/views/event.py:210 #: project/views/event.py:211
msgid "Draft successfully saved" msgid "Draft successfully saved"
msgstr "" msgstr ""
#: project/views/event.py:212 #: project/views/event.py:213
msgid "Event successfully planned" msgid "Event successfully planned"
msgstr "" msgstr ""
#: project/views/event.py:272 #: project/views/event.py:273
msgid "Event successfully updated" msgid "Event successfully updated"
msgstr "" msgstr ""
#: project/views/event.py:298 #: project/views/event.py:299
msgid "Event successfully deleted" msgid "Event successfully deleted"
msgstr "" msgstr ""
#: project/views/event.py:463
msgid "Referenced event changed"
msgstr ""
#: project/views/event.py:486
msgid "New event report"
msgstr ""
#: project/views/event_place.py:34 #: project/views/event_place.py:34
msgid "Place successfully created" msgid "Place successfully created"
msgstr "" msgstr ""
@ -2750,11 +2732,6 @@ msgstr ""
msgid "Event suggestion successfully rejected" msgid "Event suggestion successfully rejected"
msgstr "" msgstr ""
#: project/views/event_suggestion.py:87
#: project/views/reference_request_review.py:139
msgid "Event review status updated"
msgstr ""
#: project/views/js.py:33 #: project/views/js.py:33
msgid "Short name is already taken" msgid "Short name is already taken"
msgstr "" msgstr ""
@ -2855,68 +2832,114 @@ msgid ""
"notified after the other organization reviews the event." "notified after the other organization reviews the event."
msgstr "" msgstr ""
#: project/views/reference_request.py:172 #: project/views/reference_request_review.py:36
msgid "New reference request"
msgstr ""
#: project/views/reference_request.py:181
msgid "New reference automatically verified"
msgstr ""
#: project/views/reference_request_review.py:40
msgid "Request already verified" msgid "Request already verified"
msgstr "" msgstr ""
#: project/views/reference_request_review.py:61 #: project/views/reference_request_review.py:57
msgid "Reference successfully created" msgid "Reference successfully created"
msgstr "" msgstr ""
#: project/views/reference_request_review.py:69 #: project/views/reference_request_review.py:65
msgid "Request successfully updated" msgid "Request successfully updated"
msgstr "" msgstr ""
#: project/views/reference_request_review.py:92 #: project/views/reference_request_review.py:88
#: project/views/verification_request_review.py:83 #: project/views/verification_request_review.py:79
#, python-format #, python-format
msgid "" msgid ""
"If all upcoming reference requests of %(admin_unit_name)s should be " "If all upcoming reference requests of %(admin_unit_name)s should be "
"verified automatically." "verified automatically."
msgstr "" msgstr ""
#: project/views/user.py:107 #: project/views/user.py:127
msgid "" msgid ""
"You are administrator of at least one organization. Cancel your " "You are administrator of at least one organization. Cancel your "
"membership to delete your account." "membership to delete your account."
msgstr "" msgstr ""
#: project/views/user.py:114 project/views/user.py:141 #: project/views/user.py:134 project/views/user.py:161
msgid "Entered email does not match your email" msgid "Entered email does not match your email"
msgstr "" msgstr ""
#: project/views/user.py:160 #: project/views/utils.py:27
msgid "New event report"
msgstr ""
#: project/views/utils.py:28 project/views/utils.py:36
msgid "You have received an invitation"
msgstr ""
#: project/views/utils.py:29
#, python-format
msgid "Newsletter from %(site_name)s"
msgstr ""
#: project/views/utils.py:30
msgid "Organization deletion requested"
msgstr ""
#: project/views/utils.py:33
msgid "Organization invitation accepted"
msgstr ""
#: project/views/utils.py:37
msgid "New reference automatically verified"
msgstr ""
#: project/views/utils.py:40
msgid "New reference request"
msgstr ""
#: project/views/utils.py:41 project/views/utils.py:46
msgid "Event review status updated"
msgstr ""
#: project/views/utils.py:44
msgid "Referenced event changed"
msgstr ""
#: project/views/utils.py:45
msgid "New event review"
msgstr ""
#: project/views/utils.py:47
msgid "User deletion requested" msgid "User deletion requested"
msgstr "" msgstr ""
#: project/views/utils.py:91 #: project/views/utils.py:48
#, python-format
msgid "Test mail from %(site_name)s"
msgstr ""
#: project/views/utils.py:49
msgid "New verification request"
msgstr ""
#: project/views/utils.py:50
msgid "Verification request review status updated"
msgstr ""
#: project/views/utils.py:122
msgid "" msgid ""
"An entry with the entered values already exists. Duplicate entries are " "An entry with the entered values already exists. Duplicate entries are "
"not allowed." "not allowed."
msgstr "" msgstr ""
#: project/views/utils.py:142 #: project/views/utils.py:173
#, python-format #, python-format
msgid "Error in the %s field - %s" msgid "Error in the %s field - %s"
msgstr "" msgstr ""
#: project/views/utils.py:149 #: project/views/utils.py:180
msgid "Show" msgid "Show"
msgstr "" msgstr ""
#: project/views/utils.py:157 #: project/views/utils.py:188
msgid "You do not have permission for this action" msgid "You do not have permission for this action"
msgstr "" msgstr ""
#: project/views/utils.py:306 #: project/views/utils.py:382
msgid "" msgid ""
"The invitation was issued to another user. Sign in with the email address" "The invitation was issued to another user. Sign in with the email address"
" the invitation was sent to." " the invitation was sent to."
@ -2932,40 +2955,28 @@ msgstr ""
msgid "Verification request successfully deleted" msgid "Verification request successfully deleted"
msgstr "" msgstr ""
#: project/views/verification_request.py:207 #: project/views/verification_request_review.py:30
msgid "New verification request"
msgstr ""
#: project/views/verification_request_review.py:34
msgid "Verification request already verified" msgid "Verification request already verified"
msgstr "" msgstr ""
#: project/views/verification_request_review.py:64 #: project/views/verification_request_review.py:60
msgid "Organization successfully verified" msgid "Organization successfully verified"
msgstr "" msgstr ""
#: project/views/verification_request_review.py:66 #: project/views/verification_request_review.py:62
msgid "Verification request successfully updated" msgid "Verification request successfully updated"
msgstr "" msgstr ""
#: project/views/verification_request_review.py:119 #: project/views/widget.py:111
msgid "Verification request review status updated"
msgstr ""
#: project/views/widget.py:114
msgid "Thank you so much! The event is being verified." msgid "Thank you so much! The event is being verified."
msgstr "" msgstr ""
#: project/views/widget.py:118 #: project/views/widget.py:115
msgid "" msgid ""
"For more options and your own calendar of events, you can register for " "For more options and your own calendar of events, you can register for "
"free." "free."
msgstr "" msgstr ""
#: project/views/widget.py:184
msgid "New event review"
msgstr ""
#~ msgid "" #~ msgid ""
#~ "Indicate when the event will end. " #~ "Indicate when the event will end. "
#~ "An event can last a maximum of " #~ "An event can last a maximum of "
@ -3052,3 +3063,6 @@ msgstr ""
#~ msgid "Switch to place search" #~ msgid "Switch to place search"
#~ msgstr "" #~ msgstr ""
#~ msgid "Accept verification requests to organizations with these postal codes."
#~ msgstr ""

View File

@ -7,6 +7,10 @@ from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.base import NO_CHANGE, object_state from sqlalchemy.orm.base import NO_CHANGE, object_state
def dummy_gettext(message: str):
return message
def strings_are_equal_ignoring_case(string1: str, string2: str): def strings_are_equal_ignoring_case(string1: str, string2: str):
return string1 and string2 and string1.casefold() == string2.casefold() return string1 and string2 and string1.casefold() == string2.casefold()

View File

@ -25,9 +25,9 @@ from project.views.utils import (
get_pagination_urls, get_pagination_urls,
handleSqlError, handleSqlError,
non_match_for_deletion, non_match_for_deletion,
send_mail, send_template_mail,
send_mail_async, send_template_mail_async,
send_mails_async, send_template_mails_to_users_async,
) )
@ -150,18 +150,16 @@ def admin_email():
return get_celery_poll_group_result() return get_celery_poll_group_result()
if form.validate_on_submit(): if form.validate_on_submit():
subject = gettext( template = "test_email"
"Test mail from %(site_name)s", context = {"site_name": app.config["SITE_NAME"]}
site_name=app.config["SITE_NAME"],
)
if "async" in request.args: # pragma: no cover if "async" in request.args: # pragma: no cover
result = send_mail_async(form.recipient.data, subject, "test_email") result = send_template_mail_async(form.recipient.data, template, **context)
result.save() result.save()
return {"result_id": result.id} return {"result_id": result.id}
try: try:
send_mail(form.recipient.data, subject, "test_email") send_template_mail(form.recipient.data, template, **context)
flash(gettext("Mail sent successfully"), "success") flash(gettext("Mail sent successfully"), "success")
except Exception as e: # pragma: no cover except Exception as e: # pragma: no cover
flash(str(e), "danger") flash(str(e), "danger")
@ -180,13 +178,15 @@ def admin_newsletter():
return get_celery_poll_group_result() return get_celery_poll_group_result()
if form.validate_on_submit(): if form.validate_on_submit():
subject = gettext( template = "newsletter"
"Newsletter from %(site_name)s", context = {"site_name": app.config["SITE_NAME"], "message": form.message.data}
site_name=app.config["SITE_NAME"],
)
if form.recipient_choice.data == 1: # pragma: no cover if form.recipient_choice.data == 1: # pragma: no cover
recipients = [form.test_recipient.data] result = send_template_mail_async(
form.test_recipient.data,
template,
**context,
)
else: else:
users = ( users = (
User.query.filter(User.email != None) User.query.filter(User.email != None)
@ -194,11 +194,12 @@ def admin_newsletter():
.filter(User.newsletter_enabled) .filter(User.newsletter_enabled)
.all() .all()
) )
recipients = [u.email for u in users] result = send_template_mails_to_users_async(
users,
result = send_mails_async( template,
recipients, subject, "newsletter", message=form.message.data **context,
) )
result.save() result.save()
return {"result_id": result.id} return {"result_id": result.id}

View File

@ -9,7 +9,6 @@ from project import app, db
from project.access import ( from project.access import (
can_create_admin_unit, can_create_admin_unit,
get_admin_unit_for_manage_or_404, get_admin_unit_for_manage_or_404,
get_admin_unit_members_with_permission,
has_access, has_access,
) )
from project.forms.admin_unit import ( from project.forms.admin_unit import (
@ -32,7 +31,7 @@ from project.views.utils import (
manage_required, manage_required,
non_match_for_deletion, non_match_for_deletion,
permission_missing, permission_missing,
send_mails_async, send_template_mails_to_admin_unit_members_async,
) )
@ -261,14 +260,9 @@ def send_admin_unit_invitation_accepted_mails(
invitation: AdminUnitInvitation, relation: AdminUnitRelation, admin_unit: AdminUnit invitation: AdminUnitInvitation, relation: AdminUnitRelation, admin_unit: AdminUnit
): ):
# Benachrichtige alle Mitglieder der AdminUnit, die diese Einladung erstellt hatte # Benachrichtige alle Mitglieder der AdminUnit, die diese Einladung erstellt hatte
members = get_admin_unit_members_with_permission( send_template_mails_to_admin_unit_members_async(
invitation.admin_unit_id, "admin_unit:update" invitation.admin_unit_id,
) "admin_unit:update",
emails = list(map(lambda member: member.user.email, members))
send_mails_async(
emails,
gettext("Organization invitation accepted"),
"organization_invitation_accepted_notice", "organization_invitation_accepted_notice",
invitation=invitation, invitation=invitation,
relation=relation, relation=relation,
@ -277,12 +271,9 @@ def send_admin_unit_invitation_accepted_mails(
def send_admin_unit_deletion_requested_mails(admin_unit: AdminUnit): def send_admin_unit_deletion_requested_mails(admin_unit: AdminUnit):
members = get_admin_unit_members_with_permission(admin_unit.id, "admin_unit:update") send_template_mails_to_admin_unit_members_async(
emails = list(map(lambda member: member.user.email, members)) admin_unit.id,
"admin_unit:update",
send_mails_async(
emails,
gettext("Organization deletion requested"),
"organization_deletion_requested_notice", "organization_deletion_requested_notice",
admin_unit=admin_unit, admin_unit=admin_unit,
) )

View File

@ -18,7 +18,7 @@ from project.views.utils import (
handleSqlError, handleSqlError,
non_match_for_deletion, non_match_for_deletion,
permission_missing, permission_missing,
send_mail_async, send_template_mail_async,
) )
@ -84,9 +84,8 @@ def manage_admin_unit_member_invite(id):
db.session.add(invitation) db.session.add(invitation)
db.session.commit() db.session.commit()
send_mail_async( send_template_mail_async(
invitation.email, invitation.email,
gettext("You have received an invitation"),
"invitation_notice", "invitation_notice",
invitation=invitation, invitation=invitation,
) )

View File

@ -55,7 +55,8 @@ from project.views.utils import (
get_calendar_links_for_event, get_calendar_links_for_event,
get_share_links, get_share_links,
handleSqlError, handleSqlError,
send_mails_async, send_template_mails_to_admin_unit_members_async,
send_template_mails_to_users_async,
set_current_admin_unit, set_current_admin_unit,
) )
@ -453,14 +454,9 @@ def send_referenced_event_changed_mails(event):
references = EventReference.query.filter(EventReference.event_id == event.id).all() references = EventReference.query.filter(EventReference.event_id == event.id).all()
for reference in references: for reference in references:
# Alle Mitglieder der AdminUnit, die das Recht haben, Requests zu verifizieren # Alle Mitglieder der AdminUnit, die das Recht haben, Requests zu verifizieren
members = get_admin_unit_members_with_permission( send_template_mails_to_admin_unit_members_async(
reference.admin_unit_id, "reference_request:verify" reference.admin_unit_id,
) "reference_request:verify",
emails = list(map(lambda member: member.user.email, members))
send_mails_async(
emails,
gettext("Referenced event changed"),
"referenced_event_changed_notice", "referenced_event_changed_notice",
event=event, event=event,
reference=reference, reference=reference,
@ -474,16 +470,16 @@ def send_event_report_mails(event: Event, report: dict):
members = get_admin_unit_members_with_permission( members = get_admin_unit_members_with_permission(
event.admin_unit_id, "event:update" event.admin_unit_id, "event:update"
) )
emails = list(map(lambda member: member.user.email, members)) users = [member.user for member in members]
# Alle globalen Admins # Alle globalen Admins
admins = find_all_users_with_role("admin") admins = find_all_users_with_role("admin")
admin_emails = list(map(lambda admin: admin.email, admins)) users.extend(
emails.extend(x for x in admin_emails if x not in emails) admin for admin in admins if all(user.id != admin.id for user in users)
)
send_mails_async( send_template_mails_to_users_async(
emails, users,
gettext("New event report"),
"event_report_notice", "event_report_notice",
event=event, event=event,
report=report, report=report,

View File

@ -7,7 +7,7 @@ from project import app, db
from project.access import access_or_401 from project.access import access_or_401
from project.forms.event_suggestion import RejectEventSuggestionForm from project.forms.event_suggestion import RejectEventSuggestionForm
from project.models import EventReviewStatus, EventSuggestion from project.models import EventReviewStatus, EventSuggestion
from project.views.utils import flash_errors, handleSqlError, send_mail_async from project.views.utils import flash_errors, handleSqlError, send_template_mail_async
@app.route("/event_suggestion/<int:event_suggestion_id>/review") @app.route("/event_suggestion/<int:event_suggestion_id>/review")
@ -82,9 +82,8 @@ def event_suggestion_review_status(event_suggestion_id):
def send_event_suggestion_review_status_mail(event_suggestion): def send_event_suggestion_review_status_mail(event_suggestion):
if event_suggestion.contact_email and event_suggestion.contact_email_notice: if event_suggestion.contact_email and event_suggestion.contact_email_notice:
send_mail_async( send_template_mail_async(
event_suggestion.contact_email, event_suggestion.contact_email,
gettext("Event review status updated"),
"review_status_notice", "review_status_notice",
event_suggestion=event_suggestion, event_suggestion=event_suggestion,
) )

View File

@ -4,11 +4,7 @@ from flask_security import auth_required
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from project import app, db from project import app, db
from project.access import ( from project.access import can_request_event_reference, get_admin_unit_for_manage_or_404
can_request_event_reference,
get_admin_unit_for_manage_or_404,
get_admin_unit_members_with_permission,
)
from project.forms.reference_request import CreateEventReferenceRequestForm from project.forms.reference_request import CreateEventReferenceRequestForm
from project.models import ( from project.models import (
Event, Event,
@ -31,7 +27,7 @@ from project.views.utils import (
flash_errors, flash_errors,
get_pagination_urls, get_pagination_urls,
handleSqlError, handleSqlError,
send_mails_async, send_template_mails_to_admin_unit_members_async,
) )
@ -169,7 +165,6 @@ def send_reference_request_mails(
def _send_reference_request_inbox_mails(request): def _send_reference_request_inbox_mails(request):
_send_member_reference_request_verify_mails( _send_member_reference_request_verify_mails(
request.admin_unit_id, request.admin_unit_id,
gettext("New reference request"),
"reference_request_notice", "reference_request_notice",
request=request, request=request,
) )
@ -178,19 +173,13 @@ def _send_reference_request_inbox_mails(request):
def _send_auto_reference_mails(reference): def _send_auto_reference_mails(reference):
_send_member_reference_request_verify_mails( _send_member_reference_request_verify_mails(
reference.admin_unit_id, reference.admin_unit_id,
gettext("New reference automatically verified"),
"reference_auto_verified_notice", "reference_auto_verified_notice",
reference=reference, reference=reference,
) )
def _send_member_reference_request_verify_mails( def _send_member_reference_request_verify_mails(admin_unit_id, template, **context):
admin_unit_id, subject, template, **context
):
# Benachrichtige alle Mitglieder der AdminUnit, die Requests verifizieren können # Benachrichtige alle Mitglieder der AdminUnit, die Requests verifizieren können
members = get_admin_unit_members_with_permission( send_template_mails_to_admin_unit_members_async(
admin_unit_id, "reference_request:verify" admin_unit_id, "reference_request:verify", template, **context
) )
emails = list(map(lambda member: member.user.email, members))
send_mails_async(emails, subject, template, **context)

View File

@ -4,11 +4,7 @@ from flask_security import auth_required
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from project import app, db from project import app, db
from project.access import ( from project.access import access_or_401, has_access
access_or_401,
get_admin_unit_members_with_permission,
has_access,
)
from project.dateutils import get_today from project.dateutils import get_today
from project.forms.reference_request import ReferenceRequestReviewForm from project.forms.reference_request import ReferenceRequestReviewForm
from project.models import ( from project.models import (
@ -25,7 +21,7 @@ from project.views.utils import (
flash_errors, flash_errors,
flash_message, flash_message,
handleSqlError, handleSqlError,
send_mails_async, send_template_mails_to_admin_unit_members_async,
) )
@ -129,14 +125,9 @@ def event_reference_request_review_status(id):
def send_reference_request_review_status_mails(request): def send_reference_request_review_status_mails(request):
# Benachrichtige alle Mitglieder der AdminUnit, die diesen Request erstellt hatte # Benachrichtige alle Mitglieder der AdminUnit, die diesen Request erstellt hatte
members = get_admin_unit_members_with_permission( send_template_mails_to_admin_unit_members_async(
request.event.admin_unit_id, "reference_request:create" request.event.admin_unit_id,
) "reference_request:create",
emails = list(map(lambda member: member.user.email, members))
send_mails_async(
emails,
gettext("Event review status updated"),
"reference_request_review_status_notice", "reference_request_review_status_notice",
request=request, request=request,
) )

View File

@ -10,6 +10,7 @@ from project import app, db
from project.forms.security import AcceptTosForm from project.forms.security import AcceptTosForm
from project.forms.user import ( from project.forms.user import (
CancelUserDeletionForm, CancelUserDeletionForm,
GeneralForm,
NotificationForm, NotificationForm,
RequestUserDeletionForm, RequestUserDeletionForm,
) )
@ -20,7 +21,7 @@ from project.views.utils import (
get_invitation_access_result, get_invitation_access_result,
handleSqlError, handleSqlError,
non_match_for_deletion, non_match_for_deletion,
send_mail_async, send_template_mails_to_users_async,
) )
@ -50,6 +51,25 @@ def user_accept_tos():
return render_template("user/accept_tos.html", form=form) return render_template("user/accept_tos.html", form=form)
@app.route("/user/general", methods=("GET", "POST"))
@auth_required()
def user_general():
user = User.query.get_or_404(current_user.id)
form = GeneralForm(obj=user)
if form.validate_on_submit():
try:
form.populate_obj(user)
db.session.commit()
flash(gettext("Settings successfully updated"), "success")
return redirect(url_for("profile"))
except SQLAlchemyError as e: # pragma: no cover
db.session.rollback()
flash(handleSqlError(e), "danger")
return render_template("user/general.html", form=form)
@app.route("/user/notifications", methods=("GET", "POST")) @app.route("/user/notifications", methods=("GET", "POST"))
@auth_required() @auth_required()
def user_notifications(): def user_notifications():
@ -155,9 +175,8 @@ def user_cancel_deletion():
def send_user_deletion_requested_mail(user): def send_user_deletion_requested_mail(user):
send_mail_async( send_template_mails_to_users_async(
user.email, [user],
gettext("User deletion requested"),
"user_deletion_requested_notice", "user_deletion_requested_notice",
user=user, user=user,
) )

View File

@ -1,8 +1,9 @@
from functools import wraps from functools import wraps
from itertools import groupby
from urllib.parse import quote_plus from urllib.parse import quote_plus
from flask import Markup, flash, g, redirect, render_template, request, url_for from flask import Markup, flash, g, redirect, render_template, request, url_for
from flask_babel import gettext from flask_babel import force_locale, gettext
from flask_login.utils import decode_cookie from flask_login.utils import decode_cookie
from flask_mail import Message from flask_mail import Message
from flask_security import current_user from flask_security import current_user
@ -14,12 +15,42 @@ from project import app, celery, mail
from project.access import ( from project.access import (
get_admin_unit_for_manage, get_admin_unit_for_manage,
get_admin_unit_for_manage_or_404, get_admin_unit_for_manage_or_404,
get_admin_unit_members_with_permission,
get_admin_units_for_manage, get_admin_units_for_manage,
has_access, has_access,
) )
from project.dateutils import berlin_tz, round_to_next_day from project.dateutils import berlin_tz, round_to_next_day
from project.models import Event, EventAttendanceMode, EventDate from project.models import Event, EventAttendanceMode, EventDate
from project.utils import get_place_str, strings_are_equal_ignoring_case from project.utils import dummy_gettext, get_place_str, strings_are_equal_ignoring_case
mail_template_subject_mapping = {
"event_report_notice": dummy_gettext("New event report"),
"invitation_notice": dummy_gettext("You have received an invitation"),
"newsletter": dummy_gettext("Newsletter from %(site_name)s"),
"organization_deletion_requested_notice": dummy_gettext(
"Organization deletion requested"
),
"organization_invitation_accepted_notice": dummy_gettext(
"Organization invitation accepted"
),
"organization_invitation_notice": dummy_gettext("You have received an invitation"),
"reference_auto_verified_notice": dummy_gettext(
"New reference automatically verified"
),
"reference_request_notice": dummy_gettext("New reference request"),
"reference_request_review_status_notice": dummy_gettext(
"Event review status updated"
),
"referenced_event_changed_notice": dummy_gettext("Referenced event changed"),
"review_notice": dummy_gettext("New event review"),
"review_status_notice": dummy_gettext("Event review status updated"),
"user_deletion_requested_notice": dummy_gettext("User deletion requested"),
"test_email": dummy_gettext("Test mail from %(site_name)s"),
"verification_request_notice": dummy_gettext("New verification request"),
"verification_request_review_status_notice": dummy_gettext(
"Verification request review status updated"
),
}
def set_current_admin_unit(admin_unit): def set_current_admin_unit(admin_unit):
@ -160,37 +191,86 @@ def permission_missing(redirect_location, message=None):
return redirect(redirect_location) return redirect(redirect_location)
def send_mail(recipient, subject, template, **context): def send_template_mail(recipient, template, **context):
send_mails([recipient], subject, template, **context) send_template_mails([recipient], template, **context)
def send_mails(recipients, subject, template, **context): def send_template_mails(recipients, template, **context):
if len(recipients) == 0: # pragma: no cover if len(recipients) == 0: # pragma: no cover
return return
body, html = render_mail_body(template, **context) subject, body, html = render_mail_body_with_subject(template, **context)
send_mails_with_body(recipients, subject, body, html) send_mails_with_body(recipients, subject, body, html)
def send_mail_async(recipient, subject, template, **context): def send_template_mail_async(recipient, template, **context):
return send_mails_async([recipient], subject, template, **context) return send_template_mails_async([recipient], template, **context)
def send_mails_async(recipients, subject, template, **context): def send_template_mails_async(recipients, template, **context):
if len(recipients) == 0: # pragma: no cover if len(recipients) == 0: # pragma: no cover
return return
body, html = render_mail_body(template, **context) subject, body, html = render_mail_body_with_subject(template, **context)
return send_mails_with_body_async(recipients, subject, body, html) return send_mails_with_body_async(recipients, subject, body, html)
def render_mail_body_with_subject(template, **context):
subject_key = mail_template_subject_mapping.get(template)
locale = context.get("locale", None) or app.config["BABEL_DEFAULT_LOCALE"]
with force_locale(locale):
subject = gettext(subject_key, **context)
body, html = render_mail_body(template, **context)
return subject, body, html
def send_template_mails_to_admin_unit_members_async(
admin_unit_id, permissions, template, **context
):
members = get_admin_unit_members_with_permission(admin_unit_id, permissions)
users = [member.user for member in members]
return send_template_mails_to_users_async(users, template, **context)
def send_template_mails_to_users_async(users, template, **context):
if len(users) == 0: # pragma: no cover
return
# Group by locale
def locale_func(user):
return user.locale if user.locale else ""
sorted_users = sorted(users, key=locale_func)
grouped_users = groupby(sorted_users, locale_func)
signatures = list()
for locale, locale_users in grouped_users:
context["locale"] = locale
subject, body, html = render_mail_body_with_subject(template, **context)
signatures.extend([(user.email, subject, body, html) for user in locale_users])
return send_mails_with_signatures_async(signatures)
def send_mails_with_body_async(recipients, subject, body, html): def send_mails_with_body_async(recipients, subject, body, html):
signatures = [(recipient, subject, body, html) for recipient in recipients]
return send_mails_with_signatures_async(signatures)
def send_mails_with_signatures_async(signatures):
from celery import group from celery import group
from project.base_tasks import send_mail_with_body_task from project.base_tasks import send_mail_with_body_task
if len(signatures) == 0: # pragma: no cover
return
result = group( result = group(
send_mail_with_body_task.s(r, subject, body, html) for r in recipients send_mail_with_body_task.s(*signature) for signature in signatures
).delay() ).delay()
return result return result

View File

@ -4,7 +4,7 @@ from flask_security import auth_required
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from project import app, db from project import app, db
from project.access import access_or_401, get_admin_unit_members_with_permission from project.access import access_or_401
from project.forms.verification_request import ( from project.forms.verification_request import (
CreateAdminUnitVerificationRequestForm, CreateAdminUnitVerificationRequestForm,
DeleteVerificationRequestForm, DeleteVerificationRequestForm,
@ -30,7 +30,7 @@ from project.views.utils import (
handleSqlError, handleSqlError,
manage_required, manage_required,
non_match_for_deletion, non_match_for_deletion,
send_mails_async, send_template_mails_to_admin_unit_members_async,
) )
@ -189,22 +189,12 @@ def admin_unit_verification_request_delete(id):
) )
def send_member_verification_request_verify_mails(
admin_unit_id, subject, template, **context
):
# Benachrichtige alle Mitglieder der AdminUnit, die Requests verifizieren können
members = get_admin_unit_members_with_permission(
admin_unit_id, "verification_request:verify"
)
emails = list(map(lambda member: member.user.email, members))
send_mails_async(emails, subject, template, **context)
def send_verification_request_inbox_mails(request): def send_verification_request_inbox_mails(request):
send_member_verification_request_verify_mails( # Benachrichtige alle Mitglieder der AdminUnit, die Requests verifizieren können
request.target_admin_unit_id or request.target_admin_unit.id, admin_unit_id = request.target_admin_unit_id or request.target_admin_unit.id
gettext("New verification request"), send_template_mails_to_admin_unit_members_async(
admin_unit_id,
"verification_request:verify",
"verification_request_notice", "verification_request_notice",
request=request, request=request,
) )

View File

@ -4,11 +4,7 @@ from flask_security import auth_required
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from project import app, db from project import app, db
from project.access import ( from project.access import access_or_401, has_access
access_or_401,
get_admin_unit_members_with_permission,
has_access,
)
from project.forms.verification_request import VerificationRequestReviewForm from project.forms.verification_request import VerificationRequestReviewForm
from project.models import ( from project.models import (
AdminUnitVerificationRequest, AdminUnitVerificationRequest,
@ -19,7 +15,7 @@ from project.views.utils import (
flash_errors, flash_errors,
flash_message, flash_message,
handleSqlError, handleSqlError,
send_mails_async, send_template_mails_to_admin_unit_members_async,
) )
@ -109,14 +105,9 @@ def admin_unit_verification_request_review_status(id):
def send_verification_request_review_status_mails(request): def send_verification_request_review_status_mails(request):
# Benachrichtige alle Mitglieder der AdminUnit, die diesen Request erstellt hatte # Benachrichtige alle Mitglieder der AdminUnit, die diesen Request erstellt hatte
members = get_admin_unit_members_with_permission( send_template_mails_to_admin_unit_members_async(
request.source_admin_unit_id, "verification_request:create" request.source_admin_unit_id,
) "verification_request:create",
emails = list(map(lambda member: member.user.email, members))
send_mails_async(
emails,
gettext("Verification request review status updated"),
"verification_request_review_status_notice", "verification_request_review_status_notice",
request=request, request=request,
) )

View File

@ -5,10 +5,7 @@ from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.sql import func from sqlalchemy.sql import func
from project import app, db from project import app, db
from project.access import ( from project.access import admin_unit_suggestions_enabled_or_404
admin_unit_suggestions_enabled_or_404,
get_admin_unit_members_with_permission,
)
from project.dateutils import get_next_full_hour from project.dateutils import get_next_full_hour
from project.forms.event_date import FindEventDateWidgetForm from project.forms.event_date import FindEventDateWidgetForm
from project.forms.event_suggestion import CreateEventSuggestionForm from project.forms.event_suggestion import CreateEventSuggestionForm
@ -23,7 +20,7 @@ from project.views.utils import (
flash_message, flash_message,
get_pagination_urls, get_pagination_urls,
handleSqlError, handleSqlError,
send_mails_async, send_template_mails_to_admin_unit_members_async,
) )
@ -176,12 +173,9 @@ def get_styles(admin_unit):
def send_event_inbox_mails(admin_unit, event_suggestion): def send_event_inbox_mails(admin_unit, event_suggestion):
members = get_admin_unit_members_with_permission(admin_unit.id, "event:verify") send_template_mails_to_admin_unit_members_async(
emails = list(map(lambda member: member.user.email, members)) admin_unit.id,
"event:verify",
send_mails_async(
emails,
gettext("New event review"),
"review_notice", "review_notice",
event_suggestion=event_suggestion, event_suggestion=event_suggestion,
) )

View File

@ -38,6 +38,7 @@ class Seeder(object):
admin=False, admin=False,
confirm=True, confirm=True,
tos_accepted=True, tos_accepted=True,
locale=None,
): ):
from flask_security.confirmable import confirm_user from flask_security.confirmable import confirm_user
@ -63,6 +64,7 @@ class Seeder(object):
if admin: if admin:
add_admin_roles_to_user(email) add_admin_roles_to_user(email)
user.locale = locale
self._db.session.commit() self._db.session.commit()
user_id = user.id user_id = user.id

View File

@ -1,4 +1,4 @@
def test_send_mails(client, app, utils): def test_dynamic_texts(client, app, utils):
from project.i10n import print_dynamic_texts from project.i10n import print_dynamic_texts
print_dynamic_texts() print_dynamic_texts()

View File

@ -206,13 +206,20 @@ class UtilActions(object):
return mocker.patch("project.views.utils.send_mails_with_body") return mocker.patch("project.views.utils.send_mails_with_body")
def mock_send_mails_async(self, mocker): def mock_send_mails_async(self, mocker):
return mocker.patch("project.views.utils.send_mails_with_body_async") return mocker.patch("project.views.utils.send_mails_with_signatures_async")
def assert_send_mail_called( def assert_send_mail_called(
self, mock, expected_recipients, expected_contents=None self, mock, expected_recipients, expected_contents=None
): ):
mock.assert_called_once() mock.assert_called_once()
args, kwargs = mock.call_args args, kwargs = mock.call_args
if mock._extract_mock_name() == "send_mails_with_signatures_async":
signatures = args[0]
send_recipients = [s[0] for s in signatures]
first_signature = signatures[0]
_, subject, body, html = first_signature
else:
send_recipients, subject, body, html = args send_recipients, subject, body, html = args
if not isinstance(expected_recipients, list): if not isinstance(expected_recipients, list):

View File

@ -86,7 +86,8 @@ def test_newsletter(app, utils, seeder):
user_id, admin_unit_id = seeder.setup_base(True) user_id, admin_unit_id = seeder.setup_base(True)
for i in range(10): for i in range(10):
seeder.create_user(f"test{i}@test.de") locale = "de" if (i % 3) == 0 else "en" if (i % 3) == 1 else None
seeder.create_user(f"test{i}@test.de", locale=locale)
url = utils.get_url("admin_newsletter") url = utils.get_url("admin_newsletter")
response = utils.get_ok(url) response = utils.get_ok(url)

View File

@ -69,6 +69,35 @@ def test_user_favorite_events(client, seeder, utils):
utils.get_ok(url) utils.get_ok(url)
@pytest.mark.parametrize("locale", [None, "de"])
def test_user_general(client, seeder, utils, app, db, locale):
user_id, admin_unit_id = seeder.setup_base()
url = utils.get_url("user_general")
response = utils.get_ok(url)
if locale is None:
values = dict()
else:
values = {
"locale": locale,
}
response = utils.post_form(
url,
response,
values,
)
utils.assert_response_redirect(response, "profile")
with app.app_context():
from project.models import User
user = db.session.get(User, user_id)
assert user.locale == locale
def test_user_notifications(client, seeder, utils, app, db): def test_user_notifications(client, seeder, utils, app, db):
user_id, admin_unit_id = seeder.setup_base() user_id, admin_unit_id = seeder.setup_base()
@ -88,8 +117,8 @@ def test_user_notifications(client, seeder, utils, app, db):
with app.app_context(): with app.app_context():
from project.models import User from project.models import User
place = db.session.get(User, user_id) user = db.session.get(User, user_id)
assert not place.newsletter_enabled assert not user.newsletter_enabled
def test_login_flash(client, seeder, utils): def test_login_flash(client, seeder, utils):

View File

@ -1,6 +1,6 @@
def test_send_mails(client, seeder, app, db, utils): def test_send_template_mails(client, seeder, app, db, utils):
from project.models import AdminUnitMemberInvitation from project.models import AdminUnitMemberInvitation
from project.views.utils import send_mail from project.views.utils import send_template_mail
user_id, admin_unit_id = seeder.setup_base() user_id, admin_unit_id = seeder.setup_base()
email = "new@member.de" email = "new@member.de"
@ -12,9 +12,8 @@ def test_send_mails(client, seeder, app, db, utils):
mail.default_sender = None mail.default_sender = None
invitation = db.session.get(AdminUnitMemberInvitation, invitation_id) invitation = db.session.get(AdminUnitMemberInvitation, invitation_id)
send_mail( send_template_mail(
email, email,
"You have received an invitation",
"invitation_notice", "invitation_notice",
invitation=invitation, invitation=invitation,
) )