もくじ
https://tera1707.com/entry/2022/02/06/144447
下のような問題に当たったので、いろいろ試したときのメモ。
ローカライズのやり方で、OSで同じ言語設定をしてても動きが違うことがある
①WinUI3で、文字列リソースを登録してxaml、C#コードから使う(ローカライズ)
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.Languages
とWindows.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が上がっていた。
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
以下自分の記事