V předchozím článku jsem se zabýval technikami používanými pro lokalizování ASP.NET 1.x aplikací. Tentokrát se podíváme, co nového nám v tomto směru nabízí ASP.NET 2.x.

Úvod do lokalizace

Ve zmiňovaném článku o lokalizaci ASP.NET 1.x jsem se již teorií lokalizování trošku zabýval, nemělo by tedy velký smysl se zde opakovat. Proto pojmu tento článek spíše prakticky, abych vám vše co nejnázorněji demonstroval.

Lokalizace v ASP.NET 2.0

Z novinek, které ASP.NET 2.0 v oblasti lokalizace přináší, je asi nejvíc na ráně nový způsob ukládání .resx souborů. Když říkám nový, neděste se, že proběhla nějaká drtivá revoluce. Nový model staví na tom předchozím, jen se snaží nějakým rozumným způsobem usnadnit správu a přístup k lokalizovaným zdrojům. V zásadě lze říci, že nové možnosti se týkají z největší části zvýšení komfortu programátora.

Určitě jste si při práci s ASP.NET 2.0 všimli, že ve struktuře webu přibylo několik nových složek. Dvě z nich souvisí právě s lokalizacemi – App_LocalResources a App_GlobalResources.

Nové složky pro lokalizaci
Nové složky pro lokalizaci

Jak už to tak bývá, z názvů složek je poměrně jasně patrné, jaké zdroje se v nich nacházejí. Složka App_GlobalResources obsahuje lokalizované texty platné pro celou aplikaci – nejde tedy o řetězce vztahující se k jedné určité stránce. Složka App_LocalResources slouží zase k opačnému účelu – pro resource soubory náležící konkrétním stránkám. Volba toho správného lokalizovaného zdroje funguje stejně jako v předchozí verzi ASP.NET, na principu takzvaného resource-fallbacku.

Deklarativní lokalizace

Další novinkou představenou v ASP.NET 2.0 je možnost přiřadit lokalizovaný text z .resx deklarativně přímo ve zdrojovém kódu stránky bez nutnosti psát nějaký C# nebo VB.NET kód. Je to velmi šikovné, protože se v kódu stránky již o texty starat nemusíte.

Existují dva způsoby deklarativní lokalizace. Jako první si ukážeme explicitní lokalizaci, kdy každé konkrétní vlastnosti, kterou hodlám lokalizovat, přiřadím hodnotu z .resx souboru. Pro názornost ukážu kousek kódu:

<asp:Button ID=“btnSearch“ runat=“server“ Text=“<%$ Resources: btnSearchText %>“ ToolTip=“<%$ Resources: btnSearchToolTip %>“ Width=“70px“ />

A ještě screenshot z resource editoru Visual WebDevelopera:

Explicitní lokalizace
Explicitní lokalizace

Z uvedené ukázky je jasně patrné, že lokalizovat aplikaci není nic složitého. Jednodušší už to asi být nemůže. Vlastně ještě o trošku může – a taky je. Uvedené deklarace je možné nadefinovat pomocí visuálního návrháře. V okně „Properties“ je na prvním řádku položka „Expressions“ a po klepnutí na klasické tlačítko se třemi tečkami se zobrazí dialogové okno:

Definice deklarací v návrháři
Definice deklarací v návrháři

Poslední kapkou pak je, že Visual Studio (nikoli ovšem Express edice) umožňuje automatické generování .resx souborů (v menu „Tools“ položka „Generate local resource“). Tento nástroj projde všechny prvky na stránce a jejich vlastnosti označené atributem „Localizable“ vytáhne do .resx souboru do App_LocalResources. Pro vývojáře ovládacích prvků to tedy znamená, že pokud chtějí, aby Visual Studio bralo vlastnost jejich ovládacího prvku jako lokalizovatelnou a počítalo s ní při generování .resx souboru, musejí použít zmíněný atribut:

[Localizable(true)]
  public string ToolTip
  {
    get { . }
    set { . }
  }

Tento způsob však vede k trochu jinému výstupu, než který jsem vám před chvílí demonstroval – bude použita implicitní deklarativní lokalizace. Implicitní deklarace spočívá v tom, že u ovládacího prvku se defaultní hodnoty nenahradí, pouze se přidá metaatribut „resourcekey“:

<asp:Button ID=“btnSearch2″ runat=“server“ Text=“Hledej“ ToolTip=“Hledání v naší databázi článků“ meta:resourcekey=“btnSearch2″ />

V .resx se potom odkazuje na prvek pomocí tohoto klíče a vlastnosti, ke které položka náleží, pomocí klasické tečkové notace:

Odkaz na lokalizovaný prvek
Odkaz na lokalizovaný prvek

Implicitní deklarace má několik výhod, například není potřeba deklarovat v kódu každou konkrétní vlastnost. Nadefinujete pouze klíč a zbytek se naváže podle hodnot zadaných v .resx. Další výhodou je to, že text na tlačítku nemá hodnotu „Expression…“, ale je tam skutečná defaultní hodnota, což usnadňuje práci se stránkou v design módu.

Pro upřesnění bych dodal, že lokalizovat je možné nejen vlastnosti ocejchované atributem „Localizable“, ale všechny vlastnosti mohou mít specifické vlastnosti na základě kulturních nastaveních. Atribut „Localizable“ pouze označuje vlastnosti, které mají být zohledněny při automatickém generování .resx. Vlastnosti s „Localizable“ nastavenou na „false“ (nebo úplně bez) si budete muset v případě potřeby dopsat do .resx ručně.

App_GlobalResources

Druhou složkou pro lokalizované zdroje je App_GlobalResources. Tato složka obsahuje zdroje platné pro celou aplikaci, nikoli jednotlivé stránky. Může jít například o chybové hlášky a podobně.

Přestože globální zdroje je možné vázat deklarativně, nejčastěji se k nim přistupuje z programového kódu. Byla tedy snaha tuto činnost vývojářům co nejvíce zpříjemnit. Byla tedy zajištěna automatická inicializace potřebných tříd (ResourceManager) včetně kešování a navíc jsou globální zdroje přísně typové, což v praxi mimo jiné znamená to, že Visual Studio a IntelliSense vám pomůže s přístupem k jednotlivým položkám.

Podívejte se na drobný příklad. Toto je soubor Interval.resx:

Interval.resx
Interval.resx (plná velikost, cca 5 kB)

A odpovídající soubor Default.aspx.cs:

Default.aspx.cs
Default.aspx.cs

Globální zdroje už v kódu získat umíme. K lokálním zdrojům je programová cesta poněkud méně příjemná, nicméně složitá rozhodně není. Slouží k tomu přetížená metoda GetLocalResourceObject(), které v parametru předáte klíč položky. Více podrobností o této metodě najdete na MSDN.

<asp:Localize .

Do široké rodiny ASP.NET prvků přibyl jeden nový, speciálně určený pro lokalizování statického textu. Je to přímý potomek prvku Literal a obohacuje jeho funkčnost o design-time editaci přímo na místě:

<asp:Localize .
<asp:Localize .

U prvku Literal jste odkázáni na editaci buď přímo v XHTML nebo v okně vlastností.

Volba správného jazyka

V oblasti rozhodování, kterou lokalizaci použít, byly také učiněny některé změny. Když to vezmu od té, podle mého názoru, nejméně použitelné možnosti, tak první změna se objevuje v direktivě Page – můžete deklaroval atribut Culture a UICulture pro každou konkrétní stránku. Někdy se to asi hodit může.

Zajímavější novinkou však je automatické zjišťování preferovaného jazyka návštěvníka. Ve web.config nyní můžete použít například následující konstrukci:

<globalization culture=“auto“ uiCulture=“auto“></globalization>

Aplikace pak za běhu zjistí, z položky „ACCEPT_LANG“ v http hlavičce, který jazyk návštěvník preferuje a nastaví kulturu aktuálního vlákna na správnou hodnotu. Tuto hodnotu posílá v požadavku prohlížeč na základě nastavení jazykových předvoleb:

Nastavení jazykových předvoleb v prohlížeči
Nastavení jazykových předvoleb v prohlížeči

Nakonec je možné ještě definovat defaultní jazyk, pokud není hlavička k dispozici:

<globalization culture=“auto:cs-CZ“ uiCulture=“auto:cs-CZ“></globalization>

Změna jazyka programově

V ASP.NET 2.0 byla do rané fáze životního cyklu stránky přidána nová virtuální metoda InitializeCulture(), která má na starosti inicializaci jazykových preferencí. Pro nastavení požadovaného jazyka tedy použijeme tuto metodu. Pro pořádek upozorňuji, že InitializeCulture() je volána ve velmi raných fázích životního cyklu stránky, ještě před vytvářením prvků na stránce – nemůžete tedy přímo přistupovat k vlastnostem prvků, jak jste v ASP.NET zvyklí (jediná možnost by byla přes Request.Form):

protected override void InitializeCulture()
{
  if (Session[„CulturePreference“] != null )
  {
    this.Culture = (string)Session[„CulturePreference“];
    this.UICulture = (string)Session[„CulturePreference“];
  }
}
protected void ddlLang_SelectedIndexChanged(object sender, EventArgs e)
{
  Session[„CulturePreference“] = ddlLang.SelectedItem.Value;
  Response.Redirect(Request.Url.AbsolutePath);
}

Abyste nemuseli přepisovat InitializeCulture() pro každou stránku zvlášť, jeví se jako rozumné vytvořit novou třídu dědící z System.Web.UI.Page a přepsat InitializeCulture() pouze v této implementaci. Všechny stránky v aplikaci by pak nedědily od System.Web.UI.Page, ale od této nově vytvořené třídy. Tuto možnost využil například Ted Pattison ve svém sloupku Basic Instincts věnovaném lokalizacím ASP.NET 2.0.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět