diff --git a/app/[locale]/agenda/[slug]/page.tsx b/app/[locale]/agenda/[slug]/page.tsx index a60ba6b..3068104 100644 --- a/app/[locale]/agenda/[slug]/page.tsx +++ b/app/[locale]/agenda/[slug]/page.tsx @@ -43,6 +43,7 @@ export async function generateMetadata({ description: post.excerpt, url, type: "article", + siteName: "Poyraz Avsever", locale: locale === "en" ? "en_US" : "tr_TR", alternateLocale: translations.length > 1 @@ -64,6 +65,7 @@ export async function generateMetadata({ title: post.title, description: post.excerpt, images: [post.coverImage], + creator: "@poyrazavsever", }, }; } diff --git a/app/[locale]/animation-sources/[slug]/page.tsx b/app/[locale]/animation-sources/[slug]/page.tsx index 98051f8..8f0e06e 100644 --- a/app/[locale]/animation-sources/[slug]/page.tsx +++ b/app/[locale]/animation-sources/[slug]/page.tsx @@ -6,19 +6,17 @@ import { getAnimationSourceBySlug, listAnimationSources, } from "@/data/animation-sources"; - -const SITE_URL = - process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com"; +import { + createAlternates, + getAbsoluteUrl, + getLocalizedUrl, + type SiteLocale, +} from "@/lib/seo"; type AnimationSourceDetailPageProps = { params: Promise<{ locale: string; slug: string }>; }; -function getLocalizedPath(locale: string, slug: string) { - const localePrefix = locale === "tr" ? "" : `/${locale}`; - return `${localePrefix}/animation-sources/${slug}`; -} - export async function generateStaticParams() { const sources = await listAnimationSources(); return sources.map((source) => ({ @@ -39,22 +37,15 @@ export async function generateMetadata({ return { title: locale === "en" ? "Source not found" : "Kaynak bulunamadı" }; } - const url = `${SITE_URL}${getLocalizedPath(locale, source.slug)}`; - const socialImageUrl = new URL(source.coverImage, SITE_URL).toString(); - const turkishUrl = `${SITE_URL}${getLocalizedPath("tr", source.slug)}`; - const englishUrl = `${SITE_URL}${getLocalizedPath("en", source.slug)}`; + const siteLocale = locale as SiteLocale; + const path = `/animation-sources/${source.slug}`; + const url = getLocalizedUrl(siteLocale, path); + const socialImageUrl = getAbsoluteUrl(source.coverImage); return { title: source.title, description: source.excerpt, - alternates: { - canonical: url, - languages: { - "tr-TR": turkishUrl, - "en-US": englishUrl, - "x-default": turkishUrl, - }, - }, + alternates: createAlternates(siteLocale, { tr: path, en: path }), openGraph: { title: source.title, description: source.excerpt, @@ -68,7 +59,7 @@ export async function generateMetadata({ images: [ { url: socialImageUrl, - type: "image/gif", + type: "image/webp", width: 480, height: 480, alt: source.title, @@ -98,7 +89,10 @@ export default async function AnimationSourceDetailPage({ if (!source) notFound(); - const url = `${SITE_URL}${getLocalizedPath(locale, source.slug)}`; + const url = getLocalizedUrl( + locale as SiteLocale, + `/animation-sources/${source.slug}`, + ); return ( <> diff --git a/app/[locale]/animation-sources/page.tsx b/app/[locale]/animation-sources/page.tsx index 5325af5..6dcb485 100644 --- a/app/[locale]/animation-sources/page.tsx +++ b/app/[locale]/animation-sources/page.tsx @@ -2,9 +2,12 @@ import type { Metadata } from "next"; import { getTranslations } from "next-intl/server"; import { AnimationSourcesContent } from "@/components/animation-sources-content"; import { listAnimationSources } from "@/data/animation-sources"; - -const SITE_URL = - process.env.NEXT_PUBLIC_SITE_URL || "https://poyrazavsever.com"; +import { + createAlternates, + getAbsoluteUrl, + getLocalizedUrl, + type SiteLocale, +} from "@/lib/seo"; type AnimationSourcesPageProps = { params: Promise<{ locale: string }>; @@ -14,27 +17,18 @@ export async function generateMetadata({ params, }: AnimationSourcesPageProps): Promise { const { locale } = await params; - const [t, sources] = await Promise.all([ - getTranslations({ locale, namespace: "AnimationSources" }), - listAnimationSources(locale), - ]); - const localizedPath = locale === "en" ? "/en/animation-sources" : "/animation-sources"; - const url = `${SITE_URL}${localizedPath}`; - const socialImagePath = sources[0]?.coverImage ?? "/logo/logo.webp"; - const socialImageUrl = new URL(socialImagePath, SITE_URL).toString(); - const isGif = socialImagePath.toLowerCase().endsWith(".gif"); + const t = await getTranslations({ locale, namespace: "AnimationSources" }); + const siteLocale = locale as SiteLocale; + const url = getLocalizedUrl(siteLocale, "/animation-sources"); + const socialImageUrl = getAbsoluteUrl("/og.png"); return { title: t("title"), description: t("description"), - alternates: { - canonical: url, - languages: { - "tr-TR": `${SITE_URL}/animation-sources`, - "en-US": `${SITE_URL}/en/animation-sources`, - "x-default": `${SITE_URL}/animation-sources`, - }, - }, + alternates: createAlternates(siteLocale, { + tr: "/animation-sources", + en: "/animation-sources", + }), openGraph: { title: t("title"), description: t("description"), @@ -45,9 +39,9 @@ export async function generateMetadata({ images: [ { url: socialImageUrl, - type: isGif ? "image/gif" : "image/png", - width: isGif ? 480 : 1200, - height: isGif ? 480 : 1200, + type: "image/png", + width: 1200, + height: 630, alt: t("title"), }, ], diff --git a/app/[locale]/blog/[slug]/page.tsx b/app/[locale]/blog/[slug]/page.tsx index ce7bfb2..341cc4e 100644 --- a/app/[locale]/blog/[slug]/page.tsx +++ b/app/[locale]/blog/[slug]/page.tsx @@ -43,6 +43,7 @@ export async function generateMetadata({ params }: BlogDetailPageProps): Promise description: post.excerpt, url, type: "article", + siteName: "Poyraz Avsever", locale: locale === "en" ? "en_US" : "tr_TR", alternateLocale: translations.length > 1 @@ -64,6 +65,7 @@ export async function generateMetadata({ params }: BlogDetailPageProps): Promise title: post.title, description: post.excerpt, images: [post.coverImage], + creator: "@poyrazavsever", }, }; } @@ -77,8 +79,7 @@ export default async function BlogDetailPage({ params }: BlogDetailPageProps) { } if (isNewsletterCategory(post.category)) { - const localePrefix = locale === "tr" ? "" : `/${locale}`; - permanentRedirect(`${localePrefix}/agenda/${post.slug}`); + permanentRedirect(`/${locale}/agenda/${post.slug}`); } return ( diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index bbb06b3..f028dd9 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -41,6 +41,17 @@ export async function generateMetadata({ authors: [{ name: "Poyraz Avsever" }], creator: "Poyraz Avsever", publisher: "Poyraz Avsever", + robots: { + index: true, + follow: true, + googleBot: { + index: true, + follow: true, + "max-image-preview": "large", + "max-snippet": -1, + "max-video-preview": -1, + }, + }, openGraph: { type: "website", locale: locale === "tr" ? "tr_TR" : "en_US", @@ -49,10 +60,10 @@ export async function generateMetadata({ description: description, images: [ { - url: "/logo/logo.webp", + url: "/og.png", width: 1200, - height: 1200, - alt: "Poyraz Avsever Logo", + height: 630, + alt: t("socialImageAlt"), }, ], }, @@ -60,7 +71,7 @@ export async function generateMetadata({ card: "summary_large_image", title: title, description: description, - images: ["/logo/logo.webp"], + images: ["/og.png"], creator: "@poyrazavsever", }, keywords: [ diff --git a/lib/seo.ts b/lib/seo.ts index 3a960f5..bb6dab2 100644 --- a/lib/seo.ts +++ b/lib/seo.ts @@ -28,9 +28,9 @@ function normalizePath(path: string) { export function getLocalizedPath(locale: SiteLocale, path: string) { const normalizedPath = normalizePath(path); - - if (locale === "tr") return normalizedPath; - return normalizedPath === "/" ? "/en" : `/en${normalizedPath}`; + return normalizedPath === "/" + ? `/${locale}` + : `/${locale}${normalizedPath}`; } export function getAbsoluteUrl(path: string) { @@ -88,17 +88,27 @@ export function createPageMetadata({ alternates: createAlternates(locale, { tr: path, en: path }), openGraph: { type: "website", + siteName: "Poyraz Avsever", locale: locale === "tr" ? "tr_TR" : "en_US", alternateLocale: locale === "tr" ? ["en_US"] : ["tr_TR"], url, title, description, + images: [ + { + url: "/og.png", + width: 1200, + height: 630, + alt: "Poyraz Avsever", + }, + ], }, twitter: { card: "summary_large_image", title, description, creator: "@poyrazavsever", + images: ["/og.png"], }, }; } diff --git a/messages/en.json b/messages/en.json index 7959e6d..7a9dc33 100644 --- a/messages/en.json +++ b/messages/en.json @@ -171,7 +171,8 @@ "Metadata": { "title": "Poyraz Avsever | Portfolio", "titleTemplate": "%s | Poyraz Avsever", - "description": "Poyraz Avsever's personal portfolio website sharing projects, software content, and technical works." + "description": "Poyraz Avsever's personal portfolio website sharing projects, software content, and technical works.", + "socialImageAlt": "Poyraz Avsever portfolio social preview" }, "SearchCommand": { "placeholder": "Search pages and links...", diff --git a/messages/tr.json b/messages/tr.json index 9465c5e..bd9d8fa 100644 --- a/messages/tr.json +++ b/messages/tr.json @@ -171,7 +171,8 @@ "Metadata": { "title": "Poyraz Avsever | Portfolyo", "titleTemplate": "%s | Poyraz Avsever", - "description": "Poyraz Avsever'in projelerini, yazılım içeriklerini ve teknik çalışmalarını paylaştığı kişisel portfolyo sitesi." + "description": "Poyraz Avsever'in projelerini, yazılım içeriklerini ve teknik çalışmalarını paylaştığı kişisel portfolyo sitesi.", + "socialImageAlt": "Poyraz Avsever portfolyo paylaşım görseli" }, "SearchCommand": { "placeholder": "Sayfa ve bağlantılarda ara...", diff --git a/public/og.png b/public/og.png new file mode 100644 index 0000000..d88b0fb Binary files /dev/null and b/public/og.png differ