WinUI3で、文字列リソースを登録してxaml、C#コードから使う(ローカライズ)

WInUI3関連記事

https://tera1707.com/entry/2022/02/06/144447#WinUI3

やりたいこと

WPFの時にも、文字列リソースを登録してxamlやコードから使ったが、同じことをWinUI3アプリでもやりたい。

やりかた

MS公式情報の、下記を元に対応を行う。
※このページはUWPアプリ向けだからか、WinUI3アプリでそのままやろうとしてもうまくいかなかったので、詰まったところ中心にメモする。

docs.microsoft.com

手順

各国言語ごと(ja-JP、en-US等)にResource.reswファイルを作成する

VisualStudioで、WinUI3プロジェクトの中にStringsフォルダを作成する。 さらに、Stringsフォルダの中に言語の名前毎にフォルダを作成する。下図のようなイメージ。

各言語のフォルダに、リソースファイル(.resw)を追加する。

下図のようなイメージ。

リソースの値を定義する

追加したResource.reswに、リソースを追加する。

リソースを定義する際は、対象のコントロールの、値をセットするプロパティに合わせて、定義をする。

  • <TextBlock>Textプロパティに使うなら「リソース名.Text」に「あいうえお」などを定義する。
  • <Button>Contentプロパティに使うなら「リソース名.Content」に「あいうえお」などを定義する。
  • <Button>Heightプロパティに使うなら「リソース名.Height」に「150」などを定義する。
  • <Grid>Backgroundプロパティに使うなら「リソース名.Background」に「Yellow」などを定義する。

という感じでリソースを作る。

xamlでリソースを使う

各コントロールに、x:Uid="リソース名"と書く。下記のような感じ。

<StackPanel x:Uid="MyStackPanel">
    <TextBlock x:Uid="MyTextBlock"/>
    <Button x:Uid="MyButton"/>
</StackPanel>    

これを実行すると、下図のような感じになる。


※見づらいが、ボタンがHeight=150分の高さで置かれている。

これで、xamlで使う分にはOK。

【注意】

リソースを使ったコントロールに存在しないプロパティのリソースを定義してしまったとき(例えば、ButtonにはTextプロパティがないのに、上で作ったMyTextBlockを使ってしまったときなど)は、下記のような例外がInitializeComponent()で起きる。

Microsoft.UI.Xaml.Markup.XamlParseException XAML parsing failed.

OSの言語設定に追従して、使うreswを切り替えるようにする

リソースの使い方として、OSの言語の設定が日本語のときはja-JPフォルダの下に作ったResources.resw、英語のときはen-USの下のResources.reswを使ってほしいと思うが、xamlで、x:Uidにリソースをセットするだけではそのようにはなってくれない。

パッケージプロジェクトに含まれる「Package.appxmanifest」に、使う言語の設定を行ってやる。→参考

プロジェクト作成直後は

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

となっている部分を、

  <Resources>
    <Resource Language="ja-JP"/>
    <Resource Language="en-US"/>
  </Resources>

とする。使う言語の分だけ並べる。
これで、OSの言語設定に合わせて、使うリソースを変えてくれる。

C#コードでリソースを使う

ここがUWPのローカライズ資料のままやってもうまくいかなかった。

下記のようにすると、うまくいった。

// using Microsoft.Windows.ApplicationModel.Resources;

var loader = new ResourceLoader();
var str = loader.GetString("SimpleText");  // これで、上で定義した「単純な文字列リソース」が取れる

リソース名.プロパティ名で定義した奴の場合は、下記のようにする。

var loader = new ResourceLoader();
var str = loader.GetString("MyButton/Content");  // これで、上で定義した「ボタン名称」が取れる

※リソース名とプロパティ名の間の「.」を、「/」に置き換えること。

【注意】

UWPのローカライズ資料の通りに、下記のようにしてコードからリソース取得しようとすると、例外が起きる。

System.Runtime.InteropServices.COMException CoreWindow のないスレッドにリソース コンテキストは作成できません。 (0x80073B27)

添付プロパティに使うリソースを定義し使う

ここまで、コントロールが持っているプロパティに使うためのリソースの定義と使い方を書いたが、これが「添付プロパティ」に使うためのリソースだと少々やり方が異なる。(ここも、UWPのローカライズ資料のままやってもうまくいかなかった部分。)
※添付プロパティは、例えばAutomationProperties.Nameなどのこと。

リソース登録するときに、

リソース名.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name

という感じで、[using:名前空間名]を入れて登録する。

使うときは、他のリソースと同じように、

x:Uid="リソース名"

で使う。

【注意】

AutomationProperties.Name にリソースを使う場合に、UWPのローカライズ資料だと、

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

と書けとあるが、実際は

MyButton.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name

でないといけない。(名前空間名が違う)

これはUWPでの「AutomationProperties.Name」と、WinUI3でのそれの出どころの違いだと思われる。地味にはまったので要注意。
(WinRTとWindows App SDKの違いなのか?)

その他

reswの内容を変更後は、リビルドしないと変更が動作に反映されない気がする。
これも地味にはまるので注意。

参考

WinUI3でローカライズする
package.appxmanifest に書く<Resource Language="x-generate"/>の記載はココ。

https://docs.microsoft.com/ja-jp/windows/apps/winui/winui3/localize-winui3-app

UWPの文字列ローカライズのやり方 公式

UI とアプリ パッケージ マニフェスト内の文字列をローカライズする - UWP applications | Microsoft Docs

コードからリソースの文字列を取る方法のヒントになったissue

Question: How to get a string resource from code? · Issue #3589 · microsoft/microsoft-ui-xaml · GitHub

ResourceLoader のサンプル

WindowsAppSDK-Samples/MainWindow.xaml.cs at 1abe6431a40b38526fd8587f4166324b2e20c25a · microsoft/WindowsAppSDK-Samples · GitHub