# Chapter 2: Two Custom Widgets

We will now see a more complex example, with several contexts and several texts in each context. And we'll learn what a context is.

- arrowpad.h contains the definition of ArrowPad, a custom widget;
- arrowpad.cpp contains the implementation of ArrowPad;
- mainwindow.h contains the definition of MainWindow, a QMainWindow subclass;
- mainwindow.cpp contains the implementation of MainWindow;
- main.cpp contains main().

We will provide several translations, and define "several" as meaning "two". Namely, we will provide a French and a Dutch translation. The relevant lines of tt2.pro are

        HEADERS         = arrowpad.h \
                          mainwindow.h
        SOURCES         = arrowpad.cpp \
                          main.cpp \
                          mainwindow.cpp
        TRANSLATIONS    = tt2_fr.ts \
                          tt2_nl.ts

Now run lupdate. You should get two identical message files (tt2_fr.ts and tt2_nl.ts).

## Line by Line Walk-Through

### arrowpad.h

        class ArrowPad : public QGrid
        {
            Q_OBJECT

ArrowPad is a subclass of QWidget. On the screenshot above, the central widget with the four buttons is an ArrowPad.

"ArrowPad" is also a context: It is the context of the texts in the ArrowPad class. The Q_OBJECT macro defines `tr(x)` in ArrowPad like this:

        qApp->translate( "ArrowPad", x )

Knowing which class each text occurs enables the Qt Linguist to show the right texts together, which in turn eases the translation of the program.

### arrowpad.cpp

            skip();
            (void) new QPushButton( tr("&Up"), this );
            skip();
            (void) new QPushButton( tr("&Left"), this );
            skip();
            (void) new QPushButton( tr("&Right"), this );
            skip();
            (void) new QPushButton( tr("&Down"), this );
            skip();

Here, we use ArrowPad::tr() four times.

### mainwindow.h

        class MainWindow : public QMainWindow
        {
            Q_OBJECT

In the screenshot above, the whole window is a MainWindow. Here too, we use Q_OBJECT, so that MainWindow will become a context in the Qt Linguist.

### mainwindow.cpp

        MainWindow::MainWindow( QWidget *parent, const char *name )
            : QMainWindow( parent, name )
        {
            ArrowPad *ap = new ArrowPad( this, "arrow pad" );
            setCentralWidget( ap );

In the MainWindow constructor, we create an ArrowPad.

            QPopupMenu *file = new QPopupMenu( this );
            file->insertItem( tr("E&xit"), qApp, SLOT(quit()),
                              QAccel::stringToKey(tr("Ctrl+Q")) );

Here we call MainWindow::tr() twice. Notice the use of QAccel::stringToKey() to support different keys in other languages. "Ctrl+Q" is a good choice for Quit in English, but a Dutch translator might want to use "Ctrl+A" (for Afsluiten) or a German translator "Strg+E" (for, strangely, Beenden).

### main.cpp

            QTranslator tor( 0 );
            tor.load( QString("tt2_") + QTextCodec::locale(), "." );
            a.installTranslator( &tor );

Here, we determine which translation to use according to the current locale. QTextCodec::locale() can be influenced by setting the `LANG` environment variable, among others.

## Translating to French and Dutch

Let's translate the example application to French first. Start Qt Linguist with tt2_fr.ts. You should get the seven source texts ("&Up", "&Left", etc.) grouped in two contexts ("ArrowPad" and "MainWindow").

Now, enter the following translations:

- ArrowPad
  - &Up - &Haut
  - &Left - &Gauche
  - &Right - &Droite
  - &Down - &Bas
- MainWindow
  - E&xit - &Quitter
  - Ctrl+Q - Ctrl+Q
  - &File - &Fichier

(Use the "Finish & Next" button to mark the current item as finished and to start editing an unfinished item.)

Save the file and do the same for Dutch:

- ArrodPad
  - &Up - &Boven
  - &Left - &Links
  - &Right - &Rechts
  - &Down - &Onder
- MainWindow
  - E&xit - &Afsluiten
  - Ctrl+Q - Ctrl+A
  - File - &Bestand

Now, we have to convert t1_fr.ts and t1_nl.ts into .qm files. We could use Qt Linguist for that, as in chapter one, but then we would miss an opportunity to use the lrelease utility. lrelease produces .qm files from .ts files without user interaction. It's chiefly meant for use in makefiles.

Type

        lrelease tt2.pro

You should now have both tt2_fr.qm and tt2_nl.qm. Set the `LANG` environment variable to `fr`. In Unix, one of the two following commands should do it:

        export LANG=fr
        setenv LANG fr

In Windows, either modify autoexec.bat or run

        set LANG=fr

When you run the program, you should now get

Now, try the same with Dutch, by setting `LANG=nl`. You should get

## Exercices

Remove the Q_OBJECT macro from the header files, run lupdate and look at the result in Qt Linguist. What's different now?

Set `LANG=fr_CA` (French Canada) and run the example program again. Explain why the result is the same as with `LANG=fr`.

Let's go on to chapter three.

[Previous tutorial] [Next tutorial] [Main tutorial page]