Lokalisierung in ASP.MVC: Multi-Language User Interface (MUI)

Multikulti im Web
Kommentare

Nicht selten stoßen wir während unserer alltäglichen Arbeit auf die Anforderung, eine Anwendung in mehreren Sprachen zu veröffentlichen. Gerade bei international erreichbaren Serveranwendungen ist das keine Seltenheit. Dieser Artikel veranschaulicht, wie überraschend einfach es doch ist, eine ASP.NET-MVC-Anwendung in mehreren Sprachen bereit zu stellen.

Windows Developer

Der Artikel „Multikulti im Web“ von Timm Bremus ist erstmalig erschienen im Windows Developer 8.2012

Dass eine Webanwendung, die im World Wide Web veröffentlicht wurde, von der ganzen Welt aus erreicht werden kann, ist sicherlich für niemanden neu. Eine gute Anwendung, die von Jedermann genutzt werden kann, findet auch schnell regen Zuspruch. Wieso sollte man deshalb eine Software nur in einer Sprache bereitstellen und vielleicht tausende von Benutzern ausschließen? Auch Intranetanwendungen, die in großen internationalen Unternehmen eingesetzt werden, sollten nicht nur in Englisch zur Verfügung stehen. ASP.NET MVC und das .NET Framework geben uns gute Werkzeuge an die Hand, um eine Webanwendung auch in mehreren Sprachen zur Verfügung zu stellen, und das ohne erheblichen Mehraufwand. Der Benutzer kann somit selbst entscheiden, in welcher Sprache er die Anwendung gerne ausführen möchte und schaltet bequem während der Nutzung zwischen den Sprachen um.

Gedanken im Vorfeld

Bevor es ans Umsetzen unseres Multi-Language User Interface geht, sollte man sich gut überlegen, an welchen Stellen unsere Unterstützung für die Mehrsprachigkeit der Anwendung verankert werden muss. Folgende Aufgaben kommen auf uns zu:

  • Lokalisierung der Views
  • Mechanismus zum Umschalten der Sprache
  • Lokalisierung des Models und deren Validierung

Nun heißt es Ärmel hochkrempeln und ran an die Tastatur. Die genauen Handgriffe der einzelnen Aufgaben beschreiben die folgenden Abschnitte.

Anlegen von Ressourcendateien

Im Alltag nutzen wir meist Wörterbücher, um diverse Vokabeln einer fremden Sprache nachzuschlagen. Einen ähnlichen Mechanismus wollen wir auch in unserer Webanwendung nutzen. Hierzu erstellen wir für jede Sprache, die in unserer Anwendung unterstützt werden soll, ein Wörterbuch in Form einer Ressourcendatei (mehr Informationen gibt es im Kasten „Ressourcendateien“). Es ist von Vorteil, sich gleich zu Beginn alle nötigen Ressourcen anzulegen und sie in Verzeichnissen zu strukturieren (Abb. 1). In diesem Beispiel trennen wir die Übersetzungen der Models von den Views, um die Ressourcen nicht unnötig aufzublähen und die Übersicht zu behalten.

Abb. 1: Projektstruktur der Anwendung
Abb. 1: Projektstruktur der Anwendung

Als Nächstes füllen wir unsere Ressourcendateien mit Inhalt beziehungsweise mit den Texten, die wir in unserer Anwendung lokalisieren wollen. Abbildung 2 veranschaulichst zudem, dass der Access Modifier einer jeden Ressource auf Public gesetzt werden muss. Danach sollte in den Eigenschaften der Ressourcendateien geprüft werden, ob die Eigenschaft Custom Tool den Wert PublicResXFileCodeGenerator hat (Abb. 3). Sind diese beiden Einstellungen nicht korrekt gesetzt, wird die jeweilige Ressource nicht kompiliert und steht zur Laufzeit nicht zur Verfügung.

Abb. 2: Ressourcendatei als Wörterbuch
Abb. 2: Ressourcendatei als Wörterbuch
Abb. 3: Eigenschaftsfenster einer Ressourcendatei
Abb. 3: Eigenschaftsfenster einer Ressourcendatei
Ressourcendateien

Ressourcendateien bewähren sich schon lange als Hilfsmittel für MUI-Anwendungen und sind zum Standard geworden. Der Dateiname richtet sich nach folgender Konvention: [RESSOURCENNAME].[CULTURE].resx. RESSOURCENNAME steht für den Namen der jeweiligen Ressource und ist frei wählbar. Er wird zur Gruppierung beziehungsweise Kategorisierung der einzelnen Übersetzungs-Strings genutzt. CULTURE ist die Bezeichnung der jeweiligen Sprache, der in Form eines Codes angegeben wird [1]. Es gibt sowohl neutrale als auch konkrete Sprachcodes. Die neutralen Sprachcodes (z. B. de oder en) beschreiben lediglich die jeweilige Sprache der Ressource, jedoch nicht das dazugehörige Land. Genau das übernimmt ein konkreter Sprachcode wie en-US oder en-UK. Es besteht ebenso die Möglichkeit, eine Ressourcendatei nicht mit einer Culture zu versehen. Dann wird die Ressource als so genanntes Fallback interpretiert und wird immer dann genutzt, wenn für die ausgewählte Sprache im CurrentThread keine passende Ressourcendatei bereitsteht.

Lokalisierung der Views

Die Vorbereitungen sind nun abgeschlossen und endlich kann es ans Werk gehen. Zuerst passen wir die Texte in den einzelnen Views und der Master Page an. Listing 1 verdeutlicht, wie auf Ressourcen zugegriffen wird. Hierbei fällt auf, dass der Namespace der Ressourcendateien zur Vereinfachung des Zugriffs angepasst wurde. Das stellt man in den Eigenschaften der Ressourcendatei unter Custom Tool Namespace ein (Abb. 3).

Bevor man jetzt mit dem Testen seines Codes beginnt, sollte man sich noch Gedanken darüber machen, in welcher Sprache die Anwendung standardmäßig gestartet werden soll. Um die Anwendung in der gewünschten Sprache zu laden, müssen für jeden Request die Properties CurrentCulture und CurrentUICulture des CurrentThread auf den gewünschten Sprachcode umgestellt werden. Das realisiert man in der Global.asax in der Application_AcquireRequestState -Methode. Listing 2 zeigt den hierfür nötigen Code.

Listing 1

  • @ViewRes.HomeDirectory.MembershipConfig
    @ViewRes.HomeDirectory.Suggestion
  • Listing 2

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
      //Create culture info object
      CultureInfo ci = new CultureInfo("de");
      Thread.CurrentThread.CurrentUICulture = ci;
      Thread.CurrentThread.CurrentCulture = 
      CultureInfo.CreateSpecificCulture(ci.Name);
    }  
    Umschalten, bequem und jederzeit

    Im letzten Abschnitt haben wir die Sprache, die zur Anzeige genutzt werden soll, direkt im Code definiert. In diesem Schritt erweitern wir unsere Anwendung dahingehend, dass die Sprachauswahl beim Benutzer liegt. Im ersten Schritt fügen wir dem AccountController eine neue ChangeCulture Action hinzu, die beim Sprachenwechsel aufgerufen wird (Listing 3). In dieser Methode wird die ausgewählte Sprache in eine Sessionvariable für alle zukünftigen Anfragen an die Anwendung gespeichert. Weiterhin hat die Methode zwei Parameter: der erste steht für die ausgewählte Sprache und der zweite für den URL, auf den nach der Sprachumschaltung zurückverwiesen wird.

    Nun können wir ein Benutzersteuerelement erzeugen, über das wir den Umschaltmechanismus für den Benutzer zugänglich machen. Eine beispielhafte Implementierung zeigt Listing 4. Das Control kann nun an einer beliebigen Stelle in der Master Page integriert werden.

    Der letzte Schritt erfordert eine Anpassung unserer Application_AcquireRequestState -Methode. Die aktuelle Sprache wird nun nicht mehr aus einem hart codierten String bezogen, sondern vielmehr über die Sessionvariable, die wir über unsere Action gesetzt haben. Sollte diese Sessionvariable nicht zur Verfügung stehen, geben wir uns mit einer definierten Standardsprache zufrieden (Listing 5).

    Listing 3

    public ActionResult ChangeCulture(string lang, string returnUrl)
    {
      Session["Culture"] = new CultureInfo(lang);
      return Redirect(returnUrl);
    }  
    Listing 4

    @Html.ActionLink("English", "ChangeCulture", "Account", 
      new { lang = "en", returnUrl = this.Request.RawUrl }, null)
    @Html.ActionLink("Deutsch", "ChangeCulture", "Account",
      new { lang = "de", returnUrl = this.Request.RawUrl }, null)  
    Listing 5

    protected void Application_AcquireRequestState(object sender,
    EventArgs e)
    {
      // It's important to check whether session object is ready
      if (HttpContext.Current.Session != null)
      {
        CultureInfo ci = (CultureInfo)this.Session["Culture"];
        // Checking first if there is no value in session
        // and set default language this can happen
        //for first user's request
        if (ci == null)
        {
          //Sets default culture to english invariant
          string langName = "en";
          //Try to get values from Accept lang HTTP header
          if (HttpContext.Current.Request.UserLanguages != null && HttpContext.Current.Request.UserLanguages.Length != 0)
          {
            //Gets accepted list
            langName = HttpContext.Current.Request.UserLanguages[0].Substring(0, 2);
          }
          ci = new CultureInfo(langName);
          this.Session["Culture"] = ci;
        }
        // Finally setting culture for each request
        Thread.CurrentThread.CurrentUICulture = ci;
        Thread.CurrentThread.CurrentCulture =
        CultureInfo.CreateSpecificCulture(ci.Name);
      }
    }  
    Unsere Redaktion empfiehlt:

    Relevante Beiträge

    Meinungen zu diesem Beitrag

    X
    - Gib Deinen Standort ein -
    - or -