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

.NET String Resources

, 19 Apr 2012
Rate this:
Please Sign up or sign in to vote.
Concepts and patterns for the handling of strings in multilingual applications.
.NET String Resources - Desktop Demo Application .NET String Resources - Windows Phone Demo Application

Introduction

Internet and globalization have led to the same software being used in different languages, the availability of localized versions. Even if an application is initially only targeting a single language, it is advisable to keep the language dependent elements separate from the actual code, right from the start. Doing so after finishing with the programming is a daunting, time intensive and error prone task.

The .NET framework offers the ResourceManager as a basis for dealing with externalized strings and their translations. This article extends these capabilities of standard .NET string handling by the following functionality:

  • Controlled access to strings
  • Type safe formatting of variable string elements
  • Strings for enumerations (optionally with image)
  • XAML strings
  • User defined string resource files in ASP.NET

An important goal which leads to the presented solution was the uniform usage of the tools in the .NET run time and development environments for:

  • Library/DLL
  • Console application
  • Windows Forms
  • ASP.NET
  • WPF
  • Silverlight
  • Windows Phone 7

The ResStrings library discussed in the following chapters presents a pragmatic and lightweight solution for the handling of string resources.

String Resources

The handling of string resources happens through the StringsResourceManager, which extends its base class ResourceManager with several methods. In addition to GetString the StringsResourceManager offers the method Format, which interprets the content of the resource as a composite format string and applies it to the method String.Format.

The following indicators mark erroneous string resources:

  • UnknownResFormat: Unknown resource - Default=?name?
  • MissingResFormat: Missing resource content - Default=*name*
  • InvalidResFormat: Invalid resource content e.g. for Format - Default=!name!

The usage of these indicators can be turned off with the property UseStringIndicators. The StringsResourceManager is used in a class which is associated with several resource files:

  • MyStrings.cs - declaration of the strings (contains the StringsResourceManager)
    • MyStrings.resx - strings in the default language
    • MyStrings.local.resx - localized strings (optional)

The following example shows the class MyStrings.cs offering some strings:

// ------------------------------------------------------------------------
internal static class MyStrings
{

  // ----------------------------------------------------------------------
  public static string ApplicationInfo
  {
    get { return srm.Format
	( "ApplicationInfo", VersionTool.VersionOf( typeof( MyStrings ) ) ); }
  } // ApplicationInfo

  // ----------------------------------------------------------------------
  public static string CurrentCultureDescription( CultureInfo culture )
  {
    return srm.Format( "CurrentCultureDescription", culture.DisplayName );
  } // CurrentCultureDescription

  // ----------------------------------------------------------------------
  // members
  private static readonly StringsResourceManager srm =
			new StringsResourceManager( typeof( MyStrings ) );

} // class MyStrings

The class is being declared statically so UI-Designers can access the provided strings at development time. With the exception of pure resource libraries, it is advisable to only use this class from within the Assembly, hence it is declared internal. The recycling of strings from other libraries can lead to a dependency nightmare.

Each string is represented as a property or a method and thus ensures that no invalid string references exist at compile time. The method parameters further ensure the correct number and type of arguments for the required formatting parameters.

The next task is the creation of the resource file MyStrings.resx with texts in the default language:

Default language Resources

For each additional language another resource file is created, as the following example for the German language MyStrings.de.resx illustrates:

German language Resources

The usage of the strings in the code is simple and transparent:

Console.WriteLine( MyStrings.ApplicationInfo );
Console.WriteLine( MyStrings.CurrentCultureDescription
		( Thread.CurrentThread.CurrentUICulture ) );

Enumeration Resources

For the localization of enumerations, the library generates an EnumItem via reflection for each of its values. In addition to the enumeration value, each of these EnumItems contains a textual description and has the following special behavior:

  • ToString displays the textual description
  • Equals only compares the enumeration value and ignores the textual description

All values of an enumeration can be accessed in an EnumItemCollection. By implementing IEnumerable it can be used for collection bindings. The StringsResourceManager has special support for enumerations through its method GetEnumItems which creates a new instance of an EnumItemCollection.

