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


Использование системы управления ресурсами Windows 10 в устаревшем приложении или игре

Приложения и игры .NET и Win32 часто локализованы на разных языках, чтобы расширить общий адресируемый рынок. Дополнительные сведения о ценности локализации приложения см. в разделе Глобализация и локализация. Упаковав приложение или игру .NET или Win32 в виде пакета MSIX или .appx, вы можете использовать систему управления ресурсами для загрузки ресурсов, адаптированных к контексту времени выполнения. В этом подробном разделе описываются методы.

Существует множество способов локализации традиционного приложения Win32, но Windows 8 представила новую систему управления ресурсами , которая работает на разных языках программирования, в разных типах приложений и предоставляет функциональные возможности над простой локализацией. Эта система будет называться "MRT" в этом разделе. Изначально это означало "Современные ресурсы технологий", но от термина "Современные" отказались. Диспетчер ресурсов также может быть известен как MRM (современный диспетчер ресурсов) или PRI (индекс ресурсов пакета).

В сочетании с развертыванием на основе MSIX или на основе .appx (например, из Microsoft Store) MRT может автоматически доставлять наиболее применимые ресурсы для конкретного пользователя или устройства, что сводит к минимуму размер загрузки и установки приложения. Это уменьшение размера может быть значительным для приложений с большим объемом локализованного контента, возможно, в несколько гигабайтов для AAA-игр. Дополнительные преимущества MRT включают локализованные списки в Оболочке Windows и Microsoft Store, автоматическую резервную логику, если предпочтительный язык пользователя не соответствует доступным ресурсам.

В этом документе описывается высокоуровневая архитектура MRT и предоставляется руководство по переносу устаревших приложений Win32 в MRT с минимальными изменениями кода. После перехода к MRT дополнительные преимущества (например, возможность сегментировать ресурсы по коэффициенту масштабирования или системной теме) становятся доступными для разработчика. Обратите внимание, что локализация на основе MRT работает как для приложений UWP, так и для приложений Win32, обработанных с помощью Desktop Bridge (также известных как «Centennial»).

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

Работа Преимущества Расчетная стоимость
Локализация манифеста пакета Не требуется минимальная работа, необходимая для отображения локализованного содержимого в оболочке Windows и в Microsoft Store Небольшой
Использование MRT для идентификации и поиска ресурсов Предварительные требования для минимизации размеров загрузки и установки; Автоматический резервный вариант языка Средний
Создание пакетов ресурсов Последний шаг, чтобы свести к минимуму размеры загрузки и установки Небольшой
Миграция на форматы ресурсов и API MRT Значительно меньше размеров файлов (в зависимости от существующей технологии ресурсов) Крупный

Введение

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

Таким образом, основной целью любой технологии управления ресурсами является перевод, во время выполнения запрос на логического или символьного ресурса (например, ) в максимально возможное фактическое значение (например, "Сохранить") из набора возможных кандидатов (например, "Сохранить", "Speichern" или "저장"). MRT предоставляет такую функцию и позволяет приложениям определять кандидатов ресурсов, используя широкий спектр атрибутов, называемых квалификаторами, такими как язык пользователя, коэффициент масштабирования дисплея, выбранная тема пользователя и другие факторы окружающей среды. MRT даже поддерживает пользовательские квалификаторы для приложений, если это необходимо (например, приложение может предоставить различные графические ресурсы для пользователей, которые вошли с помощью учетной записи, и гостевых пользователей, не добавляя этот проверочный механизм в каждую часть приложения). MRT работает как со строковыми ресурсами, так и с файловыми ресурсами, где файловые ресурсы реализуются в виде ссылок на внешние данные (сами файлы).

Пример

Ниже приведен простой пример приложения с текстовыми метками на двух кнопках (openButton и saveButton) и PNG-файле, используемом для логотипа (logoImage). Текстовые метки локализованы на английском и немецком языках, и логотип оптимизирован для обычных экранов рабочего стола (100% коэффициент масштабирования) и телефонов с высоким разрешением (300% коэффициент масштабирования). Обратите внимание, что на этой схеме представлено высокоуровневое концептуальное представление модели; он не сопоставляется точно с реализацией.

Скриншот метки исходного кода, метки таблицы подстановки и метки файлов на диске.

На рисунке код приложения ссылается на три имена логических ресурсов. Во время выполнения псевдо-функция GetResource использует MRT для поиска этих имен ресурсов в таблице ресурсов (известной как PRI-файл) и находит наиболее подходящего кандидата на основе условий окружающей среды (язык пользователя и масштаб отображения). В случае меток строки используются напрямую. В случае изображения логотипа строки интерпретируются как имена файлов, а файлы считываются с диска.

Если пользователь говорит на языке, отличном от английского или немецкого, либо с коэффициентом масштабирования, отличным от 100% или 300%, MRT выбирает ближайший подходящий кандидат на основе набора резервных правил (см. в системе управления ресурсами для получения дополнительных сведений).

Обратите внимание, что MRT поддерживает ресурсы, адаптированные к нескольким квалификаторам — например, если изображение логотипа содержит встроенный текст, который также необходимо локализовать, у логотипа будет четыре кандидата: EN/Scale-100, DE/Scale-100, EN/Scale-300 и DE/Scale-300.

Разделы в этом документе

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

Этап 0. Создание пакета приложения

В этом разделе описывается, как подготовить ваше существующее настольное приложение для создания в виде пакета приложений. На этом этапе функции MRT не используются.

Этап 1. Локализация манифеста приложения

В этом разделе описывается локализация манифеста приложения (чтобы оно отображалось правильно в оболочке Windows) при использовании устаревшего формата ресурсов и API для упаковки и поиска ресурсов.

Этап 2. Использование MRT для идентификации и поиска ресурсов

В этом разделе описывается изменение кода приложения (и, возможно, макета ресурсов) для поиска ресурсов с помощью MRT, а также использование существующих форматов ресурсов и API для загрузки и использования ресурсов.

