Včera jsem potřeboval zlokalizovat administrační sekci svého rozpracovaného CMSka. Vývojáři ASP.NET na lokalizaci pochopitelně pamatovali, ale má to několik háčků. Pojďme se na ně podívat.
Způsoby lokalizace
V ASP.NET máme v zásadě dva způsoby, jak stránky lokalizovat – buď explicitně, anebo implicitně.
Explicitní lokalizace, kterou používá většina lidí, spočívá v tom, že na místa, kde chcete lokalizované hlášky, dáte tzv. expression buildery. V praxi to vypadá třeba takto:
<asp:Button ID="TestButton" runat="server"
Text="<%$ Resources: Test, TestButton_Text %>" />
Použitím toho <%$ %> zvířátka jsme řekli, že hodnota vlastnosti Text se bude tahat z resource Test.resx souboru v adresáři App_GlobalResources a použije se hodnota pod klíčem TestButton_Text. Pokud je stránek hodně, hodí se použití lokálních resources, kde každá stránka má svůj resource soubor ve složce App_LocalResources, v tom případě se za Resources: uvádí jen název položky. Název souboru je pak stejný jako název stránky, akorát je za ním ještě .resx. Takže třeba Test.aspx.resx. Lokální i globální resources můžeme samozřejmě kombinovat, takže věci specifické pro stránku dáme do lokálních, zatímco věci, které se používají globálně, například text Storno na tlačítkách, se hodí to globálních.
Explicitní lokalizace má své výhody, především na první pohled vidíte, co se bude lokalizovat. Na druhou stranu pokud je stránka složitější, začne se okamžitě hemžit spoustou zvířátek, což se mi nelíbí. Zvlášť když u jedné komponenty lokalizujete 5 vlastností, za chvíli je v tom pořádný zmatek. Tento způsob lokalizace je ale používanější, neznám moc lidí, kteří by používali způsob druhý.
Implicitní lokalizace, kterou mám raději, funguje trochu jinak. Každé komponentě (nebo čemukoliv, co má runat=”server”), na níž se bude něco lokalizovat, hodíme atribut meta:ResourceKey=”klíč”, klíč si zvolíme, měl by být unikátní v rámci stránky.
Jakmile máme hotovo, v ideálním případě se přepneme do design režimu stránky a v menu Tools klikneme na Generate Local Resources, čímž se pro stránku vytvoří v adresáři App_LocalResources příslušný RESX soubor. Projdou se všechny komponenty označené atributem meta:ResourceKey a pro každou se přidá několik položek klíč.vlastnost, a to pro každou vlastnost, která se má lokalizovat. To se pozná podle toho, jestli vývojáři komponenty příslušnou vlastnost označili atributem Localizable(true).
Výhodou tohoto způsobu je, že stránka není (dle mého) tak zaneřáděná a původní texty v ní zůstanou, nenahrazujete je zvířátky. Když funguje designer, je to velmi rychlý způsob, stránku jste schopni zlokalizovat během pár minut.
Teď to začne…
Svět ale není vždy ideální a mám asi tu smůlu, že toho chci moc. Vzhledem k tomu, že nové Visual Studio je zabugované a moje komponenty z CMSka si s design režimem moc nerozumí (nikdy jsem je na to netestoval, protože designer nepoužívám). Nemohl jsem použít krásnou fíčuru Generate Local Resource, protože jde vyvolat jen z design režimu. Tak jsem si ji včera večer napsal sám.
Utilitka AspNetLocalizer
Předesílám, že má mnoho nedostatků, protože vznikla asi za 2 hodiny. Lokalizaci jsem totiž potřeboval poněkud rychle, nenapadlo mě, že s tím bude takového sena. Základním jádrem je jednoduchý parser ASPX markupu, který soubor projde a pomocí jednoduchého dvoustavového automatu (jsem mimo tag, jsem uvnitř tagu) stránku projde a vrátí sekvenci tokenů – token je buď text anebo tag, v němž máte název a jeho naparsované atributy.
Další část pak už jen projde tagy, nalezne v nich vlastnosti, které se asi budou lokalizovat (jejichž název končí na Title, Text, Message a pár dalších slov). Chtělo by to lepší predikci, stačilo by samozřejmě načíst konfiguraci aplikace a pomocí Reflection projít všechny lokalizovatelné vlastnosti, ale to moje utilitka nedělá, nebyl čas. Vezme všechny atributy, které jsou komponentě nastavené, a z nich vybere dle názvu ty, jež se pravděpodobně budou lokalizovat.
Jak to použít?
Několik snadných kroků:
- Vezmete existující stránku a všemu, co hodláte lokalizovat, nastavíte meta:ResourceKey.
- Texty, které jsou ve stránce napsané natvrdo, je třeba obalit komponentou Localize, to se dá udělat velmi rychle pomocí makra – označíte text a makro jej obalí komponentou, pak jen stačí doplnit resource key.
- Zavoláte command-line aplikaci AspNetLocalizer a jako jediný argument jí dáte cestu k ASPX, ASCX nebo MASTER souboru. Aplikace sama vytvoří složku App_LocalResources, pokud je to třeba, a vytvoří v ní resource soubor. Ten pak stačí jen přidat do projektu, zkopírovat, zlokalizovat a je to.
Pokud bude zájem, dám to na CodePlex, zatím není čas, takže to uvolním jen ke stažení. Berte to jako příklad, jak se nemají psát aplikace, zvlášť generování RESX souboru je velmi pozoruhodné, smím-li to tak říct.