Jens Qvist
Dec 19, 2011
  4476
(2 votes)

Creating a BrowserLanguageCriteria for Visitor groups.

This is my first blog post ever, on any site I think! So I’m a bit nervous, but I hope that someone will find it interesting. So here goes!

This morning I decided that I should learn how to create custom criterias for Visitor groups. I started out by reading an article by Ted Nyberg, to get an idea on how it worked.

But I was short on ideas on what to build. Until I stumbled on http://criteriapack.codeplex.com/, where they had a list on planned criterias. Perfect I thought, and choose to create the BrowserLanguageCriterion. I used BrowserOSCriterion as a template for my own code, and started working.

language

So first I needed a settings class as mentioned in Teds article. I decided that the editor would want to choose a language from a dropdownlist and every culture/language in the browser should be availible. It was easy figuring out how to get all cultures. The problem for me was to get all cultures into an enum, since I started out using EnumSelectionFactory. Which I guess require an enum?

[DojoWidget(SelectionFactoryType = typeof(EnumSelectionFactory), 
        AdditionalOptions = "{ selectOnClick: true }")]
public string BrowserLanguage { get; set; }

To be honest, I haven’t used Enums much and had to google if there was a possibility to convert a list/array of languages to an enum. Apparently not, according to some forum post.

My solution was kinda easy once I figured out how to do it. I created my own SelectionFactoryType.

public class LanguageFactory : ISelectionFactory
{
    public IEnumerable<SelectListItem> GetSelectListItems(Type property)
    {
        //For each culture availible
        foreach(CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures)) {
            yield return new SelectListItem() { Text = ci.EnglishName, Value = ci.EnglishName };
        }  
    }
}
and changed my SelectionFactoryType to my newly created one;
[Required]
[DojoWidget(SelectionFactoryType = typeof(LanguageFactory))]
public string BrowserLanguage { get; set; }
Finished with my settings class, I started working on the criterion class. Basically what I needed was to compare the
browserlanguage with the language choosen in the criteria for the visitorgroup.
This was done easily by matching the two.
[VisitorGroupCriterion(
    Category = "Technical Criteria",
    Description = "Match Browser language with specified value",
    DisplayName = "Browser language")]
public class BrowserLanguageCriterion : CriterionBase<BrowserLanguageModel>
{
    private string _browserlang;
    //matches the browserlanguage with the criterion set language.
    public override bool IsMatch(System.Security.Principal.IPrincipal principal, 
System.Web.HttpContextBase httpContext)
    {
        return ((String.IsNullOrEmpty(base.Model.BrowserLanguage) ? true : 
        StringMatchHelper.IsMatch(_browserlang, base.Model.BrowserLanguage, 
        base.Model.BrowserLanguageMatchType)));             
    }

    public override void Subscribe(ICriterionEvents criterionEvents)
    {
        base.Subscribe(criterionEvents);
        criterionEvents.StartRequest += criterionEvents_StartRequest;
    }

    private void criterionEvents_StartRequest(object sender, CriterionEventArgs e)
    {
        //Get the browserlanguage
        string browserLanguage = e.HttpContext.Request.UserLanguages[0];
        Thread.CurrentThread.CurrentCulture = 
        System.Globalization.CultureInfo.CreateSpecificCulture(browserLanguage);
        _browserlang = Thread.CurrentThread.CurrentCulture.EnglishName;
    }

    public override void Unsubscribe(ICriterionEvents criterionEvents)
    {
        criterionEvents.StartRequest -= criterionEvents_StartRequest;
        base.Unsubscribe(criterionEvents);
    }
}

It was a great experience getting this to work and it made me write my first blog!
I’m quite pleased how this day turned out and hope that someone will find my
blogpost interesting!
/Jens Qvist Here is the full source;

using EPiServer.Personalization.VisitorGroups;
using EPiServer.Data;
using System.Threading;
using System.Globalization;
using EPiServer.Web.Mvc.VisitorGroups;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using System;

namespace EPiServer.Classes
{
    public class BrowserLanguageModel : CriterionModelBase
    {
        private MatchStringType _browserLanguageMatchType;
    
        [DojoWidget(SelectionFactoryType = typeof(EnumSelectionFactory), 
             AdditionalOptions = "{ selectOnClick: true }")]
        public MatchStringType BrowserLanguageMatchType
        {
            get
            {
                return this._browserLanguageMatchType;
            }
            set
            {
                this._browserLanguageMatchType = value;
            }
        }
        [Required]
        [DojoWidget(SelectionFactoryType = typeof(LanguageFactory))]
        public string BrowserLanguage { get; set; }

        public override ICriterionModel Copy()
        {
            return base.ShallowCopy();
        }
    }

    [VisitorGroupCriterion(
        Category = "Technical Criteria",
        Description = "Match Browser language with specified value",
        DisplayName = "Browser language")]
    public class BrowserLanguageCriterion : CriterionBase<BrowserLanguageModel>
    {
        private string _browserlang;
        //matches the browserlanguage with the criterion set language.
        public override bool IsMatch(System.Security.Principal.IPrincipal principal, 
        System.Web.HttpContextBase httpContext)
        {
            return ((String.IsNullOrEmpty(base.Model.BrowserLanguage) ? true : 
            StringMatchHelper.IsMatch(_browserlang, base.Model.BrowserLanguage, 
            base.Model.BrowserLanguageMatchType)));             
        }
        public override void Subscribe(ICriterionEvents criterionEvents)
        {
            base.Subscribe(criterionEvents);
            criterionEvents.StartRequest += criterionEvents_StartRequest;
        }
        private void criterionEvents_StartRequest(object sender, CriterionEventArgs e)
        {
            //Get the browserlanguage
            string browserLanguage = e.HttpContext.Request.UserLanguages[0];
            Thread.CurrentThread.CurrentCulture = 
            System.Globalization.CultureInfo.CreateSpecificCulture(browserLanguage);
            _browserlang = Thread.CurrentThread.CurrentCulture.EnglishName;
        }
        public override void Unsubscribe(ICriterionEvents criterionEvents)
        {
            criterionEvents.StartRequest -= criterionEvents_StartRequest;
            base.Unsubscribe(criterionEvents);
        }
    }
    public class LanguageFactory : ISelectionFactory
    {
        public IEnumerable<SelectListItem> GetSelectListItems(Type property)
        {
            //For each culture availible
            foreach(CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures)) {
                yield return new SelectListItem() { Text = ci.EnglishName, Value = ci.EnglishName };
            }  
        }
    }
}

Dec 19, 2011

Comments

Jens Qvist
Jens Qvist Dec 19, 2011 07:30 PM

Added an image showing how the criterion looks in EPiServer.

Please login to comment.
Latest blogs
Optimizely Commerce vs Composable Commerce: What Should You Do with CMS 13?

As organizations modernize their digital experience platforms, a common architectural question emerges: Should we continue using Optimizely Commerc...

Aniket | Mar 12, 2026

Missing Properties tool for Optimizely CMS

If you have been working with Optimizely CMS for a while you have probably accumulated some technical debt in your property definitions. When you...

Per Nergård (MVP) | Mar 10, 2026

AI Generated Optimizely Developer Newsletter

Updates in the Optimizely ecosystem are everywhere: blog posts, forums, release notes, NuGet packages, and documentation changes. This newsletter...

Allan Thraen | Mar 10, 2026 |

Lessons from Building Production-Ready Opal Tools

AI tools are becoming a normal part of modern digital platforms. With  Optimizely Opal , teams can build tools that automate real tasks across the...

Praful Jangid | Mar 7, 2026

My Takeaway from Optimizely Opal Agents in Action 2026 - What Agentic AI Means for the Future of Digital Marketing

I would like to share with you what stayed in my head after this amazing virtual event organized by Optimizely. Agents in Action 2026 , a live...

Augusto Davalos | Mar 6, 2026

From Vision to Velocity: Introducing the Optimizely MVP Technical Roundtable

Digital transformation is a two-sided coin. On one side, you have the high-level strategy, the business cases, the customer journeys, and the...

Patrick Lam | Mar 6, 2026