55 * MuseScore
66 * Music Composition & Notation
77 *
8- * Copyright (C) 2021 MuseScore Limited and others
8+ * Copyright (C) 2025 MuseScore Limited and others
99 *
1010 * This program is free software: you can redistribute it and/or modify
1111 * it under the terms of the GNU General Public License version 3 as
3232
3333#include " languageserrors.h"
3434
35- #include " global/io/buffer.h"
36- #include " global/serialization/zipreader.h"
37- #include " global/concurrency/concurrent.h"
38-
3935#include " multiinstances/resourcelockguard.h"
4036
41- #include " translation.h"
42-
43- #include " log.h"
37+ #include " global/io/buffer.h"
38+ #include " global/serialization/zipreader.h"
39+ #include " global/translation.h"
40+ #include " global/log.h"
4441
4542using namespace Qt ::Literals::StringLiterals;
4643
@@ -186,13 +183,14 @@ void LanguagesService::setCurrentLanguage(const QString& languageCode)
186183 delete t;
187184 }
188185 m_translators.clear ();
186+ m_translators.reserve (lang.files .size ());
189187
190188 for (const io::path_t & file : lang.files ) {
191189 QTranslator* translator = new QTranslator ();
192190 bool ok = translator->load (file.toQString ());
193191 if (ok) {
194192 qApp->installTranslator (translator);
195- m_translators << translator;
193+ m_translators. push_back ( translator) ;
196194 } else {
197195 LOGE () << " Error loading translator " << file.toQString ();
198196 delete translator;
@@ -314,29 +312,53 @@ Progress LanguagesService::update(const QString& languageCode)
314312 return {};
315313 }
316314
317- if (m_updateOperationsHash.contains (effectiveLanguageCode)) {
318- return m_updateOperationsHash[effectiveLanguageCode];
315+ auto progressIt = m_updateOperationsHash.find (effectiveLanguageCode);
316+ if (progressIt != m_updateOperationsHash.end ()) {
317+ return progressIt.value ();
319318 }
320319
321320 Language& lang = m_languagesHash[effectiveLanguageCode];
322-
323321 if (!lang.isLoaded ()) {
324322 loadLanguage (lang);
325323 }
326324
327- Progress progress;
325+ auto finishProgress = [this , effectiveLanguageCode](const Ret& ret) {
326+ m_updateOperationsHash[effectiveLanguageCode].finish (ret);
327+ m_updateOperationsHash.remove (effectiveLanguageCode);
328+ };
329+
330+ auto updateFinished = [this , effectiveLanguageCode, finishProgress](const muse::Ret& ret) {
331+ if (ret) {
332+ m_restartRequiredToApplyLanguage = true ;
333+ m_restartRequiredToApplyLanguageChanged.send (m_restartRequiredToApplyLanguage);
334+ }
328335
329- m_updateOperationsHash.insert (effectiveLanguageCode, progress);
336+ finishProgress (ret);
337+ };
330338
331- progress.finished ().onReceive (this , [this , effectiveLanguageCode](const ProgressResult& res) {
332- if (!res.ret && res.ret .code () != static_cast <int >(Err::AlreadyUpToDate)) {
333- LOGE () << res.ret .toString ();
339+ auto updateCheckFinished = [this , effectiveLanguageCode, updateFinished, finishProgress](const muse::RetVal<bool >& res) {
340+ if (!res.ret ) {
341+ finishProgress (res.ret );
342+ return ;
334343 }
335344
336- m_updateOperationsHash.remove (effectiveLanguageCode);
337- });
345+ if (!res.val ) {
346+ finishProgress (make_ret (Err::AlreadyUpToDate));
347+ return ;
348+ }
349+
350+ updateLanguage (effectiveLanguageCode, updateFinished);
351+ };
338352
339- Concurrent::run (this , &LanguagesService::th_update, effectiveLanguageCode, progress);
353+ if (!m_networkManager) {
354+ m_networkManager = networkManagerCreator ()->makeNetworkManager ();
355+ }
356+
357+ Progress& progress = m_updateOperationsHash[effectiveLanguageCode];
358+ progress.start ();
359+ progress.progress (0 , 0 , muse::trc (" global" , " Checking for updates…" ));
360+
361+ checkUpdateAvailable (effectiveLanguageCode, updateCheckFinished);
340362
341363 return progress;
342364}
@@ -351,84 +373,81 @@ async::Channel<bool> LanguagesService::restartRequiredToApplyLanguageChanged() c
351373 return m_restartRequiredToApplyLanguageChanged;
352374}
353375
354- void LanguagesService::th_update (const QString& languageCode, Progress progress )
376+ void LanguagesService::checkUpdateAvailable (const QString& languageCode, std::function< void ( const RetVal< bool >&)> finished )
355377{
356- progress.start ();
357-
358- progress.progress (0 , 0 , muse::trc (" languages" , " Checking for updates…" ));
359-
360- if (!canUpdate (languageCode)) {
361- progress.finish (make_ret (Err::AlreadyUpToDate));
362- return ;
363- }
364-
365- Ret ret = downloadLanguage (languageCode, progress);
366- if (!ret) {
367- progress.finish (ret);
378+ auto buff = std::make_shared<QBuffer>();
379+ RetVal<Progress> progress = m_networkManager->get (configuration ()->languagesUpdateUrl ().toString (), buff);
380+ if (!progress.ret ) {
381+ finished (RetVal<bool >::make_ret (progress.ret ));
368382 return ;
369383 }
370384
371- m_restartRequiredToApplyLanguage = true ;
372- m_restartRequiredToApplyLanguageChanged.send (m_restartRequiredToApplyLanguage);
373-
374- progress.finish (make_ret (Err::NoError));
375- }
376-
377- bool LanguagesService::canUpdate (const QString& languageCode)
378- {
379- QBuffer buff;
380- deprecated::INetworkManagerPtr networkManagerPtr = networkManagerCreator ()->makeDeprecatedNetworkManager ();
381-
382- Ret ret = networkManagerPtr->get (configuration ()->languagesUpdateUrl ().toString (), &buff);
383- if (!ret) {
384- LOGE () << ret.toString ();
385- return false ;
386- }
385+ progress.val .finished ().onReceive (this , [this , languageCode, buff, finished](const ProgressResult& res) {
386+ if (!res.ret ) {
387+ finished (RetVal<bool >::make_ret (res.ret ));
388+ return ;
389+ }
387390
388- QJsonParseError err;
389- QJsonDocument jsonDoc = QJsonDocument::fromJson (buff.data (), &err);
390- if (err.error != QJsonParseError::NoError || !jsonDoc.isObject ()) {
391- return false ;
392- }
391+ QJsonParseError err;
392+ QJsonDocument jsonDoc = QJsonDocument::fromJson (buff->data (), &err);
393+ if (err.error != QJsonParseError::NoError || !jsonDoc.isObject ()) {
394+ finished (RetVal<bool >::make_ret (make_ret (Err::ErrorParseConfig)));
395+ return ;
396+ }
393397
394- QJsonObject languageObject = jsonDoc.object ().value (languageCode).toObject ();
398+ QJsonObject languageObject = jsonDoc.object ().value (languageCode).toObject ();
399+ const Language& language = m_languagesHash[languageCode];
395400
396- Language& language = m_languagesHash[languageCode] ;
401+ bool shouldUpdate = false ;
397402
398- for (const QString& resource : LANGUAGE_RESOURCE_NAMES) {
399- RetVal<QString> hash = fileHash (language.files [resource]);
400- QString latestHash = languageObject.value (resource).toObject ().value (" hash" ).toString ();
403+ for (const QString& resource : LANGUAGE_RESOURCE_NAMES) {
404+ RetVal<QString> hash = fileHash (language.files [resource]);
405+ QString latestHash = languageObject.value (resource).toObject ().value (" hash" ).toString ();
401406
402- if (!hash.ret || hash.val != latestHash) {
403- return true ;
407+ if (!hash.ret || hash.val != latestHash) {
408+ shouldUpdate = true ;
409+ break ;
410+ }
404411 }
405- }
406412
407- return false ;
413+ finished (RetVal<bool >::make_ok (shouldUpdate));
414+ });
408415}
409416
410- Ret LanguagesService::downloadLanguage (const QString& languageCode, Progress progress) const
417+ void LanguagesService::updateLanguage (const QString& languageCode, std::function< void ( const Ret&)> finished)
411418{
412- std::string downloadingStatusTitle = muse::trc (" languages" , " Downloading…" );
413- progress.progress (0 , 0 , downloadingStatusTitle);
419+ auto qbuff = std::make_shared<QBuffer>();
420+ QUrl url = configuration ()->languageFileServerUrl (languageCode);
421+ RetVal<Progress> downloadProgress = m_networkManager->get (url, qbuff);
422+ if (!downloadProgress.ret ) {
423+ finished (make_ret (Err::ErrorDownloadLanguage));
424+ return ;
425+ }
414426
415- QBuffer qbuff;
416- deprecated::INetworkManagerPtr networkManagerPtr = networkManagerCreator ()->makeDeprecatedNetworkManager ();
427+ m_updateOperationsHash[languageCode].progress (0 , 0 , muse::trc (" global" , " Downloading…" ));
417428
418- networkManagerPtr->progress ().progressChanged ().onReceive (
419- this , [&progress, &downloadingStatusTitle](int64_t current, int64_t total, const std::string&) {
420- progress.progress (current, total, downloadingStatusTitle);
429+ downloadProgress.val .progressChanged ().onReceive (this , [this , languageCode](int64_t current, int64_t total, const std::string&) {
430+ m_updateOperationsHash[languageCode].progress (current, total, muse::trc (" global" , " Downloading…" ));
421431 });
422432
423- Ret ret = networkManagerPtr-> get ( configuration ()-> languageFileServerUrl ( languageCode), & qbuff);
424- if (!ret) {
425- LOGE () << " Error while downloading: " << ret. toString ( );
426- return make_ret (Err::ErrorDownloadLanguage) ;
427- }
433+ downloadProgress. val . finished (). onReceive ( this , [ this , languageCode, qbuff, finished]( const ProgressResult& res) {
434+ if (!res. ret ) {
435+ finished ( make_ret (Err::ErrorDownloadLanguage) );
436+ return ;
437+ }
428438
429- progress .progress (0 , 0 , muse::trc (" languages " , " Unpacking…" ));
439+ m_updateOperationsHash[languageCode] .progress (0 , 0 , muse::trc (" global " , " Unpacking…" ));
430440
431- ByteArray ba = ByteArray::fromQByteArrayNoCopy (qbuff.data ());
441+ Ret ret = unpackAndWriteLanguage (qbuff->data ());
442+ finished (ret);
443+ });
444+ }
445+
446+ Ret LanguagesService::unpackAndWriteLanguage (const QByteArray& zipData)
447+ {
448+ TRACEFUNC;
449+
450+ ByteArray ba = ByteArray::fromQByteArrayNoCopy (zipData);
432451 io::Buffer buff (&ba);
433452 ZipReader zipReader (&buff);
434453
@@ -437,18 +456,17 @@ Ret LanguagesService::downloadLanguage(const QString& languageCode, Progress pro
437456
438457 for (const auto & info : zipReader.fileInfoList ()) {
439458 io::path_t userFilePath = configuration ()->languagesUserAppDataPath ().appendingComponent (info.filePath );
440- ret = fileSystem ()->writeFile (userFilePath, zipReader.fileData (info.filePath .toStdString ()));
459+ Ret ret = fileSystem ()->writeFile (userFilePath, zipReader.fileData (info.filePath .toStdString ()));
441460 if (!ret) {
442- LOGE () << " Error while writing to disk: " << ret.toString ();
443461 return make_ret (Err::ErrorWriteLanguage);
444462 }
445463 }
446464 }
447465
448- return muse:: make_ok ();
466+ return make_ok ();
449467}
450468
451- RetVal<QString> LanguagesService::fileHash (const io::path_t & path)
469+ RetVal<QString> LanguagesService::fileHash (const io::path_t & path) const
452470{
453471 RetVal<ByteArray> fileBytes;
454472 {
0 commit comments