65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET 字符串资源

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (37投票s)

2011 年 9 月 11 日

CPOL

7分钟阅读

viewsIcon

154105

downloadIcon

2255

多语言应用程序中处理字符串的概念和模式。

.NET String Resources - Desktop Demo Application .NET String Resources - Windows Phone Demo Application

引言

互联网和全球化导致同一个软件被用于不同的语言,并提供本地化版本。即使一个应用程序最初只面向一种语言,但从一开始就建议将依赖语言的元素与实际代码分离开来。在完成编程后再这样做是一项艰巨、耗时且容易出错的任务。

.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

Default language Resources

对于每种其他语言,都会创建一个新的资源文件,如下面的德语语言 MyStrings.de.resx 示例所示。

German language Resources

在代码中使用字符串非常简单透明。

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

Enum item Resources

如果资源文件中没有包含枚举值的文本,则使用其值。以下示例说明了 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 中自定义。

Enum item Images

EnumItem/EnumItemCollection 类似,所有 EnumImageItems 都可以在 EnumImageItemCollection 中访问。StringsResourceManager 提供的 GetEnumImageItems 方法可以生成这样的集合。

MVVM 枚举

要在 MVVM 应用程序中使用枚举的字符串,需要在视图模型中集成 EnumItemEnumImageItem

// ------------------------------------------------------------------------
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_GlobalResourcesApp_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 代表该类的 publicstatic 方法。
  • 派生自 StringsResourceManagerStringName 代表资源的名称。

对于源派生自 StringsResourceManager 的情况,需要以下文件:

  • MyStrings.cs - 派生自 StringsResourceManager
    • MyStrings.resx - 默认语言的字符串
    • MyStrings.local.resx - 本地化字符串(可选)

由于直接访问字符串资源,因此不需要声明属性,以下语法就足够了:

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

基于 StringExpressionEditor,字符串甚至在设计模式下可用。绑定是实时计算的,这会立即显示无效绑定。

Web Design Strings

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 控件
    • LocalizationDesignerLocalization 的控件设计器
    • 关于如何使用新的 Localization 控件本地化 ASP.NET 日历的新示例
  • 2011 年 9 月 13 日 - v1.1.0.0
    • StringsResourceManager:添加了受保护的默认构造函数
    • StringsExpressionParser:添加了对 StringsResourceManager 作为源类型的支持
  • 2011 年 9 月 11 日 - v1.0.0.0
    • 首次公开发布
© . All rights reserved.