Next.js: تحسين محركات البحث (SEO) وواجهة Metadata
آخر تحديث: 2026-08-26
تحسين محركات البحث (SEO) هو محرك زيارات مجاني للمواقع الإلكترونية — البيانات الوصفية الصحيحة يمكنها أن تجعل Google يفهرس موقعك أسرع بثلاث مرات.
1. ما ستتعلمه
generateMetadata()توليد عنوان الصفحة والوصف وبيانات OpenGraph و Twitter الوصفية بشكل ديناميكيmetadataالتصدير الثابت والبيانات الوصفية للتوجيه الثابت- JSON-LD البيانات المنظمة (BreadcrumbList / Article / FAQPage)
- Sitemap والتوليد الديناميكي مع
robots.txt - hreflang إعداد SEO متعدد اللغات
@vercel/og+ Satori: توليد صور OG بشكل ديناميكي
2. قصة حقيقية لمدير عمليات المحتوى
(1) نقطة الألم: بعد ثلاثة أشهر من الإطلاق، Google فهرس 5 صفحات فقط
بوب مسؤول عن عمليات المحتوى في TaskFlow. بعد ثلاثة أشهر من إطلاق المدونة، كان قد كتب 50 مقالاً:
"Google Search Console يظهر أن 5 صفحات فقط تم فهرستها. عند التدقيق، اكتشفت أن جميع الصفحات تحمل
<title>بعنوان 'TaskFlow'، ولا يوجد وصف meta، ولا صورة Open Graph — لذلك عند المشاركة على وسائل التواصل الاجتماعي، تظهر كروابط عادية."
| المشكلة | التأثير | القياس الكمي |
|---|---|---|
| عنوان مكرر | محركات البحث لا تستطيع التمييز بين الصفحات | 5 فقط من أصل 50 مفهرسة |
| لا توجد صورة OG | لا توجد صورة معاينة للمشاركة الاجتماعية | نسبة النقر على المشاركة: 0.3% |
| لا يوجد JSON-LD | لا يوجد ملخص وسائط غنية | لا توجد معلومات إضافية في نتائج البحث |
| لا يوجد Sitemap | Google لا يستطيع الزحف إلى الصفحات العميقة | تأخير الفهرسة 30 يوماً |
(2) حل Next.js Metadata API
استخدم
generateMetadata()لتوليد وسوم SEO فريدة وبيانات JSON-LD منظمة لكل صفحة.
// app/blog/[slug]/page.tsx — SEO للمقالات الإخبارية
import type { Metadata } from 'next'
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const { slug } = await params
const post = await getPost(slug)
return {
title: `${post.title} - TaskFlow Blog`,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [{ url: post.ogImage, width: 1200, height: 630 }]
}
}
}
(3) النتائج
| المؤشر | قبل التحسين | بعد التحسين | التحسن |
|---|---|---|---|
| معدل فهرسة Google | 10% | 98% | 9.8x |
| نسبة النقر على المشاركة الاجتماعية | 0.3% | 2.8% | 9.3x |
| نتائج البحث الغنية بالوسائط | ❌ لا يوجد | ✅ مسار التنقل + بطاقات المقالات | — |
| سرعة اكتشاف الصفحات | 30 يوماً | < 24 ساعة | 30x |
3. البيانات الوصفية الثابتة والديناميكية
(1) التصدير الثابت
// app/about/page.tsx — بيانات وصفية للصفحة الثابتة
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'حول TaskFlow - منصة التعاون الجماعي',
description: 'TaskFlow تساعد أكثر من 10,000 فريق على العمل بكفاءة عالية، وتوفر إدارة المشاريع والتعاون الفوري والتحليلات الذكية.',
keywords: ['تعاون جماعي', 'إدارة مشاريع', 'TaskFlow', 'SaaS'],
authors: [{ name: 'فريق TaskFlow', url: 'https://taskflow.io' }]
}
(2) التوليد الديناميكي
// app/products/[id]/page.tsx — بيانات وصفية للمنتج
import type { Metadata, ResolvingMetadata } from 'next'
type Props = { params: Promise<{ id: string }>; searchParams: Promise<{ [key: string]: string | string[] | undefined }> }
export async function generateMetadata({ params, searchParams }: Props, parent: ResolvingMetadata): Promise<Metadata> {
const { id } = await params
const product = await fetch(`https://api.taskflow.io/products/${id}`).then(r => r.json())
return {
title: `${product.name} - منتجات TaskFlow`,
description: product.description,
openGraph: {
title: product.name,
description: product.description,
url: `https://taskflow.io/products/${id}`,
siteName: 'TaskFlow',
images: [
{
url: product.ogImage,
width: 1200,
height: 630,
alt: product.name
}
],
locale: 'zh_CN',
type: 'website'
},
twitter: {
card: 'summary_large_image',
title: product.name,
description: product.description,
images: [product.ogImage]
},
alternates: {
canonical: `https://taskflow.io/products/${id}`,
languages: {
'en': `https://taskflow.io/en/products/${id}`,
'ja': `https://taskflow.io/ja/products/${id}`,
'ar': `https://taskflow.io/ar/products/${id}`
}
}
}
}
(3) جدول مرجعي سريع لحقول Metadata
| الحقل | الغرض | مثال |
|---|---|---|
title |
عنوان الصفحة / <title> |
'تفاصيل المنتج - TaskFlow' |
description |
ملخص محرك البحث | 'أداة إدارة المشاريع TaskFlow...' |
openGraph |
مشاركة Facebook / LinkedIn | { title, description, images } |
twitter |
بطاقة X (Twitter) | { card: 'summary_large_image' } |
alternates.canonical |
الرابط المرجعي لمنع المحتوى المكرر | https://taskflow.io/page |
alternates.languages |
hreflang متعدد اللغات | { 'en': '...' } |
robots |
تعليمات الزواحف | { index: true, follow: true } |
▶ مثال: SEO كامل لمقالة مدونة
المخرجات:
تنفذ الدالة غير المتزامنة وتعيد البيانات التي تم جلبها.
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next'
interface Post {
title: string
excerpt: string
ogImage: string
publishedAt: string
author: string
tags: string[]
}
async function getPost(slug: string): Promise<Post> {
const res = await fetch(`https://api.taskflow.io/blog/${slug}`, { next: { revalidate: 3600 } })
return res.json()
}
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const { slug } = await params
const post = await getPost(slug)
return {
title: `${post.title} | TaskFlow Blog`,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
type: 'article',
publishedTime: post.publishedAt,
authors: [post.author],
images: [{ url: post.ogImage, width: 1200, height: 630 }]
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.excerpt,
images: [post.ogImage]
},
keywords: [...post.tags, 'TaskFlow', 'Project Management'],
alternates: { canonical: `https://taskflow.io/blog/${slug}` },
robots: { index: true, follow: true }
}
}
المخرجات:
يُعرّف أنواع TypeScript: Post.
▶ مثال: استخدام وسم robots meta للتحكم في زواحف الويب
المخرجات:
يُعرّف أنواع TypeScript: Post.
// app/private/dashboard/page.tsx — منع محركات البحث من الفهرسة
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'لوحة التحكم - TaskFlow',
robots: {
index: false, // لا تقم بالفهرسة
follow: false, // لا تتبع الروابط
noarchive: true, // لا تقم بالتخزين المؤقت
nosnippet: true, // لا تعرض الملخص
nocache: true // لا تقم بالتخزين المؤقت
}
}
export default function DashboardPage() {
return <h1>لوحة تحكم خاصة</h1>
}
<meta name="robots" content="noindex, nofollow, noarchive, nosnippet" />
المخرجات:
يعرض المتصفح: عنوان "لوحة تحكم خاصة". محركات البحث لن تفهرس هذه الصفحة — وسم <meta name="robots" content="noindex, nofollow, noarchive, nosnippet"> يمنع الزحف.
4. بيانات JSON-LD المنظمة
(1) ثلاثة مخططات شائعة
graph TB
A[بيانات JSON-LD المنظمة] --> B[BreadcrumbList<br/>مسار التنقل]
A --> C[Article<br/>تفاصيل المقالة]
A --> D[FAQPage<br/>الأسئلة الشائعة]
B --> E[عرض مسار نتائج البحث]
C --> F[لوحة المعرفة + صورة الغلاف]
D --> G[عرض الأسئلة والأجوبة مباشرة في نتائج البحث]
style A fill:#cce5ff
style B fill:#d4edda
style C fill:#d4edda
style D fill:#d4edda
| المخطط | حالة الاستخدام | العرض في نتائج البحث |
|---|---|---|
| BreadcrumbList | جميع الصفحات | عرض مسار التنقل (الرئيسية > المنتجات > التفاصيل) |
| Article | مقالة المدونة | بطاقة المقالة (العنوان + الملخص + صورة الغلاف + تاريخ النشر) |
| FAQPage | مركز المساعدة / الأسئلة الشائعة | عرض قائمة الأسئلة والأجوبة (قابلة للطي) |
| Product | صفحة المنتج | السعر + المخزون + التقييم بالنجوم |
(2) دالة حقن JSON-LD
// lib/jsonld.ts — أدوات توليد JSON-LD
export function breadcrumbJsonld(items: { name: string; url: string }[]) {
return {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: items.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.name,
item: item.url
}))
}
}
export function articleJsonld(post: {
title: string
excerpt: string
url: string
ogImage: string
publishedAt: string
author: string
}) {
return {
'@context': 'https://schema.org',
'@type': 'Article',
headline: post.title,
description: post.excerpt,
image: post.ogImage,
datePublished: post.publishedAt,
author: { '@type': 'Person', name: post.author },
publisher: { '@type': 'Organization', name: 'TaskFlow', logo: 'https://taskflow.io/logo.png' },
mainEntityOfPage: { '@type': 'WebPage', '@id': post.url }
}
}
▶ مثال: صفحة مقالة JSON-LD + مسار التنقل
المخرجات:
تنفذ وحدة TypeScript بنجاح.
// app/blog/[slug]/page.tsx — SEO كامل + JSON-LD
import { breadcrumbJsonld, articleJsonld } from '@/lib/jsonld'
export default async function BlogPostPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const post = await getPost(slug)
const url = `https://taskflow.io/blog/${slug}`
const breadcrumb = breadcrumbJsonld([
{ name: 'الرئيسية', url: 'https://taskflow.io' },
{ name: 'المدونة', url: 'https://taskflow.io/blog' },
{ name: post.title, url }
])
const article = articleJsonld({
title: post.title,
excerpt: post.excerpt,
url,
ogImage: post.ogImage,
publishedAt: post.publishedAt,
author: post.author
})
return (
<>
{/* حقن JSON-LD */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumb) }}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(article) }}
/>
<article>
<h1>{post.title}</h1>
<p>{post.excerpt}</p>
<div>{post.content}</div>
</article>
</>
)
}
المخرجات:
يعرض واجهة مكون ▶ مثال: صفحة مقالة JSON-LD + مسار التنقل كما هو موضح في القسم.
5. Sitemap و robots.txt
(1) توليد Sitemap ديناميكي
// app/sitemap.ts — Sitemap ديناميكي (يشمل جميع الصفحات تلقائياً)
import type { MetadataRoute } from 'next'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://taskflow.io'
// الصفحات الثابتة
const staticPages = [
{ url: baseUrl, lastModified: new Date(), changeFrequency: 'monthly' as const, priority: 1.0 },
{ url: `${baseUrl}/about`, lastModified: new Date(), changeFrequency: 'monthly' as const, priority: 0.8 },
{ url: `${baseUrl}/blog`, lastModified: new Date(), changeFrequency: 'weekly' as const, priority: 0.9 },
{ url: `${baseUrl}/pricing`, lastModified: new Date(), changeFrequency: 'monthly' as const, priority: 0.8 },
{ url: `${baseUrl}/contact`, lastModified: new Date(), changeFrequency: 'yearly' as const, priority: 0.5 }
]
// مقالات المدونة الديناميكية (من API)
const posts = await fetch('https://api.taskflow.io/blog/posts').then(r => r.json())
const blogPages = posts.map((post: { slug: string; updatedAt: string }) => ({
url: `${baseUrl}/blog/${post.slug}`,
lastModified: new Date(post.updatedAt),
changeFrequency: 'weekly' as const,
priority: 0.7
}))
// الصفحات متعددة اللغات
const locales = ['en', 'ja', 'ar']
const localizedPages = locales.flatMap((locale) =>
staticPages.map((page) => ({
url: `${baseUrl}/${locale}${page.url.replace(baseUrl, '')}`,
lastModified: page.lastModified,
changeFrequency: page.changeFrequency,
priority: page.priority * 0.9,
alternates: {
languages: Object.fromEntries(
locales.map((l) => [l, `${baseUrl}/${l}${page.url.replace(baseUrl, '')}`])
)
}
}))
)
return [...staticPages, ...blogPages, ...localizedPages]
}
(2) robots.txt
// app/robots.ts
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: ['/api/', '/admin/', '/_next/', '/dashboard']
},
{
userAgent: 'GPTBot',
disallow: '/'
}
],
sitemap: 'https://taskflow.io/sitemap.xml'
}
}
▶ مثال: التحقق من Sitemap متعدد اللغات
# مقتطف من sitemap.xml المُنشأ
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://taskflow.io/about</loc>
<lastmod>2026-07-06</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://taskflow.io/en/about"/>
<xhtml:link rel="alternate" hreflang="ja" href="https://taskflow.io/ja/about"/>
<xhtml:link rel="alternate" hreflang="ar" href="https://taskflow.io/ar/about"/>
</url>
</urlset>
6. توليد صور OG ديناميكية
(1) بنية @vercel/og
graph LR
A[مشاركة الرابط على وسائل التواصل] --> B[زاحف الويب يطلب صورة OG]
B --> C[@vercel/og دالة Edge]
C --> D[Satori + React]
D --> E[تحويل JSX إلى PNG]
E --> F[صورة OG بحجم 1200×630]
F --> G[Facebook / X / LinkedIn تعرض]
style C fill:#cce5ff
style D fill:#d4edda
style E fill:#fff3cd
| المكتبة | الوظيفة | الوصف |
|---|---|---|
@vercel/og |
توليد صورة OG عبر دالة Edge | صفر حمل على الخادم |
Satori |
تحويل JSX → SVG | أقل من 1 مللي ثانية |
resvg-wasm |
عرض SVG → PNG | ~5 مللي ثانية |
(2) التثبيت والأمثلة الأساسية
npm install @vercel/og
// app/og/route.tsx — API توليد صورة OG
import { ImageResponse } from '@vercel/og'
export const runtime = 'edge'
export async function GET() {
return new ImageResponse(
(
<div style={{
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
background: 'linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%)',
color: 'white',
fontSize: 60,
fontWeight: 700,
padding: 40
}}>
<h1>TaskFlow</h1>
<p style={{ fontSize: 32, opacity: 0.9 }}>منصة التعاون الجماعي</p>
</div>
),
{ width: 1200, height: 630 }
)
}
▶ مثال: صورة OG ديناميكية للمقالة
المخرجات:
يعرض واجهة مكون GET.
// app/blog/[slug]/og/route.tsx — صورة OG للمقالات المميزة
import { ImageResponse } from '@vercel/og'
export const runtime = 'edge'
export async function GET(req: Request, { params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
// جلب معلومات المقالة
const post = await fetch(`https://api.taskflow.io/blog/${slug}`).then(r => r.json())
return new ImageResponse(
(
<div style={{
width: 1200,
height: 630,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
padding: 60,
background: 'linear-gradient(135deg, #1e1b4b 0%, #4f46e5 100%)',
color: 'white'
}}>
{/* الوسوم */}
<div style={{ display: 'flex', gap: 8 }}>
{post.tags?.slice(0, 3).map((tag: string) => (
<span key={tag} style={{
padding: '4px 12px',
borderRadius: 20,
background: 'rgba(255,255,255,0.2)',
fontSize: 18
}}>{tag}</span>
))}
</div>
{/* العنوان */}
<h1 style={{ fontSize: 52, margin: '20px 0', lineHeight: 1.2 }}>
{post.title}
</h1>
{/* المؤلف + التاريخ */}
<div style={{ display: 'flex', gap: 16, fontSize: 22, opacity: 0.8 }}>
<span>{post.author}</span>
<span>{new Date(post.publishedAt).toLocaleDateString('zh-CN')}</span>
</div>
{/* الشعار */}
<div style={{ position: 'absolute', bottom: 40, right: 60, fontSize: 28, fontWeight: 'bold' }}>
TaskFlow Blog
</div>
</div>
),
{ width: 1200, height: 630 }
)
}
المخرجات:
يجلب البيانات من جانب الخادم ويعرض قائمة من العناصر من post.
المحتوى المرئي: ))} | TaskFlow Blog
/blog/[slug]/og/route.tsx، والرابط النهائي هو https://taskflow.io/blog/nextjs-seo-guide/og. ببساطة قم بالإشارة إلى هذا العنوان في generateMetadata.
7. مثال كامل: تنفيذ شامل لـ SEO متعدد اللغات + صور OG
// app/layout.tsx — التخطيط الجذري (الافتراضي) SEO + متعدد اللغات
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: { template: '%s | TaskFlow', default: 'TaskFlow - منصة التعاون الجماعي' },
description: 'TaskFlow تساعد أكثر من 10,000 فريق حول العالم على العمل بكفاءة عالية. توفر إدارة المشاريع والتعاون الفوري والتحليل الذكي بالذكاء الاصطناعي.',
openGraph: {
siteName: 'TaskFlow',
type: 'website',
locale: 'zh_CN',
images: [{ url: 'https://taskflow.io/og-default.png', width: 1200, height: 630 }]
},
twitter: { card: 'summary_large_image', site: '@taskflow' },
robots: { index: true, follow: true },
alternates: {
canonical: 'https://taskflow.io',
languages: {
'en': 'https://taskflow.io/en',
'ja': 'https://taskflow.io/ja',
'ar': 'https://taskflow.io/ar'
}
}
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="zh">
<body>{children}</body>
</html>
)
}
// app/sitemap.ts — Sitemap كامل
import type { MetadataRoute } from 'next'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://taskflow.io'
const locales = ['zh', 'en', 'ja', 'ar'] as const
const pages = ['', '/about', '/blog', '/pricing', '/contact']
return pages.flatMap((page) =>
locales.map((locale) => ({
url: `${baseUrl}/${locale}${page}`,
lastModified: new Date(),
changeFrequency: 'monthly' as const,
priority: page === '' ? 1.0 : 0.8,
alternates: {
languages: Object.fromEntries(locales.map((l) => [l, `${baseUrl}/${l}${page}`]))
}
}))
)
}
// app/robots.ts
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: { userAgent: '*', allow: '/', disallow: ['/api/', '/admin/', '/dashboard'] },
sitemap: 'https://taskflow.io/sitemap.xml'
}
}
// app/blog/[slug]/page.tsx — صفحة المقالة تجمع كل SEO
import type { Metadata } from 'next'
type Props = { params: Promise<{ slug: string }> }
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params
const post = await fetch(`https://api.taskflow.io/blog/${slug}`).then(r => r.json())
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
type: 'article',
publishedTime: post.publishedAt,
authors: [post.author],
images: [{ url: `https://taskflow.io/blog/${slug}/og`, width: 1200, height: 630 }]
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.excerpt,
images: [`https://taskflow.io/blog/${slug}/og`]
},
alternates: {
canonical: `https://taskflow.io/blog/${slug}`,
languages: {
en: `https://taskflow.io/en/blog/${slug}`,
ja: `https://taskflow.io/ja/blog/${slug}`,
ar: `https://taskflow.io/ar/blog/${slug}`
}
}
}
}
export default async function BlogPostPage({ params }: Props) {
const { slug } = await params
return (
<article>
{/* بيانات JSON-LD المنظمة */}
<script type="application/ld+json" dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'الرئيسية', item: 'https://taskflow.io' },
{ '@type': 'ListItem', position: 2, name: 'المدونة', item: 'https://taskflow.io/blog' },
{ '@type': 'ListItem', position: 3, name: slug }
]
})
}} />
<h1>عنوان المقالة</h1>
<div>محتوى المقالة...</div>
</article>
)
}
❓ أسئلة شائعة
generateMetadata() و metadata؟metadata هو تصدير ثابت يُستخدم للتوجيه الثابت (مثل /about). أما generateMetadata() فهي دالة غير متزامنة تولد البيانات الوصفية ديناميكياً بناءً على params / searchParams وتُستخدم للتوجيه الديناميكي (مثل /blog/[slug]). لا يمكن استخدام الاثنين معاً في وقت واحد.<title> / <meta name="description">) هي وسوم SEO أساسية. أما JSON-LD (<script type="application/ld+json">) فهي بيانات منظمة تمكن Google من عرض المقتطفات الغنية (مسارات التنقل، التقييمات بالنجوم، الأسئلة الشائعة القابلة للطي). الاثنان متكاملان ويجب تنفيذهما معاً.@vercel/og يعمل على Edge Runtime (الحوسبة الطرفية)، وتستغرق كل صورة حوالي 5 مللي ثانية فقط للتوليد، مما يؤدي إلى حمل شبه معدوم. في بيئة الإنتاج، نوصي بتمكين التخزين المؤقت عبر CDN (Cache-Control: public, max-age=31536000, immutable).generateMetadata() عند alternates.languages. Next.js يقوم تلقائياً بحقن <link rel="alternate" hreflang="en" href="..."> في <head> الصفحة. تحتاج أيضاً إلى تضمين وسم xhtml:link المقابل في Sitemap.changeFrequency و priority في Sitemap يؤثران على ترتيب Google؟lastModified دقيقاً — فهذا مهم للزواحف لتحديد ما إذا كانت ستعيد الزحف إلى الصفحات.📖 ملخص
generateMetadata()تنفذ SEO ديناميكياً، وتدعم جميع الحقول: العنوان والوصف وOpenGraph و Twitter- بيانات JSON-LD المنظمة (BreadcrumbList / Article / FAQPage) تمكّن نتائج البحث من عرض مقتطفات غنية
- ملفات Sitemap و robots.txt يتم توليدها ديناميكياً عبر
app/sitemap.ts/app/robots.tsوتدعم روابط بديلة متعددة اللغات - قم بإعداد hreflang لـ SEO متعدد اللغات في
alternates.languagesلضمان عرض محركات البحث لإصدار اللغة الصحيح للمستخدمين في المناطق المقابلة @vercel/og+ Satori يولدان صور OG بحجم 1200×630 ديناميكياً في Edge Runtime، مما يتيح لكل مقالة صورة معاينة فريدة للمشاركة- استراتيجية SEO الشاملة: Metadata (أساسي) + JSON-LD (محسّن) + Sitemap (اكتشاف) + صورة OG (اجتماعي)
📝 تمارين
-
سؤال أساسي (⭐): قم بإعداد تصدير
metadataفي المسار الثابت/about، متضمناً العنوان والوصف وOpen Graph وبطاقة Twitter. -
تمرين متقدم (⭐⭐): قم بتنفيذ SEO كامل لصفحة تفاصيل خبر:
generateMetadata()تولد العنوان والوصف ووسوم OG وبطاقة Twitter ديناميكياً، وحقن بيانات Article JSON-LD المنظمة، وإدراج جميع روابط المقالات في Sitemap. -
تحدٍ (⭐⭐⭐): قم ببناء نظام كامل لتوليد صور OG:
@vercel/ogيولّد صورة مشاركة بحجم 1200×630 لكل مقالة مدونة، متضمنة العنوان والمؤلف والوسوم؛ وقم بالإشارة إلى رابط الصورة فيgenerateMetadata()؛ وأخيراً، استخدم أداة Google Rich Results Test للتحقق من صحة JSON-LD ووسوم OG.