Guide: hreflang and multilingual¶
django-seo-suite integrates with Django's i18n_patterns to produce
consistent hreflang alternates on the page and in the sitemap. Both use the
same LANGUAGES configuration as their source of truth.
How it works¶
build_hreflang_alternates calls Django's translate_url for every language
in settings.LANGUAGES and produces a list of
HreflangAlt(lang, absolute_url) named tuples. Pass this list to
SeoMetadata.partial(hreflang=...) and the {% seo_hreflang %} tag renders
the <link rel="alternate" hreflang="..."> elements.
Setup¶
Ensure your URL configuration uses i18n_patterns:
# urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path
urlpatterns = i18n_patterns(
path("articles/<slug:slug>/", ArticleDetail.as_view(), name="article-detail"),
path("articles/", ArticleList.as_view(), name="article-list"),
)
Set LANGUAGES and LANGUAGE_CODE in settings:
LANGUAGES = [
("en", "English"),
("fr", "French"),
("de", "German"),
]
LANGUAGE_CODE = "en"
USE_I18N = True
Adding hreflang to a detail view¶
from django.views.generic import DetailView
from seo_suite.mixins import SeoViewMixin
from seo_suite.hreflang import build_hreflang_alternates
from seo_suite.metadata import SeoMetadata
from .models import Article
class ArticleDetail(SeoViewMixin, DetailView):
model = Article
def get_seo_metadata(self, context=None):
alternates = build_hreflang_alternates(
self.request.path,
self.request,
)
return SeoMetadata.partial(hreflang=alternates)
For a request at /en/articles/my-article/ with LANGUAGES = [("en", …),
("fr", …)] and LANGUAGE_CODE = "en", this emits:
<link rel="alternate" hreflang="en" href="https://example.com/en/articles/my-article/">
<link rel="alternate" hreflang="fr" href="https://example.com/fr/articles/my-article/">
<link rel="alternate" hreflang="x-default" href="https://example.com/en/articles/my-article/">
Controlling x-default¶
The x-default alternate is added automatically when
SEO_SUITE["HREFLANG_X_DEFAULT"] is True (the default). It points to the
URL for settings.LANGUAGE_CODE.
To disable it globally:
To disable it for a single call:
Custom language list¶
Pass a languages argument to restrict or reorder the alternates:
alternates = build_hreflang_alternates(
self.request.path,
self.request,
languages=["en", "fr"], # only these two, regardless of LANGUAGES
)
Adding hreflang to a list view¶
from django.views.generic import ListView
from seo_suite.mixins import SeoListViewMixin
from seo_suite.hreflang import build_hreflang_alternates
from seo_suite.metadata import SeoMetadata
class ArticleList(SeoListViewMixin, ListView):
model = Article
seo_title = "Articles"
def get_seo_metadata(self, context=None):
alternates = build_hreflang_alternates(self.request.path, self.request)
return SeoMetadata.partial(hreflang=alternates)
Note
hreflang is a replace-wins field: when a higher-priority layer
provides a hreflang list, it replaces any list from lower layers entirely.
Only set hreflang in one layer per page (usually the view).
Template rendering¶
{% seo_head %} renders hreflang automatically. The tag used internally is
{% seo_hreflang %}, which you can use standalone:
Sitemap hreflang¶
For hreflang in sitemaps, use Django's built-in sitemap i18n support together
with SeoSitemap. See the Sitemaps guide for a complete example.