Using Resx Files with a ResourceManager

The framework provides the ability to create a ResourceManager that reads from files via CreateFileBasedResourceManager, but it looks for files with a .resources extension.  Sure I could rename all my .resx files to .resources, but what kind of hacker would I be then? 

Why use resx files you ask?  Mostly so that the end user can easily customize strings that appear in the application.  All you need is notepad, an XML editor, or better yet, Lutz Roeder's Resourcer.  The code below will look in a folder called 'Strings' (relative to your executable) for your resx files that use the naming convention 'Strings.<culture>.resx' e.g. Strings.en-US.resx, Strings.fr-FR.resx, etc.

ResxResourceManager resourceManager = new ResxResourceManager("Strings", "Strings");
string test = resourceManager.GetString("Test");

The code for the ResxResourceManager is painfully simple, and would be even simpler if Microsoft would have made the private constructor on ResourceManager protected, but whatcha gonna do?

public class ResxResourceManager : ResourceManager
{
  public ResxResourceManager(string baseName, string resourceDir)
  {
    BaseNameField = baseName;
    ResourceSets = new Hashtable();

    Type baseType = GetType().BaseType;
    BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField;

    baseType.InvokeMember("moduleDir", flags, null, this, new object[] {resourceDir});
    baseType.InvokeMember("_userResourceSet", flags, null, this, new object[] {typeof(ResXResourceSet)});
    baseType.InvokeMember("UseManifest", flags, null, this, new object[] {false});
  }

  protected override string GetResourceFileName(CultureInfo culture)
  {
    string resourceFileName = base.GetResourceFileName(culture);
    return resourceFileName.Replace(".resources", ".resx");
  }
}

posted @ Wednesday, August 01, 2007 3:45 PM


Print

Comments on this entry:

# re: Using Resx Files with a ResourceManager

Left by Jeff Brown at 8/1/2007 10:29 PM
Gravatar

Gotta love Microsoft APIs... *sigh*

# re: Using Resx Files with a ResourceManager

Left by Jørn Cornelius Olsen at 1/23/2008 1:02 AM
Gravatar

I have run into this kind of issue several times (too often). Sometimes they make the members private or internal for no discernible reason. The we either have to dublicate lots of code using Reflector or use reflection, hoping that we run in a full trust context.
Anyway for this particular problem you could alternatively have made the private constructor protected in you subclass and just call the private base constructor using reflection.
Like this:
public class ResxResourceManager:ResourceManager {
protected ResxResourceManager(string baseName, string resourceDir, Type usingResourceSet) {
Type baseType=GetType().BaseType;
BindingFlags flags=BindingFlags.NonPublic|BindingFlags.Instance;
ConstructorInfo ctor=baseType.GetConstructor(flags, null,
new Type[] { typeof(string), typeof(string), typeof(Type) }, null);
ctor.Invoke(this, flags, null, new object[] { baseName, resourceDir, usingResourceSet }, CultureInfo.InvariantCulture);
}

public ResxResourceManager(string baseName, string resourceDir)
:this(baseName, resourceDir, typeof(ResXResourceSet)) {
}
}

# re: Using Resx Files with a ResourceManager

Left by Anders Øyvind Sætre at 8/8/2008 7:34 AM

Hi. I tried using your ResxResourceManager, but didn't manage to make it work. MissingManifestResourceException etc.

But I used the lines:
Type resource = typeof(Resources.Site);
ResourceManager rm = new ResourceManager(resource);
rm.GetString(preFix + customString);

Site is a a resx-file in AppGlobalResources-folder. That worked, which was just the thing I needed. Thought I should tell, in case I need it later and forgot. :P

Thanks.

# re: Using Resx Files with a ResourceManager

Left by Anders Øyvind Sætre at 8/8/2008 7:34 AM

Hi. I tried using your ResxResourceManager, but didn't manage to make it work. MissingManifestResourceException etc.

But I used the lines:
Type resource = typeof(Resources.Site);
ResourceManager rm = new ResourceManager(resource);
rm.GetString(preFix + customString);

Site is a a resx-file in AppGlobalResources-folder. That worked, which was just the thing I needed. Thought I should tell, in case I need it later and forgot. :P

Thanks.

# Sorry

Left by Anders Øyvind Sætre at 8/8/2008 7:42 AM

Sorry about the duplicate, I use Opera, and spent some minutes on writing the comment, hopefully you can easily remove one of them.

Bet this one will be a duplicate too, then I just say "Oops!".

# re: Using Resx Files with a ResourceManager

Left by Pascal Lindelauf at 10/15/2008 9:19 AM
Gravatar

I'm getting the same MissingManifestResourceException as Anders and it turns out that things go wrong when the resource files are stored in a path of which the name contains spaces. I haven't found a solution to this yet. Maybe one of you have?

If I find one, I'll post it.

Cheers,

Pascal.

# re: Using Resx Files with a ResourceManager

Left by Matt C at 10/29/2008 8:05 AM
Gravatar

Thanks so much - this was just what I needed for a tricky localization problem I ran into. I am using VB.net though, so here is a (working) VB.net version of your same class (for any other VB.netters out there!)

Imports System.Resources

Imports System.Reflection

Public Class ResxResourceManager
Inherits ResourceManager

Public Sub New(ByVal baseName As String, ByVal resourceDir As String)

BaseNameField = baseName
ResourceSets = New Hashtable()

Dim baseType As Type = Me.GetType.BaseType
Dim flags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.SetField

baseType.InvokeMember("moduleDir", flags, Nothing, Me, New Object() {resourceDir})
baseType.InvokeMember("_userResourceSet", flags, Nothing, Me, New Object() {GetType(ResXResourceSet)})
baseType.InvokeMember("UseManifest", flags, Nothing, Me, New Object() {False})
End Sub

Protected Overrides Function GetResourceFileName(ByVal culture As Globalization.CultureInfo) As String

Dim resourceFileName As String = MyBase.GetResourceFileName(culture).ToLower

Return resourceFileName.Replace(".resources", ".resx")

End Function

End Class

# re: Using Resx Files with a ResourceManager

Left by erwin at 12/11/2008 5:33 AM

What is ResXResourceSet?
Is this a class that I should create myself?
What are the contents?

# re: Using Resx Files with a ResourceManager

Left by Stefan Kip at 12/12/2008 8:16 AM
Gravatar

@erwin:

If you look at the documentation on MSDN:
http://msdn.microsoft.com/en-us/library/system.resources.resxresourceset.aspx

You'll see that you need a reference to System.Windows.Forms

# re: Using Resx Files with a ResourceManager

Left by Michael van Rooijen at 4/10/2009 8:05 AM

Thanks man, the code worked like a charm for me. Used it from a Console App.

Your comment:



 (will not be displayed)


 
 
 
Please add 8 and 4 and type the answer here:
 

Live Comment Preview:

 
«July»
SunMonTueWedThuFriSat
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678