Gu.Wpf.Localizationを使ってWPFアプリの動的言語切り替えを実装する。

# 前提
この記事は、WPF アプリケーションの基本的な国際化対応の知識を有していることを前提としています。国際化対応の中でも文字列リソースの扱いに着目した内容となっています。
文字列リソースの切り替えだけに着目してもその実現方法には様々なバリエーションがあります。バリエーションは、以下の記事がとても参考になります。

- [WPFアプリの文字列のローカライズ(多言語化)](https://nishy-software.com/ja/dev-sw/wpf-app-localize-strings/)

以上で紹介されている方法のうち、本記事は x:Static を使った方法に関連した内容となっています。

# Gu.Wpf.Localization の紹介
x:Static を使った文字列リソースの参照方法は、動的な言語切り替えに対応しません。言語切り替えが発生したタイミングで全ての要素を自分で再表示する必要があります。
メインコンテンツの領域を再表示することは比較的簡単かもしれません。しかし、ウィンドウのタイトルやメニューなどの再表示を考えると、アプリケーションを再起動してもらうのが現実的な解決策になります。でも、もしも簡単に対応できる方法があれば再起動は避けたいですよね？
そこで動的言語切り替えをサポートする [Gu.Wpf.Localization](https://github.com/GuOrg/Gu.Localization) というライブラリを紹介します。このライブラリの特徴は x:Static の書き方とほとんど一緒の書き方で動的言語切り替えに対応できるという点にあります。
以下に書き方の比較を紹介します。

- x:Static を使った文字列リソースの埋め込み方法（この方法は動的言語切り替えができない）

```xaml:MainWindow.xaml
<Window x:Class="AppNamespace.MainWindow"
        ... 省略 ...
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:p="clr-namespace:AppNamespace.Properties">
    <TextBlock Text="{x:Static p:Resources.ResourceKeyName}" />
</Window >
```

- [Gu.Wpf.Localization](https://github.com/GuOrg/Gu.Localization) を使った動的言語切り替え可能な文字列リソースの埋め込み方

```xaml:MainWindow.xaml
<Window x:Class="AppNamespace.MainWindow"
        ... 省略 ...
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:l="clr-namespace:Gu.Wpf.Localization;assembly=Gu.Wpf.Localization"
        xmlns:p="clr-namespace:AppNamespace.Properties">
    <TextBlock Text="{l:Static p:Resources.ResourceKeyName}" />
</Window >
```

書き方の違いは、以下の通りです。

- [Gu.Wpf.Localization](https://github.com/GuOrg/Gu.Localization) の名前空間を定義すること。公式のサンプルに準じて名前空間を l としている。
- x:Static の代わりに l:Static を使うこと。

ほとんど同じ書き方ができるので既に x:Static で文字列リソースの表示を行っているのであれば、動的言語切り替え機能を簡単に実装できます（もちろん、これから実装する人も使えます！）。

# Gu.Wpf.Localization の導入
NuGet でパッケージの参照を追加してください。

ライセンスは MIT です。

# 言語の切り替え方
言語の切り替えは Gu.Localization.Translator.Culture で行います。また、選択可能な言語の候補は Gu.Localization.Translator.Cultures で取得できます。
切り替え用 ComboBox の実装例を以下に示します（本家サイトのほぼ引用です）。

```XAML:MainWindow.xaml
<UserControl ...
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:l="clr-namespace:Gu.Wpf.Localization;assembly=Gu.Wpf.Localization"
             xmlns:localization="clr-namespace:Gu.Localization;assembly=Gu.Localization">
    ...
    <!-- Dropbownbox to select a language -->
    <ComboBox x:Name="LanguageComboBox"
              ItemsSource="{Binding Path=(localization:Translator.Cultures)}"
              SelectedItem="{Binding Path=(localization:Translator.Culture)}"
                                              Converter={x:Static l:CultureOrDefaultConverter.Default}}" />
</UserControl>
```

初期状態で選択可能な候補（Gu.Localization.Translator.Cultures）は以下の通りです（ソース見て判断しました。Gu.Localization.Translator.GetAllCultures を参照してください）。

- アプリの実行ディレクトリ配下に「言語ディレクトリ/*.resources.dll」が存在する場合、言語ディレクトリの名称で表される言語
- NeutralResourcesLanguage 属性により定義された言語
- アプリの埋め込みリソースに「言語ディレクトリ/*.resources.dll」が存在する場合、言語ディレクトリで表される言語

候補にない言語を Gu.Localization.Translator.Culture に設定すると例外が発生するので注意してください。

あくまで初期設定なので Add / Clear メソッドを使って調整できます。

# C# のコードから文字列リソースの取得や言語の切り替えを実行する
XAML からだけではなく C# のコードからの操作も可能です。

```C#:MainWindowViewModel.cs
using Gu.Localization;

// ...
    public string GetString(string resourceKeyName) {
        return Translator<Properties.Resources>.Translate(resourceKeyName);
    }

    public void ChangeCulture(CultureInfo cultureInfo)
    {
        Translator.Culture = culture;
    }
// ...
```

C# のコードで取得した文字列リソースを画面に表示する場合、動的な表示切替に対応しない点に注意してください。極力避けるのが賢明ですが、どうしても必要な部分は設定変更時に個別に再表示する仕組みを検討する必要があります。

# 補足
## x:Static の利点と l:Static の使い方
数ある文字列リソースの言語切り替え方法の中で、なぜ x:Static を採用するのか…（個人的に感じている）利点についてお話します。

x:Static の記載方法には、Visual Studio の XAML エディタで編集する際にインテリセンスが効くという特徴があるのです。直接 XAML を編集する人にとって使いやすいという利点です。ビジュアルエディタを使う方にとっては関係がないですけども！

しかし、l:Static にするとなんとインテリセンスが効かないではないですか！x:Static は特別扱いされているんでしょうね（正確には http://schemas.microsoft.com/winfx/2006/xaml 名前空間が特別扱いされているっぽい？）。

x:Static の利点が消えてしまう！？でも、落ち着いて、編集するときに一瞬だけ x:Static に直してインテリセンスを働かせれば良いのではないでしょうか？

## l:Static は静的リソースではなく Binding
x:Static で文字列リソースを参照した場合、変換後の値は静的リソースである string です。一方で l:Static は Binding であることを覚えておく必要があります。言語の切り替わりに反応して、表示を動的に切り替えるわけですから当然です。
l:Static は表記こそ Static となっていますが、その実体は Translator.Culture の変更をトリガーとした Binding です（厳密にはちょっと違うっぽいが巡り巡って概ねそういうこと）。
この違いは以下のような場合に表れます。

```XAML:MainWindow.xaml
<TextBlock Text="{Binding Price, StringFormat={x:Static p:Resources.PriceFormat}}" />
```

以上の例は l:Static にすると動作しません。理由は Binding の StringFormat は Binding を受け付けないためです。
このようなケースに遭遇した場合は、何らかの対処をする必要があります。例えば以下のような方法が考えられます。

- Binding が使えるフォーマット専用のカスタムタグを定義する。
  - 参考）[文字列をフォーマットして表示するはなし with 多言語対応とかマークアップ拡張とか](https://qiita.com/wonderful_panda/items/a45ffaaca7f9c6e0d494)

## l:Static が扱うのは文字列リソースのみ
l:Static は画像等が扱えません。
残念ながら画像リソースの国際化はやっていないので対応方法の検討していません！