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)

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

こちらのやり方でも、コードからリソースを取れそう。 →参考:https://youtu.be/prOj1j1OILU?t=688

ResourceManager resourceManager = new ResourceManager();
ResourceMap resourceMap = resourceManager.MainResourceMap.GetSubtree("Resources");

Debug.WriteLine(resourceMap.ResourceCount);

var txt1 = resourceMap.GetValue("SimpleText");
var txt2 = resourceMap.GetValue("MyTextBlock/Text");

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

ここまで、コントロールが持っているプロパティに使うためのリソースの定義と使い方を書いたが、これが「添付プロパティ」に使うためのリソースだと少々やり方が異なる。(ここも、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の違いなのか?)

コードから、言語を切り替える

下記のようにすると、コードから言語を切り替えできる。

但し、アプリの再起動が必要。

// 英語に切り替え
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "en-US";
// 日本語に切り替え
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "js-JP";

その他

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 Learn

コードからリソースの文字列を取る方法のヒントになった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

WPFローカライズの記事なのだが、x:Uidを使ったローカライズのやり方の説明もされていて、それがわかりやすかったのでメモ。
※その他、WPFローカライズの方法の説明もすごくわかりやすい。

https://nishy-software.com/ja/dev-sw/wpf-app-localize-strings/

参考書

WinUI3

WinUI3でアプリを作ろうと思ったときのとっかかりによかった。 msdocsに書いてある情報を、体系的に、順番に読みたいな、というときによいかも。(ただし英語)
この本で分からなかった、かゆいところに手が届かなかった部分を私は記事にしてる感じ。

C#①

表紙に書いてある通り、教科書として最適。 これからC#を勉強したいけど、ネットだけで勉強するのは効率が悪いから体系的に学べる本が欲しいときや、 ちょっとC#を勉強してコード書けるようになったけど、もう少し広く深く知りたいなというときによいと思う。
私は仕事で触れるコードを軸に、基本ネットで断片的にC#を学んだので、その知識の隙間を埋めて枝葉を広げるためにとても分かりやすかった。

C#②

C#の文法的に色々できるのは分かったが、いざ実装するときに、わかったことを使ってどう実装すればいいのか?と悩んだときに指針になりそうな本。
「プロパティ等の名前の付け方、どうすればいい?」「情報をクラス外部に見せるときに、プロパティにすべき?メソッドにすべき?」「異常だと判定したいとき、どんなときにどんな例外をスローすべき?」などなど、勉強になる部分が山ほどあった。
私のように「コードは書くけどこれであってるのか自信がない、レビューで指摘されるのが嫌だ、実装時の(心の)よりどころが欲しい」という人に最適。