Let's assume the following enumeration:

// ------------------------------------------------------------------------
public enum Status
{
  Ok,
  Info,
  Warning,
  Error,
} // enum Status

The integration of this enumeration into the strings class looks as follows:

// ------------------------------------------------------------------------
internal static class MyStrings
{

  // ----------------------------------------------------------------------
  public static EnumItemCollection<Status> StatusItems
  {
    get { return statusItems ?? ( statusItems = srm.GetEnumItems<Status>() ); }
  } // StatusItems

  // ----------------------------------------------------------------------
  // members
  private static EnumItemCollection<Status> statusItems;

  private static readonly StringsResourceManager srm =
			new StringsResourceManager( typeof( MyStrings ) );

} // class MyStrings

The StringsResourceManager loads a string from the resource file for each of the enumeration's values, named according to the pattern EnumType.EnumValue:

Enum item Resources

In case the resource file doesn't contain a text for an enumeration value, its value is used. The following example illustrates the usage of the Status enumeration:

// ------------------------------------------------------------------------
public class StatusDemo
{

  // ----------------------------------------------------------------------
  public StatusDemo( Status status = Status.Ok )
  {
    Status = StatusItems[ status ];
  } // StatusDemo

  // ----------------------------------------------------------------------
  public IEnumerable StatusItems
  {
    get { return MyStrings.StatusItems; }
  } // StatusItems

  // ----------------------------------------------------------------------
  public EnumItem<Status> Status { get; set; }

} // class StatusDemo

For public access to enumerations of a library, it is advisable to declare a utility class which makes the EnumItemCollection available as a property:

// ------------------------------------------------------------------------
public static class StatusHelper
{

  // ----------------------------------------------------------------------
  public static EnumItemCollection<Status> StatusItems
  {
    get { return MyStrings.StatusItems; }
  } // StatusItems

} // class StatusHelper

Enumeration Resources with Images

For WPF, Silverlight and Windows Phone enumerations can be extended with images. The class EnumImageItem additionally contains an image which is loaded from an embedded resource using the following naming convention:

EnumNamespace\{ImagesPath}\EnumType.EnumValue.{ImageExtension}

The values for ImagesPath (Default=Images) and ImageExtension (Default=.png) can be customized in the StringsResourceManager.

Enum item Images

Analogous to EnumItem/EnumItemCollection, all EnumImageItems can be accessed in an EnumImageItemCollection. The method GetEnumImageItems offered by StringsResourceManager generates such a collection.

MVVM Enumerations

To use the strings of enumerations in an MVVM application, it is necessary to integrate the EnumItem respectively the EnumImageItem into the view model:

// ------------------------------------------------------------------------
public class MyViewModel : DependencyObject
{

  // ----------------------------------------------------------------------
  public static readonly DependencyProperty StatusProperty = DependencyProperty.Register(
    "Status",
    typeof( EnumImageItem<Status> ),
    typeof( MyViewModel ),
    null );

  // ----------------------------------------------------------------------
  public IEnumerable StatusItems
  {
    get { return StatusHelper.StatusImageItems; }
  } // StatusItems

  // ----------------------------------------------------------------------
  public EnumImageItem<Status> Status
  {
    get { return (EnumImageItem<Status>)GetValue( StatusProperty ); }
    set { SetValue( StatusProperty, value ); }
  } // Status

} // class MyViewModel

In the XAML view, the textual description of an enumeration value can be displayed using a binding to the property Description. Images from EnumImageItem enumeration values can be used by binding to the property Image:

<ListBox
  ItemsSource="{Binding StatusItems}"
  SelectedItem="{Binding Status, Mode=TwoWay}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <StackPanel
        Orientation="Horizontal">
        <Image
          Source="{Binding Image.Source}" />
        <TextBlock
          Text="{Binding Description}" />
      </StackPanel>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

Silverlight and Windows Phone

Silverlight and Windows Phone do not offer the possibility to access static objects from within XAML (no x:Static). As a workaround, static resources can be used from a ResourceDictionary. The StringsResourceDictionary is a specialized ResourceDictionary, which automatically loads the string resources and provides them for use in XAML elements. The following files are required for this:

  • MyDictionary.cs - derivation of StringsResourceDictionary
    • MyDictionary.resx - strings in the default language
    • MyDictionary.local.resx - localized strings (optional)

The declaration of the string dictionary looks as follows:

// ------------------------------------------------------------------------
public class MyDictionary : StringsResourceDictionary
{
} // class MyDictionary

Because the strings are directly accessed from the corresponding resource file (.resx), no properties or methods are necessary.

To use the StringsResourceDictionary in XAML, it must be declared as a resource of the main element. Accessing the strings happens via StaticResource:

<Page ...>

  <Page.Resources>
    <app:MyDictionary />
  </Page.Resources>

  <Grid>
    <StackPanel
       Orientation="Horizontal"
      <TextBlock
        Text="{StaticResource MediaDurationLabel}" />
      <TextBlock
        Text="{Binding MediaDuration,
		StringFormat={StaticResource DurationFormatString}}" />
     </StackPanel>
  </Grid>
</Page>

To use several StringsResourceDictionaries together in one XAML, they can be provided with a context:

// ------------------------------------------------------------------------
public class MyAppDictionary : StringsResourceDictionary
{

  // ----------------------------------------------------------------------
  public MyAppDictionary() :
    base( "AppStrings" )
  {
  } // MyAppDictionary

} // class MyAppDictionary

Using a MergedDictionary, several StringsResourceDictionaries can be declared as resources of the XAML element. By prepending the dictionary context, it is possible to reference a resource from a specific resource dictionary:

<Page ...>

  <Page.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <app:MyDictionary />
        <app:MyAppDictionary />
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Page.Resources>

  <Grid>
    <StackPanel
       Orientation="Horizontal"
      <TextBlock
        Text="{StaticResource AppStrings.ApplicationInfoLabel}" />
      <TextBlock
        Text="{StaticResource AppStrings.ApplicationInfo}" />
     </StackPanel>
  </Grid>
</Page>

WPF

When localizing WPF, string resources can be integrated as static values:

<TextBlock
  Text="{x:Static app:MyStrings.MediaDurationLabel}" />
<TextBlock
  Text="{Binding MediaDuration,
	StringFormat={x:Static app:MyStrings.DurationFormatString}}" />

To reduce the management overhead of a strings class, it is possible to use a StringsResourceDictionary analogous to Silverlight/Windows Phone. But this also sacrifices control over invalid string references at design time.

Composite WPF

In certain scenarios, it is desirable to share string resources between WPF and Silverlight/Windows Phone. If no type safety is required for WPF, it is possible to use the StringsResourceDictionary for all systems.

If WPF strings are handled via StringsResourceManager, they can be transferred to the Silverlight/Windows Phone StringsResourceDictionary with the following pattern:

// ------------------------------------------------------------------------
public class MyDictionary : StringsResourceDictionary
{
  // ----------------------------------------------------------------------
  public class MyDictionary :
    base( typeof( MyStrings )
  {
  } // MyDictionary

} // class MyDictionary

ASP.NET

ASP.NET offers the possibility to use string resources through App_GlobalResources and App_LocalResources. But these mechanisms lack support for the following functionality:

  • Dynamically composed strings
  • Strings for enumerations
  • Strings from libraries

Using the StringExpressionBuilder, it is possible to use strings from the StringsResourceManager in a web control via expression binding:

<asp:Label ID="AppTextLabel" runat="server"
	Text="<%$ Strings:MyCompany.MyApp.MyStrings, AppText %>" />
<asp:Label ID="LibTextLabel" runat="server"
	Text="<%$ Strings:MyCompany.MyLib.MyStrings, LibText %>" />

The binding expression supports the following syntax:

<%$ Strings:{TypeName}, {StringName} %>

The type referenced by TypeName has to be declared public and can be one of the following:

