In my country, South Africa, there are eleven official languages. That's right eleven. That means that certain publications are published in these eleven official languages. Can you imagine trying to write an application to suit so many variations. What you wouldn't want to do is to have a different form, menu or report for each language. I don't know about you but that is a major job, never mind the nightmare of managing the changes to these objects. So what can we do? Well the ideal situation is to have one object (form, menu, or report) and to have the translation done directly or immediately and seamlessly. Now there are many different ways to accomplish this. Some are very long and laborious and some are difficult to manage. We could, for instance, have a table set up with rows and columns representing the object elements and the language translations. But then you might have to have a separate table for each object. This can be done but it is rather slow because you could end up in a situation where you have to continually move the record pointer by either locating or seeking the correct row or otherwise move to the correct row for the translation. Or, you could use memory variables. Wow that could be a nightmare.
Enter the *.DLL, or otherwise known as dynamic link library. And that's exactly what it is. It is a file that contains a library of resources. Theses resources can be any Windows resource, from bitmaps to cursors, to strings of data or text. You are possibly already using one of theses DLL's in the form of the “resource.dll”, when you choose a bitmap for a pushbutton. So the idea then is to have a library of resources that a form can access and use independently. The best way, in my opinion, is to create a DLL for each language that you intend using with the different text strings represented by different resources in the DLL. Each resource ID will be exactly the same across all languages. For example, I have put together a simple form for the English and Afrikaans languages. Therefore if a resource with an ID of 15 had a value of “ABC” in the english.dll, then the same resource ID in the afrikaans.dll would represent the Afrikaans equivalent, say “abc”.
The only problem is that you cannot create a resource DLL in Visual dBASE. You would need a third party tool or another development language that can create, compile and link or edit DLL's. I use Visual C++ to do that. But if you don't have such a animal you might have the “Resource Workshop” supplied with Visual dBASE 5. With this you can edit many of the smaller sized DLL's to suit your needs. I have found that the RW has a size limitation. The other limitation the RW has is that it is primarily a 16 bit editor so you cannot expect to create a DLL for a 32 bit system such as that developed by Visual dBASE 7.x. But what you can do is edit an existing 32 bit DLL and save it and the RW will keep it intact. Here you have two choices, 1) find a 32 bit DLL copy it to a different name in your working directory. Then edit it, delete all the original resources and add your own or, 2) use the blank DLL's supplied in the dUFLP, obtainable at the news groups and different Web sites around the globe.
That said, lets create a simple form that can be used by two different languages, English and Afrikaans. I will be concentrating on using the RW because I believe that most people reading this article would have access to that program
The first thing to do is to create a simple form. Create the form and then place a few objects on it, like text, entryfields, pushbuttons, radiobuttons checkboxes etc. For the purpose of this demo my form looks like this:
What we have to do now is to change the text value of each object. We would normally do this by changing it via the object properties. What I would do perhaps is to map each object defining what the object is and the desired text in your natural language so that you have a point of reference. For example “Text1=Name”, and “text2=Surname”.
If you don't have a blank 32 bit DLL for Visual dBASE 7.x you could always create one. The idea is to find a small DLL, copy it to another name and then edit it in the RW. If you go to your mugs folder under the samples folder, there you will find a “mugsR.dll” file. Copy this to your working directory and rename it to “english.dll”. Launch the Resource Workshop and navigate to your working directory and open the “english.dll”. To do this select “File...Open Project”. Select the correct file type by initiating the drop-down list. Here you will see different types of resource projects that you can work with. Navigate down to the line that reads “DLL, VBX, CPL library” and select it. Navigate to your working directory under the directory listing, the select your file, “english.dll” and open it. You should get a window with the different resources available in this particular DLL.
Highlight each of the resources and delete them. Remember you are doing this with your “english.dll”. Now with the blank DLL (you might already have used a blank DLL of your own or one that you obtained from the DUFLP) we need to create a resource that can hold text strings for the text value of our form objects. In the RW under “Resource”, select “New” and then Navigate to “STRINGTABLE” and select “OK”.
You will then be presented with a window where you can enter your desired string. The string table has an ID Source, ID Value, and string columns. Don't worry about the first two, use the defaults. It's the string column that we would like to edit. Highlight the string cell and edit it to read “English Form”.
To add a new resource item, from the menu select “Stringtable”, and then “New Item”. Edit the string to the desired text. Continue to do so until you have reached 17 or the desired number of text strings to match your form, menu or report.
From our demo form I have used the following strings and corresponding ID's. The id's are important as this serves as our reference to the string.
2, "Click Me"
6, "Choice 1"
7, "Choice 2"
15, "This is not an error. I bet you pushed the pushbutton"
Save the project. Now copy the “english.dll” file and rename it to name that would represent your other translation. For this demo I have renamed it to “afrikaans.dll”. Once you've done that open the DLL in the RW. Notice that the file is represented by the DOS short name, i.e. 8.3 format, so the file will be “afrika~1.dll”. Your project now has all your English version strings in them. This is why we mapped out our form so that when we enter the strings and edit the translated string we can jot down next to the mapped objects the corresponding resource id for use later in the form. Now edit the strings with your desired translation. Because I have use Afrikaans here is my Afrikaans translation.
1, "Afrikaans Vorm"
2, "Druk My"
4, "Voor Naam"
6, "Keuse #1"
7, "Keuse #2"
15, "Hierdie is nie 'n fout nie. Ek wet dat jy die knoppie gedruk"
17, "Alles Reg"
Notice that each corresponding id has a translation. Save the project.
Now in order for our form to work correctly we have to now link the text value of the objects to the corresponding resource in the correct DLL. The syntax would then be:
Object.text = resource (resource_id,resource_file_name) // The resource id is a numeric value, and the file name is a string.
So if we wanted the form.text1.text to have the Afrikaans value we would replace the text value “text1” with text = resource (4,“afrikaans.dll”). This will then replace the text value with the corresponding string value for that resource id, “Voor Naam”.
But because we want this to be dynamic, i.e. we want the user to be able to choose his/her language at runtime we need to substitute the resource file name with a place holder or variable. I use _app.langdll which holds the correct file name from the setup form . Then to make things a bit easier to read we can also put a place holder for the numeric string which we define at the top of the form:
#define form_header 1
#define form_title 1
#define form_pushbutton1 2
#define form_pushbutton2 3
#define form_text2 4
#define form_text3 5
#define form_rectangle1 6
#define form_rectangle2 7
#define form_radiobutton1 8
#define form_radiobutton2 9
#define form_checkbox1 10
#define form_checkbox2 11
#define form_checkbox3 12
** END HEADER -- do not remove this line
// Generated on 10/17/1999
Now we open our form in the resource editor and insert the define statements right at the top. Also we need to replace every text value with the correct resource value. Thus:
class langForm of FORM
scaleFontBold = false
height = 16.5
left = 16.5714
top = 0
width = 76.4286
text = resource (form_header,_app.langdll)
this.TEXT1 = new TEXT(this)
height = 1.9091
left = 3.8571
top = 0.5455
width = 66.5714
colorNormal = "0x4000/BtnFace"
alignVertical = 1 // Middle
alignHorizontal = 1 // Center
fontSize = 25
fontBold = true
text = resource (form_title,_app.langdll)
And so on until all have been replaced.
IMPORTANT NOTE: Keep in mind that if you are going to edit this form from within the form designer, the form designer will stream the string values from the resource file so that instead of text = resource (form_title,_app.langdll), you will have text = “Your string” To overcome this, which might seem like a bit of work at the beginning, you would need to place all text replacements in something like the form's onopen event. But I leave that up to you to experiment with to see what suits your needs.
The Setup Form.
To make it easier for the user we can create a setup form that has, perhaps, radiobuttons representing the different available languages. The user then chooses the desired language and the runs your app. Everything is then represented in his/her language. At the same time a different user might access your app wanting it to be presented in his/her own language.
Note: All the data will remain as it was entered. So if it was entered in English it will remain in English. Only the text for the objects will be changed.
In the onClick event of a run button we can evaluate the radiobuttons and place the result in a variable for our app to use .e.g.
case form.radiobutton1.value = true
case form.radiobutton2.value = true
So if the user chose the English version the form would look like this:
And if the user chose the Afrikaans version his/her form would look like this:
Voilà not too bad eh. Well its fast and there are so many possibilities that my mind just races thinking of the uses. Oh, click on the first pushbutton in both versions for a surprise. Study the code to see how it is accomplished. It's very simple actually. NOTE: Make it clear that it is necessary to run the self-extracting EXE file, and then open the WFMs in Visual dBASE 7.x. Furthermore, it is necessary to run the Setup.wfm.
Don't think that this is the only way, or even the best way to do this, but it works for me, looks professional and pleases the users. And yes I know that it may seem to be a big job at first; but if planned correctly can save much time and frustration.