Criar assemblies satélites para aplicativos .NET

Arquivos de recurso desempenham um papel central em aplicativos localizados. Eles permitem que um aplicativo exiba cadeias de caracteres, imagens e outros dados no idioma e na cultura do usuário e forneça dados alternativos se esses recursos não estiverem disponíveis. O .NET Usa um modelo de hub e spoke para localizar e recuperar os recursos localizados. O hub é o principal assembly que contém o código executável não localizável e os recursos para uma única cultura, que é chamada de cultura neutra ou padrão. O padrão é a cultura de fallback para o aplicativo; ela é usada quando não há recursos localizados disponíveis. Você usa o atributo NeutralResourcesLanguageAttribute para designar a cultura da cultura padrão do aplicativo. Cada spoke conecta-se a um assembly satélite que contém os recursos para uma única cultura localizada, mas não contém nenhum código. Como os assemblies satélite não fazem parte do assembly principal, você pode facilmente atualizar ou substituir recursos que correspondem a uma cultura específica sem substituir o assembly principal do aplicativo.

Observação

Os recursos da cultura padrão de um aplicativo também podem ser armazenados em um assembly satélite. Para fazer isso, você atribui ao atributo NeutralResourcesLanguageAttribute um valor de UltimateResourceFallbackLocation.Satellite.

Nome e local do assembly satélite

Esse modelo hub e spoke requer que você coloque recursos em locais específicos, para que possam ser facilmente localizados e usados. Se você não compilar e nomear os recursos conforme esperado, ou se não colocá-los nos locais corretos, o CLR não poderá localizá-los e usará os recursos da cultura padrão. O gerenciador de recursos do .NET é representado pelo tipo ResourceManager e é usado para acessar automaticamente recursos localizados. O gerenciador de recursos requer o seguinte:

  • Um assembly satélite único deve incluir todos os recursos de uma cultura específica. Em outras palavras, você deve compilar vários arquivos .txt ou .resx em um arquivo binário .resources.

  • Deve haver um subdiretório separado no diretório do aplicativo para cada cultura localizada que armazena os recursos da cultura. O nome do subdiretório deve ser igual ao nome de cultura. Como alternativa, você pode armazenar seus assemblies satélites no cache de assembly global. Nesse caso, o componente de informações de cultura do nome forte do assembly deve indicar sua cultura. Para obter mais informações, consulte Instalar assemblies satélites no cache de assembly global.

    Observação

    Se o aplicativo incluir recursos para subculturas, coloque cada subcultura em um subdiretório separado no diretório do aplicativo. Não coloque subculturas em subdiretórios no diretório principal da sua cultura.

  • O assembly satélite deve ter o mesmo nome que o aplicativo e usar a extensão de nome de arquivo ".resources.dll". Por exemplo, se um aplicativo for denominado Example.exe, o nome de cada assembly satélite deverá ser Example.resources.dll. O nome do assembly satélite não indica a cultura dos seus arquivos de recursos. No entanto, o assembly satélite aparece em um diretório que especifica a cultura.

  • Informações sobre a cultura do assembly satélite devem ser incluídas nos metadados do assembly. Para armazenar o nome da cultura nos metadados do assembly satélite, você deve especificar a opção /culture quando usar o Assembly Linker para incorporar recursos no assembly satélite.

A ilustração a seguir mostra a estrutura e os requisitos de localização de um diretório de exemplo para os aplicativos que você não está instalando no cache de assembly global. Os itens com as extensões .txt e .resources não serão fornecidos com o aplicativo final. Esses são os arquivos de recurso intermediários usados para criar os assemblies satélite finais de recursos. Neste exemplo, você poderia substituir arquivos .resx por arquivos .txt. Para obter mais informações, consulte Pacote e implantar recursos.

A imagem a seguir mostra o diretório do assembly satélite:

A satellite assembly directory with localized cultures subdirectories.

Compilar assemblies satélite

Você usa o Gerador de arquivos de recurso (resgen.exe) para compilar arquivos de texto ou XML (.resx) que contenham arquivos binários .resources. Em seguida, usa o Assembly Linker (al.exe) para compilar arquivos .resources em assemblies satélite. O al.exe cria um conjunto de arquivos usando o .resources que você especificar. Assemblies satélites podem conter somente recursos; eles não podem conter nenhum código executável.

