diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82e9f3431bd890f9664ccf40b6a0bbd3cc8fc84a..56dbde4f800670345d3c6d452c96c09300d14d8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ## [Unreleased]
 ### Changed
 - Apply updated pretix plugin cookiecutter
+- Django 4 compatibility #14
 
 ## [1.5.0] - 2022-10-09
 ### Added
diff --git a/pretix_matrix_inviter/forms.py b/pretix_matrix_inviter/forms.py
index 79daae09ea7903540fc64fb7fd7f2b8f274f38bb..37a139d016b8fa86608be397fda4104526a6b04e 100644
--- a/pretix_matrix_inviter/forms.py
+++ b/pretix_matrix_inviter/forms.py
@@ -4,7 +4,7 @@ from django.forms import (
     MultipleChoiceField,
     RegexField,
 )
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy
 from i18nfield.forms import I18nFormField, I18nTextInput
 from pretix.base.forms import SettingsForm
 
@@ -14,42 +14,46 @@ from .helpers import matrix_room_info_for_event
 class MatrixInviterForm(SettingsForm):
     matrix_inviter_items = MultipleChoiceField(
         widget=CheckboxSelectMultiple(attrs={"class": "scrolling-multiple-choice"}),
-        label=_("Ask Matrix ID for"),
+        label=gettext_lazy("Ask Matrix ID for"),
         required=True,
         choices=[],
-        help_text=_("These products will ask for a Matrix ID."),
+        help_text=gettext_lazy("These products will ask for a Matrix ID."),
     )
     matrix_inviter_authorization_token = CharField(
-        label=_("Access token"),
+        label=gettext_lazy("Access token"),
         strip=True,
-        help_text=_(
+        help_text=gettext_lazy(
             "This should be the access token of a user that can invite attendees to the target Room or Space. "
             "Please note that other administrators of this event will be able to see this token, it should not be from "
             "your own Matrix account but from a dedicated Matrix account."
         ),
     )
     matrix_inviter_matrix_server = CharField(
-        label=_("Matrix server"),
+        label=gettext_lazy("Matrix server"),
         strip=True,
-        help_text=_("The matrix server the above access token is valid for."),
+        help_text=gettext_lazy(
+            "The matrix server the above access token is valid for."
+        ),
     )
     matrix_inviter_hint = I18nFormField(
         widget=I18nTextInput,
-        label=_("Matrix ID field help text"),
+        label=gettext_lazy("Matrix ID field help text"),
         required=True,
-        help_text=_(
+        help_text=gettext_lazy(
             "This will be shown as help text on the Matrix ID field. It is recommended to inform your attendees "
             "which room they will be invited to and what that room will be used for."
         ),
     )
     matrix_inviter_reason = I18nFormField(
         widget=I18nTextInput,
-        label=_("Invitation message"),
+        label=gettext_lazy("Invitation message"),
         required=False,
-        help_text=_("This message will be added to the invitation to the Matrix room."),
+        help_text=gettext_lazy(
+            "This message will be added to the invitation to the Matrix room."
+        ),
     )
     matrix_inviter_matrix_room = RegexField(
-        label=_("Matrix room"),
+        label=gettext_lazy("Matrix room"),
         regex="(?:!|#)[^:]+:[^:,]+(?:\\s*,\\s*(?:!|#)[^:]+:[^:,]+)*",
         strip=True,
     )
@@ -62,7 +66,7 @@ class MatrixInviterForm(SettingsForm):
 
         room_info = matrix_room_info_for_event(self.obj)
         if not room_info:
-            room_help_text = _(
+            room_help_text = gettext_lazy(
                 "Comma-separated list of room IDs or aliases to invite users to."
             )
         else:
@@ -71,22 +75,24 @@ class MatrixInviterForm(SettingsForm):
                 if room["room_id"].startswith("!"):
                     if room["canonical_alias"]:
                         room_help.append(
-                            _(
+                            gettext_lazy(
                                 '"{name}" (main address: <code>{canonical_alias}</code>)'
                             ).format_map(room)
                         )
                     else:
-                        room_help.append(_('"{name}"').format_map(room))
+                        room_help.append(gettext_lazy('"{name}"').format_map(room))
                 else:
                     if room["canonical_alias"]:
                         room_help.append(
-                            _(
+                            gettext_lazy(
                                 '"{name}" (<code>{room_id}</code>, main address: <code>{canonical_alias}</code>)'
                             ).format_map(room)
                         )
                     else:
                         room_help.append(
-                            _('"{name}" (<code>{room_id}</code>)').format_map(room)
+                            gettext_lazy(
+                                '"{name}" (<code>{room_id}</code>)'
+                            ).format_map(room)
                         )
             room_help_text = ", ".join(room_help)
         self.fields["matrix_inviter_matrix_room"].help_text = room_help_text
diff --git a/pretix_matrix_inviter/signals.py b/pretix_matrix_inviter/signals.py
index a91bd8f0d4e90a2ecced49478b120b08ac3d3825..9cc033a1d1558e510527063d9aafa74241388962 100644
--- a/pretix_matrix_inviter/signals.py
+++ b/pretix_matrix_inviter/signals.py
@@ -2,7 +2,7 @@ import json
 from django import forms
 from django.dispatch import receiver
 from django.urls import resolve, reverse
-from django.utils.translation import gettext_noop, ugettext_lazy as _
+from django.utils.translation import gettext_lazy, gettext_noop
 from i18nfield.strings import LazyI18nString
 from pretix.base.settings import settings_hierarkey
 from pretix.base.signals import (
@@ -51,12 +51,14 @@ def add_matrix_id_question(sender, position, **kwargs):
 
     return {
         "matrix_inviter_matrix_id": forms.RegexField(
-            label=_("Matrix ID"),
+            label=gettext_lazy("Matrix ID"),
             required=False,
             regex="@[a-z0-9._=/-]+:[a-z0-9.-]+",
             strip=True,
             error_messages={
-                "invalid": _("Enter a Matrix ID of the form @username:homeserver.tld")
+                "invalid": gettext_lazy(
+                    "Enter a Matrix ID of the form @username:homeserver.tld"
+                )
             },
             help_text=rich_text_snippet(sender.settings.matrix_inviter_hint),
         )
@@ -132,7 +134,7 @@ def navbar_settings(sender, request=None, **kwargs):
     url = resolve(request.path_info)
     return [
         {
-            "label": _("Matrix inviter"),
+            "label": gettext_lazy("Matrix inviter"),
             "url": reverse(
                 "plugins:pretix_matrix_inviter:settings",
                 kwargs={
@@ -152,16 +154,16 @@ def logentry_display(sender, logentry, **kwargs):
         return
 
     locales = {
-        "pretix_matrix_inviter.invite_sent": _(
+        "pretix_matrix_inviter.invite_sent": gettext_lazy(
             "{matrix_id} has been invited to {matrix_room}."
         ),
-        "pretix_matrix_inviter.invite_rescinded": _(
+        "pretix_matrix_inviter.invite_rescinded": gettext_lazy(
             "{matrix_id} has been removed from {matrix_room}."
         ),
-        "pretix_matrix_inviter.error": _(
+        "pretix_matrix_inviter.error": gettext_lazy(
             "There was an error inviting {matrix_id} to {matrix_room}: {error}"
         ),
-        "pretix_matrix_inviter.remove_error": _(
+        "pretix_matrix_inviter.remove_error": gettext_lazy(
             "There was an error removing {matrix_id} from {matrix_room}: {error}"
         ),
     }