Этап 3. Создание пакетов ресурсов

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

Не рассматривается в этом документе

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

Этап 4. Миграция в форматы ресурсов MRT и API

Этот этап выходит за рамки этого документа; это влечет за собой перемещение ресурсов (особенно строк) из устаревших форматов, таких как библиотеки DLL MUI или сборки ресурсов .NET в файлы PRI. Это может привести к дальнейшей экономии места при загрузке и установке. Он также позволяет использовать другие функции MRT, такие как минимизация загрузки и установки файлов изображений на основе коэффициента масштабирования, параметров специальных возможностей и т. д.

Этап 0. Создание пакета приложения

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

  • Если у вас есть большое классическое приложение с сложным установщиком или используется множество точек расширяемости ОС, вы можете использовать средство Desktop App Converter для создания макета файла UWP и сведений о манифесте из существующего установщика приложений (например, MSI).
  • Если у вас есть небольшое настольное приложение с относительно небольшим количеством файлов или простым установщиком и без возможностей расширения, вы можете создать макет файла и информацию манифеста вручную.
  • Если вы пересобираете из исходного кода и хотите обновить приложение, чтобы сделать его чистым приложением UWP, вы можете создать новый проект в Visual Studio и довериться интегрированной среде разработки, чтобы выполнить большую часть работы за вас.

Если вы хотите использовать Конвертер приложений рабочего стола, см. Упаковка приложения рабочего стола с помощью Конвертера приложений рабочего стола для получения дополнительных сведений о процессе преобразования. Полный набор примеров Desktop Converter можно найти в репозитории GitHub моста Desktop Bridge для UWP.

Замечание

Преобразователь приложений для настольных ПК устарел. Пожалуйста, используйте новое и усовершенствованное средство MSIX для упаковки настольных приложений.

Если вы хотите вручную создать пакет, необходимо создать структуру каталогов, содержащую все файлы приложения (исполняемые файлы и содержимое, но не исходный код) и файл манифеста пакета (APPxmanifest). Пример можно найти в GitHub-примереHello, World, но базовый файл манифеста пакета, который запускает исполняемый файл рабочего стола под названием ContosoDemo.exe, выглядит следующим образом: выделенный текст будет заменен собственными значениями.

<?xml version="1.0" encoding="utf-8" ?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
         xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
         xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
         xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
         IgnorableNamespaces="uap mp rescap">
    <Identity Name="Contoso.Demo"
              Publisher="CN=Contoso.Demo"
              Version="1.0.0.0" />
    <Properties>
    <DisplayName>Contoso App</DisplayName>
    <PublisherDisplayName>Contoso, Inc</PublisherDisplayName>
    <Logo>Assets\StoreLogo.png</Logo>
  </Properties>
    <Dependencies>
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14393.0" 
                        MaxVersionTested="10.0.14393.0" />
  </Dependencies>
    <Resources>
    <Resource Language="en-US" />
  </Resources>
    <Applications>
    <Application Id="ContosoDemo" Executable="ContosoDemo.exe" 
                 EntryPoint="Windows.FullTrustApplication">
    <uap:VisualElements DisplayName="Contoso Demo" BackgroundColor="#777777" 
                        Square150x150Logo="Assets\Square150x150Logo.png" 
                        Square44x44Logo="Assets\Square44x44Logo.png" 
        Description="Contoso Demo">
      </uap:VisualElements>
    </Application>
  </Applications>
    <Capabilities>
    <rescap:Capability Name="runFullTrust" />
  </Capabilities>
</Package>

Более подробную информацию о файле манифеста пакета и структуре пакета см. в манифесте пакета приложения.

Наконец, если вы используете Visual Studio для создания нового проекта и переноса вашего существующего кода, см. статью о создании приложения "Hello, world". Вы можете включить существующий код в новый проект, но вам, скорее всего, придется внести значительные изменения кода (особенно в пользовательском интерфейсе), чтобы запустить как чистое приложение UWP. Эти изменения находятся вне области этого документа.

Этап 1. Локализация манифеста

Шаг 1.1. Обновление строк и ресурсов в манифесте

На этапе 0 вы создали базовый файл манифеста пакета (.appxmanifest) для приложения (на основе значений, предоставленных преобразователю, извлеченных из MSI или вручную введенных в манифест), но он не будет содержать локализованные сведения, а также не будет поддерживать дополнительные функции, такие как ресурсы с высоким разрешением начальных плиток и т. д.

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

Создание файла ресурсов по умолчанию

Первым шагом является создание файла ресурсов по умолчанию на языке по умолчанию (например, на английском языке). Это можно сделать вручную с помощью текстового редактора или с помощью конструктора ресурсов в Visual Studio.

Если вы хотите создать ресурсы вручную, выполните следующие действия.

  1. Создайте XML-файл с именем resources.resw и поместите его в папку Strings\en-us внутри вашего проекта. Используйте соответствующий код BCP-47, если язык по умолчанию не является американским вариантом английского.
  2. В XML-файле добавьте следующее содержимое, где выделенный текст заменяется соответствующим текстом приложения на языке по умолчанию.

Замечание

Существуют ограничения на длину некоторых из этих строк. Дополнительные сведения см. в разделе VisualElements.

<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="ApplicationDescription">
    <value>Contoso Demo app with localized resources (English)</value>
  </data>
  <data name="ApplicationDisplayName">
    <value>Contoso Demo Sample (English)</value>
  </data>
  <data name="PackageDisplayName">
    <value>Contoso Demo Package (English)</value>
  </data>
  <data name="PublisherDisplayName">
    <value>Contoso Samples, USA</value>
  </data>
  <data name="TileShortName">
    <value>Contoso (EN)</value>
  </data>
</root>

Если вы хотите использовать конструктор в Visual Studio:

  1. Создайте в проекте папку Strings\en-us (или другой язык) и добавьте новый элемент в корневую папку проекта, используя имя resources.reswпо умолчанию. Обязательно выберите файл ресурсов (.resw), а не словарь ресурсов — это файл, который используется в XAML-приложениях.
  2. Используя конструктор, введите следующие строки (используйте тот же Names, но замените Values на соответствующий текст для вашего приложения):

снимок экрана файла Resources.resw, показывающий столбцы

Замечание

Если начать с конструктора Visual Studio, вы всегда можете редактировать XML напрямую, нажав клавишу F7. Но если начать с минимального XML-файла, конструктор не распознает файл, поскольку в нем отсутствует много дополнительных метаданных; вы можете это исправить, скопировав стандартные сведения XSD из созданного конструктором файла в ваш вручную отредактированный XML-файл.

Обновление манифеста для ссылки на ресурсы

После определения значений в .resw файле следующим шагом является обновление манифеста для ссылки на строки ресурсов. Опять же, вы можете редактировать XML-файл напрямую или полагаться на конструктор манифестов Visual Studio.

Если вы редактируете XML напрямую, откройте файл AppxManifest.xml и внесите следующие изменения в выделенные значения и. Используйте этот точный текст, а не текст, характерный для вашего приложения. Нет необходимости использовать эти точные имена ресурсов ( вы можете выбрать свой собственный), но все, что вы выбираете, должно точно соответствовать тому, что находится в .resw файле. Эти имена должны совпадать с Names, который вы создали в файле .resw, с префиксом в соответствии со схемой ms-resource: и пространством имен Resources/.

Замечание

Многие элементы манифеста были опущены из этого фрагмента кода. Не удаляйте ничего!

<?xml version="1.0" encoding="utf-8"?>
<Package>
  <Properties>
    <DisplayName>ms-resource:Resources/PackageDisplayName</DisplayName>
    <PublisherDisplayName>ms-resource:Resources/PublisherDisplayName</PublisherDisplayName>
  </Properties>
  <Applications>
    <Application>
      <uap:VisualElements DisplayName="ms-resource:Resources/ApplicationDisplayName"
        Description="ms-resource:Resources/ApplicationDescription">
        <uap:DefaultTile ShortName="ms-resource:Resources/TileShortName">
          <uap:ShowNameOnTiles>
            <uap:ShowOn Tile="square150x150Logo" />
          </uap:ShowNameOnTiles>
        </uap:DefaultTile>
      </uap:VisualElements>
    </Application>
  </Applications>
</Package>

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

снимок экрана конструктора манифестов Visual Studio, показывающий вкладку

Снимок экрана конструктора манифестов Visual Studio, на котором показана вкладка

Шаг 1.2. Сборка PRI-файла, создание пакета MSIX и проверка его работы

Теперь вы можете создать .pri файл и развернуть приложение, чтобы убедиться, что в меню "Пуск" отображается правильная информация (на языке по умолчанию).

Если вы создаете в Visual Studio, просто нажмите Ctrl+Shift+B , чтобы создать проект, а затем щелкните проект правой кнопкой мыши и выберите Deploy в контекстном меню.

Если вы собираете вручную, выполните следующие действия, чтобы создать файл конфигурации для средства MakePRI и сам файл .pri (подробности в руководстве по упаковке приложений ):

  1. Откройте командную строку разработчика из папки Visual Studio 2019 или Visual Studio 2022 в меню "Пуск".

  2. Перейдите в корневой каталог проекта (тот, который содержит файл .appxmanifest и папку Strings ).

  3. Введите следующую команду, заменив "contoso_demo.xml" именем, подходящим для проекта, и "en-US" языком приложения по умолчанию (или сохраняйте его en-US, если применимо). Обратите внимание, что XML-файл создается в родительском каталоге (не в каталоге проекта), так как он не является частью приложения (вы можете выбрать любой другой каталог, который вы хотите, но обязательно замените его в будущих командах).

    makepri createconfig /cf ..\contoso_demo.xml /dq en-US /pv 10.0 /o
    

    Вы можете ввести makepri createconfig /?, чтобы узнать, что делает каждый параметр, но вкратце:

    • /cf задает имя файла конфигурации (выходные данные этой команды)
    • /dq задает квалификаторы по умолчанию, в этом случае язык en-US
    • /pv задает версию платформы, в данном случае Windows 10
    • /o задает режим перезаписи выходного файла, если файл существует
  4. Теперь у вас есть файл конфигурации, запустите MakePRI еще раз, чтобы выполнить поиск диска для ресурсов и упаковать их в PRI-файл. Замените "contoso_demop.xml" именем XML-файла, использованным на предыдущем шаге, и обязательно укажите родительский каталог для входных и выходных данных:

    makepri new /pr . /cf ..\contoso_demo.xml /of ..\resources.pri /mf AppX /o
    

    Вы можете ввести makepri new /? сведения о том, что делает каждый параметр, но в кратце:

    • /pr задает корневой каталог проекта (в данном случае текущий каталог)
    • /cf задает имя файла конфигурации, созданное на предыдущем шаге
    • /of задает выходной файл
    • /mf создает файл сопоставления (поэтому мы можем исключить файлы в пакете на следующем шаге)
    • /o задает режим перезаписи выходного файла, если файл существует
  5. Теперь у вас есть .pri файл с языковыми ресурсами по умолчанию (например, en-US). Чтобы убедиться в правильности работы, выполните следующую команду:

    makepri dump /if ..\resources.pri /of ..\resources /o
    

    Вы можете ввести makepri dump /? сведения о том, что делает каждый параметр, но в кратце:

    • /if задает имя входного файла
    • /of задает имя выходного файла (.xml будет добавлено автоматически)
    • /o задает режим перезаписи выходного файла, если файл существует
  6. Наконец, можно открыть ..\resources.xml в текстовом редакторе и проверить, перечислены ли <NamedResource> значения (например, ApplicationDescription и PublisherDisplayName) вместе с <Candidate> значениями выбранного языка по умолчанию (в начале файла будет другое содержание; пока не обращайте на это внимания).

