Empacotar e implantar recursos em aplicativos .NET

Os aplicativos dependem do Gerenciador de Recursos do .NET Framework, representado pela ResourceManager classe, para recuperar recursos localizados. O Resource Manager assume que um modelo hub e spoke é usado para empacotar e implantar recursos. O hub é o assembly principal que contém o código executável não localizável e os recursos para uma única cultura, chamada de cultura neutra ou padrão. A cultura padrão é a cultura de fallback para o aplicativo; é a cultura cujos recursos são usados se os recursos localizados não puderem ser encontrados. Cada spoke se conecta a um assembly satélite que contém os recursos para uma única cultura, mas não contém nenhum código.

Existem várias vantagens neste modelo:

  • Você pode adicionar recursos incrementalmente para novas culturas depois de implantar um aplicativo. Como o desenvolvimento subsequente de recursos específicos de cultura pode exigir uma quantidade significativa de tempo, isso permite que você lance seu aplicativo principal primeiro e forneça recursos específicos de cultura em uma data posterior.
  • Você pode atualizar e alterar os assemblies satélite de um aplicativo sem recompilá-lo.
  • Um aplicativo precisa carregar apenas os assemblies satélite que contêm os recursos necessários para uma cultura específica. Isso pode reduzir significativamente o uso de recursos do sistema.

No entanto, também existem desvantagens neste modelo:

  • Você deve gerenciar vários conjuntos de recursos.
  • O custo inicial de testar um aplicativo aumenta, porque você deve testar várias configurações. Note-se que, a longo prazo, será mais fácil e menos dispendioso testar uma aplicação central com vários satélites, do que testar e manter várias versões internacionais paralelas.

Convenções de nomenclatura de recursos

Ao empacotar os recursos do aplicativo, você deve nomeá-los usando as convenções de nomenclatura de recursos esperadas pelo Common Language Runtime. O tempo de execução identifica um recurso pelo seu nome de cultura. Cada cultura recebe um nome único, que geralmente é uma combinação de um nome de cultura minúsculo de duas letras associado a um idioma e, se necessário, um nome de subcultura maiúsculo de duas letras associado a um país ou região. O nome da subcultura segue o nome da cultura, separado por um traço (-). Exemplos incluem ja-JP para japonês falado no Japão, en-US para inglês falado nos Estados Unidos, de-DE para alemão falado na Alemanha ou de-AT para alemão falado na Áustria. Consulte a coluna Marca de idioma na lista de nomes de idioma/região suportados pelo Windows. Os nomes das culturas seguem o padrão definido pelo BCP 47.

Nota

Existem algumas exceções para os nomes de cultura de duas letras, como zh-Hans para chinês (simplificado).

Para obter mais informações, consulte Criar arquivos de recursos e Criar assemblies satélite.

O processo de fallback de recursos

O modelo hub and spoke para empacotamento e implantação de recursos usa um processo de fallback para localizar os recursos apropriados. Se um aplicativo solicitar um recurso localizado que não está disponível, o Common Language Runtime procurará na hierarquia de culturas um recurso de fallback apropriado que mais se aproxime da solicitação do aplicativo do usuário e lançará uma exceção apenas como último recurso. Em cada nível da hierarquia, se um recurso apropriado for encontrado, o tempo de execução o usará. Se o recurso não for encontrado, a pesquisa continua no próximo nível.

Para melhorar o desempenho da pesquisa, aplique o NeutralResourcesLanguageAttribute atributo ao assembly principal e passe a ele o nome da linguagem neutra que funcionará com o assembly principal.

Processo de fallback de recursos do .NET Framework

O processo de fallback de recursos do .NET Framework envolve as seguintes etapas:

Gorjeta

