- Qt 6.11
- Qt Linguist Manual
- Developers
- Text ID based translations
- Qt 6.11.2 Reference Documentation

TS file format ◦ Meta strings reference

# Text ID based translations

The text ID translation mechanism is an industrial strength system for internationalization and localization. Each text in the application has a unique identifier (text ID) that you use in the source code instead of text. This makes it much easier to manage large numbers of translated texts.

## Internationalizing with text IDs

When using text IDs instead of plain text, the general method of internationalizing an application is the same but the details are a bit different:

1. The functions and macros for the text-ID-based translation system are different from the plain-text system. You use the qsTrId() function instead of qsTr(), the QT_TRID_NOOP() macro instead of QT_TR_NOOP(), and QT_TRID_N_NOOP() macro instead of QT_TR_N_NOOP()).
2. Use text IDs as user interface strings rather than plain text strings. For example, `qsTrId("id-back-not-front")`
3. You cannot specify a context parameter with a text ID, and therefore identically spelled words with different meanings need separate text IDs. For example, `qsTrId("id-back-backstep")` differentiates the back-step Back from the `id-back-not-front` Back.
4. Since context names are not allowed for text-ID-based translations, Qt Linguist lists the IDs in a file without context names.
5. The engineering English text that you see in the user interface for development builds is indicated with a `//%` comment. If you do not include this, the text ID is shown in the user interface. This is especially important when you have texts with parameters. The `//%` comment needs to include the parameters indicators in the string. For example, `//% "Number of files: %1"`
6. The `//:` comments that provide extra information to the translator are optional in the plain-text system. However, with the text-ID-based system, this extra information becomes essential because without it you only have the text ID and the translator might not be able to make a sensible translation from that without further context. You can use long descriptive text IDs and no comments, but comments are often easier to understand.

The side-by-side code snippets below show a comparison of text-ID -based and plain-text-based translations:

text-ID-based | plain-text-based
--- | ---
     Text {
         id: backTxt;
         //: The back of the object, not the front
         //% "Back"
         //~ Context Not related to back-stepping
         text: qsTrId("id-back-not-front");
     }
     Text {
         id: backTxt;
         //: The back of the object, not the front
         //~ Context Not related to back-stepping
         text: qsTr("Back","Not front")
     }

## Localizing with text IDs

Localizing with text IDs follows much the same process as for plain text.

You use the lupdate tool to generate the TS files where you add the translations. The source values in the translation files will be text IDs rather than plain text, and therefore you need either descriptive text IDs or good additional comments, or both to ensure that the translations are accurate.

The example text-ID-based user interface text from above results in the following content in the .ts file:

     <message id="id-back-not-front">
         <source>Back</source>
         <extracomment>The back of the object, not the front</extracomment>
         <translation type="unfinished"></translation>
         <extra-Context>Not related to back-stepping</extra-Context>
     </message>

If there is no translation available for a given text (which is generally the case until late in development), the text ID will be shown in the user interface rather than a proper text. In order to make the application more usable for testing, you can make `lrelease` use the Engineering English source text (from the `//%` comments) as the translated text and mark it with some indicator, such as an exclamation mark (!), so you can see texts that are not yet translated.

### Grouping ID-Based Translations

You can assign each ID-based translation a label to organize ID-based entries of large projects into smaller groups. To assign a label to an ID-based entry, add a `//@` comment naming the label, for example in C++:

     //% "Open file"
     //@ FileOperations
     qtTrId("msg.open");

Or in QML:

     //% "Open file"
     //@ FileOperations
     qsTrId("msg.open");

When you open the TS file in Qt Linguist, the ID-based entries with the same label are grouped together, similar to text-based entries that are grouped by context. Any item without a label appears under `<unnamed label>`.

Note: Label names have no effect on lookup or uniqueness: IDs remain globally unique and can still be loaded via `qtTrId("msgid")` without referencing a label. The label tag is used only to improve the translator's navigation and does not change runtime behavior.

#### Automatic Label Generation

Instead of manually specifying label names, you can use placeholders to automatically generate labels based on the code structure:

- `//@ <context>` - Automatically uses the full context (namespace::class)
- `//@ <class>` - Automatically uses only the class name (without namespaces)
- `//@ <file>` - Automatically uses the source filename

##### Combining Placeholders

Placeholders can be combined with custom text to create more descriptive labels:

- `//@ <file>:<class>` - Combines filename and class: `filehandler.cpp:FileHandler`
- `//@ <context>_customSuffix` - Adds suffix: `MyApp::FileHandler_customSuffix`
- `//@ module_<file>_<class>-label` - Custom prefix and suffix: `module_filehandler.cpp_FileHandler-label`
- `//@ <context>:<file>` - Context with file: `MyApp::FileHandler:filehandler.cpp`

For example, in C++:

     namespace MyApp {
     class FileHandler : QObject {
         Q_OBJECT
         void open() {
             //% "Open file"
             //@ <context>
             qtTrId("msg.open"); // Label: MyApp::FileHandler

             //% "Save file"
             //@ <class>
             qtTrId("msg.save"); // Label: FileHandler

             //% "Export"
             //@ <file>
             qtTrId("msg.export"); // Label: filehandler.cpp
         }
     };
     }

Or in QML:

     Item {
         id: myComponent
         Component.onCompleted: {
             //% "Loading"
             //@ <context>
             qsTrId("msg.loading") // Label: <component-name>

             //% "Ready"
             //@ <file>
             qsTrId("msg.ready") // Label: main.qml

             //% "Initialized"
             //@ <file>:<context>-state
             qsTrId("msg.init") // Label: main.qml:<component-name>-state
         }
     }

Auto labels are particularly useful in large projects where manually managing label names across many files can be tedious. They ensure consistent grouping based on code structure and give hints to translators about where the translation will be used.

Note: When using `<class>` in C++ code outside any classes, a warning will be issued and `<unnamed>` will be used in the generated label.

Note: Using `<class>` in QML will generate a warning and `<unnamed>` will be used, since QML components are not classes. Use `<context>` instead to get the component name, or `<file>` to get the QML filename.

Note: Auto labels only work with ID-based translations (`qtTrId`, `qsTrId`). Using auto labels with text-based translations (`tr`, `qsTr`) will generate a warning and the label will be ignored.

## CMake configuration

When building with CMake, use the prefix `qml_` for .ts files. For example, `qml_en.ts`. In the CMakeLists.txt file, add the qt_add_translations function, where you list the *.ts files as values of `TS_FILES`, and set the value of RESOURCE_PREFIX to the URI of the main.qml file for the project followed by /i18n:

     qt_add_translations(${CMAKE_PROJECT_NAME}
         TS_FILES i18n/qml_de_DE.ts i18n/qml_en_US.ts
         RESOURCE_PREFIX Main/i18n
     )

## Advanced use with qmake

For projects that target a large number of locales, you can remove the TRANSLATIONS info from the .pro file and, instead, manage the translations with a separate script. The script can call `lrelease` and `lupdate` for each of the desired targets.

The updates could be scripted something like this:

     lupdate -recursive <project-dir> -ts <project-dir>/i18n/myapp-text_en_GB.ts
     lupdate -recursive <project-dir> -ts <project-dir>/i18n/myapp-text_en_US.ts
     ...

The generation of the final .qm files could be scripted something like this:

     lrelease <project-dir>/i18n/myapp-text_en_GB.ts
     lrelease <project-dir>/i18n/myapp-text_en_US.ts
     ...

TS file format ◦ Meta strings reference