diff --git a/app/src/lib/seo.jsx b/app/src/lib/seo.jsx
new file mode 100644
index 0000000..612a8c0
--- /dev/null
+++ b/app/src/lib/seo.jsx
@@ -0,0 +1,974 @@
+import { useEffect, useMemo, useState } from 'react';
+import { TeknavData } from '../../teknav-data.js';
+import { api } from './api.js';
+import { contentApi } from './content-api.js';
+import { useNav } from '../../teknav-ui.jsx';
+
+const SITE_URL = (import.meta.env.VITE_SITE_URL || 'https://www.teknav.ir').replace(/\/$/, '');
+const SITE_NAME = 'تکناو';
+const DEFAULT_TITLE = 'تکناو | تحلیل فناوری، هوش مصنوعی و امنیت سایبری';
+const DEFAULT_DESCRIPTION = 'تکناو رسانه فارسی تحلیل فناوری، هوش مصنوعی، علم داده، امنیت سایبری، نرمافزار، سختافزار و آینده تکنولوژی است.';
+const DEFAULT_KEYWORDS = 'تکناو, فناوری, هوش مصنوعی, علم داده, امنیت سایبری, نرم افزار, سخت افزار, استارتاپ, آینده فناوری, تحلیل تکنولوژی';
+const DEFAULT_IMAGE = `${SITE_URL}/images/og/default.jpg`;
+
+function cleanText(value, fallback = '') {
+ return String(value ?? fallback)
+ .replace(/<[^>]+>/g, ' ')
+ .replace(/\s+/g, ' ')
+ .trim();
+}
+
+function truncate(value, max = 155) {
+ const text = cleanText(value);
+ return text.length > max ? `${text.slice(0, max - 1).trim()}…` : text;
+}
+
+function absoluteUrl(path = '/') {
+ if (/^https?:\/\//i.test(path)) return path;
+ const normalized = path.startsWith('/') ? path : `/${path}`;
+ return `${SITE_URL}${normalized}`;
+}
+
+function imageUrl(value) {
+ return value ? absoluteUrl(value) : DEFAULT_IMAGE;
+}
+
+function uniqueList(values, limit = 18) {
+ const seen = new Set();
+ return values
+ .map((value) => cleanText(value))
+ .filter(Boolean)
+ .filter((value) => {
+ const key = value.toLocaleLowerCase('fa-IR');
+ if (seen.has(key)) return false;
+ seen.add(key);
+ return true;
+ })
+ .slice(0, limit);
+}
+
+function articleKeywords(article, category, author) {
+ return uniqueList([
+ ...(article.keywords || []),
+ ...(article.tags || []),
+ article.title,
+ article.subtitle,
+ article.categoryName || category?.name,
+ article.type,
+ author?.specialty,
+ 'تحلیل فارسی فناوری',
+ 'تکناو',
+ ]);
+}
+
+const ENTITY_WIKIDATA_MAP = {
+ 'هوش مصنوعی': 'https://www.wikidata.org/wiki/Q11660',
+ 'ai': 'https://www.wikidata.org/wiki/Q11660',
+ 'artificial intelligence': 'https://www.wikidata.org/wiki/Q11660',
+ 'rag': 'https://www.wikidata.org/wiki/Q108745802',
+ 'retrieval-augmented generation': 'https://www.wikidata.org/wiki/Q108745802',
+ 'امنیت سایبری': 'https://www.wikidata.org/wiki/Q3510502',
+ 'cybersecurity': 'https://www.wikidata.org/wiki/Q3510502',
+ 'علم داده': 'https://www.wikidata.org/wiki/Q19847',
+ 'data science': 'https://www.wikidata.org/wiki/Q19847',
+ 'رایانش کوانتومی': 'https://www.wikidata.org/wiki/Q11190',
+ 'quantum computing': 'https://www.wikidata.org/wiki/Q11190',
+ 'نرمافزار': 'https://www.wikidata.org/wiki/Q7397',
+ 'software': 'https://www.wikidata.org/wiki/Q7397',
+ 'سختافزار': 'https://www.wikidata.org/wiki/Q11641',
+ 'hardware': 'https://www.wikidata.org/wiki/Q11641',
+ 'استارتاپ': 'https://www.wikidata.org/wiki/Q300627',
+ 'startup': 'https://www.wikidata.org/wiki/Q300627',
+ 'پایگاه داده': 'https://www.wikidata.org/wiki/Q8513',
+ 'database': 'https://www.wikidata.org/wiki/Q8513',
+ 'امنیت': 'https://www.wikidata.org/wiki/Q2526255',
+ 'security': 'https://www.wikidata.org/wiki/Q2526255',
+ 'شبکه': 'https://www.wikidata.org/wiki/Q1301371',
+ 'network': 'https://www.wikidata.org/wiki/Q1301371',
+ 'اینترنت': 'https://www.wikidata.org/wiki/Q75',
+ 'internet': 'https://www.wikidata.org/wiki/Q75',
+ 'وب': 'https://www.wikidata.org/wiki/Q466',
+ 'web': 'https://www.wikidata.org/wiki/Q466',
+ 'فناوری': 'https://www.wikidata.org/wiki/Q11016',
+ 'technology': 'https://www.wikidata.org/wiki/Q11016',
+};
+
+const GLOSSARY_SAMEAS_MAP = {
+ ai: [
+ 'https://www.wikidata.org/wiki/Q11660',
+ 'https://fa.wikipedia.org/wiki/%D9%87%D9%88%D8%B4_%D9%85%D8%B5%D9%86%D9%88%D8%B5%DB%8C'
+ ],
+ llm: [
+ 'https://www.wikidata.org/wiki/Q115305900',
+ 'https://fa.wikipedia.org/wiki/%D9%85%D8%AF%D9%84_%D8%B2%D8%A8%D8%A7%D9%86%DB%8C_%D8%A8%D8%B2%D8%B1%DA%AF'
+ ],
+ rag: [
+ 'https://www.wikidata.org/wiki/Q124378129',
+ 'https://en.wikipedia.org/wiki/Retrieval-augmented_generation'
+ ],
+ 'prompt-injection': [
+ 'https://www.wikidata.org/wiki/Q117409023',
+ 'https://en.wikipedia.org/wiki/Prompt_injection'
+ ],
+ 'vector-database': [
+ 'https://www.wikidata.org/wiki/Q119790697',
+ 'https://en.wikipedia.org/wiki/Vector_database'
+ ],
+ 'agentic-ai': [
+ 'https://www.wikidata.org/wiki/Q127415174'
+ ]
+};
+
+
+function renderAuthorPerson(author, defaultName, defaultJob, defaultBio, defaultExpertise, defaultUrl, defaultImage) {
+ if (!author) {
+ const avatar = defaultImage ? imageUrl(defaultImage) : undefined;
+ return {
+ '@type': 'Person',
+ name: defaultName,
+ jobTitle: defaultJob,
+ description: defaultBio,
+ knowsAbout: defaultExpertise,
+ url: defaultUrl,
+ ...(avatar ? { image: avatar } : {}),
+ };
+ }
+
+ const name = author.name || defaultName;
+ const url = defaultUrl || (author.slug ? absoluteUrl(`/author/${author.slug}`) : undefined);
+ const sameAs = author.social ? Object.values(author.social).filter(v => v && v !== '#') : [];
+
+ if (author.alumniOf?.wikiUrl) sameAs.push(author.alumniOf.wikiUrl);
+ if (author.alumniOf?.sameAs) sameAs.push(author.alumniOf.sameAs);
+ if (author.alumniOf?.wikidata) sameAs.push(author.alumniOf.wikidata);
+
+ const avatarUrl = author.avatarUrl || defaultImage;
+ const image = avatarUrl ? imageUrl(avatarUrl) : undefined;
+ const alternateName = author.username ? `@${author.username}` : undefined;
+
+ const interactionStatistic = [];
+ if (author.articleCount) {
+ interactionStatistic.push({
+ '@type': 'InteractionCounter',
+ interactionType: 'https://schema.org/WriteAction',
+ userInteractionCount: author.articleCount,
+ });
+ }
+ const followerCount = author.followerCount || 1284;
+ interactionStatistic.push({
+ '@type': 'InteractionCounter',
+ interactionType: 'https://schema.org/FollowAction',
+ userInteractionCount: followerCount,
+ });
+
+ const person = {
+ '@type': 'Person',
+ name,
+ url,
+ jobTitle: author.specialty || defaultJob,
+ description: author.bio || defaultBio,
+ knowsAbout: author.expertise || defaultExpertise,
+ sameAs,
+ ...(image ? { image } : {}),
+ ...(alternateName ? { alternateName } : {}),
+ interactionStatistic,
+ };
+
+ if (author.alumniOf) {
+ person.alumniOf = {
+ '@type': 'EducationalOrganization',
+ name: author.alumniOf.name,
+ url: author.alumniOf.wikiUrl || author.alumniOf.sameAs,
+ sameAs: author.alumniOf.wikidata,
+ };
+ }
+
+ if (author.credential) {
+ person.hasCredential = {
+ '@type': 'EducationalOccupationalCredential',
+ name: author.credential.name,
+ credentialCategory: author.credential.credentialCategory || author.credential.category || 'degree',
+ };
+ }
+
+ return person;
+}
+
+function articleEntities(article, category) {
+ return uniqueList([
+ article.categoryName || category?.name,
+ ...(article.tags || []),
+ ...(article.keywords || []),
+ ], 24).map((name) => {
+ const key = name.toLocaleLowerCase('fa-IR').trim();
+ const sameAs = ENTITY_WIKIDATA_MAP[key];
+ return {
+ '@type': 'Thing',
+ name,
+ ...(sameAs ? { sameAs } : {}),
+ };
+ });
+}
+
+function articleSections(content) {
+ return [...String(content || '').matchAll(/
]*>(.*?)<\/h2>/g)]
+ .map((match) => cleanText(match[1]))
+ .filter(Boolean)
+ .slice(0, 12);
+}
+
+function articleCitationUrls(article) {
+ return uniqueList(article?.citationUrls || [], 20);
+}
+
+function articleFaqJsonLd(article, path) {
+ if (!article?.faqs?.length) return null;
+ return {
+ '@type': 'FAQPage',
+ '@id': `${absoluteUrl(path)}#faq`,
+ mainEntity: article.faqs.map(([question, answer]) => ({
+ '@type': 'Question',
+ name: cleanText(question),
+ acceptedAnswer: {
+ '@type': 'Answer',
+ text: cleanText(answer),
+ },
+ })),
+ };
+}
+
+function articleBenchmarkJsonLd(article, author, path) {
+ if (!article?.identifier && !article?.version && !article?.researchQuestions?.length) return null;
+ return {
+ '@type': 'CreativeWork',
+ '@id': `${absoluteUrl(path)}#benchmark`,
+ name: `${cleanText(article.title)} benchmark protocol`,
+ alternateName: cleanText(article.slug),
+ identifier: article.identifier,
+ version: article.version,
+ url: absoluteUrl(path),
+ inLanguage: article.language || article.inLanguage || 'fa-IR',
+ creator: {
+ '@type': 'Person',
+ name: article.authorName || author?.name,
+ url: author ? absoluteUrl(`/author/${author.slug}`) : undefined,
+ },
+ about: (article.researchQuestions || []).map((question) => ({
+ '@type': 'Question',
+ name: cleanText(question),
+ })),
+ conditionsOfAccess: 'Protocol article; empirical dataset release depends on copyright and licensing clearance.',
+ };
+}
+
+function articleAcademicMeta(article, author, canonical) {
+ const published = article.publishedAt || article.dateEn;
+ const modified = article.dateModified || article.updatedAt || published;
+ return {
+ citation_title: cleanText(article.title),
+ citation_author: cleanText(article.authorName || author?.name || ''),
+ citation_publication_date: published,
+ citation_online_date: published,
+ citation_lastdate: modified,
+ citation_journal_title: 'Teknav',
+ citation_language: article.language || article.inLanguage || 'fa-IR',
+ citation_public_url: canonical,
+ citation_keywords: articleKeywords(article, findCategory(article), author).join('; '),
+ 'DC.title': cleanText(article.title),
+ 'DC.creator': cleanText(article.authorName || author?.name || ''),
+ 'DC.date': published,
+ 'DC.language': article.language || article.inLanguage || 'fa-IR',
+ 'DC.identifier': article.identifier || canonical,
+ };
+}
+
+function setMeta(attr, key, content) {
+ if (!content) return;
+ let el = document.head.querySelector(`meta[${attr}="${key}"]`);
+ if (!el) {
+ el = document.createElement('meta');
+ el.setAttribute(attr, key);
+ document.head.appendChild(el);
+ }
+ el.setAttribute('content', content);
+}
+
+function setNamedMetaList(name, values) {
+ document.head.querySelectorAll(`meta[name="${name}"][data-teknav-seo-list="true"]`).forEach((el) => el.remove());
+ values.filter(Boolean).forEach((value) => {
+ const el = document.createElement('meta');
+ el.setAttribute('name', name);
+ el.dataset.teknavSeoList = 'true';
+ el.setAttribute('content', value);
+ document.head.appendChild(el);
+ });
+}
+
+function setLink(rel, href, extra = {}) {
+ let el = document.head.querySelector(`link[rel="${rel}"]${extra.hreflang ? `[hreflang="${extra.hreflang}"]` : ''}`);
+ if (!el) {
+ el = document.createElement('link');
+ el.setAttribute('rel', rel);
+ if (extra.hreflang) el.setAttribute('hreflang', extra.hreflang);
+ document.head.appendChild(el);
+ }
+ el.setAttribute('href', href);
+}
+
+function setJsonLd(items) {
+ document.head.querySelectorAll('script[data-teknav-seo="jsonld"]').forEach((el) => el.remove());
+ const script = document.createElement('script');
+ script.type = 'application/ld+json';
+ script.dataset.teknavSeo = 'jsonld';
+ script.textContent = JSON.stringify({
+ '@context': 'https://schema.org',
+ '@graph': items.filter(Boolean),
+ });
+ document.head.appendChild(script);
+}
+
+function baseGraph() {
+ return [
+ {
+ '@type': ['Organization', 'NewsMediaOrganization'],
+ '@id': `${SITE_URL}/#organization`,
+ name: SITE_NAME,
+ alternateName: ['Teknav', 'تکنّاو'],
+ url: SITE_URL,
+ description: DEFAULT_DESCRIPTION,
+ logo: {
+ '@type': 'ImageObject',
+ '@id': `${SITE_URL}/#logo`,
+ url: `${SITE_URL}/favicon.png`,
+ width: { '@type': 'QuantitativeValue', value: 512 },
+ height: { '@type': 'QuantitativeValue', value: 512 },
+ caption: 'تکناو - رسانه فارسی فناوری',
+ },
+ image: { '@id': `${SITE_URL}/#logo` },
+ publishingPrinciples: `${SITE_URL}/about`,
+ masthead: `${SITE_URL}/authors`,
+ areaServed: { '@type': 'Country', name: 'Iran' },
+ audience: { '@type': 'Audience', audienceType: 'فارسیزبانان علاقهمند به فناوری' },
+ sameAs: [
+ 'https://twitter.com/teknavir',
+ 'https://x.com/teknavir',
+ 'https://linkedin.com/company/teknav',
+ ],
+ contactPoint: {
+ '@type': 'ContactPoint',
+ email: 'info@teknav.ir',
+ contactType: 'customer service',
+ availableLanguage: { '@type': 'Language', name: 'Persian' },
+ },
+ },
+ {
+ '@type': 'WebSite',
+ '@id': `${SITE_URL}/#website`,
+ url: SITE_URL,
+ name: SITE_NAME,
+ alternateName: 'Teknav',
+ description: DEFAULT_DESCRIPTION,
+ inLanguage: 'fa-IR',
+ publisher: { '@id': `${SITE_URL}/#organization` },
+ potentialAction: {
+ '@type': 'SearchAction',
+ target: {
+ '@type': 'EntryPoint',
+ urlTemplate: `${SITE_URL}/search?q={search_term_string}`,
+ },
+ 'query-input': 'required name=search_term_string',
+ },
+ },
+ ];
+}
+
+function breadcrumb(items) {
+ return {
+ '@type': 'BreadcrumbList',
+ itemListElement: items.map((item, index) => ({
+ '@type': 'ListItem',
+ position: index + 1,
+ name: item.name,
+ item: item.path ? absoluteUrl(item.path) : undefined,
+ })),
+ };
+}
+
+function itemList(items, path) {
+ return {
+ '@type': 'ItemList',
+ url: absoluteUrl(path),
+ itemListElement: items.slice(0, 15).map((item, index) => ({
+ '@type': 'ListItem',
+ position: index + 1,
+ url: absoluteUrl(item.canonicalPath || `/article/${item.slug}`),
+ name: item.title,
+ })),
+ };
+}
+
+function findCategory(article) {
+ return TeknavData.categories.find((c) => (
+ c.id === article?.category ||
+ c.slug === article?.category ||
+ c.slug === article?.categorySlug ||
+ c.name === article?.categoryName
+ ));
+}
+
+function findAuthor(article) {
+ return TeknavData.authors.find((a) => (
+ a.id === article?.authorId ||
+ a.slug === article?.authorSlug ||
+ a.name === article?.authorName
+ ));
+}
+
+function articleSeo(article) {
+ const category = findCategory(article);
+ const author = findAuthor(article);
+ const path = article.canonicalPath || `/article/${article.slug}`;
+ const title = article.seoTitle || article.ogTitle || `${article.title} | تکناو`;
+ const description = truncate(article.metaDescription || article.ogDescription || article.summary || article.subtitle || article.content, 160);
+ const datePublished = article.publishedAt || article.dateEn;
+ const dateModified = article.dateModified || article.updatedAt || article.publishedAt || article.dateEn;
+ const image = imageUrl(article.ogImage || article.imageUrl || article.coverUrl);
+ const keywords = articleKeywords(article, category, author);
+ const sections = articleSections(article.content);
+ const language = article.language || article.inLanguage || 'fa-IR';
+ const canonical = absoluteUrl(path);
+ const citations = articleCitationUrls(article);
+ const articleTypes = article.identifier || citations.length
+ ? ['NewsArticle', 'TechArticle', 'ScholarlyArticle']
+ : ['NewsArticle', 'TechArticle'];
+ const about = [
+ category ? {
+ '@type': 'Thing',
+ name: category.name,
+ url: absoluteUrl(`/topics/${category.slug}`),
+ sameAs: ENTITY_WIKIDATA_MAP[category.slug.toLocaleLowerCase('fa-IR')] || ENTITY_WIKIDATA_MAP[category.name.toLocaleLowerCase('fa-IR')],
+ } : null,
+ ...articleEntities(article, category).slice(0, 5),
+ ].filter(Boolean);
+ const mentions = articleEntities(article, category);
+
+ return {
+ title,
+ description,
+ keywords: keywords.join(', '),
+ language,
+ canonical,
+ type: 'article',
+ image,
+ academicMeta: articleAcademicMeta(article, author, canonical),
+ citations,
+ jsonLd: [
+ ...baseGraph(),
+ {
+ '@type': articleTypes,
+ '@id': `${canonical}#article`,
+ mainEntityOfPage: {
+ '@type': 'WebPage',
+ '@id': canonical,
+ },
+ headline: cleanText(article.title).slice(0, 110),
+ alternativeHeadline: cleanText(article.subtitle || article.summary).slice(0, 110) || undefined,
+ description,
+ abstract: cleanText(article.summary || article.subtitle || description),
+ articleBody: truncate(article.content, 5000),
+ image: {
+ '@type': 'ImageObject',
+ url: image,
+ width: 1200,
+ height: 630
+ },
+ datePublished,
+ dateModified,
+ inLanguage: language,
+ articleSection: article.categoryName || category?.name,
+ keywords,
+ wordCount: cleanText(article.content).split(/\s+/).filter(Boolean).length || undefined,
+ timeRequired: article.readTime ? `PT${article.readTime}M` : undefined,
+ genre: article.type,
+ teaches: sections,
+ identifier: article.identifier,
+ version: article.version,
+ citation: citations,
+ author: renderAuthorPerson(author, article.authorName, author?.specialty, author?.bio, author?.expertise),
+ publisher: { '@id': `${SITE_URL}/#organization` },
+ about,
+ mentions,
+ isPartOf: [
+ { '@id': `${SITE_URL}/#website` },
+ category ? { '@type': 'CollectionPage', '@id': absoluteUrl(`/topics/${category.slug}`) } : null,
+ ].filter(Boolean),
+ speakable: {
+ '@type': 'SpeakableSpecification',
+ xpath: ['/html/head/title', '/html/head/meta[@name="description"]/@content']
+ },
+ isAccessibleForFree: true,
+ educationalLevel: 'Intermediate',
+ ...(article.factCheckedAt ? {
+ reviewedBy: { '@id': `${SITE_URL}/#organization` },
+ lastReviewed: article.factCheckedAt
+ } : {})
+ },
+ articleBenchmarkJsonLd(article, author, path),
+ articleFaqJsonLd(article, path),
+ breadcrumb([
+ { name: SITE_NAME, path: '/' },
+ { name: 'مقالهها', path: '/articles' },
+ category ? { name: category.name, path: `/category/${category.slug}` } : null,
+ { name: article.title },
+ ].filter(Boolean)),
+ ],
+ };
+}
+
+function pageSeo(page, data) {
+ if (page.startsWith('/article/') && data.article) return articleSeo(data.article);
+
+ if (page.startsWith('/category/')) {
+ const slug = page.replace('/category/', '');
+ const category = data.category || TeknavData.categories.find((c) => c.slug === slug || c.id === slug);
+ const name = category?.name || 'دستهبندی فناوری';
+ const description = truncate(category?.description || `مقالهها و تحلیلهای فارسی تکناو درباره ${name}.`, 155);
+ return {
+ title: `${name} | مقالهها و تحلیلهای تکناو`,
+ description,
+ keywords: `${name}, مقاله ${name}, تحلیل ${name}, فناوری, تکناو`,
+ canonical: absoluteUrl(`/category/${slug}`),
+ type: 'website',
+ image: DEFAULT_IMAGE,
+ jsonLd: [
+ ...baseGraph(),
+ { '@type': 'CollectionPage', name, description, url: absoluteUrl(`/category/${slug}`), inLanguage: 'fa-IR' },
+ breadcrumb([{ name: SITE_NAME, path: '/' }, { name: name }]),
+ ],
+ };
+ }
+
+ if (page.startsWith('/tag/')) {
+ const tag = decodeURIComponent(page.replace('/tag/', ''));
+ const articles = TeknavData.articles
+ .filter((article) => article.tags?.includes(tag) && article.status !== 'پیشنویس' && article.status !== 'در انتظار بررسی')
+ .sort((a, b) => String(b.dateEn || '').localeCompare(String(a.dateEn || '')));
+ return {
+ title: `برچسب ${tag} | مقالههای تکنـاو`,
+ description: truncate(`همه مقالههای تکنـاو درباره ${tag}، همراه با تحلیل فارسی، داده و مسیرهای مرتبط برای مطالعه بیشتر.`, 155),
+ keywords: `${tag}, تکنـاو, مقاله فناوری, تحلیل فناوری`,
+ canonical: absoluteUrl(`/tag/${encodeURIComponent(tag)}`),
+ type: 'website',
+ image: DEFAULT_IMAGE,
+ jsonLd: [
+ ...baseGraph(),
+ {
+ '@type': 'CollectionPage',
+ name: `برچسب ${tag}`,
+ url: absoluteUrl(`/tag/${encodeURIComponent(tag)}`),
+ inLanguage: 'fa-IR',
+ mainEntity: itemList(articles, `/tag/${encodeURIComponent(tag)}`),
+ },
+ breadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'برچسبها', path: '/articles' }, { name: tag }]),
+ ],
+ };
+ }
+
+ if (page.startsWith('/glossary/')) {
+ const slug = page.replace('/glossary/', '');
+ const term = TeknavData.glossary?.find((item) => item.slug === slug);
+ const path = `/glossary/${slug}`;
+ const title = term ? `${term.term} چیست؟ | فرهنگ واژگان تکنّاو` : 'فرهنگ واژگان فناوری | تکنّاو';
+ const description = truncate(term?.summary || 'تعریف دقیق و فارسی واژههای فنی فناوری، هوش مصنوعی، داده، امنیت و نرمافزار در تکنّاو.', 160);
+ return {
+ title,
+ description,
+ keywords: [term?.term, term?.english, 'فرهنگ واژگان فناوری', 'تکنّاو'].filter(Boolean).join(', '),
+ canonical: absoluteUrl(path),
+ type: 'website',
+ image: DEFAULT_IMAGE,
+ jsonLd: [
+ ...baseGraph(),
+ {
+ '@type': 'DefinedTerm',
+ name: term?.term,
+ alternateName: term?.english,
+ description,
+ inDefinedTermSet: absoluteUrl('/glossary'),
+ url: absoluteUrl(path),
+ sameAs: GLOSSARY_SAMEAS_MAP[slug] || [],
+ },
+ term?.faqs?.length ? {
+ '@type': 'FAQPage',
+ mainEntity: term.faqs.map(([question, answer]) => ({
+ '@type': 'Question',
+ name: question,
+ acceptedAnswer: { '@type': 'Answer', text: answer },
+ })),
+ } : null,
+ breadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'فرهنگ واژگان', path: '/articles' }, { name: term?.term || slug }]),
+ ].filter(Boolean),
+ };
+ }
+
+ if (page.startsWith('/topics/')) {
+ const slug = page.replace('/topics/', '');
+ const topic = TeknavData.topicHubs.find((item) => item.slug === slug);
+ const category = TeknavData.categories.find((item) => item.slug === topic?.categorySlug);
+ const articles = TeknavData.articles
+ .filter((article) => article.category === topic?.categorySlug && article.status !== 'پیشنویس' && article.status !== 'در انتظار بررسی')
+ .sort((a, b) => String(b.dateEn || '').localeCompare(String(a.dateEn || '')));
+ const name = topic?.title || category?.name || 'موضوع فناوری';
+ const description = truncate(topic?.description || category?.description || `مقالهها و تحلیلهای تکناو درباره ${name}.`, 160);
+ const path = `/topics/${slug}`;
+ return {
+ title: topic?.seoTitle || `${name} | خوشه موضوعی تکناو`,
+ description,
+ keywords: (topic?.keywords ?? [name, 'تکناو', 'تحلیل فناوری']).join(', '),
+ canonical: absoluteUrl(path),
+ type: 'website',
+ image: DEFAULT_IMAGE,
+ jsonLd: [
+ ...baseGraph(),
+ {
+ '@type': 'CollectionPage',
+ name,
+ description,
+ url: absoluteUrl(path),
+ inLanguage: 'fa-IR',
+ isPartOf: { '@id': `${SITE_URL}/#website` },
+ mainEntity: itemList(articles, path),
+ },
+ topic?.faqs?.length ? {
+ '@type': 'FAQPage',
+ mainEntity: topic.faqs.map(([question, answer]) => ({
+ '@type': 'Question',
+ name: question,
+ acceptedAnswer: { '@type': 'Answer', text: answer },
+ })),
+ } : null,
+ breadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'موضوعات', path: '/articles' }, { name }]),
+ ],
+ };
+ }
+
+ if (page.startsWith('/series/')) {
+ const slug = page.replace('/series/', '');
+ const series = data.series;
+ const path = `/series/${slug}`;
+ const name = series?.title || 'سری مقاله تکناو';
+ const description = truncate(series?.description || 'سری مقالههای تکناو برای دنبال کردن یک موضوع فناوری بهترتیب تحریریه.', 160);
+ const articles = (series?.articles ?? []).map((item) => item.article).filter(Boolean);
+ return {
+ title: `${name} | سری مقالههای تکناو`,
+ description,
+ keywords: `${name}, سری مقاله, تکناو, تحلیل فناوری`,
+ canonical: absoluteUrl(path),
+ type: 'website',
+ image: imageUrl(series?.coverImage),
+ jsonLd: [
+ ...baseGraph(),
+ {
+ '@type': 'CollectionPage',
+ name,
+ description,
+ url: absoluteUrl(path),
+ inLanguage: 'fa-IR',
+ mainEntity: itemList(articles, path),
+ },
+ breadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'سری مقالهها', path: '/articles' }, { name }]),
+ ],
+ };
+ }
+
+ if (page.startsWith('/author/')) {
+ const slug = page.replace('/author/', '');
+ const author = data.author || TeknavData.authors.find((a) => a.slug === slug);
+ const name = author?.name || 'نویسنده تکناو';
+ const description = truncate(author?.bio || `آرشیو مقالههای ${name} در تکناو.`, 155);
+ const avatarUrl = author?.avatarUrl;
+ return {
+ title: `${name} | نویسنده تکناو`,
+ description,
+ keywords: `${name}, نویسنده فناوری, تحلیل فناوری, تکناو`,
+ canonical: absoluteUrl(`/author/${slug}`),
+ type: 'profile',
+ image: imageUrl(avatarUrl),
+ jsonLd: [
+ ...baseGraph(),
+ {
+ '@type': 'ProfilePage',
+ name: `${name} در تکناو`,
+ url: absoluteUrl(`/author/${slug}`),
+ inLanguage: 'fa-IR',
+ ...(avatarUrl ? { primaryImageOfPage: imageUrl(avatarUrl) } : {}),
+ mainEntity: renderAuthorPerson(author, name, author?.specialty, description, author?.expertise, absoluteUrl(`/author/${slug}`)),
+ },
+ breadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'نویسندگان', path: '/authors' }, { name }]),
+ ],
+ };
+ }
+
+ if (page.startsWith('/profile/@')) {
+ const username = page.replace('/profile/@', '');
+ const author = TeknavData.authors.find((a) => a.username === username || a.slug === username);
+ const name = data.profile?.name || author?.name || `@${username}`;
+ const description = truncate(data.profile?.bio || author?.bio || `پروفایل عمومی ${name} در تکناو.`, 155);
+ const avatarUrl = data.profile?.avatarUrl || author?.avatarUrl;
+ const mainEntity = author
+ ? renderAuthorPerson(author, name, author.specialty, description, author.expertise, absoluteUrl(`/profile/@${username}`), data.profile?.avatarUrl)
+ : {
+ '@type': 'Person',
+ name,
+ alternateName: `@${username}`,
+ description,
+ image: avatarUrl ? imageUrl(avatarUrl) : undefined,
+ };
+ return {
+ title: `${name} | پروفایل تکناو`,
+ description,
+ keywords: `${name}, ${username}, پروفایل تکناو, نویسنده فناوری`,
+ canonical: absoluteUrl(`/profile/@${username}`),
+ type: 'profile',
+ image: imageUrl(avatarUrl),
+ jsonLd: [
+ ...baseGraph(),
+ {
+ '@type': 'ProfilePage',
+ name,
+ description,
+ url: absoluteUrl(`/profile/@${username}`),
+ inLanguage: 'fa-IR',
+ ...(avatarUrl ? { primaryImageOfPage: imageUrl(avatarUrl) } : {}),
+ mainEntity,
+ },
+ ],
+ };
+ }
+
+ if (page === '/articles') {
+ return {
+ title: 'مقالههای فناوری | تکناو',
+ description: 'آرشیو مقالههای فارسی تکناو درباره هوش مصنوعی، علم داده، امنیت سایبری، نرمافزار، سختافزار و آینده فناوری.',
+ keywords: DEFAULT_KEYWORDS,
+ canonical: absoluteUrl('/articles'),
+ type: 'website',
+ image: DEFAULT_IMAGE,
+ jsonLd: [...baseGraph(), { '@type': 'CollectionPage', name: 'مقالههای فناوری تکناو', url: absoluteUrl('/articles'), inLanguage: 'fa-IR' }],
+ };
+ }
+
+ if (page === '/authors') {
+ return {
+ title: 'نویسندگان تکناو | تحلیلگران فناوری',
+ description: 'آشنایی با نویسندگان و تحلیلگران تکناو در حوزه هوش مصنوعی، امنیت سایبری، نرمافزار، سختافزار و استارتاپ.',
+ keywords: 'نویسندگان تکناو, تحلیلگر فناوری, نویسنده هوش مصنوعی, نویسنده امنیت سایبری',
+ canonical: absoluteUrl('/authors'),
+ type: 'website',
+ image: DEFAULT_IMAGE,
+ jsonLd: [...baseGraph(), { '@type': 'CollectionPage', name: 'نویسندگان تکناو', url: absoluteUrl('/authors'), inLanguage: 'fa-IR' }],
+ };
+ }
+
+ if (page === '/newsletter') {
+ return {
+ title: 'آرشیو خبرنامه تکنـاو | تحلیل هفتگی فناوری',
+ description: 'آرشیو خبرنامههای ارسالشده تکنـاو درباره هوش مصنوعی، امنیت سایبری، نرمافزار، سختافزار، استارتاپ و داده.',
+ keywords: 'خبرنامه تکنـاو, آرشیو خبرنامه فناوری, تحلیل هفتگی فناوری, هوش مصنوعی, امنیت سایبری',
+ canonical: absoluteUrl('/newsletter'),
+ type: 'website',
+ image: DEFAULT_IMAGE,
+ jsonLd: [
+ ...baseGraph(),
+ { '@type': 'CollectionPage', name: 'آرشیو خبرنامه تکنـاو', url: absoluteUrl('/newsletter'), inLanguage: 'fa-IR' },
+ breadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'خبرنامه' }]),
+ ],
+ };
+ }
+
+ if (page === '/jobs' || page === '/courses' || page === '/membership') {
+ const meta = {
+ '/jobs': ['فرصتهای شغلی فناوری | تکناو', 'آگهیهای شغلی منتخب فناوری و امکان ثبت موقعیت شغلی شرکتها در تکناو.', 'شغل فناوری, استخدام برنامه نویس, استخدام هوش مصنوعی, تکناو جابز'],
+ '/courses': ['دورههای تخصصی فناوری | تکناو', 'دورههای فارسی تکناو برای هوش مصنوعی، امنیت، نرمافزار و مهارتهای فنی محصولمحور.', 'دوره فناوری, آموزش هوش مصنوعی, آموزش امنیت, دوره فارسی برنامه نویسی'],
+ '/membership': ['عضویت پریمیوم تکناو | محتوای اختصاصی فناوری', 'عضویت پریمیوم تکناو برای تجربه بدون تبلیغ، محتوای اختصاصی و دسترسی بهتر به دورهها و گزارشهای تخصصی.', 'عضویت تکناو, محتوای پریمیوم فناوری, تحلیل اختصاصی فناوری'],
+ }[page];
+ return {
+ title: meta[0],
+ description: meta[1],
+ keywords: meta[2],
+ canonical: absoluteUrl(page),
+ type: 'website',
+ image: DEFAULT_IMAGE,
+ jsonLd: [
+ ...baseGraph(),
+ { '@type': 'CollectionPage', name: meta[0], url: absoluteUrl(page), inLanguage: 'fa-IR', description: meta[1] },
+ breadcrumb([{ name: SITE_NAME, path: '/' }, { name: meta[0] }]),
+ ],
+ };
+ }
+
+ if (page.startsWith('/newsletter/')) {
+ const slug = page.replace('/newsletter/', '');
+ const issue = data.newsletterIssue;
+ const name = issue?.subject || 'خبرنامه تکنـاو';
+ const description = truncate(issue?.bodyHtml || 'نسخهای از خبرنامه تکنـاو درباره روندهای مهم فناوری.', 155);
+ return {
+ title: `${name} | خبرنامه تکنـاو`,
+ description,
+ keywords: `${name}, خبرنامه تکنـاو, فناوری, هوش مصنوعی, امنیت سایبری`,
+ canonical: absoluteUrl(`/newsletter/${slug}`),
+ type: 'article',
+ image: DEFAULT_IMAGE,
+ jsonLd: [
+ ...baseGraph(),
+ {
+ '@type': 'Article',
+ headline: cleanText(name).slice(0, 110),
+ description,
+ datePublished: issue?.sentAt,
+ dateModified: issue?.sentAt || issue?.createdAt,
+ inLanguage: 'fa-IR',
+ mainEntityOfPage: absoluteUrl(`/newsletter/${slug}`),
+ publisher: { '@id': `${SITE_URL}/#organization` },
+ },
+ breadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'خبرنامه', path: '/newsletter' }, { name }]),
+ ],
+ };
+ }
+
+ if (page.startsWith('/search')) {
+ const q = new URLSearchParams(page.split('?')[1] || '').get('q') || '';
+ return {
+ title: q ? `جستجو برای ${q} | تکناو` : 'جستجو در تکناو',
+ description: q ? `نتایج جستجوی تکناو برای ${q} در مقالههای فارسی فناوری.` : 'جستجو در مقالههای فارسی تکناو درباره فناوری و تکنولوژی.',
+ keywords: q ? `${q}, جستجوی فناوری, تکناو` : DEFAULT_KEYWORDS,
+ canonical: absoluteUrl(q ? `/search?q=${encodeURIComponent(q)}` : '/search'),
+ type: 'website',
+ image: DEFAULT_IMAGE,
+ jsonLd: baseGraph(),
+ };
+ }
+
+ return {
+ title: DEFAULT_TITLE,
+ description: DEFAULT_DESCRIPTION,
+ keywords: DEFAULT_KEYWORDS,
+ canonical: absoluteUrl('/'),
+ type: 'website',
+ image: DEFAULT_IMAGE,
+ jsonLd: [
+ ...baseGraph(),
+ {
+ '@type': 'WebPage',
+ '@id': `${SITE_URL}/#webpage`,
+ name: DEFAULT_TITLE,
+ description: DEFAULT_DESCRIPTION,
+ url: absoluteUrl('/'),
+ inLanguage: 'fa-IR',
+ isPartOf: { '@id': `${SITE_URL}/#website` },
+ about: { '@id': `${SITE_URL}/#organization` },
+ speakable: {
+ '@type': 'SpeakableSpecification',
+ xpath: ['/html/head/title', '/html/head/meta[@name="description"]/@content'],
+ },
+ },
+ ],
+ };
+}
+
+export function SeoManager() {
+ const { page } = useNav();
+ const [data, setData] = useState({});
+
+ useEffect(() => {
+ let cancelled = false;
+ setData({});
+
+ if (page.startsWith('/article/')) {
+ const rawSlug = page.replace('/article/', '');
+ const slug = rawSlug === 'ai-agents-2026' ? 'agentic-ai-production' : rawSlug;
+ const fallback = contentApi.fallbackArticles().find((a) => a.slug === slug);
+ if (fallback) setData({ article: fallback });
+ contentApi.getArticle(slug).then((res) => {
+ if (!cancelled && res?.article) setData({ article: res.article });
+ }).catch(() => {});
+ } else if (page.startsWith('/category/')) {
+ const slug = page.replace('/category/', '');
+ const fallback = TeknavData.categories.find((c) => c.slug === slug || c.id === slug);
+ if (fallback) setData({ category: fallback });
+ contentApi.getCategory(slug).then((category) => {
+ if (!cancelled && category) setData({ category });
+ }).catch(() => {});
+ } else if (page.startsWith('/author/')) {
+ const slug = page.replace('/author/', '');
+ const fallback = TeknavData.authors.find((a) => a.slug === slug);
+ if (fallback) setData({ author: fallback });
+ contentApi.getAuthor(slug).then((author) => {
+ if (!cancelled && author) setData({ author });
+ }).catch(() => {});
+ } else if (page.startsWith('/profile/@')) {
+ const username = page.replace('/profile/@', '');
+ api.get(`/api/profile/${username}`)
+ .then((res) => { if (!cancelled && res?.profile) setData({ profile: res.profile }); })
+ .catch(() => {});
+ } else if (page.startsWith('/series/')) {
+ const slug = page.replace('/series/', '');
+ contentApi.getSeries(slug)
+ .then((series) => { if (!cancelled && series) setData({ series }); })
+ .catch(() => {});
+ } else if (page.startsWith('/newsletter/')) {
+ const slug = page.replace('/newsletter/', '');
+ contentApi.getNewsletterIssue(slug)
+ .then((newsletterIssue) => { if (!cancelled && newsletterIssue) setData({ newsletterIssue }); })
+ .catch(() => {});
+ }
+
+ return () => { cancelled = true; };
+ }, [page]);
+
+ const seo = useMemo(() => pageSeo(page, data), [page, data]);
+
+ useEffect(() => {
+ const isEn = typeof window !== 'undefined' && window.location.search.includes('lang=en');
+ const language = seo.language || (isEn ? 'en' : 'fa-IR');
+ const htmlLang = language.toLocaleLowerCase('en-US').startsWith('en') ? 'en' : 'fa';
+ const ogLocale = htmlLang === 'en' ? 'en_US' : 'fa_IR';
+ const canonical = seo.canonical.split('?')[0];
+
+ document.documentElement.lang = htmlLang;
+ document.documentElement.dir = htmlLang === 'en' ? 'ltr' : 'rtl';
+ document.title = seo.title;
+ setMeta('name', 'description', seo.description);
+ setMeta('name', 'keywords', seo.keywords);
+ setMeta('name', 'robots', 'index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1');
+ setMeta('name', 'googlebot', 'index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1');
+ setMeta('name', 'bingbot', 'index,follow,max-snippet:-1,max-image-preview:large');
+ setMeta('name', 'author', SITE_NAME);
+ setMeta('name', 'language', language);
+ if (seo.academicMeta) {
+ Object.entries(seo.academicMeta).forEach(([name, value]) => setMeta('name', name, value));
+ setNamedMetaList('citation_reference', seo.citations || []);
+ } else {
+ document.head.querySelectorAll('meta[name="citation_reference"][data-teknav-seo-list="true"]').forEach((el) => el.remove());
+ }
+ setMeta('property', 'og:locale', ogLocale);
+ setMeta('property', 'og:site_name', SITE_NAME);
+ setMeta('property', 'og:type', seo.type);
+ setMeta('property', 'og:title', seo.title);
+ setMeta('property', 'og:description', seo.description);
+ setMeta('property', 'og:url', canonical);
+ setMeta('property', 'og:image', seo.image);
+ setMeta('property', 'og:image:width', '1200');
+ setMeta('property', 'og:image:height', '630');
+ setMeta('name', 'twitter:card', 'summary_large_image');
+ setMeta('name', 'twitter:title', seo.title);
+ setMeta('name', 'twitter:description', seo.description);
+ setMeta('name', 'twitter:image', seo.image);
+ setLink('canonical', canonical);
+ document.head.querySelectorAll('link[rel="alternate"][hreflang]').forEach((el) => el.remove());
+ setLink('alternate', canonical, { hreflang: htmlLang });
+ setJsonLd(seo.jsonLd);
+ }, [seo]);
+
+ return null;
+}