Skip to content

Commit

Permalink
Implement OTA updates of translations
Browse files Browse the repository at this point in the history
Check for and download updated translations periodically.

Over-the-Air updates like this decouple translations from Poedit code
releases and allow both faster updates and proof-reading / testing of
translations directly in the app.
  • Loading branch information
vslavik committed Oct 26, 2023
1 parent e58c8d1 commit e7173a5
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 1 deletion.
4 changes: 4 additions & 0 deletions deps/boost/Boost.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
B2167C331815ADF0003A1428 /* portability.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2167C2C1815ADF0003A1428 /* portability.cpp */; };
B2167C341815ADF0003A1428 /* unique_path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2167C2D1815ADF0003A1428 /* unique_path.cpp */; };
B2167C351815ADF0003A1428 /* utf8_codecvt_facet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2167C2E1815ADF0003A1428 /* utf8_codecvt_facet.cpp */; };
B24D1D092AE95B3A00FB31D9 /* gzip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B24D1D082AE95B3A00FB31D9 /* gzip.cpp */; };
B287410C1815AF6000C4051F /* future.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B28741061815AF6000C4051F /* future.cpp */; };
B287410D1815AF6000C4051F /* once.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B28741081815AF6000C4051F /* once.cpp */; };
B287410F1815AF6000C4051F /* thread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B287410A1815AF6000C4051F /* thread.cpp */; };
Expand Down Expand Up @@ -53,6 +54,7 @@
B2167C2E1815ADF0003A1428 /* utf8_codecvt_facet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = utf8_codecvt_facet.cpp; path = libs/filesystem/src/utf8_codecvt_facet.cpp; sourceTree = "<group>"; };
B2167C3B1815AEA5003A1428 /* libboost_thread.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libboost_thread.a; sourceTree = BUILT_PRODUCTS_DIR; };
B228A1561AD95CB9006B0BBC /* third_party.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = third_party.xcconfig; path = ../custom_build/third_party.xcconfig; sourceTree = "<group>"; };
B24D1D082AE95B3A00FB31D9 /* gzip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gzip.cpp; path = libs/iostreams/src/gzip.cpp; sourceTree = "<group>"; };
B28741061815AF6000C4051F /* future.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = future.cpp; path = libs/thread/src/future.cpp; sourceTree = "<group>"; };
B28741081815AF6000C4051F /* once.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = once.cpp; sourceTree = "<group>"; };
B287410A1815AF6000C4051F /* thread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread.cpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -230,6 +232,7 @@
B287414D1815B0FB00C4051F /* file_descriptor.cpp */,
B287414E1815B0FB00C4051F /* mapped_file.cpp */,
B287414F1815B0FB00C4051F /* zlib.cpp */,
B24D1D082AE95B3A00FB31D9 /* gzip.cpp */,
);
name = boost_iostreams;
sourceTree = "<group>";
Expand Down Expand Up @@ -497,6 +500,7 @@
B28741511815B0FB00C4051F /* mapped_file.cpp in Sources */,
B28741501815B0FB00C4051F /* file_descriptor.cpp in Sources */,
B28741521815B0FB00C4051F /* zlib.cpp in Sources */,
B24D1D092AE95B3A00FB31D9 /* gzip.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
12 changes: 12 additions & 0 deletions src/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,18 @@ void Config::Write(const std::string& key, bool value)
wxConfig::Get()->Write(key, value);
}

bool Config::Read(const std::string& key, long *out)
{
CfgLock lock;
return wxConfig::Get()->Read(key, out);
}

void Config::Write(const std::string& key, long value)
{
CfgLock lock;
wxConfig::Get()->Write(key, value);
}


::PretranslateSettings Config::PretranslateSettings()
{
Expand Down
8 changes: 8 additions & 0 deletions src/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ class Config
static std::string LocalazyMetadata() { return Read("/accounts/localazy/metadata", std::string()); }
static void LocalazyMetadata(const std::string& prj) { return Write("/accounts/localazy/metadata", prj); }

static time_t OTATranslationLastCheck() { return Read("/ota/last_check", (long)0); }
static void OTATranslationLastCheck(time_t when) { Write("/ota/last_check", (long)when); }

static std::string OTATranslationEtag() { return Read("/ota/etag", std::string()); }
static void OTATranslationEtag(const std::string& etag) { Write("/ota/etag", etag); }

private:
template<typename T>
static T Read(const std::string& key, T defval)
Expand All @@ -87,10 +93,12 @@ class Config
static bool Read(const std::string& key, std::string *out);
static bool Read(const std::string& key, std::wstring *out);
static bool Read(const std::string& key, bool *out);
static bool Read(const std::string& key, long *out);

static void Write(const std::string& key, const std::string& value);
static void Write(const std::string& key, const std::wstring& value);
static void Write(const std::string& key, bool value);
static void Write(const std::string& key, long value);
};

#endif // Poedit_configuration_h
69 changes: 68 additions & 1 deletion src/edapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
#include <wx/ipc.h>
#include <wx/translation.h>

#include <iostream>
#include <fstream>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>

#ifdef __WXOSX__
#include "macos_helpers.h"
#endif
Expand Down Expand Up @@ -584,7 +590,7 @@ void PoeditApp::SetupLanguage()
if (langinfo)
{
language = langinfo->Language;
uilang.clear(); // will go through locale
// keep 'uilang' for use below
}
}
#endif
Expand Down Expand Up @@ -623,8 +629,69 @@ void PoeditApp::SetupLanguage()
#ifdef __WXMSW__
win_sparkle_set_lang(bestTrans.utf8_str());
#endif

#ifdef SUPPORTS_OTA_UPDATES
SetupOTALanguageUpdate(trans, str::to_utf8(bestTrans));
#endif
}

#ifdef SUPPORTS_OTA_UPDATES
void PoeditApp::SetupOTALanguageUpdate(wxTranslations *trans, const std::string& lang)
{
if (lang == "en")
return;

// use downloaded OTA translations:
auto dir = GetCacheDir("Translations") + "/" + POEDIT_VERSION;
wxFileTranslationsLoader::AddCatalogLookupPathPrefix(dir);
trans->AddCatalog("poedit-ota");

// ..and update them (but at most once a day):
auto lastCheck = Config::OTATranslationLastCheck();
auto now = time(NULL);
if (now < lastCheck + 24*60*60)
return;
Config::OTATranslationLastCheck(now);

wxFileName mofile(dir + "/" + lang + "/poedit-ota.mo");
wxFileName::Mkdir(mofile.GetPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL);

http_client::headers hdrs;
std::string etag;
if (mofile.FileExists())
etag = Config::OTATranslationEtag();
if (!etag.empty())
hdrs.emplace_back("If-None-Match", etag);

http_client::download_from_anywhere("https://ota.poedit.net/i18n/" POEDIT_VERSION "/" + lang + "/poedit-ota.mo.gz", hdrs)
.then_on_main([=](downloaded_file f)
{
TempOutputFileFor temp(mofile);
std::ofstream output_file(temp.FileName().fn_str(), std::ios_base::binary);
std::ifstream input_file(f.filename().GetFullPath().mb_str(), std::ios_base::binary);

if (output_file.fail() || input_file.fail())
return;

boost::iostreams::filtering_istream input;
input.push(boost::iostreams::gzip_decompressor());
input.push(input_file);

boost::iostreams::copy(input, output_file);
input_file.close();
output_file.close();

temp.Commit();
Config::OTATranslationEtag(f.etag());

// re-add the catalog to force reloading updated file
trans->AddCatalog("poedit-ota");
})
.catch_all([](dispatch::exception_ptr){});
}
#endif // SUPPORTS_OTA_UPDATES


wxLayoutDirection PoeditApp::GetLayoutDirection() const
{
return g_layoutDirection;
Expand Down
6 changes: 6 additions & 0 deletions src/edapp.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
class WXDLLIMPEXP_FWD_BASE wxConfigBase;
class WXDLLIMPEXP_FWD_BASE wxSingleInstanceChecker;

#if defined(HAVE_HTTP_CLIENT) && (defined(__WXMSW__) || defined(__WXOSX__))
#define SUPPORTS_OTA_UPDATES
#endif

/// wxApp for use with
class PoeditApp : public wxApp, public MenusManager
Expand Down Expand Up @@ -104,6 +107,9 @@ class PoeditApp : public wxApp, public MenusManager
void HandleCustomURI(const wxString& uri);

void SetupLanguage();
#ifdef SUPPORTS_OTA_UPDATES
void SetupOTALanguageUpdate(wxTranslations *trans, const std::string& lang);
#endif

// App-global menu commands:
void OnNewFromScratch(wxCommandEvent& event);
Expand Down
2 changes: 2 additions & 0 deletions src/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ class TempOutputFileFor
{
public:
explicit TempOutputFileFor(const wxString& filename);
explicit TempOutputFileFor(const wxFileName& filename) : TempOutputFileFor(filename.GetFullPath()) {}

~TempOutputFileFor();

/// Name of the temporary placeholder
Expand Down

0 comments on commit e7173a5

Please sign in to comment.