Динамическая локализация

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 1.11.2015

В статьях Локализация чисел decimal и Локализация дат в ASP.NET MVC была рассмотрена статическая локализация, когда приложение делается под определенную культуру со всеми ее особенностями (запятая или точка в качестве разделителя, определенный формат даты). Однако если у нас веб-приложение расчитано на несколько языков или культур, такая ситуация может оказаться нежелательной. Например, пользователю из США будет привычнее использовать в качестве разделителя точку и формат даты месяц/день/год, а пользователь из Германии, наоборот, использует формат день/месяц/год и запятую в качестве разделителя. Поэтому рассмотрим, как менять динамически настройки локализации в зависимости от выбранной пользователем культуры.

Сначала создадим в проекте инфраструктуру для переключения языка. Подробно данный момент описан в статье Логика мультиязычного сайта. Фильтр локализации. Для этого определим добавим в файл Global.asax, обработчик события BeginRequest, который будет переключать текущую культуру приложения:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace LocalApp
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }

        protected void Application_BeginRequest()
        {
            string cultureName = null;
            // Получаем куки из контекста, которые могут содержать установленную культуру
            HttpCookie cultureCookie = HttpContext.Current.Request.Cookies["lang"];
            if (cultureCookie != null)
                cultureName = cultureCookie.Value;
            else
                cultureName = "ru";

            // Список культур
            List<string> cultures = new List<string>() { "ru", "en", "de" };
            if (!cultures.Contains(cultureName))
            {
                cultureName = "ru";
            }
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName);
            Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName);
        }
    }
}

Обработчик Application_BeginRequest вызывается в начале жизненного цикла запроса. В нем который получаем установленную культуру из куков. Затем мы смотрим, есть ли такая культура в списке. Если культуры нет, то используем рускоязычную культуру по умолчанию.

Далее в контроллере создадим метод, который будет сохранять выбранную пользователем культуру в куки

public class HomeController : Controller
{
    public ActionResult ChangeCulture(string lang)
    {
        string returnUrl = Request.UrlReferrer.AbsolutePath;
        
		List<string> cultures = new List<string>() { "ru", "en"};
        if (!cultures.Contains(lang))
        {
            lang = "ru";
        }
        // Сохраняем выбранную культуру в куки
        HttpCookie cookie = Request.Cookies["lang"];
        if (cookie != null)
            cookie.Value = lang;   // если куки уже установлено, то обновляем значение
        else
        {
			cookie = new HttpCookie("lang");
            cookie.HttpOnly = false;
            cookie.Value = lang;
            cookie.Expires = DateTime.Now.AddYears(1);
        }
        Response.Cookies.Add(cookie);
        return Redirect(returnUrl);
    }
}

В файле _Layout.cshtml определим переключение языка:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div>
        @using (Html.BeginForm("ChangeCulture", "Home"))
        {
            <select name="lang">
                <option disabled selected>Выберите язык</option>
                <option value="ru">Русский</option>
                <option value="en">English</option>
            </select>
            <input type="submit" name="submit" value="Изменить">
        }
    </div>
    @RenderBody()

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

С помощью выпадающего списка переключаем культуру. В данном случае опустим локализацию названий полей с помощью ресурсов.

Теперь добавим в проект модель User:

public class User
{
    public int Id { get; set; }
    [Display(Name = "Имя")]
    public string Name { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "Дата рождения")]
    public DateTime Date { get; set; }
}

Также добавим в контроллер действие Create, которое будет получать введенные пользователем данные:

public ActionResult Create()
{
    return View();
}
[HttpPost]
public ActionResult Create(User user)
{
    if (ModelState.IsValid)
    {
        return Content(user.Date.ToLongDateString());
    }
    return View(user);
}

В post-методе будем получать модель и в случае успешной валидации выводить введенную дату рождения.

Теперь самое главное - определим представление для создания модели, причем нам надо учесть возможность выбора одного из двух форматов дат в соответствии с заданной культурой. Фактически нам надо два набора настроек для виджета datepicker, который позволит установить дату: для русскоязычной и для англоязычной культры. Для упрощения работы вынесем настройки в отдельный файлы javascript.

Добавим в папку Scripts файл datepicker.en.js со следующим содержимым:

$(function () {
	$("input[type='date']")
				.datepicker({ dateFormat: 'm/d/yy' })
				.get(0).setAttribute("type", "text");
});

Для англоязычной культуры используется формат 'm/d/yy'.

Также добавим в папку Scripts файл datepicker.ru.js:

$(function () {
    $("input[type='date']")
                .datepicker({ dateFormat: 'dd/mm/yy' })
                .get(0).setAttribute("type", "text");
    $.datepicker.regional['ru'] = {
        prevText: 'Пред',
        nextText: 'След',
        monthNames: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь',
        'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
        monthNamesShort: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн',
        'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
        dayNames: ['воскресенье', 'понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота'],
        dayNamesShort: ['вск', 'пнд', 'втр', 'срд', 'чтв', 'птн', 'сбт'],
        dayNamesMin: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
        weekHeader: 'Не',
        dateFormat: 'dd/mm/yy',
        firstDay: 1,
        isRTL: false,
        showMonthAfterYear: false,
        yearSuffix: ''
    };
    $.datepicker.setDefaults($.datepicker.regional['ru']);
    $.validator.addMethod('date',
        function (value, element) {
            var ok = true;
            try {
                $.datepicker.parseDate('dd/mm/yy', value);
            }
            catch (err) {
                ok = false;
            }
            return ok;
        });
});

Здесь уже другой формат "dd/mm/yy" плюс куча дополнительный настроек.

И определим представление Create.cshtml:

@model LocalApp.Models.User

@{
    ViewBag.Title = "Create";
}

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">
        <h4>Новый пользователь</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
            </div>
        </div>

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

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    <!--подключаем стили jquery-ui-->
    <link href='@Url.Content("~/Content/themes/base/all.css")' rel="stylesheet" type="text/css" />
    <!--подключаем скрипт jquery-ui-->
    <script src='@Url.Content("~/Scripts/jquery-ui-1.11.4.min.js")' type="text/javascript"></script>

    @if (System.Threading.Thread.CurrentThread.CurrentCulture.Name == "ru-RU")
    {
        <script src='@Url.Content("~/Scripts/datepicker.ru.js")' type="text/javascript"></script>
    }
    else
    {
        <script src='@Url.Content("~/Scripts/datepicker.en.js")' type="text/javascript"></script>
    }
}

С помощью свойства System.Threading.Thread.CurrentThread.CurrentCulture мы можем получить текущую культуру и в зависимости от ее значения подключить тот или иной скрипт.

Выберем русскоязычную культуру и введем дату:

При получении данных модель будет успешно валидирована в контроллере, и браузер выведет дату в соответствии с культурой:

Теперь изменим культуру на англоязычную и соответветственно используем другой формат даты:

И также валидация модели пройдет успешно :

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850