Build a simple contextual language switcher

Heads Up!

This article is several years old now, and much has happened since then, so please keep that in mind while reading it.

Language Picker

For a current project, I had to implement a contextual language switcher, giving the user the ability to switch the language of the current page.

Briefly, I was considering Matt Brailsford's awesome package Vorto. And I even figured out how to use it to store data from the even more awesome grid editor from 7.2. If you're interested in learning this too, look at this forum post. However, I didn't find it fitting and went back to the good old "one root node per language" approach.

So here's how my content structure is set up:

Language Versions

I have the frontpage of each language set up as root nodes, with domain settings, language etc. set up. All Subpages have a multinode treepicker property, for setting up different language versions. This gives the editor the ability to precisely pick which page is the equivalent in other languages. This way I can have different structures in different languages etc.

In my templates I have implemented a language switcher, which uses the before mentioned multinode treepicker as a way to output the correct link. If no language version is set, it just links to the frontpage.

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@using umbraco.cms.businesslogic.web
@using umbraco.MacroEngines
@using System.Text.RegularExpressions

@{
    var currentLang = Domain.GetDomainsById(Model.Content.AncestorOrSelf(1).Id)[0].Language;

    var versionIds = Model.Content.GetPropertyValue("languageVersions").ToString().Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
    
    var contentRoot = new DynamicNode(-1);
    var languages = contentRoot.Descendants("Frontpage");
}

<button type="button" class="btn btn--navbar dropdown-toggle flag flag--@currentLang.CultureAlias.Substring(0,2)" data-toggle="dropdown"></button>

<ul class="dropdown-menu languageselector pull-right" role="menu">
    @foreach (var lang in languages)
    {

        var thisLang = Domain.GetDomainsById(lang.Id)[0].Language;

        var thisLangName = Regex.Replace(thisLang.FriendlyName,"(,(.*)|[(].*)","").Trim();
        var thisLangDisplay = thisLangName;
        
        switch(thisLangName) {
            case "Danish":
                thisLangDisplay = "Dansk";
                break;
            case "Swedish":
                thisLangDisplay = "Svenska";
                break;
            case "Norwegian":
                thisLangDisplay = "Norsk";
                break;
        }
        
        if (thisLang == currentLang)
        {
            <li class="active"><a href="@Model.Content.Url"><i class="flag flag--@thisLang.CultureAlias.Substring(0,2)"></i>
                @thisLangDisplay
            </a></li>
        }
        else {
            var link = lang.Url;

            if (versionIds.Any())
            {
                foreach (var versionId in versionIds)
                {
                    var version = Umbraco.TypedContent(versionId);
                    if (version != null)
                    {
                        if (version.AncestorOrSelf(1).Id == lang.Id)
                        {
                            link = v.Url;
                            break;
                        }
                    }
                }
            }
            
            <li><a href="@link">
                <i class="flag flag--@thisLang.CultureAlias.Substring(0,2)"></i>
                @thisLangDisplay
            </a></li>   
        }
    }
</ul>

This basically boils down to looping through the different rootnodes, get their language, and then loop through the language versions of the current page until it meets a match. This means if two pages from the same language are set as language versions, the first one gets the link.

Another usecase for a language version property could also be for setting canonical language URLs for SEO benefits. This could be as simple as:

if (versionIds.Any())
{
    foreach (var versionId in versionIds)
    {
        var version = Umbraco.TypedContent(versionId);
        if (version != null)
        {
        	var lang = Domain.GetDomainsById(version.AncestorOrSelf(1).Id)[0].Language.CultureAlias;

        	<link rel="alternate" href="@version.Url" hreflang="@lang" />
	    }
	}
}

Feel free to comment. Some of my code examples could probably be done in a better way :)

Søren Kottal

Søren is on Twitter as