.. _adding-translation: 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. Once strings have been translated, they are made available for users with a new release of an app. 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: .. code-block:: QML 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: .. code-block:: #: ../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``: .. code-block:: CMAKE :linenos: include(FindGettext) find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext) set(DOMAIN ${FULL_PROJECT_NAME}) set(POT_FILE ${DOMAIN}.pot) file(GLOB PO_FILES *.po) # Creates the .pot file containing the translations template add_custom_target(${POT_FILE} ALL COMMENT "Generating translation template" COMMAND ${INTLTOOL_EXTRACT} --update --type=gettext/ini --srcdir=${CMAKE_SOURCE_DIR} ${DESKTOP_FILE_NAME}.in COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} -o ${POT_FILE} -D ${CMAKE_CURRENT_SOURCE_DIR} -D ${CMAKE_CURRENT_BINARY_DIR} --from-code=UTF-8 --c++ --qt --language=javascript --add-comments=TRANSLATORS --keyword=tr --keyword=tr:1,2 --keyword=ctr:1c,2 --keyword=dctr:2c,3 --keyword=N_ --keyword=_ --keyword=dtr:2 --keyword=dtr:2,3 --keyword=tag --keyword=tag:1c,2 --package-name='${DOMAIN}' --sort-by-file ${I18N_SRC_FILES} COMMAND ${CMAKE_COMMAND} -E copy ${POT_FILE} ${CMAKE_CURRENT_SOURCE_DIR}) # Builds the binary translations catalog for each language # it finds source translations (*.po) for foreach(PO_FILE ${PO_FILES}) get_filename_component(LANG ${PO_FILE} NAME_WE) gettext_process_po_files(${LANG} ALL PO_FILES ${PO_FILE}) set(INSTALL_DIR ${CMAKE_INSTALL_LOCALEDIR}/share/locale/${LANG}/LC_MESSAGES) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG}.gmo DESTINATION ${INSTALL_DIR} RENAME ${DOMAIN}.mo) endforeach(PO_FILE) Next, you will need to add this code to your main ``CMakeLists.txt`` file of your project: .. code-block:: CMAKE :linenos: add_subdirectory(po) file(GLOB_RECURSE I18N_SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/po *.qml *.js) list(FILTER I18N_SRC_FILES EXCLUDE REGEX "${CMAKE_CURRENT_SOURCE_DIR}/build/.*" ) list(APPEND I18N_SRC_FILES ${DESKTOP_FILE_NAME}.in.h) find_program(INTLTOOL_MERGE intltool-merge) if(NOT INTLTOOL_MERGE) message(FATAL_ERROR "Could not find intltool-merge, please install the intltool package") endif() find_program(INTLTOOL_EXTRACT intltool-extract) if(NOT INTLTOOL_EXTRACT) message(FATAL_ERROR "Could not find intltool-extract, please install the intltool package") endif() add_custom_target(${DESKTOP_FILE_NAME} ALL COMMENT "Merging translations into ${DESKTOP_FILE_NAME}..." COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${CMAKE_SOURCE_DIR}/${DESKTOP_FILE_NAME}.in ${DESKTOP_FILE_NAME} COMMAND sed -i 's/${PROJECT_NAME}-//g' ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE_NAME} ) 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: .. code-block:: CMAKE :linenos: set(PROJECT_NAME "Your project") set(FULL_PROJECT_NAME "Your.project") set(CMAKE_INSTALL_PREFIX /) set(DATA_DIR /) 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: .. code-block:: QML 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 `_