Saturday, January 30, 2010

How Sharepoint sets CurrentThread.CurrentUICulture depending on Language of SPWeb

There are 2 properties in SPWeb class which can be used for localization purposes:

  1. Language – used for UI localization
  2. Locale – affects regional settings like date time, currency formatting, etc.

Sharepoint uses these properties in order to set CurrentUICulture and CurrentCulture of current thread when request is executed in context of Sharepoint site. SPWeb.Language property of current site is used to set CurrentThread.CurrentUICulture and SPWeb.Locale is used to set CurrentThread.CurrentCulture. In this post I’m going to describe internal mechanisms used by Sharepoint when it initializes localization properties of current thread.

The main class which should be considered here is SPRequestModule class which represents http module. This module exists in web.config of each web application running under Sharepoint:

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

Also important point is that SPRequestModule is located on 1st place in list of http modules so it will be executed 1st in pipeline. SPRequestModule makes many important things (e.g. it registers SPVirtualPathProvider) and exactly this module initializes CurrentUICulture and CurrentCulture properties of current thread. Lets look at SPRequest.Init() method:

   1: void IHttpModule.Init(HttpApplication app)
   2: {
   3:     if (app is SPHttpApplication)
   4:     {
   5:        ...
   6:     }
   7:     else
   8:     {
   9:         return;
  10:     }
  11:     app.BeginRequest +=
  12:         new EventHandler(this.BeginRequestHandler);
  13:     app.PostResolveRequestCache +=
  14:         new EventHandler(this.PostResolveRequestCacheHandler);
  15:     app.PostMapRequestHandler +=
  16:         new EventHandler(this.PostMapRequestHandler);
  17:     app.ReleaseRequestState +=
  18:         new EventHandler(this.ReleaseRequestStateHandler);
  19:     app.PreRequestHandlerExecute +=
  20:         new EventHandler(this.PreRequestExecuteAppHandler);
  21:     app.PostRequestHandlerExecute +=
  22:         new EventHandler(this.PostRequestExecuteHandler);
  23:     app.AuthenticateRequest +=
  24:         new EventHandler(this.AuthenticateRequestHandler);
  25:     app.PostAuthenticateRequest +=
  26:         new EventHandler(this.PostAuthenticateRequestHandler);
  27:     app.Error +=
  28:         new EventHandler(this.ErrorAppHandler);
  29:     app.EndRequest +=
  30:         new EventHandler(this.EndRequestHandler);
  31: }

As you can see it checks that application is Sharepoint http application (SPHttpApplication), makes some initialization and subscribes to number of application events. We are interesting currently only in PreRequestHandlerExecute event as exactly in handler of this event Sharepoint initializes localization properties of current thread. Lets looks inside PreRequestExecuteAppHandler:

   1:  
   2: private void PreRequestExecuteAppHandler(object oSender, EventArgs ea)
   3: {
   4:     if (this.RequestPathIndex != PathIndex._layouts)
   5:     {
   6:         if (s_isAdminWebApp &&
   7:             (this.RequestPathIndex == PathIndex._admin))
   8:         {
   9:             this._adminApp.Application_PreRequestHandlerExecute(oSender, ea);
  10:         }
  11:     }
  12:     else
  13:     {
  14:         this._layoutsApp.Application_PreRequestHandlerExecute(oSender, ea);
  15:     }
  16: }

So Sharepoint executes PreRequestHandlerExecute only if request goes to application _layout pages or if current request goes to Central Administration. This is interesting moment that for publishing pages (also known as site or content pages) PreRequestHandlerExecute is not called and as result CurrentUICulture and CurrentCulture properties of current thread are not set here (for publishing pages these properties are initialized in another place - I will describe it in another post. Update 2010-03-20: see http://sadomovalex.blogspot.com/2010/03/set-locale-of-current-thread-for.html).

Finally lets look inside _layoutsApp.Application_PreRequestHandlerExecute() method which is called for _layouts pages:

   1: internal void Application_PreRequestHandlerExecute(object sender, EventArgs e)
   2: {
   3:     SPWeb contextWeb;
   4:     ...
   5:     contextWeb = SPControl.GetContextWeb(application.Context);
   6:     ...
   7:     SPUtility.SetThreadCulture(contextWeb);
   8: }

Here Sharepoint retrieves SPWeb contextWeb for current request and calls SPUtility.SetThreadCulture(contextWeb) method which in turn calls the following method:

   1: internal static void SetThreadCulture(SPWeb spWeb, bool force)
   2: {
   3:     if (spWeb != null)
   4:     {
   5:         CultureInfo locale = spWeb.Locale;
   6:         CultureInfo uiCulture = new CultureInfo((int) spWeb.Language);
   7:         SetThreadCulture(locale, uiCulture, force);
   8:     }
   9: }
  10:  

And finally we come to the last method which initializes CurrentUICulture and CurrentCulture properties of current thread:

   1: internal static void SetThreadCulture(CultureInfo culture, CultureInfo uiCulture, bool force)
   2: {
   3:     HttpContext current = HttpContext.Current;
   4:     if ((force || (current == null)) || (current.Items[IsThreadCultureSet] == null))
   5:     {
   6:         if (!Thread.CurrentThread.CurrentCulture.Equals(culture))
   7:         {
   8:             Thread.CurrentThread.CurrentCulture = culture;
   9:         }
  10:         if (!Thread.CurrentThread.CurrentUICulture.Equals(uiCulture))
  11:         {
  12:             Thread.CurrentThread.CurrentUICulture = uiCulture;
  13:         }
  14:         if (!force && (current != null))
  15:         {
  16:             current.Items[IsThreadCultureSet] = true;
  17:         }
  18:     }
  19: }

This is the way Sharepoint uses to initialize localization properties of current thread depending on localization properties of current SPWeb.

No comments:

Post a Comment