fix(seo): configure canonical links dynamically per page

This commit is contained in:
mstfyldz
2026-05-30 22:14:59 +03:00
parent 7872e8862d
commit 2c24229caa
14 changed files with 37 additions and 5 deletions
+3
View File
@@ -65,6 +65,9 @@ export async function generateMetadata({
return { return {
title: `${content.title} | Ayris Tech`, title: `${content.title} | Ayris Tech`,
description: content.excerpt, description: content.excerpt,
alternates: {
canonical: `/${lang}/blog/${slug}`,
},
}; };
} }
+3
View File
@@ -9,6 +9,9 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc
return { return {
title: dict.meta.blog.title, title: dict.meta.blog.title,
description: dict.meta.blog.desc, description: dict.meta.blog.desc,
alternates: {
canonical: `/${lang}/blog`,
},
}; };
} }
+3
View File
@@ -8,6 +8,9 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc
return { return {
title: dict.meta.contact.title, title: dict.meta.contact.title,
description: dict.meta.contact.desc, description: dict.meta.contact.desc,
alternates: {
canonical: `/${lang}/contact`,
},
}; };
} }
+3
View File
@@ -19,6 +19,9 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc
return { return {
title: `${titleStr} | ${dict.nav.brandName}`, title: `${titleStr} | ${dict.nav.brandName}`,
description: `${titleStr} - ${dict.work.desc}`, description: `${titleStr} - ${dict.work.desc}`,
alternates: {
canonical: `/${lang}/expertise/${slug}`,
},
}; };
} }
+3
View File
@@ -8,6 +8,9 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc
return { return {
title: dict.meta.faq.title, title: dict.meta.faq.title,
description: dict.meta.faq.desc, description: dict.meta.faq.desc,
alternates: {
canonical: `/${lang}/faq`,
},
}; };
} }
-1
View File
@@ -55,7 +55,6 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc
images: ['/og-image.jpg'], images: ['/og-image.jpg'],
}, },
alternates: { alternates: {
canonical: '/',
languages: { languages: {
'en': '/en', 'en': '/en',
'tr': '/tr', 'tr': '/tr',
+3
View File
@@ -8,6 +8,9 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc
return { return {
title: dict.meta.home.title, title: dict.meta.home.title,
description: dict.meta.home.desc, description: dict.meta.home.desc,
alternates: {
canonical: `/${lang}`,
},
}; };
} }
+3
View File
@@ -8,6 +8,9 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc
return { return {
title: dict.meta.process.title, title: dict.meta.process.title,
description: dict.meta.process.desc, description: dict.meta.process.desc,
alternates: {
canonical: `/${lang}/process`,
},
}; };
} }
+3
View File
@@ -8,6 +8,9 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc
return { return {
title: dict.meta.services.title, title: dict.meta.services.title,
description: dict.meta.services.desc, description: dict.meta.services.desc,
alternates: {
canonical: `/${lang}/services`,
},
}; };
} }
+3
View File
@@ -39,6 +39,9 @@ export async function generateMetadata({
return { return {
title: `${project.title}${dict.nav.brandName}${dict.nav.brandSub}`, title: `${project.title}${dict.nav.brandName}${dict.nav.brandSub}`,
description: project.desc, description: project.desc,
alternates: {
canonical: `/${lang}/work/${slug}`,
},
}; };
} }
+3
View File
@@ -9,6 +9,9 @@ export async function generateMetadata({ params }: { params: Promise<{ lang: Loc
return { return {
title: dict.meta.work.title, title: dict.meta.work.title,
description: dict.meta.work.desc, description: dict.meta.work.desc,
alternates: {
canonical: `/${lang}/work`,
},
}; };
} }
+1
View File
@@ -164,6 +164,7 @@ export default function BlogClient({
</div> </div>
<Link <Link
href={`/${lang}/blog/${post.slug}`} href={`/${lang}/blog/${post.slug}`}
aria-label={content.title as string}
className="w-10 h-10 border border-[#C8C2B8] group-hover:border-[#0A0A0A] group-hover:bg-[#0A0A0A] group-hover:text-[#FFE600] text-[#0A0A0A] flex items-center justify-center transition-all" className="w-10 h-10 border border-[#C8C2B8] group-hover:border-[#0A0A0A] group-hover:bg-[#0A0A0A] group-hover:text-[#FFE600] text-[#0A0A0A] flex items-center justify-center transition-all"
> >
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"> <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
+2 -2
View File
@@ -109,8 +109,8 @@ export default function ContactClient({ lang, dict }: { lang: Locale; dict: any
className="w-full bg-transparent border-b border-[#C8C2B8] focus:border-[#0A0A0A] outline-none py-3 text-sm text-[#0A0A0A] placeholder-[#C8C2B8] transition-colors" /> className="w-full bg-transparent border-b border-[#C8C2B8] focus:border-[#0A0A0A] outline-none py-3 text-sm text-[#0A0A0A] placeholder-[#C8C2B8] transition-colors" />
</div> </div>
<div> <div>
<label className="block font-mono text-[9px] font-bold uppercase tracking-[0.3em] text-[#A0998E] mb-2">{dict.contact.formTrack}</label> <label htmlFor="form-service" className="block font-mono text-[9px] font-bold uppercase tracking-[0.3em] text-[#A0998E] mb-2">{dict.contact.formTrack}</label>
<select value={formState.service} onChange={e => setFormState({ ...formState, service: e.target.value })} <select id="form-service" value={formState.service} onChange={e => setFormState({ ...formState, service: e.target.value })}
className="w-full bg-transparent border-b border-[#C8C2B8] focus:border-[#0A0A0A] outline-none py-3 text-sm text-[#0A0A0A] transition-colors cursor-pointer"> className="w-full bg-transparent border-b border-[#C8C2B8] focus:border-[#0A0A0A] outline-none py-3 text-sm text-[#0A0A0A] transition-colors cursor-pointer">
<option value="AI Solutions" className="bg-[#F4F0E8]">{dict.services.items.ai.title}</option> <option value="AI Solutions" className="bg-[#F4F0E8]">{dict.services.items.ai.title}</option>
<option value="Blockchain Dev" className="bg-[#F4F0E8]">{dict.services.items.blockchain.title}</option> <option value="Blockchain Dev" className="bg-[#F4F0E8]">{dict.services.items.blockchain.title}</option>
+4 -2
View File
@@ -510,6 +510,7 @@ export default function HomeClientBelowFold({ lang, dict }: { lang: Locale; dict
</div> </div>
<Link <Link
href={`/${lang}/blog/${post.slug}`} href={`/${lang}/blog/${post.slug}`}
aria-label={content.title as string}
className="w-10 h-10 border border-[#C8C2B8] group-hover:border-[#0A0A0A] group-hover:bg-[#0A0A0A] group-hover:text-[#FFE600] text-[#0A0A0A] flex items-center justify-center transition-all" className="w-10 h-10 border border-[#C8C2B8] group-hover:border-[#0A0A0A] group-hover:bg-[#0A0A0A] group-hover:text-[#FFE600] text-[#0A0A0A] flex items-center justify-center transition-all"
> >
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"> <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
@@ -622,13 +623,14 @@ export default function HomeClientBelowFold({ lang, dict }: { lang: Locale; dict
{ label: dict.contact.formTrack, type: "select", options: ["$5k $20k", "$20k $50k", "$50k $100k", "$100k+"] }, { label: dict.contact.formTrack, type: "select", options: ["$5k $20k", "$20k $50k", "$50k $100k", "$100k+"] },
].map((field, fi) => ( ].map((field, fi) => (
<div key={fi} className="flex flex-col"> <div key={fi} className="flex flex-col">
<label className="font-mono text-[9px] tracking-[0.3em] uppercase text-[#A0998E] mb-3">{field.label}</label> <label htmlFor={`field-${fi}`} className="font-mono text-[9px] tracking-[0.3em] uppercase text-[#A0998E] mb-3">{field.label}</label>
{field.type === "select" ? ( {field.type === "select" ? (
<select className="bg-transparent border-b border-[#C8C2B8] focus:border-[#0A0A0A] text-[#0A0A0A] text-sm py-3 focus:outline-none transition-colors cursor-pointer appearance-none"> <select id={`field-${fi}`} className="bg-transparent border-b border-[#C8C2B8] focus:border-[#0A0A0A] text-[#0A0A0A] text-sm py-3 focus:outline-none transition-colors cursor-pointer appearance-none">
{field.options?.map(opt => <option key={opt} value={opt} className="bg-[#F4F0E8]">{opt}</option>)} {field.options?.map(opt => <option key={opt} value={opt} className="bg-[#F4F0E8]">{opt}</option>)}
</select> </select>
) : ( ) : (
<input <input
id={`field-${fi}`}
required required
type={field.type} type={field.type}
placeholder={field.placeholder} placeholder={field.placeholder}