How can a file based ResourceManager fall back to the embedded resources? - c#

Our winforms application needs to be able to distribute ad-hoc translations in the *.resource format and use these in preference to the embedded translations. Is there a way to acheive this fall back strategy?

Why the *.resource format. The .Net pattern is to use localised DLL files. See http://msdn.microsoft.com/en-us/goglobal/bb688110.aspx

I've created a new class that can contain two ResourceManagers. These can be both a file based ResourceManager for the ad-hoc *.resource files and an assembly based ResourceManager for the embedded resource files.
If the ad-hock mode is enabled then the primary ResourceManager is file based with the second ResourceManager as an assembly based one used in a catch block as a fall back.
If the ad-hoc mode is dissabled the primary ResourceManager is an assembly based one and there is no secondary ResourceManager.

Related

Can I use any other parameters besides CurrentCulture to select a C# resource file?

As you probably know .NET selects resource files in a solution based on the .resx filename and the CurrentCulture setting. Meaning that if I have 3 resource files (say Resource.resx, Resource.fr-FR.resx and Resource.nl-NL.resx), I can select the French resources by simply changing the CurrentCulture to "fr-FR" in my app.config. The code that gets the string from the resource file does not have to be changed.
For my current project I'm exploring ways to switch strings in a similar way to how this works, but based on a parameter of my choosing. Say I have Resource.resx, Resource.Bar.resx and Resource.Fu.resx: can I automatically select a resource by changing a custom setting that is not the CurrentCulture, or would I have to build my own ResourceManager extension?
Or could I just set the CurrentCulture to "Fu"? I imagine this would clash with other built-in .NET functions.

Can we skip ResourceManager?

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

.net Resource DLL with multiple resx files

I need to know the way to properly access strings and images from a resource DLL that has multiple resource files (.resx).
I have a very large project that I am working on and is composed of several winforms each with multiple panels. I am trying to tidy things up a bit by making each form has its own res file and limit the application to one resource DLL instead of multiple resource DLLs.
The problem is when I try and add a new resource file to my resource DLL and try to access a string, for example, that is being saved in the newly created resx file. ResourceManager return a null string.
Here is how I am creating my ResourceManager object and getting the string
static public string GetResourceString(string sStringName)
{
System.Reflection.Assembly myDllAssembly = System.Reflection.Assembly.LoadFile(ResPath);
string[] allResourcesinDLL = myDllAssembly.GetManifestResourceNames();
ResourceManager ResInstance = new ResourceManager(allResourcesinDLL[0].Replace(".resources", string.Empty), myDllAssembly);
return ResInstance.GetString(sStringName);
}
Where ResPath is the path to my resource DLL and sStringName is the resource/string title.
I Am not sure if there is a way to specify the resx file to read from or if it should be handled in some other way. Please bear in mind that my application is compiled using the .net 2.0 framework so I am kinda limited to the APIs that I am able to use.

How can I change resource files for a c# winform app, without changing the culture?

We have a winform app that has been in development for some time. It has several images and control colours that create a branded experience. We have a second customer who would like the same product, but with different branding.
So we have lines of code in the control designer.cs files like:
this.BackgroundImage = global::MyNameSpace.Properties.Resources.background;
It seems like resource files are the way to go, as I could simply change the culture and have a separate resource file with different images.
But I don't want to change the culture (I don't want the display of dates, currency, etc.) to be altered and it just smells.
Can I change the resource file manually at runtime or compile time without having to change the culture?
Is there a better way to achieve this outcome?
Also, how do you / can you use resource files to set controls colours (e.g. change all backgrounds from black to red)?
You can read in the resources from a separate file that just had the resources (but doesn't depend on the globalized satellite assembly mechanism). You could also compile default resources with your main assembly as a fall-back.
You can use Resgen.exe to create a .resources file from, e.g., a .resx file and then use ResourceManager to read it:
//note that resourceFilename does NOT include the ".resources" extension
var rm = ResourceManager.CreateFileBasedResourceManager( resourceFilename
,resourceDir
,null);
this.BackgroundImage = (Image) rm.GetObject("background");
One option you could consider is to create a custom culture, so for example you could create a culture called
ClientA-fr-FR
ClientB-fr-FR
The you create resources for each and you should be able to set the Thread Culture approriately.
Here is a link "How to: Create Custom Cultures"

C# Satellite Assemblies? Do I need to link default culture resource

We would like to employ satellite assemblies to contain various locale dependent resource files.
Question is ... do we need to link the default culture?
We have a separate project which will contain all of our different culture resource files. As is shown below, we have the project with two resource files inside of it.
ProjRES
Resource.resx
Resource.it-IT.resx
I am assuming that the "Resource.resx" will act as the default culture and if the Italian culture is selected, the application will adopt the it-IT resource file.
What do we need to do in order to get the rest of the application and projects to access the resource files. How do we set the namespaces for the resource files in order to be able to reference them.
Any help is greatly appreciated.
Basically,
if the current culture that the OS is using matches a certain culture that you've shipped, it will be used, if the current culture matches none of the cultures you've shipped, it will use the neutral culture.
In the most simplistic cases, you'll just need to include any of the localized dlls with the deployment and all will be fine..
When you're using resource managers, I think you can also pass in which culture you want to use explicitly, and the runtime will search for resources that match - this is better when a user of an ASP.Net site might have a certain culture preference that is different from that of the machine that the site is running on.
http://msdn.microsoft.com/en-us/magazine/cc163609.aspx seems to be a good starting point.

Categories