Sitecore - alternate hreflang links for SXA

Posted 6 Feb 2022 by Marek Musielak

sitecore hreflang alternate links for sxa

When you run a multilanguage site on Sitecore, you definitely want to tell Google about all the available translations of your content. Thanks to that Google Search will be able to point users to the most appropriate version of your pages. How you can achieve this? By adding rel="alternate" hreflang links in <head> part of your html.

This article explains how you can achieve that if you use Sitecore SXA. It assumes basic knowledge about SXA and that you know how to create new SXA renderings, add them to available renderings of your site and edit `Metadata` partial design.

First thing we need is a model class containing IEnumerable<KeyValuePair<string, string>> Links property only. We're going to keep language codes and corresponding URLs there:

public class HreflangLinksModel
{
    public IEnumerable<KeyValuePair<string, string>> Links { get; set; }
}

Next comes interface for repository and repository itself. We build urlOptions with AlwaysIncludeServerUrl set to true and LanguageEmbedding set to LanguageEmbedding.Always to make sure we receive absolute URL with language included in it. We loop through all languages, check if item has any version in given language and build language specific url:

public interface IHreflangLinksRepository : IAbstractRepository<HreflangLinksModel>
{
    HreflangLinksModel GetModel(Item item);
}

class HreflangLinksRepository : IHreflangLinksRepository
{
    protected IContext Context { get; }

    protected IUrlOptionsProvider UrlOptionsProvider { get; }

    public HreflangLinksRepository(IContext context, IUrlOptionsProvider provider)
    {
        Context = context;
        UrlOptionsProvider = provider;
    }

    public HreflangLinksModel GetModel(Item item)
    {
        var links = new List<KeyValuePair<string, string>>();

        var urlOptions = UrlOptionsProvider.GetUrlOptions();
        urlOptions.AlwaysIncludeServerUrl = true;
        urlOptions.LanguageEmbedding = LanguageEmbedding.Always;

        foreach (var lang in item.Languages)
        {
            var langItem = item.Database.GetItem(item.ID, lang);

            if (langItem.Versions.Count > 0)
            {
                urlOptions.Language = lang;
                var itemUrl = LinkManager.GetItemUrl(langItem, urlOptions);
                links.Add(new KeyValuePair<string, string>(lang.Name, itemUrl));
            }
        }

        return new HreflangLinksModel
        {
            Links = links
        };
    }

    HreflangLinksModel IAbstractRepository<HreflangLinksModel>.GetModel()
    {
        return GetModel(Context.Item);
    }
}

When we have model and repository, we can create a controller class inheriting from Sitecore SXA StandardController. Nothing surprising there - just inject our repository and use it to build the model:

public class HreflangLinksController : Sitecore.XA.Foundation.Mvc.Controllers.StandardController
{
    private readonly IHreflangLinksRepository _repository;

    public HreflangLinksController(IHreflangLinksRepository repository) => _repository = repository;

    protected override object GetModel() => _repository.GetModel();
}

Next thing we need is a view file. Create Views\HreflangLinks\HreflangLinks.cshtml and add content as below. It loops through Model.Links and generates rel="alternate" hreflang links:

@model MyAssembly.MyNamespace.Models.HreflangLinksModel

@{
    Layout = Sitecore.Configuration.Settings.GetSetting("XA.Foundation.Presentation.MetaComponentLayoutPath", "../SXA/Meta Component Layout.cshtml");
}

@foreach (var link in Model.Links)
{
    <link rel="alternate" hreflang="@link.Key" href="@link.Value" />
}

Finally, we need to register our repository and controller in Sitecore dependency injection with Sitecore config patch file:

<configuration>
  <sitecore>
    <services>
      <register
        serviceType="MyAssembly.MyNamespace.IHreflangLinksRepository, MyAssembly"
        implementationType="MyAssembly.MyNamespace.HreflangLinksRepository, MyAssembly" />
      <register
        serviceType="MyAssembly.MyNamespace.HreflangLinksController, MyAssembly"
        implementationType="MyAssembly.MyNamespace.HreflangLinksController, MyAssembly" />
    </services>
  </sitecore>
</configuration>

When everything is ready code wise, we can create new ControllerRendering for HreflangLinks and add it to metadata partial design:

sitecore hreflang links controller

sitecore hreflang links on metadata partial design

And that's it. Every page which uses metadata partial design will generate rel="alternate" hreflang links in <head> tag:

sitecore hreflang alternate links for sxa

Comments? Find me on or Sitecore Chat