Подтвердить что ты не робот

ASP.NET mvc, локализованные маршруты и язык по умолчанию для пользователя

Я использую локализованные маршруты asp.net mvc. Поэтому, когда пользователь переходит на английский сайт, это example.com/en/Controller/Action, а шведский - example.com/sv/Controller/Action.

Но как я могу убедиться, что, когда пользователь входит на сайт, он или она подходит к правильному языку напрямую? Я знаю, как получить язык, который я хочу, это не проблема. То, что я использовал, это то, что я поместил эту культуру в метод RegisterRoutes. Но поскольку моя страница находится в интегрированном режиме, я не могу получить запрос от Application_Start.

Итак, как я должен убедиться, что маршрут правильный с самого начала?

4b9b3361

Ответ 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) при регистрации ваших маршрутов.


  1. Вам нужно создать пользовательский 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);
        }
    }
}
  1. Теперь вам нужно создать 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="&lt;,&gt;,&amp;,:,\,?"

В вашем 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