Soluling home   Document home

ASP.NET Framework Localization and Internationalization

ASP.NET Framework Localization and Internationalization

ASP.NET Framework (Wikipedia) is a popular technology to build web applications. ASP.NET is a classic Windows only platform. It contains four technologies: Web Forms, MVC, Web API and Web Pages. Unlike PHP, ASP.NET code is compiled before executed. ASP.NET code can be stored in a separate file (.cs or .vb) or it can be embedded into a markup file. Soluling localization tool and service support ASP.NET. This document is for ASP.NET Framework. If you use ASP.NET Core read this document.

Markup and resource files used in ASP.NET

There are a few frameworks that work on top of ASP.NET. The most common are .aspx used in Web Forms, and .Razor (.cshtml/.vbhtml) used in MVC and Web Pages. They all use .resx files and Soluling can localize them all.

Localization using Resource Files

This is the most common way to localize ASP.NET applications. The main idea is that you identify all those strings that need to be translated and then you replace the strings with resource string images. On runtime .NET runtime makes sure the application uses the localized resource items if they exist. ASP.NET uses the same resource file format as other .NET technologies. This format is .resx. It is an XML file that contains one or more resource items. Each item has a name and a value. Unline the standard .NET applications ASP.NET has two special kind of resources: global resources and locale resources.

Global resources

Global resources are stored in App_GlobalResources folder in the root of your project. There are be any number of resource files and all pages of your application can access global resources. Global resources are also strongly typed just like resources in desktop .NET technologies. This means that you can easily use them in your code.

var greeting = Resources.Strings.Hello;

The namespace of global resources is Resources. The name of the actual resource item is Resources.<resxfilename>.<resourcename>. For example if you have Strings.resx file and it contains Hello resource the full name of the resource is Resources.Strings.Hello. You don't have to access the resource through strongly typed name. You can use GetGlobalResourceObject funtion too.

var greeting = GetGlobalResourceObject("Strings", "Hello").ToString();

The above code works just like the code before that but gives no compile time error if the resource does not exist. In general you should not use GetGlobalResourceObject but you should always use the strongly typed resources.

There are several ways you can access a global resource in your HTML elements. The easiest way is to use strong type resources.

<p><%=Resources.Strings.Hello %></p>

You can also use GetGlobalResourceObject but then you loose compile time checking.

<p><%=GetGlobalResourceObject("Strings", "Hello") %></p>

Global resources do not get compiled into the assembly file of your application but ASP.NET compiles a separate assembly on runtime. You need to deploy App_GlobalResources folder with your application.

Local resources

Local resources are stored in App_LocaleResources folder in the root of your project. There can be only one resource file for each page. The name of the resource file must match the page. For example if you have Index.apsx page then the resource file would be App_LocalResources\Index.aspx.resx. Only the page can access the local resources of that page. Local resources are not strongly typed so you cannot use resource ids in your code. You have to use GetLocaleResourceObject function.

var value = GetLocalResourceObject("Hello").ToString();

You can access a local resource in your HTML elements in the same way.

<p><%=GetLocalResourceObject("Hello") %></p> 

Instead of having two parameters (e.g. resource file and resource name), GetLocalResourceObject only needs only parameter: resource name. This is because there can be only one resource file for each page.

In your forms you have two choices. Either you use explicit or implicit method. In explicit method you replace the value of Text attribute to a reference to a specific resource item. For example if you have label like this.

<asp:Label runat="server" Text="Hello World" />

Add Hello resource item and set the value to "Hello World". Replace the content of Text attribute wih reference to the resource item.

<asp:Label runat="server" Text="<%$resources:Hello %>" />

On run time ASP.NET looks for a resource name Hello from page's local resource file. This method is easy to understand because is is similar the other methods we have used. However if you have many elements in your page this method may be too complicated and it certainly makes your code harder to read. In that case you can use implicit method. Here you don't specify the resource items itself but you give the element a name. On run time ASP.NET looks if there is a suitable resource for that name and uses it if a match is found.

<asp:Label runat="server" meta:resourcekey="label1" />

If the local resource contains an item named label1.Text then it is used. Implicit localization is certainly easier to use. When internationalizing remove Text attribute, replace it with meta:resourcekey attribute, and finally add the text string into page's resource using <resourcekey>.Text name. If the elment has more than one localizable attribute then you don't have to explicitically localize them. You just have to add each property (attribute value) into the local resource file. Visual Studio even has a wizard that automatically performs this change. Switch the page editor mode from Source to Design. Choose Tools | Generate Local Resource. Visual Studio refactors the page and moves the strings from the page to a local resource file.

