Поделиться через


Создание спутниковых сборок для приложений .NET

Файлы ресурсов играют важную роль в локализованных приложениях. Они позволяют приложению отображать строки, изображения и другие данные на языке пользователя и региональных параметрах, а также предоставлять альтернативные данные, если ресурсы для языка пользователя или языка и региональных параметров недоступны. .NET использует модель концентратора и периферийной среды для поиска и извлечения локализованных ресурсов. Центр — это основная сборка, содержащая нелокализуемый исполняемый код и ресурсы для одной культуры, называемой нейтральной или культурой по умолчанию. Язык и региональные параметры по умолчанию — это резервный язык и региональные параметры для приложения; он используется, если локализованные ресурсы недоступны. Атрибут NeutralResourcesLanguageAttribute используется для указания культуры по умолчанию для приложения. Каждый спиц соединяется со спутниковым сборочным узлом, который содержит ресурсы для одной локализованной культуры, но не содержит кода. Так как сателлитные сборки не являются частью основной сборки, вы можете легко обновлять или заменять ресурсы, соответствующие определенной культуре, не заменяя основную сборку приложения.

Примечание.

Ресурсы культуры по умолчанию приложения также могут храниться в сателлитной сборке. Для этого нужно назначить атрибуту NeutralResourcesLanguageAttribute значение UltimateResourceFallbackLocation.Satellite.

Название и расположение спутниковой сборки

Для реализации модели "с концентратором и спицами" нужно размещать ресурсы в определённых местах так, чтобы их можно было легко найти и использовать. Если вы не компилируете и не именуете ресурсы должным образом или не помещаете их в правильные места, общая языковая среда выполнения (CLR) не сможет найти их и будет использовать ресурсы языка и региональных параметров по умолчанию. Диспетчер ресурсов .NET представлен типом ResourceManager и используется для автоматического доступа к локализованным ресурсам. Для диспетчера ресурсов требуется следующее:

  • Отдельная сателлитная сборка должна включать все ресурсы для конкретной культуры. Иначе говоря, необходимо скомпилировать несколько файлов .txt или .resx в двоичный файл .resources.

  • Для каждой локализованной культуры в каталоге приложения должен быть выделен отдельный подкаталог, в котором хранятся ресурсы этой культуры. Имя вложенного каталога должно совпадать с именем культуры. Или вы можете хранить спутниковые сборки в глобальном кэше сборок. В этом случае компонент культуры строгого имени сборки должен указывать его культуру. Дополнительные сведения см. в разделе Установка сателлитных сборок в глобальный кэш сборок.

    Примечание.

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

  • Вспомогательная сборка должна иметь то же имя, что и приложение, а также должна использовать расширение имени файла ".resources.dll". Например, если приложение называется Example.exe, то именем каждой вспомогательной сборки должно быть Example.resources.dll. Имя сателлитной сборки не указывает культуру файлов ресурсов. Однако сателлитная сборка отображается в каталоге, который указывает на культуру.

  • Сведения о культуре спутниковой сборки должны быть включены в метаданные сборки. Чтобы сохранить имя культуры в метаданных вспомогательной сборки, необходимо задать параметр /culture при использовании компоновщика сборок для встраивания ресурсов во вспомогательную сборку.

На следующем рисунке показаны примеры требований к структуре каталогов и расположению для приложений, которые не устанавливаются в глобальном кэше сборок. Элементы с расширениями .txt и .resources не будут отправляться с конечным приложением. Они представляют собой промежуточные файлы ресурсов, используемые для создания конечных спутниковых сборок ресурсов. В этом примере можно заменить файлы .resx на файлы .txt. Дополнительные сведения см. в статье Упаковка и развертывание ресурсов.

На следующем изображении показан каталог сателлитных сборок:

Каталог спутниковых сборок с подкаталогами для локализованных языков и культур.

Компилировать сателлитные сборки

Используйте генератор файлов ресурсов (resgen.exe) для компиляции текстовых файлов или файлов XML (RESX), содержащих ресурсы, в двоичные RESOURCES-файлы. Затем используйте компоновщик сборок (al.exe) для компиляции .resources-файлов в сателлитные сборки. Программа al.exe создает сборку из указанных RESOURCES-файлов. Спутниковые сборки могут содержать только ресурсы; они не могут содержать исполняемый код.

