====== TerraLib.Core: Translator ====== The class **te::core::Translator** provides support for language translation using [[http://www.boost.org/doc/libs/1_62_0/libs/locale/doc/html/index.html | 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 [[https://www.gnu.org/software/gettext/ | GNU Gettext]] tools. The translation is not automatic, it is necessary to perform this process manually in the "po" files. ===== API ===== ==== C++ ==== 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. ==== CMake ==== 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_core * ''language'': region of translations. Ex: pt_BR * ''po-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.po * ''mo-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/translations As 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") ===== Examples ===== The files ".po" can be created from the command line with the tools of [[https://www.gnu.org/software/gettext/ | GNU Gettext]] or through graphical tools like [[https://poeditor.com/ | ​POEditor]] or [[https://poedit.net/download | Poedit]]. Next, we will show how to create these files on the command line with [[https://www.gnu.org/software/gettext/ | 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 #include // STL #include #include 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 #include // STL #include #include 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; } ===== Additional References ===== * [[http://www.boost.org/doc/libs/1_56_0/libs/locale/doc/html/messages_formatting.html | Boost.Locale - Messages Formatting (Translation)]]