← Changelog

Every language has its own address

2026.10.14-language-paths
The Forumulate team
2 min read
i18nseoapi

A translation used to be the page address plus ?locale=es. Search engines may ignore a query parameter, a CDN may drop it, and you cannot point a search console at it as "the Spanish section". Now every language has its own path, right after the space's address:

numestri.com/docs/webhooks       the original
numestri.com/docs/es/webhooks    Spanish

The same applies on every address a space has (a path of your own domain, a site, /p/<org>/<space>), on every reader (docs, blog, forum, legal and the rest), and before a version: /docs/es/v2/<page>. The original never has a code. See Language addresses.

Changed

  • ?locale=es answers 301 with the path form. So does ?lang=es when Spanish is a declared content language. Every other parameter is kept, so existing links keep working.

  • Only a declared language is a path segment. A page called es on a space with no Spanish keeps its address. When a language you add shares a name with a page, the language wins and the page is at /<code>?locale=default.

  • The interface follows the address. /docs/es/… shows its interface in Spanish too, when Spanish is an interface language.

  • Links stay in the language. Navigation, breadcrumbs, previous/next, cards, search results, the front-door redirect and your redirect rules all keep the reader in the language they are reading. The /docs?locale=es front-door redirect no longer drops the query (NUM-42).

  • Search engines see one address per language. Canonicals, hreflang (with x-default), og:locale/og:locale:alternate, <html lang> and JSON-LD inLanguage all name the path form. The space's sitemap.xml lists every published translation, each with <xhtml:link rel="alternate" hreflang> for the others.

  • A fallback page is canonical to the original and no longer noindex. This is the page served for a language with no translation when localeFallback: "source" is set. It stays out of hreflang and the sitemap.

  • Publishing a translation purges its path address on the edge, as well as the ?locale= form.

Added

  • theme.i18n.urlStyle: "path" (the default) or "query", on a space or on its site. Use "query" to keep the ?locale= form.

  • locales and localeUrlStyle on GET /public/orgs/{orgId}/spaces/{spaceId}/seo: the declared language codes, each one a path segment, and how the space addresses them.