I recently had a request to detect the browser language for a multilingual site I support. Now this certainly isn't anything new and here's the canonical/obligatory John West post written about it. I'm not trying to rewrite what he's done. My solution is different and deals with different needs. The requirements I had were:
*set the context language to the browser language if the site has item versions in that language
*if the user changes the language through a dropdown selector maintain the new language settings
These seemingly innocuous requirements add more complexity than you'd expect. The challenge was really around maintaining the language once it's been set and to not continue changing to the browser settings. I accomplished this by only using the browser settings if the lan
guage cookie had not been set. This makes the detection more passive in that it's only really determined on the first page load. It prevents a lot of jumpy behavior between languages when someone browses back to the homepage which doesn't contain the language code in the url.
So to implement this I overrode the LanguageResolver in my include config:
<processor type="Sitecore.Pipelines.HttpRequest.LanguageResolver, Sitecore.Kernel"> <patch:attribute name="type">MyLibrary.Pipelines.HttpRequest.LanguageResolver, MyLibrary</patch:attribute></processor>
Next is the class that rewrites the functionality of the stock Sitecore LanguageResolver. I typically don't extend these classes since they may change over time and they also contain so many private methods that I basically end up having to copy most of it anyway. Also bear in mind that my system stores the supported languages for a site within the site's settings so I can compare against that stored list. This code was modified from my version to check to see if the homepage has a version of the language. This could be volatile since a site might not support a language just because there is a version of the homepage in that language. So you should consider modifying the GetBrowserLang() method to query the correct language versions for each of your sites if you can't make those assumptions.
This also uses a method of retrieving the home item for a site that assumes the SiteContext.RootPath and SiteContext.StartItem fit neatly together to make a sitecore item path. Make sure this is going to work for your system.
namespace MyLibrary.Pipelines.HttpRequest { using Sitecore; using Sitecore.Diagnostics; using Sitecore.Globalization; using System; using System.Web; using Sitecore.Pipelines.HttpRequest; using System.Threading; using System.Linq; using Sitecore.Data.Managers; using Sitecore.Data; using Sitecore.Data.Fields; using System.Collections.Generic; using Sitecore.Sites; using Sitecore.Data.Items; using Sitecore.Security.Domains; using Sitecore.Web.UI.Sheer; using Sitecore.Layouts; using Sitecore.Configuration; public class LanguageResolver : HttpRequestProcessor { private Language GetQStringLang(HttpRequest request) { Language language; string str = request.QueryString["sc_lang"]; if (string.IsNullOrEmpty(str)) return null; if (!Language.TryParse(str, out language)) return null; return language; } private Language GetBrowserLang(HttpRequest request) { //get list of browser langs string[] bLangs = request.UserLanguages; if (bLangs == null || !bLangs.Any()) return null; //get the list of supported langs for this site Item homeItem = GetHomeItem(); if (homeItem == null) return null; //loop through browser languages and see if the site supports any List<string> langIDs = new List<string>(); foreach (string s in bLangs) { //split any browser weighting that is applied ie: nl;q0.5 string[] x = s.Split(new [] {";"}, StringSplitOptions.RemoveEmptyEntries); string langCode = (x.Any()) ? x.First() : string.Empty; if (string.IsNullOrEmpty(langCode)) continue; //try to get the language Language language = null; if (!Language.TryParse(langCode, out language)) continue; //then try to get the language item id from the language or two letter iso code ID langID = LanguageManager.GetLanguageItemId(language, Sitecore.Context.Database); if (ID.IsNullOrEmpty(langID)) { //sometimes the language found is slightly different than official language item used in SC language = LanguageManager.GetLanguage(language.CultureInfo.TwoLetterISOLanguageName); langID = LanguageManager.GetLanguageItemId(language, Sitecore.Context.Database); } //checks to see if the language item is a valid sitecore item if (ID.IsNullOrEmpty(langID)) continue; //if you've got a version in this language then use that language Item langVersion = Sitecore.Context.Database.GetItem(homeItem.ID, language); if (langVersion != null) return language; } return null; } private Language GetLanguageFromRequest(HttpRequest request) { //check querystring Language qsLang = GetQStringLang(request); if (qsLang != null) return qsLang; //only check if you're on the frontend and the cookie hasn't been set if (IsNotSitecore() && Sitecore.Context.PageMode.IsNormal &&!IsLanguageSetFromCookie()) { //then check browser language settings Language browserLang = GetBrowserLang(request); if (browserLang != null) return browserLang; } //default return Context.Data.FilePathLanguage; } public override void Process(HttpRequestArgs args) { Assert.ArgumentNotNull(args, "args"); Language languageFromRequest = this.GetLanguageFromRequest(args.Context.Request); if (languageFromRequest != null) { Context.Language = languageFromRequest; Tracer.Info("Language changed to "" + languageFromRequest.Name + "" as the query string of the current request contains language (sc_lang)."); } } protected static Item GetHomeItem() { SiteContext sc = Sitecore.Context.Site; return Sitecore.Context.Database.GetItem(string.Format("{0}{1}", sc.RootPath, sc.StartItem)); } protected static bool IsNotSitecore() { return (!Sitecore.Context.Domain.Name.ToLower().Contains("sitecore")); } protected static bool IsLanguageSetFromCookie() { string c = Sitecore.Web.WebUtil.GetCookieValue(Sitecore.Context.Site.GetCookieKey("lang")); return (!String.IsNullOrEmpty(c)); } }}