Bootstrap.Datepicker.Globalize を利用した国際化

日付入力用の Javascript に Bootstrap Datepicker がありますが、Bootstrap.Datepicker.Globalize を利用すると jquery-globalize を用いた国際化対応ができます。今回、これを試してみるとともに、ブラウザの言語設定に基いて姓・名の表示順を変更させてみました。

画面はこんな感じです。

言語設定日本語の一覧画面
言語設定日本語の一覧画面

言語設定英語の一覧画面
言語設定英語の一覧画面
言語設定日本語の追加画面
言語設定日本語の追加画面
言語設定英語の追加画面
言語設定英語の追加画面

ブラウザの言語設定は、メニューバーの「ツール」から「(インターネット)オプション」「言語(設定)」ボタンをクリックして「言語(の優先順位)」の最上位にあるものを変更することで変えることができます(Chrome は「設定」から「詳細設定を表示」「言語を管理」で「言語」欄の最上位をドラッグ・アンド・ドロップで移動)。

姓・名の表示順の変更は「JavaScript で動的に変更」する方法と「View の中で if 文による条件分岐」が考えられますが、今回は一覧画面では JavaScript、追加画面では View での if 文、で実装しています。なお、詳細表示機能、編集機能、削除機能は実装していません 😛

まずはプロジェクトを作成します。Visual Studio 2013 では ASP.NET Web アプリケーションを選択して、テンプレートから MVC を選択します。認証は今回使わないので、「認証の変更」ボタンをクリックして認証なしを選択して OK ボタンをクリックしてプロジェクトを生成します。
Visusl Studio 2012 の場合は、Twitter Bootstrap を利用するので、ASP.NET MVC 4 での Twitter Bootstrap の利用 を参照してください(Visual Studio 2013 で MVC 4 のプロジェクトを生成する場合も同様)。

プロジェクトの用意ができたら、NuGet から Bootstrap.Datepicker をインストールし、続いて、Bootstrap.Datepicker.Globalize をインストールします(Bootstrap.Datepicker.Globalize を先にインストールすると Bootstrap.Datepicker 1.1.3.1 がインストールされますが、この状態だと月の前後を選択するアイコンが表示されない状態が発生します。詳しくは Visual Studio 2013 での備忘録 の「Bootstrap.Datepicker.Globalize 1.0.0 をインストールすると前後の月へ移動する左右矢印アイコンが表示されない」をご覧ください)。

続いて、クライアントでのデータ検証用に jQuery.Validation と Microsoft.jQuery.Unobtrusive.Validation の バージョン 3.0.0 をインストールします。

MVC 5 で作成している場合は、続いて DB 保存用に EntityFramework と EntityFramework.SqlServerCompact を NuGet からインストールします(より正確に記述すると、EntityFramework.SqlServerCompact のインストールが必要なのは Entity Framework 6 をインストールした場合です。MVC 4 はデフォルトで Entity Framework 5 がインストールされています(逆に言うと、EF5 から EF6 へ更新をかけると EntityFramework.SqlServerCompact が必要になる))。

次に、姓・名の表示順の変更を行う JavaScript ファイルを作成します。Scripts フォルダに TableChgSeqL10n.js ファイルを作成します。

