ASP.NET 2.0 has a rich set of localization features built-in. Early in the ASP.NET 1x lifecycle we "rolled our own" using an HTTP handler that reflected for a [localize] tag, looked up the control name in a resource file and assigned the localized value. I prefer out-of-the-box solutions though when available and the MS resource provider model approach provides enough flexibility to be worthwhile.
ASP.NET 2.0 Localization adds two new resource flavors: local and global resources. Local resources are used for controls on a specific page. The resources are located in the ASP.NET App_LocalResources folder with the same name as the page you're localizing. So, if you're translating default.aspx then your App_LocalResources might also contain default.fr-FR.resx with a French translation. Global resources are contained in App_GlobalResource and can be used anywhere in the application.
You bind resources to your controls using explicit or implicit expressions. Explicit expressions use an inline server syntax similar to the data binding syntax you're already familiar with. Implicit expressions syntax use a "<meta>" tag in the control you're localizing to identify the resource.
Local Resources, Implicit Expressions
It all makes more sense in practice so here's a basic walk-though using local resources with implicit expressions:
Create an ASP.NET web application.
Add a TextBox control to the default form.
While you're on the page to localize (default.aspx in this case), select Tools |Generate local resources. This step has a number of effects.
- An App_LocalResources folder appears populated with "Default.aspx.resx".
- In the Source view, the ASP.NET markup page tag will have a Culture="auto" attribute added. The value of "auto" lets the application react to the browser language settings.
- In the ASP.NET HTML markup for the control will have a "meta:resourcekey" attribute added:
<asp:TextBoxID="TextBox1" runat="server" meta:resourcekey="TextBox1Resource1"></asp:TextBox>
This identifies a single element in the ASP.NET HTML markup. If there are other nested elements, each needs to be marked with a "meta:resourcekey" attribute before you can implicitly bind to them (as when you have BoundField elements within a GridView for example). The general rule is, if the element is qualified with a namespace, it needs a "meta:resourcekey" attribute.
The properties are marked with an icon that show they are implicitly bound.
- Double-click default.aspx.resx to edit the resource. This will contain resources for the page as a whole and for any controls on the page. The naming convention for resources is <resourcekey>:property name (you typically don't have to know that, but if localization support for a component is incomplete you can still add the "meta:resourcekey" tags by hand). Add text for the page title and TextBox Text property.
- Copy default.aspx.resx to default.aspx.fr-FR.resx.
- Edit the values in the resource file to French translations. You can use a tool like Google Translate to get a fair approximation of what the translation should look like. I wouldn't use it as a production tool though unless you want chuckles from the target audience (try translating to and from the target language a few times and you'll see what I mean).
- Run the application. Assuming Internet Explorer as your browser, go to Tools | Options | Languages. Select the "fr-FR" culture code, then refresh the page and Voila! (or "Blick dort!" or "sguardo là!". Hmm, sure hope I'm not insulting anyone).
Slightly More Complex Scenarios
The same pattern works for more complex objects, such as GridView. Notice there is a "meta:resourcekey" for the grid and also for each of the BoundField objects.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="SqlDataSource1"
meta:resourcekey="GridView1Resource1"> <Columns> <asp:BoundField DataField="ProductName" HeaderText="ProductName" meta:resourcekey="BoundFieldResource1"
SortExpression="ProductName" /> <asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock" meta:resourcekey="BoundFieldResource2"
SortExpression="UnitsInStock" /> </Columns> </asp:GridView>
Again, you can bind to any resourcekey/property name combination:
And because the page Culture is set to "Auto", you can change the browser language settings and the translated text is displayed automatically:
The localization facilities built-in to ASP.NET 2.0 should save you some time if they fit your requirements. If you need a heavier weight solution that uses a database (or other data store) instead of XML to store resources, check out this article by Jeff Modzel "ASP.NET 2.0 Custom SQL ResourceProvider".