A language version is usually tested like this: open the home page, switch the language, see translated text, close the tab. Test passed. In fact it has just not happened — because everything that matters starts with the second click.
On 1 September 2026 we were taking apart our own site after a question that sounded simple: why do links on a Russian service page lead to Ukrainian addresses. The answer turned out longer than the question. There were five causes, they lived in different places in the code, and none of them was visible on the first screen.
What the reader sees
Take a Russian service page. Standard breadcrumbs at the top:
Главная → Razrabotka → Internet magaziny → the page
Two defects in three words. First, the label is assembled from the Latin URL segment instead of the word «Разработка» — it looks like a machine oversight, and that is exactly what it is. Second, and worse, the link points at an address with no language prefix. That page does not exist: the server returns 404.
So a reader who reached the page they wanted and then decided to go one level up — to the whole category — landed in nothing. On each of 237 published service pages, in both language versions.
Further down the page: the related block, cards of adjacent services, the button into the portfolio, highlighted terms in the text. We counted on a single page — 12 links, all 12 leading to Ukrainian addresses. A reader who arrived on the Russian version and started moving around the site was in the Ukrainian one after the first click, and never came back out.
Why this stays invisible for years
Three things work together here.
Nothing breaks. A link into another language is a working link. The page opens, returns 200, looks fine. Monitoring stays quiet, the console shows no errors, a broken-link test passes.
Developers test in the default language. In Ukrainian everything is correct — that language is the base the addresses were built from. The defect only switches on once a prefix appears in the URL.
Analytics reports normality. People move around the site, pages get viewed. The fact that a Russian-speaking visitor ended up in the Ukrainian section within two clicks is not marked anywhere: same site, same page views.
Five causes of one symptom
Here is what we found once we went through the code.
Breadcrumbs took URL segments as they came. On /ru/razrabotka/... the first segment is razrabotka, and the code honestly built the link /razrabotka from it. The Ukrainian address is /rozrobka, hence the 404. The same string went into the label: the translation map is keyed by Ukrainian slugs, razrabotka was not found there, and the label was assembled from the slug with a capital letter.
The service tree translated the address but added no prefix. This one was almost right: the slug was translated, internet-magaziny instead of internet-magazyny. What was missing was /ru at the front — so the link went to a redirect instead of the target page.
List cards took the Ukrainian address deliberately. The code reached for the field holding the Ukrainian slug — not a typo, but a decision made once for a single-language site and outlived by it.
The related block never received translated addresses at all. The sneakiest of the five: the logic had already been fixed, but the database query did not include the column holding localized slugs. The code asked for a translation, got nothing, and honestly fell back to the Ukrainian address. After the other places were fixed, only five links out of fourteen changed here — and that was the single hint that something was left.
Automatic term highlighting was monolingual. The mechanism that finds technology names in the text and turns them into links stores Ukrainian target addresses. On a Russian page the word «OpenCart» led into the Ukrainian section.
A separate item — hreflang. On category hubs it declared addresses that themselves return a redirect. In other words the search engine was told «the English version of this page is here», and at that address stood a redirect to another one. Google will survive it, but the signal is spoiled, and the documentation says plainly that the final URL is what should be declared.
What this means for search
Without exaggeration. This is not a story about lost rankings — rankings cannot honestly be discussed here, and no figure would prove anything.
The mechanism is different and simpler. A search engine judges a language version largely by whether it stands on its own: does it have internal links, can you move around it without falling out into another language. When every exit from a Russian page leads into the Ukrainian one, the crawler gets exactly what the reader gets — a dead end. Pages of the second language become leaves with no branches.
The second part of the price is crawling. A link to a redirecting address wastes a crawler request. A link into a 404 wastes it more thoroughly. On a site of two or three pages that is meaningless. On several hundred it is already a noticeable share of crawling spent on nothing.
The third, and simplest: the reader. Someone who chose a language and is reading a different one two clicks later draws the obvious conclusion about the site.
How to check this on your own site in half an hour
The method that worked for us is deliberately primitive: it looks at finished HTML and knows nothing about what the code intended. That is precisely why it showed us three times that «fixed» actually meant «almost».
Take an address with a language prefix and count the links that lack it:
curl -s https://your-site.com/ru/some-page \
| grep -o 'href="/[^"]*"' \
| grep -v 'href="/ru' \
| sort -u
The output will hold three kinds of addresses.
First — what belongs there: the language switcher, files, external resources. The switcher is obliged to point at other languages, that is its job. We tripped over this: the first run reported defects on every page type, and half of those «defects» were the switcher. Tell it apart by the hreflang attribute on the same tag.
Second — pages that have no language version at all. For us that is seven client questionnaires. Adding a prefix to them would send people into a 404, so they stayed Ukrainian on purpose, and that is a sound decision rather than an oversight.
Third — the actual defects.
Repeat this for every type of page rather than every page: home, section, subsection, detail, list, article, form. There are usually a dozen or so types, each built by its own template, so a defect in one says nothing about another. Of our thirteen types six turned out clean — but we only learned that by checking all thirteen.
Check breadcrumbs separately; it is the cheapest test of all:
curl -s -o /dev/null -w "%{http_code}\n" https://your-site.com/address-from-the-breadcrumb
And hreflang — every declared address must return 200, not 301 or 308:
curl -s https://your-site.com/page | grep -o 'hreflang="[^"]*" href="[^"]*"'
The result
Before the work: 224 distinct addresses that language versions linked to in a foreign language. After: seven, and all seven are those same untranslated forms.
Eleven page types out of thirteen became fully clean. Breadcrumbs return 200 instead of 404 and are written in the words of the page's language. hreflang points at final addresses.
The figures are deliberately about the work rather than the outcome. What comes of it in search will be visible in a few weeks in Search Console, and that is where we will look. Promising growth from a fix like this is not possible: it removes a loss, it does not add demand.
Honest limits
This is not work worth starting if your language version is a formality — machine-translated and needed by no one. Removing it is cheaper than treating its linking.
It also gives nothing to a single-language site. And it does not replace translation: a page wired correctly but written in a foreign language stays foreign.
And the main thing this check does not do: it does not explain why the defect appeared. Our five causes sat in five different files, written at different times by different hands. What found them was not a code audit but a blunt sweep of finished HTML — and that is what we kept in the project as a standing check after any change to navigation.
If you have language versions and have never tested them from the second click, start with the command above. It takes less time than reading this article.




