Visual Basic Concepts

Localizing Controls

You can increase the market for your control component by localizing it. A localized control displays text — captions, titles, and error messages — using the language of the locale in which the control is used for application development, rather than the language of the locale in which it was authored.

This topic examines localization issues specific to ActiveX controls. General localization issues are discussed in "International Issues," in the Visual Basic Programmer's Guide.

Using the LocaleID

When you compile an executable with Visual Basic, the LocaleID (also referred to as the LCID) of the Visual Basic version is compiled in. Thus an application compiled with the German version of Visual Basic will contain &H0407, the LocaleID for Germany.

In the same way, the LocaleID is compiled into an ActiveX control component created with Visual Basic. This becomes the default LocaleID for your controls. If this were the end of the story, you would have to compile a new version of your component, using the correct version of Visual Basic, for every locale in which you wanted to distribute it.

Fortunately, control components are more flexible than compiled applications. Your component can be used with versions of Visual Basic for any locale, and even with development tools that support other locales, because they can determine the correct LocaleID at run time.

Discovering the LocaleID

The LocaleID property of the AmbientProperties object returns the LocaleID of the program your control was used in. The AmbientProperties object is available as a property of the UserControl object, as described in "Using the AmbientProperties Object to Stay Consistent with the Container," earlier in this chapter.

You can test the Ambient property as soon as an instance of your control is sited on the container; that is, in the InitProperties or ReadProperties events. Once you know the LocaleID, you can call code to load locale-specific captions, error message text, and so forth from a resource file or satellite DLL, as described later in this topic.

You need to call this code in both events, because the InitProperties event occurs only when a control instance is first placed on a container. Thereafter the control instance receives the ReadProperties event instead, as discussed in "Understanding Control Lifetime and Key Events," earlier in this chapter.

You should also call your locale code in the AmbientChanged event, because your control could be used in an application that resets its locale according to Windows Control Panel settings, which can be changed by the user at any time. Your control could also receive AmbientChanged events if it's used as a constituent control, as described later in this topic.

Avoid Accessing Constituent Controls in the Initialize Event

The constituent controls on your UserControl discover the LocaleID by checking the AmbientProperties object which the UserControl, like any good container, makes available to them. This happens automatically, with no effort on your part.

When the Initialize event occurs, your control has been created, and all the constituent controls have been created and sited on your control's UserControl object. However, your control has not yet been sited on the container, so the UserControl cannot supply the correct LocaleID to the constituent controls.

If code in the Initialize event accesses the properties and methods of the constituent controls, their responses will reflect the LocaleID of the version of Visual Basic you used to compile your component, rather than the LocaleID of the application in which your control is compiled. For example, a method call might return a string in the wrong language.

To avoid this, you should not access constituent controls in the Initialize event.

Responding to the AmbientChanged Event

The AmbientChanged event occurs whenever an Ambient property changes on the container your control has been placed on, as discussed in "Using the AmbientProperties Object to Stay Consistent with the Container," earlier in this chapter.

Applications compiled with Visual Basic use the LocaleID of the version of Visual Basic that compiled them. However, your control could be used in an application written using a development tool such as Microsoft Visual C++, in which it is possible to change an application's LocaleID in response to system messages.

For example, if a user opens the Control Panel and changes the locale, an application would receive a notification of the change, and reset itself accordingly. Your controls can handle this situation by including code to change locale dynamically, as in the following example.

Private Sub UserControl_AmbientChanged( _
      PropertyName As String)
   Select Case PropertyName
      Case "LocaleID"
         ' Code to load localized captions,
         ' messages, and so forth from a resource
         ' file or satellite DLL, as described below.

      ' Case statements for other properties.

   End Select
End Sub

A change in the locale can also occur if you use your control as a constituent of another control. As described above, constituent controls don't get the correct LocaleID when they're first sited on a UserControl object. When the outermost control has been sited on the application's form, all the constituent controls will receive AmbientChanged events with the correct LocaleID.

Base Language and Satellite DLLs

The most flexible localization technique is to compile your control component with the default text strings and error messages in the language of the locale you expect to be your largest market. Place text strings and error messages for other locales in satellite ActiveX DLLs, one for each locale.

This scheme makes your component very attractive to developers who create versions of their programs for multiple languages, because they can work with multiple locales on one development machine.

Satellite DLLs are also attractive to users in multilingual countries. Such users may have programs compiled by programmers in different locales; if two such programs use your control component, satellite DLLs allow both programs to coexist on a user's computer.

Important   Your control should not raise an error if the requested satellite DLL is not found, as this could cause the entire application to fail. In the event the satellite DLL is not available, simply use the default locale in which your control component was built.

Naming Satellite DLLs

