New Community Website

Ordinarily, you'd be at the right spot, but we've recently launched a brand new community website... For the community, by the community.

Yay... Take Me to the Community!

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.

New! List Localization in DNN 7.3.3

With version 7.3.3 of the DNN Platform a new “localization feature” has been implemented: list localization. Lists are a core platform feature whereby users can maintain lists that other bits of software can maintain. There are several lists that come out-of-the-box when you install DNN (see “Lists” table in SQL). The most visible ones are countries and regions. But there are also currencies and frequencies, plus a whole bunch of lists that tell DNN how to run things (i.e. they allow us to configure particular bits). The new feature means that what you will see in the front end of DNN can adapt to the language of the user viewing it.

In a nutshell the way the localization works is by examining the App_GlobalResources directory to see if there’s a resource file with the name “List_[ListName]”. So for the countries list we’d look for if we’re localizing in French. “Country” is the name of the countries list. The “Value” in the Lists table is then used to look up the resource key. So, for instance, if we take Switzerland, the key “CH.Text” is looked up in this file to find “Suisse”. Naturally when you begin to pull one string, other parts move as well. So this change has prompted a couple of other changes as well to make this as smooth as possible. So for those that want to know more about this, I’ll elaborate on these changes below.

Completing Countries, Regions and Currencies

There are several good sources of geographic information on the web. And after a little research I quickly found localized lists of countries as well as complete lists of regions and currencies around the world. So one of the simpler changes that is rolled out with this version is the completion of our country, region and currency lists. The change script for DNN 7.3.3 will make a couple of additions to the country table (a couple of new countries have emerged since the last update) and the completion of the regions and currency lists. This now means that if you use region in your user’s profile, they will most likely see the region textbox as a dropdown and show the regions of the country they selected.

You may ask “Why, Peter? Why add those to each and every installation on the planet for the sake of the non-US installations?”. And you’d probably either be from the US or you might not even have noticed as you really didn’t use this feature. My reply is: out of respect for this planet’s diversity. I’ve looked at it closely and could not detect any downsides (performance wise). So it should not in any way hinder anyone else. But what we’d gain is that we’re no longer requiring those who wish to use the feature outside the US to create their own lists. Plus: if you’d have a global site (i.e. you expect to use the full features for registration from people from around the world) we’ve now saved you a gargantuan task of filling in all those regions.

User Profile Changes

There are two properties on the user profile that make use of lists: country and region. Up until now the value stored in the user’s profile was actually the “Text” value of the selected item in the list. I.e. if you selected “Switzerland” from the dropdown, the value stored would be “Switzerland”. This is of course not a good way to do this. It should have been “CH” so that someone else seeing your profile might be able to see “Suisse”. And there are about a thousand more reasons why we don’t store the text value of things in related tables. So I aimed to correct this anomaly once and for all.

List table

In the change script for DNN 7.3.3 there is SQL code that will attempt to look up the values stored on the users’ profile and change the “Text” value to the “Value” value if it is found in the Lists table. This is done for both countries and regions. So users with “Switzerland” now get the value of “CH” stored in this field.

Profile table

Naturally this means we need to do the lookup every time the profile property is requested. That change has also been made to the user profile code.

Editing Lists

One other consequence of this change is that the list editor would need to become aware of this multi-lingual capacity. Until now the list editor would only interact with the Lists table. But now we need to come up with a strategy for editing lists that are localized. What happens if someone is editing while the page is set to Spanish? The solution we came up with is that editing in any other language than the System Locale (i.e. en-US), we will write out the text value to the resource file. But what if we first create a list entry? Well, in that case it will go to the table as well. So if you first add an entry in Spanish and later edit it in en-US, in the latter case you’d just be editing the value in the database and the original Spanish text would still remain in the resource file.

Note that to make this happen we needed to introduce two more changes. First the ListEntryInfo has a new property TextNonLocalized that stores the database value. We need this value to make sure we can still add and edit without overwriting the database value with a localized value. Secondly we keep an internal list of tables that are used just by DNN for actual logic operations (ContentTypes, Processor, etc) that will bypass this logic. These lists are actually something of an anomaly as they are not really meant for front-end consumption. So they have no business being localized.

New Localization Methods

One other gem I’d like to draw your attention to is a few extra methods to deal with resource files. In various places I’ve seen code to manipulate resource files, but what has bugged me for long is that this wasn’t done in the right place. Ideally, right next to “GetString” (which is the central retrieval method for a resource in the entire framework) you’d have something like “SaveString”. Right? I mean, that is where this should be. So we now have this method in DotNetNuke.Services.Localization. LocalizationProvider:

bool SaveString(string key, string value, string resourceFileRoot, string language, PortalSettings portalSettings, DotNetNuke.Services.Localization.LocalizationProvider.CustomizedLocale resourceType, bool addFile, bool addKey)

So basically this save a key in a resource file the same way it’d be retrieved. The last two parameters tell this method whether it should create the key and/or file if it isn’t already there.

Xml Extensions

And as I was working on the above I took the liberty of adding a few extension methods for manipulating Xml. The (somewhat outdated) XmlUtils class contains a few methods to manipulate Xml, but these methods were created with a specific scenario in mind. Creating the SaveString method mentioned above, I could not use any of those methods and still cut back on code. Microsoft’s Xml code can be a bit cumbersome at times and if a project involves a lot of XML manipulation I’ll add some methods to cut down on code. So here are a few gems for you to use in your own code. Note these methods are not just extension methods, they are largely chainable as well. Meaning you can do stuff like this:

Node.AddElement(“foo”, “bar”).AddAttribute(“fooName”, “foo1”).AddAttribute(“barName”, “bar1”)


I hope you like the additions and changes. Although it is always possible to say: this didn’t go far enough, you need to realize that a solid platform is built in baby steps. These changes are incremental. But I believe that code that was added does what it aims to do and makes a difference for those installations that are not en-US. Please let me know if you have any issues with this change.


Sebastian Leupold
Great Job, Peter!
However, for the long term we should investigate moving all resource keys to the database, allowing extensions to contribute to single lists as well (e.g. DataTypes) and provide localization for it as well.
Sebastian Leupold Tuesday, October 7, 2014 7:43 AM (link)
Peter Donker
Thanks Sebastian. We'll see what the future holds.
Peter Donker Tuesday, October 7, 2014 11:22 AM (link)
Ronald Anderson
One problem with your 'fix', I have several queries over several DNN installs that use the GetProfileElement function or directly draw from the UserProfiles table to retrieve Country, Region and City information to display in an ASP ListView list. This now pulls the abbreviated value from the UserProfiles table and not the text for each item. How do I correct for this?
Ronald Anderson Sunday, October 12, 2014 10:23 AM (link)
Sebastian Leupold
Ronald, you'll have to join userprofile with lists table, to retrieve full (english) name of Country or Region.
This is by design.
Sebastian Leupold Sunday, October 12, 2014 5:51 PM (link)
Peter Donker
If you wish to stay solely in SQL then you need to combine the result with the Lists table to look up the old text value. If you are doing it in code then call the regular methods on the ListEntryInfo object but you'll first need to look it up.
Peter Donker Sunday, October 12, 2014 5:52 PM (link)
Joe Brinkman
This highlights why we don't/can't guarantee of backwards compatibility at the database level. SQL doesn't have the same capability to support backwards compatibility the way that we can at the API level. Anytime you code directly against the db you take a risk that your code will break in future updates.
Joe Brinkman Wednesday, October 15, 2014 9:56 AM (link)
Peter Donker
Correct. Note that in my commercial work I will sometimes refer to core tables so don't take this as "it's forbidden to do so". I know the risk and have no issue with the fact that every now and then it might break (it is very rare, mind you) my module. I just need to follow what happens in SQL. Note that for this change the fix should be simple enough as the data is still there (just not localized in different languages).
Peter Donker Wednesday, October 15, 2014 10:02 AM (link)

Comment Form

Only registered users may post comments.


Aderson Oliveira (22)
Alec Whittington (11)
Alessandra Daniels (3)
Alex Shirley (10)
Andrew Hoefling (3)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (37)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Bogdan Litescu (1)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (213)
Chris Paterra (55)
Clint Patterson (108)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (181)
Daniel Valadas (48)
Dave Buckner (2)
David Poindexter (12)
David Rodriguez (3)
Dennis Shiao (1)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (80)
Francisco Perez Andres (17)
Geoff Barlow (12)
George Alatrash (12)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (274)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Kelly Ford (4)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matt Rutledge (2)
Matthias Schlomann (16)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (40)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Oliver Hine (1)
Patricio F. Salinas (1)
Patrick Ryan (1)
Peter Donker (54)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Sacha Trauwaen (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott Schlesier (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Steven Fisher (1)
Tony Henrich (3)
Torsten Weggen (3)
Tycho de Waard (4)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (180)
William Severance (5)
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out