I have be configuring localisation for a website that is already published in ASP.NET MVC 5.
The guide I was following to integrate localisation into my site is from codeproject and I found it quite good: https://www.codeproject.com/Articles/1095786/Route-friendly-localization-of-ASP-NET-MVC
Now to explain the issue, whenever I load the homepage index http://www.example.com/
with no route path it will redirect to the correct language http://www.example.com/en
which is the desired result.
My website urls are in the format of http://www.example.com/controller/action
. Whenever I visit http://www.example.com/controller/
it will now redirect me back to http://www.example.com/en
rather than http://www.example.com/en/controller/index
. And when I try typing http://www.example.com/controller/action
it throws a 404 error rather than http://www.example.com/en/controller/action
.
I think the issue is in the GetLocalistedUrl
function in the LocalizationHelper.cs
file.
public static string GetLocalisedUrl(Uri initialUri, IList<string> controllersNames, IList<string> userLangs)
{
var res = string.Empty;
var supportedLocales = GetSupportedLocales();
var origUrl = initialUri;
// Dicide requested url to parts
var cleanedSegments = origUrl.Segments.Select(X => X.Replace("/", "")).ToList();
// Check is already supported locale defined in route
// cleanedSegments[0] is empty string, so lang parameter will be in [1] url segment
var isLocaleDefined = cleanedSegments.Count > 1 && supportedLocales.Contains(cleanedSegments[1]);
// does request need to be changed
var isRequestPathToHandle =
// Url has controller's name part
(cleanedSegments.Count > 1 && cleanedSegments.Intersect(controllersNames).Count() > 0) ||
// This condition is for default (initial) route
(cleanedSegments.Count == 1) ||
// initial route with lang parameter that is not supported -> need to change it
(cleanedSegments.Count == 2 && !supportedLocales.Contains(cleanedSegments[1]));
if (!isLocaleDefined && isRequestPathToHandle)
{
var langVal = "";
// Get user preferred language from Accept-Language header
if (userLangs != null && userLangs.Count > 0)
{
// For our locale name approach we'll take only first part of lang-locale definition
var splitted = userLangs[0].Split(new char[] { '-' });
langVal = splitted[0];
}
// If we don't support requested language - then redirect to requested page with default language
if (!supportedLocales.Contains(langVal))
langVal = supportedLocales[0];
var normalisedPathAndQuery = origUrl.PathAndQuery;
if ((cleanedSegments.Count > 2 &&
!controllersNames.Contains(cleanedSegments[1]) &&
controllersNames.Contains(cleanedSegments[2])) ||
(cleanedSegments.Count == 2) && (!controllersNames.Contains(cleanedSegments[1])))
{
// Second segment contains lang parameter, third segment contains controller name
cleanedSegments.RemoveAt(1);
// Remove wrong locale name from initial Uri
normalisedPathAndQuery = string.Join("/", cleanedSegments) + origUrl.Query;
}
// Finally, create new uri with language loocale
res = string.Format("{0}://{1}:{2}/{3}{4}", origUrl.Scheme, origUrl.Host, origUrl.Port, langVal.ToLower(), normalisedPathAndQuery);
}
return res;
}
The function is called in the IHttpModule extension that was created.
public class LangQueryAppenderModule : IHttpModule
{
/// <summary>
/// List of supported locales
/// </summary>
private readonly IList<string> _supportedLocales;
/// <summary>
/// We need to have controllers list to correctly handle situations
/// when target method name is missed
/// </summary>
private readonly IList<string> _controllersNamesList;
public LangQueryAppenderModule()
{
// Get list of supported locales
_supportedLocales = LocalizationHelper.GetSupportedLocales();
// Get controllers list of current project by reflection
var asmPath = HttpContext.Current.Server.MapPath("~/bin/Central.dll");
Assembly asm = Assembly.LoadFile(asmPath);
var controllerTypes = asm.GetTypes()
.Where(type => typeof(Controller).IsAssignableFrom(type));
_controllersNamesList = new List<string>();
foreach (var controllerType in controllerTypes)
{
var fullName = controllerType.Name;
// We need only name part of Controller class that is used in route
_controllersNamesList.Add(fullName.Substring(0, fullName.Length - "Controller".Length));
}
}
// In the Init function, register for HttpApplication
// events by adding your handlers.
public void Init(HttpApplication application)
{
application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
}
private void Application_BeginRequest(Object source, EventArgs e)
{
try
{
HttpApplication app = (HttpApplication)source;
HttpContext ctx = app.Context;
// We will redirect to url with defined locale only in case for HTTP GET verb
// cause we assume that all requests with other verbs will be called from site directly
// where all the urls created with URLHelper, so it complies with routing rules and will contain "lang" parameter
if (string.Equals(ctx.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
var localisedUri = LocalizationHelper.GetLocalisedUrl(ctx.Request.Url, _controllersNamesList, ctx.Request.UserLanguages);
if (!string.IsNullOrEmpty(localisedUri))
// Perform redirect action to changed url if it exists
ctx.Response.Redirect(localisedUri);
}
}
catch (Exception)
{
// Some logging logic could be here
}
}
public void Dispose() { }
}