Click here to Skip to main content
Click here to Skip to main content

A CultureInfo switcher with Fluent syntax

By , 20 Jul 2011
 

Introduction

A few years ago, I came across with a treacherous bug in the COM interoperability for Microsoft Excel. To make it short, it happened that under globalization settings different than the US, some instructions were throwing the evil ComException named "Old format or invalid type library". Microsoft suggested as a workaround to set the CultureInfo to "en-US" immediately before the critical line(s) of code, taking care to restore the original CultureInfo immediately after. That made things go smoothly, but soon I realized that the dirty trick could be easily hidden in a small class that would have the task to change the CultureInfo in its constructor, having first buffered the original one in a member variable, for finally restoring it in the Dispose method.

And here is the whole code for the SwitchCultureInfo class:

using System;
using System.Globalization;
using System.Threading;

namespace Utilities
{
    internal class SwitchCultureInfo: IDisposable
    {
        private readonly CultureInfo _original;

        public SwitchCultureInfo(CultureInfo newCultureInfo)
        {
            _original = Thread.CurrentThread.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = newCultureInfo;
        }

        public SwitchCultureInfo(string cultureName) : 
            this(new CultureInfo(cultureName))
        {}

        public void Dispose()
        {
            Thread.CurrentThread.CurrentCulture = _original;
        }
    }
}

Background

A few days ago, while writing Unit Tests for a globalized application, I realized that the old neat small class could have been useful for testing purposes. This is the scenario: there are a few extension methods that act as shortcuts for spitting out the week day names in the user's language, such as the following one:

public static string DayName(this DayOfWeek dayOfWeek)
{
    return CultureInfo.CurrentCulture.DateTimeFormat.GetDayName(dayOfWeek);
}

Using the Code

While writing Unit Tests, it is fine to test such methods under different globalization settings, and here's how I ended writing them (using NUnit 2.5.10):

[Test]
public void DayName()
{
    using (new SwitchCultureInfo("it-IT"))
        Assert.AreEqual(DayOfWeek.Sunday.DayName(), "domenica");

    using (new SwitchCultureInfo("en-US"))
        Assert.AreEqual(DayOfWeek.Sunday.DayName(), "Sunday");

    using (new SwitchCultureInfo("pl-PL"))
        Assert.AreEqual(DayOfWeek.Tuesday.DayName(), "wtorek");
}

That initially looked fine, but I started thinking about the Fluent syntax that many frameworks are implementing (such as NUnit itself, but also NHibernate), and the using instruction started to show the signs of its age. Why not implement some Fluent extension methods that could make the Unit Test more readable and amusing? And here's how I rewrote the Test method, now much more user-friendly:

using System;
using NUnit.Framework;
using Utilities;
using Program = System.Globalization.CultureInfo;
using Tests;

namespace Tests.Utilities
{
    [TestFixture]
    public class DayOfWeekExtensionFixture
    {
        [Test]
        public void DayNameTest()
        {
            Now.WeAreTestingTheMethod(Utilities.DayOfWeekExtension.DayName);

            When.The(Program.CurrentCulture)
            .Is("Italian")
            .And().TheCountryIs("Italy",
                Assert.AreEqual, DayOfWeek.Sunday, "domenica");

            When.The(Program.CurrentCulture)
            .Is("Polish")
            .And().TheCountryIs("Poland",
                Assert.AreEqual, DayOfWeek.Tuesday, "wtorek");

            When.The(Program.CurrentCulture)
            .Is("English")
            .And().TheCountryIs("United States",
                Assert.AreEqual, DayOfWeek.Sunday, "Sunday");
        }
    }
}

Points of Interest

A reader has pointed out that it is "Nice, but not very practical". I always appreciate criticism, and I must admit that was really a good point. Moreover, in the same day I discovered that, one more time, I simply reinvented the wheel, since NUnit already has the SetCulture attribute for switching to another culture, and that is the most concise way for accomplishing that task:

[Test, SetCulture("en-US")]
public void DayNameEn()
{
    Assert.AreEqual("Sunday", DayOfWeek.Sunday.DayName());
}

[Test, SetCulture("it-IT")]
public void DayNameIt()
{
    Assert.AreEqual("domenica", DayOfWeek.Sunday.DayName());
}

[Test, SetCulture("pl-PL")]
public void DayNamePl()
{
    Assert.AreEqual("wtorek", DayOfWeek.Tuesday.DayName());
}

History

  • 2011 July, 13 - First release.
  • 2011 July, 15 - Improved the When and Now classes.
  • 2011 July, 20 - Added the NUnit example as a more concise way for checking the correct localization.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

spartacus23
Software Developer (Senior)
Italy Italy
Member
My name is Idalgo Cantelli. I'm a software developer skilled in .Net technologies. I work with .Net since February, 2002. I also have a strong experience as a technical trainer, having taught in more than thirty classroom courses. I'm MCTS and MCPD-EAD on .Net 2.0, planning an upgrade to 3.5.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberChristophe Kamieniarz21 Jul '11 - 4:04 
Hi,
 
I always have hated the expression "Do not reinvent the wheel". The wheels of our cars (and trucks, planes, bikes...) are different of the very first wheel (a massive circle of rock or wood ??). This single primitive artefact has been declined under a massive number of variations. We call that adaptating stuff to our needs.
 
Your article was a such adaptation but already existed in NUnit. So what? As a lot of developper, you do not take (do not have) the time to read the complete documentation of your tools (if the feature is documented at all). By submitting your article, you shared your thoughts and perhaps you gave some ideas to somebody having the same needs in other frameworks (as I do not know if I am able to write such tests in mbUnit, xUnit and friends).
 
Thanks for sharing it.
 
C.
GeneralMy vote of 2memberMakassi18 Jul '11 - 21:57 
Nice, but not very practical
GeneralRe: My vote of 2memberspartacus2319 Jul '11 - 2:29 
Hi Makassi, I must admit you're right, and to be honest, today I've noticed that NUnit has already a fine way for switching to another culture, that is the SetCulture attribute.
 
http://www.nunit.org/index.php?p=setCulture&r=2.5.10[^]
 
Ok, I'll study a little more next time, before posting articles.
Regards.
GeneralRe: My vote of 2membermonstercorp21 Jul '11 - 4:25 
Actually I think the article is quite valuable, at least provided a way of thinking.
 
I had an application which will need to provide some culture neutral outputs, when most of the UI part needs to be culture aware. I used to use a helper function with a delegate/lambda to switch to the EN culture.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 20 Jul 2011
Article Copyright 2011 by spartacus23
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid