Language redirect based on Browser Settings

Users can set language preferences on their browser settings – to ideally view content in their preferred language. If you’d like your Sitecore solution to respect this preference, you can do so by overriding the Language Resolver processor. 

One key consideration here, is the potential difference you might come across, between the way the browser language setting is available, vs the way languages are available in Sitecore. 

In Sitecore, while adding a language, you will notice that all languages available OOTB include the country code. 

Thus resulting in your content being qualified by both Language & Country. 

However, in browsers, while certain languages are available to be set with Country specified, some of the languages are only available without country code. 

While most of these instances are for languages which are available only for one country even in Sitecore (Eg. Russian / Polish), there are also scenarios where this is not the case. Take Portuguese for example, on the browser, these are the available options: 

While in Sitecore: 

In this case, if a user has just Portuguese selected in their browser settings, and your Sitecore site Portuguese content is specific to either Portugal or Brazil – what content do you present to your user? 

This would be a business decision, and if you are to implement this functionality in your site, the first exercise would be to map available Sitecore languages in your site, with corresponding options in the latest / compatible browsers for your site. Based on this mapping, and the scenarios outlined above, you might need to get certain business decisions sorted before you implement this feature. 

For your reference, I have outlined below in Point #3, what we implemented as a solution for this. 

Coming to the actual implementation here, you will want to add your logic to the LanguageResolver override. 

Some points to note about the implementation I have provided below: 

  1. We added logic to enable language redirect only for certain sections / pages of the website. This way, the user would not only be redirected while accessing the home page for the first time, the redirect would also work with certain crucial sections / pages as configured – if the user were to land on them directly – say via google search results. (IsRedirectEnabledPatternMatch) 
  1. The user can configure multiple languages on their browser settings. These language settings all make it through the HttpContext – and we loop through these languages sequentially and return the first match found – depending on the Sitecore languages configured for our site. 
  1. For the scenario I have described previously – regarding some languages being available in the browser without country code, we added an extension to the language template in Sitecore, to let the content author choose if language redirect would work for this language without country code. Consider these examples: 
  1. If the user has Polish selected on their browser, the only option available is Polish without country code, and in this case we DID want Sitecore to do the redirect, since there is no other variant of Polish available in Sitecore as well – other than Polish (Poland) [pl-PL]. So the new field ‘Enable Language Detection By Iso’ was checked for this. This is used in the Language Resolver override code to allow redirecting for languages coming though without country code. 
  1. If the user has Portuguese selected on their browser, we DID NOT want the user to be redirected, since in Sitecore, we had more specific content available – specific to Portugal / Brazil. So this field ‘Enable Language Detection By Iso’ was disabled for both pt-PT and pt-BR in Sitecore. This way, redirect would work only if the user had country specific Portuguese language set on their browser. 
  1. Browser language detection kicks in only when the language has not been set in the browser cookies. This way, if one wanted to view the site in a different language, our code would not force redirect them based on their browser settings. 
  1. If there WAS a language match found based on everything mentioned above, and the user was redirected – the code would set the context language, which would set the language cookie as well. On subsequent requests sent, the code would not kick in, since the language cookie would be set.  

Here is the LanguageResolver for your reference: 

using Sitecore.Data.Items; 
using Sitecore.Data.Managers; 
using Sitecore.Diagnostics; 
using Sitecore.Globalization; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
 
namespace MySite.Core.IoC.Pipelines 
{ 
    public class LanguageResolver : Sitecore.Pipelines.HttpRequest.LanguageResolver 
    { 
        public override void Process(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args) 
        { 
            Assert.ArgumentNotNull(args, "args"); 
 
            string pathWithoutLanguage = StripLanguageFromPath(args.Context.Request.Url.PathAndQuery.ToLower()); 
 
            if (Sitecore.Context.Database != null 
                && !pathWithoutLanguage.StartsWith("/-/media/") 
                && !pathWithoutLanguage.StartsWith("/sitecore") 
                && !pathWithoutLanguage.EndsWith(".js") 
                && !pathWithoutLanguage.EndsWith(".css") 
                && IsRedirectEnabledPatternMatch(pathWithoutLanguage)) 
            { 
                Language lang = GetLanguageFromBrowser(args); 
                if (lang != null) 
                { 
                    Sitecore.Context.Language = lang; 
 
                    // if current language doesn't match with default site language, it will do a redirect 
                    if (Sitecore.Context.Language.Name != Sitecore.Context.Site.Language) 
                    { 
                        // do redirect 
                        UriBuilder uriBuilder = new UriBuilder(args.Context.Request.Url) 
                        { 
                            Path = Sitecore.Context.Language.Name.ToLower() + pathWithoutLanguage 
                        }; 
                        HttpContext.Current.Response.Redirect(uriBuilder.Uri.ToString(), true); 
                        args.AbortPipeline(); 
                    } 
                } 
                else 
                { 
                    base.Process(args); 
                } 
            } 
            else 
            { 
                base.Process(args); 
            } 
        } 
 
