Files
fotospiel-app/docs/process/todo/localized-seo-hreflang-strategy.md
2025-11-20 10:44:29 +01:00

5.1 KiB
Raw Blame History

Localized SEO hreflang Strategy TODO

Goal

Establish a consistent canonical and hreflang setup for the marketing site so search engines can index German and English content without duplicate-content penalties.

Status (Stand 18.02.2026)

  • Discovery: In progress (route audit complete).
  • Implementation: In progress (canonical and locale-prefixed routing live).
  • Validation: Not started.

Discovery

  • Audit current route map and localized content coverage (marketing pages, blog, checkout flow).
    • Marketing routes live in routes/web.php without locale prefixes. Locale handling is session-based via LocaleController::set, HandleInertiaRequests, and useLocalizedRoutes.
    • Slug coverage is mixed: /contact (EN) and /kontakt (DE) coexist, /how-it-works vs. /so-funktionierts, while other key pages only exist once (e.g. /packages, /demo, /blog, legal pages such as /impressum with no EN variant). Occasion detail routes are German-only (/anlaesse/{type} with hochzeit, geburtstag, etc.).
    • Blog URLs are shared across locales (/blog, /blog/{slug}), with translated content injected server-side. Checkout surfaces (/purchase-wizard/{package}, /checkout/{package}) rely on shared slugs and localized copy but no alternate URLs.
    • MarketingLayout currently generates a single canonical URL per request and an x-default alternate, but no locale-specific hreflang links. Canonical calculation removes a /de|/en prefix even though paths are prefix-free, so both locales resolve to the same canonical if we later introduce prefixed URLs.
    • The sitemap at public/sitemap.xml already lists /de/... and /en/... alternates that the app does not currently serve, causing mismatch risk.
  • Decide on URL strategy (session-based locale vs. language-prefixed routes) and document migration implications.
    • Decision: adopt path-prefixed locales (/{locale}/{slug}) for all marketing and checkout-facing routes, matching the i18n PRP guidance. Default requests (/foo) will 301 to the locale-specific URL using the visitors persisted preference or de fallback.
    • Migration outline:
      1. Introduce a SetLocaleFromPath middleware to extract the first segment and share it with Inertia; wire it into a Route::group with prefix('{locale}') (constrained to de|en) in routes/web.php.
      2. Move existing marketing routes into the prefixed group, normalising slugs so EN and DE share identical structures where feasible (e.g. /de/kontakt/de/contact or /de/kontakt mirrored by /en/contact). Keep legacy German-only slugs (/so-funktionierts, /anlaesse/...) behind per-locale path maps.
      3. Add legacy fallback routes (without prefix) that permanently redirect to the new prefixed URLs to preserve existing backlinks and sitemap entries.
      4. Ensure Inertia helpers (useLocalizedRoutes) and navigation components build URLs with the active locale segment instead of relying on session posts to /set-locale.
    • Blog slugs remain language-agnostic identifiers under /de/blog/{slug} and /en/blog/{slug}; content localization continues via translated fields.
  • Identify required updates to MarketingLayout, sitemap generation, and Inertia responses for localized alternates.
    • MarketingLayout needs to accept structured SEO props (canonical URL, title, description, alternates) instead of deriving everything client-side. It should emit <link rel="alternate" hreflang=""> entries for each supported locale plus x-default, set og:locale/og:locale:alternate, and keep canonical URLs locale-specific (no prefix stripping).
    • Server responses should standardise SEO data via a helper (e.g. MarketingPage::make() or dedicated view model) so each Inertia::render call provides seo props with canonical, alternates, and translated meta. HandleInertiaRequests can share supportedLocales and the resolved host, while a new LocalizedUrlGenerator service maps routes/slugs per locale.
    • The static public/sitemap.xml must be regenerated (or replaced with an automated generator/Artisan command) once prefixed URLs exist, ensuring each entry carries self-referential canonicals and paired xhtml:link elements. Include blog detail pages and legal pages for both locales.

Implementation

  • Ensure canonical URLs and hreflang tags are generated per locale with reciprocal references.
  • Expose locale-specific URLs in navigation, Open Graph tags, and any structured data.
  • Update translation files and config to support the chosen URL strategy.

Validation

  • Add automated checks (feature test or Playwright) verifying hreflang/canonical tags for both locales.
  • Validate via Search Console-style inspection or lighthouse to confirm alternate links render correctly.
  • Update docs (PRP + marketing playbooks) with the final hreflang strategy and operational guidance.

Open Questions

  • How will we handle locale fallbacks for missing translations when hreflang is enforced?
  • Do we need localized sitemap indexes per language or a unified sitemap with hreflang annotations?