O comando a seguir al.exe cria um assembly satélite para o aplicativo Example usando o arquivo de recursos alemão strings.de.resources.

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

O comando a seguir al.exe cria um assembly satélite para o aplicativo Example usando o arquivo strings.de.resources. A opção /template faz com que o assembly satélite herde todos os metadados de assembly, exceto as informações de cultura do assembly pai (Example. dll).

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

A tabela a seguir descreve em detalhes as opções de al.exe usadas nesses comandos:

Opção Descrição
-target:lib Especifica que o seu assembly satélite é compilado em um arquivo de biblioteca (. dll). Como um assembly satélite não contém código executável e não é um assembly principal do aplicativo, você precisa salvar assemblies satélites como DLLs.
-embed:strings.de.resources Especifica o nome do arquivo de recurso incorporado quando o al.exe compila o assembly. Você pode incorporar vários arquivos .resources em um assembly satélite, mas se estiver seguindo o modelo hub e spoke, compile um assembly satélite para cada cultura. No entanto, você pode criar arquivos .resources separados para cadeias de caracteres e objetos.
-culture:de Especifica a cultura do recurso para compilar. O Common Language Runtime usa essas informações ao procurar os recursos de uma cultura específica. Se você omitir esta opção, o al.exe ainda compilará o recurso, mas o runtime não será capaz de localizá-lo quando um usuário solicitá-lo.
-out:Example.resources.dll Especifica o nome do arquivo de saída. O nome deve seguir o padrão de nomeação baseName.resources. extensão, onde baseName é o nome do assembly principal, e extension é uma extensão de nome de arquivo válido (como .dll). O runtime não é capaz de determinar a cultura de um assembly satélite com base em seu nome de arquivo de saída; você precisa usar a opção /culture para especificá-lo.
-template:Example.dll Especifica o assembly do qual todos os metadados de assembly devem ser herdados, exceto o campo de cultura. Esta opção afetará assemblies satélites somente se você especificar um assembly com um nome forte.

Para obter uma lista completa das opções disponíveis com al.exe, consulte Assembly Linker (al.exe).

Observação

Pode haver momentos em que você deseja usar a tarefa MSBuild do .NET Core para compilar assemblies satélites, mesmo que você esteja visando .NET Framework. Por exemplo, talvez você queira usar a opção determinística do compilador C# para poder comparar assemblies de diferentes builds. Nesse caso, defina GenerateSatelliteAssembliesForCoretrue como no arquivo .csproj para gerar assemblies satélites usando csc.exe em vez de Al.exe (Assembly Linker).

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

A tarefa MSBuild do .NET Core usa csc.exe em vez de al.exe para gerar assemblies satélites, por padrão. Para obter mais informações, consulte Tornar mais fácil aceitar a geração de assembly satélite "Core".

Assemblies satélites: um exemplo

