- Dynamic canonical URLs and alternates for all pages (SEO fix) - Shared seo.ts utility for canonical path generation - Improved duplicate detection: standalone > aggregator priority, forks/created_at tie-breakers - browseReadyFilter now includes unique aggregator skills - Stats aligned with browseReadyFilter (was showing 16k instead of 62k+) - Re-review mechanism: content_hash changes trigger needs-re-review - review_status field added to skills schema - Batch security scan and quality score scripts - Indexer Dockerfile updated for curation deps - Removed roadmap from README Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
51 lines
1.5 KiB
TypeScript
51 lines
1.5 KiB
TypeScript
import { locales } from '@/i18n';
|
|
|
|
export const primaryDomain =
|
|
process.env.NEXT_PUBLIC_APP_URL || 'https://skills.palebluedot.live';
|
|
|
|
const DEFAULT_LOCALE = 'en';
|
|
|
|
/**
|
|
* Get canonical URL path for a locale
|
|
* (no prefix for default locale, matching localePrefix: 'as-needed')
|
|
*/
|
|
export function getCanonicalPath(locale: string, route: string): string {
|
|
// Normalize route to always start with a slash and not end with a slash
|
|
let formattedRoute = route;
|
|
if (!formattedRoute.startsWith('/')) {
|
|
formattedRoute = `/${formattedRoute}`;
|
|
}
|
|
if (formattedRoute.length > 1 && formattedRoute.endsWith('/')) {
|
|
formattedRoute = formattedRoute.slice(0, -1);
|
|
}
|
|
|
|
if (locale === DEFAULT_LOCALE) {
|
|
if (formattedRoute === '/') return '';
|
|
return formattedRoute;
|
|
}
|
|
|
|
if (formattedRoute === '/') return `/${locale}`;
|
|
return `/${locale}${formattedRoute}`;
|
|
}
|
|
|
|
/**
|
|
* Generate Next.js alternates metadata for a specific route
|
|
* @param locale Current active locale
|
|
* @param route The route path (e.g. '/about', '/categories')
|
|
*/
|
|
export function getPageAlternates(locale: string, route: string) {
|
|
// Ensure we fall back to / if it's empty
|
|
const canonicalPath = getCanonicalPath(locale, route) || '/';
|
|
|
|
const languages: Record<string, string> = {};
|
|
|
|
for (const l of locales) {
|
|
languages[l] = `${primaryDomain}${getCanonicalPath(l, route) || '/'}`;
|
|
}
|
|
|
|
return {
|
|
canonical: `${primaryDomain}${canonicalPath}`,
|
|
languages,
|
|
};
|
|
}
|