Tag Archives: Lokalizace

ResourceProvider – resources z databáze nebo odjinud

ASP.NET ve výchozí konfiguraci bere při lokalizaci resources z .resx souborů umístěch ve Vaší webové aplikaci. Globální resources ze složky ~/App_GlobalResources/, lokální resources z podsložek ./App_LocalResources/ u jednotlivých stránek/controlů. Na resources pak lze přistupovat použitím syntaxe <%$ Resources: … %> z markup kódu, popř. metodami GetGlobalResourceObject(), resp. GetLocalResourceObject(). Ke globálním resources pak ASP.NET ještě generuje do namespace Resources třídy pro jednotlivé .resx soubory s properties odpovídajících jednotlivým klíčům (záznamům v .resx).

Pokud namísto .resx souborů chceme resources brát z jiného datového zdroje, např. databáze, je to jednoduché. ASP.NET pro tuto situaci používá provider-model a celá akce se v zásadě odehrává v následujících třech až čtyřech krocích:

  1. vytvoření vlastního resource-providera, tj. třídy implementující rozhraní IResourceProvider a zejména jeho metodu GetObject(),
  2. vytvoření ResourceProviderFactory třídy, na kterou se deleguje rozhodování ASP.NET o tom, kterého resource-providera má pro jednotlivé situace použít, v našem případě našeho resource-providera z bodu 1,
  3. nasměrování konfigurace ASP.NET na používání připravené ResourceProviderFactory třídy (úprava web.configu),
  4. pokud chceme zachovat, nebo alespoň simulovat generovaný namespace Resources, musíme ho buď také generovat (nepravděpodobné), nebo si od NET4 výše můžeme krásně vypomoci dynamickým objektem.

Pojďme se podívat na jednotlivé kroky podrobněji:

1. Vytvoření vlastního resource-providera

Resource-provider, je odpovědný za vlastní implementaci vyhodnocování resource-výrazu, tj. v něm ztvárním náš kód, který bude hodnoty resources číst z databáze, XML, nebo jiného zdroje, dle našeho uvážení.

Resource-provider je třída, která implementuje rozhraní IResourceProvider. To má zejména metodu object GetObject(string resourceKey, CultureInfo culture). Vstupem zde není resourceClass, který jak později uvidíme naopak dostává již ResourceProviderFactory a pro každou resourceClass je tak typicky vytvářena samostatná instance resource-providera odbavující danou resourceClass. IResourceProvider předepisuje ještě implementaci property IResourceReader ResourceReader, nicméně v běžných scénářích se bez implementaci readeru obejdete.

Primitivní implementace resource-providera pak může vypadat například takto:

namespace MyResourceProviders
{
    /// <summary>
    /// Resource-provider pro lokální i globální resources z DB.
    /// </summary>
    public class MyResourceProvider: IResourceProvider
    {
        private string _classKey;

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the  class.
        /// </summary>
        /// identifikátor resources třídy (cesta a název souboru pro lokální, název třídy pro globální)
        public XeroxWebToolResourceProvider(string classKey)
        {
            _classKey = classKey;
        }
        #endregion

        #region GetObject
        /// <summary>
        /// Returns a resource object for the key and culture.
        /// </summary>
        /// The key identifying a particular resource.
        /// The culture identifying a localized value for the resource.
        /// 
        /// An  that contains the resource value for the  and .
        /// 
        public object GetObject(string resourceKey, CultureInfo culture)
        {
            Debug.Assert(!String.IsNullOrEmpty(resourceKey)); // contract zajišťuje již interface

            if (culture == null)
            {
                culture = CultureInfo.CurrentUICulture;
            }

            return ResourceHelper.GetString(_classKey, resourceKey, culture);
        }
        #endregion

        #region ResourceReader
        /// <summary>
        /// Gets an object to read resource values from a source.
        /// </summary>
        /// 
        /// 
        /// The  associated with the current resource provider.
        /// 
        public IResourceReader ResourceReader
        {
            get
            {
                throw new NotImplementedException();
            }
        }
        #endregion
    }
}

