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

Приложения зависят от диспетчера ресурсов платформы .NET Framework, представленного классом ResourceManager, для извлечения локализованных ресурсов. Диспетчер ресурсов предполагает, что модель "звезда" используется для упаковки и развертывания ресурсов. Центр в этой модели — основная сборка, которая содержит нелокализуемый исполняемый код и ресурсы для единственного языка и региональных параметров, называемого нейтральным или языком и региональными параметрами по умолчанию. Язык и региональные параметры по умолчанию являются резервными языком и региональными параметрами; это язык и региональные параметры, чьи ресурсы используются при невозможности найти локализованные ресурсы. Каждый луч звезды ведет к вспомогательной сборке, которая содержит ресурсы для одного языка и региональных параметров, но не содержит кода.

У этой модели несколько преимуществ.

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

Тем не менее у этой модели существует ряд недостатков.

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

Соглашения об именовании ресурсов

В процессе упаковки ресурсов приложения им необходимо присваивать имена в соответствии с соглашениями об именовании, которые применяются в среде CLR. Среда определяет ресурс по имени языка и региональных параметров. Каждому языку и региональным параметрам присваивается уникальное имя, которое обычно представляет собой сочетание имени языка из двух строчных букв и (при необходимости) имени региона из двух прописных букв, обозначающего данную страну или регион. Имя субъязыка и региональных параметров следует за именем языка через дефис (-). Примеры включают ja-JP для Японии (японский язык), en-US для США (английский язык), de-DE для Германии (немецкий язык) или de-AT для Австрии (немецкий язык). См. столбец с тегами языка в списке названий языков и регионов, поддерживаемых Windows. Теги языков указаны в соответствии со стандартом BCP 47.

Примечание.

Существует несколько расхождений в названиях тегов, как в случае zh-Hans для китайского (упрощенное письмо).

Дополнительные сведения см. в разделах Создание файлов ресурсов и Создание вспомогательных сборок.

Процесс использования резервных ресурсов

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

Чтобы повысить эффективность поиска, примените атрибут NeutralResourcesLanguageAttribute к главной сборке и передайте его имени нейтрального языка, который будет использоваться с главной сборкой.

Процесс использования резервных ресурсов .NET Framework

Процесс использования резервных ресурсов .NET Framework включает следующие шаги.

Совет

Можно использовать <элемент конфигурации relativeBindForResources> для оптимизации резервного процесса ресурса и процесса, с помощью которого пробы среды выполнения для сборок ресурсов. Дополнительные сведения см. в разделе Оптимизация процесса использования резервных ресурсов.

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

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

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

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

  4. Среда выполнения вызывает событие AppDomain.AssemblyResolve, чтобы указать, что не удалось найти вспомогательную сборку. При обработке события обработчик событий может возвратить ссылку на вспомогательную сборку, чьи ресурсы будут использованы для поиска. В противном случае обработчик событий возвращает null и продолжается поиск.

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

    Родительский язык и региональные параметры определяются в качестве подходящих резервных языка и региональных параметров. Родительские сборки рекомендуется использовать в качестве резервных ресурсов, так как предоставление любых ресурсов является более желательным, чем порождение исключения. Этот процесс также позволяет многократно использовать ресурсы. Включать ресурс на родительском уровне следует только в том случае, если дочерняя культурная среда не требует локализации запрошенного ресурса. Например, если заданы вспомогательные сборки для культурных сред en (нейтральный английский), en-GB (английский, используемый в Великобритании) и en-US (английский, используемый в США), то сборка en будет содержать общие термины, а в сборках en-GB и en-US достаточно переопределить только те термины, которые отличаются в этих языках.

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

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

  8. Среда выполнения вызывает событие AppDomain.AssemblyResolve, чтобы указать, что не удалось найти подходящий резервный ресурс. При обработке события обработчик событий может возвратить ссылку на вспомогательную сборку, чьи ресурсы будут использованы для поиска. В противном случае обработчик событий возвращает null и продолжается поиск.

  9. Далее среда выполнения просматривает родительские сборки по нескольким уровням, выполняя на каждом из них те же действия, что и на предыдущем шаге. Каждый язык и региональные параметры имеют только одного родителя, который определен с помощью свойства CultureInfo.Parent, при этом родитель может иметь собственного родителя. Поиск родительского языка и региональных параметров останавливается, когда свойство Parent языка и региональных параметров возвращает CultureInfo.InvariantCulture; для перехода к резервным ресурсам инвариантный язык и региональные параметры не должны являться родительским языком и региональными параметрами или языком и региональными параметрами, которые могут иметь ресурсы.

  10. Если после просмотра первоначально заданного языка и региональных параметров и всех родительских сред ресурс не найден, используется ресурс стандартного (запасного) языка и региональных параметров. Обычно ресурсы для языка региональных параметров по умолчанию включены в основную сборку приложения. Но можно задать значение Satellite для свойства Location атрибута NeutralResourcesLanguageAttribute, чтобы указать, что конечное расположение резервных ресурсов является вспомогательной сборкой, а не основной сборкой.

    Примечание.

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

  11. В конечном итоге, если среда выполнения не находит ресурса для стандартного (резервного) языка и региональных параметров, порождается исключение MissingManifestResourceException или MissingSatelliteAssemblyException, указывающее на то, что ресурс не может быть найден.

Предположим, что приложение запрашивает ресурс, локализованный для испанского языка (Мексика) (язык и региональные параметры es-MX). Сначала среда выполнения осуществит поиск в глобальном кэше сборок для сборки, которая соответствует es-MX, но не обнаружит ее. Затем, не найдя такой сборки, среда выполнения ищет каталог es-MX в каталоге выполняемой в данный момент сборки. Если этот поиск также не приносит результатов, среда выполнения снова просматривает глобальный кэш сборок в поисках родительской сборки, соответствующей резервному языку, в данном случае — es (испанский язык). Если родительская сборка не найдена, среда выполнения просматривает все возможные уровни родительских сборок для языка и региональных параметров es-MX, пока не найдет соответствующий ресурс. Если ресурс не будет найден, среда выполнения использует ресурс для языка и региональных параметров по умолчанию.

Оптимизация процесса использования резервных ресурсов .NET Framework

В следующих условиях можно оптимизировать процесс, с помощью которого среда выполнения ищет ресурсы в вспомогательных сборках:

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

  • Вспомогательные сборки не устанавливаются по требованию.

  • Код приложения не обрабатывает событие AppDomain.AssemblyResolve.

Вы оптимизируете пробу для вспомогательных сборок, включив <элемент relativeBindForResources> и задав его enabled атрибут true в файле конфигурации приложения, как показано в следующем примере.

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

Оптимизированный поиск для вспомогательных сборок — функция по требованию. То есть среда выполнения выполняет шаги, описанные в резервном процессе ресурса, если< в файле конфигурации приложения отсутствует относительный элементBindForResources>, а его enabled атрибут имеет значение true. Если это так, то процесс проверки вспомогательной сборки изменяется следующим образом:

  • Среда выполнения использует расположение родительской сборки кода для проверки вспомогательной сборки. Если родительская сборка установлена в глобальном кэше сборок, среда выполнения проверяет кэш, но не в папке приложения. Если родительская сборка установлена в каталоге приложения, среда выполнения проверяет в каталоге приложения, но не в глобальном кэше сборок.

  • Среда выполнения не запрашивает установщик Windows для установки вспомогательных сборок по требованию.

  • Если поиск конкретной сборки ресурсов завершается неудачей, среда выполнения не вызывает событие AppDomain.AssemblyResolve.

Процесс использования резервных ресурсов .NET Core

Процесс использования резервных ресурсов .NET Core включает следующие шаги.

  1. Среда выполнения пытается загрузить вспомогательную сборку для запрошенного языка и региональных параметров.

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

      Примечание.

      В операционных системах с файловыми системами, учитывающими регистр (например, Linux и macOS), поиск подкаталога с именем языка и региональных параметров выполняется с учетом регистра. Имя подкаталога должно точно соответствовать регистру CultureInfo.Name (например, es или es-MX).

      Примечание.

      Если программист создал контекст загрузки настраиваемой сборки, производный от AssemblyLoadContext, то ситуация усложняется. Если выполняемая сборка была загружена в настраиваемый контекст, среда выполнения загружает вспомогательную сборку в этот контекст. Подробности выходят за рамки этого документа. См. раздел AssemblyLoadContext.

    • Если вспомогательная сборка не нашлась, AssemblyLoadContext вызывает событие AssemblyLoadContext.Resolving, чтобы указать, что ее не удалось найти. При обработке события обработчик событий может загрузить и возвратить ссылку на вспомогательную сборку.

    • Если вспомогательная сборка по-прежнему не была найдена, AssemblyLoadContext вызывает AppDomain для запуска события AppDomain.AssemblyResolve, чтобы указать, что ее не удалось найти. При обработке события обработчик событий может загрузить и возвратить ссылку на вспомогательную сборку.

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

    Примечание.

    Чтобы найти ресурс во вспомогательной сборке, среда выполнения выполняет поиск файла ресурсов по запросу ResourceManager для текущего CultureInfo.Name. В файле ресурсов она выполняет поиск имени запрошенного ресурса. Если имя не найдено, ресурс обрабатывается как ненайденный.

  3. Среда выполнения затем выполняет поиск родительских сборок языка и региональных параметров по нескольким уровням, повторяя каждый раз действия 1 и 2.

    Родительский язык и региональные параметры определяются в качестве подходящих резервных языка и региональных параметров. Родительские сборки рекомендуется использовать в качестве резервных ресурсов, так как предоставление любых ресурсов является более желательным, чем порождение исключения. Этот процесс также позволяет многократно использовать ресурсы. Включать ресурс на родительском уровне следует только в том случае, если дочерняя культурная среда не требует локализации запрошенного ресурса. Например, если заданы вспомогательные сборки для культурных сред en (нейтральный английский), en-GB (английский, используемый в Великобритании) и en-US (английский, используемый в США), то сборка en содержит общие термины, а в сборках en-GB и en-US достаточно переопределить только те термины, которые отличаются в этих языках.

    Каждый язык и региональные параметры имеют только одного родителя, который определен с помощью свойства CultureInfo.Parent, при этом родитель может иметь собственного родителя. Поиск родительского языка и региональных параметров останавливается, когда свойство языка и региональных параметров Parent возвращает CultureInfo.InvariantCulture. Для перехода к резервным ресурсам инвариантные язык и региональные параметры не должны являться родительским языком и региональными параметрами или языком и региональными параметрами, которые могут иметь ресурсы.

  4. Если после просмотра первоначально заданного языка и региональных параметров и всех родительских сред ресурс не найден, используется ресурс стандартного (запасного) языка и региональных параметров. Обычно ресурсы для языка региональных параметров по умолчанию включены в основную сборку приложения. Но можно задать значение Satellite для свойства Location, чтобы указать, что конечное расположение резервных ресурсов является вспомогательной, а не основной сборкой.

    Примечание.

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

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

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

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

В следующем примере .NET Framework используется атрибут NeutralResourcesLanguageAttribute для хранения резервных ресурсов приложения во вспомогательной сборке для французского языка (fr). Пример содержит два текстовых файла ресурсов, которые определяют один строковой ресурс Greeting. Первый файл resources.fr.txt содержит французский языковой ресурс.

Greeting=Bon jour!

Второй файл resources.ru.txt содержит русский языковой ресурс.

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

Эти файлы компилируются в файлы с расширением RESOURCES путем запуска генератора файловых ресурсов (resgen.exe) из командной строки. Для ресурса французского языка команда выглядит следующим образом:

resgen.exe resources.fr.txt

Для ресурса русского языка команда выглядит следующим образом:

resgen.exe resources.ru.txt

RESOURCES-файлы внедряются в библиотеки DLL путем запуска из командной строки компоновщика сборок (al.exe) для ресурса французского языка следующим образом:

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

и для ресурса русского языка — следующим образом:

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

Исходный код приложения находится в файле с именем Example1.cs или Example1.vb. Он содержит атрибут NeutralResourcesLanguageAttribute, чтобы указать, что по умолчанию ресурс приложения находится в подкаталоге fr. Он создает диспетчер ресурсов, получает значение ресурса Greeting и выводит его на консоль.

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

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

csc Example1.cs

Команды для компилятора Visual Basic очень похожа:

vbc Example1.vb

Так как нет никаких ресурсов, внедренных в основную сборку, не нужно компилировать с помощью параметра /resource.

При запуске примера из системы, язык которой является отличным от русского языка, он выводит следующие результаты:

Bon jour!

Предлагаемые альтернативные методы упаковки

Финансовые или временные ограничения могут помешать создать набора ресурсов для каждого региона, который поддерживается этим приложением. Вместо этого можно создать одну вспомогательную сборку для родительского языка и региональных параметров, которую могут использовать все связанные языки и региональные параметры. Например, можно создать одну вспомогательную сборку для английского языка (en), которая будет вызываться пользователями при запросе англоязычных ресурсов для конкретного региона, и одну сборку на немецком (de) для пользователей, запрашивающих ресурсы немецкоязычной версии для определенного региона. Так, запросы ресурсов для немецкоязычной версии Германии (de-DE), немецкоязычной версии Австрии (de-AT) и немецкоязычной версии Швейцарии (de-CH) будут переданы во вспомогательную сборку для немецкого языка (de). Стандартные ресурсы представляют собой окончательные запасные варианты, поэтому в этом качестве следует использовать ресурсы, которые будут запрашиваться большинством пользователей создаваемого приложения, поэтому необходимо выбирать эти ресурсы внимательно. В данном подходе будут задействованы ресурсы более общего характера, что позволит существенно снизить затраты на локализацию приложения.

См. также