Wstęp

To jest kolejny artykuł z serii o globalizacji i lokalizacji. Jeśli nie czytałeś poprzednich, koniecznie to nadrób. W tym artykule opisuję TYLKO jak ładować tłumaczenia w aplikacjach WinForms i WPF. Poprzedni artykuł – Tłumaczenie aplikacji cz. 3 – jak to ogarnąć? – daje całą podstawę.

Tłumaczenia w WinForms

W WinForms nie ma żadnego zmyślnego sposobu na ładowanie tłumaczeń. Po prostu każdemu przyciskowi, labelowi itd musisz zmienić TEXT w runtime. Chyba, że wymyślisz swój własny sposób, który zadziała automatycznie. Ale chyba więcej z tym nerwów niż pożytku.

Generalnie robisz to dokładnie tak samo, jak robiłbyś to w konsoli – dokładnie tak jak opisane w artykule podstawowym.

Pamiętaj, żeby domyślnie nadawać teksty w języku angielskim – wtedy jeśli czegoś nie przetłumaczysz, użytkownicy zobaczą teksty w tym właśnie języku.

Tłumaczenia w WPF

W WPF skorzystamy z ustrojstwa, co się zowie MarkupExtension. Jeśli nie wiesz co to, to odsyłam do netu: „WPF markup extension”, być może kiedyś opiszę ten mechanizm.

W skrócie – to coś, dzięki czemu możesz tworzyć własne tagi XAML. Coś jak {Binding...}

Teraz popatrz na ten fragment kodu:

<GroupBox Header="{app:Localize Receipt}" />

Tutaj widzisz mój markup extension – Localize. Efektem tego kodu będzie pobranie zasobu o kluczu Receipt i wartość tego zasobu będzie widoczna w nagłówku GroupBoxa – w Polsce: „Paragon”, wszędzie indziej: „Receipt”. Super? Ja się jaram 🙂

A teraz zobaczmy, jak coś takiego osiągnąć. Generalnie łatwo, prosto i przyjemnie…

MarkupExtension do tłumaczeń

Stwórz taką klasę najlepiej gdzieś w projekcie WPF:

[ContentProperty("ResId")]
class LocalizeExtension : MarkupExtension
{
    public string ResId { get; set; }
    public LocalizeExtension()
    {

    }

    public LocalizeExtension(string ResId)
    {
        this.ResId = ResId;
    }


    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (string.IsNullOrWhiteSpace(ResId))
            return "<???>";

        string result = LangRes.ResourceManager.GetString(ResId);
        if (string.IsNullOrEmpty(result))
            return $"<? {ResId} ?>";
        else
            return result;
    }
}

Wystarczy napisać klasę, która dziedziczy po MarkupExtension.

W linijce 1 podajesz domyślną właściwość… Tzn. gdybym tego nie zrobił, musiałbym kod w XAML napisać tak:

<GroupBox Header="{app:Localize ResId=Receipt}"/> //zwróć uwagę na obecność ResId

Klasa ma jedną metodę – ProvideValue i to w niej dzieje się cała magia. Po prostu pobieram stringa z zasobów na podstawie przekazanego klucza.

Ja tu sobie zrobiłem taki myk, że w razie jakbym nie dodał jakiegoś tłumaczenia, wtedy zamiast konkretnego stringa (którego nie ma w zasobach) zobaczę nazwę tego klucza w nawiasach ostrych. Dzięki temu wiem, że danego tłumaczenia nie ma w zasobach. Pozwalam sobie na taką nonszalancję, bo sprawdzam każde okienko, które robię.

I to właściwie tyle. Po utworzeniu takiej klasy, możesz skompilować projekt i używać swojego markup extension.

Być może nie wiesz skąd się bierze to app w kodzie:

<GroupBox Header="{app:Localize Receipt}"/>

To po prostu alias na namespace, w którym masz swoją klasę LocalizeExtension. Musisz go oczywiście zadeklarować na początku pliku XAML analogicznie do innych, np:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:app="clr-namespace:MojaAplikacja">

gdzie MojaAplikacja to namespace, w którym znajduje się Twoja klasa. Oczywiście to nie musi być app. To może być cokolwiek, ale mam nadzieję, że to wiesz.


To wszystko jeśli chodzi o tłumaczenie aplikacji desktopowych. Jeśli masz jakieś inne pomysły, podziel się w komentarzu.

Obrazek komputera w logo tego artykułu dzięki upklyak / Freepik

Podziel się artykułem na: