4

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 <a href="#">@TextResources.my_key</a> 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:

<a href="#">@.ConfigUtils.getTerms()</a>

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);
    }
}
Mister Epic
  • 16,295
  • 13
  • 76
  • 147
  • Azure instances are always popped up with a en-US locale. Check: https://yossidahan.wordpress.com/2012/02/17/locale-on-windows-azure/ – Jcl Jan 26 '15 at 13:21
  • @Jcl Interesting, but for some reason the site is sometimes localized to Dutch... – Mister Epic Jan 26 '15 at 13:38
  • 1
    @MisterEpic, what shows up if you do `Debug.WriteLine(new { SynchronizationContext.Current })` at the beginning of `OnActionExecuting`? Is it `AspNetSynchronizationContext`? – noseratio Jan 30 '15 at 02:37
  • 1
    @Noseratio comment is likely spot on - if you are using `async` in your code you have good chance to do it wrong... Possible answer added. – Alexei Levenkov Jan 31 '15 at 03:07
  • @AlexeiLevenkov, I think we have already figured it out [here](http://stackoverflow.com/a/28227165/1768303) :) – noseratio Jan 31 '15 at 03:10
  • 1
    @Noseratio - you should have answered it here too (or OP self-answered)... So wiki it is as I can't close as duplicate ... – Alexei Levenkov Jan 31 '15 at 03:15
  • @AlexeiLevenkov, I hope the OP could confirm here it's .`ConfigureAwait(false)` indeed. – noseratio Jan 31 '15 at 03:33

3 Answers3

2

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.

Community
  • 1
  • 1
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • Excellent suggestion. I did end up identifying the root cause, but I will certainly keep what you've mentioned here in mind in future. – Mister Epic Feb 23 '15 at 13:36
1

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.

Richard Schneider
  • 34,944
  • 9
  • 57
  • 73
0

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);
Mister Epic
  • 16,295
  • 13
  • 76
  • 147