オンザフライでのWPFアプリケーションのローカライズ

WPFアプリケーションをローカライズするには多くの方法がありますが、フォームを閉じて再度開いたり、アプリケーションを完全に再起動したりすることなく、自動モードで要素のラベルを変更できる方法を見つけることは困難です。 この出版物では、WPFアプリケーションをローカライズする方法について説明します。これにより、アプリケーションとフォームを再起動せずにアプリケーションのカルチャを変更できます。 このソリューションでは、ResourceDictionary(XAML)を使用してインターフェイス(UI)を変換し、コードからのメッセージをローカライズするために、リソースファイル(RESX)を使用できます。リソースファイル(RESX)は、コードで使用したり、便利なエディター( ResX Resource Manager )でプラグインがある編集に便利です。



このプロジェクトは、Visaul Basic .NETとC#で記述されています。 これにより、Visaul Basic .NETまたはC#に慣れていない人でもコードが読みやすくなることを願っています。



最初に、新しいWPFアプリケーションプロジェクトを作成します。



画像



プロジェクト全体に対して中立的な文化を示すことを忘れないでください
  1. プロジェクトのプロパティを開きます。
  2. [ アプリケーション ]タブに移動します。
  3. アセンブリ情報を開きます。
  4. 中立的な文化の選択

    画像
  5. OKをクリックします。


次に、ローカライズファイルのResourcesフォルダーをプロジェクトに追加します。



リソースフォルダーで、 リソースディクショナリ(WPF)ファイルを作成し、 lang.xamlを呼び出し、作成済みのResourceDictionary要素に属性を追加します。これにより、タイプを示す値を使用して値を記述できます。



xmlns:v="clr-namespace:System;assembly=mscorlib"
      
      





次に、ファイルをアプリケーションリソースに追加します。

  1. ファイルApplication.xaml (C#のApp.xaml)を開きます。
  2. Application.Resourcesに ResourceDictionary要素追加します
  3. ResourceDictionary要素に、 ResourceDictionary.MergedDictionaries要素を追加します (ここでは、すべてのResourceDictionaryを保存します)。
  4. ResourceDictionary.MergedDictionaries要素で、 lang.xamlファイルを参照するSource属性を持つResourceDictionary要素を追加します。


結果の例
 <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Resources/lang.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources>
      
      





次に、 lang.xamlファイルのResourceDictionary要素内にUIのローカライズされたデータを追加する必要があります。



 <v:String x:Key="m_Title">WPF Localization example</v:String>
      
      





この場合、 m_Titleキーでアクセスできるテキスト値(String)を配置しました。



アプリケーションのサンプルデータ
 <v:String x:Key="m_Title">WPF Localization example</v:String> <v:String x:Key="m_lblHelloWorld">Hello world!</v:String> <v:String x:Key="m_menu_Language">Language</v:String> <v:Double x:Key="m_Number">20.15</v:Double>
      
      





他のアプリケーションカルチャの場合Resourcesフォルダーでlang.xamlファイルを複製し、名前をlangに変更します ru-RU .xaml 、ここでru-RU文化の名前Culture name )です。 複製後、値を翻訳できます。 すべての値をlang.xamlリソースファイルに追加した後、これを行うことをお勧めします



リソースファイルをロシア文化に翻訳(ru-RU)
 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:v="clr-namespace:System;assembly=mscorlib"> <!-- Main window --> <v:String x:Key="m_Title"> WPF </v:String> <v:String x:Key="m_lblHelloWorld"> !</v:String> <v:String x:Key="m_menu_Language"></v:String> <v:Double x:Key="m_Number">10.5</v:Double> </ResourceDictionary>
      
      





次に、ウィンドウのxamlコードに要素を追加し、動的リソースを使用してそれらのテキストを取得します。







上の図からわかるように、Visual Studioには先ほど作成したリソースが表示されます。



Slider要素に関する注意: ValueプロパティはDouble型であるため、同じ型のリソースのみを使用できます。



最初の打ち上げ
ウィンドウタイトル、アプリケーションカルチャを変更するためのメニュー名、Labelのテキスト、およびリソースのSlider要素の値を取り出しました。







それでは、コードの記述を始めましょう。



まず、 Applicationクラス( App for C#)で、アプリケーションがサポートするカルチャを示します。
Visual Basic .NET
 Class Application Private Shared m_Languages As New List(Of CultureInfo) Public Shared ReadOnly Property Languages As List(Of CultureInfo) Get Return m_Languages End Get End Property Public Sub New() m_Languages.Clear() m_Languages.Add(New CultureInfo("en-US")) '     m_Languages.Add(New CultureInfo("ru-RU")) End Sub End Class
      
      



C#
 public partial class App : Application { private static List<CultureInfo> m_Languages = new List<CultureInfo>(); public static List<CultureInfo> Languages { get { return m_Languages; } } public App() { m_Languages.Clear(); m_Languages.Add(new CultureInfo("en-US")); //     m_Languages.Add(new CultureInfo("ru-RU")); } }
      
      





アプリケーションレベルでは、重複するコードなしで任意のウィンドウからカルチャを切り替えることができる機能を実装しています。

静的なLanguageプロパティをApplicationクラス( App for C#)に追加します。これにより、現在のカルチャが返されます。カルチャを変更すると、以前のカルチャのリソースディクショナリが新しいものに置き換えられ、カルチャを変更するときにすべてのウィンドウが追加のアクションを実行できるイベントがトリガーされます。



Visual Basic .NET
 '      Public Shared Event LanguageChanged(sender As Object, e As EventArgs) Public Shared Property Language As CultureInfo Get Return System.Threading.Thread.CurrentThread.CurrentUICulture End Get Set(value As CultureInfo) If value Is Nothing Then Throw New ArgumentNullException("value") If value.Equals(System.Threading.Thread.CurrentThread.CurrentUICulture) Then Exit Property '1.   : System.Threading.Thread.CurrentThread.CurrentUICulture = value '2.  ResourceDictionary    Dim dict As New ResourceDictionary() Select Case value.Name Case "ru-RU" dict.Source = New Uri(String.Format("Resources/lang.{0}.xaml", value.Name), UriKind.Relative) Case Else dict.Source = New Uri("Resources/lang.xaml", UriKind.Relative) End Select '3.   ResourceDictionary       ResourceDictionary Dim oldDict As ResourceDictionary = (From d In My.Application.Resources.MergedDictionaries _ Where d.Source IsNot Nothing _ AndAlso d.Source.OriginalString.StartsWith("Resources/lang.") _ Select d).First If oldDict IsNot Nothing Then Dim ind As Integer = My.Application.Resources.MergedDictionaries.IndexOf(oldDict) My.Application.Resources.MergedDictionaries.Remove(oldDict) My.Application.Resources.MergedDictionaries.Insert(ind, dict) Else My.Application.Resources.MergedDictionaries.Add(dict) End If '4.      . RaiseEvent LanguageChanged(Application.Current, New EventArgs) End Set End Property
      
      



C#
 //      public static event EventHandler LanguageChanged; public static CultureInfo Language { get { return System.Threading.Thread.CurrentThread.CurrentUICulture; } set { if(value==null) throw new ArgumentNullException("value"); if(value==System.Threading.Thread.CurrentThread.CurrentUICulture) return; //1.   : System.Threading.Thread.CurrentThread.CurrentUICulture = value; //2.  ResourceDictionary    ResourceDictionary dict = new ResourceDictionary(); switch(value.Name){ case "ru-RU": dict.Source = new Uri(String.Format("Resources/lang.{0}.xaml", value.Name), UriKind.Relative); break; default: dict.Source = new Uri("Resources/lang.xaml", UriKind.Relative); break; } //3.   ResourceDictionary       ResourceDictionary ResourceDictionary oldDict = (from d in Application.Current.Resources.MergedDictionaries where d.Source != null && d.Source.OriginalString.StartsWith("Resources/lang.") select d).First(); if (oldDict != null) { int ind = Application.Current.Resources.MergedDictionaries.IndexOf(oldDict); Application.Current.Resources.MergedDictionaries.Remove(oldDict); Application.Current.Resources.MergedDictionaries.Insert(ind, dict); } else { Application.Current.Resources.MergedDictionaries.Add(dict); } //4.      . LanguageChanged(Application.Current, new EventArgs()); } }
      
      





まあ、プログラムの文化を変えるために私たちの窓を教えることは残っています。 新しいウィンドウを作成する場合、アプリケーションでサポートされているすべてのカルチャをカルチャ変更メニューに追加するとともに、以前に作成したイベントハンドラーApplication.LanguageChangedを追加します。 また、ChangeLanguageClickカルチャパントをクリックするためのハンドラーを追加します。これにより、アプリケーションのカルチャとLanguageChanged関数が変更され、 Application.LanguageChangedイベントが処理されます



Visual Basic .NET
 Class MainWindow Public Sub New() InitializeComponent() '       AddHandler Application.LanguageChanged, AddressOf LanguageChanged Dim currLang = Application.Language '   : menuLanguage.Items.Clear() For Each lang In Application.Languages Dim menuLang As New MenuItem() menuLang.Header = lang.DisplayName menuLang.Tag = lang menuLang.IsChecked = lang.Equals(currLang) AddHandler menuLang.Click, AddressOf ChangeLanguageClick menuLanguage.Items.Add(menuLang) Next End Sub Private Sub LanguageChanged(sender As Object, e As EventArgs) Dim currLang = Application.Language '        For Each i As MenuItem In menuLanguage.Items Dim ci As CultureInfo = TryCast(i.Tag, CultureInfo) i.IsChecked = ci IsNot Nothing AndAlso ci.Equals(currLang) Next End Sub Private Sub ChangeLanguageClick(sender As Object, e As RoutedEventArgs) Dim mi As MenuItem = TryCast(sender, MenuItem) If mi IsNot Nothing Then Dim lang As CultureInfo = TryCast(mi.Tag, CultureInfo) If lang IsNot Nothing Then Application.Language = lang End If End If End Sub End Class
      
      



C#
 namespace WPFLocalizationCSharp { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); App.LanguageChanged += LanguageChanged; CultureInfo currLang = App.Language; //   : menuLanguage.Items.Clear(); foreach (var lang in App.Languages) { MenuItem menuLang = new MenuItem(); menuLang.Header = lang.DisplayName; menuLang.Tag = lang; menuLang.IsChecked = lang.Equals(currLang); menuLang.Click += ChangeLanguageClick; menuLanguage.Items.Add(menuLang); } } private void LanguageChanged(Object sender, EventArgs e) { CultureInfo currLang = App.Language; //        foreach (MenuItem i in menuLanguage.Items) { CultureInfo ci = i.Tag as CultureInfo; i.IsChecked = ci != null && ci.Equals(currLang); } } private void ChangeLanguageClick(Object sender, EventArgs e) { MenuItem mi = sender as MenuItem; if (mi != null) { CultureInfo lang = mi.Tag as CultureInfo; if (lang != null) { App.Language = lang; } } } } }
      
      





アプリケーションの準備ができました。 しかし、完全に幸福にするために、アプリケーションの起動時に選択した文化を記憶するようにアプリケーションを構成します。



DefaultLanguage設定をプロジェクトに追加し、タイプSystem.Globalization.CultureInfomscorlibライブラリにあります)を指定し、プロジェクトのデフォルトの中立カルチャを指定します。







また、 Applicationクラスに2つの関数を追加します。



Visaul Basic .NET
  Private Sub Application_LoadCompleted(sender As Object, e As NavigationEventArgs) Handles Me.LoadCompleted Language = My.Settings.DefaultLanguage End Sub Private Shared Sub OnLanguageChanged(sender As Object, e As EventArgs) Handles MyClass.LanguageChanged My.Settings.DefaultLanguage = Language My.Settings.Save() End Sub
      
      



C#
 private void Application_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e) { Language = WPFLocalizationCSharp.Properties.Settings.Default.DefaultLanguage; } private void App_LanguageChanged(Object sender, EventArgs e) { WPFLocalizationCSharp.Properties.Settings.Default.DefaultLanguage = Language; WPFLocalizationCSharp.Properties.Settings.Default.Save(); }
      
      





App.xamlでLoadCompletedイベントハンドラーをApplication要素追加します。



 LoadCompleted="Application_LoadCompleted"
      
      





Appクラスのコンストラクターで、 App.LanguageChangedイベントハンドラーを追加します



 App.LanguageChanged += App_LanguageChanged;
      
      





これで、アプリケーションは、アプリケーションが閉じられたときに選択されたカルチャで開始されます。



プロジェクト全体がGitHubに投稿されています



UDP :(2017.02.13)

カルチャを保持し、デフォルトのカルチャではないカルチャでプログラムを初期化するコードにバグがあります。 このバグはGitHubで修正されました。



All Articles