<asp:Label runat="server" ID="label1" Text="Hello" meta:resourcekey="label1Resource1" />

The wizard makes a good great job but there are few issues. First it resources all possible attributes (e.g. Text and ToolTip) even you have not used them. This leads into several empty items in the resource file. You should remove the empty items. Secondly the wizard lefts the original Text attribute in the element even it won't ever be used because the resource string contains a matching value. Having the Text attribute here is just extra overhead and may even lead to logical error if the author changes the value but because the value is not used the result he or she thinks won't happen. You should remove the Text attributes after you have run the wizard. Finally the wizard gives all resources too complicated names: <elementid>Resource1.<attributename>. Resource1 part is totally unnessary and better would be just <elementid>.<attributename>. If you want you can rename the resources. If you do you also have to change the values in meta:resourcekey attributes.

<asp:Label runat="server" ID="label1" meta:resourcekey="label1" />

Now you can see that there is no Text attributes and the resource key is same as elment id.

Note! If you remove the Text attributes then the page editor can not show the actual value in Desing mode but just shows [elementname].

Locale resources are not compiled into the assembly file of your application but they are stored as separate .resx file. You need to deploy App_LocalResources folder with your application.

Custom resources

You can place your resource files to other folders but App_GlobalResources or App_LocalResources. Although they have some advantages they are not very common practise. If you use such resources you can not use GetGlobalResourceObject and GetLocalResourceObject functions. Also you can not use <%@resources:x,y%> or meta:resourcekey markups. Fortunately because other resources are strongly typed you can access them by resource ids. For example if you have Resource1.resx on your root directory then you can use its Hello resource using the following code.

var greeting = Resource1.Hello;

If you want to use the resource from HTML element you have to modify the properties of the resource. When you add a new resource file the Access Modifier is set to Internal (this is same as Custom Tool property of the resource is set to ResXFileCodeGenerator).

Resource file access modifier

This will make resource to be internal and they cannot be used from page markup. Change Access Modified to Public (or Custom Tool to PublicResXFileCodeGenerator).

Public access modifier

Now you can use the resource from markup.

<p><%=ResourceSample.Resource1.Hello %></p>

The resource id syntax is <applicaitonnamespace>.<resourcefile>.<resourcename>

Custom resources are very useful and easy-to-use to in static HTML elements. Custom resources are compiled into the assembly file of your application. You do not need to deploy .resx file with your application.

Comparing different resource files

Having three different strings resource is confusing. However each resource type has it own purpose. Use local resources to store strings from asp-elements. You will have one resource file for each page making resources well categorized. Use either global resources when multipe pages need to share the same resoource ite. Use global or custom resources when you use resource from code. They are both strongly typed so accessing resource from code is easy. The following table summarizes some of the features of each resource file.

Resource Where to use Web
Forms
MVC Web
Pages
Deploying Strongly typed Usage Example What to add to Soluling
Local resource files asp-elements yes yes - .resx no meta:resourcekey attribute
GetLocalResourceObject function
<asp:Label runat="server" meta:resourcekey="label1" />
<p><%=GetLocalResourceObject("Hello") %></p> 
App_LocalResources\*.resx
Global resource files code
asp-elements
yes yes yes .resx yes strongly typed objects
GetGlobalResourceObject function
var greeting = Resources.Strings.Hello;
<p><%=GetGlobalResourceObject("Hello") %></p>
App_GlobalResources\*.resx
Custom resource files code yes yes - Inside assemblies yes strongly typed objects
var greeting = MyApplication.Resource1.Hello;
bin\<applicationname>.dll

If you have either App_LocalResources or App_GlobalResources folders you have to deploy the folders and all the .resx files (original and localized) they contain. Is you use custom resources (i.e .resx files outside App_LocalResources and App_GlobalResources folders) then you don't have to deploy the .resx files because the resource files get compiled inside the application assembly. Localized strings are deployed as localized satellite assembly files.

How the language is choosen

When ASP.NET engine processes a page it must choose what language resource it uses. To do this it uses uiculture property. You can set uiculture to a specific language. For example to set uiculture into German use following code.

uiculture="de"

Whenever a broser is requesting a page it send the preferend lanugage(s) in the HTTP header. You can set uiculture to a match the language that browser is requesting by setting language code to "auto".

uiculture="auto"

This is the recommended method because then server is providing the page in the language the user is requesting. If you use ASP.NET Web Forms you can set the uiculture in the page header.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="Driving.Index" culture="auto" uiculture="auto" %>

If you use ASP.NET MVC or Web Pages you can set the uiculture in the Web.config file.

<configuration>
  <system.web>
    <globalization culture="auto" uiCulture="auto" />
  </system.web>
</configuration>

Comments

Each resource items in .resx file can contain an optional comment value.

Comment in ResX editor

If you add a comment value Soluling will automatically import the value as a comment value of Soluling's project file.

Localization with Localized Files

In the previous page we studied ASP.NET localization process when resource files were used. Although using resource files is the "standard" way to localize ASP.NET projects it has some drawbacks. The most obvious is that you no longer can place any text into markup (.aspx or .cshtml) file but instead you add all text into resource files (.resx). This makes editing markup file more difficult. If your ASP.NET project is a compiled project (e.g. yor created a Web typed project in Visual Studio instead of Web Site typed project) then you don't have any other choices but to use resource files. However if your ASP.NET project is dynamically compiled (e.g. you created a Web Site typed project) then you have another option: you can let Soluling to create localized versions of all your files such as .aspx, code, image, audio, etc.

Let's have an example. You have a simple web site that has the following files:

Default.aspx

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>Sample</title>
</head>
<body>
  <h1><img src="flag.png" align="absmiddle" /> Sample</h1>
  <p>Enter a value and click Process.</p>
  <form id="form1" runat="server">
    <asp:Label ID="label1" runat="server" Text="Value:"></asp:Label>
    <br />
    <asp:TextBox ID="textBox" runat="server"></asp:TextBox>
    <asp:Button ID="button" runat="server" Text="Process" OnClick="button_Click" />
    <br />
    <asp:Label ID="resultLabel" runat="server"></asp:Label>
  </form>
</body>
</html>

Default.aspx.cs

public partial class _Default : System.Web.UI.Page
{
  protected void button_Click(object sender, EventArgs e)
  {
    if (textBox.Text != "")
    { 
      resultLabel.Text = String.Format("\"{0}\" processed!", textBox.Text); //loc: This is a message pattern and {0} must exists in the localized string
      textBox.Text = "";
    }
    else
      resultLabel.Text = "You must enter a value!"; //loc
  }
}

flag.png

 

Tagging

 

How to localize

Deployment

satellite assemblies

ASP.NET Core

.NET Core 1.x used a dependencies file with the main assembly. It is located on the same directory as the application assembly: <appname>.deps.json. This file must contain the satellite assembly files specified in the resources section. Either edit the dependencies file manually or let Soluling update it.

Samples

<data-dir>\Samples\ASP.NET\Framework contains sample sub directories for each ASP.NET programming models.

Web Forms

<data-dir>\Samples\ASP.NET\Framework\WebForms contains following ASP.NET Web Forms samples:

Directory Description
Driving\Original A simple driving time calculator sample that uses hard coded strings.
Driving\Localized As above but hard coded strings have been replaced with resource items and resource items are localized.
Driving\WebSite As above but the project is a web site instead of web application. No resources are used but localized files and images are created.
Driving\Output Output directory where localized sites of above web site will be written.
Image A sample that shows how to localize images.
Plural A sample that shows how to use plural messages.
Resource A sample that shows different ways to access resource from markup and code files.

MVC

<data-dir>\Samples\ASP.NET\Framework\MVC contains following ASP.NET MVC samples:

Directory Description
Driving\Original A simple driving time calculator sample that uses hard coded strings.
Driving\Localized As above but hard coded strings have been replaced with resource items and resource items are localized.
Image A sample that shows how to localize images.
Plural A sample that shows how to use plural messages.

Web Pages

<data-dir>\Samples\ASP.NET\Framework\WebPages contains following ASP.NET WebPages samples:

Directory Description
Driving\Original A simple driving time calculator sample that uses hard coded strings.
Driving\Localized As above but hard coded strings have been replaced with resource items and resource items are localized.
Image A sample that shows how to localize images.
Plural A sample that shows how to use plural messages.