You could try removing the default "lang" value from the no language route, and adding a regex constant to the language route (For example allowing culture names like "en" or "en-US", check this question about a regex for culture names):
routes.MapRoute(
name: "Default lang",
url: "{lang}/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { lang = @"[a-z]{2,3}(?:-[A-Z]{2,3})?" }
);
routes.MapRoute(
name: "Default no language",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
If you don't add a constraint for the lang parameter, an url like /Statistics/Reports will always match the language route (because its defined first) with the wrong parameters lang=Statistics, controller=Report, action=Index
So with this route configuration, you will get these results:
/ -> routed to Home/Index, there is NOT a route value for lang
/Home -> routed to Home/Index, there is NOT a route value for lang
/Home/About -> routed to Home/About, there is NOT a route value for lang
/en -> routed to Home/Index, there is a route value with lang=en
/en/Home -> routed to Home/Index, there is a route value with lang=en
/en/Home/About -> routed to Home/About, there is a route value with lang=en
Then you can use the lang route value on any logic involved in getting the resources, and when the value is missing from the route data you could treat it as the default language.
For example, let's say you have created a global filter that will set the CurrentCulture and CurrentUICulture on the thread based on this route parameter. You could leave them with their default values (the ones configured on your server or web.config) when no lang was provided, otherwise override them based on the lang parameter:
public class InitializeCultureAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.RouteData.Values.ContainsKey("lang")) return;
var culture = filterContext.RouteData.Values["lang"] as string;
if (String.IsNullOrEmpty(culture)) return;
var cultureInfo = CultureInfo.GetCultureInfo(culture);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
}
}
PS. I don't mean you should use this attribute, I just want to show an example of how you could deal with not having a lang value in the route data. So with the routes above you should be able to adapt your logic dealing with resources, by looking at the lang parameter in the route data (although I guess you may want to set the CurrentUICulture in that case!)
Hope this helps!