The class te::core::Translator provides support for language translation using Boost.Locale.
This class is a singleton that can be used to translate messages according to the system locale and translation dictionaries.
Translation dictionaries are sets of textual and binary files that are created through the GNU Gettext tools.
The translation is not automatic, it is necessary to perform this process manually in the “po” files.
The API for messages translation in TerraLib is defined by Translator class, shown below:
namespace te { namespace core { /*! \class Translator \brief This singleton is designed to deal with multi-language text translation in TerraLib. */ class Translator { public: static Translator& instance(); /*! \brief It tries to translate the specified text string. If no translation is available it will return the original message (always in English). \param message The text to be translated. \return A string of the translated message. You must not delete the memory pointed by the returned pointer. \note The returned message is UTF-8 encoded. */ std::string translate(const std::string& message); /*! \brief It tries to translate the specified text string. If no translation is available it will return the original message (always in English). \param message The text to be translated. \return A string of the translated message. You must not delete the memory pointed by the returned pointer. \note The returned message is UTF-8 encoded. */ std::string translate(const char* message); /*! \brief It tries to translate the specified text string accounting for plural forms. If no translation is available it will return the original message (always in English). \param msg1 The singular form of the text to be translated. \param msg2 The plural form of the text to be translated. \param n This parameter is used to determine the plural form. \return A string of the translated message. You must not delete the memory pointed by the returned pointer. \note The returned message is UTF-8 encoded. */ std::string translate(const std::string& msg1, const std::string& msg2, unsigned int n); /*! \brief It tries to translate the specified text string accounting for plural forms. If no translation is available it will return the original message (always in English). \param msg1 The singular form of the text to be translated. \param msg2 The plural form of the text to be translated. \param n This parameter is used to determine the plural form. \return A string of the translated message. You must not delete the memory pointed by the returned pointer. \note The returned message is UTF-8 encoded. */ std::string translate(const char* msg1, const char* msg2, unsigned int n); /*! \brief It sets the locale for the Translator. \param locale A string of the new locale. */ void setLocale(const std::string &locale); /*! \brief It adds a new text domain (text catalog). \param textDomain A given message domain (just a name). A text domain is the name of the catalog used to translate the message. \param dir Where the text domain is located. \exception Exception If the text domain already exists it raises an exception. If you are not sure about the existence of a text domain, use the exist() method. */ void addTextDomain(const std::string& textDomain, const std::string& dir); /*! \brief It returns true if the text domain (text catalog) exists and false otherwise. \param textDomain A given message domain (just a name). */ bool exist(const std::string& textDomain); }; } // end namespace core } // end namespace te
In addition to the class above, there are macros that assist in API usage process.
The macro TO ADD TEXT DOMAIN will register the name of a translation file in the language system and where it is located:
TE_ADD_TEXT_DOMAIN("terralib_mod_core", te::core::FindInTerraLibPath("share/terralib/translations"));
The macro TE_TR can be used to delimit text that should be translated:
std::cout << TE_TR("This text should be translated") << "\n";
When messages depend on the number of inflexion (plural), you should use the macro TE_TR_PLURAL:
std::cout << TE_TR_PLURAL("1 Layer is selected","2 Layers are selected", 1); // Irá traduzir para a primeira mensagem. std::cout << TE_TR_PLURAL("1 Layer is selected","2 Layers are selected", 2); // Irá traduzir para a segunda mensagem.
The macro TE_TR_LANGUAGE should only be used by the application to inform which language is in use:
TE_TR_LANGUAGE("pt_BR"); // Para que as traduções sejam feitas para Português(Brasil). TE_TR_LANGUAGE("es_ES"); // Para que as traduções sejam feitas para Espanhol.
To facilitate the creation of binary files containing the translated messages as well as their inclusion in the binary distribution of packages in TerraLib family applications, the macro TERRALIB_REGISTER_TRANSLATION was created in CMake to be able to automate this process. This macro is in build/cmake/terralib_macros.cmake file in the code repository.
All TerraLib libraries that need the message translation system should use the above macro in their build project files (CMakeLists.txt), in the following form:
TERRALIB_REGISTER_TRANSLATION("library-name" "language" "po-file-directory" "mo-file-output-directory")
Where:
library-name
: library name or executable set to target in CMake script. Ex: terralib_mod_corelanguage
: region of translations. Ex: pt_BRpo-file-directory
: folder of the translation file. The file in this folder must meet the following formation rule: library_name_language. Ex: terralib_mod_core_pt_BR.pomo-file-output-directory
: output folder to the binary file generated from the file containing the translations. Preferably, use a folder in the build tree of CMake project. Ex: ${CMAKE_BINARY_DIR}/share/terralib/translationsAs an example, we can take the line of CMakeLists.txt file of the TerraLib.Core project library:
TERRALIB_REGISTER_TRANSLATION("terralib_mod_core" "pt_BR" "${TERRALIB_ABSOLUTE_ROOT_DIR}/share/terralib/translations" "${CMAKE_BINARY_DIR}/share/terralib/translations")
The files “.po” can be created from the command line with the tools of GNU Gettext or through graphical tools like POEditor or Poedit.
Next, we will show how to create these files on the command line with GNU Gettext tools.
To start a new translation file, first we have to create a file with the extension “.pot”, which contains a template of the messages identified in the source code. The following command shows how to extract messages from the reported files:
$ xgettext --from-code="UTF-8" --no-wrap --c++ --keyword=TE_TR --keyword=TE_TR_PLURAL:1,2 -o terralib_unittest_core.pot repo/unittest/core/translator/TsTranslator.cpp repo/unittest/core/encoding/TsCharEncoding.cpp
The above command will produce the file terralib_unittest_core.pot.
If you do not already have a file with the extension “.po” in the case of creating new libraries, use the following command to create this file where should be included the translations for a particular language:
$ msginit --no-translator -l pt_BR -i terralib_unittest_core.pot -o terralib_unittest_core_pt_BR.po
The above command will produce the file terralib_unittest_core_pt_BR.po.
If you already have a file “.po” and just want to perform an update, you can do this using the following command:
$ xgettext --from-code="UTF-8" --no-wrap --c++ --keyword=TE_TR --keyword=TE_TR_PLURAL:1,2 -o terralib_unittest_core.pot repo/unittest/core/translator/TsTranslator.cpp repo/unittest/core/encoding/TsCharEncoding.cpp $ msgmerge -U --backup=none -q --lang=pt_BR terralib_unittest_core.pot terralib_unittest_core_pt_BR.po
Note: In xgettext command where you are upgrading the template file, you can enter new files if necessary.
The creation of the binary files with the extension “.mo”, containing translations to use in the systems, is done automatically in the build through the macro TERRALIB_REGISTER_TRANSLATION. Add to your CMakeLists.txt the record of translations as shown below:
TERRALIB_REGISTER_TRANSLATION("terralib_unittest_core" "pt_BR" "${TERRALIB_ABSOLUTE_ROOT_DIR}/share/terralib/translations" "${CMAKE_BINARY_DIR}/share/terralib/translations")
Note: use the same name as the library or executable in the record of your translation.
In your source code include a call to the macro TE_ADD_TEXT_DOMAIN to add at runtime your translation file. See the example below:
// TerraLib #include <terralib/core/translator/Translator.h> #include <terralib/core/utils/Platform.h> // STL #include <cstdlib> #include <iostream> int main(int argc, char *argv[]) { // Add your your text domain, will throw and error if was already added TE_ADD_TEXT_DOMAIN("terralib_example_core_translator", te::core::FindInTerraLibPath("share/terralib/translations")); ... // your code goes here! return EXIT_SUCCESS; }
Once the translation file is loaded, macros TE_TR and TE_TR_PLURAL will produce the correct translation:
// TerraLib #include <terralib/core/translator/Translator.h> #include <terralib/core/utils/Platform.h> // STL #include <cstdlib> #include <iostream> int main(int argc, char *argv[]) { TE_ADD_TEXT_DOMAIN("terralib_example_core_translator", te::core::FindInTerraLibPath("share/terralib/translations")); // if no translation is available, the phrase will result in the same message. std::cout << TE_TR("This text will remain the same, because there is no translation for it.") << "\n"; // set locale messages to pt_BR TE_TR_LANGUAGE("pt_BR"); std::cout << TE_TR("This text will be in portuguese if your system locale is pt_BR") << "\n"; // set locale messages to en_US TE_TR_LANGUAGE("en_US"); std::cout << TE_TR("This text will be in portuguese if your system locale is pt_BR") << "\n"; return EXIT_SUCCESS; }