Adding translations to your application

Ubuntu Touch is a global community with multiple languages and dialects. This can make the task of building an app daunting, because whether you are building from scratch, or porting/forking and existing application, you want to maximize the number of available users through available languages. This guide will outline how you add the ability to have translations added to your app if it doesn’t have that function built into it already.

Note: If you are a translator, looking for information on how to translate an app that already has this feature built into the app, please see the Translations documentation, under contributing.

How does a text get translated (technical background)

While this section already exists in the Translations documentation, under contributing, it is paramount that you understand the technical background before you proceed with adding it to your application.

Most apps running on Ubuntu Touch use GNU gettext for translations. A .pot template file holds an apps strings. From this template for every language an individual .po file is derived. Those .po files do contain the actual translations. To add a new language a new .po file needs to be created. To translate a string the .po file needs to be edited. Next to the original string (english in most cases) the appropriate translation needs to be entered following the .po gettext file syntax. Some apps do use QtLinguist for translation. It does follow the same principles only using .ts files for translations with another syntax. As template one .ts file is used.

Within an app strings are marked either this way i18n.tr("string") when using gettext or like this tr("string") when using QtLinguist. When the app is built, those translatable strings are extracted and written into the .pot or .ts template files. The .po and .ts language files then need to be updated from their template.

Une fois que les chaînes ont été traduites, elles sont mises à la disposition des utilisateurs/trices avec une nouvelle version d’une application.

What does a developer need to do when building a new app for Ubuntu Touch?

The recommended way to build any application for Ubuntu Touch from the ground up, is to use one the clickable templates with the command clickable create. These templates already include the ability to have translations built into the app and will require minimal effort on your part to facilitate the use of translations with your app. If you start with one of the clickable templates, then as you build your app, each string of text should be marked either this way i18n.tr("string") when using gettext or like this tr("string") when using QtLinguist. Here is an example of a button that uses the proper marking:

Button {
        id: menuNewGame
        text: i18n.tr("New Game")
        gradient: LomiriColors.orangeGradient
        anchors {
                margins: units.gu(2)
                centerIn: parent
        }

        onClicked: {
        aNewGame()
        }
}

Notice the text: field has the i18n.tr("string") marking for the sring of "New Game". When building the application, the CMakelist.txt files have a call to gettext, which will update the language files in the ./po directory for this entry. It will look like this:

#: ../components/Menu.qml:196
msgid "New Game"
msgstr ""

Using the clickable templates will ensure that every time you edit and build your new application, the .po and .ts files will be automatically updated with all available strings in your application.

How do you add translations to an existing application?

If you already started building an application without using the clickable templates, or perhaps are porting an existing application, or even updating an older Ubuntu Touch application for newer versions of Ubuntu Touch, it likely will not have this translation building feature in the existing build code. That is okay, as this gettext and translation functionality can be added to the existing application.

First, create a folder in the root directory of your application called po. Within that directory, create a file called CMakeLists.txt, and populate it with this information to call gettext:

 1     include(FindGettext)
 2     find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
 3
 4     set(DOMAIN ${FULL_PROJECT_NAME})
 5     set(POT_FILE ${DOMAIN}.pot)
 6     file(GLOB PO_FILES *.po)
 7
 8     # Creates the .pot file containing the translations template
 9     add_custom_target(${POT_FILE} ALL
10             COMMENT "Generating translation template"
11             COMMAND ${INTLTOOL_EXTRACT} --update --type=gettext/ini
12                     --srcdir=${CMAKE_SOURCE_DIR} ${DESKTOP_FILE_NAME}.in
13
14     COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} -o ${POT_FILE}
15             -D ${CMAKE_CURRENT_SOURCE_DIR}
16             -D ${CMAKE_CURRENT_BINARY_DIR}
17             --from-code=UTF-8
18             --c++ --qt --language=javascript --add-comments=TRANSLATORS
19             --keyword=tr --keyword=tr:1,2 --keyword=ctr:1c,2 --keyword=dctr:2c,3 --keyword=N_ --keyword=_
20             --keyword=dtr:2 --keyword=dtr:2,3 --keyword=tag --keyword=tag:1c,2
21             --package-name='${DOMAIN}'
22             --sort-by-file
23             ${I18N_SRC_FILES}
24     COMMAND ${CMAKE_COMMAND} -E copy ${POT_FILE} ${CMAKE_CURRENT_SOURCE_DIR})
25
26     # Builds the binary translations catalog for each language
27     # it finds source translations (*.po) for
28     foreach(PO_FILE ${PO_FILES})
29     get_filename_component(LANG ${PO_FILE} NAME_WE)
30     gettext_process_po_files(${LANG} ALL PO_FILES ${PO_FILE})
31     set(INSTALL_DIR ${CMAKE_INSTALL_LOCALEDIR}/share/locale/${LANG}/LC_MESSAGES)
32     install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG}.gmo
33             DESTINATION ${INSTALL_DIR}
34             RENAME ${DOMAIN}.mo)
35     endforeach(PO_FILE)

Next, you will need to add this code to your main CMakeLists.txt file of your project:

 1     add_subdirectory(po)
 2
 3     file(GLOB_RECURSE I18N_SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/po *.qml *.js)
 4     list(FILTER I18N_SRC_FILES EXCLUDE REGEX "${CMAKE_CURRENT_SOURCE_DIR}/build/.*" )
 5     list(APPEND I18N_SRC_FILES ${DESKTOP_FILE_NAME}.in.h)
 6
 7     find_program(INTLTOOL_MERGE intltool-merge)
 8     if(NOT INTLTOOL_MERGE)
 9     message(FATAL_ERROR "Could not find intltool-merge, please install the intltool package")
10     endif()
11     find_program(INTLTOOL_EXTRACT intltool-extract)
12     if(NOT INTLTOOL_EXTRACT)
13     message(FATAL_ERROR "Could not find intltool-extract, please install the intltool package")
14     endif()
15
16     add_custom_target(${DESKTOP_FILE_NAME} ALL
17     COMMENT "Merging translations into ${DESKTOP_FILE_NAME}..."
18     COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${CMAKE_SOURCE_DIR}/${DESKTOP_FILE_NAME}.in ${DESKTOP_FILE_NAME}
19     COMMAND sed -i 's/${PROJECT_NAME}-//g' ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE_NAME}
20     )
21
22     install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE_NAME} DESTINATION ${DATA_DIR})

You may need to add some clarifying information at the top of your project’s CMakeLists.txt file as well:

1     set(PROJECT_NAME "Your project")
2     set(FULL_PROJECT_NAME "Your.project")
3     set(CMAKE_INSTALL_PREFIX /)
4     set(DATA_DIR /)
5     set(DESKTOP_FILE_NAME ${PROJECT_NAME}.desktop)

All that remains now is to mark the text within your app with the i18n.tr(« string ») or tr(« string ») markers. You can simply edit existing text fields by adding these markers, as in this button example:

Button {
id: returnToGame
text: i18n.tr("Return to game")
gradient: LomiriColors.orangeGradient
visible: false
anchors {
    margins: units.gu(2)
    top: menuSave.bottom
    horizontalCenter: parent.horizontalCenter
}

onClicked: {
        mainView.menu = false;
        }
}

Where we edited the text: field to have the i18n.tr("string") marking for the string of "Return to game". Now when you run the build process, cmake will automatically call the gettext program and read through all of the *.qml and *.js files (in this example), and create the needed .pot file for your application, which translators can now use to translate your application!

If you need further examples of making these edits, you can look at these commits from CitySim to help you:

Reorganize for translation capability

Success adding translation capabilities

Added translations to all texts