Localize dataannotation error messages as per culture
Asked Answered
S

3

0

In my WPF appliation, I have property defined as following

[Required(AllowEmptyStrings =false, ErrorMessageResourceName ="Msg1", ErrorMessageResourceType =typeof(*<AssemblyName>*.Resources.*<ResourceFileName>*))]
public string Name
{
    get
    {
        return _name;
    }

    set
    {
        if (_name == value)
        {
            return;
        }
        _name = value;
    }
}

I have my error messages defined in separate assembly which has resource file for different cultures e.g. Resources.resx, Resources.en-GB.Resx, Resources.fr-FR.Resx, Resources.en-US.Resx, etc.

With above code in place, I'm able to retrieve the error message from default resource file in my satellite assembly but I don't see any provision to find the string resource from culture specific resource file. What I mean is if my CurrentUICluture is set as english(United Kingdom) then I want to retrieve the resource value from the file "Resources.en-GB.Resx" instead of the default file (i.e. Resources.Resx).

I don't see any way to pass the culture info in the Required attribute definition. Also, I have tested that it is not inherently look into the culture specific resource file based on the current culture set.

What I want is some way to make the resource retrieval mechanism culture aware.

Thanks,

Scotsman answered 7/10, 2015 at 15:11 Comment(0)
S
0

Finally I got the fix for my problem from this link

In my app, I was setting the culture at the time of starting the application by putting following code in app.xaml.cs file.

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        InitializeCultures();
    }

    private static void InitializeCultures()
    {
        var culture = ConfigurationManager.AppSettings.Get("Culture");
        if (!String.IsNullOrEmpty(culture))
        {
            Thread.CurrentThread.CurrentCulture =  new CultureInfo(culture);
        }

        var UICulture = ConfigurationManager.AppSettings.Get("UICulture");
        if (!String.IsNullOrEmpty(UICulture))
        {
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(UICulture);
        }
    }

However problem is even if I set the Culture initially, not all threads will use the same culture set by me initially. So the thread which was reading the resource value for me was still using the default culture and this resulted in reading of resource string always from default culture resource file rather then the culture specific resource file.

So the trick was to set all the threads in my app to use the same culture which I set initially. There are two properties for this

  1. CultureInfo.DefaultThreadCurrentCulture
  2. CultureInfo.DefaultThreadCurrentUICulture

Setting required culture value to these two properties initially will ensure that all the subsequent threads used in the application will have the same culture as set by user. These two properties sets the culture on all the threads in current app domain.

Once culture is set properly, reading of resource values inherently becomes culture aware and reads resource values from culture specific resource files.

So following is the updated version of InitializeCulture method :

    private static void InitializeCultures()
    {
        var culture = ConfigurationManager.AppSettings.Get("Culture");
        if (!String.IsNullOrEmpty(culture))
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.DefaultThreadCurrentCulture =  new CultureInfo(culture);
        }

        var UICulture = ConfigurationManager.AppSettings.Get("UICulture");
        if (!String.IsNullOrEmpty(UICulture))
        {
            Thread.CurrentThread.CurrentUICulture = CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(UICulture);
        }
    }

P.S. There is a word of caution of using this fix in web application. Pls read about it on original link.

Hope this helps...

Scotsman answered 8/10, 2015 at 7:17 Comment(0)
U
1

I came up with an easy idea that saves me from decorating all properties individually. Indeed a nasty solution because it involves reflection but works.

Caution: this can break if the .NET Core team decide to rename fields or classes or if internal stuff change, so use at your own discretion.

First create a resx file in your app be either creating one from scratch or by copying this or this one, the replace the strings with the desired translations, in the same format as they appear (pay attention to {0} placeholders and to their order).
Obviously, you can then add DataAnnotations.en-UK.resx-like files and translate them all, as long as your app is properly set to operate with the desired culture, it'll work.

In your project, call the following at early startup:

void InitializeDataAnnotationsCulture()
{
  var sr = 
    typeof(ValidationAttribute)
      .Assembly
      .DefinedTypes
      .Single(t => t.FullName == "System.SR");

  var resourceManager = 
    sr
      .DeclaredFields
      .Single(f => f.IsStatic && f.Name == "s_resourceManager");

  resourceManager
    .SetValue(null, 
      DataAnnotationsResources.ResourceManager, /* The generated RESX class in my proj */
      BindingFlags.NonPublic | BindingFlags.Static, null, null);

  var injected = resourceManager.GetValue(null) == DataAnnotationsResources.ResourceManager;
  Debug.Assert(injected);
}

What it does is it takes advantage of this field to replace the default DataAnnotations resource manager with your own.

It's been tested and works in .NET Core 3.1 WPF, but you can browse the .NET Source of any .NET to find the resource location and inject your own resources.

Ulberto answered 15/12, 2019 at 12:0 Comment(0)
L
0

If you'd like to make your website culture aware, you'll need to modify the globalization attribute in the web.config under the system.web element:

<globalization fileEncoding="utf-8" requestEncoding="utf-8" culture="auto" uiCulture="auto" enableClientBasedCulture="true" />

Then it will look for resources based on the browser's preferred language settings.

Alternatively, you can specify it explicitly:

<globalization fileEncoding="utf-8" requestEncoding="utf-8" responseEncoding="" culture="en-US" uiCulture="en-US" />
Lauritz answered 7/10, 2015 at 16:33 Comment(2)
Thanks for your reply. But mine is WPF application and not web application. Any clue how to make it work in WPF application?Scotsman
Ahh sorry, I missed that you mentioned that in your original post. I don't see any issues with the code you have provided. perhaps this SO answer will help: https://mcmap.net/q/1302148/-currentuiculture-ignores-region-and-language-settingsLauritz
S
0

Finally I got the fix for my problem from this link

In my app, I was setting the culture at the time of starting the application by putting following code in app.xaml.cs file.

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        InitializeCultures();
    }

    private static void InitializeCultures()
    {
        var culture = ConfigurationManager.AppSettings.Get("Culture");
        if (!String.IsNullOrEmpty(culture))
        {
            Thread.CurrentThread.CurrentCulture =  new CultureInfo(culture);
        }

        var UICulture = ConfigurationManager.AppSettings.Get("UICulture");
        if (!String.IsNullOrEmpty(UICulture))
        {
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(UICulture);
        }
    }

However problem is even if I set the Culture initially, not all threads will use the same culture set by me initially. So the thread which was reading the resource value for me was still using the default culture and this resulted in reading of resource string always from default culture resource file rather then the culture specific resource file.

So the trick was to set all the threads in my app to use the same culture which I set initially. There are two properties for this

  1. CultureInfo.DefaultThreadCurrentCulture
  2. CultureInfo.DefaultThreadCurrentUICulture

Setting required culture value to these two properties initially will ensure that all the subsequent threads used in the application will have the same culture as set by user. These two properties sets the culture on all the threads in current app domain.

Once culture is set properly, reading of resource values inherently becomes culture aware and reads resource values from culture specific resource files.

So following is the updated version of InitializeCulture method :

    private static void InitializeCultures()
    {
        var culture = ConfigurationManager.AppSettings.Get("Culture");
        if (!String.IsNullOrEmpty(culture))
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.DefaultThreadCurrentCulture =  new CultureInfo(culture);
        }

        var UICulture = ConfigurationManager.AppSettings.Get("UICulture");
        if (!String.IsNullOrEmpty(UICulture))
        {
            Thread.CurrentThread.CurrentUICulture = CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(UICulture);
        }
    }

P.S. There is a word of caution of using this fix in web application. Pls read about it on original link.

Hope this helps...

Scotsman answered 8/10, 2015 at 7:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.