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.
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 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) { } } |