Black Falcon Software's Technical News For .NET Development
Black Falcon Software: Localizing Your Web Applications
Many solutions have been offered providing localization solutions to web applications. However, when it comes to large amounts of content some of these solutions that rely on resource files may not be sufficient. Instead, database storage is required to incorporate large amounts of text that have to be displayed on a web page in various languages.
The design of a relatively robust solution to this issue is somewhat easier than expected and with the following code can provide not only a simplistic solution for all such localization requirements but a rather efficient one as well. Surprisingly, the database calls needed to extract the content in a selected language do not slow responsiveness down noticeably even on a shared hosting server.
To implement this solution, simply follow the suggestions below…
The Database
The database requires only a single file than can be indexed in both the form name and the control name. The basic database structure then looks as follows:
This simple table will allow you to insert data for any form, for any control that displays text, on a per language basis, which is usually denoted by a country code. Of course, you have to get the data in their, which for the first three columns is quite easy. It is the last one that will take time.
The content that is placed in the “LOCALIZED_TEXT” field should not be simple conversions of English to another language using either free online translation software or a purchased translation application. Such software is only good for short phrases. Using it for translations of substantial text will only provide the reader with a very poor representation of the original English. This is because translation software cannot provide the nuances that credible translators can. As a result, using such a methodology will only be a detriment to your web application. Thus, if you plan on presenting a site with multi-language capability, take the time to make sure that all your translations are done correctly and thoroughly.
The Web Page
With Microsoft’s master-page framework you can now easily place your language controls in a single page that will be inherited by your child web-pages. The most common way of informing a visitor to your site that you offer multiple languages is with a strip of country flags such as shown below.
In this case we are showing the flag for the United States for English and the Austrian flag for German.
Each one of these images can be implemented as an ASP.NET image-button with some basic code in the code-behind page for every country flag image as follows:
The use of the “session” variable using the default implementation (inproc) for web development is not recommended if you plan to host your web application in any type of clustered server solution. If this will be your hosting architecture you must set up your session state with either a state server or a database.
What is being shown here is the most basic of code and it will not necessarily fit your specific requirements. However, you have to store the selected country code some where that is global to the entire application so that any page being displayed can determine what language any text is to be displayed in.
Extracting Localized Text From The Database
In addition to flipping the displayed language when a language selection has been changed, upon the “Page_Load” process for any web page in the application the following code should then be implemented for each and every control on your web page that requires text display.
Notice that the code above will call the database for each control you need to display text for and extract the currently selected language.
This by no means is a very efficient way of doing things if you have a site that is going to be visited heavily. In this case, you should cache your language data into the application level cache for much more efficient access. Otherwise, for lightly visited sites, despite the protestations by some, the database IO required will not be an issue.
Now, the actual call to the middle-tier code that gets your text for you was designed to be very simple. However, it can be made far more efficient, whereby you require only a single call to the database thereby returning all of the needed text to the web-page’s code-behind module where it can then be loaded into the various controls. However for starters let’s just take the original code as designed.
The call in the previous code-segment makes a call to a localization object, which is user defined, for the following method code…
Note: There is no need to null out local objects. The CLR will take care of them during garbage collection.
Admittedly, the code in the previous method does not serve efficiency very well. It merely returns a single structure of data in an array-list for each call from the “Localization_BySelectedCountry” method, which was the first method introduced in this section. As was mentioned previously, this code was designed very simplistically. And the code works quite well.
However, to implement efficiency we simply enhance the code to return all of the data for all controls in the database query, thus extracting the data simply based upon the “FORM_NAME” instead of both the “FORM-NAME” and the “CONTROL_NAME”. This would then return an array-list of all relevant structures from a single query to the database. These structures can then in turn be used to supply the data for each control by merely looping through the associated array-list and load the controls based on their names.
As a result the “Localize_Text” method, also shown above, would then return the array-list of structures back to the primary calling method, “Localization_BySelectedCountry”. Then instead of using individual database calls to extract the data, replace these calls with a call to a loop process that would extract the data from the structures in the array-list, an array-list, which by the way, can be cached in the application cache.
Using Structures Over Datasets\DataReaders
Using a structure (or a class, whichever is preferable) to represent your data within an array-list is probably one of the most effective ways of transferring the data between tiers while at the same time keeping its use highly intuitive since you do not have to worry about the more arcane methods of accessing it with either Datasets or DataReaders. structures then are the basis for Object Relational Mapping (ORM) systems that also tout such intuitiveness as its strongest feature.
The problem with using structures is that they are tied to the underlying data and if anything substantial is changed in that data structure so too must be the structure or class be changed, changes which also ripple right up to the front-end in most instances. This is where ORMs fall flat on their faces as well as the use of structures even in the case demonstrated here.
However, in this case we are talking about an underlying data structure that has very little chance of being changed over time since it is designed to serve a single purpose, provide data for display. What will change of course is the actual data but this is a part of the text column and his little to do with the mechanics of displaying it.
Structures and\or classes are a very good way of keeping your data intuitive and at the same time adhere to the OO concepts of data encapsulation. What more perfect OO concept can there be but data as an object?
In this framework, a simple structure is used and it is loaded at the lowest level in the calling process which is exactly where it should be loaded. The structure here is as follows…
public struct strLocalization
{
private string ssFormName;
private string ssControlName;
private string ssCountryCode;
private string ssLocalizedText;
public string FORM_NAME
{
get
{
return (ssFormName);
}
set
{
ssFormName = value;
}
}
public string BFS_CONTROL_NAME
{
get
{
return (ssControlName);
}
set
{
ssControlName = value;
}
}
public string BFS_COUNTRY_CODE
{
get
{
return (ssCountryCode);
}
set
{
ssCountryCode = value;
}
}
public string BFS_LOCALIZED_TEXT
{
get
{
return (ssLocalizedText);
}
set
{
ssLocalizedText = value;
}
}
}
As you can see it is very simple and quite light when compared to a Dataset or a DataReader. And it serves the purposes here perfectly.
Thinking In Terms of Efficiency
A number of readers may consider that there may be a better way than what has been just demonstrated. However, when you think about it, exactly what would be better? You have to store the language text some place so in that respect it doesn’t matter whether you store it in a database or a resource file; you are going to have some level of data IO. Next, you have to appropriate such data on a per control basis. And it doesn’t matter whether you do this yourself in this way or through a third-party tool that does it in a way proclaimed to be highly efficient. In either case the data has to be matched to the control and then loaded into it.
The most readily available way to make such processes efficient is to use the application cache since in this case the data is completely static. And this was already discussed.
However, when we talk in terms of efficiency we have to also denote exactly what we are talking about. Efficiency in present day conceptualization appears to concern itself with the speed at which instructions are being processed if we are talking about code or query optimizations if we are discussing database access.
In both cases, the argument is rather moot since what is being contended with are speeds that are so fast that there is very little one can do to make code more efficient than what the compilers and optimizers will already do for you. Both the .NET CLR and the database optimizers are very difficult to beat even if you were to implement just average code. It is all taken and manipulated for the best effect. And you would have to write some seriously bad code to produce less optimized results.
All of this can be demonstrated very easily with code profilers and query plans. You will find that if you test out different coding scenarios you will save nothing more than a few milliseconds for each of the processes submitted. In some cases you will find that quite a few milliseconds are saved and for very intensive database access such optimizing may be called for. However, for the most part It is hardly worth refining your code on this basis.
True efficiency in the Information Technology field has very little to do with the speed of your code or your data queries. This is a myth that has been propagated by newly graduated computer science students and academics from across the globe where they have been taught or teach the details of compilers and development related to such concepts. In fact, the overriding software engineering criteria for distributed application development is in fact not speed but concurrency. Given the fact that the Internet is a massively used distributed application, the concept of performance in terms of speed is nearly impossible to realize since the majority of impediments to acceptable response rates is not code-based but physically based. In other words, the physical architecture of where your application is residing is the overriding determinant to the level of performance that can be expected. True, there have been numerous “tricks” that have been added to enhance such performance, such as AJAX, but a server request is still being made, even if less data is being returned.
Real efficiency is where the software that is developed saves the company monies on its investment in its development. So for real world considerations software efficiency then falls into those areas that Software Engineering holds as important milestones in software creation.
For example…
Does the final product have a low defect rate?
Is the code legible in terms of neatness and commenting?
Is the code easy to read and understand for even a junior level technician?
If the code is a large system, is the design intuitive in terms of understanding it?
Does the installed application run without producing any issues?
If you can provide affirmative answers to the questions above for a particular piece of software that you have developed than you can safely say that you have produced an efficient piece of software. It not only does the job but it does so in a manner that is easily understood, maintained, and thus enhanced. This then is true efficiency and it is this level of ease that went into the design of the code presented. It has no defect level, it works well, it can be tuned for greater efficiency in terms of access, and it can be done so quite easily…
No comments yet
Jump to comment form | comment rss [?] | trackback uri [?]