Usar el sistema de administración de recursos de Windows 10 en una aplicación o juego heredados

Las aplicaciones y juegos de .NET y Win32 a menudo se localizan en diferentes idiomas para expandir su mercado total direccionable. Para obtener más información sobre el valor de localizar tu app, consulta Globalización y localización. Al empaquetar la aplicación .NET o Win32 o el juego como un paquete .msix o .appx, puede aprovechar el sistema de administración de recursos para cargar los recursos de la aplicación adaptados al contexto en tiempo de ejecución. En este tema detallado se describen las técnicas.

Hay muchas maneras de localizar una aplicación Win32 tradicional, pero Windows 8 introdujo una nuevo sistema de administración de recursos que funciona en lenguajes de programación, en todos los tipos de aplicación y proporciona funcionalidad sobre y por encima de la localización simple. Este sistema se denominará "MRT" en este tema. Históricamente, eso significaba "Tecnología moderna de recursos", pero el término "Moderno" se ha dejado de usar. El administrador de recursos también se puede conocer como MRM (Modern Resource Manager) o PRI (Índice de recursos de paquete).

Combinado con la implementación basada en MSIX o basada en .appx (por ejemplo, desde Microsoft Store), MRT puede entregar automáticamente los recursos más aplicables para un usuario o dispositivo determinado, lo que minimiza el tamaño de descarga e instalación de la aplicación. Esta reducción de tamaño puede ser significativa para las aplicaciones con una gran cantidad de contenido localizado, quizás en el orden de varios gigabytes para juegos AAA. Entre las ventajas adicionales de MRT se incluyen listados localizados en Windows Shell y Microsoft Store, lógica de retroceso automática cuando el idioma preferido de un usuario no coincide con los recursos disponibles.

En este documento se describe la arquitectura de alto nivel de MRT y se proporciona una guía de portabilidad para ayudar a mover aplicaciones Win32 heredadas a MRT con cambios mínimos en el código. Una vez realizado el traslado a MRT, las ventajas adicionales (como la capacidad de segmentar los recursos por factor de escala o tema del sistema) están disponibles para el desarrollador. Ten en cuenta que la localización basada en MRT funciona tanto para aplicaciones para UWP como para aplicaciones Win32 procesadas por el Puente de escritorio (también conocido como "Centennial").

En muchas situaciones, puede seguir usando los formatos de localización y el código fuente existentes mientras se integra con MRT para resolver recursos en tiempo de ejecución y minimizar los tamaños de descarga, no es un enfoque de todo o nada. En la tabla siguiente se resumen los costos y beneficios estimados de cada fase. Esta tabla no incluye tareas de no localización, como proporcionar iconos de aplicación de alta resolución o contraste alto. Para obtener más información sobre cómo proporcionar varios recursos para mosaicos, iconos, etc., consulte el apartado sobre cómo ajustar tus recursos para el idioma, la escala, el alto contraste y otros calificadores.

Trabajo Ventajas Costo estimado
Localizar el manifiesto del paquete Trabajo mínimo necesario para que el contenido localizado aparezca en el Shell de Windows y en Microsoft Store Pequeño
Uso de MRT para identificar y localizar recursos Requisito previo para minimizar los tamaños de descarga e instalación; reserva de idioma automática Mediana
Compilación de paquetes de recursos Paso final para minimizar los tamaños de descarga e instalación Pequeño
Migrar a formatos de recursos y APIs de MRT Tamaños de archivo significativamente más pequeños (en función de la tecnología de recursos existente) Grande

Introducción

La mayoría de las aplicaciones no triviales contienen elementos de interfaz de usuario conocidos como recursos que se desacoplan del código de la aplicación (contrastados con valores codificados de forma rígida que se crean en el propio código fuente). Hay varias razones para preferir recursos sobre valores codificados de forma rígida( facilidad de edición por parte de los no desarrolladores, por ejemplo, pero una de las razones clave es permitir que la aplicación elija diferentes representaciones del mismo recurso lógico en tiempo de ejecución. Por ejemplo, el texto que se va a mostrar en un botón (o la imagen que se va a mostrar en un icono) puede diferir en función de los idiomas que comprenda el usuario, las características del dispositivo de visualización o si el usuario tiene habilitadas las tecnologías de asistencia.

Por lo tanto, el propósito principal de cualquier tecnología de administración de recursos es traducir, en tiempo de ejecución, una solicitud de un nombre de recurso lógico o simbólico (por ejemplo, SAVE_BUTTON_LABEL) en el mejor valor real posible (por ejemplo, "Guardar") desde un conjunto de posibles candidatos (por ejemplo, "Save", "Speichern" o "저장"). MRT proporciona esta función y permite a las aplicaciones identificar candidatos a recursos mediante una amplia variedad de atributos, denominados calificadores, como el lenguaje del usuario, el factor de escalado de pantalla, el tema seleccionado del usuario y otros factores ambientales. MRT incluso admite calificadores personalizados para las aplicaciones que lo necesitan (por ejemplo, una aplicación podría proporcionar diferentes recursos gráficos para los usuarios que habían iniciado sesión con una cuenta frente a los usuarios invitados, sin agregar explícitamente esta comprobación a cada parte de su aplicación). MRT funciona con recursos de cadena y recursos basados en archivos, donde los recursos basados en archivos se implementan como referencias a los datos externos (los propios archivos).

Ejemplo

Este es un ejemplo sencillo de una aplicación que tiene etiquetas de texto en dos botones (openButton y ) y saveButtonun archivo PNG usado para un logotipo (logoImage). Las etiquetas de texto se localizan en inglés y alemán, y el logotipo está optimizado para pantallas de escritorio normales (factor de escala de 100%) y teléfonos de alta resolución (300% factor de escala). Tenga en cuenta que este diagrama presenta una vista conceptual de alto nivel del modelo; no corresponde exactamente a la implementación.

Captura de pantalla de una etiqueta de código fuente, una etiqueta de tabla de búsqueda y un archivo en la etiqueta de disco.

En el gráfico, el código de la aplicación hace referencia a los tres nombres de recursos lógicos. En tiempo de ejecución, la pseudofunción GetResource utiliza MRT para buscar esos nombres de recursos en la tabla de recursos, conocida como archivo PRI, y encontrar el candidato más adecuado en función de las condiciones de entorno (el idioma del usuario y el factor de escala de la pantalla). En el caso de las etiquetas, las cadenas se usan directamente. En el caso de la imagen del logotipo, las cadenas se interpretan como nombres de archivo y los archivos se leen desde el disco.

Si el usuario habla un idioma distinto del inglés o alemán, o tiene un factor de escala de visualización distinto de 100% o 300%, MRT elige al candidato de coincidencia más cercano basado en un conjunto de reglas de recurrencia (consulte Sistema de administración de recursos para obtener más información de fondo).

Tenga en cuenta que MRT admite recursos adaptados a más de un calificador; por ejemplo, si la imagen del logotipo contenía texto incrustado que también era necesario localizar, el logotipo tendría cuatro candidatos: EN/Scale-100, DE/Scale-100, EN/Scale-300 y DE/Scale-300.

Secciones de este documento

En las secciones siguientes se describen las tareas de alto nivel necesarias para integrar MRT con la aplicación.

Fase 0: Compilación de un paquete de aplicación

En esta sección se describe cómo crear la aplicación de escritorio existente como un paquete de aplicación. No se usan características de MRT en esta fase.

Fase 1: Localizar el manifiesto de aplicación

En esta sección se describe cómo localizar el manifiesto de la aplicación (para que aparezca correctamente en el Shell de Windows) mientras sigue usando el formato de recurso heredado y la API para empaquetar y localizar recursos.

Fase 2: Uso de MRT para identificar y localizar recursos

En esta sección se describe cómo modificar el código de la aplicación (y posiblemente el diseño de recursos) para buscar recursos mediante MRT, mientras sigue usando los formatos de recursos y las API existentes para cargar y consumir los recursos.

Fase 3: Compilación de paquetes de recursos

En esta sección se describen los cambios finales necesarios para separar los recursos en paquetes de recursos independientes, lo que minimiza el tamaño de descarga (e instalación) de la aplicación.

No se trata en este documento

Después de completar las fases 0-3 anteriores, tendrá un paquete de la aplicación que se puede enviar a la Microsoft Store y que minimizará el tamaño de descarga e instalación para los usuarios omitiendo los recursos que no necesitan (por ejemplo, los idiomas que no hablan). Se pueden realizar mejoras adicionales en el tamaño y la funcionalidad de la aplicación realizando un paso final.

Fase 4: Migración a formatos de recursos y API de MRT

Esta fase está fuera del ámbito de este documento; implica mover los recursos (especialmente cadenas) de formatos heredados, como archivos DLL de MUI o ensamblados de recursos de .NET a archivos PRI. Esto puede dar lugar a ahorros de espacio adicionales para los tamaños de descarga e instalación. También permite el uso de otras características de MRT, como minimizar la descarga e instalación de archivos de imagen en función del factor de escala, la configuración de accesibilidad, etc.

Fase 0: Compilación de un paquete de aplicación

Antes de realizar cambios en los recursos de la aplicación, primero debes reemplazar la tecnología actual de empaquetado e instalación por la tecnología estándar de empaquetado e implementación de UWP. Hay tres maneras de hacerlo:

  • Si tienes una aplicación de escritorio grande con un instalador complejo o usas muchos puntos de extensibilidad del sistema operativo, puedes usar la herramienta Desktop App Converter para generar el diseño de archivo UWP y la información de manifiesto del instalador de la aplicación existente (por ejemplo, un MSI).
  • Si tiene una aplicación de escritorio más pequeña con relativamente pocos archivos o un instalador simple y sin enlaces de extensibilidad, puede crear manualmente el diseño de archivo y la información del manifiesto.
  • Si vas a compilar desde el código fuente y quieres actualizar la aplicación para que sea una aplicación UWP nativa, puedes crear un nuevo proyecto en Visual Studio y confiar en el IDE para que realice gran parte del trabajo por ti.

Si quiere usar Desktop App Converter, consulte Empaquetar una aplicación de escritorio mediante desktop App Converter para obtener más información sobre el proceso de conversión. Puede encontrar un conjunto completo de ejemplos de Desktop Converter en el repositorio de GitHub de ejemplos del puente Desktop a UWP.

Nota:

Desktop App Converter ha quedado en desuso. Use la herramienta de empaquetado MSIX nueva y mejorada para empaquetar las aplicaciones de escritorio.

Si desea crear manualmente el paquete, deberá crear una estructura de directorios que incluya todos los archivos de la aplicación (ejecutables y contenido, pero no código fuente) y un archivo de manifiesto de paquete (.appxmanifest). Puede encontrar un ejemplo en el ejemplo Hello, World de GitHub, pero un archivo de manifiesto de paquete básico que ejecuta el ejecutable de escritorio denominado ContosoDemo.exe es el siguiente, donde el texto resaltado se reemplazaría por sus propios valores.

<?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>

Para obtener más información sobre el archivo de manifiesto del paquete y el diseño del paquete, consulte manifiesto del paquete de aplicación.

Por último, si usa Visual Studio para crear un nuevo proyecto y migrar el código existente, consulte Creación de una aplicación "Hola mundo". Puedes incluir el código existente en el nuevo proyecto, pero es probable que tengas que realizar cambios significativos en el código (especialmente en la interfaz de usuario) para poder ejecutarse como una aplicación para UWP pura. Estos cambios están fuera del ámbito de este documento.

Fase 1: Localizar el manifiesto

Paso 1.1: Actualizar cadenas y activos en el manifiesto

En la fase 0 ha creado un archivo de manifiesto de paquete básico (.appxmanifest) para la aplicación (en función de los valores proporcionados al convertidor, extraídos de MSI o introducidos manualmente en el manifiesto), pero no contendrá información localizada, ni admitirá características adicionales como los recursos de mosaico Start de alta resolución, etc.

Para asegurarse de que el nombre y la descripción de la aplicación se localizan correctamente, debe definir algunos recursos en un conjunto de archivos de recursos y actualizar el manifiesto del paquete para hacer referencia a ellos.

Creación de un archivo de recursos predeterminado

El primer paso es crear un archivo de recursos predeterminado en el idioma predeterminado (por ejemplo, inglés de EE. UU.). Puede hacerlo manualmente con un editor de texto o mediante el Diseñador de recursos en Visual Studio.

Si desea crear los recursos manualmente:

  1. Cree un archivo XML denominado resources.resw y colóquelo en una Strings\en-us subcarpeta del proyecto. Use el código BCP-47 adecuado si el idioma predeterminado no es inglés de EE. UU.
  2. En el archivo XML, agregue el siguiente contenido, donde el texto resaltado se reemplaza por el texto adecuado para la aplicación, en el idioma predeterminado.

Nota:

Hay restricciones en las longitudes de algunas de estas cadenas. Para obtener más información, consulta 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>

Si desea usar el diseñador en Visual Studio:

  1. Cree la Strings\en-us carpeta (u otro idioma según corresponda) en el proyecto y agregue un nuevo elemento a la carpeta raíz del proyecto, con el nombre predeterminado de resources.resw. Asegúrese de elegir Archivo de recursos (.resw) y no diccionario de recursos: un diccionario de recursos es un archivo que usan las aplicaciones XAML.
  2. Con el diseñador, escriba las siguientes cadenas (use el mismo Names, pero reemplace el Values por el texto adecuado para la aplicación):

Captura de pantalla que muestra el archivo Resources.resw que muestra las columnas Nombre y Valor. para los recursos.

Nota:

Si comienza con el diseñador de Visual Studio, siempre puede editar el XML directamente presionando el botón F7. Pero si empieza con un archivo XML mínimo, el diseñador no reconocerá el archivo porque faltan muchos metadatos adicionales; para corregir esto, copie la información XSD predeterminada de un archivo generado por el diseñador en su archivo XML editado manualmente.

Actualizar el manifiesto para hacer referencia a los recursos

Después de tener los valores definidos en el .resw archivo, el siguiente paso es actualizar el manifiesto para hacer referencia a las cadenas de recursos. De nuevo, puede editar un archivo XML directamente o confiar en el Diseñador de manifiestos de Visual Studio.

Si está editando XML directamente, abra el archivo AppxManifest.xml y realice los siguientes cambios en los valores resaltados : use este texto exacto, no el texto específico de la aplicación. No es necesario usar estos nombres de recursos exactos (puede elegir sus propios), pero lo que elija debe coincidir exactamente con lo que se encuentra en el .resw archivo. Estos nombres deben coincidir con el Names que has creado en el archivo .resw, precedido por el esquema ms-resource: y el espacio de nombres Resources/.

Nota:

Se han omitido muchos elementos del manifiesto de este fragmento de código: no elimine nada.

<?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>

Si usa el diseñador de manifiestos de Visual Studio, abra el archivo .appxmanifest y cambie los valores resaltados por los valores en la pestaña *Aplicación y la pestaña Empaquetado.

Captura de pantalla del Diseñador de manifiestos de Visual Studio que muestra la pestaña Aplicación con los cuadros de texto de Nombre para mostrar y Descripción resaltados.

Captura de pantalla del Diseñador de manifiestos de Visual Studio que muestra la pestaña Empaquetado con el nombre visual del paquete y los cuadros de texto del nombre visual del editor resaltados.

Paso 1.2: Compilar un archivo PRI, crear un paquete MSIX y comprobar que funciona

Ahora debería poder compilar el .pri archivo e implementar la aplicación para comprobar que la información correcta (en el idioma predeterminado) aparece en el menú Inicio.

Si va a compilar en Visual Studio, simplemente presione Ctrl+Shift+B para compilar el proyecto y, a continuación, haga clic con el botón derecho en el proyecto y elija Deploy en el menú contextual.

Si va a compilar manualmente, siga estos pasos para crear un archivo de configuración para la herramienta MakePRI y para generar el archivo .pri en sí mismo (puede encontrar más información en el Manual de empaquetado de aplicaciones ):

  1. Abra un símbolo del sistema para desarrolladores desde la carpeta Visual Studio 2019 o Visual Studio 2022 en el menú Inicio.

  2. Cambie al directorio raíz del proyecto (el que contiene el archivo .appxmanifest y la carpeta Strings ).

  3. Escriba el siguiente comando, reemplazando "contoso_demo.xml" por un nombre adecuado para el proyecto y "en-US" por el idioma predeterminado de la aplicación (o guárdelo en-US si procede). Tenga en cuenta que el archivo XML se crea en el directorio primario (no en el directorio del proyecto), ya que no forma parte de la aplicación (puede elegir cualquier otro directorio que desee, pero asegúrese de sustituirlo en comandos futuros).

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

    Puede escribir makepri createconfig /? para ver lo que hace cada parámetro, pero en resumen:

    • /cf establece el nombre de archivo de configuración (la salida de este comando)
    • /dq establece los calificadores predeterminados, en este caso el idioma en-US
    • /pv establece la versión de la plataforma, en este caso Windows 10
    • /o lo establece en Sobrescribir el archivo de salida si existe.
  4. Ahora tiene un archivo de configuración, vuelva a ejecutar MakePRI para buscar recursos en el disco y empaquetarlos en un archivo PRI. Reemplace "contoso_demop.xml" por el nombre de archivo XML que usó en el paso anterior y asegúrese de especificar el directorio primario para la entrada y salida:

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

    Puede escribir makepri new /? para ver lo que hace cada parámetro, pero en pocas palabras:

    • /pr establece la raíz del proyecto (en este caso, el directorio actual)
    • /cf establece el nombre de archivo de configuración, creado en el paso anterior.
    • /of establece el archivo de salida
    • /mf crea un archivo de mapeo (para que podamos excluir archivos en el paquete en un paso posterior)
    • /o lo establece en Sobrescribir el archivo de salida si existe.
  5. Ahora tiene un .pri archivo con los recursos de idioma predeterminados (por ejemplo, en-US). Para comprobar que funcionó correctamente, puede ejecutar el siguiente comando:

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

    Puede escribir makepri dump /? para ver lo que hace cada parámetro, pero en pocas palabras:

    • /if establece el nombre de archivo de entrada
    • /of establece el nombre de archivo de salida (.xml se anexará automáticamente)
    • /o lo establece en Sobrescribir el archivo de salida si existe.
  6. Por último, puede abrir ..\resources.xml en un editor de texto y comprobar que enumera los <NamedResource> valores (como ApplicationDescription y PublisherDisplayName) junto con <Candidate> los valores del idioma predeterminado elegido (habrá otro contenido al principio del archivo; omita eso por ahora).

Puede abrir el archivo de asignación ..\resources.map.txt para verificar que éste contiene los archivos necesarios para el proyecto (incluido el archivo PRI, que no forma parte del directorio del proyecto). Importantemente, el archivo de asignación no incluir una referencia al archivo resources.resw porque el contenido de ese archivo ya se ha incrustado en el archivo PRI. Sin embargo, contendrá otros recursos como los nombres de archivo de las imágenes.

Creación y firma del paquete

Ahora se compila el archivo PRI, puede compilar y firmar el paquete:

  1. Para crear el paquete de aplicación, ejecute el siguiente comando reemplazando contoso_demo.appx por el nombre del archivo .msix/.appx que desea crear y asegurándose de elegir un directorio diferente para el archivo (en este ejemplo se usa el directorio primario; puede estar en cualquier lugar, pero no debe ser el directorio del proyecto).

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

    Puede escribir makeappx pack /? para ver lo que hace cada parámetro, pero en pocas palabras:

    • /m establece el archivo de manifiesto que se va a usar
    • /f establece el archivo de mapeo que se va a usar (creado en el paso anterior)
    • /p establece el nombre del paquete de salida
    • /o lo establece en Sobrescribir el archivo de salida si existe.
  2. Una vez creado el paquete, debe estar firmado. La manera más fácil de obtener un certificado de firma es creando un proyecto vacío de Universal Windows en Visual Studio y copiando el archivo .pfx que crea, pero puede crear uno manualmente utilizando las utilidades MakeCert y Pvk2Pfx como se describe en Cómo crear un certificado de firma de paquete de aplicación.

    Importante

    Si crea manualmente un certificado de firma, asegúrese de colocar los archivos en un directorio diferente del proyecto de origen o el origen del paquete; de lo contrario, podría incluirse como parte del paquete, incluida la clave privada.

  3. Para firmar el paquete, use el siguiente comando. Tenga en cuenta que el Publisher especificado en el elemento Identity del AppxManifest.xml debe coincidir con el Subject del certificado (esto es no el elemento <PublisherDisplayName>, que es el nombre para mostrar localizado a los usuarios). Como de costumbre, reemplace los contoso_demo... nombres de archivo por los nombres adecuados para el proyecto y (muy importante) asegúrese de que el .pfx archivo no está en el directorio actual (de lo contrario, se habría creado como parte del paquete, incluida la clave de firma privada).):

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

    Puede escribir signtool sign /? para ver lo que hace cada parámetro, pero en pocas palabras:

    • /fd establece el algoritmo de síntesis de archivo (SHA256 es el valor predeterminado para .appx)
    • /a seleccionará automáticamente el mejor certificado.
    • /f especifica el archivo de entrada que contiene el certificado de firma.

Por último, ahora puede hacer doble clic en el archivo .appx para instalarlo o, si prefiere la línea de comandos, puede abrir un símbolo del sistema de PowerShell, cambiar al directorio que contiene el paquete y escribir lo siguiente (reemplazando contoso_demo.appx por el nombre del paquete):

add-appxpackage contoso_demo.appx

Si recibe errores sobre el certificado que no es de confianza, asegúrese de que esté agregado al almacén de certificados de la máquina (no el almacén de usuarios). Para agregar el certificado al almacén de máquinas, puede usar la línea de comandos o el Explorador de Windows.

Para usar la línea de comandos:

  1. Ejecute un símbolo del sistema de Visual Studio 2019 o Visual Studio 2022 como administrador.

  2. Cambie al directorio que contiene el .cer archivo (recuerde asegurarse de que está fuera de los directorios de origen o del proyecto).

  3. Escriba el siguiente comando, reemplazando contoso_demo.cer por el nombre de archivo:

    certutil -addstore TrustedPeople contoso_demo.cer
    

    Puede ejecutar certutil -addstore /? para ver lo que hace cada parámetro, pero en pocas palabras:

    • -addstore agrega un certificado a un almacén de certificados
    • TrustedPeople indica el almacén en el que se coloca el certificado.

Para usar el Explorador de Windows:

  1. Vaya a la carpeta que contiene el .pfx archivo.
  2. Haga doble clic en el .pfx archivo y debería aparecer el Asistente para importación de certificados .
  3. Elija Local Machine y haga clic en Next
  4. Acepte el cuadro de diálogo de elevación de privilegios del administrador de Control de cuentas de usuario, si aparece, y haga clic en Next
  5. Escriba la contraseña de la clave privada, si hay una y haga clic en Next
  6. Seleccione Place all certificates in the following store.
  7. Haga clic en Browsey elija la Trusted People carpeta (no "Editores de confianza")
  8. Haga clic en Next y luego en Finish

Después de agregar el certificado al Trusted People almacén, intente instalar el paquete de nuevo.

Ahora debería ver que la aplicación aparece en la lista "Todas las aplicaciones" del menú Inicio, con la información correcta del .resw / .pri archivo. Si ve una cadena en blanco o la cadena ms-resource:... , se ha producido un error, compruebe las modificaciones y asegúrese de que son correctas. Si haces clic con el botón derecho en tu aplicación en el menú Inicio, puedes fijarla como un mosaico y verifica que la información correcta se muestra allí.

Paso 1.3: Agregar más idiomas admitidos

Una vez realizados los cambios en el manifiesto del paquete y creado el archivo inicial resources.resw , es fácil agregar idiomas adicionales.

Creación de recursos localizados adicionales

En primer lugar, cree los valores de recursos localizados adicionales.

En la Strings carpeta , cree carpetas adicionales para cada idioma que admita mediante el código BCP-47 adecuado (por ejemplo, Strings\de-DE). Dentro de cada una de estas carpetas, cree un resources.resw archivo (mediante un editor XML o el diseñador de Visual Studio) que incluya los valores de recursos traducidos. Se supone que ya tiene las cadenas localizadas disponibles en algún lugar y solo tiene que copiarlas en el .resw archivo; este documento no cubre el propio paso de traducción.

