GEO-078·Issue 07·Technical Foundation·78 / 132
Multilingual URLs and hreflang Done Right
Language path, subdomain, or ccTLD: pick the structure one team can redeploy, generate one hreflang matrix, and gate it in CI before the next locale ships.
Issue focusAcross Markets: Multilingual, Localization, and Regional Semantics
A US lighting brand signs a German distributor and has eight weeks to launch a storefront. Marketing wants brand.de because it looks local. Engineering asks who updates the tags when French and Japanese arrive next quarter. Nobody answers, so the team picks a subdomain. Six months later the pages and the file exist, and German questions still return the English page.
A multilingual store stays coherent when every language version is a subdirectory of one deployable site, each version canonicalizes to itself, and every alternate comes from one matrix; split it across two systems and the tags are decoration.
URL shape is an operations decision, not a ranking one
Pick the structure one team can regenerate in one deploy. Alternates only work when you can see every version at once, and only a language path guarantees it: one repo, one build, one owner.
| Structure | What it buys | What it costs | Choose it when |
|---|---|---|---|
Language path /de/ | One repo, one deploy, one matrix | i18n routing; assets keyed by prefix | Default for three to six markets, one team |
Subdomain de.brand.com | Independent teams and stacks | Separate logs, certs, robots.txt, sitemap per host | Legacy setup or a front end that cannot route |
ccTLD brand.de | Local entity, payment rails, region signal | Tags spread over N properties, each verified separately | Incorporation, tax, or a platform requirement |
A country domain is a legal and payments decision in an SEO costume. If counsel and your provider do not require it, settle the market order first with six variables for GEO market entry.
hreflang is a matrix; a tag is its output
The hard part is not writing the tag. It is proving every member of a cluster lists every other member, including itself.
Engines treat annotations as a set and discard contradicting entries. Retrieval is blunter: an assistant answers in the language of the URL it retrieved, so a page outside the cluster is not a weaker match — it is invisible. A German shopper gets a correct answer that reads like a foreign brand.
Key rows on page identity: one per SKU or category, columns per locale. Empty cells then become visible before a launch, not after.
x-default marks the version served when the buyer’s language is absent, usually a selector page. Pointing it at your largest market because you want it recommended is an ad slot wearing a fallback’s clothes. Keep every locale self-canonical: canonicalizing /de/ to /en/ substitutes the English page and removes German from the cluster.
Five failures a validator will happily pass
Validators check syntax. They cannot tell whether the target returns 200, whether the other side lists you back, or whether a person can reach the page without JavaScript.
| Failure | Looks like | What the model gets |
|---|---|---|
| No self-reference | Each page lists only the others | That locale drops out of its own cluster |
| One-way annotation | A lists B; B does not list A | Entry discarded, correspondence unproven |
| Target redirects or 404s | Tags left behind by a migration | Broken member, cluster unresolved |
| Inconsistent codes | zh-CN here, zh-Hans there, case mixed | Two versions read as different languages |
| Tags, no entry point | Complete head, no static link, locale absent from sitemap | Crawler never arrives |
The first four are configuration: edit and rerun. The fifth is structural — tags live in the head, entry points come from templates and navigation, usually someone else’s code. The discovery half is covered in crawl consistency across language paths.
Build it as a pipeline, not a checklist
Generate the tags; never hand-write them per template, and make one check fail the build.
| Step | Action | Owner | Passes when |
|---|---|---|---|
| 1 Set the page key | One key per SKU and category, all locales | Engineering | No orphan URL in the matrix |
| 2 Document the structure | Which prefix and host belong to which team | Engineering, growth | A fourth locale needs no meeting |
| 3 Generate three outputs | Head alternates, sitemap entries, switcher hrefs | Engineering | 20 sampled URLs agree across all three |
| 4 Fail the build | Self-reference, reciprocity, 200s, valid codes, one x-default | Engineering | Breaking a tag turns CI red |
| 5 Re-sample after launch | Ask in the locale, check the cited URL’s prefix | Growth | Same method as the AI visibility baseline |
Step 4 is the only step that prevents regression: ungated tags survive a release or two before a refactor half-deletes them silently.
Example: two systems, each writing half a cluster
Example — hypothetical brand; the structure, not a client, is the point.
A kitchen-appliance brand serves /de/ and /fr/ from its main repository while an agency runs jp.brand.com. Neither generator knows the other exists: the main one omits the subdomain, the agency’s lists only itself and English. Both validators report zero errors. German and French questions cite localized pages; Japanese questions return the English page. The difference is not content: French shares a template with English and closes reciprocally, while Japanese sits in another system with another owner.
Two fixes. Structural: migrate to /ja/, 301 the old URLs, ship hreflang and sitemap updates in one release. Procedural: export a single matrix and have both generators read it. Either way, acceptance is 20 URLs where head, sitemap, and switcher agree and every target returns 200.
Where this breaks
One market: do not pre-build locale URLs or tags; an empty cluster is maintenance with no answer to win.
Versions that are not versions: different SKUs, prices, or claims make different pages; forcing them into one cluster creates contradictions engines discard.
Marketplace-first categories: on Amazon or a local marketplace, the platform owns language and region; your URL shape never reaches those answers.
Pages that cannot be fetched: fix access with the AI crawler map first; hreflang has nothing to organize yet.
Legacy ccTLDs with no funded migration: align canonical and sitemap in place.
The switcher is not hreflang: it serves people already on site and expresses preference, but never declares versions. This line sits under the technical foundations hub.
This month, pull 20 non-default-language URLs and build a three-column sheet: alternates in the head, alternates in the sitemap, a static link a stranger could click. Disagreeing rows are the fix list — no roadmap, no migration.