1 /*************************************************************************** 2 * Free Heroes of Might and Magic II: https://github.com/ihhub/fheroes2 * 3 * Copyright (C) 2021 * 4 * * 5 * This program is free software; you can redistribute it and/or modify * 6 * it under the terms of the GNU General Public License as published by * 7 * the Free Software Foundation; either version 2 of the License, or * 8 * (at your option) any later version. * 9 * * 10 * This program is distributed in the hope that it will be useful, * 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 * GNU General Public License for more details. * 14 * * 15 * You should have received a copy of the GNU General Public License * 16 * along with this program; if not, write to the * 17 * Free Software Foundation, Inc., * 18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 ***************************************************************************/ 20 21 #include "ui_language.h" 22 #include "agg.h" 23 #include "agg_image.h" 24 #include "icn.h" 25 #include "settings.h" 26 #include "tools.h" 27 #include "translations.h" 28 29 #include <cassert> 30 #include <map> 31 #include <set> 32 33 namespace 34 { 35 const std::map<uint32_t, fheroes2::SupportedLanguage> languageCRC32 = { { 0x406967B9, fheroes2::SupportedLanguage::French }, // GoG version 36 { 0x04745D1D, fheroes2::SupportedLanguage::German }, // GoG version 37 { 0x88774771, fheroes2::SupportedLanguage::Polish }, // GoG version 38 { 0xDB10FFD8, fheroes2::SupportedLanguage::Russian }, // XXI Vek version 39 { 0xD5CF8AF3, fheroes2::SupportedLanguage::Russian }, // Buka version 40 { 0x219B3124, fheroes2::SupportedLanguage::Italian } }; 41 42 // Strings in this map must in lower case and non translatable. 43 const std::map<std::string, fheroes2::SupportedLanguage> languageName 44 = { { "pl", fheroes2::SupportedLanguage::Polish }, { "polish", fheroes2::SupportedLanguage::Polish }, { "de", fheroes2::SupportedLanguage::German }, 45 { "german", fheroes2::SupportedLanguage::German }, { "fr", fheroes2::SupportedLanguage::French }, { "french", fheroes2::SupportedLanguage::French }, 46 { "ru", fheroes2::SupportedLanguage::Russian }, { "russian", fheroes2::SupportedLanguage::Russian }, { "it", fheroes2::SupportedLanguage::Italian }, 47 { "italian", fheroes2::SupportedLanguage::Italian } }; 48 getResourceLanguage()49 fheroes2::SupportedLanguage getResourceLanguage() 50 { 51 const std::vector<uint8_t> & data = ::AGG::ReadChunk( ICN::GetString( ICN::FONT ) ); 52 if ( data.empty() ) { 53 // How is it possible to run the game without a font? 54 assert( 0 ); 55 return fheroes2::SupportedLanguage::English; 56 } 57 58 const uint32_t crc32 = fheroes2::calculateCRC32( data.data(), data.size() ); 59 auto iter = languageCRC32.find( crc32 ); 60 if ( iter == languageCRC32.end() ) { 61 return fheroes2::SupportedLanguage::English; 62 } 63 64 return iter->second; 65 } 66 } 67 68 namespace fheroes2 69 { LanguageSwitcher(const SupportedLanguage language)70 LanguageSwitcher::LanguageSwitcher( const SupportedLanguage language ) 71 : _currentLanguage( Settings::Get().getGameLanguage() ) 72 { 73 Settings::Get().setGameLanguage( getLanguageAbbreviation( language ) ); 74 } 75 ~LanguageSwitcher()76 LanguageSwitcher::~LanguageSwitcher() 77 { 78 Settings::Get().setGameLanguage( _currentLanguage ); 79 } 80 getSupportedLanguages()81 std::vector<SupportedLanguage> getSupportedLanguages() 82 { 83 std::vector<SupportedLanguage> languages; 84 85 const SupportedLanguage resourceLanguage = getResourceLanguage(); 86 if ( resourceLanguage != SupportedLanguage::English ) { 87 languages.emplace_back( resourceLanguage ); 88 } 89 90 const std::set<SupportedLanguage> possibleLanguages{ SupportedLanguage::French, SupportedLanguage::Polish, SupportedLanguage::German, SupportedLanguage::Russian, 91 SupportedLanguage::Italian }; 92 93 for ( const SupportedLanguage language : possibleLanguages ) { 94 if ( language != resourceLanguage && AGG::isAlphabetSupported( language ) ) { 95 languages.emplace_back( language ); 96 } 97 } 98 99 Settings & conf = Settings::Get(); 100 101 fheroes2::SupportedLanguage currentLanguage = fheroes2::getLanguageFromAbbreviation( conf.getGameLanguage() ); 102 103 std::vector<fheroes2::SupportedLanguage> validSupportedLanguages{ fheroes2::SupportedLanguage::English }; 104 105 for ( fheroes2::SupportedLanguage language : languages ) { 106 if ( conf.setGameLanguage( fheroes2::getLanguageAbbreviation( language ) ) ) { 107 validSupportedLanguages.emplace_back( language ); 108 } 109 } 110 111 conf.setGameLanguage( fheroes2::getLanguageAbbreviation( currentLanguage ) ); 112 113 assert( !validSupportedLanguages.empty() ); 114 115 return validSupportedLanguages; 116 } 117 getLanguageName(const SupportedLanguage language)118 const char * getLanguageName( const SupportedLanguage language ) 119 { 120 switch ( language ) { 121 case SupportedLanguage::English: 122 return _( "English" ); 123 case SupportedLanguage::French: 124 return _( "French" ); 125 case SupportedLanguage::Polish: 126 return _( "Polish" ); 127 case SupportedLanguage::German: 128 return _( "German" ); 129 case SupportedLanguage::Russian: 130 return _( "Russian" ); 131 case SupportedLanguage::Italian: 132 return _( "Italian" ); 133 default: 134 // Did you add a new language? Please add the code to handle it. 135 assert( 0 ); 136 return nullptr; 137 } 138 } 139 getLanguageAbbreviation(const SupportedLanguage language)140 const char * getLanguageAbbreviation( const SupportedLanguage language ) 141 { 142 switch ( language ) { 143 case SupportedLanguage::English: 144 return ""; // English is a special case. It always returns an empty string as it's a default language. 145 case SupportedLanguage::French: 146 return "fr"; 147 case SupportedLanguage::Polish: 148 return "pl"; 149 case SupportedLanguage::German: 150 return "de"; 151 case SupportedLanguage::Russian: 152 return "ru"; 153 case SupportedLanguage::Italian: 154 return "it"; 155 default: 156 // Did you add a new language? Please add the code to handle it. 157 assert( 0 ); 158 return nullptr; 159 } 160 } 161 getLanguageFromAbbreviation(const std::string & abbreviation)162 SupportedLanguage getLanguageFromAbbreviation( const std::string & abbreviation ) 163 { 164 if ( abbreviation.empty() ) { 165 return SupportedLanguage::English; 166 } 167 168 const std::string name( StringLower( abbreviation ) ); 169 170 auto iter = languageName.find( name ); 171 if ( iter == languageName.end() ) { 172 // Unsupported language. Fallback to English. 173 return SupportedLanguage::English; 174 } 175 176 return iter->second; 177 } 178 updateAlphabet(const std::string & abbreviation)179 void updateAlphabet( const std::string & abbreviation ) 180 { 181 const SupportedLanguage language = getLanguageFromAbbreviation( abbreviation ); 182 const bool isOriginalResourceLanguage = ( language == SupportedLanguage::English ) || ( language == getResourceLanguage() ); 183 184 AGG::updateAlphabet( language, isOriginalResourceLanguage ); 185 } 186 } 187