Introduction
This article details out all the basics that you must understand and know about satellite assemblies before you go ahead and use it in your project. The article expects the reader to have a prior knowledge of using Visual Studio editor for C# language.
Background
In today's era we all create applications which target multitude of countires, languages and culture. English is a global language but every nation has its own local language as well which is commonly spoken in there. When you build applications for a customer which is spread globally there is high chance that he would want you to write your application in every local language of the country he is dealing with. What your customer is expecting is actually localization of your application i.e. if he opens your application in Beijing your UI should be shown in chinese language, if he opens his application in Riyadh, Saudi Arabia it should open in Arabic , or if he opens your application in United States it should open in English langauge and so on. For a windows forms application the most trivial solution that comes to our mind for this problem is writing each win form of your application in all the languages you want to support and load the appropriate form by detecting the locale your application is running in. Certainly it is highly inefficient due to following reasons :
- Maintainability issue : There is code explosion. You maintain three files for three supported languages (in fact 6 including the .designer.cs partial file for each win form) for each form. Every time you have to support a new language you have to add a new language specific designer file for each form.
- Painful Deployment : Every time I've to support a new language I've to build and deploy the entire application again as source code is changing. Such a scenario can be nightmare for a big product installed at multiple global locations world wide.
- Long Development Process : Core C# developers and language translators will always be different people. Language translators will always have to keep waiting for the developers to finish their job after which they can go on with their translation part. This is a lot of time wastage in development cycle.
Don't get scared satellite assemblies in .Net are here for your rescue. Let's kick start the basic concepts about it.
Basics of Satellite Assemblies
Langauge: Any vocal/written communication method is language. For example English, Chinese, Arabic.
Locale: .Net identifies specific regions of the world as locale. .Net assigns unique Ids to each region of the world as mentioned in the following link :
http://msdn.microsoft.com/en-us/library/ms912047(v=winembedded.10).aspx
Culture: Every culture in .Net is identified by primary and secondary tags. Primary tag refers to language and secondary tag refers to Locale it represents. For e.g. "en-US" represents English language of United States locale. "ar-SA" represents arabic language spoken in Saudi Arabia Locale.
Localization & Globalization: I will leave it up to you to explore the definition of these terms at following MSDN link
http://msdn.microsoft.com/en-us/library/aa292205(v=vs.71).aspx
Satellite Assembly: A .Net assembly that contains only culture-specific resources are called satellite assemblies. These assemblies do not contain your C# code or any programming logic. It contains only localization resources which can be text, image, Icon, Audio or any other similar stuff being used in your application. For each culture you want to support in your application you create one separate satellite assembly which will contain localization resources of that specific culture. For instance if I want my application to support two languages namely Saudi Arabia and Hindi my application will have three assemblies in all for deployment as below :
- One satellite assembly for "ar-SA" culture.
- One satellite assembly for "hi-IN" culture.
- One Primary assembly containing all the C# code and logic. Primary assembly should always be kept culture neutral. Default Language (fall back) resources are always present in the primary assembly. Your assemblyInfo.cs file should have following declaration to make it culture-neutral assembly :
[assembly: AssemblyCulture("")]
Using the code
Let's create the satellite assemblies for one of the two languages we intend to support in our application. I'm using Visual studio 2010 as development tool and C# as programming langauge in my demo application. I intend to localize the UI of my application which is as below :
Development Steps:
- Create a new project named "TestSatelliteAssembly" from "Windows Forms Application" template under Installed Templates -> Visual C# -> Windows
By default visual studio create a resource file "Resources.resx for default (fallback) language of your application as shown in red box below. This resource is always contained in the primary assembly "TestSatelliteAssembly.dll" :
- Here is what you need to do next before getting into C# coding : i) Create a new folder named Resources in your solution.
ii) Move the "Resource.resx" file from Properties folder to Resources folder.
iii) Rename "Resources.resx" file to "LocalizedResources.resx".
iv) Now Inside the folder, add a new item. Go to Add -> New Item wizard found in context menu for project file. Choose "Resources file" item present inside Visual C# Items -> General. Name the new file being added as "LocalizedResources.hi-IN.resx".
v) Add the localized strings in form of key-value pairs inside the "LocalizedResources.hi-IN.resx" resource file for the two controls we are planning to localize. Snapshot below gives the details with relevant portions marked in red box :
- Now we need to write the following code which acts as glue between the windows form and the resource file meant for hindi language. Here are the code snippets from respective files for your ready reference. Additional code that I've written is in intalics to get your attention.
Source-code of Main.cs file :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Globalization;
using System.Threading;
namespace TestSatelliteAssembly
{
static class Program
{
[STAThread]
static void Main()
{
CultureInfo newCultureInfo = new System.Globalization.CultureInfo("hi-IN");
Thread.CurrentThread.CurrentCulture = newCultureInfo;
Thread.CurrentThread.CurrentUICulture = newCultureInfo;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
You will have to define "Form_Load" event for the form present inside Form1.cs. Change the code of corresponding event handler code present inside "Form1_Load" method as shown below. Additional code that I've written is in intalics to get your attention.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Resources;
using System.Reflection;
namespace TestSatelliteAssembly
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
LoadLocalizedResources();
}
private void LoadLocalizedResources()
{
ResourceManager resourceManager = new ResourceManager("TestSatelliteAssembly.Resources.LocalizedResources",(Form1).Assembly);
label1.Text = resourceManager.GetString("lblUserNameText");
button1.Text = resourceManager.GetString("btnSubmitText");
}
}
}
And you are all set to go. Just run Ctrl+F5 to see your localized UI as shown below :
What goes behind the scenes
Here is what goes behind the scene. Have a look at your obj -> Debug folder of your project directory. The resource files that I've highlighted inside red box are actually binary files converted by Visual Studio corresponding to the two resource (*.resx) files we have in our project i.e. one for default resources and other one for Hindi Resources.
You can also notice "hi-IN" folder which looks as shown below. Here the "TestSatelliteAssembly.resources.dll" file is the satellite assembly we all have been talking for so long. It contains no C# code but only hindi resources file "TestSatelliteAssembly.Resources.LocalizedResources.hi-IN.resources" embedded inside it. You will notice that this satellite assembly also goes into deployment if you check the contents of your bin -> debug folder. Bin-> Debug folder contains the satellite assembly inside the folder "hi-IN" folder. "*.resx" or "*.resources" files never go into deployment.
How our concerns got resolved
Let's quickly recape through our original problems as to how they got fixed here :
- Localized resource strings are now residing in separate physical resource file and in turn a separete physical satellite assembly. We don't have to maintain multiple forms for same view. Same form can be loaded in language of your choice by setting the localization text from corresponding satellite assembly.
- Every time we need to support new culture we simply have to build a new satellite assembly for that culture and deploy it. Building and deploying the entire application again for supporting a new language is not required now.
- Since resource files which contain the culture specific strings are residing in separate physical files they can be handed over to language experts in parallel to development work. The moment development work is complete we simply have to integrate the resource files to produce the final build.
Points of Interest
More points for you to explore are as below :
- Currently the culture in which I want to load the application is hard-coded in my main.cs file . How would you make it dynanamic at run time?
- Extend the application to language of your choice.
- What is Invariant culture in .Net?
- You can also create your resource files as "*.xml" or "*.txt" files. How?
- How assembly linker (AL.exe) tool is used to link satellite assemblies.
- Explore resgen tool provided by Visual studio to convert "*.resx" files into "*.resources" file.
- Explore following attribute that you can put inside your assemblyinfo.cs files to define the default/fall back culture of your choice
[assembly: NeutralResourcesLanguage("en-US",UltimateResourceFallbackLocation.Satellite)]
History
Fixed snapshot link.