To meet your needs change the Route config to:
routes.MapRoute(
name: "Language",
url: "{language}/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, language="en" },
constraints: new {language=new LanguageConstraint()}
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
The two key parts are the route itself, {language}/{controller}/{action}/{id}
and the constraint part, new {language=new LanguageConstraint()}
.
The first part will select the {language}
part as a variable (default being en for now) to the controller. The controller signature:
public ActionResult Index(string language) {
will pick up the new language variable. Since adding language to each and every controller could seem cumbersome you could create a ViewModelBase class to passed to every controller with a property that contains the language value, which every subsequent View Model class inherits from.
Without a constraint the route pattern would pick up all values in the url for the language part and writing a Regex expression to match all wanted language values would be tedious, I think it's easier to write an IRouteConstraint based class similar to the following:
public class LanguageConstraint : IRouteConstraint{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values,
RouteDirection routeDirection) {
//create accepted lanaguages collection from somewhere.
string[] languageArray = new[]{"en","jp", "de"};
string language = values["language"].ToString();
if (string.IsNullOrEmpty(language))
return false;
return languageArray.FirstOrDefault(l=>l.Equals(language,StringComparison.InvariantCultureIgnoreCase)) != null;
}
}
Simply it creates a list of known language values and check the provided language value against that list. If it doesn't exist false is returned and a 404 is thrown.