|
|
|||||||||||||||||||||||||||||
|
Интернационализация в ASP.NET MVC 3Источник: msdn Vladimir Yunev
Если ваш веб-сайт ориентирован на пользователей из различных частей света, эти пользователи могут хотеть просматривать контент на вашем веб-сайте на их собственном языке. Создание мультиязыкового сайта - нелегкая задача, но это позволяет привлечь большее количество аудитории. К счастью, .NET Framework уже имеет компоненты, которые поддерживают использование различных языков и культур. Мы разработаем ASP.NET MVC 3 приложение, которое будет содержать следующую функциональность:
Глобализация и локализация в ASP.NETИнтернационализация оперирует терминами Глобализация и Локализация. Глобализация - процесс проектирования приложений, поддерживающих различные культуры, локализация - процесс настройки приложения для определенной культуры. Формат имени культуры - "<languagecode2>-<country/regioncode2>", где <languagecode2> - код языка, <country/regioncode2>- код субкультуры. Например, es-CL - Чили, испанский, и en-US - США, английский. ASP.NET отслеживает два значения, связанных с культурой - Culture и UICulture. Значение Culture определяует результаты таких функций, как дата и форматирование валюты. UICulture определяет, какие ресурсы должны быть загружены ResourceManager. ResourceManager просто находит специфичные для культуры ресурсы, определяемые CurrentUICulture. Каждый тред в .NET имеет собственные объекты CurrentCulture и CurrentUICulture. Например, если текущая культура треда (CurrentCulture) определена как "en-US" (США, английский), DateTime.Now.ToLongDateString() выведет "Saturday, January 08, 2011", но если CurrentCulture определена как "es-CL" (испанский, Чили), метод выведет "sábado, 08 de enero de 2011". Поддержка различных языков в ASP.NET MVC 3Есть несколько методов реализации поддержки различных языков в ASP.NET MVC 3:
Какой метод лучше?Зависит от вас, это вопрос удобства. Некоторые люди предпочитают использовать одно представление для всех языков, так как им легче управлять, другие считают, что замена контента представления кодом типа "@Resources.Something" может привести к мешанине и нечитабельности. Представления должны быть настолько простыми, насколько возможно. Если ваше представление выглядит хорошо с большим количеством кода, пусть будет так. Однако иногда выбора нет, например, в языках, где разметка должна быть другой, например, в языках, которые читаются справа налево. Возможно, смешивание двух методов - лучший выход. В нашем примере мы будем использовать второй метод просто затем, чтобы показать, как выполнить задачу без использования обычных строк ресурсов. Конвенции именования представленийДля создания различных представлений для каждой культуры мы будем добавлять название культуры к имени представления. Например, Index.cshtml (стандартное представление), Index.es-CL.cshtml (испанский, Чили), Index.ar-JO.cshtml (арабский, Иордания). Имя представления, не имеющее суффикса культуры, распознается как стандартная культура. Глобализация на веб-сайтеМы создадим ASP.NET MVC 3 приложение и глобализируем его шаг за шагом. Нажмите File->New Project в Visual Studio и создайте новый проект ASP.NET MVC 3 с шаблоном Internet Application. Создание моделиНам нужна модель. Добавьте класс User в папку Models. Интернационализация сообщений валидацииМодель, представленная выше, не содержит логики валидации, что не является хорошей практикой для современного приложения. Мы можем использовать аннотации данных для добавления логики валидации к нашей модели, однако, чтобы глобализировать сообщения валидации, нам нужно ещё несколько параметров. Так, "ErrorMessageResourceType" определяет тип ресурса для поиска при сообщении об ошибке. "ErrorMessageResourceName" определяет имя ресурса для сообщения об ошибке. Измените класс Person и добавьте в него следующие атрибуты: namespace MvcInternationalization.Models { public class Person { [Required(ErrorMessageResourceType=typeof(MyResources.Resources), ErrorMessageResourceName="FirstNameRequired")] [StringLength(50, ErrorMessageResourceType = typeof(MyResources.Resources), ErrorMessageResourceName = "FirstNameLong")] public string FirstName { get; set; } [Required(ErrorMessageResourceType = typeof(MyResources.Resources), ErrorMessageResourceName = "LastNameRequired")] [StringLength(50, ErrorMessageResourceType = typeof(MyResources.Resources), ErrorMessageResourceName = "LastNameLong")] public string LastName { get; set; } [Required(ErrorMessageResourceType = typeof(MyResources.Resources), ErrorMessageResourceName = "AgeRequired")] [Range(0, 130, ErrorMessageResourceType = typeof(MyResources.Resources), ErrorMessageResourceName = "AgeRange")] public int Age { get; set; } [Required(ErrorMessageResourceType = typeof(MyResources.Resources), ErrorMessageResourceName = "EmailRequired")] [RegularExpression(".+@.+\\..+", ErrorMessageResourceType = typeof(MyResources.Resources), ErrorMessageResourceName = "EmailInvalid")] public string Email { get; set; } public string Biography { get; set; } } } Локализация сообщений валидацииТак как мы хотим валидировать данные модели с использованием аннотаций данных, нам нужно добавить переведенные строки ресурсов для каждой культуры, которую будет поддерживать наш сайт, в нашем случае это английский, испанский и иарабский. Мы сохраним файлы ресурсов в отдельной сборке, чтобы мы могли ссылаться на них в других проектах в будущем. Нажмите правой кнопкой на Solution и выберите Add->New Project, после чего выберите тип проекта Class Library и назовите его MyResources. Нажмите правой кнопкой на проекте MyResources и выберите Add->New Item, Resource File, назвав его Resources.resx. Это будет наша культура по умолчанию (en-US), так как у названия файла нет специальных суффиксов. Добавьте следующие имена и значения в файл как показано на изображении. Не забудьте отметить модификатор доступа к ресурсу как public, чтобы к нему можно было обращаться из других проектов. Теперь создайте новый файл ресурсов и назовите его "Resources.es-CL.resx". Его содержимое показано на изображении. Повторите последовательность действий для арабской версии. Может так получиться, что вы не сможете ввести некоторые символы из-за отсутствия поддержки арабского в вашей ОС, но вы можете просто скачать файлы со ссылки выше. Нам нужно добавить ссылку на проект MyResources в наше приложение, чтобы можно было загружать строки ресурсов прямо с веб-сайта. Нажмите правой кнопкой на References в проекте MvcInternationalization и выберите проект MyResources во вкладке Projects. Определение культурыБраузер в каждом запросе отправляет заголовок Accept-Language, содержащий список с элементами типа язык-страна, которые сконфигурированы в пользовательском браузере. Проблема в том, что культура может не относиться к предпочитаемому пользователем языку (если он находится в компьютерном клубе, например). Мы должны позволить пользователю выбирать язык. Чтобы это сделать, можно хранить предпочитаемый пользователем язык в каком-либо хранилище, например, в cookies. Мы создадим базовый контроллер, загружающий содержимое cookies или, если соответствующие cookies отсутствуют, будет использоваться заголовок Accept-Language, посылаемый пользовательским браузером. Создайте контроллер и назовите его BaseController: namespace MvcInternationalization.Controllers { public class BaseController : Controller { protected override void OnActionExecuted(ActionExecutedContext filterContext) { // Is it View ? ViewResultBase view = filterContext.Result as ViewResultBase; if (view == null) // if not exit return; string cultureName = Thread.CurrentThread.CurrentCulture.Name; // e.g. "en-US" // filterContext.HttpContext.Request.UserLanguages[0]; // needs validation return "en-us" as default // Is it default culture? exit if (cultureName == CultureHelper.GetDefaultCulture()) return; // Are views implemented separately for this culture? if not exit bool viewImplemented = CultureHelper.IsViewSeparate(cultureName); if (viewImplemented == false) return; string viewName = view.ViewName; int i = 0; if (string.IsNullOrEmpty(viewName)) viewName = filterContext.RouteData.Values["action"] + "." + cultureName; // Index.en-US else if ((i = viewName.IndexOf('.')) > 0) { // contains . like "Index.cshtml" viewName = viewName.Substring(0, i + 1) + cultureName + viewName.Substring(i); } else viewName += "." + cultureName; // e.g. "Index" ==> "Index.en-Us" view.ViewName = viewName; filterContext.Controller.ViewBag._culture = "." + cultureName; base.OnActionExecuted(filterContext); } protected override void ExecuteCore() { string cultureName = null; // Attempt to read the culture cookie from Request HttpCookie cultureCookie = Request.Cookies["_culture"]; if (cultureCookie != null) cultureName = cultureCookie.Value; else cultureName = Request.UserLanguages[0]; // obtain it from HTTP header AcceptLanguages // Validate culture name cultureName = CultureHelper.GetValidCulture(cultureName); // This is safe // Modify current thread's culture Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName); Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName); base.ExecuteCore(); } } } Контроллер проверяет на наличие cookies и определяет текущую культуру треда. Конечно, мы должны проверять значения cookies, так как они могут быть отредактированы на клиентской стороне, что и будем делать с помощью вспомогательного класса CultureHelper. Если имя культуры не проходит валидацию, будет возвращена стандартная культура. После этого, когда мы вызовем Return в методе контроллера, имя представления будет изменено контроллером. Таким образом мы подтверждаем тот факт, что пользователь получит правильный контент согласно своей культуре. Класс CultureHelperКласс CultureHelper позволяет хранить имена культур, реализованные на нашем сайте namespace MvcInternationalization.Utility { public static class CultureHelper { // Include ONLY cultures you are implementing as views private static readonly Dictionary<String, bool> _cultures = new Dictionary<string,bool> { {"en-US", true}, // first culture is the DEFAULT {"es-CL", true}, {"ar-JO", true} }; /// <summary> /// Returns a valid culture name based on "name" parameter. If "name" is not valid, it returns the default culture "en-US" /// </summary> /// <param name="name">Culture's name (e.g. en-US)</param> public static string GetValidCulture(string name) { if (string.IsNullOrEmpty(name)) return GetDefaultCulture(); // return Default culture if (_cultures.ContainsKey(name)) return name; // Find a close match. For example, if you have "en-US" defined and the user requests "en-GB", // the function will return closes match that is "en-US" because at least the language is the same (ie English) foreach(var c in _cultures.Keys) if (c.StartsWith(name.Substring(0, 2))) return c; // else return GetDefaultCulture(); // return Default culture as no match found } /// <summary> /// Returns default culture name which is the first name decalared (e.g. en-US) /// </summary> /// <returns></returns> public static string GetDefaultCulture() { return _cultures.Keys.ElementAt(0); // return Default culture } /// <summary> /// Returns "true" if view is implemented separatley, and "false" if not. /// For example, if "es-CL" is true, then separate views must exist e.g. Index.es-cl.cshtml, About.es-cl.cshtml /// </summary> /// <param name="name">Culture's name</param> /// <returns></returns> public static bool IsViewSeparate(string name) { return _cultures[name]; } } } _cultures мы должны заполнять вручную, это список имен культур, которых поддерживает наш сайт. Первый параметр - имя культуры (например, en-US), второй показывает, реализовали ли мы отдельные представления для этой культуры. Если второй параметр false, используется стандартное представление. Достоинством реализованного класса является то, что он поддерживает поиск похожих языков. Например, если пользователь из Аргентины (es-ar), то есть культуры, не поддерживаемой нашим сайтом, он увидит контент сайта с использованием es-cl (испанский, Чили) вместо английского языка. Таким образом, нет необходимости реализовывать поддержку всех культур до тех пор, пока вам не нужно реализовать поддержку таких данных, как валюта, формат даты и прочее. КонтроллерыVisual Studio уже создал контроллер HomeController, поэтому мы будем использовать его. Измените HomeController.cs: namespace MvcInternationalization.Controllers { public class HomeController : BaseController { [HttpGet] public ActionResult Index() { return View(); } [HttpPost] public ActionResult Index(Person per) { return View(); } public ActionResult SetCulture(string culture) { // Validate input culture = CultureHelper.GetValidCulture(culture); // Save culture in a cookie HttpCookie cookie = Request.Cookies["_culture"]; if (cookie != null) cookie.Value = culture; // update cookie value else { cookie = new HttpCookie("_culture"); cookie.HttpOnly = false; // Not accessible by JS. cookie.Value = culture; cookie.Expires = DateTime.Now.AddYears(1); } Response.Cookies.Add(cookie); return RedirectToAction("Index"); } public ActionResult About() { return View(); } } } Метод "SetCulture" предоставляет пользователю возможность изменять текущую культуру и сохранять ее в cookie _culture. Можно сохранять имя культуры в Session или где-либо ещё, но cookies хороши своей легковесностью и отсутствием копии на серверной стороне. Создание шаблона представленияТеперь мы создадим представление, ассоциированное с методом Index контроллера HomeController. Для этого сначала удалите существующий файл Index.cshtml в папке Views/Home. Затем нажмите правой кнопкой мыши на методе HomeController.Index() и выберите команду Add View. Отметьте "Create a strongly-typed view", выберите класс Person. Выберите также Create в выпадающем меню Scaffold template. Нажмите Add и измените содержимое созданного представления: Теперь сделайте две копии Index.cshtml, назвав их Index.es-CL.cshtml и Index.ar-JO.cshtml. Эти представления будут отображать контент локализованных версий Index.cshtml. Внесите изменения: "Арабское" представление Разумеется, для простых частичных представлений типа _LogOnPartial.cshtml и представлений, на которые не ссылаются контроллеры, мы можем спокойно использовать строки ресурсов. "Арабское" представление ПробуемЗапустите веб-сайт. Обратите внимание на то, что отрабатывает валидация на клиентской стороне.Понажимайте на radio buttons, что переключиться между культурами, и убедитесь, что язык с нестандартной разметкой отображается корректно. Использование отдельных представлений позволяет контролировать позиционирование элементов, что делает представления легкочитаемыми. Английский Испанский Арабский Локализация на клиентской сторонеСкрипты на клиентской сторонеВ случае клиентской стороны мы должны побеспокоиться о числах, датах, времени и сообщениях, так как они меняются от культуры к культуре. Есть много методов реализации локализации на клиентской стороне, вот два из них:
Для первого метода мы будем пользоваться одними правилами для представлений и файлов ресурсов. Например, для файла myscript.js нужно будет создать "myscript.es-CL.js", "myscript.ar-JO.js", и пр. На скрипты в представлении легко сделать ссылку путем добавления суффика имени культуры к имени файла javascript: <script src="@Url.Content("~/Scripts/myscript" + ViewBag._culture + ".js")" type="text/javascript"></script> Переменная _culture уже определена в контроллере. Даже если вы захотите использовать Microsoft Ajax Library, вам могут понадобиться отдельные файлы javascript, определяющие текстовые сообщения для пользователя. Вы можете определить строковый объект, содержащий список сообщений или использовать отдельные представления для каждой культуры, или использовать код javascript в представлениях вместо вынесения их в отдельные файлы. Загрузить кодРезюмеСоздание мультиязыкового веб-приложения - дело нелегкое, но необходимое, если веб-приложение ориентировано на пользователей со всего мира. Глобализация, конечно, не в первых приоритетах при разработке сайта, однако, она должна быть хорошо продумана в начале разработки, чтобы дальнейшая разработка происходила легче. К счастью, ASP.NET поддерживает глобализацию и имеет некоторые удобные .NET классы. Итак, мы рассмотрели процесс создания ASP.NET MVC 3 приложения с использованием трёх различных языков, включая один с нестандартной разметкой. Предлагаем последовательность действий для глобализации сайта ASP.NET MVC 3:
Ссылки по теме
|
|