Compartir a través de


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 objetivo. Para más información sobre la propuesta de valor de localizar la aplicación, consulta Globalización y localización. Al empaquetar la aplicación o juego en .NET o Win32 como un paquete .msix o .appx, puedes aprovechar el sistema de administración de recursos para cargar recursos de la aplicación adaptados al contexto en tiempo de ejecución. En este tema se describen en profundidad las técnicas.

Hay muchas maneras de localizar una aplicación Win32 tradicional, pero Windows 8 ha introducido un nuevo sistema de administración de recursos que funciona en lenguajes de programación, entre 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 utilizar. 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 .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 reserva 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 migración para ayudar a mover aplicaciones Win32 heredadas a MRT con cambios mínimos en el código. Una vez realizada la migración 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. Tenga en cuenta que la localización basada en MRT funciona tanto para aplicaciones para la Plataforma universal de Windows (UWP) como para aplicaciones Win32 procesadas por el Puente de dispositivo de escritorio (también conocido como "Centennial").

En muchas situaciones, puede seguir utilizando los formatos de localización y el código fuente existentes mientras se lleva a cabo la integración 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 de contraste alto. Para obtener más información sobre cómo proporcionar varios recursos para mosaicos, iconos, etc., consulte Adaptar los recursos para el idioma, la escala, el contraste alto y otros calificadores.

Trabajo Prestación Coste estimado
Localización del manifiesto del paquete Trabajo mínimo necesario para que el contenido localizado aparezca en Windows Shell 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 Media
Compilación de paquetes de recursos Paso final para minimizar los tamaños de descarga e instalación Pequeño
Migración a formatos de recursos y API 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 (por ejemplo, facilidad de edición por parte de los no desarrolladores), 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 aparecerá en un botón (o la imagen que aparecerá 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 ha habilitado 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 (como SAVE_BUTTON_LABEL) en el mejor valor real posible (por ejemplo, "Guardar") de 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 idioma del usuario, el factor de escala 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 en comparación con 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 saveButton) y un archivo PNG utilizado para un logotipo (logoImage). Las etiquetas de texto se localizan en inglés y alemán, y el logotipo se ha optimizado para pantallas de escritorio normales (factor de escala del 100 %) y teléfonos de alta resolución (factor de escala del 300 %). Tenga en cuenta que este diagrama presenta una vista conceptual de alto nivel del modelo; no se asigna 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 ambientales (el idioma del usuario y el factor de escala de la pantalla). En el caso de las etiquetas, las cadenas se utilizan directamente. En el caso de la imagen del logotipo, las cadenas se interpretan como nombres de archivo y los archivos se leen fuera del disco.

Si el usuario habla un idioma distinto del inglés o el alemán, o tiene un factor de escala de pantalla distinto del 100 % o 300 %, MRT elige el candidato coincidente "más aproximado" en función de un conjunto de reglas de reserva (consulte Sistema de administración de recursos para obtener más información contextual).

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 que se necesitan 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 utilizan características de MRT en esta fase.

Fase 1: Localización del 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 se sigue utilizando 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 se siguen utilizando 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 aborda en este documento

Después de completar las fases 0-3 anteriores, tendrá una "agrupación" de aplicaciones que se puede enviar a Microsoft Store y que minimizará el tamaño de descarga e instalación de los usuarios omitiendo los recursos que no necesitan (por ejemplo, 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 conjuntos de recursos de .NET a archivos PRI. Esto puede dar lugar a un mayor ahorro de espacio para los tamaños de instalación y descarga. 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 debe reemplazar la tecnología actual de empaquetado e instalación por la tecnología estándar de empaquetado e implementación de la UWP. Esto se puede hacer de tres maneras:

  • Si tiene una aplicación de escritorio de gran tamaño con un instalador complejo o utiliza muchos puntos de extensibilidad del sistema operativo, puede utilizar 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 de menor tamaño 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 va a volver a compilar desde el origen y quiere actualizar la aplicación para que sea una aplicación para UWP pura, puede crear un nuevo proyecto en Visual Studio y confiar en el IDE para que realice gran parte del trabajo por usted.

Si desea 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 App Converter en el repositorio de GitHub de ejemplos de pasar aplicaciones del Puente de dispositivo de escritorio a UWP.

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). Se 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 de paquete de aplicación.

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

Fase 1: Localización del manifiesto

Paso 1.1: Actualizar los recursos y cadenas 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 icono Inicio de alta resolución, etc.

Para asegurarse de que el nombre y la descripción de la aplicación se localicen 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, haga lo siguiente:

  1. Cree un archivo XML denominado resources.resw y colóquelo en una subcarpeta de Strings\en-us del proyecto. Utilice 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 sustituye 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, consulte 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 utilizar el diseñador en Visual Studio:

  1. Cree la carpeta Strings\en-us (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 (utilice los mismos Names pero sustituya los 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 F7. Pero si empieza con un archivo XML mínimo, el diseñador no reconocerá el archivo porque falta una gran cantidad de metadatos adicionales; puede corregirlo copiando la información de XSD reutilizable de un archivo generado por el diseñador en el archivo XML editado manualmente.

Actualizar el manifiesto para hacer referencia a los recursos

Después de tener los valores definidos en el archivo .resw, 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: utilice este texto exacto, no el texto específico de la aplicación. No es necesario utilizar estos nombres de recursos exactos (puede elegir los suyos), pero lo que elija debe coincidir exactamente con lo que se encuentra en el archivo .resw. Estos nombres deben coincidir con los Names que ha creado en el archivo .resw, que tiene el prefijo con 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 utiliza el diseñador de manifiestos de Visual Studio, abra el archivo .appxmanifest y cambie los valores resaltados 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 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 los cuadros de texto Nombre para mostrar del paquete y Nombre para mostrar del publicador resaltados.

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

Ahora debería poder compilar el archivo .pri e implementar la aplicación para verificar 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 generar el archivo .pri en sí (puede encontrar más información en Empaquetado manual de aplicaciones):

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

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

  3. Escriba el comando siguiente, sustituyendo "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 corresponde). 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 que tiene un archivo de configuración, vuelva a ejecutar MakePRI para buscar realmente recursos en el disco y empaquetarlos en un archivo PRI. Sustituya "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 la 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 asignación (por lo que es posible excluir archivos en el paquete en un paso posterior)
    • /o lo establece en Sobrescribir el archivo de salida si existe
  5. Ahora tiene un archivo .pri con los recursos de idioma predeterminados (por ejemplo, en-US). Para verificar que ha funcionado 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 verificar que enumera los valores <NamedResource> (como ApplicationDescription y PublisherDisplayName) junto con los valores <Candidate> 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 contiene los archivos necesarios para el proyecto (incluido el archivo PRI, que no forma parte del directorio del proyecto). Lo que es más importante, 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.

Crear y firmar el paquete disperso

Ahora que se ha compilado el archivo PRI, puede crear y firmar el paquete:

  1. Para crear el paquete de aplicación, ejecute el siguiente comando sustituyendo 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 utiliza 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 utilizar
    • /f establece el archivo de asignación 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 crear un proyecto vacío de Windows universal en Visual Studio y copiar el archivo .pfx que crea, pero puede crear uno manualmente con las utilidades MakeCert y Pvk2Pfx tal 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, utilice el siguiente comando. Tenga en cuenta que el Publisher especificado en el elemento Identity de AppxManifest.xml debe coincidir con el Subject del certificado (no es el elemento <PublisherDisplayName>, que es el nombre de visualización localizado para mostrar a los usuarios). Como de costumbre, sustituya los nombres de archivo contoso_demo... por los nombres adecuados para el proyecto y (muy importante) asegúrese de que el archivo .pfx 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 (sustituyendo 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 se agrega al almacén de máquinas (no al 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 2017 o Visual Studio 2019 como administrador.

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

  3. Escriba el siguiente comando, sustituyendo 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 utilizar el Explorador de Windows:

  1. Navegue hasta la carpeta que contiene el archivo .pfx
  2. Haga doble clic en el archivo .pfx y debería aparecer el Asistente de importación de certificados
  3. Elija Local Machine y haga clic en Next
  4. Acepte el mensaje de elevación 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 corresponde, y haga clic en Next
  6. Seleccione Place all certificates in the following store
  7. Haga clic en Browse y elija la carpeta Trusted People (no "Editores de confianza")
  8. Haga clic Next y, a continuación, haga clic en Finish

Después de agregar el certificado al almacén Trusted People, 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 archivo .resw / .pri. 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 hace clic con el botón derecho en la aplicación en el menú Inicio, puede anclarla como icono y comprobar que también se muestra la información correcta.

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.

Crear recursos localizados adicionales

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

En la carpeta Strings, 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 archivo resources.resw (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 archivo .resw; este documento no cubre el propio paso de traducción.

Por ejemplo, el archivo Strings\de-DE\resources.resw podría tener este aspecto, con el texto resaltado cambiado de 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>

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

Actualizar el manifiesto del paquete para enumerar los idiomas admitidos

El manifiesto del paquete debe actualizarse para enumerar los idiomas que admite la aplicación. Desktop App Converter agrega el idioma predeterminado, pero los demás se deben agregar explícitamente. Si va a editar el archivo AppxManifest.xml directamente, actualice el nodo Resources de la siguiente manera: agregue tantos elementos como necesite y sustituya los idiomas adecuados que admita y asegúrese de que la primera entrada de la lista es el idioma predeterminado (reserva). 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 utiliza Visual Studio, no debe hacer nada. Si visualiza Package.appxmanifest, debería ver el valor especial x-generate, lo que hace que el proceso de compilación inserte los idiomas que encuentra en el proyecto (en función de 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 (las instrucciones para cambiar el idioma se muestran a continuación).

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

Si va a compilar manualmente el proyecto, siga los mismos pasos que antes, pero agregue los idiomas adicionales, separados por guiones bajos, 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 contenga todos los idiomas especificados que puede utilizar 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 si desea las ventajas de minimizar el tamaño de instalación o descarga de los recursos es que necesita realizar el trabajo adicional de crear paquetes de idioma independientes.

Realizar pruebas 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. Ejecute la aplicación Settings (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)
  1. Seleccione el nuevo idioma en la lista de idiomas
  2. Haga clic en Set as default

Ahora abra el menú Inicio y busque la aplicación; 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: Localización de otras 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 extensión windows.fileTypeAssociation en el manifiesto, utilizando el texto resaltado en verde exactamente como se muestra (ya que hará referencia a recursos) y sustituyendo el texto resaltado en 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, mediante la pestaña Declarations, teniendo en cuenta 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 de información resaltados.

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

... 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 de Explorador de archivos que muestra una información sobre herramientas que indica Archivos usados por la aplicación de demostración de Contoso.

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

Fase 2: Uso de MRT para identificar y localizar recursos

En la sección anterior se mostró cómo utilizar 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 archivos .resw y algunas herramientas adicionales. En esta sección se muestra cómo utilizar MRT para buscar recursos en los formatos de recursos existentes y utilizar 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, en este documento se harán 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 con los requisitos de MRT y que estén mayormente 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.dll o contoso.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 la aplicación usa una estructura de archivos sin formato 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 directorio Resources 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 en lo que nos centraremos.

Nota:

Todavía es posible usar MRT y las ventajas del empaquetado incluso si no puede 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, querrá buscar el archivo que contiene un recurso localizado, cargarlo y, a continuación, utilizarlo. 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 características para hacerlo si desea aprovecharlos).

Por ejemplo, la aplicación podría usar la API GetUserPreferredUILanguages Win32, la función sprintf CRT y la API CreateFile Win32 para sustituir las tres funciones de pseudocódigo anteriores y, a continuación, analizar manualmente el archivo de texto buscando pares name=value. (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 en la búsqueda de 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 activo actualmente de valores de calificador (idioma, factor de escala, etc.)
  • ResourceManager (la versión de WinRT, no la 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 en contraste con los recursos de cadena)
  • NamedResource, que representa un recurso lógico y todos sus posibles candidatos
  • ResourceCandidate, que representa un único recurso candidato 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 solicita una carpeta de idioma específica, como UICommands\en-US\ui.txt, aunque así es cómo existen los archivos en el disco. En su lugar, solicita el nombre de archivo lógico UICommands\ui.txt y se basa en MRT para buscar el archivo en disco adecuado en uno de los directorios de idioma.

Desde aquí, la aplicación de ejemplo podría seguir usando CreateFile para cargar el absoluteFileName y analizar los pares name=value igual que antes; ninguna de esas lógicas debe cambiar en la aplicación. Si está escribiendo en C# o C++/CX, el código real no es mucho más complicado que este (y, de hecho, muchas de las variables intermedias se pueden suprimir), consulte la sección sobre Carga de recursos de .NET, que se muestra a 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, que se muestra 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 sustituya 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 carpeta MainApp:

+ 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 que en-US 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 ensamblado erróneos: el evento AssemblyResolve. Una aplicación .NET que usa MRT debe registrarse para este evento 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 solucionador de ensamblados de .NET 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, se 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 evento AssemblyResolve 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 controlador AssemblyResolve 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 un código mucho más sencillo que coincide estrechamente con el código de C# anterior, pero usa extensiones de lenguaje C++, modificadores del compilador y sobrecarga en tiempo de ejecución adicional que puede 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 conoce la programación ATL (o COM en general), WRL debe parecerle 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 el ResourceContext con el lenguaje solicitado por el tiempo de ejecución de .NET, este código se basa en el idioma 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 rutas de acceso para crear paquetes principales y paquetes de recursos independientes para minimizar los tamaños de descarga e instalación:

  • Tome un paquete completo existente y ejecútelo a través de la herramienta Bundle Generator para crear automáticamente paquetes de recursos. Este es el enfoque preferido si tiene un sistema de compilación que ya genera un paquete completo y desea procesarlo posteriormente para generar los paquetes de recursos.
  • Genere directamente los paquetes de recursos individuales y cree una agrupación. 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 sección <packaging>.

Si usa Visual Studio, consulte Garantizar que los recursos se han instalado 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 .xml creado y elimine toda la sección &lt;packaging&rt; (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 .pri y el paquete .appx 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. Después de que se haya creado el paquete, utilice 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 .pri y .appx independientes: todos son similares a los comandos utilizados 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 archivo AppXManifest.xml, 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, sustituya los nombres de archivo "Contoso" por sus propios nombres de archivo.

  1. Use el siguiente comando para crear un archivo de configuración que asigna un nombre solo al 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 .pri y .map.txt predeterminado 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 lo considere, 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 directorio ..\bundle):

    makeappx pack /m .\AppXManifest.xml /f ..\resources.map.txt /p ..\bundle\contoso_demo.main.appx /o
    
  4. Una vez creado el paquete principal, utilice el siguiente comando una vez para cada idioma adicional (es decir, repita este comando para cada archivo de asignación 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 tanto en la opción /f como en 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 de agrupaciones en un único archivo .appxbundle. La nueva opción /d especifica el directorio que se va a usar para todos los archivos de la agrupación (por lo que los archivos .appx 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 de la agrupación

Una vez que haya creado el archivo .appxbundle (ya sea a través de la herramienta Bundle Generator 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 .appxbundle firmado que contiene el paquete principal más todos los paquetes de recursos específicos del idioma. 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.