6. Internationalisierung von Ansichten
Wir werden hier auf das Problem der Internationalisierung von Ansichten eingehen. Es handelt sich um ein komplexes Problem, das im folgenden Artikel von Scott Hanselman gut beschrieben wird:
[http://www.hanselman.com/blog/GlobalizationInternationalizationAndLocalizationInASPNETMVC3JavaScriptAndJQueryPart1.aspx]
Lassen Sie uns zunächst seine Definition der verschiedenen Begriffe im Zusammenhang mit der Internationalisierung von Ansichten wiedergeben:
Internationalisierung (i18n) | Sicherstellen, dass die Anwendung verschiedene Sprachen und Ländereinstellungen unterstützt |
Lokalisierung (l10n) | Sicherstellen, dass die Anwendung ein bestimmtes Sprach-/Ländereinstellungs-Paar unterstützt |
Globalisierung | die Kombination aus Internationalisation und Localisation |
Sprache | gesprochene Sprache – gekennzeichnet durch einen Code ISO (fr: Französisch, es: Spanisch, en: Englisch, ...) |
Ländereinstellung | eine Variante der Sprache – ebenfalls durch einen Code ISO gekennzeichnet (en_GB: britisches Englisch, en_US: amerikanisches Englisch, …) |
Betrachten wir das Problem anhand eines ersten Beispiels.
6.1. Lokalisierung reeller Zahlen
Im vorherigen Eingabeformular fällt eine Unregelmäßigkeit auf:
![]() |
Für die reelle Zahl haben wir [0,3] eingegeben, was jedoch nicht akzeptiert wurde. Es muss [0.3] eingegeben werden:
![]() |
Das erwartete Format ist also das angelsächsische und nicht das französische. Bei der Suche im Internet findet man Lösungen. Hier ist eine davon.
Die Aktionen [GET] und [POST] lauten nun wie folgt:
// Aktion13-GET
[HttpGet]
public ViewResult Action13Get()
{
return View("Action13Get", new ViewModel11());
}
// Aktion13-POST
[HttpPost]
public ViewResult Action13Post(ViewModel11 modèle)
{
return View("Action13Get", modèle);
}
Die Ansicht [Action13Get.cshtml] ist bis auf die JavaScript-Skripte identisch mit der Ansicht [Action12Get.cshtml]:
<head>
<meta name="viewport" content="width=device-width" />
<title>Action13Get</title>
<link rel="stylesheet" href="~/Content/Site.css" />
<script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
...
<script type="text/javascript" src="~/Scripts/myscripts.js"></script>
</head>
Hinweis: Passen Sie in Zeile 5 die Version von jQuery an Ihre Visual Studio-Version an.
- In Zeile 9 haben wir ein Skript [myscripts.js] hinzugefügt. Dieses lautet wie folgt:
![]() |
// http://blog.instance-factory.com/?p=268
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
$.validator.methods.date = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseDate(value));
}
jQuery.extend(jQuery.validator.methods, {
range: function (value, element, param) {
//Verwenden Sie das Globalisierungs-Plugin, um den Wert
var val = Globalize.parseFloat(value);
return this.optional(element) || (
val >= param[0] && val <= param[1]);
}
});
// beim Laden des Dokuments
$(document).ready(function () {
var culture = 'fr-FR';
Globalize.culture(culture);
});
In Zeile 1 habe ich angegeben, wo dieses Skript gefunden wurde. Ich werde nicht versuchen, es zu erklären, da ich es selbst nicht verstehe. JavaScript hat manchmal seine undurchsichtigen Seiten. In den Zeilen 4, 9 und 15 wird ein Objekt namens „[Globalize]“ verwendet. Dieses wird von der Bibliothek „ JQuery Globalization“ bereitgestellt, die man mit „[NuGet]“ beziehen kann:
![]() |
![]() |
- in [1] verwalten Sie die Pakete [NuGet] des Projekts [Exemple-03];
- unter [2] die Pakete online einsehen;
- in [3] den Begriff [globalization] eingeben;
- in [4]: Installieren Sie das Paket [Globalize] aus dem Projekt JQuery.
Sobald das Paket [Globalize] installiert ist, erscheint im Ordner [Scripts] ein neuer Zweig:
![]() |
- In [1] wurde ein Ordner [globalize] mit dem Hauptskript [globalize.js] erstellt;
- in [2] wird das Hauptskript [globalize.js] durch sprachspezifische Skripte und Skripte für bestimmte Ländereinstellungen ergänzt;
- in [3] wurden die Skripte für die französische Sprache mit den belgischen (BE), kanadischen (CA), französischen (FR), schweizerischen (CH), luxemburgischen (LU) monegassisch (MC).
Das Skript [globalize.js] und das Skript unserer Kultur [globalize.culture.fr-FR.js] müssen Teil der Liste der Skripte sein, die auf unserer Seite [Action13Get.cshtml] aufgeführt sind:
<head>
<meta name="viewport" content="width=device-width" />
<title>Action13Get</title>
...
<script type="text/javascript" src="~/Scripts/globalize/globalize.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.fr-FR.js"></script>
<script type="text/javascript" src="~/Scripts/myscripts.js"></script>
</head>
- Zeile 5: das Skript [globalize];
- Zeile 6: das Skript [globalize.culture.fr-FR.js];
- Zeile 7: das Skript [myscripts.js];
Kommen wir noch einmal auf dieses letzte Skript zurück:
// http://blog.instance-factory.com/?p=268
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
...
// beim Laden des Dokuments
$(document).ready(function () {
var culture = 'fr-FR';
Globalize.culture(culture);
});
Die Zeilen 10–13 legen die clientseitige Kultur auf [fr-FR] fest:
- Zeile 10: Die Funktion JQuery [ready] wird ausgeführt, sobald das Dokument, in dem sich das Skript befindet, vollständig vom Browser geladen wurde;
- Zeilen 11–12: Die clientseitige Kultur wird auf [fr-FR] festgelegt. Dazu muss die Datei [globalize.culture.fr-FR.js] in die Liste der mit dem Dokument verknüpften JavaScript-Skripte aufgenommen werden.
Nun können wir die neue Anwendung testen:
![]() |
Man kann nun [0,3] als reelle Zahl eingeben, was zuvor nicht möglich war. Es tritt jedoch eine weitere Anomalie auf:
Im obigen Beispiel lässt die clientseitige Validierung die Eingabe von „[11.2]“ in angelsächsischer Schreibweise zu. Dieser Wert wird serverseitig bei der Formularüberprüfung jedoch nicht akzeptiert:
Man muss „[11,2]“ eingeben, dann funktioniert es sowohl auf der Client- als auch auf der Serverseite. Auf der Client-Seite sollte die angelsächsische Schreibweise nicht akzeptiert werden. Das muss möglich sein…
Kommen wir nun zur Internationalisierung der Ansichten. Wir fahren mit dem Beispiel des vorherigen Formulars fort und stellen es in zwei Sprachen bereit: Französisch und Englisch.
6.2. Eine Kultur verwalten
Die Sprache der Ansichten wird durch das Objekt [Thread.CurrentThread.CurrentUICulture] gesteuert. Um die Seiten in der Kultur [fr-FR] anzuzeigen, schreibt man:
Die Lokalisierung (Daten, Zahlen, Währungen, Uhrzeiten usw.) wird durch das Objekt [Thread.CurrentThread.CurrentCulture] gesteuert. Ähnlich wie zuvor geschrieben, lautet der Code:
Diese beiden Anweisungen könnten im Konstruktor jedes Controllers der Anwendung stehen. Man könnte diesen allen Controllern gemeinsamen Code jedoch auch auslagern. Wir verfolgen diesen Ansatz.
Wir erstellen zwei neue Controller:
![]() |
- [I18NController] wird die Basisklasse für alle Controller sein, die Internationalisierung nutzen;
- [SecondController] ist ein Beispiel-Controller, der von [I18NController] abgeleitet ist.
Der Code des Controllers [I18NController] lautet wie folgt:
using System.Threading;
using System.Web;
using System.Web.Mvc;
namespace Exemples.Controllers
{
public abstract class I18NController : Controller
{
public I18NController()
{
// Der Kontext der aktuellen Anfrage wird abgerufen
HttpContext httpContext = HttpContext.Current;
// die Anfrage wird nach dem Parameter [lang] durchsucht
// man sucht ihn in den Parametern von URL
string langue = httpContext.Request.QueryString["lang"];
if (langue == null)
{
// Er wird in den übermittelten Parametern gesucht
langue = httpContext.Request.Form["lang"];
}
if (langue == null)
{
// wird in der Benutzersitzung gesucht
langue = httpContext.Session["lang"] as string;
}
if (langue == null)
{
// 1. Parameter des Headers HTTP AcceptLanguages
langue = httpContext.Request.UserLanguages[0];
}
if (langue == null)
{
// Sprache fr-FR
langue = "fr-FR";
}
// Die Sprache wird in der Sitzung festgelegt
httpContext.Session["lang"] = langue;
// Die Kulturen des Threads werden geändert
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(langue);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
}
}
}
- Zeile 7: [I18NController] leitet sich von der Klasse [Controller] ab;
- Zeile 7: Die Klasse [abstract] wird deklariert, um ihre direkte Instanziierung zu verhindern: Sie kann nur abgeleitet werden, um verwendet zu werden;
- Zeile 9: Der Konstruktor der Klasse wird bei jeder Instanziierung eines von [I18NController] abgeleiteten Controllers ausgeführt;
- Zeile 12: Der Kontext der Anfrage HTTP, die gerade vom Controller verarbeitet wird, wird abgerufen;
- Zeile 15: Es wird davon ausgegangen, dass die Sprache durch einen Parameter [lang] festgelegt wird, der an verschiedenen Stellen zu finden ist. Es wird in folgender Reihenfolge gesucht:
- Zeile 15: in den Parametern von URL und [?lang=en-US],
- Zeile 19: in den übermittelten Parametern [lang=de],
- Zeile 24: in der Benutzersitzung,
- Zeile 29: in den vom Client gesendeten Spracheinstellungen HTTP,
- Zeile 26: Wenn nichts gefunden wurde, wird die Kultur auf [fr-FR] festgelegt;
- Zeile 37: Die Kultur wird in der Sitzung gespeichert. Dort wird sie bei nachfolgenden Abfragen wiedergefunden. Der Benutzer kann sie ändern, indem er sie in den Parametern eines Befehls GET oder POST angibt;
- Zeilen 39–40: Hier wird die Ländereinstellung der Ansicht festgelegt, die nach der Verarbeitung der aktuellen Abfrage angezeigt wird.
Der Controller [SecondController] sieht wie folgt aus:
using Exemple_03.Models;
using Exemples.Controllers;
using System.Web.Mvc;
namespace Exemple_03.Controllers
{
public class SecondController : I18NController
{
// Aktion14-GET
[HttpGet]
public ViewResult Action14Get()
{
return View("Action14Get", new ViewModel14());
}
// Aktion14-POST
[HttpPost]
public ViewResult Action14Post(ViewModel14 modèle)
{
return View("Action14Get", modèle);
}
}
}
- Zeile 7: [SecondController] leitet sich von [I18NController] ab. So wird sichergestellt, dass die Kultur der anzuzeigenden Ansicht initialisiert wurde;
- Zeile 13: Es wird die Ansichtvorlage [ViewModel14] verwendet, die wir im Folgenden vorstellen werden;
- Zeilen 13 und 20: Die Ansicht [Action14Get.cshtml] sorgt für die Anzeige des Formulars.
6.3. Internationalisierung der Ansichtsvorlage [ViewModel14]
Die Ansichtsvorlage [ViewModel14] lautet wie folgt:
![]() |
using Exemple_03.Resources;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Net.Mail;
namespace Exemple_03.Models
{
public class ViewModel14 : IValidatableObject
{
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
[Display(ResourceType = typeof(MyResources), Name = "chaineaumoins4")]
[RegularExpression(@"^.{4,}$", ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoIncorrecte")]
public string Chaine1 { get; set; }
[Display(ResourceType = typeof(MyResources), Name = "chaineauplus4")]
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
[RegularExpression(@"^.{1,4}$", ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoIncorrecte")]
public string Chaine2 { get; set; }
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
[Display(ResourceType = typeof(MyResources), Name = "chaine4exactement")]
[RegularExpression(@"^.{4,4}$", ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoIncorrecte")]
public string Chaine3 { get; set; }
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
[Display(ResourceType = typeof(MyResources), Name = "entier")]
public int Entier1 { get; set; }
[Display(ResourceType = typeof(MyResources), Name = "entierentrebornes")]
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
[Range(1, 100, ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoIncorrecte")]
public int Entier2 { get; set; }
[Display(ResourceType = typeof(MyResources), Name = "reel")]
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
public double Reel1 { get; set; }
[Display(ResourceType = typeof(MyResources), Name = "reelentrebornes")]
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
[Range(10.2, 11.3, ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoIncorrecte")]
public double Reel2 { get; set; }
[Display(ResourceType = typeof(MyResources), Name = "email")]
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
[EmailAddress(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoIncorrecte", ErrorMessage="")]
public string Email1 { get; set; }
[Display(ResourceType = typeof(MyResources), Name = "date1")]
[RegularExpression(@"\s*\d{2}/\d{2}/\d{4}\s*", ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoIncorrecte")]
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
public string Regexp1 { get; set; }
[Display(ResourceType = typeof(MyResources), Name = "date2")]
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
[DataType(DataType.Date)]
public DateTime Date1 { get; set; }
// Validierung
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// Fehlerliste
List<ValidationResult> résultats = new List<ValidationResult>();
// die gleiche Fehlermeldung für alle
string errorMessage=MyResources.ResourceManager.GetObject("infoIncorrecte", new CultureInfo(System.Web.HttpContext.Current.Session["lang"] as string)).ToString();
// Datum 1
if (Date1.Date <= DateTime.Now.Date)
{
résultats.Add(new ValidationResult(errorMessage, new string[] { "Date1" }));
}
// E-Mail 1
try
{
new MailAddress(Email1);
}
catch
{
résultats.Add(new ValidationResult(errorMessage, new string[] { "Email1" }));
}
// Regexp1
try
{
DateTime.ParseExact(Regexp1, "dd/MM/yyyy", CultureInfo.CreateSpecificCulture("fr-FR"));
}
catch
{
résultats.Add(new ValidationResult(errorMessage, new string[] { "Regexp1" }));
}
// Die Fehlerliste wird angezeigt
return résultats;
}
}
}
Dieses Modell ist die internationalisierte Version des Vorgängermodells [ViewModel11]. Wir werden den Internationalisierungsmechanismus für das erste Attribut der ersten Eigenschaft beschreiben. Die übrigen Attribute folgen demselben Mechanismus.
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
public string Chaine1 { get; set; }
Im Vorgängermodell [ViewModel11] lauteten diese Zeilen wie folgt:
[Required(ErrorMessage = "Information requise")]
public string Chaine1 { get; set; }
In der internationalisierten Version, Zeile 1, werden die anzuzeigenden Texte in einer Ressourcendatei abgelegt. Hier heißt diese Datei [MyResources.resx] (typeof) und wurde im Stammverzeichnis des Projekts abgelegt. Man bezeichnet sie als Ressourcendatei.
![]() |
Wir haben hier drei Ressourcendateien erstellt:
- [MyResources]: Standardressource, wenn keine Ressource für die aktuelle Sprachumgebung vorhanden ist;
- [MyResources.fr-FR]: Ressource für die Locale [fr-FR];
- [MyResources.en-US]: Ressource für die Sprachumgebung [en-US];
Um eine Ressourcendatei zu erstellen, geht man wie folgt vor: [1, 2, 3]:
![]() |
![]() |
Dadurch wird die Ressourcendatei [MyResources2.resx] erstellt. Wenn man darauf doppelklickt, erscheint die folgende Seite:
![]() |
Eine Ressourcendatei ist ein Wörterbuch mit Schlüsseln und den diesen Schlüsseln zugeordneten Werten. Der Schlüssel wird in [1], der Wert in [2] und der Geltungsbereich der Ressource in [3] eingegeben. Damit diese Ressourcen lesbar sind, müssen sie den Geltungsbereich [Public] haben. Kehren wir zu der Zeile zurück:
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
- [ErrorMessageResourceType]: Bezeichnet die Ressourcendatei. Der Parameter von [typeof] ist der Name der Datei. Dieser wird durch den Kompilierungsprozess in eine Klasse umgewandelt und die zugehörige Binärdatei in die Assembly des Projekts eingebunden. Somit ist [MyResources] letztendlich der Name der Ressourcenklasse;
- [ErrorMessageResourceName = "infoRequise"]: Bezeichnet einen Schlüssel in der Ressourcendatei. Letztendlich bedeutet diese Zeile, dass die anzuzeigende Fehlermeldung der Wert aus der Datei [MyResources] ist, der dem Schlüssel [infoRequise] zugeordnet ist.
Um den Schlüssel [infoRequise] und den zugehörigen Wert in der Datei [MyResources] anzulegen, gehen Sie wie folgt vor:
![]() |
Man gibt den Schlüssel in [1], den Wert in [2] und den Geltungsbereich der Ressource in [3] ein.
Ein letzter Punkt muss noch geklärt werden: der Namensraum der Klasse [MyResources]. Dieser wird in den Eigenschaften der Datei [MyResources.resx] definiert:
![]() |
In [1] definieren wir den Namensraum der Klasse [MyResources], die aus der Ressourcendatei [MyResources.resx] erstellt wird. Kehren wir zu der untersuchten internationalisierten Zeile zurück:
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
Der Operator typeof erwartet eine Klasse, in diesem Fall die Klasse [MyResources]. Damit diese gefunden wird, muss ihr Namensraum in die Klasse [ViewModel14] importiert werden:
using Exemple_03.Resources;
Damit die Klasse [MyResources] sichtbar ist, muss das Projekt seit der Erstellung der Ressourcendatei [MyResources] mindestens einmal generiert worden sein. Der Code dieser Klasse ist in der Datei [MyResources.Designer.cs] zu finden:
Wenn man auf diese Datei doppelklickt, gelangt man zum Code der Klasse [MyResources]:
namespace Exemple_03.Resources {
using System;
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class MyResources2 {
...
public static string infoRequise {
get {
return ResourceManager.GetString("infoRequise", resourceCulture);
}
}
}
}
- Zeile 1: der Namensraum der Klasse;
- Zeile 11: Der Schlüssel [infoRequise] ist zu einer statischen Eigenschaft der Klasse [MyResources] geworden. Er ist über die Notation [MyResources.infoRequise] zugänglich. Außerdem ist zu beachten, dass diese Eigenschaft den Gültigkeitsbereich [public] hat. Ohne diesen wäre sie nicht zugänglich. Es ist gut, sich dies zu merken, da der Standardgültigkeitsbereich leider [internal] ist und dies zu schwer nachvollziehbaren Fehlern führt, wenn man vergisst, diesen Gültigkeitsbereich zu ändern.
Warum nun drei Ressourcendateien?
Wir haben [MyResources.resx] erstellt. Dies ist die Stammressource. Anschließend erstellen wir so viele [MyResources.locale.resx]-Ressourcendateien, wie es zu verwaltende Sprachversionen gibt. Hier verwalten wir Französisch ([fr-FR]) und US-Englisch ([en-US]). Wenn die aktuelle Sprachversion weder [fr-FR] noch [en-US] ist, wird die Stammressource [MyResources.resx] verwendet.
Der endgültige Inhalt von [MyResources.resx] lautet wie folgt:
![]() |
Die Meldungen werden auf Französisch angezeigt, wenn die Ländereinstellung nicht erkannt wird. Der endgültige Inhalt von [MyResources.fr-FR.resx] ist identisch und wird durch einfaches Kopieren der Datei erstellt.
Der endgültige Inhalt von [MyResources.en-US.resx] wird ebenfalls durch Kopieren der Datei erstellt und anschließend wie folgt geändert:
![]() |
Kehren wir zur Ansicht [ViewModel14] und ihrer Methode [Validate] zurück:
// Validierung
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// Fehlerliste
List<ValidationResult> résultats = new List<ValidationResult>();
// für alle dieselbe Fehlermeldung
string errorMessage=MyResources.ResourceManager.GetObject("infoIncorrecte", new CultureInfo(System.Web.HttpContext.Current.Session["lang"] as string)).ToString();
// Datum 1
if (Date1.Date <= DateTime.Now.Date)
{
résultats.Add(new ValidationResult(errorMessage, new string[] { "Date1" }));
}
...
// Die Fehlerliste wird angezeigt
return résultats;
}
Zeile 7 zeigt, wie eine Meldung aus der Ressourcendatei [MyResources] abgerufen wird. Hier soll die Meldung abgerufen werden, die dem Schlüssel [infoIncorrecte] zugeordnet ist, und zwar in der aktuellen Kultur:
- MyResources.ResourceManager.GetObject("infoIncorrecte", new CultureInfo("en-US")) : Ruft das Objekt ab, das dem Schlüssel [infoIncorrecte] in der Ressourcendatei [MyResources.en-US.resx] zugeordnet ist;
- Wir haben gesehen, dass der Controller [I18NController] die aktuelle Kultur in die mit dem Schlüssel [lang] verknüpfte Sitzung einfügt. Die aktuelle Kultur kann daher über System.Web.HttpContext.Current.Session["lang"] as string abgerufen werden;
- die Ressource wird mit dem Typ [object] abgerufen. Um die Fehlermeldung zu erhalten, wenden wir die Methode [ToString] auf sie an.
6.4. Internationalisierung der Ansicht [Action14Get.cshtml]
Wir passen die Ansicht des Formulars wie folgt an:
![]() |
@model Exemple_03.Models.ViewModel14
@using Exemple_03.Resources
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action14Get</title>
<link rel="stylesheet" href="~/Content/Site.css" />
<script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/globalize.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.fr-FR.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.en-US.js"></script>
<script type="text/javascript" src="~/Scripts/myscripts2.js"></script>
<script>
$(document).ready(function () {
var culture = '@System.Threading.Thread.CurrentThread.CurrentCulture';
Globalize.culture(culture);
});
</script>
</head>
<body>
<h3>Formulaire ASP.NET MVC - Internationalisation</h3>
@using (Html.BeginForm("Action14Post", "Second"))
{
<table>
<thead>
<tr>
<th>@MyResources.type</th>
<th>@MyResources.value</th>
<th>@MyResources.error</th>
</tr>
</thead>
<tbody>
<tr>
<td>@Html.LabelFor(m => m.Chaine1)</td>
<td>@Html.EditorFor(m => m.Chaine1)</td>
<td>@Html.ValidationMessageFor(m => m.Chaine1)</td>
</tr>
...
</tbody>
</table>
<p>
<input type="submit" value="Valider" />
</p>
}
</body>
</html>
<!-- Sprachauswahl -->
@using (Html.BeginForm("Lang", "Second"))
{
<table>
<tr>
<td><a href="javascript:postForm('fr-FR','/Second/Action14Get')">Français</a></td>
<td><a href="javascript:postForm('en-US','/Second/Action14Get')">English</a></td>
</tr>
</table>
}
Hinweis: Zeile 14 – Passen Sie die Version von jQuery an Ihre Visual Studio-Version an.
Beginnen wir mit dem Einfachsten, den Zeilen 36–38. Diese nutzen die statischen Eigenschaften der Klasse [MyResources], die wir gerade beschrieben haben. Um auf die Klasse [MyResources] zugreifen zu können, muss deren Namespace importiert werden (Zeile 2).
Bei internationalisierten Meldungen müssen auch diejenigen berücksichtigt werden, die vom Framework für die clientseitige Validierung angezeigt werden. Dazu müssen die Bibliotheken JQuery in den Zeilen 17–19 verwendet werden. Wir verwenden die Dateien JQuery für die beiden von uns verwalteten Kulturen [fr-FR] und [en-US]. Außerdem erinnern Sie sich vielleicht daran, dass die Ansicht [Action13Get] das folgende JavaScript-Skript [myscripts.js] verwendete:
// beim Laden des Dokuments
$(document).ready(function () {
var culture = 'fr-FR';
Globalize.culture(culture);
});
Nun ist der Wert nicht mehr nur [fr-FR], sondern variiert. Daher werden diese Zeilen nun von der Ansicht [Action14Get] selbst in den Zeilen 21–26 generiert. Diese sechs Zeilen werden in die Seite HTML aufgenommen, die an den Client gesendet wird.
- Zeile 23: Die JavaScript-Variable [culture] wird mit der aktuellen Kultur des Threads der gerade bearbeiteten Anfrage initialisiert. Man erinnert sich vielleicht daran, dass diese durch den Konstruktor der Klasse [I18NController] initialisiert wurde:
// Die Sprache wird in der Sitzung festgelegt
httpContext.Session["lang"] = langue;
// Die Thread-Kulturen werden geändert
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(langue);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
Wenn die aktuelle Kultur [en-US] ist, lautet das in die Seite HTML eingebettete JavaScript-Skript:
<script>
$(document).ready(function () {
var culture = 'en-US';
Globalize.culture(culture);
});
</script>
Es wurde bereits erwähnt, dass die Funktion [$(document).ready] am Ende des Ladevorgangs der Seite durch den Browser ausgeführt wird. Ihre Ausführung bewirkt, dass die Sprache des clientseitigen Validierungs-Frameworks festgelegt wird. Mit der Kultur „[en-US]“ werden die Fehlermeldungen des Frameworks auf Englisch angezeigt und stammen aus der Ressourcendatei „[MyResources.en-US.resx]“. Wir werden sehen, wie das funktioniert.
Betrachten wir nun die Zeilen 57–65:
<!-- Auswahl einer Sprache -->
@using (Html.BeginForm("Lang", "Second"))
{
<table>
<tr>
<td><a href="javascript:postForm('fr-FR','/Second/Action14Get')">Français</a></td>
<td><a href="javascript:postForm('en-US','/Second/Action14Get')">English</a></td>
</tr>
</table>
}
Hier haben wir ein zweites Formular, das erste befindet sich in den Zeilen 31–53. Dieses Formular zeigt am Ende der Seite die folgenden Links an:
![]() |
- Zeile 2: Das Formular wird an die Aktion [Lang] des Controllers [Second] gesendet. Bislang ist kein Wert zu sehen, der übermittelt werden könnte;
- Zeilen 6 und 7: Ein Klick auf die Links löst die Ausführung der JavaScript-Funktion [postForm] aus. Wo befindet sich diese Funktion? Im Skript [myscripts2.js], auf das in Zeile 20 der Ansicht verwiesen wird:
![]() |
Ihr Inhalt lautet wie folgt:
function postForm(lang, url) {
// Das zweite Formular des Dokuments wird abgerufen
var form = document.forms[1];
// Dem Formular wird das versteckte Attribut „lang“ hinzugefügt
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", "lang");
hiddenField.setAttribute("value", lang);
// Hinzufügen des versteckten Felds zum Formular
form.appendChild(hiddenField);
// Dem Feld wird das versteckte Attribut „url“ hinzugefügt
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", "url");
hiddenField.setAttribute("value", url);
// Das versteckte Feld wird dem Formular hinzugefügt
form.appendChild(hiddenField);
// Absenden
form.submit();
}
// http://blog.instance-factory.com/?p=268
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
$.validator.methods.date = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseDate(value));
}
jQuery.extend(jQuery.validator.methods, {
range: function (value, element, param) {
//Verwenden Sie das Globalisierungs-Plugin, um den Wert zu analysieren
var val = Globalize.parseFloat(value);
return this.optional(element) || (
val >= param[0] && val <= param[1]);
}
});
Die Zeilen 22–40 sind bereits im Skript [myscripts.js] enthalten, das im vorherigen Beispiel verwendet wurde. Wir gehen nicht noch einmal darauf ein. Die Funktion [postForm], die beim Klicken auf die Sprachlinks ausgeführt wird, befindet sich in den Zeilen 1–20:
- Zeile 1: Die Funktion akzeptiert zwei Parameter: [lang], die vom Benutzer ausgewählte Kultur, und [url], die URL, zu der der Client-Browser nach der Änderung der Kultur weitergeleitet werden soll. Diese beiden Parameter werden beim Aufruf angegeben:
<td><a href="javascript:postForm('fr-FR','/Second/Action14Get')">Français</a></td>
<td><a href="javascript:postForm('en-US','/Second/Action14Get')">English</a></td>
- Zeile 3: Es wird eine Referenz auf das zweite Formular des Dokuments abgerufen;
- Zeilen 5–8: Das Tag
wobei [xx-XX] der Wert des Parameters [lang] der Funktion ist;
- Zeile 10: Ebenfalls programmgesteuert wird dieses Tag zum zweiten Formular hinzugefügt. Letztendlich verhält es sich so, als wäre dieses Tag von Anfang an im zweiten Formular vorhanden gewesen. Sein Wert wird daher übermittelt. Genau das war beabsichtigt;
- Zeilen 11–17: Der gleiche Vorgang wird für ein Tag wiederholt
wobei [url] der Wert des Parameters [url] der Funktion ist;
- Zeile 19: Das zweite Formular wird nun übermittelt. An welche URL?
Man muss zum Code des zweiten Formulars auf der Seite [Action14Get.cshtml] zurückkehren:
@using (Html.BeginForm("Lang", "Second"))
{
...
}
Das Formular wird also an die Seite URL [/Second/Lang] gesendet. Wir müssen daher im Controller [SecondController] eine Aktion [Lang] definieren. Diese lautet wie folgt:
public class SecondController : I18NController
{
// Action14-GET
[HttpGet]
public ViewResult Action14Get()
{
return View("Action14Get", new ViewModel14());
}
// Action14-POST
[HttpPost]
public ViewResult Action14Post(ViewModel14 modèle)
{
return View("Action14Get", modèle);
}
// Sprache
[HttpPost]
public RedirectResult Lang(string url)
{
// Der Client wird an die URL weitergeleitet
return new RedirectResult(url);
}
}
- Zeile 18: Die Aktion reagiert nur auf ein [POST];
- Zeile 19: Sie ruft nur den Parameter mit dem Namen [url] ab;
- Zeile 22: Sie weist den Client an, sich zu diesem URL weiterzuleiten.
Aber was ist aus dem Parameter mit dem Namen [lang] geworden? Man muss sich nun daran erinnern, dass der Controller [SecondController] von der Klasse [I18NController] abgeleitet ist (Zeile 1 unten). Dieser Controller verwaltet den Parameter [lang]:
public abstract class I18NController : Controller
{
public I18NController()
{
// Der Kontext der aktuellen Anfrage wird abgerufen
HttpContext httpContext = System.Web.HttpContext.Current;
// Die Anfrage wird auf den Parameter [lang] überprüft
// man sucht ihn in den Parametern von URL
string langue = httpContext.Request.QueryString["lang"];
if (langue == null)
{
// Er wird in den gesendeten Parametern gesucht
langue = httpContext.Request.Form["lang"];
}
if (langue == null)
{
// wird in der Benutzersitzung gesucht
langue = httpContext.Session["lang"] as string;
}
if (langue == null)
{
// 1. Parameter des Headers HTTP AcceptLanguages
langue = httpContext.Request.UserLanguages[0];
}
if (langue == null)
{
// Sprache fr-FR
langue = "fr-FR";
}
// Die Sprache wird in der Sitzung festgelegt
httpContext.Session["lang"] = langue;
// Die Kulturen des Threads werden geändert
Thread.CurrentThread.CurrentCulture = new CultureInfo(langue);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
}
In unserem untersuchten Beispiel wird der Parameter [lang] übermittelt. Er wird daher in Zeile 13 gefunden, in Zeile 31 in die Sitzung aufgenommen und in den Zeilen 33–34 zur Aktualisierung der Kultur des aktuellen Threads verwendet.
Was passiert als Nächstes? Schauen wir uns die Verknüpfungen noch einmal an:
<td><a href="javascript:postForm('fr-FR','/Second/Action14Get')">Français</a></td>
<td><a href="javascript:postForm('en-US','/Second/Action14Get')">English</a></td>
Die Umleitungs-URL lautet [/Second/Action14Get]. Die Aktion [Action14Get] wird also ausgeführt:
public class SecondController : I18NController
{
// Aktion14-GET
[HttpGet]
public ViewResult Action14Get()
{
return View("Action14Get", new ViewModel14());
}
...
}
Zuvor wird der Konstruktor der Klasse [I18NController] ausgeführt:
public abstract class I18NController : Controller
{
public I18NController()
{
// Der Kontext der aktuellen Anfrage wird abgerufen
HttpContext httpContext = System.Web.HttpContext.Current;
// Die Anfrage wird nach dem Parameter [lang] durchsucht
// man sucht ihn in den Parametern von URL
string langue = httpContext.Request.QueryString["lang"];
if (langue == null)
{
// Er wird in den übermittelten Parametern gesucht
langue = httpContext.Request.Form["lang"];
}
if (langue == null)
{
// wird in der Benutzersitzung gesucht
langue = httpContext.Session["lang"] as string;
}
if (langue == null)
{
// 1. Parameter des Headers HTTP AcceptLanguages
langue = httpContext.Request.UserLanguages[0];
}
if (langue == null)
{
// Sprache fr-FR
langue = "fr-FR";
}
// Die Sprache wird in der Sitzung festgelegt
httpContext.Session["lang"] = langue;
// Die Kulturen des Threads werden geändert
Thread.CurrentThread.CurrentCulture = new CultureInfo(langue);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
}
Diesmal wird der Parameter [lang] in der Sitzung in Zeile 18 gefunden. Nehmen wir an, sein Wert lautet [en-US]. Diese Kultur wird somit zur Kultur des Ausführungs-Threads der Abfrage (Zeilen 33–34). Kehren wir zur Aktion [Action14Get] zurück:
// Aktion14-GET
[HttpGet]
public ViewResult Action14Get()
{
return View("Action14Get", new ViewModel14());
}
In Zeile 5 wird eine Instanz des View-Modells [ViewModel14] erstellt:
public class ViewModel14 : IValidatableObject
{
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
[Display(ResourceType = typeof(MyResources), Name = "chaineaumoins4")]
[RegularExpression(@"^.{4,}$", ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoIncorrecte")]
public string Chaine1 { get; set; }
....
Da die Kultur des aktuellen Threads [en-US] ist, wird die Datei [MyResources.en-US.resx] verwendet. Die Fehlermeldungen werden daher auf Englisch angezeigt.
Nachdem die Vorlage [ViewModel14] instanziiert wurde, wird die Ansicht [Action14Get.cshtml] angezeigt:
@model Exemple_03.Models.ViewModel14
@using Exemple_03.Resources
@using System.Threading
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action14Get</title>
...
<script>
$(document).ready(function () {
var culture = '@Thread.CurrentThread.CurrentCulture';
Globalize.culture(culture);
});
</script>
</head>
<body>
<h3>Formulaire ASP.NET MVC - Internationalisation</h3>
@using (Html.BeginForm("Action14Post", "Second"))
{
<table>
<thead>
<tr>
<th>@MyResources.type</th>
<th>@MyResources.value</th>
<th>@MyResources.error</th>
</tr>
</thead>
<tbody>
<tr>
...
</tr>
<tr>
Da die Kultur des aktuellen Threads [en-US] ist, lautet das in die Seite eingebettete Skript in den Zeilen 15–20:
<script>
$(document).ready(function () {
var culture = 'en-US';
Globalize.culture(culture);
});
</script>
Dadurch wird sichergestellt, dass das Validierungs-Framework mit den amerikanischen Formaten (Datum, Währung, Zahlen usw.) arbeitet. Aus demselben Grund werden die Meldungen in den Zeilen 30–32 aus der Ressourcendatei [MyResources.en-US.resx] bezogen und sind daher auf Englisch.
6.5. Ausführungsbeispiele
Hier sind einige Ausführungsbeispiele:
![]() |
- in [1] das Formular auf Französisch, in [2] das Formular auf Englisch.
![]() |
- In [3] werden die Fehlermeldungen auf der Client-Seite nun auf Englisch angezeigt.
Wenn man sich den Quellcode der Seite ansieht, stellt man fest, dass diese Fehlermeldungen in die Seite eingebettet wurden und somit von der Ansicht ASP.NET [Action14Get] und deren Vorlage [ViewModel14] generiert wurden:
<tr>
<td><label for="Reel1">Real number</label></td>
<td><input class="text-box single-line" data-val="true" data-val-number="The field Real number must be a number." data-val-required="Required data" id="Reel1" name="Reel1" type="text" value="0" /></td>
<td><span class="field-validation-valid" data-valmsg-for="Reel1" data-valmsg-replace="true"></span></td>
</tr>
<tr>
<td><label for="Reel2">Real number in range [10.2-11.3]</label></td>
<td><input class="text-box single-line" data-val="true" data-val-number="The field Real number in range [10.2-11.3] must be a number." data-val-range="Invalid data" data-val-range-max="11.3" data-val-range-min="10.2" data-val-required="Required data" id="Reel2" name="Reel2" type="text" value="0" /></td>
<td><span class="field-validation-valid" data-valmsg-for="Reel2" data-valmsg-replace="true"></span></td>
</tr>
6.6. Internationalisierung von Datumsangaben
Die Internationalisierung ist ein komplexes Thema. Betrachten wir daher die Eigenschaft [Date1] und ihren Kalender:
![]() |
Es ist festzustellen, dass es sich bei dem Kalender um einen französischen Kalender handelt, während die Kultur der Seite [en-US] ist. In HTML5 gibt es ein Attribut [lang], mit dem die Sprache der Seite oder einer Komponente der Seite festgelegt werden kann. Man kann dann in der Ansicht [Action14Get.cshtml] den folgenden Code schreiben:
@model Exemple_03.Models.ViewModel14
@using Exemple_03.Resources
@using System.Threading
@{
Layout = null;
var lang = Session["lang"] as string;
}
<!DOCTYPE html>
<html lang="@lang">
<head>
...
- Zeile 6: Die Kultur wird aus der Sitzung abgerufen;
- Zeile 11: Das Attribut [lang] der Seite wird mit diesem Wert festgelegt.
Tests zeigen, dass der Kalender auf Französisch bleibt, auch wenn die Seite ansonsten auf Englisch angezeigt wird. Es gibt außerdem ein Problem mit dem anderen Datum im Formular:
![]() |
In [1] wird das Datum weiterhin im französischen Format tt/mm/jjjj (20/11/2013) abgefragt, während das amerikanische Format mm/tt/jjjj (10/21/2013) ist. Wir werden versuchen, diese beiden Probleme mit einer neuen Ansicht und einer neuen Ansichtsvorlage zu beheben.
JQuery UI ist ein vom Projekt JQuery abgeleitetes Projekt und bietet Komponenten für Formulare, darunter einen Kalender. Dieser Kalender kann internationalisiert werden. Das werden wir nun zeigen.
Fügen wir zunächst [JQuery UI] zu unserem Projekt hinzu.
![]() |
![]() |
Sobald JQuery und UI installiert sind, erscheinen neue Elemente im Projekt:
![]() |
- in [1] die Bibliothek [JQuery UI] in der normalen und der minimierten Version;
- in [2] das Stylesheet von [JQuery UI];
Der Kalender JQuery UI ist standardmäßig auf Englisch. Um ihn zu internationalisieren, müssen Skripte hinzugefügt werden, die in URL [https://github.com/jquery/jquery-ui/tree/master/ui/i18n] zu finden sind:
![]() |
Um den Kalender JQuery UI auf Französisch zu erhalten, kopieren Sie den Inhalt der oben genannten Datei [jquery.ui.datepicker-fr.js] in den Ordner [Scripts] des Projekts.
![]() |
Der Code der neuen Ansicht [Action15.cshtml] wird durch Kopieren der vorherigen Ansicht [Action14.cshtml] erstellt und anschließend geändert. Wir stellen hier nur die Änderungen vor:
![]() |
@model Exemple_03.Models.ViewModel15
@using Exemple_03.Resources
@using System.Threading
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="@Model.Culture">
<head>
<meta name="viewport" content="width=device-width" />
<title>Action15</title>
...
<link rel="stylesheet" href="~/Content/themes/base/jquery-ui.css" />
<script type="text/javascript" src="~/Scripts/jquery-ui-1.10.3.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.ui.datepicker-fr.js"></script>
<script>
$(document).ready(function () {
var culture = '@Thread.CurrentThread.CurrentCulture';
Globalize.culture(culture);
$("#Date1").datepicker($.datepicker.regional['@Model.Regionale']);
});
</script>
</head>
<body>
<h3>@MyResources.titre</h3>
@using (Html.BeginForm("Action15", "Second"))
{
<table>
...
<tr>
<td>@Html.LabelFor(m => m.Date1)</td>
<td>@Html.TextBox("Date1", Model.StrDate1)</td>
<td>@Html.ValidationMessageFor(m => m.Date1)</td>
</tr>
</tbody>
</table>
<p>
<input type="submit" value="Valider" />
</p>
}
<!-- Auswahl einer Sprache -->
@using (Html.BeginForm("Lang", "Second"))
{
<table>
<tr>
<td><a href="javascript:postForm('fr-FR','/Second/Action15')">Français</a></td>
<td><a href="javascript:postForm('en-US','/Second/Action15')">English</a></td>
</tr>
</table>
}
</body>
</html>
Hinweis: Zeile 16 – passen Sie die jQuery-UI-Version an die von Ihnen heruntergeladene an.
- Zeile 15: Es wird auf das Stylesheet von JQuery und UI verwiesen;
- Zeile 16: Verweis auf die heruntergeladene Version von JQuery UI;
- Zeile 17: Hier wird auf das Skript für den französischen Kalender verwiesen, das wir gerade heruntergeladen haben;
- Zeile 34: Die Methode [Html.TextBox] generiert hier ein Tag vom Typ [text] mit der ID [input], mit der ID „[Date1]“ und dem Namen „[Date1]“;
- Zeile 19: Sobald das Laden der Seite abgeschlossen ist, wird die Funktion JQuery UI [datepicker] auf das Element mit der ID [Date1] angewendet, also das Element in Zeile 34. Diese Funktion bewirkt, dass, sobald der Benutzer den Fokus auf das Eingabefeld mit der ID [Date1] setzt, ein Kalender erscheint, über den er ein Datum eingeben kann. Die Funktion [datepicker] akzeptiert einen Parameter, der ihr die Sprache des Kalenders angibt. Die Variable [@Model.Regionale] muss folgenden Wert haben:
- 'fr' für einen französischen Kalender,
- '' für einen englischen Kalender;
Die Vorlage der vorherigen Ansicht [Action15.cshtml] ist die folgende Vorlage [ViewModel15]:
![]() |
Sein Code entspricht dem des leicht modifizierten Modells [ViewModel14]. Wir führen hier nur die Änderungen auf:
using Exemple_03.Resources;
...
using System.Web;
namespace Exemple_03.Models
{
[Bind(Exclude = "Culture,Regionale,StrDate1,FormatDate")]
public class ViewModel15 : IValidatableObject
{
...
[Display(ResourceType = typeof(MyResources), Name = "date1")]
[RegularExpression(@"\s*\d{2}/\d{2}/\d{4}\s*", ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoIncorrecte")]
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
public string Regexp1 { get; set; }
[Display(ResourceType = typeof(MyResources), Name = "date2")]
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
[DataType(DataType.Date)]
public DateTime Date1 { get; set; }
// Konstruktor
public ViewModel15()
{
// Aktuelle Kultur
Culture = HttpContext.Current.Session["lang"] as string;
cultureInfo=new CultureInfo(Culture);
// Kalenderregion JQuery
Regionale = MyResources.ResourceManager.GetObject("regionale", cultureInfo).ToString();
// Datumsformat
FormatDate = MyResources.ResourceManager.GetObject("formatDate", cultureInfo).ToString();
}
// Validierung
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// Fehlerliste
List<ValidationResult> résultats = new List<ValidationResult>();
// gleiche Fehlermeldung für alle
string errorMessage = MyResources.ResourceManager.GetObject("infoIncorrecte", cultureInfo).ToString();
...
// Regexp1
try
{
DateTime.ParseExact(Regexp1, FormatDate, cultureInfo);
}
catch
{
résultats.Add(new ValidationResult(errorMessage, new string[] { "Regexp1" }));
}
// Die Fehlerliste wird ausgegeben
return résultats;
}
// Felder außerhalb des Aktionsmusters
public string Culture { get; set; }
public string Regionale { get; set; }
public string StrDate1 { get; set; }
public string FormatDate { get; set; }
// Lokale Daten
private CultureInfo cultureInfo;
}
}
Im Vergleich zum vorherigen Modell [ViewModel14] gibt es vier zusätzliche Eigenschaften:
- Zeile 60: die Kultur der Ansicht, „fr-FR“ oder „en-US“. Diese Kultur wird im Konstruktor in Zeile 26 initialisiert;
- Zeile 61: die regionale Kultur des Kalenders JQuery, „fr“ für einen französischen Kalender, „“ für einen englischen Kalender. Dieses Feld wird in Zeile 29 des Konstruktors initialisiert;
- Zeile 63: das Datumsformat aus Zeile 15: „dd/MM/yyyy“ für ein französisches Datum, „MM/dd/yyyy“ für ein englisches Datum. Dieses Feld wird in Zeile 31 des Konstruktors initialisiert;
- Zeile 62: Die Zeichenfolge, die im Eingabefeld von [Date1] angezeigt werden soll. Dieses Feld wird durch die Aktion initialisiert;
- Zeile 47: Das Datum [Regexp1] wird nun gemäß dem Format der aktuellen Kultur überprüft.
Die Werte der Eigenschaften [Regionale] und [FormatDate] befinden sich in den Ressourcendateien [MyResources]. Die französischen Ressourcendateien [MyResources], [MyResources.fr-FR], [1] und die englische Ressourcendatei [2] ändern sich wie folgt:
![]() |
Wir sind fast fertig. Wir fügen dem Controller [SecondController] eine Aktion [Action15] hinzu:
// Aktion15
public ViewResult Action15(FormCollection formData)
{
// Methode HTTP
string method = Request.HttpMethod.ToLower();
// Vorlage
ViewModel15 modèle = new ViewModel15();
if (method == "get")
{
modèle.StrDate1 = "";
}
else
{
TryUpdateModel(modèle, formData);
modèle.StrDate1 = modèle.Date1.ToString(modèle.FormatDate);
}
// Ansicht
return View("Action15", modèle);
}
- Zeile 2: Die Methode [Action15] verarbeitet sowohl [GET] als auch [POST]. Im letzteren Fall werden die übermittelten Werte aus dem Parameter [formData] abgerufen;
- Zeile 5: Die Methode HTTP wird aus der Abfrage abgerufen;
- Zeile 7: Es wird die Vorlage für die anzuzeigende Ansicht (das Formular) erstellt;
- Zeilen 8–11: Im Falle eines Befehls [GET] wird das Eingabefeld von [Date1] mit einer leeren Zeichenfolge initialisiert;
- Zeilen 12–16: Im Falle einer Bestellung mit der Nummer [POST]:
- Zeile 14: Die Vorlage wird mit den gebuchten Werten initialisiert;
- Zeile 15: Das Eingabefeld für [Date1] wird mit einer Zeichenkette initialisiert, die dem Wert von [Date1] entspricht und gemäß der aktuellen Ländereinstellung formatiert ist: [dd/MM/yyyy] für ein französisches Datum, [MM/dd/yyyy] für ein englisches Datum;
- Zeile 18: Die Ansicht [Action15.cshtml] wird mit ihrer Vorlage angezeigt.
Führen wir einige Tests durch:
![]() |
- in [1] einen französischen Kalender, wenn die Seite auf Französisch ist;
- in [2] einen englischen Kalender, wenn die Seite auf Englisch ist;
- in [3] ein Datum im französischen Format, wenn die Seite auf Französisch ist;
- in [4] das gleiche Datum im englischen Format, wenn die Seite auf Englisch ist;
6.7. Conclusion
Wie man sieht, ist das Thema der Internationalisierung einer Anwendung ein komplexes Thema...
































