AttributeRouting
AttributeRouting copied to clipboard
Culture and Home Route
Hello,
I have the following AR configuration:
RouteTable.Routes.MapAttributeRoutes(x => {
x.AddRoutesFromAssemblyOf<HomeController>();
x.AddDefaultRouteConstraint(@"^id$", new RegexRouteConstraint(@"^\d+$"));
x.AddTranslationProvider<RouteTranslationProvider>();
x.ConstrainTranslatedRoutesByCurrentUICulture = true;
x.UseRouteHandler(() => new CultureRouteHandler());
x.CurrentUICultureResolver = (context, data) => { return (String)data.Values["culture"] ?? Thread.CurrentThread.CurrentUICulture.Name; };
});
Where the CultureRouteHandler is the following (quite simple for testing):
protected override IHttpHandler GetHttpHandler(RequestContext context) {
String culture = (String)context.RouteData.Values[_culture];
if (culture == null) {
culture = "en";
}
if (culture != null) {
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
context.RouteData.Values[_culture] = culture;
}
return base.GetHttpHandler(context);
}
I have the following action to modify culture:
[RoutePrefix("{culture}")]
public partial class CultureController : Controller {
[GET("{culture}/Culture/Modify"), AllowAnonymous]
public virtual ActionResult Modify(String culture) {
return RedirectToAction(MVC.Contact.Index());
} // Modify
} // CultureController
I called this action with PT value and I am redirected to Contact/Index with PT culture as expected.
But when I try to access Home/Index, and only this action, the culture goes back to EN.
In Home/Index I have the following:
public partial class HomeController : Controller {
[GET(""), AllowAnonymous, GoogleAnalytics]
public virtual ActionResult Index() {
return View();
} // Index
} // HomeController
If I add [RoutePrefix("{culture}")] and GET("culture") to Home/Index action then I get a 404 error.
What am I missing here?
Thank You, Miguel
If you changed your home controller as you mention you'll have a number of issues:
[RoutePrefix("{culture}")]
public partial class HomeController : Controller
{
[GET("{culture}"), AllowAnonymous, GoogleAnalytics]
public virtual ActionResult Index()
{
return View();
}
}
First, your generated URL will be "{culture}" (AR will not doubly apply the culture
URL param). Second, requesting ~/ will not match, since your culture
parameter is not optional. If you want culture to optional, do this:
[RoutePrefix("{culture?}")]
Your CultureRouteHandler
, when responding to requests for ~/, will find no value in route data for culture (since there's no value for a culture
URL param). So by your logic, the culture will be set to en
.
Hello,
Let me explain the configuration I am looking for: A) Have localized routes as 'en/signup' and 'pt/inscrever"; B) A route such as 'en/inscrever" would be invalid. C) An action to change from one culture to another and reload the current page.
I did a few changes and followed the Wiki and I still have a few problems. So I have:
[RoutePrefix("{culture?}")]
public partial class HomeController : Controller
{
[GET("{culture?}"), AllowAnonymous]
public virtual ActionResult Index()
{
return View();
}
}
[RoutePrefix("{culture}")]
public partial class UserController : Controller
{
[GET("SignUp"), AllowAnonymous]
public virtual ActionResult SignUp()
{
return View();
}
}
[RoutePrefix("{culture}")] public partial class CultureController : Controller { [GET("Culture/Modify"), AllowAnonymous] public virtual ActionResult Modify(String culture) { return Redirect(Request.UrlReferrer.AbsoluteUri); } // Modify }
Then I use the following configuration:
RouteTable.Routes.MapAttributeRoutes(x => {
x.AddRoutesFromAssemblyOf<HomeController>();
x.AddTranslationProvider<RouteTranslationProvider>();
x.ConstrainTranslatedRoutesByCurrentUICulture = true;
x.UseLowercaseRoutes = true;
x.UseRouteHandler(() => new CultureRouteHandler());
x.CurrentUICultureResolver = (context, data) => {
return (String)data.Values["culture"] ?? Thread.CurrentThread.CurrentUICulture.Name;
};
});
Where CultureRouteHandler is as follows:
public class CultureRouteHandler : MvcRouteHandler { private const String _culture = "culture"; protected override IHttpHandler GetHttpHandler(RequestContext context) { String culture = (String)context.RouteData.Values[_culture]; if (culture == null) { culture = "en"; } Thread.CurrentThread.CurrentCulture = new CultureInfo(culture); Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture); context.RouteData.Values[_culture] = culture; return base.GetHttpHandler(context); } }
Which results in the following error when I run the site: Unable to cast object of type 'System.Web.Mvc.UrlParameter' to type 'System.String'.
In the following configuration code line:
x.CurrentUICultureResolver = (context, data) => {
return (String)data.Values["culture"] ?? Thread.CurrentThread.CurrentUICulture.Name;
};
To make this work I needed to do a few changes. In configuration:
x.CurrentUICultureResolver = (context, data) => {
return data.Values["culture"] == null ? Thread.CurrentThread.CurrentUICulture.Name : data.Values["culture"].ToString();
};
And in the CurrentHandler:
String culture = context.RouteData.Values[_culture].ToString();
if (String.IsNullOrWhiteSpace(culture)) {
culture = "en";
}
Now points (A) and (B) seems to be solved. But there is a problem with C.
When I try to change the culture by calling Culture/Modify it does not change. It works only if use RedirectToAction instead of Redirect. Something like:
return RedirectToAction(MVC.Home.Index());
I then though the problem might be having {culture} in Culture / Modify action.
But if I remove it I then get the error: Object reference not set to an instance of an object.
In my Culture route handler:
String culture = context.RouteData.Values[_culture].ToString();
Probably I am missing something on how culture is being defined in the various situations.
When I fix one I get a problem in the other part.
Do you know what am I missing?
Thank You, Miguel
I am having a few more problems. Consider the CultureHandler code I mentioned before:
String culture = context.RouteData.Values[_culture].ToString();
if (String.IsNullOrWhiteSpace(culture)) {
culture = "en";
}
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
context.RouteData.Values[_culture] = culture;
return base.GetHttpHandler(context);
1 - When I try to access routes.axd é get, in this code, the error "Culture is not supported". I checked and culture values is "routes.axd" ...
2 - I have an error controller as follows:
[RoutePrefix("{culture?}")]
public partial class ErrorController : Controller
{
[GET("{culture?}/Error/404"), AllowAnonymous]
public virtual ActionResult NotFound() {
}
}
I am able to access it using en/error/404 and pt/error/404 but not using error/404.
Why is that if culture is allowed to be null ...
3 - I occasionally get an error where culture in my Culture Handler is invalid. I checked the culture values in those cases and it is "favicon.ico". This is strange since I have a specific Ignore route for that.
Here is my routing configuration:
RouteTable.Routes.MapAttributeRoutes(x => {
x.AddRoutesFromAssemblyOf<HomeController>();
x.AddDefaultRouteConstraint(@"^id$", new RegexRouteConstraint(@"^\d+$"));
x.AddTranslationProvider<RouteTranslationProvider>();
x.AddTranslationProvider<BlomRouteTranslationProvider>();
x.ConstrainTranslatedRoutesByCurrentUICulture = true;
x.UseLowercaseRoutes = true;
x.UseRouteHandler(() => new CultureRouteHandler());
x.CurrentUICultureResolver = (context, data) => { return data.Values["culture"] == null ? Thread.CurrentThread.CurrentUICulture.Name : data.Values["culture"].ToString(); };
// x.CurrentUICultureResolver = (context, data) => { return (String)data.Values["culture"] ?? Thread.CurrentThread.CurrentUICulture.Name; };
});
GlobalConfiguration.Configuration.Routes.MapHttpAttributeRoutes(x => {
x.AddRoutesFromAssemblyOf<HomeController>();
});
AreaRegistration.RegisterAllAreas();
RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
RouteTable.Routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.([iI][cC][oO]|[gG][iI][fF])(/.*)?" });
RouteTable.Routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }, typeof(HomeController).Assembly);
Thank You, Miguel
Sorry Miguel. Will reply to this. Would be totally awesome if you could create a new web app and repro the problem for me, then put that app on github so i can fork it.
I will create a sample application during next week, ok?
I have been trying a few approaches with localization and AR. And I made a list of the problems and examples of possible solutions ...
I will upload the the new app and post those problems.
Hello,
I just placed the default MVC web site using AR for localization: https://github.com/shapper/MVCLAR
I started with a small initial commit to try to clarify two problems:
1 - When the site starts the default culture in the controller is "en-US". Not the expected "en" set in CultureRouteHandler ... And when it reaches the view it is PT. No idea why.
2 - I am using a Culture/Modify action to change the culture. But this works only when redirecting to another action.
Isn't possible to have a link for the current page with the desired culture in "culture"?
Thank you, Miguel