hreflang.sitemap-mismatch
<head> alternates and sitemap locale coverage must agree
warningsite-wide
Why it matters
The head and the sitemap are two declarations of one intent, produced by different code paths, so they drift. Under-declaring hides real translations; over-declaring points hreflang at URLs the site itself does not list, which is read as a broken cluster.
What the finding looks like
The message goflag prints, with example values substituted.
warn hreflang.sitemap-mismatch Route /pricing: the sitemap lists es, pt-br but the <head> does not advertise them. Both are derived from the same intent and must not disagree.
Derive both from one locale-availability source
Written for the Next.js App Router. The finding is correct on any stack; only the remedy assumes one.
// Compute availability once, feed both the <head> and the sitemap.
const localesFor = (slug: string) =>
allDocs.filter((d) => d.slug === slug && !d.draft).map((d) => d.locale);
// generateMetadata(): alternates.languages ← localesFor(slug)
// sitemap.ts: alternates.languages ← localesFor(slug)Says who?
This rule runs on the cross-page contract, which does not yet carry sources. It picks them up when the site-level rules move onto the same descriptor as the page rules.