Lokalizacja aplikacji WPF: ResourceDictionary

Posted by Przemysław Owsianik on 2021-10-02
<p>Pracując nad aplikacją wykorzystującą technologię&nbsp;<b>W</b>indows&nbsp;<b>P</b>resentation&nbsp;<b>F</b>undation (zresztą każdą inną też), warto zadać sobie pytanie dotyczące internacjonalizacji. W związku z tym, że Hard Training ma być aplikacją co najmniej półprofesjonalną 🙂 wypadało by móc odpalić ją w innym języku niż polski (wiadomo, że na myśli mam angielski ;))</p> <p>Do wyboru mam dwie opcje(przynajmniej tyle jest mi znanych :)): Tłumaczenia umieszczać&nbsp;w <b>plikach zasobów *.resx</b>, lub wykorzystać do tego celu funkcjonalność oferowaną przez WPF, i nazywającą się&nbsp;<b>ResourceDictionary</b>. Jako, że z resx’ów korzystam w pracy wybrałem drugą opcję – jej zaletą jest też fakt, że przy tym podejściu można łatwo i szybko zaimplementować możliwość zmiany języka interfejsu przez użytkownika.</p> <h2>Krótko o ResourceDictionary</h2> <p>ResourceDictionary jest to słownik przechowujący <b>zestawy klucz-obiekt</b>. Taki słownik definiujemy w pliku <b>xaml</b>. Oczywiście zasoby, które można przechowywać w ResourceDictionary nie ograniczają się do tłumaczeń, ale w związku z tym że ten wpis nazywa się „HardTraining: Lokalizacja aplikacj WPF”, a nie „Opis ResourceDictionary”, to skupię się właśnie na tym zastosowaniu. 🙂</p> <h2>How to</h2> <p>Jak już wspomniałem ResourceDictionary definiujemy w plikach xaml, przy czym może to być nowy plik dodany do projektu, jak i plik z definicją okna, kontrolki itd. .</p> <p>Ja postanowiłem utworzyć słownik w nowym pliku, z którego będzie korzystać każde okno aplikacji, aczkolwiek nie wykluczone, że w miarę jak projekt będzie rósł, być może będę przygotowywał dla każdego okna (albo może dla każdego modułu) osobne tłumaczenie.</p> <p>Więc jak? Do rzeczy :). Na początku tworzymy (z menu kontekstowego naszego projektu – ja korzystam z VS 2015, ale tak samo wygląda to w VS2013, więc&nbsp;keep calm&nbsp;– wybieramy opcję&nbsp;Add-&gt;New&nbsp;Item, i następnie z zakładki&nbsp;WPF&nbsp;opcję&nbsp;Resource Dictionary), taką ilość słowników, jaką chcemy mieć ilość języków w aplikacji. Następnie definiujemy w każdym swoje tłumaczenia (więc w każdym słowniku powinny znajdować się te same klucze).</p> <p>Kod słownika, na początku pracy, wygląda mniej więcej w ten sposób 🙂 :</p> <pre class="language-" tabindex="0"><code class="language-"><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="Username">Username:</system:String> <system:String x:Key="Password">Password:</system:String> </ResourceDictionary></code></pre> <p>I słownik z Polskim tekstem:</p> <pre class="language-" tabindex="0"><code class="language-"><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="Username">Nazwa użytkownika:</system:String> <system:String x:Key="Password">Hasło:</system:String> </ResourceDictionary></code></pre> <p>Kolejną kwestią jest umieszczenie w pliku App.xaml, odwołania do domyślnego języka (korzystając z <b>MergedDictionary</b>, by móc korzystać z wielu słowników zdefiniowanych poza app.xaml) :</p> <img src="/static/img/blog/MergedDictionaryInApp.png" alt="" class=" postImage" /> <p>Tearaz pozostaje się tylko odwołać, w kodzie xaml’owym okna/kontrolki (za pomocą <b>DynamicResource</b>), do właściwych obiektów (oczywiście poprzez klucz) :</p> <img src="/static/img/blog/DynamicResource.png" alt="" class=" postImage" />