# 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 - [x] 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. - [x] 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 visitor’s 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. - [x] 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 `` 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 - [x] Ensure canonical URLs and hreflang tags are generated per locale with reciprocal references. - [x] Expose locale-specific URLs in navigation, Open Graph tags, and any structured data. - [x] 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?