WPF – prevod aplikacije

Ovde smo da rešimo dilemu oko prevoda WPF aplikacije.

Tu su delovi koda i objašnjenja, nadam se da će nekome koristiti. Trudiću se da sve ispričam tako da bude razumljivo, obećavam.

Na internetu se dosta pominje prevod WPF aplikacije koristeći “Resources” fajlove, ali to nije ono što sam hteo.
Potreba je bila da postoje ekternalizovani fajlovi sa prevodima, tako da se lako dodaje novi i menja postojeći prevod.
Odluka je da za ovu aplikaciju koristimo XML format. Jednostavan i direktan, lak za pregled i izmenu.

1. XML fajl sa prevodom

Dakle, prvo što će nam trebati je XML fajl sa prevodom, snimite ga u neki folder gde ce ga aplikacija pronaći. Ja nekako volim da koristim “Lang” folder, deluje mi logično…
Evo ga primer XML-a, skraćena verzija radi lakšeg razumevanja.

<?xml version="1.0" encoding="utf-8"?>
<Language name="Srpski" code="sr">
  <Buttons>
    <Ok Text="Ok"/>
    <Save Text="Snimi"/>
    <Cancel Text="Odustani"/>
    <Close Text="Zatvori"/>
    <New Text="Novi" />
    <Edit Text="Izmeni" />
    <Delete Text="Obriši" />
    <Refresh Text="Osveži" />
    <Print Text="Štampa" />
  </Buttons>
</Language>

Vrlo jednostavno, priznaćete.
Sve one koje mrzi da koriste XML, format prevoda može biti bilo kakav, s’tim da ćete u tom slučaju morati da pišete sopstveni parser tog fajla. Ne komplikovati stvari bez preke potrebe.
Ja sam za neke ranije aplikacije koristio INI format, sto se pokazalo kao zaista isplativo, ali sam se ovde ipak odlučio za XML. Zašto, ne sećam se tačno… Ima tu nekih razloga, al nije tema ove priče…

2. Čitanje fajla sa prevodom

Sledeće što će Vam trebati je neki “parser” prethodno kreiranog XML-a, tj. klasa koja će znati da prepozna “ključeve” i vrati vrednost zadatog “ključa”.

Kreirajte novu klasu i dajte joj naziv TranslationService.

Evo ga kod:

using System;
using System.Xml;
using System.IO;

namespace GHC.Utilities
{
    public class TranslationService
    {
        private static string _language = "sr";
        public static string Language { get { return _language; } set { _language = value; } }

        private static XmlDocument doc;
        private static XmlDocument Document
        {
            get
            {
                if (doc == null)
                {
                    doc = new XmlDocument();

                    string langPath = Path.Combine("Lang", Language + ".flgx");
                    if (File.Exists(langPath))
                    {
                        doc.Load(langPath);
                    }
                }

                return doc;
            }
        }

        public static String GetTranslation(string key)
        {
            try
            {
                string xpath = "Language/" + key.Replace(".", "/");
                XmlNode nod = Document.SelectSingleNode(xpath);
                if (nod != null)
                {
                    return nod.Attributes[0].Value.Replace(@"\n", Environment.NewLine);
                }
                else
                {
                    //Ako nije pronadjen zadati kljuc, vrati isti da vidimo sta fali od prevoda
                    return key;
                }
            }
            catch
            {
                //Necemo da dozvolimo da aplikacija pukne ako nesto nije u redu, ili hocemo, vas izbor
                return key;
            }
        }
    }
}

Podrazumevani jezik je Srpski. Ukoliko želite da koristite neki drugi prevod/fajl, promenite “Language” property pre prvog poziva GetTranslation(string key) metode, i eto ga!
Zašto pre prvog poziva? Ako pogledate kod, videćete da se XmlDocument kreira jednom, prvi put kada se pozove, sa zadatim Language-om… Da usput malo razmišljamo i o optimizaciji, jel tako…

Dakle, sva pamet ove klase je u traženju xml nod-a po zadatoj putanji.

string xpath = "Language/" + key.Replace(".", "/");
XmlNode nod = Document.SelectSingleNode(xpath);

Odakle sad ova tačka i kakav je sad ovo string.Replace(“.”, “/”)? Objasniću na kraju, deluje elegantno kasnije u XAML-u.

Sve što ćete trebati da uradite je da pošaljete željenu putanju do ključa u samom XAML-u, dolazimo do toga.

3. XAML – MarkupExtension

MarkupExtension je veoma zanimljiv, ako ga niste koristili do sada, svideće Vam se, sigurno, bar meni jeste. To je ono sto ustvari daje mogućnost da svoje transformacije zadržite na nivou XAML-a, bez preteranih egzibicija u samom kodu.

Negde u kod dodajte novu klasu, mi smo je nazvali TranslationExtension koja nasledjuje baznu MarkupExtension klasu.

using System;
using System.Windows.Markup;

namespace GHC.UI.Extensions
{
    public class TranslationExtension : MarkupExtension
    {
        private string _key;

        public TranslationExtension() { }
        public TranslationExtension(string key) {
            this._key = key;
        }

        [ConstructorArgument("Key")]
        public string Key { get { return _key; } set { _key = value; } }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return GHC.Utilities.TranslationService.GetTranslation(Key);
        }
    }
}

Ne bih da mnogo širim priču dalje oko MarkupExtension-a, sve će biti jasno kada se vidi primer XAML-a

4. Konačno – XAML

I to je to, samo je ostalo da “pozovemo” naš servis za prevod iz XAML-a.

Prvo što nam treba je xmlns našeg servisa. Negde u Window tag dodajte ovo

xmlns:ext="clr-namespace:GHC.Extensions;"

Ovim smo dodali namespace gde se nalazi TranslationService u klasu, slično kao kada bismo koristili “using GHC.Extensions;” u kodu.

Na primer:


 <button name="btnOk"></button>
 <button name="btnCancel"></button>

I to je to!

Sada je jasna ona priča odakle tačka u parseru od malopre. TranslationService menja tačku sa slash-om da bi dobio ispravan xpath, odakle povlači vrednost noda, i vraća prevedeni tekst.

Sve što treba da uradite je da dodate poziv na Translation iz svog XAML-a, i prevod je tu.

Content="{ext:Translation Buttons.Save}"

Nadam se da sam bio od pomoći.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s