Click here to Skip to main content
Click here to Skip to main content

Localizing .NET Enums

By , 17 Oct 2007
 

Introduction

One of the neat things about enumerations in .NET is that you can use data binding to display and select enum values in a list or drop down combo. For instance, consider the following enum definition:

enum SampleEnum
{
    VerySmall,
    Small,
    Medium,
    Large,
    VeryLarge
}

We can display the enumeration values (and allow the user to select a value) by adding a ListBox to a form and setting the DataSource with one line of code:

_enumListBox.DataSource = Enum.GetValues(typeof(SampleEnum));

That's great - when we run our code now the list box shows the enumeration values:

Screenshot - EnumList.jpg

We can get the selected enum value by simply using the list box SelectedItem property:

SampleEnum selectedSampleEnum = (SampleEnum)_enumListBox.SelectedItem;

There are two problems with this technique however. The first problem is that the text we wish to display to the user may not be exactly the same as the enumeration value. In the example above, it would be much nicer to display Very Small with a space between the words. We can't use this as an enumeration value however because enum values cannot have spaces. The second problem is that there is no way to translate the enum values if we want to localize our application (i.e. provide a translated user interface for different cultures).

This article presents a simple solution to the above problems that leverages the power of .NET TypeConverters.

Background

The article Humanizing the Enumerations by Alex Kolesnichenko also addresses the problems discussed above by attaching a custom attribute to each enumeration value and using an adaptor class (EnumToHumanReadableConverter) as the data source. Our approach instead leverages the power of the .NET TypeConverter mechanism to automatically handle conversion to and from localized text and enum values. The benefit of this approach is that not only does it work for data binding (with no extra code), it can also be used anywhere in your application that you need to convert between localized text and enum values.

What is a TypeConverter

TypeConverters are the in-built .NET mechanism for converting objects of one type to another (for instance from an enum value to a string value). When the ListBox control (and other .NET controls) displays enum values, it first converts them to strings using a TypeConverter. The TypeConverter it uses depends on the TypeConverter that has been associated with the type using the System.ComponentModel.TypeConverterAttribute. By default all enum types use the predefined EnumConverter class. As we have seen, the EnumConverter simply converts the enum value to its exact string representation. Fortunately we can define our own derived TypeConverter class and associate it with our enum type when we declare it as follows:

[TypeConverter(typeof(LocalizedEnumConverter))]
public enum SampleEnum
{
    VerySmall,
    Small,
    Medium,
    Large,
    VeryLarge
}

In this solution we define a custom TypeConverter class (LocalizedEnumConverter) that converts enum values to and from string values using localized strings from the project resources.

Using the Code

The sample project code consists of a library (Infralution.Localization) and a separate test application. The library defines a base TypeConverter class (ResourceEnumConverter) that converts enum values to and from strings using a ResourceManager that reads the string values from a compiled RESX file. The ResourceManager that is used to do the lookup is passed to the constructor of the ResourceEnumConverter class. Follow the simple steps below to localize enums in your application using this class.

Define a class derived from ResourceEnumConverter in your application project that passes the ResourceManager for the project RESX file you wish to use for the enum lookups. Typically you would just use the standard project Properties.Resources:

class LocalizedEnumConverter : Infralution.Localization.ResourceEnumConverter
{
    public LocalizedEnumConverter(Type type)
        : base(type, Properties.Resources.ResourceManager)
    {
    }
}

Use the System.ComponentModel.TypeConverterAttribute attribute on each enum declaration to associate this TypeConverter with the enum:

[TypeConverter(typeof(LocalizedEnumConverter))]
public enum SampleEnum
{
    VerySmall,
    Small,
    Medium,
    Large,
    VeryLarge
}

Open the Properties\Resources.resx file in the resources editor and enter the text to display for each enum value. The resource name is just the enum type name followed by the value (underscore separated):

Screenshot - EnglishResx.jpg

Now we are ready to add some localized enum text values. Create a set of French resources by copying the Properties\Resources.resx file to Properties\Resources.fr.resx. Make sure that the "Custom Tool" (in the Properties window) for this new RESX file is blank - or you will end up with some strange compilation errors. Open the Properties\Resources.fr.resx file in the resources editor and enter the translated values:

Screenshot - FrenchResx.jpg

Now set the user locale to French using the Control Panel Regional options and run your application. The enum values are now displayed in French. The sample application demonstrates the above and also allows you to dynamically set the CurrentThread.CurrentCulture property by selecting it from a drop down list:

Screenshot - SampleApp.jpg

The ResourceEnumConverter class also supports converting text value back to enum values. The sample application allows you to test this by entering values into the text box and clicking the Convert button.

Localizing Enums in ASP.NET

In Windows Forms, all of the standard controls use TypeConverters to convert bound data values to display strings. Unfortunately for some reason Microsoft did not use this same approach when developing ASP.NET controls. ASP.NET controls typically just use the Object.ToString() method to convert the bound data value to text. This means that while we can still define our TypeConverter (as described above) it won't be used by default by ASP.NET controls.

To solve this problem we have added a static GetValues method to the ResourceEnumConverter class. This method uses the TypeConverter to return a list of KeyValuePair objects for the given enum type. The Key is the enum value and the Value is the localized display text for that enum. To bind an ASP.NET control we set the DataValueField property of the control to Key and the DataTextField property to Value. Then we bind the control to the list returned by the GetValues method as follows:

protected void Page_Load(object sender, EventArgs e)
{
    _enumListBox.DataSource = 
        LocalizedEnumConverter.GetValues(typeof(SampleEnum));
    this.DataBind();
}

Flag Enumerations

Andy Mase pointed out that the original code posted did not handle bit field enumerations (defined using the Flag attribute). In this case an enumeration value can be a bitwise combination of the named enumeration values. The enum may also define named enumeration values which are bitwise combinations of other named values. In the example below an All value is defined which is the bitwise combination of all other values.

[TypeConverter(typeof(LocalizedEnumConverter))]
[Flags]
public enum TextStyle : byte
{
    None = 0x0,
    Bold = 0x1,
    Italic = 0x2,
    Underline = 0x4,
    All = 0xFF
}

The ResourceEnumConverter class now handles converting bit field enums to and from text. When converting a bit field enum value to text, it first checks if the value is one of the named enumeration values. If it is, then the localized text corresponding to the named value will be used. Otherwise it finds the combination of single bit values that are set and creates the text by concatenating the localized text for these together. For example the value 0x3 would be converted to Bold, Italic in English. The download now includes a separate project (TestLocalizedFlagEnum) that demonstrates using the LocalizedEnumConverter with a bit field enum.

History

  • 2007.08.09 - Initial posting
  • 2007.10.17 - Added handling of flagged enums and localizing enums in ASP.NET

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Grant Frisken
Architect Infralution
Australia Australia
Member
I am currently the Software Architect at Infralution. Infralution develops .NET components and solutions including:
 
Globalizer - localization for .NET made easy. Let your translators instantly preview translations and even build the localized version of your application without giving away your source code.
 
Infralution Licensing System - simple, secure and affordable licensing for .NET apps and components
 
Virtual Tree - superfast, flexible, databound tree/list view

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionNice article, but quick question...memberpolo2827 Nov '12 - 22:48 
I tried to use your approch, using TypeConverter, but something is still confusing:
 
when i change the culture using
System.Threading.Thread.CurrentThread.CurrentUICulture = myculture;
System.Threading.Thread.CurrentThread.CurrentCulture = myculture;
It doesn't affect the parameter culture in my ConvertTo method!
No matter what is the CurrentThread.CurrentUICulture and CurrentThread.CurrentCulture, the culture in my argument is still {en-EN}
 
Do you have any idea of the reason?
 
Thanks!
AnswerRe: Nice article, but quick question...memberGrant Frisken28 Nov '12 - 10:59 
I'm not sure why this would be happening for you. When I run the sample project and put a break point in the ConvertTo method the culture is correctly set to the CurrentThread.CurrentCulture. Are you seeing this behaviour in the sample project or your own projectt?
Infralution - we provide .NET solutions:
 
Globalizer - .NET Localization made easy
Infralution Licensing System - simple, secure and affordable licensing
Virtual Tree - superfast, flexible, databound tree/list view

