Я знаю, что это очень старый вопрос, но мне просто нужно было решить полный набор связанных проблем, и я решил поделиться своим решением.
Ниже представлено комплексное решение, включающее несколько дополнительных приемов, позволяющих легко менять язык. Он учитывает конкретные культуры, а не только конкретные языки (но в этом примере сохраняется только языковая часть).
Особенности включают в себя:
- Откат к языку браузера в определении языка
- Использует куки для сохранения языка при посещении
- Переопределить язык с помощью URL
- Поддерживает изменение языка по ссылке (например, простые пункты меню)
Шаг 1: Изменить RegisterRoutes в 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-адреса, файла cookie или культуры браузера по умолчанию.
// 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];
var langHeader = string.Empty;
if (cookie != null)
{
// set the culture by the cookie content
culture = cookie.Value;
}
else
{
// set the culture by the location if not specified - default to English for bots
culture = filterContext.HttpContext.Request.UserLanguages == null ? "en-EN" : filterContext.HttpContext.Request.UserLanguages[0];
}
// set the lang value into route data
filterContext.RouteData.Values[LangParam] = langHeader;
}
// 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. Добавление ссылок на языковые меню
Пункты меню состоят из ссылки с новым языком, указанным в качестве параметра маршрута.
например. (Пример бритвы)
<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-кодированная версия текущего относительного адреса страницы (чтобы мы вернулись на ту же страницу с выбранным новым языком).
Шаг 6. Включение определенных «небезопасных» символов в URL-адресах
Требуемая кодировка обратного URL означает, что вам нужно будет включить определенные escape-символы в web.config
, или существующий параметр URL вызовет ошибку.
В вашем web.config найдите тег httpRuntime
(или добавьте его) в <system.web>
и добавьте к нему следующее (в основном удалите%, который есть в стандартной версии этого атрибута):
requestPathInvalidCharacters="<,>,&,:,\,?"
В вашем web.config найдите раздел <system.webserver>
и добавьте в него следующее:
<security>
<requestFiltering allowDoubleEscaping="true"/>
</security>