embroidery-viewer/src/lib/components/Head.svelte

107 lines
3.3 KiB
Svelte

<script>
import { PUBLIC_IMAGE_BASE_URL } from '$env/static/public';
import { locale, t } from '$lib/translations';
import { normalizeLocaleUnderscore } from '$lib/utils/normalizeLocaleUnderscore';
import { isDevelopment } from '$lib/utils/env';
/**
* =========================
* Props
* =========================
*/
/** @type {string} Page title (translation key) */
export let title;
/** @type {string} Page description (translation key) */
export let description;
/** @type {string} SEO keywords (translation key or raw string) */
export let keywords;
/** @type {string} Canonical URL (absolute) */
export let url;
/** @type {string} Open Graph type (e.g., 'website', 'article') */
export let ogType = 'website';
/** @type {string} Optional override for Open Graph description */
export let ogDescription = description;
/** @type {string | undefined} Optional Open Graph image URL (translation key or absolute URL) */
export let ogImage = undefined;
/** @type {string} Twitter card type */
export let twitterCard = 'summary_large_image';
/** @type {boolean} Whether the page should be indexed */
export let shouldBeIndexed = !isDevelopment();
/**
* =========================
* Derived / Computed values
* =========================
*/
let defaultImage = `${PUBLIC_IMAGE_BASE_URL}/t/f_webp/embroidery-viewer/logo-icon.webp`;
// Translations (avoid repeating $t everywhere)
$: translatedTitle = title ? $t(title) : '';
$: translatedDescription = description ? $t(description) : '';
$: translatedKeywords = keywords ? $t(keywords) : '';
// Fallbacks
$: finalOgDescription = ogDescription
? $t(ogDescription)
: translatedDescription;
$: ogImageUrl = ogImage
? ogImage.startsWith('http')
? ogImage
: $t(ogImage)
: defaultImage;
$: canonicalUrl = url
? url.startsWith('http')
? url
: $t(url)
: '';
// Locale formatting (e.g., en-US -> en_US)
$: ogLocale = normalizeLocaleUnderscore($locale);
// Robots directive
$: robotsContent = shouldBeIndexed ? 'index, follow' : 'noindex, nofollow';
</script>
<svelte:head>
<!-- Primary SEO -->
<title>{translatedTitle}</title>
<meta name="description" content={translatedDescription} />
<meta name="keywords" content={translatedKeywords} />
{#if canonicalUrl}
<link rel="canonical" href={canonicalUrl} />
{/if}
<!-- Robots -->
<meta name="robots" content={robotsContent} />
<meta name="googlebot" content={robotsContent} />
<!-- Open Graph (Facebook, LinkedIn, etc.) -->
<meta property="og:type" content={ogType} />
<meta property="og:title" content={translatedTitle} />
<meta property="og:description" content={finalOgDescription} />
<meta property="og:image" content={ogImageUrl} />
<meta property="og:url" content={canonicalUrl} />
<meta property="og:locale" content={ogLocale} />
<meta property="og:site_name" content="Embroidery Viewer" />
<!-- Twitter -->
<meta name="twitter:card" content={twitterCard} />
<meta name="twitter:title" content={translatedTitle} />
<meta name="twitter:description" content={finalOgDescription} />
<meta name="twitter:image" content={ogImageUrl} />
<!-- Optional: improves link previews in some platforms -->
<meta property="og:image:alt" content={translatedTitle} />
</svelte:head>