0

I'd like to create a ViewHelper to localize my ASP.NET MVC application. Something like this:

public class Translator
{
    private readonly ITranslationRepository _repo;
    public Translator(ITranslationRepository repo)
    {
        _repo = repo;
    }

    public static string Translate(TranslationEnum translationEnum)
    {
        return _repo.GetTranslation(translationEnum, Session.LanguageId);
    }
}

Usage in a (Razor) View looks like this:

<p>@Translator.Translate(TranslationEnum.WelcomeMessage)</p>

Now the problem is of course, I cannot make the Translate method static, because I need to access the instance variable _repo.

How can I inject the repository into a ViewHelper so I can use it in a View like above?

Sandro
  • 2,998
  • 2
  • 25
  • 51

2 Answers2

3

The responsibility of the view is just to transform the data that comes back from the controller to a HTML structure. Views are hard (to impossible) to test automatically, so best is to keep them as dumb as possible.

Instead of using the Translator in your view, inject it into your controller and let the controller call the Translator. This solves a range of problems:

  • It keeps the view simple.
  • It improves maintainability.
  • It improves testability.
  • It improves the verifiability of your object graphs (because you don't fall back on static method calls or the Service Locator anti-pattern).

Long story short, add a property to the controller's view model and return that to the view. Example:

public class HomeController : Controller {
    private readonly ITranslator translator;
    public HomeController(ITranslator translator) {
        this.translator = translator
    }
    public ActionResult Index() {
        this.View(new HomeViewModel {
            WelcomeMessage = this.translator.Translate(TranslationEnum.WelcomeMessage)
        });
    }
}

And your view can look as follows:

@model HomeViewModel

<p>@Model.WelcomeMessage</p>
Steven
  • 166,672
  • 24
  • 332
  • 435
  • That's what I tried first. But I can't access the Controller property from the View, or did I miss something? – Sandro Mar 27 '15 at 12:24
  • @Sandro: The view should know nothing about the controller and vice verse. See my update. – Steven Mar 27 '15 at 12:39
  • I see, but that's not feasible for this project. In some views we even use the Entity as model. Is this really the only way? I mean, this way it requires a few steps more than with a ViewHelper, and with a lot translations... pheew – Sandro Mar 27 '15 at 12:47
  • Can you explain why this is not feasible? You can quite easily 'enrich' your Entity classes with extra data by wrapping it in a view model class. For instance: `new HomeViewModel { Message = "foo", Product = product }`. – Steven Mar 27 '15 at 12:49
  • Sorry, there was a late edit in my last comment: It's not feasible because it requires additional work. Such as extending the model for every single translation plus assigning it in the controller. Combine this with lots of translations... that's not fun anymore. – Sandro Mar 27 '15 at 12:52
0

first of all, the intention of your design is wrong because it violates the single responsibility principal. Why is a translator dependent on repository?

secondly, why do you need a translator, you can use asp.net globalization? click me We should not reinvent the wheel.

thirdly, all the html helpers are extension methods which have to be static.

so my suggestion is if you have to use translator, please refactor the Translator class, decouple the repository from it then create a extension methods from there.

or you can use globalization, it sounds horrible to start with but trust me it's not as hard as it looks.

public class Translator
{
    private static ITranslationRepository _repo;

    public static ITranslationRepository Repo
    {
         get { /*check null here before return*/ return _repo; } set { _repo = Repo; }
    }

    public Translator()
    {

    }

    public static string Translate(TranslationEnum translationEnum)
    {
        return _repo.GetTranslation(translationEnum, Session.LanguageId);
    }
}
Larry
  • 2,172
  • 2
  • 14
  • 20
  • How is that an SRP violation? The translator has exactly one responsibility: It takes a key and returns a value for that key according to the logic of the injected ITranslationRepository implementation. I don't care if the implementation ultimately uses ASP.NET Globalization, a database or even a webservice. – Sandro Mar 27 '15 at 10:14
  • if it takes a key and return a string, why do you pass in the repository? OK, you need the repository to retrieve the key, but it's not the responsibility of the translator. – Larry Mar 27 '15 at 10:30
  • Don't think of a database repository. `ITranslationRepository` is just an interface. I could have also named it `ITranslationService` or `ITranslationHelper`. I abstract away the retrieving logic from my helper, so that I can change it anytime. – Sandro Mar 27 '15 at 10:43
  • @Sandro hi mate, in this case I think you can use setter injection. what you do is you need make a minor change of the translator. I have edited my post. – Larry Mar 27 '15 at 10:51