Steps for internationalizing your application: Globalization and Localization

Today I am going to show on how we can internationalize an asp.net application. I have gone through some scenarios and  came across some error which I have discussed here as well. Moreover, I have also described about Satellite assembly versioning facts.

Here it goes:

  1. Open the Microsoft Visual Studio 2008(Recommended VS2008 SP1).
  2. Open a new web application project in your preferred language. I am using C#.
  3. In the solution explorer, you will see default.aspx page and if we expand “Show all files”, we will see “Default.aspx.cx” and a designer file (Default.aspx.designer.cx) as well.
  4. Add a App_GlobalResources ASP.Net Folder by right clicking on the “Project” and click on Add.
  5. This will add App_GlobalResources folder to the Project/Solution.
  6. Now, here we have to be cautious on what we add.
  7. Lets add a new item to the App_GlobalResources.
  8. Right click on the App_GlobalResources folder, it should automatically reflect “Resource1.resx”file.
  9. Change the file name to a general name, say “Strings.resx” or “Localization.resx”
  10. I will keep the name of the resource as “Strings”.
  11. This will add a new resource to the App_GlobalResources folder.
  12. NOTE: It is necessary to have a default resource file . Without the default resource file(in my case it is “Strings.aspx”) it is going to throw following error.

Server Error in '/' Application.

Could not load file or assembly 'App_GlobalResources' or one of its dependencies. The system cannot find the file specified.

image

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.IO.FileNotFoundException: Could not load file or assembly 'App_GlobalResources' or one of its dependencies. The system cannot find the file specified.

 

 

Once we add a default resource, if you look into the “App_GlobalResources” folder, you will see an auto generated code.

This is what is responsible for access of the resources.

Now, as per my example, I have added a dropdown box and a label control on the page.

In the dropdown box, I have added following list items.

 

<asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="True"

onselectedindexchanged="DropDownList1_SelectedIndexChanged">

<asp:ListItem Value="French">French</asp:ListItem>

<asp:ListItem Value="Arabic">Arabic</asp:ListItem>

<asp:ListItem Value="German">German</asp:ListItem>

<asp:ListItem Value="English" Selected="True">English</asp:ListItem>

</asp:DropDownList>

 

In the Default.aspx.cx code, here is what I have added.

protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)

{

if (this.DropDownList1.SelectedIndex == 0)

{

//French

Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR");

Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR");

}

else if (this.DropDownList1.SelectedIndex == 1)

{

//Arabic

Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("ar-SA");

Thread.CurrentThread.CurrentUICulture = new CultureInfo("ar-SA");

}

else if (this.DropDownList1.SelectedIndex == 2)

{

//German

Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("de-De");

Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-De");

}

else

{

//English

Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");

Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");

}

this.Label1.Text = Resources.Strings.String1.ToString();

}

 

On changing the dropdown value, I set the culture for the thread and it automatically selects the respective resource based on the culture.

There you go, now you can select the culture in your page, and it is going to select the respective resource automatically.

 

Different ways of accessing Resources:

Retrieve Resource Values Programmatically

1. Directly accessing using the resource namespace:

We can access our resources as: this.Label1.Text = Resources.Strings.String1.ToString();

Once the default resource is added, it automatically adds a “Resources” namespace.

Within the “Resources” namespace, we see the class generated by name “Strings”. This is how we added resources to the “App_GlobalResources”, e.g Strings.resx.

So, by default, the classname generated will be “Strings”.

2. Accessing the application using ResourceManager

ResourceManager (string baseName, System.Reflection.Assembly assembly)

e.g: ResourceManager gStrings = new ResourceManager("Resources.Strings", System.Reflection.Assembly.Load("App_GlobalResources"));

Here, Resources is NameSpace, Strings is the name of the class in namespace of the resource file.

The reason I am calling “System.Reflection.Assembly.Load(“App_GlobalResources”) is because, my resource files are loaded into “App_GlobalResources” folder. By this name the dll will be generated.

In case we do not mentioned the proper namespace, as “Resources.Strings” or proper assembly name, we will get the below mentioned error message.

image

Implicit / Explicit localization:

Implicit localization:

If local resource files are created in Application, then implicit localization can be used to fill the property values for the control.

To use implicit localization, following naming convention must be used for resources in local resource file

key.property

Where key: Any name for the resource

Property: Property of the control that we are localizing

Ex: If we are creating resource for control label named Label1, you need to create following key/value pairs in local resource file

this.Label1.Text = "Error Message"

In aspx page, we need to use a special meta attribute in the markup for the control to specify implicit localization as-

<asp:Label ID="Label1" runat="server" meta:resourcekey="string1" Text="Label"></asp:Label>

No need to explicitly specify which properties are localized.

The resourcekey value matches a key in resource file.

At run time, ASP.NET matches resources to control properties using the resourcekey, if property value is defined in resource file, ASP.NET substitute the resource value for the property.

Explicit localization :

We use resource expression for each property of a control

For ex: A label control that is configured to set a text from global resource look like-

<asp:Label ID="Label1" runat="server" Text="<%$ Resources:ResourceFileName, String1 %>"></asp:Label>

The resource expression takes the following form

<%$ Resources:Class, ResourceID %>

Where Class: Identifies the resource file to be used

ResourceID: Identifier of the resource to be read from resource file

 

 

Versioning Satellite assemblies:

 

Instructs the ResourceManager to ask for a particular version of a satellite assembly to simplify updates of the main assembly of an application.

The SatelliteContractVersionAttribute establishes a contract between a main assembly and all its satellites. When the ResourceManager looks up resources, it explicitly loads the satellite version specified by this attribute on the main assembly, allowing a layer of indirection to facilitate versioning scenarios.

When the main assembly is updated, its assembly version number is incremented. However, you might not want to ship new copies of your satellite assemblies if the existing ones are sufficient and compatible with the newer version of your product. In this case, increment the main assembly's version number but leave the satellite contract version number the same. The ResourceManager will use your existing satellite assemblies.

If you need to revise a satellite assembly but not the main assembly, you must increment the version number on your satellite. In this case, ship a policy assembly along with your satellite assembly stating that your new satellite assembly has backward compatibility with your old satellite assembly. The ResourceManager will still use the old contract number written into your main assembly; however, the loader will bind to the satellite assembly version as specified by the policy assembly.

A publisher policy assembly is the way in which a vendor of a shared component makes a compatibility statement about a particular version of a released assembly. A publisher policy assembly is a strongly named assembly with a name in the format policy.<major>.<minor>.<ComponentAssemblyName>, and is registered in the Global Assembly Cache (GAC). The publisher policy is generated from an XML configuration file (see <bindingRedirect> Element) by using the Assembly Linker (Al.exe) tool. The assembly linker is used with the /link option to link the XML configuration file to a manifest assembly, that is then stored in the GAC. The publisher policy assemblies can be used when a vendor ships a maintenance release (Service Pack) that contains bug fixes.

Note:

Apply this attribute to your main assembly, passing it the version number of the satellite assembly that will work with this version of the main assembly.