mplementace třídy ResourceHelper, resp. její metody GetString(resourceClass, resourceKey, culture) již je samozřejmě na Vaší fantazii. Nemusíte samozřejmě tuto funkčnost ani vytahovat do samostatné třídy, ale můžete ji implementovat in-line.

3. Vytvoření ResourceProviderFactory

Abychom našeho připraveného resource-providera dostali do hry, musíme připravit ještě jednoduchou třídu odvozenou od předka ResourceProviderFactory, která jak už název napovídá bude odpovědná za volbu a vytváření instancí příslušných tříd resource-providerů. Teoreticky můžeme mít totiž pro různé množiny resources mít úplně jiné resource-providery, část nechat v .resx souborech, část mít v DB, atp. Rozhodujícím faktorem pro volbu resource-providera je zde informace o tom, jestli se jedná o globální nebo lokální resources a dále v případě globálních resourceClass a v případě lokálních virtualPath odkazujícího prvku.

ResourceProviderFacotry má dvě abstraktní metody, které musíme implementovat – CreateGlobalResourceProvider() a CreateLocalResourceProvider(). Primitivní implementace odkazující se na našeho resource-providera z předchozího kroku může vypadat třeba takto:

namespace Havit.XeroxWebTool.WebBase.ResourceProviders
{
    /// <summary>
    /// Resource-factory vracející InformResourceProvider pro lokální i globální resources.
    /// </summary>
    public class MyResourceProviderFactory : ResourceProviderFactory
    {
        #region CreateGlobalResourceProvider
        /// <summary>
        /// When overridden in a derived class, creates a global resource provider.
        /// </summary>
        /// The name of the resource class.
        /// 
        /// An .
        /// 
        public override IResourceProvider CreateGlobalResourceProvider(string classKey)
        {
            return new MyResourceProvider(classKey);
        }
        #endregion

        #region CreateLocalResourceProvider
        /// <summary>
        /// When overridden in a derived class, creates a local resource provider.
        /// </summary>
        /// The path to a resource file.
        /// 
        /// An .
        /// 
        public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
        {
            string classKey = virtualPath;
            if (!string.IsNullOrEmpty(virtualPath))
            {
                classKey = virtualPath.Remove(0, 1); // odstranění počátečního lomítka
            }
            return new MyResourceProvider(classKey);

        }
        #endregion
    }
}

3. Konfigurace použití ResourceProviderFactory (web.config)

Posledním nutným krokem ke zprovoznění vlastní logiky resources je sdělit ASP.NET prostřednictvím web.configu, že si přejem používat naší ResourceProviderFactory. Děje se tak prostřednictvím elementu globalization, atributu resourceProviderFatoryType:


Element globalization patří do configuration/system.web/globalization, pokud je Vaše aplikace lokalizována, již ho tam pravděpodobně budete mít.

4. Náhrada za namespace Resources (volitelně)

Pokud přesouváte resources projektu do DB dodatečně, je možné, že v kódu již na mnoha místech používáte strong-type odkaz na prvky namespace Resources, kterou ASP.NET generuje ke globálním resources. V takovém případě se budete potřebovat s tímto kódem nějak vypořádat, protože nebudete-li používat .resx soubory, namespace Resources se Vám nebude generovat.

Jednou z možných cest, jak toto vyřešit, je použití dynamického objektu NET4.

V zásadě jde o to, že místo namespace Resources vytvoříte třídu odvozenou od předka DynamicObject a v příslušných předcích (PageBase, UserControlBase) vytvoříte dynamic property Resources. Ve zkratce může implementace třídy vypadat takto:

namespace MyResources
{
    /// <summary>
    /// Pomocná třída pro přístup k Resources z kódu.
    /// </summary>
    public class Resources: DynamicObject
    {
        #region Constructors
        public Resources()
            : base()
        {
        }
        private Resources(string key):base()
        {
            this.ResourceClass = key;
        }
        #endregion