Вы можете открыть файл сопоставления ..\resources.map.txt, чтобы убедиться, что он содержит файлы, необходимые для вашего проекта (включая файл PRI, который не является частью каталога проекта). Важно, что файл сопоставления не включать ссылку на файл resources.resw, так как содержимое этого файла уже внедрено в PRI-файл. Однако он будет содержать другие ресурсы, такие как имена файлов изображений.

Создание и подписание пакета

Теперь PRI-файл построен, вы можете создать и подписать пакет:

  1. Чтобы создать пакет приложения, выполните следующую команду, заменив contoso_demo.appx имя файла MSIX/.appx, который вы хотите создать, и убедитесь, что выберите другой каталог для файла (в этом примере используется родительский каталог; он может быть в любом месте, но не должен быть каталогом проекта).

    makeappx pack /m AppXManifest.xml /f ..\resources.map.txt /p ..\contoso_demo.appx /o
    

    Вы можете ввести makeappx pack /? сведения о том, что делает каждый параметр, но в кратце:

    • /m задает используемый файл манифеста
    • /f задает используемый файл сопоставления (созданный на предыдущем шаге)
    • /p задает имя выходного пакета
    • /o задает режим перезаписи выходного файла, если файл существует
  2. После создания пакета его необходимо подписать. Самый простой способ получить сертификат подписи — создать пустой универсальный проект Windows в Visual Studio и скопировать .pfx создаваемый файл, но его можно создать вручную с помощью MakeCert служебных Pvk2Pfx программ, как описано в разделе "Создание сертификата подписи пакета приложения".

    Это важно

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

  3. Чтобы подписать пакет, используйте следующую команду. Обратите внимание, что Publisher, указанные в элементе Identity элемента AppxManifest.xml, должны соответствовать Subject сертификата (это , а не, элемент <PublisherDisplayName>, который является локализованным отображаемым именем для отображения пользователям). Как обычно, замените contoso_demo... имена файлов именами, соответствующими вашему проекту, и убедитесь, что .pfx файл не находится в текущем каталоге (в противном случае он был создан в составе пакета, включая закрытый ключ подписи!):

    signtool sign /fd SHA256 /a /f ..\contoso_demo_key.pfx ..\contoso_demo.appx
    

    Вы можете ввести signtool sign /? сведения о том, что делает каждый параметр, но в кратце:

    • /fd задает алгоритм дайджеста файлов (SHA256 по умолчанию для .appx)
    • /a Автоматически выбирает лучший сертификат
    • /f указывает входной файл, содержащий сертификат подписи

Наконец, теперь вы можете дважды щелкнуть .appx файл, чтобы установить его, или если вы предпочитаете командную строку, можно открыть строку PowerShell, перейти в каталог, содержащий пакет, и ввести следующее (заменив contoso_demo.appx его именем):

add-appxpackage contoso_demo.appx

Если вы получаете сообщения об ошибках, что сертификат не является доверенным, убедитесь, что он добавлен в системное хранилище (, а не в пользовательское хранилище). Чтобы добавить сертификат в хранилище компьютеров, можно использовать командную строку или проводник Windows.

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

  1. Запустите командную строку Visual Studio 2019 или Visual Studio 2022 от имени администратора.

  2. Перейдите в каталог, содержащий .cer файл (не забудьте убедиться, что это находится за пределами исходных каталогов или каталогов проектов!)

  3. Введите следующую команду, заменив contoso_demo.cer на имя файла:

    certutil -addstore TrustedPeople contoso_demo.cer
    

    Вы можете запустить certutil -addstore /?, чтобы узнать, что делает каждый параметр, но вкратце:

    • -addstore добавляет сертификат в хранилище сертификатов
    • TrustedPeople указывает хранилище, в которое помещается сертификат

Чтобы использовать проводник Windows, выполните приведенные действия.

  1. Перейдите в папку, содержащую .pfx файл
  2. Дважды щелкните .pfx файл и откроется мастер импорта сертификатов
  3. Выберите Local Machine и щелкните Next
  4. Подтвердите запрос повышения прав администрирования в UAC, если он отображается, и щелкните Next.
  5. Введите пароль для закрытого ключа, если есть один, и нажмите кнопку Next
  6. Выберите Place all certificates in the following store
  7. Щелкните Browseи выберите папку Trusted People (не "Доверенные издатели")
  8. Щелкните Next и затем Finish

После добавления сертификата в Trusted People хранилище повторите установку пакета.

Теперь ваше приложение должно появиться в списке "Все приложения" меню "Пуск" с правильными сведениями из файла .resw / .pri. Если вы видите пустую строку или строку ms-resource:... , то что-то пошло не так, дважды проверьте изменения и убедитесь, что они правильные. Если щелкнуть приложение правой кнопкой мыши в меню "Пуск", вы можете закрепить его как плитку и убедиться, что там также отображаются правильные сведения.

Шаг 1.3. Добавление дополнительных поддерживаемых языков

После внесения изменений в манифест пакета и создания исходного resources.resw файла можно легко добавить дополнительные языки.

Создание дополнительных локализованных ресурсов

Сначала создайте дополнительные локализованные значения ресурсов.

В папке Strings создайте дополнительные папки для каждого языка, поддерживаемого с помощью соответствующего кода BCP-47 (например, Strings\de-DE). В каждой из этих папок создайте resources.resw файл (с помощью редактора XML или конструктора Visual Studio), включающего преобразованные значения ресурсов. Предполагается, что у вас уже есть локализованные строки, доступные где-то, и вам просто нужно скопировать их в .resw файл. Этот документ не охватывает сам шаг перевода.

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