Следующая команда al.exe создает вспомогательную сборку для приложения Example из немецкого файла ресурсов strings.de.resources.

al -target:lib -embed:strings.de.resources -culture:de -out:Example.resources.dll

Следующая команда al.exe служит для создания вспомогательной сборки для приложения Example из файла strings.de.resources. Параметр /template заставляет спутниковую сборку наследовать все метаданные сборки, за исключением культурных сведений от ее родительской сборки (Example.dll).

al -target:lib -embed:strings.de.resources -culture:de -out:Example.resources.dll -template:Example.dll

В приведенной ниже таблице более подробно описываются параметры al.exe, используемые в этих командах:

Вариант Описание
-target:lib Указывает, что спутниковая сборка компилируется в файл библиотеки (.dll). Так как спутниковые сборки не содержат исполняемый код и не являются основной сборкой приложения, необходимо сохранить спутниковые сборки в виде библиотек dll.
-embed:strings.de.resources Указывает имя файла ресурсов для встраивания при компиляции сборки с помощью al.exe. Во вспомогательной сборке можно включить несколько файлов ресурсов, но если следовать модели концентратор-спица, необходимо компилировать отдельную вспомогательную сборку для каждой культурной среды. Впрочем, можно создавать и отдельные RESOURCES-файлы для строк и объектов.
-culture:de Задает культуру компилируемого ресурса. Общее средство выполнения использует эту информацию при поиске ресурсов для указанной культуры. Если этот параметр не указан, al.exe по-прежнему компилирует ресурс, но среда выполнения не сможет найти его, когда пользователь запрашивает его.
-out:Example.resources.dll Указывает имя выходного файла. Имя должно соответствовать шаблону baseName.resources.extension, где baseName — имя основной сборки, а extension — допустимое расширение имени файла (например, DLL). Среда выполнения не может определить язык и региональные параметры вспомогательной сборки на основе имени выходного файла; необходимо использовать параметр /culture для его указания.
-template:Example.dll Указывает основную сборку, от которой спутниковая сборка унаследует все метаданные за исключением поля культуры. Этот параметр влияет на сателлитные сборки только в том случае, если указана сборка, у которой есть строгое имя.

Полный список параметров, доступных в программе al.exe, см. в разделе Компоновщик сборок (al.exe).

Примечание.

Иногда может потребоваться использовать задачу MSBuild .NET Core для компиляции спутниковых сборок, даже если вы нацеливаетесь на .NET Framework. Например, можно использовать детерминированный параметр компилятора C# для сравнения сборок из разных сборок. В этом случае задайте в файле CSPROJ значение GenerateSatelliteAssembliesForCoretrue, чтобы создать вспомогательные сборки с помощью csc.exe вместо Al.exe (Компоновщик сборок).

<Project>
    <PropertyGroup>
        <GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
    </PropertyGroup>
</Project>

Задача MSBuild .NET Core использует csc.exe вместо al.exe для создания вспомогательных сборок по умолчанию. Для получения дополнительной информации см. Упрощение подключения к генерации вспомогательной сборки "Core".

Пример сателлитных сборок