        #region ResourceClass
        /// <summary>
        /// ResourceClass pro načítaný resource
        /// </summary>
        private string ResourceClass { get; set; }
        #endregion

        #region TryGetMember (override)
        /// <summary>
        /// Vrátí požadovanou property
        /// </summary>
        /// 
        /// 
        /// 
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            if (this.ResourceClass == null)
            {
                result = new Resources(binder.Name);
            }
            else
            {
                result = ResourceHelper.GetString(this.ResourceClass, binder.Name, Thread.CurrentThread.CurrentUICulture);
            }
            return true;
        }
        #endregion
    }
}

Ve třídě PageBase/UserControlBase potom zadefinujete property takto:

protected readonly dynamic Resources = new Resources();

(Předka všech stránek/usercontrolů, pokud to zatím nepoužíváte, dostanete do hry z web.configu pomocí atributů pageBaseType a userControlBaseType na elementu configuration/system.web/pages. Budete se na něj však muset odkazovat i z code-behind tříd.)

Viz též

  • Extending the ASP.NET 2.0 Resource-Provider Model [MSDN, Michèle Leroux Bustamante]
  • Český stemmer do MSSQL 2008 R2 x64

    Dneska se budu s laskavým svolením chlubit cizím peřím, uvádím postup (mnou neověřený, ale z důvěryhodného zdroje – od Roberta Kindla z firmy EXEC), jak do MS SQL 2008 R2 x64 dostat český stemmer pro fultextové vyhledávání:

    ****

    musis nainstalovat Microsoft Search Server 2010 Express – to by melo byt zdarma a cesky stemmer to obsahuje

    bohuzel je ale ten stemmer dostupny jen v tom Sharepointu (neregistruje se to do systemu)

    takze jsem vlasnimi silami vyhackoval toto:
    Windows Registry Editor Version 5.00
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10_50.SQL2008R2\MSSearch\Language\ces]
    „TsaurusFile“=“tsces.xml“
    „Locale“=dword:00000405
    „WBreakerClass“=“{468bfc77-3876-4a47-a6ff-f5f6e8ea7968}“
    „StemmerClass“=“{f51b7203-9bf9-4c39-b655-18fad8fa8a9a}“
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10_50.SQL2008R2\MSSearch\CLSID\{f51b7203-9bf9-4c39-b655-18fad8fa8a9a}]
    @=“c:\\Program Files\\Microsoft Office Servers\\14.0\\Bin\\mswb7.dll“
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10_50.SQL2008R2\MSSearch\CLSID\{468bfc77-3876-4a47-a6ff-f5f6e8ea7968}]
    @=“c:\\Program Files\\Microsoft Office Servers\\14.0\\Bin\\mswb7.dll“
    toto konkretne zaregistruje ten cesky stemmer z Microsoft Search Server 2010 Express do MSSQL 2008 R2 (pro MSSQL 2008 a MSSQL 2005 by to zrejme slo podobne)

    pak uz staci jen
    exec sp_fulltext_service ‚update_languages‘;
    exec sp_fulltext_service ‚restart_all_fdhosts‘;

    overeni instalace (vypise se stemmer) s lcid 1029
    exec sp_help_fulltext_system_components wordbreaker
    SELECT * FROM sys.fulltext_languages order by lcid

    sklonovat to umi:
    select * from sys.dm_fts_parser(‚FORMSOF(INFLECTIONAL, květina)‘, 1029, 0, 0)

    ++++

    jeste je asi treba tohle:
    copy „c:\Program Files\Microsoft Office Servers\14.0\Data\Config\tsces.xml“ „c:\MSSQL2008R2\FTData“ (do slozky k ostatnim ts???.xml)
    Noise Words, ktere jsou v Sharepointu 2010 ulozeny v c:\Program Files\Microsoft Office Servers\14.0\Data\Config\noiseces.txt by se na MSSQL 2008 R2 mely konfigurovat pres CREATE FULLTEXT STOPLIST

    web.sitemap – lokalizace a autorizace

    Po chvilce bojů a objevení několika bugů se mi nakonec podařilo rozchodit lokalizaci a autorizaci v site-mapách ASP.NET, konkrétně při použití defaultního XmlSiteMapProvider a web.sitemap souborů (částečně to lze ovšem aplikovat i na jiné providery).

    Lokalizace website.map

    Defaultní XmlSiteMapProvider přímo podporuje lokalizaci web.sitemap souborů, stačí rootovému elementu nastavit atribut enableLocalization=“true“ a dále se pak na resources můžeme explicitně odkazovat obdobně jako v .aspx souborech, přes výraz $resources:ClassName,KeyName,DefaultValue

    <siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" enableLocalization="true">
        <siteMapNode title="$resources: Navigation, UvodniStranka" url="~/default.aspx">
        ...
    </siteMap>
    

    Výše uvedený příklad by se opíral o soubor ~/App_GlobalResources/Navigation.resx. Jedná se o explicitní způsob lokalizace, existuje ještě implicitní metodika s využitím atributu resourceKey, nicméně implicitní lokalizace jako taková mě neoslovuje, takže ani tentokrát nebudu podorbněji rozvádět.

    Pozor také, že mezi dolarem ($) a klíčovým slovem resources nesmí být mezerník. Lokalizovat lze výše uvedeným způsobem atributy title a description, bohužel ne url. Atribut url takto lokalizovat bohužel nelze, ač je to v některých dokumentacích uvedeno.

    Pokud bychom chtěli lokalizovat i URL, můžeme to zařídit například tak, že nakonfigurujeme přes web.config dva nezávislé SiteMapProvidery, kde každý bude ukazovat na jiný .sitemap soubor:

    <siteMap defaultProvider="CzXmlSiteMapProvider" enabled="true">
      <providers>
        <add name="CzXmlSiteMapProvider"
          description="Provider pro cestinu."
          type="System.Web.XmlSiteMapProvider"
          siteMapFile="WebCz.sitemap"
          securityTrimmingEnabled="true" />
        <add name="EnXmlSiteMapProvider"
          description="Provider pro cestinu."
          type="System.Web.XmlSiteMapProvider"
          siteMapFile="WebEn.sitemap"
          securityTrimmingEnabled="true" />
      </providers>
    </siteMap>
    

    …následně bychom podle aktuálního jazyka museli controlu SiteMapDataSource explicitně nastavovat příslušný SiteMapProvider (nastavit property SiteMapDataSource.SiteMapProvider, typ string). Deklaratorně například fíglem:

    <asp:SiteMapDataSource
        ID="SiteMapDS"
        SiteMapProvider="<%$ Resources: Navigation, SiteMapProvider %>"
        runat="server" 
    />
    

    Viz též obecná lokalizace webových aplikací.

    Autorizace – skrývání uživateli nepřístupných položek

    XmlSiteMapProvider, přesněji řečeno už bázová třída SiteMapProvider podporuje i authorizaci, resp. skrývání uživateli nedostupných položek. Tato funkčnost je řízena přes property SiteMapProvider.SecurityTrimmingEnabled, která se však nenastavuje programově (má jen get), nýbrž přes konfiguraci provideru z web.config souboru, atributem securityTrimmingEnabled:

    <siteMap defaultProvider="MyXmlSiteMapProvider" enabled="true">
        <providers>
            <clear/>
            <add name="MyXmlSiteMapProvider"
             type="System.Web.XmlSiteMapProvider"
             siteMapFile="~/Web.sitemap"
             securityTrimmingEnabled="true" />
        </providers>
    </siteMap>
    

    Pokud nastavíme tento atribut, pravděpodobně nám začně funkčnost ihned fungovat. SiteMapProvider si sám hlídá přístupová práva uživatele nastavená přes sekci<authorization /> web.configu a uživateli nepřístupné položky skrývá.

    Ovšem pozor!!! Implementace tohoto hlídání není úplně korektní a má problém s authorization pravidly web.config souborů, která se přes <location /> odkazují na podsložky. Chceme-li tedy tuto funkčnost SiteMapProvideru využít, můžeme ve web.config souborech definovat <location /> pravidla pouze pro jednotlivé soubory v příslušné složce a přístupová práva složek jako takových musíme řídit přes samostatné web.config soubory v těchto složkách!!!

    Podle dokumentace a schématu .sitemap souboru má existovat u elementu <siteMapNode /> i atribut securityTrimmingEnabled, jakékoliv použití tohoto atributu však vyhazuje výjimku

    ConfigurationErrorsException: Unrecognized attribute ‚securityTrimmingEnabled‘. Note that attribute names are case-sensitive.

    Když jsem bádal v kódu XmlSiteMapProvideru, tak tam je tento atribut přímo zakázaný a zřejmě se jedná o nějaký přežitek z beta-verzí.

    Element <siteMapNode /> má ještě atribut roles=“…“, kam lze explicitně nastavit, jakým uživatelským rolím se má node zobrazovat. Pokud však máme dobře nastavené samotné <authorization />, pak to mnohdy nepotřebujeme. Pokud ho přesto nastavíme, pak se vyhodnocuje MÍSTO standardních přístupových práv, nikoliv jako průnik.

    Atribut roles=“*“ (nebo podobně) se nám bude hodit u externích odkazů, jejichž autorizaci nelze jinak vyhodnotit a bez explicitní volby roles se nám odkaz vůbec nezobrazí.

    Dále je potřeba si uvědomit, že SiteMapProvider vyhodnocuje přístupová práva od rootu sitemap dolů a pokud nevyhoví nadřazená položka, ustřelí se celý podstrom. Například je tedy problematické bez dalšího použít prázdný atribut url=““, protože jeho přístupová práva není jak vyhodnotit a příslušný podstrom se tak nezobrazí – naopak je však možné do url dávat cestu k neexistujícím stránkám, vyhodnocení se provede, jako by existovali. Pokud potřebujeme udělat siteNode bez odkazu, pak musíme použít atribut roles=“…“, aby bylo přístupová práva jak vyhodnotit.

    Související články

    Lokalizace snadno a rychle – explicitní lokalizace

    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?

    1. Vytvořit resources – .resx soubory s lokalizovanými texty, popř. i jinými objekty.
    2. Přidat do stránek/kódu odkazy na resources tam, kde chceme lokalizaci.
    3. Vytvoření lokalizovaných verzí .resx souborů.
    4. 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:

    1. 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.
    2. lokální resources
      1. data v nich jsou přímo přístupná jen ze stránky/controlu/…, které se týkají,
      2. umísťují se do složky App_LocalResources, která je podsložkou sloužky, kde je stránka/control,
      3. 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
      &lt;asp:Localize Text="&lt;%$ Resources: UvodniText %&gt;" ... runat="server" /&gt;
      &lt;asp:Label Text="&lt;%$ Resources: Glossary, Telefon %&gt;" ... runat="server" /&gt;
      &lt;asp:Image ImageUrl="&lt;%$ Resources: Images, Vlajka %&gt;" ... runat="server" /&gt;
      

      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í

      1. 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.
      2. 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:

      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:

      &lt;globalization
         requestEncoding="utf-8"
         responseEncoding="utf-8"
         culture="auto:cs-CZ"
         uiCulture="auto:cs-CZ"
      /&gt;
      

      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) &amp;&amp; (culture.Length &gt; 0))
         {
            HttpCookie cookie = new HttpCookie("Culture");
            cookie.Value = culture;
            Response.Cookies.Add(cookie);
         }
         string returnUrl = Request.QueryString["returnUrl"];
         if ((returnUrl != null) &amp;&amp; (returnUrl.Length &gt; 0))
         {
            Response.Redirect(returnUrl);
         }
         else
         {
            Response.Redirect("~/");
         }
         base.OnInit(e);
      }
      

      odkaz pro přepnutí stránky, pak může být třeba

      &lt;a href="language.aspx?culture=en-US"&gt;en&lt;/a&gt;
      

      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