diff --git a/apps/frontend/src/composables/project-settings-head.ts b/apps/frontend/src/composables/project-settings-head.ts
new file mode 100644
index 0000000000..346e83a764
--- /dev/null
+++ b/apps/frontend/src/composables/project-settings-head.ts
@@ -0,0 +1,26 @@
+import {
+ defineMessages,
+ injectProjectPageContext,
+ type MessageDescriptor,
+ useVIntl,
+} from '@modrinth/ui'
+
+const messages = defineMessages({
+ headTitle: {
+ id: 'project.settings.head-title',
+ defaultMessage: '⚙️{section} - {project}',
+ },
+})
+
+export function useProjectSettingsHeadTitle(section: MessageDescriptor) {
+ const { formatMessage } = useVIntl()
+ const { projectV2: project } = injectProjectPageContext()
+
+ useHead({
+ title: () =>
+ `${formatMessage(messages.headTitle, {
+ section: formatMessage(section),
+ project: project.value.title,
+ })}`,
+ })
+}
diff --git a/apps/frontend/src/locales/en-US/index.json b/apps/frontend/src/locales/en-US/index.json
index b232c86b23..e91fed890e 100644
--- a/apps/frontend/src/locales/en-US/index.json
+++ b/apps/frontend/src/locales/en-US/index.json
@@ -3968,6 +3968,9 @@
"project.settings.general.url.title": {
"message": "URL"
},
+ "project.settings.head-title": {
+ "message": "⚙️{section} - {project}"
+ },
"project.settings.permissions.attention-needed.description.proj-approved": {
"message": "Please provide proof that you have permission to redistribute all of the following files. Once completed, withheld versions will be automatically published."
},
diff --git a/apps/frontend/src/pages/[type]/[project]/settings/analytics.vue b/apps/frontend/src/pages/[type]/[project]/settings/analytics.vue
index 352e4ffae3..7b3651a0c9 100644
--- a/apps/frontend/src/pages/[type]/[project]/settings/analytics.vue
+++ b/apps/frontend/src/pages/[type]/[project]/settings/analytics.vue
@@ -3,5 +3,9 @@
diff --git a/apps/frontend/src/pages/[type]/[project]/settings/description.vue b/apps/frontend/src/pages/[type]/[project]/settings/description.vue
index ef168ec53f..ac31b035f7 100644
--- a/apps/frontend/src/pages/[type]/[project]/settings/description.vue
+++ b/apps/frontend/src/pages/[type]/[project]/settings/description.vue
@@ -42,6 +42,7 @@
import { TriangleAlertIcon } from '@modrinth/assets'
import { countText, MIN_DESCRIPTION_CHARS } from '@modrinth/moderation'
import {
+ commonProjectSettingsMessages,
ConfirmLeaveModal,
injectProjectPageContext,
MarkdownEditor,
@@ -56,6 +57,8 @@ import { useImageUpload } from '~/composables/image-upload.ts'
const { projectV2: project, currentMember, patchProject } = injectProjectPageContext()
+useProjectSettingsHeadTitle(commonProjectSettingsMessages.description)
+
const { saved, current, saving, hasChanges, reset, save } = useSavable(
() => ({ description: project.value.body }),
async ({ description }) => {
diff --git a/apps/frontend/src/pages/[type]/[project]/settings/disclosures.vue b/apps/frontend/src/pages/[type]/[project]/settings/disclosures.vue
index 7066265879..0331e115d0 100644
--- a/apps/frontend/src/pages/[type]/[project]/settings/disclosures.vue
+++ b/apps/frontend/src/pages/[type]/[project]/settings/disclosures.vue
@@ -84,6 +84,8 @@ const issueMessages = defineMessages({
},
}) satisfies Record
+useProjectSettingsHeadTitle(messages.title)
+
const disclosuresQueryKey = computed(
() => ['project', 'disclosures', 'v3', project.value.id] as const,
)
diff --git a/apps/frontend/src/pages/[type]/[project]/settings/environment.vue b/apps/frontend/src/pages/[type]/[project]/settings/environment.vue
index 84e4ec6cb9..b1acaa6a75 100644
--- a/apps/frontend/src/pages/[type]/[project]/settings/environment.vue
+++ b/apps/frontend/src/pages/[type]/[project]/settings/environment.vue
@@ -28,6 +28,7 @@
import { SettingsIcon } from '@modrinth/assets'
import {
ButtonStyled,
+ commonProjectSettingsMessages,
EnvironmentMigration,
injectProjectPageContext,
LoadingIndicator,
@@ -36,6 +37,8 @@ import { isStaff } from '@modrinth/utils'
const { currentMember, projectV2, projectV3 } = injectProjectPageContext()
+useProjectSettingsHeadTitle(commonProjectSettingsMessages.environment)
+
const showEnvironmentMigration = computed(() => {
return isStaff(currentMember.value?.user)
})
diff --git a/apps/frontend/src/pages/[type]/[project]/settings/gallery.vue b/apps/frontend/src/pages/[type]/[project]/settings/gallery.vue
index cf135ac425..018b62d599 100644
--- a/apps/frontend/src/pages/[type]/[project]/settings/gallery.vue
+++ b/apps/frontend/src/pages/[type]/[project]/settings/gallery.vue
@@ -291,6 +291,7 @@ import {
} from '@modrinth/assets'
import {
ButtonStyled,
+ commonProjectSettingsMessages,
ConfirmModal,
DropArea,
FileInput,
@@ -316,15 +317,7 @@ const {
deleteGalleryItem: deleteGalleryItemMutation,
} = injectProjectPageContext()
-const title = `${project.value.title} - Gallery`
-const description = `View ${project.value.gallery?.length ?? 0} images of ${project.value.title} on Modrinth.`
-
-useSeoMeta({
- title,
- description,
- ogTitle: title,
- ogDescription: description,
-})
+useProjectSettingsHeadTitle(commonProjectSettingsMessages.gallery)
const modal_edit_item = ref(null)
const modal_confirm = ref(null)
diff --git a/apps/frontend/src/pages/[type]/[project]/settings/general.vue b/apps/frontend/src/pages/[type]/[project]/settings/general.vue
index 54570dde34..8a4bf18911 100644
--- a/apps/frontend/src/pages/[type]/[project]/settings/general.vue
+++ b/apps/frontend/src/pages/[type]/[project]/settings/general.vue
@@ -1,5 +1,6 @@