Multilingual SEO: hreflang Implementation and Canonical Strategy for Bilingual Sites

A practical SEO guide for multilingual sites: what JekCMS gives you out of the box (output_canonical_tag, a base hreflang link) versus what you build yourself — full hreflang tag sets, x-default, per-language sitemaps, og:locale alternates — plus the mistakes that break rankings.

Multilingual SEO: hreflang Implementation and Canonical Strategy for Bilingual Sites

A practical SEO guide for multilingual sites: what JekCMS gives you out of the box (output_canonical_tag, a base hreflang link) versus what you build yourself — full hreflang tag sets, x-default, per-language sitemaps, og:locale alternates — plus the mistakes that break rankings.

The Multilingual SEO Problem

When you publish the same content in two languages on the same domain, search engines face a dilemma: which version should rank for which audience? Without explicit signals, Google might show your Turkish page to English speakers, or worse, treat both versions as duplicate content and hurt your rankings for both.

jekcms.com itself runs into this directly — every page exists in both Turkish and English on the same domain, differentiated by URL structure. This guide covers the pieces JekCMS gives you out of the box for that setup, and the pieces you build yourself in your theme.

URL Structure: Subdirectory vs. Subdomain vs. Parameter

There are three standard approaches for multilingual URLs:

  • example.com/en/about — Subdirectory
  • en.example.com/about — Subdomain
  • example.com/about?lang=en — Parameter (avoid this)

Subdirectories are the safer default because they consolidate domain authority — every backlink to any language version strengthens the whole domain. Subdomains split authority, since Google sometimes treats them as separate sites. Parameters are the worst option, because Google often ignores URL parameters for indexing purposes. jekcms.com itself uses this pattern: Turkish as the default language with unprefixed URLs, English behind an /en/ prefix.

Canonical URLs: What JekCMS Gives You

A common mistake is using a single canonical URL for both language versions. That tells Google one version is the "original" and the other a copy — exactly the opposite of what you want. Each language version needs its own canonical URL pointing to itself.

JekCMS's real helper for this is output_canonical_tag(?string $url = null): void — call it in your theme's <head> and it prints a self-referencing canonical link for the current page:

<?php output_canonical_tag(); ?>

<!-- On the Turkish page -->
<link rel="canonical" href="https://jekcms.com/blog/jekcms-rehberi" />

<!-- On the English page -->
<link rel="canonical" href="https://jekcms.com/en/blog/jekcms-guide" />

Pass an explicit URL as the optional argument if you need to override the auto-detected current URL — useful on pages assembled from query parameters you want stripped from the canonical.

hreflang: What's Built In, and What You Add Yourself

The hreflang attribute tells search engines which language and region a page targets. JekCMS's language switcher (in lang_helpers.php) emits a single hreflang link pointing at the alternate-language version of the current page — a useful starting signal, but not the full bidirectional set Google's documentation describes, and there's no built-in x-default tag or automatic reciprocal linking between language versions out of the box.

If you want the complete implementation — self-referencing tags on every page, a reciprocal link from each language version to the other, and an x-default fallback — you add it yourself in your theme's header partial:

<?php
function output_hreflang_tags(string $slugTr, string $slugEn): void {
    $baseUrl = rtrim(SITE_URL, '/');
    $trUrl = $baseUrl . '/blog/' . $slugTr;
    $enUrl = $baseUrl . '/en/blog/' . $slugEn;

    echo '<link rel="alternate" hreflang="tr" href="' . $trUrl . '" />' . "\n";
    echo '<link rel="alternate" hreflang="en" href="' . $enUrl . '" />' . "\n";
    echo '<link rel="alternate" hreflang="x-default" href="' . $trUrl . '" />' . "\n";
}
?>

<?php output_hreflang_tags($post['slug_tr'], $post['slug_en']); ?>

Three details matter once you've added this:

  1. Self-referencing: The Turkish page includes a hreflang pointing to itself (hreflang="tr"). Google expects this — every page should reference all language versions, including its own.
  2. Bidirectional: Both the Turkish and English pages must include the exact same set of hreflang tags. If the Turkish page points to the English version but the English page doesn't point back, Google ignores the signal.
  3. x-default: Tells search engines which version to show when nothing else matches the user's language. We set it to Turkish since that's the primary audience on jekcms.com.

Per-Language Sitemaps

Google recommends hreflang annotations in your sitemap as a second signal alongside the HTML tags. JekCMS's built-in Sitemap.php generates standard XML sitemaps for posts, categories, pages, and tags, but it doesn't emit hreflang xhtml:link annotations automatically — if you want them, you generate a dedicated sitemap for your bilingual content yourself:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <url>
        <loc>https://jekcms.com/blog/jekcms-rehberi</loc>
        <xhtml:link rel="alternate" hreflang="tr"
            href="https://jekcms.com/blog/jekcms-rehberi" />
        <xhtml:link rel="alternate" hreflang="en"
            href="https://jekcms.com/en/blog/jekcms-guide" />
        <xhtml:link rel="alternate" hreflang="x-default"
            href="https://jekcms.com/blog/jekcms-rehberi" />
        <lastmod>2026-03-20</lastmod>
    </url>
    <url>
        <loc>https://jekcms.com/en/blog/jekcms-guide</loc>
        <xhtml:link rel="alternate" hreflang="tr"
            href="https://jekcms.com/blog/jekcms-rehberi" />
        <xhtml:link rel="alternate" hreflang="en"
            href="https://jekcms.com/en/blog/jekcms-guide" />
        <xhtml:link rel="alternate" hreflang="x-default"
            href="https://jekcms.com/blog/jekcms-rehberi" />
        <lastmod>2026-03-20</lastmod>
    </url>
</urlset>

Both entries include the full set of hreflang annotations, referencing all language variants — the same pairing you built in the HTML tags above. You can build this loop over your posts table with a small custom script, since it isn't something the platform generates for you.

Open Graph and Social Sharing

Social platforms use Open Graph tags to generate link previews. For bilingual content, add language-specific OG tags per page:

<!-- Turkish page -->
<meta property="og:locale" content="tr_TR" />
<meta property="og:locale:alternate" content="en_US" />
<meta property="og:title" content="<?= htmlspecialchars($post['title_tr']) ?>" />
<meta property="og:url" content="<?= $trUrl ?>" />

<!-- English page -->
<meta property="og:locale" content="en_US" />
<meta property="og:locale:alternate" content="tr_TR" />
<meta property="og:title" content="<?= htmlspecialchars($post['title_en']) ?>" />
<meta property="og:url" content="<?= $enUrl ?>" />

Like the hreflang tags, these aren't generated automatically — you add them to your theme's head partial alongside the canonical and hreflang output. The og:locale tag uses the full locale code (e.g. tr_TR) rather than just the language code; og:locale:alternate tells platforms another language version exists.

Common Mistakes and How to Avoid Them

The Turkish page has hreflang="en" pointing to the English version, but the English page doesn't have hreflang="tr" pointing back. Google ignores unconfirmed hreflang signals — both pages must reference each other.

Fix: Use one shared function that generates the complete set of hreflang tags, called from both templates, so consistency isn't something you have to remember by hand.

Mistake 2: Wrong Language Codes

Using hreflang="tr-TR" instead of hreflang="tr". The region subtag (-TR) is only needed when you have multiple regional variants of the same language (e.g. pt-BR vs pt-PT). For Turkish and English without regional targeting, just use tr and en.

Mistake 3: hreflang on Non-Canonical URLs

If a page redirects (301) to another URL, the hreflang tags belong on the final destination, not the redirecting URL. Google ignores hreflang on pages that redirect.

Mistake 4: Different Content Between Languages

Google expects hreflang-linked pages to be translations of each other, not entirely different content. If your English "About" page says something different from your Turkish one, Google may ignore the hreflang connection. The content should be equivalent in meaning, even if not word-for-word.

Mistake 5: Mixing Canonical and hreflang Signals

Pointing the English page's canonical at the Turkish page while also adding hreflang tags creates a conflict — canonical says "the Turkish page is the original," hreflang says "these are equal alternatives." Keep canonical self-referencing for each language version; that's exactly what output_canonical_tag() does by default.

Google Search Console Validation

After deploying hreflang tags, validate them through Google Search Console:

  1. Go to Search Console > International Targeting
  2. Check the Language tab for hreflang errors
  3. Common errors: "No return tag," "Unknown language code," "Tag on non-canonical page"
  4. Use URL Inspection to verify specific pages show the correct hreflang annotations

It takes Google a few weeks to fully process hreflang changes. During that window you might see temporary ranking fluctuations as Google re-evaluates which version to show for which queries — don't panic and start changing things mid-adjustment.

Testing Your Implementation

Before relying on Search Console, which has a processing delay, test your implementation immediately with these methods:

  • View page source: Check that both hreflang tags and the canonical tag are present in the <head> section
  • Google's Rich Results Test: Enter your URL and inspect the rendered HTML for hreflang tags
  • Screaming Frog: Crawl your site and check the hreflang report for inconsistencies
  • Manual cross-check: Visit the Turkish page, note the hreflang URLs, then visit each one and verify it links back

Multilingual SEO requires precision, but jekcms's real building blocks — output_canonical_tag() and a base hreflang link in the language switcher — cover the easy part. The rest is a small, well-defined amount of theme code you add once and reuse on every bilingual template.

Order Today

One-time payment, lifetime access. Setup in 30 minutes.

View Pricing
  • Setup and live in 30 minutes
  • 14+ professional themes
  • n8n automation integration
  • Automatic SEO — Sitemap, Schema.org
  • iyzico payment support

Be the first to know

New features, release notes & CMS guides — a couple of emails a month, no spam.