Использование системы управления ресурсами 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, обработанных мост для классических приложений (ака "Centennial").

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

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

Введение

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

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

Пример

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

Screenshot of a Source code label, a Lookup table label, and a Files on disk label.

На рисунке код приложения ссылается на три имена логических ресурсов. Во время выполнения 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 или сборки ресурсов .NET в PRI-файлы. Это может привести к дальнейшему экономии места для загрузки и установки размеров. Он также позволяет использовать другие функции MRT, такие как минимизация загрузки и установки файлов изображений на основе коэффициента масштабирования, параметров специальных возможностей и т. д.

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

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

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

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

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

Screenshot showing the Resources.resw file showing the Name and Value columns. for the resources.

Примечание.

Если начать с конструктора 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 и измените выделенные значения на вкладке *Application и вкладке "Упаковка ":

Screenshot of the Visual Studio Manifest Designer showing the Application tab with the Display name and Description text boxes called out.

Screenshot of the Visual Studio Manifest Designer showing the Packaging tab with the Package display name and Publisher display name text boxes called out.

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

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

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

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

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

  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 2017 или Visual Studio 2019 в качестве Администратор istrator.

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

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

    certutil -addstore TrustedPeople contoso_demo.cer
    

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

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

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

  1. Перейдите в папку, содержащую .pfx файл
  2. Дважды щелкните .pfx файл, а мастер импорта Certicicate должен появиться
  3. Выберите Local Machine и щелкните Next
  4. Примите запрос администратора управления учетными записями, если он отображается, и нажмите кнопку 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 файл. Этот документ не охватывает сам шаг перевода.

Например, файл может выглядеть следующим образомStrings\de-DE\resources.resw:en-US

<?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)
  1. Выбор нового языка в списке языков
  2. Щелкните 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 , запишите выделенные значения:

Screenshot of the Visual Studio Manifest Designer showing the Declarations tab with the Display name and Info tip text boxes called out.

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

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

Затем появится в частях оболочки Windows, например проводник:

Screenshot of File Explorer showing a tooltip that says Files used by 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++/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. AFter пакет создан, используйте следующую команду, чтобы создать пакет, используя соответствующие имена каталогов и файлов:

    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. После создания основного пакета используйте следующую команду один раз для каждого дополнительного языка (т. е. повторите эту команду для каждого файла карты языка, созданного на предыдущем шаге). Опять же, выходные данные должны находиться в отдельном каталоге (тот же, что и основной пакет). Обратите внимание, что язык указан как в параметре, так и /p в /f параметре, а также в использовании нового /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 пользователя.