Artykuł

paź 14 2011
0

WPF Tutorial - aplikacje wielojęzyczne

Jakiś czas temu, we wpisie Tworzenie aplikacji wielojęzycznych z C# i ASP.NET w Visual Studio opisywałem jak stworzyć aplikację, która będzie przystosowana do obsługi wielu języków i wykorzystywać będzie do realizacji tego celu mechanizm zasobów udostępniony w platformie .Net.

Wpis ten, nie opisywał jednak zastosowanie tego mechanizmu, dla aplikacji napisanych pod kątem frameworka WPF, który korzysta z języka XAML. Dlatego też dziś, w ramach kolejnego wpisu z cyklu WPF Tutorial, chciałbym nadrobić braki w tamtym opisie.

Idea tworzenia aplikacji wielojęzycznych

Tworząc naszą aplikację w Polsce - w języku polskim, z góry ograniczamy sobie grono potencjalnych odbiorców (czy też klientów jeśli nasza aplikacja ma charakter biznesowy). Oczywiście nie mam nic przeciwko tworzeniu lokalnym aplikacjom dedykowanym na określony rynek, jednak jeśli nasz program jest dobry, a użytkownicy zadowoleni, to dlaczego by sobie nie zadać trochę więcej trudu i dodać mechanizmy, które pozwalają na lokalizowanie aplikacji? Przed takim właśnie problemem/dylematem, stanął zapewne niejeden developer.

Na szczęście, implementacja internacjonalizacji w C# nie stanowi większego problemu (o czym przekonaliście się czytając poprzedni wpis na ten temat). Lokalizacja aplikacji, nie będzie również żadnym wielkim wyzwaniem dla programisty aplikacji pisanej w WPF.

Implementacja

Implementacja w wielu aspektach przebiega podobnie, jak choćby w aplikacji opartej o Windows Forms, dlatego pewne rzeczy związane z tą tematyką będę opisywać bardziej skrótowo.

Po pierwsze, aplikacje w WPF również korzystają ze standardowych zasobów dostępnych w Visual Studio (mowa tutaj o plikach resx, nie o elementach typu ResourceDictionary omawianych w poprzednim wpisie).

Również w tym przypadku, definiujemy poszczególne łańcuchy w określonych plikach, przeznaczonych dla poszczególnych ustawień regionalnych. W tej materii podtrzymuje swoje zdanie, że w głównym pliku powinny się znaleźć łańcuchy w języku angielskim. Łańcuchy dla języka polskiego, trafią zaś do pliku nazwa_pliku_zasobu.pl-PL.resx.

Należy również pamiętać, by dla każdego pliku resx ustawić Access modifier na Public (ustawienie to można znaleźć po otwarciu wskazanego pliku).

Pliki zasobów, dobrze jest również wydzielić do osobnego katalogu w projekcie np. Languages. Koncepcję tą, zastosowałem dla mojej autorskiej aplikacji Multime, co widać na screenie 1:

Tłumaczenie elementów w kodzie C#

Po utworzeniu plików zasobów, powinniśmy rozpocząć lokalizowanie elementów aplikacji. W samym kodzie C# proces ten można zrealizować bardzo prosto.

Po pierwsze, musimy dodać odpowiedni using, czyli podpiąć przestrzeń nazw, w której znajdują się nasze zasoby. W moim przypadku, cel zrealizuje za pomocą następującego kodu:

using Multime.Languages;

Multime to nazwa projektu, a Languages to folder, w którym znajdują się zlokalizowane zasoby.

Po tym zabiegu, możemy korzystać z naszych zasobów językowych w następujący sposób:

MessageBox.Show(Lang.ConfimLanguageChange, Lang.Question, 
    MessageBoxButton.YesNo, MessageBoxImage.Question);

Lang, to nazwa naszego pliku zasobów. Wartość po kropce, to poszczególne łańcuchy, które zdefiniowaliśmy wewnątrz tych plików. Każdy łańcuch musi znajdować się w pliku głównym, ale już nie każdy łańcuch musi znajdować się w pozostałych plikach. Jeśli określony tekst, nie został zlokalizowany np. w polskim tłumaczeniu, to w takim przypadku pobrana zostanie wartość z głównego pliku zasobów. Takie same zasady panować będą również w przypadku elementów interfejsu definiowanych w XAMLu.

Lokalizacja interfejsu w XAMLu

Wiemy już jak lokalizować nasz code-behind i zasadniczo w skrajnym przypadku moglibyśmy zlokalizować w ten sposób całą aplikację. Nie byłoby to jednak do końca normalne i efektywne rozwiązanie. Z zasobów językowych, możemy korzystać również z poziomu kodu XAML. Również w tym przypadku, należy dodać odpowiedni namespace, tym razem do elementu Window:

<Window ...
    xmlns:p="clr-namespace:Multime.Languages"
    ...>

Przedrostek p, możecie zastąpić dowolnym innym - takim który łatwo będzie się Wam kojarzyć z tym typem zasobu (np. lang).

Aby teraz zlokalizować np. tekst wyświetlany na przycisku, należy zastosować następującą konstrukcję:

<Button Content="{x:Static p:Lang.ButtonClear}" />

Gdzie ButtonClear jest jednym z istniejących łańcuchów w pliku zasobów.

Tworząc interfejs oparty na internacjonalizacji, należy pamiętać, że pewne słowa/zwroty w określonym języku mogą być dłuższe/krótsze. W związku z czym, w takim przypadku stosowanie stałych szerokości np. przycisków, etykiet itp. jest skrajnie niezalecane. Pamiętajcie o tej uwadze, kiedy będziecie projektować Wasz interfejs użytkownika.

Wybór języka w aplikacji

Jeśli skompilujecie teraz swoje aplikacje i uruchomicie je, to prawdopodobnie przemówią one w tym momencie do Was, w takim języku jaki macie ustawiony w Waszym systemie. W moim przypadku jest to język polski.

Jeśli używany przez Was język, nie będzie obsługiwany przez aplikację, to w takiej sytuacji, zostanie wybrany główny plik zasobów. Jeśli zastosowaliście się do mojej wcześniejszej rady, będzie to plik z językiem angielskim.

Takie rozwiązanie ma swoje wady i zalety. Zasadniczo - jest bardzo proste w implementacji, ale w praktyce może być uciążliwe dla naszych użytkowników, którzy mogą chcieć korzystać z innego języka, niż ten który nasza aplikacja dla nich inteligentnie wykryła.

Alternatywnie możemy zastosować rozwiązanie, w którym użytkownik wybiera język w trakcie pracy aplikacji, a informacje o tym języku zapisywane są w pliku konfiguracyjnym. Rozwiązanie to ma też jednak swoją wadę. Jeśli użytkownik, dla którego stworzyliśmy język w naszej aplikacji nie zna angielskiego (czyli naszego domyślnego języka) to może mieć problem z odnalezieniem odpowiedniej opcji w aplikacji, która umożliwia zmianę języka.

Moim zdaniem, najlepszym rozwiązaniem jest połączenie obu technik. Domyślnie (przy pierwszym uruchomieniu) aplikacja powinna sama wykryć język. Jeśli użytkownik będzie niezadowolony z wybranego języka, zmieni go w aplikacji, a stosowana informacja zostanie zapisana w pliku konfiguracyjnym, bądź też np. rejestrze systemowym.

Język samej aplikacji powinniśmy określić jak najwcześniej. Prawdopodobnie najlepszym miejscem przeznaczonym do tego celu, jest plik App.xaml.cs, który dostępny jest w każdej aplikacji WPF.

W pliku tym, możemy przeciążyć metodę OnStartup:

protected override void OnStartup(StartupEventArgs e)
{
    Thread.CurrentThread.CurrentUICulture = new CultureInfo("pl-PL");
    base.OnStartup(e);
}

Oczywiście kod metody, należy opakować odpowiednią logiką biznesową, a nazwę określonej kultury, nie będziemy raczej umieszczać w kodzie stałym tekstem;)

Poprzednia część

Data ostatniej modyfikacji: 28.08.2013, 14:41.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus