ASP.NET local resources' Culture property scope - c#

I have an ASP.NET 5 application and I am currently implementing a localization.
Resources are located in the ASP.NET folder App_LocalResources and everything is working properly. At least, while it is in development phase. I have the following ResourceCultureFilterAttribute:
using MyProject.App_LocalResources;
public class ResourceCultureFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string language = GetClientLanguageFromCookiesDoesntMatter();
var culture = new CultureInfo(language);
GlobalResources.Culture = culture;
}
}
And now I access it this way in Razor view:
#GlobalResources.SomeResourceName
Now, I am worrying that Culture propery of auto-generated resource class is static.
Does it mean that this property is set per-thread or per-application-pool (please, specify the right one) but not per-user?
If yes, then several users will just override and use the single value and definitely cause problems to localization.
I know how to fix it by replacing direct access by:
#GlobalResources.ResourceManager.GetString("ResourceName",
new CultureInfo(GetClientLanguageFromCookiesDoesntMatter());
But, maybe, there is a more proper solution - something important that I just don't see or don't know. Maybe, there is a special property like CurrentUserCulture, special multi-user resource class, attribute or something else to provide a simple and plain localization into an ASP.NET project.

Related

StructureMap and HTTP request-scoped services - why is my service created twice in a single scope?

I have an ASP.NET MVC application using StructureMap.
I have created a service called SecurityContext which has a static Current property. A simplified version looks like this:
public class SecurityContext : ISecurityContext
{
public bool MyProperty { get; private set; }
public static SecurityContext Current
{
get
{
return new SecurityContext() { MyProperty = true };
}
}
}
I've hooked this up in my StructureMap registry as follows:
For<ISecurityContext>().Use(() => SecurityContext.Current);
My understanding of this Linq expression overload of the Use method is that the returned concrete object is the same for the entire HTTP request scope.
However, I've set up a test case where my context interface is injected in two places, once in the controller's constructor and again using the SetterProperty attribute in the base class my view inherits from.
When debugging I observe the Current static method being hit twice so clearly my assumptions are wrong. Can anyone correct what I'm doing here? The reason I want this request-scoped is because I'm loading certain data into my context class from the database so I don't want this to happen multiple times for a given page load.
Thanks in advance.
The default lifecycle for a configuration is Transient, thus each request for an ISecurityContext will create a new instance of SecurityContext. What I think you want is to use the legacy HttpContext lifecycle.
Include the StructureMap.Web nuget package. Then change your configuration to the following:
For<ISecurityContext>()
.Use(() => SecurityContext.Current)
.LifeCycleIs<HttpContextLifecycle>();
More information on lifecyles can be found here.
The HttpContextLifecycle is obsolete, however I do not know if or when it will be removed. The StructureMap team does recommend against using this older ASP.Net lifecycle. They state in the documentation that most modern web frameworks use a nested container per request to accomplish the same scoping. Information about nested containers can be found here.
I don't know if the version of ASP.Net MVC you are using is considered a modern web framework. I doubt it is because ASP.Net Core 1.0 is the really the first in the ASP.Net line to fully embrace the use of DI. However, I will defer to #jeremydmiller on this one.

Culture not being applied in multilingual site

