Localizar cadenas en la interfaz de usuario y el manifiesto de paquete de la aplicación

Para más información sobre la propuesta de valor de localizar la aplicación, consulta Globalización y localización.

Si quieres que tu aplicación admita diferentes idiomas de pantalla y tenga literales de cadena en el código, o bien marcado XAML o manifiesto de paquete de la aplicación, mueve esas cadenas a un archivo de recursos (.resw). A continuación, puedes hacer una copia traducida de ese archivo de recursos para cada idioma que admita la aplicación.

Los literales de cadena codificados de forma rígida pueden aparecer en código imperativo o en marcado XAML; por ejemplo, como la propiedad Text de un tipo TextBlock. También pueden aparecer en el archivo de origen del manifiesto de paquete de la aplicación (el archivo Package.appxmanifest); por ejemplo, como el valor de Nombre para mostrar en la pestaña Aplicación del Diseñador de manifiestos de Visual Studio. Mueva estas cadenas a un archivo de recursos (.resw) y reemplace los literales de cadena codificados de forma rígida en la aplicación y en el manifiesto con referencias a identificadores de recursos.

A diferencia de los recursos de imagen, donde solo se incluye un recurso de imagen en un archivo de recursos de imagen, se encuentran varios recursos de cadena en un archivo de recursos de cadena. Un archivo de recursos de cadena es un archivo de recursos (.resw) y normalmente se crea este tipo de archivo de recursos en una carpeta \Strings del proyecto. Para obtener información sobre cómo utilizar calificadores en los nombres de los archivos de recursos (.resw), consulte Adaptar los recursos para el idioma, la escala y otros calificadores.

Almacenar cadenas en un archivo de recursos

  1. Establezca el idioma predeterminado de la aplicación.

    1. Con la solución abierta en Visual Studio, abra Package.appxmanifest.
    2. En la pestaña Aplicación, confirme que el idioma predeterminado se haya establecido correctamente (por ejemplo, "en" o "en-US"). En los pasos restantes se supone que ha establecido el idioma predeterminado en "en-US".

    Nota:

     Como mínimo, debe proporcionar recursos de cadena localizados para este idioma predeterminado. Estos son los recursos que se cargarán si no se puede encontrar ninguna coincidencia mejor para las opciones de idioma preferido o idioma para mostrar del usuario.

  2. Cree un archivo de recursos (.resw) para el idioma predeterminado.

    1. En el nodo del proyecto, cree una carpeta y asígnale el nombre Strings.
    2. En Strings, cree una subcarpeta y asígnele el nombre en-US.
    3. En en-US, cree un nuevo archivo de recursos (.resw) (en los tipos de archivo XAML en el cuadro de diálogo Agregar nuevo elemento) y confirme que se denomina Resources.resw.

    Nota:

    Si tiene archivos de recursos (.resx) de .NET que desea portar, consulte Migración de XAML y la interfaz de usuario.

  3. Abra Resources.resw y agregue estos recursos de cadena.

    Strings/en-US/Resources.resw

    Screenshot of the Add Resource table of the Strings > E N U S > Resources.resw file.

    En este ejemplo, "Greeting" es un identificador de recursos de cadena al que puede hacer referencia desde el marcado, como se mostrará a continuación. Para el identificador "Greeting", se proporciona una cadena para una propiedad Text y se proporciona una cadena para una propiedad Width. "Greeting.Text" es un ejemplo de un identificador de propiedad porque corresponde a una propiedad de un elemento de la interfaz de usuario. También puede, por ejemplo, agregar "Greeting.Foreground" en la columna Name y establecer Value en "Red". El identificador "Farewall" es un identificador de recurso de cadena simple; no tiene subpropiedades y se puede cargar desde código imperativo, como se mostrará. La columna Comment es un buen lugar para indicar instrucciones especiales a los traductores.

    En este ejemplo, dado que hay una entrada de identificador de recurso de cadena simple denominada "Farewell", tampoco podemos tener identificadores de propiedad basados en ese mismo identificador. Por lo tanto, al agregar "Farewell.Text" se produciría un error de entrada duplicada al compilar Resources.resw.

    Los identificadores de recursos no distinguen mayúsculas de minúsculas y deben ser únicos por cada archivo de recursos. Asegúrese de utilizar identificadores de recursos significativos para proporcionar contexto adicional para los traductores. Y no cambie los identificadores de recursos después de enviar los recursos de cadena para traducir. Los equipos de localización utilizan el identificador de recursos para realizar un seguimiento de las adiciones, eliminaciones y actualizaciones de los recursos. Los cambios en los identificadores de recursos, que también se conocen como "desplazamiento de identificadores de recursos", requieren que se vuelvan a traducir las cadenas, ya que aparecerán como si se hubiesen eliminado las cadenas y se hubiesen agregado otras.

