Click here to Skip to main content
Click here to Skip to main content
Go to top

Organizing Resource Strings

, 2 Nov 2008
Rate this:
Please Sign up or sign in to vote.
This article will give you a brief idea of how we can manage resources in our project.

Introduction

One of the cool features of Visual Studio .NET 2005/2008 IDE is a custom tool called ResXFileCodeGenerator that is automatically associated with resources (*.resx files) every time they are added into a project. Whenever your project is rebuilt, a resource file is saved or a custom tool is run manually. The tool generates a managed class that exposes every resource you have in the *.resx file as a strongly typed static property. In order to support more than one language, our application needs to support localization. So, we need to store our strings in resource files so that .NEt will automatically create a satellite assembly for a specific culture. There are two approaches to manage resource strings in our application:

  1. Storing resources in each individual assembly (not a good approach).
  2. Creating a centralized resource strings assembly.

Background

The project that I am working on has almost 40 assemblies. Among 40 assemblies, 30 assemblies have resource strings. We are going to support three different languages. Since .NET will create a satellite assembly for each culture, we will have 60 satellite assemblies. We felt this is quite large and unmanageable. And, we preferred to create a centralized resource strings assembly. In this article, I am going to explain both approaches.

Storing the resources in each individual assembly

Step 1: Create a console project “ResourceMultipleProjects”.

Step 2: Add the resource file Strings.resx. This is the default resource file for the default culture.

Step 3: Add the resource files Strings.de-DE.resx and Strings.fr-FR.resx for the cultures German and French, respectively. A culture specific file name should be in the following format:

mainresourceFileName.culturespecificString.resx

Take the example "Strings.de-DE.resx". Here:

  • “Strings” is our main resource filename.
  • “de-DE” is the culture specific string for German in Germany.

Step 4: Add the following resource string in all three resource files (double click the resource file to open the following view):

Step 5: Right click each resource file and open the Properties window and set these properties:

  • Build Action: Embedded Resource
  • Custom Tool: RexFileCodeGenerator

The Custom Tool is responsible for generating the code.

Build the project. In the output bin directory, you can see the following folders:

  1. de-De
  2. fr-FR

Inside each folder, you can see the “ResourceMultipleProjects.resources.dll” assembly. This is the satellite assembly. To access the resource string “ApplicationName”, you have to use: Strings.ApplicationName. When you build the application, .NET will automatically generate the class “Strings”. In order to see the auto-generated code, you can open the file “Strings.Designer.cs”.

Shown below is the auto generated code for Strings.Designer.cs:

Since the “Strings” class is internal, you can’t add the resource strings of other assemblies. Because you can’t access an internal member from outside the assembly, for each assembly, you need to add a separate resource strings file. The following screenshot shows that:

You have two projects in the above solution. Each project contains separate resource strings. For each culture specific resource strings file, .NET will create a separate satellite assembly. Run the sample application in order to understand more. When you run the sample “ResourceMultipleProjects.exe”, you will get the following output:

Note: To differentiate the cultures, I prefixed each resource string with its culture string, like FR and DE.

Demerits

Though it is easy to add individual resources in each assembly, it has the following issues:

  1. If the number of assemblies increases, then the number of satellite assemblies will increase drastically. (A medium size project will have 15 to 25 assemblies.)
  2. Even for one or two resource strings, you need to create the entire resource strings files for each culture in the assembly.
  3. The resource strings will be scattered across all the assemblies.
  4. In future, if you decide to support one more language, you need to add a separate resource file for each assembly. In this case, the installer creation will be a nightmare for anyone.
  5. The .NET runtime has to load all the satellite assemblies. So it will affect performance.

In order to overcome the above issues, we need to use a centralized resource location for each culture.

Creating a centralized resource strings assembly

In this approach, we are not going to create separate resource strings in each assembly. Instead, we are going to put all our resource strings in one common assembly. Create a project solution as per the following diagram, or open the sample solution ResourceInSingleProject.

Here, the “StringManager” assembly contains all the resource strings for each culture. The individual project doesn’t have a separate resources string file. You should refer to the String Manger assembly (project reference) from all the other projects. Now, you need to do the following steps in the resource strings file.

Step 1. Right click the individual resource file and open the Properties window:

Set the BuilAction property to Embedded Resource, as you do normally. Then, you need to set the Custom Tool property. If you set the CustomTool property to ResXFileCodeGenerator, then it will generate the class Strings in the file “Strings.Designer.cs”. The access specifier of the Strings class will be internal. So, you can’t access the resources from outside the assembly. In order to create a public Strings class, you need to set the CustomTool property to “ResXFileCodeGeneratorEx”. To get this option, you need to install ResXFileCodeGeneratorEx. This is a free tool, and you can download it from the following link: http://www.codeproject.com/KB/dotnet/ResXFileCodeGeneratorEx.aspx. With this sample application, I have put the source code and the installer for this tool. This tool was developed by “Dmytro Kryvko”.

Add some sample resource strings in the Strings.resx, Strings.de-DE.resx, and Strings.fr-FR.resx files and open the file “Strings.Designer.cs”. You can see that the class Strings has the public access modifier.

Since the “Strings” class is public, we can access the resources from any other assembly. When you run the sample "ResourceInSingleProject.exe", you will get the following output:

Conclusion

The advantages of ResXFileCodeGeneratorEx and ResXFileCodeGenerator are:

  1. ResXFileCodeGeneratorEx will create a public resource class file so that you can access the resource strings from other assemblies.
  2. ResXFileCodeGeneratorEx is thread safe.

In my experience, using a centralized resource manager is the best option. It is very easy to maintain, and you can easily add new culture specific assemblies. In this approach, we will have only one satellite assembly for each culture.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

rajeshjj
Software Developer (Senior) MphasiS an HP company
India India
I am dotnet programmer

Comments and Discussions

 
GeneralBest Localization Plug-in for Visual Studio. PinmemberAlexander Nesterenko17-Dec-08 21:48 
Questionhow to access the resource Strings organized by individual folders? Pinmemberleegool28-Aug-08 16:17 
AnswerRe: how to access the resource Strings organized by individual folders? Pinmemberrajeshjj15-Sep-08 2:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 3 Nov 2008
Article Copyright 2008 by rajeshjj
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid