.NET 字符串资源






4.85/5 (37投票s)
多语言应用程序中处理字符串的概念和模式。
![]() |
![]() |
引言
互联网和全球化导致同一个软件被用于不同的语言,并提供本地化版本。即使一个应用程序最初只面向一种语言,但从一开始就建议将依赖语言的元素与实际代码分离开来。在完成编程后再这样做是一项艰巨、耗时且容易出错的任务。
.NET 框架提供了 ResourceManager
作为处理外部化字符串及其翻译的基础。本文通过以下功能扩展了标准 .NET 字符串处理的能力:
- 字符串的受控访问
- 变量字符串元素的类型安全格式化
- 枚举的字符串(可选配图片)
- XAML 字符串
- ASP.NET 中的用户自定义字符串资源文件
推动本解决方案的一个重要目标是统一使用 .NET 运行时和开发环境中的工具进行:
- 库/DLL
- 控制台应用程序
- Windows Forms
- ASP.NET
- WPF
- Silverlight
- Windows Phone 7
以下章节讨论的 ResStrings
库提供了一种实用且轻量级的字符串资源处理解决方案。
字符串资源
字符串资源的处理通过 StringsResourceManager
进行,它通过几个方法扩展了其基类 ResourceManager
。除了 GetString
之外,StringsResourceManager
还提供了 Format
方法,该方法将资源内容解释为复合格式字符串,并将其应用于 String.Format
方法。
以下指示符标记了错误的字符串资源:
UnknownResFormat
:未知资源 - 默认=?name?MissingResFormat
:缺少资源内容 - 默认=*name*InvalidResFormat
:无效的资源内容(例如,用于Format
) - 默认=!name!
可以通过 UseStringIndicators
属性关闭这些指示符的使用。StringsResourceManager
在一个与多个资源文件关联的类中使用。
- MyStrings.cs - 字符串声明(包含
StringsResourceManager
)- MyStrings.resx - 默认语言的字符串
- MyStrings.local.resx - 本地化字符串(可选)
以下示例展示了 MyStrings.cs 类提供的一些字符串。
// ------------------------------------------------------------------------
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
该类被声明为静态的,因此 UI 设计师可以在开发时访问提供的字符串。除了纯资源库之外,建议仅在 Assembly
内部使用此类,因此将其声明为 internal
。从其他库回收字符串可能导致依赖关系混乱。
每个字符串都表示为一个属性或方法,从而确保在编译时不存在无效的字符串引用。方法参数进一步确保了所需格式化参数的正确数量和类型。
下一个任务是创建带有默认语言文本的资源文件 MyStrings.resx。
对于每种其他语言,都会创建一个新的资源文件,如下面的德语语言 MyStrings.de.resx 示例所示。
在代码中使用字符串非常简单透明。
Console.WriteLine( MyStrings.ApplicationInfo );
Console.WriteLine( MyStrings.CurrentCultureDescription
( Thread.CurrentThread.CurrentUICulture ) );
枚举资源
为了本地化枚举,该库通过反射为枚举的每个值生成一个 EnumItem
。除了枚举值之外,每个 EnumItem
都包含一个文本描述,并具有以下特殊行为:
ToString
显示文本描述。Equals
只比较枚举值,忽略文本描述。
枚举的所有值都可以在 EnumItemCollection
中访问。通过实现 IEnumerable
,它可以用于集合绑定。StringsResourceManager
通过其 GetEnumItems
方法对枚举提供特殊支持,该方法创建一个新的 EnumItemCollection
实例。
假设有以下枚举:
// ------------------------------------------------------------------------
public enum Status
{
Ok,
Info,
Warning,
Error,
} // enum Status
将此枚举集成到字符串类中的外观如下:
// ------------------------------------------------------------------------
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
StringsResourceManager
从资源文件中加载每个枚举值的字符串,命名模式为 EnumType.EnumValue
。
如果资源文件中没有包含枚举值的文本,则使用其值。以下示例说明了 Status
枚举的使用。
// ------------------------------------------------------------------------
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
为了公共访问库的枚举,建议声明一个实用程序类,该类将 EnumItemCollection
作为属性提供。
// ------------------------------------------------------------------------
public static class StatusHelper
{
// ----------------------------------------------------------------------
public static EnumItemCollection<Status> StatusItems
{
get { return MyStrings.StatusItems; }
} // StatusItems
} // class StatusHelper
带图片的枚举资源
对于 WPF、Silverlight 和 Windows Phone,枚举可以扩展图片。EnumImageItem
类此外还包含一个图片,该图片使用以下命名约定从嵌入式资源加载:
EnumNamespace\{ImagesPath}\EnumType.EnumValue.{ImageExtension}
ImagesPath
(默认=Images)和 ImageExtension
(默认=.png)的值可以在 StringsResourceManager
中自定义。
与 EnumItem
/EnumItemCollection
类似,所有 EnumImageItems
都可以在 EnumImageItemCollection
中访问。StringsResourceManager
提供的 GetEnumImageItems
方法可以生成这样的集合。
MVVM 枚举
要在 MVVM 应用程序中使用枚举的字符串,需要在视图模型中集成 EnumItem
或 EnumImageItem
。
// ------------------------------------------------------------------------
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
在 XAML 视图中,可以使用绑定到 Description
属性来显示枚举值的文本描述。EnumImageItem
枚举值的图片可以通过绑定到 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 和 Windows Phone
Silverlight 和 Windows Phone 不提供从 XAML 内部访问 static
对象的可能性(没有 x:Static
)。作为一种变通方法,可以使用 ResourceDictionary
中的 static
资源。StringsResourceDictionary
是一个专门的 ResourceDictionary
,它可以自动加载字符串资源并提供给 XAML 元素使用。为此需要以下文件:
- MyDictionary.cs - 派生自
StringsResourceDictionary
- MyDictionary.resx - 默认语言的字符串
- MyDictionary.local.resx - 本地化字符串(可选)
字符串字典的声明外观如下:
// ------------------------------------------------------------------------
public class MyDictionary : StringsResourceDictionary
{
} // class MyDictionary
由于字符串直接从相应的资源文件(.resx)访问,因此不需要属性或方法。
要在 XAML 中使用 StringsResourceDictionary
,必须将其声明为主元素的资源。通过 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>
要一起使用多个 StringsResourceDictionary
,可以为其提供一个上下文。
// ------------------------------------------------------------------------
public class MyAppDictionary : StringsResourceDictionary
{
// ----------------------------------------------------------------------
public MyAppDictionary() :
base( "AppStrings" )
{
} // MyAppDictionary
} // class MyAppDictionary
使用 MergedDictionary
,可以将多个 StringsResourceDictionaries
声明为 XAML 元素的资源。通过加上字典上下文的前缀,可以引用特定资源字典中的资源。
<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
在本地化 WPF 时,字符串资源可以作为 static
值集成。
<TextBlock
Text="{x:Static app:MyStrings.MediaDurationLabel}" />
<TextBlock
Text="{Binding MediaDuration,
StringFormat={x:Static app:MyStrings.DurationFormatString}}" />
为了减少字符串类的管理开销,可以像 Silverlight/Windows Phone 一样使用 StringsResourceDictionary
。但这也会牺牲在设计时对无效字符串引用的控制。
复合 WPF
在某些场景下,希望在 WPF 和 Silverlight/Windows Phone 之间共享字符串资源。如果 WPF 不需要类型安全,则可以在所有系统中使用 StringsResourceDictionary
。
如果 WPF 字符串通过 StringsResourceManager
处理,则可以使用以下模式将其传输到 Silverlight/Windows Phone StringsResourceDictionary
:
// ------------------------------------------------------------------------
public class MyDictionary : StringsResourceDictionary
{
// ----------------------------------------------------------------------
public class MyDictionary :
base( typeof( MyStrings )
{
} // MyDictionary
} // class MyDictionary
ASP.NET
ASP.NET 提供了通过 App_GlobalResources
和 App_LocalResources
使用字符串资源的可能性。但这些机制缺乏对以下功能的 S支持:
- 动态组合的字符串
- 枚举的字符串
- 来自库的字符串
使用 StringExpressionBuilder
,可以通过表达式绑定在 Web 控件中使用 StringsResourceManager
中的字符串。
<asp:Label ID="AppTextLabel" runat="server"
Text="<%$ Strings:MyCompany.MyApp.MyStrings, AppText %>" />
<asp:Label ID="LibTextLabel" runat="server"
Text="<%$ Strings:MyCompany.MyLib.MyStrings, LibText %>" />
绑定表达式支持以下语法:
<%$ Strings:{TypeName}, {StringName} %>
由 TypeName
引用的类型必须声明为 public
,并且可以是以下类型之一:
- 带字符串的静态类:
StringName
代表该类的public
和static
方法。 - 派生自
StringsResourceManager
:StringName
代表资源的名称。
对于源派生自 StringsResourceManager
的情况,需要以下文件:
- MyStrings.cs - 派生自
StringsResourceManager
- MyStrings.resx - 默认语言的字符串
- MyStrings.local.resx - 本地化字符串(可选)
由于直接访问字符串资源,因此不需要声明属性,以下语法就足够了:
// ------------------------------------------------------------------------
public class MyStrings : StringsResourceManager
{
} // class MyStrings
基于 StringExpressionEditor
,字符串甚至在设计模式下可用。绑定是实时计算的,这会立即显示无效绑定。
StringExpressionBuilder
的声明位于 Web 应用程序的配置 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
到目前为止介绍的概念可以在 Windows Forms 应用程序中不受限制地使用。Windows Forms 字符串资源的管理最好通过其集成工具进行,这些工具也支持在设计模式下的正确显示。
库
ResStrings
库有以下版本:
项目 | 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 |
每个版本都附带一个示例应用程序。ResStrings
CLR 2.0 库的用法在一个 Windows Forms 项目中得到演示。
有关为不同平台开发库的更多信息,请参阅文章 Time Period Library for .NET 中的“复合库开发”一章。
有关 Microsoft 提供的本地化基础知识,请参见此处:
历史
- 2012 年 4 月 20 日 - v1.2.0.0
StringsExpressionParser
:解析线程安全化Localization
:用于复合本地化的新 ASP.NET 控件LocalizationDesigner
:Localization
的控件设计器- 关于如何使用新的
Localization
控件本地化 ASP.NET 日历的新示例 - 2011 年 9 月 13 日 - v1.1.0.0
StringsResourceManager
:添加了受保护的默认构造函数StringsExpressionParser
:添加了对StringsResourceManager
作为源类型的支持- 2011 年 9 月 11 日 - v1.0.0.0
- 首次公开发布