feat(seo): complete OG/Twitter/structured-data coverage + clean encoding
Blog list (/blog): add robots, full Open Graph + Twitter, Blog + BreadcrumbList JSON-LD, per-page self-canonical, and rel=prev/next for paginated pages. Blog post: add robots, og:site_name, article:published_time / modified_time / author / section, twitter:image, og:image:alt, and a BreadcrumbList JSON-LD (Home → Blog → Category → Post). Gallery (/gallery): add robots, full OG + Twitter (with first image as og:image), ImageGallery + BreadcrumbList JSON-LD. Encoding: register HtmlEncoder.Create(UnicodeRanges.All) so Persian text in meta tags and JSON-LD renders literally instead of &#xXXXX; entities (smaller, cleaner output; friendlier to SEO validators). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -18,17 +18,28 @@
|
||||
@if (!string.IsNullOrEmpty(ViewData["Keywords"]?.ToString())) {
|
||||
<meta name="keywords" content="@ViewData["Keywords"]" />
|
||||
}
|
||||
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1" />
|
||||
<meta name="author" content="@post.Author" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:site_name" content="@ViewData["SiteName"]" />
|
||||
<meta property="og:title" content="@ViewData["Title"]" />
|
||||
<meta property="og:description" content="@ViewData["MetaDesc"]" />
|
||||
<meta property="og:url" content="@canonicalUrl" />
|
||||
<meta property="og:locale" content="fa_IR" />
|
||||
@if (!string.IsNullOrEmpty(pubDate)) { <meta property="article:published_time" content="@pubDate" /> }
|
||||
<meta property="article:modified_time" content="@updDate" />
|
||||
<meta property="article:author" content="@post.Author" />
|
||||
@if (post.Category != null) { <meta property="article:section" content="@post.Category.Name" /> }
|
||||
@if (!string.IsNullOrEmpty(ogImage)) {
|
||||
<meta property="og:image" content="@(ogImage.StartsWith("http") ? ogImage : baseUrl + ogImage)" />
|
||||
<meta property="og:image" content="@(ogImage.StartsWith("http") ? ogImage : baseUrl + ogImage)" />
|
||||
<meta property="og:image:alt" content="@post.Title" />
|
||||
}
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="@ViewData["Title"]" />
|
||||
<meta name="twitter:description" content="@ViewData["MetaDesc"]" />
|
||||
@if (!string.IsNullOrEmpty(ogImage)) {
|
||||
<meta name="twitter:image" content="@(ogImage.StartsWith("http") ? ogImage : baseUrl + ogImage)" />
|
||||
}
|
||||
<link rel="canonical" href="@canonicalUrl" />
|
||||
|
||||
<script type="application/ld+json">
|
||||
@@ -52,6 +63,24 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@@context": "https://schema.org",
|
||||
"@@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{ "@@type": "ListItem", "position": 1, "name": "خانه", "item": "@baseUrl/" },
|
||||
{ "@@type": "ListItem", "position": 2, "name": "وبلاگ", "item": "@baseUrl/blog" }@(post.Category != null ? "," : "")
|
||||
@if (post.Category != null) {
|
||||
@:{ "@@type": "ListItem", "position": 3, "name": "@J(post.Category.Name)", "item": "@baseUrl/blog?category=@post.Category.Slug" },
|
||||
@:{ "@@type": "ListItem", "position": 4, "name": "@J(post.Title)", "item": "@canonicalUrl" }
|
||||
}
|
||||
else {
|
||||
@:{ "@@type": "ListItem", "position": 3, "name": "@J(post.Title)", "item": "@canonicalUrl" }
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* ─── Post Layout ──────────────────────────────────────────────── */
|
||||
.post-layout{max-width:1100px;margin:0 auto;padding:5rem 2rem 3rem;display:grid;grid-template-columns:1fr 320px;gap:3rem;align-items:start}
|
||||
|
||||
Reference in New Issue
Block a user