Ниже приведен простой пример "Hello world", который отображает окно сообщения, содержащее локализованное приветствие. Пример включает ресурсы для английского (США), французского (Франция) и русского (Россия) языков, и резервным языком является английский. Для создания этого примера выполните следующие действия.

  1. Создайте файл ресурсов с именем Greeting.resx или Greeting.txt, содержащий ресурс для культуры по умолчанию. Сохраните одну строку с именем HelloString "Hello world!" в этом файле.

  2. Чтобы указать, что по умолчанию язык и региональные параметры приложения — английский (en), добавьте следующий атрибут System.Resources.NeutralResourcesLanguageAttribute в файл AssemblyInfo приложения или в основной файл исходного кода, который будет скомпилирован в основную сборку приложения.

    [assembly: NeutralResourcesLanguage("en")]
    
    <Assembly: NeutralResourcesLanguage("en")>
    
  3. Добавьте поддержку дополнительных культурных параметров (en-US, fr-FR и ru-RU) в приложение следующим образом.

    • Для поддержки языка и региональных параметров en-US или "Английский (США)" создайте файл ресурсов с именем Greeting.en-US.resx или Greeting.en-US.txt. Затем сохраните в нем строку с именем HelloString, значение которой — "Hi world!".

    • Для поддержки языка и региональных параметров fr-FR или "Французский (Франция)" создайте файл ресурсов с именем Greeting.fr-FR.resx или Greeting.fr-FR.txt и сохраните в нем одну строку с именем HelloString и значением "Salut tout le monde!".

    • Для поддержки языка и региональных параметров ru-RU или "Русский (Россия)" создайте файл ресурсов с именем Greeting.ru-RU.resx или Greeting.ru-RU.txt и сохраните в нем одну строку с именем HelloString, значением которой является "Всем привет!".

  4. Используйте resgen.exe для компиляции каждого текстового файла или файла ресурсов XML в двоичный RESOURCES-файл. Выходными данными будет набор файлов, имеющий то же корневое имя файла, что и RESX- и TXT-файлы, но с расширением RESOURCES. При создании примера с помощью Visual Studio процесс компиляции отрегулируется автоматически. Если используется среда, отличная от Visual Studio, выполните следующие команды для компиляции RESX-файлов в RESOURCES-файлы:

    resgen Greeting.resx
    resgen Greeting.en-us.resx
    resgen Greeting.fr-FR.resx
    resgen Greeting.ru-RU.resx
    

    Если ресурсы располагаются в текстовых файлах вместо XML-файлов, замените расширение RESX на TXT.

  5. Скомпилируйте следующий исходный код и ресурсы по умолчанию в основную сборку приложения.

    Внимание

    При использовании командной строки, а не Visual Studio, для создания примера, необходимо изменить вызов конструктора класса ResourceManager на следующий: ResourceManager rm = new ResourceManager("Greeting", typeof(Example).Assembly);

    using System;
    using System.Globalization;
    using System.Reflection;
    using System.Resources;
    using System.Threading;
    using System.Windows.Forms;
    
    class Example
    {
       static void Main()
       {
          // Create array of supported cultures
          string[] cultures = {"en-CA", "en-US", "fr-FR", "ru-RU"};
          Random rnd = new Random();
          int cultureNdx = rnd.Next(0, cultures.Length);
          CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture;
    
          try {
             CultureInfo newCulture = new CultureInfo(cultures[cultureNdx]);
             Thread.CurrentThread.CurrentCulture = newCulture;
             Thread.CurrentThread.CurrentUICulture = newCulture;
             ResourceManager rm = new ResourceManager("Example.Greeting",
                                                      typeof(Example).Assembly);
             string greeting = String.Format("The current culture is {0}.\n{1}",
                                             Thread.CurrentThread.CurrentUICulture.Name,
                                             rm.GetString("HelloString"));
    
             MessageBox.Show(greeting);
          }
          catch (CultureNotFoundException e) {
             Console.WriteLine($"Unable to instantiate culture {e.InvalidCultureName}");
          }
          finally {
             Thread.CurrentThread.CurrentCulture = originalCulture;
             Thread.CurrentThread.CurrentUICulture = originalCulture;
          }
       }
    }
    
    Imports System.Globalization
    Imports System.Resources
    Imports System.Threading
    
    Module Module1
    
        Sub Main()
            ' Create array of supported cultures
            Dim cultures() As String = {"en-CA", "en-US", "fr-FR", "ru-RU"}
            Dim rnd As New Random()
            Dim cultureNdx As Integer = rnd.Next(0, cultures.Length)
            Dim originalCulture As CultureInfo = Thread.CurrentThread.CurrentCulture
    
            Try
                Dim newCulture As New CultureInfo(cultures(cultureNdx))
                Thread.CurrentThread.CurrentCulture = newCulture
                Thread.CurrentThread.CurrentUICulture = newCulture
                Dim greeting As String = String.Format("The current culture is {0}.{1}{2}",
                                                       Thread.CurrentThread.CurrentUICulture.Name,
                                                       vbCrLf, My.Resources.Greetings.HelloString)
    
                MsgBox(greeting)
            Catch e As CultureNotFoundException
                Console.WriteLine("Unable to instantiate culture {0}", e.InvalidCultureName)
            Finally
                Thread.CurrentThread.CurrentCulture = originalCulture
                Thread.CurrentThread.CurrentUICulture = originalCulture
            End Try
        End Sub
    End Module
    

    Если приложение называется Пример и вы компилируете из командной строки, выполните команду для компилятора C#:

    csc Example.cs -res:Greeting.resources
    

    Соответствующая команда для компилятора Visual Basic имеет вид:

    vbc Example.vb -res:Greeting.resources
    
  6. Создайте подкаталог в основном каталоге приложения для каждой поддерживаемой культуры локализации. Необходимо создать подкаталоги en-US, fr-FR и ru-RU. Visual Studio автоматически создает эти вложенные каталоги в процессе компиляции.

  7. Включите отдельные .resources-файлы, специфичные для культуры, во вспомогательные сборки и сохраните их в соответствующий каталог. Для этого можно воспользоваться командой для каждого RESOURCES-файла:

    al -target:lib -embed:Greeting.culture.resources -culture:culture -out:culture\Example.resources.dll
    

    где culture — имя культуры, ресурсы которой содержит сателлитная сборка. Visual Studio автоматически обрабатывает этот процесс.