I have a very unusual issue that has me completely stumped. We have a multilingual website so we employ Resource files. However, every piece of text on our Views that has been burned in like #TextResources.my_key will be localized to a random culture. This happens only on my Azure deployment, I cannot reproduce locally.
Adding to the mystery is that there is a lone bit of text that ALWAYS respects my change of culture. That text gets retrieved via a method call:
#.ConfigUtils.getTerms()
Method is:
public static string getTerms()
{
string key = GetKeyFromDb(CONSTANTS.TERMS);
if (!string.IsNullOrEmpty(key))
{
return TextResources.ResourceManager.GetString(key);
I'm still reading from our resource file, but in this context, it is being localized as desired! Is the culture being applied after the resource file is read in the view, but before this method is called?!
All our controllers inherit from a base controller, where we override OnActionExecuting() to apply our culture:
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
ContextModel ctx = (ContextModel) Session["ContextModel"];
// Set the correct localization according to the value set by the user
if (ctx != null && ctx.UserLanguageId != null){
Thread.CurrentThread.CurrentUICulture = new CultureInfo (ctx.UserLanguageId);
Thread.CurrentThread.CurrentCulture = new CultureInfo(ctx.UserLanguageId);
}
}
Before I start moving the culture management code to different spots and re-deploying to Azure in hopes that resolves the issue, I'm hoping that someone has a thought as to why only the text retrieved via the method call gets localized.
OnActionExecuting() is executed before the action, so I had thought this would be the appropriate spot to put culture management code. Is there somewhere else that would be better?
UPDATE
It looks like this issue presents itself after a deployment, but can be resolved by restarting the cloud service.
UPDATE 2
Per #RichardSchneider's request, the auto-generated TextResources code is as follows:
public static string my_key{
get {
return ResourceManager.GetString("my_key", resourceCulture);
}
}
The question is already answered here: Asynchrony and thread culture
One likely option is you are using async and .ConfigureAwait(false) in your code. As result code after awiat (that get text/renders view) picks up random culture from the thread it happen to execute instead of one you were hoping to set in the synchronous part in your action filter.
Possible fixes - don't use that .ConfigureAwait(false) or if you have to than pass culture explicitly through all calls and make sure to set it back before making locale-sensetive calls.
More remote possibility: Assuming you use default resource file generation for its normal functioning in ASP.Net application you should
set CurrentUICulture
make sure that Culture property of generated resource manager set to null.
Based on behavior you see I guess something sets Culture wrong in initial start and as result you get "random" culture.
To debug try to check value of Culture property - should be null.
The method works because it using the ResourceManager which respects the current culture.
TextResources.my_key fails because it is (most likely) only assigned a value once on its fist use.
Update
The auto-generated code for my_key is passing the culture info to ResourceManager.GetString. I suggest modifying the autogenerator to use the overload of just one argument to produce:
public static string my_key{
get {
return ResourceManager.GetString("my_key");
}
}
If this is not possible, then you need OnActionExecuting to set resource_culture. resource_culture will need to be a thread local variable for this to work.
In case this happens to someone else, I determined the root cause. We had a page where users were able to download a report via a URL provided to them in an email. The URL included a language querystring variable used for localization:
/report/download?id=123&lang=de
In the controller action, the original author set the culture on our resource object, not the thread:
TextResources.Culture = new CultureInfo("de");
Looking at the metadata:
[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
public class TextResources
{
public static CultureInfo Culture { get; set; }
So, it appears this property takes precedence over the thread culture when it comes times to apply localization in our views. And since the property is static, it was applied globally across whichever Azure instance was serving the report. So as people were downloading these reports in various languages, the effect appeared random.
I admit I am still unclear as to why the following always respected thread culture:
public static string getTerms()
{
string key = GetKeyFromDb(CONSTANTS.TERMS);
if (!string.IsNullOrEmpty(key))
{
return TextResources.ResourceManager.GetString(key);

Localization messages in Fluent validation (MVC3)

In my MVC 3 application I am using fluent validation.
public class AccountModelValidator : AbstractValidator<AccountModel>
{
public AccountModelValidator()
{
m.NewPassword).WithMessage(Translator.Data["ConfirmPasswordValidation"]);
}
}
For localization I am using an example that I have found from here, but I have found that I have the same validation message for all languages.
The reason is that validator doesn't know that I have change the language.
How can I correctly do this?
Maybe I should use WithLocalizedMessage but it works only with .resx
When the WithLocalizedMessage returns the same value for all languages, most likely the culture isn't set yet (this happens on a POST, where the action handlers haven't run yet)
Solution is to set the culture in a different location:
Best place to set CurrentCulture for multilingual ASP.NET MVC web applications

Good way to changing language resources dynamically by request

I have an ASP.NET Web API application which should react to user's Accept-Language header appropriately.
Currently, the strings are stored in the resx and accessed in compile-safe manner through Visual Studio's generated class. What I would like to do is to keep the current approach and create satellite assemblies for each translated version of resx. Then to analyze the user's Accept-Language header to see what languages a user accepts and load the resources for the requested language from the satellite assembly.
I suppose I could implement all this behavior myself by creating a set of language-specific ResourceManager objects with the help of the ResourceSet but then it would not be possible to keep the compile-time safety, since Visual Studio takes care of automatically updating the class for resx file.
What would be the best way to pick the localized language resource dynamically?
From reading your question, I don't see anything that ASP.NET doesn't offer automatically. You can configure your ASP.NET (whether WebForms or MVC) to use the accept-language request header and set the appropriate UICulture (which will impact which satellite assembly is loaded by ResourceManager) and Culture (which will impact locale-dependent formatting and parsing such as dates and numbers) appropriately.
To configure your app to use the accept-language list to set both UICulture and Culture for each request, (as per this MSDN page), configure your web.config like this:
<globalization uiCulture="auto" culture="auto" />
There is also an equivalent configuration setting per page.
Then, as per the Resource Fallback process, if your app includes a satellite assembly for the matching culture (or, failing that, its parent neutral culture), it will be used by the Resource Manager. If not, then your default resources will be used (English if that's your base language).
You could write an HttpModule that detects the language header, and sets the current thread culture.
public class LanguageModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
void context_BeginRequest(object sender, EventArgs e)
{
var application = sender as HttpApplication;
var context = application.Context;
var lang = context.Request.Headers["Accept-Language"];
// eat the cookie (if any) and set the culture
if (!string.IsNullOrEmpty(lang))
{
var culture = new System.Globalization.CultureInfo(lang); // you may need to interpret the value of "lang" to match what is expected by CultureInfo
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
}
}
}
ResourceManager et al will figure out the correct localized version to use from the thread culture.

How do i get user Culture in a HtmlHelper extension for MVC 3 .NET Application?

I'm creating a new HtmlHelper extension for my MVC 3 Application.
I need to get the Culture Info of the user viewing the page inside my new helper.
How can i get it??
public static class HtmlExtension
{
public static string StringFor(this HtmlHelper html, string key)
{
...
//I need to get CultureInfo in here!
...
}
}
The direct answer is "grab it through CultureInfo.CurrentCulture", but by itself it won't help you.
Consider that your application has no notion of the "user's culture" -- the user talks to you through their browser, you are not on their system.
Now the browser does give you an Accept-Language header you can work with (Google will give you many hits), but it's not recommended to use this setting to determine the user's locale because in practice hardly any users know about this setting and have customized it to reflect their preference.
The best approach is probably to unilaterally decide what the user's culture is (e.g. have en as a default, and give them a way to change it through the UI), store it somewhere (either inside session state or as a route variable) and push it into CultureInfo.CurrentCulture inside HttpApplication.AcquireRequestState. You can do this inside Global.asax (example taken from MSDN):
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
CultureInfo ci = (CultureInfo)this.Session["culture"];
if (ci != null) {
CurrentThread.CurrentUICulture = ci;
CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
}
}
At this point, this answer becomes a self-fulfilling prophecy and you can access the culture through CultureInfo.CurrentCulture from anywhere.

Categories

Resources