Smysl lokalizace
V moderních aplikacích se často můžeme střetnout s požadavkem na lokalizaci uživatelského rozhraní. To znamená, že aplikace bude schopna s uživatelem komunikovat v jeho mateřském jazyce a používat symboly a zvyklosti související s jeho kulturou. Mezi tyto symboly a zvyklosti patří mimo jiné formátování čísel, formátování data a času, používaný kalendář či konvence spojené s měnou.
Podpora v .NET frameworku
Podpora pro vývoj lokalizovaných aplikací je v .NET frameworku na velmi vysoké úrovni a je velmi snadno použitelná. Třídou, se kterou bychom se měli v souvislosti s lokalizací seznámit asi nejdříve je třída CultureInfo, která se, stejně jako několik dalších typů související s lokalizací, nachází ve jmenném prostoru System.Globalization. Instance této třídy slouží k získání informací, které souvisejí s nějakou kulturou. Objekty typu CultureInfo mohou reprezentovat specifickou kulturu, neutrální kulturu či kulturu neměnnou. Specifická kultura je asociována s jazykem a zemí či regionem, tedy například angličtina ve Velké Británii čí angličtina ve Spojených státech. Neutrální kultura je asociována pouze s jazykem a kultura neměnná je nezávislá na aktuálním kulturním prostředí. Jméno kultury je specifikováno podle standardu RFC 1766 a tím je tvar kód jazyka – kód země (en-US). V případě neutrální kultury logicky pouze kódem jazyka (cs) a pokud chceme použít kulturu neměnnou specifikujeme prázdným řetězcem.
V .NET frameworku existuje podpora pro obsáhlou množinu kultur, což demonstruje i náš první příklad, který použitím GetCultures třídy CultureInfo získá a vypíše obsah této množiny.
CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
foreach (CultureInfo ci in cultures)
{
Console.WriteLine("{0} - {1}", ci.TwoLetterISOLanguageName, ci.DisplayName);
}
Console.WriteLine("Pocet moznych kultur : {0}", cultures.Length);
Pomocí instance třídy CultureInfo jsme schopni zjistit velké množství informací pro danou kulturu, mezi které patří například použitý kalendář, ale kromě toho je skrze ní možné získat instanci třídy NumberFormatInfo, díky které zjistíme například jednotku měny a také lze díky vlastnosti DateTimeFormat získat odkaz na instancí třídy DateTimeFormatInfo, pomocí níž zjistíme mimo jiné názvy dní či měsíců. To vlastně již ukazuje zdrojový kód následujícího příkladu.
//vytvoreni instance pro cesky jazyk v ceske republice
CultureInfo czechCulture = new CultureInfo("cs-CZ");
Console.WriteLine(czechCulture.DisplayName);
Console.WriteLine(czechCulture.NativeName);
Console.WriteLine(czechCulture.Calendar);
//ziskani informaci o formatu cisel a meny
NumberFormatInfo ni = czechCulture.NumberFormat;
Console.WriteLine("Symbol meny : {0}", ni.CurrencySymbol);
Console.WriteLine("SYMBOLY NEKONECNA : ");
Console.WriteLine(ni.PositiveInfinitySymbol);
Console.WriteLine(ni.NegativeInfinitySymbol);
//ziskani informaci o formatu datumu
DateTimeFormatInfo di = czechCulture.DateTimeFormat;
Console.WriteLine("NAZVY DNU TYDNU : ");
foreach (string day in di.DayNames)
{
Console.Write(day + ", ");
}
Console.WriteLine();
Console.WriteLine("NAZVY MESICU :");
foreach (string month in di.MonthNames)
{
Console.Write(month + ", ");
}
Každé běžící vlákno si s sebou nese informaci o tom s jakou kulturou je právě asociováno. Tuto informaci jsme schopni zjistit, ale i změnit pomocí vlastnosti CurrentCulture na instanci známé třídy Thread.
CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
Console.WriteLine(currentCulture.DisplayName);
Alternativní možností k dosažení téhož výsledku je použití vlastnosti CultureInfo.CurrentCulture.
Další třídou, která souvisí s lokalizací je třída RegionInfo, která na rozdíl od třídy CultureInfo nese informace pouze o zemi či jejím regionu, kdy některé z těchto informací nejsou pomocí třídy CultureInfo k dostání.
RegionInfo czechRegion = new RegionInfo("cz");
Console.WriteLine("Nazev oblasti : {0}", czechRegion.NativeName);
Console.WriteLine("Nazev meny : {0}", czechRegion.CurrencyNativeName);
Console.WriteLine("Pouziva metrickou soustavu : {0}", czechRegion.IsMetric);
Práce s lokalizovanými zdroji
Po seznámení se způsobem uchovávání informací o kulturním prostředí uživatele aplikace nyní přejdeme k tomu, jakým způsobem lze získat lokalizované zdroje pro naši aplikaci. Zdroje, které mohou být lokalizovány, tedy přizpůsobeny prostředí uživatele, smějí být několika různých typů. Nejčastějším typem lokalizovaných zdrojů patří jednoduché řetězce použité jako texty různých hlášek čí popisky uživatelského rozhraní. Ale tady možnosti samozřejmě nekončí a podpora lokalizace .NET frameworku je schopna udržovat objekty prakticky jakéhokoli typu (Obrázky, soubory..).
K získávání lokalizovaných zdrojů slouží v rámci .NET frameworku typ ResourceManager ze jmenného prostoru System.Resources. Zdroje pro různé kultury mohou být v různých formátech. My si dnes ukážeme použití formátu resx, kde jsou zdroje zapisovány v XML. Pro to, aby byl ResourceManager schopen najít a použít naše zdroje, musí být soubory pojmenovány podle určitých pravidel. Pokud vytváříme soubor zdrojů pro neměnnou kultury, tedy zdroje, které nejsou závislé na kulturním prostředí, pojmenujeme soubor jednoduše například Messages.resx. Zdroje z takovýchto souborů jsou použity pokud není správcem zdrojů (ResourceManager) nalezen soubor zdrojů pro konkrétní kulturu. Pokud vytváříme zdroje pro specifickou kulturu tedy například pro angličtinu ve Spojených státech umístíme tyto zdroje do souboru s názvem ve tvaru Messages.en-US.resx a pokud chceme definovat zdroje pro neutrální kulturu, například pro češtinu soubor nazveme Messages.cs.resx.
V souboru zdrojů ve formátu resx je obsaženo několik druhů elementů, avšak pro nás jsou nejzajímavější elementy data. Následující část ukázkového souboru Messages.resx definuje dva zdrojové texty – Msg1 a Msg2.
<data name="Msg1" xml:space="preserve">
<value>Prvni zprava</value>
</data>
<data name="Msg2" xml:space="preserve">
<value>Druha zprava</value>
</data>
Soubor obsahující zdrojové texty pro kulturu en-US, který nese název Messages.en-US.resx, obsahuje texty těchto zpráv v angličtině.
<data name="Msg1" xml:space="preserve">
<value>First message</value>
</data>
<data name="Msg2" xml:space="preserve">
<value>Second message</value>
</data>
A nyní již jen zbývá odpovědět na otázku : jakým způsobem tyto texty získáme? V odpovědi samozřejmě bude figurovat slovo ResourceManager. Pro získání těchto textů použijeme jeho metodu GetString, jak ukazuje následující příklad.
//vytvoreni instance resource manageru
ResourceManager resMan = new ResourceManager("PrikladyZive74.Messages", typeof(ResourceManagerExample).Assembly);
//ziskani ceske a americke kultury
CultureInfo czechCulture = CultureInfo.CurrentCulture;
CultureInfo usCulture = new CultureInfo("en-US");
//ziskani lokalizovanych retezcu
string messageCZ = resMan.GetString("Msg1", czechCulture);
string messageUS = resMan.GetString("Msg1", usCulture);
Console.WriteLine(messageCZ);
Console.WriteLine(messageUS);
Pro získání instance ResourceManageru, který je asociován s našimi zdrojovými soubory musíme jeho konstruktoru sdělit název zdrojů, které v sobě nese také název assembly do které je vložen a další informací , kterou jsme v příkladu podali je instance typu Assembly, ke které zdroje patří. Protože je na mém počítači nastaveno prostředí na české, použili jsme pro získání instance CultureInfo pro českou kulturu CultureInfo.CurrentCulture, ale mohli bychom samozřejmě použít i postupu pomocí konstruktoru, jak je tomu v případě americké kultury. Jelikož ResourceManager nenalezne soubor se zdroji pro českou kulturu (Messages.cs-CZ.resx) použije soubor neměnné kultury (Messages.resx).
Samozřejmě není vždy nutné, abychom metodě GetString sdělovali pro jakou kulturu chceme zdrojový text získat. Pokud tuto informaci metodě nedodáme, je použita instance CultureInfo získaná z vlastnosti Thread.CurrentUICulture. Upozorňuji, že je to vlastnosti jiná, než Thread.CurrentCulture. Příkladným rozdílem mezi významem těchto dvou vlastností je případ, kdy jsou na počítači nainstalována anglická Windows a regionální nastavení nastavena na české, což je zrovna můj příklad. V takovéto situaci je hodnota Thread.CurrentUICulture instance CultureInfo en-US a v hodnotě vlastnosti ThreadCurrentCulture je instance CultureInfo cs-CZ.
ResourceManager resMan = new ResourceManager("PrikladyZive74.Messages", typeof(ResourceManagerExample).Assembly);
Console.WriteLine(Thread.CurrentThread.CurrentCulture);
Console.WriteLine(Thread.CurrentThread.CurrentUICulture);
string message = resMan.GetString("Msg1");
Console.WriteLine(message);
Na závěr bych chtěl vyzdvihnout skvělou vlastnost Visual Studia 2005, kterou je podpora pro lokalizaci ve formě příjemného rozhraní pro definici zdrojů ve zdrojovém souboru a hlavně také schopnost automaticky generovat třídy pro přístup ke zdrojovým hodnotám typově bezpečně. To znamená, že po automatickém vygenerování zmíněné třídy je možné ke zdrojovým textům přistupovat následujícím způsobem.
string message1 = Messages.Msg1;
string message2 = Messages.Msg2;
Příklady ke článku naleznete zde.