Background
I maintain a white label chat site written in .NET MVC 4 for our company, and today I was refactoring a standalone DLL that contains tons of the RESX files which enable us to decline the site into many different languages.
The twist is that there are many places in the translations where I want to be able to replace entries with variables instead of the usual string translation facilities that RESX files provide.
Welcome to [[Brand]]
Visit [[Domain]]
{0} is not a valid value for {1}
To do this, I use T4Resx, which enables me to transform all RESX files into a huge .cs file (then a DLL), and can invoke the translations in my .cshtml files with:
@Resources.User.Age
bind them to a ViewModel
with:
[Display(Name = "Age", ResourceType = typeof(Resources.User.Age))]
or print out formatted messages:
@Resources.User.AgeInvalid(33, "ageTextBox")
public string GetAgeError(int age, string element) {
return Resources.User.AgeInvalid(age, element);
}
Problem
The replacement function I was using resides in another DLL. At the moment, I simply added a reference of that DLL to my RESX project and all was fine.
But while I was refactoring, I wanted to add a reference of my RESX project to the DLL that contains the replacement function, because I wanted to start using certain translations on ViewModel
s and the like.
However, when I compiled the project, I got a nice error about circular references, arg !
...of course, I should have seen that coming.
Solution
At first I started thinking, oh God...now I'm going to have to add some IOC magic, which implies adding more dependencies to my projects. Nothing against IOC libraries in general, just the fact that I simply hate dependencies.
So I started running around looking at the usual IOC libraries (Ninject, Unity, Windsor, StructureMap...) out there, since up to now, I never really had the need to resort to any type of IOC in my projects.
While looking at those libraries, it still bothered me to think of having to add extra dependencies because of a stupid simple replacement function, when it hit me that we have Func<> in .NET !
Func<>
is nothing more than a cute shortcut to declaring delegates. The nice thing is they are shorter to write, and you can declare them inline, instead of doing this:
public delegate string ReplacementDelegate(string key);
public static ReplacementDelegate GetReplacement = key => key;
They allow you to declare a function with a signature, and then later attach the actual function to it. So now, we have a sort of dependency injection... at least at the function level.
And after a bit of testing, I came up with the following scenario:
In my RESX project, I declare:
public static Func<string, string> GetReplacement = key => key;
And still in the RESX project, I now call my replacement function with:
Utilities.GetReplacement("somevalue");
Now, as long I don't set up the bindings, GetReplacement()
will simply return the value that was passed to it. However, I can now remove the dependency of the project that was providing me with the original replacement function from my RESX project. And can also add my RESX project to the project I originally wanted to add it to, without getting circular reference errors.
The replacements actually take place on the website, so that's where I will want to setup the bindings for my replacement function. And it couldn't be easier:
In my global.asax, inside Application_Start()
, I simply declare the following:
Resources.Utilities.GetReplacement = c => DBController.Current.GetReplacement(c);
The replacement function inside the RESX project is now bound to another replacement function that can access a database and return all my replacements.
No IOC library, no dependencies... and I'm really really happy now.