WPF　多言語化対応

# 多言語対応とは

WPFアプリケーションのGUIやメッセージボックスで出力する文字列を国際化（多言語化）することである。 (※)
今回はアプリケーション内で動的に変更する方法を２種類紹介する。

:::note warn
本来の国際化は、数値や日時の表現方法、UIレイアウトの自動調整まで含まれるが本説明では文字列の多言語化のみ行う。
:::

# 多言語化対応① (resx使用編)
#### 多言語リソース設定
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516889/726111eb-812d-64b9-d7e3-1d80e760fc72.png" width="50%">

| 項目 | 説明 |
|:-|:-|
| Properties | [追加]→[新しい項目]→[リソースファイル]を選択することで新規作成可能 |
| Resources.resx | Defaultで定義してあるリソースファイル |
| ResourcesTest.resx | 今回は規定値以外のリソースファイルで多言語化対応が可能かも実験 |

---

#### 多言語リソース設定（補足）
.resxの前にある拡張子はロケールID（LCID : LoCale Identifiers）であり各カルチャを示している。
**言語コード (ISO-639, RFC 4656)**に則っている。

例）
　ja-JPの場合、jaがjapanese(日本語)，JPがJAPAN(日本[場所])となる
　2つに分ける理由は、言語だけでは場所が特定できないものや、
　場所によって複数の公用語を持つ場合を想定している

　<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516889/d407143b-08df-eba4-b18e-7b53fc675af2.png" width="25%">
　今回は`ResourcesTest.resx`無印の規定カルチャを英語圏(English)として使用している。

---
#### 多言語リソース設定（ファイル内容）
[名前] にリソース名、[値] にリソース値 (実際に画面に表示する文字列) を入力する。
他のリソースでは、先に作った Resources.resx と同じキー([名前])を用意し、
リソース値に対象の文字列を設定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516889/02a00537-e672-894d-4030-8fb4fe1a1a8d.png)

---
#### 多言語リソース設定（出力ファイル）
`Multilingual.resources.dll`はサテライトアセンブリと呼ばれ、カルチャに固有のリソースのみを含んだアセンブリである。
リソースはカルチャ毎のフォルダ内にあるサテライトアセンブリをロードして画面に出力する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516889/f444ffef-0055-8557-6cc8-5a6e3ced71b2.png)

---
#### 多言語リソースの動的切り替え
■ ResourceServiceクラスを用意して、切り替えプロパティを定義

``` C#:ResourceService.cs
    public class ResourceService : INotifyPropertyChanged
    {
        // シングルトン対策
        public static ResourceService Current { get; } = new ResourceService();

        // INotifyPropertyChanged対策
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// リソースを取得
        /// </summary>
        public ResourcesTest ResourcesTest => new ResourcesTest();

        /// <summary>
        /// リソースのカルチャーを変更
        /// </summary>
        /// <param name="name">カルチャー名</param>
        public void ChangeCulture(string name)
        {
            ResourcesTest.Culture = CultureInfo.GetCultureInfo(name);
            RaisePropertyChanged("ResourcesTest");

            var dictionary = new ResourceDictionary();
            try
            {
                var language = (name == "ja-JP") ? "Japanese" :
                               (name == "zh-CN") ? "Chinese" :
                              /*else...*/          "English";
                dictionary.Source = new Uri("/Multilingual;component/Resources/" + language + ".xaml", UriKind.Relative);
            }
            catch (Exception)
            {
                return;
            }
        }
```

---
■ xamlでのリソース表現方法

