Localize dataannotation error messages as per culture - c#

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,

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.

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" />

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
CultureInfo.DefaultThreadCurrentCulture
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...

Related

Resources.resx not loading in WPF

I try to make my application multilingual. For this I have created two resource files:
Resource.de-DE.resx
Resource.en-US.resx
Both are set to public and contain the same keyword and translation. I can also access the keywords in the backend -> see image.
Unfortunately my program does not load the files when I change the language.
My steps:
set the default Resources.resx to public.
copied this file twice
renamed file to (see above)
keywords+translation entered and checked
project created again
included on the Button-Content by:
<Button x:Name="Home_Button" Style="{DynamicResource Menuebutton}" Content="{x:Static p:Resources.Menue_home}" Click="Home_Click"/>
access of the object set by:
xmlns:p="clr-namespace:VS_Launcher.Properties"
include in the MainWindow.xaml.cs by:
CultureInfo myCultureInfo = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentCulture = myCultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = myCultureInfo;
Thread.CurrentThread.CurrentCulture = myCultureInfo;
Thread.CurrentThread.CurrentUICulture = myCultureInfo;
In another project I did the same thing. But there the solution worked. What is my error?
Resource Files
Directory structure
Are you sure the file names of alternative language resources (excluding culture-locale) are identical with default one? If the default one is Resources.resx, alternative ones must be like:
Resources.de-DE.resx
Resources.en-US.resx

Default Resource.resx file not used

I have a WPF application which is localized.
When I set Format to Hindi(India) from ControlPanel -> Region -> Formats, Following lines of code in my WPF application at the beginning of launching of my WPF Application is not reading CultureInfo.CurrentCulture(hi-IN) instead it uses en-US.
Application.Current.MainWindow = new MainWindow();
Application.Current.MainWindow.Show();
Because of this, My WPF Application is not using greeting message from Resources.resx file. Instead, it is use greeting message from in Resources.en.resx
I am getting proper value in CultureInfo.CurrentCulture.
Any idea why above lines of code are not picking proper value?
The ControlPanel->Region->Formats setting doesn't apply to .resx files. It is in ControlPanel->Region->Language that you specify the default language.
What is the difference between CurrentCulture and CurrentUICulture properties of CultureInfo in .NET?
Alternatively you could specify the default language of your resources in your App class (App.xaml.cs):
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Resources.Culture = System.Threading.Thread.CurrentThread.CurrentCulture;
}
}
Please refer to the following link for more information: https://social.msdn.microsoft.com/Forums/vstudio/en-US/6bfb8d13-3a86-4c10-a632-bb20c99d0535/localization-in-wpf-using-resx-files-for-different-languages?forum=wpf.

Culture Resource Manager

Problem
I am trying to implement a system in my program to switch language. I found out that i can use CultureInfo and ResourceManager to achieve that. I built up this code after a couple of hours having problem with the resource not found, finally i found and answer here on stackoverflow and i arranged the following code:
CultureInfo culture;
culture = CultureInfo.CreateSpecificCulture("it-IT");
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
Assembly resourceAssembly = Assembly.Load("MY ASSEMBLY NAME");
ResourceManager manager = Properties.Resources.ResourceManager;
string[] manifests = resourceAssembly.GetManifestResourceNames();
string manifest = manifests[0].Replace(".resources", string.Empty);
manager = new ResourceManager(manifest, resourceAssembly);
string greeting = String.Format("The current culture is {0}.\n{1}",
Thread.CurrentThread.CurrentUICulture.Name,
manager.GetString("HelloString"));
MessageBox.Show(greeting);
Since this is a really big program with a lot of pages, windows and usercontrols, i need to access the language from a lot of different files.
The code i posted above, should look into the root of my Solution and look for a file named it-IT.resx. it says that the current culture is it-IT but it doesn't write the value of HelloString, but it doesn't give any error so it's definitely a problem with Resource Manager but i don't know why it doesn't crash saying it doesn't find the resource. I am sure that inside the resx file there is a value called HelloString.
Is there a reason to load the types dynamically with Assembly.Load()? It is asking for trouble. Another way would be adding a static assembly reference.
This approach has many advantages:
the resource names can be accesses as property names, it is comfortable
the calling code uses always existing resources
in the calling code there are no references to non-existing resources
To be able to use resources from another assembly the resource access modifier needs to be set to public:
If the resources file is named RText, as in the example above, the value of a resource can retrieved from another project with:
var val = ProjectWithResourcesNamespace.RText.HelloString;

WPFLocalizeExtension proper Folder structure

I am having issues with the already mentioned NuGet package. I have created a folder named Resources, where I have inserted the following files:
Strings.en.resx
Strings.sk.resx (my default language)
I am using Prism, where my MainWindow (plain container) is located under root window and all the other pages are located under Views folder.
I have added the following to my MainPage view:
xmlns:lex="http://wpflocalizeextension.codeplex.com"
lex:LocalizeDictionary.DesignCulture="en"
lex:LocalizeDictionary.OutputMissingKeys="True"
lex:ResxLocalizationProvider.DefaultAssembly="eManagement"
lex:ResxLocalizationProvider.DefaultDictionary="Strings"
and here is an example of a TextBlock:
<TextBlock Text="{lex:Loc Key=Greetings}"/>
The problem is, that if I place the Strings.resx file in the root folder, the control is translated without a problem. But when I want to keep my translations under a Resources folder (meaning that english translation will be under Resources/Strings.en.resx), it suddenly does not work.
What do I need to change in order to make it work?
I was missing the following line:
lex:InheritingResxLocalizationProvider.DefaultDictionary="Strings"
Plus in the code-behind I had to manually set the culture as otherwise my culture was always invariant
#region Language Selection
var culture = new CultureInfo(Properties.Settings.Default.LanguageSettings);
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
WPFLocalizeExtension.Engine.LocalizeDictionary.Instance.Culture = Thread.CurrentThread.CurrentCulture;
#endregion
This has solved the problem.

Error message accessing asp.net Global Resource string programmatically

Trying to access a specific localised resource file such as WebResource.en.us which is located in my App_GlobalResources folder using the following code:
string resData = GetGlobalResourceObject("WebResource.en.us", "SomeResource").ToString();
but this keeps giving me the error below:
Could not find any resources appropriate for the specified culture or
the neutral culture. Make sure "Resources.WebResource.en-us.resources"
was correctly embedded or linked into assembly
"App_GlobalResources.bpqqrnv4" at compile time.
Any ideas ?
I tried the following Code
Page_Load(....)
{
/// note - i did NOT mention the culture when accesing my resourceFiles
Debug.WriteLineIf(
GetGlobalResourceObject("WebResource", "someResource")!=null,
GetGlobalResourceObject("WebResource", "someResource").ToString());
/// accessing a culture specific resource without changing Page Culture
CultureInfo yourCI = new CultureInfo("en-US");
Debug.WriteLine(
HttpContext.GetGlobalResourceObject(
"WebResource",
"someResource",
yourCI).ToString());
}
my Page directive
<% Page Culture="en-US" UICulture="en-US" ..... %>
My App_GlobalResources folder contains two files
WebResource.resx
WebResource.en-US.resx
Using this settings and code - my Debugger printed the value without any problems.
When removing WebResource.resx (my default ressource file) the same code throws an exception.
I would assume that you have to add a default resx file and remove the explicit culture notation in GetGlobalResourceObject(..., ..).
update: added some code to access specific resx culture file
see also MSDN

Categories