6. Internationalisierung von Ansichten
Hier werden wir uns mit dem Thema der Internationalisierung von Ansichten befassen. Dies ist ein komplexes Thema, und eine gute Beschreibung dazu findet sich im folgenden Artikel von Scott Hanselman:
Betrachten wir zunächst seine Definitionen der verschiedenen Begriffe im Zusammenhang mit der Internationalisierung von Ansichten:
Internationalisierung (i18n) | die Anwendung so gestalten, dass sie verschiedene Sprachen und Ländereinstellungen unterstützt |
Lokalisierung (l10n) | die Anwendung so gestalten, dass sie ein bestimmtes Sprach-/Ländereinstellungs-Paar unterstützt |
Globalisierung | die Kombination aus Internationalisierung und Lokalisierung |
Sprache | gesprochene Sprache – gekennzeichnet durch einen ISO-Code (fr: Französisch, es: Spanisch, en: Englisch, ...) |
Ländereinstellung | eine Variante der Sprache – ebenfalls durch einen ISO-Code gekennzeichnet (en_GB: britisches Englisch, en_US: amerikanisches Englisch, ...) |
Lassen Sie uns das Problem anhand eines ersten Beispiels angehen.
6.1. Lokalisierung reeller Zahlen
Vielleicht fällt Ihnen eine Unregelmäßigkeit im vorherigen Eingabeformular auf:

Für die reelle Zahl haben wir [0,3] eingegeben, was jedoch nicht akzeptiert wurde. Sie müssen [0,3] eingeben:

Das erwartete Format ist also das angelsächsische Format, nicht das französische. Eine kurze Online-Suche liefert einige Lösungen. Hier ist eine davon.
Die [GET]- und [POST]-Aktionen sehen nun wie folgt aus:
// Action13-GET
[HttpGet]
public ViewResult Action13Get()
{
return View("Action13Get", new ViewModel11());
}
// Action13-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 jQuery-Version an Ihre Visual Studio-Version an.
![]() |
// 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) {
//Use the Globalization plugin to parse the value
var val = Globalize.parseFloat(value);
return this.optional(element) || (
val >= param[0] && val <= param[1]);
}
});
// au chargement du document
$(document).ready(function () {
var culture = 'fr-FR';
Globalize.culture(culture);
});
Ich habe in Zeile 1 angegeben, wo dieses Skript gefunden wurde. Ich werde nicht versuchen, es zu erklären, da ich es selbst nicht verstehe. JavaScript kann manchmal etwas kryptisch sein. In den Zeilen 4, 9 und 15 wird ein [Globalize]-Objekt verwendet. Dieses wird von der jQuery-Globalization-Bibliothek bereitgestellt, die über [NuGet] bezogen werden kann:
![]() |
![]() |
- in [1] verwalten Sie die [NuGet]-Pakete für das Projekt [Example-03];
- in [2] durchsuchen Sie die Pakete online;
- in [3] den Begriff [globalization] eingeben;
- in [4] das [Globalize]-Paket für das JQuery-Projekt installieren.
Sobald das [Globalize]-Paket installiert ist, erscheint ein neuer Ordner im Ordner [Scripts]:
![]() |
- 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] die Skripte für die französische Sprache mit den Ländereinstellungen Belgien (BE), Kanada (CA), Frankreich (FR), Schweiz (CH), Luxemburg (LU) und Monaco (MC).
Das Skript [globalize.js] und unser Kulturskript [globalize.culture.fr-FR.js] müssen in die Liste der Skripte auf unserer Seite [Action13Get.cshtml] aufgenommen werden:
<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];
Schauen wir uns dieses letzte Skript einmal genauer an:
// http://blog.instance-factory.com/?p=268
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
...
// au chargement du document
$(document).ready(function () {
var culture = 'fr-FR';
Globalize.culture(culture);
});
Die Zeilen 10–13 setzen die clientseitige Kultur auf [fr-FR]:
- Zeile 10: Die jQuery-Funktion [ready] wird ausgeführt, sobald das Dokument, das das Skript enthält, vollständig vom Browser geladen wurde;
- Zeilen 11–12: Die clientseitige Kultur wird auf [fr-FR] gesetzt. Damit dies funktioniert, 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:

Wir können nun [0,3] als reelle Zahl eingeben, was zuvor nicht möglich war. Allerdings stößt man auf ein weiteres Problem:
![]()
Oben ermöglicht uns die clientseitige Validierung die Eingabe von [11,2] in der angelsächsischen Schreibweise. Dieser Wert wird auf der Serverseite nicht akzeptiert, wenn wir das Formular absenden:
![]()
Wir müssen [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...
Wenden wir uns nun der Internationalisierung von Ansichten zu. Wir knüpfen an das Beispiel des vorherigen Formulars an und bieten es in zwei Sprachen an: Französisch und Englisch.
6.2. Verwaltung einer Kultur
Die Sprache der Ansichten wird durch das Objekt [Thread.CurrentThread.CurrentUICulture] gesteuert. Um Seiten in der Kultur [fr-FR] anzuzeigen, schreiben wir:
Die Lokalisierung (Daten, Zahlen, Währungen, Zeiten usw.) wird durch das Objekt [Thread.CurrentThread.CurrentCulture] gesteuert. Ähnlich wie zuvor schreiben wir:
Diese beiden Anweisungen könnten im Konstruktor jedes Controllers in der Anwendung platziert werden. Wir möchten diesen Code, der allen Controllern gemeinsam ist, jedoch möglicherweise auslagern. Wir werden diesen Ansatz verfolgen.
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 für den Controller [I18NController] lautet wie folgt:
using System.Threading;
using System.Web;
using System.Web.Mvc;
namespace Exemples.Controllers
{
public abstract class I18NController : Controller
{
public I18NController()
{
// retrieve the context of the current query
HttpContext httpContext = HttpContext.Current;
// examine the query for the [lang] parameter
// look for it in the URL parameters
string langue = httpContext.Request.QueryString["lang"];
if (langue == null)
{
// look for it in the posted parameters
langue = httpContext.Request.Form["lang"];
}
if (langue == null)
{
// search for it in the user's session
langue = httpContext.Session["lang"] as string;
}
if (langue == null)
{
// 1st header parameter HTTP AcceptLanguages
langue = httpContext.Request.UserLanguages[0];
}
if (langue == null)
{
// culture fr-FR
langue = "fr-FR";
}
// put your tongue in session
httpContext.Session["lang"] = langue;
// changing thread cultures
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 wird als [abstract] deklariert, um eine direkte Instanziierung zu verhindern: Sie kann nur abgeleitet und verwendet werden;
- Zeile 9: Der Klassenkonstruktor wird jedes Mal ausgeführt, wenn ein von [I18NController] abgeleiteter Controller instanziiert wird;
- Zeile 12: Wir rufen den Kontext der HTTP-Anfrage ab, die gerade vom Controller verarbeitet wird;
- Zeile 15: Wir gehen davon aus, dass die Sprache durch einen [lang]-Parameter festgelegt wird, der an verschiedenen Stellen zu finden ist. Wir suchen in der folgenden Reihenfolge:
- Zeile 15: in den URL-Parametern [?lang=en-US],
- Zeile 19: in den übermittelten Parametern [lang=de],
- Zeile 24: in der Sitzung des Benutzers,
- Zeile 29: in den vom HTTP-Client gesendeten Spracheinstellungen,
- Zeile 26: Wenn nichts gefunden wird, wird die Locale auf [fr-FR] gesetzt;
- Zeile 37: Wir speichern die Locale in der Sitzung. Von dort wird sie für nachfolgende Anfragen abgerufen. Der Benutzer kann sie ändern, indem er sie in die Parameter einer GET- oder POST-Anfrage einfügt;
- Zeilen 39–40: Wir legen die Locale für die Ansicht fest, die nach der Verarbeitung der aktuellen Anfrage 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
{
// 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);
}
}
}
- Zeile 7: [SecondController] extends [I18NController]. Dies stellt sicher, dass die Ländereinstellung für die anzuzeigende Ansicht initialisiert wurde;
- Zeile 13: Wir verwenden das View-Modell [ViewModel14], das wir in Kürze vorstellen werden;
- Zeilen 13 und 20: Die Ansicht [Action14Get.cshtml] zeigt das Formular an.
6.3. Internationalisierung des View-Modells [ViewModel14]
Das View-Modell [ViewModel14] sieht wie folgt aus:
![]() |
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; }
// validation
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// error list
List<ValidationResult> résultats = new List<ValidationResult>();
// the same error msg for all
string errorMessage=MyResources.ResourceManager.GetObject("infoIncorrecte", new CultureInfo(System.Web.HttpContext.Current.Session["lang"] as string)).ToString();
// Date 1
if (Date1.Date <= DateTime.Now.Date)
{
résultats.Add(new ValidationResult(errorMessage, new string[] { "Date1" }));
}
// Email1
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" }));
}
// return the list of errors
return résultats;
}
}
}
Dieses Modell ist die internationalisierte Version des vorherigen Modells [ViewModel11]. Wir werden den Internationalisierungsmechanismus für das erste Attribut der ersten Eigenschaft beschreiben. Die anderen Attribute folgen demselben Mechanismus.
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
public string Chaine1 { get; set; }
Im vorherigen Modell [ViewModel11] lauteten diese Zeilen wie folgt:
[Required(ErrorMessage = "Information requise")]
public string Chaine1 { get; set; }
In der internationalisierten Version, Zeile 1, wird der anzuzeigende Text in einer Ressourcendatei abgelegt. Hier heißt diese Datei [MyResources.resx] (typeof) und wurde im Projektstammverzeichnis abgelegt. Sie wird als Ressourcendatei bezeichnet.
![]() |
Wir haben hier drei Ressourcendateien erstellt:
- [MyResources]: Standardressource, wenn keine Ressource für die aktuelle Ländereinstellung vorhanden ist;
- [MyResources.fr-FR]: Ressource für die Locale [fr-FR];
- [MyResources.en-US]: Ressource für die Locale [en-US];
Um eine Ressourcendatei zu erstellen, führen Sie die folgenden Schritte aus [1, 2, 3]:
![]() |
![]() |
Dadurch wird die Ressourcendatei [MyResources2.resx] erstellt. Wenn Sie darauf doppelklicken, wird die folgende Seite angezeigt:
![]() |
Eine Ressourcendatei ist ein Wörterbuch, das Schlüssel und die diesen Schlüsseln zugeordneten Werte enthält. Geben Sie den Schlüssel in [1], den Wert in [2] und den Ressourcenbereich in [3] ein. Damit diese Ressourcen lesbar sind, müssen sie den Bereich [Public] haben. Kehren wir zu der Zeile zurück:
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
- [ErrorMessageResourceType]: bezieht sich auf die Ressourcendatei. Der Parameter [typeof] ist der Dateiname. Dieser wird während des Kompilierungsprozesses in eine Klasse umgewandelt, und deren Binärdatei wird in die Projektassembly aufgenommen. Letztendlich ist [MyResources] also der Name der Ressourcenklasse;
- [ErrorMessageResourceName = "infoRequise"]: bezieht sich auf einen Schlüssel in der Ressourcendatei. Letztendlich bedeutet die Zeile, dass die anzuzeigende Fehlermeldung der Wert in der [MyResources]-Datei ist, der dem Schlüssel [infoRequise] zugeordnet ist.
Um den Schlüssel [infoRequise] und den zugehörigen Wert in der Datei [MyResources] zu erstellen, gehen Sie wie folgt vor:
![]() |
Geben Sie den Schlüssel in [1], den Wert in [2] und den Ressourcenbereich in [3] ein.
Es gibt noch einen letzten Punkt zu klären: den Namespace der Klasse [MyResources]. Dieser ist in den Eigenschaften der Datei [MyResources.resx] definiert:
![]() |
In [1] definieren wir den Namespace der Klasse [MyResources], die aus der Ressourcendatei [MyResources.resx] erstellt wird. Kehren wir zu der internationalisierten Zeile zurück, die wir untersucht haben:
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "infoRequise")]
Der typeof-Operator erwartet eine Klasse, in diesem Fall die Klasse [MyResources]. Damit diese Klasse gefunden werden kann, muss ihr Namespace 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 kompiliert worden sein. Der Code für diese Klasse ist in der Datei [MyResources.Designer.cs] zu finden:
![]()
Wenn Sie auf diese Datei doppelklicken, gelangen Sie 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 Klassen-Namespace;
- Zeile 11: Der Schlüssel [infoRequise] ist zu einer statischen Eigenschaft der Klasse [MyResources] geworden. Er ist über die Notation [MyResources.infoRequise] zugänglich. Beachten Sie außerdem, dass diese Eigenschaft den Gültigkeitsbereich [public] hat. Ohne diesen wäre sie nicht zugänglich. Es ist wichtig, dies zu beachten, da der Standard-Gültigkeitsbereich leider [internal] ist und dies zu schwer nachvollziehbaren Fehlern führen kann, wenn man vergisst, den Gültigkeitsbereich zu ändern.
Warum gibt es nun drei Ressourcendateien?
![]()
Wir haben [MyResources.resx] erstellt. Dies ist die Stammressource. Als Nächstes erstellen wir so viele [MyResources.locale.resx]-Ressourcendateien, wie es zu verwaltende Sprachumgebungen (Sprachen) gibt. Hier behandeln wir Französisch [fr-FR] und amerikanisches Englisch [en-US]. Wenn die aktuelle Sprachumgebung 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 Sprache nicht erkannt wird. Der endgültige Inhalt von [MyResources.fr-FR.resx] ist identisch und wird durch einfaches Kopieren der Datei erhalten.
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:
// validation
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// liste des erreurs
List<ValidationResult> résultats = new List<ValidationResult>();
// le même msg d'erreur pour tous
string errorMessage=MyResources.ResourceManager.GetObject("infoIncorrecte", new CultureInfo(System.Web.HttpContext.Current.Session["lang"] as string)).ToString();
// Date 1
if (Date1.Date <= DateTime.Now.Date)
{
résultats.Add(new ValidationResult(errorMessage, new string[] { "Date1" }));
}
...
// on rend la liste des erreurs
return résultats;
}
Zeile 7 zeigt, wie eine Meldung aus der Ressourcendatei [MyResources] abgerufen wird. Hier möchten wir die Meldung abrufen, die dem Schlüssel [infoIncorrecte] in der aktuellen Kultur zugeordnet ist:
- 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 [I18NController]-Controller die aktuelle Kultur in der Sitzung festlegt, die mit dem Schlüssel [lang] verknüpft ist. Die aktuelle Kultur kann daher mit System.Web.HttpContext.Current.Session["lang"] als string abgerufen werden;
- Die Ressource wird als [object] abgerufen. Um die Fehlermeldung zu erhalten, wenden wir die Methode [ToString] darauf an.
6.4. Internationalisierung der Ansicht [Action14Get.cshtml]
Wir aktualisieren die Formularanzeige wie folgt:

@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>
<!-- choice of language -->
@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 – Achte darauf, dass die jQuery-Version mit deiner Visual Studio-Version übereinstimmt.
Beginnen wir mit dem einfachsten Teil, den Zeilen 36–38. Sie nutzen die statischen Eigenschaften der soeben beschriebenen Klasse [MyResources]. Um auf die Klasse [MyResources] zugreifen zu können, müssen Sie deren Namespace importieren (Zeile 2).
Bei internationalisierten Meldungen müssen Sie auch diejenigen einbeziehen, die vom clientseitigen Validierungs-Framework angezeigt werden. Verwenden Sie dazu die jQuery-Bibliotheken in den Zeilen 17–19. Wir verwenden die jQuery-Dateien für die beiden von uns unterstützten Sprachumgebungen: [fr- FR] und [en-US]. Vielleicht erinnern Sie sich außerdem daran, dass die Ansicht [Action13Get] das folgende JavaScript-Skript [myscripts.js] verwendet hat:
// document loading
$(document).ready(function () {
var culture = 'fr-FR';
Globalize.culture(culture);
});
Nun ist die Kultur nicht mehr nur [fr-FR]; sie variiert. Daher werden diese Zeilen nun von der Ansicht [Action14Get] selbst in den Zeilen 21–26 generiert. Diese sechs Zeilen werden in die an den Client gesendete HTML-Seite aufgenommen.
- Zeile 23: Die JavaScript-Variable [culture] wird mit der aktuellen Kultur des Threads initialisiert, der die Anfrage bearbeitet. Sie erinnern sich vielleicht, dass diese durch den Konstruktor der Klasse [I18NController] initialisiert wurde:
// on met la langue en session
httpContext.Session["lang"] = langue;
// on modifie les cultures du thread
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 HTML-Seite eingebettete JavaScript-Skript:
<script>
$(document).ready(function () {
var culture = 'en-US';
Globalize.culture(culture);
});
</script>
Wir haben bereits erwähnt, dass die Funktion [$(document).ready] ausgeführt wird, sobald der Browser die Seite vollständig geladen hat. Durch ihre Ausführung wird die Kultur des clientseitigen Validierungs-Frameworks festgelegt. 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:
<!-- language selection -->
@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 ist 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. Derzeit sehen wir keine Werte, die gesendet werden könnten;
- 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) {
// on récupère le deuxième formulaire du document
var form = document.forms[1];
// on lui ajoute l'hidden attribute lang
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", "lang");
hiddenField.setAttribute("value", lang);
// ajout du champ caché dans le formulaire
form.appendChild(hiddenField);
// on lui ajoute l'hidden attribute url
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", "url");
hiddenField.setAttribute("value", url);
// ajout du champ caché dans le formulaire
form.appendChild(hiddenField);
// soumission
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) {
//Use the Globalization plugin to parse the value
var val = Globalize.parseFloat(value);
return this.optional(element) || (
val >= param[0] && val <= param[1]);
}
});
Die Zeilen 22–40 entsprechen denen, die bereits im Skript [myscripts.js] aus dem vorherigen Beispiel enthalten sind. Wir werden hier nicht noch einmal darauf eingehen. Die Funktion [postForm], die ausgeführt wird, wenn auf die Sprachlinks geklickt wird, befindet sich in den Zeilen 1–20:
- Zeile 1: Die Funktion nimmt zwei Parameter entgegen: [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 im 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: Wir rufen eine Referenz auf das zweite Formular im Dokument ab;
- Zeilen 5–8: Wir erstellen programmgesteuert das Tag
wobei [xx-XX] der Wert des Parameters [lang] der Funktion ist;
- Zeile 10: Auch hier fügen wir dieses Feld mithilfe von Code zum zweiten Formular hinzu. Letztendlich verhält es sich so, als wäre dieses Feld von Anfang an im zweiten Formular vorhanden gewesen. Sein Wert wird daher übermittelt. Genau das wollten wir erreichen;
- Zeilen 11–17: Wir wiederholen denselben Vorgang für ein Tag
wobei [url] der Wert des Parameters [url] der Funktion ist;
- Zeile 19: Das zweite Formular wird nun gesendet. An welche URL?
Wir müssen zurück zum Code für das zweite Formular auf der Seite [Action14Get.cshtml] gehen:
@using (Html.BeginForm("Lang", "Second"))
{
...
}
Das Formular wird daher an die URL [/Second/Lang] gesendet. Anschließend müssen wir im Controller [SecondController] eine Aktion [Lang] definieren. Diese sieht wie folgt aus:
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);
}
// language
[HttpPost]
public RedirectResult Lang(string url)
{
// we redirect the client to url
return new RedirectResult(url);
}
}
- Zeile 18: Die Aktion reagiert nur auf einen [POST];
- Zeile 19: Sie ruft nur den Parameter namens [url] ab;
- Zeile 22: Sie weist den Client an, zu dieser URL umzuleiten.
Aber was ist mit dem Parameter namens [lang] passiert? Wir müssen uns nun daran erinnern, dass der Controller [SecondController] von der Klasse [I18NController] abgeleitet ist (Zeile 1 unten). Es ist dieser Controller, der den Parameter [lang] verarbeitet:
public abstract class I18NController : Controller
{
public I18NController()
{
// on récupère le contexte de la requête courante
HttpContext httpContext = System.Web.HttpContext.Current;
// on examine la requête à la recherche du paramètre [lang]
// on le cherche dans les paramètres de l'URL
string langue = httpContext.Request.QueryString["lang"];
if (langue == null)
{
// on le cherche dans les paramètres postés
langue = httpContext.Request.Form["lang"];
}
if (langue == null)
{
// on le cherche dans la session de l'utilisateur
langue = httpContext.Session["lang"] as string;
}
if (langue == null)
{
// 1er paramètre de l'entête HTTP AcceptLanguages
langue = httpContext.Request.UserLanguages[0];
}
if (langue == null)
{
// culture fr-FR
langue = "fr-FR";
}
// on met la langue en session
httpContext.Session["lang"] = langue;
// on modifie les cultures du thread
Thread.CurrentThread.CurrentCulture = new CultureInfo(langue);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
}
In unserem Beispiel wird der Parameter [lang] per Referenz übergeben. Er wird daher in Zeile 13 gefunden, in Zeile 31 in der Sitzung gespeichert und in den Zeilen 33–34 zur Aktualisierung der Kultur des aktuellen Threads verwendet.
Was passiert als Nächstes? Schauen wir uns die Links 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 Weiterleitungs-URL lautet [/Second/Action14Get]. Die Aktion [Action14Get] wird daher ausgeführt:
public class SecondController : I18NController
{
// Action14-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()
{
// on récupère le contexte de la requête courante
HttpContext httpContext = System.Web.HttpContext.Current;
// on examine la requête à la recherche du paramètre [lang]
// on le cherche dans les paramètres de l'URL
string langue = httpContext.Request.QueryString["lang"];
if (langue == null)
{
// on le cherche dans les paramètres postés
langue = httpContext.Request.Form["lang"];
}
if (langue == null)
{
// on le cherche dans la session de l'utilisateur
langue = httpContext.Session["lang"] as string;
}
if (langue == null)
{
// 1er paramètre de l'entête HTTP AcceptLanguages
langue = httpContext.Request.UserLanguages[0];
}
if (langue == null)
{
// culture fr-FR
langue = "fr-FR";
}
// on met la langue en session
httpContext.Session["lang"] = langue;
// on modifie les cultures du thread
Thread.CurrentThread.CurrentCulture = new CultureInfo(langue);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
}
Diesmal wird der Parameter [lang] in Zeile 18 in der Sitzung gefunden. Nehmen wir an, sein Wert ist [en-US]. Diese Kultur wird somit zur Kultur des Threads, der die Anfrage ausführt (Zeilen 33–34). Kehren wir zur Aktion [Action14Get] zurück:
// Action14-GET
[HttpGet]
public ViewResult Action14Get()
{
return View("Action14Get", new ViewModel14());
}
In Zeile 5 wird eine Instanz des ViewModels [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. Fehlermeldungen werden daher auf Englisch angezeigt.
Sobald das Modell [ViewModel14] instanziiert ist, 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 aktuelle Thread-Locale [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 US-Formate (Datum, Währung, Zahlen usw.) verwendet. Aus demselben Grund werden die Meldungen in den Zeilen 30–32 aus der Ressourcendatei [MyResources.en-US.resx] abgerufen und sind daher auf Englisch.
6.5. Beispiele für die Ausführung
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 wir uns den Quellcode der Seite ansehen, können wir erkennen, dass diese Fehlermeldungen in die Seite eingebettet wurden, was bedeutet, dass sie von der ASP.NET-Ansicht [Action14Get] und ihrem Ansichtsmodell [ViewModel14] generiert werden:
<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
Internationalisierung ist ein komplexes Thema. Betrachten wir die Eigenschaft [Date1] und ihren Kalender:

Wir sehen, dass es sich um einen französischen Kalender handelt, obwohl die Kultur der Seite [en-US] ist. In HTML5 gibt es ein [lang]-Attribut, mit dem Sie die Sprache der Seite oder einer Seitenkomponente festlegen können. Wir können dann den folgenden Code in die Ansicht [Action14Get.cshtml] 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: Abrufen der Locale aus der Sitzung;
- Zeile 11: Wir setzen das [lang]-Attribut der Seite auf diesen Wert.
Tests zeigen, dass der Kalender auf Französisch bleibt, auch wenn die Seite ansonsten auf Englisch angezeigt wird. Es gibt auch ein Problem mit dem anderen Datumsfeld 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 (21/10/2013) ist. Wir werden versuchen, diese beiden Probleme mit einer neuen Ansicht und einem neuen Ansichtsmodell zu beheben.
jQuery UI ist ein vom jQuery-Projekt abgeleitetes Projekt und bietet Formularkomponenten, darunter einen Kalender. Dieser Kalender kann lokalisiert werden. Das werden wir nun demonstrieren.
Fügen wir zunächst [jQuery UI] zu unserem Projekt hinzu.
![]() |
![]() |
Sobald jQuery UI installiert ist, erscheinen neue Elemente im Projekt:
![]() |
- in [1] die [jQuery UI]-Bibliothek sowohl in der normalen als auch in der minimierten Version;
- in [2] das [JQuery UI]-Stylesheet;
Der jQuery-UI-Kalender ist standardmäßig auf Englisch. Um ihn zu internationalisieren, müssen Sie Skripte hinzufügen, die unter der URL [https://github.com/jquery/jquery-ui/tree/master/ui/i18n] zu finden sind:
![]() |
Um den jQuery-UI-Kalender auf Französisch anzuzeigen, kopieren Sie den Inhalt der oben genannten Datei [jquery.ui.datepicker-fr.js] in den Ordner [Scripts] des Projekts.
![]() |
Den Code für die neue Ansicht [Action15.cshtml] erhalten Sie, indem Sie die vorherige Ansicht [Action14.cshtml] kopieren und anschließend anpassen. Wir zeigen hier nur die Änderungen:
![]() |
@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>
}
<!-- language selection -->
@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: Passen Sie in Zeile 16 die jQuery-UI-Version an die von Ihnen heruntergeladene Version an.
- Zeile 15: Verweisen Sie auf das jQuery-UI-Stylesheet;
- Zeile 16: Verweisen Sie auf die heruntergeladene Version von jQuery UI;
- Zeile 17: Verweisen Sie auf das soeben heruntergeladene Skript für den französischen Kalender;
- Zeile 34: Die Methode [Html.TextBox] generiert hier ein [input]-Tag vom Typ [text] mit der ID [Date1] und dem Namen [Date1];
- Zeile 19: Sobald die Seite vollständig geladen ist, wird die jQuery-UI-Funktion [datepicker] auf das Element mit der ID [Date1] angewendet, d. h. auf das Element in Zeile 34. Diese Funktion sorgt dafür, dass beim Fokus auf das Eingabefeld [Date1] ein Kalender erscheint, über den der Benutzer ein Datum auswählen kann. Die [datepicker]-Funktion akzeptiert einen Parameter, der die Sprache des Kalenders angibt. Die Variable [@Model.Regionale] muss auf
- 'fr' für einen französischen Kalender,
- '' für einen englischen Kalender;
Das Modell für die vorherige Ansicht [Action15.cshtml] ist das folgende [ViewModel15]-Modell:
![]() |
Der Code entspricht dem des Modells [ViewModel14], ist jedoch leicht modifiziert. Wir zeigen hier nur die Änderungen:
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; }
// manufacturer
public ViewModel15()
{
// Culture of the moment
Culture = HttpContext.Current.Session["lang"] as string;
cultureInfo=new CultureInfo(Culture);
// Regional calendar JQuery
Regionale = MyResources.ResourceManager.GetObject("regionale", cultureInfo).ToString();
// date format
FormatDate = MyResources.ResourceManager.GetObject("formatDate", cultureInfo).ToString();
}
// validation
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// error list
List<ValidationResult> résultats = new List<ValidationResult>();
// the same error msg for all
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" }));
}
// return the list of errors
return résultats;
}
// fields outside the action model
public string Culture { get; set; }
public string Regionale { get; set; }
public string StrDate1 { get; set; }
public string FormatDate { get; set; }
// local data
private CultureInfo cultureInfo;
}
}
Im Vergleich zum vorherigen Modell [ViewModel14] haben wir vier zusätzliche Eigenschaften:
- Zeile 60: die Ländereinstellung der Ansicht, 'fr-FR' oder 'en-US'. Diese Ländereinstellung wird im Konstruktor in Zeile 26 initialisiert;
- Zeile 61: die regionale Kultur des jQuery-Kalenders, „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 [Date1] angezeigt werden soll. Dieses Feld wird durch die Aktion initialisiert;
- Zeile 47: Das Datum [Regexp1] wird nun gemäß dem Format der aktuellen Ländereinstellung validiert.
Die Werte der Eigenschaften [Regionale] und [FormatDate] befinden sich in den [MyResources]-Ressourcendateien. 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:
// Action15
public ViewResult Action15(FormCollection formData)
{
// method HTTP
string method = Request.HttpMethod.ToLower();
// model
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);
}
// view display
return View("Action15", modèle);
}
- Zeile 2: Die Methode [Action15] verarbeitet sowohl [GET]- als auch [POST]-Anfragen. Im letzteren Fall werden die übermittelten Werte im Parameter [formData] abgerufen;
- Zeile 5: Die HTTP-Methode der Anfrage wird abgerufen;
- Zeile 7: Die anzuzeigende Ansichtsvorlage (das Formular) wird erstellt;
- Zeilen 8–11: Im Falle einer [GET]-Anfrage wird das Eingabefeld [Date1] mit einer leeren Zeichenkette initialisiert;
- Zeilen 12–16: Im Falle einer [POST]-Anfrage:
- Zeile 14: Das Modell wird mit den übermittelten Werten initialisiert;
- Zeile 15: Das Eingabefeld [Date1] wird mit einer Zeichenkette initialisiert, die dem Wert von [Date1] entspricht, formatiert gemäß der aktuellen Ländereinstellung [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] dasselbe Datum im englischen Format, wenn die Seite auf Englisch ist;
6.7. Fazit
Wie wir sehen können, ist das Thema der Anwendungsinternationalisierung ein komplexes...
























