Hi All,
I am fairly new to localization in ASP.NET 2.0 and I would like to get some feedback and recommendations from the experts regarding my current implementation.
My company is in the process of migrating an existing, mostly static website to the ASP.NET 2.0 platform. The current site has three languages—English, Traditional Chinese and Simplified Chinese. The site has about 30 static pages for each language. The maintenance of the site is a nightmare as you could imagine—one copy of the same content for each language. The make thing worse, the previous developer did not employ a template solution (only SOME server-side includes, even the use of them are not very consistent), etc. I have designed to redo the site from ground up. I will be the only one updating the site (as the update is not overly frequent) and I want to make the site much more manageable for myself. Instead of updating three files and uploading each of them whenever I have updates, I want to be able to update only one file; if I need to change the overall look and feel, I want to be able to modify only one file and the CSS to achieve that. SEO would be important to us as well. A full-blown, database driven CMS solution, however, may be a little overkill for this project.
The first challenge I have is to create a template for the site. I am using the Master Page feature. For SEO purpose, I want the URL for each language page to be different. I have decided to pass the language code as a Query String and use UrlRewrite to create a SEO friendly URL. I won’t get into details regarding the set up of the UrlRewrite. I am using the UrlRewriting.net component and you can find more information about the project at
http://www.urlrewriting.net/
I set up a rule so that
http://www.blah-travelagency.com/zh-TW/contact-us.aspx
will be rewritten as
http://blah-travelagency.com/contact-us.aspx?lang=zh-TW
I also have a Utility Method to ensure the specified culture is supported and well-formed.
The supported cultures are stored as a comma separated string in the web.config AppSetting section. English would be the fall-back culture so I need to specify English (Canada), Traditional Chinese and Simplied Chinese as supported.
<appSettings>
<add key="supportedCultures" value="en-CA,zh-TW,zh-CN"/>
</appSettings>
The code to ensure the specified culture is supported is as follows:
public static bool IsSupportedCulture(string cultureName)
{
StringCollection sc = new StringCollection();
sc.AddRange(WebConfigurationManager.AppSettings["supportedCultures"].Split(','));
return sc.Contains(cultureName);
}
Within the PreRender Method of my masterpage code, I added the following code to assign the desired culture to the current thread:
string culture = Request.QueryString["lang"];
System.Globalization.CultureInfo lang;
if (CultureUtilities.IsSupportedCulture(culture))
{
lang = new System.Globalization.CultureInfo(culture);
System.Threading.Thread.CurrentThread.CurrentCulture = lang;
System.Threading.Thread.CurrentThread.CurrentUICulture = lang;
}
I then use explicit localization to assign Localized Menu Text, NavigateUrl, etc from the Global Resource.
This approach allows me to auto detect the user’s preference when a language is not explicitly set, but also allows me to explicitly set the UI culture as necessary (by using a querystring / url rewrite).
Unfortunately, the resource editor in VS 2005 doesn’t handle large amount of text very well. While I am planning to use a SQL server as the backend for the tours information, I think the overhead of creating a CMS for the relatively static pages such as visa information, contact us may be too much. True I can leave those pages as three separate versions but I am sure there are better ways than that.
I looked at the list of WebControls panel and I felt the MultiView control might be a viable workaround for my problem.
I want to be able create Views based on the localization and paste the localized text into the appropriate view. I also need the mechanism to auto detect the culture setting or set the culture setting explicitly as needed.
The only logic we need to change is to activate the appropriate culture view. My solution is geared towards static html code so I disable the viewstate for this control. My implementation is as follows:
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Text;
5 using System.Web;
6 using System.Web.UI;
7 using System.Web.UI.WebControls;
8 using System.Globalization;
9
10 namespace Dtkh.CompanionHolidays.WebControls
11 {
12 [ToolboxData(<{0}:LocalizedContent runat=server></{0}:LocalizedContent>")]
13 public class LocalizedContent : MultiView
14 {
15 /// <summary>
16 /// Default contructor; initialize the LocalizeContent class to its default values
17 /// </summary>
18 public LocalizedContent()
19 {
20 _cultureName = "";
21 _invariantCultureViewName = "default";
22 _autoCultureDetection = true;
23 this.EnableViewState = false;
24 }
25
26 /// <summary>
27 /// True if culture auto detection is enable. The _currentName will be set accordingly to CurrentUICulture of the current thread.
28 /// </summary>
29 protected bool _autoCultureDetection;
30 /// <summary>
31 /// Name of the region code;
32 /// </summary>
33 protected string _cultureName;
34 /// <summary>
35 /// the default, fallback view
36 /// </summary>
37 protected string _invariantCultureViewName;
38
39 public string InvariantCultureViewName
40 {
41 get
42 {
43 return _invariantCultureViewName;
44 }
45 set
46 {
47 _invariantCultureViewName = value;
48 }
49 }
50
51 public bool AutoCultureDetection
52 {
53 get
54 {
55 return _autoCultureDetection;
56 }
57 set
58 {
59 _autoCultureDetection = value;
60 }
61 }
62
63 public string CultureName
64 {
65 get
66 {
67 return _cultureName;
68 }
69 set
70 {
71 _cultureName = value;
72 }
73 }
74
75
76 /// <summary>
77 /// A utility method to set the active view by name instead of index
78 /// </summary>
79 /// <param name="viewName">Set the active view to the specified view by its name</param>
80 /// <returns>true when the view specified is found; false otherwise</returns>
81 protected bool SwitchToView(string viewName)
82 {
83 View view = this.FindControl(viewName) as View;
84 if (view != null)
85 {
86 this.SetActiveView(view);
87 return true;
88 }
89 else
90 {
91 return false;
92 }
93 }
94
95 /// <summary>
96 /// Set the active view by the _cultureName variable
97 /// </summary>
98 protected void SetViewByCulture()
99 {
100 if (_autoCultureDetection)
101 {
102 _cultureName = System.Threading.Thread.CurrentThread.CurrentUICulture.ToString();
103 }
104 if (!(SwitchToView(_cultureName.Replace('-','_')))) {
105 SwitchToView(_invariantCultureViewName);
106 }
107 }
108
109 protected override void Render(HtmlTextWriter output)
110 {
111 // set the view to the appropriate language
112 SetViewByCulture();
113 base.Render(output);
114 }
115 }
116 }
117
I then register the assembly with the page…
<%@ Register Namespace="Dtkh.CompanionHolidays.WebControls" Assembly="Dtkh.CompanionHolidays" TagPrefix="DtkhCh" %>
…and use the control much like the below example. Please note that I have to use an underscore instead of a dash for the id of the views as dashes are not valid characters for the id attribute.
<DtkhCh:LocalizedContent ID="content" runat="server">
<asp:View ID="default" runat="server">
english
</asp:View>
<asp:View ID="zh_TW" runat="server">
繁體中文
</asp:View>
<asp:View ID="zh_CN" runat="server">
簡體
</asp:View>
</DtkhCh:LocalizedContent>
By setting AutoCultureDetection or the CultureName, I am able to display localized the appropriate content to your users.