102 lines
4.8 KiB
TypeScript
102 lines
4.8 KiB
TypeScript
import type { Lang } from '@/lib/i18n'
|
|
|
|
/**
|
|
* Zentrale Sprach-Architektur.
|
|
*
|
|
* `routeGroups`: echte Übersetzungspaare. Sie steuern den Sprachumschalter
|
|
* UND erzeugen die hreflang-Alternates in der Metadata.
|
|
*
|
|
* `fuzzyTargets`: thematisch nächstliegende Seite, wenn es keine echte
|
|
* Übersetzung gibt. Nur der Umschalter nutzt sie — hreflang ignoriert sie,
|
|
* damit Google keine falschen Übersetzungspaare gemeldet bekommt.
|
|
*
|
|
* Neue Subpage? Echte Übersetzung in routeGroups eintragen, sonst
|
|
* fuzzyTargets ergänzen (oder weglassen, dann geht's zur Locale-Startseite).
|
|
*/
|
|
const routeGroups: Partial<Record<Lang, string>>[] = [
|
|
{ de: '/', en: '/en', es: '/es' },
|
|
{ de: '/pflanzen-erkennen-app', en: '/plant-identifier-app', es: '/es/identificador-de-plantas' },
|
|
{ de: '/blumen-scanner', en: '/flower-scanner' },
|
|
{ de: '/pflanzen-bestimmen', en: '/identify-plant-photo' },
|
|
{ de: '/zimmerpflanzen-bestimmen', en: '/houseplant-identifier' },
|
|
{ de: '/pflanzen-pflege-app', en: '/plant-care-app', es: '/es/app-para-cuidar-plantas' },
|
|
{ de: '/pflanzen-krankheiten-erkennen', en: '/plant-disease-identifier', es: '/es/diagnosticar-enfermedades-plantas' },
|
|
{ de: '/pflanzen-erkennen-kostenlos', en: '/best-plant-identification-app' },
|
|
{ en: '/plant-scanner', es: '/es/escaner-de-plantas' },
|
|
{ de: '/vs/google-lens', es: '/es/comparar/google-lens' },
|
|
// Ratgeber-Cluster: deutsche SEO-Seite und englischer Blogpost sind echte
|
|
// Übersetzungspaare, auch wenn die Templates unterschiedlich sind.
|
|
{ de: '/giessplan-zimmerpflanzen', en: '/blog/houseplant-watering-schedule' },
|
|
{ de: '/pflanzen-diagnose', en: '/blog/plant-health-diagnosis-guide' },
|
|
{ de: '/welche-pflanze-ist-das', en: '/blog/which-plant-is-this-guide' },
|
|
]
|
|
|
|
const fuzzyTargets: Record<string, Partial<Record<Lang, string>>> = {
|
|
'/blumen-scanner': { es: '/es/escaner-de-plantas' },
|
|
'/flower-scanner': { es: '/es/escaner-de-plantas' },
|
|
'/plant-scanner': { de: '/blumen-scanner' },
|
|
'/pflanzen-bestimmen': { es: '/es/identificador-de-plantas' },
|
|
'/identify-plant-photo': { es: '/es/identificador-de-plantas' },
|
|
'/zimmerpflanzen-bestimmen': { es: '/es/identificador-de-plantas' },
|
|
'/houseplant-identifier': { es: '/es/identificador-de-plantas' },
|
|
'/pflanzen-erkennen-kostenlos': { es: '/es/identificador-de-plantas' },
|
|
'/best-plant-identification-app': { es: '/es/identificador-de-plantas' },
|
|
'/giess-erinnerung-app': { en: '/plant-care-app', es: '/es/app-para-cuidar-plantas' },
|
|
'/plant-health-app': { de: '/pflanzen-krankheiten-erkennen', es: '/es/diagnosticar-enfermedades-plantas' },
|
|
'/succulent-identifier': { de: '/zimmerpflanzen-bestimmen', es: '/es/identificador-de-plantas' },
|
|
'/es/escaner-de-plantas': { de: '/blumen-scanner' },
|
|
'/es/identificador-de-plantas': { de: '/pflanzen-erkennen-app' },
|
|
'/vs/picturethis': { de: '/pflanzen-erkennen-app', es: '/es/identificador-de-plantas' },
|
|
'/vs/plantum': { de: '/pflanzen-erkennen-app', es: '/es/identificador-de-plantas' },
|
|
'/vs/inaturalist': { de: '/pflanzen-erkennen-app', es: '/es/identificador-de-plantas' },
|
|
}
|
|
|
|
/** Seiten, die in allen Sprachen unter derselben URL leben (Client-Übersetzung). */
|
|
const sharedPaths = new Set(['/support', '/imprint', '/privacy', '/terms'])
|
|
|
|
export const localeHome: Record<Lang, string> = { de: '/', en: '/en', es: '/es' }
|
|
|
|
const englishPaths = new Set([
|
|
'/en',
|
|
'/best-plant-identification-app',
|
|
'/plant-identifier-app',
|
|
'/plant-scanner',
|
|
'/flower-scanner',
|
|
'/houseplant-identifier',
|
|
'/succulent-identifier',
|
|
'/identify-plant-photo',
|
|
'/plant-disease-identifier',
|
|
'/plant-health-app',
|
|
'/plant-care-app',
|
|
])
|
|
|
|
export function getLocaleForPath(pathname: string): Lang {
|
|
if (pathname === '/es' || pathname.startsWith('/es/')) return 'es'
|
|
if (pathname === '/vs/google-lens') return 'de'
|
|
if (englishPaths.has(pathname) || pathname.startsWith('/vs/') || pathname.startsWith('/blog')) return 'en'
|
|
return 'de'
|
|
}
|
|
|
|
function findGroup(pathname: string): Partial<Record<Lang, string>> | undefined {
|
|
return routeGroups.find((group) => Object.values(group).includes(pathname))
|
|
}
|
|
|
|
/** Ziel-URL beim Sprachwechsel: Übersetzung, thematischer Fallback oder Locale-Home. */
|
|
export function getAlternatePath(pathname: string, target: Lang): string {
|
|
if (sharedPaths.has(pathname)) return pathname
|
|
if (getLocaleForPath(pathname) === target) return pathname
|
|
return findGroup(pathname)?.[target] ?? fuzzyTargets[pathname]?.[target] ?? localeHome[target]
|
|
}
|
|
|
|
/** hreflang-Alternates für die Metadata einer Seite (nur echte Übersetzungen). */
|
|
export function getHreflangAlternates(pathname: string): Record<string, string> | undefined {
|
|
const group = findGroup(pathname)
|
|
if (!group || Object.keys(group).length < 2) return undefined
|
|
return {
|
|
...(group.de && { de: group.de }),
|
|
...(group.en && { en: group.en }),
|
|
...(group.es && { es: group.es }),
|
|
'x-default': group.de ?? group.en ?? '/',
|
|
}
|
|
}
|