TL;DR — If you ship the same page in several languages, hreflang is what tells Google "these are translations of each other, not duplicates." Three mistakes make Google ignore (or actively penalize) your setup: a non-reciprocal cluster, hreflang pointing at URLs that 404, and a canonical that points at the English master instead of the page itself. None throws an error. None fails your build. You only catch them by reading the rendered <head>. Here's the Next.js 16 Metadata API pattern that avoids all three.

Multilingual SEO has a cruel property: the failure mode is silence. Your translated pages render fine, your build is green, TypeScript is happy — and Google quietly decides your French page is a duplicate of your English one and drops it. No error anywhere. This post is the checklist I wish I'd had.

I'll use a fictional example.com throughout. The pattern is framework-light: no i18n library, just the Next.js 16 Metadata API's alternates field and a small helper.

The shape: one helper, per-page locale sets

alternates.languages in the Metadata API renders the <link rel="alternate" hreflang="..."> tags for you. A tiny helper keeps it consistent: