检索桌面应用程序中的资源Retrieving Resources in Desktop Apps

使用 .NET Framework 桌面应用中的本地化资源时,最好用主程序集打包默认或非特定区域性的资源,并为应用支持的每种语言或区域性单独创建附属程序集。When you work with localized resources in .NET Framework desktop apps, you should ideally package the resources for the default or neutral culture with the main assembly and create a separate satellite assembly for each language or culture that your app supports. 可以使用下一节中介绍的 ResourceManager 类访问已命名的资源。You can then use the ResourceManager class as described in the next section to access named resources. 如果选择不在主程序集和附属程序集中嵌入资源,也可以按本文后面的从 .resources 文件中检索资源一节中所述直接访问二进制 .resources 文件。If you choose not to embed your resources in the main assembly and satellite assemblies, you can also access binary .resources files directly, as discussed in the section Retrieving Resources from .resources files later in this article. 若要检索 Windows 8.x 应用商店应用中的资源,请参阅在 Windows 应用商店应用中创建和检索资源To retrieve resources in Windows 8.x Store apps, see Creating and retrieving resources in Windows Store apps.

从程序集中检索资源Retrieving Resources from Assemblies

ResourceManager 类提供对运行时资源的访问权限。The ResourceManager class provides access to resources at run time. 使用 ResourceManager.GetString 方法检索字符串资源和 ResourceManager.GetObject 或使用 ResourceManager.GetStream 方法检索非字符串资源。You use the ResourceManager.GetString method to retrieve string resources and the ResourceManager.GetObject or ResourceManager.GetStream method to retrieve non-string resources. 每个方法都有两种重载:Each method has two overloads:

资源管理器使用资源回退进程控制应用检索区域性特定资源的方式。The resource manager uses the resource fallback process to control how the app retrieves culture-specific resources. 有关详细信息,请参阅打包和部署资源中的“资源回退进程”一节。For more information, see the "Resource Fallback Process" section in Packaging and Deploying Resources. 有关实例化 ResourceManager 对象的详细信息,请参阅 ResourceManager 类主题中的“实例化 ResourceManager 对象”一节。For information about instantiating a ResourceManager object, see the "Instantiating a ResourceManager Object" section in the ResourceManager class topic.

检索字符串数据:示例Retrieving String Data: An Example

下面的示例调用 GetString(String) 方法检索当前 UI 区域性的字符串资源。The following example calls the GetString(String) method to retrieve the string resources of the current UI culture. 它包括英语(美国)区域性的非特定字符串资源和法语(法国)和俄语(俄罗斯)区域性的本地化资源。It includes a neutral string resource for the English (United States) culture and localized resources for the French (France) and Russian (Russia) cultures. 下面的英语(美国)资源位于名为 Strings.txt 的文件中:The following English (United States) resource is in a file named Strings.txt:

TimeHeader=The current time is  

法语(法国)资源位于名为 Strings.fr-FR.txt 的文件中:The French (France) resource is in a file named Strings.fr-FR.txt:

TimeHeader=L'heure actuelle est  

俄语(俄罗斯)资源位于名为 Strings.ru-RU.txt 的文件中:The Russian (Russia) resource is in a file named Strings.ru-RU-txt:

TimeHeader=Текущее время —  

此示例的源代码(在代码的 C# 版本中位于名为 GetString.cs 的文件中,在 Visual Basic 版本中位于名为 GetString.vb 的文件中)定义包含四种区域性名称(资源可用的三种区域性和西班牙语(西班牙)区域性)的字符串数组。The source code for this example, which is in a file named GetString.cs for the C# version of the code and GetString.vb for the Visual Basic version, defines a string array that contains the name of four cultures: the three cultures for which resources are available and the Spanish (Spain) culture. 一个随机执行 5 次的循环选择其中一种区域性并将其分配到 Thread.CurrentCultureCultureInfo.CurrentUICulture 属性。A loop that executes five times randomly selects one of these cultures and assigns it to the Thread.CurrentCulture and CultureInfo.CurrentUICulture properties. 然后调用 GetString(String) 方法检索与一天当中的时间一起显示的本地化的字符串。It then calls the GetString(String) method to retrieve the localized string, which it displays along with the time of day.

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: {0}", culture.NativeName);
         string timeString = rm.GetString("TimeHeader");
         Console.WriteLine("{0} {1:T}\n", timeString, DateTime.Now);   
      }   
   }
}
// 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) 文件编译该示例,并在相应的目录中生成附属程序集。The following batch (.bat) file compiles the example and generates satellite assemblies in the appropriate directories. 为 C# 语言和编译器提供了命令。The commands are provided for the C# language and compiler. 对于 Visual Basic ,将 csc 更改为 vbc,并将 GetString.cs 更改为 GetString.vbFor Visual Basic, change csc to vbc, and change GetString.cs to 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 区域性为西班牙语(西班牙)时,请注意该示例会显示英语语言资源,因为西班牙语语言资源不可用,而英语是该示例的默认区域性。When the current UI culture is Spanish (Spain), note that the example displays English language resources, because Spanish language resources are unavailable, and English is the example's default culture.

检索对象数据:2 个示例Retrieving Object Data: Two Examples

可以使用 GetObjectGetStream 方法检索对象数据。You can use the GetObject and GetStream methods to retrieve object data. 这包括基元数据类型、可序列化对象和以二进制格式存储的对象(如图像)。This includes primitive data types, serializable objects, and objects that are stored in binary format (such as images).

下面的示例使用 GetStream(String) 方法检索应用启动初始窗口中使用的位图。The following example uses the GetStream(String) method to retrieve a bitmap that is used in an app's opening splash window. 以下源代码位于名为 CreateResources.cs 的文件中(C# 版本)或位于名为 CreateResources.vb 的文件中(Visual Basic 版本),它能生成包含序列化图像的 .resx 文件。The following source code in a file named CreateResources.cs (for C#) or CreateResources.vb (for Visual Basic) generates a .resx file that contains the serialized image. 在这种情况下,图片从一个名为 SplashScreen.jpg 的文件中加载;可以修改文件名以替换你自己的图像。In this case, the image is loaded from a file named SplashScreen.jpg; you can modify the file name to substitute your own image.

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 控件中显示图像。The following code retrieves the resource and displays the image in a PictureBox control.

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# 示例。You can use the following batch file to build the C# example. 对于 Visual Basic,将 csc 更改为 vbc,并将源代码文件的扩展名由 .cs 更改为 .vbFor Visual Basic, change csc to vbc, and change the extension of the source code file from .cs to .vb.

csc CreateResources.cs  
CreateResources  
  
resgen AppResources.resx  
  
csc GetStream.cs -resource:AppResources.resources  

下面的示例使用 ResourceManager.GetObject(String) 方法反序列化一个自定义对象。The following example uses the ResourceManager.GetObject(String) method to deserialize a custom object. 该示例包含一个名为 UIElements.cs (对于 Visual Basic 则为 UIElements.vb)的源代码文件,用于定义以下名为 PersonTable的结构。The example includes a source code file named UIElements.cs (UIElements.vb for Visual Basic) that defines the following structure named PersonTable. 此结构应由显示表列的本地化名称的常规表显示例程使用。This structure is intended to be used by a general table display routine that displays the localized names of table columns. 请注意, PersonTable 结构标有 SerializableAttribute 属性。Note that the PersonTable structure is marked with the SerializableAttribute attribute.

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(对于 Visual Basic 则为 CreateResources.vb)的文件,该代码创建一个名为 UIResources.resx 的 XML 资源文件,该文件存储有表标题和包含已针对英语语言本地化的应用的信息的 PersonTable 对象。The following code from a file named CreateResources.cs (CreateResources.vb for Visual Basic) creates an XML resource file named UIResources.resx that stores a table title and a PersonTable object that contains information for an app that is localized for the English language.

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) 的源代码文件中,然后检索资源并将其显示在控制台上。The following code in a source code file named GetObject.cs (GetObject.vb) then retrieves the resources and displays them to the console.

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

可以生成必要的资源文件和程序集,并通过执行以下批处理文件运行该应用。You can build the necessary resource file and assemblies and run the app by executing the following batch file. 必须使用 /r 选项提供具有对 UIElements.dll 的引用的 Resgen.exe,以便其能够访问有关 PersonTable 结构的信息。You must use the /r option to supply Resgen.exe with a reference to UIElements.dll so that it can access information about the PersonTable structure. 如果使用 C#,请将 vbc 编译器名称替换为 csc,并将 .vb 扩展名替换为 .csIf you're using C#, replace the vbc compiler name with csc, and replace the .vb extension with .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  

附属程序集的版本控制支持Versioning Support for Satellite Assemblies

默认情况下, ResourceManager 对象检索请求的资源时,会寻找版本号与主程序集版本号相匹配的附属程序集。By default, when the ResourceManager object retrieves requested resources, it looks for satellite assemblies that have version numbers that match the version number of the main assembly. 部署应用后,建议更新主程序集或特定资源附属程序集。After you have deployed an app, you might want to update the main assembly or specific resource satellite assemblies. .NET Framework 提供对主程序集和附属程序集的版本控制支持。The .NET Framework provides support for versioning the main assembly and satellite assemblies.

SatelliteContractVersionAttribute 属性提供对主程序集的版本控制支持。The SatelliteContractVersionAttribute attribute provides versioning support for a main assembly. 在应用的主程序集上指定此属性,无需更新主程序集的附属程序集即可更新和重新部署主程序集。Specifying this attribute on an app's main assembly enables you to update and redeploy a main assembly without updating its satellite assemblies. 更新主程序集后,递增主程序集的版本号,但附属协定版本号保持不变。After you update the main assembly, increment the main assembly's version number but leave the satellite contract version number unchanged. 资源管理器检索请求的资源时,会加载此属性指定的附属程序集版本。When the resource manager retrieves requested resources, it loads the satellite assembly version specified by this attribute.

发行者策略程序集提供对附属程序集的版本控制支持。Publisher policy assemblies provide support for versioning satellite assemblies. 你可以更新并重新部署附属程序集,而不用更新主程序集。You can update and redeploy a satellite assembly without updating the main assembly. 更新附属程序集后,递增其版本号,并将其附带到发行者策略程序集中。After you update a satellite assembly, increment its version number and ship it with a publisher policy assembly. 在发行者策略程序集中,指定新附属程序集为后向兼容其之前版本。In the publisher policy assembly, specify that your new satellite assembly is backward-compatible with its previous version. 资源管理器会使用 SatelliteContractVersionAttribute 属性确定附属程序集的版本,但程序集加载程序将绑定到发行者策略所指定的附属程序集版本。The resource manager will use the SatelliteContractVersionAttribute attribute to determine the version of the satellite assembly, but the assembly loader will bind to the satellite assembly version specified by the publisher policy. 有关发行者策略程序集的详细信息,请参阅 创建发行者策略文件For more information about publisher policy assemblies, see Creating a Publisher Policy File.

若要启用完全的程序集版本控制支持,建议你在 全局程序集缓存 中部署具有强名称的程序集,并将不具有强名称的程序集部署在应用程序目录中。To enable full assembly versioning support, we recommend that you deploy strong-named assemblies in the global assembly cache and deploy assemblies that don't have strong names in the application directory. 若在应用程序目录中部署具有强名称的程序集,则无法在更新程序集时递增附属程序集的版本号。If you want to deploy strong-named assemblies in the application directory, you will not be able to increment a satellite assembly's version number when you update the assembly. 相反,必须在使用更新的代码替换现有代码处执行就地更新,并保持相同的版本号。Instead, you must perform an in-place update where you replace the existing code with the updated code and maintain the same version number. 例如,若要使用完全指定的程序集名称 "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 来覆盖它。For example, if you want to update version 1.0.0.0 of a satellite assembly with the fully specified assembly name "myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a", overwrite it with the updated myApp.resources.dll that has been compiled with the same, fully specified assembly name "myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a". 请注意,在附属程序集文件上使用就地更新会使应用难以准确确定附属程序集的版本。Note that using in-place updates on satellite assembly files makes it difficult for an app to accurately determine the version of a satellite assembly.

有关程序集版本控制的详细信息,请参阅 程序集版本控制运行时如何定位程序集For more information about assembly versioning, see Assembly Versioning and How the Runtime Locates Assemblies.

检索 .resources 文件中的资源Retrieving Resources from .resources Files

如果选择不在附属程序集中部署资源,你仍可以使用 ResourceManager 对象直接访问 .resources 文件中的资源。If you choose not to deploy resources in satellite assemblies, you can still use a ResourceManager object to access resources from .resources files directly. 要执行此操作,必须正确部署.resources 文件。To do this, you must deploy the .resources files correctly. 然后使用 ResourceManager.CreateFileBasedResourceManager 方法实例化 ResourceManager 对象,并指定包含独立 .resources 文件的目录。Then you use the ResourceManager.CreateFileBasedResourceManager method to instantiate a ResourceManager object and specify the directory that contains the standalone .resources files.

部署 .resources 文件Deploying .resources Files

将 .resources 文件嵌入应用程序程序集和附属程序集后,每个附属程序集都具有相同的文件名,但被放在反射附属程序集区域性的子目录中。When you embed .resources files in an application assembly and satellite assemblies, each satellite assembly has the same file name, but is placed in a subdirectory that reflects the satellite assembly's culture. 与此相反,从 .resources 文件直接访问资源时,可以将所有 .resources 文件放在单一目录(通常为应用程序目录的子目录)中。In contrast, when you access resources from .resources files directly, you can place all the .resources files in a single directory, usually a subdirectory of the application directory. 应用的默认 .resources 文件名称仅包含一个根名称,不带有其区域性的指示(例如 strings.resources)。The name of the app's default .resources file consists of a root name only, with no indication of its culture (for example, strings.resources). 每个本地化的区域性资源存储在名称包含根名称,后带有区域性标记所组成的文件中(例如 strings.ja.resources 或 strings.de-DE.resources)。The resources for each localized culture are stored in a file whose name consists of the root name followed by the culture (for example, strings.ja.resources or strings.de-DE.resources).

下图显示资源文件应被放置在目录结构中的何处。The following illustration shows where resource files should be located in the directory structure. 它还提供了.resource 文件的命名约定。It also gives the naming conventions for .resource files.

显示应用程序主目录的插图。

使用资源管理器Using the Resource Manager

创建资源并将其放置在相应的目录中后,通过调用 ResourceManager 方法创建 CreateFileBasedResourceManager(String, String, Type) 对象以使用资源。After you have created your resources and placed them in the appropriate directory, you create a ResourceManager object to use the resources by calling the CreateFileBasedResourceManager(String, String, Type) method. 第一个参数指定应用的默认 .resources 文件的根名称(在上一节的示例中为 "strings")。The first parameter specifies the root name of the app's default .resources file (this would be "strings" for the example in the previous section). 第二个参数指定的资源的位置(上一个示例中为 "Resources")。The second parameter specifies the location of the resources ("Resources" for the previous example). 第三个参数指定要使用的 ResourceSet 实现。The third parameter specifies the ResourceSet implementation to use. 如果第三个参数为 null,则使用默认运行时 ResourceSetIf the third parameter is null, the default runtime ResourceSet is used.

备注

请勿使用独立 .resources 文件部署 ASP.NET 应用。Do not deploy ASP.NET apps using standalone .resources files. 这可能会导致锁定问题并破坏 XCOPY 部署。This can cause locking issues and breaks XCOPY deployment. 建议部署附属程序集中的 ASP.NET 资源。We recommend that you deploy ASP.NET resources in satellite assemblies. 有关详细信息,请参阅 ASP.NET 网页资源概述For more information, see ASP.NET Web Page Resources Overview.

实例化 ResourceManager 对象后,使用前文所述的 GetStringGetObjectGetStream 方法检索资源。After you instantiate the ResourceManager object, you use the GetString, GetObject, and GetStream methods as discussed earlier to retrieve the resources. 但是,直接从 .resources 文件中检索资源与从程序集中检索嵌入的资源有所不同。However, the retrieval of resources directly from .resources files differs from the retrieval of embedded resources from assemblies. 从 .resources 文件中检索资源时, GetString(String)GetObject(String)GetStream(String) 方法总是忽略当前区域性检索默认区域性的资源。When you retrieve resources from .resources files, the GetString(String), GetObject(String), and GetStream(String) methods always retrieve the default culture's resources regardless of the current culture. 若要检索应用的当前区域性资源或指定区域性的资源,必须调用 GetString(String, CultureInfo)GetObject(String, CultureInfo)GetStream(String, CultureInfo) 方法并指定要检索资源的区域性。To retrieve the resources of the either the app's current culture or a specific culture, you must call the GetString(String, CultureInfo), GetObject(String, CultureInfo), or GetStream(String, CultureInfo) method and specify the culture whose resources are to be retrieved. 若要检索当前区域性的资源,请将 CultureInfo.CurrentCulture 属性的值指定为 culture 参数。To retrieve the resources of the current culture, specify the value of the CultureInfo.CurrentCulture property as the culture argument. 如果资源管理器无法检索 culture的资源,则使用标准资源回退规则检索相应的资源。If the resource manager cannot retrieve the resources of culture, it uses the standard resource fallback rules to retrieve the appropriate resources.

示例An Example

下面的示例说明资源管理器如何直接从 .resources 文件中检索资源。The following example illustrates how the resource manager retrieves resources directly from .resources files. 此示例由三个基于文本的资源文件组成,区域性分别为英语(美国)、法语(法国)和俄语(俄罗斯)。The example consists of three text-based resource files for the English (United States), French (France), and Russian (Russia) cultures. 英语(美国)为示例的默认区域性。English (United States) is the example's default culture. 其资源存储在以下名为 Strings.txt 的文件中:Its resources are stored in the following file named Strings.txt:

Greeting=Hello  
Prompt=What is your name?  

法语(法国) 区域性的资源存储在以下名为 Strings.fr-FR.txt 的文件中:Resources for the French (France) culture are stored in the following file, which is named Strings.fr-FR.txt:

Greeting=Bon jour  
Prompt=Comment vous appelez-vous?  

俄语(俄罗斯) 区域性的资源存储在以下名为 Strings.ru-RU.txt 的文件中:Resources for the Russian (Russia) culture are stored in the following file, which is named Strings.ru-RU.txt:

Greeting=Здравствуйте  
Prompt=Как вас зовут?  

以下是该实例的源代码。The following is the source code for the example. 该示例为英语(美国)、英语(加拿大)、法语(法国)和俄语(俄罗斯)区域性实例化 CultureInfo 对象,并将以上每一种语言作为当前区域性。The example instantiates CultureInfo objects for the English (United States), English (Canada), French (France), and Russian (Russia) cultures, and makes each the current culture. ResourceManager.GetString(String, CultureInfo) 方法提供 CultureInfo.CurrentCulture 属性的值作为 culture 参数来检索相应的区域性指定资源。The ResourceManager.GetString(String, CultureInfo) method then supplies the value of the CultureInfo.CurrentCulture property as the culture argument to retrieve the appropriate culture-specific resources.

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{0}!", 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# 版本。You can compile the C# version of the example by running the following batch file. 如果使用 Visual Basic,请将 csc 替换为 vbc,并将 .cs 扩展名替换为 .vbIf you're using Visual Basic, replace csc with vbc, and replace the .cs extension with .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  

另请参阅See also