Você poderá usar o <elemento de configuração relativeBindForResources> para otimizar o processo de fallback de recursos e o processo pelo qual o tempo de execução investiga assemblies de recursos. Para obter mais informações, consulte Otimizando o processo de fallback de recursos.

  1. O tempo de execução primeiro verifica o cache de assembly global em busca de um assembly que corresponda à cultura solicitada para seu aplicativo.

    O cache de assembly global pode armazenar assemblies de recursos que são compartilhados por muitos aplicativos. Isso libera você de ter que incluir conjuntos específicos de recursos na estrutura de diretórios de cada aplicativo que você criar. Se o tempo de execução encontrar uma referência ao assembly, ele procurará o recurso solicitado no assembly. Se ele encontrar a entrada no assembly, ele usa o recurso solicitado. Se não encontrar a entrada, continua a pesquisa.

  2. Em seguida, o tempo de execução verifica o diretório do assembly em execução no momento em busca de um subdiretório que corresponda à cultura solicitada. Se ele encontrar o subdiretório, ele procurará nesse subdiretório um assembly satélite válido para a cultura solicitada. Em seguida, o tempo de execução procura o recurso solicitado no assembly satélite. Se ele encontrar o recurso no assembly, ele o usará. Se não encontrar o recurso, continua a pesquisa.

  3. Em seguida, o tempo de execução consulta o Windows Installer para determinar se o assembly satélite deve ser instalado sob demanda. Em caso afirmativo, ele lida com a instalação, carrega o assembly e pesquisa ele ou o recurso solicitado. Se ele encontrar o recurso no assembly, ele o usará. Se não encontrar o recurso, continua a pesquisa.

  4. O tempo de execução gera o AppDomain.AssemblyResolve evento para indicar que não é possível encontrar o assembly satélite. Se você optar por manipular o evento, seu manipulador de eventos poderá retornar uma referência ao assembly satélite cujos recursos serão usados para a pesquisa. Caso contrário, o manipulador de eventos retornará null e a pesquisa continuará.

  5. Em seguida, o tempo de execução pesquisa novamente o cache de assembly global, desta vez para o assembly pai da cultura solicitada. Se o assembly pai existir no cache de assembly global, o tempo de execução procurará o recurso solicitado no assembly.

    A cultura pai é definida como a cultura de fallback apropriada. Considere os pais como candidatos alternativos, porque fornecer qualquer recurso é preferível a lançar uma exceção. Este processo também permite reutilizar recursos. Você deve incluir um recurso específico no nível pai somente se a cultura filho não precisar localizar o recurso solicitado. Por exemplo, se você fornecer conjuntos de satélites para en (inglês neutro), en-GB (inglês falado no Reino Unido) e en-US (inglês falado nos Estados Unidos), o en satélite conterá a terminologia comum, e os en-GB satélites e en-US poderão fornecer substituições apenas para os termos que diferem.

  6. Em seguida, o tempo de execução verifica o diretório do assembly em execução no momento para ver se ele contém um diretório pai. Se existir um diretório pai, o tempo de execução procurará no diretório um assembly satélite válido para a cultura pai. Se encontrar o assembly, o tempo de execução procurará o assembly pelo recurso solicitado. Se encontrar o recurso, utiliza-o. Se não encontrar o recurso, continua a pesquisa.

  7. Em seguida, o tempo de execução consulta o Windows Installer para determinar se o assembly satélite pai deve ser instalado sob demanda. Em caso afirmativo, ele lida com a instalação, carrega o assembly e pesquisa ele ou o recurso solicitado. Se ele encontrar o recurso no assembly, ele o usará. Se não encontrar o recurso, continua a pesquisa.

  8. O tempo de execução gera o AppDomain.AssemblyResolve evento para indicar que ele não consegue encontrar um recurso de fallback apropriado. Se você optar por manipular o evento, seu manipulador de eventos poderá retornar uma referência ao assembly satélite cujos recursos serão usados para a pesquisa. Caso contrário, o manipulador de eventos retornará null e a pesquisa continuará.

  9. O tempo de execução em seguida pesquisa assemblies pai, como nas três etapas anteriores, através de muitos níveis potenciais. Cada cultura tem apenas um progenitor, que CultureInfo.Parent é definido pela propriedade, mas um progenitor pode ter o seu próprio progenitor. A busca por culturas parentais Parent para quando a propriedade de uma cultura retorna CultureInfo.InvariantCulture, para fallback de recursos, a cultura invariante não é considerada uma cultura pai ou uma cultura que pode ter recursos.

  10. Se a cultura que foi originalmente especificada e todos os pais tiverem sido pesquisados e o recurso ainda não for encontrado, o recurso para a cultura padrão (fallback) será usado. Normalmente, os recursos para a cultura padrão são incluídos no assembly do aplicativo principal. No entanto, você pode especificar um valor de para a Location propriedade do NeutralResourcesLanguageAttribute atributo para indicar que o local de fallback final para recursos é um assembly satélite, em vez do Satellite assembly principal.

    Nota

    O recurso padrão é o único recurso que pode ser compilado com o assembly principal. A menos que você especifique um assembly satélite usando o NeutralResourcesLanguageAttribute atributo, ele é o fallback final (pai final). Portanto, recomendamos que você sempre inclua um conjunto padrão de recursos em seu assembly principal. Isso ajuda a evitar que exceções sejam lançadas. Ao incluir um recurso padrão, você fornece um fallback para todos os recursos e garante que pelo menos um recurso esteja sempre presente para o usuário, mesmo que não seja culturalmente específico.

  11. Finalmente, se o tempo de execução não encontrar um recurso para uma cultura padrão (fallback), uma MissingManifestResourceException exceção ou MissingSatelliteAssemblyException será lançada para indicar que o recurso não pôde ser encontrado.

Por exemplo, suponha que o aplicativo solicite um recurso localizado para espanhol (México) (a es-MX cultura). O tempo de execução primeiro pesquisa o cache de assembly global para o assembly que corresponde es-MXao , mas não o encontra. Em seguida, o tempo de execução pesquisa um es-MX diretório no diretório do assembly em execução no momento. Caso contrário, o tempo de execução pesquisa o cache de assembly global novamente em busca de um assembly pai que reflita a cultura de fallback apropriada — neste caso, es (espanhol). Se o assembly pai não for encontrado, o tempo de execução pesquisará todos os níveis potenciais de assemblies pai para a es-MX cultura até encontrar um recurso correspondente. Se um recurso não for encontrado, o tempo de execução usará o recurso para a cultura padrão.

Otimizar o processo de fallback de recursos do .NET Framework

Sob as seguintes condições, você pode otimizar o processo pelo qual o tempo de execução procura recursos em assemblies satélite:

  • Os assemblies de satélite são implantados no mesmo local que o assembly de código. Se o assembly de código estiver instalado no Cache de Assembly Global, os assemblies satélite também serão instalados no cache de assembly global. Se o assembly de código estiver instalado em um diretório, os assemblies satélite serão instalados em pastas específicas da cultura desse diretório.

  • Os conjuntos de satélite não são instalados sob demanda.

  • O código do aplicativo não manipula o AppDomain.AssemblyResolve evento.

Você otimiza o teste para assemblies satélite incluindo o <elemento relativeBindForResources> e definindo seu enabled atributo como true no arquivo de configuração do aplicativo, conforme mostrado no exemplo a seguir.

<configuration>
   <runtime>
      <relativeBindForResources enabled="true" />
   </runtime>
</configuration>

A sonda otimizada para conjuntos de satélites é um recurso opcional. Ou seja, o tempo de execução segue as etapas documentadas em O processo de fallback do recurso, a menos que o <elemento relativeBindForResources> esteja presente no arquivo de configuração do aplicativo e seu enabled atributo esteja definido como true. Se este for o caso, o processo de sondagem de um conjunto satélite é modificado da seguinte forma:

  • O tempo de execução usa o local do assembly de código pai para investigar o assembly satélite. Se o assembly pai estiver instalado no cache de assembly global, o tempo de execução será investigado no cache, mas não no diretório do aplicativo. Se o assembly pai estiver instalado em um diretório de aplicativo, o tempo de execução investigará no diretório do aplicativo, mas não no cache de assembly global.

  • O tempo de execução não consulta o Windows Installer para instalação sob demanda de assemblies satélite.

  • Se o teste para um assembly de recurso específico falhar, o tempo de execução não gerará o AppDomain.AssemblyResolve evento.

Processo de fallback de recursos do .NET Core

