ASP.NET MVC I18n – Teil 6: Nutzung von .NET Ressourcen

4. Februar 2014

Der letzte Beitrag adressierte größere Inhaltsbereiche aus Test oder Bildern. In diesen Fällen ist die Lokalisierung über einfache switches im Code ein valider Ansatz. Für Menüs, Links, Labels und ähnliches bietet .NET mit seinem Ressource-Konzept eine bessere Lösung an.

 

Hinweis: Dieser Beitrag ist Teil einer Serie, die Übersicht findet sich hier.

Das Thema ist nicht neu, daher im Schnelldurchgang…

.NET Ressourcen basieren auf Manifest Ressourcen, und Satellitenassemblies für die Lokalisierung. Zugriff auf die Ressourcen erfolgt über die Klasse ResourceManager – entweder direkt oder indirekt über typsichere Zugriffsklassen, die Visual Studio für jede .resx-Datei automatisch erzeugt. Das hat den Vorteil, dass einem die Codevervollständigung Arbeit abnimmt, außerdem werden Schreibfehler eliminiert.

Für eine ASP.NET MVC Anwendung muss zusätzlich noch den “Access Modifier” der Zugriffsklasse auf public abändern, was sich einfach im Editor für .resx-Dateien erledigen lässt:

Lokalisierung-6-Labels

Grund dafür ist, dass ASP.NET dynamisch Code generiert (das gilt für WebForms genauso wie für Razor). Dieser generierte Code liegt dann jedoch in einem separaten Assembly und hätte somit keinen Zugriff auf die Zugriffsklasse, wenn sie nicht public wäre.

Das bezieht sich ausdrücklich nur auf die vom Visual Studio generierte Zugriffsklasse, nicht die Ressource selbst. Über die Klasse RessourceManager kann man jederzeit auf Ressourcen in beliebigen Assemblies zugreifen.

Und um noch einen weiteren Punkt aus dem Weg zu räumen: Mit ASP.NET WebForms hätte man vieleicht die speziellen Verzeichnisse App_GlobalResources und App_LocalResources verwendet, um dort seine Ressourcen abzulegen. Aber: App_GlobalResources und App_LocalResources vertragen sich nicht mit ASP.NET MVC!

Das Problem ist, dass Visual Studio und ASP.NET Ressourcen in diesen Verzeichnissen anders behandeln, als in anderen Verzeichnissen. Zwar kann man die daraus resultierenden Unterschiede und Fallstricke über Workarounds umgehen, aber warum sich darauf einlassen, wenn es in anderen Verzeichnissen sauber funktioniert.

Wer sich für die Hintergründe interessiert, Scott hat das zusammengeschrieben.

In medias res…

Ich habe die Ressourcen in meinem eigenen Ressourcen-Verzeichnis gesammelt. Der offensichtliche Name – Resouces – ist dafür leider keine gute Wahl, da er bereits vergeben ist. (Durch die Standard-Ressource, die aus den Projektsettings generiert werden kann, und als namespace für Ressourcen in App_GlobalRessources). Ich habe mich für Localizations entschieden und solche dann auch für Labels bereitgestellt:

Lokalisierung-6-Files

Labels.resx und die lokalisierten Varianten stellen die Texte für Menüs und Links für die Navigation zur Verfügung.

Um die Verwendung in Views einfacher zu machen, kann man den Namespace in der web.config (im Views Verzeichnis!) anmelden:

   1: <system.web.webPages.razor>

   2:   <pages pageBaseType="System.Web.Mvc.WebViewPage">

   3:     <namespaces>

   4:         [...]

   5:       <add namespace="MyStocks.Mvc" />

   6:       <add namespace="MyStocks.Mvc.Localizations" />

   7:     </namespaces> 

   8:   </pages>

   9: </system.web.webPages.razor>

Das macht den Code gleich lesbarer, als wenn man den voll qualifizierten Namen verwenden müsste:

   1: <ol class="round">

   2:     <li class="one">

   3:         @Html.ActionLink(Labels.Navigation_Stock_Index + "...", "Index", "Stock")

   4:     </li>

Entsprechend lokalisierte Ressourcen vorausgesetzt haben wir den nächsten Baustein für unsere Anwendung bereitgestellt.

 

Ich kann mit der Verwaltung der Ressourcen in beliebigen Dateien in einem zentralen Verzeichnis sehr gut leben. Falls aber jemand eher View-spezifische Ressourcen hat und das lieber anders regeln möchte, bietet Matt vielleicht einen interessanten Ansatz: Er stellt eine View-Basisklasse und eine ViewEngine bereit, mit der sich Ressourcen anhand einer Namenskonvention mit der View verheiraten lassen. Ggf. kann man das noch mit einem Fallback-Mechanismus anreichern, der allgemeingültige Übersetzungen dann doch wieder zentral in einer Datei vorhält.