Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Localize your ASP.NET Application

Best Practices

Localize your ASP.NET Application

by  tyris  Posted    (Edited  )
This FAQ is not the only solution for simple localization, but i know this way works.
I didn't test other configurations (i followed the microsoft recommanded steps)


1. How it works

The aim is to separate the display content from the application. Doing this will allow you to create or modify the culture content without compiling again your application project. Consequently without using .NET framework.
The main principles are:
- The localization files (.resx extension) are separated and sorted in a special folder that only contains localization files. The sort order is very important, as you will see later.
- The Web application will detect the language selected by the clientÆs browser (Internet Explorer)
- Only the default language is compiled with the application. This case is apart from the rest.
- There is ONE resx file for each WebForm.


2. Language naming conventions

You cannot give whatever name for your languages. You must absolutely follow the specifications given by the RFC 1766 (http://www.ietf.org/rfc/rfc1766.txt)
A complete list of all languages and their codes can be found here: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemGlobalizationCultureInfoClassTopic.asp


3. Resource fallback process

(Pasted from MSDN site)
The hub and spoke model for packaging and deploying resources uses a fallback process to locate appropriate resources. If an application user requests a ResourceSet that is unavailable, the common language runtime searches the hierarchy of cultures looking for an appropriate fallback resource that most closely matches the user's request, and raises an exception only as a last resort. At each level of the hierarchy, if an appropriate resource is found, the runtime uses it. If the resource is not found, the search continues at the next level. The resource fallback process is described in the following steps.

- The runtime first checks the Global Assembly Cache for an assembly matching the requested culture for your application.
The global assembly cache can store resource assemblies that are shared by many applications. This frees you from having to include specific sets of resources in the directory structure of every application you create. If the runtime finds a reference to the assembly, it searches the assembly for the requested resource. If it finds the entry in the assembly, it uses the requested resource. If it does not find the entry, it continues the search.
- The runtime next checks the directory of the currently executing assembly for a directory matching the requested culture. If it finds the directory, it searches that directory for a valid satellite assembly for the requested culture. The runtime then searches the satellite assembly for the requested resource. If it finds the resource in the assembly, it uses it. If it does not find the resource, it continues the search.

- The runtime next searches the global assembly cache again, this time for the parent assembly of the requested resource. If the parent assembly exists in the global assembly cache, the runtime searches the assembly for the requested resource.
The parent is defined as the appropriate fallback culture. Consider parents as best-fit candidates; providing any resource is preferable to throwing an exception. This process also allows you to reuse resources. You need to include a particular resource at the parent level only if the child culture does not need to localize the requested resource. For example, if you supply satellites for en (neutral English), en-GB (English as spoken in the UK), and en-US (English as spoken in the US), the en satellite would contain the common terminology, and the en-GB and en-US satellites could provide overrides for only those terms that differ.

- The runtime next checks the directory of the currently executing assembly to see if it contains a parent directory.
If a parent directory exists, the runtime searches the directory for a valid satellite assembly for the parent culture. If it finds the assembly, the runtime searches the assembly for the requested resource. If it finds the resource, it uses it. If it does not find the resource, it continues the search.

- The runtime next searches parent assemblies, as in the previous step, through many potential levels. Each culture has only one parent, but a parent might have its own parent.

- If the culture that was originally specified and all parents have been searched and the resource is still not found, the resource for the default (neutral) culture is used.
Note The default resource is the only resource that is compiled with the main assembly. It is the absolute fallback (final parent). Therefore, it is strongly suggested that you always include a default set of resources in your main assembly. This helps to ensure that exceptions are not thrown. By including a default resource file you provide a fallback for all resources, and ensure that at least one resource is always present for the user, even if it is not culturally specific.

- Finally, if the runtime does not find a resource for a default (neutral) culture, an exception is thrown indicating that the resource could not be found.


As an example of how the search for a requested resource is conducted, suppose the user requests a resource localized for Mexican Spanish. In accordance with the resource naming conventions described above, the runtime first searches the global assembly cache for the assembly matching the requested culture, "es-MX". Not finding it, the runtime then searches the directory of the currently executing assembly for an "es-MX" directory. Failing that, the runtime searches the global assembly cache again for a parent assembly reflecting the appropriate fallback culture ù in this case, "es" (Spanish). If the parent assembly is not found, the runtime searches all potential levels of parent assemblies for the "es-MX" culture until a corresponding resource is found. If a resource is not found, the runtime uses the resource for the default culture.

4. Organize your files


4.1 Files

The naming convention of the files is very strict. If you do not follow the given recommendation, this may not work at all.



The default language files must be named this way:
WebFormName.resx

The specific language files must be named this way: ApplicationName.WebFormName.language.resx


So in our example, if we have 2 WebForms called WebForm_A and WebForm_B this will make:
Code:
Resources:
             WebForm_A.resx 		
             WebForm_B.resx		
             en: 
                          MyApp.WebForm_A.en.resx
                          MyApp.WebForm_B.en.resx
             en-us:
                          MyApp.WebForm_A.en-us.resx
                          MyApp.WebForm_B.en-us.resx
             fr: 
                          MyApp.WebForm_A.fr.resx
                          MyApp.WebForm_B.fr.resx

The last step but not the least: Do not forget to delete
all generated .resx files that are created at the WebForm
creation (and also when changinf WebForm desing content,
the .resx is created again) (they are sometimes hidden
by .NET Framework, to show them click ôShow all filesö and
expend the menu of your WebForm). Delete the resx file, or
you could have a conflict with your files. (Error oncompile)



5. Inside the ASP.NET Code

Here is the code to implement the detection of the language
into your ASP pages.
It has 2 parts: the Global.asax and the WebForm


5.1 The resx file

LetÆs say you have 2 labels in your form.
In your resx file you will define the content of the label:


5.2 The Global.asax

Code:
    Sub Application_Start(ByVal sender As Object, ByVal e 
As EventArgs)
        Application("RM4WebForm1") = New 
System.Resources.ResourceManager(GetType
(WebForm1)) 'ResourceManager for WebForm1
        Application("RM4WebForm2") = New 
System.Resources.ResourceManager(GetType
(WebForm2)) 'ResourceManager for WebForm2
    End Sub

    Sub Application_BeginRequest(ByVal sender As Object, 
ByVal e As EventArgs)
        ' Fires at the beginning of each request
        ' For each request initialize the culture values 
with
        ' the user language as specified by the browser.
        ' DO NOT PUT this code in Session_Start or it won't 
work
        Try
            Thread.CurrentThread.CurrentCulture = 
CultureInfo.CreateSpecificCulture(Request.UserLanguages(0))
        Catch ex As Exception
            Thread.CurrentThread.CurrentCulture = New 
CultureInfo("en")
        End Try
        Thread.CurrentThread.CurrentUICulture = 
Thread.CurrentThread.CurrentCulture
    End Sub

Explanations:

Request.UserLanguages(0) Retrieves the Language defined
by the user.
Thread.CurrentThread.CurrentCulture Sets the current
culture for the session


5.3 The WebForm
Code:
    Private Sub Page_Init(ByVal sender As System.Object, 
ByVal e As System.EventArgs) Handles MyBase.Init

        ' Get the ResourceManager from the Application 
object.
        rm = Application("RM4WebForm1")

    End Sub

    Private Sub Page_Load(ByVal sender As System.Object, 
ByVal e As System.EventArgs) Handles MyBase.Load
        InitializeComponent()
        Label1.Text = rm.GetString("Label1")
        Label2.Text = rm.GetString("Label2")
    End Sub

Explanations:
You retrieve the content of the resx file with the function
GetString("whatever")

6. Compile your resources into DLL without rebuilding your
application

The aim of this documentation is to allow the modification
of your resx files without touching your application.
This is the hardest part. If the .dll files are not
generated correctly nothing will work, and the default
language will always loaded even if you asked for ôEnglishö
for example.

Two tools will help you to process this: resgen.exe (stand
for Resources generator) and al.exe (stands for Assembly
linker)

First check that you named your files EXACTLY as described
above.
Then you will have 2 steps:
- Transform your .resx file to a .resources file
- Link all .resources files into a single .dll (one
dll by language)

For those 2 tools, you will have to use the Open Visual
Studio .NET Command Prompt.

6.1 RESGEN.exe

Transform each .resx file into a .resources file this way:
Code:
>resgen.exe [the resx file]
For example:
For the file named MyApp.WebForm_A.en.resx your command
will be:
Code:
>resgen MyApp.WebForm_A.en.resx
And the result will be the creation of a file:
MyApp.WebForm_A.en.resources

6.2 AL.exe

This part is a bit more complicated.
You must select all .resources files contained into your
language folder.
In our example, the dll generation for English (en) will be:

Code:
>al.exe /t:lib /embed:MyApp.WebForm_A.en.resx /embed:MyApp.W
ebForm_B.en.resx /culture:en /out:MyApp.resources.dll

Options:
/t: tells that you want a dll
/embed: repeat this option X times (one by file)
/culture: this option specifies the language to associate
with the dll
/out: the name of the dll

The name of the dll will always be:
Application_Name.resources.dll

6.3 Place the files

The negated dll must be placed this way :
Go in the BIN directory of you application.
You can see a folder for each language (if there is not the
folder for your language create it, always following the
naming conventions)
Put your generated dll for English in the folder ôenö.
Put your generated dll for English-US in the folder ôen-usö.
Put your generated dll for French in the folder ôfrö and so
on.



7. Tools that may help you

If you get an error, like: ½ Could not find any resources
appropriate for the specified culture (or the neutral
culture) in the given assembly. + a tool called ildasm.exe
may help you.
To use this tool:
Always in the Open Visual Studio .NET Command Prompt.

Enter:
+ ildasm.exe your_dll_file.dll

This tool helps you in viewing the content of a dll.
I recommend you to launch this tool for the .NET framework
generated dll and one for your own, generated dll, so that
you can compare the too versions.
The most common problems are for ô.mresource publicö field
in the manifest. If the field is not the same, itÆs because
you didnÆt name correctly the .resx files (not good).

8. Documentation that may help you

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconlocatingusingresourcesforparticularculture.asp
http://www.ondotnet.com/pub/a/dotnet/2002/10/14/local2.htm
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcreatingsatelliteassemblies.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemGlobalizationCultureInfoClassTopic.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cptutorials/html/resourcemanager_and_asp_net.asp

And I simply recommend you the MSDN website.



Hope it was helpfull.
Elise, NTIC developper (France).
Register to rate this FAQ  : BAD 1 2 3 4 5 6 7 8 9 10 GOOD
Please Note: 1 is Bad, 10 is Good :-)

Part and Inventory Search

Back
Top