Lokalizowanie aplikacji WPF oraz Silverlight 5 przy użyciu MarkupExtension

W poprzednim wpisie przedstawiłem w jaki sposób można lokalizować aplikację napisaną w Silverlight 4 oraz Windows Phone, wykorzystując do tego ten sam mechanizm. Tym razem zademonstruje w jaki sposób można nieco uprościć składnie tłumaczenia wykorzystując do tego MarkupExtension.
Jeżeli kiedykolwiek pisałeś coś w Silverlighcie, Windows Phonie lub WPF-ie istnieje duża szansa, że używałeś już MarkupExtension. Do najpopularniejszych MarkupExtensions należą takie słowa kluczowe (używane w XAML-u) jak:

  • Binding
  • StaticResource
  • DynamicResource
  • TemplateBinding

Na potrzeby mechanizmu lokalizowania aplikacji WPF (ewentualnie Silverlight 5) stworzymy customowe MarkupExtenssion, które będzie odpowiedzialne za tłumaczenie elementów UI naszej aplikacji.
Zacznijmy od przygotowania plików zasobów. Podobnie jak w poprzednim wpisie utwórzmy trzy pliki:

  • Localization.resx
  • Localization.pl-PL.resx
  • Localization.en-US.resx

W plikach tych będziemy przechowywać nasze tłumaczenia. Następnie stwórzmy klasę Translator, która będzie dziedziczyła po klasie MarkupExtension.Do klasy tej dodajmy właściwość

,która będzie przechowywać klucz dzięki któremu z pliku zasobów wyciągniemy tekst w odpowiednim języku. W kolejnym kroku musimy przeciążyć funkcję

tak aby dostarczyła nam ona przetłumaczony tekst.W moim pierwszym podejściu funkcja ta wyglądała w następujący sposób

Wadami takiego rozwiązania było:

  • duża liczba tworzonych obiektów – przy każdym tłumaczeniu tworzyłem nowy obiekt Localization(), który prawdopodobnie może być dość ciężkim obiektem (zwłaszcza gdy będzie przechowywał dużo tłumaczonego tekstu)
  • brak możliwości dynamicznej zmiany języka

Ostatecznie zatem zrezygnowałem z przedstawionej wyżej opcji i zdecydowałem się na metodę odrobinę bardziej zaawansowaną. Po pierwsze utworzyłem klasę TranslationManager

która będzie zarządzała tłumaczeniami. Najważniejszą metodą tej klasy jest oczywiście funkcja

która to zwraca tekst w odpowiednim języku – w zależności od kultury, która zostanie ustawiona w TranslationManagerze. Ponadto TranslationManager posiada jedno zdarzenie

które ma za zadanie poinformować UI o potrzebie odświeżenia zbindowanych elementów. W jaki sposób się to odbywa ? Wszystko zawdzięczamy interfejsowi INotifyPropertyChanged oraz bindingom. Funkcja ProvideValue została zmodyfikowana w następujący sposób

W funkcji tej tworze binding, który binduje się do właściwości Value obiektu TranslationItem. TranslationItem jest to prosty obiekt który udostępnia właściwość Value zwracającą przetłumaczony tekst. Dodatkowo obiekt ten podpina się do zdarzenia LanguageChanged z klasy TranslationManager. W przypadku gdy ktoś zmieni język aplikacji, odpalone zostanie zdarzenie NotifyPropertyChanged, które poinformuje widok o potrzebie odświeżenia odpowiednich elementów. Klasa TranslationItem wygląda zatem w następujący sposób

Mając gotowy mechanizm tłumaczący możemy wykorzystać go w następujący sposób w XAML-u

Zauważmy, że tekst do labelki jest przypisywany z wykorzystywaniem naszego customoweog MarkupExtension – WPFMarkupExtension:Translator (WPFMarkupExtension – jest to alias na namespace, w którym znajduje się nasza klasa Translator). W składni przekazujemy do właściwości Key klucz do tekstu (znajdującego się w resourcach), który chcemy tłumaczyć. W celu zmiany języka wystarczy, że ustawimy interesującą nas kulturę w klasie TranslationManager

Przykładowy widok wykorzystujący napisany translator, oraz pokazujący dynamiczną zmianę języka może wyglądać w następujący sposób (dorzucamy następujące linijki do MainWindow.xaml)

Następnie tworzymy ViewModel do naszego okna

Ostatecznie przypisujemy obiekt klasy MainPageViewModel do DataContextu MainWindow.xaml

MarkupExtension
Kod do projektu można znaleźć pod tym linkiem
http://www.4shared.com/rar/SwDXay7V/WPFMarkupExtension.html

Lokalizowanie aplikacji WPF oraz Silverlight 5 przy użyciu MarkupExtension