TerraLib and TerraView Wiki Page

This is an old revision of the document!


TerraLib.Core: Translator

A classe te::core::Translator fornece suporte a tradução de idiomas utilizando a Boost.Locale.

Esta classe é um singleton que pode ser utilizado para traduzir mensagens de acordo com a região do sistema e dicionários de traduções.

Os dicionários de traduções são conjuntos de arquivos textuais e binários que são criados através das ferramentas GNU Gettext.

A tradução não é automática, sendo necessário realizar este processo manualmente nos arquivos “.po”.

API

C++

A API para tradução de mensagens na TerraLib é definida pela classe Translator, mostrada abaixo:

namespace te
{
  namespace core
  {
    /*!
      \class Translator
 
      \brief This singleton is designed to deal with multi-language text translation in TerraLib.
     */
    class TECOREEXPORT 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);
 
      private:
 
        /*! \brief Singleton constructor must be private or protected. */
        Translator(){}
 
        /*! \brief Singleton destructor must be private or protected. */
        ~Translator(){}
 
        /*! \brief Singleton copy constructor must be private or protected. */
        Translator(Translator const&); //No copy allowed
 
        /*! \brief Singleton copy assignment operator must be private or protected. */
        Translator& operator=(Translator const&); //No copy allowed
 
      private:
 
        std::string m_locale;                         //!< If not empty, it is the current locale.
        std::map<std::string, std::vector<std::string> > m_textDomainMap;  //!< A vector from text domains to base directory for the message catalog.
    };
 
  } // end namespace core
}   // end namespace te

Além da classe acima, existem macros que auxiliam no processo de uso da API.

A macro TE_ADD_TEXT_DOMAIN irá registrar o nome de um arquivo de tradução no sistema de idiomas e onde ele está localizado:

TE_ADD_TEXT_DOMAIN("terralib_mod_core", te::core::FindInTerraLibPath("share/terralib/translations"));

A macro TE_TR pode ser utilizada para delimitar os textos que devam ser traduzidos:

std::cout << TE_TR("This text should be translated") << "\n";

Quando as mensagens dependem da flexão gramatical de número (plural), deve-se utilizar a 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.

A macro TE_TR_LANGUAGE só deve ser utilizada pela aplicação para informar qual o idioma em uso:

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

Para facilitar a criação de arquivos binários contendo as mensagens traduzidas, bem como sua inclusão nos pacotes de distribuição binária dos aplicativos da família TerraLib, foi criada uma macro no CMake chamada TERRALIB_REGISTER_TRANSLATION capaz de automatizar esse processo. Esta macro encontra-se no arquivo build/cmake/terralib_macros.cmake do repositório de código.

Todas as bibliotecas da TerraLib que necessitarem do sistema de tradução de mensagens deverão usar a macro acima em seus arquivos de projeto de build (CMakeLists.txt). Ela possui a seguinte forma:

TERRALIB_REGISTER_TRANSLATION("nome-biblioteca" "idioma" "diretório-arquivo-po" "diretório-saída-arquivo-mo")

Onde:

  • nome-biblioteca: nome da biblioteca ou executável definido como target no script CMake. Ex: terralib_mod_core
  • idioma: região que se encontram as traduções. Ex: pt_BR
  • diretório-arquivo-po: pasta onde se encontra o arquivo de traduções. O arquivo nesta pasta deve obedecer a seguinte regra de formação: nome_da_biblioteca_idioma. Ex: terralib_mod_core_pt_BR.po
  • diretório-saída-arquivo-mo: pasta de saída para o arquivo binário gerado a partir do arquivo contendo as traduções. De preferência, utilize uma pasta na árvore de build do projeto CMake. Ex: ${CMAKE_BINARY_DIR}/share/terralib/translations

Como exemplo, vamos tomar a linha do arquivo CMakeLists.txt do projeto da biblioteca TerraLib.Core:

TERRALIB_REGISTER_TRANSLATION("terralib_mod_core" "pt_BR"
                              "${TERRALIB_ABSOLUTE_ROOT_DIR}/share/terralib/translations"
                              "${CMAKE_BINARY_DIR}/share/terralib/translations")

Exemplos

Os arquivos “.po” podem ser criados na linha de comando com as ferramentas da GNU Gettext ou através de ferramentas gráficas como o ​POEditor ou o Poedit.

A seguir, iremos mostrar como criar esses arquivos na linha de comando com as ferramentas da GNU Gettext.

Para começar um novo arquivo de tradução, primeiramente, temos que criar um arquivo com a extensão “.pot”, que conterá um template das mensagens identificadas no código fonte. O comando a seguir mostra como extrair as mensagens dos arquivos informados:

$ 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

O comando acima irá produzir o arquivo terralib_unittest_core.pot.

Caso você ainda não tenha um arquivo com a extensão “.po”, no caso da criação de novas bibliotecas, utilize o comando abaixo para criar este arquivo onde deverão ser incluídas as traduções para um determinado idioma:

$ msginit --no-translator -l pt_BR -i terralib_unittest_core.pot -o terralib_unittest_core_pt_BR.po

O comando acima irá produzir o arquivo terralib_unittest_core_pt_BR.po.

Caso você já possua um arquivo “.po” e deseje apenas realizar uma atualização do mesmo, você pode fazer isso através do seguinte comando:

$ 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 

Nota: No comando xgettext, onde você está atualizando o arquivo template, você pode informar novos arquivos se necessário.

A criação dos arquivo binários com a extensão “.mo”, contendo as traduções para uso nos sistemas, é feita de forma automática no build através da macro TERRALIB_REGISTER_TRANSLATION. Para isso, acrescente no seu CMakeLists.txt o registro de traduções como mostrado abaixo:

TERRALIB_REGISTER_TRANSLATION("terralib_unittest_core" "pt_BR"
                              "${TERRALIB_ABSOLUTE_ROOT_DIR}/share/terralib/translations"
                              "${CMAKE_BINARY_DIR}/share/terralib/translations")

Nota: utilizar o mesmo nome da biblioteca ou executável no registro de sua tradução.

No seu código fonte inclua uma chamada à macro TE_ADD_TEXT_DOMAIN para adicionar em tempo de execução o seu arquivo de tradução. Veja o exemplo abaixo:

// 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;
}

Uma vez que o arquivo de tradução encontra-se carregado, as macros TE_TR e TE_TR_PLURAL irão produzir a tradução correta:

// 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;
}

Referências