I'm working on a website that must be in both English and Spanish. I have used resx files and am adapting the culture setting based on the browser's language setting as the code below shows (this is on the global.asax.cs):
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
string culture = "en-US";
if (Request.UserLanguages != null)
{
culture = Request.UserLanguages[0];
}
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture);
}
This does work! However, the user should also be able to change the language. To accomplish this, so I created a CultureController as my code below shows, which is consumed by the site through a link:
public class CultureController : Controller
{
public ActionResult SetLanguage(string name)
{
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(name);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
System.Web.HttpContext.Current.Session["culture"] = name;
return RedirectToAction("Index", "Home");
}
}
And I have these links on my site:
<li>EspaƱol</li>
<li>English</li>
This does not work. It's weird, cause when I debug it the method is actually used and the data flow seems correct, it gets the name parameter and sets it, but after the redirection the language does not change and the requests are shown as failed. Do you have any suggestions what am I doing wrong?
Would using a different URL for the languages work?
I did that at a previous employer, so that we didn't have to change one's browser language in order to test the different languages, and also if that a user prefers one language over another (situationally) but their browser isn't configured correctly, they have control.
Example:
http://example.com/en/home vs http://example.com/es/home
I was able to use URL rewriting to extract that into a querystring parameter (quite unnecessary with MVC as you can use routing parameters)
You could quite easily add some code to detect browser language and redirect the user to the correct URL, and allow a cookie to override these preferences.
I'm aware that this is a vastly different answer than you were looking for, but it made it a snap to test ("/en/" was a hack we didn't actually set up per se; I just made it work so that we could debug the pages without having to translate the text.)
It was quite easy to throw up the 6 versions of the page in different tabs and check that everything looked correct. We had German and Chinese, and those are good test languages: German nouns get long, and it's very easy to determine if you missed translating any phrases with Chinese.
It seems you don't have route for localization.First you have to define routes like below.
routes.MapRoute(
name: "LocalizedDefault",
url: "{lang}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index"},
constraints: new {lang="es-ES|fr-FR|en-US"}
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index", lang = en-US }
);
After setting thread language you have to direct it with lang like below.
filterContext.HttpContext.Response.RedirectToRoute("LocalizedDefault",
new { lang=culture,
controller = filterContext.RouteData.Values["controller"],
action = filterContext.RouteData.Values["action"]
});
return;
For full implementation have a look at this article.
Related
I'm having a problem either with my routing config file or something else in the Asp.Net MVC app that keeps triggering each index action method in every controller I visit. If I click the action link below I will be taken to the page /profile/general, see the view results rendered on the screen, but then something in the app, not sure what, triggers the index action to be called on that controller! Being that I don't have index action methods on most of the controllers I end up getting exceptions visiting each page.
ex. I click the action link below
#Html.ActionLink("Profile", "general", "profile", new { }, new { #class = "btn-sm", #style = "font-weight: bold; font-size: 1em;" })
or just refresh the browser.
I enter into the general action method seen below
public ActionResult General()
{
try
{
ViewBag.MenuItem = "profile";
ViewBag.UserMenuItem = "general";
var viewModel = _yogaProfileService.GetGeneralInfo(User.Identity.GetUserId());
return View(viewModel);
}
catch (Exception ex)
{
_errorService.LogError(ex, Request);
ViewBag.Message = "Oh No! Something went wrong fetching your info. We're looking into this now!";
return View("Error");
}
}
the view gets called and I see it on the page, but then something calls the index method on that same controller and I step into the index action method here. The debugger enters into the method below. It doesn't render the index's view on the screen, I still see the general view, so not sure what's going on here!
public ActionResult Index()
{
try
{
ViewBag.MenuItem = "profile";
return View();
}
catch (Exception ex)
{
_errorService.LogError(ex, Request);
ViewBag.Message = "Oh No! Something went wrong fetching your profile. We're looking into this now!";
return View("Error");
}
}
of course I just have the index method here for display reasons, so in the app I have it removed, so I get an exception thrown on almost every page I visit because it looks for a index action method on each controller I visit
System.Web.HttpException: A public action method 'Index' was not found on controller 'YogaBandy2017.Controllers.ProfileController'.
I don't know if it's a routing issue or something else? I tried looking in the debugger to see the networking to determine what was calling the index, and also in the call stack but it just says 'external' is it possible some Javascript is calling it, if so, how would I look for this?
Here is my routing file
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "RateRoute",
url: "rate/event/{id}",
defaults: new { controller = "rate", action = "event" },
constraints: new { id = #"\d+" }
);
routes.MapRoute(
name: "SpaceCleanRoute",
url: "space/{id}",
defaults: new { controller = "space", action = "index", id = UrlParameter.Optional },
constraints: new { id = #"\d+" }
);
routes.MapRoute(
name: "PublicSpaceRoute",
url: "space/public/{title}",
defaults: new { controller = "space", action = "public" },
constraints: new { title = #"^[A-Za-z0-9-]+$" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
UPDATE - I only see it in IE, Chrome and Firefox don't have my issue
Here is a pic of my debugger showing the pending request
UPDATE2 - I don't believe it's a Javascript issue, if I don't load the .js file the index still gets called. So I don't believe it has anything to do with Javascript
After discussions in the Stackoverflow chat, we discovered that IE 11 was triggering a call to the default action for the following:
<link href="" rel="icon">
IE was generating XMLHttpRequest to the Controller's default action.
This is occurring in IE 11.
By removing the empty href="", we were able to resolve the issue.
The following was the solution:
<link rel="icon">
Per the HTML5 preview spec:
https://dev.w3.org/html5/spec-preview/the-link-element.html#attr-link-href
The destination of the link(s) is given by the href attribute, which
must be present and must contain a valid non-empty URL potentially
surrounded by spaces. If the href attribute is absent, then the
element does not define a link.
The types of link indicated (the relationships) are given by the value of the rel attribute, which, if present, must have a value that is a set of space-separated tokens. The allowed keywords and their meanings are defined in a later section. If the rel attribute is absent, has no keywords, or if none of the keywords used are allowed according to the definitions in this specification, then the element does not create any links.
Two categories of links can be created using the link element: Links
to external resources and hyperlinks. The link types section defines
whether a particular link type is an external resource or a hyperlink.
One link element can create multiple links (of which some might be
external resource links and some might be hyperlinks); exactly which
and how many links are created depends on the keywords given in the
rel attribute. User agents must process the links on a per-link basis,
not a per-element basis
The exact behavior for links to external resources depends on the
exact relationship, as defined for the relevant link type. Some of the
attributes control whether or not the external resource is to be
applied (as defined below).
For external resources that are represented in the DOM (for example,
style sheets), the DOM representation must be made available even if
the resource is not applied. To obtain the resource, the user agent
must run the following steps:
If the href attribute's value is the empty string, then abort these steps.
Resolve the URL given by the href attribute, relative to the
element.
If the previous step fails, then abort these steps.
Fetch the resulting absolute URL.
With that being said, IE11 does not appear to be aborting the steps defined above due to an empty "href" value... It continues to resolve the external resource as defined in step 4.
There is some chatter on this with Edge.
https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8088887/
I am fairly new to MVC. Recently I developed a site and hosted it behind a proxy server. I access my site using internalhostname/site/{controller}/{action}/{id} for test, which works fine.
However, when my users are connecting the site, they would use an external url like this: externalhostname/apps/site/{controller}/{action}/{id}. Now when my views attempt to initiate a call to the controller according to the default route config, the URLs being generated become externalhostname/site/{controller}/{action}/{id}, notice "/apps" is gone.
Well, this is a known problem to me. Because when creating the URL, host name does not include "/apps". In other sites created in regular ASP.NET page, I would simply hijack the URL creation and replace host with host/apps, that can fix the issue. But I don't know how to do this in MVC world.
Also I am using Telerik controls (for MVC) that also initiate requests by controller and action, which lead to the wrong URL eventually.
The route config is default. I did try to change the rule but all of those only affect the url format after . Nothing could allow me to change the behavior ahead of it.
I have been struggling for days and couldn't see a way out. Appreciate for some advice. Thank you.
I cannot change the proxy rule. That is not an option to me.
Environment: MVC 4, IIS 7.0, if these matter.
Try to add new entry in your route config.
routes.MapRoute(
"Default2", // Route name
"apps/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults,
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Take note of the order.
I have a Controller with the name "Hem" and Action name is "Om".
And default language i have set Swedish.
So route will be on Swedish site, it's
/sv/Hem/Om
Now I want to change language to "en" by clicking English in language section.
So route will be set automatically like this way :
/en/Home/About
But functionality should be work of /sv/Hem/Om and In address bar should be display as /en/Home/About
Experts can you please help me out.
You can do this way.
routes.MapRoute(
"English route",
"en/{controller}/{action}/{id}"
new { controller = "Home", action = "Index", language = "en" },
);
routes.MapRoute(
"FrenchHome",
"/sv/Hem/Om",
new { controller = "Home", action = "Index", language = "fr" }
);
or you can do that way:
public class GenericRoutes
{
public string Controller {get;set;}
public string Action {get;set;}
public string Url{get;set;}
public string RouteName{get;set;}
}
public List<GenericRoutes> Routes = new List<GenericRoutes>();
Routes.Add(new GenericRoutes{Cotroller="bl",Action="cl",Url="bl/cl"})
for(int i=0;i<Routes.count();i++)
{
routes.MapRoute(
Routes[i].RouteName,
Routes[i].Url,
new { controller = Routes[i].Controller, action = Routes[i].Action },
);
}
I personally would avoid this approach for multilingual sites. Yes it is technically possible to do what you are asking but most sites do not handle multiple languages in this way. ASP.net has had ability to localize pages for a long time and I would recommend this approach instead.
Localization involves putting resource keys in your view template instead of hard coding your strings. Then you would set the culture of your thread, usually by the http accept-language header and the site would chose the appropriate strings for that culture to put into the page view. The only thing you need to maintain then is sets of strings for each language.
The benefit for this approach is that you write your views only once. When you have 2 or even 3 sets of views you run the risk of having those versions of your site diverge. I personally have seen this happen and its a hard problem to get back from. Also you get to separate your "language problem" from whatever problem your website is solving, meaning your domain isn't cluttered with boilerplate code to maintain a fancy language switching technique, instead of going with the solution that's included with the platform.
If you are interested in doing multi-language sites in .net the right way I would recommend learning about Localization and Globalization, here is a good place to start :)
Beginners Tutorial
Scott Hanselman tutorial - MVC 3 + Jquery version
First of all, I am not an expert. But in order to route user to different controller you can implement custom routing and configure routes dynamically.
To understand how routing works you may consider checking this link out.
Here is a quick trick to do that:
Here is RegisterRoutes method which register all routes for application
public static void RegisterRoutes(RouteCollection routes)
{}
now get current language from url(get first segment of url (en in your case))
query your data source for current language
add your routes here from database or any other source using foreach.
.
foreach (var route in RouteValues)
{
route.UniqueName,
routes.MapRoute("prefix/{controller}/{action}/{id},
new { controller = route.Controller , action = route.Action , id = route.Id });
}
How can I implement solution, where language switcher won't be consisted on session variables or xyz.com/{language} parameters in URL (I have that approach now).
Just en.XYZ.com/Account/Login or for example de.XYZ.com/SomeController/SomeAction (subdomain switch the language variable - that is more friendly for SEO)
How to implement it?
is there any reason why you are using the session variable? a more common solution is to include the language code in the route, i.e. blah.com/en/info or blah.com/jp/info (for english and japanese)
if you did this every page on the site could contain links to each language. if you are writing a publicly accessible site this would also make it easier for google to index all your content.
this article explains how to include the language in the domain, ie. en.blah.com or jp.blah.com: http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx
UPDATED: Here's a simple example of including the language code in the URL route.
Change the default route to include a language parameter:
routes.MapRoute(
"Default",
"{language}/{controller}/{action}/{id}",
new { language = "en", controller = "Home", action = "Index", id = "" }
);
Add links for each language to your masterpage:
<li>#Html.ActionLink(
"Spanish",
ViewContext.RouteData.Values["action"].ToString(),
new { language = "es" })</li>
These will render as links back to the page you are on - only with the language changed.
I've come across a bit of a weird problem I can't make sense of. One of my controllers has stopped working, but if I rename it then it works fine. I don't have any special routing wrapped around this controller, it just uses my default.
To give specifics, I have a controller called "Kangaroo". In the browser, if I go to {server}/Kangaroo, then I get the "The Resource cannot be found" message. However, if I go to {server}/Kangaroo/Index, then my page loads as normal. I don't have this problem on any of my other controllers, only this one. If I rename the controller (and my view folder) to "Kangaroo2", then it works perfectly fine.
Here is my route:
public class RouteDefinitions {
public static void AddRoutes(RouteCollection routes) {
routes.Ignore("{resource}.axd/{*pathInfo}");
routes.MapRoute("Resources",
"cache/{action}/{key}/{version}/{type}",
new { controller = "Cache",
action = "CacheContent",
key = "",
version = "",
type = "" });
routes.MapRoute("Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new {
controller = "Home",
action = "Index",
id = ""
} // Parameter defaults
);
}
}
Does anyone have an idea of what could be going on here? I thought it might just be a weird visual studio thing, but restarting did not correct the issue.
Just figured out what the problem was. There was a folder in my project called "/Kangaroo". I guess it was treating it like a script or other content. Since the path existed it was attempting to load something from the path.