``` xml:MainWindow.xaml
        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="140"/>
                <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0"
                       Text="{Binding Source={x:Static local:ResourceService.Current}, Path=ResourcesTest.UI_0000, Mode=OneWay}"
                       FontSize="16" Foreground="White"
                       HorizontalAlignment="Left" VerticalAlignment="Center"
                       Margin="5,0,0,0"/>
            <ComboBox Grid.Column="1"
                      SelectedIndex="{Binding LanguageIndex.Value}"
                      HorizontalAlignment="Stretch" VerticalAlignment="Center"
                      Height="24" Margin="5">
                <ComboBoxItem Content="English"/>
                <ComboBoxItem Content="日本語 (ja-JP)"/>
                <ComboBoxItem Content="中文 (zh-CN)"/>
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <i:InvokeCommandAction Command="{Binding ChangeLanguageCommand}"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </ComboBox>
        </Grid>
```

---
■ 言語変更イベント定義
csファイル内でも`ResourcesTest.MSG_xxxx`の様な形式でリソース文字を取得可能


``` C#:MainWindowViewModel.cs
    class MainWindowViewModel : BindableBase, IDisposable
    {
        /// <summary>
        /// まとめてDisposeするためのオブジェクト
        /// </summary>
        protected CompositeDisposable Disposable { get; } = new CompositeDisposable();
        public void Dispose()
        {
            Disposable.Dispose();
        }

        private string[] cultures = { "en-US", "ja-JP", "zh-CN" };

        public ReactivePropertySlim<int> LanguageIndex { get; set; } = new ReactivePropertySlim<int>(1);

        public ReactiveCommand ChangeLanguageCommand { get; set; } = new ReactiveCommand();
        public ReactiveCommand PushResxCommand { get; set; } = new ReactiveCommand();
        public ReactiveCommand PushXamlCommand { get; set; } = new ReactiveCommand();

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public MainWindowViewModel()
        {
            // CommandBinding
            ChangeLanguageCommand.Subscribe(() =>
            {
                if (LanguageIndex.Value < 0) return;
                ResourceService.Current.ChangeCulture(cultures[LanguageIndex.Value]);
            });
            PushResxCommand.Subscribe(() =>
            {
                MessageBox.Show(ResourcesTest.MSG_0001, ResourcesTest.MSG_0000);
            });
            PushXamlCommand.Subscribe(() =>
            {
                MessageBox.Show(ResourceService.GetResource("Message"), ResourceService.GetResource("Title"));
            });
        }
    }
```

# 多言語化対応② (xaml使用編)
#### 多言語リソース設定
xamlでテキストを管理し、GUI(xaml)では`StaticResource / DynamicResource`を使って表現する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516889/d8523ff5-7ae3-c52a-495f-bc6a8f20c0d7.png" width="50%">

---

#### 多言語リソース設定（ファイル内容）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516889/f549a615-3a5e-7647-550a-0bc8efbffdd9.png)


---
#### 多言語リソースの動的切り替え

ResourceServiceクラス内(resx編と共有したかったため)で、変更する。

``` C#:ResourceService.cs
    public class ResourceService : INotifyPropertyChanged
    {
        /// <summary>
        /// リソースのカルチャーを変更
        /// </summary>
        /// <param name="name">カルチャー名</param>
        public void ChangeCulture(string name)
        {
            //
            // resx編の処理は省略
            //

            Application.Current.Resources.MergedDictionaries[0] = dictionary;
        }

        public static string GetResource(string key)
        {
            return Application.Current.Resources.MergedDictionaries[0][key] as string;
        }
```

xamlでのリソース表現方法
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516889/fff3e67c-ea2c-523d-cdf5-d449e5813077.png)


# 参考サイト
[WPF アプリの国際化 (多言語対応) と、実行中の動的な言語切り替え](http://grabacr.net/archives/1647)
[WPFアプリケーションを多言語対応する方法のまとめ](https://www.k-karakuri.com/entry/2017/09/18/WPF%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E5%A4%9A%E8%A8%80%E8%AA%9E%E5%AF%BE%E5%BF%9C%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%81%AE%E3%81%BE#3-%E7%8B%AC%E8%87%AA%E3%81%AE%E8%A8%80%E8%AA%9E%E7%AE%A1%E7%90%86%E6%A9%9F%E8%83%BD%E3%82%92%E3%81%A4%E3%81%8F%E3%82%8B)