Good evening,
I happen to have an issue with Winforms and Resources.resx.
The app I'm working on is built via 'Winforms App' template selectable via Visual Studio.
The issue I'm having is kind of outside of winforms localization - which I think is important to mention.
Here's my issue:
- I've created a 'HistoryManager' class, which has only one method and one action to perform - add a history to the SQL database.
- I've created Resource.resx file, entered a format string into it under "MsgTaskAdded" which is equal to "Task {0} has been added to {1}".
- The said string adds to the database flawlessly.
Resources.resx works well - if I change MsgTaskAdded resource string, it changes what will be added to database .
Now, the issue I'm having is.
- I've created a Resources.de-DE.resx file, copied strings from Resources.resx and translated to german.
- Changed CurrentThread.CurrentCulture and CurrentThread.CurrentUICulture to 'de-DE'.
As a result, the text added to database is STILL in english, as if the file wasn't found and it fell back into using the default Resources.resx.
Make certain that your Resources.de-DE.resx file is contained within the Properties folder of your project, and that it has a Build Action of Embedded Resource, so that it gets properly associated to your default Resources.resx during compile. The culture settings on CurrentThread also need to happen before the call to the database, ideally somewhere in your static void Main() function before the Application.Run(…) call.
This should produce a culture-specific folder in your bin\<build_configuration> folder that contains a file named <your_app>.resources.dll. If needed, you can crack this open with a tool like ILSpy or Reflector to verify that the translated resources exist in the expected place within the assembly.
Maybe the reason in that how you're changing the thread culture. I did exectly what you did and its worked. That is how I've changed the culture
static void Main(string[] args)
{
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("de-DE");
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("de-DE");
Console.WriteLine(Resources.Hello);
}
it prints "Hallo"
I found a solution.
If you guys ever have issue with the Resources.resx, make sure the localized file isn't: Resources.de-DE.resx or Resources.pl-PL.resx, instead - use only the first bit (Resources.de.resx) and make sure to open the file and set accessors to 'Internal' so it generates you a Designer file with proper code.
Related
I have an application that I am developing that is made with Window Forms. For localizing all my Labels, ToolStripMenuItems, Buttons, etc I use resx resource files. Specifically to localize my application for German, I open my Main.en-CA.resx file in winres. I then go through all the terms found in the form and change them to their German translation. I then save the file to Main.de-DE.resx. I now have a Main.en-CA.resx file and a Main.de-DE.resx file. In my code I then only have to change the current culture to whatever language I want and apply the change to all my Labels, Controls, Buttons, etc. For example something like this:
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(language);
// Must re-apply resources after changing the culture
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Main));
resources.ApplyResources(this, "$this");
foreach (Control c in this.Controls)
{
resources.ApplyResources(c, c.Name);
}
This seems to work great for all Labels etc that do not change. I do however have entries that are changed. For example I might have a dropdown ComboBox that is filled with the entries: "Apple", "Banana", "Orange". Or I might have some error messages: "Missing Input", "Cannot find xml file" that are only sometimes displayed. Now I suppose maybe for the error messages I could just have Labels and selectively change their visibility depending on whether they need to be shown, however for the dropdown ComboBox these entries might change depending on say which file the user loads.
I am wondering then, is there a way to store these entries in the resx files and then access them from my code. I tried opening the resx files and adding them manually (i.e. without using winres) but attempting to do this resulted in the warning:
You are trying to edit a resource file that is a part of another project item (such as a form or control). Editing this item could corrupt the project item, and you will have to recover it by hand. In addition, changes made to this resource file may be lost if further changes are made to the project item.
Do you really want to edit this file?
This sounded like a bad idea so I didn't try that any further. Additionally I am not sure on how I would access the terms in the file manually. I am very new to windows forms and resource files (this is my first time using them) so I realize this might be a simple question but I have had trouble finding information on how exactly to do this.
Ok as it turns out I have uncovered how I can achieve what I am looking for. Ok from the SO post I can access any strings stored in the files Resource.resx by the code:
myLabel.Text = Properties.Resources.MissingController;
where MissingController is a key (i.e. Name) in the file Resources.resx.
Therefore all I need to do is add additional resource files such as Resource.de-DE.resx in the case of German and fill in the translations (i.e. the values in the resource file) corresponding to the same keys (i.e. the names in the resource file).
The Resources.resx file looks like:
and the Resources.de-DE.resx looks like:
As mentioned in the question I had already created some resource files for translating my forms but I had used winres. Whereas they had been located under my Main.cs [Design] file, the Resources.resx and Resources.de-de.resx are located under Properties. Because I had used winres to make my resx files I think that meant I was not supposed to manually edit them hence the warning it gave?? I'm still not 100% sure about this.
Regardless I can now just manually add terms to my Resource.resx file as well as create different versions of this file for different languages and the localization will work. When right clicking on Properties and going Add->New Item and then selecting Resource, if you do not see the Resource file type as an option (as happened to me) then that might mean you need to add the development tools that did not get installed with your version of visual studio. You can achieve this by just running the visual studio installer again and clicking modify and adding the .NET development tools.
I'm trying to add localization to my .NET MVC project. As far as I've seen (here, here, and here), I should simply be able to create a total of three files (if I have two languages).
Resources.resx
Resources.en-us.resx
Resources.da-dk.resx
When I open the .resx files, I can add entries to them. Once I've done that (and set "Access Modifier" to either Internal or Public), it generates a Resources.*.Designer.cs file (as it should). However, for en-us and da-dk they are empty. No errors or anything.
As far as I could read (here, here, and here), I cannot have a dot between the file name and the .resx extension. And to my surprise, it's true. If I rename any of those en-us/da-dk files to Whatever.resx the Whatever.Designer.cs file will be created.
I've read a lot of answers, tried my way with T4 templates, and a bunch of other things, but I simply cannot get it to create a working Designer.cs file.
Am I doing it wrong? I feel like I've tried everything now. I just want to be able to do Resources.TestText and have my application do the translation depending on the culture.
It is by design.
The Resources.EN-US.resx file types, doesn't have a designer because the actual designer is in it's "parent" file, Resources.EN-US.resx. The en-us file only holds the key/value XML.
If you are calling your Resource, you probably use it like:
var someVar = Resources.SomeLocalizedString;
You don't have to differentiate between the EN-US types.
If you look at the designer's code, you can see whats happening (hold on, I'll fetch an example)
So, you don't need those designers, and it should work out of the box if you set the culture info of the UI thread.
Thread.CurrentThread.CurrentUICulture = new CultureInfo("EN-US");
I've working on a project where I'm using ResourceManager extensively and this question just crossed my mind.
Can we read from .resx files without using ResourceManager? I mean, is there another way?
ResourceManager is a convenience class, it works very well with the way the build system supports .resx files. No, it is not a strict necessity.
A .NET assembly has the generic capability of embedding arbitrary data into the manifest of the assembly. Just a blob of bytes, it can be anything you want. Directly supported by the build system as well, just add a file to your project and set its Build Action to "Embedded Resource". At runtime, you retrieve the data in that file with Assembly.GetManifestResourceStream().
You can stop right there, but that's just a single file, it doesn't scale very well if you have many small resources you want to embed. Which is where a .resx file starts, it is an XML file that contains resources in a friendly format. One that gives you a fighting chance to recover the source again when the original got lost.
But an XML format is not a very good format for resource data, it is bulky and it is expensive to find data back. So .NET has resgen.exe, a build tool that turns the XML file into a binary file, a .resources file. Compact and easy to find stuff back. And fit to be embedded directly as a single manifest resource.
What you don't want to do is having to read the .resources data yourself. You'll want to use a helper class that can find specific resources back from the blob of bytes. You want use the ResourceReader class, its GetResourceData() lets you specify the resource name and it will spit the resource type and data back out.
You can stop right there, but an app often has a need for different sets of resources. A very common localization need. Which is what satellite assemblies are all about, different assemblies that contain nothing but resources, each for a specific culture. They are separate so you don't pay for the virtual memory that's required to store all the localized resources when you need only one set of them. What's needed here is a helper class that automatically locates and loads the correct satellite assembly and retrieves the resource for you, based on the current culture.
That helper class is ResourceManager.
If you choose to skip the use of the ResourceManager you can let Visual Studio handle code generation for you. Ultimately the generated code uses a ResourceManager, but you're no longer writing that code manually. Additionally, you get compile-time checking since you're referencing a generated static class.
If you add a resource file to your project and double click it from the Solution Explorer, Visual Studio presents you with a dialog where you can enter a name for a resource, and its value. The dialog presents you with options to add resources as strings, images, audio, etc. (look at the dropdowns at the top of the dialog). Next, to get the code generation bit, you need to set the Access Modifier to either "Public" or "Internal". The third option is "No code generation."
For example, add a resource file called "MyResources", then add a string resource with the name Greeting and a value of Hello! With one of the former two options selected for code generation (start off with public to test it, restrict the access as needed), you should now be able to reference the resources from your code via MyResources.Greeting. If you don't see it right away, make sure you've saved the file and try compiling.
string greeting = MyResources.Greeting; // "Hello!"
If you add other resource types (image, audio, etc.) then the return types will differ, of course.
At this point you could inspect the generated .cs file and see that the generated code is using a ResourceManager. The other use for resource files is localization. Let's say you wanted a Spanish version of MyResources. You would add a new file called MyResources.es.resx, where es corresponds to the language code desired (Spanish in this case). Now add the same resource name of Greeting with a Spanish value of Hola!.
If you change the thread culture to Spanish, referencing the resource will now return the Spanish version:
string defaultGreeting = MyResources.Greeting; // "Hello!"
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("es");
string spanishGreeting = MyResources.Greeting; // "Hola!"
Note that you only really need to set the access modifier to one of the code generation options for your default resource file (i.e., MyResources.resx), not for all the other localized versions you add. There's no harm in doing so, but in my opinion it's cleaner to have the main file generated while the others just have the resource values desired without code generation.
Well, Resources are compiled into the assembly. You could try to read the assembly by reading the bytes (or the IL), and extract the resources from there.
ResourceManager does this all for you, so I could not think of any reason you want to do this... Maybe one, if you don't want to load the assembly in memory, you could do it without ResourceManager.
Ref Microsoft: Represents a resource manager that provides convenient access to culture-specific resources at run time.
I expect, I'd you use multi Lang, you will get a more consistent result and better compatibility.
IMHO
This is my first time trying to localize, so bear with me. I've read up on it, but am still dealing with limited experience in the subject.
I've gone through a demo and have it working fine and I feel like I understand what happens behind the scenes. I moved the same concepts into my actual application, and it will not work. I've set the Localizable property to true and created an English and French resource file.
I've confirmed that my application is still pulling from the regular .resx file instead of en-US.resx or the French one.
I've tried setting these before the InitializeComponent call:
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
I've even tried to call a few particular ones explicitly:
resources.ApplyResources(this, "$this", new CultureInfo("en-US"));
resources gets declared as:
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(myForm));
It all still pulls from the .resx file. One difference in this over my test project is that this form inherits from another, but I have made sure to set that form's Localizable property and resource files as well. Any ideas would be appreciated.
EDIT: I've still been working on this. I have a test project that works and my main application that does not. If I copy over the form that works in the the test over to the main, it does not work. If I add a reference to the main app in my test, the form from the main app also does not work even though it is straight copied from the test that works. Any ideas?
EDIT: It seems like the main difference/issue is that the basic project that is working is an executable by itself while my main application is calling an assembly with this localized form in it. Still no idea what is wrong. A lot of the tutorials I am seeing use System.Resources.ResourceManager, but when I change the form to localizable it auto-uses System.ComponentModel.ComponentResourceManager.
EDIT: I can literally say
resources.ApplyResources(this, "$this", new CultureInfo("fr-CA"));
and it will ignore the $this.Text in the .fr-CA.resx file and use the default .resx file instead. I Don't Understand. And that is just me trying to FORCE it to work. I still need to just set the culture like normal. Does anyone have any ideas?
Use the CreateSpecificCulture instead of new CultureInfo() for the threads CurrentCulture.
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(languageCode);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(languageCode);
As a side note, if your default culture is going to be English then you do not need to create a labels.en-US.rex. You can put that in the labels.resx and that will load up front and be the fallback. Gives you one less file you need to keep in sync.
If you add the relevant line below to the main method in Program.cs (other lines added for context)
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// THIS LINE - Only need to set UI culture the other one is for currency etc
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR");
// Or whatever your form is called.
Application.Run(new Form1());
This guarantees you are setting the culture before any InitialiseComponent method calls.
I've eyeballed this working.
When you localize a form, it creates a folder per language in the build's output path that holds a resource file projectname.resources.dll.
I have a post build command that puts our .dlls elsewhere. I needed to change the command to also push out the resource files so that they were in the same directory. Once I did this, I was able to get it to work.
I am developing a WinForm application which require localization.
If I try to set the Localizable property of the form to True and set the text for all the languages then every thing works fine.
What I want is to maintain all languages resource files in a separate folder (one file for each form).
-Project
-Resources
-Language
frmFirstForm.en-US.resx
frmFirstForm.en-GB.resx
frmSecondForm.en-US.resx
frmSecondForm.en-GB.resx
frmFirst.cs
frmSecond.cs
In my resource file I have defined all strings as follows:
**Key Value**
lblName Name
lblAddress Address
.....
The key is my control names, I will also keep form specific strings in the resource files. Now the issue is when I compile the solution, it do generate the language files but while running the application it just displays the default values. I don't even know whether the localized resource file is loaded or not. Also, though I have specified two separate form files but while compiling the system is generating only one single resource file per language for a project (means no separate resource file for FirstForm and SecondForm).
Is there any way where the form controls are changed as per the specified localized thread?
I have already added the following line in my main application Program.cs file:
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-GB");
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
Please suggest me how to proceed with this..
#Ashish : this is what you want exactly!!!!