From 0a71104127636067e6e464ed95f0208bfe0db8c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=AD=C2=ADn=20Lucas=20Golini?= Date: Thu, 16 Mar 2017 02:41:55 -0300 Subject: [PATCH] Translator class WIP. --HG-- branch : dev --- include/eepp/system.hpp | 1 + include/eepp/system/translator.hpp | 58 +++++++ projects/linux/ee.files | 2 + projects/linux/ee.includes | 2 + src/eepp/system/translator.cpp | 237 +++++++++++++++++++++++++++++ src/test/eetest.cpp | 9 ++ 6 files changed, 309 insertions(+) create mode 100644 include/eepp/system/translator.hpp create mode 100644 src/eepp/system/translator.cpp diff --git a/include/eepp/system.hpp b/include/eepp/system.hpp index 139810cfb..e55249d33 100644 --- a/include/eepp/system.hpp +++ b/include/eepp/system.hpp @@ -26,5 +26,6 @@ #include #include #include +#include #endif diff --git a/include/eepp/system/translator.hpp b/include/eepp/system/translator.hpp new file mode 100644 index 000000000..2e1ce4854 --- /dev/null +++ b/include/eepp/system/translator.hpp @@ -0,0 +1,58 @@ +#ifndef EE_SYSTEM_STRINGLOCALERESOURCE_HPP +#define EE_SYSTEM_STRINGLOCALERESOURCE_HPP + +#include + +namespace pugi { +class xml_node; +} + +namespace EE { namespace System { + +class IOStream; +class Pack; + +class Translator { + public: + typedef std::map StringDictionary; + typedef std::map StringLocaleDictionary; + + Translator( const std::locale& locale = std::locale() ); + + void loadFromDirectory( std::string dirPath, std::string ext = "xml" ); + + void loadFromFile( const std::string& path, std::string lang = "" ); + + void loadFromString( const std::string& string, std::string lang = "" ); + + void loadFromMemory( const void * buffer, Int32 bufferSize, std::string lang = "" ); + + void loadFromStream( IOStream& stream, std::string lang = "" ); + + void loadFromPack( Pack * pack, const std::string& FilePackPath, std::string lang = "" ); + + String getString( const std::string& key ); + + String getStringf( const char * key, ... ); + + void setLanguageFromLocale( std::locale locale ); + + std::string getDefaultLanguage() const; + + void setDefaultLanguage( const std::string& defaultLanguage ); + + std::string getCurrentLanguage() const; + + void setCurrentLanguage( const std::string& currentLanguage ); + protected: + std::string mDefaultLanguage; + std::string mCurrentLanguage; + StringLocaleDictionary mDictionary; + + void loadNodes( pugi::xml_node node, std::string lang = "" ); +}; + +}} + +#endif + diff --git a/projects/linux/ee.files b/projects/linux/ee.files index a3af7d8f3..f7846b839 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -5,6 +5,7 @@ ../../include/eepp/graphics/text.hpp ../../include/eepp/math/interpolation1d.hpp ../../include/eepp/math/interpolation2d.hpp +../../include/eepp/system/translator.hpp ../../include/eepp/ui/uidragablecontrol.hpp ../../include/eepp/ui/uiimage.hpp ../../include/eepp/ui/uilinearlayout.hpp @@ -25,6 +26,7 @@ ../../src/eepp/graphics/text.cpp ../../src/eepp/math/interpolation1d.cpp ../../src/eepp/math/interpolation2d.cpp +../../src/eepp/system/translator.cpp ../../src/eepp/ui/uidragablecontrol.cpp ../../src/eepp/ui/uihelper.cpp ../../src/eepp/ui/uiimage.cpp diff --git a/projects/linux/ee.includes b/projects/linux/ee.includes index 34f2ac886..8cc7c7f65 100644 --- a/projects/linux/ee.includes +++ b/projects/linux/ee.includes @@ -8,3 +8,5 @@ ../../include/eepp/ui ../../src/eepp/ui ../../src/eepp/helper/freetype2/include/ +../../include/eepp/system +../../src/eepp/system diff --git a/src/eepp/system/translator.cpp b/src/eepp/system/translator.cpp new file mode 100644 index 000000000..f42dac85b --- /dev/null +++ b/src/eepp/system/translator.cpp @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace EE { namespace System { + +Translator::Translator( const std::locale& locale ) : + mDefaultLanguage( "en" ) +{ + setLanguageFromLocale( locale ); +} + +void Translator::loadFromDirectory( std::string dirPath, std::string ext ) { + FileSystem::dirPathAddSlashAtEnd( dirPath ); + + if ( FileSystem::isDirectory( dirPath ) ) { + std::vector files = FileSystem::filesGetInPath( dirPath, true, true ); + String::toLowerInPlace( ext ); + + for ( size_t i = 0; i < files.size(); i++ ) { + std::string path = dirPath + files[i]; + + if ( FileSystem::fileExtension( path ) == ext ) { + std::string lang = FileSystem::fileRemoveExtension( files[i] ); + + loadFromFile( path, lang ); + } + } + } +} + +void Translator::loadNodes( pugi::xml_node node, std::string lang ) { + for ( pugi::xml_node resources = node; resources; resources = resources.next_sibling() ) { + std::string name = String::toLower( resources.name() ); + + if ( name == "resources" ) { + lang = lang.size() == 2 ? lang : resources.attribute("language").as_string(); + + if ( lang.empty() || lang.size() != 2 ) { + eePRINTL( "Error: Couldn't load i18n language strings: language not specified in file name or resources attribute \"language\"." ); + return; + } + + for ( pugi::xml_node string = resources.child("string"); string; string = string.next_sibling("string") ) { + std::string key = string.attribute("name").as_string(); + + if ( !key.empty() ) { + String txt( string.text().as_string() ); + + mDictionary[ lang ][ key ] = txt; + } + } + } + } +} + +void Translator::loadFromFile( const std::string& path, std::string lang ) { + if ( FileSystem::fileExists( path ) ) { + lang = lang.size() == 2 ? lang : FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( path ) ); + + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file( path.c_str() ); + + if ( result ) { + loadNodes( doc.first_child(), lang ); + } else { + eePRINTL( "Error: Couldn't load i18n file: %s", path.c_str() ); + eePRINTL( "Error description: %s", result.description() ); + eePRINTL( "Error offset: %d", result.offset ); + } + } else if ( PackManager::instance()->isFallbackToPacksActive() ) { + std::string path( path ); + Pack * pack = PackManager::instance()->exists( path ); + + if ( NULL != pack ) { + loadFromPack( pack, path, lang ); + } + } +} +void Translator::loadFromString( const std::string& string, std::string lang ) { + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_string( string.c_str() ); + + if ( result ) { + loadNodes( doc.first_child(), lang ); + } else { + eePRINTL( "Error: Couldn't load i18n file from string: %s", string.c_str() ); + eePRINTL( "Error description: %s", result.description() ); + eePRINTL( "Error offset: %d", result.offset ); + } +} + +void Translator::loadFromMemory( const void * buffer, Int32 bufferSize, std::string lang ) { + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_buffer( buffer, bufferSize ); + + if ( result ) { + loadNodes( doc.first_child(), lang ); + } else { + eePRINTL( "Error: Couldn't load i18n file from buffer" ); + eePRINTL( "Error description: %s", result.description() ); + eePRINTL( "Error offset: %d", result.offset ); + } +} + +void Translator::loadFromStream( IOStream& stream, std::string lang ) { + if ( !stream.isOpen() ) + return; + + ios_size bufferSize = stream.getSize(); + SafeDataPointer safeDataPointer( eeNewArray( Uint8, bufferSize ), bufferSize ); + stream.read( reinterpret_cast( safeDataPointer.Data ), safeDataPointer.DataSize ); + + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_buffer( safeDataPointer.Data, safeDataPointer.DataSize ); + + if ( result ) { + loadNodes( doc.first_child(), lang ); + } else { + eePRINTL( "Error: Couldn't load i18n file from stream" ); + eePRINTL( "Error description: %s", result.description() ); + eePRINTL( "Error offset: %d", result.offset ); + } +} + +void Translator::loadFromPack( Pack * pack, const std::string& FilePackPath, std::string lang ) { + SafeDataPointer PData; + + if ( pack->isOpen() && pack->extractFileToMemory( FilePackPath, PData ) ) { + lang = lang.size() == 2 ? lang : FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( FilePackPath ) ); + + loadFromMemory( PData.Data, PData.DataSize, lang ); + } +} + +String Translator::getString( const std::string& key ) { + StringLocaleDictionary::iterator lang = mDictionary.find( mCurrentLanguage ); + + if ( lang != mDictionary.end() ) { + StringDictionary& dictionary = lang->second; + StringDictionary::iterator string = dictionary.find( key ); + + if ( string != dictionary.end() ) { + return string->second; + } + } + + lang = mDictionary.find( mDefaultLanguage ); + + if ( lang != mDictionary.end() ) { + StringDictionary& dictionary = lang->second; + StringDictionary::iterator string = dictionary.find( key ); + + if ( string != dictionary.end() ) { + return string->second; + } + } + + return String(); +} + +String Translator::getStringf( const char * key, ... ) { + std::string str( getString( key ).toUtf8() ); + + if ( str.empty() ) + return String(); + + const char * format = str.c_str(); + + int n, size = 256; + std::string tstr( size, '\0' ); + + va_list args; + + while (1) { + va_start( args, key ); + + n = vsnprintf( &tstr[0], size, format, args ); + + if ( n > -1 && n < size ) { + tstr.resize( n ); + + va_end( args ); + + return tstr; + } + + if ( n > -1 ) // glibc 2.1 + size = n+1; // precisely what is needed + else // glibc 2.0 + size *= 2; // twice the old size + + tstr.resize( size ); + } + + return String( tstr ); +} + +void Translator::setLanguageFromLocale( std::locale locale ) { + std::string name = locale.name(); + + if ( "C" == name ) { + locale = std::locale( setlocale( LC_ALL, "" ) ); + + if ( "C" == locale.name() ) { + mCurrentLanguage = "en"; + } else { + mCurrentLanguage = locale.name().substr(0,2); + } + } else { + mCurrentLanguage = name.substr(0,2); + } +} + +std::string Translator::getDefaultLanguage() const { + return mDefaultLanguage; +} + +void Translator::setDefaultLanguage(const std::string & defaultLanguage) { + mDefaultLanguage = defaultLanguage; +} + +std::string Translator::getCurrentLanguage() const { + return mCurrentLanguage; +} + +void Translator::setCurrentLanguage(const std::string & currentLanguage) { + mCurrentLanguage = currentLanguage; +} + +}} diff --git a/src/test/eetest.cpp b/src/test/eetest.cpp index 83b8aef21..f6cd75894 100644 --- a/src/test/eetest.cpp +++ b/src/test/eetest.cpp @@ -11,6 +11,15 @@ namespace Demo_Test { void EETest::init() { EE = Engine::instance(); + Translator t; + + t.loadFromString( + "" + " eepp" + " Test %d %s" + "" + ); + Log::instance()->setLiveWrite( true ); Log::instance()->setConsoleOutput( true );