226 lines
6.1 KiB
TypeScript
226 lines
6.1 KiB
TypeScript
import type { SupportedLocale } from '@heygermany/sdk/config'
|
|
import { DEFAULT_LOCALE, LOCALES } from '@/config'
|
|
import { mTextIn } from '@/i18n/messages'
|
|
|
|
type MessageKey = string
|
|
|
|
export const SITE_NAME = 'HeyGermany'
|
|
export const BRAND_ALT_NAME = 'Hey Germany'
|
|
export const CONTACT_EMAIL = 'info@hey-germany.com'
|
|
const viteEnv = (import.meta as ImportMeta & {
|
|
env?: Record<string, string | undefined>
|
|
}).env
|
|
|
|
export const SITE_URL =
|
|
viteEnv?.VITE_SITE_URL || process.env.VITE_SITE_URL || 'https://hey-germany.com'
|
|
export const DEFAULT_SITE_TITLE = `${SITE_NAME} | Nursing Career and Qualification Check in Germany`
|
|
export const DEFAULT_SITE_DESCRIPTION =
|
|
'HeyGermany helps internationally qualified nurses check their qualifications and take the next step toward a nursing career in Germany.'
|
|
export const DEFAULT_OG_IMAGE = '/heygermany-logo.png'
|
|
export const NO_INDEX_METADATA = {
|
|
robots: {
|
|
index: false,
|
|
follow: false,
|
|
nocache: true,
|
|
googleBot: {
|
|
index: false,
|
|
follow: false,
|
|
noimageindex: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
type CreatePageMetadataInput = {
|
|
locale: string
|
|
pathname?: string
|
|
title: string
|
|
description: string
|
|
imagePath?: string
|
|
index?: boolean
|
|
availableLocales?: readonly SupportedLocale[]
|
|
canonicalLocale?: SupportedLocale
|
|
}
|
|
|
|
function isSupportedLocale(locale: string): locale is SupportedLocale {
|
|
return LOCALES.includes(locale as SupportedLocale)
|
|
}
|
|
|
|
function normalizePath(pathname: string): string {
|
|
if (!pathname || pathname === '/') {
|
|
return '/'
|
|
}
|
|
|
|
return pathname.startsWith('/') ? pathname : `/${pathname}`
|
|
}
|
|
|
|
function buildRobots(index: boolean) {
|
|
return index
|
|
? {
|
|
index: true,
|
|
follow: true,
|
|
}
|
|
: NO_INDEX_METADATA.robots
|
|
}
|
|
|
|
export function normalizeLocale(locale: string): SupportedLocale {
|
|
return isSupportedLocale(locale) ? locale : DEFAULT_LOCALE
|
|
}
|
|
|
|
export function getAbsoluteUrl(pathname: string): string {
|
|
return new URL(pathname, SITE_URL).toString()
|
|
}
|
|
|
|
export function getLocalizedPath(locale: string, pathname = '/'): string {
|
|
const normalizedLocale = normalizeLocale(locale)
|
|
const normalizedPath = normalizePath(pathname)
|
|
|
|
return normalizedPath === '/'
|
|
? `/${normalizedLocale}`
|
|
: `/${normalizedLocale}${normalizedPath}`
|
|
}
|
|
|
|
export function withSiteName(title: string): string {
|
|
return title.includes(SITE_NAME) ? title : `${title} | ${SITE_NAME}`
|
|
}
|
|
|
|
export function getMessage(locale: string, key: MessageKey): string {
|
|
return mTextIn(normalizeLocale(locale), key)
|
|
}
|
|
|
|
function buildLanguageAlternates(
|
|
pathname: string,
|
|
locales: readonly SupportedLocale[]
|
|
): Record<string, string> | undefined {
|
|
if (locales.length < 2) {
|
|
return undefined
|
|
}
|
|
|
|
const alternates = Object.fromEntries(
|
|
locales.map((locale) => [locale, getAbsoluteUrl(getLocalizedPath(locale, pathname))])
|
|
) as Record<string, string>
|
|
|
|
alternates['x-default'] = getAbsoluteUrl(getLocalizedPath(DEFAULT_LOCALE, pathname))
|
|
|
|
return alternates
|
|
}
|
|
|
|
export function createPageMetadata({
|
|
locale,
|
|
pathname = '/',
|
|
title,
|
|
description,
|
|
imagePath = DEFAULT_OG_IMAGE,
|
|
index = true,
|
|
availableLocales = LOCALES,
|
|
canonicalLocale,
|
|
}: CreatePageMetadataInput) {
|
|
const resolvedLocale = normalizeLocale(locale)
|
|
const resolvedCanonicalLocale = canonicalLocale ?? resolvedLocale
|
|
const canonicalPath = getLocalizedPath(resolvedCanonicalLocale, pathname)
|
|
const resolvedTitle = withSiteName(title)
|
|
const imageUrl = getAbsoluteUrl(imagePath)
|
|
const languages = buildLanguageAlternates(pathname, availableLocales)
|
|
|
|
return {
|
|
title: resolvedTitle,
|
|
description,
|
|
alternates: {
|
|
canonical: getAbsoluteUrl(canonicalPath),
|
|
...(languages ? { languages } : {}),
|
|
},
|
|
openGraph: {
|
|
title: resolvedTitle,
|
|
description,
|
|
url: getAbsoluteUrl(canonicalPath),
|
|
siteName: SITE_NAME,
|
|
type: 'website',
|
|
images: [
|
|
{
|
|
url: imageUrl,
|
|
alt: resolvedTitle,
|
|
},
|
|
],
|
|
},
|
|
twitter: {
|
|
card: 'summary_large_image',
|
|
title: resolvedTitle,
|
|
description,
|
|
images: [imageUrl],
|
|
},
|
|
robots: buildRobots(index),
|
|
}
|
|
}
|
|
|
|
export function createHomepageMetadata(locale: string) {
|
|
const resolvedLocale = normalizeLocale(locale)
|
|
|
|
const homepageMetadata: Partial<
|
|
Record<SupportedLocale, { title: string; description: string }>
|
|
> = {
|
|
en: {
|
|
title: DEFAULT_SITE_TITLE,
|
|
description: DEFAULT_SITE_DESCRIPTION,
|
|
},
|
|
de: {
|
|
title: `${SITE_NAME} | Pflegekarriere und Qualifikationscheck in Deutschland`,
|
|
description:
|
|
'HeyGermany unterstützt internationale Pflegefachkräfte dabei, ihre Qualifikation für Deutschland zu prüfen und den nächsten Schritt in ihre Pflegekarriere zu gehen.',
|
|
},
|
|
}
|
|
|
|
const localizedBrandMetadata = homepageMetadata[resolvedLocale]
|
|
|
|
return createPageMetadata({
|
|
locale: resolvedLocale,
|
|
title: localizedBrandMetadata?.title ?? `${SITE_NAME} | ${getMessage(locale, 'website.hero.title')}`,
|
|
description: localizedBrandMetadata?.description ?? getMessage(locale, 'website.hero.subtitle'),
|
|
})
|
|
}
|
|
|
|
export function getOrganizationStructuredData() {
|
|
return {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'Organization',
|
|
'@id': `${SITE_URL}#organization`,
|
|
name: SITE_NAME,
|
|
alternateName: BRAND_ALT_NAME,
|
|
url: SITE_URL,
|
|
logo: {
|
|
'@type': 'ImageObject',
|
|
url: getAbsoluteUrl(DEFAULT_OG_IMAGE),
|
|
},
|
|
email: CONTACT_EMAIL,
|
|
description: DEFAULT_SITE_DESCRIPTION,
|
|
contactPoint: [
|
|
{
|
|
'@type': 'ContactPoint',
|
|
contactType: 'customer support',
|
|
email: CONTACT_EMAIL,
|
|
url: SITE_URL,
|
|
availableLanguage: LOCALES,
|
|
},
|
|
],
|
|
brand: {
|
|
'@type': 'Brand',
|
|
name: SITE_NAME,
|
|
alternateName: BRAND_ALT_NAME,
|
|
},
|
|
}
|
|
}
|
|
|
|
export function getWebsiteStructuredData() {
|
|
return {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'WebSite',
|
|
'@id': `${SITE_URL}#website`,
|
|
url: SITE_URL,
|
|
name: SITE_NAME,
|
|
alternateName: BRAND_ALT_NAME,
|
|
description: DEFAULT_SITE_DESCRIPTION,
|
|
publisher: {
|
|
'@id': `${SITE_URL}#organization`,
|
|
},
|
|
inLanguage: LOCALES,
|
|
}
|
|
}
|