diff --git a/pdm.lock b/pdm.lock
index 58ead28a30d4e7f10561ecaa48d2a74c33ad95a2..d74b5f1625891e6aeb84ad493db5a3a54ce25a8b 100644
--- a/pdm.lock
+++ b/pdm.lock
@@ -2,10 +2,10 @@
 # It is not intended for manual editing.
 
 [metadata]
-groups = ["default", "dev", "lint", "local"]
+groups = ["default", "dev", "lint", "local", "typing"]
 strategy = ["inherit_metadata"]
 lock_version = "4.5.0"
-content_hash = "sha256:a7320608dc0e3c00f197dee7ada9261e62bd35dd235e56b9532d22126d225946"
+content_hash = "sha256:1a9697a08aa14dbec10f593156b8222ce9b548a907c96e9d60c8874dadcccb5c"
 
 [[metadata.targets]]
 requires_python = "==3.13.*"
@@ -32,7 +32,7 @@ name = "asgiref"
 version = "3.8.1"
 requires_python = ">=3.8"
 summary = "ASGI specs, helper code, and adapters"
-groups = ["default"]
+groups = ["default", "typing"]
 dependencies = [
     "typing-extensions>=4; python_version < \"3.11\"",
 ]
@@ -393,7 +393,7 @@ name = "django"
 version = "5.1.2"
 requires_python = ">=3.10"
 summary = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
