feat: comprehensive SEO system with keywords and schema markup
- Add 4-locale keyword configurations (SR, EN, DE, FR) - Create schema generators (Product, Organization, Breadcrumb) - Add React components for JSON-LD rendering - Implement caching for keyword performance - Abstract all SEO logic for maintainability
This commit is contained in:
104
src/lib/seo/schema/productSchema.ts
Normal file
104
src/lib/seo/schema/productSchema.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { ProductSchema } from './types';
|
||||
import { Locale } from '../keywords/types';
|
||||
import { getBrandKeywords, getCategoryKeywords } from '../keywords';
|
||||
|
||||
interface ProductData {
|
||||
name: string;
|
||||
slug: string;
|
||||
description: string;
|
||||
images: string[];
|
||||
price: {
|
||||
amount: number;
|
||||
currency: string;
|
||||
};
|
||||
sku?: string;
|
||||
availability?: 'InStock' | 'OutOfStock' | 'PreOrder';
|
||||
category?: string;
|
||||
rating?: {
|
||||
value: number;
|
||||
count: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Product schema (JSON-LD)
|
||||
* Pure function - takes product data, returns schema object
|
||||
*
|
||||
* @param baseUrl - Site base URL
|
||||
* @param locale - Locale code
|
||||
* @param product - Product data
|
||||
* @returns ProductSchema object
|
||||
*/
|
||||
export function generateProductSchema(
|
||||
baseUrl: string,
|
||||
locale: Locale,
|
||||
product: ProductData
|
||||
): ProductSchema {
|
||||
const brandKeywords = getBrandKeywords(locale);
|
||||
const productUrl = `${baseUrl}/${locale === 'sr' ? '' : locale + '/'}products/${product.slug}`;
|
||||
|
||||
// Build full image URLs
|
||||
const imageUrls = product.images.map(img =>
|
||||
img.startsWith('http') ? img : `${baseUrl}${img}`
|
||||
);
|
||||
|
||||
const schema: ProductSchema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Product',
|
||||
name: product.name,
|
||||
image: imageUrls,
|
||||
description: product.description.slice(0, 5000), // Schema.org limit
|
||||
sku: product.sku,
|
||||
brand: {
|
||||
'@type': 'Brand',
|
||||
name: brandKeywords.companyName,
|
||||
},
|
||||
offers: {
|
||||
'@type': 'Offer',
|
||||
url: productUrl,
|
||||
price: product.price.amount.toString(),
|
||||
priceCurrency: product.price.currency,
|
||||
availability: `https://schema.org/${product.availability || 'InStock'}`,
|
||||
itemCondition: 'https://schema.org/NewCondition',
|
||||
},
|
||||
};
|
||||
|
||||
// Add aggregate rating if available
|
||||
if (product.rating && product.rating.count > 0) {
|
||||
schema.aggregateRating = {
|
||||
'@type': 'AggregateRating',
|
||||
ratingValue: product.rating.value.toString(),
|
||||
reviewCount: product.rating.count.toString(),
|
||||
};
|
||||
}
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Product schema with category context
|
||||
* Uses category-specific keywords for enhanced SEO
|
||||
*
|
||||
* @param baseUrl - Site base URL
|
||||
* @param locale - Locale code
|
||||
* @param product - Product data
|
||||
* @param categoryKey - Category key for keyword targeting
|
||||
* @returns ProductSchema object
|
||||
*/
|
||||
export function generateCategorizedProductSchema(
|
||||
baseUrl: string,
|
||||
locale: Locale,
|
||||
product: ProductData,
|
||||
categoryKey: 'antiAging' | 'hydration' | 'glow' | 'sensitive' | 'natural' | 'organic'
|
||||
): ProductSchema {
|
||||
const categoryKeywords = getCategoryKeywords(locale, categoryKey);
|
||||
|
||||
// Enhance description with category keywords
|
||||
const enhancedDescription = product.description +
|
||||
' ' + categoryKeywords.slice(0, 3).join('. ');
|
||||
|
||||
return generateProductSchema(baseUrl, locale, {
|
||||
...product,
|
||||
description: enhancedDescription,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user