ResourcesResourceManager and ResXResourceManager


The ResourcesResourceManager and the ResXResourceManager allow resources to be read from stand-alone .resources and .resx files, respectively. In this respect, they offer the same functionality as the ResourceManager.CreateFile BasedResourceManager method, with the exceptions that ResXResourceManager reads resx files and, more important, both resource managers read resources in their entirety into memory and, therefore, do not keep locks on the files. As such, these resource managers offer you another strategy for translating your application:

You can ship a version of your application that uses the ResourcesResource Manager or ResXResourceManager to the translator. The translator can update resources/resx files using whatever utilities you provide. The translator can immediately see the changes in the application without having to return these changes to the developers. When the translator is satisfied with the translations, the translator can ship the translated resources/resx files back to the developers, who can build a satellite assembly from the result. Everyone wins. The translator gets immediate feedback on their work, developers get to package their resources more neatly into satellite assemblies for the release version, and the users do not suffer the performance hit of a "slower" resource manager.

It would be entirely possible to extend the ResourcesResource Manager class to understand .txt resource files or, indeed, resource files of any format. I haven't done this here, for two reasons. First, the .NET Framework doesn't include resource readers and resource writers for .txt files (even ResGen has its own custom code for reading .txt resource files). Second, I don't see a great value in supporting .txt files when the XML .resx files offer a superior text format.


The ResourcesResourceManager includes the whole functionality for both resource managers. The ResXResourceManager simply inherits from Resource ResourceManager and sets an "extension" field. The ResourcesResourceManager class is almost identical to the DbResourceManager class, with the following differences:

  • The ResourcesResourceManager doesn't have a connectionString field or ConnectionString property.

  • The ResourcesResourceManager has a private string field called extension, which is initialized to "resources".

  • The ResourcesResourceManager.InternalGetResourceSet method creates a ResourcesResourceSet object instead of a DbResourceSet object, and passes a third parameter to the constructornamely, the extension field.

The ResourcesResourceSet class is equally as simple as the DbResourceSet class:

 public class ResourcesResourceSet: CustomResourceSet {     public ResourcesResourceSet(string baseNameField,         CultureInfo cultureInfo, string extension):         base(new ResourcesResourceReader(         baseNameField, cultureInfo, extension))     {     } } 


As before with the DbResourceSet class, we could implement the Getdefault Reader and GetdefaultWriter methods, but as it isn't necessary for our purposes, I leave this until later.

The ResourcesResourceReader class also follows the blueprint laid down by the DbResourceReader class. Here is the ResourcesResourceReader class (without the GetEnumerator method):

 public class ResourcesResourceReader: IResourceReader {     private string baseNameField;     private CultureInfo cultureInfo;     private string extension;     public ResourcesResourceReader(string baseNameField,         CultureInfo cultureInfo, string extension)     {         this.baseNameField = baseNameField;         this.cultureInfo = cultureInfo;         this.extension = extension;     }     protected virtual string GetResourceFileName()     {         if (cultureInfo.Equals(CultureInfo.InvariantCulture))             return baseNameField + "." + extension;         else             return baseNameField + "." +                 cultureInfo.Name + "." + extension;     }     protected virtual IResourceReader GetResourceReader(         string fileName)     {         if (extension == "resx")             return new ResXResourceReader(fileName);         else if (extension == "resources")             return new ResourceReader(GetResourceFileName());         else             throw new ArgumentException(String.Format(                 "Unknown resource extension ({0})", extension));     }     public void Close()     {     }     System.Collections.IEnumerator         System.Collections.IEnumerable.GetEnumerator()     {         return this.GetEnumerator();     }     public void Dispose()     {     } } 


The constructor assigns the base name, culture, and extension to their respective private fields. The GetresourceFileName method returns the filename for the resource. The filename is constructed from the base name, culture, and extension, so in our earlier example, the name would be "CustomResourceManagers Example.Greetings.resources" for the invariant culture when the extension is "resources" and "CustomResourceManagersExample.Greetings.en-GB.resx" for the "en-GB" culture when the extension is "resx".

Unlike the ResourceManager.CreateFileBasedResourceManager method, I have assumed that the resource files are in the executable's working directory. If this doesn't follow your implementation, you can modify the GetresourceFileName method to support your model.

The GetresourceReader method gets an IResourceReader for the given file based upon the extension. If you wanted to support extensions other than .resources and .resx, you would modify the GetresourceFileName and Get ResourceReader methods. The GetEnumerator method is:

 public System.Collections.IDictionaryEnumerator GetEnumerator() {     Hashtable hashTable = new Hashtable();     string fileName = GetResourceFileName();     if (File.Exists(fileName))     {         IResourceReader reader = GetResourceReader(fileName);         try         {             IDictionaryEnumerator enumerator =                 reader.GetEnumerator();             while (enumerator.MoveNext())             {                 hashTable.Add(enumerator.Key, enumerator.Value);             }         }         finally         {             reader.Close();         }     }     return hashTable.GetEnumerator(); } 


This simple method gets the resource filename and, if the resource file exists, gets an IResourceReader to read the resource file and enumerates through the whole resource, loading all the items into a Hashtable. Herein lies the difference between this implementation and the file-based ResourceManager implementation: The resource is read in its entirety and then closed, whereas the ResourceManager class reads the resource as necessary and leaves it open. In exchange for this improved functionality, you may take a performance hit, depending upon whether your resource managers get reused often or are created and disposed of frequently.

The ResXResourceManager class simply uses all of the functionality offered in the ResourcesResourceManager class and changes the extension to "resx":

 public class ResXResourceManager: ResourcesResourceManager {     protected override void Initialize(         string baseName, Assembly assembly)     {         Extension = "resx";         base.Initialize(baseName, assembly);     }     public ResXResourceManager(string baseName, Assembly assembly):         base(baseName, assembly)     {     }     public ResXResourceManager(string baseName): base(baseName)     {     }     public ResXResourceManager(Type resourceType): base(resourceType)     {     } } 





.NET Internationalization(c) The Developer's Guide to Building Global Windows and Web Applications
.NET Internationalization: The Developers Guide to Building Global Windows and Web Applications
ISBN: 0321341384
EAN: 2147483647
Year: 2006
Pages: 213

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net