SEO-справочник KOMERO

Витрина KOMERO предоставляет каждую SEO-поверхность, которую мерчант ожидает от WooCommerce+Yoast, упакованную как встроенная функциональность. Метаданные хранятся в конфигурации тенанта и записях товаров/категорий как LocalizedText, разрешаемые через хелпер i18nPageMetadataAsync в lib/seo.ts. JSON-LD билдеры (buildProductJsonLd, buildOrganizationJsonLd, buildBreadcrumbJsonLd, buildWebsiteJsonLd, buildFaqJsonLd, buildItemListJsonLd) выводят валидную разметку schema.org с настройкой для каждого тенанта для organizationType, foundingDate, areaServed и priceRange. Sitemap.xml генерируется с force-dynamic и ISR ревалидацией на час и включает записи изображений. Robots.txt генерируется для каждого хоста с защитой staging и разрешённым списком AI-краулеров. Hreflang и x-default альтернаты выводятся автоматически для каждой локали на каждом маршруте через buildI18nUrls. Скрипты аналитики инжектируются через компонент AnalyticsScripts, учитывающий согласие.

Справочник разработчика

SEO-возможности справочник

Каждая SEO-поверхность, предоставляемая витриной KOMERO, сопоставленная с типами, местом в админке и генерируемой разметкой.

Метаданные (заголовок, описание, ключевые слова)

Сущности товара, категории и поста блога предоставляют metaTitle, metaDescription и metaKeywords как поля LocalizedText. Значения разрешаются во время запроса через i18nPageMetadataAsync в lib/seo.ts и возвращаются через Next.js generateMetadata. Когда тенант оставляет поле пустым, KOMERO использует разумное значение по умолчанию (имя товара / имя категории / заголовок блога). Пустые описания заменяются первыми 160 символами описания сущности с удалением ссылок на сущности.

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

Разрешение канонического URL

Канонические URL разрешаются в этом порядке: product.canonicalOverride (валидированный URL) → siteConfig.seo.canonicalBaseUrl + path + locale → siteConfig.url + path + locale. Категории и динамические страницы сейчас поддерживают только базовое переопределение для тенанта; переопределение для страницы в планах.

// 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, но не заполняется, пока не выйдет бэкенд рейтингов.

// 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 } // ← планы по рейтингам
  };
}

Sitemap.xml

Генерируется в app/sitemap.ts с force-dynamic + revalidate = 3600. Для каждого хоста и тенанта. Включает товары (только когда isIndexable равно true), категории, бренды, посты блога, категории блога, теги блога, динамические страницы и статические информационные страницы. Записи товаров включают дочерний <image:image> с 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 и работает для каждого хоста. Продакшн-хосты разрешают GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot и PerplexityBot для видимости в AI-поиске. Краулеры только для обучения (CCBot, anthropic-ai, Bytespider) заблокированы. Staging-хосты возвращают Disallow: / для каждого user agent, чтобы защитить черновые витрины от индексации.

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

User-agent: ClaudeBot
Allow: /

User-agent: CCBot
Disallow: /

Hreflang и x-default

buildI18nUrls возвращает { canonical, languages } для пути по всем активным локалям тенанта. x-default устанавливается в резервную локаль тенанта (обычно 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) генерируются для каждой страницы из сущности плюс дефолтов SEOConfig тенанта. og:type — это 'product' на страницах товара и 'website' на остальных. Тип twitter card по умолчанию summary_large_image и автоматически понижается до summary, когда изображение меньше 1200×630. twitter:site и twitter:creator настраиваются для каждого тенанта.

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

Alt-текст изображений

ProductImageModel.altText это LocalizedText, поэтому каждая активная локаль может иметь свой alt-текст для изображения. Управляется в админском ReviewImagesModal, который поддерживает перетаскивание для изменения порядка, выбор обложки, удаление + отмену, загрузку вставкой и сжатие изображений. Alt-текст никогда не генерируется автоматически — тенанты предоставляют его вручную.

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

Инжекция скриптов аналитики

ID для GA4, GTM, Meta Pixel, Hotjar и Microsoft Clarity хранятся в SEOConfig тенанта и инжектируются через AnalyticsScripts.tsx. Каждый скрипт обёрнут в гейт согласия, который уважает категории согласия на cookies (analytics vs marketing). Произвольная инжекция <script> умышленно не предоставляется по соображениям безопасности и производительности.

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

AI-генерация метаданных (Gemini)

Эндпоинт /api/ai/generate-seo вызывает Google Gemini через Vercel AI SDK @ai-sdk/google для составления metaTitle, metaDescription и metaKeywords для товара, категории или поста блога на основе имени сущности, описания и локали. Черновики вставляются прямо в админскую форму, чтобы тенант мог просмотреть и сохранить.

// api/ai/generate-seo
POST { entityType, entityId, locale }
=> { metaTitle, metaDescription, metaKeywords }
Справочник по SEO-возможностям | Документация KOMERO