function TableChgSeqL10n(setCulture, culture) {
    if (setCulture == culture) return; // 設定言語とブラウザの言語が同じだったらリターン

    // 順序の変更情報を取得
    var target = $("table[data-role='targetL10n'] th");
    var targetLen = target.length;
    var array = {};
    var item = [];
    for (var i = 0; i < targetLen; ++i) {
        var targetVal = target.eq(i).attr("data-l10nposition");
        if (typeof targetVal !== "undefined") {
            if (targetVal in array) {
                // 移動相手側の位置を追加
                item = array[targetVal];
                if (item.length == 3)
                    throw new Error("移動対象が3つ以上指定されています(" + item[0] + ")");
                item.push(i);
            }
            else {
                // 移動対象の位置を作成(item[0]:対象のカスタムデータ属性名, item[1]:移動対象の位置, item[2]: 移動相手側の位置)
                item = [ targetVal, i ];
                array[targetVal] = item;
            }
        }
    }

    var hashLength = function (array) {
        var len = 0;
        for (var key in array) { ++len; }
        return len;
    }

    if (hashLength(array) == 0) return; // 移動対象がなかったらリターン

    // 順序変更用のマップを作成
    var seq = new Array(targetLen);
    for (var i = 0; i < targetLen; ++i) {
        seq[i] = i;
    }
    for (var key in array) {
        data = array[key];
        if (data.length == 2)
            throw new Error("移動対象が1つしか指定されていません(" + data[0] + ")");
        seq[data[1]] = data[2];
        seq[data[2]] = data[1];
    }

    // 順序変更を実施
    var rewriteSeq = function (tr, seq, target) {
        // 対象要素を退避
        var items = tr.children(target);
        var itemsLen = items.length;
        if (itemsLen == 0) return; // 対象の要素がなかったらリターン

        // 対象要素を削除
        tr.children(target).remove();
        // 順序を入れ替えて退避した要素を追加
        for (var i = 0; i < itemsLen; ++i) {
            tr.append(items[seq[i]]);
        }
    }

    var items = "";
    var trs = $("table[data-role='targetL10n'] tr");
    var trsLen = target.length;
    for (var i = 0; i < trsLen; ++i) {
        rewriteSeq(trs.eq(i), seq, "th");
        rewriteSeq(trs.eq(i), seq, "td");
    }
}

適宜コメントを記入したので、やっていることは分かるかと思います。

次に、インストール及び作成した JavaScript ファイルを利用するために App_Start/BundleConfig.cs を変更します。

using System.Web;
using System.Web.Optimization;

namespace L10n001
{
    public class BundleConfig
    {
        // バンドルの詳細については、http://go.microsoft.com/fwlink/?LinkId=301862 を参照してください
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                        "~/Scripts/jquery-{version}.js"));

            // 開発と学習には、Modernizr の開発バージョンを使用します。次に、実稼働の準備が
            // できたら、http://modernizr.com にあるビルド ツールを使用して、必要なテストのみを選択します。
            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                        "~/Scripts/modernizr-*"));

            bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                      "~/Scripts/jquery.validate.js",
                      "~/Scripts/jquery.validate.unobtrusive.js"));

            bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
                      "~/Scripts/bootstrap.js",
                      "~/Scripts/respond.js"));

            bundles.Add(new ScriptBundle("~/bundles/globalize").Include(
                      "~/Scripts/globalize/globalize.js"));

            bundles.Add(new ScriptBundle("~/bundles/tableChgSeqL10n").Include(
                      "~/Scripts/TableChgSeqL10n.js"));

            bundles.Add(new ScriptBundle("~/bundles/bootstrap-datepicker").Include(
                      "~/Scripts/bootstrap-datepicker.js",
                      "~/Scripts/bootstrap-datepicker-globalize.js"));

            bundles.Add(new StyleBundle("~/Content/css").Include(
                      "~/Content/bootstrap.css",
                      "~/Content/bootstrap-theme.css",
                      "~/Content/bootstrap-datepicker.css",
                      "~/Content/site.css"));
        }
    }
}

変更点は次のとおりです。

  • ~/bundles/jqueryval の設定
  • ~/bundles/globalize の設定
  • ~/bundles/tableChgSeqL10n の設定
  • ~/bundles/bootstrap-datepicker の設定
  • ~/Content/css の変更

次に、L10n 対応のため、カルチャをブラウザの言語設定から取得するように Web.config に設定を行います(ついでに接続文字列も)。

<configuration>
  <configSections>
...snip...
  </configSections>
  <connectionStrings>
    <add name="PersonDb" connectionString="Data Source=|DataDirectory|\PersonDb.sdf" providerName="System.Data.SqlServerCe.4.0" />
  </connectionStrings>
  <appSettings>
...snip...
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <globalization culture="auto" uiCulture="auto" />
  </system.web>

「…snip…」のところは前後関係の目印に残しているもので、必要な設定は、「connectionStrings の設定」と「system.web の globalization culture="auto" uiCulture="auto" の設定」です。

