OpenCOR is a multilingual application, currently supporting both English and French. By default, it will try to use the system language, but if it is not supported, then English will be used instead. Alternatively, any of the languages supported by OpenCOR can be used.

OpenCOR

There are two sets of language files to consider:

  1. Qt-specific translation files: these files are originally located under [Qt]/[QtVersion]/[CompilerVersion]/translations/qtXXX_xx.qm with XXX the type of translations (e.g. base) and xx the language code (e.g. fr for French). For every language supported by OpenCOR (except for English, which is natively supported by Qt), we need to add the corresponding Qt-specific translation files to [OpenCOR]/res/translations. Then, an entry for those files must be added to [OpenCOR]/res/translations.qrc:
    <file>translations/qt_help_xx.qm</file>
    <file>translations/qtbase_xx.qm</file>
    <file>translations/qtxmlpatterns_xx.qm</file>
  2. OpenCOR-specific translation files: for each supported language (again, except for English), a file called [OpenCOR]/i18n/OpenCOR_xx.ts must be created. The best way to go about it is by starting from an existing language file (e.g. [OpenCOR]/i18n/OpenCOR_fr.ts). From there, locate the following line:
    <TS version="2.1" language="xx_XX" sourcelanguage="en_GB">
    and replace xx_XX accordingly. Otherwise, as for the Qt-specific file above, an entry for the OpenCOR-specific translation file must be added to [OpenCOR]/res/i18n.qrc.in (PROJECT_BUILD_DIR is automatically replaced during the build process):
    <file alias="app_xx">${PROJECT_BUILD_DIR}/OpenCOR_xx.qm</file>
    OpenCOR_xx.qm gets automatically generated from OpenCOR_xx.ts when building OpenCOR. This does, however, require updating [OpenCOR]/cmake/common.cmake as follows:
    MACRO(UPDATE_LANGUAGE_FILES TARGET_NAME)
        ...
        SET(LANGUAGES ... xx ...)
        ...
    )

On the graphical user interface (GUI) side of OpenCOR, both an action and a menu item must be created for each supported language. The best way to add GUI support for a new language is by mimicking what has been done for actionEnglish in [OpenCOR]/src/mainwindow.ui. Then, a similar mimicking work must be done in [OpenCOR]/src/mainwindow.cpp and [OpenCOR]/src/mainwindow.h (look for actionEnglish and EnglishLocale).

Plugins

A file called [PluginName]_xx.ts must be created for each plugin that requires internationalisation and it must be located in [PluginName]/i18n (e.g. [OpenCOR]/src/plugins/miscellaneous/Core/i18n/Core_fr.ts; see here for more information on the TS file format):

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="xx_XX" sourcelanguage="en_GB">
<context>
</context>
</TS>

A [PluginName]_i18n.qrc.in file must also be created in [PluginName]/res (e.g. [OpenCOR]/src/plugins/miscellaneous/Core/res/Core_i18n.qrc.in; PLUGIN_NAME and PROJECT_BUILD_DIR are automatically replaced during the build process):

<RCC>
    <qresource prefix="/">
        <file alias="${PLUGIN_NAME}_xx">${PROJECT_BUILD_DIR}/${PLUGIN_NAME}_xx.qm</file>
    </qresource>
</RCC>

A plugin requires a plugin class and for internationalisation to be supported, it needs to inherit from I18nInterface, as well as reference OpenCOR::I18nInterface and include the i18ninterface.inl file (e.g. [OpenCOR]/src/plugins/miscellaneous/Core/src/coreplugin.h):

...
class [PluginName]Plugin : ..., public I18nInterface, ...
{
    ...
    Q_INTERFACES(OpenCOR::I18nInterface)
    ...

public:
...
#include "i18ninterface.inl"
...
};
...

The internationalisation interface has only one method that needs to be implemented (e.g. [OpenCOR]/src/plugins/miscellaneous/Core/src/coreplugin.cpp):

...
//==============================================================================
// I18n interface
//==============================================================================

void [PluginName]Plugin::retranslateUi()
{
    ...
}
...

Qt objects (e.g. menus, actions) need to be retranslated either by the plugin class itself or by an object owned directly or indirectly by the plugin class (e.g. [OpenCOR]/src/plugins/miscellaneous/Core/src/coreplugin.cpp). To help with this process, I18nInterface comes with two methods that ensure that menus and actions get properly retranslated: retranslateMenu(QMenu *pMenu, const QString &pTitle) and retranslateAction(QAction *pAction, const QString &pText, const QString &pStatusTip).

It may happen that a plugin does not own any Qt objects, but still needs to support internationalisation. This is the case with the EditorWidget plugin, which implements a Qt widget that can be both instantiated and retranslated by others. This means that its retranslateUi() method is empty (see [OpenCOR]/src/plugins/widget/EditorWidget/src/editorwidgetplugin.cpp).