  • Static class with strings: StringName represents a public and static method of that class
  • Derivation of StringsResourceManager: StringName represents the name of the resource

In case of a source which derives from StringsResourceManager, the following files are required:

  • MyStrings.cs - derivation of StringsResourceManager
    • MyStrings.resx - strings in the default language
    • MyStrings.local.resx - localized strings (optional)

Because access to the string resources happens directly, no declaration of properties is required and the following syntax is sufficient:

// ------------------------------------------------------------------------
public class MyStrings : StringsResourceManager
{
} // class MyStrings

Based on the StringExpressionEditor, the strings are even available in design mode. The binding is calculated live, which immediately reveals an invalid binding:

Web Design Strings

The declaration of the StringExpressionBuilder is found in the configuration of the web application web.config:

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0">
      <expressionBuilders>
        <add expressionPrefix="Strings"
          type="Itenso.Community.StringResources.ResStrings.StringsExpressionBuilder,
          Itenso.Community.StringResources.ResStrings"/>
      </expressionBuilders>
    </compilation>
  </system.web>
</configuration>

Windows Forms

The concepts presented so far can be used without restrictions in a Windows Forms application. The management of Windows Forms string resources preferably happens through its integrated tools which also support the proper display in design mode.

Library

The ResStrings library is available in the following versions:

Project Content CLR
ResStrings StringsResourceManager
EnumItem
2.0
ResStrings.Web StringsResourceManager
StringExpressionBuilder
EnumItem
2.0 Web
ResStrings.Desktop StringsResourceManager
StringsResourceDictionary
EnumItem
EnumImageItem
4.0 WPF
ResStrings.Silverlight StringsResourceManager
StringsResourceDictionary
EnumItem
EnumImageItem
SL 4
ResStrings.WindowsPhone StringsResourceManager
StringsResourceDictionary
EnumItem
EnumImageItem
SL 4 WP

Each version is accompanied by a sample application. Usage of the ResStrings CLR 2.0 library is demonstrated in a Windows Forms project.

Further information about developing libraries for different platforms can be found in the article Time Period Library for .NET in the chapter Composite Library Development.

The basics concerning localization offered by Microsoft can be found here:

History

  • 20th April, 2012 - v1.2.0.0
    • StringsExpressionParser: Made parsing thread safe
    • Localization: New ASP.NET control for composite localization
    • LocalizationDesigner: Control designer for Localization
    • New example on how to localize the ASP.NET calendar using the new Localization control
  • 13th September, 2011 - v1.1.0.0
    • StringsResourceManager: Added protected default constructor
    • StringsExpressionParser: Added support for StringsResourceManager as source type
  • 11th September, 2011 - v1.0.0.0
    • Initial public release

License

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

About the Author

Jani Giannoudis
Software Developer (Senior)
Switzerland Switzerland
Jani is Co-founder of Meerazo.com, a free service which allows to share resources like locations, things, persons and their services in a cooperating group of people.

Comments and Discussions

 
QuestionAn alternative approach for enumerations PinmemberGrant Frisken1-May-12 12:49 
I might be missing something but it seems like you have to do quite a lot of hand coding of the strings wrapper classes using this approach. I use a different approach (see Localizing .NET Enums[^]) for enums which doesn't require any hand coding (other than adding a TypeConverter attribute to your enums). It also allows you to use the standard ResourceManager with strongly typed wrapper classes auto generated by the standard ResXFileCodeGenerator.
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

AnswerRe: An alternative approach for enumerations PinmvpJani Giannoudis1-May-12 20:47 
GeneralRe: An alternative approach for enumerations PinmemberGrant Frisken1-May-12 23:01 
GeneralRe: An alternative approach for enumerations PinmvpJani Giannoudis1-May-12 23:31 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140718.1 | Last Updated 20 Apr 2012
Article Copyright 2011 by Jani Giannoudis
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid