A few years back I wrote an article on creating a custom ASP.NET Localization Resource Provider that described how to create a custom ASP.NET ResourceProvider and ResourceManger that get resources from a database. It’s an older article but it’s been one of the more popular ones on this site, and the resource provider described has been picked up (based on feedback received) by quite a good number of developers.
In the last month I put a bit of effort into cleaning up the original provider and the result is a more formal Westwind.Globalization library that I hope more people will check out and take advantage of.
In the process I added better documentation, set up a new home page, created a GitHub project for the source code (moved from the old Westwind Web Toolkit project) and created a NuGet package that makes it easy to integrate the provider into your own applications.
I also created a new Getting Started video:
that takes you through installing and setting up the provider, adding some resources using the resource editor, and then demonstrate a variety of ways that you can use the localized resources. It’s a combo Getting Started and basic features overview – check it out if you have a few minutes to spare.
Why now?
The provider’s been around for a number of years, and it’s been updated and bug fixes have been made based on user submissions and bug reports. But the project has been off the beaten track. There was the article with the download, and the library was embedded as part of the Westwind West Wind Toolkit that’s now been broken up into its smaller component parts as separate projects. Westwind.Globalization was one of the last small projects left mainly because some work was required to clean up some of the samples and go the final mile to create a redistributable version of the online Web Resource Editor.
Additionally, in the last year or so I’ve been involved in several apps that have made fairly heavy use of this provider and as part of those project I was able to add a number of enhancements that came up in the process. I found some time to smooth out some of the rough edges in the provider and front end integration pieces.
Among the enhancements:
- Better support for resources in MVC/WebAPI – or any .NET app really (console, service, desktop)
(you can use DbRest.T() anywhere, or generate strongly typed resources that work either with ResX or Db resources) - Improved standard ResourceManger implementation for any .NET application
- New DbRes helper class that allows direct access to resources bypassing ResourceProviders
(ie. no resource provider config required in ASP.NET) - Improved strongly typed resource generation
- Much improved Db Resource –> Resx export capabilities
- Updated JavaScript resource generation handler
- Update online Web Resource Editor
- NuGet Package
Database Resources?
A lot of times when I mention Database resources for localization I get: “Isn’t that really slow?” The answer to that is – nope, not really. Yes, resources *are* loaded from a database, but they are loaded only once per ResourceSet per locale. This means once resources are loaded and in memory, they stay there – the process is identical to using Resx resources except that the initial read of a resource set is done from a database rather than from assembly ResX resources. So while there’s a small db hit for first time resource load on each ResourceSet, over the lifetime of an ASP.NET/.NET application there’s not a significant performance difference.
Additionally this library has the ability to import and export resources from Resx resources, so you can pull in existing resources, edit them in the database while your application is in development, then when complete export the resources back out to Resx and use them that way. You can even generate strongly typed resources that work with either database or Resx resources simply by switching the ASP.NET ResourceProvider. However, I’ve found although a number of clients had the intention of doing this, they actually preferred running with the Db resources because it allowed them to change the resources on the fly in the application as needed, without having to recompile the app.
Database resources are incredibly useful in localized applications as it’s much easier to modify resources interactively. This resource provider includes an interactive ASP.NET Web based resource editor that allows editing and updating of resources in a running application. The ability to manage resources interactively and update them however, adds a ton of flexibility which is a big sticking point in many a localized application I’ve worked on in the past.
Additionally if you use this provider, it has the optional capability to automatically add new resource keys to the database as keys are accessed. So if you use a resource that doesn’t exist, the value of the key is used, but also the key can automatically be added to the database. You can then lookup all missing ‘values’ and localize those.
DbResource Provider Support
The DbResourceProvider works with standard ASP.NET localization schemes so everything that works with ResX resources also works with the database provider. The DbResourceManager also works in any .NET application including services, console apps and even desktop applications.
All of the following are supported in ASP.NET:
- DbRes Helper (part of Westwind.Globalization)
Say Hello: @DbRes.T("HelloWorld", "Resources")
- HttpContext.GetGlobalResourceObject()
Say Hello: @HttpContext.GetGlobalResourceObject("Resources", "HelloWorld")
- Page.GetLocalResourceObject() (WebForms only)
Say Hello: <%= GetLocalResourceObject("lblHelloWorldLabel.Text") %>
- WebForms Control meta:resource tags (WebForms only)
<asp:Label ID="lblHelloLabel" runat="server" meta:resourcekey="lblHelloWorldLabel"></asp:Label>
- WebForms Control Resource expressions (WebForms only)
<asp:Label ID="Label1" runat="server" Text="<%$ Resources:Resources,HelloWorld %>"></asp:Label>
- You can also generate strongly typed resources (once generated)
Say Hello: @Resources.Helloworld
Alternate Localization Scheme
ASP.NET localization is based on ResourceID localization. The idea is that you have a resource ID value and that value is then matched in a ResX file for each of the locales you want to localize for. Typically using this approach you create keys that are more like Ids, rather than values, which in a lot of ways can be limiting.
More and more customers I’ve worked with though prefer using direct translation – ie. rather than embedding resource Ids, they want to embed actual resource string values in the default language and then translate those values later.
For example imagine I have a resource embedded like this in an MVC application:
<legend>@DbRes.T("Your Details","Account")</legend>
<label>@DbRes.T("First Name")</label>
@Html.TextBoxFor(mdl => mdl.User.FirstName,
new { @class = "input-block-level", placeholder = DbRes.T("First Name") })
<label>@DbRes.T("Last Name","Account")</label>
@Html.TextBoxFor(mdl => mdl.User.LastName,
new { @class = "input-block-level", placeholder = DbRes.T("Last Name") })
For example Last Name is a Resource Id here, which is the default text used. Even if there’s no resource named "Last Name" in the resource set, Last Name will be returned. Later when you translate to other languages those values can then be added and the translated values will be returned instead.
DbRes.T(resourceid,resourceSet,localeId) for translate is a small helper method directly accesses the Db Resource manager to retrieve values and it’s an easy way to use the
Unlike the standard ASP.NET ResourceProvider and ResourceManager, DbRes.T() and also the ASP.NET DbResourceProvider and DbResourceManager all automatically return the resource key if the resource Id was not found, so that when there’s no match at the very least you get the resource key value, rather than an empty string which sucks! If you’ve ever loaded an application with missing ResX resources you know what I’m talking about – it’s no fun to have empty labels and buttons and other UI controls without any content :-) This ResourceProvider will always return something…
Using actual translated default values for ResourceIds makes it easy to get default content into a page.
Further, If I add
addMissingResources="true"
to the provider configuration resources are automatically added if they are not found. This adds resources in the Resource Editor for easy later translation simply by running the app rather than having to manually add the resources.
This works surprisingly well as resources are added to the application as you run it and you access the actual values on pages.
There is a caveat here however, if you change a resource string in your markup you potentially break the link to your resources in the database. So you have to be aware to either leave the original resource key intact and add an actual default text value, or you can rename the resource name in the Web editor which is actually very easy (advantage database :-)).
Although I was resistant to work with this approach initially I’ve come to appreciate how easy it is to work with this setup. You get default resources always, and with keys added automatically you can easily localize and add new language later. The database manager also includes a method to cleanup resources that have entries with null values and no localization, so when you’re done localizing you can clear out any clutter in the db of resource values that don’t have any actual translated data.
Anyway – it’s something to consider when setting up a new localization project.
Check it out
If you are doing localization in your application and you’re looking to do it a little more dynamically then what you can do with Resx, check out the database resource provider described here. It’s easy to get started and it provides a ton of flexibility to get resources added easily interactively or under program control.
Resources
Other Posts you might also like