CurrentCulture and CurrentUICulture


Of course, our work is not quite done yet. The problem with the solution so far is that the French resources exist but are not being used. For reasons that will become clear in a moment, the only time the French version of the application will be seen is when it is run on a French version of Windows. Apart from usability considerations, this makes testing your application unnecessarily difficult.

Two properties determine the default internationalization behavior of an application are: CurrentCulture and CurrentUICulture. Both properties can be accessed either from the current thread or from the CultureInfo class, but they can be assigned only from the current thread. Assuming that the following directives have been added to Form1.cs:

 using System.Globalization; using System.Threading; 


The first two lines provide the same output as the second two lines:

 MessageBox.Show(Thread.CurrentThread.CurrentCulture.DisplayName); MessageBox.Show(Thread.CurrentThread.CurrentUICulture.DisplayName); MessageBox.Show(CultureInfo.CurrentCulture.DisplayName); MessageBox.Show(CultureInfo.CurrentUICulture.DisplayName); 


CurrentCulture represents the default culture for all classes in System.Globalization and thus affects issues such as culture-specific formatting (such as date/time and number/currency formats), parsing, and sorting. CurrentUICulture represents the default culture used by ResourceManager methods and thus affects the retrieval of user interface resources such as strings and bitmaps. CurrentCulture defaults to the Win32 function GetUserDefaultLCID. This value is set in the Regional and Language Options Control Panel applet, shown in Figure 3.6. Consequently, in a Windows Forms application, the user has direct control over this setting. In an ASP.NET application, the value is set in the same way, but because it is set on the server, its setting applies to all users and a more flexible solution is required (see Chapter 5).

Figure 3.6. Regional and Language Options


Bear in mind that the CurrentCulture is culture-specific, not culture-neutral. That is, the value includes a region as well as a language. If you consider that this value determines issues such as date/time formats and the number and currency formats, you can understand that it is meaningless to assign a "French" culture to CurrentCulture because French in Canada has completely different globalization values to French in France. In general, your application should strive to acquire a specific culture for the CurrentCulture, but there is an option to manufacture a specific culture, which can be considered a last resort. The CultureInfo.Create-SpecificCulture method accepts a culture and returns a specific culture from it. So if you pass it "fr" for French, you get a culture for French in France. Similarly, for Spanish you get Spanish (Spain), and for German you get German (Germany). You can forgive the people of England for being a little surprised that the specific culture for English is not England; it is the United States.

The CurrentUICulture, however, defaults to the Win32 function GetUserDefaultUILanguage. This value is usually determined by the user interface language version of the operating system and cannot be changed. So if you install the French version of Windows, GetUserDefaultUILanguage returns French; therefore, CurrentUICulture defaults to French. However, if you install Windows Multiple User Interface Pack (Windows MUI; see Chapter 2, "Unicode, Windows, and the .NET Framework"), the user can change the language version of the user interface through a new option that appears in the Regional and Language Options. The CurrentUICulture can be culture-neutral, culture-specific, or the invariant culture.

Armed with this knowledge, you can see why on a typical machine running in the U.K., the following code results in "English (United States)," followed by "English (United Kingdom)":

 MessageBox.Show(Thread.CurrentThread.CurrentCulture.DisplayName); MessageBox.Show(Thread.CurrentThread.CurrentUICulture.DisplayName); 


To see the French resources in our example application, we need to provide a means by which the user can select a language. This facility is simplistic in the extreme; see Chapters 4 and 5 for more advanced solutions. Add a RadioButton to the form, set its Text to "French," and add a CheckChanged event with this code:

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


This line creates a new CultureInfo object for neutral French and assigns it to the CurrentUICulture of the current thread. This affects all ResourceManager methods on this thread, which default to the CurrentUICulture from here on. You can also set the CurrentCulture in a similar fashion:

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


Notice that, in this example, the culture is a specific culture ("fr-FR"), not a neutral culture ("fr"). Add another RadioButton, set its Text to English, and add a CheckChanged event with this code:

 Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; 


This line doesn't actually use the English resources as the RadioButton's Text implies it does. Instead, it sets the CurrentUICulture to the invariant culture. Although the effect will be the same and the user will see English resources, the setting simply causes the ResourceManager to use the resources that are embedded in the main assembly instead of a satellite assembly. Now you can run the application, select one of the RadioButtons, and see the MessageBox use the correct resource string according to your selection.

We have made our application localizable, and we have localized it. To add new languages, we need only add new versions of Form1Resources.resx (e.g., Form1Resources.es.resx for Spanish), add a RadioButton to set the CurrentUICulture to the new language, and compile our application to create the new satellite assembly (e.g., es\WindowsApplication1.resources.dll).

CurrentCulture, CurrentUICulture, and Threads

I mentioned in the previous section that the CurrentCulture and CurrentUI Culture properties are set on a thread. The full implication of this might not be immediately apparent. This means each thread must have its CurrentCulture and CurrentUICulture properties explicitly and manually set. If you create your own threads, you must set these properties in code. The important point to grasp here is that new threads do not automatically "inherit" these values from the thread from which they were created; a new thread is completely new and needs to be reminded of these values. To create a new thread, you could write this:

 Thread thread = new Thread(new ThreadStart(Work)); thread.CurrentCulture   = Thread.CurrentThread.CurrentCulture; thread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture; thread.Start(); 


This solves the problem, but it is cumbersome and relies on every developer remembering to set these properties (developers will eventually forget). A better solution is to create a thread factory:

 public class ThreadFactory {   public static Thread CreateThread(ThreadStart start)   {     Thread thread = new Thread(start);     thread.CurrentCulture   = Thread.CurrentThread.CurrentCulture;     thread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;     return thread;   } } 


Of course, now you are relying on your developers to remember to use the ThreadFactory instead of creating threads manually. See Chapter 13, "Testing Internationalization Using FxCop," for the "Thread not provided by ThreadFactory" rule, which ensures that new threads are not created using the System.Threading.Thread constructor.




.NET Internationalization(c) The Developer's Guide to Building Global Windows and Web Applications
.NET Internationalization: The Developers Guide to Building Global Windows and Web Applications
ISBN: 0321341384
EAN: 2147483647
Year: 2006
Pages: 213

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net