Локализация строк в манифесте пакета приложения и интерфейсе пользователя

Дополнительные сведения о преимуществах локализации приложений см. в разделе Глобализация и локализация.

Если вы хотите, чтобы ваше приложение поддерживало разные языки интерфейса, а в вашем коде или разметке XAML либо манифесте пакета приложения есть строковые литералы, переместите эти строки из кода или разметки в файл ресурсов (RESW-файл). Затем можно создать переведенную копию этого файла ресурсов для каждого языка, поддерживаемого вашим приложением.

Жестко закодированные строковые литералы могут отображаться в императивном коде или в разметке XAML, например в качестве свойства Text элемента TextBlock. Они также могут отображаться в исходном файле манифеста пакета приложения ( Package.appxmanifest файл), например в качестве значения отображаемого имени на вкладке "Приложение" конструктора манифестов Visual Studio. Переместите эти строки в файл ресурсов (RESW) и замените жестко закодированные строковые литералы в приложении и в манифесте ссылками на идентификаторы ресурсов.

В отличие от ресурсов изображений, где в файле ресурсов образа содержится только один ресурс образа, в файле строкового ресурса содержится несколько строковых ресурсов. Строковый файл ресурсов — это файл ресурсов (RESW), который обычно создается в папке \Strings в проекте. Дополнительные сведения об использовании квалификаторов в именах файлов ресурсов (RESW) см. в разделе "Настройка ресурсов" для языка, масштабирования и других квалификаторов.

Хранение строк в файле ресурсов

  1. Задайте язык приложения по умолчанию.

    1. Откройте решение в Visual Studio Package.appxmanifest.
    2. На вкладке "Приложение" убедитесь, что язык по умолчанию задан соответствующим образом (например, en или en-US). Остальные шаги предполагают, что для языка по умолчанию задано значение en-US.

    Примечание.

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

  2. Создайте файл ресурсов (RESW) для языка по умолчанию.

    1. В узле проекта создайте папку и назовите ее Strings.
    2. В разделе Stringsсоздайте вложенную папку и назовите ее en-US.
    3. В разделе en-USсоздайте файл ресурсов (RESW) (под типами ФАЙЛОВ XAML в диалоговом окне "Добавление нового элемента ") и убедитесь, что он называется Resources.resw.

    Примечание.

    Если у вас есть файлы ресурсов .NET (RESX), которые вы хотите перенести, см . раздел "Перенос XAML" и пользовательского интерфейса.

  3. Откройте Resources.resw и добавьте эти строковые ресурсы.

    Strings/en-US/Resources.resw

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

    В этом примере "Приветствие" — это строковый идентификатор ресурса, который можно ссылаться на разметку, как показано ниже. Для идентификатора "Приветствие" строка предоставляется для свойства Text, а строка предоставляется для свойства Width. "Greeting.Text" является примером идентификатора свойства, так как он соответствует свойству элемента пользовательского интерфейса. Кроме того, можно добавить "Greeting.Foreground" в столбец "Имя" и задать для него значение "Красный". Идентификатор "Прощание" — это простой строковый идентификатор ресурса; Он не имеет вложенных свойств, и его можно загрузить из императивного кода, как показано ниже. Столбец комментариев является хорошим местом для предоставления каких-либо специальных инструкций переводчикам.

    В этом примере, так как у нас есть простая запись идентификатора ресурса строки с именем "Farewell", мы также не можем иметь идентификаторы свойств на основе этого же идентификатора. Таким образом, добавление "Farewell.Text" приведет к ошибке повторяющихся записей при сборке Resources.resw.

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

Ссылка на строковый идентификатор ресурса из XAML

Директива x:Uid используется для связывания элемента управления или другого элемента в разметке со строковым идентификатором ресурса.

<TextBlock x:Uid="Greeting"/>

