Multilingual website SEO works well in Google when three conditions are met: every language version has its own URL, hreflang tags link the versions to each other, and the content is genuinely written in that language. The typical problems, such as the English version being shown to users in Poland or a German version that never makes it into the index, come from one of those conditions being missing.
Below we go through the decisions in the order you have to make them: whether you need language versions or country versions, which URL structure to choose, how to implement hreflang and reconcile it with the canonical tag, what besides the text has to change when you localise, and how to find the errors Search Console no longer reports. The site you are reading runs in seven languages, so we made most of these decisions for ourselves too.
- First decide whether you are targeting languages or countries. Most businesses only need language versions, for example
pl,enandde, without country codes. - The simplest structure is subdirectories on one domain:
/pl/,/en/,/de/. Google advises against telling versions apart with URL parameters or cookies. - Each version lists itself and all the others in hreflang, and each of the others points back to it. Without return links Google may ignore the tags.
- The canonical of each version points to that version, never to a version in another language.
- Do not redirect automatically based on browser language or IP address. Googlebot usually crawls from US addresses and without a language preference, so it would only ever see one version.
First decision: language versions or country versions
This distinction determines your hreflang codes, your URL structure and how much maintenance you take on. A language version is for anyone who reads that language, whatever country they live in: one English version for customers in the UK, Ireland, the Netherlands and the US. A country version has different content for each country, even in the same language: different prices and currency, different delivery terms, different legal documents, a different phone number.
Most businesses that want to turn a Polish website into a multilingual one only need language versions. Country versions make sense only when the offer really differs between countries. A site whose en-GB and en-US versions differ only in spelling "colour" and "color" is, as far as Google is concerned, two very similar pages in the same language, and it doubles the work on every content change.
Hreflang codes have a strict format. Google's documentation on localised versions requires a language code from ISO 639-1, optionally followed by a hyphen and a region code from ISO 3166-1 Alpha 2. A country code on its own is not valid, because the first code always denotes the language: be is Belarusian, not Belgium. Google ignores reserved codes such as UK or EU, so the United Kingdom is en-GB. Codes outside these standards, for example es-419 for Latin American Spanish, are not supported.
| Situation | hreflang codes | Notes |
|---|---|---|
| One English version for everyone | en | The most common and simplest case |
| Separate versions for the UK and the US with different prices | en-GB, en-US and en | en as the version for all other English speakers |
| One German version for Germany, Austria and Switzerland | de | A single version, if the offer is the same everywhere |
| Different prices and delivery in Germany and Austria | de-DE, de-AT | The content has to genuinely differ, otherwise Google may treat the versions as duplicates |
| Polish version | pl | pl-PL adds nothing if there are no other Polish versions |
Multilingual website URL structure: domains, subdomains or subdirectories
Every language version needs its own URL. In its guide to multi-regional and multilingual sites, Google recommends a separate URL for each version rather than switching the language with a cookie or browser settings, because with that kind of switching the crawler may not find every version. The same guide describes four possible structures:
| Structure | Example | Pros | Cons |
|---|---|---|---|
| Country-code domains | example.de, example.fr | A clear signal of which country the site is for | Cost and paperwork for every domain, separate infrastructure, one domain means one country |
| Subdomains | de.example.com | Easy to set up, can run on different servers | The URL alone does not always show who the version is for |
| Subdirectories | example.com/de/ | Easiest to implement and maintain, one domain | All versions on one server, harder to split apart |
| URL parameters | example.com?lang=de | None | Google advises against this approach |
For most businesses subdirectories are the best choice. One domain means one certificate, one server configuration and one sitemap, and links from other sites point to a single website instead of being spread across several domains. This site works that way: seven language versions in the subdirectories /pl/, /en/, /de/, /fr/, /it/, /es/ and /ru/.
Country-code domains make sense when, in practice, separate companies operate in different countries, each with its own offer, team and customer service. The cost is not just registration. Every domain builds its visibility from zero, and some national registries have requirements about a registered office or a presence in the country.
According to Google, server location is one of the signals of who a site is for, but alongside it Google lists the country-code domain, hreflang, local addresses and phone numbers, currency and the language of the content. A separate server in Germany is therefore not a precondition for a good German version. Search Console also no longer has a target country setting: Google retired the International Targeting report, concluding that choosing a country in the dashboard had little value, and it continues to support hreflang.
A separate decision is whether to translate the URLs themselves. /de/angebot reads better than /de/oferta with the Polish slug left in place, and it contains the word a German user actually searches for. It does, however, require a table that maps the equivalents between languages, because they cannot be worked out by swapping the prefix. On our site the Polish URLs use Polish segments, for example /pl/oferty rather than /pl/offer, and the equivalents are defined in the routing code.
How hreflang works and what it does not do
Hreflang tags tell Google that several URLs are the same content in different languages or for different regions, and they let Google show users the version that matches their language. If someone searches in English and the site has an English version of the page, Google can show that one instead of the Polish one.
Hreflang is not used to detect language. Google states plainly that it uses neither hreflang nor the HTML lang attribute to work out the language of a page, only its own algorithms applied to the visible content. A page marked hreflang="de" whose main content is in Polish does not become German because of the tag.
Language versions are not duplicates. Google's documentation says localised versions of a page are only treated as duplicates if the main content has not been translated. A translation therefore does not compete with the original, but a version with only the menu translated and the body text still in Polish does.
The lang attribute is still needed, just for a different reason. Screen readers use it to choose pronunciation, and browsers take it into account when they offer to translate a page. Declaring the language of the page is a requirement of WCAG 3.1.1 at the lowest level, A, so <html lang="de"> on the German version is an accessibility obligation, not an SEO one.
Implementing hreflang: three methods and one rule
Google accepts hreflang in three ways: as <link> elements in the HTML head, as an HTTP Link header and as entries in an XML sitemap. All three work the same way. The documentation adds that you can use all of them at once, but that brings no benefit, and three implementations are harder to maintain. Pick one method.
Tags in the HTML are the most common. The Polish version of an offer page looks like this:
The English and German versions have exactly the same set of four hreflang tags in their head; only lang and the canonical change. The example shows the rules from Google's documentation:
- Each version points to itself and to all the others. The Polish page has an hreflang
plthat points to itself. - Return links. If page A points to page B, page B must point to page A. If it does not, Google may ignore those tags or interpret them incorrectly.
- Absolute URLs. With the protocol and the domain, not
/en/offer. - x-default for everyone else. The
x-defaultvalue points to the page for users whose language does not match any version. It can be a language selection page or the main version. On our sitex-defaultpoints to the English version.
With many versions and many pages a sitemap is often more convenient, because the tags then do not add weight to every HTML page. Each URL gets its own entry with the full set of equivalents, including itself:
The HTTP header is useful for files that are not HTML pages, for example a price list PDF in two languages:
Whichever method you choose, the tags should be generated automatically from a single source: a table that says which equivalents each page has. Hand-written hreflang falls out of sync with the first new page, and a mistake in one place breaks the return links in several. When a page has no translation yet, the generator simply leaves out the tag for the missing language.
hreflang and canonical: each version points to itself
Canonical and hreflang answer different questions. The canonical says which URL is the main version of the same content. Hreflang says which URLs are the same content in another language. That is why the canonical of the English version points to the English version, not the Polish one.
The opposite mistake usually comes from a template that builds the canonical from the URL of the Polish version. The English version then tells Google that its main version is the Polish page, in other words that it is itself a duplicate that does not need to be indexed. Hreflang says the opposite, and the two signals cancel each other out.
The second rule: hreflang only points to canonical URLs that return a 200 status. A URL that redirects, returns a 404, carries noindex or has a canonical pointing to another page is not a valid equivalent. This most often happens after URLs change in one language version while the other versions keep pointing to the old ones.
Versions in the same language for different countries need care. If de-DE and de-AT differ only in currency, Google may treat them as very similar pages. For similar content in the same language, Google's guide advises choosing a preferred version and using canonical together with hreflang. It is simpler either to make one de version or to make the versions genuinely different: in price, delivery, contact details and documents.
Translation vs localisation: what changes beyond the text
Translation turns text into another language. Localisation adapts the site to a reader in another country, and the text is only part of it. A site that has been translated without being localised looks fine until the first form, where a German customer cannot enter their postcode because the validation expects the Polish 00-000 format.
| Element | Translation only | Localisation |
|---|---|---|
| Prices | An amount in zloty with a translated description | The reader's currency and whether the price includes tax |
| Numbers and dates | Polish format: 12 345,67 and 4.09.2026 | The reader's format: 12,345.67 and 04/09/2026 in the UK, 9/4/2026 in the US |
| Forms | Translated labels | Postcode, phone number and tax number validation for the given country |
| Legal documents | None, or a link to the Polish terms | Terms, privacy policy and consent banner in the language of the version |
| Examples and references | Polish realities translated literally | Examples the reader understands, without local abbreviations and institutions |
| Emails and messages | Translated site, order confirmation in Polish | Transactional emails and error messages in the language the customer ordered in |
| Meta titles and descriptions | Translated from the Polish ones | Written for the phrases the reader searches for in their own language |
Number, currency and date formats should not be hard-coded. Browsers and Node.js have built-in formatting that depends on language and region:
Output in Node.js 22:
The same day is 04/09/2026 in the UK and 9/4/2026 in the US, so a date hard-coded in one format for every English-language version will mislead some of your readers. The spaces in the Polish and German amounts are non-breaking spaces, so the amount will not split in half at the end of a line. Intl also follows the Polish convention of not separating four-digit numbers with a space: the same function returns 1234,56 zł, not 1 234,56 zł.
Keywords take the most work. A phrase translated literally is often not the one people actually type. It is easiest to see in the reverse direction: a foreign company that translates "web development" literally into Polish as "rozwój stron" ("development of pages") will write correct copy around a phrase a Polish customer is unlikely to type, because Polish customers search for "tworzenie stron internetowych" ("building websites"). So for every language you have to research the phrases separately and write titles for them, in the language and script of the content, which Google also requires for title links.
Machine translation: when it helps and when it hurts
Machine translation is now good enough to produce a first draft. The problem is not the tool itself but publishing the output without reading it. In its spam policies, Google gives as an example of scaled content abuse the generation of many pages from content taken from other sources, including through automated translation, when those pages add little value for users. The example concerns content taken from elsewhere, not the translation of your own site, but the test is the same: does the page give the reader anything? The quality of a translation is judged by the reader, not by the tool.
A sensible process for a business without a translator on the team:
a list of service names, product names and industry terms with an agreed translation, so the same service does not end up with three different names.
machine translation or a translator, but always with the glossary.
mandatory for the home page, offer pages, forms and legal documents.
meta titles and descriptions written for the phrases people search for in that language, not translated from the Polish ones.
tags only for a version that is complete.
The worst option is a partially translated version. Google's guide points out that translating only the template elements while the main content stays in one language makes for a poor user experience, because the same content shows up in the results several times. It is better to publish an English version with ten fully translated pages than one with fifty, forty of which have an English menu and Polish text.
Language switcher and automatic redirects
Google's guide is explicit: avoid automatically redirecting users from one language version to another. The reason is technical. According to the documentation on locale-adaptive pages, Googlebot's default IP addresses appear to be based in the US, and by default the crawler does not send an Accept-Language header. A site that redirects by country or by browser language always shows the crawler the same version, and the others may never be fetched.
Redirects hurt people too. A Pole living in Germany who clicked a Polish result in Google wants to read the Polish page, not be sent to the German one because of their IP address. A suggestion without a redirect works better: a bar at the top of the page, in the reader's language, saying "This page is available in Polish" with a link to the equivalent, shown only when the browser language does not match the version of the page.
The language switcher itself follows a few rules as well:
- It leads to the equivalent page, not the home page. Someone reading the offer in Polish who switches to English should see the offer in English. When there is no equivalent, the switcher can lead to the home page of that version, but it should say so clearly.
- It uses plain
<a href>links. Google recommends linking between versions, and a switcher that works only through JavaScript, with no URL inhref, is not a link as far as the crawler is concerned. - Language names in their own language. "Deutsch", "Polski", "English", not flags. A flag stands for a country, not a language: English has no single flag, and Switzerland has four national languages.
The root of the domain, / without a language prefix, needs separate handling. It is a good place for a language selection page or for the default version marked as x-default.
Common hreflang mistakes and how to detect them
Search Console no longer has a report that showed hreflang errors, so the checking is up to you. The most common mistakes look like this:
| Mistake | Effect | How to catch it |
|---|---|---|
| Missing return link from one of the versions | Google may ignore the tags for that pair | The script below, checking every pair of versions |
| No self-reference | Incomplete set, the version is not part of the group | Compare the hreflang list with the page URL |
en-UK or UK code | The region part is ignored | The list of codes in the template, correctly en-GB |
A country code instead of a language, for example at for Austria | The tag is invalid, because the first code always denotes the language | Every code starts with a language code, de-AT for Austria |
| hreflang points to a URL that redirects or returns 404 | The equivalent is not a canonical page | The status code of every URL in hreflang |
| Canonical points to another language version | The version may be treated as a duplicate, and the signals contradict hreflang | Each version's canonical points to itself |
| Relative URLs in hreflang | Google requires absolute URLs | Every href starts with https:// |
| Every version points to the home page of the other languages | Users land on the home page instead of the equivalent | Equivalents from the translation table, not fixed URLs |
The Python script below checks most of these points for the URLs you pass to it. It needs no extra libraries; we tested it on Python 3.12. For each page it fetches the HTML, reads the canonical and hreflang tags, then fetches every equivalent and checks that it returns 200 without a redirect and points back to the starting page.
Run against three versions of a page, the German one with errors, it produces this output:
The script checks the tags in the HTML, not in the sitemap or in HTTP headers, and it compares URLs literally, so it treats /en/offer and /en/offer/ as different. That is deliberate, because to Google they are different URLs too. The easiest source for the list of URLs to check is the sitemap. A non-zero exit code lets you add the script to the checks that run on every deployment, so you catch an error before Google does.
How adding languages affects the SEO of your existing site
Adding an English version should not take traffic away from the Polish one, because the two versions answer queries in different languages and translated content is not a duplicate. The risk lies elsewhere: in changing the URLs of the Polish version while you add the new languages.
If the Polish site currently runs at URLs without a prefix and the new structure puts every Polish page under /pl/, then every existing URL changes. That is a full migration, with an inventory, a redirect map and monitoring, which we described in the post on website migration without losing rankings. The alternative is to leave the Polish version at its current URLs and add new subdirectories only for the other languages. The structure is less symmetrical, but you do not have to move pages that already rank.
It is worth monitoring each version separately. In the Search Console Performance report you can filter pages by part of the URL, for example /en/, and compare results in the Countries tab. You can also add a separate URL-prefix property for each subdirectory, which covers only the URLs that start with that prefix.
The last thing is maintenance. Every new page and every content change in the Polish version raises the question of when it will appear in the other languages. Without an agreed process the versions drift apart over time: the Polish one has new prices and services, the English one describes the offer from a year ago. Two up-to-date language versions are better than five, three of which nobody looks after.
Multilingual website SEO and hreflang: frequently asked questions
Do I need hreflang if I only have two language versions?
It is not mandatory, but with two versions implementing it takes a few lines in the template, and it helps Google show the right version to the right users. Without it, the English version may appear in results for people searching in Polish, and the other way round.
Is the English version of my site a duplicate of the Polish one?
No. Google treats localised versions as duplicates only when the main content has not been translated. A version only becomes a duplicate when it has a translated menu and Polish text.
Subdomain or subdirectory: which is better for multilingual SEO?
Google describes both structures as valid. Subdirectories are simpler to implement and maintain, and all versions benefit from one domain. Subdomains make sense when the versions run on different servers or are managed by different teams. The only thing Google advises against is URL parameters.
Should I redirect automatically based on browser language?
No. Google advises against automatic redirects between language versions, because the crawler usually connects from US addresses and without a language preference, so it might never see the other versions. Instead of redirecting, show a suggestion with a link to the version in the browser's language.
Is x-default mandatory?
No. It points to the page for users whose language does not match any version. It is useful when you have a language selection page, or one version that should be the default, for example English.
Is the lang attribute needed if Google does not use it?
Yes. Google detects the language from the content, but screen readers and browsers rely on the lang attribute, and setting it is a WCAG Level A requirement.
Where to start
Before the first translated page is created, write down three decisions: languages or countries, the URL structure, and the list of pages that will be fully translated in each language. These three points belong in your project brief, because the quote, the routing and the sitemap all depend on them. The other technical items to check before any site goes live are collected in our technical SEO checklist.
We have been writing software since 2020, and when a project is handed over you get the rights to the code and the documentation. How we work from brief to launch is described on the How we work page, and the scope of our services is in our offer. If you are planning language versions, or your existing ones are not performing in Google the way they should, get in touch.


