feat(seo): expand sitemap and structured data
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { AboutContent } from "@/components/about-content";
|
import { AboutContent } from "@/components/about-content";
|
||||||
import { PersonJsonLd } from "@/components/json-ld";
|
import { ProfilePageJsonLd } from "@/components/json-ld";
|
||||||
import { getStaticPageMetadata } from "@/lib/seo";
|
import { getLocalizedUrl, getStaticPageMetadata } from "@/lib/seo";
|
||||||
|
import { getTranslations } from "next-intl/server";
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
params,
|
params,
|
||||||
@@ -11,10 +12,25 @@ export async function generateMetadata({
|
|||||||
return getStaticPageMetadata({ locale, page: "about", path: "/about" });
|
return getStaticPageMetadata({ locale, page: "about", path: "/about" });
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AboutPage() {
|
export default async function AboutPage({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ locale: string }>;
|
||||||
|
}) {
|
||||||
|
const { locale } = await params;
|
||||||
|
const siteLocale = locale === "en" ? "en" : "tr";
|
||||||
|
const t = await getTranslations({
|
||||||
|
locale: siteLocale,
|
||||||
|
namespace: "Seo.about",
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PersonJsonLd
|
<ProfilePageJsonLd
|
||||||
|
pageUrl={getLocalizedUrl(siteLocale, "/about")}
|
||||||
|
pageName={t("title")}
|
||||||
|
description={t("description")}
|
||||||
|
locale={siteLocale}
|
||||||
name="Poyraz Avsever"
|
name="Poyraz Avsever"
|
||||||
jobTitle="Fullstack Developer"
|
jobTitle="Fullstack Developer"
|
||||||
sameAs={[
|
sameAs={[
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ export default async function AgendaDetailPage({ params }: AgendaDetailPageProps
|
|||||||
image={post.coverImage}
|
image={post.coverImage}
|
||||||
datePublished={post.date}
|
datePublished={post.date}
|
||||||
authorName={post.author}
|
authorName={post.author}
|
||||||
|
locale={locale as SiteLocale}
|
||||||
/>
|
/>
|
||||||
<BlogDetailContent post={post} section="agenda" />
|
<BlogDetailContent post={post} section="agenda" />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ export default async function AnimationSourceDetailPage({
|
|||||||
image={source.coverImage}
|
image={source.coverImage}
|
||||||
datePublished={source.date}
|
datePublished={source.date}
|
||||||
authorName={source.author}
|
authorName={source.author}
|
||||||
|
locale={locale === "en" ? "en" : "tr"}
|
||||||
/>
|
/>
|
||||||
<AnimationSourceDetailContent source={source} />
|
<AnimationSourceDetailContent source={source} />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ export default async function BlogDetailPage({ params }: BlogDetailPageProps) {
|
|||||||
image={post.coverImage}
|
image={post.coverImage}
|
||||||
datePublished={post.date}
|
datePublished={post.date}
|
||||||
authorName={post.author}
|
authorName={post.author}
|
||||||
|
locale={locale as SiteLocale}
|
||||||
/>
|
/>
|
||||||
<BlogDetailContent post={post} />
|
<BlogDetailContent post={post} />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
import { ProjectsContent } from "@/components/projects-content";
|
import { ProjectsContent } from "@/components/projects-content";
|
||||||
import { getStaticPageMetadata } from "@/lib/seo";
|
import { ProjectsJsonLd } from "@/components/json-ld";
|
||||||
|
import {
|
||||||
|
EXTENSIONS,
|
||||||
|
FIGMA_TEMPLATES,
|
||||||
|
MOBILE_APPS,
|
||||||
|
WEB_APPS,
|
||||||
|
} from "@/data/projects";
|
||||||
|
import { getLocalizedValue } from "@/lib/locale";
|
||||||
|
import { getLocalizedUrl, getStaticPageMetadata } from "@/lib/seo";
|
||||||
|
import { getTranslations } from "next-intl/server";
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
params,
|
params,
|
||||||
@@ -14,6 +23,40 @@ export async function generateMetadata({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ProjectsPage() {
|
export default async function ProjectsPage({
|
||||||
return <ProjectsContent />;
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ locale: string }>;
|
||||||
|
}) {
|
||||||
|
const { locale } = await params;
|
||||||
|
const siteLocale = locale === "en" ? "en" : "tr";
|
||||||
|
const t = await getTranslations({
|
||||||
|
locale: siteLocale,
|
||||||
|
namespace: "Seo.projects",
|
||||||
|
});
|
||||||
|
const projects = [
|
||||||
|
...WEB_APPS,
|
||||||
|
...MOBILE_APPS,
|
||||||
|
...EXTENSIONS,
|
||||||
|
...FIGMA_TEMPLATES,
|
||||||
|
].map((project) => ({
|
||||||
|
name: getLocalizedValue(project.title, siteLocale),
|
||||||
|
description: getLocalizedValue(project.description, siteLocale),
|
||||||
|
image: project.image,
|
||||||
|
url: project.href,
|
||||||
|
technologies: project.technologies,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ProjectsJsonLd
|
||||||
|
name={t("title")}
|
||||||
|
description={t("description")}
|
||||||
|
url={getLocalizedUrl(siteLocale, "/projects")}
|
||||||
|
locale={siteLocale}
|
||||||
|
projects={projects}
|
||||||
|
/>
|
||||||
|
<ProjectsContent />
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+115
-85
@@ -1,10 +1,54 @@
|
|||||||
import { listBlogDetails } from "@/data/blog-detail";
|
|
||||||
import { isNewsletterCategory } from "@/data/blog";
|
|
||||||
import { listAnimationSources } from "@/data/animation-sources";
|
|
||||||
import type { MetadataRoute } from "next";
|
import type { MetadataRoute } from "next";
|
||||||
|
import { listAnimationSources } from "@/data/animation-sources";
|
||||||
|
import { isNewsletterCategory } from "@/data/blog";
|
||||||
|
import { listBlogDetails } from "@/data/blog-detail";
|
||||||
|
import {
|
||||||
|
getAbsoluteUrl,
|
||||||
|
getLocalizedUrl,
|
||||||
|
type SiteLocale,
|
||||||
|
} from "@/lib/seo";
|
||||||
|
|
||||||
const SITE_URL =
|
const LOCALES: SiteLocale[] = ["tr", "en"];
|
||||||
process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com";
|
|
||||||
|
const STATIC_ROUTES = [
|
||||||
|
{ path: "/", changeFrequency: "weekly", priority: 1 },
|
||||||
|
{ path: "/about", changeFrequency: "monthly", priority: 0.8 },
|
||||||
|
{ path: "/about/references", changeFrequency: "monthly", priority: 0.5 },
|
||||||
|
{
|
||||||
|
path: "/about/volunteer-community",
|
||||||
|
changeFrequency: "monthly",
|
||||||
|
priority: 0.5,
|
||||||
|
},
|
||||||
|
{ path: "/blog", changeFrequency: "weekly", priority: 0.9 },
|
||||||
|
{ path: "/agenda", changeFrequency: "weekly", priority: 0.9 },
|
||||||
|
{ path: "/projects", changeFrequency: "monthly", priority: 0.8 },
|
||||||
|
{ path: "/content", changeFrequency: "weekly", priority: 0.7 },
|
||||||
|
{ path: "/gallery", changeFrequency: "monthly", priority: 0.6 },
|
||||||
|
{ path: "/contact", changeFrequency: "yearly", priority: 0.5 },
|
||||||
|
{ path: "/links", changeFrequency: "monthly", priority: 0.4 },
|
||||||
|
{
|
||||||
|
path: "/animation-sources",
|
||||||
|
changeFrequency: "monthly",
|
||||||
|
priority: 0.7,
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
function toValidDate(value: string) {
|
||||||
|
const date = new Date(value);
|
||||||
|
return Number.isNaN(date.getTime()) ? undefined : date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLanguageLinks(paths: Partial<Record<SiteLocale, string>>) {
|
||||||
|
const languages: Record<string, string> = {};
|
||||||
|
|
||||||
|
if (paths.tr) languages["tr-TR"] = getLocalizedUrl("tr", paths.tr);
|
||||||
|
if (paths.en) languages["en-US"] = getLocalizedUrl("en", paths.en);
|
||||||
|
languages["x-default"] = paths.tr
|
||||||
|
? getLocalizedUrl("tr", paths.tr)
|
||||||
|
: getLocalizedUrl("en", paths.en!);
|
||||||
|
|
||||||
|
return languages;
|
||||||
|
}
|
||||||
|
|
||||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||||
const [posts, animationSources] = await Promise.all([
|
const [posts, animationSources] = await Promise.all([
|
||||||
@@ -12,89 +56,75 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|||||||
listAnimationSources(),
|
listAnimationSources(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const staticRoutes: MetadataRoute.Sitemap = [
|
const staticRoutes: MetadataRoute.Sitemap = STATIC_ROUTES.flatMap((route) => {
|
||||||
{
|
const paths = { tr: route.path, en: route.path };
|
||||||
url: SITE_URL,
|
const languages = getLanguageLinks(paths);
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "weekly",
|
|
||||||
priority: 1.0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${SITE_URL}/about`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "monthly",
|
|
||||||
priority: 0.8,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${SITE_URL}/about/references`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "monthly",
|
|
||||||
priority: 0.5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${SITE_URL}/about/volunteer-community`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "monthly",
|
|
||||||
priority: 0.5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${SITE_URL}/blog`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "weekly",
|
|
||||||
priority: 0.9,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${SITE_URL}/agenda`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "weekly",
|
|
||||||
priority: 0.9,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${SITE_URL}/projects`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "monthly",
|
|
||||||
priority: 0.8,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${SITE_URL}/content`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "weekly",
|
|
||||||
priority: 0.7,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${SITE_URL}/contact`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "yearly",
|
|
||||||
priority: 0.5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${SITE_URL}/links`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "monthly",
|
|
||||||
priority: 0.4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: `${SITE_URL}/animation-sources`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: "monthly",
|
|
||||||
priority: 0.7,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const blogRoutes: MetadataRoute.Sitemap = posts.map((post) => ({
|
return LOCALES.map((locale) => ({
|
||||||
url: `${SITE_URL}/${isNewsletterCategory(post.category) ? "agenda" : "blog"}/${post.slug}`,
|
url: getLocalizedUrl(locale, route.path),
|
||||||
lastModified: post.date ? new Date(post.date) : new Date(),
|
changeFrequency: route.changeFrequency,
|
||||||
changeFrequency: "monthly",
|
priority: route.priority,
|
||||||
priority: 0.7,
|
alternates: { languages },
|
||||||
}));
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const postGroups = new Map<string, typeof posts>();
|
||||||
|
for (const post of posts) {
|
||||||
|
const key = `${post.category.toLocaleLowerCase()}:${post.coverImage}`;
|
||||||
|
postGroups.set(key, [...(postGroups.get(key) ?? []), post]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const blogRoutes: MetadataRoute.Sitemap = posts.map((post) => {
|
||||||
|
const section = isNewsletterCategory(post.category) ? "agenda" : "blog";
|
||||||
|
const key = `${post.category.toLocaleLowerCase()}:${post.coverImage}`;
|
||||||
|
const translations = postGroups.get(key) ?? [post];
|
||||||
|
const paths = Object.fromEntries(
|
||||||
|
translations.map((translation) => [
|
||||||
|
translation.lang,
|
||||||
|
`/${section}/${translation.slug}`,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: getLocalizedUrl(post.lang, `/${section}/${post.slug}`),
|
||||||
|
lastModified: toValidDate(post.date),
|
||||||
|
changeFrequency: "monthly",
|
||||||
|
priority: 0.7,
|
||||||
|
alternates: { languages: getLanguageLinks(paths) },
|
||||||
|
images: [getAbsoluteUrl(post.coverImage)],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const animationGroups = new Map<string, typeof animationSources>();
|
||||||
|
for (const source of animationSources) {
|
||||||
|
animationGroups.set(source.slug, [
|
||||||
|
...(animationGroups.get(source.slug) ?? []),
|
||||||
|
source,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
const animationSourceRoutes: MetadataRoute.Sitemap = animationSources.map(
|
const animationSourceRoutes: MetadataRoute.Sitemap = animationSources.map(
|
||||||
(source) => ({
|
(source) => {
|
||||||
url: `${SITE_URL}${source.lang === "en" ? "/en" : ""}/animation-sources/${source.slug}`,
|
const translations = animationGroups.get(source.slug) ?? [source];
|
||||||
lastModified: source.date ? new Date(source.date) : new Date(),
|
const paths = Object.fromEntries(
|
||||||
changeFrequency: "monthly",
|
translations.map((translation) => [
|
||||||
priority: 0.6,
|
translation.lang,
|
||||||
}),
|
`/animation-sources/${translation.slug}`,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: getLocalizedUrl(
|
||||||
|
source.lang,
|
||||||
|
`/animation-sources/${source.slug}`,
|
||||||
|
),
|
||||||
|
lastModified: toValidDate(source.date),
|
||||||
|
changeFrequency: "monthly",
|
||||||
|
priority: 0.6,
|
||||||
|
alternates: { languages: getLanguageLinks(paths) },
|
||||||
|
images: [getAbsoluteUrl(source.coverImage)],
|
||||||
|
};
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return [...staticRoutes, ...blogRoutes, ...animationSourceRoutes];
|
return [...staticRoutes, ...blogRoutes, ...animationSourceRoutes];
|
||||||
|
|||||||
+120
-6
@@ -1,5 +1,9 @@
|
|||||||
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com";
|
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com";
|
||||||
|
|
||||||
|
function serializeJsonLd(data: object) {
|
||||||
|
return JSON.stringify(data).replace(/</g, "\\u003c");
|
||||||
|
}
|
||||||
|
|
||||||
export type PersonJsonLdProps = {
|
export type PersonJsonLdProps = {
|
||||||
name: string;
|
name: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
@@ -28,7 +32,114 @@ export function PersonJsonLd({
|
|||||||
return (
|
return (
|
||||||
<script
|
<script
|
||||||
type="application/ld+json"
|
type="application/ld+json"
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
|
dangerouslySetInnerHTML={{ __html: serializeJsonLd(data) }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProfilePageJsonLdProps = PersonJsonLdProps & {
|
||||||
|
pageUrl: string;
|
||||||
|
pageName: string;
|
||||||
|
description: string;
|
||||||
|
locale: "tr" | "en";
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ProfilePageJsonLd({
|
||||||
|
pageUrl,
|
||||||
|
pageName,
|
||||||
|
description,
|
||||||
|
locale,
|
||||||
|
name,
|
||||||
|
url = SITE_URL,
|
||||||
|
image = `${SITE_URL}/logo/logo.webp`,
|
||||||
|
jobTitle = "Fullstack Developer",
|
||||||
|
sameAs = [],
|
||||||
|
}: ProfilePageJsonLdProps) {
|
||||||
|
const data = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "ProfilePage",
|
||||||
|
"@id": `${pageUrl}#profile-page`,
|
||||||
|
url: pageUrl,
|
||||||
|
name: pageName,
|
||||||
|
description,
|
||||||
|
inLanguage: locale === "tr" ? "tr-TR" : "en-US",
|
||||||
|
mainEntity: {
|
||||||
|
"@type": "Person",
|
||||||
|
"@id": `${SITE_URL}/#person`,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
image,
|
||||||
|
jobTitle,
|
||||||
|
sameAs,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<script
|
||||||
|
type="application/ld+json"
|
||||||
|
dangerouslySetInnerHTML={{ __html: serializeJsonLd(data) }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProjectsJsonLdItem = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
image: string;
|
||||||
|
url?: string;
|
||||||
|
technologies: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ProjectsJsonLd({
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
url,
|
||||||
|
locale,
|
||||||
|
projects,
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
url: string;
|
||||||
|
locale: "tr" | "en";
|
||||||
|
projects: ProjectsJsonLdItem[];
|
||||||
|
}) {
|
||||||
|
const data = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "CollectionPage",
|
||||||
|
"@id": `${url}#projects`,
|
||||||
|
url,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
inLanguage: locale === "tr" ? "tr-TR" : "en-US",
|
||||||
|
mainEntity: {
|
||||||
|
"@type": "ItemList",
|
||||||
|
numberOfItems: projects.length,
|
||||||
|
itemListElement: projects.map((project, index) => ({
|
||||||
|
"@type": "ListItem",
|
||||||
|
position: index + 1,
|
||||||
|
item: {
|
||||||
|
"@type": "CreativeWork",
|
||||||
|
name: project.name,
|
||||||
|
description: project.description,
|
||||||
|
image: project.image.startsWith("http")
|
||||||
|
? project.image
|
||||||
|
: `${SITE_URL}${project.image}`,
|
||||||
|
url: project.url,
|
||||||
|
keywords: project.technologies,
|
||||||
|
creator: {
|
||||||
|
"@type": "Person",
|
||||||
|
"@id": `${SITE_URL}/#person`,
|
||||||
|
name: "Poyraz Avsever",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<script
|
||||||
|
type="application/ld+json"
|
||||||
|
dangerouslySetInnerHTML={{ __html: serializeJsonLd(data) }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -40,6 +151,7 @@ export type ArticleJsonLdProps = {
|
|||||||
image: string;
|
image: string;
|
||||||
datePublished: string;
|
datePublished: string;
|
||||||
authorName?: string;
|
authorName?: string;
|
||||||
|
locale?: "tr" | "en";
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ArticleJsonLd({
|
export function ArticleJsonLd({
|
||||||
@@ -49,6 +161,7 @@ export function ArticleJsonLd({
|
|||||||
image,
|
image,
|
||||||
datePublished,
|
datePublished,
|
||||||
authorName = "Poyraz Avsever",
|
authorName = "Poyraz Avsever",
|
||||||
|
locale = "tr",
|
||||||
}: ArticleJsonLdProps) {
|
}: ArticleJsonLdProps) {
|
||||||
const data = {
|
const data = {
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
@@ -56,8 +169,11 @@ export function ArticleJsonLd({
|
|||||||
headline: title,
|
headline: title,
|
||||||
description,
|
description,
|
||||||
url,
|
url,
|
||||||
|
mainEntityOfPage: url,
|
||||||
image: image.startsWith("http") ? image : `${SITE_URL}${image}`,
|
image: image.startsWith("http") ? image : `${SITE_URL}${image}`,
|
||||||
datePublished,
|
datePublished,
|
||||||
|
dateModified: datePublished,
|
||||||
|
inLanguage: locale === "tr" ? "tr-TR" : "en-US",
|
||||||
author: {
|
author: {
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
name: authorName,
|
name: authorName,
|
||||||
@@ -66,17 +182,15 @@ export function ArticleJsonLd({
|
|||||||
publisher: {
|
publisher: {
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
name: authorName,
|
name: authorName,
|
||||||
logo: {
|
url: SITE_URL,
|
||||||
"@type": "ImageObject",
|
image: `${SITE_URL}/logo/logo.webp`,
|
||||||
url: `${SITE_URL}/logo/logo.webp`,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<script
|
<script
|
||||||
type="application/ld+json"
|
type="application/ld+json"
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
|
dangerouslySetInnerHTML={{ __html: serializeJsonLd(data) }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user