International Language Support

PhAB has builtin support for applications that need to be translated into other languages.

This chapter includes:

By keeping a few design considerations in mind, and then following a few simple steps, your application can very easily be translated into other languages without the need to recompile or rebuild your application:

  1. PhAB generates a database of all the text strings used by your application.
  2. This text database is used by PhAB's language editor to allow you to translate each text string into another language.
  3. The translated text strings are saved in a translation file, and are shipped with your application.
  4. To run your application in another language, simply set an environment variable before starting the application. When PhAB's API builds the application windows, dialogs and other modules, it replaces the text strings with the new translations.

It's that simple.

Application design considerations

This section provides a few design considerations to assist you in creating a language-independent application. You should keep these ideas in mind as you are designing and implementing your application, since modifying the application after it's complete is more difficult.

Size of text-based widgets

Typically, when you design an application, you lay out the window using widgets that have the default application text already preset. For example, if you had a Done button at the bottom of a dialog window, the button itself would be only large enough to hold the text string “Done”. You would also place the Done button based on its current size. This works well in an application that doesn't require translation, but causes many problems for a language-independent application. What would happen if the translated text were 12 characters instead of the default of 4 characters?

For example, these buttons are too small to accommodate translated text:

bad buttons

The solution is simple. Make the button larger to accommodate longer translated text strings. Here's an example:

good buttons

Justification

In addition to making text-based widgets wider to accommodate translated text, you should give some thought to the justification of text, based on the widget's usage. For example, in a simple text entry field, it's quite common to place a label to the left side of the field. If you make the label wider to allow for translation, the label itself moves to the far left:

left justification

This problem is easily solved by setting the label's horizontal alignment to be right-justified. This allows for longer translated text strings, and still keeps a tight alignment with the text entry field:

right justification

Another common labeling method is to place a label centered above or within the border of a box. Usually the text is centered by placing it in the desired position based on its current text:

small title

When the text is later translated, it's either too short or too long, and the box label looks lopsided. The simple solution is to make the box title much wider than necessary, and set the horizontal alignment to be centered.

large title

There are probably many other cases similar to this but the important point is to think about how the translated text will effect the look of the application. A lot of aesthetics can be maintained simply by making text-based widgets wider and setting an appropriate justification.

Font height

The fonts for some languages, such as Japanese or Chinese, are only readable at large point sizes. For these fonts, the minimum size may be 14 points or even larger. If you've designed your entire application using a 10-point Helvetica font, you'll have lots of problems when all your text-based widgets are stretched 4 or more pixels taller to accommodate the larger fonts. If your application needs to be translated to other languages, look into the font requirements before you begin, and use this minimum font size in the default language built into the application.

If you really want to use the smaller font sizes for your default application text, you can borrow a tip from the previous section. You can make the height of widget larger and set the vertical alignment to center. However, this may not work well for text input fields, and you should keep this consideration in mind.

Hard-coded strings

Another major area for consideration is with informational, warning, error or any textual messages that are displayed in popup dialog windows or other points within the application. Examples include calls to PtAlert(), PtNotice(), and PtPrompt(). The most common way to handle text messages is to embed the text strings in the application code. For example:

char *btns[] = { "&Yes", "&No", "&Cancel" };

answer = PtAlert( base_wgt, NULL, NULL, NULL,
                  "File has changed. Save it?",
                  NULL, 3, btns, NULL, 1, 3,
                  Pt_MODAL );

While this is quick to code, it's impossible to translate without rewriting the application code, recompiling, and so on. Essentially, you need to create a complete new version of the application for each language supported.

A much better method is to take advantage of PhAB's message databases. Using a message database, you can put all your text messages into a single file and give each message a unique name. To retrieve the text at runtime, call ApGetMessage().

char *btns[3];
ApMsgDBase_t *textdb;
ApLoadMessageDB(textdb, "MyMessageDb");

btns[0] = ApGetMessage( textdb, "msgyes" );
btns[1] = ApGetMessage( textdb, "msgno" );
btns[2] = ApGetMessage( textdb, "msgcancel" );
answer = PtAlert( base_wgt, NULL, NULL, NULL,
                  ApGetMessage( textdb, "file_save_msg" ),
                  NULL, 3, btns, NULL, 1, 3,
                  Pt_MODAL );

ApCloseMessageDB(textdb);

An advantage of message databases is that they can be shared between applications. If several applications use the same types of strings, this can make translation easier.

See the section Message Databases below for more information.

Another method of storing strings is to use PhAB's widget databases. You can put all your text messages in a single (or multiple) widget database and give each message a unique name. To retrieve the text at runtime, call ApGetTextRes() (see the Photon Library Reference for details). In the PtAlert() example above, it would become:

char *btns[3];

btns[0] = ApGetTextRes( textdb, "@msgyes" );
btns[1] = ApGetTextRes( textdb, "@msgno" );
btns[2] = ApGetTextRes( textdb, "@msgcancel" );
answer = PtAlert( base_wgt, NULL, NULL, NULL,
                  ApGetTextRes( textdb, "@msg001"),
                  NULL, 3, btns, NULL, 1, 3,
                  Pt_MODAL );

This method allows the application to have no predefined text-based messages within it, and it can be easily translated. In addition, because the text strings are put into a widget database, PhAB automatically takes care of including the message texts when it generates the application's text string database. This may be more convenient than using a separate message database, especially if you only have a few strings, they're only used in one application, and the application has a widget database anyway.

Use of @ in instance names

By default, PhAB ignores widgets that have no instance name or have the instance set to the class name. This means if you place a label within a window and change the text to something appropriate, PhAB skips this widget when it generates code for your application. This is because PhAB assumes the label is constant and the application doesn't require access to it. However, when it comes to translating your application to another language, this label becomes very important.

To differentiate between widgets that are important for translation but not for code generation, PhAB recognizes a special character when placed in the first position of the instance name. This special character is the @ character. This means you can give a label the instance name of @label1, and PhAB will recognize this label when generating the text language database, but skip over it when generating code.

Instance name starting with @

This sounds fine, except PhAB also requires that all instance names be unique. This rule must be adhered to so that PhAB knows which text string to replace at run time. Unfortunately, dreaming up potentially hundreds of unique instance names that you don't really care about can be a lot of work. To simplify this task, PhAB lets you specify a single @ character for the instance name, and PhAB appends an internal sequence number to the end. This eliminates the need to keep track of all constant text strings that require instance names just for translation.

If you want to group translation text strings (say, by module), you can give them all the same instance name, and PhAB will append a sequence number to make the name unique. For example, if you assign the name @base to several widgets, PhAB generates @base, @base0, @base1, ... as instance names.

Bilingual applications

Sometimes it's necessary to design an application to be bilingual. This means two different languages are displayed in every text string. While this can be done, it's usually difficult for the user to read and understand.

PhAB allows you to use another approach. You can create the application in one language and provide the ability to flip to the other language within application control. This is done via a PhAB API function named ApSetTranslation(). This function (which is described in the Photon Library Reference) changes the current translation file for the application immediately, such that all future dialogs, windows, and so on are drawn using the new translation file.


Note: Any existing modules and widgets aren't translated, only new ones. If you want immediate feedback, you need to recreate the modules. This is easy for dialogs, but more difficult for the base window; remember that destroying the base window exits the application. One way to translate the contents of the base window is to put them in a picture module, which can be recreated.

Common strings

If you have several applications to translate, you can reduce the work by sharing the common text strings and translating them separately. To do this:

  1. Create a message database populated with the common text strings.
  2. Use the PhAB Language Editor to translate the strings.
  3. Once the database is created and translated, you can open it and use the strings in other applications using ApLoadMessageDB() and ApGetMessage().

Generating a language database

