Tag Archives: Localisation

Resource File Testing

Admittedly this does sound like a strange one. How or why would you both to test a resource file? The only reason I could think of was this: what if someone adds a resource, and forgets to localise it?

Without a test such as this, the first way you’d know this is when you ran the program in a localised culture and spotted the default culture appearing. This is something that could potentially go unnoticed for a long time. Consider British and American English, or Spanish and Latin-American Spanish.

To set-up the test, create three resource files:

res1

Create two resources in the base resource file (.resx):

res2

And then in each localised file, create one of these; so, in Resource.en-GB.resx create testphraseone (test phrase one english), and in the Spanish: testphrasetwo (prueba frase dos).

We also need to expose the resource manager:

    public class GetRes
    {
        public static ResourceManager GetResMgr()
        {            
            ResourceManager resMgr = new ResourceManager("resourcetest.Properties.Resource", typeof(resourcetest.Properties.Resource).Assembly);
            return resMgr;
        }

The Test

Now that we have the ResourceManager, we can simply call the GetResourceSet function. The third parameter determines whether to try the parent if the resource doesn’t exist, so setting this to false will force it to get the resource from the file in question.

The test could look like this:

        [TestMethod]
        public void TestMethod1()
        {
            ResourceManager resMgr = resourcetest.GetRes.GetResMgr();
            
            var baseList = resMgr.GetResourceSet(CultureInfo.InvariantCulture, true, true).Cast<DictionaryEntry>();            
            var spanishList = resMgr.GetResourceSet(new CultureInfo("es-ES"), true, false).Cast<DictionaryEntry>();
            var britishList = resMgr.GetResourceSet(new CultureInfo("en-GB"), true, false).Cast<DictionaryEntry>();
            
            var missing = baseList.Where(m => !spanishList.Any(s => string.Compare(s.Key.ToString(), m.Key.ToString()) == 0));
            Assert.AreEqual(0, missing.Count());

            missing = baseList.Where(m => !britishList.Any(s => string.Compare(s.Key.ToString(), m.Key.ToString()) == 0));
            Assert.AreEqual(0, missing.Count());
        }

This should then fail every time it encounters a resource in the base list that is not localised. If you needed to see the reverse, you could simply reverse the statement.

Conclusion

If you work in multiple languages, this can prove to be a very useful test. It also means that you can safely decide to delay the translation without being concerned that you’ll miss anything.

Creating a centralised resource project

Once you’ve created your project, should you wish you distribute your software to multiple countries, then the question of localisation must, inevitably, arise. If you’re using .NET then a very tempting option is to use resource files. Most of the plumbing is already done for you, you simply need to create a resource file, and then localise it by copying the file and changing the extension to the correct culture.

Example

Say, for example, you had written your software in British English, and you wanted to localise to American English. Here’s you’re English version:

Image

For an American version, copy the resource file:

Image

Call the new file the same as the old one, but with the correct culture; for example:

Resource1.en-US.resx

Image

And here’s some code to use the resource string in question:

         static void Main( string[] args)
        {
            Console.WriteLine(Properties. Resource1.LocalString);
        }

Okay, so here’s the brilliant thing about resources; that’s it. When I change my culture it uses the culture specific resource file:

         static void Main( string[] args)
        {
            Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US" );

            Console.WriteLine(Properties. Resource1.LocalString);
        }

So – what’s new?

It’s pretty likely that you’ve already seen that part. You may also have seen the next part too – but my guess is that if you’ve read this long, you haven’t.

Centralise resources

In the above project, having a resource file and just accessing it seems to make a lot of sense, but what if there’s more than one project; what if you have a solution with a few projects?

Image

You could have a resource file for each project, and that works. But, what if you decide to translate to French, but no-one on your team speaks fluent (and it does have to be fluent) French? There are LOTS of companies that will accept a resource file and translate – they’ll also accept several, and translate them all, but why make it so that you need several?

Centralised resources in action

Create a new class library in the following vein:

namespace Localise
{
    public class Localise
    {
        private static ResourceManager _resourceMan;

        private static ResourceManager ResourceMan
        {
            get
            {
                if (_resourceMan == null)
                {
                    ResourceManager tmpRes = new ResourceManager("Localise.Localise.Properties.Resources" , typeof(Resources ).Assembly);
                    _resourceMan = tmpRes;
                }
                return _resourceMan;
            }
        }

        public static string GetResString( string key)
        {
            string valString = ResourceMan.GetString(key);
            return valString;
        }
    }
}

If you’ve ever had a look at the resources.designer.cs then this code should look very familiar. That of course is internal, so doesn’t work for this.

All your resources now go in here, and you simply reference it from your project like so:

Console.WriteLine(Localise.Localise.GetResString("LocalString"));

Conclusion

There are other ways to handle resource strings – you can hold them in the database, in text files, you could build a whole infrastructure of resource assets. If you have a massive solution distributed to hundreds of countries then that’s probably worth it. If not, use what Microsoft give you for free.