O processo de fallback de recursos do .NET Core envolve as seguintes etapas:

  1. O tempo de execução tenta carregar um assembly satélite para a cultura solicitada.

    • Verifica o diretório do assembly em execução no momento em busca de um subdiretório que corresponda à cultura solicitada. Se ele encontrar o subdiretório, ele procura nesse subdiretório um assembly satélite válido para a cultura solicitada e o carrega.

      Nota

      Em sistemas operacionais com sistemas de arquivos que diferenciam maiúsculas de minúsculas (ou seja, Linux e macOS), a pesquisa do subdiretório do nome da cultura diferencia maiúsculas de minúsculas. O nome do subdiretório deve corresponder exatamente ao caso do CultureInfo.Name (por exemplo, es ou es-MX).

      Nota

      Se o programador tiver derivado um contexto de carga de montagem personalizado do AssemblyLoadContext, a situação é complicada. Se o assembly em execução foi carregado no contexto personalizado, o tempo de execução carrega o assembly satélite no contexto personalizado. Os detalhes estão fora do escopo deste documento. Consulte AssemblyLoadContext.

    • Se uma montagem de satélite não foi encontrada, o AssemblyLoadContext levanta o AssemblyLoadContext.Resolving evento para indicar que é incapaz de encontrar o conjunto de satélite. Se você optar por manipular o evento, seu manipulador de eventos poderá carregar e retornar uma referência ao assembly satélite.

    • Se um assembly satélite ainda não tiver sido encontrado, o AssemblyLoadContext fará com que o AppDomain acione um AppDomain.AssemblyResolve evento para indicar que ele não consegue localizar o assembly satélite. Se você optar por manipular o evento, seu manipulador de eventos poderá carregar e retornar uma referência ao assembly satélite.

  2. Se um assembly satélite for encontrado, o tempo de execução o procurará pelo recurso solicitado. Se ele encontrar o recurso no assembly, ele o usará. Se não encontrar o recurso, continua a pesquisa.

    Nota

    Para encontrar um recurso dentro do assembly satélite, o tempo de execução procura o arquivo de recurso solicitado pelo ResourceManager para o atual CultureInfo.Name. Dentro do arquivo de recurso, ele procura o nome do recurso solicitado. Se um deles não for encontrado, o recurso será tratado como não encontrado.

  3. Em seguida, o tempo de execução pesquisa os assemblies de cultura pai através de muitos níveis potenciais, sempre repetindo as etapas 1 & 2.

    A cultura pai é definida como uma cultura de fallback apropriada. Considere os pais como candidatos alternativos, porque fornecer qualquer recurso é preferível a lançar uma exceção. Este processo também permite reutilizar recursos. Você deve incluir um recurso específico no nível pai somente se a cultura filho não precisar localizar o recurso solicitado. Por exemplo, se você fornecer conjuntos de satélites para en (inglês neutro), en-GB (inglês falado no Reino Unido) e en-US (inglês falado nos Estados Unidos), o en satélite conterá a terminologia comum, e o en-GB e en-US satélites fornecerá substituições apenas para os termos que diferem.

    Cada cultura tem apenas um progenitor, que CultureInfo.Parent é definido pela propriedade, mas um progenitor pode ter o seu próprio progenitor. A pesquisa por culturas pai para quando a propriedade de Parent uma cultura retorna CultureInfo.InvariantCulture. Para fallback de recursos, a cultura invariante não é considerada uma cultura pai ou uma cultura que pode ter recursos.

  4. Se a cultura que foi originalmente especificada e todos os pais tiverem sido pesquisados e o recurso ainda não for encontrado, o recurso para a cultura padrão (fallback) será usado. Normalmente, os recursos para a cultura padrão são incluídos no assembly do aplicativo principal. No entanto, você pode especificar um valor de para a Location propriedade para indicar que o local de fallback final para recursos é um assembly satélite em vez do Satellite assembly principal.

    Nota

    O recurso padrão é o único recurso que pode ser compilado com o assembly principal. A menos que você especifique um assembly satélite usando o NeutralResourcesLanguageAttribute atributo, ele é o fallback final (pai final). Portanto, recomendamos que você sempre inclua um conjunto padrão de recursos em seu assembly principal. Isso ajuda a evitar que exceções sejam lançadas. Ao incluir um arquivo de recurso padrão, você fornece um fallback para todos os recursos e garante que pelo menos um recurso esteja sempre presente para o usuário, mesmo que não seja culturalmente específico.

  5. Finalmente, se o tempo de execução não encontrar um arquivo de recurso para uma cultura padrão (fallback), uma MissingManifestResourceException exceção ou MissingSatelliteAssemblyException será lançada para indicar que o recurso não pôde ser encontrado. Se o arquivo de recurso for encontrado, mas o recurso solicitado não estiver presente, a solicitação retornará null.

Fallback definitivo para a montagem de satélites

