在 .NET 应用中使用本地化资源时,最好使用主程序集打包默认或中性区域性的资源,并为应用支持的每种语言或区域性创建单独的附属程序集。 然后,可以使用下一节中所述的 ResourceManager 类来访问命名资源。 如果选择不将资源嵌入主程序集和附属程序集中,也可以直接访问二进制 .resources 文件,如本文后面部分的 “从 .resources 文件检索资源 ”部分中所述。
从程序集中检索资源
该 ResourceManager 类在运行时提供对资源的访问。 使用ResourceManager.GetString方法检索字符串资源,使用ResourceManager.GetObject或ResourceManager.GetStream方法检索非字符串资源。 每个方法都有两种重载:
一个重载,其单个参数是包含资源名称的字符串。 该方法尝试检索当前文化的资源。 有关详细信息,请参阅GetString(String)、GetObject(String)和GetStream(String)方法。
具有两个参数的重载:一个字符串包含资源名称,一个 CultureInfo 对象表示要对其检索资源的区域性。 如果找不到该区域性的资源集,资源管理器将使用回退规则检索相应的资源。 有关详细信息,请参阅GetString(String, CultureInfo)、GetObject(String, CultureInfo)和GetStream(String, CultureInfo)方法。
资源管理器使用资源回退进程控制应用检索区域性特定资源的方式。 有关详细信息,请参阅 包和部署资源的“资源回退过程”部分。 有关实例化 ResourceManager 对象的信息,请参阅类主题中的 ResourceManager “实例化 ResourceManager 对象”部分。
检索字符串数据示例
以下示例调用 GetString(String) 以检索当前 UI 文化的字符串资源。 它包括适用于英语(美国)文化的中性字符串资源,以及法语(法国)和俄语(俄罗斯)文化的本地化资源。 以下英语(美国)资源位于名为 Strings.txt的文件中:
TimeHeader=The current time is
法语(法国)资源位于名为 Strings.fr-FR.txt的文件中:
TimeHeader=L'heure actuelle est
俄罗斯(俄罗斯)资源位于名为 Strings.ru-RU.txt的文件中:
TimeHeader=Текущее время —
此示例的源代码(位于名为 C# 版本的代码GetString.cs的文件中,Visual Basic 版本的GetString.vb)定义了一个字符串数组,其中包含四种区域性的名称:资源可用的三种区域性和西班牙语(西班牙)区域性。 循环执行五次,随机选择其中一种文化,并将其分配给 Thread.CurrentCulture 和 CultureInfo.CurrentUICulture 属性。 然后,它会调用 GetString(String) 该方法来检索本地化字符串,该字符串与一天中的时间一起显示。
using System;
using System.Globalization;
using System.Resources;
using System.Threading;
[assembly: NeutralResourcesLanguageAttribute("en-US")]
public class Example
{
public static void Main()
{
string[] cultureNames = { "en-US", "fr-FR", "ru-RU", "es-ES" };
Random rnd = new Random();
ResourceManager rm = new ResourceManager("Strings",
typeof(Example).Assembly);
for (int ctr = 0; ctr <= cultureNames.Length; ctr++) {
string cultureName = cultureNames[rnd.Next(0, cultureNames.Length)];
CultureInfo culture = CultureInfo.CreateSpecificCulture(cultureName);
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
Console.WriteLine($"Current culture: {culture.NativeName}");
string timeString = rm.GetString("TimeHeader");
Console.WriteLine($"{timeString} {DateTime.Now:T}\n");
}
}
}
// The example displays output like the following:
// Current culture: English (United States)
// The current time is 9:34:18 AM
//
// Current culture: Español (España, alfabetización internacional)
// The current time is 9:34:18
//
// Current culture: русский (Россия)
// Текущее время — 9:34:18
//
// Current culture: français (France)
// L'heure actuelle est 09:34:18
//
// Current culture: русский (Россия)
// Текущее время — 9:34:18
Imports System.Globalization
Imports System.Resources
Imports System.Threading
<Assembly: NeutralResourcesLanguageAttribute("en-US")>
Module Example
Public Sub Main()
Dim cultureNames() As String = {"en-US", "fr-FR", "ru-RU", "es-ES"}
Dim rnd As New Random()
Dim rm As New ResourceManager("Strings", GetType(Example).Assembly)
For ctr As Integer = 0 To cultureNames.Length
Dim cultureName As String = cultureNames(rnd.Next(0, cultureNames.Length))
Dim culture As CultureInfo = CultureInfo.CreateSpecificCulture(cultureName)
Thread.CurrentThread.CurrentCulture = culture
Thread.CurrentThread.CurrentUICulture = culture
Console.WriteLine("Current culture: {0}", culture.NativeName)
Dim timeString As String = rm.GetString("TimeHeader")
Console.WriteLine("{0} {1:T}", timeString, Date.Now)
Console.WriteLine()
Next
End Sub
End Module
' The example displays output similar to the following:
' Current culture: English (United States)
' The current time is 9:34:18 AM
'
' Current culture: Español (España, alfabetización internacional)
' The current time is 9:34:18
'
' Current culture: русский (Россия)
' Текущее время — 9:34:18
'
' Current culture: français (France)
' L'heure actuelle est 09:34:18
'
' Current culture: русский (Россия)
' Текущее время — 9:34:18
以下批处理文件(.bat)编译示例,并在相应的目录中生成卫星程序集。 为 C# 语言和编译器提供命令。 对于 Visual Basic,请将csc
更改为vbc
,并将GetString.cs
更改为GetString.vb
。
resgen strings.txt
csc GetString.cs -resource:strings.resources
resgen strings.fr-FR.txt
md fr-FR
al -embed:strings.fr-FR.resources -culture:fr-FR -out:fr-FR\GetString.resources.dll
resgen strings.ru-RU.txt
md ru-RU
al -embed:strings.ru-RU.resources -culture:ru-RU -out:ru-RU\GetString.resources.dll
当当前 UI 区域性为西班牙语(西班牙)时,请注意,该示例显示英语资源,因为西班牙语资源不可用,而英语是示例的默认区域性。
检索对象数据示例
可以使用 GetObject 和 GetStream 方法检索对象数据。 这包括基元数据类型、可序列化对象和以二进制格式存储的对象(如图像)。
以下示例使用 GetStream(String) 方法检索在应用程序启动初始窗口中使用的位图。 名为 CreateResources.cs(for C#)或 CreateResources.vb(for Visual Basic)的文件中的以下源代码生成包含序列化图像的 .resx 文件。 在这种情况下,将从名为 SplashScreen.jpg; 的文件加载映像;可以修改文件名以替换自己的映像。
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Resources;
public class Example
{
public static void Main()
{
Bitmap bmp = new Bitmap(@".\SplashScreen.jpg");
MemoryStream imageStream = new MemoryStream();
bmp.Save(imageStream, ImageFormat.Jpeg);
ResXResourceWriter writer = new ResXResourceWriter("AppResources.resx");
writer.AddResource("SplashScreen", imageStream);
writer.Generate();
writer.Close();
}
}
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Resources
Module Example
Public Sub Main()
Dim bmp As New Bitmap(".\SplashScreen.jpg")
Dim imageStream As New MemoryStream()
bmp.Save(imageStream, ImageFormat.Jpeg)
Dim writer As New ResXResourceWriter("AppResources.resx")
writer.AddResource("SplashScreen", imageStream)
writer.Generate()
writer.Close()
End Sub
End Module
以下代码检索资源并在 PictureBox 控件中显示图像。
using System;
using System.Drawing;
using System.IO;
using System.Resources;
using System.Windows.Forms;
public class Example
{
public static void Main()
{
ResourceManager rm = new ResourceManager("AppResources", typeof(Example).Assembly);
Bitmap screen = (Bitmap) Image.FromStream(rm.GetStream("SplashScreen"));
Form frm = new Form();
frm.Size = new Size(300, 300);
PictureBox pic = new PictureBox();
pic.Bounds = frm.RestoreBounds;
pic.BorderStyle = BorderStyle.Fixed3D;
pic.Image = screen;
pic.SizeMode = PictureBoxSizeMode.StretchImage;
frm.Controls.Add(pic);
pic.Anchor = AnchorStyles.Top | AnchorStyles.Bottom |
AnchorStyles.Left | AnchorStyles.Right;
frm.ShowDialog();
}
}
Imports System.Drawing
Imports System.IO
Imports System.Resources
Imports System.Windows.Forms
Module Example
Public Sub Main()
Dim rm As New ResourceManager("AppResources", GetType(Example).Assembly)
Dim screen As Bitmap = CType(Image.FromStream(rm.GetStream("SplashScreen")), Bitmap)
Dim frm As New Form()
frm.Size = new Size(300, 300)
Dim pic As New PictureBox()
pic.Bounds = frm.RestoreBounds
pic.BorderStyle = BorderStyle.Fixed3D
pic.Image = screen
pic.SizeMode = PictureBoxSizeMode.StretchImage
frm.Controls.Add(pic)
pic.Anchor = AnchorStyles.Top Or AnchorStyles.Bottom Or
AnchorStyles.Left Or AnchorStyles.Right
frm.ShowDialog()
End Sub
End Module
可以使用以下批处理文件生成 C# 示例。 对于 Visual Basic,请将 csc
更改为 vbc
,并将源代码文件的扩展名从 .cs
更改为 .vb
。
csc CreateResources.cs
CreateResources
resgen AppResources.resx
csc GetStream.cs -resource:AppResources.resources
以下示例使用 ResourceManager.GetObject(String) 该方法反序列化自定义对象。 该示例包含一个名为 UIElements.cs(UIElements.vb for Visual Basic)的源代码文件,该文件定义了以下名为 <PersonTable
用 SerializableAttribute 属性标记。
using System;
[Serializable] public struct PersonTable
{
public readonly int nColumns;
public readonly string column1;
public readonly string column2;
public readonly string column3;
public readonly int width1;
public readonly int width2;
public readonly int width3;
public PersonTable(string column1, string column2, string column3,
int width1, int width2, int width3)
{
this.column1 = column1;
this.column2 = column2;
this.column3 = column3;
this.width1 = width1;
this.width2 = width2;
this.width3 = width3;
this.nColumns = typeof(PersonTable).GetFields().Length / 2;
}
}
<Serializable> Public Structure PersonTable
Public ReadOnly nColumns As Integer
Public Readonly column1 As String
Public ReadOnly column2 As String
Public ReadOnly column3 As String
Public ReadOnly width1 As Integer
Public ReadOnly width2 As Integer
Public ReadOnly width3 As Integer
Public Sub New(column1 As String, column2 As String, column3 As String,
width1 As Integer, width2 As Integer, width3 As Integer)
Me.column1 = column1
Me.column2 = column2
Me.column3 = column3
Me.width1 = width1
Me.width2 = width2
Me.width3 = width3
Me.nColumns = Me.GetType().GetFields().Count \ 2
End Sub
End Structure
以下名为 CreateResources.cs(CreateResources.vb for Visual Basic)的文件的代码创建一个名为 UIResources.resx 的 XML 资源文件,该文件存储表标题和一个 PersonTable
对象,其中包含为英语本地化的应用的信息。
using System;
using System.Resources;
public class CreateResource
{
public static void Main()
{
PersonTable table = new PersonTable("Name", "Employee Number",
"Age", 30, 18, 5);
ResXResourceWriter rr = new ResXResourceWriter(@".\UIResources.resx");
rr.AddResource("TableName", "Employees of Acme Corporation");
rr.AddResource("Employees", table);
rr.Generate();
rr.Close();
}
}
Imports System.Resources
Module CreateResource
Public Sub Main()
Dim table As New PersonTable("Name", "Employee Number", "Age", 30, 18, 5)
Dim rr As New ResXResourceWriter(".\UIResources.resx")
rr.AddResource("TableName", "Employees of Acme Corporation")
rr.AddResource("Employees", table)
rr.Generate()
rr.Close()
End Sub
End Module
然后,名为 GetObject.cs (GetObject.vb) 的源代码文件中的以下代码检索资源并将其显示在控制台。
using System;
using System.Resources;
[assembly: NeutralResourcesLanguageAttribute("en")]
public class Example
{
public static void Main()
{
string fmtString = String.Empty;
ResourceManager rm = new ResourceManager("UIResources", typeof(Example).Assembly);
string title = rm.GetString("TableName");
PersonTable tableInfo = (PersonTable) rm.GetObject("Employees");
if (! String.IsNullOrEmpty(title)) {
fmtString = "{0," + ((Console.WindowWidth + title.Length) / 2).ToString() + "}";
Console.WriteLine(fmtString, title);
Console.WriteLine();
}
for (int ctr = 1; ctr <= tableInfo.nColumns; ctr++) {
string columnName = "column" + ctr.ToString();
string widthName = "width" + ctr.ToString();
string value = tableInfo.GetType().GetField(columnName).GetValue(tableInfo).ToString();
int width = (int) tableInfo.GetType().GetField(widthName).GetValue(tableInfo);
fmtString = "{0,-" + width.ToString() + "}";
Console.Write(fmtString, value);
}
Console.WriteLine();
}
}
Imports System.Resources
<Assembly: NeutralResourcesLanguageAttribute("en")>
Module Example
Public Sub Main()
Dim fmtString As String = String.Empty
Dim rm As New ResourceManager("UIResources", GetType(Example).Assembly)
Dim title As String = rm.GetString("TableName")
Dim tableInfo As PersonTable = DirectCast(rm.GetObject("Employees"), PersonTable)
If Not String.IsNullOrEmpty(title) Then
fmtString = "{0," + ((Console.WindowWidth + title.Length) \ 2).ToString() + "}"
Console.WriteLine(fmtString, title)
Console.WriteLine()
End If
For ctr As Integer = 1 To tableInfo.nColumns
Dim columnName As String = "column" + ctr.ToString()
Dim widthName As String = "width" + ctr.ToString()
Dim value As String = CStr(tableInfo.GetType().GetField(columnName).GetValue(tableInfo))
Dim width As Integer = CInt(tableInfo.GetType().GetField(widthName).GetValue(tableInfo))
fmtString = "{0,-" + width.ToString() + "}"
Console.Write(fmtString, value)
Next
Console.WriteLine()
End Sub
End Module
可以通过执行以下批处理文件来生成必要的资源文件和程序集并运行应用。 必须使用此选项 /r
向 Resgen.exe 提供对 UIElements.dll 的引用,以便它可以访问有关 PersonTable
结构的信息。 如果使用 C#,请将 vbc
编译器名称 csc
替换为 ,并将 .vb
扩展替换为 .cs
。
vbc -t:library UIElements.vb
vbc CreateResources.vb -r:UIElements.dll
CreateResources
resgen UIResources.resx -r:UIElements.dll
vbc GetObject.vb -r:UIElements.dll -resource:UIResources.resources
GetObject.exe
附属程序集的版本支持
默认情况下,当对象检索请求的资源时 ResourceManager ,它会查找具有与主程序集版本号匹配的附属程序集。 部署应用后,可能需要更新主程序集或特定资源附属程序集。 .NET Framework 支持对主程序集和附属程序集进行版本控制。
该 SatelliteContractVersionAttribute 属性为主程序集提供版本控制支持。 在应用的主程序集上指定此属性可让你在不更新其附属程序集的情况下更新和重新部署主程序集。 更新主程序集后,递增主程序集的版本号,但保留附属协定版本号不变。 当资源管理器检索请求的资源时,它会加载由此属性指定的卫星程序集版本。
发布者策略程序集为版本化卫星程序集提供支持。 无需更新主程序集即可更新和重新部署附属程序集。 更新附属程序集后,递增其版本号,并将其附带到发行者策略程序集中。 在发行者策略程序集中,指定新附属程序集为后向兼容其之前版本。 资源管理器将使用属性 SatelliteContractVersionAttribute 来确定附属程序集的版本,但程序集加载程序将绑定到发布者策略指定的附属程序集版本。 有关发布者策略程序集的详细信息,请参阅 “创建发布者策略文件”。
若要启用完整的程序集版本控制支持,建议在 全局程序集缓存 中部署强名称程序集,并部署应用程序目录中没有强名称的程序集。 如果要在应用程序目录中部署强命名程序集,则在更新程序集时将无法增加卫星程序集的版本号。 相反,必须执行就地更新,在其中将现有代码替换为更新的代码并维护相同的版本号。 例如,若要使用完全指定的程序集名称 "myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a" 更新版本 1.0.0.0 的附属程序集,请使用已编译同一个完全指定的程序集名称 "myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a" 的更新的 myApp.resources.dll 来覆盖它。 请注意,对附属程序集文件使用就地更新使得应用难以准确确定附属程序集的版本。
有关程序集版本控制的详细信息,请参阅 程序集版本控制 以及 运行时如何查找程序集。
从 .resources 文件检索资源
如果选择不在卫星程序集部署资源,您仍然可以使用 ResourceManager 对象直接从 .resources 文件访问资源。 为此,必须正确部署 .resources 文件。 然后使用该方法 ResourceManager.CreateFileBasedResourceManager 实例化对象 ResourceManager 并指定包含独立 .resources 文件的目录。
部署 .resources 文件
在应用程序程序集和卫星程序集中嵌入 .resources 文件时,每个卫星程序集具有相同的文件名,但放置在反映该卫星程序集文化的子目录中。 相比之下,直接从 .resources 文件访问资源时,可以将所有 .resources 文件放在单个目录中,通常是应用程序目录的子目录。 应用程序默认的 .resources 文件名仅包含根名称,并未标明其文化特征(例如 strings.resources)。 每个本地化文化的资源存储在一个文件中,该文件的名称由根名称加上文化标识组成(例如 strings.ja.resources 或 strings.de-DE.resources)。
下图显示了资源文件应位于目录结构中的位置。 它还为 .resource 文件提供命名约定。
使用资源管理器
创建资源并将其放置在相应的目录中后,可以通过调用ResourceManager该方法创建一个CreateFileBasedResourceManager(String, String, Type)对象以使用资源。 第一个参数指定应用的默认 .resources 文件的根名称(这将是上一节中的示例的“字符串”。 第二个参数指定上一个示例的资源位置(“Resources”)。 第三个参数指定要使用的 ResourceSet 实现。 如果第三个参数为 null
,则使用默认运行时 ResourceSet 。
注释
不要使用独立 .resources 文件部署 ASP.NET 应用。 这可能会导致锁定问题并中断 XCOPY 部署。 建议部署附属程序集中的 ASP.NET 资源。 有关详细信息,请参阅 ASP.NET 网页资源概述。
实例化ResourceManager对象后,使用前面讨论的GetString和GetObjectGetStream方法检索资源。 但是,直接从 .resources 文件中检索资源不同于从程序集检索嵌入的资源。 从 .resources 文件检索资源时,无论当前文化如何,GetString(String)、GetObject(String) 和 GetStream(String) 方法始终用于检索默认文化的资源。 若要检索应用当前区域性或特定区域性的资源,必须调用GetString(String, CultureInfo)GetObject(String, CultureInfo)或GetStream(String, CultureInfo)方法并指定要检索其资源的区域性。 若要检索当前文化的资源,请将 CultureInfo.CurrentCulture 属性的值指定为 culture
参数。 如果资源管理器无法检索其 culture
资源,则它使用标准资源回退规则检索相应的资源。
示例
以下示例演示资源管理器如何直接从 .resources 文件检索资源。 该示例由英语(美国)、法语(法国)和俄罗斯(俄罗斯)文化的三个基于文本的资源文件组成。 英语(美国)是范例的默认文化设置。 其资源存储在名为 Strings.txt的以下文件中:
Greeting=Hello
Prompt=What is your name?
法语(法国)文化的资源存储在以下文件中,该文件命名为 Strings.fr-FR.txt:
Greeting=Bon jour
Prompt=Comment vous appelez-vous?
俄罗斯(俄罗斯)文化的资源存储在以下文件中,该文件命名为 Strings.ru-RU.txt:
Greeting=Здравствуйте
Prompt=Как вас зовут?
下面是示例的源代码。 该示例实例化 CultureInfo 英语(美国)、英语(加拿大)、法语(法国)和俄语(俄罗斯)文化的对象,并将每个对象设置为当前文化。 然后,该方法 ResourceManager.GetString(String, CultureInfo) 将 CultureInfo.CurrentCulture 属性的值作为 culture
参数,来检索适合的文化特定资源。
using System;
using System.Globalization;
using System.Resources;
using System.Threading;
[assembly: NeutralResourcesLanguage("en-US")]
public class Example
{
public static void Main()
{
string[] cultureNames = { "en-US", "en-CA", "ru-RU", "fr-FR" };
ResourceManager rm = ResourceManager.CreateFileBasedResourceManager("Strings", "Resources", null);
foreach (var cultureName in cultureNames) {
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName);
string greeting = rm.GetString("Greeting", CultureInfo.CurrentCulture);
Console.WriteLine($"\n{greeting}!");
Console.Write(rm.GetString("Prompt", CultureInfo.CurrentCulture));
string name = Console.ReadLine();
if (! String.IsNullOrEmpty(name))
Console.WriteLine("{0}, {1}!", greeting, name);
}
Console.WriteLine();
}
}
// The example displays output like the following:
// Hello!
// What is your name? Dakota
// Hello, Dakota!
//
// Hello!
// What is your name? Koani
// Hello, Koani!
//
// Здравствуйте!
// Как вас зовут?Samuel
// Здравствуйте, Samuel!
//
// Bon jour!
// Comment vous appelez-vous?Yiska
// Bon jour, Yiska!
Imports System.Globalization
Imports System.Resources
Imports System.Threading
<Assembly: NeutralResourcesLanguageAttribute("en-US")>
Module Example
Public Sub Main()
Dim cultureNames() As String = {"en-US", "en-CA", "ru-RU", "fr-FR"}
Dim rm As ResourceManager = ResourceManager.CreateFileBasedResourceManager("Strings", "Resources", Nothing)
For Each cultureName In cultureNames
Console.WriteLine()
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName)
Dim greeting As String = rm.GetString("Greeting", CultureInfo.CurrentCulture)
Console.WriteLine("{0}!", greeting)
Console.Write(rm.GetString("Prompt", CultureInfo.CurrentCulture))
Dim name As String = Console.ReadLine()
If Not String.IsNullOrEmpty(name) Then
Console.WriteLine("{0}, {1}!", greeting, name)
End If
Next
Console.WriteLine()
End Sub
End Module
' The example displays output like the following:
' Hello!
' What is your name? Dakota
' Hello, Dakota!
'
' Hello!
' What is your name? Koani
' Hello, Koani!
'
' Здравствуйте!
' Как вас зовут?Samuel
' Здравствуйте, Samuel!
'
' Bon jour!
' Comment vous appelez-vous?Yiska
' Bon jour, Yiska!
可以通过运行以下批处理文件来编译示例的 C# 版本。 如果使用的是 Visual Basic,请将 csc
替换为 vbc
,并将 .cs
扩展替换为 .vb
。
md Resources
resgen Strings.txt Resources\Strings.resources
resgen Strings.fr-FR.txt Resources\Strings.fr-FR.resources
resgen Strings.ru-RU.txt Resources\Strings.ru-RU.resources
csc Example.cs