Затем можно запустить пример. Он случайным образом сделает одну из поддерживаемых культур текущей и отобразит локализованное приветствие.

Установка спутниковых сборок в Глобальный кэш сборок

Вместо установки сборок во вложенном каталоге локального приложения их можно установить в глобальный кэш сборок. Это особенно полезно, если библиотеки классов и сборки ресурсов библиотеки классов используются несколькими приложениями.

Все сборки, устанавливаемые в глобальный кэш сборок, должны иметь строгие имена. Сборки со строгими именами подписываются допустимой парой открытого и закрытого ключей. Они содержат сведения о версии, с помощью которых среда выполнения определяет, какую сборку следует использовать для удовлетворения запроса привязки. Дополнительные сведения о строгих именах и управлении версиями см. в разделе Управление версиями сборки. Дополнительные сведения о строгих именах см. в разделе Сборки со строгими именами.

При разработке приложения маловероятно, что у вас будет доступ к последней паре открытых и закрытых ключей. Чтобы установить спутниковую сборку в глобальном кэше сборок и убедиться, что она работает должным образом, можно использовать метод, называемый отложенной подписью. Если подписание сборки откладывается, во время построения в файле резервируется место для подписи строгим именем. Фактически подписание откладывается до того момента, когда становится доступной итоговая пара открытого и закрытого ключей. Для получения дополнительной информации об отложенной подписи см. раздел Отложенная подпись сборки.

Получение открытого ключа

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

Следующая команда Sn.exe создает тестовые пары открытого и закрытого ключей. Параметр –k указывает, что Sn.exe необходимо создать новую пару ключей и сохранить ее в заданном файле с именем TestKeyPair.snk.

sn –k TestKeyPair.snk

Открытый ключ можно извлечь из файла, содержащего тестовую пару ключей. Следующая команда извлекает открытый ключ из TestKeyPair.snk и сохраняет его в PublicKey.snk:

sn –p TestKeyPair.snk PublicKey.snk

Отложенная подпись сборки

После получения или создания открытого ключа, можно скомпилировать сборку и указать отложенную подпись, используя компоновщик сборок (al.exe).

Следующая команда al.exe создает спутниковую сборку со строгим именем для приложения StringLibrary из файла strings.ja.resources:

al -target:lib -embed:strings.ja.resources -culture:ja -out:StringLibrary.resources.dll -delay+ -keyfile:PublicKey.snk

Параметр -delay+ указывает, что компоновщик сборок должен отложить подпись сборки. Параметр -keyfile задает имя файла ключа, в котором содержится открытый ключ, применяемый для откладывания подписи сборки.

Повторная подпись сборки

Прежде чем развертывать приложение, вы должны заново подписать спутниковую сборку, подписание которой было отложено, реальной парой ключей. Это можно сделать с помощью программы Sn.exe.

Следующая команда Sn.exe используется для подписи StringLibrary.resources.dll парой ключей, хранящихся в файле RealKeyPair.snk. Параметр –R указывает, что сборка, которая была ранее подписана или с отложенной подписью, должна быть подписана снова.

sn –R StringLibrary.resources.dll RealKeyPair.snk

Установка спутниковой сборки в глобальный кэш сборок

Когда среда выполнения ищет ресурсы в процессе перехода на резервные средства, в первую очередь она ищет глобальный кэш сборок. (Дополнительные сведения см. в разделе "Резервный процесс ресурсов" разделаУпаковка и развертывание ресурсов.) Как только спутниковая сборка подписана строгим именем, ее можно установить в глобальном кэше сборок с помощью средства глобального кэша сборок (gacutil.exe).

Следующая команда Gacutil.exe устанавливает StringLibrary.resources.dll* в глобальный кэш сборок:

gacutil -i:StringLibrary.resources.dll

Параметр /i указывает программе Gacutil.exe, что данную сборку требуется установить в глобальный кэш сборок. После установки сателлитной сборки в кэш, содержащиеся в ней ресурсы становятся доступными для всех приложений, предназначенных для её использования.

Пример ресурсов в глобальном кэше сборок

В следующем примере метод в библиотеке классов .NET используется для извлечения и возврата локализованного приветствия из файла ресурса. Библиотека и ее ресурсы зарегистрированы в глобальном кэше сборок. Пример включает ресурсы для культур английского (США), французского (Франция), русского (Россия) и английского языков. Английский язык является культурой по умолчанию; его ресурсы хранятся в основной сборке. Пример сначала временно подписывает эту библиотеку и вспомогательные сборки с открытым ключом, а затем повторно подписывает их парой открытого и закрытого ключей. Для создания этого примера выполните следующие действия.

  1. Если вы не используете Visual Studio, используйте следующую команду средства строгого имени (Sn.exe), чтобы создать пару открытого и закрытого ключа с именем ResKey.snk:

    sn –k ResKey.snk
    

    Если вы используете Visual Studio, используйте вкладку "Подписывание" диалогового окна "Свойства проекта" для создания файла ключа.

  2. Используйте следующую команду утилиты Strong Name (Sn.exe), чтобы создать файл открытого ключа с именем PublicKey.snk:

    sn –p ResKey.snk PublicKey.snk
    
  3. Создайте файл ресурсов с именем Strings.resx, который будет содержать ресурсы для культуры по умолчанию. Сохраните одну строку с именем Greeting , значение которой — "Как вы делаете?" в этом файле.

  4. Чтобы указать, что культура приложения по умолчанию — "en", необходимо добавить в файл AssemblyInfo приложения или в основной файл исходного кода, который будет скомпилирован в главную сборку приложения, следующий атрибут System.Resources.NeutralResourcesLanguageAttribute:

    [assembly:NeutralResourcesLanguageAttribute("en")]
    
    <Assembly: NeutralResourcesLanguageAttribute("en")>
    
  5. Добавьте поддержку дополнительных культур (en-US, fr-FR и ru-RU) в приложение, как указано ниже.

    • Для поддержки региональных настроек "en-US" (английского языка, США) создайте файл ресурсов с именем Strings.en-US.resx или Strings.en-US.txt и сохраните в нем одну строку с именем Greeting и значением "Hello!".

    • Для поддержки культуры "fr-FR" или французской (Франция) создайте файл ресурсов с именем Strings.fr-FR.resx или Strings.fr-FR.txt и сохраните в нем одну строку с именем Greeting и значением "Bon jour!".

    • Для поддержки культуры "ru-RU" или русского (Россия) создайте файл ресурсов с именем Strings.ru-RU.resx или Strings.ru-RU.txt и сохраните в нем одну строку с именем Greeting, значением которой будет "Привет!".

  6. Используйте resgen.exe для компиляции каждого текстового файла или файла ресурсов XML в двоичный RESOURCES-файл. Выходными данными будет набор файлов, имеющий то же корневое имя файла, что и RESX- и TXT-файлы, но с расширением RESOURCES. При создании примера с помощью Visual Studio процесс компиляции отрегулируется автоматически. Если вы не используете Visual Studio, выполните следующую команду для компиляции файлов .resx в файлы .resources:

    resgen filename
    

    Где filename — дополнительный путь, имя и расширение RESX-файла или текстового файла.

  7. Скомпилируйте следующий исходный код в виде StringLibrary.vb или StringLibrary.cs вместе с ресурсами для региональных настроек по умолчанию в сборку с отложенной подписью с именем StringLibrary.dll:

    Внимание

    При использовании командной строки, а не Visual Studio, для создания примера следует изменить вызов конструктора класса ResourceManager на следующий: ResourceManager rm = new ResourceManager("Strings",typeof(Example).Assembly);.

    using System;
    using System.Globalization;
    using System.Reflection;
    using System.Resources;
    using System.Threading;
    
    [assembly:NeutralResourcesLanguageAttribute("en")]
    
    public class StringLibrary
    {
       public string GetGreeting()
       {
          ResourceManager rm = new ResourceManager("Strings",
                               Assembly.GetAssembly(typeof(StringLibrary)));
          string greeting = rm.GetString("Greeting");
          return greeting;
       }
    }
    
    Imports System.Globalization
    Imports System.Reflection
    Imports System.Resources
    Imports System.Threading
    
    <Assembly: NeutralResourcesLanguageAttribute("en")>
    
    Public Class StringLibrary
        Public Function GetGreeting() As String
            Dim rm As New ResourceManager("Strings", _
                                          Assembly.GetAssembly(GetType(StringLibrary)))
            Dim greeting As String = rm.GetString("Greeting")
            Return greeting
        End Function
    End Class
    

    Команда для компилятора C# имеет вид:

    csc -t:library -resource:Strings.resources -delaysign+ -keyfile:publickey.snk StringLibrary.cs
    

    Соответствующая команда для компилятора Visual Basic имеет вид:

    vbc -t:library -resource:Strings.resources -delaysign+ -keyfile:publickey.snk StringLibrary.vb
    
  8. Создайте подкаталог в каталоге основного приложения для каждой локализованной культуры, поддерживаемой приложением. Вы должны создать подкаталоги en-US, fr-FR и ru-RU. Visual Studio автоматически создает эти вложенные каталоги в процессе компиляции. Поскольку все спутниковые сборки имеют одинаковое имя файла, подкаталоги используются для хранения отдельных спутниковых сборок, специфичных для конкретной культуры, до тех пор, пока они не будут подписаны парой открытого и закрытого ключа.

  9. Встраивайте отдельные .resources файлы, специфичные для культуры, в сборки-сателлиты с отложенной подписью и сохраняйте их в соответствующем каталоге. Для этого можно воспользоваться командой для каждого RESOURCES-файла:

    al -target:lib -embed:Strings.culture.resources -culture:culture -out:culture\StringLibrary.resources.dll -delay+ -keyfile:publickey.snk
    

    где culture — название культуры. В этом примере коды культур: en-US, fr-FR и ru-RU.

  10. Подпишите заново StringLibrary.dll с помощью средства строгих имен (sn.exe) следующим образом:

    sn –R StringLibrary.dll RealKeyPair.snk
    
  11. Подпишите заново отдельные спутниковые сборки. Для этого используйте средство строгих имен (sn.exe) для каждой сателлитной сборки следующим образом:

    sn –R StringLibrary.resources.dll RealKeyPair.snk
    
  12. Зарегистрируйте StringLibrary.dll и все вспомогательные сборки в глобальном кэше сборок с помощью следующей команды:

    gacutil -i filename
    

    где filename — имя регистрируемого файла.

  13. Если вы используете Visual Studio, создайте проект консольного приложения с именем Example, добавьте ссылку на StringLibrary.dll и следующий исходный код в него и скомпилируйте его.

    using System;
    using System.Globalization;
    using System.Threading;
    
    public class Example
    {
       public static void Main()
       {
          string[] cultureNames = { "en-GB", "en-US", "fr-FR", "ru-RU" };
          Random rnd = new Random();
          string cultureName = cultureNames[rnd.Next(0, cultureNames.Length)];
          Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName);
          Console.WriteLine($"The current UI culture is {Thread.CurrentThread.CurrentUICulture.Name}");
          StringLibrary strLib = new StringLibrary();
          string greeting = strLib.GetGreeting();
          Console.WriteLine(greeting);
       }
    }
    
    Imports System.Globalization
    Imports System.Threading
    
    Module Example
        Public Sub Main()
            Dim cultureNames() As String = {"en-GB", "en-US", "fr-FR", "ru-RU"}
            Dim rnd As New Random()
            Dim cultureName As String = cultureNames(rnd.Next(0, cultureNames.Length))
            Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName)
            Console.WriteLine("The current UI culture is {0}",
                              Thread.CurrentThread.CurrentUICulture.Name)
            Dim strLib As New StringLibrary()
            Dim greeting As String = strLib.GetGreeting()
            Console.WriteLine(greeting)
        End Sub
    End Module
    

    Чтобы скомпилировать из командной строки используйте следующую команду для компилятора C#:

    csc Example.cs -r:StringLibrary.dll
    

    Команда компилятора для Visual Basic:

    vbc Example.vb -r:StringLibrary.dll
    
  14. Запустите Example.exe.

См. также