WinUI3プロジェクトに持っている言語リソースと、appmanifestの中の言語リソース設定、OSの表示言語設定の関係の実験

もくじ
https://tera1707.com/entry/2022/02/06/144447

下のような問題に当たったので、いろいろ試したときのメモ。

ローカライズのやり方で、OSで同じ言語設定をしてても動きが違うことがある

WPFとWinUI3で、ローカライズを以前やった。

①WinUI3で、文字列リソースを登録してxamlC#コードから使う(ローカライズ
https://tera1707.com/entry/2022/03/24/224855

②Propertiesでxamlの画面を多言語対応(ローカライズ)する(WPF .NET6版)
https://tera1707.com/entry/2023/03/01/221055

仕事のアプリでは、上の3つのうちの、①をWinUI3アプリで、②をWPFアプリで使っていたのだが、 その①と②の動きが、特定のケースでちょっとことなることが分かった。

①はよくUWP/WInUI3のローカライズで使われるやり方で、
②はWPFアプリで昔から使われていたやりかたな様子。(※②はWPF限定というわけではなく、WinUI3でも使える)

で、その2つの動きが異なってしまう特定のケースというのが、 「アプリの中にリソースを用意していない言語を、OSの表示言語の設定で選択したとき」 の動き。

どういう動きか

reswの場合

.reswローカライズする場合、ここで調べたやり方でやるのだが、 このやりかたでローカライズした場合、OSの言語設定の優先順位が一番高い言語のリソースが使われる

上図の場合、

  • Windowsの表示言語の設定は「日本語」にしているけど、
  • アプリの中の言語リソースには日本語(ja-JP)がない。
    (Stringsフォルダの中に、ja-JPフォルダがないし、app.Manifestの中の言語リソース定義の中にもja-JPがない)

この場合、アプリが持っているリソースの中で、OSの言語設定の優先度が一番高いのはロシア語なので、ロシア語のリソースが使われる。

resxの場合

.resxローカライズする場合、ここで調べたやり方でやるのだが、 このやりかたでローカライズした場合、アプリのニュートラル言語として使うリソースが使われる。

アプリのニュートラル言語の設定はコレ。

ニュートラル言語として使うリソースはコレ。
(Resource.xx.resxの言語コード部分のない「Resource.resx」がソレ。)

以前調べた記事によると、

通常、ニュートラル言語を設定するのであれば、その言語に対応するリソースを「Resources.resx」に書かないといけない。

また、ニュートラル言語を「なし」にしていると、今現在のWindowsの表示言語設定に対応する「Resources.〇〇.resx」があればその中の情報を使い、対応するのがなければ「Resources.resx」が使われる様子。(以前の自分調べ)

なので、resxの場合の動きとしては、

アプリのニュートラル言語として使うリソースが使われる。

イコール「Resource.resxが使われる」。

となる。

という感じで動きが異なる。

reswのほうは、「優先度が一番高い言語のリソースが使われる」

resxのほうは、「アプリのニュートラル言語用のリソースが使われる」

やりたいこと

で、そういう違いがあるので、どちらかに合わせないといけないことになりそう。

なにかやりようがあるか、考えてみる。

使えそうなAPI

リソースをアプリの中に用意していない言語を、OSの表示言語の設定で選択したときの、reswでローカライズしたときのアプリのの動きを、resxのアプリ側の動きに合わせてみようと思う。

つまり、resw方式のローカライズで、
アプリの中にリソースを用意していない言語を、OSの表示言語の設定で選択したときに、
アプリのニュートラル言語用のリソースが使われるようにしたい。

言語関連の設定をとる、セットするAPIは、今回調べた限り、下記のようなものがあった。

// ①ユーザープロファイルの言語設定
var userProfileLangList = Windows.System.UserProfile.GlobalizationPreferences.Languages;

// ②アプリマニフェストの言語設定
var appManifestLangList = Windows.Globalization.ApplicationLanguages.ManifestLanguages;

// ③ OSの表示言語設定
string dispLangSetting = CultureInfo.CurrentUICulture.Name

// ④ ①と②を合わせたもの
var manager = new ResourceManager();
var context = manager.CreateResourceContext();
var runtimeLangList = context.QualifierValues["Language"];

// ⑤ UIが使う言語を指定(上書き)できる
ApplicationLanguages.PrimaryLanguageOverride = "de";

Windows.System.UserProfile.GlobalizationPreferences.Languagesは、OSの設定の「優先する言語」の設定。 つまり、OSにインストールされている言語のリスト。
「言語と地域」画面の「優先する言語」の下のリストにある通りに、優先度が高い順番にならんだ言語コードのリストになっている。

②は、アプリパッケージプロジェクトのPackage.appxmanifestの中の言語リソースの一覧にある言語のリストになっている。
(ここは、自分が書く部分。存在する言語リソースの分だけここを書かないといけないので、イコールアプリが持ってる言語の一覧とみなせそう)

③が、「表示言語」の設定。
OSがなんの言語で表示されるかの設定。

