Извлечение ресурсов в приложениях для настольных систем

При работе с локализованными ресурсами в классических приложениях .NET Framework желательно упаковывать ресурсы для нейтральной или стандартной комбинации языка и региональных параметров в основную сборку и создавать отдельную вспомогательную сборку для каждого языка или каждой комбинации языка и региональных параметров, поддерживаемых вашим приложением. Затем можно использовать класс ResourceManager для доступа к именованным ресурсам, как описано в следующем разделе. Если вы решили не внедрять ресурсы в основную и вспомогательные сборки, можно обратиться к двоичным файлам RESOURCES напрямую, как описано в разделе Извлечение ресурсов из файлов RESOURCES далее в этой статье. Сведения об извлечении ресурсов в приложениях магазина Windows 8.x см. в статье Создание и извлечение ресурсов в приложениях магазина Windows.

Извлечение ресурсов из сборок

Класс ResourceManager предоставляет доступ к ресурсам во время выполнения. Вы можете использовать метод ResourceManager.GetString для извлечения строковых ресурсов и метод ResourceManager.GetObject или ResourceManager.GetStream для извлечения нестроковых ресурсов. Каждый метод имеет две перегрузки:

  • Перегрузка, единственным параметром которой является строка с именем ресурса. Метод пытается извлечь этот ресурс для языка и региональных параметров текущего потока. Дополнительные сведения см. в описании методов GetString(String), GetObject(String)и GetStream(String) .

  • Перегрузка, имеющая два параметра: строка с именем ресурса, а также объект CultureInfo , представляющий язык и региональные параметры, для которых требуется извлечь ресурс. Если не удается найти набор ресурсов для данной комбинации языка и региональных параметров, диспетчер ресурсов использует резервные правила для получения соответствующего ресурса. Дополнительные сведения см. в описании методов GetString(String, CultureInfo), GetObject(String, CultureInfo)и GetStream(String, CultureInfo) .

Диспетчер ресурсов использует процесс резервных ресурсов, чтобы управлять тем, как приложение извлекает ресурсы, связанные с языком и региональными параметрами. Дополнительные сведения см. в разделе "Процесс использования резервных ресурсов" статьи Packaging and Deploying Resources. Сведения о создании экземпляра объекта ResourceManager см. в разделе "Создание экземпляров объекта ResourceManager" статьи о классе ResourceManager .

Извлечение строковых данных. Пример

В следующем примере вызывается метод GetString(String) для извлечения строковых ресурсов текущего языка и региональных параметров пользовательского интерфейса. Он включает нейтральный строковый ресурс для английского языка (США) и локализованные ресурсы для французского (Франция) и русского (Россия) языков и соответствующих региональных параметров. Следующий ресурс для английского языка (США) находится в файле Strings.txt:

TimeHeader=The current time is  

Ресурс для французского языка (Франция) находится в файле Strings.fr-FR.txt:

TimeHeader=L'heure actuelle est  

Ресурс для русского языка (Россия) находится в файле Strings.ru-RU-txt:

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

Исходный код этого примера, находящийся в файле GetString.cs для версии C# и GetString.vb для версии Visual Basic, определяет строковый массив, содержащий имя четырех комбинаций языков и региональных параметров: три комбинации, для которых доступны ресурсы, а также комбинацию для испанского языка (Испания). Цикл, который выполняется пять раз, случайным образом выбирает одну из этих комбинаций языков и региональных параметров и присваивает ее свойствам 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: {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) компилирует этот пример и создает вспомогательные сборки в соответствующих каталогах. Команды приведены для языка и компилятора 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  

Если текущая комбинация языка и региональных параметров пользовательского интерфейса относится к испанскому языку (Испания), обратите внимание, что пример отображает ресурсы на английском языке, так как ресурсы для испанского языка недоступны, а в примере английский язык задан по умолчанию.

Извлечение объектных данных. Два примера

Для извлечения данных объекта можно использовать методы GetObject и GetStream . Сюда входят типы-примитивы, сериализуемые объекты и объекты, хранящиеся в двоичном формате (например, изображения).

В следующем примере метод GetStream(String) используется для извлечения точечного рисунка, который используется на экране-заставке приложения. Следующий исходный код в файле с именем CreateResources.cs (для C#) или CreateResources.vb (для 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 для Visual Basic), который определяет следующую структуру с именем PersonTable. Эта структура предназначена для использования подпрограммой общего отображения таблиц, которая отображает локализованные имена для столбцов таблиц. Обратите внимание, что структура 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 для Visual Basic) создает файл ресурсов XML с именем UIResources.resx, где хранится заголовок таблицы, и объект 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 , чтобы определить версию вспомогательной сборки, но загрузчик сборок будет привязан к версии вспомогательной сборки, указанной в политике издателя. Дополнительные сведения о сборках политик издателей см. в разделе Создание файла политики издателя.

Чтобы включить полную поддержку управления версиями сборок, рекомендуется развернуть сборки со строгими именами в глобальном кэше сборок , а сборки без строгих имен — в каталоге приложения. Если вы хотите развернуть сборки со строгими именами в каталоге приложения, то при обновлении сборки не сможете увеличить номер версии для вспомогательной сборки. Вместо этого необходимо выполнить обновление на месте, при котором вы заменяете существующий код обновленным, сохраняя тот же номер версии. Например, если вы хотите обновить версию 1.0.0.0 вспомогательной сборки с полностью заданным именем "myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a", перезапишите ее обновленным файлом myApp.resources.dll, скомпилированным с использованием того же полностью заданного имени сборки "myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a". Обратите внимание, что использование обновления на месте для файлов вспомогательных сборок затрудняет приложению задачу по корректному определению версии вспомогательной сборки.

Дополнительные сведения об управлении версиями сборок см. в разделах Управление версиями сборок и Обнаружение сборок в среде выполнения.

Извлечение ресурсов из файлов RESOURCES

Если вы решили не развертывать ресурсы в вспомогательных сборках, то все равно можете воспользоваться объектом ResourceManager для прямого доступа к ресурсам из файлов RESOURCES. Для этого требуется развернуть файлы RESOURCES правильным образом. После этого используйте метод ResourceManager.CreateFileBasedResourceManager для создания экземпляра объекта ResourceManager и укажите каталог, содержащий автономные файлы RESOURCES.

Развертывание файлов RESOURCES

При внедрении файлов RESOURCES в сборку приложения и вспомогательные сборки все вспомогательные сборки имеют одинаковое имя файла, однако каждая из них помещается в подкаталог, который отражает ее язык и региональные параметры. И наоборот, при прямом доступе к ресурсам из RESOURCES-файлов все эти файлы можно поместить в один каталог, который обычно находится в каталоге приложения. Имя RESOURCES-файла по умолчанию для приложения состоит только из корневого имени без указания языка и региональных параметров (например, strings.resources). Ресурсы для каждого локализованного языка хранятся в файле, имя которого состоит из корневого имени, за которым следует обозначение языка и региональных параметров (например, strings.ja.resources или strings.de DE.resources).

На следующем рисунке показано, где должны находиться файлы ресурсов в структуре каталогов. На нем также приведены соглашения об именовании для файлов RESOURCE.

Рисунок, показывающий главную папку приложения.

Использование диспетчера ресурсов

После создания ресурсов и помещения их в соответствующий каталог создайте объект ResourceManager , чтобы использовать ресурсы путем вызова метода CreateFileBasedResourceManager(String, String, Type) . Первый параметр указывает корневое имя RESOURCES-файла по умолчанию в приложении (в примере из предыдущего раздела этому соответствует "strings"). Второй параметр указывает расположение ресурсов ("Resources" в предыдущем примере). Третий параметр указывает используемую реализацию ResourceSet . Если третий параметр равен null, используется среда выполнения по умолчанию ResourceSet .

Примечание

Не развертывайте приложения ASP.NET с использованием автономных файлов RESOURCES. Это может привести к проблемам с блокировкой и нарушениям развертывания XCOPY. Рекомендуется развертывать ресурсы ASP.NET во вспомогательных сборках. Дополнительные сведения см. в разделе ASP.NET Web Page Resources Overview.

После его создания экземпляра объекта ResourceManager используйте методы GetString, GetObjectи GetStream для извлечения ресурсов, как было описано выше. Однако получение ресурсов непосредственно из 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{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# можно скомпилировать, запустив следующий пакетный файл. Если используется 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  

См. также