Patterns for navigation controls, part 4

I was going to stop at three posts on simple patterns for navigation (Parts one,two and three are here) – but earlier this week I realised I’d not covered an important topic in navigation for Sitecore-based websites: How you can navigate between different language versions of your sites and pages. So this week, I’ll start looking at language navigation.

There are two common patterns for how you might structure your content in Sitecore if you have a multi-lingual site. Depending on what sort of content you’re dealing with, you might choose to:

  • You have a single content tree, where each page can have multiple languages
    This is Sitecore’s “normal” approach to multi-language sites. It assumes that most (if not all) pages will be translated into multiple languages, and it works best when all your pages exist in all your languages.
  • You have multiple content trees, where each page has one language
    This works better where the structure of your content one language differs dramatically to the structure of content you need in other languages.

(Technically, there’s also a third option, where you have multiple content trees which may also have multiple languages – but that’s just a combination of the two ideas I’ll present)

The core navigation controls for these sites work in exactly the same way as the ideas I’ve presented in previous posts. But if you have multiple languages, then you need some code to display links to let the users swap between languages. And you need slightly different code for these two scenarios. So this week I’ll look at the first of these:

Swapping between different language versions of the current page

To achieve this we need to provide a set of links for the different language versions of the context page. There is a Languages property on each Sitecore Item which gives you a list of the languages themselves, but this doesn’t give you the content for the different versions. Also an Item can have zero versions in a language – so we need to filter the data to make sure we don’t show a link to an unpublished or missing language version.

The code for this is fairly simple. Starting with some very basic mark-up:

<asp:Repeater runat="server" ID="languageRepeater">
            <asp:HyperLink runat="server" id="languageLink" />

And adding some code-behind:

protected void Page_Load(object sender, EventArgs e)
    languageRepeater.DataSource = Sitecore.Context.Item.Languages;
    languageRepeater.ItemDataBound += languageRepeater_ItemDataBound;


private void languageRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
    if(e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item)
        var languageLink = e.Item.FindControl("languageLink") as HyperLink;
        var language = e.Item.DataItem as Language;

        var options = new Sitecore.Links.UrlOptions{ LanguageEmbedding=Sitecore.Links.LanguageEmbedding.Always, Language = language };

        var itm = Sitecore.Context.Database.GetItem(Sitecore.Context.Item.ID, language);

        if(itm.Versions.Count > 0)
            languageLink.Text = itm.DisplayName;
            languageLink.NavigateUrl = Sitecore.Links.LinkManager.GetItemUrl(itm, options);
            e.Item.Visible = false;

The data source for our repeater is the list of Languages that Sitecore has recorded for the current page. When each of those languages gets processed we need to find the right data to display.

For each language, having grabbed references to the link we plan to display and to the language that the repeater has passed us, we construct a UrlOptions object. This is used to customise the generation of links to our page in a minute: we need to ensure that the language data we’re processing is added to the URL.

Then the code reloads the context item in our current language. This ensures we have the data for the correct language version of the item for processing. With this loaded, we test to see if the number of versions the Item object holds is greater than zero. If there are none then we don’t want to show a link to this language – the appropriate data hasn’t been created or published. In this case we hide the repeater item from view. But if we do have a version then we can bind the DisplayName and the url (generated with our options) to our link. DisplayName is a good choice for what to display because it’s a field you can translate – but you might choose to use another field if you have specific “navigation title” data in your site’s schema.

If we create an item which has these versions:


Then the result of running this code is to generate: (Please excuse my Google Translated page titles – chances are they make no sense if you speak Japanese or German – sorry…)

Language Links

Note that the “English UK” language which is present in the Languages drop-down is not shown in the navigation data – since it has no version to display.

Hovering over or clicking those links will reveal a URL in the format:


By passing the language data to the LinkManager when generating the URL, the data describing which language to show has been included in the URL. This is not the only way that language data can be inserted into the URL however. It can also be added to the sc_lang querystring parameter if preferred – though that seems less useful from an SEO perspective to me.

Note that the url is using the Name of the item by default – which is not a translated value. Hence the Japanese link has an English page name in it. It is possible to translate the URLs themselves by enabling the configuration property for “use display names in URLs“.

So with the basic logic described here you can create a component that matches the look and feel of your site.

Next week, I’ll look at an approach to navigation between separate content trees for languages.


One thought on “Patterns for navigation controls, part 4

  1. Pingback: Patterns for navigation controls, part 5 | Jeremy Davis

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s