Este é um exemplo simples "Olá, Mundo", que exibe uma caixa de mensagem com uma saudação localizada. O exemplo inclui recursos para as culturas inglesas (Estados Unidos), francesas (França) e russas (Rússia), e sua cultura de fallback é o inglês. Para criar o exemplo, faça o seguinte:

  1. Crie um arquivo de recurso chamado Greeting.resx ou Greeting.txt para conter o recurso para a cultura padrão. Armazene uma única cadeia de caracteres denominada HelloString, cujo valor é "Hello world!" neste arquivo.

  2. Para indicar que inglês (en) é a cultura padrão do aplicativo, adicione o seguinte atributo System.Resources.NeutralResourcesLanguageAttribute ao arquivo AssemblyInfo do aplicativo ou ao arquivo de código-fonte principal que será compilado no assembly principal do aplicativo.

    [assembly: NeutralResourcesLanguage("en")]
    
    <Assembly: NeutralResourcesLanguage("en")>
    
  3. Adicione suporte a culturas adicionais (en-US, fr-FR e ru-RU) ao aplicativo da seguinte maneira:

    • Para oferecer suporte à cultura en-US ou inglesa (Estados Unidos) ou en-US, crie um arquivo de recurso chamado Greeting.en-US.resx ou Greeting.en-US.txt e armazene nele uma única cadeia de caracteres denominada HelloString, cujo valor é "Hi world!".

    • Para oferecer suporte à cultura fr-FR ou francesa (França) ou fr-FR, crie um arquivo de recurso chamado Greeting.fr-FR.resx ou Greeting.fr-FR.txt e armazene nele uma única cadeia de caracteres denominada HelloString, cujo valor é "Salout tout le monde!".

    • Para oferecer suporte à cultura ru-RU ou russa (Rússia) ou ru-RU, crie um arquivo de recurso chamado Greeting.ru-RU.resx ou Greeting.ru-RU.txt e armazene nele uma única cadeia de caracteres denominada HelloString, cujo valor é "Всем привет!".

  4. Use resgen.exe para compilar cada texto ou um arquivo de recursos XML em um arquivo binário .resources. A saída é um conjunto de arquivos que têm o mesmo nome de arquivo raiz que os arquivos .resx ou .txt, mas uma extensão .resources. Se você criar o exemplo com o Visual Studio, o processo de compilação será manipulado automaticamente. Se você não estiver usando o Visual Studio, execute os seguintes comandos para compilar os arquivos .resx em arquivos .resources:

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

    Se os recursos estiverem em arquivos de texto em vez de arquivos XML, substitua a extensão .resx por .txt.

  5. Compile o código-fonte abaixo com os recursos para a cultura padrão no assembly principal do aplicativo:

    Importante

    Se você estiver usando a linha de comando em vez do Visual Studio para criar o exemplo, deverá modificar a chamada para o construtor de classe ResourceManager para o seguinte: 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 {0}", 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
    

    Se o aplicativo for chamado de Example e você estiver compilando na linha de comando, o comando para o compilador C# será:

    csc Example.cs -res:Greeting.resources
    

    O comando do compilador Visual Basic correspondente é:

    vbc Example.vb -res:Greeting.resources
    
  6. Crie um subdiretório no diretório do aplicativo principal para cada cultura localizada com suporte do aplicativo. Você deve criar um subdiretório en-US, um fr-FR e um ru-RU. O Visual Studio cria esses subdiretórios automaticamente como parte do processo de compilação.

  7. Incorpore os arquivos individuais de culturas específicas .resources a assemblies satélites e os salve no diretório apropriado. O comando para fazer isso para cada arquivo .resources é:

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

    onde cultura é o nome da cultura cujos recursos o assembly satélite contém. O Visual Studio trata esse processo automaticamente.

Você pode executar o exemplo. Isso tornará aleatoriamente uma das culturas com suporte a cultura atual e exibirá uma saudação localizada.

Instalação de assemblies satélite no cache de assembly global

Em vez de instalar assemblies em um subdiretório do aplicativo local, você pode instalá-los no cache de assembly global. Isso é particularmente útil se você tiver bibliotecas de classes e assemblies de recursos de biblioteca de classe usados por vários aplicativos.

A instalação de assemblies no cache de assembly global exige que eles tenham nomes fortes. Assemblies com nomes fortes são assinados com um par de chaves públicas/privadas válido. Elas contêm informações de versão que o runtime usa para determinar qual assembly usar para atender a uma solicitação de associação. Para obter mais informações sobre nomes fortes e controle de versão, consulte Controle de versão do assembly. Para obter mais informações sobre nomes fortes, consulte Assemblies com nome forte.

Quando você estiver desenvolvendo um aplicativo, é improvável que tenha acesso ao par de chaves públicas/privadas final. Para instalar um assembly satélite no cache de assembly global e garantir que ele funcione conforme o esperado, você pode usar uma técnica chamada assinatura atrasada. Quando você atrasar a assinatura de um assembly, no momento da compilação, reserve espaço no arquivo para a assinatura de nome forte. A assinatura real é atrasada até mais tarde, quando o par de chaves públicas/privadas final ficar disponível. Para obter mais informações sobre o processo de assinatura com atraso, consulte Assinatura com atraso de um assembly.

Obter a chave pública

Para atrasar a assinatura de um assembly, você deve ter acesso à chave pública. Você pode obter a chave pública real da organização para sua empresa que fará a eventual assinatura, ou criar uma chave pública usando a Ferramenta de Nome Forte (sn.exe).

