Localization Like the Pros—Localization Example Using Visual Studio .NET
Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio
| CSharp-Online.NET:Articles |
| C# Articles |
| © 2004 Wiley Publishing, Inc. |
Localization Example Using Visual Studio .NET
For this section, we create a simple Windows application to show you how to use Visual Studio .NET for localization. This application will not use complex Windows Forms and does not have any real inner functionality, because the key feature we want to demonstrate here is localization. In the automatically generated source code change the namespace to Wrox.ProCSharp.Localization, and the class name to BookOfTheDayForm. The namespace is not only changed in the source file BookOfTheDayForm.cs, but also in the project settings, so that all generated resource files will get this namespace, too. You can change the namespace for all new items that are created by selecting Common Properties of Project -> Properties.
To show some issues with localization, this program has a picture, some text, a date, and a number. The picture shows a flag that is also localized. Figure 17-13 shows this form of the application as seen in the Windows Forms Designer.
The following table lists the values for the Name and Text properties of the Windows Forms elements.
| Name | Text |
labelBookOfTheDay
| Book of the day |
labelItemsSold
| Books sold |
textDate
| Date |
textTitle
| Professional C# |
textItemsSold
| 30000 |
pictureFlag
|
In addition to this form, you might want a message box that displays a welcome message; this message might change depending on the current time of day. This example will demonstrate that the localization for dynamically created dialogs must be done differently. In the method WelcomeMessage(), display a message box using MessageBox.Show(). Call the method WelcomeMessage() in the constructor of the form class BookOfTheDayForm, before the call to InitializeComponent().
Here is the code for the method WelcomeMessage():
public void WelcomeMessage() { DateTime now = DateTime.Now; string message; if (now.Hour <= 12) { message = "Good Morning"; } else if (now.Hour <= 19) { message = "Good Afternoon"; } else { message = "Good Evening"; } MessageBox.Show(message + "\nThis is a localization sample"); }
The number and date in the form should be set by using formatting options. Add a new method SetDateAndNumber() to set the values with the format option. In a real application, these values could be received from a Web Service or a database, but in this example we are just concentrating on localization. The date is formatted using the D option (to display the long date name). The number is displayed using the picture number format string ###,###,### where # represents a digit and "," is the group separator:
public void SetDateAndNumber() { DateTime today = DateTime.Today; textDate.Text = today.ToString("D"); int itemsSold = 327444; textItemsSold.Text = itemsSold.ToString("###,###,###"); }
In the constructor of the BookOfTheDayForm class both the WelcomeMessage() and SetDateAndNumber() methods are called.
public BookOfTheDayForm() { WelcomeMessage(); // // Required for Windows Form Designer support // InitializeComponent(); SetDateAndNumber(); }
A magic feature of the Windows Forms designer is started when we set the Localizable property of the form from false to true: this results in the creation of an XML-based resource file for the dialog box that stores all resource strings, properties (including the location and size of Windows Forms elements), embedded pictures, and so on. In addition, the implementation of the InitializeComponent() method is changed; an instance of the class System.Resources.ResourceManager is created, and to get to the values and positions of the text fields and pictures, the GetObject() method is used instead of writing the values directly into the code. GetObject() uses the CurrentUICulture property of the current thread for finding the correct localization of the resources.
Here is part of InitializeComponent() before the Localizable property is set to true, where all properties of textboxTitle are set:
private void InitializeComponent() { //... this.textTitle = new System.Windows.Forms.TextBox(); //... // // textTitle // this.textTitle.Location = new System.Drawing.Point(24, 152); this.textTitle.Name = "textTitle"; this.textTitle.Size = new System.Drawing.Size(256, 20); this.textTitle.TabIndex = 2; this.textTitle.Text = "Professional C#";
This is automatically changed code for IntializeComponent() with the Localizable property set to true:
private void InitializeComponent() { System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(BookOfTheDayForm)); //... this.textTitle = new System.Windows.Forms.TextBox(); //... // // textTitle // this.textTitle.AccessibleDescription = resources.GetString("textTitle.AccessibleDescription"); this.textTitle.AccessibleName = resources.GetString("textTitle.AccessibleName"); this.textTitle.Anchor = ((System.Windows.Forms.AnchorStyles) (resources.GetObject("textTitle.Anchor"))); this.textTitle.AutoSize = ((bool)(resources.GetObject("textTitle.AutoSize"))); this.textTitle.BackgroundImage = ((System.Drawing.Image) (resources.GetObject("textTitle.BackgroundImage"))); this.textTitle.Dock = ((System.Windows.Forms.DockStyle) (resources.GetObject("textTitle.Dock"))); this.textTitle.Enabled = ((bool) (resources.GetObject("textTitle.Enabled"))); this.textTitle.Font = ((System.Drawing.Font) (resources.GetObject("textTitle.Font"))); this.textTitle.ImeMode = ((System.Windows.Forms.ImeMode) (resources.GetObject("textTitle.ImeMode"))); this.textTitle.Location = ((System.Drawing.Point) (resources.GetObject("textTitle.Location"))); this.textTitle.MaxLength = ((int) (resources.GetObject("textTitle.MaxLength"))); this.textTitle.Multiline = ((bool) (resources.GetObject("textTitle.Multiline"))); this.textTitle.Name = "textTitle"; this.textTitle.PasswordChar = ((char) (resources.GetObject("textTitle.PasswordChar"))); this.textTitle.RightToLeft = ((System.Windows.Forms.RightToLeft) (resources.GetObject("textTitle.RightToLeft"))); this.textTitle.ScrollBars = ((System.Windows.Forms.ScrollBars) (resources.GetObject("textTitle.ScrollBars"))); this.textTitle.Size = ((System.Drawing.Size) (resources.GetObject("textTitle.Size"))); this.textTitle.TabIndex = ((int) (resources.GetObject("textTitle.TabIndex"))); this.textTitle.Text = resources.GetString("textTitle.Text"); this.textTitle.TextAlign = ((System.Windows.Forms.HorizontalAlignment) (resources.GetObject("textTitle.TextAlign"))); this.textTitle.Visible = ((bool) (resources.GetObject("textTitle.Visible"))); this.textTitle.WordWrap = ((bool) (resources.GetObject("textTitle.WordWrap")));
Where does the resource manager get the data from? When the Localizable property is set to true, the resource file BookOfTheDay.resX is generated. In this file, you can find the scheme of the XML resource, followed by all elements in the form: Type, Text, Location, TabIndex, and so on.
The following XML segment shows a few of the properties of textBoxTitle: the Location property has a value of 24, 152, the TabIndex property has a value of 2, the Text property is set to Professional C#, and so on. For every value, the type of the value is stored as well. For example, the Location property is of type System.Drawing.Point, and this class can be found in the assembly System.Drawing.
Why are the locations and sizes stored in this XML file? With translations, many strings will have completely different sizes and do not any longer fit in to the original positions. When the locations and sizes are all stored inside the resource file, everything that is needed for localizations is stored in these files, separate from the C# code:
<data name="textTitle.Location" type="System.Drawing.Point, System.Drawing, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <value>24, 152</value> </data> <data name="textTitle.RightToLeft" type="System.Windows.Forms.RightToLeft, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Inherit</value> </data> <data name="textTitle.Size" type="System.Drawing.Size, System.Drawing, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <value>256, 20</value> </data> <data name="textTitle.TabIndex" type="System.Int32, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>2</value> </data> <data name="textTitle.Text"> <value>Professional C#</value> </data> <data name="textTitle.TextAlign" type="System.Windows.Forms.HorizontalAlignment, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Left</value> </data> <data name="textTitle.Visible" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>True</value> </data>
When changing some of these resource values, it is not necessary to work directly with the XML code. You can change these resources directly in the Visual Studio designer. Whenever you change the Language property of the form and the properties of some form elements, a new resource file is generated for the specified language. Create a German version of the form by setting the Language property to German, and a French version by setting the Language property to French. For every language, you get a resource file with the changed properties: BookOfTheDayForm.de.resX and BookOfTheDayForm.fr.resX.
The following table shows the changes needed for the German version.
$this.Text (title of the form)
| Buch des Tages |
labelItemsSold.Text
| Bücher verkauft: |
labelBookOfTheDay.Text
| Buch des Tages |
The following table lists the changes for the French version. For both languages, we also change the flag representing the country.
| French Name | Value |
$this.Text (title of the form)
| Le livre du jour |
labelItemsSold.Text
| Des livres vendus |
labelBookOfTheDay.Text
| Le livre du jour |
Compiling the project now creates a satellite assembly for each language. Inside the debug directory (or the release, depending on your active configuration), language subdirectories like de and fr are created. In such a subdirectory, you will find the file LocalizationDemo.resources.dll. Such a file is a satellite assembly that only includes localized resources. Opening this assembly with ildasm (see Figure 17-14), we see a manifest with the embedded resources and a defined locale. The assembly has the locale de in the assembly attributes, and so it can be found in the de subdirectory. You can also see the name of the resource with .mresource; it is prefixed with the namespace name Wrox.ProCSharp.Localization, followed by the class name BookOfTheDayForm and the language code de.
|



