Tigraine

Daniel Hoelbling-Inzko talks about programming

View localization with dynamics in MVC3

I ended up doing a good bit of Javascript and Ruby development lately and whenever I go back doing .NET afterwards I feel like things are getting in my way..

For instance the way localization is handled by .NET is a major pain point in all things I’ve been doing lately. WPF is probably the shining example of how not to do it (LocBaml? Serious?), but MVC3 also throws stones your way.

Problem is, that Views are not compiled – so you don’t get access to the static Resources.XXX properties from your Views. This is unfortunate and there are ways around that like using Helpers etc.

But I really grew fond of dynamic in C# for so I decided to simply use that to wrap the usage of my resources so I can write code like this in my Views:

@App.L10n.Product.Name

This is done by hanging my custom dynamic ResourceWrapper onto the dynamic ApplicationContext in Global.asax.

protected void Application_Start() { Application.Add("L10n", new MultiResourceWrapper()); … }

And my MultiResourceWrapper is simply wrapping all Resources existing in the MVC application on startup, and allows access to them through their name:

public class MultiResourceWrapper : DynamicObject { private readonly Dictionary<string, object> resources = new Dictionary<string, object>(); public MultiResourceWrapper() { var assemblyNamespace = this.GetType().Assembly.GetManifestResourceNames(); foreach (var s in assemblyNamespace) { var manager = new ResourceManager(ExtractResourceFullName(s), GetType().Assembly); var name = ExtractResourceName(s); resources.Add(name, new ResourceWrapper(manager)); } }

string ExtractResourceFullName(string s) { var regex = new Regex(@"(.*).resources$"); return regex.Match(s).Groups1.Value; }

string ExtractResourceName(string s) { var regex = new Regex(@"([a|A-z|Z]*).resources$"); return regex.Match(s).Groups1.Value; }

public override bool TryGetMember(GetMemberBinder binder, out object result) { result = resources[binder.Name]; return true; }

public class ResourceWrapper : DynamicObject { readonly ResourceManager manager; public ResourceWrapper(ResourceManager manager) { this.manager = manager; }

public override bool TryGetMember(GetMemberBinder binder, out object result) { result = manager.GetString(binder.Name); return true; } } }

As you can see all I need to do now to have localized strings for something is to add a Resource somewhere in my MVC application and then access it through it’s name from within the View.

One important thing though: I intentionally omitted any error handling code for the case where you mistype something. Nothing is more annoying than WPF bindings that simply don’t work because of typos, so having the app blow up with a YSOD is to me preferrable to a silent failure you have to spot later in deployment. But feel free to expand this to your liking.

The code is also available as a Gist from GitHub: MultiResourceWrapper.cs

Filed under net, programmierung

Redmine extensions

I may have just hit an all-time-low in lines-of-code per hour, but it was fun nonetheless, so I thought I’d share this with you:

A customer had the crazy requirement for his Redmine Ticketing system that users can enter their email address to a ticket and be notified alongside the user that is associated with the user account. (This also means that many users are sharing one user to create Tickets in the System)

The solution was quite simple in the end, but getting there for a total stranger to Rails was a bit challenging.

First I created a custom field in Redmine that is required on all tickets and contains the email address the email should be sent to.

Next I went into app/models/issue.rb and changed the recipients method like this:

# Returns the mail adresses of users that should be notified def recipients notified = project.notified_users # Author and assignee are always notified unless they have been locked notified << author if author && author.active? notified << assigned_to if assigned_to && assigned_to.active? notified.uniq! # Remove users that can not view the issue notified.reject! {|user| !visible?(user)} notified.collect(&:mail) << custom_field_values end

As you can see, all it took was to append the custom_field_values (assuming there is only one custom field!) to the list of emails that should be notified. Once that was done I just had to restart the ruby server and it worked.

If this looks hacky to you: It is. But the requirements didn’t really warrant any more work to be put into this.

Finally: Ruby is a fun language to work with, although it’s also rather hard to understand a complex project like Redmine if you are looking at it without any prior ruby knowledge.

What really got me in the beginning was that ruby has no return statement. Quite simply the last statement of a function is returned to it’s caller. If you don’t know that about the language, trying to understand what a function does get quite challenging :)

Filed under programmierung, rails, ruby

My Photography business

Projects

dynamic css for .NET

Archives

more