④の値は、①と②を合わせて、実際にアプリが使える言語が「;」区切りで入ってくる。

つまり、Windows.System.UserProfile.GlobalizationPreferences.LanguagesWindows.Globalization.ApplicationLanguages.ManifestLanguages の値のorを取ったもの。

orをとるときには、フォールバックも含むっぽい。

※フォールバックとは、例えばappManifestLangListにruがあれば、userProfileLangListにあるruもru-UAも両方いけるということ。つまり、下図のようになる。

こういう設定をしているときに、

マニフェストにこう書いてると、

こうなる

⑤のApplicationLanguages.PrimaryLanguageOverrideに、”ja-JP”などの言語コードの文字列を入れてやると、 そのアプリで使用する言語を自由に決めれる。

例えば、

ApplicationLanguages.PrimaryLanguageOverride = "de"

とすると、そのアプリでは、表示にドイツ語を使うことができる。

やれそうなこと

以上のAPIを使って、下記のような動きにできそう。

  • まず③で、表示言語設定をとってきて 、
  • ②に③が含まれてたら、③のリソースを使う、
  • ②に③が含まれてなかったら、固定で英語リソースを使う。(英語が「ニュートラル言語」な場合)

やってみたときの注意

Windows.Globalization.ApplicationLanguages.ManifestLanguagesで取れる値について

このAPIで、言語の優先順位設定にある言語のリストが取れるが、

どうも、「日本語」は、"ja-JP"ではなく、"ja" になる。
でも、「英語(米国)」は、"en-US"になる。

あまりいろいろ試せてないが、おそらく、

その言語に、地域コードが1つしかないと、言語コードだけになるっぽい。

上の例で行くと、日本語には「日本語」、つまり「日本語(日本)」しかないから、"ja"だけになり、
英語には、「英語(米国)」="en-US"をはじめ、複数種類、英語のコードがあるので、ちゃんと"en-US"のように"言語コード-地域コード"になるっぽい。

reswには、en-USのリソースが必須?

reswのやり方の場合、en-USのリソースを含んでいないと、下記のようなワーニングが出た。

1>GENERATEPROJECTPRIFILE : warning : PRI263: 0xdef01051 - No default or neutral resource given for 'Resources/String1/Text'. The application may throw an exception for certain user configurations when retrieving the resources.

エラー文言を見る限り、ニュートラル言語に該当するリソースがないよ、という意味に見えるので、アプリのニュートラル言語の設定を、リソースがある言語にしてみたりしたが、それでも同じワーニングがでた。

このメッセージでいうところの「ニュートラル言語」「デフォルトの言語」が何を指しているのかがわからなかった。(WinUI3などのパッケージアプリでは、固定で、en-USがニュートラル言語なのか?)

存在するリソースフォルダと、Package.aooxmanifest中のResourceの記載はそろえておかないと変な動きする

リソースフォルダとその中身(ruフォルダとその中のResources.reswのような)があるのに、 app.appxmanifestの中の言語リソースの中にその言語の名前を書き忘れている場合、 x:Uidで紐づけているリソースは、固定でen-USの言語を取りに行く動作をするっぽい。

実験でコードを書いてるときに、うっかりそういう状態になってしまうと、x:Uidで紐づけたところと、ResourceLoaderクラスでコード中でリソースとって表示するところが、別の言語で表示されちゃう、みたいなへんなことになって焦るので要注意。

resw方式で、現在のOS表示言語設定に該当するリソースがなく、優先度一覧の中にもない場合

その場合は、しっかり試せてはいないのだが、どうも、アプリが持っているリソースの中から、言語コードのアルファベット順で、一番最初にくるリソースが使われてるように見える。(なので,deとruならdeが使われてるっぽい)(要検証)

その他消すのもったいないメモ

ApplicationLanguages.PrimaryLanguageOverride の初期値は空文字。

ApplicationLanguages.PrimaryLanguageOverride に値をセットすると、(例えば"de"とか)アプリの表示言語を強制的に上書できるが、その上書きは、アプリをアンインストールするまで続く。(次回起動時も、値が保持されてる)

2023/08/05追記

上の「使えそうなAPI」の項で挙げたAPI以外にも、下記のサイトに使えそうなAPIが上がっていた。

https://atmarkit.itmedia.co.jp/ait/articles/1410/23/news092.html#:~:text=%E3%81%BE%E3%81%A8%E3%82%81,%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E8%A8%80%E8%AA%9E%E3%81%AF%E3%80%81CultureInfo.

ApplicationLanguages.Languagesが使えそう。

おそらく、「使えそうなAPI」で挙げたApplicationLanguages.ManifestLanguagesと同じクラスのプロパティっぽい。
必要そうなら調べる。

参考

Understand user profile languages and app manifest languages

https://learn.microsoft.com/en-us/windows/apps/design/globalizing/manage-language-and-region

Localize strings in your UI and app package manifest

https://learn.microsoft.com/en-us/windows/uwp/app-resources/localize-strings-ui-manifest

以下自分の記事