ASP.NET mvc, локализованные маршруты и язык по умолчанию для пользователя
Я использую локализованные маршруты asp.net mvc. Поэтому, когда пользователь переходит на английский сайт, это example.com/en/Controller/Action, а шведский - example.com/sv/Controller/Action.
Но как я могу убедиться, что, когда пользователь входит на сайт, он или она подходит к правильному языку напрямую? Я знаю, как получить язык, который я хочу, это не проблема. То, что я использовал, это то, что я поместил эту культуру в метод RegisterRoutes
. Но поскольку моя страница находится в интегрированном режиме, я не могу получить запрос от Application_Start.
Итак, как я должен убедиться, что маршрут правильный с самого начала?
Ответы
Ответ 1
Вот как я это сделаю.
~~ Отказ от ответственности: psuedo code ~~
global.asax
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{*favicon}",
new { favicon = @"(.*/)?favicon.ico(/.*)?" });
routes.MapRoute(
"Question-Answer", // Route name
"{languageCode}/{controller}/{action}", // URL with parameters
new {controller = "home", action = "index"} // Parameter defaults
);
}
Обратите внимание: контроллер и/или действие НЕ должны быть первыми и вторыми. на самом деле, они вообще не должны существовать в разделе url with parameters
.
Затем...
HomeController.cs
public ActionResult Index(string languageCode)
{
if (string.IsNullOrEmpty(languageCode) ||
languageCode != a valid language code)
{
// No code was provided OR we didn't receive a valid code
// which you can't handle... so send them to a 404 page.
// return ResourceNotFound View ...
}
// .. do whatever in here ..
}
Бонусное предложение
Вы также можете добавить Route Constraint к вашему маршруту, поэтому он принимает только определенные строки для параметра languageCode
. Так украсть этот код чужих....
(больше кода pseduo)...
public class FromValuesListConstraint : IRouteConstraint
{
public FromValuesListConstraint(params string[] values)
{
this._values = values;
}
private string[] _values;
public bool Match(HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection)
{
// Get the value called "parameterName" from the
// RouteValueDictionary called "value"
string value = values[parameterName].ToString();
// Return true is the list of allowed values contains
// this value.
return _values.Contains(value);
}
}
означает, что вы можете это сделать......
routes.MapRoute(
"Question-Answer", // Route name
"{languageCode}/{controller}/{action}", // URL with parameters
new {controller = "home", action = "index"} // Parameter defaults
new { languageCode = new FromValuesListConstraint("en", "sv", .. etc) }
);
и там у вас есть:)
Я делаю что-то подобное для версии моего MVC Api.
GL:) Надеюсь, это поможет.
Ответ 2
Хорошо.. другое предложение.
Чтобы убедиться, что я понимаю, вы хотите.
- Каждое действие должно быть в состоянии выяснить, что такое LanguageCode?
- Если указан недопустимый языковой код, он должен быть reset для допустимого значения по умолчанию.
если это так.. этот ответ состоит из трех частей: -
- Добавьте маршрут. (это вырезано-вставка из моего предыдущего ответа).
global.asax
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{*favicon}",
new { favicon = @"(.*/)?favicon.ico(/.*)?" });
routes.MapRoute(
"Question-Answer", // Route name
"{languageCode}/{controller}/{action}", // URL with parameters
new {controller = "home", action = "index"} // Parameter defaults
);
}
Обновление (на основе комментариев)
Итак, если вы хотите иметь маршрут http://www.example.com/sv/account/logon
, то указанный маршрут будет работать.
LanguageCode
== sv (или en или fr или любой язык, который вы поддерживаете)
account
== контроллер: AccountController
login
== действие.
тот факт, что я сказал controller = "home"
и action="index"
, означает только то, что эти два параметра по умолчанию не соответствуют этим значениям, если они не были предоставлены. Итак, если вы goto http://www.example.com/sv/account/logon
, тогда структура MVC достаточно умна, чтобы знать (на основе этого маршрута), что параметры языкаCode == sv, controller == action и action (method) == index.
ПРИМЕЧАНИЕ. порядок ваших маршрутов ВАЖНО. критически важно. Этот маршрут должен быть одним (если не первым) маршрутами (после IgonoreRoute) при регистрации ваших маршрутов.
- Вам нужно создать пользовательский ActionFilter, который будет вызван до того, как действие будет выполнено. вот моя быстрая попытка...
.
using System.Linq;
using System.Web.Mvc;
namespace YourNamespace.Web.Application.Models
{
public class LanguageCodeActionFilter : ActionFilterAttribute
{
// This checks the current langauge code. if there one missing, it defaults it.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
const string routeDataKey = "languageCode";
const string defaultLanguageCode = "sv";
var validLanguageCodes = new[] {"en", "sv"};
// Determine the language.
if (filterContext.RouteData.Values[routeDataKey] == null ||
!validLanguageCodes.Contains(filterContext.RouteData.Values[routeDataKey]))
{
// Add or overwrite the langauge code value.
if (filterContext.RouteData.Values.ContainsKey(routeDataKey))
{
filterContext.RouteData.Values[routeDataKey] = defaultLanguageCode;
}
else
{
filterContext.RouteData.Values.Add(routeDataKey, defaultLanguageCode);
}
}
base.OnActionExecuting(filterContext);
}
}
}
- Теперь вам нужно создать BaseController, из которого все ваши контроллеры наследуются. Затем это создаст легко доступное свойство, доступное всем вашим действиям.. и затем отобразить все, что захочет, на основе этого значения.
здесь мы идем... (псевдо-код снова....)
public abstract class BaseController : Controller
{
protected string LanguageCode
{
get { return (string) ControllerContext.RouteData.Values["LanguageCode"]; }
}
}
Итак, мы украшаем наши контроллеры следующим образом:)
[LanguageCodeActionFilter]
public class ApiController : BaseController
{
public ActionResult Index()
{
if (this.LanguageCode == "sv") ... // whatever.. etc..
}
}
Обратите внимание, как я украсил класс.. не только каждое действие. это означает, что все действия в классе будут затронуты ActionFilter:)
Кроме того, вы можете добавить новый маршрут в global.asax, который обрабатывает NO languageCode.. и hardcode по умолчанию, это значение...
like (также непроверенный)...
routes.MapRoute(
"Question-Answer", // Route name
"{controller}/{action}", // URL with parameters
new {controller = "home", action = "index", languageCode = "sv"} // Parameter defaults
);
Помогает ли это?
Ответ 3
Я знаю, что это очень старый вопрос, но мне просто нужно было решить полный набор связанных вопросов, я думал, что поделюсь своим решением.
Ниже представлено полное решение, в том числе несколько дополнительных приемов, позволяющих легко менять язык. Это позволяет использовать определенные культуры, а не только определенные языки (но в этом примере сохраняется только часть языка).
Особенности включают:
- Возврат к языку браузера при определении языка
- Использование файлов cookie для сохранения языка во время посещений.
- Переопределить язык с помощью URL
- Поддержка изменения языка по ссылке (например, простые параметры меню)
Шаг 1: изменение регистрационных записей в RouteConfig
Эта новая маршрутизация включает ограничение (как другие также предлагают), чтобы гарантировать, что языковой маршрут не захватывает определенные стандартные пути. Нет необходимости в значении языка по умолчанию, поскольку все это обрабатывается с помощью LocalisationAttribute
(см. Шаг 2).
public static void RegisterRoutes(RouteCollection routes)
{
...
// Special localisation route mapping - expects specific language/culture code as first param
routes.MapRoute(
name: "Localisation",
url: "{lang}/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { lang = @"[a-z]{2}|[a-z]{2}-[a-zA-Z]{2}" }
);
// Default routing
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Шаг 2: Создайте атрибут Локализация
Это рассмотрит запросы контроллера перед их обработкой и изменит текущую культуру на основе URL-адреса, куки файлов или культуры браузера по умолчанию.
// Based on: http://geekswithblogs.net/shaunxu/archive/2010/05/06/localization-in-asp.net-mvc-ndash-3-days-investigation-1-day.aspx
public class LocalisationAttribute : ActionFilterAttribute
{
public const string LangParam = "lang";
public const string CookieName = "mydomain.CurrentUICulture";
// List of allowed languages in this app (to speed up check)
private const string Cultures = "en-GB en-US de-DE fr-FR es-ES ro-RO ";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Try getting culture from URL first
var culture = (string)filterContext.RouteData.Values[LangParam];
// If not provided, or the culture does not match the list of known cultures, try cookie or browser setting
if (string.IsNullOrEmpty(culture) || !Cultures.Contains(culture))
{
// load the culture info from the cookie
var cookie = filterContext.HttpContext.Request.Cookies[CookieName];
if (cookie != null)
{
// set the culture by the cookie content
culture = cookie.Value;
}
else
{
// set the culture by the location if not specified
culture = filterContext.HttpContext.Request.UserLanguages[0];
}
// set the lang value into route data
filterContext.RouteData.Values[LangParam] = culture;
}
// Keep the part up to the "-" as the primary language
var language = culture.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries)[0];
filterContext.RouteData.Values[LangParam] = language;
// Set the language - ignore specific culture for now
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(language);
// save the locale into cookie (full locale)
HttpCookie _cookie = new HttpCookie(CookieName, culture);
_cookie.Expires = DateTime.Now.AddYears(1);
filterContext.HttpContext.Response.SetCookie(_cookie);
// Pass on to normal controller processing
base.OnActionExecuting(filterContext);
}
}
Шаг 3: Примените локализацию ко всем контроллерам
например.
[Localisation] <<< ADD THIS TO ALL CONTROLLERS (OR A BASE CONTROLLER)
public class AccountController : Controller
{
Шаг 4: Чтобы изменить язык (например, из меню)
Здесь он немного запутан и требует некоторых обходных решений.
Добавьте метод ChangeLanguage к контроллеру вашей учетной записи. Это позволит исключить любой существующий код языка из "предыдущего пути", чтобы новый язык вступил в силу.
// Regex to find only the language code part of the URL - language (aa) or locale (aa-AA) syntax
static readonly Regex removeLanguage = new Regex(@"/[a-z]{2}/|/[a-z]{2}-[a-zA-Z]{2}/", RegexOptions.Compiled);
[AllowAnonymous]
public ActionResult ChangeLanguage(string id)
{
if (!string.IsNullOrEmpty(id))
{
// Decode the return URL and remove any language selector from it
id = Server.UrlDecode(id);
id = removeLanguage.Replace(id, @"/");
return Redirect(id);
}
return Redirect(@"/");
}
Шаг 5: Добавление ссылок на языковые меню
Параметры меню состоят из ссылки с новым языком, указанным в качестве параметра маршрута.
например. (Пример Razor)
<li>@Html.ActionLink("English", "ChangeLanguage", "Account", new { lang = "en", id = HttpUtility.UrlEncode(Request.RawUrl) }, null)</li>
<li>@Html.ActionLink("Spanish", "ChangeLanguage", "Account", new { lang = "es", id = HttpUtility.UrlEncode(Request.RawUrl) }, null)</li>
Возвращаемая URl - это текущая страница, закодированная так, чтобы она могла стать параметром id URL-адреса. Это означает, что вам необходимо включить определенные escape-последовательности, которые в противном случае отказываются от Razor как потенциальное нарушение безопасности.
Примечание. Для не-бритвенных установок вы в основном хотите привязать якорь, который имеет новый язык и текущий URL-адрес страницы, по пути, например:
http://website.com/{language}/account/changelanguage/{existingURL}
где {language} - новый код культуры, а {existingURL} - URL-адрес текущей адресной страницы URL-адреса, поэтому мы вернемся к той же странице с выбранным новым языком.
Шаг 6: Включите определенные "небезопасные" символы в URL-адресах
Необходимая кодировка URL-адреса возврата означает, что вам нужно включить определенные escape-символы, в web.config
или существующий параметр URL приведет к ошибке.
В вашем web.config найдите тег httpRuntime
(или добавьте его) в <system.web>
и добавьте к нему следующее (в основном удалите%, который находится в стандартной версии этого атрибута):
requestPathInvalidCharacters="<,>,&,:,\,?"
В вашем web.config найдите раздел <system.webserver>
и добавьте в него следующее:
<security>
<requestFiltering allowDoubleEscaping="true"/>
</security>
Ответ 4
Вы можете задать в global.asax
BeginRequest, если URL-адрес хорошо сформирован для вашего сайта.
Вы также можете попытаться сделать это с помощью маршрутов, но, по моему опыту, ваши маршруты будут очень неустойчивыми, если вы не уверены, что первым параметром является lang.
Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
Dim lang As String = "es"
If not Request.Path.ToLower.StartsWith("sv/") and _
not Request.Path.ToLower.StartsWith("en/")
''//ask the browser for the preferred lang
Select Case Mid(Request.UserLanguages(0).ToString(), 1, 2).ToLower
Case "en"
Response.Redirect("en/")
Case "sv"
Response.Redirect("sv/")
Case Else
Response.Redirect("sv/") ''//the default
End Select
end if
end sub
Неподтвержденный код. Простите мой VB