O comando a seguir Sn.exe cria um par de chaves públicas/privadas de teste. A opção –k especifica que Sn.exe deve criar um novo par de chaves e salvá-lo em um arquivo chamado TestKeyPair.snk.

sn –k TestKeyPair.snk

Você pode extrair a chave pública do arquivo que contém o par de chaves de teste. O comando a seguir extrai a chave pública de TestKeyPair.snk e a salva no PublicKey.snk:

sn –p TestKeyPair.snk PublicKey.snk

Atrasando a assinatura de um assembly

Depois de obter ou criar a chave pública, use oAssembly Linker (al.exe) para compilar o assembly e especificar a assinatura com atraso.

O comando a seguir al.exe cria um assembly satélite com nome forte para o aplicativo StringLibrary usando o arquivo strings.ja.resources:

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

A opção -delay+ especifica que o Assembly Linker deve atrasar a assinatura do assembly. A opção -keyfile especifica o nome do arquivo da chave que contém a chave pública a ser usada para atrasar a assinatura do assembly.

Nova assinatura de um assembly com atraso

Antes de implantar seu aplicativo, você deve reassinar o assembly satélite com atraso com o par de chaves real. Você pode fazer isso usando Sn.exe.

O comando a seguir Sn.exe assina StringLibrary.resources.dll com o par de chaves armazenado no arquivo RealKeyPair.snk. A opção – R especifica que um assembly assinado anteriormente ou assinado com atraso deve ser assinado novamente.

sn –R StringLibrary.resources.dll RealKeyPair.snk

Instalação de assemblies satélite no Cache de Assembly Global (GAC)

Quando o runtime procura recursos no processo de fallback de recursos, ele examina o cache de assembly global primeiro. (Para obter mais informações, consulte a seção "Processo de Fallback de Recursos" do Pacote e implantar recursos.) Assim que um assembly satélite for assinado com um nome forte, ele poderá ser instalado no cache de assembly global usando a Ferramenta de cache de assembly global (gacutil.exe).

O comando a seguir Gacutil.exe instala o StringLibrary.resources.dll* no cache de assembly global:

gacutil -i:StringLibrary.resources.dll

A opção /i especifica que Gacutil.exe deve instalar o assembly especificado no cache de assembly global. Após o satélite assembly ser instalado no cache, os recursos que ele contém ficam disponíveis para todos os aplicativos que são projetados para usar o assembly satélite.

Recursos no Cache de Assembly Global: um exemplo

