多言語対応をしよう
今やグローバル時代!世界を相手にしなければ勝てない!
そう、東京弁だけでなく大阪弁、名古屋弁、博多弁など…ごめんなさい調子に乗りました、単純に「日本語」「英語」「ロシア語」対応について語ります。
真面目な話、ソフトウェアで最低限英語対応すると、ターゲットが英語圏の人にまで届く可能性が高まります。
もちろん、ドキュメントも英語化が必要ですけど、今ならDeepLとか使えば便利にそれっぽく翻訳してくれますからね。 さらにChatGPTで添削とかさせれば、ニュアンス踏まえて指摘してくれますよ!
今回は、WPFにおいてViewとViewModelだけを使った、極めてシンプルな多言語対応サンプルを示したいと思います。
プロジェクト作成
今回は「WPFアプリケーション」で MultiLanguageTest
というプロジェクトを作りましょう。フレームワークは「.NET 9.0」を使用します。
そして開かれた MainWindow.xaml
を以下のようにしましょう。
「Language」の中に「Japanese」「English」「Russian」として、ウィンドウ内全域にボタンを貼り付けているだけです。
<Window x:Class="MultiLanguageTest.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:local="clr-namespace:MultiLanguageTest"
mc:Ignorable="d"
Title="MultiLanguageTest" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Menu>
<MenuItem Header="Language">
<MenuItem Header="Japanese" Command="{Binding ToJapanese}"/>
<MenuItem Header="English" Command="{Binding ToEnglish}"/>
<MenuItem Header="Russian" Command="{Binding ToRussian}"/>
</MenuItem>
</Menu>
<Button Grid.Row="1" Margin="5" Content="{Binding Greetings}" Command="{Binding ExecuteGreetings}"/>
</Grid>
</Window>
シンプルなテストなので、UIに関しては最低限の実装になっています。画面とかは本題じゃないからいいよね?
下準備に CommunityToolkit.Mvvm
の導入
利便性のため、NuGetで CommunityToolkit.Mvvm
を導入します。
メニューの「ツール」-「NuGetパッケージマネージャ」-「ソリューションのNuGetパッケージの管理」から、
「参照」タブをクリックしてCommunityToolkit.Mvvm
を検索してプロジェクトにインストールします。
ライセンスが不安かも知れませんが CommunityToolkit.Mvvm
はMITライセンスという、オープンソースライセンスの中で最も緩いライセンスです。
CommunityToolkit.Mvvm
自体を配布しないのであれば、商用とかクローズドソースもOKなので心配は要らないでしょう。
ほら、あの Xamarin で使われていた(もう過去形なんだよな…) Mono
と同じライセンスです。
っていうか、これは CommunityToolkit.Mvvm
覚え書きに書くべき内容だったか…?
自分の目で確認したい方は.NET Community Toolkitをどうぞ。
また、CommunityToolkit.Mvvm
に自信がない方は、拙作カップ麺シリーズ:WPF における CommunityToolkit.Mvvm(MVVM ToolKit) 覚え書き(1) 簡単な依存性注入とデータバインディングっs
ViewModelの作成とバインディング
ソリューションエクスプローラーの「ソリューション MultiLanguageTest
」内にある MultiLanguageTest
を右クリックします。
そして「追加」-「クラス」で「名前」に MainWindowViewModel
と入れて「追加」を押します。
MainWindowViewModel.cs
が開かれるはずなので、ひとまず内容をこうして、ボタンに「あいさつ」と表示します。
using CommunityToolkit.Mvvm.ComponentModel;
namespace MultiLanguageTest
{
public class MainWindowViewModel : ObservableObject
{
/// <summary>
/// 挨拶をするボタンのテキスト
/// </summary>
public string Greetings
{
get => "あいさつ";
}
}
}
そして、忘れちゃいけない VIewModel と View の結びつけです。MainWindow.xaml.cs
を開き、以下のように変更します。
using System.Windows;
namespace MultiLanguageTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
}
これで、画面中央のボタンに「あいさつ」と表示されました。
ここからが本番です!
各言語のリソースファイル作成
プロジェクト直下にリソースファイルを置くことは、基本的にプロジェクト規模からして適切ではないので、その前提で書きます。
なので MultiLanguageTest
プロジェクトを右クリックして「追加」-「新しいフォルダー」として、名前を Resources
にします。
これでフォルダが作られたので、この内部に3つのリソースファイルを作成します。
Resources
フォルダを右クリックして「追加」-「新しい項目」-「リソース ファイル」を選び、まずは Resource.resx
として追加します。
同様に Resource.ja.resx
と Resource.ru.resx
を作成します。
Resource.resx
が英語(標準)、Resource.ja.resx
が日本語、Resource.ru.resx
がロシア語のリソースファイルですね。
Resource.
の後にカルチャーコードを付けるのが命名規則となっています。カルチャーコードというのは ja
とか ru
などです。
早速 Resource.resx
を開いてみましょう、リソースエクスプローラーが開かれます。
ここで、リソースエクスプローラーのリストビュー上部にある、右から二つ目のアイコン
「Columns」を押してみましょう。
メニュー内に「ja」「ru」があるかと思います。両方にチェックを付けると、リソースエクスプローラーに内容が表示され、編集できるようになります。
では、リソース「Greetings」を作ってみましょう。
「リソースの作成」を押して、開かれたウィンドウの「名前」に Greetings
と入力します。
ニュートラル値は一般的には英語 なので、ニュートラル値には英語で Greetings
と入れましょう。
すると、リソースエディタの Greetings
の ja
と ru
の所が赤い枠で囲まれます。
ja
には あいさつ
、ru
には Приветствие
と入力します。
多言語対応の準備
いよいよ、お待ちかねの多言語対応の準備です。
MultiLanguageTest
プロジェクトを右クリックして「追加」-「クラス」として、名前を ResourceService.cs
として作成します。内容は以下の通り。
using System.Globalization;
using System.Resources;
namespace MultiLanguageTest
{
public static class ResourceService
{
private static ResourceManager resourceManager = new("MultiLanguageTest.Resources.Resource", typeof(ResourceService).Assembly);
/// <summary>
/// 言語を変更します。
/// </summary>
/// <param name="cultureCode">変更するカルチャーコード</param>
public static void ChangeCulture(string cultureCode)
{
var culture = new CultureInfo(cultureCode);
CultureInfo.CurrentUICulture = culture;
}
/// <summary>
/// リソースファイルから名前をキーに、カルチャーコードに基づいた文字列を取得します。
/// </summary>
/// <param name="key">文字列を取得するリソースファイルキー</param>
/// <returns>取得したい文字列</returns>
public static string GetString(string key)
{
return resourceManager.GetString(key, CultureInfo.CurrentUICulture) ?? string.Empty;
}
}
}
コメントが日本語なのは、とりあえず理解を促すためですから、多言語対応の際には各自書き換えてくださいね…
ここで "MultiLanguageTest.Resources.Resource"
という引数を使っていますが、
これは MultiLanguageTest
プロジェクトにある Resources
フォルダ内の Resource
にアクセスする、という意味になっています。
プロジェクト名が変わる場合は MultiLanguageTest
を書き換え、フォルダ名が違う場合は Resources
を書き換え、
リソースファイル名が Resource.resx
や Resource.ja.resx
などではない場合 Resource
を書き換えてください。
多言語対応の開始
まずは、最初の MainWindow.xaml
で書いたけど、まだバインディングしていない諸々にバインディングします。
MainWindowViewModel.cs
を以下のように書き換えてください。
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace MultiLanguageTest
{
public class MainWindowViewModel : ObservableObject
{
/// <summary>
/// 挨拶をするボタンのテキスト
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression")]
public string Greetings
{
get => ResourceService.GetString("Greetings");
}
/// <summary>
/// 日本語にする
/// </summary>
public RelayCommand ToJapanese { get; set; }
/// <summary>
/// 英語にする
/// </summary>
public RelayCommand ToEnglish { get; set; }
/// <summary>
/// ロシア語にする
/// </summary>
public RelayCommand ToRussian { get; set; }
public MainWindowViewModel()
{
ToEnglish = new RelayCommand(() => ChangeGreetingsCulture("en"));
ToJapanese = new RelayCommand(() => ChangeGreetingsCulture("ja"));
ToRussian = new RelayCommand(() => ChangeGreetingsCulture("ru"));
ResourceService.ChangeCulture("en");
}
/// <summary>
/// 挨拶のためのカルチャーコードを変更します。
/// </summary>
/// <param name="cultureCode">カルチャーコード</param>
private void ChangeGreetingsCulture(string cultureCode)
{
ResourceService.ChangeCulture(cultureCode);
OnPropertyChanged(nameof(Greetings));
}
}
}
ここで [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static")]
を入れないと「static
にできますよ」って言ってくるんです。
ですが、そのメッセージ通りに static
にしてしまうと悲惨なことに、言語切り替えがされなくなります。なので CA1822
を抑えるために必須です。
加えて [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression")]
を入れないと「不要な抑制を削除します」って言ってくるんです。
なので、不要な抑制じゃないという事をこうして指示します。
これで Language
から言語選択すれば「Greetings」が各言語に書き換わります
最後にプレゼント、完成品をGitHubに上げておきます。(※記事公開当初、リポジトリがPrivateになっていました、伏してお詫び申し上げます)
あなたへの宿題
今回はあなたに宿題を出します。実際に手を動かしてみると、意外と理解が深まりますよ。私も普段から手を動かしてます。
まだ、ExecuteGreetings
コマンドが実装されていません。
英語では Hello
、日本語では こんにちは
、ロシア語では Здравствуйте
と、メッセージボックスを表示するコマンドを実装してくださいね。
この記事や CommunityToolkit.Mvvm 覚え書きを読んでいれば、そんなに難しくないと思うけど…どうしてもお手上げって人は、Mastodonかメールで質問してください。