4

I have created a new C# class library project which will contain the resource files for all translations. The examples I have seen have a flat structure with one giant resource file for each language like this:

  • Messages.resx
  • Messages.fr-FR.resx

But for this particular project it is preferred to split the resource files into logical groupings:

  • SystemMessages.resx
  • UserMessages.resx
  • SystemMessages.fr-FR.resx
  • UserMessages.fr-FR.resx

This will get messy when we add more resource files and more translations, so it would be more manageable to have sub-folders for each language e.g.

  • en-GB\SystemMessages.resx
  • en-GB\UserMessages.resx
  • fr-FR\SystemMessages.fr-FR.resx
  • fr-FR\UserMessages.fr-FR.resx

But doing this will add the language code to the namespace e.g. Translations.en-GB.SystemMessages.MyString and I assume because en-GB is now in the namespace that the resource manager will no longer be able to find the fr-FR translation.

How can I put the resources into sub-folders based on the language code?

row1
  • 5,568
  • 3
  • 46
  • 72

1 Answers1

1

Let's assume your project looks like this:

enter image description here

You could use different ResourceManager depending on the Thread Culture.
With a small T4 template you can make it strongly typed(iterate the resource file and create a property for each string)

using ConsoleApplication6.Translations.French;
using System;
using System.Resources;

namespace ConsoleApplication6
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(SystemMessagesManager.GetString("Title"));
            Console.ReadLine();
        }

       public static class SystemMessagesManager
       {
           static ResourceManager rsManager;
           static SystemMessagesManager()
           {
               //Get the current manager based on the current Culture
              if (Thread.CurrentThread.CurrentCulture.Name == "fr-FR")
                   rsManager = SystemMessagesFrench.ResourceManager;
              else if (Thread.CurrentThread.CurrentCulture.Name == "el-GR")
                   rsManager = SystemMessagesGreek.ResourceManager;
              else
                   rsManager = SystemMessagesEnglish.ResourceManager;
           }
           public static string GetString(string Key)
           {
              return rsManager.GetString(Key) ?? SystemMessagesEnglish.ResourceManager.GetString(Key);
           }
       }
   }
}

About the T4 template,check this :
How to use a resx resource file in a T4 template
Below you can see a sample code.
Let's say you have this in your project: enter image description here

Using the following template you can auto generate the class:

<#@ template debug="false" hostspecific="true" language="C#" #>

<#@ assembly name="System" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Linq" #>

<#@ output extension=".cs" #>
using System.Resources;
using System.Threading;

namespace YourNameSpace
{
    public static class SystemMessagesManager
    {       
        static ResourceManager rsManager;
        static SystemMessagesManager()
        {
               //Get the current manager based on the current Culture
              if (Thread.CurrentThread.CurrentCulture.Name == "fr-FR")
                    rsManager = SystemMessagesFrench.ResourceManager;
              else if (Thread.CurrentThread.CurrentCulture.Name == "el-GR")
                    rsManager = SystemMessagesGreek.ResourceManager;
              else
                    rsManager = SystemMessagesEnglish.ResourceManager;
         }
        private static string GetString(string Key)
        {
              return rsManager.GetString(Key) ?? SystemMessagesEnglish.ResourceManager.GetString(Key);
        }

        <# 
         XmlDocument xml = new XmlDocument();        
         xml.Load(Host.ResolvePath(@"ResourceStrings.resx"));

         foreach(string key in xml.SelectNodes("root/data").Cast<XmlNode>().Select(xn => xn.Attributes["name"].Value)){
         #>
public static string <#= key #> { get { return GetString("<#=key#>"); } }
        <# } #>                 
    }
}
Community
  • 1
  • 1
George Vovos
  • 7,563
  • 2
  • 22
  • 45
  • Looks promising. I am not very familiar with T4, but are you saying that it would be used to update the `SystemMessagesManager` wrapper class? Would this be a manual or automatic process to update the class as new strings are added to the resource file? – row1 Feb 26 '16 at 01:10
  • 1
    @row1 Updated the answer with a t4 template.One small problem is that you have to open/save your template if you add a key in the resource file.There are plugins that auto-run T4 templates on build but I don' mind this – George Vovos Feb 26 '16 at 12:14