Por ejemplo, el archivo de Strings\de-DE\resources.resw podría tener este aspecto, con el texto resaltado :

<?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>

En los pasos siguientes se supone que ha agregado recursos para de-DE y fr-FR, pero se puede seguir el mismo patrón para cualquier lenguaje.

Actualización del manifiesto del paquete para enumerar los idiomas admitidos

El manifiesto del paquete debe actualizarse para enumerar los idiomas admitidos por la aplicación. Desktop App Converter agrega el idioma predeterminado, pero los demás se deben agregar explícitamente. Si está editando el archivo AppxManifest.xml directamente, actualice el nodo Resources de la siguiente manera: agregue tantos elementos como necesite, sustituya los idiomas adecuados que admita y asegúrese de que la primera entrada de la lista sea el idioma predeterminado (alternativa). En este ejemplo, el valor predeterminado es inglés (EE. UU.) con compatibilidad adicional para alemán (Alemania) y francés (Francia):

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

Si usa el Visual Studio, no debería necesitar hacer nada; si observa Package.appxmanifest debería ver el valor especial x-generate de, lo que provoca que el proceso de compilación inserte los idiomas que encuentra en su proyecto (basándose en las carpetas denominadas con códigos BCP-47). Tenga en cuenta que no es un valor válido para un manifiesto de paquete real; solo funciona para proyectos de Visual Studio:

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

Volver a compilar con los valores localizados

Ahora puede compilar e implementar la aplicación de nuevo y, si cambia la preferencia de idioma en Windows, debería ver que los valores recién localizados aparecen en el menú Inicio (instrucciones para cambiar el idioma a continuación).

Para Visual Studio, de nuevo puede usar Ctrl+Shift+B para compilar y hacer clic con el botón derecho en el proyecto para Deploy.

Si va a compilar manualmente el proyecto, siga los mismos pasos que antes, pero agregue los idiomas adicionales, separados por caracteres de subrayado, a la lista de calificadores predeterminados (/dq) al crear el archivo de configuración. Por ejemplo, para admitir los recursos de inglés, alemán y francés agregados en el paso anterior:

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

Esto creará un archivo PRI que contiene todos los idiomas especificados que puede usar fácilmente para las pruebas. Si el tamaño total de los recursos es pequeño o solo admite un número reducido de idiomas, esto puede ser aceptable para la aplicación de envío; solo es si desea las ventajas de minimizar el tamaño de instalación o descarga de los recursos que necesita para realizar el trabajo adicional de crear paquetes de idioma independientes.

Prueba con los valores localizados

Para probar los nuevos cambios localizados, basta con agregar un nuevo idioma de interfaz de usuario preferido a Windows. No es necesario descargar paquetes de idioma, reiniciar el sistema o hacer que toda la interfaz de usuario de Windows aparezca en un idioma extranjero.

  1. Ejecución de la Settings aplicación (Windows + I)
  2. Vaya a Time & language
  3. Vaya a Region & language
  4. Haga clic en Add a language
  5. Escriba (o seleccione) el idioma que desee (por ejemplo Deutsch , o German)
    • Si hay sub-idiomas, elija el que desee (por ejemplo, Deutsch / Deutschland).
  6. Seleccione el nuevo idioma en la lista de idiomas.
  7. Haga clic en Set as default

Ahora abra el menú Inicio y busque la aplicación, y debería ver los valores localizados para el idioma seleccionado (es posible que otras aplicaciones también aparezcan localizadas). Si no ve el nombre localizado inmediatamente, espere unos minutos hasta que se actualice la memoria caché del menú Inicio. Para volver a su idioma nativo, solo tiene que convertirlo en el idioma predeterminado en la lista de idiomas.

Paso 1.4: Localizar más partes del manifiesto del paquete (opcional)

Se pueden localizar otras secciones del manifiesto del paquete. Por ejemplo, si la aplicación controla extensiones de archivo, debe tener una windows.fileTypeAssociation extensión en el manifiesto, usando el texto resaltado verde exactamente como se muestra (ya que hará referencia a recursos) y reemplazando el texto resaltado amarillo por información específica de la aplicación:

<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>

También puede agregar esta información mediante el Diseñador de manifiestos de Visual Studio, con la Declarations pestaña , tomando nota de los valores resaltados:

Captura de pantalla del Diseñador de manifiestos de Visual Studio que muestra la pestaña Declaraciones con los cuadros de texto Nombre para mostrar e Información indicados.

Ahora agregue los nombres de recursos correspondientes a cada uno de los archivos de .resw, reemplazando el texto resaltado por el texto adecuado para la aplicación (recuerde hacerlo para cada idioma compatible!):

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

A continuación, se mostrará en partes del shell de Windows, como el Explorador de archivos:

Captura de pantalla del Explorador de archivos que muestra un mensaje emergente que indica Archivos usados por la aplicación de demostración de Contoso.

Construya y pruebe el paquete como antes, ejecutando los nuevos escenarios que deben mostrar las nuevas cadenas de la interfaz de usuario.

Fase 2: Uso de MRT para identificar y localizar recursos

En la sección anterior se mostró cómo usar MRT para localizar el archivo de manifiesto de la aplicación para que el Shell de Windows pueda mostrar correctamente el nombre de la aplicación y otros metadatos. No se requiere ningún cambio de código para esto; simplemente requiere el uso de .resw archivos y algunas herramientas adicionales. En esta sección se muestra cómo usar MRT para buscar recursos en los formatos de recursos existentes y usar el código de control de recursos existente con cambios mínimos.

Suposiciones sobre el diseño de archivo y el código de aplicación existentes

Dado que hay muchas maneras de localizar aplicaciones de escritorio win32, este documento hará algunas suposiciones de simplificación sobre la estructura de la aplicación existente que necesitará asignar a su entorno específico. Es posible que tenga que realizar algunos cambios en el diseño de código base o recurso existente para cumplir los requisitos de MRT y que estén en gran medida fuera del ámbito de este documento.

Diseño del archivo de recursos

En este artículo se supone que los recursos localizados tienen todos los mismos nombres de archivo (por ejemplo, contoso_demo.exe.mui o contoso_strings.dllcontoso.strings.xml) pero que se colocan en carpetas diferentes con nombres BCP-47 (en-US, de-DE, etc.). No importa cuántos archivos de recursos tenga, cuáles son sus nombres, cuáles son sus formatos de archivo o api asociadas, etc. Lo único que importa es que cada recurso lógico tenga el mismo nombre de archivo (pero colocado en un directorio físico diferente).

Como contraejemplo, si tu aplicación utiliza una estructura de archivos plana con un único directorio Resources que contiene los archivos english_strings.dll y french_strings.dll, no se asignaría bien a MRT. Una estructura mejor sería un Resources directorio con subdirectorios y archivos en\strings.dll y fr\strings.dll. También es posible usar el mismo nombre de archivo base, pero con calificadores incrustados, como strings.lang-en.dll y strings.lang-fr.dll, pero el uso de directorios con los códigos de idioma es conceptualmente más sencillo, por lo que es lo que nos centraremos.

Nota:

Todavía es posible usar MRT y los beneficios del empaquetado incluso si no puedes seguir esta convención de nomenclatura de archivos; solo requiere más trabajo.

Por ejemplo, la aplicación podría tener un conjunto de comandos de interfaz de usuario personalizados (usados para etiquetas de botón, etc.) en un archivo de texto simple denominado ui.txt, establecido en una carpeta 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...

Código de carga de recursos

En este artículo se da por supuesto que, en algún momento del código, desea buscar el archivo que contiene un recurso localizado, cargarlo y, a continuación, usarlo. Las API que se usan para cargar los recursos, las API que se usan para extraer los recursos, etc. no son importantes. En pseudocódigo, básicamente hay tres pasos:

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

MRT solo requiere cambiar los dos primeros pasos de este proceso: cómo se determinan los mejores recursos candidatos y cómo los localiza. No requiere que cambie la forma de cargar o usar los recursos (aunque proporciona instalaciones para hacerlo si desea aprovecharlos).

Por ejemplo, la aplicación podría usar la API GetUserPreferredUILanguagesde Win32 , la función sprintfCRT y la API CreateFile de Win32 para reemplazar las tres funciones de pseudocódigo anteriores y, a continuación, analizar manualmente el archivo de texto buscando name=value pares. (Los detalles no son importantes; esto es simplemente para ilustrar que MRT no tiene ningún impacto en las técnicas usadas para controlar los recursos una vez que se han localizado).

