3

I have an ASP.NET MVC 3 site using a custom route for specifying the language in the URL like so:

routes.MapRoute("Default", "{culture}/{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", culture = "en", id = UrlParameter.Optional });

My base controller then sets the current thread culture appropriately in its OnActionExecuting event like so:

protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var culture = filterContext.RouteData.Values["culture"] ?? "en";
    var cultureInfo = CultureInfo.GetCultureInfo((string)culture);
    Thread.CurrentThread.CurrentCulture = cultureInfo;
    Thread.CurrentThread.CurrentUICulture = cultureInfo;
    base.OnActionExecuting(filterContext);
}

Wherever I need a locale-specific string in my view, I reference the resource appropriately:

<img src="@Url.Content(Resources.Paths.ChallengeLogo)" alt="@Resources.Strings.AltChallengeLogo" class="logo" />

However, for images, the URL (src="...") will follow a convention "~/Content/Culture/Images/Whatever.jpg", and I'd rather not have to manually maintain a resource file for each image location. In short, what I'd prefer to do is:

<img src="@Url.Content("Images/ChallengeLogo.png")" alt="@Resources.Strings.AltChallengeLogo" class="logo" />

In this scenario, the Url.Content method would apply the path convention and translate the src attribute value to, for example:

/Content/en-US/Images/ChallengeLogo.png
/Content/en-GB/Images/ChallengeLogo.png
/Content/de/Images/ChallengeLogo.png
etc...

I don't have to use Url.Content specifically. I'm more than willing to create an extension method for this purpose, but I'm not sure if I'm even taking the best approach. What is the standard approach here?

Edit: I ended up creating an helper. The helper has the added benefit of checking that the path to the current culture actually exists, or falling back to the parent / default culture if necessary. Code below:

private static CultureInfo _defaultCulture = CultureInfo.GetCultureInfo("en");

public static string Localized(this UrlHelper url, string pathTemplate)
{
    var server = url.RequestContext.HttpContext.Server;
    var culture = Thread.CurrentThread.CurrentUICulture;
    var localizedPath = string.Format(pathTemplate, culture.Name);
    var physicalPath = server.MapPath(localizedPath);

    while (!File.Exists(physicalPath) && culture.Parent != null)
    {
        culture = culture.Parent;
        localizedPath = string.Format(pathTemplate, culture.Name);
        physicalPath = server.MapPath(localizedPath);
    }

    if (!File.Exists(physicalPath))
        localizedPath = string.Format(pathTemplate, _defaultCulture.Name);

    return url.Content(localizedPath);
}

In my views, I can use this helper as follows:

<div class="toolbox">

    <a href="#" class="button register">
        <img src="@Url.Localized("~/Content/Localized/{0}/Images/Button_Register.png")" 
             alt="@Resources.Strings.AltRegisterButton" /></a>

    <a href="#" class="button login">
        <img src="@Url.Localized("~/Content/Localized/{0}/Images/Button_Login.png")" 
             alt="@Resources.Strings.AltLoginButton" /></a>

</div>

2 Answers 2

3

I think your suggestion is the first one that came to my mind too. You can easily solve this with creating your own html helper that constructs the localized url. See http://develoq.net/2011/how-to-create-custom-html-helpers-for-asp-net-mvc-3-and-razor-view-engine/ as an example for creating a custom html helper.

Sign up to request clarification or add additional context in comments.

3 Comments

I ended up doing a helper as you suggested, but I'm going to hold off on crediting you with the answer because I'd like to see if anyone else has any interesting alternatives.
If all your localized images are always gonna be in the same folder structure, you could leave out the first part of the url like this @Url.Localized("Images/Button_Register.png")" and add it in the helper class. This will make it shorter and easier to read.
I actually did end up doing just that. Thanks!
0

Well, I haven't actually done it but I came across this post when looking for someone who actually did.

I was thinking that you could extend the StaticFileHandler to first look for some "localized" version of a static file before serving the actual file.

Something like, when serving http://yoursite.com/Content/images/logo.jpg

  1. Mangle the requested URL and see if /Content/images/es-ES/logo.jpg exists?
  2. If exists, then serve the file
  3. If not exists, then either roll back to the default culture or try to find the neutral culture before serving the default.

** this may all be bound to the fact that /Content/images/logo.jpg MUST exist, as that is the way the StaticFileHandler would be triggered in the first place.

If I end up doing that, I'll write up a blog post and share a link here.

UPDATE: Did implement it as I mentioned, though it ended up being a custom HttpHandler instead of an extension of the StaticFileHandler: http://www.prosoftnearshore.com/2011/10/localizing-files-jpg-css-js-etc-in-asp-net/

3 Comments

Interesting approach. In doing so, you eliminate the need to determine a fallback culture - the original location is the default - and you don't have to make the view culture-aware. I like it!
The link is broken and I can't seem to find it by doing a search. Any chance your post is still online?
@NightOwl888 Just updated the post with the URL. Sorry for the delay.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.