[WPF/xaml] Propertiesでxamlの画面を多言語対応(ローカライズ)する＆現在の言語と地域のコードを取得する

**もくじ**
→https://qiita.com/tera1707/items/4fda73d86eded283ec4f

# やりたいこと
WPF(画面はxaml)のアプリで、OSの言語設定を切り替えると、その設定に合った言語でUIの文言を表示できるようにしたい。( = 多言語対応したい)

# 前提
`.NET Framework 4.7.2`を使用。

# やり方
`Properties.Resources.resx`を使う。

元々下図のようになっているのを、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/e3da9db8-d491-fb72-f641-8429ea542d46.png)

表示したい言語の分のリソースを追加する形になる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/097cb00e-630a-a38d-2ecd-78a650fc791f.png)

この場合だと、下記のようになる。

|リソースファイル名|OS言語設定|
|:--|:--|
|Resources.fi.resx|フィンランド語|
|Resources.ja.resx|日本語|
|Resources.resx|英語|

**今回は、デフォルトの言語 = 英語、としてやってみる。**
※デフォルト言語のリソースが、ロケールがファイル名についてない`Resources.resx`になるということ。

### 手順
#### Propertiesの準備
.NET Frameworkでプロジェクトを作成する。
作成したら、プロジェクトの下の`Properties`を開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/ccf53260-7f1c-b14d-7a02-5bc7f1e1ce55.png)

`Resources.resx`をダブルクリックして開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/a4e23233-5140-1a1e-16a7-0f18a433cb9e.png)

言語別で表示分けしたい文言をここに設定する。(標準の言語は今回英語にするので、英語で入力。)
まず呼び出すときの名前を付けて、その値も入力する。
※一つ追加したら、その下に空欄が追加されるので、複数登録したかったら下に追加していけばいい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/a3dfb3e8-8ed9-94fb-c135-eaa7b823e50d.png)

アクセス修飾子の設定を`public`にする。
これをしておくと、xamlのコードからここの名前を使って呼び出せるようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/30ea857b-c242-5743-bab8-81e10b82a7d3.png)
※publicにしてないと、xaml編集中は問題ないが、実行時の`InitializeComponent()`で下記エラーになる。

```txt
System.Windows.Markup.XamlParseException: '''System.Windows.Markup.StaticExtension' の値の指定時に例外がスローされました。' 行番号 '12'、行位置 '10'。'
ArgumentException: 'WpfApp52.Properties.Resources.MyString1' StaticExtension 値は、列挙値、静的フィールド、または静的プロパティに解決できません。
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/8fca7d71-36af-d80e-685b-523e167799aa.png)

#### xaml画面の準備
xaml画面に、下記2点追加する。

- Propertiesへのネームスペースの定義を追加する。  
書き方は、**xmlns:properties="clr-namespace:`プロジェクト名`.Properties"**
- 言語別に表示したい文字列のためのTextBlockを追加する。  
書き方は、**{x:Static `追加したネームスペース`:Resources.`追加したリソースの名前`}**

``` .xaml
<Window x:Class="WpfApp52.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:properties="clr-namespace:WpfApp52.Properties" ★ネームスペース追加(「WpfApp52」はプロジェクト名)
        xmlns:local="clr-namespace:WpfApp52"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <!-- ★言語別に表示したい文字列のためのTextBlockを追加する -->
        <TextBlock Text="{x:Static properties:Resources.MyString1}" />
    </Grid>
</Window>
```

ここまでで、デフォルトの言語の表示はできた。
これだけだとデフォルトの英語だけになるので、ほかの言語のリソースを追加する。

#### 他の言語用のResources.resxの追加
まずは、日本語のリソースを追加する。

Propertiesを右クリック > [追加] > [新しい項目] を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/3ca47d5d-7aae-32f9-c25a-94f22c2629b5.png)

[リソースファイル]を追加する。名前は、`Resources.ja.resx`にしておく。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/918e0ae4-77e0-1e84-ab6f-4c8d6c5670ca.png)

デフォルト言語のリソースと同じように、下記を行う。

- リソースの名前とその値を追加
- `アクセス修飾子`をpublicにする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/07acdea5-e32c-c764-fb97-9b0156d79560.png)

これで、日本語用リソースの追加完了。
今WindowsのOS言語設定を日本語にしているので、画面エディタ上で文言が日本語リソースのものになった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/b23056df-8944-0ca0-f7fb-e98ae8d4807d.png)

手順は以上。


# Resource.〇〇.resxの〇〇の名付け方

下記を参考に付ける。
https://docs.microsoft.com/ja-jp/previous-versions/windows/scripting/cc392381(v=msdn.10)?redirectedfrom=MSDN

一覧
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/358af64b-670e-18e6-a08c-272ecaa06497.png)

WindowsPCの言語設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/ab4fdcf2-4c95-cc5a-ba8a-53733ce958c6.png)


例えば、言語設定の画面の設定が、

- `英語(米国)`だったら、`en`をつければいいので`Resource.en.resx`
- `スペイン語 (アルゼンチン)`だったら、`es-ar`をつければいいので`Resource.es-ar.resx`

とする。

ちなみに、`es-ar`のように、ハイフンで2つをつなげているものは、`〇〇-△△`の`〇〇`が**言語コード**、`△△`が**地域コード**らしい。
だから、`ja-jp`だと、**日本語-日本**、`es-ar`だったら、そのままだが、**スペイン語-アルゼンチン**、を表している。

# 現在の言語と地域のコードをC#でとってくる

下記のようなコードで、現在のロケール設定を取ってこれる。
→https://docs.microsoft.com/ja-jp/dotnet/api/system.globalization.cultureinfo.currentculture?view=netcore-3.1

```cs
// ja
string code1 = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
// ja-JP
string code2 = CultureInfo.CurrentUICulture.Name;