<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="ApplicationDescription">
    <value>Contoso Demo app with localized resources (German)</value>
  </data>
  <data name="ApplicationDisplayName">
    <value>Contoso Demo Sample (German)</value>
  </data>
  <data name="PackageDisplayName">
    <value>Contoso Demo Package (German)</value>
  </data>
  <data name="PublisherDisplayName">
    <value>Contoso Samples, DE</value>
  </data>
  <data name="TileShortName">
    <value>Contoso (DE)</value>
  </data>
</root>

В следующих шагах предполагается, что вы добавили ресурсы для обоих de-DE и fr-FR, но один и тот же шаблон можно использовать для любого языка.

Обновление манифеста пакета до списка поддерживаемых языков

Манифест пакета должен быть обновлен для перечисления языков, поддерживаемых приложением. Desktop App Converter добавляет язык по умолчанию, но другие должны быть добавлены явным образом. Если вы редактируете файл AppxManifest.xml напрямую, обновите узел Resources следующим образом, добавив столько элементов, сколько вам нужно, и замените нужные соответствующие языки, которые вы поддерживаете, и убедитесь, что первая запись в списке является языком по умолчанию (резервный вариант). В этом примере по умолчанию используется английский (США) с дополнительной поддержкой как немецкого (Германия) так и французского (Франция):

<Resources>
  <Resource Language="EN-US" />
  <Resource Language="DE-DE" />
  <Resource Language="FR-FR" />
</Resources>

Если вы используете Visual Studio, вам не нужно ничего делать; Если вы посмотрите Package.appxmanifest , следует увидеть специальное значение x-generate , которое приводит к тому, что процесс сборки вставляет языки, которые он находит в проекте (на основе папок с кодами BCP-47). Обратите внимание, что это недопустимое значение для реального манифеста пакета; он работает только для проектов Visual Studio:

<Resources>
  <Resource Language="x-generate" />
</Resources>

Повторная сборка с локализованными значениями

Теперь вы можете создать и развернуть приложение, опять же, и если вы измените языковые предпочтения в Windows, в меню "Пуск" отображаются только что локализованные значения (инструкции по изменению языка приведены ниже).

Для Visual Studio можно просто использовать Ctrl+Shift+B для сборки и щелкнуть проект правой кнопкой мыши, чтобы Deploy.

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

makepri createconfig /cf ..\contoso_demo.xml /dq en-US_de-DE_fr-FR /pv 10.0 /o

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

Тестирование с локализованными значениями

Чтобы протестировать новые локализованные изменения, вы просто добавите новый предпочитаемый язык пользовательского интерфейса в Windows. Нет необходимости скачивать языковые пакеты, перезагружать систему или отображать весь пользовательский интерфейс Windows на иностранном языке.

  1. Settings Запуск приложения (Windows + I)
  2. Перейдите по адресу Time & language
  3. Перейдите по адресу Region & language
  4. Щелкните Add a language.
  5. Введите (или выберите) нужный язык (например Deutsch , или German)
    • Если есть вложенные языки, выберите нужный (например, Deutsch / Deutschland)
  6. Выбор нового языка в списке языков
  7. Щелкните Set as default.

Теперь откройте меню "Пуск" и найдите приложение, и вы увидите локализованные значения выбранного языка (другие приложения также могут отображаться локализованными). Если локализованное имя не отображается сразу, подождите несколько минут, пока кэш меню "Пуск" не будет обновлен. Чтобы вернуться к собственному языку, просто сделайте его языком по умолчанию в списке языков.

Шаг 1.4. Локализация дополнительных частей манифеста пакета (необязательно)

Можно локализовать другие разделы манифеста пакета. Например, если приложение обрабатывает расширения файлов, оно должно иметь windows.fileTypeAssociation расширение в манифесте, используя зеленый выделенный текст точно так же, как показано (так как он будет ссылаться на ресурсы) и заменяя желтый выделенный текст информацией, относяющейся к приложению:

<Extensions>
  <uap:Extension Category="windows.fileTypeAssociation">
    <uap:FileTypeAssociation Name="default">
      <uap:DisplayName>ms-resource:Resources/FileTypeDisplayName</uap:DisplayName>
      <uap:Logo>Assets\StoreLogo.png</uap:Logo>
      <uap:InfoTip>ms-resource:Resources/FileTypeInfoTip</uap:InfoTip>
      <uap:SupportedFileTypes>
        <uap:FileType ContentType="application/x-contoso">.contoso</uap:FileType>
      </uap:SupportedFileTypes>
    </uap:FileTypeAssociation>
  </uap:Extension>
</Extensions>

Эти сведения также можно добавить с помощью конструктора манифестов Visual Studio, используя вкладку Declarations, и обратить внимание на выделенные значения .

Снимок экрана конструктора манифестов Visual Studio, показывающий вкладку

Теперь добавьте соответствующие имена ресурсов в каждый из файлов , заменив выделенный текст на соответствующий текст приложения (не забудьте сделать это для каждом поддерживаемом языке!):

... existing content...
<data name="FileTypeDisplayName">
  <value>Contoso Demo File</value>
</data>
<data name="FileTypeInfoTip">
  <value>Files used by Contoso Demo App</value>
</data>

Затем это отобразится в элементах оболочки Windows, таких как Проводник.

Снимок экрана проводника с всплывающим окном, в котором указано, что файлы используются приложением Contoso Demo App.

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

Этап 2. Использование MRT для идентификации и поиска ресурсов

В предыдущем разделе показано, как использовать MRT для локализации файла манифеста приложения, чтобы оболочка Windows правильно отображала имя приложения и другие метаданные. Для этого не требуется никаких изменений кода; он просто требует использования .resw файлов и некоторых дополнительных средств. В этом разделе показано, как использовать MRT для поиска ресурсов в существующих форматах ресурсов и использования существующего кода обработки ресурсов с минимальными изменениями.

Предположения о существующем макете файла и коде приложения

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

Макет файла ресурсов

В этой статье предполагается, что локализованные ресурсы имеют одинаковые имена файлов (например, contoso_demo.exe.mui или contoso_strings.dllcontoso.strings.xml) но они помещаются в разные папки с именами BCP-47 (en-US, de-DEи т. д.). Это не имеет значения, сколько файлов ресурсов у вас есть, каковы их имена, какие форматы файлов / связанные API и т. д. Единственное, что важно, заключается в том, что каждый логический ресурс имеет одно и то же имя файла (но помещается в другой физический каталог).

В качестве противоположного примера, если ваше приложение использует плоскую файловую структуру с одним каталогом Resources, содержащим файлы english_strings.dll и french_strings.dll, это не будет хорошо сопоставляться с MRT. Более эффективная структура будет каталогом Resources с подкаталогами и файлами en\strings.dll и fr\strings.dll. Кроме того, можно использовать одно и то же базовое имя файла с такими встроенными квалификаторами, как strings.lang-en.dll и strings.lang-fr.dll, однако использование каталогов с языковыми кодами концептуально проще, поэтому мы сосредоточимся на этом.

Замечание

По-прежнему можно использовать MRT и преимущества упаковки, даже если вы не можете следовать этому соглашению об именовании файлов; это просто требует больше работы.

Например, приложение может иметь набор пользовательских команд пользовательского интерфейса (используемых для меток кнопок и т. д.) в простом текстовом файле с именемui.txt, который выложен в папку UICommands :

+ ProjectRoot
|--+ Strings
|  |--+ en-US
|  |  \--- resources.resw
|  \--+ de-DE
|     \--- resources.resw
|--+ UICommands
|  |--+ en-US
|  |  \--- ui.txt
|  \--+ de-DE
|     \--- ui.txt
|--- AppxManifest.xml
|--- ...rest of project...

Код загрузки ресурсов

В этой статье предполагается, что в какой-то момент в коде вы хотите найти файл, содержащий локализованный ресурс, загрузить его, а затем использовать его. API, используемые для загрузки ресурсов, API, используемые для извлечения ресурсов и т. д., не важны. В псевдокоде в основном три шага:

set userLanguage = GetUsersPreferredLanguage()
set resourceFile = FindResourceFileForLanguage(MY_RESOURCE_NAME, userLanguage)
set resource = LoadResource(resourceFile) 
    
// now use 'resource' however you want

MRT требует только изменения первых двух шагов в этом процессе - как определить лучшие ресурсы кандидатов и как их найти. Это не требует изменения того, как вы загружаете или используете ресурсы (хотя он предоставляет средства для этого, если вы хотите воспользоваться преимуществами).

Например, приложение может использовать API GetUserPreferredUILanguagesWin32, функцию sprintfCRT и API CreateFile Win32 для замены трех функций псевдокода выше, а затем вручную проанализировать текстовый файл, который ищет name=value пары. (Сведения не важны. Это просто для иллюстрации того, что MRT не влияет на методы, используемые для обработки ресурсов после их расположения).

Шаг 2.1. Изменение кода для поиска файлов с помощью MRT

Переключение кода на использование MRT для поиска ресурсов несложно. Для этого требуется использование нескольких типов WinRT и нескольких строк кода. Основные типы, которые будут использоваться, приведены ниже.

  • ResourceContext, который инкапсулирует текущий активный набор значений квалификатора (язык, коэффициент масштабирования и т. д.).
  • ResourceManager (версия WinRT, а не версия .NET), которая обеспечивает доступ ко всем ресурсам из файла PRI.
  • ResourceMap, представляющий определенное подмножество ресурсов в файле PRI (в этом примере файловые ресурсы и строковые ресурсы)
  • NamedResource, представляющий логический ресурс и все возможные кандидаты.
  • ResourceCandidate, представляющий один конкретный ресурс кандидата

В псевдокоде способ разрешения заданного имени файла ресурса (например UICommands\ui.txt , в примере выше) выглядит следующим образом:

// Get the ResourceContext that applies to this app
set resourceContext = ResourceContext.GetForViewIndependentUse()
    
// Get the current ResourceManager (there's one per app)
set resourceManager = ResourceManager.Current
    
// Get the "Files" ResourceMap from the ResourceManager
set fileResources = resourceManager.MainResourceMap.GetSubtree("Files")
    
// Find the NamedResource with the logical filename we're looking for,
// by indexing into the ResourceMap
set desiredResource = fileResources["UICommands\ui.txt"]
    
// Get the ResourceCandidate that best matches our ResourceContext
set bestCandidate = desiredResource.Resolve(resourceContext)
   
// Get the string value (the filename) from the ResourceCandidate
set absoluteFileName = bestCandidate.ValueAsString

Обратите внимание, что код не запрашивает определенную языковую папку, как UICommands\en-US\ui.txt, хотя файлы существуют на диске. Вместо этого он запрашивает логические имя файла UICommands\ui.txt и использует MRT, чтобы найти соответствующий файл на диске в одном из каталогов языка.

Отсюда пример приложения может продолжать использовать CreateFile для загрузки absoluteFileName и синтаксического анализа пар name=value так же, как и раньше; ни одна из этой логики не должна изменяться в приложении. Если вы пишете в C# или C++/CX, фактический код не гораздо сложнее, чем это (и на самом деле многие промежуточные переменные могут быть многочисленными) — см. раздел о загрузке ресурсов .NETниже. Приложения на базе C++/WRL будут более сложными из-за низкоуровневых API на базе COM, которые используются для активации и вызова API WinRT, но основные шаги будут теми же. См. раздел о загрузке ресурсов MUI в Win32, ниже.

Загрузка ресурсов .NET

Так как .NET имеет встроенный механизм для поиска и загрузки ресурсов (известных как "вспомогательные сборки"), в .NET нет необходимости заменять код, как это сделано в синтетическом примере выше. Вам просто нужно поместить ваши ресурсные DLL в соответствующие каталоги, и они автоматически обнаруживаются. Если приложение упаковано как MSIX или .appx с помощью пакетов ресурсов, структура каталогов немного отличается: директории ресурсов не являются подкаталогами основного каталога приложения, а находятся на одном уровне с ним (или отсутствуют, если язык не указан в настройках пользователя).

Например, представьте приложение .NET со следующим макетом, где все файлы существуют в папке MainApp :

+ MainApp
|--+ en-us
|  \--- MainApp.resources.dll
|--+ de-de
|  \--- MainApp.resources.dll
|--+ fr-fr
|  \--- MainApp.resources.dll
\--- MainApp.exe

После преобразования в .appx макет будет выглядеть примерно так, при условии, что en-US является языком по умолчанию, и в списке языков пользователя указаны как немецкий, так и французский.

+ WindowsAppsRoot
|--+ MainApp_neutral
|  |--+ en-us
|  |  \--- MainApp.resources.dll
|  \--- MainApp.exe
|--+ MainApp_neutral_resources.language_de
|  \--+ de-de
|     \--- MainApp.resources.dll
\--+ MainApp_neutral_resources.language_fr
   \--+ fr-fr
      \--- MainApp.resources.dll

Так как локализованные ресурсы больше не существуют в вложенных каталогах в директории установки основного исполняемого файла программы, встроенное разрешение ресурсов .NET не удается. К счастью, .NET имеет четко определенный механизм обработки неудачных попыток загрузки сборки — AssemblyResolve событие. Приложение .NET с помощью MRT должно зарегистрировать это событие и предоставить недостающую сборку для подсистемы ресурсов .NET.

Краткий пример использования API WinRT для поиска спутниковых сборок, используемых .NET, выглядит следующим образом: представленный код намеренно упрощён, чтобы показать минимальную реализацию, хотя видно, что он тесно соответствует псевдокоду выше, где переданный ResolveEventArgs предоставляет имя сборки, которую необходимо найти. Запустите версию этого кода (с подробными комментариями и обработкой ошибок) можно найти в файле PriResourceRsolver.cs в примере сопоставителя сборок .NET наGitHub.

static class PriResourceResolver
{
  internal static Assembly ResolveResourceDll(object sender, ResolveEventArgs args)
  {
    var fullAssemblyName = new AssemblyName(args.Name);
    var fileName = string.Format(@"{0}.dll", fullAssemblyName.Name);

    var resourceContext = ResourceContext.GetForViewIndependentUse();
    resourceContext.Languages = new[] { fullAssemblyName.CultureName };

    var resource = ResourceManager.Current.MainResourceMap.GetSubtree("Files")[fileName];

    // Note use of 'UnsafeLoadFrom' - this is required for apps installed with .appx, but
    // in general is discouraged. The full sample provides a safer wrapper of this method
    return Assembly.UnsafeLoadFrom(resource.Resolve(resourceContext).ValueAsString);
  }
}

Учитывая приведенный выше класс, вы добавите следующее в код запуска приложения (прежде чем все локализованные ресурсы потребуется загрузить):

void EnableMrtResourceLookup()
{
  AppDomain.CurrentDomain.AssemblyResolve += PriResourceResolver.ResolveResourceDll;
}

Среда выполнения .NET создаст событие AssemblyResolve всякий раз, когда она не сможет найти библиотеки DLL ресурсов, в этот момент предоставленный обработчик событий найдет необходимый файл при помощи MRT и возвратит сборку.

Замечание

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

Загрузка ресурсов MUI Win32

Загрузка ресурсов Win32 MUI по сути аналогична загрузке вспомогательных сборок .NET, но вместо этого используется код на C++/CX или C++/WRL. Использование C++/CX обеспечивает гораздо более простой код, который тесно соответствует приведенному выше коду C#, но он использует расширения языка C++, ключи компилятора и дополнительные издержки времени выполнения, которых вы, возможно, захотите избежать. Если это так, использование C++/WRL обеспечивает решение с минимальным воздействием, хотя и с более многословным кодом. Тем не менее, если вы знакомы с программированием ATL (или COM в целом), WRL должен показаться вам знакомым.

В следующем примере функции показано, как использовать C++/WRL для загрузки определенной библиотеки DLL ресурсов и возврата HINSTANCE, которые можно использовать для загрузки дополнительных ресурсов с помощью обычных API ресурсов Win32. Обратите внимание, что в отличие от примера C#, который явно инициализирует ResourceContext язык, запрошенный средой выполнения .NET, этот код использует текущий язык пользователя.

#include <roapi.h>
#include <wrl\client.h>
#include <wrl\wrappers\corewrappers.h>
#include <Windows.ApplicationModel.resources.core.h>
#include <Windows.Foundation.h>
   
#define IF_FAIL_RETURN(hr) if (FAILED((hr))) return hr;
    
