Locale routing (URL strategy)

Giving every language its own URL (site.com/es/, es.site.com, or site.es) so each translation can be linked, cached, and indexed on its own.

/es/ vs es.site.comhow to structure language URLslanguage subfoldercountry subdomain for each languageshould every language get its own URLlocale prefix in the pathccTLD for each market

See it

Live demo coming soon

What it is

Three schemes, one decision. Subpath (site.com/es/pricing) is the default answer: one domain, one certificate, one pool of SEO authority, and routing you control in middleware. Subdomain (es.site.com) buys you separate hosting or a separate CMS per market, useful when different teams own different regions. ccTLD (site.es) sends the strongest country signal and costs the most: separate domains to buy, secure, and grow authority on from zero.

Two sub-decisions come with it. Does the default locale get a prefix (site.com/en/ versus a bare root)? Prefixing everything is more consistent and easier to reason about; a bare root keeps your existing English URLs alive. And do you translate the path segments themselves (/es/precios) or keep them stable (/es/pricing)? Translated slugs read better and rank better locally, stable slugs are far easier to maintain and redirect.

Gotcha: whatever you pick, one language per URL is the rule. Swapping the content at a single URL based on headers means crawlers index whichever version they happened to receive and the rest of your translations never appear. Pair the scheme with hreflang tags on every variant, including x-default, and make the language switcher swap only the locale segment so people stay on the page they were reading instead of getting dumped on the homepage.

Ask AI for it

Set up subpath locale routing in this Next.js App Router project. Move the routes under a dynamic [locale] segment, add middleware that redirects the bare root to the negotiated locale, and generate static params for every supported locale. On each page emit a canonical URL plus hreflang alternate links for all locales including x-default, and build a language switcher that rewrites only the locale segment so the current path and query string are preserved. Keep path segments untranslated for now, but centralize them so translated slugs can be added later.

You might have meant

language negotiationhreflanglanguage switcherlocalelocale fallback chain