<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4091304575863058921</id><updated>2012-02-09T13:50:09.696+04:00</updated><category term='C#'/><category term='DisplayName attribute'/><category term='OutputCache'/><category term='Globalization'/><category term='Routing'/><category term='ASP.NET MVC'/><category term='.NET'/><category term='Localization'/><title type='text'>While my keyboard gently weeps...</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://adamyan.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4091304575863058921/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://adamyan.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Alex Adamyan</name><uri>http://www.blogger.com/profile/11560575110495312401</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='31' src='http://4.bp.blogspot.com/_tzjn1Yeo5bM/S4OAg_yHPOI/AAAAAAAAAB4/sNGQVhZxznM/S220/avatar.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>2</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4091304575863058921.post-2726076951729180690</id><published>2010-07-29T17:51:00.005+05:00</published><updated>2010-09-03T13:09:58.799+05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Routing'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Localization'/><category scheme='http://www.blogger.com/atom/ns#' term='Globalization'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET MVC'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Addition to ASP.NET MVC Localization - Using routing</title><content type='html'>&lt;style&gt; .csharpcode, .csharpcode pre { overflow: visible; font-size: small; color: black; font-family: Consolas, "Courier New", Courier, Monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } &lt;/style&gt;&lt;br /&gt;In my previous post I described the way of localization using session, but in real-world applications it's definitely not the best way of localization. Now I'll describe very simple and very powerful way of storing it in URL using routing mechanism.&lt;br /&gt;&lt;br /&gt;Also this way of localization will &lt;b&gt;not&lt;/b&gt; require OutputCache tricks described in previous post&lt;br /&gt;&lt;br /&gt;The goal of this post is to show how to get URL&amp;nbsp; like this &lt;b&gt;/{culture}/{Controller}/{Action}... &lt;/b&gt;in your application like &lt;b&gt;/ru/Home/About&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Trebuchet MS&amp;quot;,sans-serif; font-size: large;"&gt;Custom Route Handlers&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;First of all we'll need to extend standard &lt;b style="color: #0b5394;"&gt;MvcRouteHandler &lt;/b&gt;class. One class &lt;b style="color: #0b5394;"&gt;MultiCultureMvcRouteHandler&lt;/b&gt;&lt;span style="color: #0b5394;"&gt; &lt;/span&gt;for routes that will use culture in params and &lt;b style="color: #0b5394;"&gt;SingleCultureMvcRouteHandler &lt;/b&gt;class (will be used as a marker, no implementation changes)&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; MultiCultureMvcRouteHandler : MvcRouteHandler&lt;br /&gt;{&lt;br /&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; IHttpHandler GetHttpHandler(RequestContext requestContext)&lt;br /&gt;    {&lt;br /&gt;        var culture = requestContext.RouteData.Values[&lt;span class="str"&gt;"culture"&lt;/span&gt;].ToString();&lt;br /&gt;        var ci = &lt;span class="kwrd"&gt;new&lt;/span&gt; CultureInfo(culture);&lt;br /&gt;        Thread.CurrentThread.CurrentUICulture = ci;&lt;br /&gt;        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);&lt;br /&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;base&lt;/span&gt;.GetHttpHandler(requestContext);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;In the overridden &lt;span style="color: #0b5394;"&gt;GetHttpHandler &lt;/span&gt;before calling it's base implementation we just get "culture" param from &lt;span style="color: #0b5394;"&gt;RouteData &lt;/span&gt;collection, create &lt;span style="color: #0b5394;"&gt;CultureInfo &lt;/span&gt;object and set it to current thread current culture. So here is a place where we set culture and will &lt;b&gt;not&lt;/b&gt; use &lt;b&gt;&lt;span style="color: #0b5394;"&gt;Application_AcquireRequestState&lt;/span&gt; &lt;/b&gt;method in Global.asax&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; SingleCultureMvcRouteHandler : MvcRouteHandler {}&lt;/pre&gt;&lt;br /&gt;As I mention this class will be used only for marking some routes for case if you'll need some routes to be culture independent.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Trebuchet MS&amp;quot;,sans-serif;"&gt;&lt;span style="font-size: large;"&gt;Registering routes&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Now lets go to Global.asax file where we have route registering method &lt;span style="font-family: inherit; font-size: small;"&gt;RegisterRoutes&lt;/span&gt;(). Right after last route mapping add &lt;b style="color: #0b5394;"&gt;foreach &lt;/b&gt;statement code snippet like in the following example.&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; RegisterRoutes(RouteCollection routes)&lt;br /&gt;{&lt;br /&gt;    routes.IgnoreRoute(&lt;span class="str"&gt;"{resource}.axd/{*pathInfo}"&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;    routes.MapRoute(&lt;br /&gt;         &lt;span class="str"&gt;"Default"&lt;/span&gt;, &lt;span class="rem"&gt;// Route name&lt;/span&gt;&lt;br /&gt;         &lt;span class="str"&gt;"{controller}/{action}/{id}"&lt;/span&gt;, &lt;span class="rem"&gt;// URL with parameters&lt;/span&gt;&lt;br /&gt;         &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;"Home"&lt;/span&gt;, action = &lt;span class="str"&gt;"Index"&lt;/span&gt;, id = UrlParameter.Optional } &lt;span class="rem"&gt;// Parameter defaults&lt;/span&gt;&lt;br /&gt;    );&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (Route r &lt;span class="kwrd"&gt;in&lt;/span&gt; routes)&lt;br /&gt;    {&lt;br /&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (!(r.RouteHandler &lt;span class="kwrd"&gt;is&lt;/span&gt; SingleCultureMvcRouteHandler))&lt;br /&gt;        {&lt;br /&gt;            r.RouteHandler = &lt;span class="kwrd"&gt;new&lt;/span&gt; MultiCultureMvcRouteHandler();&lt;br /&gt;            r.Url = &lt;span class="str"&gt;"{culture}/"&lt;/span&gt; + r.Url;&lt;br /&gt;           &lt;span class="rem"&gt;//Adding default culture &lt;/span&gt;&lt;br /&gt;           &lt;span class="kwrd"&gt;if&lt;/span&gt; (r.Defaults == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;br /&gt;           {&lt;br /&gt;               r.Defaults = &lt;span class="kwrd"&gt;new&lt;/span&gt; RouteValueDictionary();&lt;br /&gt;           }&lt;br /&gt;           r.Defaults.Add(&lt;span class="str"&gt;"culture"&lt;/span&gt;, Culture.ru.ToString());&lt;br /&gt;&lt;br /&gt;           &lt;span class="rem"&gt;//Adding constraint for culture param&lt;/span&gt;&lt;br /&gt;           &lt;span class="kwrd"&gt;if&lt;/span&gt; (r.Constraints == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;br /&gt;           {&lt;br /&gt;               r.Constraints = &lt;span class="kwrd"&gt;new&lt;/span&gt; RouteValueDictionary();&lt;br /&gt;           }&lt;br /&gt;           r.Constraints.Add(&lt;span class="str"&gt;"culture"&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; CultureConstraint(Culture.en.ToString(),&amp;nbsp;&lt;/pre&gt;&lt;pre class="csharpcode"&gt;Culture.ru.ToString()));&lt;br /&gt;        }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;OK, lets go through this code... So for each route we first of all check whether its handler type is SingleCultureMvcRouteHandler or not... So if not we change RouteHandler property of the current route to MultiCulture one, add prefix to Url, add default culture and finally add constraint for culture param checking.&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CultureConstraint : IRouteConstraint&lt;br /&gt;{&lt;br /&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt;[] _values;&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; CultureConstraint(&lt;span class="kwrd"&gt;params&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt;[] values)&lt;br /&gt;    {&lt;br /&gt;        &lt;span class="kwrd"&gt;this&lt;/span&gt;._values = values;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; Match(HttpContextBase httpContext,Route route,&lt;span class="kwrd"&gt;string&lt;/span&gt; parameterName,&lt;br /&gt;                        RouteValueDictionary values, RouteDirection routeDirection)&lt;br /&gt;    {&lt;br /&gt;&lt;br /&gt;        &lt;span class="rem"&gt;// Get the value called "parameterName" from the &lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;// RouteValueDictionary called "value"&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;string&lt;/span&gt; &lt;span class="kwrd"&gt;value&lt;/span&gt; = values[parameterName].ToString();&lt;br /&gt;        &lt;span class="rem"&gt;// Return true is the list of allowed values contains &lt;/span&gt;&lt;br /&gt;        &lt;span class="rem"&gt;// this value.&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; _values.Contains(&lt;span class="kwrd"&gt;value&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;And enum of cultures&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;    public&lt;/span&gt; &lt;span class="kwrd"&gt;enum&lt;/span&gt; Culture&lt;br /&gt;    {&lt;br /&gt;        ru = 1,&lt;br /&gt;        en = 2&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Trebuchet MS&amp;quot;,sans-serif; font-size: large;"&gt;Simple culture switching mechanism&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For changing culture we'll need following simple action which I placed in AccountController&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult ChangeCulture(Culture lang, &lt;span class="kwrd"&gt;string&lt;/span&gt; returnUrl)&lt;br /&gt;{&lt;br /&gt;     &lt;span class="kwrd"&gt;if&lt;/span&gt; (returnUrl.Length &amp;gt;= 3)&lt;br /&gt;     {&lt;br /&gt;         returnUrl = returnUrl.Substring(3);&lt;br /&gt;     }&lt;br /&gt;     &lt;span class="kwrd"&gt;return&lt;/span&gt; Redirect(&lt;span class="str"&gt;"/"&lt;/span&gt; + lang.ToString() + returnUrl);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;and partial view with languages links - CultureSwitchControl.ascx&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&amp;lt;%@ Control Language=&lt;span class="str"&gt;"C#"&lt;/span&gt; Inherits=&lt;span class="str"&gt;"System.Web.Mvc.ViewUserControl"&lt;/span&gt; %&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;%= Html.ActionLink(&lt;span class="str"&gt;"eng"&lt;/span&gt;, &lt;span class="str"&gt;"ChangeCulture"&lt;/span&gt;, &lt;span class="str"&gt;"Account"&lt;/span&gt;,&lt;br /&gt;    &lt;span class="kwrd"&gt;new&lt;/span&gt; { lang = (&lt;span class="kwrd"&gt;int&lt;/span&gt;)MvcLocalization.Helpers.Culture.en, returnUrl =  &lt;br /&gt;    &lt;span class="kwrd"&gt;this&lt;/span&gt;.Request.RawUrl }, &lt;span class="kwrd"&gt;new&lt;/span&gt; { @&lt;span class="kwrd"&gt;class&lt;/span&gt; = &lt;span class="str"&gt;"culture-link"&lt;/span&gt; })%&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;%= Html.ActionLink(&lt;span class="str"&gt;"рус"&lt;/span&gt;, &lt;span class="str"&gt;"ChangeCulture"&lt;/span&gt;, &lt;span class="str"&gt;"Account"&lt;/span&gt;,&lt;br /&gt;    &lt;span class="kwrd"&gt;new&lt;/span&gt; { lang = (&lt;span class="kwrd"&gt;int&lt;/span&gt;)MvcLocalization.Helpers.Culture.ru, returnUrl = &lt;br /&gt;    &lt;span class="kwrd"&gt;this&lt;/span&gt;.Request.RawUrl }, &lt;span class="kwrd"&gt;new&lt;/span&gt; { @&lt;span class="kwrd"&gt;class&lt;/span&gt; = &lt;span class="str"&gt;"culture-link"&lt;/span&gt; })%&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Trebuchet MS&amp;quot;,sans-serif; font-size: large;"&gt;Single culture case&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Finally, if we need some single culture route all we need to do is to set &lt;span style="color: #0b5394;"&gt;RouteHandler &lt;/span&gt;property to &lt;span style="color: #0b5394;"&gt;SingleCultureMvcRouteHandler&amp;nbsp; &lt;/span&gt;like this&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;routes.MapRoute(&lt;br /&gt;          &lt;span class="str"&gt;"AboutRoute"&lt;/span&gt;,&lt;br /&gt;          &lt;span class="str"&gt;"About"&lt;/span&gt;,&lt;br /&gt;          &lt;span class="kwrd"&gt;new&lt;/span&gt; { controller = &lt;span class="str"&gt;"Home"&lt;/span&gt;, action = &lt;span class="str"&gt;"About"&lt;/span&gt;}&lt;br /&gt;   ).RouteHandler = &lt;span class="kwrd"&gt;new&lt;/span&gt; SingleCultureMvcRouteHandler();&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, that's it :) Localization without using Session, without problems with OutputCache(will be explained in my next post) and with use of routing.&lt;br /&gt;&lt;br /&gt;Here is the link of&amp;nbsp; &lt;a href="http://www.4shared.com/file/H9SanGs-/MvcLocalization.html"&gt;source code(project created in VS2010) &lt;/a&gt;&lt;br /&gt;&lt;span id="goog_13216141"&gt;&lt;/span&gt;&lt;span id="goog_13216142"&gt;&lt;/span&gt;&lt;a href="http://www.blogger.com/"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fadamyan.blogspot.com%2f2010%2f07%2faddition-to-aspnet-mvc-localization.html"&gt;&lt;img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fadamyan.blogspot.com%2f2010%2f07%2faddition-to-aspnet-mvc-localization.html%26bgcolor%3DFF9933" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4091304575863058921-2726076951729180690?l=adamyan.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adamyan.blogspot.com/feeds/2726076951729180690/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://adamyan.blogspot.com/2010/07/addition-to-aspnet-mvc-localization.html#comment-form' title='18 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4091304575863058921/posts/default/2726076951729180690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4091304575863058921/posts/default/2726076951729180690'/><link rel='alternate' type='text/html' href='http://adamyan.blogspot.com/2010/07/addition-to-aspnet-mvc-localization.html' title='Addition to ASP.NET MVC Localization - Using routing'/><author><name>Alex Adamyan</name><uri>http://www.blogger.com/profile/11560575110495312401</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='31' src='http://4.bp.blogspot.com/_tzjn1Yeo5bM/S4OAg_yHPOI/AAAAAAAAAB4/sNGQVhZxznM/S220/avatar.jpg'/></author><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4091304575863058921.post-2687016707360634227</id><published>2010-02-23T01:40:00.014+04:00</published><updated>2010-09-03T13:19:03.957+05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Localization'/><category scheme='http://www.blogger.com/atom/ns#' term='Globalization'/><category scheme='http://www.blogger.com/atom/ns#' term='DisplayName attribute'/><category scheme='http://www.blogger.com/atom/ns#' term='OutputCache'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET MVC'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>ASP.NET MVC 2 Localization complete guide</title><content type='html'>&lt;style&gt; .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: Consolas, "Courier New", Courier, Monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } &lt;/style&gt;&lt;br /&gt;In this article we are going to explore all aspects of localization web application based on ASP.NET MVC framework. The version I'll be using for that purpose will be 2 RC 2 which is last available at the time of writing.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;b&gt;NOTE 01.09.2010:&lt;/b&gt; In this article Session is used for storing current culture, please also as an addition consider reading my &lt;a href="http://adamyan.blogspot.com/2010/07/addition-to-aspnet-mvc-localization.html"&gt;next post&lt;/a&gt; about localization where routing mechanism used for that purpose(better SEO and simpler implementation). Also you will find there link to the source code. &lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Before we start I would like to thank the MVC framework team, great job guys, I really like it :) I really enjoy writing web application with the framework.&amp;nbsp; I was searching for such kind a framework after small experience with Ruby on Rails&lt;br /&gt;&lt;br /&gt;OK, lets see what issues we'll cover&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Views validation&lt;/li&gt;&lt;li&gt;Simple culture switching mechanism&lt;/li&gt;&lt;li&gt;Model Validation messages localization&lt;/li&gt;&lt;li&gt;DisplayName attribute localization&lt;/li&gt;&lt;li&gt;OutputCache and Localization&lt;/li&gt;&lt;/ol&gt;For this guide you'll need Visual Studio 2008 Express and ASP.NET MVC 2 RC2 installed.&amp;nbsp;To follow instructions of the guide please create new MVC 2 web project.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Views localization&lt;/span&gt;&lt;/h2&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;For localized strings, of course, we'll be using resource files, but we'll not use asp.net standard folders for storing them.&lt;br /&gt;&lt;br /&gt;Here is folder structure I suggest&lt;br /&gt;&lt;br /&gt;&lt;a href="http://alexadamyan.files.wordpress.com/2010/02/resourcestructure.png"&gt;&lt;img alt="Resources folder structure" class="alignnone size-full wp-image-15" height="546" src="http://alexadamyan.files.wordpress.com/2010/02/resourcestructure.png" title="Resources folder structure" width="279" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Views - &lt;/b&gt;resource files for views aspx pages. &lt;b&gt;Models &lt;/b&gt;- resource files for view models localization.&lt;br /&gt;&lt;br /&gt;Views folder contains sub folders for each controller and each folder will contain resource files(as much as many languages we'll support)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Models&lt;/b&gt; contains sub folders for each group of view models. For generated Account models (LogOn, Register, ChangePassword) we have &lt;b&gt;Account &lt;/b&gt; folder and resource files for each language-culture.&lt;br /&gt;&lt;h3&gt;&lt;br /&gt;&lt;/h3&gt;&lt;h3&gt;Resource files&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Some word about resource files naming convention. Resource files have following format&lt;br /&gt;[RESOURCE-NAME].[CULTURE].resx&lt;br /&gt;&lt;br /&gt;RESOURCE-NAME - the name of file. Can be anything you like. It's used for grouping, so when there are several resource files with same resource-name they construct one resource with different cultures described by CULTURE&lt;br /&gt;&lt;br /&gt;CULTURE - word indicating culture of resource file. Cultures are two type: &lt;b&gt;neutral&lt;/b&gt;(also called &lt;i&gt;invariant&lt;/i&gt;) and &lt;b&gt;concrete. &lt;/b&gt;Neutral culture consists from language code only(examples: &lt;b&gt;en&lt;/b&gt;, &lt;b&gt;ru&lt;/b&gt;, &lt;b&gt;de&lt;/b&gt; etc), Concrete culture consists from language code and region code(examples: &lt;b&gt;en-US&lt;/b&gt;, &lt;b&gt;en-UK&lt;/b&gt; etc).&lt;br /&gt;&lt;br /&gt;Also there is special meaning for resource files that haven't any culture specified, they are called &lt;i&gt;default &lt;/i&gt; or &lt;i&gt;fall-back. &lt;/i&gt;As you can guess from it's name they are used as default resource files if string was not found in specified culture's resource file or even when there is no resource file for specified culture. I strongly encourage you to use default resource file if user somehow can change to unsupported culture.&lt;br /&gt;&lt;br /&gt;Some example of resource files:&lt;br /&gt;&lt;br /&gt;MyStrings.en-US.resx -English US&lt;br /&gt;&lt;br /&gt;MyStrings.en-UK.resx - English UK&lt;br /&gt;&lt;br /&gt;MyStrings.en.resx - English neutral (this is also fall back for English)&lt;br /&gt;&lt;br /&gt;MyStrings.ru.resx - Russian neutral&lt;br /&gt;&lt;br /&gt;MyStrings.resx - Fall back resource file&lt;br /&gt;&lt;br /&gt;OK, now we are ready to localize something and look how it works. I'll show you small example how to localize title of the created web application. Throughout the tutorial I'll use two languages: English(as default) and Russian neutral, but you are free to use any languages you wish.&lt;br /&gt;&lt;br /&gt;First of all create folder structure I described above for resource files, and&amp;nbsp;particularly&amp;nbsp;we'll need resource files for Site.Master master page. I create folder &lt;b&gt;Shared &lt;/b&gt;under &lt;b&gt;Resources\Views &lt;/b&gt;and create two resource files&lt;br /&gt;&lt;br /&gt;&lt;b&gt;SharedStrings.resx&lt;/b&gt; - Default resource file with English values&lt;br /&gt;&lt;br /&gt;&lt;b&gt;SharedStrings.ru.resx&lt;/b&gt; - resource file with Russian values&lt;br /&gt;&lt;br /&gt;Add property "Title" in both files and fill values.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://alexadamyan.files.wordpress.com/2010/02/titleproperty.png"&gt;&lt;img alt="Title property in resource file" class="alignnone size-full wp-image-16" height="158" src="http://alexadamyan.files.wordpress.com/2010/02/titleproperty.png" title="Application Title property " width="600" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Important! &lt;/b&gt; Make sure to change &lt;b&gt;access modifier&lt;/b&gt; of each resource file you create to &lt;b&gt;public&lt;/b&gt;. Also check that resource files Custom Tool property value is &lt;b&gt;"PublicResXFileCodeGenerator". &lt;/b&gt;Otherwise resource files won't be compiled and won't be&amp;nbsp;accessible.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://alexadamyan.files.wordpress.com/2010/02/accessmodifer.png"&gt;&lt;img alt="" class="alignnone size-full wp-image-17" height="158" src="http://alexadamyan.files.wordpress.com/2010/02/accessmodifer.png" title="AccessModifer" width="600" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://alexadamyan.files.wordpress.com/2010/02/customtool.png"&gt;&lt;img alt="Custom tool property" class="alignnone size-full wp-image-18" height="312" src="http://alexadamyan.files.wordpress.com/2010/02/customtool.png" title="Custom Tool property" width="356" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Some words about resource files namespace. Created this way resource file will have namespace:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #333399;"&gt;[PROJECT-NAME].Resources.Views.Shared&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #333333;"&gt;To make it more readable and convenient I changed&amp;nbsp;&lt;b&gt;Custom Tool Namespace&lt;/b&gt; properties of resource files to&amp;nbsp;&lt;b&gt;ViewRes&lt;/b&gt; (for views resource files)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now it's time to make modifications in Site.Master page.&lt;br /&gt;&lt;br /&gt;Locate following HTML code snippet&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="title"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;h1&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;My MVC Application&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;h1&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;and replace with&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="title"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;h1&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt;=ViewRes.SharedStrings.Title&lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;h1&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;span style="color: #333333;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #333333;"&gt;Run application and make sure everything is still working, and you can see title in its place(now it should be read from resource file). If everything is OK, it's time to somehow change culture and check whether that will work.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To change culture we need to change CurrentCulture and CurrentUICulture properties of CurrentThread for &lt;b&gt;every&lt;/b&gt; request!!! To do this we need to place culture changing code to&amp;nbsp;&lt;b&gt;Global.asax&lt;span style="font-weight: normal;"&gt; &lt;span style="color: #333399;"&gt;Application_AcquireRequestState&amp;nbsp;&lt;span style="color: #333333;"&gt;method(this method is event handler and is called for every request).&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-weight: normal;"&gt;&lt;span style="color: #333399;"&gt;&lt;span style="color: #333333;"&gt;Add following code to Global.asax.cs file&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-weight: normal;"&gt;&lt;span style="color: #333399;"&gt;&lt;span style="color: #333333;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-weight: normal;"&gt;&lt;span style="color: #333399;"&gt;&lt;span style="color: #333333;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Application_AcquireRequestState(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;  &lt;span class="rem"&gt;//Create culture info object &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;  CultureInfo ci = &lt;span class="kwrd"&gt;new&lt;/span&gt; CultureInfo(&lt;span class="str"&gt;"en"&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;  System.Threading.Thread.CurrentThread.CurrentUICulture = ci;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7: &lt;/span&gt;   System.Threading.Thread.CurrentThread.CurrentCulture =&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;CultureInfo.CreateSpecificCulture(ci.Name);&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8: &lt;/span&gt; }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Run application to check that everything is working. Than change string parameter of CulturInfo constructor (in my case this will be "ru") and run again. You should get following results for both cases&lt;br /&gt;&lt;br /&gt;&lt;a href="http://alexadamyan.files.wordpress.com/2010/02/viewlocalizationpage-en.png"&gt;&lt;img alt="Master page localized Title" class="alignnone size-full wp-image-20" height="311" src="http://alexadamyan.files.wordpress.com/2010/02/viewlocalizationpage-en.png" title="Master page localized Title - English" width="725" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://alexadamyan.files.wordpress.com/2010/02/viewlocalizationpage-ru.png"&gt;&lt;img alt="Master page localized title" class="alignnone size-full wp-image-19" height="311" src="http://alexadamyan.files.wordpress.com/2010/02/viewlocalizationpage-ru.png" title="Master page localized title - Russian" width="725" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;That's all. We have localized Site.Master's title and you can do the same with any string you need.&lt;br /&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;Simple culture switching mechanism&lt;/h2&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;In the previous chapter we successfully localized title of the application, but there wasn't any chance to change the culture at the runtime. Now we are going to create some mechanism which can help us to control culture setting at the runtime.&lt;br /&gt;&lt;br /&gt;As a place for storing users selected culture we'll use session object. And for changing culture we'll place links for each language on the master page. Clicking links will call some action in Account controller which will change session's value corresponding to culture.&lt;br /&gt;&lt;br /&gt;Add following code to AccountController class&lt;br /&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult ChangeCulture(&lt;span class="kwrd"&gt;string&lt;/span&gt; lang, &lt;span class="kwrd"&gt;string&lt;/span&gt; returnUrl)&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;     Session[&lt;span class="str"&gt;"Culture"&lt;/span&gt;] = &lt;span class="kwrd"&gt;new&lt;/span&gt; CultureInfo(lang);&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;     &lt;span class="kwrd"&gt;return&lt;/span&gt; Redirect(returnUrl);&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;We have here action method with two parameters, first one is for culture code, second is for redirecting back user to original page. There is no much to do in this action, it's just setting new culture to session dictionary, but remember to add some user input validation here to prevent setting unsupported culture code.&lt;br /&gt;&lt;br /&gt;Now we'll create simple user control with supported culture hyper links. Add new partial view to &lt;b&gt;Views\Shared&lt;/b&gt; folder &lt;i&gt;CultureChooserUserControl.ascx&lt;/i&gt; and paste following&lt;br /&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt;= Html.ActionLink(&lt;span class="str"&gt;"English"&lt;/span&gt;, &lt;span class="str"&gt;"ChangeCulture"&lt;/span&gt;, &lt;span class="str"&gt;"Account"&lt;/span&gt;, &lt;span class="kwrd"&gt;&amp;nbsp;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;     new&lt;/span&gt; { lang = &lt;span class="str"&gt;"en"&lt;/span&gt;, returnUrl = &lt;span class="kwrd"&gt;this&lt;/span&gt;.Request.RawUrl }, &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt;= Html.ActionLink(&lt;span class="str"&gt;"Русский"&lt;/span&gt;, &lt;span class="str"&gt;"ChangeCulture"&lt;/span&gt;, &lt;span class="str"&gt;"Account"&lt;/span&gt;, &lt;span class="kwrd"&gt;&amp;nbsp;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;     new&lt;/span&gt; { lang = &lt;span class="str"&gt;"ru"&lt;/span&gt;, returnUrl = &lt;span class="kwrd"&gt;this&lt;/span&gt;.Request.RawUrl }, &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;We just now created two hyper links, first one for English and second one for Russian languages. And now its time to place this culture chooser user control to Site.Master master page. I'll add this to &amp;lt;div&amp;gt; corresponding to &lt;b&gt;login&lt;/b&gt; functionality just as an example.&lt;br /&gt;&lt;br /&gt;Find and replace &amp;lt;div id="logindisplay"&amp;gt; with following&lt;br /&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="logindisplay"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt; Html.RenderPartial(&lt;span class="str"&gt;"LogOnUserControl"&lt;/span&gt;); &lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="asp"&gt;&amp;lt;%&lt;/span&gt; Html.RenderPartial(&lt;span class="str"&gt;"CultureChooserUserControl"&lt;/span&gt;); &lt;span class="asp"&gt;%&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;What else? Right, the most important is left, we put culture info object to session, but we never use it. It's time to make some changes in Global.asax.cs, and again this is Application_AcquireRequestState&amp;nbsp;method.&lt;br /&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Application_AcquireRequestState(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;     &lt;span class="rem"&gt;//It's important to check whether session object is ready&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;     &lt;span class="kwrd"&gt;if&lt;/span&gt; (HttpContext.Current.Session != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;     {&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;         CultureInfo ci = (CultureInfo)&lt;span class="kwrd"&gt;this&lt;/span&gt;.Session[&lt;span class="str"&gt;"Culture"&lt;/span&gt;];&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;         &lt;span class="rem"&gt;//Checking first if there is no value in session &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;         &lt;span class="rem"&gt;//and set default language &lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;         &lt;span class="rem"&gt;//this can happen for first user's request&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;         &lt;span class="kwrd"&gt;if&lt;/span&gt; (ci == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;         {&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;             &lt;span class="rem"&gt;//Sets default culture to english invariant&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;             &lt;span class="kwrd"&gt;string&lt;/span&gt; langName = &lt;span class="str"&gt;"en"&lt;/span&gt;;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;             &lt;span class="rem"&gt;//Try to get values from Accept lang HTTP header&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;             &lt;span class="kwrd"&gt;if&lt;/span&gt; (HttpContext.Current.Request.UserLanguages != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;HttpContext.Current.Request.UserLanguages.Length != 0)&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;             {&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;                 &lt;span class="rem"&gt;//Gets accepted list &lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;                 langName = HttpContext.Current.Request.UserLanguages[0].Substring(0, 2);&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;             }&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;             ci = &lt;span class="kwrd"&gt;new&lt;/span&gt; CultureInfo(langName);&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;             &lt;span class="kwrd"&gt;this&lt;/span&gt;.Session[&lt;span class="str"&gt;"Culture"&lt;/span&gt;] = ci;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;         }&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;         &lt;span class="rem"&gt;//Finally setting culture for each request&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;         Thread.CurrentThread.CurrentUICulture = ci;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;         Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;     }&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Running this will result with following page, and clicking language links will reload page with selected culture&lt;br /&gt;&lt;br /&gt;&lt;a href="http://alexadamyan.files.wordpress.com/2010/02/culturechooserpage.png"&gt;&lt;img alt="Culture Chooser " class="alignnone size-full wp-image-21" height="311" src="http://alexadamyan.files.wordpress.com/2010/02/culturechooserpage.png" title="Culture Chooser " width="725" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;Model Validation Messages Localization&lt;/h2&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;There is already good solution I found recently available on the net posted by Phil Haack, but as this should be full guide, I can't leave that aspect untouched and because there are some misunderstandings which I want to clarify. But before you begin I strongly recommend you to read Phil Haack's &lt;a href="http://haacked.com/archive/2009/12/07/localizing-aspnetmvc-validation.aspx" target="_blank" title="post"&gt;post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I'm going to explain how to localize Account models validation messages, and particularly for RegistrationModel. Also I want to describe how to localize Membership validation messages which are hard coded in AccountController.&lt;br /&gt;&lt;br /&gt;OK, lets create &lt;i&gt;ValidationStrings.resx&lt;/i&gt; and &lt;i&gt;ValidationStrings.ru.resx&lt;/i&gt; in &lt;b&gt;Resources\Models\Account&lt;/b&gt; folder(make sure you set access modifier to &lt;i&gt;public&lt;/i&gt;). As you can guess we'll be storing all validation messages in that files.&lt;br /&gt;&lt;br /&gt;I created following properties in both resource files(English example)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://alexadamyan.files.wordpress.com/2010/02/validation-msg.png"&gt;&lt;img alt="Validation messages" class="alignnone size-full wp-image-25" height="215" src="http://alexadamyan.files.wordpress.com/2010/02/validation-msg.png" title="Validation messages" width="600" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We need to modify our models in following way(example of RegisterModel)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;[PropertiesMustMatch(&lt;span class="str"&gt;"Password"&lt;/span&gt;, &lt;span class="str"&gt;"ConfirmPassword"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;ErrorMessageResourceName = &lt;span class="str"&gt;"PasswordsMustMatch"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; RegisterModel&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    {&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;        [Required(ErrorMessageResourceName = &lt;span class="str"&gt;"Required"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;        [DisplayName(&lt;span class="str"&gt;"Username"&lt;/span&gt;)]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; UserName { get; set; }&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;        [Required(ErrorMessageResourceName = &lt;span class="str"&gt;"Required"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;        [DataType(DataType.EmailAddress)]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;        [DisplayName(&lt;span class="str"&gt;"Email"&lt;/span&gt;)]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Email { get; set; }&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;        [Required(ErrorMessageResourceName = &lt;span class="str"&gt;"Required"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;        [ValidatePasswordLength(ErrorMessageResourceName = &lt;span class="str"&gt;"PasswordMinLength"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;        [DataType(DataType.Password)]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;        [DisplayName(&lt;span class="str"&gt;"Password"&lt;/span&gt;)]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Password { get; set; }&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;        [Required(ErrorMessageResourceName = &lt;span class="str"&gt;"Required"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;        [DataType(DataType.Password)]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;        [DisplayName(&lt;span class="str"&gt;"Confirm password"&lt;/span&gt;)]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; ConfirmPassword { get; set; }&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;    }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;We add &lt;i&gt;ErrorMessageResourceName &lt;/i&gt;and &lt;i&gt;ErrorMessageResourceType &lt;/i&gt;properties to &lt;i&gt;Required&lt;/i&gt;, &lt;i&gt;PropertiesMustMatch &lt;/i&gt;and &lt;i&gt;ValidatePasswordLength &lt;/i&gt;attributes, where &lt;i&gt;ErrorMessageResourceType &lt;/i&gt;is type of resource class where messages are stored and &lt;i&gt;ErrorMessageResourceName &lt;/i&gt;is property name. Unfortunately there is no way to provide strongly typed mechanism for reading that values, so be sure that these magic string have right values.&lt;br /&gt;&lt;br /&gt;We are almost done, just one little thing. There are two custom validation attributes PropertiesMustMatchAttribute and ValidatePasswordLenghtAttribute in which we should change &lt;i&gt;CultureInfo.CurrentUICulture &lt;/i&gt;in FormatErrorMessage method to &lt;i&gt;CultureInfo.CurrentCulture &lt;/i&gt; otherwise this will not work for our configuration.&lt;br /&gt;&lt;br /&gt;OK, now run application, go to registration page, click language link to change culture and you should get something like this when try to submit empty form&lt;br /&gt;&lt;br /&gt;&lt;a href="http://alexadamyan.files.wordpress.com/2010/02/registration-page.png"&gt;&lt;img alt="Registration page validation" class="alignnone size-full wp-image-26" height="684" src="http://alexadamyan.files.wordpress.com/2010/02/registration-page.png" title="Registration page validation" width="798" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Oops, as you can notice we forgot to localize names of properties in view models and now it's mixed language. To do so we need to localize DisplayName attribute value, but it's not so simple as it seems to be. I'm going to cover this issue in the next chapter, and now we have some little thing left. It's Membership API validation messages localization.&lt;br /&gt;&lt;br /&gt;Open AccountController and scroll down to the end, there should be method ErrorCodeToString which create error messages in case when user registration was failed. All messages are hard coded. All we need to do is create&amp;nbsp;appropriate&amp;nbsp;properties for each one in already created ValidationStrings resource files and put them instead of strings in ErrorCodeToString method.&lt;br /&gt;&lt;br /&gt;That's all with model validation. Now it's time for DisplayName!&lt;br /&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;DisplayName Attribute localization&lt;/h2&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;As we see in the previous chapter DisplayName value participates in validation messages which are using parameters for formatting. Also one more reason to think about DisplayName attribute is labels of fields in HTML form, these are created using value of DisplayName.&lt;br /&gt;&lt;br /&gt;The real problem is that DisplayName doesn't support localization, there is no way to provide resource file from which it can read its value.&lt;br /&gt;&lt;br /&gt;This both mean that we need to extend DisplayNameAttribute and override DisplayName property which will always return localized name. I created such derived class and named it LocalizedDisplayName&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; LocalizedDisplayNameAttribute : DisplayNameAttribute&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;   &lt;span class="kwrd"&gt;private&lt;/span&gt; PropertyInfo _nameProperty;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;   &lt;span class="kwrd"&gt;private&lt;/span&gt; Type _resourceType;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;   &lt;span class="kwrd"&gt;public&lt;/span&gt; LocalizedDisplayNameAttribute(&lt;span class="kwrd"&gt;string&lt;/span&gt; displayNameKey)&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;       : &lt;span class="kwrd"&gt;base&lt;/span&gt;(displayNameKey)&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;   {&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;   }&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;   &lt;span class="kwrd"&gt;public&lt;/span&gt; Type NameResourceType&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;   {&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;       get&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;       {&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;           &lt;span class="kwrd"&gt;return&lt;/span&gt; _resourceType;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;       }&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;       set&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;       {&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;           _resourceType = &lt;span class="kwrd"&gt;value&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;           &lt;span class="rem"&gt;//initialize nameProperty when type property is provided by setter&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;           _nameProperty = _resourceType.GetProperty(&lt;span class="kwrd"&gt;base&lt;/span&gt;.DisplayName,&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;BindingFlags.Static | BindingFlags.Public);&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;       }&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;   }&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;   &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; DisplayName&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;   {&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;      get&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;      {&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;           &lt;span class="rem"&gt;//check if nameProperty is null and return original display name value&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  31:  &lt;/span&gt;           &lt;span class="kwrd"&gt;if&lt;/span&gt; (_nameProperty == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  32:  &lt;/span&gt;           {&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;               &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;base&lt;/span&gt;.DisplayName;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;           }&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;           &lt;span class="kwrd"&gt;return&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt;)_nameProperty.GetValue(_nameProperty.DeclaringType, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;       }&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Important thing here to understand is that we need to read property value every time it's called, that's why GetValue method called in the getter of DisplayName property, and not in the constructor.&lt;br /&gt;&lt;br /&gt;For storing display names I created &lt;i&gt;Names.resx&lt;/i&gt; and &lt;i&gt;Names.ru.resx&lt;/i&gt; resource files under &lt;b&gt;Resources\Models\Account &lt;/b&gt;folder and create following properties&lt;br /&gt;&lt;br /&gt;&lt;a href="http://alexadamyan.files.wordpress.com/2010/02/displaynames.png"&gt;&lt;img alt="Display names localized" class="alignnone size-full wp-image-28" height="370" src="http://alexadamyan.files.wordpress.com/2010/02/displaynames.png" title="Display names localized" width="798" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now we need to change DisplayName attribute to LocalizedDisplayName and provide resource class type. The modified RegisterModel code will look like this&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;[PropertiesMustMatch(&lt;span class="str"&gt;"Password"&lt;/span&gt;, &lt;span class="str"&gt;"ConfirmPassword"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;ErrorMessageResourceName = &lt;span class="str"&gt;"PasswordsMustMatch"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; RegisterModel&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    {&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;        [Required(ErrorMessageResourceName = &lt;span class="str"&gt;"Required"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;        [LocalizedDisplayName(&lt;span class="str"&gt;"RegUsername"&lt;/span&gt;, NameResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(Names))]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; UserName { get; set; }&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;        [Required(ErrorMessageResourceName = &lt;span class="str"&gt;"Required"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;        [DataType(DataType.EmailAddress)]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;        [LocalizedDisplayName(&lt;span class="str"&gt;"RegEmail"&lt;/span&gt;, NameResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(Names))]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Email { get; set; }&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;        [Required(ErrorMessageResourceName = &lt;span class="str"&gt;"Required"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;        [ValidatePasswordLength(ErrorMessageResourceName = &lt;span class="str"&gt;"PasswordMinLength"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;        [DataType(DataType.Password)]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;        [LocalizedDisplayName(&lt;span class="str"&gt;"RegPassword"&lt;/span&gt;, NameResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(Names))]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Password { get; set; }&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;        [Required(ErrorMessageResourceName = &lt;span class="str"&gt;"Required"&lt;/span&gt;,&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;ErrorMessageResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ValidationStrings))]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;        [DataType(DataType.Password)]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;        [LocalizedDisplayName(&lt;span class="str"&gt;"RegConfirmPassword"&lt;/span&gt;, NameResourceType = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(Names))]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; ConfirmPassword { get; set; }&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;    }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Run application to make sure that everything work as expected, for me this will be like&lt;br /&gt;&lt;br /&gt;&lt;a href="http://alexadamyan.files.wordpress.com/2010/02/displaynamesfixedregistration.png"&gt;&lt;img alt="DisplayNames Fixed Registration" class="alignnone size-full wp-image-29" height="684" src="http://alexadamyan.files.wordpress.com/2010/02/displaynamesfixedregistration.png" title="DisplayNames Fixed Registration" width="798" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;OutputCache and Localization&lt;/h2&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;What a strange chapter? You think how can caching and localization be connected? OK, lets try following scenario: open HomeController and add OutputCache attribute to Index action method, so that action code will look like following:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;[OutputCache(Duration=3600, VaryByParam=&lt;span class="str"&gt;"none"&lt;/span&gt;)]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Index()&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;{&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;    ViewData[&lt;span class="str"&gt;"Message"&lt;/span&gt;] = &lt;span class="str"&gt;"Welcome to ASP.NET MVC!"&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;    &lt;span class="kwrd"&gt;return&lt;/span&gt; View();&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now build application and try to change language in Index page to check that localized Title is still localized.&lt;br /&gt;&lt;br /&gt;Oh no? You already think that these two things can't be used together? Don't hurry, there is a solution :)&lt;br /&gt;&lt;br /&gt;What do you know about OutputCache and particularly what do you know about VaryByCustom property? Now it's time to use it.&lt;br /&gt;&lt;br /&gt;What's happening here? When we request Index page for first time OutputCache caches the page. For the second request (when we click language link) &amp;nbsp;OutputCache thinks that nothing was changed and returns result from cache, so the page is not created again. That's why language chooser doesn't work.&amp;nbsp;To solve the problem we need somehow say to OutputCache that page version was changed(like as it's working in case if action has some param and we put that param to VaryByParam property).&lt;br /&gt;&lt;br /&gt;VaryByCustom is ideal candidate for that purpose and there is special method in System.Web.HttpApplication which derived class placed in Global.asax.cs file. We'll override the default implementation of that method.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; GetVaryByCustomString(HttpContext context, &lt;span class="kwrd"&gt;string&lt;/span&gt; &lt;span class="kwrd"&gt;value&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;     &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;value&lt;/span&gt;.Equals(&lt;span class="str"&gt;"lang"&lt;/span&gt;))&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;     {&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;         &lt;span class="kwrd"&gt;return&lt;/span&gt; Thread.CurrentThread.CurrentUICulture.Name;&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;     }&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;     &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;base&lt;/span&gt;.GetVaryByCustomString(context,&lt;span class="kwrd"&gt;value&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;First the method checks if value param equals to "lang" (no special meaning, just string which will be used as value for VaryByCustom) and in case if they are equal returns name of current culture. Otherwise we return value of default implementation. &lt;br /&gt;&lt;br /&gt;Now add VaryByCustom property with value "lang" to each OutputCache attribute you want to use together with localization and that's all. The updated Index action method will look like this&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;[OutputCache(Duration=3600,VaryByParam=&lt;span class="str"&gt;"none"&lt;/span&gt;, VaryByCustom=&lt;span class="str"&gt;"lang"&lt;/span&gt;)]&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; ActionResult Index()&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;{&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;     ViewData[&lt;span class="str"&gt;"Message"&lt;/span&gt;] = &lt;span class="str"&gt;"Welcome to ASP.NET MVC!"&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;     &lt;span class="kwrd"&gt;return&lt;/span&gt; View();&lt;/pre&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Try to run again and ensure that culture chooser is working again.&lt;br /&gt;&lt;br /&gt;We've finished the last chapter and I hope I didn't miss anything. If you don't think so please let me know.&lt;br /&gt;&lt;br /&gt;Thanks for reading, feel free to contact me if you have any question and make comments :)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fadamyan.blogspot.com%2f2010%2f02%2faspnet-mvc-2-localization-complete.html"&gt;&lt;img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fadamyan.blogspot.com%2f2010%2f02%2faspnet-mvc-2-localization-complete.html&amp;amp;fgcolor=000000&amp;amp;bgcolor=00CCFF" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fadamyan.blogspot.com%2f2010%2f02%2faspnet-mvc-2-localization-complete.html"&gt;&lt;/a&gt;&lt;a href="http://dotnetshoutout.com/ASPNET-MVC-2-Localization-complete-guide" rev="vote-for"&gt;&lt;img alt="Shout it" src="http://dotnetshoutout.com/image.axd?url=http%3A%2F%2Fadamyan.blogspot.com%2F2010%2F02%2Faspnet-mvc-2-localization-complete.html" style="border-width: 0px;" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4091304575863058921-2687016707360634227?l=adamyan.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adamyan.blogspot.com/feeds/2687016707360634227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://adamyan.blogspot.com/2010/02/aspnet-mvc-2-localization-complete.html#comment-form' title='85 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4091304575863058921/posts/default/2687016707360634227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4091304575863058921/posts/default/2687016707360634227'/><link rel='alternate' type='text/html' href='http://adamyan.blogspot.com/2010/02/aspnet-mvc-2-localization-complete.html' title='ASP.NET MVC 2 Localization complete guide'/><author><name>Alex Adamyan</name><uri>http://www.blogger.com/profile/11560575110495312401</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='31' src='http://4.bp.blogspot.com/_tzjn1Yeo5bM/S4OAg_yHPOI/AAAAAAAAAB4/sNGQVhZxznM/S220/avatar.jpg'/></author><thr:total>85</thr:total></entry></feed>