        private bool IsRedirectEnabledPatternMatch(string relativeUrl) 
        { 
            if (relativeUrl == "/") 
                return true; 
 
            List<string> redirectEnabledSections = Sitecore.Configuration.Settings 
                .GetAppSetting("MySiteBrowserLanguageDetection.Paths.Sections") 
                .Split('|').ToList(); 
 
            List<string> redirectEnabledPages = Sitecore.Configuration.Settings 
                .GetAppSetting("MySiteBrowserLanguageDetection.Paths.Pages") 
                .Split('|').ToList(); 
 
            relativeUrl = relativeUrl.Replace(' ', '-').Replace("%20", "").ToLower(); 
 
            if (redirectEnabledSections.Any(r => relativeUrl.Contains("/" + r.Replace(' ', '-').ToLower())) 
            || redirectEnabledPages.Any(r => (r.StartsWith("/") ? r : "/" + r) == relativeUrl)) 
            { 
                return true; 
            } 
 
            return false; 
        } 

        private string StripLanguageFromPath(string urlPathAndQuery) 
        { 
            if (Sitecore.Context.Database != null) 
            { 
                var languages = LanguageManager.GetLanguages(Sitecore.Context.Database); 
 
                foreach (Language language in languages) 
                { 
                    if (urlPathAndQuery.StartsWith("/" + language.Name + "/")) 
                    { 
                        return urlPathAndQuery.Replace("/" + language.Name + "/", "/"); 
                    } 
 
                    if (urlPathAndQuery == "/" + language.Name) 
                    { 
                        return urlPathAndQuery.Replace("/" + language.Name, "/"); 
                    } 
                } 
            } 
 
            return urlPathAndQuery; 
        } 
 
        public Language GetLanguageFromBrowser(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args) 
        { 
            if (IsBrowserLangDetectionAllowed(args) && Sitecore.Context.Database != null) 
            { 
                string[] userLanguages = args.Context.Request.UserLanguages; 
                if (userLanguages != null && userLanguages.Any()) 
                { 
                    var systemLanguages = LanguageManager.GetLanguages(Sitecore.Context.Database); 

                    foreach (string userLanguage in userLanguages) 
                    { 
                        // gets first part where information about language is stored 
                        string browserLanguage = userLanguage.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); 
                        if (!string.IsNullOrWhiteSpace(browserLanguage)) 
                        { 
                            foreach (Language systemLanguage in systemLanguages) 
                            { 
                                Item systemLanguageItem = 
                                    LanguageManager.GetLanguageItem(systemLanguage, Sitecore.Context.Database); 
 
                                if (systemLanguageItem != null) 
                                { 
                                    if (systemLanguageItem                                       .Fields[Constants.Fields.SystemLanguage.EnableLanguageDetectionByIso] 
                                            ?.Value != "1") 
                                    { 
                                        if (browserLanguage == systemLanguage.CultureInfo.Name) 
                                        { 
                                            return systemLanguage; 
                                        } 
                                    } 
                                    else 
                                    { 
                                        if (browserLanguage == systemLanguage.CultureInfo.TwoLetterISOLanguageName) 
                                        { 
                                            return systemLanguage; 
                                        } 
                                    } 
                                } 
                            } 
                        } 
                    } 
 
                    return LanguageManager.GetLanguage(Sitecore.Context.Site.Language); 
                } 
            } 
            return null; 
        } 
 
        protected bool IsBrowserLangDetectionAllowed(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args) 
        { 
            // site must be defined 
            return Sitecore.Context.Site != null 
                // lang cookie is not already set 
               && !args.Context.Request.Cookies.AllKeys.Contains(Sitecore.Context.Site.GetCookieKey("lang")) 
                // user agent is not robot 
                && !Sitecore.Analytics.Configuration.AnalyticsSettings.Robots.ExcludeList.ContainsUserAgent(args.Context.Request.UserAgent); 
        } 
    } 
} 

As for the configuration: 

<?xml version="1.0"?> 
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> 
  <sitecore> 
    <settings> 
        <add key="MySiteBrowserLanguageDetection.Paths.Sections" value="services|careers|about-us|locations"/> 
  	<add key="MySiteBrowserLanguageDetection.Paths.Pages" value=""/> 
    </settings> 
    <pipelines> 
      <httpRequestBegin> 
        <processor type="Sitecore.Pipelines.HttpRequest.LanguageResolver, Sitecore.Kernel"> 
          <patch:attribute name="type">MySite.Core.IoC.Pipelines.LanguageResolver, MySite.Core</patch:attribute> 
        </processor> 
      </httpRequestBegin> 
    </pipelines> 
  </sitecore> 
</configuration> 

Based on your business requirements, this code can obviously be tweaked as needed, but you get the picture! 

Leave a Reply

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

WordPress.com Logo

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

Google photo

You are commenting using your Google 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 )

Connecting to %s