-groups = ["default"]
+groups = ["default", "typing"]
 dependencies = [
     "asgiref<4,>=3.8.1",
     "sqlparse>=0.3.1",
@@ -530,6 +530,55 @@ files = [
     {file = "django_storages-1.14.4-py3-none-any.whl", hash = "sha256:d61930acb4a25e3aebebc6addaf946a3b1df31c803a6bf1af2f31c9047febaa3"},
 ]
 
+[[package]]
+name = "django-stubs"
+version = "5.1.1"
+requires_python = ">=3.8"
+summary = "Mypy stubs for Django"
+groups = ["typing"]
+dependencies = [
+    "asgiref",
+    "django",
+    "django-stubs-ext>=5.1.1",
+    "tomli; python_version < \"3.11\"",
+    "types-PyYAML",
+    "typing-extensions>=4.11.0",
+]
+files = [
+    {file = "django_stubs-5.1.1-py3-none-any.whl", hash = "sha256:c4dc64260bd72e6d32b9e536e8dd0d9247922f0271f82d1d5132a18f24b388ac"},
+    {file = "django_stubs-5.1.1.tar.gz", hash = "sha256:126d354bbdff4906c4e93e6361197f6fbfb6231c3df6def85a291dae6f9f577b"},
+]
+
+[[package]]
+name = "django-stubs-ext"
+version = "5.1.1"
+requires_python = ">=3.8"
+summary = "Monkey-patching and extensions for django-stubs"
+groups = ["typing"]
+dependencies = [
+    "django",
+    "typing-extensions",
+]
+files = [
+    {file = "django_stubs_ext-5.1.1-py3-none-any.whl", hash = "sha256:3907f99e178c93323e2ce908aef8352adb8c047605161f8d9e5e7b4efb5a6a9c"},
+    {file = "django_stubs_ext-5.1.1.tar.gz", hash = "sha256:db7364e4f50ae7e5360993dbd58a3a57ea4b2e7e5bab0fbd525ccdb3e7975d1c"},
+]
+
+[[package]]
+name = "django-stubs"
+version = "5.1.1"
+extras = ["mypy-compatible"]
+requires_python = ">=3.8"
+summary = "Mypy stubs for Django"
+groups = ["typing"]
+dependencies = [
+    "django-stubs==5.1.1",
+]
+files = [
+    {file = "django_stubs-5.1.1-py3-none-any.whl", hash = "sha256:c4dc64260bd72e6d32b9e536e8dd0d9247922f0271f82d1d5132a18f24b388ac"},
+    {file = "django_stubs-5.1.1.tar.gz", hash = "sha256:126d354bbdff4906c4e93e6361197f6fbfb6231c3df6def85a291dae6f9f577b"},
+]
+
 [[package]]
 name = "django-timezone-field"
 version = "7.0"
@@ -1698,7 +1747,7 @@ name = "sqlparse"
 version = "0.5.1"
 requires_python = ">=3.8"
 summary = "A non-validating SQL parser."
-groups = ["default"]
+groups = ["default", "typing"]
 files = [
     {file = "sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4"},
     {file = "sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e"},
@@ -1780,12 +1829,23 @@ files = [
     {file = "truststore-0.9.2.tar.gz", hash = "sha256:a1dee0d0575ff22d2875476343783a5d64575419974e228f3248772613c3d993"},
 ]
 
+[[package]]
+name = "types-pyyaml"
+version = "6.0.12.20240917"
+requires_python = ">=3.8"
+summary = "Typing stubs for PyYAML"
+groups = ["typing"]
+files = [
+    {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"},
+    {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"},
+]
+
 [[package]]
 name = "typing-extensions"
 version = "4.12.2"
 requires_python = ">=3.8"
 summary = "Backported and Experimental Type Hints for Python 3.8+"
-groups = ["default"]
+groups = ["default", "typing"]
 files = [
     {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
     {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
@@ -1796,7 +1856,7 @@ name = "tzdata"
 version = "2024.2"
 requires_python = ">=2"
 summary = "Provider of IANA time zone data"
-groups = ["default"]
+groups = ["default", "typing"]
 files = [
     {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"},
     {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"},
diff --git a/pyproject.toml b/pyproject.toml
index ce737d4e3f11d3e6ef3c777d2a38263fbf0c1762..48ea6537b9ebe084c687ad6d9fb95178edd0fc25 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -70,6 +70,9 @@ dev = [
     "pre-commit>=3.6.0",
     "docutils>=0.21.2",
 ]
+typing = [
+    "django-stubs[mypy-compatible]>=5.1.1",
+]
 
 [tool.pdm.scripts]
 # Generic scripts
@@ -107,3 +110,10 @@ output = ".tools/coverage/coverage.xml"
 
 [tool.coverage.html]
 directory = ".tools/coverage/html_report"
+
+
+[tool.mypy]
+plugins=["mypy_django_plugin.main"]
+
+[tool.django-stubs]
+django_settings_module="hub.settings.base"
diff --git a/requirements.dev.txt b/requirements.dev.txt
index da13333eeabfcc34a0ea3697db434cabd74c7543..8f0630377cc004f2a709cfaeed831b082c3b7a50 100644
--- a/requirements.dev.txt
+++ b/requirements.dev.txt
@@ -35,6 +35,8 @@ django-oauth-toolkit==3.0.1
 django-ratelimit==4.1.0
 django-redis==5.4.0
 django-storages==1.14.4
+django-stubs-ext==5.1.1
+django-stubs[mypy-compatible]==5.1.1
 django-timezone-field==7.0
 django-widget-tweaks==1.5.0
 djangorestframework==3.15.2
@@ -117,6 +119,7 @@ tox==4.23.0
 tox-pdm==0.7.2
 tqdm==4.66.5
 truststore==0.9.2; python_version >= "3.10"
+types-pyyaml==6.0.12.20240917
 typing-extensions==4.12.2
 tzdata==2024.2
 unearth==0.17.2
diff --git a/src/backoffice/templates/backoffice/project/create_edit.html b/src/backoffice/templates/backoffice/project/create_edit.html
index d074807db26896da7a60f34dbf32152cf37f7a27..2a59c4f6bc965f0f8ef6ca7658d5e824b999e5f0 100644
--- a/src/backoffice/templates/backoffice/project/create_edit.html
+++ b/src/backoffice/templates/backoffice/project/create_edit.html
@@ -142,7 +142,7 @@
         </div>
       </div>
       {% include 'backoffice/link/link_display.html' with links=project.links %}
-      {% trans 'Project__submit' as button_text%}
+      {% trans 'Project__submit' as button_text %}
       <div class="card-footer">
       {% bootstrap_button button_text button_type="submit" button_class="btn-primary float-end" %}
     </div>
diff --git a/src/backoffice/urls.py b/src/backoffice/urls.py
index e4b583c81fd42c99c87f1aed20066c1d1ccb8d4d..6931b8b45b50914fa3098b95e62e79e1bb2a060a 100644
--- a/src/backoffice/urls.py
+++ b/src/backoffice/urls.py
@@ -1,6 +1,15 @@
 from django.urls import path, re_path
 from django.views.generic import RedirectView
 
+from backoffice.views.map import (
+    FloorCreateView,
+    FloorListView,
+    FloorUpdateView,
+    POICreateView,
+    POIListView,
+    POIUpdateView,
+)
+
 from .views import (
     assemblies,
     assemblyteam,
@@ -8,7 +17,6 @@ from .views import (
     badges,
     channelteam,
     events,
-    map,
     misc,
     moderation,
     profile,
@@ -94,12 +102,12 @@ urlpatterns = [
     path('assembly/<uuid:assembly>/r/<uuid:room>/new_link', assemblies.CreateRoomLinkView.as_view(), name='roomlink-create'),
     path('assembly/<uuid:assembly>/r/<uuid:room>/remove_link', assemblies.RemoveRoomLinkView.as_view(), name='roomlink-remove'),
     path('assembly/<uuid:assembly>/r/<uuid:room>/remove', assemblies.RemoveRoomView.as_view(), name='assembly-remove-room'),
-    path('map/floors', map.FloorListView.as_view(), name='map-floor-list'),
-    path('map/floor/new', map.FloorCreateView.as_view(), name='map-floor-create'),
-    path('map/floor/<uuid:pk>', map.FloorUpdateView.as_view(), name='map-floor-edit'),
-    path('map/pois', map.POIListView.as_view(), name='map-poi-list'),
-    path('map/poi/new', map.POICreateView.as_view(), name='map-poi-create'),
-    path('map/poi/<uuid:pk>', map.POIUpdateView.as_view(), name='map-poi-edit'),
+    path('map/floors', FloorListView.as_view(), name='map-floor-list'),
+    path('map/floor/new', FloorCreateView.as_view(), name='map-floor-create'),
+    path('map/floor/<uuid:pk>', FloorUpdateView.as_view(), name='map-floor-edit'),
+    path('map/pois', POIListView.as_view(), name='map-poi-list'),
+    path('map/poi/new', POICreateView.as_view(), name='map-poi-create'),
+    path('map/poi/<uuid:pk>', POIUpdateView.as_view(), name='map-poi-edit'),
     path('moderation', moderation.IndexView.as_view(), name='moderation'),
     path('moderation/assemblies', moderation.ModerationAssemblyListView.as_view(), name='moderation-assembly-list'),
     path('moderation/assembly/<uuid:pk>', moderation.ModerationAssemblyDetailView.as_view(), name='moderation-assembly-detail'),
diff --git a/src/backoffice/views/projects.py b/src/backoffice/views/projects.py
index 76aa2df895158207c699cf1f426faad2a0f4a6bd..c38dccb6ace2522f131c3bfcdec9f48dffab1b47 100644
--- a/src/backoffice/views/projects.py
+++ b/src/backoffice/views/projects.py
@@ -124,7 +124,7 @@ class ProjectFormMixin(ConferenceLoginRequiredMixin, FormView):
 
     def get_context_data(self, *args, **kwargs):
         ctx = super().get_context_data(*args, **kwargs)
-        if 'form' not in kwargs:
+        if 'form' not in kwargs and 'form' not in ctx:
             ctx['form'] = self.get_form()
         if 'link_forms' not in kwargs:
             ctx['link_forms'] = self.get_links_formset()
@@ -187,24 +187,12 @@ class CreateProjectView(SoProjectFormMixin, CreateView):
         self.object = None
         return super().post(request, *args, **kwargs)
 
-    def get_form_kwargs(self) -> dict[str, Any]:
-        return {
-            **super().get_form_kwargs(),
-            'create': True,
-        }
-
 
 class AssemblyCreateProjectView(AssemblyProjectFormMixin, CreateView):
     def post(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse:
         self.object = None
         return super().post(request, *args, **kwargs)
 
-    def get_form_kwargs(self) -> dict[str, Any]:
-        return {
-            **super().get_form_kwargs(),
-            'create': True,
-        }
-
 
 class ProjectView(SoProjectFormMixin, UpdateView):
     def post(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse:
diff --git a/src/core/forms.py b/src/core/forms.py
index 5e4f619ec4710069f8c62e6d42c82a1a0615ed45..86a57d08fd577dec00ac0f9b1a3bafcba8ac42d7 100644
--- a/src/core/forms.py
+++ b/src/core/forms.py
@@ -270,16 +270,16 @@ class ProjectForm(TranslatedFieldsForm):
         conference,
         assembly: Assembly | None = None,
         owner: PlatformUser | None = None,
-        create: bool = False,
+        instance: Project | None = None,
         publish: bool = False,
         **kwargs,
     ):
         self.conference = conference
-        self.create = create
         self.assembly = assembly
         self.owner = owner
         self.publish = publish
-        super().__init__(*args, **kwargs)
+        self.create = instance is None
+        super().__init__(*args, instance=instance, **kwargs)
         if self.instance.pk:
             self.initial['tags_list'] = ', '.join(self.instance.sorted_tags)
 
diff --git a/src/hub/settings/base.py b/src/hub/settings/base.py
index 458d9f8e1d4c66f8054c91d15a58dfae8127358e..634517dd042300c7ac9b7c2a0ee29c25687e8528 100644
--- a/src/hub/settings/base.py
+++ b/src/hub/settings/base.py
@@ -212,6 +212,7 @@ TEMPLATES = [
                 'django.template.context_processors.request',
                 'django.contrib.auth.context_processors.auth',
                 'django.contrib.messages.context_processors.messages',
+                'django.template.context_processors.i18n',
             ],
         },
     },