Localization with ASP.NET MVC using Routing - asp.net-mvc

I'm going to implement an ASP.NET MVC3 powered site which will be multilingual with distinct Urls for different languages (ie. http://acme.com/en/faq, http://acme.com/de/faq etc).
Is the way outlined in this article still the way to go with ASP.Net MVC3?

I haven't use a route handler like the author of the blog suggest but it seems like a good idea.
I typically just add the language as a parameter to the route, though.
routes.MapRoute("someroute", "{language}/some/path/{p1}/{p2}",
new { controller = "SomeController", action = "SomeAction"});
You can default the language parameter right there on the route definition but I usually do it on the base controller since I try to default to the language that the user has defined on their browser preferences (it comes in the HTTP request.)
One caveat of the approach described in the blog post is that is changes the main "CurrentCulture". You don't want to change the main "CurrentCulture" on every request, you only need to change the "CurrentUICulture". Changing the main "CurrentCulture" would affect the way your server behaves. For example, when talking to the database it will be using the user's culture and that's probably not what you want.
People tend to change the main CurrentCulture to gain formatting on dates and numbers (which is nice) but don't realize there are some side effects to doing that. Instead of changing the main CultureThread you'll need to pass the users' culture to your number and formatting functions (e.g. someDate.ToString(format, culture)

Yes, nothing have changed from the routing perspective.

Related

How to set {city} in all the routes MVC 5

I am developing a marketplace application which is supposed to have different products and vendors which are mapped to different cities.
The idea is that I need to store the CurrentCity in context so that I can use it to construct urls, filter data, fetch delivery areas etc.
e.g.
www.mywebsite.com/cityA/listings
www.mywebsite.com/cityB/listings
www.mywebsite.com/cityA/cart
www.mywebsite.com/cityB/cart
Something like the MoonPig website (https://www.moonpig.com/uk/Gift/Flowers/)
Currently, I am passing the city as a parameter in almost all the controller methods and also storing it in a cookie.
Alternatively, I am also thinking of creating a BaseController and possibly inject it in the OnActionExecuting(ActionExecutingContext context).
But the problem with the first approach is that all the Action methods need to have "city" as a parameter and I need to have it in context for doing something like
#Url.Action("Index", "Listings", new {city = cityName})
If I use the second approach, then I don't think I'll get the urls which have city in them.
I am ideally looking for a solution with which I can inject a city parameter as Base route / segment in the MVC RouteDictionary so that all the Urls are generated accordingly (with #url helper).
Is this possible or is there a better way to tackle this problem?
Would really appreciate if someone can show me a direction.
But the problem with the first approach is that all the Action methods need to have "city" as a parameter
This assumption is incorrect, since MVC automatically passes values from the current context when generating URLs, so there is no need to explicitly pass city as long as it is configured in the route and present in the URL. See this answer for how you can utilize this behavior for localization, which is similar to what you are doing.

Using custom routes instead of /controller/action/{id}

I have to make vanity urls for an already existing site. The requirements are:
The existing controllers are a kind of hierarchical structure and can't go away or be overridden in any way. Examples: domain.com/Dog/vanity and domain.com/Cat/vanity.
Keep existing actions. Any existing actions must take priority so that the page for that action is not stomped on by the vanity url.
take future pages and actions into account so that the above requirement is met (a new vanity url is ignored and the action/view executed instead)
To date, I have tried various solutions with routing that allow me to have domain.com/vanity which is nice but the marketing guys don't like because of the different departments within the company. I've tried routing which will override the existing actions and treats them all as vanities (also not feasible). I've a solution in place that programmatically deals with the url that was requested and redirects to a page that actually exists but this is not scalable in any way.
So far, I know that the vanity portion can be treated as a parameter to the action so that I can fire off the default page in the route (Index) but this is, so far, doesn't preserve the structure.
TL;DR: I need to have a solution that allows for domain/controller/vanity structure while also allowing domain/controller/action
Using AttributeRouting for MVC4 you can accomplish a working solution until you ramp up the replacement project. It'll allow you to keep existing routes while adding new, custom ones with little impact.
[Route("my-vanity/is-cool/post/{id}")]
public ActionResult Index(int id)
{
}
The important part is to remember priority, so you write routes that don't overwrite/are overwritten by existing routes. You can steer this to some degree with properties on the attribute. Below is just an example that will put the added route last in priority for the entire site.
[Route("my-vanity/is-cool", SitePrecedence = -1)]
public ActionResult Index()
{
}
ASP.NET WebApi2 have built in support for attribute routing. With it you can define URL's in whatever way you like (instead of following the /controller/action pattern)
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

ASP.NET MVC - CMS Questions

I'm looking at developing an application that will include a CMS. I'm a seasoned web forms developer but only really just moving into MVC.
I have a couple of questions that I hope some of you guys can answer:
First, my current web forms CMS allows users to create a page, and then "drop" any number of user controls onto that page they have created. The way I do this is to create an entry in the DB together with the path and then use the LoadControl method.
I can see I can do this with partial views, but partial views have no code behind. If I've potentially got 100 controls that people can drop onto a page, does this mean that the ViewBag in the controller needs to cater for all 100 controls just in case they are used on the view? For example, a web forms user control will contain logic: rptItems.DataSource = blah; rptItems.DataBind()
With MVC, I'm assuming that logic will be in the view controller and the view would access it by the ViewBag? I'm a little confused at how to do this.
Secondly, how would you handle deep routing?
EG:
Store/Products/Category is fine, but what about Store/Products/Category/Delivery/UK ? Would I need to set up a route in global.asax for each route I need? In web forms, I just called the ReWritePath method and handled the routing myself using regular expressions.
Thanks for the time to read this, and hopefully answer some of my queries
For your second question, (ie, "deep routing"), you can handle this within your controller instead of adding real routes. Each part of the url is available via the RouteData.Values collection inside of your controller action. So, your route may look like
~/Store/Products/Category/{*params}
Assuming typical route configuration, this would call the Category(...) action method on ~/areas/store/controllers/storeController, which could then grap delivery and uk from the RouteData.Values collection.
There are a lot of other approaches to this - storing routes in a database and using associated metadata to find the correct controller and method - but I think this is the simplest. Also, it may be obvious, but if you really only need two parameters beyond 'Category' in your example, you could just use
public ActionResult Category(string category, string region)
{
...
}
and a route:
~/store/{controller}/{action}/{category}/{region}/{*params}
Delivery and UK would be mapped to the the category and region parameters, respectively. Anything beyond uk would still be available via the RouteData.Values collection. This assumes that you don't have more specific routes, like
~/store/{controller}/{action}/{category}/{region}/{foo}/{bar}/{long_url}/{etc}
that would be a better match. ({*params} might conflict with the second route; you'll have to investigate to see if it's a problem.)
For your first question:
You can dynamically generate the view source and return it as a string from the controller, eliminating the need to pass a lot of stuff via ViewBag. If a virtual page from your CMS database requires inclusion of partial views, you would add the references to those components when generating the page. (This may or may not address your problem - if not, please provide more information.)

Suggestions for supporting multilingual routes in ASP.NET MVC

There were questions about multilingual apps in MVC here on SO but they were mostly answered by giving details about implementing Resource files and then referencing those Resource strings in Views or Controller. This works fine for me in conjunction with the detection of user's locale.
What I want to do now is support localized routes. For instance, we have some core pages for each website like the Contact Us page.
What I want to achieve is this:
1) routes like this
/en/Contact-us (English route)
/sl/Kontakt (Slovenian route)
2) these two routes both have to go to the same controller and action and these will not be localized (they will be in English because they are hidden away from the user since they are part of the site's core implementation):
My thought is to make the Controller "Feedback" and Action "FeedbackForm"
3) FeedbackForm would be a View or View User control (and it would use references to strings in RESX files, as I said before, I already have set this up and it works)
4) I have a SetCulture attribute attached to BaseController which is the parent of all of my controllers and this attribute actually inherits FilterAttribute and implements IActionFilter - but what does it do? Well, it detects browser culture and sets that culture in a Session and in a cookie (forever) - this functionality is working fine too. It already effects the Views and View User Controls but at this time it does not effect routes.
5) at the top of the page I will give the user a chance to choose his language (sl|en). This decision must override 4). If a user arrives at our website and is detected as Slovenian and they decide to switch to English, this decision must become permanent. From this time on SetCulture attribute must somehow loose its effect.
6) After the switch, routes should immediately update - if the user was located at /sl/Kontakt
he should immediately be redirected to /en/Contact-us.
These are the constraints of the design I would like. Simply put, I do not want English routes while displaying localized content or vice-versa.
Suggestions are welcome.
EDIT:
There's some information and guidance here - Multi-lingual websites with ASP.NET MVC, but I would still like to hear more thoughts or practices on this problem.
Translating routes (ASP.NET MVC and Webforms)
How about this?
Create custom translate route class.
Localization with ASP.NET MVC using Routing
Preview:
For my site the URL schema should look
like this in general:
/{culture}/{site}
Imagine there is a page called FAQ,
which is available in different
languages. Here are some sample URLs
for these pages:
/en-US/FAQ /de-DE/FAQ /de-CH/FAQ
Why not create the action names desired and simply RedirectToAction for the single, real implementation?
public ActionResult Kontakt() {
return RedirectToAction("Contact");
}
public ActionResult Contact() {
return View();
}
I just used a simple solution with "Globalization Resources", like this:
routes.MapRoute(
"nameroute", // Route name
App_GlobalResources.Geral.Route_nameroute+"/{Obj}", // URL with parameters
new { controller = "Index", action = "Details", Obj = UrlParameter.Optional } // Parameter defaults
);
But, you could customize as needed.

ASP.NET MVC: When to set Thread.CurrentThread.CurrentUICulture?

I am just beginning to localize an ASP.NET MVC application. Most of the strings will be defined in resource files and retrieved via Matt's Localization Helpers. Other strings must be stored in a database.
My Question:
Should I set CurrentUICulture early in the request pipeline and use that throughout the application, or directly use Request.UserLanguages[0] whenever needed?
Right now I'm thinking that I should set CurrentUICulture in Application_BeginRequest. The implementation would look something like this:
protected void Application_BeginRequest(object sender, EventArgs e)
{
var cultureName = HttpContext.Current.Request.UserLanguages[0];
Thread.CurrentThread.CurrentUICulture = new CultureInfo(cultureName);
}
Is this the best place to set CurrentUICulture and is Request.UserLanguages[0] the best place to get that info?
Update:
Ariel's post shows this can be defined without code, using web.config
<system.web>
<!--If enableClientBasedCulture is true, ASP.NET can set the UI culture and culture for a Web page automatically, based on the values that are sent by a browser.-->
<globalization enableClientBasedCulture="true" culture="auto:en-US" uiCulture="auto:en"/>
Here is a sample using an HttpModule:
http://weblogs.manas.com.ar/smedina/2008/12/17/internationalization-in-aspnet-mvc/
Other options, create a base Controller class and implement the localization logic there.
Or use an action filter attribute, but you'll have to remember to add it on every controller or combine this approach with the base Controller class.
Request.UserLanguages[0] can only be a hint what language the users wishes to see. Most users dont know where to change the browser language.
Another point: Dont be sure that Request.UserLanguages[0] is a valid language. It can even be null. (Not sure what bots have there)
You usually have a Language chooser on the page. Once a user has selected a language there, it is stored in a cookie, session or url. I like to use url because I think it looks pretty.
If a user sees your page without having set a language on your page, you should check if Request.UserLanguages[0] is a language you support and set Thread.CurrentThread.CurrentUICulture.
I use a filter to set Thread.CurrentThread.CurrentUICulture. Thats ok as long as no other filter is using Thread.CurrentThread.CurrentUICulture. Otherwise you would need to set the right execution order for filters.
I also use Matts helper and it worked very well so far.

Resources