我知道这是一个非常古老的问题,但最近刚刚解决了所有相关问题,所以我想分享我的解决方案。
以下是一个完整的解决方案,包括一些额外的技巧,以便轻松更改语言。它允许指定特定的文化,而不仅仅是指定特定的语言(但在本例中仅保留语言部分)。
功能包括:
- 回退到浏览器区域设置以确定语言
- 使用 cookie 在访问间保留语言
- 通过 URL 覆盖语言
- 支持通过链接更改语言(例如简单的菜单选项)
步骤 1:修改 RouteConfig 中的 RegisterRoutes
这个新路由包括一个限制条件(正如其他人也建议的那样),以确保语言路径不会获取某些标准路径。没有必要设置默认语言值,因为所有这些都由 LocalisationAttribute 处理(请参见步骤 2)。
public static void RegisterRoutes(RouteCollection routes)
{
...
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}" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
步骤二:创建本地化属性
这将在处理控制器请求之前,根据URL、cookie或默认浏览器语言环境更改当前语言环境。
public class LocalisationAttribute : ActionFilterAttribute
{
public const string LangParam = "lang";
public const string CookieName = "mydomain.CurrentUICulture";
private const string Cultures = "en-GB en-US de-DE fr-FR es-ES ro-RO ";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var culture = (string)filterContext.RouteData.Values[LangParam];
if (string.IsNullOrEmpty(culture) || !Cultures.Contains(culture))
{
var cookie = filterContext.HttpContext.Request.Cookies[CookieName];
var langHeader = string.Empty;
if (cookie != null)
{
culture = cookie.Value;
}
else
{
culture = filterContext.HttpContext.Request.UserLanguages == null ? "en-EN" : filterContext.HttpContext.Request.UserLanguages[0];
}
filterContext.RouteData.Values[LangParam] = langHeader;
}
var language = culture.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries)[0];
filterContext.RouteData.Values[LangParam] = language;
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(language);
HttpCookie _cookie = new HttpCookie(CookieName, culture);
_cookie.Expires = DateTime.Now.AddYears(1);
filterContext.HttpContext.Response.SetCookie(_cookie);
base.OnActionExecuting(filterContext);
}
}
步骤三:将本地化应用于所有控制器
例如:
[Localisation] <<< ADD THIS TO ALL CONTROLLERS (OR A BASE CONTROLLER)
public class AccountController : Controller
{
步骤四:更改语言(例如从菜单中)
这是一个有些棘手并需要一些解决方法的地方。
在您的账户控制器中添加一个ChangeLanguage方法。这将从“先前路径”中剥离出任何现有的语言代码,以使新语言生效。
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))
{
id = Server.UrlDecode(id);
id = removeLanguage.Replace(id, @"/");
return Redirect(id);
}
return Redirect(@"/");
}
步骤五:添加语言菜单链接
菜单选项包括一个链接,新语言作为路由参数指定。
例如(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是当前页面的编码形式,以便成为URL的id参数。这意味着您需要启用某些转义序列,否则Razor会将其拒绝作为潜在的安全违规行为。
注意:对于非Razor设置,您基本上需要一个锚点,其中包含新语言和当前页面相对URL的路径,例如:
http://website.com/{language}/account/changelanguage/{existingURL}
其中,{language}
是新的区域代码,{existingURL}
是当前相对页面地址的URL编码版本(以便我们选择新语言时返回到同一页面)。
步骤6:启用URL中的某些“不安全”字符
由于需要对返回URL进行编码,因此您需要在web.config
中启用某些转义字符,否则现有的URL参数将导致错误。
在您的web.config
中,找到<system.web>
下的httpRuntime
标签(或添加它),并添加以下内容(基本上是删除该属性中的%):
requestPathInvalidCharacters="<,>,&,:,\,?"
在您的web.config文件中,找到
<system.webserver>
部分,并在其中添加以下内容:
<security>
<requestFiltering allowDoubleEscaping="true"/>
</security>