Automated Coder

Exploring the Code of CruiseControl.Net

Archive for November 8th, 2008

Internationalisation – part 4

Posted by Craig Sutherland on 8 November, 2008

Previously

Up until now I’ve been working on converting hard-coded strings to resource-based strings. This has all been in preparation for allowing people to change their language (or at least have their current language displayed). These changes make use of the resource functionality in .Net, plus the resource-based code generation in Visual Studio.

Now that I have some translated strings (thanks Ruben), I’m ready to test the language changing. In this post I’ll go over what I’ve done and why.

Configuring the Language

When I initially wrote about allowing different languages, the first feedback I got was that people like the idea, but they also want to be able to stick with English for their own use. This means giving people the ability to change which language they use.

By default the current UI culture is set to the UI culture of the machine. This is normally a combination of the language and region (i.e. for me, my UI culture is en-NZ). This allows both different languages and different date/time/numeric settings depending on where in the world a person is (i.e. I live in New Zealand and speak English).

In CruiseControl.Net people want to be able to change this (at least I’m assuming that their culture settings are non-English). Since the CruiseControl.Net server has no user interface, the best place for doing this is in the configuration.

Back when I started contributing to CruiseControl.Net I added the ability to set extensions in CruiseControl.Net. Part of this work involved letting people set, in the configuration file, which extensions to use. To do this I added a class called ServerConfigurationHandler, which implements IConfigurationSectionHandler. This means that configuration will be handled by the .Net configuration framework.

Now it’s time to modify this class and how it works. For my initial implementation I just returned a List<ExtensionConfiguration>, now I need it to return more (but still the list). To handle this I have added a new class called ServerConfiguration, which includes the current list plus adds the ability for people to add other configuration settings. This required modify ServerConfigurationHandler to return the new class and then modifying CruiseServerFactory to use the new class instead of the list.

Now I’m ready to add some new configuration. The only setting I’m going to add is culture name – this will be the language code the application will use. The default setting is the current UI culture name. The handler checks the configuration to see if the value is set, if so it loads it in, otherwise it just uses the default.

So, the new configuration looks like this:

<cruiseServer>
  <culture name="nl"/>
</cruiseServer>

To change the language just change the name of the culture :) But default this is commented out so the machine’s settings are used.

Changing the Language

Changing the language is very simple – just need to set the culture on the current thread. There is one limitation – the CurrentCulture property cannot be a neutral culture. To get around this, I’ve used CurrentUICulture instead. This is the property used by the resource manager to work out which language resources to use, while CurrentCulture is used for additional settings like number and date/time formats.

There are two projects where this needs to be set – console and service. The console project uses ConsoleRunner in the core project, while service does all its own instantiation and initialisation. Therefore the language changing logic needs to be in both locations.

The actual logic is straight forward. First attempt to get the configuration using ConfigurationManager. Next check if the configuration was retrieved (it will return null if there is none) and see if the culture has been set. If these conditions have been met, then instantiate the culture and set the CurrentUICulture property on the current thread.

I also modified the locations where a new thread is started to set the CurrentUICulture based on the culture of the calling thread – this should propagate the culture to all child threads.

Some quick testing showed that the culture is successfully changed and the applicaton now uses the desired language :)

Note: culture codes in .Net have two parts – the language and the area. The language code is always used, while the area defines the additional formatting settings. The resource manager will first attempt to match the language/area combination. If this fails it will attempt the language. Finally if both of these checks fail it will use the default resources – in this case English. As such it is possible to set the culture in the configuration to either (e.g. I could set en-NZ) and it will still work. But unless the language has been added you’ll get English. The other format settings are ignored (for the moment).

The Service

As I stated earlier, the service project also uses the culture settings. Like console, it provides an access point to the functionality in the core project. Unlike console, it has its own hard-coded strings. Therefore my final change for this post was to go through this project and convert all hard-coded UI strings to resource strings.

Now both of the back-end interfaces support multi-lingual interfaces.

The next step in this process is to start looking at the user interfaces – CCTray and the dashboard. Over the next few days I’ll take a look at them and then start to plan how I’ll convert them to use resources.

Posted in CruiseControl.Net | Tagged: | Leave a Comment »