This is the easy part. The most important aspect to this step is to know when to generate the text string database. Ideally, you want to do this when all application development is complete. This is because the run-time translation mechanism is hinged on the widget's instance name. If you generate your database mid-way through the development and do the translations, it's quite likely that a lot of widgets will be changed or deleted, and translations may be deleted or the time wasted.

One exception to this would be bilingual applications. In this case, you might want to generate and translate the application continuously so that the application's translations can be tested throughout the development.

To generate an application's language database:

  1. Select the Project-->Language Editor-->Generate Language Database.
  2. A progress dialog appears, and the language database is generated.
  3. Click Done.

Note: The Languages item in the Application menu is disabled if you haven't saved your application for the first time and given it a name.

The database has now been generated and is ready for use with the PhAB Language Editor. The name of the database is app.ldb, where app is the name of the executable file for the application (which is typically the same as the name of the application, unless you've used the Save As command to rename the application). The language database is placed in the application's directory (where the abapp.dfn file is found).

Message databases

A message database is a file that contains textual messages. Each message is identified by a tag name.

To load a message database, call ApLoadMessageDB(). This function does the usual file search based on the ABLPATH environment variable and the current language:

To retrieve a message, given its tag, call ApGetMessage().

To close the message database, call ApCloseMessageDB().

You can create message databases using the PhAB message database utility phabmsg, located in the PhAB application directory. This utility allows you to create new message databases, and edit existing message databases.

Language editor

After the database has been generated, you can use PhAB's Language Editor to translate the default text strings to another language. The Language Editor is designed to work both as a stand-alone application that you can distribute with your application, or as an integrated part of PhAB itself.


language editor


PhAB Language Editor.

Starting the Language Editor within PhAB

When you are developing an application within PhAB, you can run the Language Editor using the current application's language database quite easily:

=>> Select Project-->Language Editor-->Run Language Editor.

This starts the Language Editor using the current application's language database. At this point, you can proceed to create a new translation file or edit an existing one.

Starting the Language Editor as a stand-alone application

If you plan to allow your application to be translated at a customer site, you'll need to include the following files with your application:

To start at the client site, you can:

Once phablang is started:

  1. Click on the Open Folder icon

    open folder icon

    to bring up the file selector.

  2. Using the file selector, find the application's xxx.ldb file.
  3. Open the xxx.ldb file.

Creating a new translation file

To create a new translation file:

  1. Click on the New button located at the bottom of the window. The Language Selection dialog is displayed:

    language selector


    Language Selection dialog.

  2. Choose the desired language from the list of supported language file types.
  3. Click on Add.
  4. At this point you're asked to reconfirm your selection. Click on Yes.

The Language Selection dialog closes, and you should now see the newly created translation file in the list of available translations.

Editing an existing translation file

To edit a translation file in the Translations list:

  1. Click on the desired language from the list.
  2. Click on the Open button.

The Text Translation Editor dialog appears. This editor displays all the text strings available for translation in the current language database.

Translating the text

To translate a text string:

  1. Click on the text string you want to translate. The selected text string is displayed in the text areas at the bottom of the window:

    translating text

    Default Text
    the default text bound into the application
    Translation
    the current translation (if any) for the text string. This is the area you use to type in the new translation.

    Note:
    • If you need to type characters that don't appear on your keyboard, you can use the compose sequences listed in Photon compose sequences in the Unicode Multilingual Support appendix.
    • You don't have to translate every string. If a translation isn't available, the default text is used.

    You can use the cut, copy, and paste buttons that are above the Translation area when editing the translations.

  2. Once you change the translated string, a green check mark and red X appear above the Translation area.
  3. Click on the green check mark to accept your changes (the shortcut is Ctrl-Enter).
  4. Click on the red X to cancel your changes.

Repeat the above steps for all the text strings you need to translate. When you're finished, click on the Save & Close button.

Hotkeys

One problem with translating an application is that the hotkey assignments no longer match up if the translated string doesn't include the accelerator key value. For this reason, PhAB adds the accelerator key strings to the language database too.

When translating the text string, the translator can also change the accelerator key. If the key used in the hotkey isn't a function key (i.e. the key code is less than 0xF000), PhAB automatically changes the hotkey to match the accelerator key.

For example, suppose your application has a button labeled Cancel. You'd set the button's Pt_ARG_ACCEL_KEY to be C, and arrange for Alt-C to invoke Pt_CB_HOTKEY.

When you generate the language database, you'll find that it includes the button's label and its accelerator key. If you translate the application into French, the button's label would become Annuler, so the hotkey Alt-C is no longer appropriate. Just translate the button's Pt_ARG_ACCEL_KEY to be A, and the hotkey automatically becomes Alt-A when you run the application in French.


Note: You'll need to make sure there are no duplicate accelerator keys. If it does happen by accident, only the first key defined is accepted.

Help resources

If you use the Photon Helpviewer for your application help and you plan on providing multiple language help files for your application, the translator can also translate the help topic paths to point to the correct positions within the corresponding help files.

Translation functions

You can build a custom language editor if the default one doesn't meet your needs. You'll find these functions (described in the Photon Library Reference) useful:

AlClearTranslation()
Clear all the translations in a language or message database
AlCloseDBase()
Close a language or message database
AlGetEntry()
Get an entry from a language or message database
AlGetSize()
Get the number of records in a language or message database
AlOpenDBase()
Load a language or message database
AlReadTranslation()
Read a translation file into a database
AlSaveTranslation()
Save translations from a language or message database
AlSetEntry()
Set the translated string for a database entry

You can use these functions to create your own language editor, or to convert a language database to a different file format (for example, so you can send the file to a non-Photon or non-QNX system for translation).

Running your application

After the language database is fully translated, the last step is to run the application.

When you create the translation files, they're placed in the same directory as your application's abapp.dfn file. You can think of these as the working versions of the files. When you run your application from PhAB, these are the versions you'll use.

When you run your application outside of PhAB, it looks for the translation files as follows:

  1. In the directories listed in the ABLPATH environment variable, if defined. This list takes the form:
    dir:dir:dir:dir
        

    Unlike the PATH environment variable, the current directory must be indicated by a period, not a space. A space indicates the directory where the executable is.

  2. In the same directory as the executable, if the ABLPATH environment variable isn't defined.

You can think of these as the production versions of the translation files.

In order for the PhAB API to know which translation file you want to use, you must set the ABLANG environment variable to one of the values below:

Language: Value:
Belgian French fr_BE
Canadian English en_CA
Canadian French fr_CA
Chinese zh_CN
Danish da_DK
Dutch nl_NL
French fr_FR
German de_DE
Italian it_IT
Japanese ja_JP
Korean (North) ko_KP
Korean (South) ko_KR
Norwegian no_NO
Polish pl_PL
Portuguese pt_PT
Slovak sk_SK
Spanish es_ES
Swedish se_SE
Swiss French fr_CH
Swiss German de_CH
UK English en_GB
USA English en_US

Note: This list is current at the time this document was written, but may have since been updated. For the latest version, see the file:

/usr/photon/appbuilder/languages.def


For example, to run an application in German (as spoken in Germany), you would do the following:

$ export ABLANG=de_DE
$ myapplication

Note: The application looks for the best match. For example, if the language extension specified is fr_CA, the search is as follows:
  1. Exact match (e.g. fr_CA).
  2. General match (e.g. fr).
  3. Wildcard match (e.g. fr*).

If no translation is found, the original text in the application is used.


The export command could be put in the user's login profile so that the application will run in each user's preferred language.

Distributing your application

When you ship your application to the customer site, you must make sure to include the translation files in your distribution list. For example, if your application is named myapp, and you have translation files for French and German, you would need to include the myapp.fr_FR and myapp.de_DE files with the application. These files must be located:

If you want each customer to be able to translate the application, you'll also need to distribute:

The language database and the translation files that the customer creates should be in: