Friday, May 11, 2018

Sitecore context language based on Geo IP location

Sitecore context language


Sitecore uses a context language (Sitecore.Context.Language) to fetch the correct version of an item to display. There is an out-of-the-box logic to determine the context language - it uses:
  1. The "sc_lang" query string parameter
  2. The language prefix in the url
  3. The language cookie for the site
  4. The default language in the site config.
  5. The DefaultLanguage setting in the Sitecore config
There are many other functional scenario's to determine the language of a (new) visitor and they can be developed in a custom processor that is placed in the httpRequestBegin pipeline. Create a class that overrides HttpRequestProcessor, implement an override for the Process method containing any logic you need and adapt the Sitecore Context properties (Language) as needed.

What if you want visitors to be directed to a language based on their location?

Geo IP detection

In Sitecore you can setup Geo IP location detection. This will add information to the Tracker about the location of your visitor. This information can be used for several purposes, and possibly also for language detection.

Geo IP to set the context language

We had to set the language of the new visitor to en-US when the visitor came from the USA.
As we had languages enabled in the urls, we only had to take care of the homepage - all other urls had a defined language already.

Seems so easy! Just create the language resolver as mentioned above and set the language based on the country in the tracker. Done.

Issue 1 : the tracker

If you place your language resolver where you normally would (just after the one from Sitecore) you'll notice that your tracker is not et initialized. This is normal. If you want more information on the complete pipeline sequence in a request, you should read Martin Davies' blog
Solution: put your resolver elsewhere..  :
<startAnalytics>
   <processor patch:after="*[@type='Sitecore.Analytics.Pipelines.StartAnalytics.StartTracking, Sitecore.Analytics']" type="your-language-resolver, .." />
</startAnalytics>
As this is another pipeline, we'll also use another base class: my processor derives from RenderLayoutProcessor - the rest stays the same.

Issue 2: the Geo IP

On the first visit, you are most likely to find no geo ip data in the request pipeline. It's just too soon. The request has been made (async), but the results are not there yet. An article by Pavel Veller got us in the right direction. A new CreateVisitProcessor was made:
public class UpdateGeoIpData : CreateVisitProcessor
{
    public override void Process(CreateVisitArgs args)
    {
        var url = WebUtil.GetRawUrl();
        if (!url.Equals("/", StringComparison.OrdinalIgnoreCase))
        {
            return;
        }

        args.Interaction.UpdateGeoIpData(TimeSpan.FromMilliseconds(500));
    }
}

<pipelines>
    <createVisit>
        <processor type="Sitecore.Analytics.Pipelines.CreateVisits.UpdateGeoIpData, Sitecore.Analytics">
          <patch:delete/>
        </processor>
        <processor type="...UpdateGeoIpData, .." patch:after="processor[@type='Sitecore.Analytics.Pipelines.CreateVisits.XForwardedFor, Sitecore.Analytics']" />
    </createVisit>
</pipelines>

This will make sure that we wait a little longer, but only on the pages where needed.

Resolving

if (Tracker.Current.Interaction.HasGeoIpData)
{
    if (Tracker.Current.Interaction.GeoData.Country.Equals("US", StringComparison.OrdinalIgnoreCase))
    {
        return "en-US";
    }
}

Our final logic looked something as above. We test the country from the GeoData and handle accordingly.

For the moment we are running some performance tests, but so far it seems ok.

1 comment:

  1. Great post, Gert! I think that you forgot to call args.Interaction.UpdateGeoIpData(); without delay in that case for all other pages, just to make sure that Geo IP personalization rules on other pages will work as expected (at least for 2nd, 3rd, etc visits), so UpdateGeoIpData should look like this:

    public class UpdateGeoIpData : CreateVisitProcessor
    {
    public override void Process(CreateVisitArgs args)
    {
    var url = WebUtil.GetRawUrl();
    if (!url.Equals("/", StringComparison.OrdinalIgnoreCase))
    {
    args.Interaction.UpdateGeoIpData();
    return;
    }

    args.Interaction.UpdateGeoIpData(TimeSpan.FromMilliseconds(500));
    }
    }

    ReplyDelete