Lokalizacja UI z wykorzystaniem plików XML i mechanizmu refleksji – Część 2

W części 1 przedstawiłem ogólny zarys naszego modułu lokalizacyjnego. W tej części zajmiemy się formatem pliku xml z zasobami językowymi oraz deserializacją i przechowywaniem danych. Na koniec pokażę jak identyfikować na dysku interesujące nas pliki.

Format pakietu językowego

Jak już wcześniej wspomniałem plik xml, w naszym wypadku, będzie odzwierciedlał słownik. Klucz słownika reprezentuje klucz wstawiony do atrybutu (o tym atrybucie w części 3), którym opatrzymy odpowiedni property. Setter będzie ustawiał tekst danego elementu GUI. Wartość natomiast zawiera tekst, który ma zostać wyświetlony w tym elemencie.

Przykładowy plik xml:



  
    Język
    Wyjście
    ID
    Nazwa
    Wiek
  

Klucze zostały dobrane tak, aby były unikatowe oraz sugerowały, którego elementu w GUI dotyczą. Np. wartość dla Form1.ExitButton jest tekstem wyświetlanym na przycisku, którego rodzicem jest Form1 (specyficzne dla technologii Windows Forms). Oczywiście każdy może wybrać swoją konwencję. Najważniejsze to być w tej kwestii konsekwentnym gdyż szybko można się pogubić w gąszczu kluczy.

Jeżeli kiedyś już serializowałeś obiekty do XML bez problemu zorientujesz się w strukturze powyższego pliku. LanguagePack jest to nazwa klasy obiektu serializowanego, Labels jest nazwą obiektu klasy List, natomiast LanguagePackItem jest nazwą klasy obiektów reprezentujących konkretne tłumaczenie (para klucz – wartość). Ale po kolei…

Reprezentacja danych w pamięci

Para klucz – wartość / tłumaczenie
Odpowiedzialna za to będzie klasa LanguagePackItem. Nie robi on nic specjalnego. Przechowuje jedynie 2 właściwości: Key oraz Value. Aby uzyskać plik xml w takim formacie jak powyżej, należy opatrzyć te właściwości odpowiednimi atrybutami.

[Serializable]
public class LanguagePackItem
{
    [XmlAttribute]
    public string Key { get; set; }
    [XmlText]
    public string Value { get; set; }

    public LanguagePackItem() { }
}

Cały pakiet językowy
Listę wszystkich par klucz-wartość będziemy przechowywać w obiekcie klasy LanguagePack. Obiekt ten musi być opatrzony atrybutem Serializable, aby można było dokonać deserializacji danych. Tak jak z przykładowego pliku XML wynika, listę obiektów LanguagePackItem nazwiemy Labels. Czyli mamy…

[Serializable]
public class LanguagePack
{
    public List(LanguagePackItem) Labels { get; set; }

    public LanguagePack()
    {
       Labels = new List(LanguagePackItem)();
    }
    //...
}

* oczywiście List jest to typ generyczny, więc zamiast nawiasów okrągłych używamy nawiasów kątowych (odnośnie typu elementów listy)

Deserializacja danych

Mamy już plik XML z danymi, mamy klasy których obiekty mogą przechowywać całą zawartość pliku. Musimy teraz w jakiś sposób te dane wczytać. Jeżeli ktoś pewnie czuje się pracując na obiektach typu XmlDocument czy XmlNode, może próbować zrobić to ręcznie. Dla pozostałych polecam deserializację. Nie bez powodu nazwy klas z kodu C# pokrywają się z tymi zastosowanymi w pliku XML.

Do klasy LanguagePack dopiszemy metodę statyczną deserializującą dane z pliku.

public static LanguagePack Load(string file)
{
    if (File.Exists(file))
    {
        XmlSerializer xml = new XmlSerializer(typeof(LanguagePack), new Type[] { typeof(LanguagePackItem) });

        using (Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            LanguagePack pack = (LanguagePack)xml.Deserialize(stream);

            return pack;
        }
    }
    else throw new FileNotFoundException(file);
}

W linii 5 tworzymy obiekt, który zdeserializuje nam dane. Pierwszy parametr konstruktora to typ obiektu do deserializacji (dane z jakiego typu obiektu znajdują się w pliku). Drugi parametr to tablica typów, które zostały wykorzystane dodatkowo (zwracamy uwagę tylko na typy złożone). W linii 7 otwieramy nasz plik XML, a później strumień przekazujemy do deserializacji. W ten prosty sposób utworzyliśmy obiekt klasy LanguagePack z wczytaną zawartością naszego pliku.

Wyszukiwanie plików językowych

W części 1 opisałem zalety rozwiązania opartego na plikach XML. Jedną z nich była możliwość doklejenia w locie kolejnych języków. Jak zatem otrzymać aktualną listę dostępnych języków? Przeszukujemy wybrany katalog i sprawdzamy wszystkie pliki o rozszerzeniu *.xml. Te, które uda się zdeserializować zawierają odpowiednie dla nas dane. Sugeruję, aby nazwę języka zawrzeć w nazwie pliku. Tj. dla języka polskiego Polski.xml, dla angielskiego English.xml itd. Jeżeli ktoś chce może zapisać taką informację bezpośrednio w pliku xml.

Oto kod C# dla uzyskiwania listy języków:

// Filtr po plikach xml w bieżącym katalogu
foreach (string file in Directory.GetFiles(".", "*.xml"))
{
    try {
        LanguagePack pack = LanguagePack.Load(file);
        
        // Dodaj pack do listy języków
        // ...
    }
    catch { }
}

Na zakończenie

To wszystko jeśli chodzi o część 2. Kod źródłowy całego programu można pobrać poprzez link znajdujący się w ostatniej części tej serii. W następnym artykule opiszę w jaki sposób wykorzystać dostępne zasoby do tłumaczenia elementów interfejsu. Przedstawię wszystko na przykładzie bardzo prostego programu w Windows Forms.

Share

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Spam protection by WP Captcha-Free