Во время \Strings\en-US\Resources.resw выполнения загружается (так как сейчас это единственный файл ресурсов в проекте). Директива x:Uid в TextBlock приводит к тому, что поиск будет выполняться, чтобы найти идентификаторы свойств внутри Resources.resw , в котором содержится строковый идентификатор ресурса "Приветствие". Идентификаторы свойств "Greeting.Text" и "Greeting.Width" найдены, а их значения применяются к TextBlock, переопределяя значения, заданные локально в разметке. Если вы добавили это, будет применено значение "Приветствие.Foreground". Но для задания свойств в элементах разметки XAML используются только идентификаторы свойств, поэтому параметр x:Uid значение "Farewell" в этом TextBlock не будет иметь никакого эффекта. Resources.reswсодержит идентификатор строкового ресурса "Farewell", но он не содержит идентификаторов свойств для него.

При назначении строкового идентификатора ресурса элементу XAML убедитесь, что все идентификаторы свойств для этого идентификатора подходят для элемента XAML. Например, если задано x:Uid="Greeting" в TextBlock, то "Приветствие.Текст" будет разрешаться, так как тип TextBlock имеет свойство Text. Но если вы настроили x:Uid="Greeting" кнопку, "Приветствие.Текст" приведет к ошибке во время выполнения, так как тип кнопки не имеет свойства Text. Одним из решений этого случая является создание идентификатора свойства с именем ButtonGreeting.Content и установка x:Uid="ButtonGreeting" кнопки.

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

Примечание. Дляприсоединенных свойств требуется специальный синтаксис в столбце "Имя" resw-файла. Например, чтобы задать значение для присоединенного свойства AutomationProperties.Name для идентификатора "Приветствие", это то, что вы введете в столбец Name.

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

Ссылка на строковый идентификатор ресурса из кода

Вы можете явно загрузить строковый ресурс на основе простого идентификатора ресурса строки.

Примечание.

Если у вас есть вызов любого метода GetForCurrentView , который может выполняться в фоновом или рабочем потоке, то защитите этот вызов с if (Windows.UI.Core.CoreWindow.GetForCurrentThread() != null) помощью теста. Вызов GetForCurrentView из фонового или рабочего потока приводит к исключению "<имя> _типа не может быть создано в потоках, которые не имеют 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");

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

Если имя ресурса сегментировано (оно содержит символы "." ), замените точки косой чертой ("/") в имени ресурса. Идентификаторы свойств, например, содержат точки; поэтому для загрузки одного из них из кода необходимо выполнить подстановку.

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

Если вы сомневаетесь, вы можете использовать MakePri.exe для дампа файла PRI приложения. Каждый ресурс uri отображается в дамповом файле.

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

Сведения о строковом идентификаторе ресурса из манифеста пакета приложения

  1. Откройте исходный файл манифеста пакета приложения ( Package.appxmanifest файл), в котором по умолчанию приложение Display name выражается как строковый литерал.

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

  2. Чтобы сделать локализованную версию этой строки, откройте Resources.resw и добавьте новый строковый ресурс с именем AppDisplayName и значением Adventure Works Cycles.

  3. Замените литерал строки отображаемого имени ссылкой на только что созданный идентификатор ресурса строки (AppDisplayName). Для этого используется схема URI (универсальный ms-resource идентификатор ресурса).

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

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

Локализация строковых ресурсов

  1. Создайте копию файла ресурсов (RESW) для другого языка.

    1. В разделе "Строки" создайте вложенную папку и назовите ее "de-DE" для Deutsch (Deutschland).
      Примечание. Для имени папки можно использовать любой тег языка BCP-47. Дополнительные сведения о квалификаторе языка и списке тегов общего языка см. в статье "Настройка ресурсов" для языка, масштабирования и других квалификаторов .
    2. Создайте копию Strings/en-US/Resources.resw в папке Strings/de-DE .
  2. Перевод строк.

    1. Откройте и преобразуете Strings/de-DE/Resources.resw значения в столбце Value. Вам не нужно переводить комментарии.

    Strings/de-DE/Resources.resw

    add resource, german

При желании можно повторить шаги 1 и 2 для дальнейшего языка.

Strings/fr-FR/Resources.resw

add resource, french

Тестирование приложения

Протестируйте приложение для языка отображения по умолчанию. Затем можно изменить язык отображения в Параметры> Time и>языкового региона и языков> и повторно протестировать приложение. Просмотрите строки в пользовательском интерфейсе, а также в оболочке (например, строка заголовка — отображаемое имя) и короткое имя на плитках.

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

Факторирование строк в несколько файлов ресурсов

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

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

Чтобы область ссылку на строковый идентификатор ресурса к конкретному файлу, необходимо добавить /<resources-file-name>/ перед идентификатором. В приведенном ниже примере разметки предполагается, что ErrorMessages.resw содержит ресурс, имя которого — PasswordTooWeak.Text и значение которого описывает ошибку.

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

Необходимо добавить /<resources-file-name>/ только до идентификатора строкового ресурса для файлов ресурсов, отличных отResources.resw этого. Это связано с тем, что "Resources.resw" — это имя файла по умолчанию, поэтому предполагается, что вы опустите имя файла (как и в предыдущих примерах в этом разделе).

В приведенном ниже примере кода предполагается, что ErrorMessages.resw содержит ресурс, имя которого — "MismatchedPasswords" и значение которого описывает ошибку.

Примечание.

Если у вас есть вызов любого метода GetForCurrentView , который может выполняться в фоновом или рабочем потоке, то защитите этот вызов с if (Windows.UI.Core.CoreWindow.GetForCurrentThread() != null) помощью теста. Вызов GetForCurrentView из фонового или рабочего потока приводит к исключению "<имя> _типа не может быть создано в потоках, которые не имеют 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");

Если вы переместили ресурс AppDisplayName из Resources.resw и ManifestResources.reswв, в манифест пакета приложения, вы измените ms-resource:AppDisplayName его ms-resource:/ManifestResources/AppDisplayName.

Если имя файла ресурса сегментировано (оно содержит символы "." ), оставьте точки в имени при его ссылке. Не заменяйте точки косой чертой ("/") символами, например для имени ресурса.

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

Если вы сомневаетесь, вы можете использовать MakePri.exe для дампа файла PRI приложения. Каждый ресурс uri отображается в дамповом файле.

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

Загрузка строки для определенного языка или другого контекста

ResourceContext по умолчанию (полученный из ResourceContext.GetForCurrentView) содержит значение квалификатора для каждого имени квалификатора, представляющего контекст среды выполнения по умолчанию (другими словами, параметры текущего пользователя и компьютера). Файлы ресурсов (RESW) сопоставляются на основе квалификаторов в их именах с значениями квалификатора в этом контексте среды выполнения.

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

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

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;

Использование qualifierValues , как в приведенном выше примере кода, работает для любого квалификатора. Для специального случая языка вместо этого можно сделать это.

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

Для того же эффекта на глобальном уровне можно переопределить значения квалификатора в resourceContext по умолчанию. Но вместо этого мы советуем вызвать ResourceContext.SetGlobalQualifierValue. Значения задаются один раз при вызове SetGlobalQualifierValue, а затем эти значения применяются к ResourceContext по умолчанию при каждом использовании для подстановок.

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

Некоторые квалификаторы имеют системный поставщик данных. Таким образом, вместо вызова SetGlobalQualifierValue можно вместо этого настроить поставщик через собственный API. Например, в этом коде показано, как задать PrimaryLanguageOverride.

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

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

Работающее приложение может реагировать на изменения в системных параметрах, влияющих на значения квалификатора в resourceContext по умолчанию. Любой из этих системных параметров вызывает событие MapChanged в ResourceContext.QualifierValues.

В ответ на это событие можно перезагрузить строки из ResourceContext по умолчанию.

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");
}

Загрузка строк из библиотеки классов или библиотеки среда выполнения Windows

Строковые ресурсы библиотеки классов (универсальной windows) или библиотеки среда выполнения Windows (универсальная windows) обычно добавляются в вложенную папку пакета, в который они включены во время сборки. Идентификатор ресурса такой строки обычно принимает имя _библиотеки формы/ResourcesFileName/ResourceIdentifier.

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

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

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

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

Это не нужно делать для библиотеки классов (универсальная windows). При сомнении можно указать параметры командной строки MakePri.exe для дампа файла PRI компонента или библиотеки. Каждый ресурс uri отображается в дамповом файле.

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

Загрузка строк из других пакетов

Ресурсы для пакета приложений управляются и получают доступ к ним с помощью собственного ресурса верхнего уровня пакета, доступного из текущего ResourceManager. В каждом пакете различные компоненты могут иметь собственные поддери ResourceMap, к которым можно получить доступ с помощью ResourceMap.GetSubtree.

Пакет платформы может получить доступ к собственным ресурсам с абсолютным URI идентификатора ресурса. См. также схемы URI.

Загрузка строк в распакованных приложениях

Начиная с Windows версии 1903 (обновление за май 2019 г.), распакованные приложения также могут использовать систему управления ресурсами.

Просто создайте пользовательские элементы управления или библиотеки UWP и сохраните все строки в файле ресурсов. Затем можно ссылаться на строковый идентификатор ресурса из XAML, ссылаться на строковый идентификатор ресурса из кода или загружать строки из библиотеки классов или библиотеки среда выполнения Windows.

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

  1. Используйте GetForViewIndependentUse вместо GetForCurrentView при разрешении ресурсов из кода, так как текущее представление в неупакованных сценариях отсутствует. Следующее исключение возникает при вызове GetForCurrentView в непакованных сценариях: контексты ресурсов могут не создаваться в потоках, у которых нет CoreWindow.
  2. Используйте MakePri.exe , чтобы вручную создать файл resources.pri приложения.
    • Выполнить makepri new /pr <PROJECTROOT> /cf <PRICONFIG> /of resources.pri
    • <PRICONFIG> должен опустить раздел "<упаковка>", чтобы все ресурсы были упаковываются в один файл resources.pri. При использовании файла конфигурации MakePri.exe по умолчанию, созданного с помощью createconfig, необходимо удалить< раздел упаковки> вручную после его создания.
    • <PRICONFIG> должен содержать все соответствующие индексаторы, необходимые для объединения всех ресурсов в проекте в один файл resources.pri. Файл конфигурации MakePri.exe по умолчанию, созданный с помощью createconfig, включает все индексаторы.
    • Если вы не используете конфигурацию по умолчанию, убедитесь, что индексатор PRI включен (просмотрите конфигурацию по умолчанию для этого), чтобы объединить pris, найденные из ссылок на проекты UWP, ссылки NuGet и т. д., расположенные в корне проекта.

      Примечание.

      Пропуская /IndexNameи не имея манифеста приложения, пространство имен IndexName/root файла PRI автоматически присваивается приложению, которое среда выполнения понимает для распакованных приложений (это удаляет предыдущую жесткую зависимость от идентификатора пакета). При указании URI ресурсов ms-resource:/// ссылки, которые опустили корневое пространство имен в качестве корневого пространства имен для распакованных приложений (или можно явно указать приложение как в ms-resource://Application/).

  3. Скопируйте PRI-файл в выходной каталог сборки exe
  4. Запустите .exe

    Примечание.

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

Важно!

При изменении ресурсов необходимо вручную перестроить файлы PRI. Рекомендуется использовать скрипт после сборки, который обрабатывает команду MakePri.exe и копирует выходные данные resources.pri в каталог .exe.

Важные API