I need to add support for alternate languages to my VB application. I have started by creating a resource file with strings and values for one form, using GetString. Before I go too far, I want to figure out the best organization. In VB6 I could have a string table with multiple languages in a file which made it easy to add more. It looks like I need files for each langue in .NET.
Should there be a single large file for each language for the entire project, or multiple files, say for groups of forms?
Here is a function I am using to access the strings:
Public Function GetString(ByVal strValue As String)
Select Case m_str_System_Language
Case "EN" 'English
rm = My.Resources.English.ResourceManager
Case "FR" 'French
'rm = My.Resources.French.ResourceManager
Case "ES" 'Spanish
'rm = My.Resources.Spanish.ResourceManager
Case "DE" 'German
'rm = My.Resources.German.ResourceManager
Case Else '"EN" 'English
rm = My.Resources.English.ResourceManager
End Select
'Return language specific string
GetString = rm.GetString(strValue)
End Function
Well, this is a big field. For the beginning, read Walkthrough: Localizing Windows Forms. You will learn a lot from it. Sometimes you find some gems even on the Microsoft website. ;-)
I use three strategies to localize apps. The first is:
Thats quite easy.
Sometimes you have ComboBoxes in which all texts must be localized. Then I use the old database table approach. This would look like:
ID Group ItemID en_US de_DE
1 Sexes 0 male männlich
2 Sexes 1 female weiblich
3 Sexes 2 diverse divers
4 FamilyStatus 0 single ledig
5 FamilyStatus 1 married verheiratet
6 FamilyStatus 2 divorced geschieden
7 FamilyStatus 3 widowed verwitwet
And then get the right SELECT Statement depending on the language you want and the group you want.
The other stuff means e.g. MessageBox messages. That's stuff which is just shown temporaily or changes from time to time.
Here the resources table in project properties is your default language store. To add new languages do:
Position & String.Format(" of {0} records", CounterText, DataTable.Rows.Count)
.Now, how to use it:
To test localization I added a ComboBox to my test form (in the release in your options dialog) and filled it with these Items (in the properties window):
(machine default)
English
German
French
Gaelic
Then the according event handler looks like:
Imports System.Threading
Private Sub CboLanguage_SelectedIndexChanged(sender As Object, e As EventArgs) Handles CboLanguage.SelectedIndexChanged
If CboLanguage.SelectedItem Is Nothing Then Return
Dim Infos = {"", "en-US", "de-DE", "fr-FR", "gd-GB"}
If CboLanguage.SelectedIndex = 0 Then
Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture
Else
Thread.CurrentThread.CurrentUICulture = New CultureInfo(Infos(CboLanguage.SelectedIndex))
End If
End Sub
The list contains Gaelic only to test a language I really dont have in my resources, which means to test the switch to the default resource. In production you would delete the languages you don't have resources for from the list and of course the (machine default) entry.
Note that there are two default cultures when you start an application. We have the default culture which is defined in your project properties about which I told before. But there is another one which is the computers language the app is running on. There are two scenarios how an app will find the culture it will use.
You start the app, the app determines the computers culture and searches in your resources for the according ressources (this will happen every time a resource is needed). If the according resource is found, it will be used, otherwise the resource from the default resources will be used.
The user sets another language in the options, this is saved in whatever settings strategy (My.Settings, app.config, INI-File, Registry, Isolated Storage, Database), you load it at Initialization/Start of your app and set the proper Threads UICulture, then this Culture will take place where the computers was. From now on resources will be searched in the according Resource file and analog, if not found, in the Default resources.
Regarding the forms you have to do nothing. Depending on your Thread.CurrentThread.CurrentUICulture
the according texts will be shown.
As soon as you add a resource text to the default resources or one of your other language resource files VS will create a new property with the name of your resource text in then Resources.Designer.vb which is located in the "My Project" folder.
This means, if you create a resource text named "New" and with value "&New" in the default resources or the Resources.en-US.resx file, the same with value "&Nouveau" in the Resources.fr-FR.resx file and "&Neu" in the Resources.de-DE.resx file, you can access the text with:
My.Resources.New
That's it. Depending on your Thread.CurrentThread.CurrentUICulture
you will get the text in the according language.
Note that the My.Resources
namespace is declared with the Friend modifier which means it applies only to the local assembly. And every assembly has its own My.Resources
namespace.
If you do not Multithreading and every DLL has its own localized texts in the same cultures, you have no problem. But if you want to manage texts in a DLL from your main assembly then you need to have a reference of the main ResourceManager in your DLL. This looks like in your DLL class:
Public Class MyPublicDLLClass
' Members
Public Property ResourceManager As Resources.ResourceManager = Nothing
' Other members
End Class
And in the calling assembly:
Dim DLLObject = New MyPublicDLLClass With {
.ResourceManager = My.Resources.ResourceManager
}
Then you could do this to have access to the resources of the calling assembly:
Function GetText(Item As String) As String
Return GetText(Item, Nothing)
End Function
Function GetText(Item As String, [Default] As String) As String
Dim Def As String = If([Default], Item)
If ResourceManager IsNot Nothing Then
Dim Text = ResourceManager.GetString(Item)
Return If(String.IsNullOrEmpty(Text), Def, Text)
Else
Return Def
End If
End Function