次に、ブラウザの言語設定に基いて表示する文字列を保存するリソースファイルです。今回は、日本語と英語を用意しておきます。プロジェクトに Resources フォルダを追加します。追加した Resources フォルダに ViewText.resx ファイルを作成します (Resources フォルダを右クリックして「追加」「新しい項目」をクリックして、新しい項目の追加ダイアログで「アセンブリ リソース ファイル」を選択して ViewText.resx を作成)。ViewText.resx のアクセス修飾子は「Public」にしておいてください。
内容は次のとおりです。

HomeCreateBackList : Back to List
HomeCreateButton : Create
HomeCreateContact : Contact
HomeCreateTitle : Create
HomeIndexCreateNew : Create New
HomeIndexDelete : Delete
HomeIndexDetails : Details
HomeIndexEdit : Edit
HomeIndexTitle : List
LayoutAbout : About
LayoutAppName : L10n
LayoutContact : Contact
LayoutFooter : MakCraft
LayoutHome : Home
LayoutTitle : - L10n Application

次に ViewText.resx をコピーして ViewText.ja.resx を作ります(ViewText.resx を保存してからソリューション エクスプローラーで ViewText.resx を右クリックしてコピーし、Resources フォルダを右クリックして貼り付けし、作成された ViewText – コピー.resx を右クリックして名前の変更)。ViewText.ja.resx のアクセス修飾子は「コード生成なし」にしておいてください。
内容は次のとおりです。

HomeCreateBackList : 一覧に戻る
HomeCreateButton : 新規作成
HomeCreateContact : 連絡先
HomeCreateTitle : 新規作成
HomeIndexCreateNew : 新規作成
HomeIndexDelete : 削除
HomeIndexDetails : 詳細
HomeIndexEdit : 編集
HomeIndexTitle : 一覧
LayoutAbout : このサイトについて
LayoutAppName : L10n
LayoutContact : コンタクト
LayoutFooter : MakCraft
LayoutHome : ホーム
LayoutTitle : - L10n アプリケーション

同様に Validate.resx ファイルを作成します。Validate.resx のアクセス修飾子は「Public」にしておいてください。

AgeRage : Must be between 10 and 130
AgeRequired : Age is required
Birthday : Birthday
BirthdayRequired : Birthday is required
Email : Email address
EmailInvalid : Email is not valid
EmailRequired : Email is required
FamilyNameEng : Family name(English)
FamilyNameEngLong : Must be less than 50 characters
FamilyNameEngRequired : Family name(English) is required
FamilyNameJpn : Family name(Japanese)
FamilyNameJpnLong : Must be less than 50 characters
FamilyNameJpnRequired : Family name(Japanese) is required
PersonalNameEng : Personal name(English)
PersonalNameEngLong : Must be less than 50 characters
PersonalNameEngRequired : Personal name(English) is required
PersonalNameJpn : Personal name(Japanese)
PersonalNameJpnLong : Must be less than 50 characters
PersonalNameJpnRequired : Personal name(Japanese) is required

同様に Validate.ja.resx ファイルを作成します。Validate.ja.resx のアクセス修飾子は「コード生成なし」にしておいてください。

AgeRage : 入力できる年齢は 10 歳から 130 歳までの範囲です。
AgeRequired : 年齢は必須です。
Birthday : 誕生日
BirthdayRequired : 誕生日は必須です。
Email : メールアドレス
EmailInvalid : メールアドレスの形式が不正です。
EmailRequired : メールアドレスは必須です。
FamilyNameEng : 姓(ローマ字)
FamilyNameEngLong : 入力できる姓は 50 文字までです。
FamilyNameEngRequired : 姓(ローマ字)は必須です。
FamilyNameJpn : 姓
FamilyNameJpnLong : 入力できる姓は 50 文字までです。
FamilyNameJpnRequired : 姓は必須です。
PersonalNameEng : 名前(ローマ字)
PersonalNameEngLong : 入力できる名前は 50 文字までです。
PersonalNameEngRequired : 名前(ローマ字)は必須です。
PersonalNameJpn : 名前
PersonalNameJpnLong : 入力できる名前は 50 文字までです。
PersonalNameJpnRequired :名前は必須です。

次にモデルを作ります。
Models フォルダに Person クラスを作成します。

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace L10n001.Models
{
    public class Person
    {
        public int Id { get; set; }

        [Display(Name = "PersonalNameJpn", ResourceType = typeof(Resources.Validate))]
        [Required(ErrorMessageResourceName = "PersonalNameJpnRequired", ErrorMessageResourceType = typeof(Resources.Validate))]
        [StringLength(50, ErrorMessageResourceName = "PersonalNameJpnLong", ErrorMessageResourceType = typeof(Resources.Validate))]
        public string PersonalName_Jpn { get; set; }

        [Display(Name = "FamilyNameJpn", ResourceType = typeof(Resources.Validate))]
        [Required(ErrorMessageResourceName = "FamilyNameJpnRequired", ErrorMessageResourceType = typeof(Resources.Validate))]
        [StringLength(50, ErrorMessageResourceName = "FamilyNameJpnLong", ErrorMessageResourceType = typeof(Resources.Validate))]
        public string FamilyName_Jpn { get; set; }

        [Display(Name = "PersonalNameEng", ResourceType = typeof(Resources.Validate))]
        [Required(ErrorMessageResourceName = "PersonalNameEngRequired", ErrorMessageResourceType = typeof(Resources.Validate))]
        [StringLength(50, ErrorMessageResourceName = "PersonalNameEngLong", ErrorMessageResourceType = typeof(Resources.Validate))]
        public string PersonalName_Eng { get; set; }

        [Display(Name = "FamilyNameEng", ResourceType = typeof(Resources.Validate))]
        [Required(ErrorMessageResourceName = "FamilyNameEngRequired", ErrorMessageResourceType = typeof(Resources.Validate))]
        [StringLength(50, ErrorMessageResourceName = "FamilyNameEngLong", ErrorMessageResourceType = typeof(Resources.Validate))]
        public string FamilyName_Eng { get; set; }

        public DateTime Birthday { get; set; }

        [Display(Name = "Email", ResourceType = typeof(Resources.Validate))]
        [Required(ErrorMessageResourceName = "EmailRequired", ErrorMessageResourceType = typeof(Resources.Validate))]
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }

        private string _birthdayString;
        [Display(Name = "Birthday", ResourceType = typeof(Resources.Validate))]
        [Required(ErrorMessageResourceName = "BirthdayRequired", ErrorMessageResourceType = typeof(Resources.Validate))]
        [NotMapped]
        public string BirthdayString
        {
            get
            {
                if (Birthday != DateTime.MinValue && string.IsNullOrEmpty(_birthdayString))
                    return Birthday.ToString("d");
                else
                    return _birthdayString;
            }
            set
            {
                _birthdayString = value;
                DateTime temp;
                if (!DateTime.TryParse(value, out temp))
                    throw new ArgumentException("日付の形式が不正です。", "BirthdayString");
                if (temp < DateTime.Parse("1900/01/01") || temp > DateTime.Today)
                    throw new ArgumentException("日付の範囲が不正です。", "BirthdayString");
                Birthday = temp;
            }
        }
    }
}

表示名と検証エラーメッセージをリソースから取得するようにしています。

次に DbContext とリソースです。
プロジェクトに DAL フォルダを作り、DAL フォルダに PersonContext を作成します。

using System.Data.Entity;

using L10n001.Models;

namespace L10n001.DAL
{
    public class PersonContext : DbContext
    {
        public PersonContext() : base("PersonDb") { }

        public DbSet<Person> Persons { get; set; }
    }
}

次に、DAL フォルダに IPersonRepository インターフェイスを作成します。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using L10n001.Models;

namespace L10n001.DAL
{
    public interface IPersonRepository : IDisposable
    {
        Task<IEnumerable<Person>> GetPersonsAsync();
        Task<Person> GetPersonAsync(int id);
        Person AddPerson(Person person);
        void UpdatePerson(Person person);
        Task DeletePersonAsync(int id);

        Task SaveAsync();
    }
}

次に、DAL フォルダに PersonRepository クラスを作成します。

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Threading.Tasks;

using L10n001.Models;

namespace L10n001.DAL
{
    public class PersonRepository : IPersonRepository
    {
        public PersonRepository()
        {
            _context = new PersonContext();
        }

        private PersonContext _context;

        #region IPersonRepository

        public async Task<IEnumerable<Person>> GetPersonsAsync()
        {
            return await _context.Persons.ToListAsync();
        }

        public async Task<Person> GetPersonAsync(int id)
        {
            return await _context.Persons.FindAsync(id);
        }

        public Person AddPerson(Person person)
        {
            return _context.Persons.Add(person);
        }

        public void UpdatePerson(Person person)
        {
            _context.Entry(person).State = EntityState.Modified;
        }

        public async Task DeletePersonAsync(int id)
        {
            var target = await GetPersonAsync(id);
            if (target != null)
                _context.Persons.Remove(target);
        }

        public async Task SaveAsync()
        {
            await _context.SaveChangesAsync();
        }

        #endregion IPersonRepository

        #region IDisposable

        private bool _disposed = false;

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) return;

            _disposed = true;

            if (disposing)
            {
                // マネージ リソースの解放処理
            }
            // アンマネージ リソースの解放処理
            _context.Dispose();
        }

        #endregion IDisposable

        ~PersonRepository()
        {
            Dispose(false);
        }
    }
}

次に、HomeController です。

using System.Threading.Tasks;
using System.Web.Mvc;

using L10n001.DAL;
using L10n001.Models;

namespace L10n001.Controllers
{
    public class HomeController : Controller
    {
        public HomeController() : this(new PersonRepository()) { }
        public HomeController(IPersonRepository repository)
        {
            _repository = repository;
        }

        private IPersonRepository _repository;

        public async Task<ActionResult> Index()
        {
            var culture = Request.UserLanguages != null ? Request.UserLanguages[0] : "en-US";
            ViewBag.Culture = culture;
            return View(await _repository.GetPersonsAsync());
        }

        public async Task<ActionResult> DetailsAsync(int id)
        {
            return View(await _repository.GetPersonAsync(id));
        }

        public ActionResult Create()
        {
            var culture = Request.UserLanguages != null ? Request.UserLanguages[0] : "en-US";
            ViewBag.Culture = culture;
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Create([Bind(Include = "PersonalName_Jpn,FamilyName_Jpn,PersonalName_Eng,FamilyName_Eng,Email,BirthdayString")] Person person)
        {
            if (ModelState.IsValid)
            {
                _repository.AddPerson(person);
                await _repository.SaveAsync();

                return RedirectToAction("Index");
            }

            var culture = Request.UserLanguages != null ? Request.UserLanguages[0] : "en-US";
            ViewBag.Culture = culture;
            return View(person);
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View();
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }

        protected override void Dispose(bool disposing)
        {
            _repository.Dispose();
            base.Dispose(disposing);
        }
    }
}

ViewBag.Culture にカルチャの文字列をセットして View に渡しています(デフォルトは "en-US")。

次は、ビューです。
Views/Shared/_Layout.cshtml

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title @L10n001.Resources.ViewText.LayoutTitle</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink(@L10n001.Resources.ViewText.LayoutAppName, "Index", "Home", null, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink(@L10n001.Resources.ViewText.LayoutHome, "Index", "Home")</li>
                    <li>@Html.ActionLink(@L10n001.Resources.ViewText.LayoutAbout, "About", "Home")</li>
                    <li>@Html.ActionLink(@L10n001.Resources.ViewText.LayoutContact, "Contact", "Home")</li>
                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - @L10n001.Resources.ViewText.LayoutFooter</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

表示するメッセージをリソースから取得するようにしています。

Views/Home/Index.cshtml

@model IEnumerable<L10n001.Models.Person>

@{
    ViewBag.Title = @L10n001.Resources.ViewText.HomeIndexTitle;
}

<h2>@L10n001.Resources.ViewText.HomeIndexTitle</h2>

<p>
    @Html.ActionLink(@L10n001.Resources.ViewText.HomeIndexCreateNew, "Create")
</p>
<table class="table" data-role="targetL10n">
    <tr>
        <th data-l10nposition="jpnName">
            @Html.DisplayNameFor(model => model.FamilyName_Jpn)
        </th>
        <th data-l10nposition="jpnName">
            @Html.DisplayNameFor(model => model.PersonalName_Jpn)
        </th>
        <th data-l10nposition="engName">
            @Html.DisplayNameFor(model => model.FamilyName_Eng)
        </th>
        <th data-l10nposition="engName">
            @Html.DisplayNameFor(model => model.PersonalName_Eng)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.BirthdayString)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Email)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.FamilyName_Jpn)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.PersonalName_Jpn)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.FamilyName_Eng)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.PersonalName_Eng)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.BirthdayString)
       </td>
        <td>
            @Html.DisplayFor(modelItem => item.Email)
        </td>
        <td>
            @Html.ActionLink(@L10n001.Resources.ViewText.HomeIndexEdit, "Edit", new { id=item.Id }) |
            @Html.ActionLink(@L10n001.Resources.ViewText.HomeIndexDetails, "Details", new { id=item.Id }) |
            @Html.ActionLink(@L10n001.Resources.ViewText.HomeIndexDelete, "Delete", new { id=item.Id })
        </td>
    </tr>
}

</table>
@section Scripts {
    @Scripts.Render("~/bundles/tableChgSeqL10n")
    <script type="text/javascript">
        $(document).ready(function () {
            TableChgSeqL10n("ja", "@(((string)ViewBag.Culture).Substring(0, 2).ToLower())");
        });
    </script>
}

TableChgSeqL10n の引数に View の設定の "ja" と ViewBag.Culture の文字列を渡し、両者が異なった時にパーソナルネームとファミリーネームの表示順を入れ替えるようにしています。

Views/Home/Create.cshtml

@model L10n001.Models.Person

@{
    ViewBag.Title = @L10n001.Resources.ViewText.HomeCreateTitle;
}

<h2>@L10n001.Resources.ViewText.HomeCreateTitle</h2>


@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">
        <h4>@L10n001.Resources.ViewText.HomeCreateContact</h4>
        <hr />
        @Html.ValidationSummary(true)

    @if (((string)ViewBag.Culture).ToLower().StartsWith("ja"))
    {
        <div class="form-group">
            @Html.LabelFor(model => model.FamilyName_Jpn, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.FamilyName_Jpn)
                @Html.ValidationMessageFor(model => model.FamilyName_Jpn)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.PersonalName_Jpn, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.PersonalName_Jpn)
                @Html.ValidationMessageFor(model => model.PersonalName_Jpn)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.FamilyName_Eng, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.FamilyName_Eng)
                @Html.ValidationMessageFor(model => model.FamilyName_Eng)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.PersonalName_Eng, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.PersonalName_Eng)
                @Html.ValidationMessageFor(model => model.PersonalName_Eng)
            </div>
        </div>
    }
    else
    {
        <div class="form-group">
            @Html.LabelFor(model => model.PersonalName_Jpn, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.PersonalName_Jpn)
                @Html.ValidationMessageFor(model => model.PersonalName_Jpn)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.FamilyName_Jpn, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.FamilyName_Jpn)
                @Html.ValidationMessageFor(model => model.FamilyName_Jpn)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.PersonalName_Eng, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.PersonalName_Eng)
                @Html.ValidationMessageFor(model => model.PersonalName_Eng)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.FamilyName_Eng, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.FamilyName_Eng)
                @Html.ValidationMessageFor(model => model.FamilyName_Eng)
            </div>
        </div>
    }

        <div class="form-group">
            @Html.LabelFor(model => model.BirthdayString, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.BirthdayString, new { @class = "datepicker" })
                @Html.ValidationMessageFor(model => model.BirthdayString)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Email, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Email)
                @Html.ValidationMessageFor(model => model.Email)
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value=@L10n001.Resources.ViewText.HomeCreateButton class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink(@L10n001.Resources.ViewText.HomeCreateBackList, "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    @Scripts.Render("~/bundles/globalize")
    @Html.Raw(string.Format("<script src='/Scripts/globalize/cultures/globalize.culture.{0}.js'></script>", ViewBag.Culture))
    @Scripts.Render("~/bundles/bootstrap-datepicker")
    <script type="text/javascript">
        $(function () {
            $('.datepicker').datepicker({ dateFormat: 'yy/mm/dd', todayHighlight: 'true', autoclose: 'true' });
        });
    </script>
}

こちらは if 文で順番を入れ替えています。
また、globalize.js のローカライズファイルを ViewBag.Culture を利用して設定しています。

以上で最初の画面例のように動くようになります 😉


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です