Lokalisierung in WPF

Da die Lokalisierung, auch Mehrsprachigkeit genannt, im Vergleich zu Windows-Forms sich verändert hat, möchte ich hier eine einfache Lösung darstellen:

Zu allererst erstellen wir ein neues Projekt mit dem Namen "Lokalisierung":

wpf-cult1

Im nächsten Schritt, wird eine einfache Oberfläche erstellt. Dazu benutzen wir den integrierten Designer von Visual Studio:

wpf-cult2

Mit dem dazu gehörenden XAML-Code:

<Window x:Class="Lokalisierung.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="{DynamicResource MainWindow-Title}" 
    Height="155" Width="525" ResizeMode="NoResize">
  <Grid>
  <StackPanel>
    <StackPanel Orientation="Horizontal" VerticalAlignment="Top">
    <Label Width="120" Margin="10" 
      Content="{DynamicResource MainWindow-lblExample}"
      Height="28" Name="lblTxt" VerticalAlignment="Top" />
    <Border BorderBrush="#000000" BorderThickness="1" Margin="10">
      <TextBlock Height="30" Width="340" Name="txtBlock"
        Text="{DynamicResource MainWindow-lblLang}" Padding="5" />
    </Border>
    </StackPanel>
    <StackPanel Orientation="Horizontal" VerticalAlignment="Top">
    <Label  Width="120" Margin="10" Content="{DynamicResource MainWindow-cbLang}"
      Height="28"  Name="lblLang" VerticalAlignment="Top" />
    <ComboBox Margin="10" Height="23" Name="cbLang" Width="120"
      ItemsSource="{Binding}"
      SelectionChanged="cbLang_SelectionChanged">
      <ComboBoxItem Content="Deutsch" IsEnabled="True"
        IsSelected="True" />
      <ComboBoxItem Content="English" />
    </ComboBox>
    </StackPanel>
  </StackPanel>
  </Grid>
</Window>

Als nächsten Schritt fügen wir ein Resource Dictionary zum Projekt hinzu.

wpf-cult3

Das erste Wörterbuch „CultResource.xaml“ stellt das Default-Wörterbuch dar. Dieses wird immer geladen, wenn ein anderes Wörterbuch nicht vorhanden ist.

wpf-cult4

In diesem Wörterbuch werden nun die Zeichenketten definiert, welche wir in der Oberfläche dynamisch geändert haben möchten. Für die Zuordnung werden eindeutige Schlüssel (x:Key) vergeben:

<ResourceDictionary 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:system="clr-namespace:System;assembly=mscorlib">

  <system:String x:Key="MainWindow-Title">MainWindow-Titel</system:String>
  <system:String x:Key="MainWindow-lblExample">lblExample</system:String>
  <system:String x:Key="MainWindow-lblLang">lblLang</system:String>
  <system:String x:Key="MainWindow-cbLang">cbLang</system:String>
</ResourceDictionary>

Im Folgenden werden schließlich die Wörterbücher für die einzelnen Sprachen angelegt. Dazu braucht man nur das Standard-Wörterbuch zu kopieren und dessen Einträge übersetzen.

Den Wörterbüchern werden noch die Culture-Kennungen, „de-DE“ für das deutsche Wörterbuch und „en-US“ für das englische Wörterbuch, angehangen.

wpf-cult5

Die XAML-Dateien sollten nach dem Übersetzen ungefähr so aussehen:

<!-- CultResource.de-DE.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:system="clr-namespace:System;assembly=mscorlib">

  <system:String x:Key="MainWindow-Title">Lokalisierung</system:String>
  <system:String x:Key="MainWindow-lblExample">Beispieltext:</system:String>
  <system:String x:Key="MainWindow-lblLang">Globalisierung ist die Folge des 
          technologischen Fortschritts.</system:String>
  <system:String x:Key="MainWindow-cbLang">Sprache wählen:</system:String>

</ResourceDictionary>

<!-- CultResource.en-US.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:system="clr-namespace:System;assembly=mscorlib">

  <system:String x:Key="MainWindow-Title">Localization</system:String>
  <system:String x:Key="MainWindow-lblExample">Sample text:</system:String>
  <system:String x:Key="MainWindow-lblLang">Globalisation is the result of technological
         progress.</system:String>
  <system:String x:Key="MainWindow-cbLang">Choose Language:</system:String>

</ResourceDictionary>

Nun wird das Wörterbuch noch in der App.xaml eingetragen.

<Application x:Class="Lokalisierung.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Cultures/CultResource.xaml"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

Im Anschluss werden die Controls an die dynamische Ressource, mit dem eindeutigen Schlüssel, gebunden:

<!-- Titel -->
Title="{DynamicResource MainWindow-Title}" Height="155" Width="525" ResizeMode="NoResize">

<!-- Label Beispiel -->
<Label Width="120" Margin="10" Content="{DynamicResource MainWindow-lblExample}"
 Height="28"  Name="lblTxt" VerticalAlignment="Top" />

<!-- Text im Textblock -->
<Label Width="120" Margin="10" Content="{DynamicResource MainWindow-lblExample}"
 Height="28"  Name="lblTxt" VerticalAlignment="Top" />

<!-- Label Sprachwahl -->
<Label Width="120" Margin="10" Content="{DynamicResource MainWindow-lblExample}"
Height="28"  Name="lblTxt" VerticalAlignment="Top" />

Diese Wörterbücher werden ebenfalls der Anwendung, in der Datei App.xaml, bekannt gemacht:

<ResourceDictionary Source="Cultures/CultResource.de-DE.xaml"/>
<ResourceDictionary Source="Cultures/CultResource.en-US.xaml"/>

Standardmäßig wird immer das letzte Wörterbuch in der “App.xaml” beim Starten der Anwendung geladen.

Um die Sprache beim Starten der Anwendung und dynamisch während der Laufzeit zu ändern, benötigen wir dazu noch eine Methode.

/// <summary>
/// Sprache wechseln
/// </summary>
/// <param name="culture">specific culture</param>
public void ChangeLanguage(string culture)
{
  // Alle Woerterbuecher finden   
  List<ResourceDictionary> dictionaryList = new List<ResourceDictionary>();
  foreach (ResourceDictionary dictionary in
                 Application.Current.Resources.MergedDictionaries)
  {
    dictionaryList.Add(dictionary);
  }

  // Woerterbuch waehlen
  string requestedCulture = string.Format("Cultures/CultResource.{0}.xaml", culture);
  ResourceDictionary resourceDictionary = 
          dictionaryList.FirstOrDefault(d => d.Source.OriginalString == requestedCulture);
  if (resourceDictionary == null)
  {
    // Wenn das gewuenschte Woerterbuch nicht gefunden wird,
    // lade Standard-Woerterbuch
    requestedCulture = "Cultures/CultResource.xaml";
    resourceDictionary = dictionaryList.FirstOrDefault(d => 
                  d.Source.OriginalString == requestedCulture);
  }

  // Altes Woerterbuch loeschen und Neues hinzufuegen       
  if (resourceDictionary != null)
  {
    Application.Current.Resources.MergedDictionaries.Remove(resourceDictionary);
    Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);
  }

  // Hauptthread ueber neues Culture informieren
  Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(culture);
  Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
}

Diese Methode wird einfach in den Konstruktor mit der favoritisierende Sprache aufgerufen.
Schlussendlich benötigen wir noch einen Eventhandler für die Combobox um dynamisch die Sprache zur Laufzeit zu ändern:

private void cbLang_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox cmd = sender as ComboBox;
    string language =   ((ComboBoxItem)cmd.Items[cmd.SelectedIndex]).Content.ToString();

  if (language == "English")
  {
    ChangeLanguage("en-US");
  }
  else
  {
    ChangeLanguage("de-DE");
  }
}

Download Visual-Studio-Projekt: vsp-lokalisierung.rar