// 細かいこと調べられてないので、参考程度に。
```


# ニュートラル言語設定とresxの関係

プロジェクトのプロパティの中にある「**ニュートラル言語**」の設定を例えば日本語にしていると、「Resources.ja.resx」があったとしても「Resources.resx」の方が日本語として扱われる。

ニュートラル言語に設定している言語のリソースとして「Resources.resx」が使われる様子。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/fab168d8-b6d1-4c06-b56c-d110b43cacba.png)

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


また、ニュートラル言語を「なし」にしていると、今現在のWindowsの表示言語設定に対応する「Resources.〇〇.resx」があればその中の情報を使い、対応するのがなければ「Resources.resx」が使われる様子。（やってみた限りそう見えた）


# 注意と今後やること
この方法は、上記手順だけでは**アプリ起動後、動的に言語切り替えはできない**もののお手軽ではあるが、xaml上でエラーになり、画面エディタが使えなくなる場合がある。
今回の実験だとそういうことは起きなかったが、実際仕事のアプリではそういうエラーになっていた。そうなるとき、ならないときの違いはよくわからない。

wpfとしては、「リソースディクショナリ」を使って多言語対応することを推奨しているようで、そっちのやり方ならそういうことが起きないっぽい。(未検証)

下の「参考」に挙げた[「WPFアプリの文字列のローカライズ(多言語化)」
](https://nishy-software.com/ja/dev-sw/wpf-app-localize-strings/)のページにその方法が書かれていたので、次回試したい。

#### 追記
.NET CoreのWPFプロジェクトだと、そもそも`Properties`の項目がない。
(下記は.NET core 3.0)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/9ff55061-4787-32d3-c299-d543bd9e74df.png)
今後はPropertiesを使った言語切り替えはしてくれるな、ということか？
そういう意味でも正しい言語切り替えのやり方を調べたい。

#### 追記２
追加でいろいろ手順をやると、動的にも言語切り替えできるらしい。[こちら](http://grabacr.net/archives/1647)参照。

## 気づいたこと
#### OSの言語設定が、追加したリソースの中にない言語だったとき
デフォルトのリソール`Resources.resx`は、現在のOSの言語設定が、用意したリソース(今回だったら日本語)の中にないものだった場合に表示する、という動きをするみたい。
なので、上の状態でOSの言語設定を「フランス語」にすると、デフォルトのリソースが使われて英語表示になる。

#### 追加したリソースの実体の見方

仕組みとしては、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/0c5bbdcc-b93c-97bf-dcd4-5d6069e82606.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/3463d523-6a62-6054-062b-18e49904856e.png)

ビルドすると、上記のように、追加で作成したリソースのdll(ここでは`jp`フォルダの中の`WpfApp51.resources.dll`)が作成される。
※別プロジェクト作って実験したのでPJ名が上の方と変わっている。
※試しにフィンランド語の`Resources.fi.resx`も追加したので`fi`フォルダも追加されている。

OSの言語設定を切り替えたときには、各言語のフォルダの中の`<PJ名>.resources.dll`がリソースとして使われて、言語の切り替えが行われる。

標準の言語のリソースはどこに行ったかというと、exe(今回だと`WpfApp51.exe`)の中に含まれているらしい。(だから、「標準」として使われて、追加のリソースがない時はそれが使われる)

# 参考
**ロケールの動的変更の仕方**
http://grabacr.net/archives/1647

**WPFアプリの文字列のローカライズ(多言語化)**
ローカライズのやり方の種類をいくつか挙げているページ。
→今回のやり方は、このページの「4.Resources.resxを使う方法」にあたる。
https://nishy-software.com/ja/dev-sw/wpf-app-localize-strings/

---

CultureInfoや CultureInfo.CurrentCultureのMS docs説明
https://docs.microsoft.com/ja-jp/dotnet/api/system.globalization.cultureinfo.currentculture?view=netcore-3.1

MSのロケールIDの一覧
https://docs.microsoft.com/ja-jp/previous-versions/windows/scripting/cc392381(v=msdn.10)?redirectedfrom=MSDN


jaとja-jpの違い
http://hensa40.cutegirl.jp/archives/3190

ja=ニュートラルカルチャ、ja-jp=特定のカルチャ、優先順位は？
https://www.atmarkit.co.jp/fdotnet/dotnettips/314winmultilang/winmultilang.html