Předesílám, že se v tomto článku budu zabývat výhradně explicitní lokalizací webových projektů, tedy přímým odkazováním na resources pomocí <%$ Resources: … %>, popř. metod GetLocalResourceObject() nebo GetGlobalResourceObject(). Osobně mám tuto metodu radši, protože mám přesně pod explicitní kontrolou každý bajt, který chci lokalizovat.
Co všechno je tedy pro úspěšnou implementaci lokalizace udělat?
- Vytvořit resources – .resx soubory s lokalizovanými texty, popř. i jinými objekty.
- Přidat do stránek/kódu odkazy na resources tam, kde chceme lokalizaci.
- Vytvoření lokalizovaných verzí .resx souborů.
- Zajistit nastavení a případně i přepínání CurrentUICulture, popř. i CurrentCulture.
Vytvoření primárních resources – .resx souborů
Předpokládám běžnou práci ve Visual Studiu nebo Web Developer Express, nebudu se tedy zabývat takovými věcmi jako je resgen. VS/WDE přímo podporují přehledné vytváření a editaci .resx souborů.
Důležité je, že rozlišujeme dva typy resources:
- globální resources
- data v nich jsou přístupná ze všech míst webové aplikace,
- jsou umístěny ve složce ~/App_GlobalResources
- pojmenovávají se MojeJmeno.resx, např. ~/App_GlobalResources/Glossary.resx
- může jich být libovolné množství, nicméně je rozumné používat jen několik logických celků, např. Navigation.resx, Glossary.resx, atp.
- lokální resources
- data v nich jsou přímo přístupná jen ze stránky/controlu/…, které se týkají,
- umísťují se do složky App_LocalResources, která je podsložkou sloužky, kde je stránka/control,
- pojmenovávají se MojeStranka.aspx.resx, popř. MujControl.ascx.resx, atp.; například pro stránku ~/admin/stranka.aspx bude lokální resource file ~/admin/App_LocalResources/stranka.aspx.resx
Resource-file vytvoříme snadno, ve VS/WDE prostě vytvoříme příslušnou složku a do ní přes Add přidáme položku typu Resource. Resource soubor .resx je ve skutečnosti XML souborem, který je přibuildován do assembly, pozadí však nechme stranou.
Po otevření .resx souboru se nám standardně zobrazí grid pro editaci/přidávání objektů typu string. Resource nemusí být jen typu string, ale například i obrázky, ikony, zvuky, soubory nebo obecné objekty, nicméně pro běžnou lokalizaci si naprosto vystačíme s typem String, protože ve webových aplikací i u obrázků a ostatních objektů obvykle „lokalizujeme“ jen odkaz na příslušný soubor na disku (ImageUrl = „vlajka_cz.gif“) spíše než samotné soubory.
V praxi .resx záznamy v .resx souborech vznikají současně s prvním odkazem na ně, přistupme tedy k použití ve stránce/kódu.
Použití resources ve stránce/kódu
Explicitní použití resource ve stránce je primitivní, v tagu controlu, všude, kde lze přiřadit string, stačí zapsat odkaz
- <%$ Resources: ResourceName %> pro lokální resources
- <%$ Resources: FileNameBezPripony, ResourceName %> pro globální resources
- Například tedy
<asp:Localize Text="<%$ Resources: UvodniText %>" ... runat="server" /> <asp:Label Text="<%$ Resources: Glossary, Telefon %>" ... runat="server" /> <asp:Image ImageUrl="<%$ Resources: Images, Vlajka %>" ... runat="server" />
Pozor, že nejde použít <%$ … %> mimo serverové tagy, pro takový účel slouží právě nový control Localize, který není ničím jiným než Literalem s lepší design-time podporou.
Pro použití resources v kódu je několik možností
- Globální resources se zrcadlí do strong-typed properties ve třídách namespace Resources. Například Resources.Glossary.Telefon nebo Resources.Images.Vlajka. Tento wrapper generuje ASP.NET v rámci web-site a je tak přístupný pouze v rámci web-site a nelze na něj odkazovat například z jiných assembly.
- Pro obecný přístup ke globálním a lokálním resources slouží metody třídy HttpContext
public static object GetGlobalResourceObject(string classKey, string resourceKey); public static object GetGlobalResourceObject(string classKey, string resourceKey, CultureInfo culture); public static object GetLocalResourceObject(string virtualPath, string resourceKey); public static object GetLocalResourceObject(string virtualPath, string resourceKey, CultureInfo culture); // typicky třeba maily mail.Subject = (string)HttpContext.GetGlobalResourceObject("MailTemplates", "MyMailSubject"); // nebo ve stránce MessageLb.Text = (string)GetLocalResourceObject("Neuspech");
Metody jsou mimo třídy HttpContext přístupné i v třídě TemplateControl a jejích potomcích, tedy např. i Page.
Pro local resources bohužel ASP.NET žádný wrapper negeneruje a z kódu na ně přistupujeme rovnou přes GetLocalResourceObject().Pro přístup k resources můžeme použít samozřejmě i standardní techniky přes ResourceManager atp., ale ve webových aplikacích to není běžné.
Co jsme dosud udělali nám samo o sobě už bude fungovat a zobrazovat, nicméně stále jsme jen u jednoho primárního jazyka. Pro přidání dalších jazyků a případně i možnost jejich přepínání musíme provést ještě dva kroky.
Vytvoření resources (.resx souborů) pro další jazyky
Vytvoření resources pro další jazyky je velmi primitivní záležitostí. Stačí vzít .resx soubor primárního jazyka a vytvořit jeho kopii se jménem Soubor.jazyk.resx, například tedy ze souboru ~/App_GlobalResources/Glossary.resx vytvoříme kopii ~/App_GlobalResources/Glossary.en.resx (pro angličtinu), nebo ze souboru ~/App_LocalResources/Login.aspx.resx vytvoříme kopii ~/App_LocalResources/Login.aspx.de.resx (pro němčinu).
…a nyní nezbývá než hodnoty v novém souboru přeložit. Pokud navíc nainstalujeme překladateli WDE a naučíme ho editovat přímo .resx soubory, krása. Existují dokonce mnohé šikovné utilitky pro práci s .resx soubory, aby nebylo nutné instalovat něco tak velkého jako WDE:
- PeopleWords ResxEditor – primitivní editor .resx, zahrnuje i počítadlo slov,
- PeopleWords Resx2Word, Word2Resx – konvertor .resx <-> .doc,
Volba jazyka a přepínání jazyků
Jakou jazykovou verzi odešle ASP.NET na klienta se řídí hodnotou Thread.CurrentThread.CurrentUICulture, zároveň se doporučuje volit i Thread.CurrentThread.CurrentCulture, protože tím se řídí formát čísel, času, dat, měny, atp.
Automatická volba dle požadavku prohlížeče
První, co můžeme chtít, je, aby ASP.NET použilo jazykový požadavek prohlížeče, který ho posílá v HTTP requestu. Česká prostředí tedy rovnou uvidí české stránky, anglická anglické, atp. a nic nemusíme ani přepínat. Prohlížeče mají jazyk přednastaven od instalace a například v Internet Exploreru je možné ho měnit přes Nástroje ~ Možnosti Internetu ~ Obecné ~ Jazyky.
Jediné, co pro implementaci tohoto postupu potřebujeme udělat, je ve web.config nastavit hodnoty elementu <globalization>, atributy uiCulture, popř. culture:
<globalization requestEncoding="utf-8" responseEncoding="utf-8" culture="auto:cs-CZ" uiCulture="auto:cs-CZ" />
Volba auto říká, že se má použít požadavek prohlížeče, za dvojtečku lze volitelně umístit primární nastavení, které se má použít, pokud není požadavek prohlížeče realizovatelný, resp. pokud prohlížeč požadavek neudal. Pro úplnost uvádím i volby encoding, ty však nejsou předmětem tohoto článku.
Explicitní volba jazyka uživatelem
Dále můžeme chtít, aby si uživatel sám mohl přepnou jazyk, nezávisle na požadavku prohlížeče. Je jasné, že budeme muset přepnout Thread.CurrentThread.CurrentUICulture popř. i CurrentCulture. Zvolený jazyk si budeme ukládat třeba do cookie (použitelná je i session) a je potřeba ho přepnout pro každý request, a to co nejdříve. Buď můžeme udělat override prázdné virtuální metody Page.InitializeCulture(), pokud máme připravenou bázovou třídu pro všechny stránky, anebo změnu provádět rovnou v události Application_BeginRequest, v Global.asax.cs:
private void Application_BeginRequest() { HttpCookie cookie = Request.Cookies["Culture"]; if (cookie != null) { CultureInfo culture = new CultureInfo(cookie.Value); Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = culture; } }
a pro vlastní přepínání si uděláme třeba stránku Language.aspx:
protected override void OnInit(EventArgs e) { string culture = Request.QueryString["culture"]; if ((culture != null) && (culture.Length > 0)) { HttpCookie cookie = new HttpCookie("Culture"); cookie.Value = culture; Response.Cookies.Add(cookie); } string returnUrl = Request.QueryString["returnUrl"]; if ((returnUrl != null) && (returnUrl.Length > 0)) { Response.Redirect(returnUrl); } else { Response.Redirect("~/"); } base.OnInit(e); }
odkaz pro přepnutí stránky, pak může být třeba
<a href="language.aspx?culture=en-US">en</a>
nebo s ReturnUrl stejně jako login-page.
Záznam z cvičné přednášky na toto téma
V příloze najdete PowerPoint slides: Lokalizace webových aplikací.ppt