- Create taxonomy system with oils.json (5 oils) and concerns.json (9 concerns) - Migrate 10 content files to new data/content/oil-for-concern/ structure - Add scripts: generate-urls.js, validate-taxonomy.js, migrate-content.js - Update dataLoader.ts to use centralized taxonomy - Generate 40 URLs (10 pairs × 4 languages) - Create sitemap-programmatic.xml for SEO - Update by-oil and by-concern directory pages
204 lines
5.8 KiB
JavaScript
204 lines
5.8 KiB
JavaScript
const oils = require('../data/taxonomy/oils.json');
|
|
const concerns = require('../data/taxonomy/concerns.json');
|
|
|
|
const LOCALES = ['sr', 'en', 'de', 'fr'];
|
|
const DEFAULT_LOCALE = 'sr';
|
|
|
|
function generateUrl(oilId, concernId, locale) {
|
|
const oil = oils.oils[oilId];
|
|
const concern = concerns.concerns[concernId];
|
|
|
|
if (!oil || !concern) return null;
|
|
|
|
const localePrefix = locale === DEFAULT_LOCALE ? '' : `/${locale}`;
|
|
const oilSlug = oil.slug[locale];
|
|
const concernSlug = concern.slug[locale];
|
|
|
|
return {
|
|
url: `${localePrefix}/solutions/${oilSlug}-for-${concernSlug}`,
|
|
canonical: `https://manoonoils.com${localePrefix}/solutions/${oilSlug}-for-${concernSlug}`,
|
|
locale,
|
|
oil: {
|
|
id: oilId,
|
|
name: oil.name[locale],
|
|
slug: oilSlug
|
|
},
|
|
concern: {
|
|
id: concernId,
|
|
name: concern.name[locale],
|
|
slug: concernSlug
|
|
}
|
|
};
|
|
}
|
|
|
|
function generateAllUrls() {
|
|
const urls = [];
|
|
const stats = {
|
|
total: 0,
|
|
byLocale: {},
|
|
byOil: {},
|
|
byConcern: {}
|
|
};
|
|
|
|
LOCALES.forEach(l => stats.byLocale[l] = 0);
|
|
Object.keys(oils.oils).forEach(o => stats.byOil[o] = 0);
|
|
Object.keys(concerns.concerns).forEach(c => stats.byConcern[c] = 0);
|
|
|
|
for (const oilId of Object.keys(oils.oils)) {
|
|
const oil = oils.oils[oilId];
|
|
|
|
for (const concernId of oil.concerns) {
|
|
const concern = concerns.concerns[concernId];
|
|
|
|
if (!concern) {
|
|
console.warn(`Warning: Concern ${concernId} not found for oil ${oilId}`);
|
|
continue;
|
|
}
|
|
|
|
for (const locale of LOCALES) {
|
|
const urlData = generateUrl(oilId, concernId, locale);
|
|
if (urlData) {
|
|
urls.push(urlData);
|
|
stats.total++;
|
|
stats.byLocale[locale]++;
|
|
stats.byOil[oilId]++;
|
|
stats.byConcern[concernId]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return { urls, stats };
|
|
}
|
|
|
|
function generateSitemap() {
|
|
const { urls } = generateAllUrls();
|
|
|
|
let sitemap = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
sitemap += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n';
|
|
|
|
for (const url of urls) {
|
|
sitemap += ' <url>\n';
|
|
sitemap += ` <loc>${url.canonical}</loc>\n`;
|
|
sitemap += ' <changefreq>weekly</changefreq>\n';
|
|
sitemap += ' <priority>0.8</priority>\n';
|
|
sitemap += ` <xhtml:link rel="alternate" hreflang="${url.locale}" href="${url.canonical}" />\n`;
|
|
sitemap += ' </url>\n';
|
|
}
|
|
|
|
sitemap += '</urlset>';
|
|
return sitemap;
|
|
}
|
|
|
|
function generateUrlReport() {
|
|
const { urls, stats } = generateAllUrls();
|
|
|
|
let report = '# Programmatic SEO URL Report\n\n';
|
|
report += `Generated: ${new Date().toISOString()}\n\n`;
|
|
|
|
report += '## Summary\n\n';
|
|
report += `- **Total URLs**: ${stats.total}\n`;
|
|
report += `- **Languages**: ${LOCALES.join(', ')}\n`;
|
|
report += `- **Oils**: ${Object.keys(oils.oils).length}\n`;
|
|
report += `- **Concerns**: ${Object.keys(concerns.concerns).length}\n\n`;
|
|
|
|
report += '## URLs by Locale\n\n';
|
|
for (const [locale, count] of Object.entries(stats.byLocale)) {
|
|
report += `- **${locale.toUpperCase()}**: ${count} URLs\n`;
|
|
}
|
|
report += '\n';
|
|
|
|
report += '## URLs by Oil\n\n';
|
|
for (const [oilId, count] of Object.entries(stats.byOil)) {
|
|
const oil = oils.oils[oilId];
|
|
report += `- **${oil.name.en}** (${oilId}): ${count} URLs\n`;
|
|
}
|
|
report += '\n';
|
|
|
|
report += '## URLs by Concern\n\n';
|
|
for (const [concernId, count] of Object.entries(stats.byConcern)) {
|
|
const concern = concerns.concerns[concernId];
|
|
report += `- **${concern.name.en}** (${concernId}): ${count} URLs\n`;
|
|
}
|
|
report += '\n';
|
|
|
|
report += '## All Generated URLs\n\n';
|
|
|
|
const urlsByOil = {};
|
|
for (const url of urls) {
|
|
if (!urlsByOil[url.oil.id]) {
|
|
urlsByOil[url.oil.id] = [];
|
|
}
|
|
urlsByOil[url.oil.id].push(url);
|
|
}
|
|
|
|
for (const [oilId, oilUrls] of Object.entries(urlsByOil)) {
|
|
const oil = oils.oils[oilId];
|
|
report += `### ${oil.name.en}\n\n`;
|
|
|
|
const byConcern = {};
|
|
for (const url of oilUrls) {
|
|
if (!byConcern[url.concern.id]) {
|
|
byConcern[url.concern.id] = [];
|
|
}
|
|
byConcern[url.concern.id].push(url);
|
|
}
|
|
|
|
for (const [concernId, concernUrls] of Object.entries(byConcern)) {
|
|
const concern = concerns.concerns[concernId];
|
|
report += `#### ${concern.name.en}\n\n`;
|
|
|
|
for (const url of concernUrls) {
|
|
report += `- ${url.locale.toUpperCase()}: \`${url.canonical}\`\n`;
|
|
}
|
|
report += '\n';
|
|
}
|
|
}
|
|
|
|
return report;
|
|
}
|
|
|
|
module.exports = {
|
|
generateUrl,
|
|
generateAllUrls,
|
|
generateSitemap,
|
|
generateUrlReport,
|
|
LOCALES,
|
|
DEFAULT_LOCALE
|
|
};
|
|
|
|
if (require.main === module) {
|
|
const { urls, stats } = generateAllUrls();
|
|
|
|
console.log('\n=== PROGRAMMATIC SEO URL GENERATOR ===\n');
|
|
console.log(`Total URLs Generated: ${stats.total}`);
|
|
console.log('\nBy Locale:');
|
|
for (const [locale, count] of Object.entries(stats.byLocale)) {
|
|
console.log(` ${locale.toUpperCase()}: ${count}`);
|
|
}
|
|
console.log('\nBy Oil:');
|
|
for (const [oilId, count] of Object.entries(stats.byOil)) {
|
|
const oil = oils.oils[oilId];
|
|
console.log(` ${oil.name.en}: ${count}`);
|
|
}
|
|
console.log('\nBy Concern:');
|
|
for (const [concernId, count] of Object.entries(stats.byConcern)) {
|
|
const concern = concerns.concerns[concernId];
|
|
console.log(` ${concern.name.en}: ${count}`);
|
|
}
|
|
|
|
console.log('\n=== SAMPLE URLS ===\n');
|
|
const sampleUrls = urls.filter((_, i) => i < 12);
|
|
for (const url of sampleUrls) {
|
|
console.log(`${url.locale.toUpperCase()}: ${url.canonical}`);
|
|
}
|
|
|
|
const fs = require('fs');
|
|
const report = generateUrlReport();
|
|
fs.writeFileSync('./url-report.md', report);
|
|
console.log('\n✓ Full report saved to: url-report.md');
|
|
|
|
const sitemap = generateSitemap();
|
|
fs.writeFileSync('./sitemap-programmatic.xml', sitemap);
|
|
console.log('✓ Sitemap saved to: sitemap-programmatic.xml');
|
|
} |