Using Satellite Assemblies for Localized Resources

Problem
You want to know how to use satellite assemblies to hold the strings and other resources (images, sound files, etc.) that your program uses.

Solution
Mark satellite assemblies with the AssemblyCulture attribute.

Comments
Satellite assemblies should contain no code, and their names should include .resources. For example, MyUtil.resources.dll for an app with the main assembly called MyUtil.dll.

Code example

// Add the following entry to the AssemblyInfo.cs file.
[assembly: AssemblyCulture("fr-FR")]

// Code in the main app to retrieve the resources.
string resourcesFile = "TestAutomation.UiStrings";
ResourceManager resources = new ResourceManager(resourcesFile, Assembly.GetExecutingAssembly());
string message = resources.GetString("Message");

/* Here's an additional way to retrieve a resource for a specific culture:
** Pass a CultureInfo object to the GetString method of the ResourceManager class.
*/
CultureInfo frCi = new CultureInfo("es-ES");
string greetings = resources.GetString("Message", frCi);

// Apply the usage of French resources.
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR");

Resources vs. Hard-Coded Strings

Problem
You have a number of user-interface strings, but you don’t want to store them in code. What can you do?

Solution
Place all user-interface strings in a Resources (.resx) file. Storing them in a centralized location makes them easier to modify, spell check, and localize.

Example
Let’s say you’ve added a resources file to your project with the name UiStrings.resx. Here’s how you can reference a string resource named Message from the application code whose default namespace is TestAutomation.

string resourcesFile = "TestAutomation.UiStrings";
ResourceManager resources = new ResourceManager(resourcesFile, Assembly.GetExecutingAssembly());
string message = resources.GetString("Message");

Pro-tip
Store the ResourceManager reference in a variable that can be accessed by the entire application. This way, you won’t need to re-create it every time you need to retrieve a resource.

The Ngen Utility

Problem
You’ve heard about precompiling assemblies and the Ngen utility. When should you use it?

Solution
Use Ngen.exe to precompile Windows Forms applications and Windows Forms Control DLLs. As a general rule, don’t use the utility for any other application type.

Comments
Why would you want to use this neat little utility? In short, because precompiling these assemblies significantly speed up their loading time. In addition, precompiled assemblies run faster on machines with less RAM, and that just isn’t possible with JIT-compiled assemblies. So, then, why would you not want to use Ngen and precompile your assemblies? Well, that’s because Ngen-compiled assemblies aren’t as optimized as JIT-compiled ones, and they can run slower. The difference is between load/start and run times. That being said, each application reacts differently to Ngen compilation. You can always test out a Ngen-compiled version and see if it loads more rapidly and runs fast enough for your needs. Like Gacutil, you can run Ngen as a post-build event or create a custom macro to execute it.

GAC Registration

Problem
You want to know the pros and cons of registering your DLL assemblies in the GAC.

Solution
The pros of registering are 1. The .NET runtime finds GAC assemblies more quickly and loads them faster because it doesn’t have to check their integrity. 2. Assemblies in the GAC are protected from accidental deletion because only an administrator can access the GAC directory. The cons of registering are 1. More complicated deployments since you can’t rely on XCOPY to automatically register the assembly for you. 2. Having to uninstall the previous version from the GAC each time you recompile.

Comments
You don’t want to use the GAC if your sole purpose is to share types among different applications; there are better ways to accomplish this goal. You can share an assembly, including private ones, among different applications if all applications’ executable files are deployed in the same directory. Or, using a different approach, you can reference a strong-named assembly that is located outside the application’s main directory by using the element in the application’s configuration file.

Additional details
There are ways to make GAC registration simpler. There’s a utility named Gacutil that comes with the Windows SDK and is automatically installed with Visual Studio. If you’re not a Visual Studio user like me, you can simply get it with the Windows SDK. You can configure Gacutil to run as a post-build event. In Visual Studio, you can configure the command to run in the Project Properties dialog box. Otherwise, you can add it to the .csproj file like so:

<Target Name="AfterBuild">
    <Exec Command="Path to SDK\...\Gacutil.exe" /i $(TargetPath)" Condition="" />
</Target>

Culture Attribute for Main Assembly

Problem
You want to know the best way to mark your main assembly for a given locale.

Solution
Use the NeutralResourcesLanguage attribute on the main assembly. This will inform the resource manager about the language for neutral resources that are embedded in the main assembly.

Comments
Using this attribute speeds up resource loading when the current user’s locale matches the culture used for resources in the main assembly. Don’t use the AssemblyCultureAttribute in the main assembly. The main assembly should only contain neutral resources (for example, strings and images related to the default language).

Example

// This particular assembly contains resources for the US English culture.
[assembly: NeutralResourcesLanguage("en-US")]