HRESULT GetMrtResourceHandle(LPCWSTR resourceFilePath,  HINSTANCE* resourceHandle)
{
  using namespace Microsoft::WRL;
  using namespace Microsoft::WRL::Wrappers;
  using namespace ABI::Windows::ApplicationModel::Resources::Core;
  using namespace ABI::Windows::Foundation;
    
  *resourceHandle = nullptr;
  HRESULT hr{ S_OK };
  RoInitializeWrapper roInit{ RO_INIT_SINGLETHREADED };
  IF_FAIL_RETURN(roInit);
    
  // Get Windows.ApplicationModel.Resources.Core.ResourceManager statics
  ComPtr<IResourceManagerStatics> resourceManagerStatics;
  IF_FAIL_RETURN(GetActivationFactory(
    HStringReference(
    RuntimeClass_Windows_ApplicationModel_Resources_Core_ResourceManager).Get(),
    &resourceManagerStatics));
    
  // Get .Current property
  ComPtr<IResourceManager> resourceManager;
  IF_FAIL_RETURN(resourceManagerStatics->get_Current(&resourceManager));
    
  // get .MainResourceMap property
  ComPtr<IResourceMap> resourceMap;
  IF_FAIL_RETURN(resourceManager->get_MainResourceMap(&resourceMap));
    
  // Call .GetValue with supplied filename
  ComPtr<IResourceCandidate> resourceCandidate;
  IF_FAIL_RETURN(resourceMap->GetValue(HStringReference(resourceFilePath).Get(),
    &resourceCandidate));
    
  // Get .ValueAsString property
  HString resolvedResourceFilePath;
  IF_FAIL_RETURN(resourceCandidate->get_ValueAsString(
    resolvedResourceFilePath.GetAddressOf()));
    
  // Finally, load the DLL and return the hInst.
  *resourceHandle = LoadLibraryEx(resolvedResourceFilePath.GetRawBuffer(nullptr),
    nullptr, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
    
  return S_OK;
}

Этап 3. Создание пакетов ресурсов

Теперь, когда у вас есть "жировый пакет", содержащий все ресурсы, существует два пути к созданию отдельных основных пакетов и пакетов ресурсов, чтобы свести к минимуму размеры загрузки и установки:

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

Шаг 3.1. Создание пакета

Использование средства генератора пакетов

Чтобы использовать средство генератора пакетов, файл конфигурации PRI, созданный для пакета, необходимо вручную обновить, чтобы удалить <packaging> раздел.

Если вы используете Visual Studio, обратитесь к убедитесь, что на устройстве установлены ресурсы независимо от того, требуется ли для них сведения о том, как создать все языки в основной пакет, создав файлы priconfig.packaging.xml и priconfig.default.xml.

Если вы редактируете файлы вручную, выполните следующие действия.

  1. Создайте файл конфигурации так же, как и раньше, заменив правильный путь, имя файла и языки:

    makepri createconfig /cf ..\contoso_demo.xml /dq en-US_de-DE_es-MX /pv 10.0 /o
    
  2. Вручную откройте созданный .xml файл и удалите весь &lt;packaging&rt; раздел (но сохраните все остальное без изменений):

    <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
    <resources targetOsVersion="10.0.0" majorVersion="1">
      <!-- Packaging section has been deleted... -->
      <index root="\" startIndexAt="\">
        <default>
        ...
        ...
    
  3. Соберите файл .pri и пакет .appx как раньше, используя обновленный файл конфигурации и соответствующие имена для каталогов и файлов (см. дополнительные сведения об этих командах):

    makepri new /pr . /cf ..\contoso_demo.xml /of ..\resources.pri /mf AppX /o
    makeappx pack /m AppXManifest.xml /f ..\resources.map.txt /p ..\contoso_demo.appx /o
    
  4. После создания пакета используйте следующую команду, чтобы создать пакет, используя соответствующие имена каталогов и файлов:

    BundleGenerator.exe -Package ..\contoso_demo.appx -Destination ..\bundle -BundleName contoso_demo
    

Теперь вы можете перейти к последнему шагу, подписав (см. ниже).

Создание пакетов ресурсов вручную

Для создания пакетов ресурсов вручную требуется немного другой набор команд для сборки отдельных файлов .pri и .appx — эти команды аналогичны тем, что использовались выше для создания больших пакетов, поэтому минимальное объяснение дается. Примечание. Все команды предполагают, что текущий каталог является каталогом, содержащим AppXManifest.xml файл, но все файлы помещаются в родительский каталог (при необходимости можно использовать другой каталог, но при необходимости не следует загрязнять каталог проекта любым из этих файлов). Как всегда, замените имена файлов Contoso собственными именами файлов.

  1. Используйте следующую команду, чтобы создать файл конфигурации, который назначает только язык по умолчанию в качестве квалификатора. В этом случае en-US:

    makepri createconfig /cf ..\contoso_demo.xml /dq en-US /pv 10.0 /o
    
  2. Создайте файлы по умолчанию .pri и .map.txt для основного пакета, а также дополнительный набор файлов для каждого языка, найденного в проекте, используя следующую команду:

    makepri new /pr . /cf ..\contoso_demo.xml /of ..\resources.pri /mf AppX /o
    
  3. Используйте следующую команду, чтобы создать основной пакет (который содержит исполняемый код и языковые ресурсы по умолчанию). Как всегда, измените имя по вашему усмотрению, но следует поместить пакет в отдельный каталог, чтобы упростить создание сборки (в этом примере используется каталог ..\bundle):

    makeappx pack /m .\AppXManifest.xml /f ..\resources.map.txt /p ..\bundle\contoso_demo.main.appx /o
    
  4. После создания основного пакета используйте следующую команду один раз для каждого дополнительного языка (т. е. повторите эту команду для каждого файла карты языка, созданного на предыдущем шаге). Опять же, выходные данные должны находиться в отдельном каталоге (тот же, что и основной пакет). Обратите внимание, что язык указан оба в параметре /f и параметре /p, а также использование нового аргумента /r (который указывает, что пакет ресурсов требуется):

    makeappx pack /r /m .\AppXManifest.xml /f ..\resources.language-de.map.txt /p ..\bundle\contoso_demo.de.appx /o
    
  5. Объедините все пакеты из каталога пакета в один .appxbundle файл. Новый /d параметр указывает каталог, используемый для всех файлов в пакете (именно поэтому .appx файлы помещаются в отдельный каталог на предыдущем шаге):

    makeappx bundle /d ..\bundle /p ..\contoso_demo.appxbundle /o
    

Последний шаг в создании пакета — подпись.

Шаг 3.2. Подписывание пакета

После создания .appxbundle файла (с помощью средства генератора пакетов или вручную) у вас будет один файл, содержащий основной пакет, а также все пакеты ресурсов. Последний шаг заключается в том, чтобы подписать файл, чтобы Windows установит его:

signtool sign /fd SHA256 /a /f ..\contoso_demo_key.pfx ..\contoso_demo.appxbundle

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