2017-04-18 18:21:03 +03:00
/*
This file is part of Telegram Desktop ,
the official desktop version of Telegram messaging app , see https : //telegram.org
Telegram Desktop is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
It is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
In addition , as a special exception , the copyright holders give permission
to link the code of portions of this program with the OpenSSL library .
Full license : https : //github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright ( c ) 2014 - 2017 John Preston , https : //desktop.telegram.org
*/
# include "lang/lang_cloud_manager.h"
# include "lang/lang_instance.h"
# include "mtproto/mtp_instance.h"
# include "storage/localstorage.h"
# include "messenger.h"
# include "apiwrap.h"
# include "auth_session.h"
2017-04-18 20:37:14 +03:00
# include "boxes/confirm_box.h"
2017-05-30 20:58:25 +03:00
# include "lang/lang_file_parser.h"
# include "core/file_utilities.h"
2017-04-18 18:21:03 +03:00
namespace Lang {
2017-05-30 19:58:13 +03:00
CloudManager : : CloudManager ( Instance & langpack , gsl : : not_null < MTP : : Instance * > mtproto ) : MTP : : Sender ( )
2017-04-18 18:21:03 +03:00
, _langpack ( langpack ) {
requestLangPackDifference ( ) ;
}
void CloudManager : : requestLangPackDifference ( ) {
2017-04-18 20:37:14 +03:00
if ( _langpack . isCustom ( ) | | _langPackRequestId ) {
2017-04-18 18:21:03 +03:00
return ;
}
2017-04-18 20:37:14 +03:00
auto version = _langpack . version ( ) ;
2017-04-18 18:21:03 +03:00
if ( version > 0 ) {
_langPackRequestId = request ( MTPlangpack_GetDifference ( MTP_int ( version ) ) ) . done ( [ this ] ( const MTPLangPackDifference & result ) {
_langPackRequestId = 0 ;
applyLangPackDifference ( result ) ;
} ) . fail ( [ this ] ( const RPCError & error ) {
_langPackRequestId = 0 ;
} ) . send ( ) ;
} else {
2017-05-30 12:31:32 +03:00
_langPackRequestId = request ( MTPlangpack_GetLangPack ( MTP_string ( _langpack . cloudLangCode ( ) ) ) ) . done ( [ this ] ( const MTPLangPackDifference & result ) {
2017-04-18 18:21:03 +03:00
_langPackRequestId = 0 ;
applyLangPackDifference ( result ) ;
} ) . fail ( [ this ] ( const RPCError & error ) {
_langPackRequestId = 0 ;
} ) . send ( ) ;
}
}
2017-05-30 12:31:32 +03:00
void CloudManager : : setSuggestedLanguage ( const QString & langCode ) {
2017-05-30 16:54:05 +03:00
if ( ! langCode . isEmpty ( )
2017-05-30 12:31:32 +03:00
& & langCode ! = Lang : : DefaultLanguageId ( ) ) {
_suggestedLanguage = langCode ;
} else {
_suggestedLanguage = QString ( ) ;
}
if ( ! _languageWasSuggested ) {
_languageWasSuggested = true ;
_firstLanguageSuggestion . notify ( ) ;
2017-05-30 16:54:05 +03:00
if ( AuthSession : : Exists ( ) & & _langpack . id ( ) . isEmpty ( ) & & ! _suggestedLanguage . isEmpty ( ) ) {
2017-06-04 16:09:53 +03:00
auto isLegacy = [ ] ( const QString & languageId ) {
for ( auto & legacyString : kLegacyLanguages ) {
auto legacyId = str_const_toString ( legacyString ) ;
if ( ConvertLegacyLanguageId ( legacyId ) = = languageId ) {
return true ;
}
}
return false ;
} ;
// The old available languages (de/it/nl/ko/es/pt_BR) won't be
// suggested anyway, because everyone saw the suggestion in intro.
if ( ! isLegacy ( _suggestedLanguage ) ) {
_offerSwitchToId = _suggestedLanguage ;
offerSwitchLangPack ( ) ;
}
2017-05-30 12:31:32 +03:00
}
}
}
2017-04-18 18:21:03 +03:00
void CloudManager : : applyLangPackDifference ( const MTPLangPackDifference & difference ) {
Expects ( difference . type ( ) = = mtpc_langPackDifference ) ;
2017-04-18 20:37:14 +03:00
if ( _langpack . isCustom ( ) ) {
2017-04-18 18:21:03 +03:00
return ;
}
auto & langpack = difference . c_langPackDifference ( ) ;
2017-04-18 20:37:14 +03:00
auto langpackId = qs ( langpack . vlang_code ) ;
if ( needToApplyLangPack ( langpackId ) ) {
applyLangPackData ( langpack ) ;
2017-05-30 20:58:25 +03:00
if ( _restartAfterSwitch ) {
App : : restart ( ) ;
}
2017-04-18 18:21:03 +03:00
} else {
2017-04-18 20:37:14 +03:00
LOG ( ( " Lang Warning: Ignoring update for '%1' because our language is '%2' " ) . arg ( langpackId ) . arg ( _langpack . id ( ) ) ) ;
2017-04-18 18:21:03 +03:00
}
}
void CloudManager : : requestLanguageList ( ) {
_languagesRequestId = request ( MTPlangpack_GetLanguages ( ) ) . done ( [ this ] ( const MTPVector < MTPLangPackLanguage > & result ) {
auto languages = Languages ( ) ;
for_const ( auto & langData , result . v ) {
t_assert ( langData . type ( ) = = mtpc_langPackLanguage ) ;
auto & language = langData . c_langPackLanguage ( ) ;
2017-06-04 16:09:53 +03:00
languages . push_back ( { qs ( language . vlang_code ) , qs ( language . vname ) , qs ( language . vnative_name ) } ) ;
2017-04-18 18:21:03 +03:00
}
if ( _languages ! = languages ) {
_languages = languages ;
_languagesChanged . notify ( ) ;
}
_languagesRequestId = 0 ;
} ) . fail ( [ this ] ( const RPCError & error ) {
_languagesRequestId = 0 ;
} ) . send ( ) ;
}
2017-04-18 20:37:14 +03:00
bool CloudManager : : needToApplyLangPack ( const QString & id ) {
auto currentId = _langpack . id ( ) ;
if ( currentId = = id ) {
return true ;
} else if ( currentId . isEmpty ( ) & & id = = DefaultLanguageId ( ) ) {
return true ;
}
return false ;
}
void CloudManager : : offerSwitchLangPack ( ) {
Expects ( ! _offerSwitchToId . isEmpty ( ) ) ;
Expects ( _offerSwitchToId ! = DefaultLanguageId ( ) ) ;
if ( ! showOfferSwitchBox ( ) ) {
subscribe ( languageListChanged ( ) , [ this ] {
showOfferSwitchBox ( ) ;
} ) ;
requestLanguageList ( ) ;
}
}
QString CloudManager : : findOfferedLanguageName ( ) {
for_const ( auto & language , _languages ) {
if ( language . id = = _offerSwitchToId ) {
return language . name ;
}
}
return QString ( ) ;
}
bool CloudManager : : showOfferSwitchBox ( ) {
auto name = findOfferedLanguageName ( ) ;
if ( name . isEmpty ( ) ) {
return false ;
}
Ui : : show ( Box < ConfirmBox > ( " Do you want to switch your language to " + name + " ? You can always change your language in Settings. " , " Change " , lang ( lng_cancel ) , [ this ] {
Ui : : hideLayer ( ) ;
if ( _offerSwitchToId . isEmpty ( ) ) {
return ;
}
2017-05-30 20:58:25 +03:00
request ( _langPackRequestId ) . cancel ( ) ;
performSwitchAndRestart ( _offerSwitchToId ) ;
2017-04-18 20:37:14 +03:00
} , [ this ] {
Ui : : hideLayer ( ) ;
changeIdAndReInitConnection ( DefaultLanguageId ( ) ) ;
Local : : writeLangPack ( ) ;
2017-05-30 20:58:25 +03:00
} ) , KeepOtherLayers ) ;
2017-04-18 20:37:14 +03:00
return true ;
}
void CloudManager : : applyLangPackData ( const MTPDlangPackDifference & data ) {
switchLangPackId ( qs ( data . vlang_code ) ) ;
if ( _langpack . version ( ) < data . vfrom_version . v ) {
requestLangPackDifference ( ) ;
} else if ( ! data . vstrings . v . isEmpty ( ) ) {
_langpack . applyDifference ( data ) ;
Local : : writeLangPack ( ) ;
} else {
LOG ( ( " Lang Info: Up to date. " ) ) ;
}
}
2017-05-30 20:58:25 +03:00
bool CloudManager : : canApplyWithoutRestart ( const QString & id ) const {
if ( id = = qstr ( " TEST_X " ) | | id = = qstr ( " TEST_0 " ) ) {
return true ;
}
// We don't support instant language switch if the auth session exists :(
return ! AuthSession : : Exists ( ) ;
}
void CloudManager : : resetToDefault ( ) {
performSwitch ( DefaultLanguageId ( ) ) ;
}
void CloudManager : : switchToLanguage ( QString id ) {
if ( id . isEmpty ( ) ) {
id = DefaultLanguageId ( ) ;
}
2017-06-02 14:46:02 +03:00
if ( _langpack . id ( ) = = id & & id ! = qstr ( " custom " ) ) {
2017-05-30 20:58:25 +03:00
return ;
}
request ( _switchingToLanguageRequest ) . cancel ( ) ;
if ( id = = qstr ( " custom " ) ) {
performSwitchToCustom ( ) ;
} else if ( canApplyWithoutRestart ( id ) ) {
performSwitch ( id ) ;
} else {
QVector < MTPstring > keys ;
keys . reserve ( 3 ) ;
keys . push_back ( MTP_string ( " lng_sure_save_language " ) ) ;
keys . push_back ( MTP_string ( " lng_box_ok " ) ) ;
keys . push_back ( MTP_string ( " lng_cancel " ) ) ;
_switchingToLanguageRequest = request ( MTPlangpack_GetStrings ( MTP_string ( id ) , MTP_vector < MTPstring > ( std : : move ( keys ) ) ) ) . done ( [ this , id ] ( const MTPVector < MTPLangPackString > & result ) {
auto values = Instance : : ParseStrings ( result ) ;
auto getValue = [ & values ] ( LangKey key ) {
auto it = values . find ( key ) ;
return ( it = = values . cend ( ) ) ? GetOriginalValue ( key ) : it - > second ;
} ;
auto text = getValue ( lng_sure_save_language ) ;
auto save = getValue ( lng_box_ok ) ;
auto cancel = getValue ( lng_cancel ) ;
Ui : : show ( Box < ConfirmBox > ( text , save , cancel , [ this , id ] {
performSwitchAndRestart ( id ) ;
} ) , KeepOtherLayers ) ;
} ) . send ( ) ;
}
}
void CloudManager : : performSwitchToCustom ( ) {
auto filter = qsl ( " Language files (*.strings) " ) ;
auto title = qsl ( " Choose language .strings file " ) ;
FileDialog : : GetOpenPath ( title , filter , [ weak = base : : weak_unique_ptr < CloudManager > ( this ) ] ( const FileDialog : : OpenResult & result ) {
if ( ! weak | | result . paths . isEmpty ( ) ) {
return ;
}
auto filePath = result . paths . front ( ) ;
Lang : : FileParser loader ( filePath , { lng_sure_save_language , lng_box_ok , lng_cancel } ) ;
if ( loader . errors ( ) . isEmpty ( ) ) {
weak - > request ( weak - > _switchingToLanguageRequest ) . cancel ( ) ;
if ( weak - > canApplyWithoutRestart ( qsl ( " custom " ) ) ) {
weak - > _langpack . switchToCustomFile ( filePath ) ;
} else {
auto values = loader . found ( ) ;
auto getValue = [ & values ] ( LangKey key ) {
auto it = values . find ( key ) ;
return ( it = = values . cend ( ) ) ? GetOriginalValue ( key ) : it . value ( ) ;
} ;
auto text = getValue ( lng_sure_save_language ) ;
auto save = getValue ( lng_box_ok ) ;
auto cancel = getValue ( lng_cancel ) ;
Ui : : show ( Box < ConfirmBox > ( text , save , cancel , [ weak , filePath ] {
weak - > _langpack . switchToCustomFile ( filePath ) ;
App : : restart ( ) ;
} ) , KeepOtherLayers ) ;
}
} else {
Ui : : show ( Box < InformBox > ( " Custom lang failed :( \n \n Error: " + loader . errors ( ) ) , KeepOtherLayers ) ;
}
} ) ;
}
void CloudManager : : switchToTestLanguage ( ) {
auto testLanguageId = ( _langpack . id ( ) = = qstr ( " TEST_X " ) ) ? qsl ( " TEST_0 " ) : qsl ( " TEST_X " ) ;
performSwitch ( testLanguageId ) ;
}
void CloudManager : : performSwitch ( const QString & id ) {
_restartAfterSwitch = false ;
2017-04-18 18:21:03 +03:00
switchLangPackId ( id ) ;
requestLangPackDifference ( ) ;
}
2017-05-30 20:58:25 +03:00
void CloudManager : : performSwitchAndRestart ( const QString & id ) {
performSwitch ( id ) ;
if ( _langPackRequestId ) {
_restartAfterSwitch = true ;
} else {
App : : restart ( ) ;
}
}
2017-04-18 18:21:03 +03:00
void CloudManager : : switchLangPackId ( const QString & id ) {
2017-04-18 20:37:14 +03:00
auto currentId = _langpack . id ( ) ;
auto notChanged = ( currentId = = id ) | | ( currentId . isEmpty ( ) & & id = = DefaultLanguageId ( ) ) ;
if ( ! notChanged ) {
changeIdAndReInitConnection ( id ) ;
2017-04-18 18:21:03 +03:00
}
}
2017-04-18 20:37:14 +03:00
void CloudManager : : changeIdAndReInitConnection ( const QString & id ) {
_langpack . switchToId ( id ) ;
auto mtproto = requestMTP ( ) ;
mtproto - > reInitConnection ( mtproto - > mainDcId ( ) ) ;
}
2017-04-18 18:21:03 +03:00
CloudManager & CurrentCloudManager ( ) {
auto result = Messenger : : Instance ( ) . langCloudManager ( ) ;
t_assert ( result ! = nullptr ) ;
return * result ;
}
} // namespace Lang