Link to home
Start Free TrialLog in
Avatar of yerffoeg
yerffoegFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Internationalisation in VB

I need to Internationalise my VB app, I would use a resource file a string tables per LCID but:

Each Customer will need their own string table due to differing names of fields (i.e. some use Branch, others use depot, other location, etc) It seems that you can only have one string table per language per resource file, so this would be impossible. We have lots on English customers so a resource file per customer and recompiling for each is implausible.

We would need to change language on the fly, when dialling in to fix problems or during on site product set up, and as we have found to our cost switch Locales around is not a good idea.

Also some customers have a German LCID but use our product in English, why?, I don't know either.

So what I need is a way to have one string table per customer set from a registry key or database field.
Any tools or ways we can do this?
Avatar of waty
waty
Flag of Belgium image

You have several possibilities :
  - Add all language in the same ressource file but with different ID. Ex : for English, begin to ID 0, for French begin to ID 5000
      ID 0 => Print      ID 5000 => Imprimer
      ID 1 => Window     ID 5001 => Fenêtre
Use a global variable to set the actual language. Parse all the windows, text using a function wich make the translation. I use the following function in all my applications (see below)

  - Use a database : Have a table per language, or all language in one table and one field per language.



' *** Here is the code for traduction. The comments are in french.

Option Explicit

' *** Langue
Global gnLangue   As Long

Sub TraductForm(TheForm As Form)
   ' *** Traduction d'une fenêtre ***
   ' *** On utilise l'ID qui est identifié par le @ ***
   
   On Error GoTo ERROR_TraductForm
   
   Dim nI   As Integer
   
   ' *** On commence par traduire le caption de la fenêtre ***
   TheForm.Caption = Traduction(CStr(TheForm.Caption))
   
   For nI = 0 To TheForm.Controls.Count - 1
      ' *** La traduction dépend du type du contrôle ***
   
      If TypeOf TheForm.Controls(nI) Is Label Then
         TheForm.Controls(nI).Caption = Traduction(CStr(TheForm.Controls(nI).Caption))
      ElseIf TypeOf TheForm.Controls(nI) Is Menu Then
         TheForm.Controls(nI).Caption = Traduction(CStr(TheForm.Controls(nI).Caption))
      ElseIf TypeOf TheForm.Controls(nI) Is CommandButton Then
         TheForm.Controls(nI).Caption = Traduction(CStr(TheForm.Controls(nI).Caption))
      ElseIf TypeOf TheForm.Controls(nI) Is Frame Then
         TheForm.Controls(nI).Caption = Traduction(CStr(TheForm.Controls(nI).Caption))
      ElseIf TypeOf TheForm.Controls(nI) Is CheckBox Then
         TheForm.Controls(nI).Caption = Traduction(CStr(TheForm.Controls(nI).Caption))
      ElseIf TypeOf TheForm.Controls(nI) Is OptionButton Then
         TheForm.Controls(nI).Caption = Traduction(CStr(TheForm.Controls(nI).Caption))
      End If
   Next
   
   Exit Sub
   
ERROR_TraductForm:
   Exit Sub
   
End Sub

Function Traduction(szText As String) As String
   ' *** Traduction d'un texte en recherchant l'ID dans le string ***
   
   Dim szTemp     As String
   Dim szID       As String
   Dim nPos       As Integer
   Dim nReturn    As Integer

   On Error GoTo FIND_DLL

   szTemp = ""

   ' *** On recherche le caractère séparateur ***
   nPos = InStr(szText, "@")
   
   If (nPos = 1) Then
      nPos = 1
   End If
   
   ' *** On quitte, pas de traduction ***
   If (nPos = 0) Then
      Traduction = szText
      Exit Function
   End If

   szID = Right(szText, Len(szText) - (nPos))
   
   ' *** On quitte, pas de traduction ***
   If (IsNumeric(szID) = False) Then
      Traduction = szText
      Exit Function
   End If

   If (gbModifyIterfaceText = False) Then
      szTemp = Trim(LoadResString(gnLangue + CLng(szID)))
   Else
      szTemp = Trim(colUserText("ID" & CStr(gnLangue + CLng(szID))))
   End If
   
   ' *** On quitte, pas de traduction ***
   If (szTemp = "") Then
      Traduction = left(szText, nPos - 1)
      Exit Function
   End If

   Traduction = szTemp

   Exit Function

FIND_DLL:
   szTemp = ""
   Resume Next

End Function




Public Sub SetLanguageEnglish()
   ' *** set the english traduction
   
   gnLangue = 0

End Sub

Public Sub SetLanguageFrench()
   ' *** set the french traduction
   
   gnLangue = 1000

End Sub

Public Sub SetLanguageSpanish()
   ' *** set the Spanish traduction
   
   gnLangue = 2000

End Sub

Public Sub SetLanguageGerman()
   ' *** set the german traduction
   
   gnLangue = 3000

End Sub

Public Sub SetLanguageDutch()
   ' *** set the Dutch traduction
   
   gnLangue = 4000

End Sub

Public Sub SetLanguageItalian()
   ' *** set the Italian traduction
   
   gnLangue = 5000

End Sub


Avatar of yerffoeg

ASKER

It doesn't allow for easy standardisation throughout the code, you end up with large resource files (which will be loaded into memory).
I believe the Microsoft MSDN Document Q188659
Answers our question perfectly keeps the size of the application installation down to a minimum, and the size of resource file loaded into memory.
I use my way, it is very easy to maintain, and it is very quick. So good work.
Does anyone know how I can cancel this question as the MSDN Doc mentioned above suggests the use of satellite DLL's, which seem to fit our needs perfectly.

Or does someone have to answer it?
Does anyone know how I can cancel this question as the MSDN Doc mentioned above suggests the use of satellite DLL's, which seem to fit our needs perfectly.

Or does someone have to answer it?
Someone have to answer this.
Avatar of Mirkwood
Mirkwood

Answered :-)
What is going on, should I just say Mirkwood has answered it and be done with it, I'd prefer to give Waty the points as he did give me an answer, but it's not like he needs them.
It was just a joke. Nobody answered the question so I did.
I can give you another solution though.
Use the functions below. Put the resources in a seperate library.
With loadlibrary load the correct resource library and close the handle of the library after you don't need it anymore.
With LoadResource you can load the resource of the determined library.

Declare Function LoadResource Lib "kernel32" Alias "LoadResource" (ByVal hInstance As Long, ByVal hResInfo As Long) As Long

Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long

Declare Function CloseHandle Lib "kernel32" Alias "CloseHandle" (ByVal hObject As Long) As Long



If you want, I can accept the answer.
That's ok to. Just reject my answer. Waty answers it and accept his answer.
Rejected for Waty, Sorry I'm a bit confused I'm just getting the used to the way this works.
ASKER CERTIFIED SOLUTION
Avatar of waty
waty
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
If you want to know the full idea read the article Q188659 on MSDN, basically it involves creating an Activex DLL which contains the resource file and a couple of functions to read/write the resouce string. Then you can compile a DLL for every customer/language, just be changing the resource file. The id number can obviously stay the same throughout. Then all you need is a simply way to tell the program which DLL to use, in a regkey or the like. I like it better as a solution it just seems neater, a better for our needs than yours, especially if you have alot of customers. I given you a B because I'm sure your method would suit some people more than the method I found. Anyway thanks for your response, hears a ton.

Dave.