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 }).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 | undefined { if (locales.length < 2) { return undefined } const alternates = Object.fromEntries( locales.map((locale) => [locale, getAbsoluteUrl(getLocalizedPath(locale, pathname))]) ) as Record 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 > = { 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, } }