Referencia a un identificador de recurso de cadena desde XAML

Use una directiva x:Uid para asociar un control u otro elemento en el marcado con un identificador de recurso de cadena.

<TextBlock x:Uid="Greeting"/>

En tiempo de ejecución, se carga \Strings\en-US\Resources.resw (ya que ahora es el único archivo de recursos del proyecto). La directiva x:Uid en TextBlock hace que se produzca una búsqueda, para buscar identificadores de propiedad dentro de Resources.resw que contengan el identificador de recurso de cadena "Greeting". Se encuentran los identificadores de las propiedades "Greeting.Text" y "Greeting.Width" y sus valores se aplican a TextBlock, reemplazando los valores establecidos localmente en el marcado. También se aplicaría el valor "Greeting.Foreground" si lo hubiera agregado. Pero solo se usan identificadores de propiedad para establecer propiedades en elementos de marcado XAML, por lo que establecer x:Uid en "Farewell" en este TextBlock no tendría ningún efecto. Resources.resw contiene el identificador de recurso de cadena "Farewell", pero no contiene identificadores de propiedad para él.

Al asignar un identificador de recurso de cadena a un elemento XAML, asegúrese de que todos los identificadores de propiedad de ese identificador sean adecuados para el elemento XAML. Por ejemplo, si establece x:Uid="Greeting" en un TextBlock, "Greeting.Text" se resolverá porque el tipo TextBlock tiene una propiedad Text. Pero si establece x:Uid="Greeting" en un Button, "Greeting.Text" provocará un error en tiempo de ejecución porque el tipo Button no tiene una propiedad Text. Una solución para ese caso es crear un identificador de propiedad denominado "ButtonGreeting.Content" y establecer x:Uid="ButtonGreeting" en el Button.

En lugar de establecer Width desde un archivo de recursos, es probable que desee permitir que los controles ajusten el tamaño dinámicamente al contenido.

Nota: Para las propiedades adjuntas, necesita una sintaxis especial en la columna Nombre de un archivo .resw. Por ejemplo, para establecer un valor para la propiedad adjunta AutomationProperties.Name para el identificador "Greeting", esto es lo que escribiría en la columna Name.

Greeting.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name

Consultar un identificador de recurso de cadena desde el código

Puede cargar explícitamente un recurso de cadena basado en un identificador de recurso de cadena simple.

Nota:

Si tiene una llamada a cualquier método GetForCurrentView que se pueda ejecutar en un subproceso de trabajo o en segundo plano, guarde esa llamada con una prueba if (Windows.UI.Core.CoreWindow.GetForCurrentThread() != null). Llamar a GetForCurrentView desde un subproceso de trabajo o en segundo plano da como resultado la excepción "<typename> may not be created on threads that do not have a CoreWindow".

var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
this.myXAMLTextBlockElement.Text = resourceLoader.GetString("Farewell");
auto resourceLoader{ Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView() };
myXAMLTextBlockElement().Text(resourceLoader.GetString(L"Farewell"));
auto resourceLoader = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView();
this->myXAMLTextBlockElement->Text = resourceLoader->GetString("Farewell");

Puede usar este mismo código desde una biblioteca de clases (Windows universal) o un proyecto de biblioteca Windows Runtime (Windows universal). En tiempo de ejecución, se cargan los recursos de la aplicación que hospeda la biblioteca. Se recomienda que una biblioteca cargue los recursos de la aplicación que lo hospeda, ya que es probable que la aplicación tenga un mayor grado de localización. Si una biblioteca necesita proporcionar recursos, debe proporcionar a su aplicación de hospedaje la opción de reemplazar esos recursos como entrada.

Si un nombre de recurso está segmentado (contiene caracteres "."), sustituya puntos por caracteres de barra diagonal ("/") en el nombre del recurso. Por ejemplo, los identificadores de propiedad contienen puntos; por lo tanto, tendría que realizar esta sustitución para cargar uno de ellos desde el código.

this.myXAMLTextBlockElement.Text = resourceLoader.GetString("Fare/Well"); // <data name="Fare.Well" ...> ...

Si tiene dudas, puede utilizar MakePri.exe para volcar el archivo PRI de la aplicación. Cada uri de recurso se muestra en el archivo volcado.

<ResourceMapSubtree name="Fare"><NamedResource name="Well" uri="ms-resource://<GUID>/Resources/Fare/Well">...

Consultar un identificador de recurso de cadena desde el manifiesto de paquete de la aplicación

  1. Abra el archivo de origen del manifiesto del paquete de la aplicación (el archivo Package.appxmanifest), en el que el Display name de la aplicación se expresa como un literal de cadena.

    Screenshot of the Package.appxmanifest file showing the Application tab with the Display name set to Adventure Works Cycles.

  2. Para crear una versión localizable de esta cadena, abra Resources.resw y agregue un nuevo recurso de cadena con el nombre "AppDisplayName" y el valor "Adventure Works Cycles".

  3. Sustituya el literal de cadena Nombre de visualización por una referencia al identificador de recurso de cadena que acaba de crear ("AppDisplayName"). Use el esquema URI (identificador uniforme de recursos) ms-resource para hacerlo.

    Screenshot of the Package.appxmanifest file showing the Application tab with the Display name set to M S resource App Display Name.

  4. Repita este proceso para cada cadena del manifiesto que quiera localizar. Por ejemplo, el nombre corto de la aplicación (que puede configurar para que aparezca en el icono de la aplicación en Inicio). Para obtener una lista de todos los elementos del manifiesto del paquete de la aplicación que puede localizar, consulte Elementos de manifiesto localizables.

Localizar los recursos de cadena

  1. Haga una copia del archivo de recursos (.resw) para otro idioma.

    1. En "Strings", cree una nueva subcarpeta y asígnele el nombre "de-DE" para alemán (Alemania).
      Nota: Para el nombre de la carpeta, puede usar cualquier etiqueta de idioma BCP-47. Consulte Adaptar los recursos para el idioma, la escala y otros calificadores para obtener más información sobre el calificador de idioma y una lista de etiquetas de idioma comunes.
    2. Realice una copia de Strings/en-US/Resources.resw en la carpeta Strings/de-DE.
  2. Traduzca las cadenas.

    1. Abra Strings/de-DE/Resources.resw y traduzca los valores en la columna Value. No es necesario traducir los comentarios.

    Strings/de-DE/Resources.resw

    add resource, german

Si lo desea, puede repetir los pasos 1 y 2 para un idioma adicional.

Strings/fr-FR/Resources.resw

add resource, french

Prueba de la aplicación

Pruebe la aplicación para el idioma de visualización predeterminado. A continuación, puede cambiar el idioma de visualización en Configuración>Hora e idiomas>Región e idioma>Idiomas y volver a probar la aplicación. Examine las cadenas de la interfaz de usuario y también en el shell (por ejemplo, la barra de título, que es el nombre de visualización) y el nombre corto en los iconos.

Nota: Si se puede encontrar un nombre de carpeta que coincida con la configuración de idioma para mostrar, se carga el archivo de recursos dentro de esa carpeta. De lo contrario, se realiza la reserva, finalizando con los recursos para el idioma predeterminado de la aplicación.

Factorización de cadenas en varios archivos de recursos

Puede mantener todas las cadenas en un único archivo de recursos (resw) o puede factorizarlas en varios archivos de recursos. Por ejemplo, es posible que desee mantener los mensajes de error en un archivo de recursos, las cadenas de manifiesto del paquete de la aplicación en otra y las cadenas de la interfaz de usuario en otra. Este es el aspecto de la estructura de carpetas en ese caso.

Screenshot of the Solution panel showing the Adventure Works Cycles > Strings folder with German, U S English, and French locale folders and files.

Para establecer el ámbito de una referencia de identificador de recurso de cadena a un archivo determinado, basta con agregar /<resources-file-name>/ antes del identificador. En el ejemplo de marcado siguiente se supone que ErrorMessages.resw contiene un recurso cuyo nombre es "PasswordTooWeak.Text" y cuyo valor describe el error.

<TextBlock x:Uid="/ErrorMessages/PasswordTooWeak"/>

Solo tiene que agregar /<resources-file-name>/ antes del identificador de recurso de cadena para los archivos de recursos distintos deResources.resw. Esto se debe a que "Resources.resw" es el nombre de archivo predeterminado, por lo que es lo que se supone que se omite un nombre de archivo (como se ha hecho en los ejemplos anteriores de este tema).

En el ejemplo de código siguiente se supone que ErrorMessages.resw contiene un recurso cuyo nombre es "MismatchedPasswords" y cuyo valor describe el error.

Nota:

Si tiene una llamada a cualquier método GetForCurrentView que se pueda ejecutar en un subproceso de trabajo o en segundo plano, guarde esa llamada con una prueba if (Windows.UI.Core.CoreWindow.GetForCurrentThread() != null). Llamar a GetForCurrentView desde un subproceso de trabajo o en segundo plano da como resultado la excepción "<typename> may not be created on threads that do not have a CoreWindow".

var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView("ErrorMessages");
this.myXAMLTextBlockElement.Text = resourceLoader.GetString("MismatchedPasswords");
auto resourceLoader{ Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView(L"ErrorMessages") };
myXAMLTextBlockElement().Text(resourceLoader.GetString(L"MismatchedPasswords"));
auto resourceLoader = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView("ErrorMessages");
this->myXAMLTextBlockElement->Text = resourceLoader->GetString("MismatchedPasswords");

Si fuera a mover el recurso "AppDisplayName" fuera de Resources.resw y a ManifestResources.resw, en el manifiesto del paquete de la aplicación cambiaría ms-resource:AppDisplayName a ms-resource:/ManifestResources/AppDisplayName.

Si se segmenta un nombre de archivo de recursos (contiene caracteres "."), deje los puntos en el nombre al hacer referencia a él. No sustituya puntos por caracteres de barra diagonal ("/"), como lo haría para un nombre de recurso.

var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView("Err.Msgs");

Si tiene dudas, puede utilizar MakePri.exe para volcar el archivo PRI de la aplicación. Cada uri de recurso se muestra en el archivo volcado.

<ResourceMapSubtree name="Err.Msgs"><NamedResource name="MismatchedPasswords" uri="ms-resource://<GUID>/Err.Msgs/MismatchedPasswords">...

Carga de una cadena para un idioma específico u otro contexto

El ResourceContext predeterminado (obtenido de ResourceContext.GetForCurrentView) contiene un valor de calificador para cada nombre de calificador, que representa el contexto en tiempo de ejecución predeterminado (es decir, la configuración del usuario y la máquina actuales). Los archivos de recursos (.resw) se comparan (en función de los calificadores de sus nombres) con los valores de calificador de ese contexto en tiempo de ejecución.

Pero es posible que haya ocasiones en las que quiera que la aplicación invalide la configuración del sistema y sea explícita sobre el idioma, la escala u otro valor calificador que se va a usar al buscar que se cargue un archivo de recursos coincidente. Por ejemplo, es posible que quiera que los usuarios puedan seleccionar un idioma alternativo para la información sobre herramientas o los mensajes de error.

Para ello, puede construir un nuevo ResourceContext (en lugar de usar el predeterminado), invalidar sus valores y, a continuación, usar ese objeto de contexto en las búsquedas de cadenas.

var resourceContext = new Windows.ApplicationModel.Resources.Core.ResourceContext(); // not using ResourceContext.GetForCurrentView
resourceContext.QualifierValues["Language"] = "de-DE";
var resourceMap = Windows.ApplicationModel.Resources.Core.ResourceManager.Current.MainResourceMap.GetSubtree("Resources");
this.myXAMLTextBlockElement.Text = resourceMap.GetValue("Farewell", resourceContext).ValueAsString;

Usar QualifierValues como en el ejemplo de código anterior funciona para cualquier calificador. Para el caso especial de Language, también puede hacerlo en su lugar.

resourceContext.Languages = new string[] { "de-DE" };

Para el mismo efecto en un nivel global, puede invalidar los valores de calificador en ResourceContext predeterminado. Pero en su lugar le recomendamos que llame a ResourceContext.SetGlobalQualifierValue. Los valores se establecen una vez con una llamada a SetGlobalQualifierValue y, a continuación, esos valores están en vigor en el ResourceContext predeterminado cada vez que se usa para las búsquedas.

Windows.ApplicationModel.Resources.Core.ResourceContext.SetGlobalQualifierValue("Language", "de-DE");
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
this.myXAMLTextBlockElement.Text = resourceLoader.GetString("Farewell");

Algunos calificadores tienen un proveedor de datos del sistema. Por lo tanto, en lugar de llamar a SetGlobalQualifierValue, podría ajustar el proveedor a través de su propia API. Por ejemplo, este código muestra cómo establecer PrimaryLanguageOverride.

Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "de-DE";

Actualización de cadenas en respuesta a eventos de cambio de valor calificador

La aplicación en ejecución puede responder a los cambios en la configuración del sistema que afectan a los valores de calificador del ResourceContext predeterminado. Cualquiera de estas opciones de configuración del sistema invoca el evento MapChanged en ResourceContext.QualifierValues.

En respuesta a este evento, puede volver a cargar las cadenas desde ResourceContext predeterminado.

public MainPage()
{
    this.InitializeComponent();

    ...

    // Subscribe to the event that's raised when a qualifier value changes.
    var qualifierValues = Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView().QualifierValues;
    qualifierValues.MapChanged += new Windows.Foundation.Collections.MapChangedEventHandler<string, string>(QualifierValues_MapChanged);
}

private async void QualifierValues_MapChanged(IObservableMap<string, string> sender, IMapChangedEventArgs<string> @event)
{
    var dispatcher = this.myXAMLTextBlockElement.Dispatcher;
    if (dispatcher.HasThreadAccess)
    {
        this.RefreshUIText();
    }
    else
    {
        await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => this.RefreshUIText());
    }
}

private void RefreshUIText()
{
    var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
    this.myXAMLTextBlockElement.Text = resourceLoader.GetString("Farewell");
}

Cargar cadenas desde una biblioteca de clases o una biblioteca de Windows Runtime

Los recursos de cadena de una biblioteca de clases a la que se hace referencia (Windows universal) o Windows Runtime Library (Windows universal) se suelen agregar a una subcarpeta del paquete en el que se incluyen durante el proceso de compilación. El identificador de recurso de esta cadena suele tener el formato LibraryName/ResourcesFileName/ResourceIdentifier.

Una biblioteca puede obtener un ResourceLoader para sus propios recursos. Por ejemplo, el código siguiente muestra cómo una biblioteca o una aplicación que hace referencia a ella pueden obtener un ResourceLoader para los recursos de cadena de la biblioteca.

var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView("ContosoControl/Resources");
this.myXAMLTextBlockElement.Text = resourceLoader.GetString("exampleResourceName");

Para una biblioteca de Windows Runtime (Windows universal), si el espacio de nombres predeterminado está segmentado (contiene caracteres "." ), use puntos en el nombre del mapa de recursos.

var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView("Contoso.Control/Resources");

No es necesario hacerlo para una biblioteca de clases (Windows universal). Si tiene dudas, puede especificar opciones de línea de comandos de MakePri.exe para volcar el archivo PRI del componente o de la biblioteca. Cada uri de recurso se muestra en el archivo volcado.

<NamedResource name="exampleResourceName" uri="ms-resource://Contoso.Control/Contoso.Control/ReswFileName/exampleResourceName">...

Carga de cadenas desde otros paquetes

Los recursos de un paquete de aplicación se administran y se accede a ellos a través del ResourceMap de nivel superior del paquete al que se puede acceder desde el ResourceManager actual. Dentro de cada paquete, varios componentes pueden tener sus propios subárboles ResourceMap, a los que puede acceder a través de ResourceMap.GetSubtree.

Un paquete de marco puede acceder a sus propios recursos con un URI de identificador de recursos absoluto. Consulte también esquemas de URI.

Carga de cadenas en aplicaciones sin empaquetar

A partir de Windows, versión 1903 (actualización de mayo de 2019), las aplicaciones sin empaquetar también pueden aprovechar el sistema de administración de recursos.

Solo tiene que crear sus controles o bibliotecas de usuario para UWP y almacenar cualquier cadena en un archivo de recursos. A continuación, puede hacer referencia a un identificador de recurso de cadena desde XAML, hacer referencia a un identificador de recurso de cadena desde el código o cargar cadenas desde una biblioteca de clases o una biblioteca de Windows Runtime.

Para usar recursos en aplicaciones sin empaquetar, debe hacer algunas cosas:

  1. Use GetForViewIndependentUse en lugar de GetForCurrentView al resolver recursos desde código, ya que no hay ninguna vista actual en escenarios sin empaquetar. La siguiente excepción se produce si llama a GetForCurrentView en escenarios sin empaquetar: Es posible que los contextos de recursos no se creen en subprocesos que no tengan CoreWindow.
  2. Utilice MakePri.exe para generar manualmente el archivo resources.pri de la aplicación.
    • Ejecute makepri new /pr <PROJECTROOT> /cf <PRICONFIG> /of resources.pri:
    • El <PRICONFIG> debe omitir la sección "<empaquetado>" para que todos los recursos se incluyan en un único archivo resources.pri. Si usa el archivo de configuración de MakePri.exe predeterminado creado por createconfig, debe eliminar la sección "<empaquetado>" manualmente después de crearlo.
    • El <PriCONFIG> debe contener todos los indexadores pertinentes necesarios para combinar todos los recursos del proyecto en un único archivo resources.pri. El archivo de configuración de MakePri.exe predeterminado creado por createconfig incluye todos los indexadores.
    • Si no usa la configuración predeterminada, asegúrese de que el indexador PRI se ha habilitado (revise la configuración predeterminada de cómo hacerlo) para combinar las PRI que se encuentran en referencias de proyecto de UWP, referencias de NuGet, etc., que se encuentran dentro de la raíz del proyecto.

      Nota:

      Al omitir /IndexName, y por el proyecto que no tiene un manifiesto de aplicación, el espacio de nombres IndexName/raíz del archivo PRI se establece automáticamente en Aplicación, que el tiempo de ejecución entiende para las aplicaciones sin empaquetar (esto quita la dependencia de disco duro anterior en el identificador del paquete). Al especificar URI de recursos, ms-resource:/// hace referencias que omiten la inferencia de espacio de nombres raíz Aplicación como espacio de nombres raíz para aplicaciones sin empaquetar (o puede especificar Aplicación explícitamente como en ms-resource://Application/).

  3. Copie el archivo PRI en el directorio de salida de compilación del archivo .exe
  4. Ejecute el archivo .exe

    Nota:

    El sistema de administración de recursos usa el idioma para mostrar del sistema en lugar de la lista de idiomas preferidos por el usuario al resolver recursos basados en el idioma en aplicaciones sin empaquetar. La lista de idiomas preferidos por el usuario solo se usa para aplicaciones para UWP.

Importante

Debe recompilar manualmente los archivos PRI siempre que se modifiquen los recursos. Se recomienda usar un script posterior a la compilación que controle el comando MakePri.exe y copie la salida resources.pri en el directorio .exe.

API importantes