- BaseLayout: WebPage schema on every page + canonical URL - Homepages: WebSite schema - GeneratorLayout: SoftwareApplication + BreadcrumbList schemas - Add <slot name='head'> to BaseLayout for child-injected head content
125 lines
3.4 KiB
Plaintext
125 lines
3.4 KiB
Plaintext
---
|
|
import BaseLayout from "./BaseLayout.astro";
|
|
import AdBanner from "../components/AdBanner.astro";
|
|
import SeoBlock from "../components/SeoBlock.astro";
|
|
import { useT } from "../i18n/translations";
|
|
import type { Lang } from "../i18n/translations";
|
|
import type { Generator } from "../data/generators";
|
|
|
|
interface Props {
|
|
generator: Generator;
|
|
}
|
|
|
|
const { generator } = Astro.props;
|
|
const lang = (Astro.currentLocale as Lang) || "en";
|
|
const T = useT(lang);
|
|
const isRu = lang === "ru";
|
|
|
|
const canonicalUrl = `https://randify.pro${Astro.url.pathname}`;
|
|
|
|
const appSchema = {
|
|
"@context": "https://schema.org",
|
|
"@type": "SoftwareApplication",
|
|
name: isRu ? generator.ruTitle : generator.title,
|
|
applicationCategory: "UtilityApplication",
|
|
operatingSystem: "Any",
|
|
offers: {
|
|
"@type": "Offer",
|
|
price: "0",
|
|
priceCurrency: "USD",
|
|
},
|
|
inLanguage: ["en", "ru"],
|
|
description: isRu ? generator.ruDescription : generator.description,
|
|
url: canonicalUrl,
|
|
};
|
|
|
|
const breadcrumbSchema = {
|
|
"@context": "https://schema.org",
|
|
"@type": "BreadcrumbList",
|
|
itemListElement: [
|
|
{
|
|
"@type": "ListItem",
|
|
position: 1,
|
|
name: "Randify.pro",
|
|
item: "https://randify.pro/",
|
|
},
|
|
{
|
|
"@type": "ListItem",
|
|
position: 2,
|
|
name: isRu ? generator.ruTitle : generator.title,
|
|
item: canonicalUrl,
|
|
},
|
|
],
|
|
};
|
|
---
|
|
|
|
<BaseLayout
|
|
title={isRu ? generator.ruSeoTitle : generator.seoTitle}
|
|
description={isRu ? generator.ruSeoDescription : generator.seoDescription}
|
|
>
|
|
<Fragment slot="head">
|
|
<script type="application/ld+json" set:html={JSON.stringify(appSchema)} />
|
|
<script
|
|
type="application/ld+json"
|
|
set:html={JSON.stringify(breadcrumbSchema)}
|
|
/>
|
|
</Fragment>
|
|
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
|
|
<nav aria-label="Breadcrumb" class="mb-10">
|
|
<a
|
|
href={isRu ? "/ru/" : "/"}
|
|
class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded"
|
|
aria-label={T.backToAll}
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="14"
|
|
height="14"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2.5"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
aria-hidden="true"
|
|
>
|
|
<path d="m15 18-6-6 6-6"></path>
|
|
</svg>
|
|
{T.backToAll}
|
|
</a>
|
|
</nav>
|
|
|
|
<header class="mb-2">
|
|
<div class="flex items-center gap-2 mb-4">
|
|
<span
|
|
class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]"
|
|
aria-hidden="true"></span>
|
|
<span
|
|
class="text-sm font-medium text-zinc-400 uppercase tracking-widest"
|
|
>randify</span
|
|
>
|
|
</div>
|
|
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">
|
|
{isRu ? generator.ruPageTitle : generator.pageTitle}
|
|
</h1>
|
|
<p class="mt-2 text-base text-zinc-400">
|
|
{isRu ? generator.ruDescription : generator.description}
|
|
</p>
|
|
</header>
|
|
|
|
<main>
|
|
<slot />
|
|
</main>
|
|
|
|
<div class="mt-12 flex justify-center">
|
|
<AdBanner size="tile" />
|
|
</div>
|
|
|
|
<SeoBlock
|
|
lang={lang}
|
|
howTo={isRu ? generator.ruHowTo : generator.howTo}
|
|
whenTo={isRu ? generator.ruWhenTo : generator.whenTo}
|
|
/>
|
|
</div>
|
|
</BaseLayout>
|