Opcionalmente, você pode remover recursos do assembly principal e especificar que o tempo de execução deve carregar os recursos de fallback finais de um assembly satélite que corresponda a uma cultura específica. Para controlar o processo de fallback, use o NeutralResourcesLanguageAttribute(String, UltimateResourceFallbackLocation) construtor e forneça um valor para o parâmetro que especifica se o UltimateResourceFallbackLocation Resource Manager deve extrair os recursos de fallback do assembly principal ou de um assembly satélite.

O exemplo do .NET Framework a seguir usa o NeutralResourcesLanguageAttribute atributo para armazenar recursos de fallback de um aplicativo em um assembly satélite para o idioma francês (fr). O exemplo tem dois arquivos de recurso baseados em texto que definem um único recurso de cadeia de caracteres chamado Greeting. O primeiro, resources.fr.txt, contém um recurso em língua francesa.

Greeting=Bon jour!

O segundo, recursos,ru.txt, contém um recurso em língua russa.

Greeting=Добрый день

Esses dois arquivos são compilados para arquivos .resources executando o Resource File Generator (resgen.exe) a partir da linha de comando. Para o recurso em francês, o comando é:

resgen.exe resources.fr.txt

Para o recurso de idioma russo, o comando é:

resgen.exe resources.ru.txt

Os arquivos .resources são incorporados em bibliotecas de vínculo dinâmico executando Assembly Linker (al.exe) a partir da linha de comando para o recurso de idioma francês da seguinte maneira:

al /t:lib /embed:resources.fr.resources /culture:fr /out:fr\Example1.resources.dll

e para o recurso de língua russa da seguinte forma:

al /t:lib /embed:resources.ru.resources /culture:ru /out:ru\Example1.resources.dll

O código-fonte do aplicativo reside em um arquivo chamado Example1.cs ou Example1.vb. Ele inclui o NeutralResourcesLanguageAttribute atributo para indicar que o recurso de aplicativo padrão está no subdiretório fr. Ele instancia o Gerenciador de Recursos, recupera o valor do Greeting recurso e o exibe no console.

using System;
using System.Reflection;
using System.Resources;

[assembly:NeutralResourcesLanguage("fr", UltimateResourceFallbackLocation.Satellite)]

public class Example
{
   public static void Main()
   {
      ResourceManager rm = new ResourceManager("resources",
                                               typeof(Example).Assembly);
      string greeting = rm.GetString("Greeting");
      Console.WriteLine(greeting);
   }
}
Imports System.Reflection
Imports System.Resources

<Assembly: NeutralResourcesLanguage("fr", UltimateResourceFallbackLocation.Satellite)>
Module Example
    Public Sub Main()
        Dim rm As New ResourceManager("resources", GetType(Example).Assembly)
        Dim greeting As String = rm.GetString("Greeting")
        Console.WriteLine(greeting)
    End Sub
End Module

Em seguida, você pode compilar o código-fonte C# a partir da linha de comando da seguinte maneira:

csc Example1.cs

O comando para o compilador do Visual Basic é muito semelhante:

vbc Example1.vb

Como não há recursos incorporados no assembly principal, não é necessário compilar usando o /resource switch.

Quando você executa o exemplo de um sistema cujo idioma é qualquer coisa diferente do russo, ele exibe a seguinte saída:

Bon jour!

Alternativa de embalagem sugerida

Restrições de tempo ou orçamento podem impedir que você crie um conjunto de recursos para cada subcultura suportada pelo seu aplicativo. Em vez disso, você pode criar um único assembly satélite para uma cultura pai que todas as subculturas relacionadas possam usar. Por exemplo, você pode fornecer um único assembly de satélite em inglês (en) que é recuperado por usuários que solicitam recursos em inglês específicos da região e um único assembly de satélite alemão (de) para usuários que solicitam recursos alemães específicos da região. Por exemplo, os pedidos de alemão falado na Alemanha (de-DE), Áustria (de-AT) e Suíça (de-CH) recairiam para a assembleia satélite alemã (de). Os recursos padrão são o fallback final e, portanto, devem ser os recursos que serão solicitados pela maioria dos usuários do seu aplicativo, portanto, escolha esses recursos com cuidado. Essa abordagem implanta recursos que são menos específicos culturalmente, mas podem reduzir significativamente os custos de localização do seu aplicativo.

Consulte também