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 ) ) ); }
}
public static string CurrentCultureDescription( CultureInfo culture )
{
return srm.Format( "CurrentCultureDescription", culture.DisplayName );
}
private static readonly StringsResourceManager srm =
new StringsResourceManager( typeof( 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:
For each additional language another resource file is created, as the following example for the German language MyStrings.de.resx illustrates:
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 EnumItem
s contains a textual description and has the following special behavior:
ToString
displays the textual descriptionEquals
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,
}
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>() ); }
}
private static EnumItemCollection<Status> statusItems;
private static readonly StringsResourceManager srm =
new StringsResourceManager( typeof( MyStrings ) );
}
The StringsResourceManager
loads a string from the resource file for each of the enumeration's values, named according to the pattern EnumType.EnumValue
:
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 ];
}
public IEnumerable StatusItems
{
get { return MyStrings.StatusItems; }
}
public EnumItem<Status> Status { get; set; }
}
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; }
}
}
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
.
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; }
}
public EnumImageItem<Status> Status
{
get { return (EnumImageItem<Status>)GetValue( StatusProperty ); }
set { SetValue( StatusProperty, value ); }
}
}
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
{
}
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" )
{
}
}
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 )
{
}
}
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
{
}
Based on the StringExpressionEditor
, the strings are even available in design mode. The binding is calculated live, which immediately reveals an invalid binding:
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 safeLocalization
: New ASP.NET control for composite localizationLocalizationDesigner
: 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 constructorStringsExpressionParser
: Added support for StringsResourceManager
as source type
- 11th September, 2011 - v1.0.0.0