I had to implement a custom resource provider so that the resources were stored in a database instead of satellite assemblies. The following links helped me:
a) Bonus Chapter 1(Resources and Localization) of Pro ASP.NET 3.5 in C# 2008, which is available for free.
b) Extending the ASP.NET 2.0 Resource Provider Model by Michèle Leroux Bustamante.
c) Creating a Data Driven ASP.NET Localization Resource Provider and Editor by Rick Strahl.
d) Custom localization resource provider using an Access database by Eilon Lipton.
The articles provide all the details and a better implementation than the one I resorted to. So I am not going to give details about my implementation (I don’t have the energy to write a 20 page essay!). A few notes, however:
1) Rick Strahl’s blog is a mine of information regarding the development of the resource provider. So try searching it for further details.
2) I wanted a common localization store for all the applications in our organization. So I went with a global resource provider with [Group], [Property] explicit global resource pattern for all my resources. [Group] could be any entity like ‘User’ or ‘Product’, while [Property] could be a property of the entity with some other attribute joined to it in an object-walker syntax like ‘Name-DisplayName’, ‘Name-Description’, ‘Name-Help’, etc. This helped me to use the resource provider to localize the tooltips, the help text, and provide the validation required for the text boxes etc.
3) ASP.NET uses a runtime Resource Provider Factory class to retrieve the resource provider for a particular virtual path during runtime. This resource provider (for any specific virtual path/page) is cached for the application. There is also a design-time Resource Provider Factory class that can be used to generate local resources for the page at design-time. Read Bonus Chapter 1 above for details.
4) At compile-time, the compiler creates the runtime resource provider and tries to retrieve each resource specified in the page with an invariant culture. If the resource provider returns a value for the invariant culture, then things go well. Else, two things could happen. If the local resource was specified in an implicit manner in the page and the provider does not return a value, the code required to retrieve the resource at runtime is not generated by the compiler. So you won’t get anything at runtime. If the resource was specified in an explicit manner and the compiler did not get a resource value for the invariant culture, it would generate a compile-time error. Very annoying behavior that was extremely hard to debug because it was happening at compile-time. See this post by Rick Strahl; pay attention to the comments by Eilon Lipton and David Ebbo.
5) We did not want our pages to hit the database during compilation as this would increase compilation time for the application. Therefore, we decided to return an empty string for the invariant culture in our runtime Resource Provider implementation because we were supporting only two cultures, English and Arabic. If you want to support the invariant culture, then you can skip the database by checking for an HttpContext and returning empty string if it does not exist. This way, at runtime you can return the appropriate resource value. I initially thought that the Design-time classes would be the one that needs to be changed to return the empty string, but apparently the compiler uses the runtime classes to check if a resource is present and not the design-time classes. Go figure!
6) Since the compile-time error in (4) above was happening at, well, compile-time, and not at runtime, I could not debug the issue at runtime during which it was working fine. I was stuck for a while trying to figure out a way to debug at compile-time. Googling told me to set the Debug properties of the project to start another instance of Visual Studio for debugging. Debug the project, and a new instance of Visual Studio will start up. Load the same project in the new instance and compile. I was able to step through the code and figure out that the runtime classes were being created and not design-time and that the culture that was being passed to check the availability of the resource by the compiler was invariant culture. One of those days when 6GB of RAM and a Core i7 comes handy, thank God!
7) I mentioned above that the Resource Provider object is created for each virtual path (web page) and then cached for reuse for local resources. For global resources, the Resource Provider is cached for the same Application Key if you are using the [ApplicationKey], [Resource Name] pattern to specify the resource.
If my notes do not make any sense, that’s fine! Just use the links I have provided and they should do for most cases. Happy localizing!