Mnoho zákazníků požaduje, aby jejich stránky měly několik jazykových mutací. S tímto požadavkem jsou v zásadě spojeny dva úkoly. Za prvé je nutné umožnit obsluze webu (nejčastěji tedy zadavateli zakázky), aby mohl vkládat obsah v požadovaných jazykových mutacích, a druhým úkolem, před který jsme jako programátoři postaveni, je lokalizovat aplikaci a prvky stránky, které obsluha editovat nemůže. Mám na mysli například obrázky v layoutu stránky, které obsahují text, tlačítka, hlášky, výzvy a podobně. Podívejme se tedy, jaké možnosti nám v tomto směru ASP.NET ve své první verzi nabízí.

Primitivní metody lokalizace

Lokalizovat aplikaci je možné několika více či méně elegantními způsoby. Nemyslím, že by mělo nějaký extrémní význam zabývat se zde takovými přístupy, jako je například fyzická existence stránek v různých jazykových mutacích nebo bloky kódu v následujícím tvaru:

switch(Request.QueryString[‚lang‘])
{
  case ‚cs‘:
    btnSearch.Text = ‚Hledej
    break;
  case ‚en‘:
    btnSearch.Text = ‚Search‘
    break;
  .
  .
  .

Jsou to nesporně přístupy funkční a jejich využití má v některých případech i svůj smysl. Pro začátečníka, který se právě naučil základní principy práce s ASP.NETem a chce si udělat jednoduchou stránku ve více jazykových verzích, je to každopádně stravitelnější varianta. Prvek Page_Init přidá takový switch a každému prvku na stránce přidá hodnotu podle požadovaného jazyka. Tento přístup od něj nevyžaduje studování dalších tříd – je rád, že je schopen dát dohromady fungující stránku a o tom, jak to udělat co nejelegantněji, pochopitelně zatím nepřemýšlí.

Alespoň do té doby, než bude chtít přidat další jazykovou verzi. Pro větší aplikace, nebo pro aplikace, u kterých se neví, do kolika a kterých jazyků bude přeložena, je to postup velmi neschůdný. Nehledě na to, že „překladatel“ by byl nucen procházet celý zdrojový kód a zasahovat přímo do něj.

Mnohem lepší je vyčlenit lokalizované zdroje ze zdrojových kódů (jak z VB a CS, tak i z .aspx a .ascx) a vytvořit soubory s různými jazykovými mutacemi jednotlivých hlášek, textů a podobně. V kódu pak zajistíte načtení požadované jazykové mutace. Kdybych měl stvořit jednoduchý web uvádějící tyto principy v praxi, postupoval bych nejspíš tak, že bych vytvořil složku lang, do které bych přidával podsložky jednotlivých jazykových mutací (tedy cs, en a další). V nich bych pak asi ukládal lokalizované obrázky a třeba XML soubor s frázemi.

Uložení XML souborů
Uložení XML souborů

V kódu bych se pak jen postaral o načtení textů a obrázků ze správného adresáře:

string lang = Request.QueryString[„lang“];
if (lang == string.Empty || lang == null) lang = „cs“;
XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath(string.Format(„lang/{0}/langfile.xml“, lang)));
btnSearch.ImageUrl = string.Format(„lang/{0}/img/btnSearch.gif“, lang);
btnSearch.AlternateText = doc.SelectSingleNode(„//item[name=’btnSearchAlt‘]/value“).InnerText;

Odpovídající XML dokument by pak mohl vypadat například takto:

<?xml version=“1.0″ encoding=“utf-8″ ?>
<lang>
  <item>
    <name>btnSearchAlt</name>
    <value>Hledej</value>
    <desc>Informační text pro vyhledávací tlačítko.</desc>
  </item>
</lang>

Je vidět, že tento přístup je o mnoho přístupnější pro rozšiřování základny podporovaných jazyků. Bude-li potřeba přeložit stránku do dalšího jazyka, vytvoří se složka, do které se nahrají lokalizované obrázky, a přeloží se pouze tento XML dokument. Nejsou již potřeba žádné zásahy do zdrojových kódů a není tudíž potřeba učit osobu překládající web programovat v ASP.NET. Aby navíc překladatel nemusel vytvářet XML dokumenty přímo, můžete během několika málo minut natahat na jednoduchý winform pár textových polí a „naučit“ program číst originální zdrojový dokument a požadovat přeložený text a vytvářet XML nenápadně na pozadí. Naučit pak překladatele pracovat s takovým programem není problém. Překlad stránek je pak oproti prvnímu evolučnímu stupínku, který jsem naznačoval na začátku, úplnou hračkou.

Pojďme nyní udělat další evoluční krok. Uvedený příklad zcela jistě funguje a je nesporně lepší než napevno „nabušit“ hodnoty přímo do kódu. ASP.NET nám ale nabízí ještě něco jiného. Můžeme použít třídu System.Resources.ResourceManager, která si s trochou naší pomoci umí velmi šikovně poradit s lokalizovanými zdroji. Oproti vlastním mechanismům, které jsme se doposud pokoušeli implementovat, nabízí tato cesta také další výhody.

System.Resources.ResourceManager

Takzvaný ResourceManager zajišťuje vyhledávání zdrojů v závislosti na zvolených jazykových a kulturních preferencích návštěvníka a poskytuje mechanismy pro resource fallback, pokud požadovaný lokalizovaný zdroj nemůže nalézt.

Abych mohl vysvětlit co je resource fallback, musím se nejdříve zmínit o tom, jakým způsobem jsou nazývány lokalizované zdroje. Identifikátor kulturní a jazykové verze se skládá ze dvou částí oddělených pomlčkou – první část je záladní jazykový identifikátor (malými písmeny), druhá část představuje region nebo zemi. Jako příklad uvedu angličtinu a němčinu. Anglicky se mluví mimo jiné v USA a Kanadě. Německy v Německu a například v Rakousku. Identifikátory pak vypadají takto:

  • en-US pro americkou angličtinu
  • en-CA pro kanadskou angličtinu
  • de-DE pro německou němčinu
  • de-AT pro rakouskou němčinu

Kompletní seznam identifikátorů najdete v .NET Framework Class Library v detailech třídy CultureInfo.

Při vytváření lokalizovaných zdrojů se postupuje v jakési stromové struktuře. Vytvoříte základní .resx soubor s texty (default), pak přidáte například základní anglickou verzi (strings.en.resx), pak třeba americkou angličtinu (strings.en-US.resx) a tak můžete pokračovat, jak dlouho bude potřeba. Framework .NET při požadavku na stránku zjistí, kterou mutaci má použít, a pokud nenajde konkrétní požadovanou mutaci, hledá v obecnějších mutacích, případně použije základní mutaci.

Uložení jazykových verzí
Uložení jazykových verzí

Pokud tedy přijde požadavek na kanadskou angličtinu, ale my jsme přidali pouze americkou variantu, .NET poté, co strings.en-CA.resx nenajde, se pokusí najít alespoň strings.en.resx. Kdyby nenašel ani tu, použije defaultní strings.resx.

Když máme připravené .resx soubory, můžeme s nimi začít pracovat. Nejdříve ve web.config specifikujeme defaultní jazykovou mutaci:

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

V default.aspx.cs potom pro získání lokalizovaných řetězců použijeme tento kód:

private void Page_Load( object sender, System.EventArgs e)
{
  if (!Page.IsPostBack)
    GetLocalization();
}
void GetLocalization()
{
  ResourceManager rm = new ResourceManager(„Localization_1x.strings“, Assembly.GetExecutingAssembly());
  btnSearch.AlternateText = rm.GetString(„btnSearchAlt“);
  btnSearch.ImageUrl = string .Format(„images/{0}/btnSearch.gif“, rm.GetString(„imagesFolder“));
}

Na předposledním řádku je „objížďka“ uložení obrázků do .resx – uložil jsem tam jen řetězec s názvem složky, ve které jsou lokalizované obrázky uloženy. Z uvedeného zdrojového kódu stojí ještě za vysvětlení snad jen konstruktor třídy ResourceManager. První z parametrů, které přebírá, je základ názvu zdroje (se jmenným prostorem). Druhý argument je sestavení, ze kterého se mají zdroje načíst.

Nakonec můžete na stránku přidat DropDownList s výběrem podporovaných jazykových mutací a po změně požadované mutace návštěvníkem změnit jazykovou verzi pro aktuální vlákno:

private void DropDownList1_SelectedIndexChanged( object sender, System.EventArgs e)
{
  CultureInfo ci = new CultureInfo(DropDownList1.SelectedValue);
  Thread.CurrentThread.CurrentUICulture = ci;
  Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
  GetLocalization();
}

Třída ResourceManager hledá lokalizované zdroje na základě aktuálních kulturních nastavení daného vlákna (Thread). Z toho vyplývá mimo jiné také to, že se změna kultury projeví i v ostatních kulturních zvyklostech (formát data, měny a podobně).

Jak vidíte, lokalizace ASP.NET ve verzi 1.x není tak složitá záležitost, jak by se mohlo nezasvěcenému zdát, do skutečně komfortního řešení má však ještě daleko. Microsoft v tomto směru udělal velký kus práce ve druhé verzi ASP.NET, proto si nové možnosti zaslouží samostatný článek.

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