Paso 2.1: Cambios de código para usar MRT para buscar archivos

No es difícil cambiar el código para usar MRT para buscar recursos. Requiere usar unos cuantos tipos de WinRT y algunas líneas de código. Los tipos principales que usará son los siguientes:

  • ResourceContext, que encapsula el conjunto de valores calificadores que está actualmente activo (idioma, factor de escala, etc.)
  • ResourceManager (la versión de WinRT, no la versión de .NET), que permite el acceso a todos los recursos desde el archivo PRI.
  • ResourceMap, que representa un subconjunto específico de los recursos del archivo PRI (en este ejemplo, los recursos basados en archivos frente a los recursos de cadena)
  • NamedResource, que representa un recurso lógico y todos sus posibles candidatos
  • ResourceCandidate, que representa un único candidato a recurso concreto

En pseudocódigo, la forma en que resolvería un nombre de archivo de recursos determinado (como UICommands\ui.txt en el ejemplo anterior) es el siguiente:

// 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

Tenga en cuenta, en particular, que el código no solicitar una carpeta de idioma específica (como UICommands\en-US\ui.txt), aunque así sea como existen los archivos en el disco. En su lugar, solicita el nombre de archivo lógico UICommands\ui.txt y se apoya en MRT para localizar el archivo adecuado ubicado en el disco en uno de los directorios de idioma.

Desde aquí, la aplicación de ejemplo podría seguir usando CreateFile para cargar absoluteFileName y analizar pares de name=value igual que antes; esa lógica no necesita cambiar en la aplicación. Si escribes en C# o C++/CX, el código real no es mucho más complicado que esto (y, de hecho, muchas de las variables intermedias se pueden omitir); consulta la sección sobre Carga de recursos de .NETa continuación. Las aplicaciones basadas en C++/WRL serán más complejas debido a las API basadas en COM de bajo nivel que se usan para activar y llamar a las API de WinRT, pero los pasos fundamentales que se realizan son los mismos; consulte la sección sobre la carga de recursos MUI de Win32, a continuación.

Carga de recursos de .NET

Dado que .NET tiene un mecanismo integrado para buscar y cargar recursos (conocidos como "ensamblados satélite"), no hay código explícito que reemplace como en el ejemplo sintético anterior: en .NET solo necesita los archivos DLL de recursos en los directorios adecuados y se encuentran automáticamente. Cuando una aplicación se empaqueta como MSIX o .appx mediante paquetes de recursos, la estructura de directorios es algo diferente, en lugar de tener los directorios de recursos como subdirectorios del directorio de aplicaciones principal, son elementos del mismo nivel (o no están presentes en absoluto si el usuario no tiene el idioma enumerado en sus preferencias).

Por ejemplo, imagine una aplicación .NET con el siguiente diseño, donde todos los archivos existen en la MainApp carpeta :

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

Después de la conversión a .appx, el diseño tendrá un aspecto similar al siguiente, suponiendo en-US que era el idioma predeterminado y el usuario tiene alemán y francés enumerados en su lista de idiomas:

+ 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

Dado que los recursos localizados ya no existen en subdirectorios debajo de la ubicación de instalación del ejecutable principal, se produce un error en la resolución de recursos integrada de .NET. Afortunadamente, .NET tiene un mecanismo bien definido para controlar los intentos de carga de ensamblados erróneos: el evento AssemblyResolve. Una aplicación .NET que utiliza MRT debe registrarse para el evento correspondiente y proporcionar el ensamblado que falta para el subsistema de recursos de .NET.

Un ejemplo conciso de cómo usar las API de WinRT para localizar ensamblados satélite usados por .NET es el siguiente: El código tal como se presenta se comprime intencionadamente para mostrar una implementación mínima, aunque se puede ver que se asigna estrechamente al pseudocódigo anterior, con el pasado ResolveEventArgs que proporciona el nombre del ensamblado que necesitamos localizar. Puede encontrar una versión ejecutable de este código (con comentarios detallados y control de errores) en el archivo PriResourceRsolver.cs en el ejemplo de resolución de ensamblados de .NET de en 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);
  }
}

Dada la clase anterior, agregaría lo siguiente al principio en el código de inicio de la aplicación (antes de que los recursos localizados deban cargarse):

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

El entorno de ejecución de .NET generará el AssemblyResolve evento siempre que no encuentre los archivos DLL de recursos, en cuyo momento el controlador de eventos proporcionado localizará el archivo deseado a través de MRT y devolverá el ensamblado.

Nota:

Si la aplicación ya tiene un AssemblyResolve controlador para otros fines, deberá integrar el código de resolución de recursos con el código existente.

Carga de recursos MUI de Win32

La carga de recursos MUI de Win32 es básicamente la misma que la carga de ensamblados satélite de .NET, pero en su lugar usa código C++/CX o C++/WRL. El uso de C++/CX permite lograr un código mucho más sencillo que se asemeja estrechamente al código de C# mencionado anteriormente. Sin embargo, utiliza extensiones del lenguaje C++, opciones del compilador y sobrecarga adicional en tiempo de ejecución que podría desear evitar. Si es así, el uso de C++/WRL proporciona una solución de impacto mucho menor a costa de código más detallado. Sin embargo, si está familiarizado con la programación ATL (o COM en general), WRL debería resultarle familiar.

La siguiente función de ejemplo muestra cómo usar C++/WRL para cargar un archivo DLL de recursos específico y devolver un HINSTANCE que se puede usar para cargar más recursos mediante las API de recursos de Win32 habituales. Tenga en cuenta que, a diferencia del ejemplo de C# que inicializa explícitamente con ResourceContext el lenguaje solicitado por el entorno de ejecución de .NET, este código se basa en el lenguaje actual del usuario.

#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;
}

Fase 3: Creación de paquetes de recursos

Ahora que tiene un "paquete completo" que contiene todos los recursos, hay dos formas de crear paquetes principales y paquetes de recursos independientes para minimizar los tamaños de descarga e instalación.

  • Tome un paquete existente y ejecútelo a través de la herramienta Generador de Paquetes para crear automáticamente paquetes de recursos. Este es el enfoque preferido si tiene un sistema de compilación que ya genera un paquete grande y desea realizar un post-procesamiento para generar los paquetes de recursos.
  • Produzca directamente los paquetes de recursos individuales e inclúyalos en un conjunto. Este es el enfoque preferido si tiene más control sobre el sistema de compilación y puede compilar los paquetes directamente.

Paso 3.1: Creación de la agrupación

Uso de la herramienta Bundle Generator

Para usar la herramienta Bundle Generator, el archivo de configuración PRI creado para el paquete debe actualizarse manualmente para quitar la <packaging> sección.

Si usa Visual Studio, consulte Asegurarse de que los recursos están instalados en un dispositivo, independientemente de si un dispositivo los requiere para obtener información sobre cómo compilar todos los idiomas en el paquete principal mediante la creación de los archivos priconfig.packaging.xml y priconfig.default.xml.

Si está editando archivos manualmente, siga estos pasos:

  1. Cree el archivo de configuración de la misma manera que antes, sustituyendo la ruta de acceso correcta, el nombre de archivo y los idiomas:

    makepri createconfig /cf ..\contoso_demo.xml /dq en-US_de-DE_es-MX /pv 10.0 /o
    
  2. Abra manualmente el archivo creado .xml y elimine toda &lt;packaging&rt; la sección (pero mantenga intacto todo lo demás):

    <?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. Compile el archivo y el .pri.appx paquete como antes, con el archivo de configuración actualizado y los nombres de directorio y archivo adecuados (consulte más arriba para obtener más información sobre estos comandos):

    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. Una vez creado el paquete, use el siguiente comando para crear la agrupación, con los nombres de directorio y archivo adecuados:

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

Ahora puede pasar al paso final, firma (consulte a continuación).

Creación manual de paquetes de recursos

La creación manual de paquetes de recursos requiere ejecutar un conjunto ligeramente diferente de comandos para compilar archivos independientes .pri y .appx; todos son similares a los comandos usados anteriormente para crear paquetes completos, por lo que se da una explicación mínima. Nota: Todos los comandos asumen que el directorio actual es el directorio que contiene el AppXManifest.xml archivo, pero todos los archivos se colocan en el directorio primario (puede usar otro directorio, si es necesario, pero no debe contaminar el directorio del proyecto con ninguno de estos archivos). Como siempre, reemplace los nombres de archivo "Contoso" por sus propios nombres de archivo.

  1. Use el comando siguiente para crear un archivo de configuración que asigna nombres solo el idioma predeterminado como calificador predeterminado; en este caso, en-US:

    makepri createconfig /cf ..\contoso_demo.xml /dq en-US /pv 10.0 /o
    
  2. Cree un archivo predeterminado .pri y .map.txt para el paquete principal, además de un conjunto adicional de archivos para cada idioma que se encuentre en el proyecto, con el siguiente comando:

    makepri new /pr . /cf ..\contoso_demo.xml /of ..\resources.pri /mf AppX /o
    
  3. Use el siguiente comando para crear el paquete principal (que contiene el código ejecutable y los recursos de lenguaje predeterminados). Como siempre, cambie el nombre según se ajuste, aunque debe colocar el paquete en un directorio independiente para facilitar la creación de la agrupación más adelante (en este ejemplo se usa el ..\bundle directorio):

    makeappx pack /m .\AppXManifest.xml /f ..\resources.map.txt /p ..\bundle\contoso_demo.main.appx /o
    
  4. Una vez creado el paquete principal, use el siguiente comando una vez para cada idioma adicional (es decir, repita este comando para cada archivo de mapa de idioma generado en el paso anterior). De nuevo, la salida debe estar en un directorio independiente (el mismo que el paquete principal). Tenga en cuenta que el idioma se especifica en la opción /f y la opción /p y el uso del nuevo argumento /r (lo que indica que se desea un paquete de recursos):

    makeappx pack /r /m .\AppXManifest.xml /f ..\resources.language-de.map.txt /p ..\bundle\contoso_demo.de.appx /o
    
  5. Combine todos los paquetes del directorio bundle en un único .appxbundle archivo. La nueva /d opción especifica el directorio que se va a usar para todos los archivos de la agrupación (por lo que los .appx archivos se colocan en un directorio independiente en el paso anterior):

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

El último paso para compilar el paquete es firmar.

Paso 3.2: Firma del lote

Una vez que haya creado el .appxbundle archivo (ya sea a través de la herramienta Generador de paquetes o manualmente), tendrá un único archivo que contiene el paquete principal más todos los paquetes de recursos. El último paso es firmar el archivo para que Windows lo instale:

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

Esto generará un archivo firmado .appxbundle que contiene el paquete principal más todos los paquetes de recursos específicos del lenguaje. Se puede hacer doble clic como un archivo de paquete para instalar la aplicación más los idiomas adecuados en función de las preferencias de idioma de Windows del usuario.