GeneralRe: Nice article, but quick question...memberpolo2828 Nov '12 - 21:21 
I'm seeing this behavior in my project, whereas in your sample the argument matches the current culture. That's why it is confusing!
 
I'll try to reproduce it in a small sample application, and i'll let you know.
 
Thanks for the reply
QuestionGreat article! ..i have 2 problems (underscore as separator, enums with same name and value)memberMember 311836615 Oct '12 - 22:42 
Hi Grant, thank for your article, really helpful.
 
I have 2 problems, maybe you can help me.
 
1. I have existings enums using '_' underscore as value.
    Public Enum MyEnum
        None
        Value_1
        Value_2
    End Enum
How would you suggest to fix it?
 
2. I have Enums with same name and value in different classes, i.e.:
Public Class MyDoc1
    Public Enum DocSubType
        None
        Value_1
        Value_2
    End Enum
End Class
 
Public Class MyDoc2
    Public Enum DocSubType
        None
        Value_1
    End Enum
End Class
 
Respecting the convention, i should insert in the resource file, i.e:
DocSubType_None
DocSubType_Value1
DocSubType_Value2

for the first enum, and for the second, again:
DocSubType_None
DocSubType_Value1

Which is not possible.
 
How would you suggest to manage it?
Thank you!
Paolo

AnswerRe: Great article! ..i have 2 problems (underscore as separator, enums with same name and value)memberGrant Frisken16 Oct '12 - 12:10 
The first issue should not be a problem. The resource keys should just be MyEnum_Value_1 and MyEnum_Value_2.
 
The second issue is only a problem if you want the translations to be different. You could just use the same entries in the resource file for both enums ie you just would put entries:
 
DocSubType_None
DocSubType_Value1
DocSubType_Value2

 
Both enums will then read the same entries. If for some reason you need different text and/or translations for the same entry then you would have to consider a different approach. One possibility would be to modify the ResourceEnumConverter.GetValueText method so that instead of just using type.Name it uses type.FullName (ie the fully qualified name of the type). You would need to replace the "." in the fully qualified name with underscores.
Infralution - we provide .NET solutions:
 
Globalizer - .NET Localization made easy
Infralution Licensing System - simple, secure and affordable licensing
Virtual Tree - superfast, flexible, databound tree/list view

GeneralRe: Great article! ..i have 2 problems (underscore as separator, enums with same name and value)memberMember 311836621 Oct '12 - 22:41 
For the first issue you're right, it works, don't know why, seemed to me like it wasn't working, sorry. Smile | :)
 
For the second, your consideration in correct, at the moment, i don't know if i want different translations. Eventually, your solution seems good to me, probably i will go for it.
 
Thank you Grant!
GeneralMy vote of 5memberemoulin26 Jul '12 - 0:41 
Thank's a lot. Very usefull.
QuestionGreat code, but some questionsmemberBerryl Hesh27 May '12 - 4:32 
1. Is there a hook to sort the display values? Ideally you could do this on a change in language.
2. How would you add special values that may not be defined as part of the enum; am thinking of a ComboBox where the initial SelectedItem might be something like "None" or "Select Gender"
3. Any extra guidance for using this technique within an MVVM scenario?
 
Nice code though, and my vote of 5
AnswerRe: Great code, but some questionsmemberGrant Frisken28 May '12 - 1:50 
If you want to sort the display values in a ComboBox or ListBox you can just set the Sorted property of the control to true. When the language is changed you have to reload the DataSource of the control (as the sample does anyway).
 
To add a special value you can simply add all the Enum values to a object collection (eg ArrayList) and insert a string for your special value at the top. Then set the Combo/ListBox to the collection.
Infralution - we provide .NET solutions:
 
Globalizer - .NET Localization made easy
Infralution Licensing System - simple, secure and affordable licensing
Virtual Tree - superfast, flexible, databound tree/list view

QuestionNicememberGlimmerMan2 Nov '11 - 10:45 
Actually do not have a need for this currently but decided to try this out in VS2008/VB.NET. Results, works great.
Kevin S. Gallagher
Programming is an art form that fights back

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 17 Oct 2007
Article Copyright 2007 by Grant Frisken
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid