Error Message: Model not picking language-specific

2020-03-24 08:49发布

问题:

I want to display model error message in spanish and I have defined those string in resourse files. I have done the same for other string on the page using razor syntax but the ones from ViewModel annotation are not being picked.

It is actually picking the default values - English. So my guess is that may be the language/culture was not detected, but other string on the page are displayed in spanish

//Spanish: El campo {0} se requiere
//English: The {0} field is required  <--- this comes out always irrespective of set language

[Required(ErrorMessageResourceName = "ErrorMessage_Required", 
     ErrorMessageResourceType = typeof(GlobalResources.Resources))]
[Display(Name = "CardNumber", ResourceType = typeof(GlobalResources.Resources) )]
public string CardNumber { get; set; }

I set the language in my Controller

protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
    base.OnActionExecuting(filterContext);

    HttpCookie cookie = Request.Cookies["lang"];

    string lang = cookie != null ? cookie.Value : "en-US";
    System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(lang);
}

How do I extend the culture settings to ViewModels?

Update A similar post: MVC3 globalization: need global filter before model binding

Update Changing the preferred language in my browser settings made it work. This means the model attributes is using this settings which is not affected by System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(lang);. Is there a way to make this happen? - Still searching....

Update: Moving the code to Application_AcquireRequestState seems to solve it.

protected void Application_AcquireRequestState()
{
    HttpCookie cookie = Request.Cookies["lang"];

    string lang = cookie != null ? cookie.Value : "en-US";
    System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(lang);
}

The explanation I got, also found in the link posted in this question, that it was too late for the model to make use of the culture being set in controller overridden method as the binding had already occurred before the method is called. This link was helpful

回答1:

Make sure you set both CurrentCulture and CurrentUICulture. I suspect CurrentCulture is actually what the resource files are looking at:

 //I've taken off System.Threading, add a using to it (aids readability)

 Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(lang);
 Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(lang);


回答2:

Try using the below class

public sealed class LanguageManager
{
    /// <summary>
    /// Default CultureInfo
    /// </summary>
    public static readonly CultureInfo DefaultCulture = new CultureInfo("en-US");

    /// <summary>
    /// Available CultureInfo that according resources can be found
    /// </summary>
    public static readonly CultureInfo[] AvailableCultures;

    static LanguageManager()
    {
        List<string> availableResources = new List<string>();
        string resourcespath = Path.Combine(System.Web.HttpRuntime.AppDomainAppPath, "App_GlobalResources");
        DirectoryInfo dirInfo = new DirectoryInfo(resourcespath);
        foreach (FileInfo fi in dirInfo.GetFiles("*.*.resx", SearchOption.AllDirectories))
        {
            //Take the cultureName from resx filename, will be smt like en-US
            string cultureName = Path.GetFileNameWithoutExtension(fi.Name); //get rid of .resx
            if (cultureName.LastIndexOf(".") == cultureName.Length - 1)
                continue; //doesnt accept format FileName..resx
            cultureName = cultureName.Substring(cultureName.LastIndexOf(".") + 1);
            availableResources.Add(cultureName);
        }

        List<CultureInfo> result = new List<CultureInfo>();
        foreach (CultureInfo culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
        {
            //If language file can be found
            if (availableResources.Contains(culture.ToString()))
            {
                result.Add(culture);
            }
        }

        AvailableCultures = result.ToArray();

        CurrentCulture = DefaultCulture;
        if (!result.Contains(DefaultCulture) && result.Count > 0)
        {
            CurrentCulture = result[0];
        }
    }

    /// <summary>
    /// Current selected culture
    /// </summary>
    public static CultureInfo CurrentCulture
    {
        get { return Thread.CurrentThread.CurrentCulture; }
        set
        {
            Thread.CurrentThread.CurrentUICulture = value;
            Thread.CurrentThread.CurrentCulture = value;
        }
    }
}

And finally set the culture like below.

LanguageManager.CurrentCulture = new CultureInfo("Your culture");

and now override the culture