Sunday, April 11, 2010

Change current UI locale using query string parameter in Sharepoint

When we need to develop multilanguage solution for Sharepoint the common approach is to keep all language specific resources in resx files (I don’t mention site variations here as they are out of scope of this post). In this case it is useful to have quick testing tool which will allow us to see how page or user control looks like using different locales. In this post I will describe how to implement custom http module which will allow to change current locale dynamically based on query string “lcid” parameter.

In my previous posts (here and here) about localization I described how Sharepoint sets locale of current thread based on locale of requested SPWeb. Summary of these posts: when user requests application _layout page – Sharepoint sets locale in SPRequestModule.PreRequestExecuteAppHandler; when user requests publishing page then Sharepoint initializes current thread locale in earlier steps in SPWeb.InitWeb() method.

The main idea – is to override locale initialized by Sharepoint by our locale using “lcid” query string parameter. From 2 mentioned places – the later one is SPRequestModule.PreRequestExecuteAppHandler. So we need to implement http handler and subscribe to PreRequestHandlerExecute event of http application. Also our custom http handler should be defined after SPRequestModule in <httpModules> section (it is required that SPRequestModule is the 1st module in Sharepoint) so its handler of PreRequestHandlerExecute  will be executed after SPRequestModule.PreRequestExecuteAppHandler and will override locale set by Sharepoint.

The code of the http module is quite simple:

   1: public class ChangeLocaleModule : IHttpModule
   2: {
   3:     private const string KEY_CULTURE = "lcid";
   4:  
   5:     public void Init(HttpApplication application)
   6:     {
   7:         application.PreRequestHandlerExecute += ApplicationPreRequestHandlerExecute;
   8:     }
   9:  
  10:     private void ApplicationPreRequestHandlerExecute(object sender, EventArgs e)
  11:     {
  12:         var application = (HttpApplication)sender;
  13:         var context = application.Context;
  14:  
  15:         var requestedCulture = this.getRequestedCulture(context);
  16:  
  17:         SPUtility.SetThreadCulture(requestedCulture, requestedCulture);
  18:     }
  19:  
  20:     private CultureInfo getRequestedCulture(HttpContext context)
  21:     {
  22:         var cultureFromQueryString =
  23:             this.getCultureFromQueryStringOrNull(context.Request.QueryString);
  24:         if (cultureFromQueryString != null)
  25:         {
  26:             return cultureFromQueryString;
  27:         }
  28:  
  29:         return Thread.CurrentThread.CurrentUICulture;
  30:     }
  31:  
  32:     private CultureInfo getCultureFromQueryStringOrNull(
  33:         NameValueCollection queryString)
  34:     {
  35:         if (string.IsNullOrEmpty(queryString[KEY_CULTURE]))
  36:             return null;
  37:  
  38:         var ci = queryString[KEY_CULTURE];
  39:         int languageId;
  40:         if (!int.TryParse(ci, out languageId))
  41:             return null;
  42:  
  43:         var culture = CultureInfo.GetCultureInfo(languageId);
  44:         return culture;
  45:     }
  46:  
  47:     public void Dispose()
  48:     {
  49:     }
  50: }

This module reads value specified in “lcid” query string parameter and if it represents valid locale id – changes locale of current thread using SPUtility.SetThreadCulture(…) method.

In order to make it work you need to add this custom http module in your web.config:

   1: <httpModules>
   2:   <clear />
   3:   <add name="SPRequest" type="Microsoft.SharePoint.ApplicationRuntime.SPRequestModule,
   4:     Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,
   5:     PublicKeyToken=71e9bce111e9429c" />
   6:   ...
   7:   <add name="ChangeLocaleModule" type="NamespaceName.ChangeLocaleModule,
   8:     AssemblyName" />
   9: </httpModules>

Now if you specify URL like http://example.com/_layouts/page.aspx – locale of http://example.com site will be used. But if you will append “lcid”query string parameter e.g. like this http://example.com/_layouts/page.aspx?lcid=1033 – then English locale (id = 1033) will be used to display content (of course you can use any valid locale id). It is not necessary to have single “lcid” query string param. You can use it with another parameters as well http://example.com/_layouts/page.aspx?param1=value1&lcid=1033.

3 comments:

  1. Thanks a lot for your post. This handler solved our problem on server with incorrectly installed language pack.

    ReplyDelete
  2. Great write up. However, I have noticed that on the _layout pages (i.e. recyclebin.aspx), when the page is initially loaded (after an iisreset), the page displays correctly based on the language (I am using french). But, after a refresh to the page, it seems that some portions are reverting back to english (the intial language). I believe it has something to do with the inline code within the page. It seems that the thread has been reset back to english.

    Is it possible the SPRequestModule is resetting the language at some other Event?

    ReplyDelete