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:
@@ -0,0 +1,148 @@
|
||||
import { Locale, LocaleKeywords } from '../types';
|
||||
import { serbianKeywords } from '../locales/sr';
|
||||
import { englishKeywords } from '../locales/en';
|
||||
import { germanKeywords } from '../locales/de';
|
||||
import { frenchKeywords } from '../locales/fr';
|
||||
|
||||
/**
|
||||
* Cache for loaded keywords to avoid repeated imports
|
||||
*/
|
||||
const keywordsCache: Record<Locale, LocaleKeywords | null> = {
|
||||
sr: null,
|
||||
en: null,
|
||||
de: null,
|
||||
fr: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all SEO keywords for a specific locale
|
||||
* Uses caching for performance
|
||||
*
|
||||
* @param locale - The locale code ('sr', 'en', 'de', 'fr')
|
||||
* @returns LocaleKeywords object with all keywords for that locale
|
||||
* @example
|
||||
* const keywords = getKeywords('sr');
|
||||
* console.log(keywords.pages.home.primary); // ['prirodni serum za lice', ...]
|
||||
*/
|
||||
export function getKeywords(locale: Locale): LocaleKeywords {
|
||||
// Return from cache if available
|
||||
if (keywordsCache[locale]) {
|
||||
return keywordsCache[locale]!;
|
||||
}
|
||||
|
||||
// Load keywords based on locale
|
||||
const keywordsMap: Record<Locale, LocaleKeywords> = {
|
||||
sr: serbianKeywords,
|
||||
en: englishKeywords,
|
||||
de: germanKeywords,
|
||||
fr: frenchKeywords
|
||||
};
|
||||
|
||||
const keywords = keywordsMap[locale];
|
||||
|
||||
// Cache for future use
|
||||
keywordsCache[locale] = keywords;
|
||||
|
||||
return keywords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get keywords for a specific page type
|
||||
* Convenience function for page-level keyword access
|
||||
*
|
||||
* @param locale - The locale code
|
||||
* @param pageType - The page type ('home', 'products', 'product', 'about', 'contact', 'checkout', 'blog')
|
||||
* @returns PageKeywords for the specified page
|
||||
* @example
|
||||
* const homeKeywords = getPageKeywords('sr', 'home');
|
||||
* console.log(homeKeywords.primary); // Primary keywords for homepage
|
||||
*/
|
||||
export function getPageKeywords(
|
||||
locale: Locale,
|
||||
pageType: keyof LocaleKeywords['pages']
|
||||
) {
|
||||
const keywords = getKeywords(locale);
|
||||
return keywords.pages[pageType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category-specific keywords
|
||||
*
|
||||
* @param locale - The locale code
|
||||
* @param category - The category key ('antiAging', 'hydration', 'glow', 'sensitive', 'natural', 'organic')
|
||||
* @returns Array of keywords for that category
|
||||
*/
|
||||
export function getCategoryKeywords(
|
||||
locale: Locale,
|
||||
category: keyof LocaleKeywords['categories']
|
||||
): string[] {
|
||||
const keywords = getKeywords(locale);
|
||||
return keywords.categories[category];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content topic keywords for blog/article generation
|
||||
*
|
||||
* @param locale - The locale code
|
||||
* @param contentType - Type of content ('educational', 'benefits', 'comparison', 'ingredients')
|
||||
* @returns Array of content topic keywords
|
||||
*/
|
||||
export function getContentKeywords(
|
||||
locale: Locale,
|
||||
contentType: keyof LocaleKeywords['content']
|
||||
): string[] {
|
||||
const keywords = getKeywords(locale);
|
||||
return keywords.content[contentType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get competitor keywords for comparison content
|
||||
*
|
||||
* @param locale - The locale code
|
||||
* @param competitorType - Type of competitor data ('brands', 'comparisons', 'alternatives')
|
||||
* @returns Array of competitor-related keywords
|
||||
*/
|
||||
export function getCompetitorKeywords(
|
||||
locale: Locale,
|
||||
competitorType: keyof LocaleKeywords['competitors']
|
||||
): string[] {
|
||||
const keywords = getKeywords(locale);
|
||||
return keywords.competitors[competitorType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get brand information for the locale
|
||||
*
|
||||
* @param locale - The locale code
|
||||
* @returns BrandKeywords with localized tagline, category, etc.
|
||||
*/
|
||||
export function getBrandKeywords(locale: Locale) {
|
||||
const keywords = getKeywords(locale);
|
||||
return keywords.brand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the keywords cache (useful for testing or hot-reloading)
|
||||
*/
|
||||
export function clearKeywordsCache(): void {
|
||||
keywordsCache.sr = null;
|
||||
keywordsCache.en = null;
|
||||
keywordsCache.de = null;
|
||||
keywordsCache.fr = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available locales
|
||||
*/
|
||||
export function getAvailableLocales(): Locale[] {
|
||||
return ['sr', 'en', 'de', 'fr'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if a locale is supported
|
||||
*/
|
||||
export function isValidLocale(locale: string): locale is Locale {
|
||||
return ['sr', 'en', 'de', 'fr'].includes(locale);
|
||||
}
|
||||
|
||||
export default getKeywords;
|
||||
Reference in New Issue
Block a user