Compartilhar via


Recuperar recursos em aplicativos .NET

Quando você trabalha com recursos localizados em aplicativos .NET, o ideal é empacotar os recursos para a cultura padrão ou neutra com o assembly principal e criar um assembly satélite separado para cada idioma ou cultura a que o seu aplicativo dá suporte. Você pode usar a classe ResourceManager conforme descrito na próxima seção para acessar recursos nomeados. Se optar por não inserir os recursos no assembly principal e nos assemblies satélites, você também pode acessar os arquivos binários .resources diretamente, conforme discutido na seção Recuperar recursos de arquivos .resources, mais adiante neste artigo.

Recuperar recursos de assemblies

A classe ResourceManager fornece acesso a recursos em tempo de execução. Você usa o método ResourceManager.GetString para recuperar os recursos de cadeia de caracteres e o método ResourceManager.GetObject ou ResourceManager.GetStream para recuperar recursos que não são cadeias de caracteres. Cada método possui duas sobrecargas:

O gerenciador de recursos usa o processo de fallback de recurso para controlar como o aplicativo recupera os recursos específicos de cultura. Para obter mais informações, confira a seção "Processo de Fallback de Recurso" em Empacotar e implantar recursos. Para obter informações sobre como criar uma instância de um objeto ResourceManager, consulte a seção “Criando uma instância de um objeto ResourceManager” no tópico de classe ResourceManager.

Recuperar exemplo de dados de cadeia de caracteres

O exemplo a seguir chama o método GetString(String) para recuperar os recursos de cadeia de caracteres da cultura da interface do usuário atual. Ele inclui um recurso de cadeia de caracteres neutro para a cultura do inglês (Estados Unidos) e recursos localizados para as culturas de francês (França) e russo (Rússia). O seguinte recurso de inglês (Estados Unidos) está em um arquivo chamado Strings.txt:

TimeHeader=The current time is

O recurso de francês (França) está em um arquivo chamado Strings.fr-FR.txt:

TimeHeader=L'heure actuelle est

O recurso de Russo (Rússia) está em um arquivo chamado Strings.ru-RU.txt:

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

O código-fonte para este exemplo, que está em um arquivo chamado GetString.cs para a versão em C# do código e GetString.vb para a versão do Visual Basic, define uma matriz de cadeia de caracteres que contém o nome de quatro culturas: três culturas para as quais os recursos estão disponíveis e a cultura de espanhol (Espanha). Um loop executado cinco vezes aleatoriamente seleciona uma dessas culturas e a atribui às propriedades Thread.CurrentCulture e CultureInfo.CurrentUICulture. Depois, ele chama o método GetString(String) para recuperar a cadeia de caracteres localizada, que ele exibe com a hora do dia.

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

O arquivo em lotes (.bat) a seguir compila o exemplo e gera assemblies satélites nos diretórios apropriados. Os comandos são fornecidos para a linguagem C# e para o compilador. Para o Visual Basic, altere csc para vbc, e altere GetString.cs para 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

Quando a cultura da interface do usuário atual for espanhol (Espanha), observe que o exemplo exibe os recursos do idioma inglês, porque não estão disponíveis recursos de idioma espanhol e inglês é a cultura padrão de exemplo.

Exemplos da recuperação de dados de objeto

Você pode usar os métodos GetObject e GetStream para recuperar dados de objeto. Isso inclui tipos de dados primitivos, objetos serializáveis e objetos que são armazenados em formato binário (como imagens).

O exemplo a seguir usa o método GetStream(String) para recuperar um bitmap que é usado em uma janela inicial de abertura do aplicativo. O código-fonte a seguir em um arquivo chamado CreateResources.cs (para C#) ou CreateResources.vb (para Visual Basic) gera um arquivo .resx que contém a imagem serializada. Nesse caso, a imagem é carregada a partir de um arquivo chamado SplashScreen.jpg; você pode modificar o nome do arquivo para substituir sua própria imagem.

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

O código a seguir recupera o recurso e exibe a imagem em um controle 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

Você pode usar o seguinte arquivo em lotes para criar o exemplo de C#. Para o Visual Basic, altere csc para vbc e altere a extensão do arquivo de código-fonte de .cs para .vb.

csc CreateResources.cs
CreateResources

resgen AppResources.resx

csc GetStream.cs -resource:AppResources.resources

O exemplo a seguir usa o método ResourceManager.GetObject(String) para desserializar um objeto personalizado. O exemplo inclui um arquivo de código-fonte chamado UIElements.cs (UIElements.vb para o Visual Basic) que define a seguinte estrutura denominada PersonTable. Essa estrutura destina-se a ser usada por uma rotina de exibição geral da tabela que exibe os nomes localizados das colunas da tabela. Observe que a estrutura PersonTable é marcada com o atributo 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

O código a seguir de um arquivo chamado CreateResources.cs (CreateResources.vb para o Visual Basic) cria um arquivo de recurso XML chamado UIResources.resx que armazena um título de tabela e um objeto PersonTable que contém informações para um aplicativo localizado para o idioma inglês.

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

O código a seguir em um arquivo de código-fonte chamado GetObject.cs (GetObject.vb), em seguida, recupera os recursos e os exibe no 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

Você pode criar o arquivo de recurso necessário e os assemblies e executar o aplicativo, executando o seguinte arquivo em lotes. Você deve usar a opção /r para fornecer ao arquivo Resgen.exe uma referência para UIElements.dll para que ele possa acessar informações sobre a estrutura PersonTable. Se você estiver usando C#, substitua o nome do compilador vbc por csc e substitua a extensão .vb por .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

Suporte ao controle de versão dos assemblies satélites

Por padrão, quando o objeto ResourceManager recupera os recursos solicitados, ele procura por assemblies satélite que tenham números de versão que correspondam ao número de versão do assembly principal. Depois de ter implantado um aplicativo, convém atualizar o assembly principal ou assemblies de satélite de recursos específicos. O .NET Framework fornece suporte para controle de versão do assembly principal e dos assemblies satélites.

O atributo SatelliteContractVersionAttribute dá suporte ao controle de versão para um assembly principal. A especificação desse atributo no assembly principal do aplicativo permite que você atualize e implante novamente um assembly principal sem atualizar seus assemblies satélites. Depois de atualizar o assembly principal, incremente o número de versão do assembly principal mas deixe o número de versão do contrato do satélite inalterado. Quando o gerenciador de recursos recupera os recursos solicitados, ele carrega a versão do assembly satélite especificada por esse atributo.

Os assemblies de política do publicador oferecem suporte para controle de versão de assemblies satélites. Você pode atualizar e reimplantar um assembly satélite sem atualizar o assembly principal. Depois de atualizar um assembly satélite, aumente o número da versão e envie-o com um assembly de política do publicador. No assembly de política do publicador, especifique que seu novo assembly satélite é compatível com a versão anterior. O gerenciador de recursos usará o atributo SatelliteContractVersionAttribute para determinar a versão do assembly satélite, mas o carregador de assembly será associado à versão do assembly satélite especificada pela política do publicador. Para obter mais informações sobre assemblies de política do publicador, confira Criar um arquivo de política do publicador.

Para habilitar o suporte de controle de versão completo do assembly, é recomendável você implantar assemblies de nome forte no cache de assembly global e implantar assemblies que não tenham nomes fortes no diretório do aplicativo. Se você desejar implantar assemblies de nome forte no diretório do aplicativo, você não poderá incrementar o número da versão de um assembly satélite quando você atualiza o assembly. Em vez disso, você deve executar uma atualização no local onde você substitui o código existente pelo código atualizado e mantém o mesmo número de versão. Por exemplo, se você deseja atualizar a versão 1.0.0.0 de um assembly satélite com o nome de assembly totalmente especificado "myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a", substitua-o pelo myApp.resources.dll atualizado que foi compilado com o mesmo nome de assembly totalmente especificado "myApp.resources, Version=1.0.0.0, Culture=de, PublicKeyToken=b03f5f11d50a3a". Observe que usar atualizações locais nos arquivos de assembly satélite torna difícil para um aplicativo determinar com precisão a versão de um assembly satélite.

Para obter mais informações sobre o controle de versão de assemblies, confira Controle de versão de assemblies e Como o runtime localiza assemblies.

Recuperar recursos dos arquivos .resources

Se você optar por não implantar recursos em assemblies satélites, ainda poderá usar um objeto ResourceManager para acessar recursos de arquivos .resources diretamente. Para fazer isso, você deve implantar os arquivos .resources corretamente. Em seguida, você usa o método ResourceManager.CreateFileBasedResourceManager para criar instâncias de um objeto ResourceManager e especificar o diretório que contém os arquivos .resources autônomos.

Implantar arquivos .resources

Quando você incorporar arquivos .resources em um assembly de aplicativo e assemblies satélites, cada assembly satélite possui o mesmo nome de arquivo, mas é colocado em um subdiretório que reflete a cultura do assembly satélite. Por outro lado, quando você acessar diretamente os recursos dos arquivos .resources, você pode colocar todos os arquivos .resources em um único diretório, geralmente um subdiretório do diretório do aplicativo. O nome do arquivo .resources do aplicativo padrão consiste em um nome de raiz, sem nenhuma indicação de sua cultura (por exemplo, strings.resources). Os recursos de cada cultura localizada são armazenados em um arquivo cujo nome consiste no nome de raiz seguido pela cultura (por exemplo, strings.ja.resources ou strings.de-DE.resources).

A ilustração a seguir mostra onde os arquivos de recurso devem estar localizados na estrutura de diretórios. Ela também fornece as convenções de nomenclatura para arquivos .resource.

Ilustração que mostra o diretório principal do aplicativo.

Usar o gerenciador de recursos

Depois de ter criado os recursos e colocando-os no diretório apropriado, você cria um objeto ResourceManager para usar os recursos chamando o método CreateFileBasedResourceManager(String, String, Type). O primeiro parâmetro especifica o nome da raiz do arquivo .resources padrão do aplicativo ("strings" para o exemplo na seção anterior). O segundo parâmetro especifica o local dos recursos ("Resources" no exemplo anterior). O terceiro parâmetro especifica a implementação de ResourceSet a ser usada. Se o terceiro parâmetro for null, o runtime padrão ResourceSet será usado.

Observação

Não implante aplicativos do ASP.NET usando arquivos .resources autônomos. Isso pode causar problemas de bloqueio e interrompe a implantação de XCOPY. É recomendável que você implante recursos do ASP.NET em assemblies satélites. Para obter mais informações, consulte Visão geral dos recursos da página da Web do ASP.NET.

Depois de criar uma instância do objeto ResourceManager, você usará os métodos GetString, GetObject e GetStream conforme discutido anteriormente para recuperar os recursos. No entanto, a recuperação de recursos diretamente de arquivos .resources é diferente da recuperação de recursos incorporados em assemblies. Quando você recupera os recursos de arquivos .resources, os métodos GetString(String), GetObject(String) e GetStream(String) sempre recuperam os recursos da cultura padrão independentemente da cultura atual. Para recuperar os recursos da cultura atual ou de uma cultura específica do aplicativo, você deve chamar o método GetString(String, CultureInfo), GetObject(String, CultureInfo) ou GetStream(String, CultureInfo) e especificar a cultura cujos recursos devem ser recuperados. Para recuperar os recursos da cultura atual, especifique o valor da propriedade CultureInfo.CurrentCulture como o argumento culture. Se o gerenciador de recursos não pode recuperar os recursos de culture, ele usa as regras de fallback de recurso padrões para recuperar os recursos apropriados.

Um exemplo

O exemplo a seguir ilustra como o gerenciador de recursos recupera recursos diretamente dos arquivos .resources. O exemplo consiste em três arquivos de recurso baseados em texto para as culturas de inglês (Estados Unidos), francês (França) e russo (Rússia). Inglês (Estados Unidos) é a cultura padrão do exemplo. Os recursos estão armazenados no seguinte arquivo chamado Strings.txt:

Greeting=Hello
Prompt=What is your name?

Recursos para a cultura francês (França) são armazenados no seguinte arquivo, que é chamado Strings.fr-FR.txt:

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

Recursos para a cultura russo (França) são armazenados no seguinte arquivo, que é chamado Strings.ru-RU.txt:

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

Este é o código-fonte para o exemplo. O exemplo cria uma instância dos objetos CultureInfo para as culturas inglês (Estados Unidos), inglês (Canadá), francês (França) e russo (Rússia) e faz de cada cultura a cultura atual. O método ResourceManager.GetString(String, CultureInfo), em seguida, fornece o valor da propriedade CultureInfo.CurrentCulture como o argumento culture para recuperar os recursos específicos à cultura apropriados.

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!

Você pode compilar a versão em C# do exemplo, executando o seguinte arquivo em lotes. Se você estiver usando Visual Basic, substitua csc por vbc e substitua a extensão .cs por .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

Confira também