The most common mistake is to think of a multilingual site as "translating the UI strings". For search engines, each language is a separate set of pages, each needing its own URL, title, description and alternate-language links. This post walks through the structure we also use on this website.

URL strategy: prefix or subdomain?

Three common options:

Method Example Note
Subdirectory site.com/en/services One domain, shared authority. Recommended.
Subdomain en.site.com Treated like a separate site.
Separate domain site.co.uk For country targeting; costly.

No prefix for the default language (/hizmetler) and a prefix for the others (/en/services) gives the cleanest result for both users and SEO.

Defining routes per language

In Laravel we define routes in a loop, named separately per language. URL segments change by language too: /hizmetler in Turkish, /services in English.

foreach (config('tuzun.locales') as $locale => $meta) {
    $seg = config("tuzun.segments.{$locale}");

    Route::prefix($meta['prefix'])
        ->name("{$locale}.")
        ->middleware("locale:{$locale}")
        ->group(function () use ($seg) {
            Route::get('/', [PageController::class, 'home'])->name('home');
            Route::get("/{$seg['services']}", [ServiceController::class, 'index'])->name('services.index');
            // ...
        });
}

route('tr.services.index') and route('en.services.index') now work independently; a small lroute() helper picks the right one for the active locale.

Translatable slugs

For blog posts we keep separate slug_tr and slug_en columns. If a post has no English version, slug_en stays empty and the post doesn't appear in English listings. This keeps "half-translated" pages away from search engines.

hreflang and canonical

On every page:

  • <link rel="canonical"> points to the page's own URL.
  • A <link rel="alternate" hreflang="tr|en"> exists for each language.
  • x-default points to the default language (Turkish).

hreflang must be reciprocal: if the TR page points to EN, the EN page must point back to TR. For untranslated posts we only emit links for the languages that exist.

Structured data

We add Organization, WebSite, BreadcrumbList, Service, BlogPosting and FAQPage schemas as JSON-LD depending on page type, with the language in inLanguage. Validate with the Rich Results Test.

Sitemap

A single sitemap.xml lists xhtml:link alternates for every URL. This lets Google see the language relationship through the sitemap too and protects crawl budget on large sites.

Summary

  1. Default language without prefix, others with.
  2. Routes and URL segments per language.
  3. Per-language slugs; untranslated content is ignored in that language.
  4. Canonical + reciprocal hreflang + x-default on every page.
  5. Language info in JSON-LD and sitemap.

This structure turns adding a third language later into a one-line configuration change.

Berkcan Tüzün

Founder · Fullstack Developer & Technology Solutions Architect · berkcan.tr