diff --git a/components/nav/NavTitle.vue b/components/nav/NavTitle.vue
index 50aea711..3e442316 100644
--- a/components/nav/NavTitle.vue
+++ b/components/nav/NavTitle.vue
@@ -29,7 +29,7 @@ router.afterEach(() => {
@click.prevent="onClickLogo"
>
-
+
{{ $t('app_name') }} {{ env === 'release' ? 'alpha' : env }}
diff --git a/components/status/StatusDetails.vue b/components/status/StatusDetails.vue
index 3a0bd148..6a822674 100644
--- a/components/status/StatusDetails.vue
+++ b/components/status/StatusDetails.vue
@@ -20,7 +20,7 @@ const createdAt = useFormattedDateTime(status.createdAt)
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => `${getDisplayName(status.account)} ${t('common.in')} ${t('app_name')}: "${removeHTMLTags(status.content) || ''}"`,
})
diff --git a/composables/setups.ts b/composables/setups.ts
index 739f45ad..28b08c0a 100644
--- a/composables/setups.ts
+++ b/composables/setups.ts
@@ -12,7 +12,7 @@ export function setupPageHeader() {
return acc
}, {} as Record
)
- useHead({
+ useHydratedHead({
htmlAttrs: {
lang: () => locale.value,
dir: () => localeMap[locale.value] ?? 'ltr',
diff --git a/composables/vue.ts b/composables/vue.ts
index 9c6cb38d..7f98f6b0 100644
--- a/composables/vue.ts
+++ b/composables/vue.ts
@@ -1,5 +1,7 @@
import type { ComponentInternalInstance } from 'vue'
import { onActivated, onDeactivated, ref } from 'vue'
+import type { ActiveHeadEntry, HeadEntryOptions, UseHeadInput } from '@vueuse/head'
+import type { SchemaAugmentations } from '@unhead/schema'
export const isHydrated = ref(false)
@@ -34,3 +36,25 @@ export function onReactivated(hook: Function, target?: ComponentInternalInstance
}, target)
onDeactivated(() => initial.value = false)
}
+
+export function useHydratedHead(input: UseHeadInput, options?: HeadEntryOptions): ActiveHeadEntry> | void {
+ if (input && typeof input === 'object' && !('value' in input)) {
+ const title = 'title' in input ? input.title : undefined
+ if (process.server && title) {
+ input.meta = input.meta || []
+ if (Array.isArray(input.meta)) {
+ input.meta.push(
+ { property: 'og:title', content: (typeof input.title === 'function' ? input.title() : input.title) as string },
+ )
+ }
+ }
+ else if (title) {
+ (input as any).title = () => isHydrated.value ? typeof title === 'function' ? title() : title : ''
+ }
+ }
+ return useHead(() => {
+ if (!isHydrated.value)
+ return {}
+ return resolveUnref(input)
+ }, options)
+}
diff --git a/pages/[[server]]/@[account]/index/followers.vue b/pages/[[server]]/@[account]/index/followers.vue
index dcb4ebe4..691ae2fa 100644
--- a/pages/[[server]]/@[account]/index/followers.vue
+++ b/pages/[[server]]/@[account]/index/followers.vue
@@ -11,7 +11,7 @@ const paginator = account ? useMastoClient().v1.accounts.listFollowers(account.i
const isSelf = useSelfAccount(account)
if (account) {
- useHead({
+ useHydratedHead({
title: () => `${t('account.followers')} | ${getDisplayName(account)} (@${account.acct})`,
})
}
diff --git a/pages/[[server]]/@[account]/index/following.vue b/pages/[[server]]/@[account]/index/following.vue
index b65f749d..173502b9 100644
--- a/pages/[[server]]/@[account]/index/following.vue
+++ b/pages/[[server]]/@[account]/index/following.vue
@@ -11,7 +11,7 @@ const paginator = account ? useMastoClient().v1.accounts.listFollowing(account.i
const isSelf = useSelfAccount(account)
if (account) {
- useHead({
+ useHydratedHead({
title: () => `${t('account.following')} | ${getDisplayName(account)} (@${account.acct})`,
})
}
diff --git a/pages/[[server]]/@[account]/index/index.vue b/pages/[[server]]/@[account]/index/index.vue
index e05b07e8..b909ca2d 100644
--- a/pages/[[server]]/@[account]/index/index.vue
+++ b/pages/[[server]]/@[account]/index/index.vue
@@ -17,7 +17,7 @@ function reorderAndFilter(items: mastodon.v1.Status[]) {
const paginator = useMastoClient().v1.accounts.listStatuses(account.id, { limit: 30, excludeReplies: true })
if (account) {
- useHead({
+ useHydratedHead({
title: () => `${t('account.posts')} | ${getDisplayName(account)} (@${account.acct})`,
})
}
diff --git a/pages/[[server]]/@[account]/index/media.vue b/pages/[[server]]/@[account]/index/media.vue
index 6db9760a..1ff955ac 100644
--- a/pages/[[server]]/@[account]/index/media.vue
+++ b/pages/[[server]]/@[account]/index/media.vue
@@ -10,7 +10,7 @@ const account = await fetchAccountByHandle(handle)
const paginator = useMastoClient().v1.accounts.listStatuses(account.id, { onlyMedia: true, excludeReplies: false })
if (account) {
- useHead({
+ useHydratedHead({
title: () => `${t('tab.media')} | ${getDisplayName(account)} (@${account.acct})`,
})
}
diff --git a/pages/[[server]]/@[account]/index/with_replies.vue b/pages/[[server]]/@[account]/index/with_replies.vue
index b5501c91..189a8d5f 100644
--- a/pages/[[server]]/@[account]/index/with_replies.vue
+++ b/pages/[[server]]/@[account]/index/with_replies.vue
@@ -10,7 +10,7 @@ const account = await fetchAccountByHandle(handle)
const paginator = useMastoClient().v1.accounts.listStatuses(account.id, { excludeReplies: false })
if (account) {
- useHead({
+ useHydratedHead({
title: () => `${t('tab.posts_with_replies')} | ${getDisplayName(account)} (@${account.acct})`,
})
}
diff --git a/pages/[[server]]/explore/index.vue b/pages/[[server]]/explore/index.vue
index df53e0be..56d825d0 100644
--- a/pages/[[server]]/explore/index.vue
+++ b/pages/[[server]]/explore/index.vue
@@ -7,7 +7,7 @@ const paginator = useMastoClient().v1.trends.listStatuses()
const hideNewsTips = useLocalStorage(STORAGE_KEY_HIDE_EXPLORE_POSTS_TIPS, false)
-useHead({
+useHydratedHead({
title: () => `${t('tab.posts')} | ${t('nav.explore')}`,
})
diff --git a/pages/[[server]]/explore/links.vue b/pages/[[server]]/explore/links.vue
index 92b10309..bafcfbc2 100644
--- a/pages/[[server]]/explore/links.vue
+++ b/pages/[[server]]/explore/links.vue
@@ -7,7 +7,7 @@ const paginator = useMastoClient().v1.trends.listLinks()
const hideNewsTips = useLocalStorage(STORAGE_KEY_HIDE_EXPLORE_NEWS_TIPS, false)
-useHead({
+useHydratedHead({
title: () => `${t('tab.news')} | ${t('nav.explore')}`,
})
diff --git a/pages/[[server]]/explore/tags.vue b/pages/[[server]]/explore/tags.vue
index dadbc5ed..d21aeb2a 100644
--- a/pages/[[server]]/explore/tags.vue
+++ b/pages/[[server]]/explore/tags.vue
@@ -10,7 +10,7 @@ const paginator = client.v1.trends.listTags({
const hideTagsTips = useLocalStorage(STORAGE_KEY_HIDE_EXPLORE_TAGS_TIPS, false)
-useHead({
+useHydratedHead({
title: () => `${t('tab.hashtags')} | ${t('nav.explore')}`,
})
diff --git a/pages/[[server]]/explore/users.vue b/pages/[[server]]/explore/users.vue
index b3080094..b711423d 100644
--- a/pages/[[server]]/explore/users.vue
+++ b/pages/[[server]]/explore/users.vue
@@ -4,7 +4,7 @@ const { t } = useI18n()
// limit: 20 is the default configuration of the official client
const paginator = useMastoClient().v2.suggestions.list({ limit: 20 })
-useHead({
+useHydratedHead({
title: () => `${t('tab.for_you')} | ${t('nav.explore')}`,
})
diff --git a/pages/[[server]]/list/[list]/index.vue b/pages/[[server]]/list/[list]/index.vue
index 1780ebcf..05407553 100644
--- a/pages/[[server]]/list/[list]/index.vue
+++ b/pages/[[server]]/list/[list]/index.vue
@@ -35,7 +35,7 @@ const { client } = $(useMasto())
const { data: listInfo, refresh } = $(await useAsyncData(() => client.v1.lists.fetch(list), { default: () => shallowRef() }))
if (listInfo) {
- useHead({
+ useHydratedHead({
title: () => `${listInfo.title} | ${route.fullPath.endsWith('/accounts') ? t('tab.accounts') : t('tab.posts')} | ${t('nav.lists')}`,
})
}
diff --git a/pages/[[server]]/lists.vue b/pages/[[server]]/lists.vue
index e7bf9153..605d8dc6 100644
--- a/pages/[[server]]/lists.vue
+++ b/pages/[[server]]/lists.vue
@@ -11,7 +11,7 @@ const client = useMastoClient()
const paginator = client.v1.lists.list()
-useHead({
+useHydratedHead({
title: () => t('nav.lists'),
})
diff --git a/pages/[[server]]/public/index.vue b/pages/[[server]]/public/index.vue
index e1fb4a21..99b9db20 100644
--- a/pages/[[server]]/public/index.vue
+++ b/pages/[[server]]/public/index.vue
@@ -3,7 +3,7 @@
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => t('title.federated_timeline'),
})
diff --git a/pages/[[server]]/public/local.vue b/pages/[[server]]/public/local.vue
index 18e80bec..93ed9d17 100644
--- a/pages/[[server]]/public/local.vue
+++ b/pages/[[server]]/public/local.vue
@@ -2,7 +2,7 @@
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => t('title.local_timeline'),
})
diff --git a/pages/[[server]]/tags/[tag].vue b/pages/[[server]]/tags/[tag].vue
index 847ae7f5..dfbc0cfe 100644
--- a/pages/[[server]]/tags/[tag].vue
+++ b/pages/[[server]]/tags/[tag].vue
@@ -13,7 +13,7 @@ const paginator = client.v1.timelines.listHashtag(tagName)
const stream = useStreaming(client => client.v1.stream.streamTagTimeline(tagName))
if (tag) {
- useHead({
+ useHydratedHead({
title: () => `#${tag.name}`,
})
}
diff --git a/pages/blocks.vue b/pages/blocks.vue
index 4e3b6015..df78355d 100644
--- a/pages/blocks.vue
+++ b/pages/blocks.vue
@@ -5,7 +5,7 @@ definePageMeta({
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => t('nav.blocked_users'),
})
diff --git a/pages/bookmarks.vue b/pages/bookmarks.vue
index 1d0ed886..efebad8c 100644
--- a/pages/bookmarks.vue
+++ b/pages/bookmarks.vue
@@ -5,7 +5,7 @@ definePageMeta({
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => t('nav.bookmarks'),
})
diff --git a/pages/compose.vue b/pages/compose.vue
index 656ca1fc..a21ad452 100644
--- a/pages/compose.vue
+++ b/pages/compose.vue
@@ -1,6 +1,6 @@
diff --git a/pages/conversations.vue b/pages/conversations.vue
index 5c17537b..f0ab6d40 100644
--- a/pages/conversations.vue
+++ b/pages/conversations.vue
@@ -5,7 +5,7 @@ definePageMeta({
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => t('nav.conversations'),
})
diff --git a/pages/domain_blocks.vue b/pages/domain_blocks.vue
index 663292b6..5f981c3e 100644
--- a/pages/domain_blocks.vue
+++ b/pages/domain_blocks.vue
@@ -5,7 +5,7 @@ definePageMeta({
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => t('nav.blocked_domains'),
})
diff --git a/pages/favourites.vue b/pages/favourites.vue
index 7f840e4b..e4458fbd 100644
--- a/pages/favourites.vue
+++ b/pages/favourites.vue
@@ -5,7 +5,7 @@ definePageMeta({
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => t('nav.favourites'),
})
diff --git a/pages/home.vue b/pages/home.vue
index 3bbf29fb..ef85d197 100644
--- a/pages/home.vue
+++ b/pages/home.vue
@@ -12,7 +12,7 @@ if (process.client && route.path === '/signin/callback')
router.push('/home')
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => t('nav.home'),
})
diff --git a/pages/mutes.vue b/pages/mutes.vue
index c1e0bda9..a9b5d242 100644
--- a/pages/mutes.vue
+++ b/pages/mutes.vue
@@ -5,7 +5,7 @@ definePageMeta({
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => t('nav.muted_users'),
})
diff --git a/pages/notifications/index.vue b/pages/notifications/index.vue
index e9873b95..99d18ebd 100644
--- a/pages/notifications/index.vue
+++ b/pages/notifications/index.vue
@@ -1,6 +1,6 @@
diff --git a/pages/notifications/mention.vue b/pages/notifications/mention.vue
index 8a584914..e65a7522 100644
--- a/pages/notifications/mention.vue
+++ b/pages/notifications/mention.vue
@@ -1,6 +1,6 @@
diff --git a/pages/pinned.vue b/pages/pinned.vue
index f573c10f..5ea9c7c4 100644
--- a/pages/pinned.vue
+++ b/pages/pinned.vue
@@ -5,7 +5,7 @@ definePageMeta({
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => t('account.pinned'),
})
diff --git a/pages/settings.vue b/pages/settings.vue
index 38c81872..9f382594 100644
--- a/pages/settings.vue
+++ b/pages/settings.vue
@@ -5,7 +5,7 @@ definePageMeta({
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => t('nav.settings'),
})
@@ -22,12 +22,12 @@ const isRootPath = computedEager(() => route.name === 'settings')
-
{{ $t('nav.settings') }}
+
{{ isHydrated ? $t('nav.settings') : '' }}
route.name === 'settings')
@@ -52,28 +52,28 @@ const isRootPath = computedEager(() => route.name === 'settings')
diff --git a/pages/settings/about/index.vue b/pages/settings/about/index.vue
index 03b19e10..c7bae8da 100644
--- a/pages/settings/about/index.vue
+++ b/pages/settings/about/index.vue
@@ -2,7 +2,7 @@
const buildInfo = useBuildInfo()
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => `${t('settings.about.label')} | ${t('nav.settings')}`,
})
diff --git a/pages/settings/index.vue b/pages/settings/index.vue
index c0ee6207..e3c4d759 100644
--- a/pages/settings/index.vue
+++ b/pages/settings/index.vue
@@ -2,7 +2,7 @@
-
{{ $t('settings.select_a_settings') }}
+
{{ isHydrated ? $t('settings.select_a_settings') : '' }}
diff --git a/pages/settings/interface/index.vue b/pages/settings/interface/index.vue
index 2ac20970..94ef7e97 100644
--- a/pages/settings/interface/index.vue
+++ b/pages/settings/interface/index.vue
@@ -1,7 +1,7 @@
diff --git a/pages/settings/language/index.vue b/pages/settings/language/index.vue
index 5f16bb11..2ff488ca 100644
--- a/pages/settings/language/index.vue
+++ b/pages/settings/language/index.vue
@@ -5,7 +5,7 @@ const { t, locale } = useI18n()
const translationStatus: ElkTranslationStatus = await import('~/elk-translation-status.json').then(m => m.default)
-useHead({
+useHydratedHead({
title: () => `${t('settings.language.label')} | ${t('nav.settings')}`,
})
const status = computed(() => {
diff --git a/pages/settings/notifications/index.vue b/pages/settings/notifications/index.vue
index 80704319..af14ddf3 100644
--- a/pages/settings/notifications/index.vue
+++ b/pages/settings/notifications/index.vue
@@ -6,7 +6,7 @@ definePageMeta({
const { t } = useI18n()
const pwaEnabled = useAppConfig().pwaEnabled
-useHead({
+useHydratedHead({
title: () => `${t('settings.notifications.label')} | ${t('nav.settings')}`,
})
@@ -15,20 +15,20 @@ useHead({
- {{ $t('settings.notifications.label') }}
+ {{ isHydrated ? $t('settings.notifications.label') : '' }}
diff --git a/pages/settings/notifications/notifications.vue b/pages/settings/notifications/notifications.vue
index e58ee448..22d30220 100644
--- a/pages/settings/notifications/notifications.vue
+++ b/pages/settings/notifications/notifications.vue
@@ -5,7 +5,7 @@ definePageMeta({
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => `${t('settings.notifications.notifications.label')} | ${t('settings.notifications.label')} | ${t('nav.settings')}`,
})
@@ -14,14 +14,14 @@ useHead({
- {{ $t('settings.notifications.notifications.label') }}
+ {{ isHydrated ? $t('settings.notifications.notifications.label') : '' }}
- {{ $t('settings.notifications.notifications.label') }}
+ {{ isHydrated ? $t('settings.notifications.notifications.label') : '' }}
- {{ $t('settings.notifications.under_construction') }} 🚧
+ {{ isHydrated ? $t('settings.notifications.under_construction') : '' }} 🚧
diff --git a/pages/settings/notifications/push-notifications.vue b/pages/settings/notifications/push-notifications.vue
index 5bf238cb..cae515c1 100644
--- a/pages/settings/notifications/push-notifications.vue
+++ b/pages/settings/notifications/push-notifications.vue
@@ -8,7 +8,7 @@ definePageMeta({
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => `${t('settings.notifications.push_notifications.label')} | ${t('settings.notifications.label')} | ${t('nav.settings')}`,
})
@@ -17,7 +17,7 @@ useHead({
- {{ $t('settings.notifications.push_notifications.label') }}
+ {{ isHydrated ? $t('settings.notifications.push_notifications.label') : '' }}
diff --git a/pages/settings/preferences/index.vue b/pages/settings/preferences/index.vue
index c3344105..3fef0a2c 100644
--- a/pages/settings/preferences/index.vue
+++ b/pages/settings/preferences/index.vue
@@ -1,7 +1,7 @@
diff --git a/pages/settings/profile/index.vue b/pages/settings/profile/index.vue
index c480767a..38b7894b 100644
--- a/pages/settings/profile/index.vue
+++ b/pages/settings/profile/index.vue
@@ -5,7 +5,7 @@ definePageMeta({
const { t } = useI18n()
-useHead({
+useHydratedHead({
title: () => `${t('settings.profile.label')} | ${t('nav.settings')}`,
})
@@ -14,22 +14,22 @@ useHead({
- {{ $t('settings.profile.label') }}
+ {{ isHydrated ? $t('settings.profile.label') : '' }}
`${t('settings.users.label')} | ${t('nav.settings')}`,
})
diff --git a/plugins/setup-i18n.client.ts b/plugins/setup-i18n.client.ts
index c9f1c9dd..8cf007c0 100644
--- a/plugins/setup-i18n.client.ts
+++ b/plugins/setup-i18n.client.ts
@@ -11,6 +11,9 @@ export default defineNuxtPlugin(async (nuxt) => {
if (!supportLanguages.includes(lang))
userSettings.value.language = getDefaultLanguage(supportLanguages)
+ if (lang !== i18n.locale)
+ await setLocale(userSettings.value.language)
+
watch([$$(lang), isHydrated], () => {
if (isHydrated.value && lang !== i18n.locale)
setLocale(lang)