KOMERO SEO საცნობარო

KOMERO-ს მაღაზია გამოაქვს ყოველი SEO ზედაპირი, რასაც მოვაჭრე მოელოდა WooCommerce+Yoast-ისგან, პირველი მხარის ფუნქციონალის სახით. მეტადატა ინახება tenant კონფიგურაციაში და პროდუქტის/კატეგორიის ჩანაწერებში LocalizedText ველების სახით, რომელიც წყდება i18nPageMetadataAsync helper-ით lib/seo.ts-ში. JSON-LD ბილდერები (buildProductJsonLd, buildOrganizationJsonLd, buildBreadcrumbJsonLd, buildWebsiteJsonLd, buildFaqJsonLd, buildItemListJsonLd) გამოსცემენ ვალიდურ schema.org მარკაპს ცალკე მოვაჭრის კონფიგურაციით organizationType, foundingDate, areaServed და priceRange ველებისთვის. Sitemap.xml გენერირდება force-dynamic-ით და საათი ISR revalidate-ით, სურათების ჩართვით. Robots.txt გენერირდება თითოეული ჰოსტისთვის staging დაცვით და AI კრაულერების allow-list-ით. Hreflang და x-default alternates ავტომატურად გაიცემა ყოველი ლოკალისთვის ყოველ როუტზე buildI18nUrls-ის საშუალებით. ანალიტიკის სკრიპტები ინიექცირდება consent-aware AnalyticsScripts კომპონენტით.

დეველოპერული საცნობარო

SEO შესაძლებლობების საცნობარო

ყოველი SEO ზედაპირი, რასაც KOMERO მაღაზია გამოსცემს, გვერდში ტიპებზე, ადმინში მდებარეობასა და გენერირებულ მარკაპზე მიბმულია.

მეტადატა (title, description, keywords)

პროდუქტი, კატეგორია და ბლოგპოსტი თითო-თითო LocalizedText ველად ინახავენ metaTitle-ს, metaDescription-სა და metaKeywords-ს. მნიშვნელობები რენდერის დროს რესოლვდება i18nPageMetadataAsync-ის მეშვეობით lib/seo.ts-ში და აბრუნდება Next.js-ის generateMetadata-ს. თუ მოვაჭრეს ველი ცარიელი აქვს, KOMERO იყენებს გონივრულ default-ს (პროდუქტის სახელი / კატეგორიის სახელი / ბლოგის სათაური). ცარიელი description-ი შედის entity description-ის პირველ 160 სიმბოლოზე entity references-ის გამოკლების შემდეგ.

// types/product.ts
export type ProductModel = {
  metaTitle?: LocalizedText;
  metaDescription?: LocalizedText;
  metaKeywords?: LocalizedText;
  canonicalOverride?: string;
  slug: string;
  isIndexable: boolean;
  // ...
};

Canonical URL-ის რესოლუცია

Canonical URL რესოლვდება ამ თანმიმდევრობით: product.canonicalOverride (ვალიდირებული URL) → siteConfig.seo.canonicalBaseUrl + path + locale → siteConfig.url + path + locale. კატეგორია და დინამიური გვერდები ჯერ მხოლოდ tenant-wide base override-ს უჭერენ მხარს; per-page override roadmap-ზეა.

// lib/seo.ts
function getBASE(seo: SEOConfig, siteUrl: string) {
  return seo.canonicalBaseUrl?.trim() || siteUrl;
}

JSON-LD სქემები

მაღაზია გამოსცემს Product, Organization (ContactPoint / PostalAddress / GeoCoordinates / OpeningHoursSpecification-ით), BreadcrumbList, Website (SearchAction-ით), BlogPosting, ItemList და FAQPage. AggregateRating სტრუქტურა არსებობს buildProductJsonLd-ში, მაგრამ არ ავსდება სანამ ratings backend არ ჩაირთვება.

// lib/seo.ts
export function buildProductJsonLd(product, siteConfig, locale) {
  return {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name, description, image, brand,
    offers: { '@type': 'Offer', price, priceCurrency, availability, url },
    // aggregateRating?: { ratingValue, reviewCount } // ← ratings roadmap
  };
}

Sitemap.xml

გენერირდება app/sitemap.ts-ში force-dynamic + revalidate = 3600 კონფიგურაციით. Per-host და per-tenant. მოიცავს პროდუქტებს (მხოლოდ როცა isIndexable არის true), კატეგორიებს, ბრენდებს, ბლოგპოსტებს, ბლოგის კატეგორიებს, ბლოგის ტეგებს, დინამიურ გვერდებს და info გვერდებს. პროდუქტის ჩანაწერი მოიცავს <image:image> child-ს კავერის URL-ით. არავალიდური ან 2000-მდე თარიღები უარიყოფა.

// app/sitemap.ts
export const dynamic = 'force-dynamic';
export const revalidate = 3600;
export default async function sitemap() {
  // fetch tenant, products, categories, blog...
  return entries;
}

Robots.txt (AI კრაულერების პოლიტიკა)

app/robots.ts არის force-dynamic და per-host. საწარმოო ჰოსტები აშვებენ GPTBot-ს, OAI-SearchBot-ს, ChatGPT-User-ს, ClaudeBot-სა და PerplexityBot-ს AI ძიების ხილვადობისთვის. ტრეინინგისთვის განკუთვნილი კრაულერები (CCBot, anthropic-ai, Bytespider) დახურულია. Staging ჰოსტები აბრუნებენ Disallow: / ყოველი user agent-ისთვის draft მაღაზიების დასაცავად.

// app/robots.ts (production)
User-agent: GPTBot
Allow: /

User-agent: ClaudeBot
Allow: /

User-agent: CCBot
Disallow: /

Hreflang & x-default

buildI18nUrls აბრუნებს { canonical, languages } path-ისთვის ყოველ აქტიურ tenant ლოკალზე. x-default უჩვენებს tenant-ის fallback ლოკალს (ჩვეულებრივ ka ქართული მოვაჭრეებისთვის, en საერთაშორისოსთვის). Next.js გამოსცემს, როგორც <link rel='alternate' hreflang='...' href='...'>.

// lib/seo.ts
const { canonical, languages } = buildI18nUrls('/product/xyz', activeLocales, siteConfig);
return { alternates: { canonical, languages } };

Open Graph & Twitter Card

OG ტეგები (og:title, og:description, og:image, og:type, og:locale, og:site_name) გენერირდება ცალკე გვერდისთვის entity-სა და tenant SEOConfig default-ების საფუძველზე. og:type არის 'product' პროდუქტის გვერდზე და 'website' სხვაგან. Twitter card-ის ტიპი default-ად არის summary_large_image და ავტომატურად უბრუნდება summary-ს, თუ სურათი 1200×630-ზე ნაკლებია. twitter:site და twitter:creator კონფიგურირდება tenant-ის დონეზე.

// lib/seo.ts
const card = resolveTwitterCard(image.width, image.height);
// -> 'summary_large_image' or 'summary'

სურათის alt ტექსტი

ProductImageModel.altText არის LocalizedText, ასე რომ ყოველი აქტიური ლოკალი შეიძლება ჰქონდეს საკუთარი alt ტექსტი სურათზე. მართვა ხდება ადმინის ReviewImagesModal-ში, სადაც არის drag-to-reorder, cover-ის არჩევა, წაშლა + undo, paste upload და სურათის კომპრესია. Alt ტექსტი არასდროს გენერირდება ავტომატურად - მოვაჭრე მას ხელით უთითებს.

// types/product.ts
export type ProductImageModel = {
  imagePath: string;
  altText: LocalizedText;
  isCover: boolean;
  order: number;
};

ანალიტიკის სკრიპტის ინიექცია

GA4, GTM, Meta Pixel, Hotjar და Microsoft Clarity ID-ები ინახება tenant SEOConfig-ში და ინიექცირდება AnalyticsScripts.tsx-ის მეშვეობით. ყოველი სკრიპტი consent gate-შია გახვეული, რომელიც ითვალისწინებს cookie consent კატეგორიებს (analytics vs marketing). თავისუფალი <script> ინიექცია განზრახ არ არის დაშვებული უსაფრთხოებისა და პროდუქტიულობის მიზნით.

// components/marketing/analytics/AnalyticsScripts.tsx
if (consent.analytics && seo.googleAnalyticsId) {
  loadGA4(seo.googleAnalyticsId);
}

AI-გენერირებული მეტადატა (Gemini)

/api/ai/generate-seo endpoint-ი მიმართავს Google Gemini-ს @ai-sdk/google Vercel AI SDK-ის მეშვეობით და გენერირებს metaTitle-ს, metaDescription-სა და metaKeywords-ს პროდუქტისთვის, კატეგორიისთვის ან ბლოგპოსტისთვის entity name-ის, description-ისა და ლოკალის საფუძველზე. Draft-ები ჩერდება პირდაპირ ადმინის ფორმაში, სადაც მოვაჭრე მას გადახედავს და შეინახავს.

// api/ai/generate-seo
POST { entityType, entityId, locale }
=> { metaTitle, metaDescription, metaKeywords }
SEO შესაძლებლობების საცნობარო | KOMERO Docs