If you use an open-ended naming convention for your satellite DLLs, you can supply additional DLLs later without recompiling your program. An example of such a naming convention would be to include the LocaleID in the DLL names. Using this convention, your satellite DLLs for the Belgian French, German, and US English locales might be named MyControls20C.dll, MyControls407.dll and MyControls409.dll.

If you use Windows API calls to load and extract resources from your satellite DLLs, you can create the name of the DLL to be loaded by converting the LocaleID to a string, and appending it to a base name. (Note that the examples above use the hexadecimal representation of the LocaleID.)

As an alternative to using API calls, you can build your satellite DLLs as Visual Basic ActiveX DLL projects. To do this, create a class module with methods for retrieving resources. Give the class a name such as Localizer. Add this class module to each DLL project.

Use your open-ended naming scheme for the Project Name, so that each DLL has a unique programmatic ID, or ProgID, in the Windows registry. Each time you compile a new satellite DLL, you create a new Localizer class, whose full programmatic ID includes the Project Name of the DLL.

In your ActiveX control project, you can then create an instance of the appropriate Localizer class using code such as the following:

Dim strProgID As String
Dim objLoc As Object
' Generate the ProgID of the Localizer object
' for the appropriate satellite DLL.
strProgID = "MyControls" & Hex$(AmbientProperties.LocaleID) _
   & ".Localizer"
Set objLoc = CreateObject(strProgID)
If objLoc Is Nothing Then
   ' Satellite DLL not found; use default locale.
Else
   ' Call methods of Localizer object to retrieve
   ' localized string and bitmap resources.
End If

The code above uses late binding (that is, the variable objLoc is declared As Object). You can get better performance with early binding, by using the Implements feature of Visual Basic. Instead of making the resource retrieval methods members of the Localizer class, you can define them in an abstract class named IResources.

In your Localizer class, use the Implements statement to implement IResources as a second interface. You can call the methods of this interface with early binding, as shown below.

' Early-bound variable for IResources interface.
Dim ires As IResources
' Get the IResources interface of the Localizer
' object obtained from the satellite DLL.
Set ires = objLoc
' Call the methods of your IResources interface
' to retrieve localized resources.
Set cmdOK.Caption = ires.GetString(ID_CMDOK)

As with the late-bound Localizer object, you can simply add the Localizer class module, with its second interface, to each satellite DLL project. The ability to add the same interface to several different classes is called polymorphism.

For More Information   The Implements feature is discussed in "Providing Polymorphism by Implementing Interfaces," in "General Principles of Component Design."

Accessing satellite DLLs is discussed in "International Issues," in the Visual Basic Programmer's Guide.

Adding resource files to Visual Basic projects is discussed in "More About Programming," in the Visual Basic Programmer's Guide.

Resource Files

An alternative to satellite DLLs is to place text strings and error messages in a resource file, and compile the file into your control component. There are disadvantages to this technique.

  • If you use one resource file for each locale, you must compile a separate .ocx file for each locale. To avoid file name conflicts, you can put a locale indicator in the name of each .ocx file, as for example MyControlsDE.ocx for German, or MyControlsFR.ocx for French.

  • Unfortunately, you cannot avoid type library name conflicts so easily. A developer can have only one locale version of your control installed at a time. This may be a drawback in multilingual markets.

  • Although you can avoid the problem of compiling multiple .ocx files by putting the text and error message strings for all locales into a single resource file, the result will be a much larger .ocx file, and you will have to recompile the component to add support for new locales.

Localizing Interfaces

If you localize property, method, and event names, you must compile a separate version of your control for each locale. To allow multiple versions of your control to coexist on one computer, you must use a different name for your control in each version.

As a result of the different interface and control names, multilingual developers will have to rewrite their code for each language version of a program that uses your control. This will make your control component less attractive to such developers.

Microsoft applications, such as Visual Basic, do not localize interface member names.

Localizing Property Pages

Microsoft applications localize property pages, but not property names. If you use this scheme, the caption that shows up on a property page may not match the name of the property in the Properties window.

When localizing captions for properties on property pages, take care to select captions that make it obvious what the property is. Alternatively, you may wish to include the property name in parentheses.

For principles of form layout that simplify localization, see "International Issues," in the Visual Basic Programmer's Guide.

Localizing Type Library Information

There is no way to retrieve a browser string from a localized DLL or resource file, so browser strings must be compiled into your type library. To produce localized type libraries, you must use the Procedure Attributes dialog box to change the browser strings for all your properties, methods, and events. You must then re-compile your executable.

Localizing type library information thus limits your ability to localize using satellite DLLs. You may wish to leave your browser strings in the default language of your control.

For More Information*   See "International Issues," in the *Visual Basic Programmer's Guide.