O exemplo a seguir usa um método em uma biblioteca de classes .NET para extrair e retornar uma saudação localizada de um arquivo de recurso. A biblioteca e seus recursos são registrados no cache de assembly global. O exemplo do inclui recursos para as culturas inglesa (Estados Unidos), francesa (França), russa (Rússia) e inglesas. Inglês é a cultura padrão; seus recursos são armazenados no assembly principal. O exemplo inicialmente atrasa a assinatura da biblioteca e seus assemblies satélites com uma chave pública. Em seguida, assina-os novamente com um par de chaves públicas/privadas. Para criar o exemplo, faça o seguinte:

  1. Se você não estiver usando o Visual Studio, use o seguinte comando Ferramenta de Nome Forte (Sn.exe) para criar um par de chaves públicas/privadas denominado ResKey.snk:

    sn –k ResKey.snk
    

    Se você estiver usando o Visual Studio, use a guia Assinatura da caixa de diálogo Propriedades do projeto para gerar o arquivo de chave.

  2. Use o seguinte comando Ferramenta de Nome Forte (Sn.exe) para criar um arquivo de chave pública denominado PublicKey.snk:

    sn –p ResKey.snk PublicKey.snk
    
  3. Crie um arquivo de recurso chamado Strings.resx para conter o recurso para a cultura padrão. Armazene uma única cadeia de caracteres denominada Greeting cujo valor é "How do you do?" nesse arquivo.

  4. Para indicar que “en” é a cultura padrão do aplicativo, adicione o seguinte atributo System.Resources.NeutralResourcesLanguageAttribute ao arquivo AssemblyInfo do aplicativo ou ao arquivo de código-fonte principal que será compilado no assembly principal do aplicativo:

    [assembly:NeutralResourcesLanguageAttribute("en")]
    
    <Assembly: NeutralResourcesLanguageAttribute("en")>
    
  5. Adicione suporte a culturas adicionais (en-US, fr-FR e ru-RU) ao aplicativo da seguinte maneira:

    • Para oferecer suporte à cultura inglesa (Estados Unidos) ou “en-US”, crie um arquivo de recurso chamado Strings.en-US.resx ou Strings.en-US.txt e armazene nele uma única cadeia de caracteres denominada Greeting, cujo valor é "Olá!".

    • Para oferecer suporte à cultura francesa (França) ou “fr-FR”, crie um arquivo de recurso chamado Strings.fr-FR.resx ou Strings.fr-FR.txt e armazene nele uma única cadeia de caracteres denominada Greeting, cujo valor é "Bon jour!".

    • Para oferecer suporte à cultura russa (Rússia) ou “ru-RU”, crie um arquivo de recurso chamado Strings.ru-RU.resx ou Strings.ru-RU.txt e armazene nele uma única cadeia de caracteres denominada Greeting, cujo valor é "Привет!".

  6. Use resgen.exe para compilar cada texto ou um arquivo de recursos XML em um arquivo binário .resources. A saída é um conjunto de arquivos que têm o mesmo nome de arquivo raiz que os arquivos .resx ou .txt, mas uma extensão .resources. Se você criar o exemplo com o Visual Studio, o processo de compilação será manipulado automaticamente. Se você não estiver usando o Visual Studio, execute o seguinte comando para compilar os arquivos .resx em arquivos .resources:

    resgen filename
    

    onde filename é o caminho opcional, o nome e a extensão do arquivo .resx ou texto.

  7. Compile o seguinte código-fonte para StringLibrary.vb ou StringLibrary.cs com os recursos da cultura padrão em um assembly de biblioteca assinado com atraso chamado StringLibrary.dll:

    Importante

    Se você estiver usando a linha de comando em vez do Visual Studio para criar o exemplo, modifique a chamada ao construtor de classe ResourceManager para 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
    

    O comando para o compilador do C# é:

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

    O comando do compilador Visual Basic correspondente é:

    vbc -t:library -resource:Strings.resources -delaysign+ -keyfile:publickey.snk StringLibrary.vb
    
  8. Crie um subdiretório no diretório do aplicativo principal para cada cultura localizada com suporte do aplicativo. Você deve criar um subdiretório en-US, um fr-FR e um ru-RU. O Visual Studio cria esses subdiretórios automaticamente como parte do processo de compilação. Como todos os assemblies satélite têm o mesmo nome de arquivo, os subdiretórios são usados para armazenar assemblies satélite de cultura específica individuais até que eles sejam assinados com um par de chaves públicas/privadas.

  9. Incorpore os arquivos individuais de culturas específicas .resources a assemblies satélites assinados com atraso e os salve no diretório apropriado. O comando para fazer isso para cada arquivo .resources é:

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

    onde culture é o nome de uma cultura. Neste exemplo, os nomes de cultura são ru-RU, en-US e fr-FR.

  10. Assine novamente a StringLibrary.dll usando a Ferramenta de Nome Forte (Sn.exe) da seguinte maneira:

    sn –R StringLibrary.dll RealKeyPair.snk
    
  11. Assine novamente os assemblies satélite individuais. Para fazer isso, use a Ferramenta de Nome Forte (sn.exe) da seguinte forma para cada assembly satélite:

    sn –R StringLibrary.resources.dll RealKeyPair.snk
    
  12. Registre a StringLibrary.dll e cada um dos seus assemblies satélites no cache de assembly global usando o comando a seguir:

    gacutil -i filename
    

    onde filename é o nome do arquivo para registrar.

  13. Se você estiver usando o Visual Studio, crie um novo projeto Aplicativo de Console chamado Example, adicione uma referência à StringLibrary.dll e o seguinte código-fonte a ela e compile.

    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 {0}",
                            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
    

    Para compilar da linha de comando, use o seguinte comando para o compilador do C#:

    csc Example.cs -r:StringLibrary.dll
    

    A linha de comando para o compilador do Visual Basic é:

    vbc Example.vb -r:StringLibrary.dll
    
  14. Execute Example.exe.

Confira também