http://www.developer.com/net/vb/article.php/3083921/Using-Satellite-Assemblies-for-Multi-Language-Programming.htm
A generally accepted programming practice is to use constants for content like text strings. By taking this practice one step further with a modest amount of work you can prepare your applications for customers that speak other languages. In this article I will demonstrate how to build an external resource file for your application's text and build additional files for multi-language support. After reading this article you will know how to build a resource file, compile the resource file using the resgen.exe utility and embed the resource file in a satellite assembly using the al.exe (assembly linker) utility. Finally, we will wrap up with a practical approach that developers can employ to simplify testing resource assemblies before throwing them over the wall to testing teams or customers. It is easy to use the Visual Studio .NET IDE and the Properties window to add text content to your application's controls. Other content, like that displayed in a MessageBox, Console, or Web page, is often embedded amongst the lines of code or as constants. Externalizing this text content only requires a bit of additional work and prepares your application for globalization. A resource file can be started by adding a text file to your project or by using any text editor like Notepad. Name the text file the same name as your target assembly name with a .txt extension. Use a line of text preceded with a semi-colon if you want to add a comment to your resource source file and add the text in name = value pairs. By convention the names use an RES_ prefix and all uppercase words separated by underscores—similar to the popular constant naming convention. Hence, if you had a constant for a database connection error one might write the resource string as follows: Each of the name and value pairs must be on a single line in the text file. After each of the content strings we are ready to compile the text file into a resources file using the resgen.exe (resource generator) utility. If you click on the Start|All Programs|Microsoft Visual Studio .NET 2003|Visual Studio .NET Tools|Visual Studio .NET 2003 Command Prompt then the necessary environment variables, including paths to the .NET SDK utilities, will be properly configured. To compile the text file into a resources file we need to run the resource generator passing the name of the input text file and optionally the resources output file. For example, if our target assembly is myapp.exe then my input file should by myapp.txt and the output file should be myapp.resources, and the resource generator command line would be After compiling the resource text file into a resources file we can add the resources file to the application. This resource will be embedded into the target assembly and will represent the default resource and consequently default language of the application. For our demo I created a text file containing some English text, the default for my laptop. I named the resource source file the same as my demo application's name, and compiled the default resource file with the following command line: To use the external resource file we need to create an instance of the System.Resources.ResourceManager manager class, initializing the ResourceManager object with the resource type name and the Assembly containing the resource. A statement similar to the following works well. The member field is shared because only one instance is needed for all instances of the containing entity. The field is in the Form class, so we can get the namespace and the Form's containing assembly from the type object of the Form. GetType(Form1).Namespace + ".InternationalizationDemo" will build the resource type name correctly regardless of language the culture name, and GetType(Form1).Assembly will return the Form's assembly. For other applications substitute the Form1 parameter with the name of the class containing your ResourceManager field, and substitute the ".InternationalizationDemo" part with the file name part of your resource file. After creating an instance of the ResourceManager we can request content anywhere we need to use the content. For example, if I want to display the What question then I could write resourceManager.GetString("RES_WHAT"), which would return the text "What time is it?" if we were running the application with the default culture settings of en-US. To define a satellite assembly, add a folder to your project containing the culture name for languages you'd like to support. For example, to support German we would add a folder named de-DE—case counts here. Next, add a file with the same prefix as your default file, a .txt suffix, and de-DE separated by a period in between. Placing this file in the proper folder would yield a Project Explorer that looks like the one shown in figure 1. Figure 1: The demo application showing the default and German resource source and compiled resource files. The figure shows the German language folder with the source text file and compiled resource file. We would add an addition folder for each language version we'd like our application to support. In addition, to the resources file we need to link our additional culture files into a DLL assembly—the default resource file is embedded into the application assembly—and we use the assembly linker to do this part. We completed the first step by creating the culture specific sub-folder. The next step is to create the language version of the text file. If you just want to stub out the language text file append some additional text to a temporary file. Knowing enough German for our demo file I created the following German resource text file. The next step is to compile the resource file using the resource generator. The command is almost identical to the previous example; all we need to do is substitute the file names to include the culture name in the file name. Finally the following command shows how to use the assembly linker to embed the German resource into a satellite assembly that will be loaded when the culture name matches the German culture name, de-DE. The last step is to test our satellite assemblies for each culture version we'd like to support. I'm a firm believer that developers should perform unit testing and get as many bugs out the code before it goes to testing. To that end we need to test our satellite assemblies. There are two good ways to test multi-language clients: one is to change the current culture information for the assembly and the second is to install software like VMWare or Virtual PC and set up a partition on your development machine with the language versions you'd like to test. For our purposes we'll change the culture information to see if the same code, without revision, properly loads the correct culture text when we change the current culture to de-DE. An easy way to change the culture information is to import System.Threading and set the culture name to the names we'd like to test. In our simple Windows Forms application we can accomplish this by assigning a new CultureInfo object to Form Thread's CurrentUICulture property, as demonstrated: Re-run the sample application. Now when the statement requesting the RES_WHAT resource string is requested the application should automatically load the satellite assembly and the correct language version of the string. To verify this you can open the Debug|Windows|Modules window and see that the resource satellite is automatically resolved when the ResourceManager.GetString method is called. Figure 2: The correct satellite resource assembly is loaded, as shown, when the culture information has changed and resources are requested. Using resource files is a good way to separate content from the behavior of an application. It is possible to implement the application and let the language lawyers figure out the content. A greater benefit is that by externalizing resources and creating satellite assemblies the same body of code will load resources needed for each of the languages you'd like to support. Additionally, the satellite assemblies can be added and shipped over time as you elect to support a greater variety of languages. Paul Kimmel is a software architect, writer, and columnist for codeguru.com. Look for his recent book Visual Basic .NET Power Coding from Addison-Wesley. Paul Kimmel is available to help design and build your .NET solutions and can be contacted at pkimmel@softconcepts.com . # # #
Using Satellite Assemblies for Multi-Language Programming
September 26, 2003
Externalizing Text Content
Creating a Resource Text File
RES_DATABASE_CONNECTION_ERROR = Unable to connect to database
Compiling a Resource File
resgen myapp.txt myapp.resources
Using External Resource Files
; This is my default resource file
RES_WHAT=What time is it?
RES_WHERE=Where is the train station?
resgen InternationalizationDemo.txt InternationalizationDemo.resources
Private Shared resourceManager As resourceManager = _
New resourceManager(GetType(Form1).Namespace + _
".InternationalizationDemo", GetType(Form1).Assembly)
Defining a Satellite Assembly

; This is the German version of the resource file
RES_WHAT=Wie viel Uhr ist es?
RES_WHERE=Wo ist der Bahnhof?
resgen InternationalizationDemo.de-DE.txt InternationalizationDemo.de-DE.resources
al /out:InternationalizationDemo.resources.dll /v:1.0.0.0
/c:de-DE /embed:InternationalizationDemo.de-DE.resources,
InternationalizationDemo.InternationalizationDemo.de-DE.resources,
Private
Testing a Language Assembly
Thread.CurrentThread.CurrentUICulture = _
New CultureInfo("de-DE")
Summary
About the Author